channel.c revision 8a46d827a124555f0c48fb2368ec1bba8e079ab6
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
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
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
103typedef enum
104{
105  ExtractChannelOp,
106  AssignChannelOp,
107  ExchangeChannelOp,
108  TransferChannelOp
109} ChannelFx;
110
111static 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
118static 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) 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
196MagickExport 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
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*/
454MagickExport 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
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*/
613MagickExport 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) 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=InversesRGBCompandor(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
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*/
769MagickExport 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}
804