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