]> granicus.if.org Git - imagemagick/blob - MagickCore/channel.c
cleanup identical conditions (#1339)
[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-2018 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://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) shared(status) \
142     magick_number_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 CompositeMaskPixelChannel:
380             {
381               destination_image->channels=(ChannelType)
382                 (destination_image->channels | CompositeMaskChannel);
383               break;
384             }
385             case ReadMaskPixelChannel:
386             {
387               destination_image->channels=(ChannelType)
388                 (destination_image->channels | ReadMaskChannel);
389               break;
390             }
391             case WriteMaskPixelChannel:
392             {
393               destination_image->channels=(ChannelType)
394                 (destination_image->channels | WriteMaskChannel);
395               break;
396             }
397             case MetaPixelChannel:
398             default:
399             {
400               (void) SetPixelMetaChannels(destination_image,(size_t) (
401                 destination_channel-GetPixelChannels(destination_image)+1),
402                 exception);
403               break;
404             }
405           }
406         channel_mask=(ChannelType) (channel_mask | ParseChannelOption(token));
407         if (((channels >= 1)  || (destination_channel >= 1)) &&
408             (IsGrayColorspace(destination_image->colorspace) != MagickFalse))
409           (void) SetImageColorspace(destination_image,sRGBColorspace,exception);
410         GetNextToken(p,&p,MagickPathExtent,token);
411         break;
412       }
413       default:
414         break;
415     }
416     status=ChannelImage(destination_image,destination_channel,channel_op,
417       source_image,source_channel,ClampToQuantum(pixel),exception);
418     if (status == MagickFalse)
419       {
420         destination_image=DestroyImageList(destination_image);
421         break;
422       }
423     channels++;
424     if (channel_op == ExchangeChannelOp)
425       {
426         status=ChannelImage(destination_image,source_channel,channel_op,
427           source_image,destination_channel,ClampToQuantum(pixel),exception);
428         if (status == MagickFalse)
429           {
430             destination_image=DestroyImageList(destination_image);
431             break;
432           }
433         channels++;
434       }
435     switch (channel_op)
436     {
437       case ExtractChannelOp:
438       {
439         channel_mask=(ChannelType) (channel_mask |
440           (1UL << destination_channel));
441         destination_channel=(PixelChannel) (destination_channel+1);
442         break;
443       }
444       default:
445         break;
446     }
447     status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
448       strlen(expression));
449     if (status == MagickFalse)
450       break;
451   }
452   (void) SetPixelChannelMask(destination_image,channel_mask);
453   if ((channel_op == ExtractChannelOp) && (channels == 1))
454     {
455       (void) SetPixelMetaChannels(destination_image,0,exception);
456       (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
457     }
458   return(GetFirstImageInList(destination_image));
459 }
460 \f
461 /*
462 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
463 %                                                                             %
464 %                                                                             %
465 %                                                                             %
466 %     C o m b i n e I m a g e s                                               %
467 %                                                                             %
468 %                                                                             %
469 %                                                                             %
470 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
471 %
472 %  CombineImages() combines one or more images into a single image.  The
473 %  grayscale value of the pixels of each image in the sequence is assigned in
474 %  order to the specified channels of the combined image.   The typical
475 %  ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
476 %
477 %  The format of the CombineImages method is:
478 %
479 %      Image *CombineImages(const Image *images,const ColorspaceType colorspace,
480 %        ExceptionInfo *exception)
481 %
482 %  A description of each parameter follows:
483 %
484 %    o images: the image sequence.
485 %
486 %    o colorspace: the image colorspace.
487 %
488 %    o exception: return any errors or warnings in this structure.
489 %
490 */
491 MagickExport Image *CombineImages(const Image *image,
492   const ColorspaceType colorspace,ExceptionInfo *exception)
493 {
494 #define CombineImageTag  "Combine/Image"
495
496   CacheView
497     *combine_view;
498
499   Image
500     *combine_image;
501
502   MagickBooleanType
503     status;
504
505   MagickOffsetType
506     progress;
507
508   ssize_t
509     y;
510
511   /*
512     Ensure the image are the same size.
513   */
514   assert(image != (const Image *) NULL);
515   assert(image->signature == MagickCoreSignature);
516   if (image->debug != MagickFalse)
517     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
518   assert(exception != (ExceptionInfo *) NULL);
519   assert(exception->signature == MagickCoreSignature);
520   combine_image=CloneImage(image,0,0,MagickTrue,exception);
521   if (combine_image == (Image *) NULL)
522     return((Image *) NULL);
523   if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
524     {
525       combine_image=DestroyImage(combine_image);
526       return((Image *) NULL);
527     }
528   if (colorspace != UndefinedColorspace)
529     (void) SetImageColorspace(combine_image,colorspace,exception);
530   else
531     if (fabs(image->gamma-1.0) <= MagickEpsilon)
532       (void) SetImageColorspace(combine_image,RGBColorspace,exception);
533     else
534       (void) SetImageColorspace(combine_image,sRGBColorspace,exception);
535   switch (combine_image->colorspace)
536   {
537     case UndefinedColorspace:
538     case sRGBColorspace:
539     {
540       if (GetImageListLength(image) > 3)
541         combine_image->alpha_trait=BlendPixelTrait;
542       break;
543     }
544     case LinearGRAYColorspace:
545     case GRAYColorspace:
546     {
547       if (GetImageListLength(image) > 1)
548         combine_image->alpha_trait=BlendPixelTrait;
549       break;
550     }
551     case CMYKColorspace:
552     {
553       if (GetImageListLength(image) > 4)
554         combine_image->alpha_trait=BlendPixelTrait;
555       break;
556     }
557     default:
558       break;
559   }
560   /*
561     Combine images.
562   */
563   status=MagickTrue;
564   progress=0;
565   combine_view=AcquireAuthenticCacheView(combine_image,exception);
566   for (y=0; y < (ssize_t) combine_image->rows; y++)
567   {
568     CacheView
569       *image_view;
570
571     const Image
572       *next;
573
574     Quantum
575       *pixels;
576
577     register const Quantum
578       *magick_restrict p;
579
580     register Quantum
581       *magick_restrict q;
582
583     register ssize_t
584       i;
585
586     if (status == MagickFalse)
587       continue;
588     pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
589       1,exception);
590     if (pixels == (Quantum *) NULL)
591       {
592         status=MagickFalse;
593         continue;
594       }
595     next=image;
596     for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++)
597     {
598       register ssize_t
599         x;
600
601       PixelChannel channel = GetPixelChannelChannel(combine_image,i);
602       PixelTrait traits = GetPixelChannelTraits(combine_image,channel);
603       if (traits == UndefinedPixelTrait)
604         continue;
605       if (next == (Image *) NULL)
606         continue;
607       image_view=AcquireVirtualCacheView(next,exception);
608       p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
609       if (p == (const Quantum *) NULL)
610         continue;
611       q=pixels;
612       for (x=0; x < (ssize_t) combine_image->columns; x++)
613       {
614         if (x < (ssize_t) next->columns)
615           {
616             q[i]=GetPixelGray(next,p);
617             p+=GetPixelChannels(next);
618           }
619         q+=GetPixelChannels(combine_image);
620       }
621       image_view=DestroyCacheView(image_view);
622       next=GetNextImageInList(next);
623     }
624     if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
625       status=MagickFalse;
626     if (image->progress_monitor != (MagickProgressMonitor) NULL)
627       {
628         MagickBooleanType
629           proceed;
630
631         proceed=SetImageProgress(image,CombineImageTag,progress++,
632           combine_image->rows);
633         if (proceed == MagickFalse)
634           status=MagickFalse;
635       }
636   }
637   combine_view=DestroyCacheView(combine_view);
638   if (status == MagickFalse)
639     combine_image=DestroyImage(combine_image);
640   return(combine_image);
641 }
642 \f
643 /*
644 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
645 %                                                                             %
646 %                                                                             %
647 %                                                                             %
648 %   G e t I m a g e A l p h a C h a n n e l                                   %
649 %                                                                             %
650 %                                                                             %
651 %                                                                             %
652 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
653 %
654 %  GetImageAlphaChannel() returns MagickFalse if the image alpha channel is
655 %  not activated.  That is, the image is RGB rather than RGBA or CMYK rather
656 %  than CMYKA.
657 %
658 %  The format of the GetImageAlphaChannel method is:
659 %
660 %      MagickBooleanType GetImageAlphaChannel(const Image *image)
661 %
662 %  A description of each parameter follows:
663 %
664 %    o image: the image.
665 %
666 */
667 MagickExport MagickBooleanType GetImageAlphaChannel(const Image *image)
668 {
669   assert(image != (const Image *) NULL);
670   if (image->debug != MagickFalse)
671     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
672   assert(image->signature == MagickCoreSignature);
673   return(image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse);
674 }
675 \f
676 /*
677 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
678 %                                                                             %
679 %                                                                             %
680 %                                                                             %
681 %     S e p a r a t e I m a g e                                               %
682 %                                                                             %
683 %                                                                             %
684 %                                                                             %
685 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
686 %
687 %  SeparateImage() separates a channel from the image and returns it as a
688 %  grayscale image.
689 %
690 %  The format of the SeparateImage method is:
691 %
692 %      Image *SeparateImage(const Image *image,const ChannelType channel,
693 %        ExceptionInfo *exception)
694 %
695 %  A description of each parameter follows:
696 %
697 %    o image: the image.
698 %
699 %    o channel: the image channel.
700 %
701 %    o exception: return any errors or warnings in this structure.
702 %
703 */
704 MagickExport Image *SeparateImage(const Image *image,
705   const ChannelType channel_type,ExceptionInfo *exception)
706 {
707 #define GetChannelBit(mask,bit)  (((size_t) (mask) >> (size_t) (bit)) & 0x01)
708 #define SeparateImageTag  "Separate/Image"
709
710   CacheView
711     *image_view,
712     *separate_view;
713
714   Image
715     *separate_image;
716
717   MagickBooleanType
718     status;
719
720   MagickOffsetType
721     progress;
722
723   ssize_t
724     y;
725
726   /*
727     Initialize separate image attributes.
728   */
729   assert(image != (Image *) NULL);
730   assert(image->signature == MagickCoreSignature);
731   if (image->debug != MagickFalse)
732     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
733   assert(exception != (ExceptionInfo *) NULL);
734   assert(exception->signature == MagickCoreSignature);
735   separate_image=CloneImage(image,0,0,MagickTrue,exception);
736   if (separate_image == (Image *) NULL)
737     return((Image *) NULL);
738   if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
739     {
740       separate_image=DestroyImage(separate_image);
741       return((Image *) NULL);
742     }
743   separate_image->alpha_trait=UndefinedPixelTrait;
744   (void) SetImageColorspace(separate_image,GRAYColorspace,exception);
745   separate_image->gamma=image->gamma;
746   /*
747     Separate image.
748   */
749   status=MagickTrue;
750   progress=0;
751   image_view=AcquireVirtualCacheView(image,exception);
752   separate_view=AcquireAuthenticCacheView(separate_image,exception);
753 #if defined(MAGICKCORE_OPENMP_SUPPORT)
754   #pragma omp parallel for schedule(static) shared(progress,status) \
755     magick_number_threads(image,image,image->rows,1)
756 #endif
757   for (y=0; y < (ssize_t) image->rows; y++)
758   {
759     register const Quantum
760       *magick_restrict p;
761
762     register Quantum
763       *magick_restrict q;
764
765     register ssize_t
766       x;
767
768     if (status == MagickFalse)
769       continue;
770     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
771     q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
772       exception);
773     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
774       {
775         status=MagickFalse;
776         continue;
777       }
778     for (x=0; x < (ssize_t) image->columns; x++)
779     {
780       register ssize_t
781         i;
782
783       SetPixelChannel(separate_image,GrayPixelChannel,(Quantum) 0,q);
784       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
785       {
786         PixelChannel channel = GetPixelChannelChannel(image,i);
787         PixelTrait traits = GetPixelChannelTraits(image,channel);
788         if ((traits == UndefinedPixelTrait) ||
789             (GetChannelBit(channel_type,channel) == 0))
790           continue;
791         SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
792       }
793       p+=GetPixelChannels(image);
794       q+=GetPixelChannels(separate_image);
795     }
796     if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
797       status=MagickFalse;
798     if (image->progress_monitor != (MagickProgressMonitor) NULL)
799       {
800         MagickBooleanType
801           proceed;
802
803 #if defined(MAGICKCORE_OPENMP_SUPPORT)
804         #pragma omp critical (MagickCore_SeparateImage)
805 #endif
806         proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
807         if (proceed == MagickFalse)
808           status=MagickFalse;
809       }
810   }
811   separate_view=DestroyCacheView(separate_view);
812   image_view=DestroyCacheView(image_view);
813   (void) SetImageChannelMask(separate_image,DefaultChannels);
814   if (status == MagickFalse)
815     separate_image=DestroyImage(separate_image);
816   return(separate_image);
817 }
818 \f
819 /*
820 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
821 %                                                                             %
822 %                                                                             %
823 %                                                                             %
824 %     S e p a r a t e I m a g e s                                             %
825 %                                                                             %
826 %                                                                             %
827 %                                                                             %
828 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
829 %
830 %  SeparateImages() returns a separate grayscale image for each channel
831 %  specified.
832 %
833 %  The format of the SeparateImages method is:
834 %
835 %      Image *SeparateImages(const Image *image,ExceptionInfo *exception)
836 %
837 %  A description of each parameter follows:
838 %
839 %    o image: the image.
840 %
841 %    o exception: return any errors or warnings in this structure.
842 %
843 */
844 MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
845 {
846   Image
847     *images,
848     *separate_image;
849
850   register ssize_t
851     i;
852
853   assert(image != (Image *) NULL);
854   assert(image->signature == MagickCoreSignature);
855   if (image->debug != MagickFalse)
856     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
857   images=NewImageList();
858   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
859   {
860     PixelChannel channel = GetPixelChannelChannel(image,i);
861     PixelTrait traits = GetPixelChannelTraits(image,channel);
862     if ((traits == UndefinedPixelTrait) || ((traits & UpdatePixelTrait) == 0))
863       continue;
864     separate_image=SeparateImage(image,(ChannelType) (1UL << channel),
865       exception);
866     if (separate_image != (Image *) NULL)
867       AppendImageToList(&images,separate_image);
868   }
869   if (images == (Image *) NULL)
870     images=SeparateImage(image,UndefinedChannel,exception);
871   return(images);
872 }
873 \f
874 /*
875 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
876 %                                                                             %
877 %                                                                             %
878 %                                                                             %
879 %   S e t I m a g e A l p h a C h a n n e l                                   %
880 %                                                                             %
881 %                                                                             %
882 %                                                                             %
883 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
884 %
885 %  SetImageAlphaChannel() activates, deactivates, resets, or sets the alpha
886 %  channel.
887 %
888 %  The format of the SetImageAlphaChannel method is:
889 %
890 %      MagickBooleanType SetImageAlphaChannel(Image *image,
891 %        const AlphaChannelOption alpha_type,ExceptionInfo *exception)
892 %
893 %  A description of each parameter follows:
894 %
895 %    o image: the image.
896 %
897 %    o alpha_type:  The alpha channel type: ActivateAlphaChannel,
898 %      AssociateAlphaChannel, CopyAlphaChannel, DeactivateAlphaChannel,
899 %      DisassociateAlphaChannel,  ExtractAlphaChannel, OffAlphaChannel,
900 %      OnAlphaChannel, OpaqueAlphaChannel, SetAlphaChannel, ShapeAlphaChannel,
901 %      and TransparentAlphaChannel.
902 %
903 %    o exception: return any errors or warnings in this structure.
904 %
905 */
906
907 static inline void FlattenPixelInfo(const Image *image,const PixelInfo *p,
908   const double alpha,const Quantum *q,const double beta,
909   Quantum *composite)
910 {
911   double
912     Da,
913     gamma,
914     Sa;
915
916   register ssize_t
917     i;
918
919   /*
920     Compose pixel p over pixel q with the given alpha.
921   */
922   Sa=QuantumScale*alpha;
923   Da=QuantumScale*beta,
924   gamma=Sa*(-Da)+Sa+Da;
925   gamma=PerceptibleReciprocal(gamma);
926   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
927   {
928     PixelChannel channel = GetPixelChannelChannel(image,i);
929     PixelTrait traits = GetPixelChannelTraits(image,channel);
930     if (traits == UndefinedPixelTrait)
931       continue;
932     switch (channel)
933     {
934       case RedPixelChannel:
935       {
936         composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
937           (double) p->red,alpha));
938         break;
939       }
940       case GreenPixelChannel:
941       {
942         composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
943           (double) p->green,alpha));
944         break;
945       }
946       case BluePixelChannel:
947       {
948         composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
949           (double) p->blue,alpha));
950         break;
951       }
952       case BlackPixelChannel:
953       {
954         composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
955           (double) p->black,alpha));
956         break;
957       }
958       case AlphaPixelChannel:
959       {
960         composite[i]=ClampToQuantum(QuantumRange*(Sa*(-Da)+Sa+Da));
961         break;
962       }
963       default:
964         break;
965     }
966   }
967 }
968
969 MagickExport MagickBooleanType SetImageAlphaChannel(Image *image,
970   const AlphaChannelOption alpha_type,ExceptionInfo *exception)
971 {
972   CacheView
973     *image_view;
974
975   MagickBooleanType
976     status;
977
978   ssize_t
979     y;
980
981   assert(image != (Image *) NULL);
982   if (image->debug != MagickFalse)
983     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
984   assert(image->signature == MagickCoreSignature);
985   status=MagickTrue;
986   switch (alpha_type)
987   {
988     case ActivateAlphaChannel:
989     {
990       image->alpha_trait=BlendPixelTrait;
991       break;
992     }
993     case AssociateAlphaChannel:
994     {
995       /*
996         Associate alpha.
997       */
998       status=SetImageStorageClass(image,DirectClass,exception);
999       if (status == MagickFalse)
1000         break;
1001       image_view=AcquireAuthenticCacheView(image,exception);
1002 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1003       #pragma omp parallel for schedule(static) shared(status) \
1004         magick_number_threads(image,image,image->rows,1)
1005 #endif
1006       for (y=0; y < (ssize_t) image->rows; y++)
1007       {
1008         register Quantum
1009           *magick_restrict q;
1010
1011         register ssize_t
1012           x;
1013
1014         if (status == MagickFalse)
1015           continue;
1016         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1017           exception);
1018         if (q == (Quantum *) NULL)
1019           {
1020             status=MagickFalse;
1021             continue;
1022           }
1023         for (x=0; x < (ssize_t) image->columns; x++)
1024         {
1025           double
1026             gamma;
1027
1028           register ssize_t
1029             i;
1030
1031           gamma=QuantumScale*GetPixelAlpha(image,q);
1032           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1033           {
1034             PixelChannel channel = GetPixelChannelChannel(image,i);
1035             PixelTrait traits = GetPixelChannelTraits(image,channel);
1036             if (channel == AlphaPixelChannel)
1037               continue;
1038             if ((traits & UpdatePixelTrait) == 0)
1039               continue;
1040             q[i]=ClampToQuantum(gamma*q[i]);
1041           }
1042           q+=GetPixelChannels(image);
1043         }
1044         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1045           status=MagickFalse;
1046       }
1047       image_view=DestroyCacheView(image_view);
1048       image->alpha_trait=CopyPixelTrait;
1049       return(status);
1050     }
1051     case BackgroundAlphaChannel:
1052     {
1053       /*
1054         Set transparent pixels to background color.
1055       */
1056       if (image->alpha_trait == UndefinedPixelTrait)
1057         break;
1058       status=SetImageStorageClass(image,DirectClass,exception);
1059       if (status == MagickFalse)
1060         break;
1061       image_view=AcquireAuthenticCacheView(image,exception);
1062 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1063       #pragma omp parallel for schedule(static) shared(status) \
1064         magick_number_threads(image,image,image->rows,1)
1065 #endif
1066       for (y=0; y < (ssize_t) image->rows; y++)
1067       {
1068         register Quantum
1069           *magick_restrict q;
1070
1071         register ssize_t
1072           x;
1073
1074         if (status == MagickFalse)
1075           continue;
1076         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1077           exception);
1078         if (q == (Quantum *) NULL)
1079           {
1080             status=MagickFalse;
1081             continue;
1082           }
1083         for (x=0; x < (ssize_t) image->columns; x++)
1084         {
1085           if (GetPixelAlpha(image,q) == TransparentAlpha)
1086             {
1087               SetPixelViaPixelInfo(image,&image->background_color,q);
1088               SetPixelChannel(image,AlphaPixelChannel,TransparentAlpha,q);
1089             }
1090           q+=GetPixelChannels(image);
1091         }
1092         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1093           status=MagickFalse;
1094       }
1095       image_view=DestroyCacheView(image_view);
1096       return(status);
1097     }
1098     case CopyAlphaChannel:
1099     {
1100       image->alpha_trait=UpdatePixelTrait;
1101       status=CompositeImage(image,image,IntensityCompositeOp,MagickTrue,0,0,
1102         exception);
1103       break;
1104     }
1105     case DeactivateAlphaChannel:
1106     {
1107       if (image->alpha_trait == UndefinedPixelTrait)
1108         status=SetImageAlpha(image,OpaqueAlpha,exception);
1109       image->alpha_trait=CopyPixelTrait;
1110       break;
1111     }
1112     case DisassociateAlphaChannel:
1113     {
1114       /*
1115         Disassociate alpha.
1116       */
1117       status=SetImageStorageClass(image,DirectClass,exception);
1118       if (status == MagickFalse)
1119         break;
1120       image->alpha_trait=BlendPixelTrait;
1121       image_view=AcquireAuthenticCacheView(image,exception);
1122 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1123       #pragma omp parallel for schedule(static) shared(status) \
1124         magick_number_threads(image,image,image->rows,1)
1125 #endif
1126       for (y=0; y < (ssize_t) image->rows; y++)
1127       {
1128         register Quantum
1129           *magick_restrict q;
1130
1131         register ssize_t
1132           x;
1133
1134         if (status == MagickFalse)
1135           continue;
1136         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1137           exception);
1138         if (q == (Quantum *) NULL)
1139           {
1140             status=MagickFalse;
1141             continue;
1142           }
1143         for (x=0; x < (ssize_t) image->columns; x++)
1144         {
1145           double
1146             gamma,
1147             Sa;
1148
1149           register ssize_t
1150             i;
1151
1152           Sa=QuantumScale*GetPixelAlpha(image,q);
1153           gamma=PerceptibleReciprocal(Sa);
1154           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1155           {
1156             PixelChannel channel = GetPixelChannelChannel(image,i);
1157             PixelTrait traits = GetPixelChannelTraits(image,channel);
1158             if (channel == AlphaPixelChannel)
1159               continue;
1160             if ((traits & UpdatePixelTrait) == 0)
1161               continue;
1162             q[i]=ClampToQuantum(gamma*q[i]);
1163           }
1164           q+=GetPixelChannels(image);
1165         }
1166         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1167           status=MagickFalse;
1168       }
1169       image_view=DestroyCacheView(image_view);
1170       image->alpha_trait=UndefinedPixelTrait;
1171       return(status);
1172     }
1173     case DiscreteAlphaChannel:
1174     {
1175       if (image->alpha_trait == UndefinedPixelTrait)
1176         status=SetImageAlpha(image,OpaqueAlpha,exception);
1177       image->alpha_trait=UpdatePixelTrait;
1178       break;
1179     }
1180     case ExtractAlphaChannel:
1181     {
1182       status=CompositeImage(image,image,AlphaCompositeOp,MagickTrue,0,0,
1183         exception);
1184       image->alpha_trait=UndefinedPixelTrait;
1185       break;
1186     }
1187     case OffAlphaChannel:
1188     {
1189       image->alpha_trait=UndefinedPixelTrait;
1190       break;
1191     }
1192     case OnAlphaChannel:
1193     {
1194       if (image->alpha_trait == UndefinedPixelTrait)
1195         status=SetImageAlpha(image,OpaqueAlpha,exception);
1196       image->alpha_trait=BlendPixelTrait;
1197       break;
1198     }
1199     case OpaqueAlphaChannel:
1200     {
1201       status=SetImageAlpha(image,OpaqueAlpha,exception);
1202       break;
1203     }
1204     case RemoveAlphaChannel:
1205     {
1206       /*
1207         Remove transparency.
1208       */
1209       if (image->alpha_trait == UndefinedPixelTrait)
1210         break;
1211       status=SetImageStorageClass(image,DirectClass,exception);
1212       if (status == MagickFalse)
1213         break;
1214       image_view=AcquireAuthenticCacheView(image,exception);
1215 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1216       #pragma omp parallel for schedule(static) shared(status) \
1217         magick_number_threads(image,image,image->rows,1)
1218 #endif
1219       for (y=0; y < (ssize_t) image->rows; y++)
1220       {
1221         register Quantum
1222           *magick_restrict q;
1223
1224         register ssize_t
1225           x;
1226
1227         if (status == MagickFalse)
1228           continue;
1229         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1230           exception);
1231         if (q == (Quantum *) NULL)
1232           {
1233             status=MagickFalse;
1234             continue;
1235           }
1236         for (x=0; x < (ssize_t) image->columns; x++)
1237         {
1238           FlattenPixelInfo(image,&image->background_color,
1239             image->background_color.alpha,q,(double) GetPixelAlpha(image,q),q);
1240           q+=GetPixelChannels(image);
1241         }
1242         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1243           status=MagickFalse;
1244       }
1245       image_view=DestroyCacheView(image_view);
1246       image->alpha_trait=image->background_color.alpha_trait;
1247       break;
1248     }
1249     case SetAlphaChannel:
1250     {
1251       if (image->alpha_trait == UndefinedPixelTrait)
1252         status=SetImageAlpha(image,OpaqueAlpha,exception);
1253       break;
1254     }
1255     case ShapeAlphaChannel:
1256     {
1257       /*
1258         Set alpha channel by shape.
1259       */
1260       status=SetImageStorageClass(image,DirectClass,exception);
1261       if (status == MagickFalse)
1262         break;
1263       image->alpha_trait=UpdatePixelTrait;
1264       (void) SetImageMask(image,WritePixelMask,image,exception);
1265       (void) LevelImageColors(image,&image->background_color,
1266         &image->background_color,MagickTrue,exception);
1267       (void) SetImageMask(image,WritePixelMask,(Image *) NULL,exception);
1268       break;
1269     }
1270     case TransparentAlphaChannel:
1271     {
1272       status=SetImageAlpha(image,TransparentAlpha,exception);
1273       break;
1274     }
1275     case UndefinedAlphaChannel:
1276       break;
1277   }
1278   if (status == MagickFalse)
1279     return(status);
1280   (void) SetPixelChannelMask(image,image->channel_mask);
1281   return(SyncImagePixelCache(image,exception));
1282 }