diff --git a/MagickCore/channel.c b/MagickCore/channel.c
index 4fd75d6..d790821 100644
--- a/MagickCore/channel.c
+++ b/MagickCore/channel.c
@@ -44,7 +44,10 @@
#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/token.h"
#include "MagickCore/utility.h"
#include "MagickCore/version.h"
@@ -61,15 +64,28 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% ChannelOperationImage() 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 certain operation symbols as follows:
+%
+% <=> exchange two channels (e.g. red<=>blue)
+% => transfer a channel to another (e.g. red=>green)
+% , separate channel operations (e.g. red, green)
+% | read channels from next input image (e.g. red | green)
+% ; write channels to next output image (e.g. red; green; blue)
+%
+% A channel without a operation symbol implies extract. For example, to create
+% 3 grayscale images from the red, green, and blue channels of an image, use:
+%
+% -channel-ops "red; green; blue"
%
% The format of the ChannelOperationImage method is:
%
-% Image *ChannelOperationImage(const Image *images,
+% Image *ChannelOperationImage(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.
%
@@ -84,16 +100,97 @@
TransferChannelOp
} ChannelOperation;
-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,
+static MagickBooleanType ChannelImage(Image *destination_image,
+ const Image *source_image,const ChannelOperation channel_op,
+ const PixelChannel source_channel,const PixelChannel destination_channel,
+ ExceptionInfo *exception)
+{
+ CacheView
+ *source_view,
+ *destination_view;
+
+ MagickBooleanType
+ status;
+
+ size_t
+ height;
+
+ ssize_t
+ y;
+
+ status=MagickTrue;
+ source_view=AcquireCacheView(source_image);
+ destination_view=AcquireCacheView(destination_image);
+ height=MagickMin(source_image->rows,destination_image->rows);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(static) shared(status)
+#endif
+ for (y=0; y < (ssize_t) height; y++)
+ {
+ register const Quantum
+ *restrict p;
+
+ register Quantum
+ *restrict q;
+
+ register ssize_t
+ x;
+
+ size_t
+ width;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
+ exception);
+ q=QueueCacheViewAuthenticPixels(destination_view,0,y,
+ destination_image->columns,1,exception);
+ if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ width=MagickMin(source_image->columns,destination_image->columns);
+ for (x=0; x < (ssize_t) width; x++)
+ {
+ PixelTrait
+ destination_traits,
+ source_traits;
+
+ ssize_t
+ offset;
+
+ source_traits=GetPixelChannelMapTraits(source_image,source_channel);
+ destination_traits=GetPixelChannelMapTraits(destination_image,
+ destination_channel);
+ if ((source_traits == UndefinedPixelTrait) ||
+ (destination_traits == UndefinedPixelTrait))
+ continue;
+ offset=GetPixelChannelMapOffset(source_image,source_channel);
+ SetPixelChannel(destination_image,destination_channel,p[offset],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 *ChannelOperationImage(const Image *image,
const char *expression,ExceptionInfo *exception)
{
+#define ChannelOperationImageTag "ChannelOperation/Image"
+
char
token[MaxTextExtent];
@@ -103,22 +200,40 @@
const char
*p;
+ const Image
+ *source_image;
+
Image
- *channel_images;
+ *destination_image;
PixelChannel
- p_channel,
- q_channel;
+ source_channel,
+ destination_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);
+ 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 (SetImageBackgroundColor(destination_image,exception) == MagickFalse)
+ {
+ destination_image=GetLastImageInList(destination_image);
+ return((Image *) NULL);
+ }
+ if (expression == (const char *) NULL)
+ return(destination_image);
+ destination_channel=RedPixelChannel;
p=(char *) expression;
GetMagickToken(p,&p,token);
- for (q_channel=RedPixelChannel; *p != '\0'; )
+ for (channels=0; *p != '\0'; )
{
MagickBooleanType
status;
@@ -131,33 +246,50 @@
*/
if (*token == ',')
{
- q_channel=(PixelChannel) ((ssize_t) q_channel+1);
+ destination_channel=(PixelChannel) ((ssize_t) destination_channel+1);
GetMagickToken(p,&p,token);
}
if (*token == '|')
{
- 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);
}
if (*token == ';')
{
- AppendImageToList(&channel_images,CloneImage(images,
- channel_images->columns,channel_images->rows,MagickTrue,exception));
- channel_images=GetLastImageInList(channel_images);
+ Image
+ *canvas;
+
+ if (channels == 1)
+ destination_image->colorspace=GRAYColorspace;
+ canvas=CloneImage(source_image,0,0,MagickTrue,exception);
+ if (canvas == (Image *) NULL)
+ {
+ destination_image=GetLastImageInList(destination_image);
+ return((Image *) NULL);
+ }
+ AppendImageToList(&destination_image,canvas);
+ destination_image=GetLastImageInList(destination_image);
+ if (SetImageBackgroundColor(destination_image,exception) == MagickFalse)
+ {
+ destination_image=GetLastImageInList(destination_image);
+ return((Image *) NULL);
+ }
GetMagickToken(p,&p,token);
+ channels=0;
+ destination_channel=RedPixelChannel;
}
i=ParsePixelChannelOption(token);
if (i < 0)
{
(void) ThrowMagickException(exception,GetMagickModule(),OptionError,
"UnableToParseExpression","`%s'",p);
- channel_images=DestroyImageList(channel_images);
+ destination_image=DestroyImageList(destination_image);
break;
}
- p_channel=(PixelChannel) i;
+ source_channel=(PixelChannel) i;
channel_op=ExtractChannelOp;
GetMagickToken(p,&p,token);
if (*token == '<')
@@ -180,18 +312,25 @@
{
(void) ThrowMagickException(exception,GetMagickModule(),OptionError,
"UnableToParseExpression","`%s'",p);
- channel_images=DestroyImageList(channel_images);
+ destination_image=DestroyImageList(destination_image);
break;
}
- q_channel=(PixelChannel) i;
+ destination_channel=(PixelChannel) i;
}
- status=ChannelImage(channel_images,images,channel_op,p_channel,q_channel,
- exception);
+ status=ChannelImage(destination_image,source_image,channel_op,
+ source_channel,destination_channel,exception);
if (status == MagickFalse)
{
- channel_images=DestroyImageList(channel_images);
+ destination_image=DestroyImageList(destination_image);
break;
}
+ channels++;
+ status=SetImageProgress(source_image,ChannelOperationImageTag,p-expression,
+ strlen(expression));
+ if (status == MagickFalse)
+ break;
}
- return(channel_images);
+ if (channels == 1)
+ destination_image->colorspace=GRAYColorspace;
+ return(destination_image);
}