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