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