]> granicus.if.org Git - imagemagick/blobdiff - MagickCore/channel.c
The 8bim profile will be updated when the iptc profile is changed.
[imagemagick] / MagickCore / channel.c
index c3cb921580d400e2d2e4ab7ec02d7e4abe2f48d3..57cde8f3d0b3915aa75bce27d60ee9135156dfe9 100644 (file)
 %                      MagickCore Image Channel Methods                       %
 %                                                                             %
 %                              Software Design                                %
-%                                John Cristy                                  %
+%                                   Cristy                                    %
 %                               December 2003                                 %
 %                                                                             %
 %                                                                             %
-%  Copyright 1999-2013 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"
@@ -49,6 +53,7 @@
 #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"
@@ -141,7 +146,7 @@ static MagickBooleanType ChannelImage(Image *destination_image,
   width=MagickMin(source_image->columns,destination_image->columns);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   #pragma omp parallel for schedule(static,4) shared(status) \
-    dynamic_number_threads(source_image,width,height,1)
+    magick_threads(source_image,source_image,height,1)
 #endif
   for (y=0; y < (ssize_t) height; y++)
   {
@@ -239,8 +244,6 @@ 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 (IsGrayColorspace(image->colorspace) != MagickFalse)
-    (void) TransformImageColorspace((Image *) image,RGBColorspace,exception);
   if (expression == (const char *) NULL)
     return(destination_image);
   destination_channel=RedPixelChannel;
@@ -257,26 +260,29 @@ MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
     /*
       Interpret channel expression.
     */
-    if (*token == ',')
+    switch (*token)
+    {
+      case ',':
       {
-        destination_channel=(PixelChannel) ((ssize_t) destination_channel+1);
         GetMagickToken(p,&p,token);
+        break;
       }
-    if (*token == '|')
+      case '|':
       {
         if (GetNextImageInList(source_image) != (Image *) NULL)
           source_image=GetNextImageInList(source_image);
         else
           source_image=GetFirstImageInList(source_image);
         GetMagickToken(p,&p,token);
+        break;
       }
-    if (*token == ';')
+      case ';':
       {
         Image
           *canvas;
 
         SetPixelChannelMask(destination_image,channel_mask);
-        if ((channel_op == ExtractChannelOp) && (destination_channel == 1))
+        if ((channel_op == ExtractChannelOp) && (channels == 1))
           (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
         status=SetImageStorageClass(destination_image,DirectClass,exception);
         if (status == MagickFalse)
@@ -290,20 +296,22 @@ MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
             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);
         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,
-          "UnrecognizedChannelType","'%s'",token);
+          "UnrecognizedChannelType","`%s'",token);
         destination_image=DestroyImageList(destination_image);
         return(destination_image);
       }
@@ -342,32 +350,46 @@ 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);
             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;
+          }
+          case MetaPixelChannel:
+          default:
+          {
+            (void) SetPixelMetaChannels(destination_image,(size_t) (i-
+              GetPixelChannels(destination_image)+1),exception);
+            break;
+          }
+        }
         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);
+        if (((channels >= 1)  || (destination_channel >= 1)) &&
+            (IsGrayColorspace(destination_image->colorspace) != MagickFalse))
+          (void) SetImageColorspace(destination_image,sRGBColorspace,exception);
         GetMagickToken(p,&p,token);
         break;
       }
@@ -410,7 +432,7 @@ MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
       break;
   }
   SetPixelChannelMask(destination_image,channel_mask);
-  if ((channel_op == ExtractChannelOp) && (destination_channel == 1))
+  if ((channel_op == ExtractChannelOp) && (channels == 1))
     (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
   status=SetImageStorageClass(destination_image,DirectClass,exception);
   if (status == MagickFalse)
@@ -488,8 +510,7 @@ MagickExport Image *CombineImages(const Image *image,
       combine_image=DestroyImage(combine_image);
       return((Image *) NULL);
     }
-  if (IsGrayColorspace(image->colorspace) != MagickFalse)
-    (void) SetImageColorspace(combine_image,RGBColorspace,exception);
+  (void) SetImageColorspace(combine_image,colorspace,exception);
   if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
     combine_image->alpha_trait=BlendPixelTrait;
   /*
@@ -530,21 +551,15 @@ MagickExport Image *CombineImages(const Image *image,
     next=image;
     for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++)
     {
-      PixelChannel
-        channel;
-
-      PixelTrait
-        traits;
-
       register ssize_t
         x;
 
-      if (next == (Image *) NULL)
-        continue;
-      channel=GetPixelChannelChannel(combine_image,i);
-      traits=GetPixelChannelTraits(combine_image,channel);
+      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)
@@ -578,7 +593,6 @@ MagickExport Image *CombineImages(const Image *image,
   combine_view=DestroyCacheView(combine_view);
   if (status == MagickFalse)
     combine_image=DestroyImage(combine_image);
-  (void) TransformImageColorspace(combine_image,colorspace,exception);
   return(combine_image);
 }
 \f
@@ -587,6 +601,39 @@ MagickExport Image *CombineImages(const Image *image,
 %                                                                             %
 %                                                                             %
 %                                                                             %
+%   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                                               %
 %                                                                             %
 %                                                                             %
@@ -633,7 +680,7 @@ MagickExport Image *SeparateImage(const Image *image,
     y;
 
   /*
-    Initialize spread image attributes.
+    Initialize separate image attributes.
   */
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
@@ -650,8 +697,8 @@ MagickExport Image *SeparateImage(const Image *image,
       separate_image=DestroyImage(separate_image);
       return((Image *) NULL);
     }
-  separate_image->alpha_trait=UndefinedPixelTrait;
   (void) SetImageColorspace(separate_image,GRAYColorspace,exception);
+  separate_image->alpha_trait=UndefinedPixelTrait;
   /*
     Separate image.
   */
@@ -661,7 +708,7 @@ MagickExport Image *SeparateImage(const Image *image,
   separate_view=AcquireAuthenticCacheView(separate_image,exception);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   #pragma omp parallel for schedule(static,4) shared(progress,status) \
-    dynamic_number_threads(image,image->columns,image->rows,1)
+    magick_threads(image,image,image->rows,1)
 #endif
   for (y=0; y < (ssize_t) image->rows; y++)
   {
@@ -689,8 +736,9 @@ MagickExport Image *SeparateImage(const Image *image,
       register ssize_t
         i;
 
-      if (GetPixelMask(image,p) != 0)
+      if (GetPixelReadMask(image,p) == 0)
         {
+          SetPixelBackgoundColor(separate_image,q);
           p+=GetPixelChannels(image);
           q+=GetPixelChannels(separate_image);
           continue;
@@ -698,25 +746,12 @@ 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;
-
-        PixelTrait
-          traits;
-
-        channel=GetPixelChannelChannel(image,i);
-        traits=GetPixelChannelTraits(image,channel);
+        PixelChannel channel=GetPixelChannelChannel(image,i);
+        PixelTrait traits=GetPixelChannelTraits(image,channel);
         if ((traits == UndefinedPixelTrait) ||
             (GetChannelBit(channel_type,channel) == 0))
           continue;
-        pixel=p[i];
-        if (IssRGBColorspace(image->colorspace) != MagickFalse)
-          pixel=DecodesRGBGamma(pixel);
-        SetPixelChannel(separate_image,GrayPixelChannel,ClampToQuantum(pixel),
-          q);
+        SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
       }
       p+=GetPixelChannels(image);
       q+=GetPixelChannels(separate_image);
@@ -738,6 +773,8 @@ MagickExport Image *SeparateImage(const Image *image,
   }
   separate_view=DestroyCacheView(separate_view);
   image_view=DestroyCacheView(image_view);
+  if (status == MagickFalse)
+    separate_image=DestroyImage(separate_image);
   return(separate_image);
 }
 \f
@@ -782,14 +819,8 @@ MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
   images=NewImageList();
   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   {
-    PixelChannel
-      channel;
-
-    PixelTrait
-      traits;
-
-    channel=GetPixelChannelChannel(image,i);
-    traits=GetPixelChannelTraits(image,channel);
+    PixelChannel channel=GetPixelChannelChannel(image,i);
+    PixelTrait traits=GetPixelChannelTraits(image,channel);
     if ((traits == UndefinedPixelTrait) ||
         ((traits & UpdatePixelTrait) == 0))
       continue;
@@ -801,3 +832,267 @@ MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
     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;
+  }
+  if (status == MagickFalse)
+    return(status);
+  return(SyncImagePixelCache(image,exception));
+}