% MagickCore Image Channel Methods %
% %
% Software Design %
-% John Cristy %
+% Cristy %
% December 2003 %
% %
% %
-% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
+% Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization %
% dedicated to making software imaging solutions freely available. %
% %
% You may not use this file except in compliance with the License. You may %
Include declarations.
*/
#include "MagickCore/studio.h"
+#include "MagickCore/cache-private.h"
+#include "MagickCore/channel.h"
+#include "MagickCore/colorspace-private.h"
+#include "MagickCore/composite-private.h"
+#include "MagickCore/enhance.h"
#include "MagickCore/image.h"
#include "MagickCore/list.h"
#include "MagickCore/log.h"
+#include "MagickCore/monitor.h"
+#include "MagickCore/monitor-private.h"
#include "MagickCore/option.h"
+#include "MagickCore/pixel-accessor.h"
+#include "MagickCore/pixel-private.h"
+#include "MagickCore/resource_.h"
+#include "MagickCore/string-private.h"
+#include "MagickCore/thread-private.h"
#include "MagickCore/token.h"
#include "MagickCore/utility.h"
#include "MagickCore/version.h"
% %
% %
% %
-% C h a n n e l O p e r a t i o n I m a g e %
+% C h a n n e l F x I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
-% ChannelOperationImage() applies a channel expression to the specified image.
+% ChannelFxImage() applies a channel expression to the specified image. The
+% expression consists of one or more channels, either mnemonic or numeric (e.g.
+% red, 1), separated by actions as follows:
%
-% The format of the ChannelOperationImage method is:
+% <=> exchange two channels (e.g. red<=>blue)
+% => copy one channel to another channel (e.g. red=>green)
+% = assign a constant value to a channel (e.g. red=50%)
+% , write new image channels in the specified order (e.g. red, green)
+% | add a new output image for the next set of channel operations
+% ; move to the next input image for the source of channel data
%
-% Image *ChannelOperationImage(const Image *images,
-% const char *expression,ExceptionInfo *exception)
+% For example, to create 3 grayscale images from the red, green, and blue
+% channels of an image, use:
+%
+% -channel-fx "red; green; blue"
+%
+% A channel without an operation symbol implies separate (i.e, semicolon).
+%
+% The format of the ChannelFxImage method is:
+%
+% Image *ChannelFxImage(const Image *image,const char *expression,
+% ExceptionInfo *exception)
%
% A description of each parameter follows:
%
-% o images: the images.
+% o image: the image.
%
% o expression: A channel expression.
%
typedef enum
{
ExtractChannelOp,
+ AssignChannelOp,
ExchangeChannelOp,
TransferChannelOp
-} ChannelOperation;
+} ChannelFx;
-static MagickBooleanType ChannelImage(Image *channel_image,const Image *image,
- const ChannelOperation channel_op,const PixelChannel p_channel,
- const PixelChannel q_channel,ExceptionInfo *exception)
+static inline size_t MagickMin(const size_t x,const size_t y)
{
- return(MagickTrue);
+ if (x < y)
+ return(x);
+ return(y);
}
-MagickExport Image *ChannelOperationImage(const Image *images,
- const char *expression,ExceptionInfo *exception)
+static MagickBooleanType ChannelImage(Image *destination_image,
+ const PixelChannel destination_channel,const ChannelFx channel_op,
+ const Image *source_image,const PixelChannel source_channel,
+ const Quantum pixel,ExceptionInfo *exception)
{
- char
- token[MaxTextExtent];
+ CacheView
+ *source_view,
+ *destination_view;
+
+ MagickBooleanType
+ status;
+
+ size_t
+ height,
+ width;
- ChannelOperation
+ ssize_t
+ y;
+
+ status=MagickTrue;
+ source_view=AcquireVirtualCacheView(source_image,exception);
+ destination_view=AcquireAuthenticCacheView(destination_image,exception);
+ height=MagickMin(source_image->rows,destination_image->rows);
+ width=MagickMin(source_image->columns,destination_image->columns);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(static,4) shared(status) \
+ magick_threads(source_image,source_image,height,1)
+#endif
+ for (y=0; y < (ssize_t) height; y++)
+ {
+ PixelTrait
+ destination_traits,
+ source_traits;
+
+ register const Quantum
+ *restrict p;
+
+ register Quantum
+ *restrict q;
+
+ register ssize_t
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
+ exception);
+ q=GetCacheViewAuthenticPixels(destination_view,0,y,
+ destination_image->columns,1,exception);
+ if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ destination_traits=GetPixelChannelTraits(destination_image,
+ destination_channel);
+ source_traits=GetPixelChannelTraits(source_image,source_channel);
+ if ((destination_traits == UndefinedPixelTrait) ||
+ (source_traits == UndefinedPixelTrait))
+ continue;
+ for (x=0; x < (ssize_t) width; x++)
+ {
+ if (channel_op == AssignChannelOp)
+ SetPixelChannel(destination_image,destination_channel,pixel,q);
+ else
+ SetPixelChannel(destination_image,destination_channel,
+ GetPixelChannel(source_image,source_channel,p),q);
+ p+=GetPixelChannels(source_image);
+ q+=GetPixelChannels(destination_image);
+ }
+ if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse)
+ status=MagickFalse;
+ }
+ destination_view=DestroyCacheView(destination_view);
+ source_view=DestroyCacheView(source_view);
+ return(status);
+}
+
+MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
+ ExceptionInfo *exception)
+{
+#define ChannelFxImageTag "ChannelFx/Image"
+
+ ChannelFx
channel_op;
+ ChannelType
+ channel_mask;
+
+ char
+ token[MaxTextExtent];
+
const char
*p;
+ const Image
+ *source_image;
+
+ double
+ pixel;
+
Image
- *channel_images;
+ *destination_image;
+
+ MagickBooleanType
+ status;
PixelChannel
- p_channel,
- q_channel;
-
- assert(images != (Image *) NULL);
- assert(images->signature == MagickSignature);
- if (images->debug != MagickFalse)
- (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
- channel_images=CloneImage(images,images->columns,images->columns,MagickTrue,
- exception);
+ source_channel,
+ destination_channel;
+
+ ssize_t
+ channels;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ source_image=image;
+ destination_image=CloneImage(source_image,0,0,MagickTrue,exception);
+ if (destination_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (expression == (const char *) NULL)
+ return(destination_image);
+ destination_channel=RedPixelChannel;
+ channel_mask=UndefinedChannel;
+ pixel=0.0;
p=(char *) expression;
GetMagickToken(p,&p,token);
- for (q_channel=RedPixelChannel; *p != '\0'; )
+ channel_op=ExtractChannelOp;
+ for (channels=0; *token != '\0'; )
{
- MagickBooleanType
- status;
-
ssize_t
i;
/*
Interpret channel expression.
*/
- if (*token == ',')
+ switch (*token)
+ {
+ case ',':
{
- q_channel=(PixelChannel) ((ssize_t) q_channel+1);
GetMagickToken(p,&p,token);
+ break;
}
- if (*token == '|')
+ case '|':
{
- if (GetNextImageInList(images) != (Image *) NULL)
- images=GetNextImageInList(images);
+ if (GetNextImageInList(source_image) != (Image *) NULL)
+ source_image=GetNextImageInList(source_image);
else
- images=GetFirstImageInList(images);
+ source_image=GetFirstImageInList(source_image);
GetMagickToken(p,&p,token);
+ break;
}
- if (*token == ';')
+ case ';':
{
- AppendImageToList(&channel_images,CloneImage(images,
- channel_images->columns,channel_images->rows,MagickTrue,exception));
- channel_images=GetLastImageInList(channel_images);
+ Image
+ *canvas;
+
+ SetPixelChannelMask(destination_image,channel_mask);
+ if ((channel_op == ExtractChannelOp) && (channels == 1))
+ (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
+ status=SetImageStorageClass(destination_image,DirectClass,exception);
+ if (status == MagickFalse)
+ {
+ destination_image=DestroyImageList(destination_image);
+ return(destination_image);
+ }
+ canvas=CloneImage(source_image,0,0,MagickTrue,exception);
+ if (canvas == (Image *) NULL)
+ {
+ destination_image=DestroyImageList(destination_image);
+ return(destination_image);
+ }
+ AppendImageToList(&destination_image,canvas);
+ destination_image=GetLastImageInList(destination_image);
GetMagickToken(p,&p,token);
+ channels=0;
+ destination_channel=RedPixelChannel;
+ channel_mask=UndefinedChannel;
+ break;
}
+ default:
+ break;
+ }
i=ParsePixelChannelOption(token);
if (i < 0)
{
(void) ThrowMagickException(exception,GetMagickModule(),OptionError,
- "UnableToParseExpression","`%s'",p);
- channel_images=DestroyImageList(channel_images);
- break;
+ "UnrecognizedChannelType","`%s'",token);
+ destination_image=DestroyImageList(destination_image);
+ return(destination_image);
}
- p_channel=(PixelChannel) i;
+ source_channel=(PixelChannel) i;
channel_op=ExtractChannelOp;
GetMagickToken(p,&p,token);
if (*token == '<')
GetMagickToken(p,&p,token);
}
if (*token == '=')
- GetMagickToken(p,&p,token);
+ {
+ if (channel_op != ExchangeChannelOp)
+ channel_op=AssignChannelOp;
+ GetMagickToken(p,&p,token);
+ }
if (*token == '>')
{
if (channel_op != ExchangeChannelOp)
channel_op=TransferChannelOp;
GetMagickToken(p,&p,token);
}
- if (channel_op != ExtractChannelOp)
+ switch (channel_op)
+ {
+ case AssignChannelOp:
+ {
+ pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
+ GetMagickToken(p,&p,token);
+ break;
+ }
+ case ExchangeChannelOp:
+ case TransferChannelOp:
{
i=ParsePixelChannelOption(token);
if (i < 0)
{
(void) ThrowMagickException(exception,GetMagickModule(),OptionError,
- "UnableToParseExpression","`%s'",p);
- channel_images=DestroyImageList(channel_images);
+ "UnrecognizedChannelType","`%s'",token);
+ destination_image=DestroyImageList(destination_image);
+ return(destination_image);
+ }
+ destination_channel=(PixelChannel) i;
+ switch (destination_channel)
+ {
+ case RedPixelChannel:
+ case GreenPixelChannel:
+ case BluePixelChannel:
+ case BlackPixelChannel:
+ case IndexPixelChannel:
+ break;
+ case AlphaPixelChannel:
+ {
+ destination_image->alpha_trait=BlendPixelTrait;
+ break;
+ }
+ case ReadMaskPixelChannel:
+ {
+ destination_image->read_mask=MagickTrue;
+ break;
+ }
+ case WriteMaskPixelChannel:
+ {
+ destination_image->write_mask=MagickTrue;
break;
}
- q_channel=(PixelChannel) i;
+ case MetaPixelChannel:
+ default:
+ {
+ (void) SetPixelMetaChannels(destination_image,(size_t) (i-
+ GetPixelChannels(destination_image)+1),exception);
+ break;
+ }
+ }
+ channel_mask=(ChannelType) (channel_mask | ParseChannelOption(token));
+ if (((channels >= 1) || (destination_channel >= 1)) &&
+ (IsGrayColorspace(destination_image->colorspace) != MagickFalse))
+ (void) SetImageColorspace(destination_image,sRGBColorspace,exception);
+ GetMagickToken(p,&p,token);
+ break;
}
- status=ChannelImage(channel_images,images,channel_op,p_channel,q_channel,
- exception);
+ default:
+ break;
+ }
+ status=ChannelImage(destination_image,destination_channel,channel_op,
+ source_image,source_channel,ClampToQuantum(pixel),exception);
if (status == MagickFalse)
{
- channel_images=DestroyImageList(channel_images);
+ destination_image=DestroyImageList(destination_image);
break;
}
+ channels++;
+ if (channel_op == ExchangeChannelOp)
+ {
+ status=ChannelImage(destination_image,source_channel,channel_op,
+ source_image,destination_channel,ClampToQuantum(pixel),exception);
+ if (status == MagickFalse)
+ {
+ destination_image=DestroyImageList(destination_image);
+ break;
+ }
+ channels++;
+ }
+ switch (channel_op)
+ {
+ case ExtractChannelOp:
+ {
+ channel_mask=(ChannelType) (channel_mask | (1 << destination_channel));
+ destination_channel=(PixelChannel) (destination_channel+1);
+ break;
+ }
+ default:
+ break;
+ }
+ status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
+ strlen(expression));
+ if (status == MagickFalse)
+ break;
+ }
+ SetPixelChannelMask(destination_image,channel_mask);
+ if ((channel_op == ExtractChannelOp) && (channels == 1))
+ (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
+ status=SetImageStorageClass(destination_image,DirectClass,exception);
+ if (status == MagickFalse)
+ {
+ destination_image=GetLastImageInList(destination_image);
+ return((Image *) NULL);
+ }
+ return(GetFirstImageInList(destination_image));
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o m b i n e I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CombineImages() combines one or more images into a single image. The
+% grayscale value of the pixels of each image in the sequence is assigned in
+% order to the specified channels of the combined image. The typical
+% ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
+%
+% The format of the CombineImages method is:
+%
+% Image *CombineImages(const Image *images,const ColorspaceType colorspace,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o images: the image sequence.
+%
+% o colorspace: the image colorspace.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *CombineImages(const Image *image,
+ const ColorspaceType colorspace,ExceptionInfo *exception)
+{
+#define CombineImageTag "Combine/Image"
+
+ CacheView
+ *combine_view;
+
+ Image
+ *combine_image;
+
+ MagickBooleanType
+ status;
+
+ MagickOffsetType
+ progress;
+
+ ssize_t
+ y;
+
+ /*
+ Ensure the image are the same size.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ combine_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (combine_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
+ {
+ combine_image=DestroyImage(combine_image);
+ return((Image *) NULL);
+ }
+ (void) SetImageColorspace(combine_image,colorspace,exception);
+ if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
+ combine_image->alpha_trait=BlendPixelTrait;
+ /*
+ Combine images.
+ */
+ status=MagickTrue;
+ progress=0;
+ combine_view=AcquireAuthenticCacheView(combine_image,exception);
+ for (y=0; y < (ssize_t) combine_image->rows; y++)
+ {
+ CacheView
+ *image_view;
+
+ const Image
+ *next;
+
+ Quantum
+ *pixels;
+
+ register const Quantum
+ *restrict p;
+
+ register Quantum
+ *restrict q;
+
+ register ssize_t
+ i;
+
+ if (status == MagickFalse)
+ continue;
+ pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
+ 1,exception);
+ if (pixels == (Quantum *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ next=image;
+ for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++)
+ {
+ register ssize_t
+ x;
+
+ PixelChannel channel=GetPixelChannelChannel(combine_image,i);
+ PixelTrait traits=GetPixelChannelTraits(combine_image,channel);
+ if (traits == UndefinedPixelTrait)
+ continue;
+ if (next == (Image *) NULL)
+ continue;
+ image_view=AcquireVirtualCacheView(next,exception);
+ p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
+ if (p == (const Quantum *) NULL)
+ continue;
+ q=pixels;
+ for (x=0; x < (ssize_t) combine_image->columns; x++)
+ {
+ if (x < (ssize_t) next->columns)
+ {
+ q[i]=GetPixelGray(next,p);
+ p+=GetPixelChannels(next);
+ }
+ q+=GetPixelChannels(combine_image);
+ }
+ image_view=DestroyCacheView(image_view);
+ next=GetNextImageInList(next);
+ }
+ if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+ proceed=SetImageProgress(image,CombineImageTag,progress++,
+ combine_image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ combine_view=DestroyCacheView(combine_view);
+ if (status == MagickFalse)
+ combine_image=DestroyImage(combine_image);
+ return(combine_image);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e A l p h a C h a n n e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageAlphaChannel() returns MagickFalse if the image alpha channel is
+% not activated. That is, the image is RGB rather than RGBA or CMYK rather
+% than CMYKA.
+%
+% The format of the GetImageAlphaChannel method is:
+%
+% MagickBooleanType GetImageAlphaChannel(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType GetImageAlphaChannel(const Image *image)
+{
+ assert(image != (const Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ return(image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e p a r a t e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SeparateImage() separates a channel from the image and returns it as a
+% grayscale image.
+%
+% The format of the SeparateImage method is:
+%
+% Image *SeparateImage(const Image *image,const ChannelType channel,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the image channel.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *SeparateImage(const Image *image,
+ const ChannelType channel_type,ExceptionInfo *exception)
+{
+#define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
+#define SeparateImageTag "Separate/Image"
+
+ CacheView
+ *image_view,
+ *separate_view;
+
+ Image
+ *separate_image;
+
+ MagickBooleanType
+ status;
+
+ MagickOffsetType
+ progress;
+
+ ssize_t
+ y;
+
+ /*
+ Initialize separate image attributes.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ exception);
+ if (separate_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
+ {
+ separate_image=DestroyImage(separate_image);
+ return((Image *) NULL);
+ }
+ (void) SetImageColorspace(separate_image,GRAYColorspace,exception);
+ separate_image->alpha_trait=UndefinedPixelTrait;
+ /*
+ Separate image.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireVirtualCacheView(image,exception);
+ separate_view=AcquireAuthenticCacheView(separate_image,exception);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(static,4) shared(progress,status) \
+ magick_threads(image,image,image->rows,1)
+#endif
+ for (y=0; y < (ssize_t) image->rows; y++)
+ {
+ register const Quantum
+ *restrict p;
+
+ register Quantum
+ *restrict q;
+
+ register ssize_t
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
+ exception);
+ if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (ssize_t) image->columns; x++)
+ {
+ register ssize_t
+ i;
+
+ if (GetPixelReadMask(image,p) == 0)
+ {
+ SetPixelBackgoundColor(separate_image,q);
+ p+=GetPixelChannels(image);
+ q+=GetPixelChannels(separate_image);
+ continue;
+ }
+ SetPixelChannel(separate_image,GrayPixelChannel,0,q);
+ for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+ {
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
+ if ((traits == UndefinedPixelTrait) ||
+ (GetChannelBit(channel_type,channel) == 0))
+ continue;
+ SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
+ }
+ p+=GetPixelChannels(image);
+ q+=GetPixelChannels(separate_image);
+ }
+ if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_SeparateImage)
+#endif
+ proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ separate_view=DestroyCacheView(separate_view);
+ image_view=DestroyCacheView(image_view);
+ if (status == MagickFalse)
+ separate_image=DestroyImage(separate_image);
+ return(separate_image);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e p a r a t e I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SeparateImages() returns a separate grayscale image for each channel
+% specified.
+%
+% The format of the SeparateImages method is:
+%
+% Image *SeparateImages(const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
+{
+ Image
+ *images,
+ *separate_image;
+
+ register ssize_t
+ i;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ images=NewImageList();
+ for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+ {
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
+ if ((traits == UndefinedPixelTrait) ||
+ ((traits & UpdatePixelTrait) == 0))
+ continue;
+ separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
+ if (separate_image != (Image *) NULL)
+ AppendImageToList(&images,separate_image);
+ }
+ if (images == (Image *) NULL)
+ images=SeparateImage(image,UndefinedChannel,exception);
+ return(images);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e A l p h a C h a n n e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageAlphaChannel() activates, deactivates, resets, or sets the alpha
+% channel.
+%
+% The format of the SetImageAlphaChannel method is:
+%
+% MagickBooleanType SetImageAlphaChannel(Image *image,
+% const AlphaChannelOption alpha_type,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o alpha_type: The alpha channel type: ActivateAlphaChannel,
+% CopyAlphaChannel, DeactivateAlphaChannel, ExtractAlphaChannel,
+% OpaqueAlphaChannel, SetAlphaChannel, ShapeAlphaChannel, and
+% TransparentAlphaChannel.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline void FlattenPixelInfo(const Image *image,const PixelInfo *p,
+ const double alpha,const Quantum *q,const double beta,
+ Quantum *composite)
+{
+ double
+ Da,
+ gamma,
+ Sa;
+
+ register ssize_t
+ i;
+
+ /*
+ Compose pixel p over pixel q with the given alpha.
+ */
+ Sa=QuantumScale*alpha;
+ Da=QuantumScale*beta,
+ gamma=Sa*(-Da)+Sa+Da;
+ gamma=PerceptibleReciprocal(gamma);
+ for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+ {
+ PixelChannel channel=GetPixelChannelChannel(image,i);
+ PixelTrait traits=GetPixelChannelTraits(image,channel);
+ if (traits == UndefinedPixelTrait)
+ continue;
+ switch (channel)
+ {
+ case RedPixelChannel:
+ {
+ composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
+ (double) p->red,alpha));
+ break;
+ }
+ case GreenPixelChannel:
+ {
+ composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
+ (double) p->green,alpha));
+ break;
+ }
+ case BluePixelChannel:
+ {
+ composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
+ (double) p->blue,alpha));
+ break;
+ }
+ case BlackPixelChannel:
+ {
+ composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
+ (double) p->black,alpha));
+ break;
+ }
+ case AlphaPixelChannel:
+ {
+ composite[i]=ClampToQuantum(QuantumRange*(Sa*(-Da)+Sa+Da));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+MagickExport MagickBooleanType SetImageAlphaChannel(Image *image,
+ const AlphaChannelOption alpha_type,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ status=MagickTrue;
+ switch (alpha_type)
+ {
+ case ActivateAlphaChannel:
+ {
+ image->alpha_trait=BlendPixelTrait;
+ break;
+ }
+ case BackgroundAlphaChannel:
+ {
+ CacheView
+ *image_view;
+
+ ssize_t
+ y;
+
+ /*
+ Set transparent pixels to background color.
+ */
+ if (image->alpha_trait != BlendPixelTrait)
+ break;
+ if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
+ break;
+ image_view=AcquireAuthenticCacheView(image,exception);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(static,4) shared(status) \
+ magick_threads(image,image,image->rows,1)
+#endif
+ for (y=0; y < (ssize_t) image->rows; y++)
+ {
+ register Quantum
+ *restrict q;
+
+ register ssize_t
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (Quantum *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (ssize_t) image->columns; x++)
+ {
+ if (GetPixelAlpha(image,q) == TransparentAlpha)
+ {
+ SetPixelInfoPixel(image,&image->background_color,q);
+ SetPixelChannel(image,AlphaPixelChannel,TransparentAlpha,q);
+ }
+ q+=GetPixelChannels(image);
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+ }
+ case CopyAlphaChannel:
+ case ShapeAlphaChannel:
+ {
+ /*
+ Copy pixel intensity to the alpha channel.
+ */
+ status=CompositeImage(image,image,IntensityCompositeOp,MagickTrue,0,0,
+ exception);
+ if (alpha_type == ShapeAlphaChannel)
+ (void) LevelImageColors(image,&image->background_color,
+ &image->background_color,MagickTrue,exception);
+ break;
+ }
+ case DeactivateAlphaChannel:
+ {
+ image->alpha_trait=CopyPixelTrait;
+ break;
+ }
+ case ExtractAlphaChannel:
+ {
+ status=CompositeImage(image,image,AlphaCompositeOp,MagickTrue,0,0,
+ exception);
+ image->alpha_trait=CopyPixelTrait;
+ break;
+ }
+ case OpaqueAlphaChannel:
+ {
+ status=SetImageAlpha(image,OpaqueAlpha,exception);
+ break;
+ }
+ case RemoveAlphaChannel:
+ {
+ CacheView
+ *image_view;
+
+ ssize_t
+ y;
+
+ /*
+ Remove transparency.
+ */
+ if (image->alpha_trait != BlendPixelTrait)
+ break;
+ if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
+ break;
+ image_view=AcquireAuthenticCacheView(image,exception);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(static,4) shared(status) \
+ magick_threads(image,image,image->rows,1)
+#endif
+ for (y=0; y < (ssize_t) image->rows; y++)
+ {
+ register Quantum
+ *restrict q;
+
+ register ssize_t
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (Quantum *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (ssize_t) image->columns; x++)
+ {
+ FlattenPixelInfo(image,&image->background_color,
+ image->background_color.alpha,q,(double)
+ GetPixelAlpha(image,q),q);
+ q+=GetPixelChannels(image);
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ image->alpha_trait=image->background_color.alpha_trait;
+ return(status);
+ }
+ case SetAlphaChannel:
+ {
+ if (image->alpha_trait != BlendPixelTrait)
+ status=SetImageAlpha(image,OpaqueAlpha,exception);
+ break;
+ }
+ case TransparentAlphaChannel:
+ {
+ status=SetImageAlpha(image,TransparentAlpha,exception);
+ break;
+ }
+ case UndefinedAlphaChannel:
+ break;
}
- return(channel_images);
+ if (status == MagickFalse)
+ return(status);
+ return(SyncImagePixelCache(image,exception));
}