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  EEE     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%                                   Cristy                                    %
17%                               December 2003                                 %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2016 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/cache-private.h"
45#include "MagickCore/channel.h"
46#include "MagickCore/colorspace-private.h"
47#include "MagickCore/composite-private.h"
48#include "MagickCore/enhance.h"
49#include "MagickCore/image.h"
50#include "MagickCore/list.h"
51#include "MagickCore/log.h"
52#include "MagickCore/monitor.h"
53#include "MagickCore/monitor-private.h"
54#include "MagickCore/option.h"
55#include "MagickCore/pixel-accessor.h"
56#include "MagickCore/pixel-private.h"
57#include "MagickCore/resource_.h"
58#include "MagickCore/string-private.h"
59#include "MagickCore/thread-private.h"
60#include "MagickCore/token.h"
61#include "MagickCore/utility.h"
62#include "MagickCore/version.h"
63
64/*
65%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
66%                                                                             %
67%                                                                             %
68%                                                                             %
69%     C h a n n e l F x I m a g e                                             %
70%                                                                             %
71%                                                                             %
72%                                                                             %
73%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
74%
75%  ChannelFxImage() applies a channel expression to the specified image.  The
76%  expression consists of one or more channels, either mnemonic or numeric (e.g.
77%  red, 1), separated by actions as follows:
78%
79%    <=>     exchange two channels (e.g. red<=>blue)
80%    =>      copy one channel to another channel (e.g. red=>green)
81%    =       assign a constant value to a channel (e.g. red=50%)
82%    ,       write new image channels in the specified order (e.g. red, green)
83%    |       add a new output image for the next set of channel operations
84%    ;       move to the next input image for the source of channel data
85%
86%  For example, to create 3 grayscale images from the red, green, and blue
87%  channels of an image, use:
88%
89%    -channel-fx "red; green; blue"
90%
91%  A channel without an operation symbol implies separate (i.e, semicolon).
92%
93%  The format of the ChannelFxImage method is:
94%
95%      Image *ChannelFxImage(const Image *image,const char *expression,
96%        ExceptionInfo *exception)
97%
98%  A description of each parameter follows:
99%
100%    o image: the image.
101%
102%    o expression: A channel expression.
103%
104%    o exception: return any errors or warnings in this structure.
105%
106*/
107
108typedef enum
109{
110  ExtractChannelOp,
111  AssignChannelOp,
112  ExchangeChannelOp,
113  TransferChannelOp
114} ChannelFx;
115
116static 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    width;
131
132  ssize_t
133    y;
134
135  status=MagickTrue;
136  source_view=AcquireVirtualCacheView(source_image,exception);
137  destination_view=AcquireAuthenticCacheView(destination_image,exception);
138  height=MagickMin(source_image->rows,destination_image->rows);
139  width=MagickMin(source_image->columns,destination_image->columns);
140#if defined(MAGICKCORE_OPENMP_SUPPORT)
141  #pragma omp parallel for schedule(static,4) shared(status) \
142    magick_threads(source_image,source_image,height,1)
143#endif
144  for (y=0; y < (ssize_t) height; y++)
145  {
146    PixelTrait
147      destination_traits,
148      source_traits;
149
150    register const Quantum
151      *magick_restrict p;
152
153    register Quantum
154      *magick_restrict q;
155
156    register ssize_t
157      x;
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=GetPixelChannelTraits(destination_image,
171      destination_channel);
172    source_traits=GetPixelChannelTraits(source_image,source_channel);
173    if ((destination_traits == UndefinedPixelTrait) ||
174        (source_traits == UndefinedPixelTrait))
175      continue;
176    for (x=0; x < (ssize_t) width; x++)
177    {
178      if (channel_op == AssignChannelOp)
179        SetPixelChannel(destination_image,destination_channel,pixel,q);
180      else
181        SetPixelChannel(destination_image,destination_channel,
182          GetPixelChannel(source_image,source_channel,p),q);
183      p+=GetPixelChannels(source_image);
184      q+=GetPixelChannels(destination_image);
185    }
186    if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse)
187      status=MagickFalse;
188  }
189  destination_view=DestroyCacheView(destination_view);
190  source_view=DestroyCacheView(source_view);
191  return(status);
192}
193
194MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
195  ExceptionInfo *exception)
196{
197#define ChannelFxImageTag  "ChannelFx/Image"
198
199  ChannelFx
200    channel_op;
201
202  ChannelType
203    channel_mask;
204
205  char
206    token[MagickPathExtent];
207
208  const char
209    *p;
210
211  const Image
212    *source_image;
213
214  double
215    pixel;
216
217  Image
218    *destination_image;
219
220  MagickBooleanType
221    status;
222
223  PixelChannel
224    source_channel,
225    destination_channel;
226
227  ssize_t
228    channels;
229
230  assert(image != (Image *) NULL);
231  assert(image->signature == MagickCoreSignature);
232  if (image->debug != MagickFalse)
233    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
234  assert(exception != (ExceptionInfo *) NULL);
235  assert(exception->signature == MagickCoreSignature);
236  source_image=image;
237  destination_image=CloneImage(source_image,0,0,MagickTrue,exception);
238  if (destination_image == (Image *) NULL)
239    return((Image *) NULL);
240  if (expression == (const char *) NULL)
241    return(destination_image);
242  destination_channel=RedPixelChannel;
243  channel_mask=UndefinedChannel;
244  pixel=0.0;
245  p=(char *) expression;
246  GetNextToken(p,&p,MagickPathExtent,token);
247  channel_op=ExtractChannelOp;
248  for (channels=0; *token != '\0'; )
249  {
250    ssize_t
251      i;
252
253    /*
254      Interpret channel expression.
255    */
256    switch (*token)
257    {
258      case ',':
259      {
260        GetNextToken(p,&p,MagickPathExtent,token);
261        break;
262      }
263      case '|':
264      {
265        if (GetNextImageInList(source_image) != (Image *) NULL)
266          source_image=GetNextImageInList(source_image);
267        else
268          source_image=GetFirstImageInList(source_image);
269        GetNextToken(p,&p,MagickPathExtent,token);
270        break;
271      }
272      case ';':
273      {
274        Image
275          *canvas;
276
277        (void) SetPixelChannelMask(destination_image,channel_mask);
278        if ((channel_op == ExtractChannelOp) && (channels == 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        AppendImageToList(&destination_image,canvas);
293        destination_image=GetLastImageInList(destination_image);
294        GetNextToken(p,&p,MagickPathExtent,token);
295        channels=0;
296        destination_channel=RedPixelChannel;
297        channel_mask=UndefinedChannel;
298        break;
299      }
300      default:
301        break;
302    }
303    i=ParsePixelChannelOption(token);
304    if (i < 0)
305      {
306        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
307          "UnrecognizedChannelType","`%s'",token);
308        destination_image=DestroyImageList(destination_image);
309        return(destination_image);
310      }
311    source_channel=(PixelChannel) i;
312    channel_op=ExtractChannelOp;
313    GetNextToken(p,&p,MagickPathExtent,token);
314    if (*token == '<')
315      {
316        channel_op=ExchangeChannelOp;
317        GetNextToken(p,&p,MagickPathExtent,token);
318      }
319    if (*token == '=')
320      {
321        if (channel_op != ExchangeChannelOp)
322          channel_op=AssignChannelOp;
323        GetNextToken(p,&p,MagickPathExtent,token);
324      }
325    if (*token == '>')
326      {
327        if (channel_op != ExchangeChannelOp)
328          channel_op=TransferChannelOp;
329        GetNextToken(p,&p,MagickPathExtent,token);
330      }
331    switch (channel_op)
332    {
333      case AssignChannelOp:
334      {
335        pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
336        GetNextToken(p,&p,MagickPathExtent,token);
337        break;
338      }
339      case ExchangeChannelOp:
340      case TransferChannelOp:
341      {
342        i=ParsePixelChannelOption(token);
343        if (i < 0)
344          {
345            (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
346              "UnrecognizedChannelType","`%s'",token);
347            destination_image=DestroyImageList(destination_image);
348            return(destination_image);
349          }
350        destination_channel=(PixelChannel) i;
351        switch (destination_channel)
352        {
353          case RedPixelChannel:
354          case GreenPixelChannel:
355          case BluePixelChannel:
356          case BlackPixelChannel:
357          case IndexPixelChannel:
358            break;
359          case AlphaPixelChannel:
360          {
361            destination_image->alpha_trait=BlendPixelTrait;
362            break;
363          }
364          case ReadMaskPixelChannel:
365          {
366            destination_image->read_mask=MagickTrue;
367            break;
368          }
369          case WriteMaskPixelChannel:
370          {
371            destination_image->write_mask=MagickTrue;
372            break;
373          }
374          case MetaPixelChannel:
375          default:
376          {
377            (void) SetPixelMetaChannels(destination_image,(size_t) (i-
378              GetPixelChannels(destination_image)+1),exception);
379            break;
380          }
381        }
382        channel_mask=(ChannelType) (channel_mask | ParseChannelOption(token));
383        if (((channels >= 1)  || (destination_channel >= 1)) &&
384            (IsGrayColorspace(destination_image->colorspace) != MagickFalse))
385          (void) SetImageColorspace(destination_image,sRGBColorspace,exception);
386        GetNextToken(p,&p,MagickPathExtent,token);
387        break;
388      }
389      default:
390        break;
391    }
392    status=ChannelImage(destination_image,destination_channel,channel_op,
393      source_image,source_channel,ClampToQuantum(pixel),exception);
394    if (status == MagickFalse)
395      {
396        destination_image=DestroyImageList(destination_image);
397        break;
398      }
399    channels++;
400    if (channel_op == ExchangeChannelOp)
401      {
402        status=ChannelImage(destination_image,source_channel,channel_op,
403          source_image,destination_channel,ClampToQuantum(pixel),exception);
404        if (status == MagickFalse)
405          {
406            destination_image=DestroyImageList(destination_image);
407            break;
408          }
409        channels++;
410      }
411    switch (channel_op)
412    {
413      case ExtractChannelOp:
414      {
415        channel_mask=(ChannelType) (channel_mask | (1 << destination_channel));
416        destination_channel=(PixelChannel) (destination_channel+1);
417        break;
418      }
419      default:
420        break;
421    }
422    status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
423      strlen(expression));
424    if (status == MagickFalse)
425      break;
426  }
427  (void) SetPixelChannelMask(destination_image,channel_mask);
428  if ((channel_op == ExtractChannelOp) && (channels == 1))
429    (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
430  status=SetImageStorageClass(destination_image,DirectClass,exception);
431  if (status == MagickFalse)
432    {
433      destination_image=GetLastImageInList(destination_image);
434      return((Image *) NULL);
435    }
436  return(GetFirstImageInList(destination_image));
437}
438
439/*
440%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
441%                                                                             %
442%                                                                             %
443%                                                                             %
444%     C o m b i n e I m a g e s                                               %
445%                                                                             %
446%                                                                             %
447%                                                                             %
448%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
449%
450%  CombineImages() combines one or more images into a single image.  The
451%  grayscale value of the pixels of each image in the sequence is assigned in
452%  order to the specified channels of the combined image.   The typical
453%  ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
454%
455%  The format of the CombineImages method is:
456%
457%      Image *CombineImages(const Image *images,const ColorspaceType colorspace,
458%        ExceptionInfo *exception)
459%
460%  A description of each parameter follows:
461%
462%    o images: the image sequence.
463%
464%    o colorspace: the image colorspace.
465%
466%    o exception: return any errors or warnings in this structure.
467%
468*/
469MagickExport Image *CombineImages(const Image *image,
470  const ColorspaceType colorspace,ExceptionInfo *exception)
471{
472#define CombineImageTag  "Combine/Image"
473
474  CacheView
475    *combine_view;
476
477  Image
478    *combine_image;
479
480  MagickBooleanType
481    status;
482
483  MagickOffsetType
484    progress;
485
486  ssize_t
487    y;
488
489  /*
490    Ensure the image are the same size.
491  */
492  assert(image != (const Image *) NULL);
493  assert(image->signature == MagickCoreSignature);
494  if (image->debug != MagickFalse)
495    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
496  assert(exception != (ExceptionInfo *) NULL);
497  assert(exception->signature == MagickCoreSignature);
498  combine_image=CloneImage(image,0,0,MagickTrue,exception);
499  if (combine_image == (Image *) NULL)
500    return((Image *) NULL);
501  if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
502    {
503      combine_image=DestroyImage(combine_image);
504      return((Image *) NULL);
505    }
506  if ((colorspace == UndefinedColorspace) || (image->number_channels == 1))
507    (void) SetImageColorspace(combine_image,sRGBColorspace,exception);
508  else
509    (void) SetImageColorspace(combine_image,colorspace,exception);
510  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
511    combine_image->alpha_trait=BlendPixelTrait;
512  /*
513    Combine images.
514  */
515  status=MagickTrue;
516  progress=0;
517  combine_view=AcquireAuthenticCacheView(combine_image,exception);
518  for (y=0; y < (ssize_t) combine_image->rows; y++)
519  {
520    CacheView
521      *image_view;
522
523    const Image
524      *next;
525
526    Quantum
527      *pixels;
528
529    register const Quantum
530      *magick_restrict p;
531
532    register Quantum
533      *magick_restrict q;
534
535    register ssize_t
536      i;
537
538    if (status == MagickFalse)
539      continue;
540    pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
541      1,exception);
542    if (pixels == (Quantum *) NULL)
543      {
544        status=MagickFalse;
545        continue;
546      }
547    next=image;
548    for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++)
549    {
550      register ssize_t
551        x;
552
553      PixelChannel channel=GetPixelChannelChannel(combine_image,i);
554      PixelTrait traits=GetPixelChannelTraits(combine_image,channel);
555      if (traits == UndefinedPixelTrait)
556        continue;
557      if (next == (Image *) NULL)
558        continue;
559      image_view=AcquireVirtualCacheView(next,exception);
560      p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
561      if (p == (const Quantum *) NULL)
562        continue;
563      q=pixels;
564      for (x=0; x < (ssize_t) combine_image->columns; x++)
565      {
566        if (x < (ssize_t) next->columns)
567          {
568            q[i]=GetPixelGray(next,p);
569            p+=GetPixelChannels(next);
570          }
571        q+=GetPixelChannels(combine_image);
572      }
573      image_view=DestroyCacheView(image_view);
574      next=GetNextImageInList(next);
575    }
576    if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
577      status=MagickFalse;
578    if (image->progress_monitor != (MagickProgressMonitor) NULL)
579      {
580        MagickBooleanType
581          proceed;
582
583        proceed=SetImageProgress(image,CombineImageTag,progress++,
584          combine_image->rows);
585        if (proceed == MagickFalse)
586          status=MagickFalse;
587      }
588  }
589  combine_view=DestroyCacheView(combine_view);
590  if (status == MagickFalse)
591    combine_image=DestroyImage(combine_image);
592  return(combine_image);
593}
594
595/*
596%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
597%                                                                             %
598%                                                                             %
599%                                                                             %
600%   G e t I m a g e A l p h a C h a n n e l                                   %
601%                                                                             %
602%                                                                             %
603%                                                                             %
604%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
605%
606%  GetImageAlphaChannel() returns MagickFalse if the image alpha channel is
607%  not activated.  That is, the image is RGB rather than RGBA or CMYK rather
608%  than CMYKA.
609%
610%  The format of the GetImageAlphaChannel method is:
611%
612%      MagickBooleanType GetImageAlphaChannel(const Image *image)
613%
614%  A description of each parameter follows:
615%
616%    o image: the image.
617%
618*/
619MagickExport MagickBooleanType GetImageAlphaChannel(const Image *image)
620{
621  assert(image != (const Image *) NULL);
622  if (image->debug != MagickFalse)
623    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
624  assert(image->signature == MagickCoreSignature);
625  return(image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse);
626}
627
628/*
629%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
630%                                                                             %
631%                                                                             %
632%                                                                             %
633%     S e p a r a t e I m a g e                                               %
634%                                                                             %
635%                                                                             %
636%                                                                             %
637%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
638%
639%  SeparateImage() separates a channel from the image and returns it as a
640%  grayscale image.
641%
642%  The format of the SeparateImage method is:
643%
644%      Image *SeparateImage(const Image *image,const ChannelType channel,
645%        ExceptionInfo *exception)
646%
647%  A description of each parameter follows:
648%
649%    o image: the image.
650%
651%    o channel: the image channel.
652%
653%    o exception: return any errors or warnings in this structure.
654%
655*/
656MagickExport Image *SeparateImage(const Image *image,
657  const ChannelType channel_type,ExceptionInfo *exception)
658{
659#define GetChannelBit(mask,bit)  (((size_t) (mask) >> (size_t) (bit)) & 0x01)
660#define SeparateImageTag  "Separate/Image"
661
662  CacheView
663    *image_view,
664    *separate_view;
665
666  Image
667    *separate_image;
668
669  MagickBooleanType
670    status;
671
672  MagickOffsetType
673    progress;
674
675  ssize_t
676    y;
677
678  /*
679    Initialize separate image attributes.
680  */
681  assert(image != (Image *) NULL);
682  assert(image->signature == MagickCoreSignature);
683  if (image->debug != MagickFalse)
684    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
685  assert(exception != (ExceptionInfo *) NULL);
686  assert(exception->signature == MagickCoreSignature);
687  separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
688    exception);
689  if (separate_image == (Image *) NULL)
690    return((Image *) NULL);
691  if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
692    {
693      separate_image=DestroyImage(separate_image);
694      return((Image *) NULL);
695    }
696  (void) SetImageColorspace(separate_image,GRAYColorspace,exception);
697  separate_image->alpha_trait=UndefinedPixelTrait;
698  /*
699    Separate image.
700  */
701  status=MagickTrue;
702  progress=0;
703  image_view=AcquireVirtualCacheView(image,exception);
704  separate_view=AcquireAuthenticCacheView(separate_image,exception);
705#if defined(MAGICKCORE_OPENMP_SUPPORT)
706  #pragma omp parallel for schedule(static,4) shared(progress,status) \
707    magick_threads(image,image,image->rows,1)
708#endif
709  for (y=0; y < (ssize_t) image->rows; y++)
710  {
711    register const Quantum
712      *magick_restrict p;
713
714    register Quantum
715      *magick_restrict q;
716
717    register ssize_t
718      x;
719
720    if (status == MagickFalse)
721      continue;
722    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
723    q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
724      exception);
725    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
726      {
727        status=MagickFalse;
728        continue;
729      }
730    for (x=0; x < (ssize_t) image->columns; x++)
731    {
732      register ssize_t
733        i;
734
735      if (GetPixelReadMask(image,p) == 0)
736        {
737          SetPixelBackgoundColor(separate_image,q);
738          p+=GetPixelChannels(image);
739          q+=GetPixelChannels(separate_image);
740          continue;
741        }
742      SetPixelChannel(separate_image,GrayPixelChannel,0,q);
743      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
744      {
745        PixelChannel channel=GetPixelChannelChannel(image,i);
746        PixelTrait traits=GetPixelChannelTraits(image,channel);
747        if ((traits == UndefinedPixelTrait) ||
748            (GetChannelBit(channel_type,channel) == 0))
749          continue;
750        SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
751      }
752      p+=GetPixelChannels(image);
753      q+=GetPixelChannels(separate_image);
754    }
755    if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
756      status=MagickFalse;
757    if (image->progress_monitor != (MagickProgressMonitor) NULL)
758      {
759        MagickBooleanType
760          proceed;
761
762#if defined(MAGICKCORE_OPENMP_SUPPORT)
763        #pragma omp critical (MagickCore_SeparateImage)
764#endif
765        proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
766        if (proceed == MagickFalse)
767          status=MagickFalse;
768      }
769  }
770  separate_view=DestroyCacheView(separate_view);
771  image_view=DestroyCacheView(image_view);
772  (void) SetImageChannelMask(separate_image,DefaultChannels);
773  if (status == MagickFalse)
774    separate_image=DestroyImage(separate_image);
775  return(separate_image);
776}
777
778/*
779%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
780%                                                                             %
781%                                                                             %
782%                                                                             %
783%     S e p a r a t e I m a g e s                                             %
784%                                                                             %
785%                                                                             %
786%                                                                             %
787%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
788%
789%  SeparateImages() returns a separate grayscale image for each channel
790%  specified.
791%
792%  The format of the SeparateImages method is:
793%
794%      Image *SeparateImages(const Image *image,ExceptionInfo *exception)
795%
796%  A description of each parameter follows:
797%
798%    o image: the image.
799%
800%    o exception: return any errors or warnings in this structure.
801%
802*/
803MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
804{
805  Image
806    *images,
807    *separate_image;
808
809  register ssize_t
810    i;
811
812  assert(image != (Image *) NULL);
813  assert(image->signature == MagickCoreSignature);
814  if (image->debug != MagickFalse)
815    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
816  images=NewImageList();
817  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
818  {
819    PixelChannel channel=GetPixelChannelChannel(image,i);
820    PixelTrait traits=GetPixelChannelTraits(image,channel);
821    if ((traits == UndefinedPixelTrait) ||
822        ((traits & UpdatePixelTrait) == 0))
823      continue;
824    separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
825    if (separate_image != (Image *) NULL)
826      AppendImageToList(&images,separate_image);
827  }
828  if (images == (Image *) NULL)
829    images=SeparateImage(image,UndefinedChannel,exception);
830  return(images);
831}
832
833/*
834%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
835%                                                                             %
836%                                                                             %
837%                                                                             %
838%   S e t I m a g e A l p h a C h a n n e l                                   %
839%                                                                             %
840%                                                                             %
841%                                                                             %
842%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
843%
844%  SetImageAlphaChannel() activates, deactivates, resets, or sets the alpha
845%  channel.
846%
847%  The format of the SetImageAlphaChannel method is:
848%
849%      MagickBooleanType SetImageAlphaChannel(Image *image,
850%        const AlphaChannelOption alpha_type,ExceptionInfo *exception)
851%
852%  A description of each parameter follows:
853%
854%    o image: the image.
855%
856%    o alpha_type:  The alpha channel type: ActivateAlphaChannel,
857%      AssociateAlphaChannel, CopyAlphaChannel, DeactivateAlphaChannel,
858%      DisassociateAlphaChannel,  ExtractAlphaChannel, OffAlphaChannel,
859%      OnAlphaChannel, OpaqueAlphaChannel, SetAlphaChannel, ShapeAlphaChannel,
860%      and TransparentAlphaChannel.
861%
862%    o exception: return any errors or warnings in this structure.
863%
864*/
865
866static inline void FlattenPixelInfo(const Image *image,const PixelInfo *p,
867  const double alpha,const Quantum *q,const double beta,
868  Quantum *composite)
869{
870  double
871    Da,
872    gamma,
873    Sa;
874
875  register ssize_t
876    i;
877
878  /*
879    Compose pixel p over pixel q with the given alpha.
880  */
881  Sa=QuantumScale*alpha;
882  Da=QuantumScale*beta,
883  gamma=Sa*(-Da)+Sa+Da;
884  gamma=PerceptibleReciprocal(gamma);
885  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
886  {
887    PixelChannel channel=GetPixelChannelChannel(image,i);
888    PixelTrait traits=GetPixelChannelTraits(image,channel);
889    if (traits == UndefinedPixelTrait)
890      continue;
891    switch (channel)
892    {
893      case RedPixelChannel:
894      {
895        composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
896          (double) p->red,alpha));
897        break;
898      }
899      case GreenPixelChannel:
900      {
901        composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
902          (double) p->green,alpha));
903        break;
904      }
905      case BluePixelChannel:
906      {
907        composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
908          (double) p->blue,alpha));
909        break;
910      }
911      case BlackPixelChannel:
912      {
913        composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
914          (double) p->black,alpha));
915        break;
916      }
917      case AlphaPixelChannel:
918      {
919        composite[i]=ClampToQuantum(QuantumRange*(Sa*(-Da)+Sa+Da));
920        break;
921      }
922      default:
923        break;
924    }
925  }
926}
927
928MagickExport MagickBooleanType SetImageAlphaChannel(Image *image,
929  const AlphaChannelOption alpha_type,ExceptionInfo *exception)
930{
931  CacheView
932    *image_view;
933
934  MagickBooleanType
935    status;
936
937  ssize_t
938    y;
939
940  assert(image != (Image *) NULL);
941  if (image->debug != MagickFalse)
942    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
943  assert(image->signature == MagickCoreSignature);
944  status=MagickTrue;
945  switch (alpha_type)
946  {
947    case ActivateAlphaChannel:
948    {
949      image->alpha_trait=BlendPixelTrait;
950      break;
951    }
952    case AssociateAlphaChannel:
953    {
954      /*
955        Associate alpha.
956      */
957      status=SetImageStorageClass(image,DirectClass,exception);
958      if (status == MagickFalse)
959        break;
960      image_view=AcquireAuthenticCacheView(image,exception);
961#if defined(MAGICKCORE_OPENMP_SUPPORT)
962      #pragma omp parallel for schedule(static,4) shared(status) \
963        magick_threads(image,image,image->rows,1)
964#endif
965      for (y=0; y < (ssize_t) image->rows; y++)
966      {
967        register Quantum
968          *magick_restrict q;
969
970        register ssize_t
971          x;
972
973        if (status == MagickFalse)
974          continue;
975        q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
976          exception);
977        if (q == (Quantum *) NULL)
978          {
979            status=MagickFalse;
980            continue;
981          }
982        for (x=0; x < (ssize_t) image->columns; x++)
983        {
984          double
985            gamma;
986
987          register ssize_t
988            i;
989
990          if (GetPixelReadMask(image,q) == 0)
991            {
992              q+=GetPixelChannels(image);
993              continue;
994            }
995          gamma=QuantumScale*GetPixelAlpha(image,q);
996          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
997          {
998            PixelChannel channel=GetPixelChannelChannel(image,i);
999            PixelTrait traits=GetPixelChannelTraits(image,channel);
1000            if (channel == AlphaPixelChannel)
1001              continue;
1002            if ((traits & UpdatePixelTrait) == 0)
1003              continue;
1004            q[i]=ClampToQuantum(gamma*q[i]);
1005          }
1006          q+=GetPixelChannels(image);
1007        }
1008        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1009          status=MagickFalse;
1010      }
1011      image_view=DestroyCacheView(image_view);
1012      image->alpha_trait=CopyPixelTrait;
1013      return(status);
1014    }
1015    case BackgroundAlphaChannel:
1016    {
1017      /*
1018        Set transparent pixels to background color.
1019      */
1020      if (image->alpha_trait == UndefinedPixelTrait)
1021        break;
1022      status=SetImageStorageClass(image,DirectClass,exception);
1023      if (status == MagickFalse)
1024        break;
1025      image_view=AcquireAuthenticCacheView(image,exception);
1026#if defined(MAGICKCORE_OPENMP_SUPPORT)
1027      #pragma omp parallel for schedule(static,4) shared(status) \
1028        magick_threads(image,image,image->rows,1)
1029#endif
1030      for (y=0; y < (ssize_t) image->rows; y++)
1031      {
1032        register Quantum
1033          *magick_restrict q;
1034
1035        register ssize_t
1036          x;
1037
1038        if (status == MagickFalse)
1039          continue;
1040        q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1041          exception);
1042        if (q == (Quantum *) NULL)
1043          {
1044            status=MagickFalse;
1045            continue;
1046          }
1047        for (x=0; x < (ssize_t) image->columns; x++)
1048        {
1049          if (GetPixelAlpha(image,q) == TransparentAlpha)
1050            {
1051              SetPixelViaPixelInfo(image,&image->background_color,q);
1052              SetPixelChannel(image,AlphaPixelChannel,TransparentAlpha,q);
1053            }
1054          q+=GetPixelChannels(image);
1055        }
1056        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1057          status=MagickFalse;
1058      }
1059      image_view=DestroyCacheView(image_view);
1060      return(status);
1061    }
1062    case CopyAlphaChannel:
1063    case ShapeAlphaChannel:
1064    {
1065      /*
1066        Copy pixel intensity to the alpha channel.
1067      */
1068      image->alpha_trait=UpdatePixelTrait;
1069      status=CompositeImage(image,image,IntensityCompositeOp,MagickTrue,0,0,
1070        exception);
1071      if (alpha_type == ShapeAlphaChannel)
1072        (void) LevelImageColors(image,&image->background_color,
1073          &image->background_color,MagickTrue,exception);
1074      break;
1075    }
1076    case DeactivateAlphaChannel:
1077    {
1078      if (image->alpha_trait == UndefinedPixelTrait)
1079        status=SetImageAlpha(image,OpaqueAlpha,exception);
1080      image->alpha_trait=CopyPixelTrait;
1081      break;
1082    }
1083    case DisassociateAlphaChannel:
1084    {
1085      /*
1086        Disassociate alpha.
1087      */
1088      status=SetImageStorageClass(image,DirectClass,exception);
1089      if (status == MagickFalse)
1090        break;
1091      image->alpha_trait=BlendPixelTrait;
1092      image_view=AcquireAuthenticCacheView(image,exception);
1093#if defined(MAGICKCORE_OPENMP_SUPPORT)
1094      #pragma omp parallel for schedule(static,4) shared(status) \
1095        magick_threads(image,image,image->rows,1)
1096#endif
1097      for (y=0; y < (ssize_t) image->rows; y++)
1098      {
1099        register Quantum
1100          *magick_restrict q;
1101
1102        register ssize_t
1103          x;
1104
1105        if (status == MagickFalse)
1106          continue;
1107        q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1108          exception);
1109        if (q == (Quantum *) NULL)
1110          {
1111            status=MagickFalse;
1112            continue;
1113          }
1114        for (x=0; x < (ssize_t) image->columns; x++)
1115        {
1116          double
1117            gamma,
1118            Sa;
1119
1120          register ssize_t
1121            i;
1122
1123          if (GetPixelReadMask(image,q) == 0)
1124            {
1125              q+=GetPixelChannels(image);
1126              continue;
1127            }
1128          Sa=QuantumScale*GetPixelAlpha(image,q);
1129          gamma=PerceptibleReciprocal(Sa);
1130          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1131          {
1132            PixelChannel channel=GetPixelChannelChannel(image,i);
1133            PixelTrait traits=GetPixelChannelTraits(image,channel);
1134            if (channel == AlphaPixelChannel)
1135              continue;
1136            if ((traits & UpdatePixelTrait) == 0)
1137              continue;
1138            q[i]=ClampToQuantum(gamma*q[i]);
1139          }
1140          q+=GetPixelChannels(image);
1141        }
1142        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1143          status=MagickFalse;
1144      }
1145      image_view=DestroyCacheView(image_view);
1146      image->alpha_trait=UndefinedPixelTrait;
1147      return(status);
1148    }
1149    case DiscreteAlphaChannel:
1150    {
1151      if (image->alpha_trait == UndefinedPixelTrait)
1152        status=SetImageAlpha(image,OpaqueAlpha,exception);
1153      image->alpha_trait=UpdatePixelTrait;
1154      break;
1155    }
1156    case ExtractAlphaChannel:
1157    {
1158      status=CompositeImage(image,image,AlphaCompositeOp,MagickTrue,0,0,
1159        exception);
1160      image->alpha_trait=UndefinedPixelTrait;
1161      break;
1162    }
1163    case OffAlphaChannel:
1164    {
1165      image->alpha_trait=UndefinedPixelTrait;
1166      break;
1167    }
1168    case OnAlphaChannel:
1169    {
1170      if (image->alpha_trait == UndefinedPixelTrait)
1171        status=SetImageAlpha(image,OpaqueAlpha,exception);
1172      image->alpha_trait=BlendPixelTrait;
1173      break;
1174    }
1175    case OpaqueAlphaChannel:
1176    {
1177      status=SetImageAlpha(image,OpaqueAlpha,exception);
1178      break;
1179    }
1180    case RemoveAlphaChannel:
1181    {
1182      /*
1183        Remove transparency.
1184      */
1185      if (image->alpha_trait == UndefinedPixelTrait)
1186        break;
1187      status=SetImageStorageClass(image,DirectClass,exception);
1188      if (status == MagickFalse)
1189        break;
1190      image_view=AcquireAuthenticCacheView(image,exception);
1191#if defined(MAGICKCORE_OPENMP_SUPPORT)
1192      #pragma omp parallel for schedule(static,4) shared(status) \
1193        magick_threads(image,image,image->rows,1)
1194#endif
1195      for (y=0; y < (ssize_t) image->rows; y++)
1196      {
1197        register Quantum
1198          *magick_restrict q;
1199
1200        register ssize_t
1201          x;
1202
1203        if (status == MagickFalse)
1204          continue;
1205        q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1206          exception);
1207        if (q == (Quantum *) NULL)
1208          {
1209            status=MagickFalse;
1210            continue;
1211          }
1212        for (x=0; x < (ssize_t) image->columns; x++)
1213        {
1214          FlattenPixelInfo(image,&image->background_color,
1215            image->background_color.alpha,q,(double)
1216            GetPixelAlpha(image,q),q);
1217          q+=GetPixelChannels(image);
1218        }
1219        if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1220          status=MagickFalse;
1221      }
1222      image_view=DestroyCacheView(image_view);
1223      image->alpha_trait=image->background_color.alpha_trait;
1224      break;
1225    }
1226    case SetAlphaChannel:
1227    {
1228      if (image->alpha_trait == UndefinedPixelTrait)
1229        status=SetImageAlpha(image,OpaqueAlpha,exception);
1230      break;
1231    }
1232    case TransparentAlphaChannel:
1233    {
1234      status=SetImageAlpha(image,TransparentAlpha,exception);
1235      break;
1236    }
1237    case UndefinedAlphaChannel:
1238      break;
1239  }
1240  if (status == MagickFalse)
1241    return(status);
1242  (void) SetPixelChannelMask(image,image->channel_mask);
1243  return(SyncImagePixelCache(image,exception));
1244}
1245