]> 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-2012 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/image.h"
45 #include "MagickCore/list.h"
46 #include "MagickCore/log.h"
47 #include "MagickCore/monitor.h"
48 #include "MagickCore/monitor-private.h"
49 #include "MagickCore/option.h"
50 #include "MagickCore/pixel-accessor.h"
51 #include "MagickCore/string-private.h"
52 #include "MagickCore/token.h"
53 #include "MagickCore/utility.h"
54 #include "MagickCore/version.h"
55 \f
56 /*
57 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
58 %                                                                             %
59 %                                                                             %
60 %                                                                             %
61 %     C h a n n e l F x I m a g e                                             %
62 %                                                                             %
63 %                                                                             %
64 %                                                                             %
65 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
66 %
67 %  ChannelFxImage() applies a channel expression to the specified image.  The
68 %  expression consists of one or more channels, either mnemonic or numeric (e.g.
69 %  red, 1), separated by actions as follows:
70 %
71 %    <=>     exchange two channels (e.g. red<=>blue)
72 %    =>      copy one channel to another channel (e.g. red=>green)
73 %    =       assign a constant value to a channel (e.g. red=50%)
74 %    ,       write new image channels in the specified order (e.g. red, green)
75 %    |       add a new output image for the next set of channel operations
76 %    ;       move to the next input image for the source of channel data
77 %
78 %  For example, to create 3 grayscale images from the red, green, and blue
79 %  channels of an image, use:
80 %
81 %    -channel-fx "red; green; blue"
82 %
83 %  A channel without an operation symbol implies separate (i.e, semicolon).
84 %
85 %  The format of the ChannelFxImage method is:
86 %
87 %      Image *ChannelFxImage(const Image *image,const char *expression,
88 %        ExceptionInfo *exception)
89 %
90 %  A description of each parameter follows:
91 %
92 %    o image: the image.
93 %
94 %    o expression: A channel expression.
95 %
96 %    o exception: return any errors or warnings in this structure.
97 %
98 */
99
100 typedef enum
101 {
102   ExtractChannelOp,
103   AssignChannelOp,
104   ExchangeChannelOp,
105   TransferChannelOp
106 } ChannelFx;
107
108 static inline size_t MagickMin(const size_t x,const size_t y)
109 {
110   if (x < y)
111     return(x);
112   return(y);
113 }
114
115 static MagickBooleanType ChannelImage(Image *destination_image,
116   const PixelChannel destination_channel,const ChannelFx channel_op,
117   const Image *source_image,const PixelChannel source_channel,
118   const Quantum pixel,ExceptionInfo *exception)
119 {
120   CacheView
121     *source_view,
122     *destination_view;
123
124   MagickBooleanType
125     status;
126
127   size_t
128     height;
129
130   ssize_t
131     y;
132
133   status=MagickTrue;
134   source_view=AcquireCacheView(source_image);
135   destination_view=AcquireCacheView(destination_image);
136   height=MagickMin(source_image->rows,destination_image->rows);
137 #if defined(MAGICKCORE_OPENMP_SUPPORT)
138   #pragma omp parallel for schedule(static) shared(status)
139 #endif
140   for (y=0; y < (ssize_t) height; y++)
141   {
142     PixelTrait
143       destination_traits,
144       source_traits;
145
146     register const Quantum
147       *restrict p;
148
149     register Quantum
150       *restrict q;
151
152     register ssize_t
153       x;
154
155     size_t
156       width;
157
158     if (status == MagickFalse)
159       continue;
160     p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
161       exception);
162     q=GetCacheViewAuthenticPixels(destination_view,0,y,
163       destination_image->columns,1,exception);
164     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
165       {
166         status=MagickFalse;
167         continue;
168       }
169     destination_traits=GetPixelChannelMapTraits(destination_image,
170       destination_channel);
171     source_traits=GetPixelChannelMapTraits(source_image,source_channel);
172     if ((destination_traits == UndefinedPixelTrait) ||
173         (source_traits == UndefinedPixelTrait))
174       continue;
175     width=MagickMin(source_image->columns,destination_image->columns);
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   char
203     token[MaxTextExtent];
204
205   const char
206     *p;
207
208   const Image
209     *source_image;
210
211   double
212     pixel;
213
214   Image
215     *destination_image;
216
217   MagickBooleanType
218     status;
219
220   PixelChannel
221     source_channel,
222     destination_channel;
223
224   ssize_t
225     channels;
226
227   assert(image != (Image *) NULL);
228   assert(image->signature == MagickSignature);
229   if (image->debug != MagickFalse)
230     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
231   assert(exception != (ExceptionInfo *) NULL);
232   assert(exception->signature == MagickSignature);
233   source_image=image;
234   destination_image=CloneImage(source_image,0,0,MagickTrue,exception);
235   if (destination_image == (Image *) NULL)
236     return((Image *) NULL);
237   if (destination_image->colorspace == GRAYColorspace)
238     destination_image->colorspace=RGBColorspace;
239   if (expression == (const char *) NULL)
240     return(destination_image);
241   destination_channel=RedPixelChannel;
242   pixel=0.0;
243   p=(char *) expression;
244   GetMagickToken(p,&p,token);
245   channel_op=ExtractChannelOp;
246   for (channels=0; *token != '\0'; )
247   {
248     ssize_t
249       i;
250
251     /*
252       Interpret channel expression.
253     */
254     if (*token == ',')
255       {
256         destination_channel=(PixelChannel) ((ssize_t) destination_channel+1);
257         GetMagickToken(p,&p,token);
258       }
259     if (*token == '|')
260       {
261         if (GetNextImageInList(source_image) != (Image *) NULL)
262           source_image=GetNextImageInList(source_image);
263         else
264           source_image=GetFirstImageInList(source_image);
265         GetMagickToken(p,&p,token);
266       }
267     if (*token == ';')
268       {
269         Image
270           *canvas;
271
272         if ((channel_op == ExtractChannelOp) && (channels == 1))
273           {
274             destination_image->colorspace=GRAYColorspace;
275             InitializePixelChannelMap(destination_image);
276           }
277         status=SetImageStorageClass(destination_image,DirectClass,exception);
278         if (status == MagickFalse)
279           {
280             destination_image=DestroyImageList(destination_image);
281             return(destination_image);
282           }
283         canvas=CloneImage(source_image,0,0,MagickTrue,exception);
284         if (canvas == (Image *) NULL)
285           {
286             destination_image=DestroyImageList(destination_image);
287             return(destination_image);
288           }
289         if (canvas->colorspace == GRAYColorspace)
290           canvas->colorspace=RGBColorspace;
291         AppendImageToList(&destination_image,canvas);
292         destination_image=GetLastImageInList(destination_image);
293         GetMagickToken(p,&p,token);
294         channels=0;
295         destination_channel=RedPixelChannel;
296       }
297     i=ParsePixelChannelOption(token);
298     if (i < 0)
299       {
300         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
301           "UnrecognizedChannelType","`%s'",token);
302         destination_image=DestroyImageList(destination_image);
303         return(destination_image);
304       }
305     source_channel=(PixelChannel) i;
306     channel_op=ExtractChannelOp;
307     GetMagickToken(p,&p,token);
308     if (*token == '<')
309       {
310         channel_op=ExchangeChannelOp;
311         GetMagickToken(p,&p,token);
312       }
313     if (*token == '=')
314       {
315         if (channel_op != ExchangeChannelOp)
316           channel_op=AssignChannelOp;
317         GetMagickToken(p,&p,token);
318       }
319     if (*token == '>')
320       {
321         if (channel_op != ExchangeChannelOp)
322           channel_op=TransferChannelOp;
323         GetMagickToken(p,&p,token);
324       }
325     switch (channel_op)
326     {
327       case AssignChannelOp:
328       {
329         pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
330         GetMagickToken(p,&p,token);
331         break;
332       }
333       case ExchangeChannelOp:
334       case TransferChannelOp:
335       {
336         i=ParsePixelChannelOption(token);
337         if (i < 0)
338           {
339             (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
340               "UnrecognizedChannelType","`%s'",token);
341             destination_image=DestroyImageList(destination_image);
342             return(destination_image);
343           }
344         destination_channel=(PixelChannel) i;
345         if ((LocaleCompare(token,"black") == 0) ||
346             (LocaleCompare(token,"c") == 0) ||
347             (LocaleCompare(token,"cyan") == 0) ||
348             (LocaleCompare(token,"k") == 0) ||
349             (LocaleCompare(token,"m") == 0) ||
350             (LocaleCompare(token,"magenta") == 0) ||
351             (LocaleCompare(token,"y") == 0) ||
352             (LocaleCompare(token,"yellow") == 0))
353           (void) SetImageColorspace(destination_image,CMYKColorspace,exception);
354         if ((LocaleCompare(token,"Cb") == 0) ||
355             (LocaleCompare(token,"Cr") == 0))
356           (void) SetImageColorspace(destination_image,YCbCrColorspace,
357             exception);
358         if (LocaleCompare(token,"alpha") == 0)
359           (void) SetImageAlpha(destination_image,OpaqueAlpha,exception);
360         if (i >= (ssize_t) GetPixelChannels(destination_image))
361           (void) SetPixelMetaChannels(destination_image,(size_t) (i-
362             GetPixelChannels(destination_image)+1),exception);
363         GetMagickToken(p,&p,token);
364         break;
365       }
366       case ExtractChannelOp:
367       {
368         destination_channel++;
369         break;
370       }
371     }
372     status=ChannelImage(destination_image,destination_channel,channel_op,
373       source_image,source_channel,ClampToQuantum(pixel),exception);
374     if (status == MagickFalse)
375       {
376         destination_image=DestroyImageList(destination_image);
377         break;
378       }
379     channels++;
380     if (channel_op == ExchangeChannelOp)
381       {
382         status=ChannelImage(destination_image,source_channel,channel_op,
383           source_image,destination_channel,ClampToQuantum(pixel),exception);
384         if (status == MagickFalse)
385           {
386             destination_image=DestroyImageList(destination_image);
387             break;
388           }
389         channels++;
390       }
391     status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
392       strlen(expression));
393     if (status == MagickFalse)
394       break;
395   }
396   if ((channel_op == ExtractChannelOp) && (channels == 1))
397     {
398       destination_image->colorspace=GRAYColorspace;
399       InitializePixelChannelMap(destination_image);
400     }
401   status=SetImageStorageClass(destination_image,DirectClass,exception);
402   if (status == MagickFalse)
403     {
404       destination_image=GetLastImageInList(destination_image);
405       return((Image *) NULL);
406     }
407   return(GetFirstImageInList(destination_image));
408 }
409 \f
410 /*
411 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
412 %                                                                             %
413 %                                                                             %
414 %                                                                             %
415 %     C o m b i n e I m a g e s                                               %
416 %                                                                             %
417 %                                                                             %
418 %                                                                             %
419 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
420 %
421 %  CombineImages() combines one or more images into a single image.  The
422 %  grayscale value of the pixels of each image in the sequence is assigned in
423 %  order to the specified channels of the combined image.   The typical
424 %  ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
425 %
426 %  The format of the CombineImages method is:
427 %
428 %      Image *CombineImages(const Image *image,ExceptionInfo *exception)
429 %
430 %  A description of each parameter follows:
431 %
432 %    o image: the image.
433 %
434 %    o exception: return any errors or warnings in this structure.
435 %
436 */
437 MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
438 {
439 #define CombineImageTag  "Combine/Image"
440
441   CacheView
442     *combine_view;
443
444   Image
445     *combine_image;
446
447   MagickBooleanType
448     status;
449
450   MagickOffsetType
451     progress;
452
453   ssize_t
454     y;
455
456   /*
457     Ensure the image are the same size.
458   */
459   assert(image != (const Image *) NULL);
460   assert(image->signature == MagickSignature);
461   if (image->debug != MagickFalse)
462     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
463   assert(exception != (ExceptionInfo *) NULL);
464   assert(exception->signature == MagickSignature);
465   combine_image=CloneImage(image,0,0,MagickTrue,exception);
466   if (combine_image == (Image *) NULL)
467     return((Image *) NULL);
468   if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
469     {
470       combine_image=DestroyImage(combine_image);
471       return((Image *) NULL);
472     }
473   if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
474     combine_image->matte=MagickTrue;
475   /*
476     Combine images.
477   */
478   status=MagickTrue;
479   progress=0;
480   combine_view=AcquireCacheView(combine_image);
481   for (y=0; y < (ssize_t) combine_image->rows; y++)
482   {
483     CacheView
484       *image_view;
485
486     const Image
487       *next;
488
489     Quantum
490       *pixels;
491
492     register const Quantum
493       *restrict p;
494
495     register Quantum
496       *restrict q;
497
498     register ssize_t
499       i;
500
501     if (status == MagickFalse)
502       continue;
503     pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
504       1,exception);
505     if (pixels == (Quantum *) NULL)
506       {
507         status=MagickFalse;
508         continue;
509       }
510     next=image;
511     for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
512     {
513       PixelChannel
514         channel;
515
516       PixelTrait
517         combine_traits,
518         traits;
519
520       register ssize_t
521         x;
522
523       if (next == (Image *) NULL)
524         continue;
525       channel=GetPixelChannelMapChannel(image,i);
526       traits=GetPixelChannelMapTraits(image,channel);
527       combine_traits=GetPixelChannelMapTraits(combine_image,channel);
528       if ((traits == UndefinedPixelTrait) ||
529           (combine_traits == UndefinedPixelTrait))
530         continue;
531       image_view=AcquireCacheView(next);
532       p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
533       if (p == (const Quantum *) NULL)
534         continue;
535       q=pixels;
536       for (x=0; x < (ssize_t) combine_image->columns; x++)
537       {
538         if (x < (ssize_t) image->columns)
539           {
540             q[i]=GetPixelGray(image,p);
541             p+=GetPixelChannels(image);
542           }
543         q+=GetPixelChannels(combine_image);
544       }
545       image_view=DestroyCacheView(image_view);
546       next=GetNextImageInList(next);
547       if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
548         status=MagickFalse;
549       if (image->progress_monitor != (MagickProgressMonitor) NULL)
550         {
551           MagickBooleanType
552             proceed;
553
554           proceed=SetImageProgress(image,CombineImageTag,progress++,
555             combine_image->rows);
556           if (proceed == MagickFalse)
557             status=MagickFalse;
558         }
559     }
560   }
561   combine_view=DestroyCacheView(combine_view);
562   if (status == MagickFalse)
563     combine_image=DestroyImage(combine_image);
564   return(combine_image);
565 }
566 \f
567 /*
568 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
569 %                                                                             %
570 %                                                                             %
571 %                                                                             %
572 %     S e p a r a t e I m a g e                                               %
573 %                                                                             %
574 %                                                                             %
575 %                                                                             %
576 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
577 %
578 %  SeparateImage() separates a channel from the image and returns it as a
579 %  grayscale image.
580 %
581 %  The format of the SeparateImage method is:
582 %
583 %      Image *SeparateImage(const Image *image,const ChannelType channel,
584 %        ExceptionInfo *exception)
585 %
586 %  A description of each parameter follows:
587 %
588 %    o image: the image.
589 %
590 %    o channel: the image channel.
591 %
592 %    o exception: return any errors or warnings in this structure.
593 %
594 */
595 MagickExport Image *SeparateImage(const Image *image,
596   const ChannelType channel_type,ExceptionInfo *exception)
597 {
598 #define GetChannelBit(mask,bit)  (((size_t) (mask) >> (size_t) (bit)) & 0x01)
599 #define SeparateImageTag  "Separate/Image"
600
601   CacheView
602     *image_view,
603     *separate_view;
604
605   Image
606     *separate_image;
607
608   MagickBooleanType
609     status;
610
611   MagickOffsetType
612     progress;
613
614   ssize_t
615     y;
616
617   /*
618     Initialize spread image attributes.
619   */
620   assert(image != (Image *) NULL);
621   assert(image->signature == MagickSignature);
622   if (image->debug != MagickFalse)
623     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
624   assert(exception != (ExceptionInfo *) NULL);
625   assert(exception->signature == MagickSignature);
626   separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
627     exception);
628   if (separate_image == (Image *) NULL)
629     return((Image *) NULL);
630   if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
631     {
632       separate_image=DestroyImage(separate_image);
633       return((Image *) NULL);
634     }
635   separate_image->colorspace=GRAYColorspace;
636   /*
637     Separate image.
638   */
639   status=MagickTrue;
640   progress=0;
641   image_view=AcquireCacheView(image);
642   separate_view=AcquireCacheView(separate_image);
643 #if defined(MAGICKCORE_OPENMP_SUPPORT)
644   #pragma omp parallel for schedule(static) shared(progress,status)
645 #endif
646   for (y=0; y < (ssize_t) image->rows; y++)
647   {
648     register const Quantum
649       *restrict p;
650
651     register Quantum
652       *restrict q;
653
654     register ssize_t
655       x;
656
657     if (status == MagickFalse)
658       continue;
659     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
660     q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
661       exception);
662     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
663       {
664         status=MagickFalse;
665         continue;
666       }
667     for (x=0; x < (ssize_t) image->columns; x++)
668     {
669       register ssize_t
670         i;
671
672       if (GetPixelMask(image,p) != 0)
673         {
674           p+=GetPixelChannels(image);
675           q+=GetPixelChannels(separate_image);
676           continue;
677         }
678       SetPixelChannel(separate_image,GrayPixelChannel,0,q);
679       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
680       {
681         PixelChannel
682           channel;
683
684         PixelTrait
685           traits;
686
687         channel=GetPixelChannelMapChannel(image,i);
688         traits=GetPixelChannelMapTraits(image,channel);
689         if ((traits == UndefinedPixelTrait) ||
690             (GetChannelBit(channel_type,channel) == 0))
691           continue;
692         SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
693       }
694       p+=GetPixelChannels(image);
695       q+=GetPixelChannels(separate_image);
696     }
697     if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
698       status=MagickFalse;
699     if (image->progress_monitor != (MagickProgressMonitor) NULL)
700       {
701         MagickBooleanType
702           proceed;
703
704 #if defined(MAGICKCORE_OPENMP_SUPPORT)
705   #pragma omp critical (MagickCore_SeparateImage)
706 #endif
707         proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
708         if (proceed == MagickFalse)
709           status=MagickFalse;
710       }
711   }
712   separate_view=DestroyCacheView(separate_view);
713   image_view=DestroyCacheView(image_view);
714   return(separate_image);
715 }
716 \f
717 /*
718 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
719 %                                                                             %
720 %                                                                             %
721 %                                                                             %
722 %     S e p a r a t e I m a g e s                                             %
723 %                                                                             %
724 %                                                                             %
725 %                                                                             %
726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727 %
728 %  SeparateImages() returns a separate grayscale image for each channel
729 %  specified.
730 %
731 %  The format of the SeparateImages method is:
732 %
733 %      Image *SeparateImages(const Image *image,ExceptionInfo *exception)
734 %
735 %  A description of each parameter follows:
736 %
737 %    o image: the image.
738 %
739 %    o exception: return any errors or warnings in this structure.
740 %
741 */
742 MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
743 {
744   Image
745     *images,
746     *separate_image;
747
748   register ssize_t
749     i;
750
751   assert(image != (Image *) NULL);
752   assert(image->signature == MagickSignature);
753   if (image->debug != MagickFalse)
754     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
755   images=NewImageList();
756   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
757   {
758     PixelChannel
759       channel;
760
761     PixelTrait
762       traits;
763
764     channel=GetPixelChannelMapChannel(image,i);
765     traits=GetPixelChannelMapTraits(image,channel);
766     if ((traits == UndefinedPixelTrait) ||
767         ((traits & UpdatePixelTrait) == 0))
768       continue;
769     separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
770     if (separate_image != (Image *) NULL)
771       AppendImageToList(&images,separate_image);
772   }
773   return(images);
774 }