]> granicus.if.org Git - imagemagick/blob - MagickCore/channel.c
Added missing calls to xmlFreeDoc to fix memory leak reported in #1766.
[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-2019 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   (void) 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         (void) 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         (void) 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         (void) 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     (void) GetNextToken(p,&p,MagickPathExtent,token);
324     if (*token == '<')
325       {
326         channel_op=ExchangeChannelOp;
327         (void) GetNextToken(p,&p,MagickPathExtent,token);
328       }
329     if (*token == '=')
330       {
331         if (channel_op != ExchangeChannelOp)
332           channel_op=AssignChannelOp;
333         (void) GetNextToken(p,&p,MagickPathExtent,token);
334       }
335     if (*token == '>')
336       {
337         if (channel_op != ExchangeChannelOp)
338           channel_op=TransferChannelOp;
339         (void) 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         (void) 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 #if defined(MAGICKCORE_OPENMP_SUPPORT)
632         #pragma omp atomic
633 #endif
634         progress++;
635         proceed=SetImageProgress(image,CombineImageTag,progress,
636           combine_image->rows);
637         if (proceed == MagickFalse)
638           status=MagickFalse;
639       }
640   }
641   combine_view=DestroyCacheView(combine_view);
642   if (status == MagickFalse)
643     combine_image=DestroyImage(combine_image);
644   return(combine_image);
645 }
646 \f
647 /*
648 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
649 %                                                                             %
650 %                                                                             %
651 %                                                                             %
652 %   G e t I m a g e A l p h a C h a n n e l                                   %
653 %                                                                             %
654 %                                                                             %
655 %                                                                             %
656 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
657 %
658 %  GetImageAlphaChannel() returns MagickFalse if the image alpha channel is
659 %  not activated.  That is, the image is RGB rather than RGBA or CMYK rather
660 %  than CMYKA.
661 %
662 %  The format of the GetImageAlphaChannel method is:
663 %
664 %      MagickBooleanType GetImageAlphaChannel(const Image *image)
665 %
666 %  A description of each parameter follows:
667 %
668 %    o image: the image.
669 %
670 */
671 MagickExport MagickBooleanType GetImageAlphaChannel(const Image *image)
672 {
673   assert(image != (const Image *) NULL);
674   if (image->debug != MagickFalse)
675     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
676   assert(image->signature == MagickCoreSignature);
677   return(image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse);
678 }
679 \f
680 /*
681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
682 %                                                                             %
683 %                                                                             %
684 %                                                                             %
685 %     S e p a r a t e I m a g e                                               %
686 %                                                                             %
687 %                                                                             %
688 %                                                                             %
689 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
690 %
691 %  SeparateImage() separates a channel from the image and returns it as a
692 %  grayscale image.
693 %
694 %  The format of the SeparateImage method is:
695 %
696 %      Image *SeparateImage(const Image *image,const ChannelType channel,
697 %        ExceptionInfo *exception)
698 %
699 %  A description of each parameter follows:
700 %
701 %    o image: the image.
702 %
703 %    o channel: the image channel.
704 %
705 %    o exception: return any errors or warnings in this structure.
706 %
707 */
708 MagickExport Image *SeparateImage(const Image *image,
709   const ChannelType channel_type,ExceptionInfo *exception)
710 {
711 #define GetChannelBit(mask,bit)  (((size_t) (mask) >> (size_t) (bit)) & 0x01)
712 #define SeparateImageTag  "Separate/Image"
713
714   CacheView
715     *image_view,
716     *separate_view;
717
718   Image
719     *separate_image;
720
721   MagickBooleanType
722     status;
723
724   MagickOffsetType
725     progress;
726
727   ssize_t
728     y;
729
730   /*
731     Initialize separate image attributes.
732   */
733   assert(image != (Image *) NULL);
734   assert(image->signature == MagickCoreSignature);
735   if (image->debug != MagickFalse)
736     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
737   assert(exception != (ExceptionInfo *) NULL);
738   assert(exception->signature == MagickCoreSignature);
739   separate_image=CloneImage(image,0,0,MagickTrue,exception);
740   if (separate_image == (Image *) NULL)
741     return((Image *) NULL);
742   if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
743     {
744       separate_image=DestroyImage(separate_image);
745       return((Image *) NULL);
746     }
747   separate_image->alpha_trait=UndefinedPixelTrait;
748   (void) SetImageColorspace(separate_image,GRAYColorspace,exception);
749   separate_image->gamma=image->gamma;
750   /*
751     Separate image.
752   */
753   status=MagickTrue;
754   progress=0;
755   image_view=AcquireVirtualCacheView(image,exception);
756   separate_view=AcquireAuthenticCacheView(separate_image,exception);
757 #if defined(MAGICKCORE_OPENMP_SUPPORT)
758   #pragma omp parallel for schedule(static) shared(progress,status) \
759     magick_number_threads(image,image,image->rows,1)
760 #endif
761   for (y=0; y < (ssize_t) image->rows; y++)
762   {
763     register const Quantum
764       *magick_restrict p;
765
766     register Quantum
767       *magick_restrict q;
768
769     register ssize_t
770       x;
771
772     if (status == MagickFalse)
773       continue;
774     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
775     q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
776       exception);
777     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
778       {
779         status=MagickFalse;
780         continue;
781       }
782     for (x=0; x < (ssize_t) image->columns; x++)
783     {
784       register ssize_t
785         i;
786
787       SetPixelChannel(separate_image,GrayPixelChannel,(Quantum) 0,q);
788       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
789       {
790         PixelChannel channel = GetPixelChannelChannel(image,i);
791         PixelTrait traits = GetPixelChannelTraits(image,channel);
792         if ((traits == UndefinedPixelTrait) ||
793             (GetChannelBit(channel_type,channel) == 0))
794           continue;
795         SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
796       }
797       p+=GetPixelChannels(image);
798       q+=GetPixelChannels(separate_image);
799     }
800     if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
801       status=MagickFalse;
802     if (image->progress_monitor != (MagickProgressMonitor) NULL)
803       {
804         MagickBooleanType
805           proceed;
806
807 #if defined(MAGICKCORE_OPENMP_SUPPORT)
808         #pragma omp atomic
809 #endif
810         progress++;
811         proceed=SetImageProgress(image,SeparateImageTag,progress,image->rows);
812         if (proceed == MagickFalse)
813           status=MagickFalse;
814       }
815   }
816   separate_view=DestroyCacheView(separate_view);
817   image_view=DestroyCacheView(image_view);
818   (void) SetImageChannelMask(separate_image,DefaultChannels);
819   if (status == MagickFalse)
820     separate_image=DestroyImage(separate_image);
821   return(separate_image);
822 }
823 \f
824 /*
825 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
826 %                                                                             %
827 %                                                                             %
828 %                                                                             %
829 %     S e p a r a t e I m a g e s                                             %
830 %                                                                             %
831 %                                                                             %
832 %                                                                             %
833 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
834 %
835 %  SeparateImages() returns a separate grayscale image for each channel
836 %  specified.
837 %
838 %  The format of the SeparateImages method is:
839 %
840 %      Image *SeparateImages(const Image *image,ExceptionInfo *exception)
841 %
842 %  A description of each parameter follows:
843 %
844 %    o image: the image.
845 %
846 %    o exception: return any errors or warnings in this structure.
847 %
848 */
849 MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
850 {
851   Image
852     *images,
853     *separate_image;
854
855   register ssize_t
856     i;
857
858   assert(image != (Image *) NULL);
859   assert(image->signature == MagickCoreSignature);
860   if (image->debug != MagickFalse)
861     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
862   images=NewImageList();
863   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
864   {
865     PixelChannel channel = GetPixelChannelChannel(image,i);
866     PixelTrait traits = GetPixelChannelTraits(image,channel);
867     if ((traits == UndefinedPixelTrait) || ((traits & UpdatePixelTrait) == 0))
868       continue;
869     separate_image=SeparateImage(image,(ChannelType) (1UL << channel),
870       exception);
871     if (separate_image != (Image *) NULL)
872       AppendImageToList(&images,separate_image);
873   }
874   if (images == (Image *) NULL)
875     images=SeparateImage(image,UndefinedChannel,exception);
876   return(images);
877 }
878 \f
879 /*
880 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
881 %                                                                             %
882 %                                                                             %
883 %                                                                             %
884 %   S e t I m a g e A l p h a C h a n n e l                                   %
885 %                                                                             %
886 %                                                                             %
887 %                                                                             %
888 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
889 %
890 %  SetImageAlphaChannel() activates, deactivates, resets, or sets the alpha
891 %  channel.
892 %
893 %  The format of the SetImageAlphaChannel method is:
894 %
895 %      MagickBooleanType SetImageAlphaChannel(Image *image,
896 %        const AlphaChannelOption alpha_type,ExceptionInfo *exception)
897 %
898 %  A description of each parameter follows:
899 %
900 %    o image: the image.
901 %
902 %    o alpha_type:  The alpha channel type: ActivateAlphaChannel,
903 %      AssociateAlphaChannel, CopyAlphaChannel, DeactivateAlphaChannel,
904 %      DisassociateAlphaChannel,  ExtractAlphaChannel, OffAlphaChannel,
905 %      OnAlphaChannel, OpaqueAlphaChannel, SetAlphaChannel, ShapeAlphaChannel,
906 %      and TransparentAlphaChannel.
907 %
908 %    o exception: return any errors or warnings in this structure.
909 %
910 */
911
912 static inline void FlattenPixelInfo(const Image *image,const PixelInfo *p,
913   const double alpha,const Quantum *q,const double beta,
914   Quantum *composite)
915 {
916   double
917     Da,
918     gamma,
919     Sa;
920
921   register ssize_t
922     i;
923
924   /*
925     Compose pixel p over pixel q with the given alpha.
926   */
927   Sa=QuantumScale*alpha;
928   Da=QuantumScale*beta,
929   gamma=Sa*(-Da)+Sa+Da;
930   gamma=PerceptibleReciprocal(gamma);
931   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
932   {
933     PixelChannel channel = GetPixelChannelChannel(image,i);
934     PixelTrait traits = GetPixelChannelTraits(image,channel);
935     if (traits == UndefinedPixelTrait)
936       continue;
937     switch (channel)
938     {
939       case RedPixelChannel:
940       {
941         composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
942           (double) p->red,alpha));
943         break;
944       }
945       case GreenPixelChannel:
946       {
947         composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
948           (double) p->green,alpha));
949         break;
950       }
951       case BluePixelChannel:
952       {
953         composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
954           (double) p->blue,alpha));
955         break;
956       }
957       case BlackPixelChannel:
958       {
959         composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
960           (double) p->black,alpha));
961         break;
962       }
963       case AlphaPixelChannel:
964       {
965         composite[i]=ClampToQuantum(QuantumRange*(Sa*(-Da)+Sa+Da));
966         break;
967       }
968       default:
969         break;
970     }
971   }
972 }
973
974 MagickExport MagickBooleanType SetImageAlphaChannel(Image *image,
975   const AlphaChannelOption alpha_type,ExceptionInfo *exception)
976 {
977   CacheView
978     *image_view;
979
980   MagickBooleanType
981     status;
982
983   ssize_t
984     y;
985
986   assert(image != (Image *) NULL);
987   if (image->debug != MagickFalse)
988     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
989   assert(image->signature == MagickCoreSignature);
990   status=MagickTrue;
991   switch (alpha_type)
992   {
993     case ActivateAlphaChannel:
994     {
995       image->alpha_trait=BlendPixelTrait;
996       break;
997     }
998     case AssociateAlphaChannel:
999     {
1000       /*
1001         Associate alpha.
1002       */
1003       status=SetImageStorageClass(image,DirectClass,exception);
1004       if (status == MagickFalse)
1005         break;
1006       image_view=AcquireAuthenticCacheView(image,exception);
1007 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1008       #pragma omp parallel for schedule(static) shared(status) \
1009         magick_number_threads(image,image,image->rows,1)
1010 #endif
1011       for (y=0; y < (ssize_t) image->rows; y++)
1012       {
1013         register Quantum
1014           *magick_restrict q;
1015
1016         register ssize_t
1017           x;
1018
1019         if (status == MagickFalse)
1020           continue;
1021         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1022           exception);
1023         if (q == (Quantum *) NULL)
1024           {
1025             status=MagickFalse;
1026             continue;
1027           }
1028         for (x=0; x < (ssize_t) image->columns; x++)
1029         {
1030           double
1031             gamma;
1032
1033           register ssize_t
1034             i;
1035
1036           gamma=QuantumScale*GetPixelAlpha(image,q);
1037           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1038           {
1039             PixelChannel channel = GetPixelChannelChannel(image,i);
1040             PixelTrait traits = GetPixelChannelTraits(image,channel);
1041             if (channel == AlphaPixelChannel)
1042               continue;
1043             if ((traits & UpdatePixelTrait) == 0)
1044               continue;
1045             q[i]=ClampToQuantum(gamma*q[i]);
1046           }
1047           q+=GetPixelChannels(image);
1048         }
1049         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1050           status=MagickFalse;
1051       }
1052       image_view=DestroyCacheView(image_view);
1053       image->alpha_trait=CopyPixelTrait;
1054       return(status);
1055     }
1056     case BackgroundAlphaChannel:
1057     {
1058       /*
1059         Set transparent pixels to background color.
1060       */
1061       if (image->alpha_trait == UndefinedPixelTrait)
1062         break;
1063       status=SetImageStorageClass(image,DirectClass,exception);
1064       if (status == MagickFalse)
1065         break;
1066       image_view=AcquireAuthenticCacheView(image,exception);
1067 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1068       #pragma omp parallel for schedule(static) shared(status) \
1069         magick_number_threads(image,image,image->rows,1)
1070 #endif
1071       for (y=0; y < (ssize_t) image->rows; y++)
1072       {
1073         register Quantum
1074           *magick_restrict q;
1075
1076         register ssize_t
1077           x;
1078
1079         if (status == MagickFalse)
1080           continue;
1081         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1082           exception);
1083         if (q == (Quantum *) NULL)
1084           {
1085             status=MagickFalse;
1086             continue;
1087           }
1088         for (x=0; x < (ssize_t) image->columns; x++)
1089         {
1090           if (GetPixelAlpha(image,q) == TransparentAlpha)
1091             {
1092               SetPixelViaPixelInfo(image,&image->background_color,q);
1093               SetPixelChannel(image,AlphaPixelChannel,TransparentAlpha,q);
1094             }
1095           q+=GetPixelChannels(image);
1096         }
1097         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1098           status=MagickFalse;
1099       }
1100       image_view=DestroyCacheView(image_view);
1101       return(status);
1102     }
1103     case CopyAlphaChannel:
1104     {
1105       image->alpha_trait=UpdatePixelTrait;
1106       status=CompositeImage(image,image,IntensityCompositeOp,MagickTrue,0,0,
1107         exception);
1108       break;
1109     }
1110     case DeactivateAlphaChannel:
1111     {
1112       if (image->alpha_trait == UndefinedPixelTrait)
1113         status=SetImageAlpha(image,OpaqueAlpha,exception);
1114       image->alpha_trait=CopyPixelTrait;
1115       break;
1116     }
1117     case DisassociateAlphaChannel:
1118     {
1119       /*
1120         Disassociate alpha.
1121       */
1122       status=SetImageStorageClass(image,DirectClass,exception);
1123       if (status == MagickFalse)
1124         break;
1125       image->alpha_trait=BlendPixelTrait;
1126       image_view=AcquireAuthenticCacheView(image,exception);
1127 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1128       #pragma omp parallel for schedule(static) shared(status) \
1129         magick_number_threads(image,image,image->rows,1)
1130 #endif
1131       for (y=0; y < (ssize_t) image->rows; y++)
1132       {
1133         register Quantum
1134           *magick_restrict q;
1135
1136         register ssize_t
1137           x;
1138
1139         if (status == MagickFalse)
1140           continue;
1141         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1142           exception);
1143         if (q == (Quantum *) NULL)
1144           {
1145             status=MagickFalse;
1146             continue;
1147           }
1148         for (x=0; x < (ssize_t) image->columns; x++)
1149         {
1150           double
1151             gamma,
1152             Sa;
1153
1154           register ssize_t
1155             i;
1156
1157           Sa=QuantumScale*GetPixelAlpha(image,q);
1158           gamma=PerceptibleReciprocal(Sa);
1159           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1160           {
1161             PixelChannel channel = GetPixelChannelChannel(image,i);
1162             PixelTrait traits = GetPixelChannelTraits(image,channel);
1163             if (channel == AlphaPixelChannel)
1164               continue;
1165             if ((traits & UpdatePixelTrait) == 0)
1166               continue;
1167             q[i]=ClampToQuantum(gamma*q[i]);
1168           }
1169           q+=GetPixelChannels(image);
1170         }
1171         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1172           status=MagickFalse;
1173       }
1174       image_view=DestroyCacheView(image_view);
1175       image->alpha_trait=UndefinedPixelTrait;
1176       return(status);
1177     }
1178     case DiscreteAlphaChannel:
1179     {
1180       if (image->alpha_trait == UndefinedPixelTrait)
1181         status=SetImageAlpha(image,OpaqueAlpha,exception);
1182       image->alpha_trait=UpdatePixelTrait;
1183       break;
1184     }
1185     case ExtractAlphaChannel:
1186     {
1187       status=CompositeImage(image,image,AlphaCompositeOp,MagickTrue,0,0,
1188         exception);
1189       image->alpha_trait=UndefinedPixelTrait;
1190       break;
1191     }
1192     case OffAlphaChannel:
1193     {
1194       image->alpha_trait=UndefinedPixelTrait;
1195       break;
1196     }
1197     case OnAlphaChannel:
1198     {
1199       if (image->alpha_trait == UndefinedPixelTrait)
1200         status=SetImageAlpha(image,OpaqueAlpha,exception);
1201       image->alpha_trait=BlendPixelTrait;
1202       break;
1203     }
1204     case OpaqueAlphaChannel:
1205     {
1206       status=SetImageAlpha(image,OpaqueAlpha,exception);
1207       break;
1208     }
1209     case RemoveAlphaChannel:
1210     {
1211       /*
1212         Remove transparency.
1213       */
1214       if (image->alpha_trait == UndefinedPixelTrait)
1215         break;
1216       status=SetImageStorageClass(image,DirectClass,exception);
1217       if (status == MagickFalse)
1218         break;
1219       image_view=AcquireAuthenticCacheView(image,exception);
1220 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1221       #pragma omp parallel for schedule(static) shared(status) \
1222         magick_number_threads(image,image,image->rows,1)
1223 #endif
1224       for (y=0; y < (ssize_t) image->rows; y++)
1225       {
1226         register Quantum
1227           *magick_restrict q;
1228
1229         register ssize_t
1230           x;
1231
1232         if (status == MagickFalse)
1233           continue;
1234         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1235           exception);
1236         if (q == (Quantum *) NULL)
1237           {
1238             status=MagickFalse;
1239             continue;
1240           }
1241         for (x=0; x < (ssize_t) image->columns; x++)
1242         {
1243           FlattenPixelInfo(image,&image->background_color,
1244             image->background_color.alpha,q,(double) GetPixelAlpha(image,q),q);
1245           q+=GetPixelChannels(image);
1246         }
1247         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1248           status=MagickFalse;
1249       }
1250       image_view=DestroyCacheView(image_view);
1251       image->alpha_trait=image->background_color.alpha_trait;
1252       break;
1253     }
1254     case SetAlphaChannel:
1255     {
1256       if (image->alpha_trait == UndefinedPixelTrait)
1257         status=SetImageAlpha(image,OpaqueAlpha,exception);
1258       break;
1259     }
1260     case ShapeAlphaChannel:
1261     {
1262       PixelInfo
1263         background;
1264
1265       /*
1266         Remove transparency.
1267       */
1268       ConformPixelInfo(image,&image->background_color,&background,exception);
1269       background.alpha_trait=BlendPixelTrait;
1270       image->alpha_trait=BlendPixelTrait;
1271       status=SetImageStorageClass(image,DirectClass,exception);
1272       if (status == MagickFalse)
1273         break;
1274       image_view=AcquireAuthenticCacheView(image,exception);
1275 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1276       #pragma omp parallel for schedule(static) shared(status) \
1277         magick_number_threads(image,image,image->rows,1)
1278 #endif
1279       for (y=0; y < (ssize_t) image->rows; y++)
1280       {
1281         PixelInfo
1282           pixel;
1283
1284         register Quantum
1285           *magick_restrict q;
1286
1287         register ssize_t
1288           x;
1289
1290         if (status == MagickFalse)
1291           continue;
1292         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1293           exception);
1294         if (q == (Quantum *) NULL)
1295           {
1296             status=MagickFalse;
1297             continue;
1298           }
1299         pixel=background;
1300         for (x=0; x < (ssize_t) image->columns; x++)
1301         {
1302           pixel.alpha=GetPixelIntensity(image,q);
1303           SetPixelViaPixelInfo(image,&pixel,q);
1304           q+=GetPixelChannels(image);
1305         }
1306         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1307           status=MagickFalse;
1308       }
1309       image_view=DestroyCacheView(image_view);
1310       break;
1311     }
1312     case TransparentAlphaChannel:
1313     {
1314       status=SetImageAlpha(image,TransparentAlpha,exception);
1315       break;
1316     }
1317     case UndefinedAlphaChannel:
1318       break;
1319   }
1320   if (status == MagickFalse)
1321     return(status);
1322   (void) SetPixelChannelMask(image,image->channel_mask);
1323   return(SyncImagePixelCache(image,exception));
1324 }