]> 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         if (LocaleCompare(token,"black") == 0)
337           SetImageColorspace(destination_image,CMYKColorspace,exception);
338         i=ParsePixelChannelOption(token);
339         if (i < 0)
340           {
341             (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
342               "UnrecognizedChannelType","`%s'",token);
343             destination_image=DestroyImageList(destination_image);
344             return(destination_image);
345           }
346         destination_channel=(PixelChannel) i;
347         GetMagickToken(p,&p,token);
348         break;
349       }
350       case ExtractChannelOp:
351       {
352         destination_channel++;
353         break;
354       }
355     }
356     status=ChannelImage(destination_image,destination_channel,channel_op,
357       source_image,source_channel,ClampToQuantum(pixel),exception);
358     if (status == MagickFalse)
359       {
360         destination_image=DestroyImageList(destination_image);
361         break;
362       }
363     channels++;
364     if (channel_op == ExchangeChannelOp)
365       {
366         status=ChannelImage(destination_image,source_channel,channel_op,
367           source_image,destination_channel,ClampToQuantum(pixel),exception);
368         if (status == MagickFalse)
369           {
370             destination_image=DestroyImageList(destination_image);
371             break;
372           }
373         channels++;
374       }
375     status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
376       strlen(expression));
377     if (status == MagickFalse)
378       break;
379   }
380   if ((channel_op == ExtractChannelOp) && (channels == 1))
381     {
382       destination_image->colorspace=GRAYColorspace;
383       InitializePixelChannelMap(destination_image);
384     }
385   status=SetImageStorageClass(destination_image,DirectClass,exception);
386   if (status == MagickFalse)
387     {
388       destination_image=GetLastImageInList(destination_image);
389       return((Image *) NULL);
390     }
391   return(GetFirstImageInList(destination_image));
392 }
393 \f
394 /*
395 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
396 %                                                                             %
397 %                                                                             %
398 %                                                                             %
399 %     C o m b i n e I m a g e s                                               %
400 %                                                                             %
401 %                                                                             %
402 %                                                                             %
403 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
404 %
405 %  CombineImages() combines one or more images into a single image.  The
406 %  grayscale value of the pixels of each image in the sequence is assigned in
407 %  order to the specified channels of the combined image.   The typical
408 %  ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
409 %
410 %  The format of the CombineImages method is:
411 %
412 %      Image *CombineImages(const Image *image,ExceptionInfo *exception)
413 %
414 %  A description of each parameter follows:
415 %
416 %    o image: the image.
417 %
418 %    o exception: return any errors or warnings in this structure.
419 %
420 */
421 MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
422 {
423 #define CombineImageTag  "Combine/Image"
424
425   CacheView
426     *combine_view;
427
428   Image
429     *combine_image;
430
431   MagickBooleanType
432     status;
433
434   MagickOffsetType
435     progress;
436
437   ssize_t
438     y;
439
440   /*
441     Ensure the image are the same size.
442   */
443   assert(image != (const Image *) NULL);
444   assert(image->signature == MagickSignature);
445   if (image->debug != MagickFalse)
446     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
447   assert(exception != (ExceptionInfo *) NULL);
448   assert(exception->signature == MagickSignature);
449   combine_image=CloneImage(image,0,0,MagickTrue,exception);
450   if (combine_image == (Image *) NULL)
451     return((Image *) NULL);
452   if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
453     {
454       combine_image=DestroyImage(combine_image);
455       return((Image *) NULL);
456     }
457   if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
458     combine_image->matte=MagickTrue;
459   /*
460     Combine images.
461   */
462   status=MagickTrue;
463   progress=0;
464   combine_view=AcquireCacheView(combine_image);
465   for (y=0; y < (ssize_t) combine_image->rows; y++)
466   {
467     CacheView
468       *image_view;
469
470     const Image
471       *next;
472
473     Quantum
474       *pixels;
475
476     register const Quantum
477       *restrict p;
478
479     register Quantum
480       *restrict q;
481
482     register ssize_t
483       i;
484
485     if (status == MagickFalse)
486       continue;
487     pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
488       1,exception);
489     if (pixels == (Quantum *) NULL)
490       {
491         status=MagickFalse;
492         continue;
493       }
494     next=image;
495     for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
496     {
497       PixelChannel
498         channel;
499
500       PixelTrait
501         combine_traits,
502         traits;
503
504       register ssize_t
505         x;
506
507       if (next == (Image *) NULL)
508         continue;
509       channel=GetPixelChannelMapChannel(image,i);
510       traits=GetPixelChannelMapTraits(image,channel);
511       combine_traits=GetPixelChannelMapTraits(combine_image,channel);
512       if ((traits == UndefinedPixelTrait) ||
513           (combine_traits == UndefinedPixelTrait))
514         continue;
515       image_view=AcquireCacheView(next);
516       p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
517       if (p == (const Quantum *) NULL)
518         continue;
519       q=pixels;
520       for (x=0; x < (ssize_t) combine_image->columns; x++)
521       {
522         if (x < (ssize_t) image->columns)
523           {
524             q[i]=GetPixelGray(image,p);
525             p+=GetPixelChannels(image);
526           }
527         q+=GetPixelChannels(combine_image);
528       }
529       image_view=DestroyCacheView(image_view);
530       next=GetNextImageInList(next);
531       if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
532         status=MagickFalse;
533       if (image->progress_monitor != (MagickProgressMonitor) NULL)
534         {
535           MagickBooleanType
536             proceed;
537
538           proceed=SetImageProgress(image,CombineImageTag,progress++,
539             combine_image->rows);
540           if (proceed == MagickFalse)
541             status=MagickFalse;
542         }
543     }
544   }
545   combine_view=DestroyCacheView(combine_view);
546   if (status == MagickFalse)
547     combine_image=DestroyImage(combine_image);
548   return(combine_image);
549 }
550 \f
551 /*
552 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
553 %                                                                             %
554 %                                                                             %
555 %                                                                             %
556 %     S e p a r a t e I m a g e                                               %
557 %                                                                             %
558 %                                                                             %
559 %                                                                             %
560 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
561 %
562 %  SeparateImage() separates a channel from the image and returns it as a
563 %  grayscale image.
564 %
565 %  The format of the SeparateImage method is:
566 %
567 %      Image *SeparateImage(const Image *image,const ChannelType channel,
568 %        ExceptionInfo *exception)
569 %
570 %  A description of each parameter follows:
571 %
572 %    o image: the image.
573 %
574 %    o channel: the image channel.
575 %
576 %    o exception: return any errors or warnings in this structure.
577 %
578 */
579 MagickExport Image *SeparateImage(const Image *image,
580   const ChannelType channel_type,ExceptionInfo *exception)
581 {
582 #define GetChannelBit(mask,bit)  (((size_t) (mask) >> (size_t) (bit)) & 0x01)
583 #define SeparateImageTag  "Separate/Image"
584
585   CacheView
586     *image_view,
587     *separate_view;
588
589   Image
590     *separate_image;
591
592   MagickBooleanType
593     status;
594
595   MagickOffsetType
596     progress;
597
598   ssize_t
599     y;
600
601   /*
602     Initialize spread image attributes.
603   */
604   assert(image != (Image *) NULL);
605   assert(image->signature == MagickSignature);
606   if (image->debug != MagickFalse)
607     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
608   assert(exception != (ExceptionInfo *) NULL);
609   assert(exception->signature == MagickSignature);
610   separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
611     exception);
612   if (separate_image == (Image *) NULL)
613     return((Image *) NULL);
614   if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
615     {
616       separate_image=DestroyImage(separate_image);
617       return((Image *) NULL);
618     }
619   separate_image->colorspace=GRAYColorspace;
620   /*
621     Separate image.
622   */
623   status=MagickTrue;
624   progress=0;
625   image_view=AcquireCacheView(image);
626   separate_view=AcquireCacheView(separate_image);
627 #if defined(MAGICKCORE_OPENMP_SUPPORT)
628   #pragma omp parallel for schedule(static) shared(progress,status)
629 #endif
630   for (y=0; y < (ssize_t) image->rows; y++)
631   {
632     register const Quantum
633       *restrict p;
634
635     register Quantum
636       *restrict q;
637
638     register ssize_t
639       x;
640
641     if (status == MagickFalse)
642       continue;
643     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
644     q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
645       exception);
646     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
647       {
648         status=MagickFalse;
649         continue;
650       }
651     for (x=0; x < (ssize_t) image->columns; x++)
652     {
653       register ssize_t
654         i;
655
656       if (GetPixelMask(image,p) != 0)
657         {
658           p+=GetPixelChannels(image);
659           q+=GetPixelChannels(separate_image);
660           continue;
661         }
662       SetPixelChannel(separate_image,GrayPixelChannel,0,q);
663       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
664       {
665         PixelChannel
666           channel;
667
668         PixelTrait
669           traits;
670
671         channel=GetPixelChannelMapChannel(image,i);
672         traits=GetPixelChannelMapTraits(image,channel);
673         if ((traits == UndefinedPixelTrait) ||
674             (GetChannelBit(channel_type,channel) == 0))
675           continue;
676         SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
677       }
678       p+=GetPixelChannels(image);
679       q+=GetPixelChannels(separate_image);
680     }
681     if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
682       status=MagickFalse;
683     if (image->progress_monitor != (MagickProgressMonitor) NULL)
684       {
685         MagickBooleanType
686           proceed;
687
688 #if defined(MAGICKCORE_OPENMP_SUPPORT)
689   #pragma omp critical (MagickCore_SeparateImage)
690 #endif
691         proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
692         if (proceed == MagickFalse)
693           status=MagickFalse;
694       }
695   }
696   separate_view=DestroyCacheView(separate_view);
697   image_view=DestroyCacheView(image_view);
698   return(separate_image);
699 }
700 \f
701 /*
702 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
703 %                                                                             %
704 %                                                                             %
705 %                                                                             %
706 %     S e p a r a t e I m a g e s                                             %
707 %                                                                             %
708 %                                                                             %
709 %                                                                             %
710 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
711 %
712 %  SeparateImages() returns a separate grayscale image for each channel
713 %  specified.
714 %
715 %  The format of the SeparateImages method is:
716 %
717 %      MagickBooleanType SeparateImages(const Image *image,
718 %        ExceptionInfo *exception)
719 %
720 %  A description of each parameter follows:
721 %
722 %    o image: the image.
723 %
724 %    o exception: return any errors or warnings in this structure.
725 %
726 */
727 MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
728 {
729   Image
730     *images,
731     *separate_image;
732
733   register ssize_t
734     i;
735
736   assert(image != (Image *) NULL);
737   assert(image->signature == MagickSignature);
738   if (image->debug != MagickFalse)
739     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
740   images=NewImageList();
741   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
742   {
743     PixelChannel
744       channel;
745
746     PixelTrait
747       traits;
748
749     channel=GetPixelChannelMapChannel(image,i);
750     traits=GetPixelChannelMapTraits(image,channel);
751     if ((traits == UndefinedPixelTrait) ||
752         ((traits & UpdatePixelTrait) == 0))
753       continue;
754     separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
755     if (separate_image != (Image *) NULL)
756       AppendImageToList(&images,separate_image);
757   }
758   return(images);
759 }