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