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