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