]> granicus.if.org Git - imagemagick/blobdiff - MagickCore/channel.c
(no commit message)
[imagemagick] / MagickCore / channel.c
index ec86cff1cb4422f75c8fbeff5b4db03268d844fc..bd96b55cf461fbd5a74ea7fd7d14c15d32fe3be0 100644 (file)
@@ -41,6 +41,7 @@
   Include declarations.
 */
 #include "MagickCore/studio.h"
+#include "MagickCore/colorspace-private.h"
 #include "MagickCore/image.h"
 #include "MagickCore/list.h"
 #include "MagickCore/log.h"
@@ -48,7 +49,9 @@
 #include "MagickCore/monitor-private.h"
 #include "MagickCore/option.h"
 #include "MagickCore/pixel-accessor.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"
 %  red, 1), separated by actions as follows:
 %
 %    <=>     exchange two channels (e.g. red<=>blue)
-%    =>      transfer a channel to another (e.g. red=>green)
-%    =       assign a constant (e.g. red=>50%)
-%    ,       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)
+%    =>      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
 %
-%  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:
+%  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,
@@ -111,8 +116,8 @@ static inline size_t MagickMin(const size_t x,const size_t y)
 }
 
 static MagickBooleanType ChannelImage(Image *destination_image,
-  const Image *source_image,const PixelChannel source_channel,
   const PixelChannel destination_channel,const ChannelFx channel_op,
+  const Image *source_image,const PixelChannel source_channel,
   const Quantum pixel,ExceptionInfo *exception)
 {
   CacheView
@@ -123,20 +128,27 @@ static MagickBooleanType ChannelImage(Image *destination_image,
     status;
 
   size_t
-    height;
+    height,
+    width;
 
   ssize_t
     y;
 
   status=MagickTrue;
-  source_view=AcquireCacheView(source_image);
-  destination_view=AcquireCacheView(destination_image);
+  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) shared(status)
+  #pragma omp parallel for schedule(static) shared(status) \
+    dynamic_number_threads(source_image,width,height,1)
 #endif
   for (y=0; y < (ssize_t) height; y++)
   {
+    PixelTrait
+      destination_traits,
+      source_traits;
+
     register const Quantum
       *restrict p;
 
@@ -146,41 +158,30 @@ static MagickBooleanType ChannelImage(Image *destination_image,
     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,
+    q=GetCacheViewAuthenticPixels(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);
+    destination_traits=GetPixelChannelMapTraits(destination_image,
+      destination_channel);
+    source_traits=GetPixelChannelMapTraits(source_image,source_channel);
+    if ((destination_traits == UndefinedPixelTrait) ||
+        (source_traits == UndefinedPixelTrait))
+      continue;
     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);
       if (channel_op == AssignChannelOp)
         SetPixelChannel(destination_image,destination_channel,pixel,q);
       else
-        SetPixelChannel(destination_image,destination_channel,p[offset],q);
+        SetPixelChannel(destination_image,destination_channel,
+          GetPixelChannel(source_image,source_channel,p),q);
       p+=GetPixelChannels(source_image);
       q+=GetPixelChannels(destination_image);
     }
@@ -197,12 +198,15 @@ MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
 {
 #define ChannelFxImageTag  "ChannelFx/Image"
 
-  char
-    token[MaxTextExtent];
-
   ChannelFx
     channel_op;
 
+  ChannelType
+    channel_mask;
+
+  char
+    token[MaxTextExtent];
+
   const char
     *p;
 
@@ -215,6 +219,9 @@ MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
   Image
     *destination_image;
 
+  MagickBooleanType
+    status;
+
   PixelChannel
     source_channel,
     destination_channel;
@@ -232,22 +239,18 @@ MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
   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 (IsGrayColorspace(image->colorspace) != MagickFalse)
+    (void) TransformImageColorspace((Image *) image,RGBColorspace,exception);
   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 (channels=0; *p != '\0'; )
+  channel_op=ExtractChannelOp;
+  for (channels=0; *token != '\0'; )
   {
-    MagickBooleanType
-      status;
-
     ssize_t
       i;
 
@@ -272,35 +275,37 @@ MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
         Image
           *canvas;
 
-        if (channels == 1)
+        SetPixelChannelMapMask(destination_image,channel_mask);
+        if ((channel_op == ExtractChannelOp) && (destination_channel == 1))
+          (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
+        status=SetImageStorageClass(destination_image,DirectClass,exception);
+        if (status == MagickFalse)
           {
-            destination_image->colorspace=GRAYColorspace;
-            InitializePixelChannelMap(destination_image);
+            destination_image=DestroyImageList(destination_image);
+            return(destination_image);
           }
         canvas=CloneImage(source_image,0,0,MagickTrue,exception);
         if (canvas == (Image *) NULL)
           {
-            destination_image=GetLastImageInList(destination_image);
-            return((Image *) NULL);
+            destination_image=DestroyImageList(destination_image);
+            return(destination_image);
           }
+        if (IsGrayColorspace(canvas->colorspace) != MagickFalse)
+          (void) TransformImageColorspace(canvas,RGBColorspace,exception);
         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;
+        channel_mask=UndefinedChannel;
       }
     i=ParsePixelChannelOption(token);
     if (i < 0)
       {
         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
-          "UnrecognizedChannelType","`%s'",token);
+          "UnrecognizedChannelType","'%s'",token);
         destination_image=DestroyImageList(destination_image);
-        break;
+        return(destination_image);
       }
     source_channel=(PixelChannel) i;
     channel_op=ExtractChannelOp;
@@ -327,6 +332,7 @@ MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
       case AssignChannelOp:
       {
         pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
+        GetMagickToken(p,&p,token);
         break;
       }
       case ExchangeChannelOp:
@@ -336,45 +342,83 @@ MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
         if (i < 0)
           {
             (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
-              "UnrecognizedChannelType","`%s'",token);
+              "UnrecognizedChannelType","'%s'",token);
             destination_image=DestroyImageList(destination_image);
-            break;
+            return(destination_image);
           }
         destination_channel=(PixelChannel) i;
+        channel_mask=(ChannelType) (channel_mask | ParseChannelOption(token));
+        if (LocaleCompare(token,"gray") == 0)
+          (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
+        if ((LocaleCompare(token,"black") == 0) ||
+            (LocaleCompare(token,"c") == 0) ||
+            (LocaleCompare(token,"cyan") == 0) ||
+            (LocaleCompare(token,"k") == 0) ||
+            (LocaleCompare(token,"m") == 0) ||
+            (LocaleCompare(token,"magenta") == 0) ||
+            (LocaleCompare(token,"y") == 0) ||
+            (LocaleCompare(token,"yellow") == 0))
+          (void) SetImageColorspace(destination_image,CMYKColorspace,exception);
+        if ((LocaleCompare(token,"Cb") == 0) ||
+            (LocaleCompare(token,"Cr") == 0))
+          (void) SetImageColorspace(destination_image,YCbCrColorspace,
+            exception);
+        if (LocaleCompare(token,"alpha") == 0)
+          (void) SetImageAlpha(destination_image,OpaqueAlpha,exception);
+        if (i >= (ssize_t) GetPixelChannels(destination_image))
+          (void) SetPixelMetaChannels(destination_image,(size_t) (i-
+            GetPixelChannels(destination_image)+1),exception);
+        GetMagickToken(p,&p,token);
         break;
       }
-      case ExtractChannelOp:
+      default:
         break;
-     }
-    status=ChannelImage(destination_image,source_image,source_channel,
-      destination_channel,channel_op,ClampToQuantum(pixel),exception);
+    }
+    status=ChannelImage(destination_image,destination_channel,channel_op,
+      source_image,source_channel,ClampToQuantum(pixel),exception);
     if (status == MagickFalse)
       {
         destination_image=DestroyImageList(destination_image);
         break;
       }
+    channels++;
     if (channel_op == ExchangeChannelOp)
       {
-        status=ChannelImage(destination_image,source_image,destination_channel,
-          source_channel,channel_op,ClampToQuantum(pixel),exception);
+        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++;
       }
-    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;
   }
-  if (channels == 1)
+  SetPixelChannelMapMask(destination_image,channel_mask);
+  if ((channel_op == ExtractChannelOp) && (destination_channel == 1))
+    (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
+  status=SetImageStorageClass(destination_image,DirectClass,exception);
+  if (status == MagickFalse)
     {
-      destination_image->colorspace=GRAYColorspace;
-      InitializePixelChannelMap(destination_image);
+      destination_image=GetLastImageInList(destination_image);
+      return((Image *) NULL);
     }
-  return(destination_image);
+  return(GetFirstImageInList(destination_image));
 }
 \f
 /*
@@ -395,16 +439,20 @@ MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
 %
 %  The format of the CombineImages method is:
 %
-%      Image *CombineImages(const Image *image,ExceptionInfo *exception)
+%      Image *CombineImages(const Image *images,const ColorspaceType colorspace,
+%        ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
-%    o image: the image.
+%    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,ExceptionInfo *exception)
+MagickExport Image *CombineImages(const Image *image,
+  const ColorspaceType colorspace,ExceptionInfo *exception)
 {
 #define CombineImageTag  "Combine/Image"
 
@@ -440,6 +488,8 @@ MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
       combine_image=DestroyImage(combine_image);
       return((Image *) NULL);
     }
+  if (IsGrayColorspace(image->colorspace) != MagickFalse)
+    (void) SetImageColorspace(combine_image,RGBColorspace,exception);
   if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
     combine_image->matte=MagickTrue;
   /*
@@ -447,7 +497,7 @@ MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
   */
   status=MagickTrue;
   progress=0;
-  combine_view=AcquireCacheView(combine_image);
+  combine_view=AcquireAuthenticCacheView(combine_image,exception);
   for (y=0; y < (ssize_t) combine_image->rows; y++)
   {
     CacheView
@@ -478,13 +528,12 @@ MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
         continue;
       }
     next=image;
-    for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+    for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++)
     {
       PixelChannel
         channel;
 
       PixelTrait
-        combine_traits,
         traits;
 
       register ssize_t
@@ -492,45 +541,44 @@ MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
 
       if (next == (Image *) NULL)
         continue;
-      channel=GetPixelChannelMapChannel(image,i);
-      traits=GetPixelChannelMapTraits(image,channel);
-      combine_traits=GetPixelChannelMapTraits(combine_image,channel);
-      if ((traits == UndefinedPixelTrait) ||
-          (combine_traits == UndefinedPixelTrait))
+      channel=GetPixelChannelMapChannel(combine_image,i);
+      traits=GetPixelChannelMapTraits(combine_image,channel);
+      if (traits == UndefinedPixelTrait)
         continue;
-      image_view=AcquireCacheView(next);
+      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) image->columns)
+        if (x < (ssize_t) next->columns)
           {
-            q[i]=GetPixelGray(image,p);
-            p+=GetPixelChannels(image);
+            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;
-        }
     }
+    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);
+  (void) TransformImageColorspace(combine_image,colorspace,exception);
   return(combine_image);
 }
 \f
@@ -602,16 +650,18 @@ MagickExport Image *SeparateImage(const Image *image,
       separate_image=DestroyImage(separate_image);
       return((Image *) NULL);
     }
-  separate_image->colorspace=GRAYColorspace;
+  separate_image->matte=MagickFalse;
+  (void) SetImageColorspace(separate_image,GRAYColorspace,exception);
   /*
     Separate image.
   */
   status=MagickTrue;
   progress=0;
-  image_view=AcquireCacheView(image);
-  separate_view=AcquireCacheView(separate_image);
+  image_view=AcquireVirtualCacheView(image,exception);
+  separate_view=AcquireAuthenticCacheView(separate_image,exception);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(static) shared(progress,status)
+  #pragma omp parallel for schedule(static) shared(progress,status) \
+    dynamic_number_threads(image,image->columns,image->rows,1)
 #endif
   for (y=0; y < (ssize_t) image->rows; y++)
   {
@@ -648,6 +698,9 @@ MagickExport Image *SeparateImage(const Image *image,
       SetPixelChannel(separate_image,GrayPixelChannel,0,q);
       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
       {
+        double
+          pixel;
+
         PixelChannel
           channel;
 
@@ -659,7 +712,11 @@ MagickExport Image *SeparateImage(const Image *image,
         if ((traits == UndefinedPixelTrait) ||
             (GetChannelBit(channel_type,channel) == 0))
           continue;
-        SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
+        pixel=p[i];
+        if (IssRGBColorspace(image->colorspace) != MagickFalse)
+          pixel=InversesRGBCompandor(pixel);
+        SetPixelChannel(separate_image,GrayPixelChannel,ClampToQuantum(pixel),
+          q);
       }
       p+=GetPixelChannels(image);
       q+=GetPixelChannels(separate_image);
@@ -672,7 +729,7 @@ MagickExport Image *SeparateImage(const Image *image,
           proceed;
 
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp critical (MagickCore_SeparateImage)
+        #pragma omp critical (MagickCore_SeparateImage)
 #endif
         proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
         if (proceed == MagickFalse)
@@ -700,8 +757,7 @@ MagickExport Image *SeparateImage(const Image *image,
 %
 %  The format of the SeparateImages method is:
 %
-%      MagickBooleanType SeparateImages(const Image *image,
-%        ExceptionInfo *exception)
+%      Image *SeparateImages(const Image *image,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -741,5 +797,7 @@ MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
     if (separate_image != (Image *) NULL)
       AppendImageToList(&images,separate_image);
   }
+  if (images == (Image *) NULL)
+    images=SeparateImage(image,UndefinedChannel,exception);
   return(images);
 }