]> granicus.if.org Git - imagemagick/blobdiff - MagickCore/pixel.c
(no commit message)
[imagemagick] / MagickCore / pixel.c
index b95c79743dc3384f2b8e541c71cdabb09383c364..304a5dd6b40e14fa3a677ecc0a66001f5f57d0bc 100644 (file)
@@ -3567,16 +3567,455 @@ MagickExport MagickBooleanType ImportImagePixels(Image *image,
 %                                                                             %
 %                                                                             %
 %                                                                             %
+%   I n t e r p o l a t e P i x e l C h a n n e l                             %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  InterpolatePixelChannel() applies a pixel interpolation method between a
+%  floating point coordinate and the pixels surrounding that coordinate.  No
+%  pixel area resampling, or scaling of the result is performed.
+%
+%  The format of the InterpolatePixelChannel method is:
+%
+%      MagickBooleanType InterpolatePixelChannel(const Image *image,
+%        const PixelChannel channel,const CacheView *image_view,
+%        const InterpolatePixelMethod method,const double x,const double y,
+%        double *pixel,ExceptionInfo *exception)
+%
+%  A description of each parameter follows:
+%
+%    o image: the image.
+%
+%    o image_view: the image view.
+%
+%    o channel: the pixel channel to interpolate.
+%
+%    o method: the pixel color interpolation method.
+%
+%    o x,y: A double representing the current (x,y) position of the pixel.
+%
+%    o pixel: return the interpolated pixel here.
+%
+%    o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline double MagickMax(const MagickRealType x,const MagickRealType y)
+{
+  if (x > y)
+    return(x);
+  return(y);
+}
+
+static inline MagickRealType CubicWeightingFunction(const MagickRealType x)
+{
+  MagickRealType
+    alpha,
+    gamma;
+
+  alpha=MagickMax(x+2.0,0.0);
+  gamma=1.0*alpha*alpha*alpha;
+  alpha=MagickMax(x+1.0,0.0);
+  gamma-=4.0*alpha*alpha*alpha;
+  alpha=MagickMax(x+0.0,0.0);
+  gamma+=6.0*alpha*alpha*alpha;
+  alpha=MagickMax(x-1.0,0.0);
+  gamma-=4.0*alpha*alpha*alpha;
+  return(gamma/6.0);
+}
+
+static inline double MeshInterpolate(const PointInfo *delta,const double p,
+  const double x,const double y)
+{
+  return(delta->x*x+delta->y*y+(1.0-delta->x-delta->y)*p);
+}
+
+static inline ssize_t NearestNeighbor(const MagickRealType x)
+{
+  if (x >= 0.0)
+    return((ssize_t) (x+0.5));
+  return((ssize_t) (x-0.5));
+}
+
+MagickExport MagickBooleanType InterpolatePixelChannel(const Image *image,
+  const CacheView *image_view,const PixelChannel channel,
+  const InterpolatePixelMethod method,const double x,const double y,
+  double *pixel,ExceptionInfo *exception)
+{
+  MagickBooleanType
+    status;
+
+  MagickRealType
+    alpha[16],
+    gamma,
+    pixels[16];
+
+  PixelTrait
+    traits;
+
+  register const Quantum
+    *p;
+
+  register ssize_t
+    i;
+
+  ssize_t
+    x_offset,
+    y_offset;
+
+  assert(image != (Image *) NULL);
+  assert(image != (Image *) NULL);
+  assert(image->signature == MagickSignature);
+  assert(image_view != (CacheView *) NULL);
+  status=MagickTrue;
+  *pixel=0.0;
+  traits=GetPixelChannelMapTraits(image,channel);
+  x_offset=(ssize_t) floor(x);
+  y_offset=(ssize_t) floor(y);
+  switch (method == UndefinedInterpolatePixel ? image->interpolate : method)
+  {
+    case AverageInterpolatePixel:
+    {
+      p=GetCacheViewVirtualPixels(image_view,x_offset-1,y_offset-1,4,4,
+        exception);
+      if (p == (const Quantum *) NULL)
+        {
+          status=MagickFalse;
+          break;
+        }
+      if (((traits & BlendPixelTrait) == 0) ||
+          (GetPixelAlphaTraits(image) == UndefinedPixelTrait) ||
+          (image->matte == MagickFalse))
+        for (i=0; i < 16; i++)
+        {
+          alpha[i]=1.0;
+          pixels[i]=(MagickRealType) p[i*GetPixelChannels(image)+channel];
+        }
+      else
+        for (i=0; i < 16; i++)
+        {
+          alpha[i]=QuantumScale*GetPixelAlpha(image,p+i*
+            GetPixelChannels(image));
+          pixels[i]=alpha[i]*p[i*GetPixelChannels(image)+channel];
+        }
+      for (i=0; i < 16; i++)
+      {
+        gamma=1.0/(fabs((double) alpha[i]) <= MagickEpsilon ? 1.0 : alpha[i]);
+        *pixel+=gamma*0.0625*pixels[i];
+      }
+      break;
+    }
+    case BicubicInterpolatePixel:
+    {
+      MagickRealType
+        u[4],
+        v[4];
+
+      PointInfo
+        delta;
+
+      p=GetCacheViewVirtualPixels(image_view,x_offset-1,y_offset-1,4,4,
+        exception);
+      if (p == (const Quantum *) NULL)
+        {
+          status=MagickFalse;
+          break;
+        }
+      if (((traits & BlendPixelTrait) == 0) ||
+          (GetPixelAlphaTraits(image) == UndefinedPixelTrait) ||
+          (image->matte == MagickFalse))
+        for (i=0; i < 16; i++)
+        {
+          alpha[i]=1.0;
+          pixels[i]=(MagickRealType) p[i*GetPixelChannels(image)+channel];
+        }
+      else
+        for (i=0; i < 16; i++)
+        {
+          alpha[i]=QuantumScale*GetPixelAlpha(image,p+i*
+            GetPixelChannels(image));
+          pixels[i]=alpha[i]*p[i*GetPixelChannels(image)+channel];
+        }
+      delta.x=x-x_offset;
+      delta.y=y-y_offset;
+      for (i=0; i < 4; i++)
+      {
+        u[0]=(pixels[4*i+3]-pixels[4*i+2])-(pixels[4*i+0]-pixels[4*i+1]);
+        u[1]=(pixels[4*i+0]-pixels[4*i+1])-u[0];
+        u[2]=pixels[4*i+2]-pixels[4*i+0];
+        u[3]=pixels[4*i+1];
+        v[i]=(delta.x*delta.x*delta.x*u[0])+(delta.x*delta.x*u[1])+(delta.x*
+          u[2])+u[3];
+      }
+      u[0]=(v[3]-v[2])-(v[0]-v[1]);
+      u[1]=(v[0]-v[1])-u[0];
+      u[2]=v[2]-v[0];
+      u[3]=v[1];
+      *pixel=(delta.y*delta.y*delta.y*u[0])+(delta.y*delta.y*u[1])+(delta.y*
+        u[2])+u[3];
+      break;
+    }
+    case BilinearInterpolatePixel:
+    default:
+    {
+      PointInfo
+        delta,
+        epsilon;
+
+      p=GetCacheViewVirtualPixels(image_view,x_offset,y_offset,2,2,exception);
+      if (p == (const Quantum *) NULL)
+        {
+          status=MagickFalse;
+          break;
+        }
+      if (((traits & BlendPixelTrait) == 0) ||
+          (GetPixelAlphaTraits(image) == UndefinedPixelTrait) ||
+          (image->matte == MagickFalse))
+        for (i=0; i < 4; i++)
+        {
+          alpha[i]=1.0;
+          pixels[i]=(MagickRealType) p[i*GetPixelChannels(image)+channel];
+        }
+      else
+        for (i=0; i < 4; i++)
+        {
+          alpha[i]=QuantumScale*GetPixelAlpha(image,p+i*
+            GetPixelChannels(image));
+          pixels[i]=alpha[i]*p[i*GetPixelChannels(image)+channel];
+        }
+      delta.x=x-x_offset;
+      delta.y=y-y_offset;
+      epsilon.x=1.0-delta.x;
+      epsilon.y=1.0-delta.y;
+      gamma=((epsilon.y*(epsilon.x*alpha[0]+delta.x*alpha[1])+delta.y*
+        (epsilon.x*alpha[2]+delta.x*alpha[3])));
+      gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+      *pixel=gamma*(epsilon.y*(epsilon.x*pixels[0]+delta.x*pixels[1])+delta.y*
+        (epsilon.x*pixels[2]+delta.x*pixels[3]));
+      break;
+    }
+    case FilterInterpolatePixel:
+    {
+      CacheView
+        *filter_view;
+
+      Image
+        *excerpt_image,
+        *filter_image;
+
+      RectangleInfo
+        geometry;
+
+      geometry.width=4L;
+      geometry.height=4L;
+      geometry.x=x_offset-1;
+      geometry.y=y_offset-1;
+      excerpt_image=ExcerptImage(image,&geometry,exception);
+      if (excerpt_image == (Image *) NULL)
+        {
+          status=MagickFalse;
+          break;
+        }
+      filter_image=ResizeImage(excerpt_image,1,1,image->filter,image->blur,
+        exception);
+      excerpt_image=DestroyImage(excerpt_image);
+      if (filter_image == (Image *) NULL)
+        break;
+      filter_view=AcquireCacheView(filter_image);
+      p=GetCacheViewVirtualPixels(filter_view,0,0,1,1,exception);
+      if (p == (const Quantum *) NULL)
+        status=MagickFalse;
+      else
+        *pixel=(double) p[channel];
+      filter_view=DestroyCacheView(filter_view);
+      filter_image=DestroyImage(filter_image);
+      break;
+    }
+    case IntegerInterpolatePixel:
+    {
+      p=GetCacheViewVirtualPixels(image_view,x_offset,y_offset,1,1,exception);
+      if (p == (const Quantum *) NULL)
+        {
+          status=MagickFalse;
+          break;
+        }
+      *pixel=(double) p[channel];
+      break;
+    }
+    case NearestNeighborInterpolatePixel:
+    {
+      p=GetCacheViewVirtualPixels(image_view,NearestNeighbor(x),
+        NearestNeighbor(y),1,1,exception);
+      if (p == (const Quantum *) NULL)
+        {
+          status=MagickFalse;
+          break;
+        }
+      *pixel=(double) p[channel];
+      break;
+    }
+    case MeshInterpolatePixel:
+    {
+      PointInfo
+        delta,
+        luminance;
+
+      p=GetCacheViewVirtualPixels(image_view,x_offset,y_offset,2,2,exception);
+      if (p == (const Quantum *) NULL)
+        {
+          status=MagickFalse;
+          break;
+        }
+      if (((traits & BlendPixelTrait) == 0) ||
+          (GetPixelAlphaTraits(image) == UndefinedPixelTrait) ||
+          (image->matte == MagickFalse))
+        for (i=0; i < 4; i++)
+        {
+          alpha[i]=1.0;
+          pixels[i]=(MagickRealType) p[i*GetPixelChannels(image)+channel];
+        }
+      else
+        for (i=0; i < 4; i++)
+        {
+          alpha[i]=QuantumScale*GetPixelAlpha(image,p+i*
+            GetPixelChannels(image));
+          pixels[i]=alpha[i]*p[i*GetPixelChannels(image)+channel];
+        }
+      delta.x=x-x_offset;
+      delta.y=y-y_offset;
+      luminance.x=GetPixelLuminance(image,p)-(double)
+        GetPixelLuminance(image,p+3*GetPixelChannels(image));
+      luminance.y=GetPixelLuminance(image,p+1*GetPixelChannels(image))-(double)
+        GetPixelLuminance(image,p+2*GetPixelChannels(image));
+      if (fabs(luminance.x) < fabs(luminance.y))
+        {
+          /*
+            Diagonal 0-3 NW-SE.
+          */
+          if (delta.x <= delta.y)
+            {
+              /*
+                Bottom-left triangle (pixel: 2, diagonal: 0-3).
+              */
+              delta.y=1.0-delta.y;
+              gamma=MeshInterpolate(&delta,alpha[2],alpha[3],alpha[0]);
+              gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+              *pixel=gamma*MeshInterpolate(&delta,pixels[2],pixels[3],
+                pixels[0]);
+            }
+          else
+            {
+              /*
+                Top-right triangle (pixel: 1, diagonal: 0-3).
+              */
+              delta.x=1.0-delta.x;
+              gamma=MeshInterpolate(&delta,alpha[1],alpha[0],alpha[3]);
+              gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+              *pixel=gamma*MeshInterpolate(&delta,pixels[1],pixels[0],
+                pixels[3]);
+            }
+        }
+      else
+        {
+          /*
+            Diagonal 1-2 NE-SW.
+          */
+          if (delta.x <= (1.0-delta.y))
+            {
+              /*
+                Top-left triangle (pixel: 0, diagonal: 1-2).
+              */
+              gamma=MeshInterpolate(&delta,alpha[0],alpha[1],alpha[2]);
+              gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+              *pixel=gamma*MeshInterpolate(&delta,pixels[0],pixels[1],
+                pixels[2]);
+            }
+          else
+            {
+              /*
+                Bottom-right triangle (pixel: 3, diagonal: 1-2).
+              */
+              delta.x=1.0-delta.x;
+              delta.y=1.0-delta.y;
+              gamma=MeshInterpolate(&delta,alpha[3],alpha[2],alpha[1]);
+              gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+              *pixel=gamma*MeshInterpolate(&delta,pixels[3],pixels[2],
+                pixels[1]);
+            }
+        }
+      break;
+    }
+    case SplineInterpolatePixel:
+    {
+      MagickRealType
+        dx,
+        dy;
+
+      PointInfo
+        delta;
+
+      ssize_t
+        j,
+        n;
+
+      p=GetCacheViewVirtualPixels(image_view,x_offset-1,y_offset-1,4,4,
+        exception);
+      if (p == (const Quantum *) NULL)
+        {
+          status=MagickFalse;
+          break;
+        }
+      if (((traits & BlendPixelTrait) == 0) ||
+          (GetPixelAlphaTraits(image) == UndefinedPixelTrait) ||
+          (image->matte == MagickFalse))
+        for (i=0; i < 16; i++)
+        {
+          alpha[i]=1.0;
+          pixels[i]=(MagickRealType) p[i*GetPixelChannels(image)+channel];
+        }
+      else
+        for (i=0; i < 16; i++)
+        {
+          alpha[i]=QuantumScale*GetPixelAlpha(image,p+i*
+            GetPixelChannels(image));
+          pixels[i]=alpha[i]*p[i*GetPixelChannels(image)+channel];
+        }
+      delta.x=x-x_offset;
+      delta.y=y-y_offset;
+      n=0;
+      for (i=(-1); i < 3L; i++)
+      {
+        dy=CubicWeightingFunction((MagickRealType) i-delta.y);
+        for (j=(-1); j < 3L; j++)
+        {
+          dx=CubicWeightingFunction(delta.x-(MagickRealType) j);
+          gamma=1.0/(fabs((double) alpha[n]) <= MagickEpsilon ? 1.0 : alpha[n]);
+          *pixel+=gamma*dx*dy*pixels[n];
+          n++;
+        }
+      }
+      break;
+    }
+  }
+  return(status);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
 %   I n t e r p o l a t e P i x e l I n f o                                   %
 %                                                                             %
 %                                                                             %
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  InterpolatePixelInfo() applies bi-linear or tri-linear interpolation
-%  between a floating point coordinate and the pixels surrounding that
-%  coordinate.  No pixel area resampling, or scaling of the result is
-%  performed.
+%  InterpolatePixelInfo() applies a pixel interpolation method between a
+%  floating point coordinate and the pixels surrounding that coordinate.  No
+%  pixel area resampling, or scaling of the result is performed.
 %
 %  The format of the InterpolatePixelInfo method is:
 %
@@ -3667,43 +4106,6 @@ static void BicubicInterpolate(const PixelInfo *pixels,const double dx,
     }
 }
 
-static inline double MagickMax(const MagickRealType x,const MagickRealType y)
-{
-  if (x > y)
-    return(x);
-  return(y);
-}
-
-static inline MagickRealType CubicWeightingFunction(const MagickRealType x)
-{
-  MagickRealType
-    alpha,
-    gamma;
-
-  alpha=MagickMax(x+2.0,0.0);
-  gamma=1.0*alpha*alpha*alpha;
-  alpha=MagickMax(x+1.0,0.0);
-  gamma-=4.0*alpha*alpha*alpha;
-  alpha=MagickMax(x+0.0,0.0);
-  gamma+=6.0*alpha*alpha*alpha;
-  alpha=MagickMax(x-1.0,0.0);
-  gamma-=4.0*alpha*alpha*alpha;
-  return(gamma/6.0);
-}
-
-static inline double MeshInterpolate(const PointInfo *delta,const double p,
-  const double x,const double y)
-{
-  return(delta->x*x+delta->y*y+(1.0-delta->x-delta->y)*p);
-}
-
-static inline ssize_t NearestNeighbor(const MagickRealType x)
-{
-  if (x >= 0.0)
-    return((ssize_t) (x+0.5));
-  return((ssize_t) (x-0.5));
-}
-
 MagickExport MagickBooleanType InterpolatePixelInfo(const Image *image,
   const CacheView *image_view,const InterpolatePixelMethod method,
   const double x,const double y,PixelInfo *pixel,ExceptionInfo *exception)
@@ -3864,6 +4266,8 @@ MagickExport MagickBooleanType InterpolatePixelInfo(const Image *image,
         pixel->black=gamma*(epsilon.y*(epsilon.x*pixels[0].black+delta.x*
           pixels[1].black)+delta.y*(epsilon.x*pixels[2].black+delta.x*
           pixels[3].black));
+      gamma=((epsilon.y*(epsilon.x+delta.x)+delta.y*(epsilon.x+delta.x)));
+      gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
       pixel->alpha=(epsilon.y*(epsilon.x*pixels[0].alpha+delta.x*
         pixels[1].alpha)+delta.y*(epsilon.x*pixels[2].alpha+delta.x*
         pixels[3].alpha));
@@ -3921,23 +4325,22 @@ MagickExport MagickBooleanType InterpolatePixelInfo(const Image *image,
         delta,
         luminance;
 
-      p=GetCacheViewVirtualPixels(image_view,x_offset,y_offset,2,2,
-        exception);
+      p=GetCacheViewVirtualPixels(image_view,x_offset,y_offset,2,2,exception);
       if (p == (const Quantum *) NULL)
         {
           status=MagickFalse;
           break;
         }
+      delta.x=x-x_offset;
+      delta.y=y-y_offset;
+      luminance.x=GetPixelLuminance(image,p)-(double)
+        GetPixelLuminance(image,p+3*GetPixelChannels(image));
+      luminance.y=GetPixelLuminance(image,p+1*GetPixelChannels(image))-(double)
+        GetPixelLuminance(image,p+2*GetPixelChannels(image));
       AlphaBlendPixelInfo(image,p,pixels+0,alpha+0);
       AlphaBlendPixelInfo(image,p+1*GetPixelChannels(image),pixels+1,alpha+1);
       AlphaBlendPixelInfo(image,p+2*GetPixelChannels(image),pixels+2,alpha+2);
       AlphaBlendPixelInfo(image,p+3*GetPixelChannels(image),pixels+3,alpha+3);
-      delta.x=x-x_offset;
-      delta.y=y-y_offset;
-      luminance.x=GetPixelInfoLuminance(pixels+0)-(double)
-        GetPixelInfoLuminance(pixels+3);
-      luminance.y=GetPixelInfoLuminance(pixels+1)-(double)
-        GetPixelInfoLuminance(pixels+2);
       if (fabs(luminance.x) < fabs(luminance.y))
         {
           /*
@@ -3946,7 +4349,7 @@ MagickExport MagickBooleanType InterpolatePixelInfo(const Image *image,
           if (delta.x <= delta.y)
             {
               /*
-                Bottom-left triangle  (pixel:2, diagonal: 0-3).
+                Bottom-left triangle (pixel: 2, diagonal: 0-3).
               */
               delta.y=1.0-delta.y;
               gamma=MeshInterpolate(&delta,alpha[2],alpha[3],alpha[0]);
@@ -3960,13 +4363,14 @@ MagickExport MagickBooleanType InterpolatePixelInfo(const Image *image,
               if (image->colorspace == CMYKColorspace)
                 pixel->black=gamma*MeshInterpolate(&delta,pixels[2].black,
                   pixels[3].black,pixels[0].black);
+              gamma=MeshInterpolate(&delta,1.0,1.0,1.0);
               pixel->alpha=gamma*MeshInterpolate(&delta,pixels[2].alpha,
                 pixels[3].alpha,pixels[0].alpha);
             }
           else
             {
               /*
-                Top-right triangle (pixel:1, diagonal: 0-3).
+                Top-right triangle (pixel:1 , diagonal: 0-3).
               */
               delta.x=1.0-delta.x;
               gamma=MeshInterpolate(&delta,alpha[1],alpha[0],alpha[3]);
@@ -3980,6 +4384,7 @@ MagickExport MagickBooleanType InterpolatePixelInfo(const Image *image,
               if (image->colorspace == CMYKColorspace)
                 pixel->black=gamma*MeshInterpolate(&delta,pixels[1].black,
                   pixels[0].black,pixels[3].black);
+              gamma=MeshInterpolate(&delta,1.0,1.0,1.0);
               pixel->alpha=gamma*MeshInterpolate(&delta,pixels[1].alpha,
                 pixels[0].alpha,pixels[3].alpha);
             }
@@ -3992,7 +4397,7 @@ MagickExport MagickBooleanType InterpolatePixelInfo(const Image *image,
           if (delta.x <= (1.0-delta.y))
             {
               /*
-                Top-left triangle (pixel 0, diagonal: 1-2).
+                Top-left triangle (pixel: 0, diagonal: 1-2).
               */
               gamma=MeshInterpolate(&delta,alpha[0],alpha[1],alpha[2]);
               gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
@@ -4005,6 +4410,7 @@ MagickExport MagickBooleanType InterpolatePixelInfo(const Image *image,
               if (image->colorspace == CMYKColorspace)
                 pixel->black=gamma*MeshInterpolate(&delta,pixels[0].black,
                   pixels[1].black,pixels[2].black);
+              gamma=MeshInterpolate(&delta,1.0,1.0,1.0);
               pixel->alpha=gamma*MeshInterpolate(&delta,pixels[0].alpha,
                 pixels[1].alpha,pixels[2].alpha);
             }
@@ -4026,6 +4432,7 @@ MagickExport MagickBooleanType InterpolatePixelInfo(const Image *image,
               if (image->colorspace == CMYKColorspace)
                 pixel->black=gamma*MeshInterpolate(&delta,pixels[3].black,
                   pixels[2].black,pixels[1].black);
+              gamma=MeshInterpolate(&delta,1.0,1.0,1.0);
               pixel->alpha=gamma*MeshInterpolate(&delta,pixels[3].alpha,
                 pixels[2].alpha,pixels[1].alpha);
             }