]> 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     magick_threads(source_image,source_image,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 (expression == (const char *) NULL)
243     return(destination_image);
244   destination_channel=RedPixelChannel;
245   channel_mask=UndefinedChannel;
246   pixel=0.0;
247   p=(char *) expression;
248   GetMagickToken(p,&p,token);
249   channel_op=ExtractChannelOp;
250   for (channels=0; *token != '\0'; )
251   {
252     MagickBooleanType
253       delete_channel;
254
255     ssize_t
256       i;
257
258     /*
259       Interpret channel expression.
260     */
261     delete_channel=MagickFalse;
262     switch (*token)
263     {
264       case '~':
265       {
266         delete_channel=MagickTrue;
267         GetMagickToken(p,&p,token);
268         break;
269       }
270       case ',':
271       {
272         destination_channel=(PixelChannel) ((ssize_t) destination_channel+1);
273         GetMagickToken(p,&p,token);
274         break;
275       }
276       case '|':
277       {
278         if (GetNextImageInList(source_image) != (Image *) NULL)
279           source_image=GetNextImageInList(source_image);
280         else
281           source_image=GetFirstImageInList(source_image);
282         GetMagickToken(p,&p,token);
283         break;
284       }
285       case ';':
286       {
287         Image
288           *canvas;
289
290         SetPixelChannelMask(destination_image,channel_mask);
291         if ((channel_op == ExtractChannelOp) && (channels == 1))
292           (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
293         status=SetImageStorageClass(destination_image,DirectClass,exception);
294         if (status == MagickFalse)
295           {
296             destination_image=DestroyImageList(destination_image);
297             return(destination_image);
298           }
299         canvas=CloneImage(source_image,0,0,MagickTrue,exception);
300         if (canvas == (Image *) NULL)
301           {
302             destination_image=DestroyImageList(destination_image);
303             return(destination_image);
304           }
305         AppendImageToList(&destination_image,canvas);
306         destination_image=GetLastImageInList(destination_image);
307         GetMagickToken(p,&p,token);
308         channels=0;
309         destination_channel=RedPixelChannel;
310         channel_mask=UndefinedChannel;
311         break;
312       }
313       default:
314         break;
315     }
316     i=ParsePixelChannelOption(token);
317     if (i < 0)
318       {
319         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
320           "UnrecognizedChannelType","`%s'",token);
321         destination_image=DestroyImageList(destination_image);
322         return(destination_image);
323       }
324     source_channel=(PixelChannel) i;
325     channel_op=ExtractChannelOp;
326     GetMagickToken(p,&p,token);
327     if (*token == '<')
328       {
329         channel_op=ExchangeChannelOp;
330         GetMagickToken(p,&p,token);
331       }
332     if (*token == '=')
333       {
334         if (channel_op != ExchangeChannelOp)
335           channel_op=AssignChannelOp;
336         GetMagickToken(p,&p,token);
337       }
338     if (*token == '>')
339       {
340         if (channel_op != ExchangeChannelOp)
341           channel_op=TransferChannelOp;
342         GetMagickToken(p,&p,token);
343       }
344     switch (channel_op)
345     {
346       case AssignChannelOp:
347       {
348         pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
349         GetMagickToken(p,&p,token);
350         break;
351       }
352       case ExchangeChannelOp:
353       case TransferChannelOp:
354       {
355         i=ParsePixelChannelOption(token);
356         if (i < 0)
357           {
358             (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
359               "UnrecognizedChannelType","`%s'",token);
360             destination_image=DestroyImageList(destination_image);
361             return(destination_image);
362           }
363         destination_channel=(PixelChannel) i;
364         switch (destination_channel)
365         {
366           case RedPixelChannel:
367           case GreenPixelChannel:
368           case BluePixelChannel:
369           case BlackPixelChannel:
370           case IndexPixelChannel:
371             break;
372           case AlphaPixelChannel:
373           {
374             destination_image->alpha_trait=BlendPixelTrait;
375             break;
376           }
377           case ReadMaskPixelChannel:
378           {
379             destination_image->read_mask=MagickTrue;
380             break;
381           }
382           case WriteMaskPixelChannel:
383           {
384             destination_image->write_mask=MagickTrue;
385             break;
386           }
387           case MetaPixelChannel:
388           default:
389           {
390             (void) SetPixelMetaChannels(destination_image,(size_t) (i-
391               GetPixelChannels(destination_image)+1),exception);
392             break;
393           }
394         }
395         channel_mask=(ChannelType) (channel_mask | ParseChannelOption(token));
396         if (((channels >= 1)  || (destination_channel >= 1)) &&
397             (IsGrayColorspace(destination_image->colorspace) != MagickFalse))
398           (void) SetImageColorspace(destination_image,sRGBColorspace,exception);
399         GetMagickToken(p,&p,token);
400         break;
401       }
402       default:
403         break;
404     }
405     status=ChannelImage(destination_image,destination_channel,channel_op,
406       source_image,source_channel,ClampToQuantum(pixel),exception);
407     if (status == MagickFalse)
408       {
409         destination_image=DestroyImageList(destination_image);
410         break;
411       }
412     channels++;
413     if (channel_op == ExchangeChannelOp)
414       {
415         status=ChannelImage(destination_image,source_channel,channel_op,
416           source_image,destination_channel,ClampToQuantum(pixel),exception);
417         if (status == MagickFalse)
418           {
419             destination_image=DestroyImageList(destination_image);
420             break;
421           }
422         channels++;
423       }
424     switch (channel_op)
425     {
426       case ExtractChannelOp:
427       {
428         if (delete_channel == MagickFalse)
429           channel_mask=(ChannelType) (channel_mask |
430             (1 << destination_channel));
431         else
432           channel_mask=(ChannelType) (channel_mask &~
433             (1 << destination_channel));
434         destination_channel=(PixelChannel) (destination_channel+1);
435         break;
436       }
437       default:
438         break;
439     }
440     status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
441       strlen(expression));
442     if (status == MagickFalse)
443       break;
444   }
445   SetPixelChannelMask(destination_image,channel_mask);
446   if ((channel_op == ExtractChannelOp) && (channels == 1))
447     (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
448   status=SetImageStorageClass(destination_image,DirectClass,exception);
449   if (status == MagickFalse)
450     {
451       destination_image=GetLastImageInList(destination_image);
452       return((Image *) NULL);
453     }
454   return(GetFirstImageInList(destination_image));
455 }
456 \f
457 /*
458 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
459 %                                                                             %
460 %                                                                             %
461 %                                                                             %
462 %     C o m b i n e I m a g e s                                               %
463 %                                                                             %
464 %                                                                             %
465 %                                                                             %
466 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
467 %
468 %  CombineImages() combines one or more images into a single image.  The
469 %  grayscale value of the pixels of each image in the sequence is assigned in
470 %  order to the specified channels of the combined image.   The typical
471 %  ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
472 %
473 %  The format of the CombineImages method is:
474 %
475 %      Image *CombineImages(const Image *images,const ColorspaceType colorspace,
476 %        ExceptionInfo *exception)
477 %
478 %  A description of each parameter follows:
479 %
480 %    o images: the image sequence.
481 %
482 %    o colorspace: the image colorspace.
483 %
484 %    o exception: return any errors or warnings in this structure.
485 %
486 */
487 MagickExport Image *CombineImages(const Image *image,
488   const ColorspaceType colorspace,ExceptionInfo *exception)
489 {
490 #define CombineImageTag  "Combine/Image"
491
492   CacheView
493     *combine_view;
494
495   Image
496     *combine_image;
497
498   MagickBooleanType
499     status;
500
501   MagickOffsetType
502     progress;
503
504   ssize_t
505     y;
506
507   /*
508     Ensure the image are the same size.
509   */
510   assert(image != (const Image *) NULL);
511   assert(image->signature == MagickSignature);
512   if (image->debug != MagickFalse)
513     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
514   assert(exception != (ExceptionInfo *) NULL);
515   assert(exception->signature == MagickSignature);
516   combine_image=CloneImage(image,0,0,MagickTrue,exception);
517   if (combine_image == (Image *) NULL)
518     return((Image *) NULL);
519   if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
520     {
521       combine_image=DestroyImage(combine_image);
522       return((Image *) NULL);
523     }
524   (void) SetImageColorspace(combine_image,colorspace,exception);
525   if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
526     combine_image->alpha_trait=BlendPixelTrait;
527   /*
528     Combine images.
529   */
530   status=MagickTrue;
531   progress=0;
532   combine_view=AcquireAuthenticCacheView(combine_image,exception);
533   for (y=0; y < (ssize_t) combine_image->rows; y++)
534   {
535     CacheView
536       *image_view;
537
538     const Image
539       *next;
540
541     Quantum
542       *pixels;
543
544     register const Quantum
545       *restrict p;
546
547     register Quantum
548       *restrict q;
549
550     register ssize_t
551       i;
552
553     if (status == MagickFalse)
554       continue;
555     pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
556       1,exception);
557     if (pixels == (Quantum *) NULL)
558       {
559         status=MagickFalse;
560         continue;
561       }
562     next=image;
563     for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++)
564     {
565       register ssize_t
566         x;
567
568       PixelChannel channel=GetPixelChannelChannel(combine_image,i);
569       PixelTrait traits=GetPixelChannelTraits(combine_image,channel);
570       if (traits == UndefinedPixelTrait)
571         continue;
572       if (next == (Image *) NULL)
573         continue;
574       image_view=AcquireVirtualCacheView(next,exception);
575       p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
576       if (p == (const Quantum *) NULL)
577         continue;
578       q=pixels;
579       for (x=0; x < (ssize_t) combine_image->columns; x++)
580       {
581         if (x < (ssize_t) next->columns)
582           {
583             q[i]=GetPixelGray(next,p);
584             p+=GetPixelChannels(next);
585           }
586         q+=GetPixelChannels(combine_image);
587       }
588       image_view=DestroyCacheView(image_view);
589       next=GetNextImageInList(next);
590     }
591     if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
592       status=MagickFalse;
593     if (image->progress_monitor != (MagickProgressMonitor) NULL)
594       {
595         MagickBooleanType
596           proceed;
597
598         proceed=SetImageProgress(image,CombineImageTag,progress++,
599           combine_image->rows);
600         if (proceed == MagickFalse)
601           status=MagickFalse;
602       }
603   }
604   combine_view=DestroyCacheView(combine_view);
605   if (status == MagickFalse)
606     combine_image=DestroyImage(combine_image);
607   return(combine_image);
608 }
609 \f
610 /*
611 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
612 %                                                                             %
613 %                                                                             %
614 %                                                                             %
615 %     S e p a r a t e I m a g e                                               %
616 %                                                                             %
617 %                                                                             %
618 %                                                                             %
619 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
620 %
621 %  SeparateImage() separates a channel from the image and returns it as a
622 %  grayscale image.
623 %
624 %  The format of the SeparateImage method is:
625 %
626 %      Image *SeparateImage(const Image *image,const ChannelType channel,
627 %        ExceptionInfo *exception)
628 %
629 %  A description of each parameter follows:
630 %
631 %    o image: the image.
632 %
633 %    o channel: the image channel.
634 %
635 %    o exception: return any errors or warnings in this structure.
636 %
637 */
638 MagickExport Image *SeparateImage(const Image *image,
639   const ChannelType channel_type,ExceptionInfo *exception)
640 {
641 #define GetChannelBit(mask,bit)  (((size_t) (mask) >> (size_t) (bit)) & 0x01)
642 #define SeparateImageTag  "Separate/Image"
643
644   CacheView
645     *image_view,
646     *separate_view;
647
648   Image
649     *separate_image;
650
651   MagickBooleanType
652     status;
653
654   MagickOffsetType
655     progress;
656
657   ssize_t
658     y;
659
660   /*
661     Initialize separate image attributes.
662   */
663   assert(image != (Image *) NULL);
664   assert(image->signature == MagickSignature);
665   if (image->debug != MagickFalse)
666     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
667   assert(exception != (ExceptionInfo *) NULL);
668   assert(exception->signature == MagickSignature);
669   separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
670     exception);
671   if (separate_image == (Image *) NULL)
672     return((Image *) NULL);
673   if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
674     {
675       separate_image=DestroyImage(separate_image);
676       return((Image *) NULL);
677     }
678   (void) SetImageColorspace(separate_image,GRAYColorspace,exception);
679   separate_image->alpha_trait=UndefinedPixelTrait;
680   /*
681     Separate image.
682   */
683   status=MagickTrue;
684   progress=0;
685   image_view=AcquireVirtualCacheView(image,exception);
686   separate_view=AcquireAuthenticCacheView(separate_image,exception);
687 #if defined(MAGICKCORE_OPENMP_SUPPORT)
688   #pragma omp parallel for schedule(static,4) shared(progress,status) \
689     magick_threads(image,image,image->rows,1)
690 #endif
691   for (y=0; y < (ssize_t) image->rows; y++)
692   {
693     register const Quantum
694       *restrict p;
695
696     register Quantum
697       *restrict q;
698
699     register ssize_t
700       x;
701
702     if (status == MagickFalse)
703       continue;
704     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
705     q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
706       exception);
707     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
708       {
709         status=MagickFalse;
710         continue;
711       }
712     for (x=0; x < (ssize_t) image->columns; x++)
713     {
714       register ssize_t
715         i;
716
717       if (GetPixelReadMask(image,p) == 0)
718         {
719           p+=GetPixelChannels(image);
720           q+=GetPixelChannels(separate_image);
721           continue;
722         }
723       SetPixelChannel(separate_image,GrayPixelChannel,0,q);
724       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
725       {
726         PixelChannel channel=GetPixelChannelChannel(image,i);
727         PixelTrait traits=GetPixelChannelTraits(image,channel);
728         if ((traits == UndefinedPixelTrait) ||
729             (GetChannelBit(channel_type,channel) == 0))
730           continue;
731         SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
732       }
733       p+=GetPixelChannels(image);
734       q+=GetPixelChannels(separate_image);
735     }
736     if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
737       status=MagickFalse;
738     if (image->progress_monitor != (MagickProgressMonitor) NULL)
739       {
740         MagickBooleanType
741           proceed;
742
743 #if defined(MAGICKCORE_OPENMP_SUPPORT)
744         #pragma omp critical (MagickCore_SeparateImage)
745 #endif
746         proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
747         if (proceed == MagickFalse)
748           status=MagickFalse;
749       }
750   }
751   separate_view=DestroyCacheView(separate_view);
752   image_view=DestroyCacheView(image_view);
753   if (status == MagickFalse)
754     separate_image=DestroyImage(separate_image);
755   return(separate_image);
756 }
757 \f
758 /*
759 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
760 %                                                                             %
761 %                                                                             %
762 %                                                                             %
763 %     S e p a r a t e I m a g e s                                             %
764 %                                                                             %
765 %                                                                             %
766 %                                                                             %
767 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
768 %
769 %  SeparateImages() returns a separate grayscale image for each channel
770 %  specified.
771 %
772 %  The format of the SeparateImages method is:
773 %
774 %      Image *SeparateImages(const Image *image,ExceptionInfo *exception)
775 %
776 %  A description of each parameter follows:
777 %
778 %    o image: the image.
779 %
780 %    o exception: return any errors or warnings in this structure.
781 %
782 */
783 MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
784 {
785   Image
786     *images,
787     *separate_image;
788
789   register ssize_t
790     i;
791
792   assert(image != (Image *) NULL);
793   assert(image->signature == MagickSignature);
794   if (image->debug != MagickFalse)
795     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
796   images=NewImageList();
797   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
798   {
799     PixelChannel channel=GetPixelChannelChannel(image,i);
800     PixelTrait traits=GetPixelChannelTraits(image,channel);
801     if ((traits == UndefinedPixelTrait) ||
802         ((traits & UpdatePixelTrait) == 0))
803       continue;
804     separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
805     if (separate_image != (Image *) NULL)
806       AppendImageToList(&images,separate_image);
807   }
808   if (images == (Image *) NULL)
809     images=SeparateImage(image,UndefinedChannel,exception);
810   return(images);
811 }