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