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