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