]> granicus.if.org Git - imagemagick/blob - MagickCore/channel.c
https://github.com/ImageMagick/ImageMagick/issues/666
[imagemagick] / MagickCore / channel.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               CCCC  H   H   AAA   N   N  N   N  EEEEE   L                   %
7 %              C      H   H  A   A  NN  N  NN  N  E       L                   %
8 %              C      HHHHH  AAAAA  N N N  N N N  EEE     L                   %
9 %              C      H   H  A   A  N  NN  N  NN  E       L                   %
10 %               CCCC  H   H  A   A  N   N  N   N  EEEEE   LLLLL               %
11 %                                                                             %
12 %                                                                             %
13 %                      MagickCore Image Channel Methods                       %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                               December 2003                                 %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2017 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    https://www.imagemagick.org/script/license.php                           %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 %
38 */
39
40 /*
41   Include declarations.
42 */
43 #include "MagickCore/studio.h"
44 #include "MagickCore/cache-private.h"
45 #include "MagickCore/channel.h"
46 #include "MagickCore/colorspace-private.h"
47 #include "MagickCore/composite-private.h"
48 #include "MagickCore/enhance.h"
49 #include "MagickCore/image.h"
50 #include "MagickCore/list.h"
51 #include "MagickCore/log.h"
52 #include "MagickCore/monitor.h"
53 #include "MagickCore/monitor-private.h"
54 #include "MagickCore/option.h"
55 #include "MagickCore/pixel-accessor.h"
56 #include "MagickCore/pixel-private.h"
57 #include "MagickCore/resource_.h"
58 #include "MagickCore/string-private.h"
59 #include "MagickCore/thread-private.h"
60 #include "MagickCore/token.h"
61 #include "MagickCore/utility.h"
62 #include "MagickCore/version.h"
63 \f
64 /*
65 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
66 %                                                                             %
67 %                                                                             %
68 %                                                                             %
69 %     C h a n n e l F x I m a g e                                             %
70 %                                                                             %
71 %                                                                             %
72 %                                                                             %
73 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
74 %
75 %  ChannelFxImage() applies a channel expression to the specified image.  The
76 %  expression consists of one or more channels, either mnemonic or numeric (e.g.
77 %  red, 1), separated by actions as follows:
78 %
79 %    <=>     exchange two channels (e.g. red<=>blue)
80 %    =>      copy one channel to another channel (e.g. red=>green)
81 %    =       assign a constant value to a channel (e.g. red=50%)
82 %    ,       write new image channels in the specified order (e.g. red, green)
83 %    |       add a new output image for the next set of channel operations
84 %    ;       move to the next input image for the source of channel data
85 %
86 %  For example, to create 3 grayscale images from the red, green, and blue
87 %  channels of an image, use:
88 %
89 %    -channel-fx "red; green; blue"
90 %
91 %  A channel without an operation symbol implies separate (i.e, semicolon).
92 %
93 %  The format of the ChannelFxImage method is:
94 %
95 %      Image *ChannelFxImage(const Image *image,const char *expression,
96 %        ExceptionInfo *exception)
97 %
98 %  A description of each parameter follows:
99 %
100 %    o image: the image.
101 %
102 %    o expression: A channel expression.
103 %
104 %    o exception: return any errors or warnings in this structure.
105 %
106 */
107
108 typedef enum
109 {
110   ExtractChannelOp,
111   AssignChannelOp,
112   ExchangeChannelOp,
113   TransferChannelOp
114 } ChannelFx;
115
116 static MagickBooleanType ChannelImage(Image *destination_image,
117   const PixelChannel destination_channel,const ChannelFx channel_op,
118   const Image *source_image,const PixelChannel source_channel,
119   const Quantum pixel,ExceptionInfo *exception)
120 {
121   CacheView
122     *source_view,
123     *destination_view;
124
125   MagickBooleanType
126     status;
127
128   size_t
129     height,
130     width;
131
132   ssize_t
133     y;
134
135   status=MagickTrue;
136   source_view=AcquireVirtualCacheView(source_image,exception);
137   destination_view=AcquireAuthenticCacheView(destination_image,exception);
138   height=MagickMin(source_image->rows,destination_image->rows);
139   width=MagickMin(source_image->columns,destination_image->columns);
140 #if defined(MAGICKCORE_OPENMP_SUPPORT)
141   #pragma omp parallel for schedule(static,4) shared(status) \
142     magick_threads(source_image,source_image,height,1)
143 #endif
144   for (y=0; y < (ssize_t) height; y++)
145   {
146     PixelTrait
147       destination_traits,
148       source_traits;
149
150     register const Quantum
151       *magick_restrict p;
152
153     register Quantum
154       *magick_restrict q;
155
156     register ssize_t
157       x;
158
159     if (status == MagickFalse)
160       continue;
161     p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
162       exception);
163     q=GetCacheViewAuthenticPixels(destination_view,0,y,
164       destination_image->columns,1,exception);
165     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
166       {
167         status=MagickFalse;
168         continue;
169       }
170     destination_traits=GetPixelChannelTraits(destination_image,
171       destination_channel);
172     source_traits=GetPixelChannelTraits(source_image,source_channel);
173     if ((destination_traits == UndefinedPixelTrait) ||
174         (source_traits == UndefinedPixelTrait))
175       continue;
176     for (x=0; x < (ssize_t) width; x++)
177     {
178       if (channel_op == AssignChannelOp)
179         SetPixelChannel(destination_image,destination_channel,pixel,q);
180       else
181         SetPixelChannel(destination_image,destination_channel,
182           GetPixelChannel(source_image,source_channel,p),q);
183       p+=GetPixelChannels(source_image);
184       q+=GetPixelChannels(destination_image);
185     }
186     if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse)
187       status=MagickFalse;
188   }
189   destination_view=DestroyCacheView(destination_view);
190   source_view=DestroyCacheView(source_view);
191   return(status);
192 }
193
194 MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
195   ExceptionInfo *exception)
196 {
197 #define ChannelFxImageTag  "ChannelFx/Image"
198
199   ChannelFx
200     channel_op;
201
202   ChannelType
203     channel_mask;
204
205   char
206     token[MagickPathExtent];
207
208   const char
209     *p;
210
211   const Image
212     *source_image;
213
214   double
215     pixel;
216
217   Image
218     *destination_image;
219
220   MagickBooleanType
221     status;
222
223   PixelChannel
224     source_channel,
225     destination_channel;
226
227   ssize_t
228     channels;
229
230   assert(image != (Image *) NULL);
231   assert(image->signature == MagickCoreSignature);
232   if (image->debug != MagickFalse)
233     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
234   assert(exception != (ExceptionInfo *) NULL);
235   assert(exception->signature == MagickCoreSignature);
236   source_image=image;
237   destination_image=CloneImage(source_image,0,0,MagickTrue,exception);
238   if (destination_image == (Image *) NULL)
239     return((Image *) NULL);
240   if (expression == (const char *) NULL)
241     return(destination_image);
242   status=SetImageStorageClass(destination_image,DirectClass,exception);
243   if (status == MagickFalse)
244     {
245       destination_image=GetLastImageInList(destination_image);
246       return((Image *) NULL);
247     }
248   destination_channel=RedPixelChannel;
249   channel_mask=UndefinedChannel;
250   pixel=0.0;
251   p=(char *) expression;
252   GetNextToken(p,&p,MagickPathExtent,token);
253   channel_op=ExtractChannelOp;
254   for (channels=0; *token != '\0'; )
255   {
256     ssize_t
257       i;
258
259     /*
260       Interpret channel expression.
261     */
262     switch (*token)
263     {
264       case ',':
265       {
266         GetNextToken(p,&p,MagickPathExtent,token);
267         break;
268       }
269       case '|':
270       {
271         if (GetNextImageInList(source_image) != (Image *) NULL)
272           source_image=GetNextImageInList(source_image);
273         else
274           source_image=GetFirstImageInList(source_image);
275         GetNextToken(p,&p,MagickPathExtent,token);
276         break;
277       }
278       case ';':
279       {
280         Image
281           *canvas;
282
283         (void) SetPixelChannelMask(destination_image,channel_mask);
284         if ((channel_op == ExtractChannelOp) && (channels == 1))
285           {
286             (void) SetPixelMetaChannels(destination_image,0,exception);
287             (void) SetImageColorspace(destination_image,GRAYColorspace,
288               exception);
289           }
290         canvas=CloneImage(source_image,0,0,MagickTrue,exception);
291         if (canvas == (Image *) NULL)
292           {
293             destination_image=DestroyImageList(destination_image);
294             return(destination_image);
295           }
296         AppendImageToList(&destination_image,canvas);
297         destination_image=GetLastImageInList(destination_image);
298         status=SetImageStorageClass(destination_image,DirectClass,exception);
299         if (status == MagickFalse)
300           {
301             destination_image=GetLastImageInList(destination_image);
302             return((Image *) NULL);
303           }
304         GetNextToken(p,&p,MagickPathExtent,token);
305         channels=0;
306         destination_channel=RedPixelChannel;
307         channel_mask=UndefinedChannel;
308         break;
309       }
310       default:
311         break;
312     }
313     i=ParsePixelChannelOption(token);
314     if (i < 0)
315       {
316         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
317           "UnrecognizedChannelType","`%s'",token);
318         destination_image=DestroyImageList(destination_image);
319         return(destination_image);
320       }
321     source_channel=(PixelChannel) i;
322     channel_op=ExtractChannelOp;
323     GetNextToken(p,&p,MagickPathExtent,token);
324     if (*token == '<')
325       {
326         channel_op=ExchangeChannelOp;
327         GetNextToken(p,&p,MagickPathExtent,token);
328       }
329     if (*token == '=')
330       {
331         if (channel_op != ExchangeChannelOp)
332           channel_op=AssignChannelOp;
333         GetNextToken(p,&p,MagickPathExtent,token);
334       }
335     if (*token == '>')
336       {
337         if (channel_op != ExchangeChannelOp)
338           channel_op=TransferChannelOp;
339         GetNextToken(p,&p,MagickPathExtent,token);
340       }
341     switch (channel_op)
342     {
343       case AssignChannelOp:
344       case ExchangeChannelOp:
345       case TransferChannelOp:
346       {
347         if (channel_op == AssignChannelOp)
348           pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
349         else
350           {
351             i=ParsePixelChannelOption(token);
352             if (i < 0)
353               {
354                 (void) ThrowMagickException(exception,GetMagickModule(),
355                   OptionError,"UnrecognizedChannelType","`%s'",token);
356                 destination_image=DestroyImageList(destination_image);
357                 return(destination_image);
358               }
359           }
360         destination_channel=(PixelChannel) i;
361         if (i >= (ssize_t) GetPixelChannels(destination_image))
362           (void) SetPixelMetaChannels(destination_image,(size_t) (
363             destination_channel-GetPixelChannels(destination_image)+1),
364             exception);
365         if (image->colorspace != UndefinedColorspace)
366           switch (destination_channel)
367           {
368             case RedPixelChannel:
369             case GreenPixelChannel:
370             case BluePixelChannel:
371             case BlackPixelChannel:
372             case IndexPixelChannel:
373               break;
374             case AlphaPixelChannel:
375             {
376               destination_image->alpha_trait=BlendPixelTrait;
377               break;
378             }
379             case ReadMaskPixelChannel:
380             {
381               destination_image->read_mask=MagickTrue;
382               break;
383             }
384             case WriteMaskPixelChannel:
385             {
386               destination_image->write_mask=MagickTrue;
387               break;
388             }
389             case MetaPixelChannel:
390             default:
391             {
392               (void) SetPixelMetaChannels(destination_image,(size_t) (
393                 destination_channel-GetPixelChannels(destination_image)+1),
394                 exception);
395               break;
396             }
397           }
398         channel_mask=(ChannelType) (channel_mask | ParseChannelOption(token));
399         if (((channels >= 1)  || (destination_channel >= 1)) &&
400             (IsGrayColorspace(destination_image->colorspace) != MagickFalse))
401           (void) SetImageColorspace(destination_image,sRGBColorspace,exception);
402         GetNextToken(p,&p,MagickPathExtent,token);
403         break;
404       }
405       default:
406         break;
407     }
408     status=ChannelImage(destination_image,destination_channel,channel_op,
409       source_image,source_channel,ClampToQuantum(pixel),exception);
410     if (status == MagickFalse)
411       {
412         destination_image=DestroyImageList(destination_image);
413         break;
414       }
415     channels++;
416     if (channel_op == ExchangeChannelOp)
417       {
418         status=ChannelImage(destination_image,source_channel,channel_op,
419           source_image,destination_channel,ClampToQuantum(pixel),exception);
420         if (status == MagickFalse)
421           {
422             destination_image=DestroyImageList(destination_image);
423             break;
424           }
425         channels++;
426       }
427     switch (channel_op)
428     {
429       case ExtractChannelOp:
430       {
431         channel_mask=(ChannelType) (channel_mask | (1 << destination_channel));
432         destination_channel=(PixelChannel) (destination_channel+1);
433         break;
434       }
435       default:
436         break;
437     }
438     status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
439       strlen(expression));
440     if (status == MagickFalse)
441       break;
442   }
443   (void) SetPixelChannelMask(destination_image,channel_mask);
444   if ((channel_op == ExtractChannelOp) && (channels == 1))
445     {
446       (void) SetPixelMetaChannels(destination_image,0,exception);
447       (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
448     }
449   return(GetFirstImageInList(destination_image));
450 }
451 \f
452 /*
453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
454 %                                                                             %
455 %                                                                             %
456 %                                                                             %
457 %     C o m b i n e I m a g e s                                               %
458 %                                                                             %
459 %                                                                             %
460 %                                                                             %
461 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
462 %
463 %  CombineImages() combines one or more images into a single image.  The
464 %  grayscale value of the pixels of each image in the sequence is assigned in
465 %  order to the specified channels of the combined image.   The typical
466 %  ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
467 %
468 %  The format of the CombineImages method is:
469 %
470 %      Image *CombineImages(const Image *images,const ColorspaceType colorspace,
471 %        ExceptionInfo *exception)
472 %
473 %  A description of each parameter follows:
474 %
475 %    o images: the image sequence.
476 %
477 %    o colorspace: the image colorspace.
478 %
479 %    o exception: return any errors or warnings in this structure.
480 %
481 */
482 MagickExport Image *CombineImages(const Image *image,
483   const ColorspaceType colorspace,ExceptionInfo *exception)
484 {
485 #define CombineImageTag  "Combine/Image"
486
487   CacheView
488     *combine_view;
489
490   Image
491     *combine_image;
492
493   MagickBooleanType
494     status;
495
496   MagickOffsetType
497     progress;
498
499   ssize_t
500     y;
501
502   /*
503     Ensure the image are the same size.
504   */
505   assert(image != (const Image *) NULL);
506   assert(image->signature == MagickCoreSignature);
507   if (image->debug != MagickFalse)
508     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
509   assert(exception != (ExceptionInfo *) NULL);
510   assert(exception->signature == MagickCoreSignature);
511   combine_image=CloneImage(image,0,0,MagickTrue,exception);
512   if (combine_image == (Image *) NULL)
513     return((Image *) NULL);
514   if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
515     {
516       combine_image=DestroyImage(combine_image);
517       return((Image *) NULL);
518     }
519   (void) SetImageColorspace(combine_image,colorspace == UndefinedColorspace ?
520     sRGBColorspace : colorspace,exception);
521   switch (combine_image->colorspace)
522   {
523     case UndefinedColorspace:
524     case sRGBColorspace:
525     {
526       if (GetImageListLength(image) > 3)
527         combine_image->alpha_trait=BlendPixelTrait;
528       break;
529     }
530     case GRAYColorspace:
531     {
532       if (GetImageListLength(image) > 1)
533         combine_image->alpha_trait=BlendPixelTrait;
534       break;
535     }
536     case CMYKColorspace:
537     {
538       if (GetImageListLength(image) > 4)
539         combine_image->alpha_trait=BlendPixelTrait;
540       break;
541     }
542     default:
543       break;
544   }
545   /*
546     Combine images.
547   */
548   status=MagickTrue;
549   progress=0;
550   combine_view=AcquireAuthenticCacheView(combine_image,exception);
551   for (y=0; y < (ssize_t) combine_image->rows; y++)
552   {
553     CacheView
554       *image_view;
555
556     const Image
557       *next;
558
559     Quantum
560       *pixels;
561
562     register const Quantum
563       *magick_restrict p;
564
565     register Quantum
566       *magick_restrict q;
567
568     register ssize_t
569       i;
570
571     if (status == MagickFalse)
572       continue;
573     pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
574       1,exception);
575     if (pixels == (Quantum *) NULL)
576       {
577         status=MagickFalse;
578         continue;
579       }
580     next=image;
581     for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++)
582     {
583       register ssize_t
584         x;
585
586       PixelChannel channel=GetPixelChannelChannel(combine_image,i);
587       PixelTrait traits=GetPixelChannelTraits(combine_image,channel);
588       if (traits == UndefinedPixelTrait)
589         continue;
590       if (next == (Image *) NULL)
591         continue;
592       image_view=AcquireVirtualCacheView(next,exception);
593       p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
594       if (p == (const Quantum *) NULL)
595         continue;
596       q=pixels;
597       for (x=0; x < (ssize_t) combine_image->columns; x++)
598       {
599         if (x < (ssize_t) next->columns)
600           {
601             q[i]=GetPixelGray(next,p);
602             p+=GetPixelChannels(next);
603           }
604         q+=GetPixelChannels(combine_image);
605       }
606       image_view=DestroyCacheView(image_view);
607       next=GetNextImageInList(next);
608     }
609     if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
610       status=MagickFalse;
611     if (image->progress_monitor != (MagickProgressMonitor) NULL)
612       {
613         MagickBooleanType
614           proceed;
615
616         proceed=SetImageProgress(image,CombineImageTag,progress++,
617           combine_image->rows);
618         if (proceed == MagickFalse)
619           status=MagickFalse;
620       }
621   }
622   combine_view=DestroyCacheView(combine_view);
623   if (status == MagickFalse)
624     combine_image=DestroyImage(combine_image);
625   return(combine_image);
626 }
627 \f
628 /*
629 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
630 %                                                                             %
631 %                                                                             %
632 %                                                                             %
633 %   G e t I m a g e A l p h a C h a n n e l                                   %
634 %                                                                             %
635 %                                                                             %
636 %                                                                             %
637 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
638 %
639 %  GetImageAlphaChannel() returns MagickFalse if the image alpha channel is
640 %  not activated.  That is, the image is RGB rather than RGBA or CMYK rather
641 %  than CMYKA.
642 %
643 %  The format of the GetImageAlphaChannel method is:
644 %
645 %      MagickBooleanType GetImageAlphaChannel(const Image *image)
646 %
647 %  A description of each parameter follows:
648 %
649 %    o image: the image.
650 %
651 */
652 MagickExport MagickBooleanType GetImageAlphaChannel(const Image *image)
653 {
654   assert(image != (const Image *) NULL);
655   if (image->debug != MagickFalse)
656     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
657   assert(image->signature == MagickCoreSignature);
658   return(image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse);
659 }
660 \f
661 /*
662 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
663 %                                                                             %
664 %                                                                             %
665 %                                                                             %
666 %     S e p a r a t e I m a g e                                               %
667 %                                                                             %
668 %                                                                             %
669 %                                                                             %
670 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
671 %
672 %  SeparateImage() separates a channel from the image and returns it as a
673 %  grayscale image.
674 %
675 %  The format of the SeparateImage method is:
676 %
677 %      Image *SeparateImage(const Image *image,const ChannelType channel,
678 %        ExceptionInfo *exception)
679 %
680 %  A description of each parameter follows:
681 %
682 %    o image: the image.
683 %
684 %    o channel: the image channel.
685 %
686 %    o exception: return any errors or warnings in this structure.
687 %
688 */
689 MagickExport Image *SeparateImage(const Image *image,
690   const ChannelType channel_type,ExceptionInfo *exception)
691 {
692 #define GetChannelBit(mask,bit)  (((size_t) (mask) >> (size_t) (bit)) & 0x01)
693 #define SeparateImageTag  "Separate/Image"
694
695   CacheView
696     *image_view,
697     *separate_view;
698
699   Image
700     *separate_image;
701
702   MagickBooleanType
703     status;
704
705   MagickOffsetType
706     progress;
707
708   ssize_t
709     y;
710
711   /*
712     Initialize separate image attributes.
713   */
714   assert(image != (Image *) NULL);
715   assert(image->signature == MagickCoreSignature);
716   if (image->debug != MagickFalse)
717     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
718   assert(exception != (ExceptionInfo *) NULL);
719   assert(exception->signature == MagickCoreSignature);
720   separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
721     exception);
722   if (separate_image == (Image *) NULL)
723     return((Image *) NULL);
724   if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
725     {
726       separate_image=DestroyImage(separate_image);
727       return((Image *) NULL);
728     }
729   separate_image->intensity=Rec709LuminancePixelIntensityMethod;
730   separate_image->alpha_trait=UndefinedPixelTrait;
731   (void) SetImageColorspace(separate_image,GRAYColorspace,exception);
732   /*
733     Separate image.
734   */
735   status=MagickTrue;
736   progress=0;
737   image_view=AcquireVirtualCacheView(image,exception);
738   separate_view=AcquireAuthenticCacheView(separate_image,exception);
739 #if defined(MAGICKCORE_OPENMP_SUPPORT)
740   #pragma omp parallel for schedule(static,4) shared(progress,status) \
741     magick_threads(image,image,image->rows,1)
742 #endif
743   for (y=0; y < (ssize_t) image->rows; y++)
744   {
745     register const Quantum
746       *magick_restrict p;
747
748     register Quantum
749       *magick_restrict q;
750
751     register ssize_t
752       x;
753
754     if (status == MagickFalse)
755       continue;
756     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
757     q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
758       exception);
759     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
760       {
761         status=MagickFalse;
762         continue;
763       }
764     for (x=0; x < (ssize_t) image->columns; x++)
765     {
766       register ssize_t
767         i;
768
769       if (GetPixelWriteMask(image,p) == 0)
770         {
771           SetPixelBackgoundColor(separate_image,q);
772           p+=GetPixelChannels(image);
773           q+=GetPixelChannels(separate_image);
774           continue;
775         }
776       SetPixelChannel(separate_image,GrayPixelChannel,0,q);
777       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
778       {
779         PixelChannel channel=GetPixelChannelChannel(image,i);
780         PixelTrait traits=GetPixelChannelTraits(image,channel);
781         if ((traits == UndefinedPixelTrait) ||
782             (GetChannelBit(channel_type,channel) == 0))
783           continue;
784         SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
785       }
786       p+=GetPixelChannels(image);
787       q+=GetPixelChannels(separate_image);
788     }
789     if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
790       status=MagickFalse;
791     if (image->progress_monitor != (MagickProgressMonitor) NULL)
792       {
793         MagickBooleanType
794           proceed;
795
796 #if defined(MAGICKCORE_OPENMP_SUPPORT)
797         #pragma omp critical (MagickCore_SeparateImage)
798 #endif
799         proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
800         if (proceed == MagickFalse)
801           status=MagickFalse;
802       }
803   }
804   separate_view=DestroyCacheView(separate_view);
805   image_view=DestroyCacheView(image_view);
806   (void) SetImageChannelMask(separate_image,DefaultChannels);
807   if (status == MagickFalse)
808     separate_image=DestroyImage(separate_image);
809   return(separate_image);
810 }
811 \f
812 /*
813 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
814 %                                                                             %
815 %                                                                             %
816 %                                                                             %
817 %     S e p a r a t e I m a g e s                                             %
818 %                                                                             %
819 %                                                                             %
820 %                                                                             %
821 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
822 %
823 %  SeparateImages() returns a separate grayscale image for each channel
824 %  specified.
825 %
826 %  The format of the SeparateImages method is:
827 %
828 %      Image *SeparateImages(const Image *image,ExceptionInfo *exception)
829 %
830 %  A description of each parameter follows:
831 %
832 %    o image: the image.
833 %
834 %    o exception: return any errors or warnings in this structure.
835 %
836 */
837 MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
838 {
839   Image
840     *images,
841     *separate_image;
842
843   register ssize_t
844     i;
845
846   assert(image != (Image *) NULL);
847   assert(image->signature == MagickCoreSignature);
848   if (image->debug != MagickFalse)
849     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
850   images=NewImageList();
851   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
852   {
853     PixelChannel channel=GetPixelChannelChannel(image,i);
854     PixelTrait traits=GetPixelChannelTraits(image,channel);
855     if ((traits == UndefinedPixelTrait) || ((traits & UpdatePixelTrait) == 0))
856       continue;
857     separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
858     if (separate_image != (Image *) NULL)
859       AppendImageToList(&images,separate_image);
860   }
861   if (images == (Image *) NULL)
862     images=SeparateImage(image,UndefinedChannel,exception);
863   return(images);
864 }
865 \f
866 /*
867 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
868 %                                                                             %
869 %                                                                             %
870 %                                                                             %
871 %   S e t I m a g e A l p h a C h a n n e l                                   %
872 %                                                                             %
873 %                                                                             %
874 %                                                                             %
875 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
876 %
877 %  SetImageAlphaChannel() activates, deactivates, resets, or sets the alpha
878 %  channel.
879 %
880 %  The format of the SetImageAlphaChannel method is:
881 %
882 %      MagickBooleanType SetImageAlphaChannel(Image *image,
883 %        const AlphaChannelOption alpha_type,ExceptionInfo *exception)
884 %
885 %  A description of each parameter follows:
886 %
887 %    o image: the image.
888 %
889 %    o alpha_type:  The alpha channel type: ActivateAlphaChannel,
890 %      AssociateAlphaChannel, CopyAlphaChannel, DeactivateAlphaChannel,
891 %      DisassociateAlphaChannel,  ExtractAlphaChannel, OffAlphaChannel,
892 %      OnAlphaChannel, OpaqueAlphaChannel, SetAlphaChannel, ShapeAlphaChannel,
893 %      and TransparentAlphaChannel.
894 %
895 %    o exception: return any errors or warnings in this structure.
896 %
897 */
898
899 static inline void FlattenPixelInfo(const Image *image,const PixelInfo *p,
900   const double alpha,const Quantum *q,const double beta,
901   Quantum *composite)
902 {
903   double
904     Da,
905     gamma,
906     Sa;
907
908   register ssize_t
909     i;
910
911   /*
912     Compose pixel p over pixel q with the given alpha.
913   */
914   Sa=QuantumScale*alpha;
915   Da=QuantumScale*beta,
916   gamma=Sa*(-Da)+Sa+Da;
917   gamma=PerceptibleReciprocal(gamma);
918   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
919   {
920     PixelChannel channel=GetPixelChannelChannel(image,i);
921     PixelTrait traits=GetPixelChannelTraits(image,channel);
922     if (traits == UndefinedPixelTrait)
923       continue;
924     switch (channel)
925     {
926       case RedPixelChannel:
927       {
928         composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
929           (double) p->red,alpha));
930         break;
931       }
932       case GreenPixelChannel:
933       {
934         composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
935           (double) p->green,alpha));
936         break;
937       }
938       case BluePixelChannel:
939       {
940         composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
941           (double) p->blue,alpha));
942         break;
943       }
944       case BlackPixelChannel:
945       {
946         composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
947           (double) p->black,alpha));
948         break;
949       }
950       case AlphaPixelChannel:
951       {
952         composite[i]=ClampToQuantum(QuantumRange*(Sa*(-Da)+Sa+Da));
953         break;
954       }
955       default:
956         break;
957     }
958   }
959 }
960
961 MagickExport MagickBooleanType SetImageAlphaChannel(Image *image,
962   const AlphaChannelOption alpha_type,ExceptionInfo *exception)
963 {
964   CacheView
965     *image_view;
966
967   MagickBooleanType
968     status;
969
970   ssize_t
971     y;
972
973   assert(image != (Image *) NULL);
974   if (image->debug != MagickFalse)
975     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
976   assert(image->signature == MagickCoreSignature);
977   status=MagickTrue;
978   switch (alpha_type)
979   {
980     case ActivateAlphaChannel:
981     {
982       image->alpha_trait=BlendPixelTrait;
983       break;
984     }
985     case AssociateAlphaChannel:
986     {
987       /*
988         Associate alpha.
989       */
990       status=SetImageStorageClass(image,DirectClass,exception);
991       if (status == MagickFalse)
992         break;
993       image_view=AcquireAuthenticCacheView(image,exception);
994 #if defined(MAGICKCORE_OPENMP_SUPPORT)
995       #pragma omp parallel for schedule(static,4) shared(status) \
996         magick_threads(image,image,image->rows,1)
997 #endif
998       for (y=0; y < (ssize_t) image->rows; y++)
999       {
1000         register Quantum
1001           *magick_restrict q;
1002
1003         register ssize_t
1004           x;
1005
1006         if (status == MagickFalse)
1007           continue;
1008         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1009           exception);
1010         if (q == (Quantum *) NULL)
1011           {
1012             status=MagickFalse;
1013             continue;
1014           }
1015         for (x=0; x < (ssize_t) image->columns; x++)
1016         {
1017           double
1018             gamma;
1019
1020           register ssize_t
1021             i;
1022
1023           if (GetPixelWriteMask(image,q) == 0)
1024             {
1025               q+=GetPixelChannels(image);
1026               continue;
1027             }
1028           gamma=QuantumScale*GetPixelAlpha(image,q);
1029           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1030           {
1031             PixelChannel channel=GetPixelChannelChannel(image,i);
1032             PixelTrait traits=GetPixelChannelTraits(image,channel);
1033             if (channel == AlphaPixelChannel)
1034               continue;
1035             if ((traits & UpdatePixelTrait) == 0)
1036               continue;
1037             q[i]=ClampToQuantum(gamma*q[i]);
1038           }
1039           q+=GetPixelChannels(image);
1040         }
1041         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1042           status=MagickFalse;
1043       }
1044       image_view=DestroyCacheView(image_view);
1045       image->alpha_trait=CopyPixelTrait;
1046       return(status);
1047     }
1048     case BackgroundAlphaChannel:
1049     {
1050       /*
1051         Set transparent pixels to background color.
1052       */
1053       if (image->alpha_trait == UndefinedPixelTrait)
1054         break;
1055       status=SetImageStorageClass(image,DirectClass,exception);
1056       if (status == MagickFalse)
1057         break;
1058       image_view=AcquireAuthenticCacheView(image,exception);
1059 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1060       #pragma omp parallel for schedule(static,4) shared(status) \
1061         magick_threads(image,image,image->rows,1)
1062 #endif
1063       for (y=0; y < (ssize_t) image->rows; y++)
1064       {
1065         register Quantum
1066           *magick_restrict q;
1067
1068         register ssize_t
1069           x;
1070
1071         if (status == MagickFalse)
1072           continue;
1073         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1074           exception);
1075         if (q == (Quantum *) NULL)
1076           {
1077             status=MagickFalse;
1078             continue;
1079           }
1080         for (x=0; x < (ssize_t) image->columns; x++)
1081         {
1082           if (GetPixelAlpha(image,q) == TransparentAlpha)
1083             {
1084               SetPixelViaPixelInfo(image,&image->background_color,q);
1085               SetPixelChannel(image,AlphaPixelChannel,TransparentAlpha,q);
1086             }
1087           q+=GetPixelChannels(image);
1088         }
1089         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1090           status=MagickFalse;
1091       }
1092       image_view=DestroyCacheView(image_view);
1093       return(status);
1094     }
1095     case CopyAlphaChannel:
1096     {
1097       image->alpha_trait=UpdatePixelTrait;
1098       status=CompositeImage(image,image,IntensityCompositeOp,MagickTrue,0,0,
1099         exception);
1100       break;
1101     }
1102     case DeactivateAlphaChannel:
1103     {
1104       if (image->alpha_trait == UndefinedPixelTrait)
1105         status=SetImageAlpha(image,OpaqueAlpha,exception);
1106       image->alpha_trait=CopyPixelTrait;
1107       break;
1108     }
1109     case DisassociateAlphaChannel:
1110     {
1111       /*
1112         Disassociate alpha.
1113       */
1114       status=SetImageStorageClass(image,DirectClass,exception);
1115       if (status == MagickFalse)
1116         break;
1117       image->alpha_trait=BlendPixelTrait;
1118       image_view=AcquireAuthenticCacheView(image,exception);
1119 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1120       #pragma omp parallel for schedule(static,4) shared(status) \
1121         magick_threads(image,image,image->rows,1)
1122 #endif
1123       for (y=0; y < (ssize_t) image->rows; y++)
1124       {
1125         register Quantum
1126           *magick_restrict q;
1127
1128         register ssize_t
1129           x;
1130
1131         if (status == MagickFalse)
1132           continue;
1133         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1134           exception);
1135         if (q == (Quantum *) NULL)
1136           {
1137             status=MagickFalse;
1138             continue;
1139           }
1140         for (x=0; x < (ssize_t) image->columns; x++)
1141         {
1142           double
1143             gamma,
1144             Sa;
1145
1146           register ssize_t
1147             i;
1148
1149           if (GetPixelWriteMask(image,q) == 0)
1150             {
1151               q+=GetPixelChannels(image);
1152               continue;
1153             }
1154           Sa=QuantumScale*GetPixelAlpha(image,q);
1155           gamma=PerceptibleReciprocal(Sa);
1156           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1157           {
1158             PixelChannel channel=GetPixelChannelChannel(image,i);
1159             PixelTrait traits=GetPixelChannelTraits(image,channel);
1160             if (channel == AlphaPixelChannel)
1161               continue;
1162             if ((traits & UpdatePixelTrait) == 0)
1163               continue;
1164             q[i]=ClampToQuantum(gamma*q[i]);
1165           }
1166           q+=GetPixelChannels(image);
1167         }
1168         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1169           status=MagickFalse;
1170       }
1171       image_view=DestroyCacheView(image_view);
1172       image->alpha_trait=UndefinedPixelTrait;
1173       return(status);
1174     }
1175     case DiscreteAlphaChannel:
1176     {
1177       if (image->alpha_trait == UndefinedPixelTrait)
1178         status=SetImageAlpha(image,OpaqueAlpha,exception);
1179       image->alpha_trait=UpdatePixelTrait;
1180       break;
1181     }
1182     case ExtractAlphaChannel:
1183     {
1184       status=CompositeImage(image,image,AlphaCompositeOp,MagickTrue,0,0,
1185         exception);
1186       image->alpha_trait=UndefinedPixelTrait;
1187       break;
1188     }
1189     case OffAlphaChannel:
1190     {
1191       image->alpha_trait=UndefinedPixelTrait;
1192       break;
1193     }
1194     case OnAlphaChannel:
1195     {
1196       if (image->alpha_trait == UndefinedPixelTrait)
1197         status=SetImageAlpha(image,OpaqueAlpha,exception);
1198       image->alpha_trait=BlendPixelTrait;
1199       break;
1200     }
1201     case OpaqueAlphaChannel:
1202     {
1203       status=SetImageAlpha(image,OpaqueAlpha,exception);
1204       break;
1205     }
1206     case RemoveAlphaChannel:
1207     {
1208       /*
1209         Remove transparency.
1210       */
1211       if (image->alpha_trait == UndefinedPixelTrait)
1212         break;
1213       status=SetImageStorageClass(image,DirectClass,exception);
1214       if (status == MagickFalse)
1215         break;
1216       image_view=AcquireAuthenticCacheView(image,exception);
1217 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1218       #pragma omp parallel for schedule(static,4) shared(status) \
1219         magick_threads(image,image,image->rows,1)
1220 #endif
1221       for (y=0; y < (ssize_t) image->rows; y++)
1222       {
1223         register Quantum
1224           *magick_restrict q;
1225
1226         register ssize_t
1227           x;
1228
1229         if (status == MagickFalse)
1230           continue;
1231         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1232           exception);
1233         if (q == (Quantum *) NULL)
1234           {
1235             status=MagickFalse;
1236             continue;
1237           }
1238         for (x=0; x < (ssize_t) image->columns; x++)
1239         {
1240           FlattenPixelInfo(image,&image->background_color,
1241             image->background_color.alpha,q,(double)
1242             GetPixelAlpha(image,q),q);
1243           q+=GetPixelChannels(image);
1244         }
1245         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1246           status=MagickFalse;
1247       }
1248       image_view=DestroyCacheView(image_view);
1249       image->alpha_trait=image->background_color.alpha_trait;
1250       break;
1251     }
1252     case SetAlphaChannel:
1253     {
1254       if (image->alpha_trait == UndefinedPixelTrait)
1255         status=SetImageAlpha(image,OpaqueAlpha,exception);
1256       break;
1257     }
1258     case ShapeAlphaChannel:
1259     {
1260       /*
1261         Set alpha channel by shape.
1262       */
1263       status=SetImageStorageClass(image,DirectClass,exception);
1264       if (status == MagickFalse)
1265         break;
1266       image->alpha_trait=UpdatePixelTrait;
1267       (void) SetImageMask(image,WritePixelMask,image,exception);
1268       (void) LevelImageColors(image,&image->background_color,
1269         &image->background_color,MagickTrue,exception);
1270       (void) SetImageMask(image,WritePixelMask,(Image *) NULL,exception);
1271       break;
1272     }
1273     case TransparentAlphaChannel:
1274     {
1275       status=SetImageAlpha(image,TransparentAlpha,exception);
1276       break;
1277     }
1278     case UndefinedAlphaChannel:
1279       break;
1280   }
1281   if (status == MagickFalse)
1282     return(status);
1283   (void) SetPixelChannelMask(image,image->channel_mask);
1284   return(SyncImagePixelCache(image,exception));
1285 }