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