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