]> granicus.if.org Git - imagemagick/blobdiff - MagickCore/distort.c
(no commit message)
[imagemagick] / MagickCore / distort.c
index e2e170cbf74c772c9eb140b1cad2fc323f62ded2..fd81081b0b8efd117130f53fe2f05a4c93e7bf1c 100644 (file)
@@ -18,7 +18,7 @@
 %                                 June 2007                                   %
 %                                                                             %
 %                                                                             %
-%  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
+%  Copyright 1999-2012 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 "MagickCore/image.h"
 #include "MagickCore/list.h"
 #include "MagickCore/matrix.h"
+#include "MagickCore/matrix-private.h"
 #include "MagickCore/memory_.h"
 #include "MagickCore/monitor-private.h"
 #include "MagickCore/option.h"
 #include "MagickCore/pixel.h"
 #include "MagickCore/pixel-accessor.h"
+#include "MagickCore/pixel-private.h"
 #include "MagickCore/resample.h"
 #include "MagickCore/resample-private.h"
 #include "MagickCore/registry.h"
+#include "MagickCore/resource_.h"
 #include "MagickCore/semaphore.h"
+#include "MagickCore/shear.h"
 #include "MagickCore/string_.h"
 #include "MagickCore/string-private.h"
 #include "MagickCore/thread-private.h"
@@ -101,7 +105,7 @@ static void InvertAffineCoefficients(const double *coeff,double *inverse)
   /* From "Digital Image Warping" by George Wolberg, page 50 */
   double determinant;
 
-  determinant=1.0/(coeff[0]*coeff[4]-coeff[1]*coeff[3]);
+  determinant=MagickEpsilonReciprocal(coeff[0]*coeff[4]-coeff[1]*coeff[3]);
   inverse[0]=determinant*coeff[4];
   inverse[1]=determinant*(-coeff[1]);
   inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[2]*coeff[4]);
@@ -116,7 +120,7 @@ static void InvertPerspectiveCoefficients(const double *coeff,
   /* From "Digital Image Warping" by George Wolberg, page 53 */
   double determinant;
 
-  determinant=1.0/(coeff[0]*coeff[4]-coeff[3]*coeff[1]);
+  determinant=MagickEpsilonReciprocal(coeff[0]*coeff[4]-coeff[3]*coeff[1]);
   inverse[0]=determinant*(coeff[4]-coeff[7]*coeff[5]);
   inverse[1]=determinant*(coeff[7]*coeff[2]-coeff[1]);
   inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[4]*coeff[2]);
@@ -271,6 +275,64 @@ static double poly_basis_dy(ssize_t n, double x, double y)
 %                                                                             %
 %                                                                             %
 %                                                                             %
+%     A f f i n e T r a n s f o r m I m a g e                                 %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  AffineTransformImage() transforms an image as dictated by the affine matrix.
+%  It allocates the memory necessary for the new Image structure and returns
+%  a pointer to the new image.
+%
+%  The format of the AffineTransformImage method is:
+%
+%      Image *AffineTransformImage(const Image *image,
+%        AffineMatrix *affine_matrix,ExceptionInfo *exception)
+%
+%  A description of each parameter follows:
+%
+%    o image: the image.
+%
+%    o affine_matrix: the affine matrix.
+%
+%    o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *AffineTransformImage(const Image *image,
+  const AffineMatrix *affine_matrix,ExceptionInfo *exception)
+{
+  double
+    distort[6];
+
+  Image
+    *deskew_image;
+
+  /*
+    Affine transform image.
+  */
+  assert(image->signature == MagickSignature);
+  if (image->debug != MagickFalse)
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+  assert(affine_matrix != (AffineMatrix *) NULL);
+  assert(exception != (ExceptionInfo *) NULL);
+  assert(exception->signature == MagickSignature);
+  distort[0]=affine_matrix->sx;
+  distort[1]=affine_matrix->rx;
+  distort[2]=affine_matrix->ry;
+  distort[3]=affine_matrix->sy;
+  distort[4]=affine_matrix->tx;
+  distort[5]=affine_matrix->ty;
+  deskew_image=DistortImage(image,AffineProjectionDistortion,6,distort,
+    MagickTrue,exception);
+  return(deskew_image);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
 +   G e n e r a t e C o e f f i c i e n t s                                   %
 %                                                                             %
 %                                                                             %
@@ -1401,6 +1463,9 @@ MagickExport Image *DistortResizeImage(const Image *image,
 {
 #define DistortResizeImageTag  "Distort/Image"
 
+  double
+    distort_args[12];
+
   Image
     *resize_image,
     *tmp_image;
@@ -1408,9 +1473,6 @@ MagickExport Image *DistortResizeImage(const Image *image,
   RectangleInfo
     crop_area;
 
-  double
-    distort_args[12];
-
   VirtualPixelMethod
     vp_save;
 
@@ -1427,8 +1489,6 @@ MagickExport Image *DistortResizeImage(const Image *image,
     return((Image *) NULL);
   /* Do not short-circuit this resize if final image size is unchanged */
 
-  (void) SetImageVirtualPixelMethod(image,TransparentVirtualPixelMethod);
-
   (void) ResetMagickMemory(distort_args,0,12*sizeof(double));
   distort_args[4]=(double) image->columns;
   distort_args[6]=(double) columns;
@@ -1440,23 +1500,23 @@ MagickExport Image *DistortResizeImage(const Image *image,
   tmp_image=CloneImage(image,0,0,MagickTrue,exception);
   if ( tmp_image == (Image *) NULL )
     return((Image *) NULL);
-  (void) SetImageVirtualPixelMethod(tmp_image,TransparentVirtualPixelMethod);
+  (void) SetImageVirtualPixelMethod(tmp_image,TransparentVirtualPixelMethod,
+    exception);
 
   if (image->matte == MagickFalse)
     {
       /*
         Image has not transparency channel, so we free to use it
       */
-      (void) SetImageAlphaChannel(tmp_image,SetAlphaChannel);
+      (void) SetImageAlphaChannel(tmp_image,SetAlphaChannel,exception);
       resize_image=DistortImage(tmp_image,AffineDistortion,12,distort_args,
-            MagickTrue,exception),
+        MagickTrue,exception),
 
       tmp_image=DestroyImage(tmp_image);
       if ( resize_image == (Image *) NULL )
         return((Image *) NULL);
 
-      (void) SetImageAlphaChannel(resize_image,DeactivateAlphaChannel);
-      InheritException(exception,&image->exception);
+      (void) SetImageAlphaChannel(resize_image,DeactivateAlphaChannel,exception);
     }
   else
     {
@@ -1464,17 +1524,18 @@ MagickExport Image *DistortResizeImage(const Image *image,
         Image has transparency so handle colors and alpha separatly.
         Basically we need to separate Virtual-Pixel alpha in the resized
         image, so only the actual original images alpha channel is used.
+
+        distort alpha channel separately
       */
       Image
         *resize_alpha;
 
-      /* distort alpha channel separately */
-      (void) SeparateImageChannel(tmp_image,TrueAlphaChannel);
-      (void) SetImageAlphaChannel(tmp_image,OpaqueAlphaChannel);
+      (void) SetImageAlphaChannel(tmp_image,ExtractAlphaChannel,exception);
+      (void) SetImageAlphaChannel(tmp_image,OpaqueAlphaChannel,exception);
       resize_alpha=DistortImage(tmp_image,AffineDistortion,12,distort_args,
-            MagickTrue,exception),
+        MagickTrue,exception),
       tmp_image=DestroyImage(tmp_image);
-      if ( resize_alpha == (Image *) NULL )
+      if (resize_alpha == (Image *) NULL)
         return((Image *) NULL);
 
       /* distort the actual image containing alpha + VP alpha */
@@ -1482,25 +1543,25 @@ MagickExport Image *DistortResizeImage(const Image *image,
       if ( tmp_image == (Image *) NULL )
         return((Image *) NULL);
       (void) SetImageVirtualPixelMethod(tmp_image,
-                   TransparentVirtualPixelMethod);
+        TransparentVirtualPixelMethod,exception);
       resize_image=DistortImage(tmp_image,AffineDistortion,12,distort_args,
-            MagickTrue,exception),
+        MagickTrue,exception),
       tmp_image=DestroyImage(tmp_image);
       if ( resize_image == (Image *) NULL)
         {
           resize_alpha=DestroyImage(resize_alpha);
           return((Image *) NULL);
         }
-
       /* replace resize images alpha with the separally distorted alpha */
-      (void) SetImageAlphaChannel(resize_image,DeactivateAlphaChannel);
-      (void) SetImageAlphaChannel(resize_alpha,DeactivateAlphaChannel);
-      (void) CompositeImage(resize_image,CopyOpacityCompositeOp,resize_alpha,
-                    0,0);
-      InheritException(exception,&resize_image->exception);
+      (void) SetImageAlphaChannel(resize_image,DeactivateAlphaChannel,
+        exception);
+      (void) SetImageAlphaChannel(resize_alpha,DeactivateAlphaChannel,
+        exception);
+      (void) CompositeImage(resize_image,resize_alpha,CopyAlphaCompositeOp,
+        MagickTrue,0,0,exception);
       resize_alpha=DestroyImage(resize_alpha);
     }
-  (void) SetImageVirtualPixelMethod(resize_image,vp_save);
+  (void) SetImageVirtualPixelMethod(resize_image,vp_save,exception);
 
   /*
     Clean up the results of the Distortion
@@ -1608,7 +1669,6 @@ MagickExport Image *DistortResizeImage(const Image *image,
 %                    instead
 %
 */
-
 MagickExport Image *DistortImage(const Image *image,DistortImageMethod method,
   const size_t number_arguments,const double *arguments,
   MagickBooleanType bestfit,ExceptionInfo *exception)
@@ -1739,28 +1799,28 @@ MagickExport Image *DistortImage(const Image *image,DistortImageMethod method,
         s.x = (double) image->page.x;
         s.y = (double) image->page.y;
         scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
-        scale=1.0/(  (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
+        scale=MagickEpsilonReciprocal(scale);
         d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
         d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
         InitalBounds(d);
         s.x = (double) image->page.x+image->columns;
         s.y = (double) image->page.y;
         scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
-        scale=1.0/(  (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
+        scale=MagickEpsilonReciprocal(scale);
         d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
         d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
         ExpandBounds(d);
         s.x = (double) image->page.x;
         s.y = (double) image->page.y+image->rows;
         scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
-        scale=1.0/(  (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
+        scale=MagickEpsilonReciprocal(scale);
         d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
         d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
         ExpandBounds(d);
         s.x = (double) image->page.x+image->columns;
         s.y = (double) image->page.y+image->rows;
         scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
-        scale=1.0/(  (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
+        scale=MagickEpsilonReciprocal(scale);
         d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
         d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
         ExpandBounds(d);
@@ -1889,13 +1949,18 @@ MagickExport Image *DistortImage(const Image *image,DistortImageMethod method,
   { const char *artifact=GetImageArtifact(image,"distort:viewport");
     viewport_given = MagickFalse;
     if ( artifact != (const char *) NULL ) {
-      (void) ParseAbsoluteGeometry(artifact,&geometry);
-      viewport_given = MagickTrue;
+      MagickStatusType flags=ParseAbsoluteGeometry(artifact,&geometry);
+      if (flags==NoValue)
+        (void) ThrowMagickException(exception,GetMagickModule(),
+             OptionWarning,"InvalidSetting","'%s' '%s'",
+             "distort:viewport",artifact);
+      else
+        viewport_given = MagickTrue;
     }
   }
 
   /* Verbose output */
-  if ( GetImageArtifact(image,"verbose") != (const char *) NULL ) {
+  if ( IfStringTrue(GetImageArtifact(image,"verbose")) ) {
     register ssize_t
        i;
     char image_gen[MaxTextExtent];
@@ -2183,11 +2248,11 @@ MagickExport Image *DistortImage(const Image *image,DistortImageMethod method,
     artifact=GetImageArtifact(image,"distort:scale");
     output_scaling = 1.0;
     if (artifact != (const char *) NULL) {
-      output_scaling = fabs(InterpretLocaleValue(artifact,(char **) NULL));
-      geometry.width  *= output_scaling;
-      geometry.height *= output_scaling;
-      geometry.x      *= output_scaling;
-      geometry.y      *= output_scaling;
+      output_scaling = fabs(StringToDouble(artifact,(char **) NULL));
+      geometry.width=(size_t) (output_scaling*geometry.width+0.5);
+      geometry.height=(size_t) (output_scaling*geometry.height+0.5);
+      geometry.x=(ssize_t) (output_scaling*geometry.x+0.5);
+      geometry.y=(ssize_t) (output_scaling*geometry.y+0.5);
       if ( output_scaling < 0.1 ) {
         coeff = (double *) RelinquishMagickMemory(coeff);
         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
@@ -2210,16 +2275,18 @@ MagickExport Image *DistortImage(const Image *image,DistortImageMethod method,
   if (distort_image == (Image *) NULL)
     return((Image *) NULL);
   /* if image is ColorMapped - change it to DirectClass */
-  if (SetImageStorageClass(distort_image,DirectClass) == MagickFalse)
+  if (SetImageStorageClass(distort_image,DirectClass,exception) == MagickFalse)
     {
-      InheritException(exception,&distort_image->exception);
       distort_image=DestroyImage(distort_image);
       return((Image *) NULL);
     }
+  if ((IsPixelInfoGray(&distort_image->background_color) == MagickFalse) &&
+      (IsGrayColorspace(distort_image->colorspace) != MagickFalse))
+    (void) TransformImageColorspace(distort_image,RGBColorspace,exception);
+  if (distort_image->background_color.matte != MagickFalse)
+    distort_image->matte=MagickTrue;
   distort_image->page.x=geometry.x;
   distort_image->page.y=geometry.y;
-  if (distort_image->background_color.alpha != OpaqueAlpha)
-    distort_image->matte=MagickTrue;
 
   { /* ----- MAIN CODE -----
        Sample the source image to each pixel in the distort image.
@@ -2247,9 +2314,10 @@ MagickExport Image *DistortImage(const Image *image,DistortImageMethod method,
     GetPixelInfo(distort_image,&zero);
     resample_filter=AcquireResampleFilterThreadSet(image,
       UndefinedVirtualPixelMethod,MagickFalse,exception);
-    distort_view=AcquireCacheView(distort_image);
+    distort_view=AcquireAuthenticCacheView(distort_image,exception);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+    #pragma omp parallel for schedule(static,4) shared(progress,status) \
+      dynamic_number_threads(image,image->columns,image->rows,1)
 #endif
     for (j=0; j < (ssize_t) distort_image->rows; j++)
     {
@@ -2278,7 +2346,7 @@ MagickExport Image *DistortImage(const Image *image,DistortImageMethod method,
 
       q=QueueCacheViewAuthenticPixels(distort_view,0,j,distort_image->columns,1,
         exception);
-      if (q == (const Quantum *) NULL)
+      if (q == (Quantum *) NULL)
         {
           status=MagickFalse;
           continue;
@@ -2306,11 +2374,9 @@ MagickExport Image *DistortImage(const Image *image,DistortImageMethod method,
       */
       validity = 1.0;
 
-      GetPixelInfo(distort_image,&invalid);
-      SetPixelInfoPacket(distort_image,&distort_image->matte_color,&invalid);
+      invalid=distort_image->matte_color;
       if (distort_image->colorspace == CMYKColorspace)
         ConvertRGBToCMYK(&invalid);   /* what about other color spaces? */
-
       for (i=0; i < (ssize_t) distort_image->columns; i++)
       {
         /* map pixel coordinate to distortion space coordinate */
@@ -2645,11 +2711,12 @@ if ( d.x == 0.5 && d.y == 0.5 ) {
 
         if ( validity <= 0.0 ) {
           /* result of distortion is an invalid pixel - don't resample */
-          SetPixelPixelInfo(distort_image,&invalid,q);
+          SetPixelInfoPixel(distort_image,&invalid,q);
         }
         else {
           /* resample the source image to find its correct color */
-          (void) ResamplePixelColor(resample_filter[id],s.x,s.y,&pixel);
+          (void) ResamplePixelColor(resample_filter[id],s.x,s.y,&pixel,
+            exception);
           /* if validity between 0.0 and 1.0 mix result with invalid pixel */
           if ( validity < 1.0 ) {
             /* Do a blend of sample color and invalid pixel */
@@ -2657,7 +2724,7 @@ if ( d.x == 0.5 && d.y == 0.5 ) {
             CompositePixelInfoBlend(&pixel,validity,&invalid,(1.0-validity),
               &pixel);
           }
-          SetPixelPixelInfo(distort_image,&pixel,q);
+          SetPixelInfoPixel(distort_image,&pixel,q);
         }
         q+=GetPixelChannels(distort_image);
       }
@@ -2670,7 +2737,7 @@ if ( d.x == 0.5 && d.y == 0.5 ) {
             proceed;
 
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp critical (MagickCore_DistortImage)
+          #pragma omp critical (MagickCore_DistortImage)
 #endif
           proceed=SetImageProgress(image,DistortImageTag,progress++,
             image->rows);
@@ -2701,6 +2768,86 @@ if ( d.x == 0.5 && d.y == 0.5 ) {
 %                                                                             %
 %                                                                             %
 %                                                                             %
+%   R o t a t e I m a g e                                                     %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  RotateImage() creates a new image that is a rotated copy of an existing
+%  one.  Positive angles rotate counter-clockwise (right-hand rule), while
+%  negative angles rotate clockwise.  Rotated images are usually larger than
+%  the originals and have 'empty' triangular corners.  X axis.  Empty
+%  triangles left over from shearing the image are filled with the background
+%  color defined by member 'background_color' of the image.  RotateImage
+%  allocates the memory necessary for the new Image structure and returns a
+%  pointer to the new image.
+%
+%  The format of the RotateImage method is:
+%
+%      Image *RotateImage(const Image *image,const double degrees,
+%        ExceptionInfo *exception)
+%
+%  A description of each parameter follows.
+%
+%    o image: the image.
+%
+%    o degrees: Specifies the number of degrees to rotate the image.
+%
+%    o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *RotateImage(const Image *image,const double degrees,
+  ExceptionInfo *exception)
+{
+  Image
+    *distort_image,
+    *rotate_image;
+
+  double
+    angle;
+
+  PointInfo
+    shear;
+
+  size_t
+    rotations;
+
+  /*
+    Adjust rotation angle.
+  */
+  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);
+  angle=degrees;
+  while (angle < -45.0)
+    angle+=360.0;
+  for (rotations=0; angle > 45.0; rotations++)
+    angle-=90.0;
+  rotations%=4;
+  shear.x=(-tan((double) DegreesToRadians(angle)/2.0));
+  shear.y=sin((double) DegreesToRadians(angle));
+  if ((fabs(shear.x) < MagickEpsilon) && (fabs(shear.y) < MagickEpsilon))
+    return(IntegralRotateImage(image,rotations,exception));
+  distort_image=CloneImage(image,0,0,MagickTrue,exception);
+  if (distort_image == (Image *) NULL)
+    return((Image *) NULL);
+  (void) SetImageVirtualPixelMethod(distort_image,BackgroundVirtualPixelMethod,
+    exception);
+  rotate_image=DistortImage(distort_image,ScaleRotateTranslateDistortion,1,
+    &degrees,MagickTrue,exception);
+  distort_image=DestroyImage(distort_image);
+  return(rotate_image);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
 %   S p a r s e C o l o r I m a g e                                           %
 %                                                                             %
 %                                                                             %
@@ -2712,7 +2859,7 @@ if ( d.x == 0.5 && d.y == 0.5 ) {
 %
 %  The format of the SparseColorImage() method is:
 %
-%      Image *SparseColorImage(const Image *image,const ChannelType channel,
+%      Image *SparseColorImage(const Image *image,
 %        const SparseColorMethod method,const size_t number_arguments,
 %        const double *arguments,ExceptionInfo *exception)
 %
@@ -2720,9 +2867,6 @@ if ( d.x == 0.5 && d.y == 0.5 ) {
 %
 %    o image: the image to be filled in.
 %
-%    o channel: Specify which color values (in RGBKA sequence) are being set.
-%        This also determines the number of color_values in above.
-%
 %    o method: the method to fill in the gradient between the control points.
 %
 %        The methods used for SparseColor() are often simular to methods
@@ -2739,9 +2883,8 @@ if ( d.x == 0.5 && d.y == 0.5 ) {
 %
 */
 MagickExport Image *SparseColorImage(const Image *image,
-  const ChannelType channel,const SparseColorMethod method,
-  const size_t number_arguments,const double *arguments,
-  ExceptionInfo *exception)
+  const SparseColorMethod method,const size_t number_arguments,
+  const double *arguments,ExceptionInfo *exception)
 {
 #define SparseColorTag  "Distort/SparseColor"
 
@@ -2766,11 +2909,18 @@ MagickExport Image *SparseColorImage(const Image *image,
 
   /* Determine number of color values needed per control point */
   number_colors=0;
-  if ( channel & RedChannel     ) number_colors++;
-  if ( channel & GreenChannel   ) number_colors++;
-  if ( channel & BlueChannel    ) number_colors++;
-  if ( channel & AlphaChannel ) number_colors++;
-  if ( channel & BlackChannel   ) number_colors++;
+  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
+    number_colors++;
+  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
+    number_colors++;
+  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
+    number_colors++;
+  if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
+      (image->colorspace == CMYKColorspace))
+    number_colors++;
+  if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
+      (image->matte != MagickFalse))
+    number_colors++;
 
   /*
     Convert input arguments into mapping coefficients, this this case
@@ -2798,26 +2948,28 @@ MagickExport Image *SparseColorImage(const Image *image,
   }
 
   /* Verbose output */
-  if ( GetImageArtifact(image,"verbose") != (const char *) NULL ) {
+  if ( IfStringTrue(GetImageArtifact(image,"verbose")) ) {
 
     switch (sparse_method) {
       case BarycentricColorInterpolate:
       {
         register ssize_t x=0;
         (void) FormatLocaleFile(stderr, "Barycentric Sparse Color:\n");
-        if ( channel & RedChannel )
+        if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
           (void) FormatLocaleFile(stderr, "  -channel R -fx '%+lf*i %+lf*j %+lf' \\\n",
               coeff[x], coeff[x+1], coeff[x+2]),x+=3;
-        if ( channel & GreenChannel )
+        if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
           (void) FormatLocaleFile(stderr, "  -channel G -fx '%+lf*i %+lf*j %+lf' \\\n",
               coeff[x], coeff[x+1], coeff[x+2]),x+=3;
-        if ( channel & BlueChannel )
+        if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
           (void) FormatLocaleFile(stderr, "  -channel B -fx '%+lf*i %+lf*j %+lf' \\\n",
               coeff[x], coeff[x+1], coeff[x+2]),x+=3;
-        if ( channel & BlackChannel )
+        if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
+            (image->colorspace == CMYKColorspace))
           (void) FormatLocaleFile(stderr, "  -channel K -fx '%+lf*i %+lf*j %+lf' \\\n",
               coeff[x], coeff[x+1], coeff[x+2]),x+=3;
-        if ( channel & AlphaChannel )
+        if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
+            (image->matte != MagickFalse))
           (void) FormatLocaleFile(stderr, "  -channel A -fx '%+lf*i %+lf*j %+lf' \\\n",
               coeff[x], coeff[x+1], coeff[x+2]),x+=3;
         break;
@@ -2826,23 +2978,25 @@ MagickExport Image *SparseColorImage(const Image *image,
       {
         register ssize_t x=0;
         (void) FormatLocaleFile(stderr, "Bilinear Sparse Color\n");
-        if ( channel & RedChannel )
+        if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
           (void) FormatLocaleFile(stderr, "   -channel R -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
               coeff[ x ], coeff[x+1],
               coeff[x+2], coeff[x+3]),x+=4;
-        if ( channel & GreenChannel )
+        if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
           (void) FormatLocaleFile(stderr, "   -channel G -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
               coeff[ x ], coeff[x+1],
               coeff[x+2], coeff[x+3]),x+=4;
-        if ( channel & BlueChannel )
+        if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
           (void) FormatLocaleFile(stderr, "   -channel B -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
               coeff[ x ], coeff[x+1],
               coeff[x+2], coeff[x+3]),x+=4;
-        if ( channel & BlackChannel )
+        if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
+            (image->colorspace == CMYKColorspace))
           (void) FormatLocaleFile(stderr, "   -channel K -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
               coeff[ x ], coeff[x+1],
               coeff[x+2], coeff[x+3]),x+=4;
-        if ( channel & AlphaChannel )
+        if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
+            (image->matte != MagickFalse))
           (void) FormatLocaleFile(stderr, "   -channel A -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
               coeff[ x ], coeff[x+1],
               coeff[x+2], coeff[x+3]),x+=4;
@@ -2863,9 +3017,8 @@ MagickExport Image *SparseColorImage(const Image *image,
   sparse_image=CloneImage(image,0,0,MagickTrue,exception);
   if (sparse_image == (Image *) NULL)
     return((Image *) NULL);
-  if (SetImageStorageClass(sparse_image,DirectClass) == MagickFalse)
+  if (SetImageStorageClass(sparse_image,DirectClass,exception) == MagickFalse)
     { /* if image is ColorMapped - change it to DirectClass */
-      InheritException(exception,&image->exception);
       sparse_image=DestroyImage(sparse_image);
       return((Image *) NULL);
     }
@@ -2884,9 +3037,10 @@ MagickExport Image *SparseColorImage(const Image *image,
 
     status=MagickTrue;
     progress=0;
-    sparse_view=AcquireCacheView(sparse_image);
+    sparse_view=AcquireAuthenticCacheView(sparse_image,exception);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+    #pragma omp parallel for schedule(static,4) shared(progress,status) \
+      dynamic_number_threads(image,image->columns,image->rows,1)
 #endif
     for (j=0; j < (ssize_t) sparse_image->rows; j++)
     {
@@ -2904,7 +3058,7 @@ MagickExport Image *SparseColorImage(const Image *image,
 
       q=GetCacheViewAuthenticPixels(sparse_view,0,j,sparse_image->columns,
         1,exception);
-      if (q == (const Quantum *) NULL)
+      if (q == (Quantum *) NULL)
         {
           status=MagickFalse;
           continue;
@@ -2912,47 +3066,51 @@ MagickExport Image *SparseColorImage(const Image *image,
       GetPixelInfo(sparse_image,&pixel);
       for (i=0; i < (ssize_t) image->columns; i++)
       {
-        SetPixelInfo(image,q,&pixel);
+        GetPixelInfoPixel(image,q,&pixel);
         switch (sparse_method)
         {
           case BarycentricColorInterpolate:
           {
             register ssize_t x=0;
-            if ( channel & RedChannel )
+            if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
               pixel.red     = coeff[x]*i +coeff[x+1]*j
                               +coeff[x+2], x+=3;
-            if ( channel & GreenChannel )
+            if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
               pixel.green   = coeff[x]*i +coeff[x+1]*j
                               +coeff[x+2], x+=3;
-            if ( channel & BlueChannel )
+            if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
               pixel.blue    = coeff[x]*i +coeff[x+1]*j
                               +coeff[x+2], x+=3;
-            if ( channel & AlphaChannel )
-              pixel.alpha = coeff[x]*i +coeff[x+1]*j
-                              +coeff[x+2], x+=3;
-            if ( channel & BlackChannel )
+            if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
+                (image->colorspace == CMYKColorspace))
               pixel.black   = coeff[x]*i +coeff[x+1]*j
                               +coeff[x+2], x+=3;
+            if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
+                (image->matte != MagickFalse))
+              pixel.alpha = coeff[x]*i +coeff[x+1]*j
+                              +coeff[x+2], x+=3;
             break;
           }
           case BilinearColorInterpolate:
           {
             register ssize_t x=0;
-            if ( channel & RedChannel )
+            if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
               pixel.red     = coeff[x]*i     + coeff[x+1]*j +
                               coeff[x+2]*i*j + coeff[x+3], x+=4;
-            if ( channel & GreenChannel )
+            if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
               pixel.green   = coeff[x]*i     + coeff[x+1]*j +
                               coeff[x+2]*i*j + coeff[x+3], x+=4;
-            if ( channel & BlueChannel )
+            if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
               pixel.blue    = coeff[x]*i     + coeff[x+1]*j +
                               coeff[x+2]*i*j + coeff[x+3], x+=4;
-            if ( channel & AlphaChannel )
-              pixel.alpha = coeff[x]*i     + coeff[x+1]*j +
-                              coeff[x+2]*i*j + coeff[x+3], x+=4;
-            if ( channel & BlackChannel )
+            if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
+                (image->colorspace == CMYKColorspace))
               pixel.black   = coeff[x]*i     + coeff[x+1]*j +
                               coeff[x+2]*i*j + coeff[x+3], x+=4;
+            if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
+                (image->matte != MagickFalse))
+              pixel.alpha = coeff[x]*i     + coeff[x+1]*j +
+                              coeff[x+2]*i*j + coeff[x+3], x+=4;
             break;
           }
           case InverseColorInterpolate:
@@ -2963,11 +3121,18 @@ MagickExport Image *SparseColorImage(const Image *image,
             double
               denominator;
 
-            if ( channel & RedChannel     ) pixel.red     = 0.0;
-            if ( channel & GreenChannel   ) pixel.green   = 0.0;
-            if ( channel & BlueChannel    ) pixel.blue    = 0.0;
-            if ( channel & BlackChannel   ) pixel.black   = 0.0;
-            if ( channel & AlphaChannel ) pixel.alpha = 0.0;
+            if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
+              pixel.red=0.0;
+            if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
+              pixel.green=0.0;
+            if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
+              pixel.blue=0.0;
+            if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
+                (image->colorspace == CMYKColorspace))
+              pixel.black=0.0;
+            if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
+                (image->matte != MagickFalse))
+              pixel.alpha=0.0;
             denominator = 0.0;
             for(k=0; k<number_arguments; k+=2+number_colors) {
               register ssize_t x=(ssize_t) k+2;
@@ -2977,23 +3142,32 @@ MagickExport Image *SparseColorImage(const Image *image,
               if ( method == InverseColorInterpolate )
                 weight = sqrt(weight);  /* inverse, not inverse squared */
               weight = ( weight < 1.0 ) ? 1.0 : 1.0/weight;
-              if ( channel & RedChannel )
+              if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
                 pixel.red     += arguments[x++]*weight;
-              if ( channel & GreenChannel )
+              if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
                 pixel.green   += arguments[x++]*weight;
-              if ( channel & BlueChannel )
+              if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
                 pixel.blue    += arguments[x++]*weight;
-              if ( channel & AlphaChannel )
-                pixel.alpha += arguments[x++]*weight;
-              if ( channel & BlackChannel )
+              if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
+                  (image->colorspace == CMYKColorspace))
                 pixel.black   += arguments[x++]*weight;
+              if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
+                  (image->matte != MagickFalse))
+                pixel.alpha += arguments[x++]*weight;
               denominator += weight;
             }
-            if ( channel & RedChannel     ) pixel.red     /= denominator;
-            if ( channel & GreenChannel   ) pixel.green   /= denominator;
-            if ( channel & BlueChannel    ) pixel.blue    /= denominator;
-            if ( channel & AlphaChannel ) pixel.alpha /= denominator;
-            if ( channel & BlackChannel   ) pixel.black   /= denominator;
+            if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
+              pixel.red/=denominator;
+            if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
+              pixel.green/=denominator;
+            if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
+              pixel.blue/=denominator;
+            if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
+                (image->colorspace == CMYKColorspace))
+              pixel.black/=denominator;
+            if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
+                (image->matte != MagickFalse))
+              pixel.alpha/=denominator;
             break;
           }
           case VoronoiColorInterpolate:
@@ -3010,11 +3184,18 @@ MagickExport Image *SparseColorImage(const Image *image,
                 + ((double)j-arguments[k+1])*((double)j-arguments[k+1]);
               if ( distance < minimum ) {
                 register ssize_t x=(ssize_t) k+2;
-                if ( channel & RedChannel     ) pixel.red     = arguments[x++];
-                if ( channel & GreenChannel   ) pixel.green   = arguments[x++];
-                if ( channel & BlueChannel    ) pixel.blue    = arguments[x++];
-                if ( channel & AlphaChannel ) pixel.alpha = arguments[x++];
-                if ( channel & BlackChannel   ) pixel.black   = arguments[x++];
+                if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
+                  pixel.red=arguments[x++];
+                if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
+                  pixel.green=arguments[x++];
+                if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
+                  pixel.blue=arguments[x++];
+                if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
+                    (image->colorspace == CMYKColorspace))
+                  pixel.black=arguments[x++];
+                if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
+                    (image->matte != MagickFalse))
+                  pixel.alpha=arguments[x++];
                 minimum = distance;
               }
             }
@@ -3022,12 +3203,19 @@ MagickExport Image *SparseColorImage(const Image *image,
           }
         }
         /* set the color directly back into the source image */
-        if ( channel & RedChannel     ) pixel.red     *= QuantumRange;
-        if ( channel & GreenChannel   ) pixel.green   *= QuantumRange;
-        if ( channel & BlueChannel    ) pixel.blue    *= QuantumRange;
-        if ( channel & BlackChannel   ) pixel.black   *= QuantumRange;
-        if ( channel & AlphaChannel ) pixel.alpha *= QuantumRange;
-        SetPixelPixelInfo(sparse_image,&pixel,q);
+        if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
+          pixel.red*=QuantumRange;
+        if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
+          pixel.green*=QuantumRange;
+        if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
+          pixel.blue*=QuantumRange;
+        if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
+            (image->colorspace == CMYKColorspace))
+          pixel.black*=QuantumRange;
+        if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
+            (image->matte != MagickFalse))
+          pixel.alpha*=QuantumRange;
+        SetPixelInfoPixel(sparse_image,&pixel,q);
         q+=GetPixelChannels(sparse_image);
       }
       sync=SyncCacheViewAuthenticPixels(sparse_view,exception);
@@ -3039,7 +3227,7 @@ MagickExport Image *SparseColorImage(const Image *image,
             proceed;
 
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp critical (MagickCore_SparseColorImage)
+          #pragma omp critical (MagickCore_SparseColorImage)
 #endif
           proceed=SetImageProgress(image,SparseColorTag,progress++,image->rows);
           if (proceed == MagickFalse)