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