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