resample.c

Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                                                                             %
00006 %           RRRR    EEEEE   SSSSS   AAA   M   M  PPPP   L      EEEEE          %
00007 %           R   R   E       SS     A   A  MM MM  P   P  L      E              %
00008 %           RRRR    EEE      SSS   AAAAA  M M M  PPPP   L      EEE            %
00009 %           R R     E          SS  A   A  M   M  P      L      E              %
00010 %           R  R    EEEEE   SSSSS  A   A  M   M  P      LLLLL  EEEEE          %
00011 %                                                                             %
00012 %                                                                             %
00013 %                      MagickCore Pixel Resampling Methods                    %
00014 %                                                                             %
00015 %                              Software Design                                %
00016 %                                John Cristy                                  %
00017 %                              Anthony Thyssen                                %
00018 %                                August 2007                                  %
00019 %                                                                             %
00020 %                                                                             %
00021 %  Copyright 1999-2008 ImageMagick Studio LLC, a non-profit organization      %
00022 %  dedicated to making software imaging solutions freely available.           %
00023 %                                                                             %
00024 %  You may not use this file except in compliance with the License.  You may  %
00025 %  obtain a copy of the License at                                            %
00026 %                                                                             %
00027 %    http://www.imagemagick.org/script/license.php                            %
00028 %                                                                             %
00029 %  Unless required by applicable law or agreed to in writing, software        %
00030 %  distributed under the License is distributed on an "AS IS" BASIS,          %
00031 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
00032 %  See the License for the specific language governing permissions and        %
00033 %  limitations under the License.                                             %
00034 %                                                                             %
00035 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00036 %
00037 %
00038 */
00039 
00040 /*
00041   Include declarations.
00042 */
00043 #include "magick/studio.h"
00044 #include "magick/artifact.h"
00045 #include "magick/color-private.h"
00046 #include "magick/cache.h"
00047 #include "magick/draw.h"
00048 #include "magick/exception-private.h"
00049 #include "magick/gem.h"
00050 #include "magick/image.h"
00051 #include "magick/image-private.h"
00052 #include "magick/log.h"
00053 #include "magick/memory_.h"
00054 #include "magick/pixel-private.h"
00055 #include "magick/quantum.h"
00056 #include "magick/random_.h"
00057 #include "magick/resample.h"
00058 #include "magick/resize.h"
00059 #include "magick/resize-private.h"
00060 #include "magick/transform.h"
00061 #include "magick/signature-private.h"
00062 /*
00063   Typedef declarations.
00064 */
00065 #define WLUT_WIDTH 1024
00066 struct _ResampleFilter
00067 {
00068   Image
00069     *image;
00070 
00071   ViewInfo
00072     *view;
00073 
00074   ExceptionInfo
00075     *exception;
00076 
00077   MagickBooleanType
00078     debug;
00079 
00080   /* Information about image being resampled */
00081   long
00082     image_area;
00083 
00084   InterpolatePixelMethod
00085     interpolate;
00086 
00087   VirtualPixelMethod
00088     virtual_pixel;
00089 
00090   FilterTypes
00091     filter;
00092 
00093   /* processing settings needed */
00094   MagickBooleanType
00095     limit_reached,
00096     do_interpolate,
00097     average_defined;
00098 
00099   MagickPixelPacket
00100     average_pixel;
00101 
00102   /* current ellipitical area being resampled around center point */
00103   double
00104     A, B, C,
00105     sqrtA, sqrtC, sqrtU, slope;
00106 
00107   /* LUT of weights for filtered average in elliptical area */
00108   double
00109     filter_lut[WLUT_WIDTH],
00110     support;
00111 
00112   unsigned long
00113     signature;
00114 };
00115 
00116 /*
00117 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00118 %                                                                             %
00119 %                                                                             %
00120 %                                                                             %
00121 %   A c q u i r e R e s a m p l e I n f o                                     %
00122 %                                                                             %
00123 %                                                                             %
00124 %                                                                             %
00125 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00126 %
00127 %  AcquireResampleFilter() initializes the information resample needs do to a
00128 %  scaled lookup of a color from an image, using area sampling.
00129 %
00130 %  The algorithm is based on a Elliptical Weighted Average, where the pixels
00131 %  found in a large elliptical area is averaged together according to a
00132 %  weighting (filter) function.  For more details see "Fundamentals of Texture
00133 %  Mapping and Image Warping" a master's thesis by Paul.S.Heckbert, June 17,
00134 %  1989.  Available for free from, http://www.cs.cmu.edu/~ph/
00135 %
00136 %  As EWA resampling (or any sort of resampling) can require a lot of
00137 %  calculations to produce a distorted scaling of the source image for each
00138 %  output pixel, the ResampleFilter structure generated holds that information
00139 %  between individual image resampling.
00140 %
00141 %  This function will make the appropriate AcquireCacheView() calls
00142 %  to view the image, calling functions do not need to open a cache view.
00143 %
00144 %  Usage Example...
00145 %      resample_filter=AcquireResampleFilter(image,exception);
00146 %      for (y=0; y < (long) image->rows; y++) {
00147 %        for (x=0; x < (long) image->columns; x++) {
00148 %          X= ....;   Y= ....;
00149 %          ScaleResampleFilter(resample_filter, ... scaling vectors ...);
00150 %          (void) ResamplePixelColor(resample_filter,X,Y,&pixel);
00151 %          ... assign resampled pixel value ...
00152 %        }
00153 %      }
00154 %      DestroyResampleFilter(resample_filter);
00155 %
00156 %  The format of the AcquireResampleFilter method is:
00157 %
00158 %     ResampleFilter *AcquireResampleFilter(const Image *image,
00159 %       ExceptionInfo *exception)
00160 %
00161 %  A description of each parameter follows:
00162 %
00163 %    o image: the image.
00164 %
00165 %    o exception: return any errors or warnings in this structure.
00166 %
00167 */
00168 MagickExport ResampleFilter *AcquireResampleFilter(const Image *image,
00169   ExceptionInfo *exception)
00170 {
00171   register ResampleFilter
00172     *resample_filter;
00173 
00174   assert(image != (Image *) NULL);
00175   assert(image->signature == MagickSignature);
00176   if (image->debug != MagickFalse)
00177     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00178   assert(exception != (ExceptionInfo *) NULL);
00179   assert(exception->signature == MagickSignature);
00180 
00181   resample_filter=(ResampleFilter *) AcquireMagickMemory(
00182     sizeof(*resample_filter));
00183   if (resample_filter == (ResampleFilter *) NULL)
00184     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00185   (void) ResetMagickMemory(resample_filter,0,sizeof(*resample_filter));
00186 
00187   resample_filter->image=ReferenceImage((Image *) image);
00188   resample_filter->view=AcquireCacheView(resample_filter->image);
00189   resample_filter->exception=exception;
00190 
00191   resample_filter->debug=IsEventLogging();
00192   resample_filter->signature=MagickSignature;
00193 
00194   resample_filter->image_area = (long) resample_filter->image->columns *
00195     resample_filter->image->rows;
00196   resample_filter->average_defined = MagickFalse;
00197 
00198   /* initialise the resampling filter settings */
00199   SetResampleFilter(resample_filter, resample_filter->image->filter,
00200     resample_filter->image->blur);
00201   resample_filter->interpolate = resample_filter->image->interpolate;
00202   resample_filter->virtual_pixel=GetImageVirtualPixelMethod(image);
00203 
00204   /* init scale to a default of a unit circle */
00205   ScaleResampleFilter(resample_filter, 1.0, 0.0, 0.0, 1.0);
00206 
00207   return(resample_filter);
00208 }
00209 
00210 /*
00211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00212 %                                                                             %
00213 %                                                                             %
00214 %                                                                             %
00215 %   D e s t r o y R e s a m p l e I n f o                                     %
00216 %                                                                             %
00217 %                                                                             %
00218 %                                                                             %
00219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00220 %
00221 %  DestroyResampleFilter() finalizes and cleans up the resampling
00222 %  resample_filter as returned by AcquireResampleFilter(), freeing any memory
00223 %  or other information as needed.
00224 %
00225 %  The format of the DestroyResampleFilter method is:
00226 %
00227 %      ResampleFilter *DestroyResampleFilter(ResampleFilter *resample_filter)
00228 %
00229 %  A description of each parameter follows:
00230 %
00231 %    o resample_filter: resampling information structure
00232 %
00233 */
00234 MagickExport ResampleFilter *DestroyResampleFilter(
00235   ResampleFilter *resample_filter)
00236 {
00237   assert(resample_filter != (ResampleFilter *) NULL);
00238   assert(resample_filter->signature == MagickSignature);
00239   assert(resample_filter->image != (Image *) NULL);
00240   if (resample_filter->debug != MagickFalse)
00241     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
00242       resample_filter->image->filename);
00243   resample_filter->view=DestroyCacheView(resample_filter->view);
00244   resample_filter->image=DestroyImage(resample_filter->image);
00245   resample_filter->signature=(~MagickSignature);
00246   resample_filter=(ResampleFilter *) RelinquishMagickMemory(resample_filter);
00247   return(resample_filter);
00248 }
00249 
00250 /*
00251 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00252 %                                                                             %
00253 %                                                                             %
00254 %                                                                             %
00255 %   I n t e r p o l a t e R e s a m p l e F i l t e r                         %
00256 %                                                                             %
00257 %                                                                             %
00258 %                                                                             %
00259 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00260 %
00261 %  InterpolateResampleFilter() applies bi-linear or tri-linear interpolation
00262 %  between a floating point coordinate and the pixels surrounding that
00263 %  coordinate.  No pixel area resampling, or scaling of the result is
00264 %  performed.
00265 %
00266 %  The format of the InterpolateResampleFilter method is:
00267 %
00268 %      MagickBooleanType InterpolateResampleFilter(
00269 %        ResampleInfo *resample_filter,const InterpolatePixelMethod method,
00270 %        const double x,const double y,MagickPixelPacket *pixel)
00271 %
00272 %  A description of each parameter follows:
00273 %
00274 %    o resample_filter: the resample filter.
00275 %
00276 %    o method: the pixel clor interpolation method.
00277 %
00278 %    o x,y: A double representing the current (x,y) position of the pixel.
00279 %
00280 %    o pixel: return the interpolated pixel here.
00281 %
00282 */
00283 
00284 static inline double MagickMax(const double x,const double y)
00285 {
00286   if (x > y)
00287     return(x);
00288   return(y);
00289 }
00290 
00291 static void BicubicInterpolate(const MagickPixelPacket *pixels,const double dx,
00292   MagickPixelPacket *pixel)
00293 {
00294   MagickRealType
00295     dx2,
00296     p,
00297     q,
00298     r,
00299     s;
00300 
00301   dx2=dx*dx;
00302   p=(pixels[3].red-pixels[2].red)-(pixels[0].red-pixels[1].red);
00303   q=(pixels[0].red-pixels[1].red)-p;
00304   r=pixels[2].red-pixels[0].red;
00305   s=pixels[1].red;
00306   pixel->red=(dx*dx2*p)+(dx2*q)+(dx*r)+s;
00307   p=(pixels[3].green-pixels[2].green)-(pixels[0].green-pixels[1].green);
00308   q=(pixels[0].green-pixels[1].green)-p;
00309   r=pixels[2].green-pixels[0].green;
00310   s=pixels[1].green;
00311   pixel->green=(dx*dx2*p)+(dx2*q)+(dx*r)+s;
00312   p=(pixels[3].blue-pixels[2].blue)-(pixels[0].blue-pixels[1].blue);
00313   q=(pixels[0].blue-pixels[1].blue)-p;
00314   r=pixels[2].blue-pixels[0].blue;
00315   s=pixels[1].blue;
00316   pixel->blue=(dx*dx2*p)+(dx2*q)+(dx*r)+s;
00317   p=(pixels[3].opacity-pixels[2].opacity)-(pixels[0].opacity-pixels[1].opacity);
00318   q=(pixels[0].opacity-pixels[1].opacity)-p;
00319   r=pixels[2].opacity-pixels[0].opacity;
00320   s=pixels[1].opacity;
00321   pixel->opacity=(dx*dx2*p)+(dx2*q)+(dx*r)+s;
00322   if (pixel->colorspace == CMYKColorspace)
00323     {
00324       p=(pixels[3].index-pixels[2].index)-(pixels[0].index-pixels[1].index);
00325       q=(pixels[0].index-pixels[1].index)-p;
00326       r=pixels[2].index-pixels[0].index;
00327       s=pixels[1].index;
00328       pixel->index=(dx*dx2*p)+(dx2*q)+(dx*r)+s;
00329     }
00330 }
00331 
00332 static inline MagickRealType CubicWeightingFunction(const MagickRealType x)
00333 {
00334   MagickRealType
00335     alpha,
00336     gamma;
00337 
00338   alpha=MagickMax(x+2.0,0.0);
00339   gamma=1.0*alpha*alpha*alpha;
00340   alpha=MagickMax(x+1.0,0.0);
00341   gamma-=4.0*alpha*alpha*alpha;
00342   alpha=MagickMax(x+0.0,0.0);
00343   gamma+=6.0*alpha*alpha*alpha;
00344   alpha=MagickMax(x-1.0,0.0);
00345   gamma-=4.0*alpha*alpha*alpha;
00346   return(gamma/6.0);
00347 }
00348 
00349 static inline double MeshInterpolate(const PointInfo *delta,const double p,
00350   const double x,const double y)
00351 {
00352   return(delta->x*x+delta->y*y+(1.0-delta->x-delta->y)*p);
00353 }
00354 
00355 static inline long NearestNeighbor(MagickRealType x)
00356 {
00357   if (x >= 0.0)
00358     return((long) (x+0.5));
00359   return((long) (x-0.5));
00360 }
00361 
00362 static MagickBooleanType InterpolateResampleFilter(
00363   ResampleFilter *resample_filter,const InterpolatePixelMethod method,
00364   const double x,const double y,MagickPixelPacket *pixel)
00365 {
00366   MagickBooleanType
00367     status;
00368 
00369   register const IndexPacket
00370     *indexes;
00371 
00372   register const PixelPacket
00373     *p;
00374 
00375   register long
00376     i;
00377 
00378   assert(resample_filter != (ResampleFilter *) NULL);
00379   assert(resample_filter->signature == MagickSignature);
00380   status=MagickTrue;
00381   switch (method)
00382   {
00383     case AverageInterpolatePixel:
00384     {
00385       MagickPixelPacket
00386         pixels[16];
00387 
00388       MagickRealType
00389         alpha[16],
00390         gamma;
00391 
00392       GetMagickPixelPacket(resample_filter->image,pixel);
00393       p=GetCacheViewVirtualPixels(resample_filter->view,(long) floor(x)-1,(long)
00394         floor(y)-1,4,4,resample_filter->exception);
00395       if (p == (const PixelPacket *) NULL)
00396         {
00397           status=MagickFalse;
00398           break;
00399         }
00400       indexes=GetCacheViewVirtualIndexQueue(resample_filter->view);
00401       for (i=0; i < 16L; i++)
00402       {
00403         GetMagickPixelPacket(resample_filter->image,pixels+i);
00404         SetMagickPixelPacket(resample_filter->image,p,indexes+i,pixels+i);
00405         alpha[i]=1.0;
00406         if (resample_filter->image->matte != MagickFalse)
00407           {
00408             alpha[i]=QuantumScale*((MagickRealType) QuantumRange-p->opacity);
00409             pixels[i].red*=alpha[i];
00410             pixels[i].green*=alpha[i];
00411             pixels[i].blue*=alpha[i];
00412             if (resample_filter->image->colorspace == CMYKColorspace)
00413               pixels[i].index*=alpha[i];
00414           }
00415         gamma=alpha[i];
00416         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00417         pixel->red+=gamma*0.0625*pixels[i].red;
00418         pixel->green+=gamma*0.0625*pixels[i].green;
00419         pixel->blue+=gamma*0.0625*pixels[i].blue;
00420         pixel->opacity+=0.0625*pixels[i].opacity;
00421         if (resample_filter->image->colorspace == CMYKColorspace)
00422           pixel->index+=gamma*0.0625*pixels[i].index;
00423         p++;
00424       }
00425       break;
00426     }
00427     case BicubicInterpolatePixel:
00428     {
00429       MagickPixelPacket
00430         pixels[16],
00431         u[4];
00432 
00433       MagickRealType
00434         alpha[16];
00435 
00436       PointInfo
00437         delta;
00438 
00439       GetMagickPixelPacket(resample_filter->image,pixel);
00440       p=GetCacheViewVirtualPixels(resample_filter->view,(long) floor(x)-1,(long)
00441         floor(y)-1,4,4,resample_filter->exception);
00442       if (p == (const PixelPacket *) NULL)
00443         {
00444           status=MagickFalse;
00445           break;
00446         }
00447       indexes=GetCacheViewVirtualIndexQueue(resample_filter->view);
00448       for (i=0; i < 16L; i++)
00449       {
00450         GetMagickPixelPacket(resample_filter->image,pixels+i);
00451         SetMagickPixelPacket(resample_filter->image,p,indexes+i,pixels+i);
00452         alpha[i]=1.0;
00453         if (resample_filter->image->matte != MagickFalse)
00454           {
00455             alpha[i]=QuantumScale*((MagickRealType) QuantumRange-p->opacity);
00456             pixels[i].red*=alpha[i];
00457             pixels[i].green*=alpha[i];
00458             pixels[i].blue*=alpha[i];
00459             if (resample_filter->image->colorspace == CMYKColorspace)
00460               pixels[i].index*=alpha[i];
00461           }
00462         p++;
00463       }
00464       delta.x=x-floor(x);
00465       for (i=0; i < 4L; i++)
00466         BicubicInterpolate(pixels+4*i,delta.x,u+i);
00467       delta.y=y-floor(y);
00468       BicubicInterpolate(u,delta.y,pixel);
00469       break;
00470     }
00471     case BilinearInterpolatePixel:
00472     default:
00473     {
00474       MagickPixelPacket
00475         pixels[4];
00476 
00477       MagickRealType
00478         alpha[4],
00479         gamma;
00480 
00481       PointInfo
00482         delta;
00483 
00484       p=GetCacheViewVirtualPixels(resample_filter->view,(long) floor(x),(long)
00485         floor(y),2,2,resample_filter->exception);
00486       if (p == (const PixelPacket *) NULL)
00487         {
00488           status=MagickFalse;
00489           break;
00490         }
00491       indexes=GetCacheViewVirtualIndexQueue(resample_filter->view);
00492       for (i=0; i < 4L; i++)
00493       {
00494         pixels[i].red=(MagickRealType) p[i].red;
00495         pixels[i].green=(MagickRealType) p[i].green;
00496         pixels[i].blue=(MagickRealType) p[i].blue;
00497         pixels[i].opacity=(MagickRealType) p[i].opacity;
00498         alpha[i]=1.0;
00499       }
00500       if (resample_filter->image->matte != MagickFalse)
00501         for (i=0; i < 4L; i++)
00502         {
00503           alpha[i]=QuantumScale*((MagickRealType) QuantumRange-p[i].opacity);
00504           pixels[i].red*=alpha[i];
00505           pixels[i].green*=alpha[i];
00506           pixels[i].blue*=alpha[i];
00507         }
00508       if (indexes != (IndexPacket *) NULL)
00509         for (i=0; i < 4L; i++)
00510         {
00511           pixels[i].index=(MagickRealType) indexes[i];
00512           if (resample_filter->image->colorspace == CMYKColorspace)
00513             pixels[i].index*=alpha[i];
00514         }
00515       delta.x=x-floor(x);
00516       delta.y=y-floor(y);
00517       gamma=(((1.0-delta.y)*((1.0-delta.x)*alpha[0]+delta.x*alpha[1])+delta.y*
00518         ((1.0-delta.x)*alpha[2]+delta.x*alpha[3])));
00519       gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00520       pixel->red=gamma*((1.0-delta.y)*((1.0-delta.x)*pixels[0].red+delta.x*
00521         pixels[1].red)+delta.y*((1.0-delta.x)*pixels[2].red+delta.x*
00522         pixels[3].red));
00523       pixel->green=gamma*((1.0-delta.y)*((1.0-delta.x)*pixels[0].green+delta.x*
00524         pixels[1].green)+delta.y*((1.0-delta.x)*pixels[2].green+
00525         delta.x*pixels[3].green));
00526       pixel->blue=gamma*((1.0-delta.y)*((1.0-delta.x)*pixels[0].blue+delta.x*
00527         pixels[1].blue)+delta.y*((1.0-delta.x)*pixels[2].blue+delta.x*
00528         pixels[3].blue));
00529       pixel->opacity=((1.0-delta.y)*((1.0-delta.x)*pixels[0].opacity+delta.x*
00530         pixels[1].opacity)+delta.y*((1.0-delta.x)*pixels[2].opacity+delta.x*
00531         pixels[3].opacity));
00532       if (resample_filter->image->colorspace == CMYKColorspace)
00533         pixel->index=gamma*((1.0-delta.y)*((1.0-delta.x)*pixels[0].index+
00534           delta.x*pixels[1].index)+delta.y*((1.0-delta.x)*pixels[2].index+
00535           delta.x*pixels[3].index));
00536       break;
00537     }
00538     case FilterInterpolatePixel:
00539     {
00540       Image
00541         *excerpt_image,
00542         *filter_image;
00543 
00544       MagickPixelPacket
00545         pixels[1];
00546 
00547       RectangleInfo
00548         geometry;
00549 
00550       ViewInfo
00551         *filter_view;
00552 
00553       GetMagickPixelPacket(resample_filter->image,pixel);
00554       geometry.width=4L;
00555       geometry.height=4L;
00556       geometry.x=(long) floor(x)-1L;
00557       geometry.y=(long) floor(y)-1L;
00558       excerpt_image=ExcerptImage(resample_filter->image,&geometry,
00559         resample_filter->exception);
00560       if (excerpt_image == (Image *) NULL)
00561         {
00562           status=MagickFalse;
00563           break;
00564         }
00565       filter_image=ResizeImage(excerpt_image,1,1,resample_filter->image->filter,
00566         resample_filter->image->blur,resample_filter->exception);
00567       excerpt_image=DestroyImage(excerpt_image);
00568       if (filter_image == (Image *) NULL)
00569         break;
00570       filter_view=AcquireCacheView(filter_image);
00571       p=GetCacheViewVirtualPixels(filter_view,0,0,1,1,
00572         resample_filter->exception);
00573       if (p != (const PixelPacket *) NULL)
00574         {
00575           indexes=GetVirtualIndexQueue(filter_image);
00576           GetMagickPixelPacket(resample_filter->image,pixels);
00577           SetMagickPixelPacket(resample_filter->image,p,indexes,pixel);
00578         }
00579       filter_view=DestroyCacheView(filter_view);
00580       filter_image=DestroyImage(filter_image);
00581       break;
00582     }
00583     case IntegerInterpolatePixel:
00584     {
00585       MagickPixelPacket
00586         pixels[1];
00587 
00588       GetMagickPixelPacket(resample_filter->image,pixel);
00589       p=GetCacheViewVirtualPixels(resample_filter->view,(long) floor(x),(long)
00590         floor(y),1,1,resample_filter->exception);
00591       if (p == (const PixelPacket *) NULL)
00592         {
00593           status=MagickFalse;
00594           break;
00595         }
00596       indexes=GetCacheViewVirtualIndexQueue(resample_filter->view);
00597       GetMagickPixelPacket(resample_filter->image,pixels);
00598       SetMagickPixelPacket(resample_filter->image,p,indexes,pixel);
00599       break;
00600     }
00601     case MeshInterpolatePixel:
00602     {
00603       MagickPixelPacket
00604         pixels[4];
00605 
00606       MagickRealType
00607         alpha[4],
00608         gamma;
00609 
00610       PointInfo
00611         delta,
00612         luminance;
00613 
00614       GetMagickPixelPacket(resample_filter->image,pixel);
00615       p=GetCacheViewVirtualPixels(resample_filter->view,(long) floor(x),(long)
00616         floor(y),2,2,resample_filter->exception);
00617       if (p == (const PixelPacket *) NULL)
00618         {
00619           status=MagickFalse;
00620           break;
00621         }
00622       indexes=GetCacheViewVirtualIndexQueue(resample_filter->view);
00623       for (i=0; i < 4L; i++)
00624       {
00625         GetMagickPixelPacket(resample_filter->image,pixels+i);
00626         SetMagickPixelPacket(resample_filter->image,p,indexes+i,pixels+i);
00627         alpha[i]=1.0;
00628         if (resample_filter->image->matte != MagickFalse)
00629           {
00630             alpha[i]=QuantumScale*((MagickRealType) QuantumRange-p->opacity);
00631             pixels[i].red*=alpha[i];
00632             pixels[i].green*=alpha[i];
00633             pixels[i].blue*=alpha[i];
00634             if (resample_filter->image->colorspace == CMYKColorspace)
00635               pixels[i].index*=alpha[i];
00636           }
00637         p++;
00638       }
00639       delta.x=x-floor(x);
00640       delta.y=y-floor(y);
00641       luminance.x=MagickPixelLuminance(pixels+0)-MagickPixelLuminance(pixels+3);
00642       luminance.y=MagickPixelLuminance(pixels+1)-MagickPixelLuminance(pixels+2);
00643       if (fabs(luminance.x) < fabs(luminance.y))
00644         {
00645           /*
00646             Diagonal 0-3 NW-SE.
00647           */
00648           if (delta.x <= delta.y)
00649             {
00650               /*
00651                 Bottom-left triangle  (pixel:2, diagonal: 0-3).
00652               */
00653               delta.y=1.0-delta.y;
00654               gamma=MeshInterpolate(&delta,alpha[2],alpha[3],alpha[0]);
00655               gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00656               pixel->red=gamma*MeshInterpolate(&delta,pixels[2].red,
00657                 pixels[3].red,pixels[0].red);
00658               pixel->green=gamma*MeshInterpolate(&delta,pixels[2].green,
00659                 pixels[3].green,pixels[0].green);
00660               pixel->blue=gamma*MeshInterpolate(&delta,pixels[2].blue,
00661                 pixels[3].blue,pixels[0].blue);
00662               pixel->opacity=gamma*MeshInterpolate(&delta,pixels[2].opacity,
00663                 pixels[3].opacity,pixels[0].opacity);
00664               if (resample_filter->image->colorspace == CMYKColorspace)
00665                 pixel->index=gamma*MeshInterpolate(&delta,pixels[2].index,
00666                   pixels[3].index,pixels[0].index);
00667             }
00668           else
00669             {
00670               /*
00671                 Top-right triangle (pixel:1, diagonal: 0-3).
00672               */
00673               delta.x=1.0-delta.x;
00674               gamma=MeshInterpolate(&delta,alpha[1],alpha[0],alpha[3]);
00675               gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00676               pixel->red=gamma*MeshInterpolate(&delta,pixels[1].red,
00677                 pixels[0].red,pixels[3].red);
00678               pixel->green=gamma*MeshInterpolate(&delta,pixels[1].green,
00679                 pixels[0].green,pixels[3].green);
00680               pixel->blue=gamma*MeshInterpolate(&delta,pixels[1].blue,
00681                 pixels[0].blue,pixels[3].blue);
00682               pixel->opacity=gamma*MeshInterpolate(&delta,pixels[1].opacity,
00683                 pixels[0].opacity,pixels[3].opacity);
00684               if (resample_filter->image->colorspace == CMYKColorspace)
00685                 pixel->index=gamma*MeshInterpolate(&delta,pixels[1].index,
00686                   pixels[0].index,pixels[3].index);
00687             }
00688         }
00689       else
00690         {
00691           /*
00692             Diagonal 1-2 NE-SW.
00693           */
00694           if (delta.x <= (1.0-delta.y))
00695             {
00696               /*
00697                 Top-left triangle (pixel 0, diagonal: 1-2).
00698               */
00699               gamma=MeshInterpolate(&delta,alpha[0],alpha[1],alpha[2]);
00700               gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00701               pixel->red=gamma*MeshInterpolate(&delta,pixels[0].red,
00702                 pixels[1].red,pixels[2].red);
00703               pixel->green=gamma*MeshInterpolate(&delta,pixels[0].green,
00704                 pixels[1].green,pixels[2].green);
00705               pixel->blue=gamma*MeshInterpolate(&delta,pixels[0].blue,
00706                 pixels[1].blue,pixels[2].blue);
00707               pixel->opacity=gamma*MeshInterpolate(&delta,pixels[0].opacity,
00708                 pixels[1].opacity,pixels[2].opacity);
00709               if (resample_filter->image->colorspace == CMYKColorspace)
00710                 pixel->index=gamma*MeshInterpolate(&delta,pixels[0].index,
00711                   pixels[1].index,pixels[2].index);
00712             }
00713           else
00714             {
00715               /*
00716                 Bottom-right triangle (pixel: 3, diagonal: 1-2).
00717               */
00718               delta.x=1.0-delta.x;
00719               delta.y=1.0-delta.y;
00720               gamma=MeshInterpolate(&delta,alpha[3],alpha[2],alpha[1]);
00721               gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00722               pixel->red=gamma*MeshInterpolate(&delta,pixels[3].red,
00723                 pixels[2].red,pixels[1].red);
00724               pixel->green=gamma*MeshInterpolate(&delta,pixels[3].green,
00725                 pixels[2].green,pixels[1].green);
00726               pixel->blue=gamma*MeshInterpolate(&delta,pixels[3].blue,
00727                 pixels[2].blue,pixels[1].blue);
00728               pixel->opacity=gamma*MeshInterpolate(&delta,pixels[3].opacity,
00729                 pixels[2].opacity,pixels[1].opacity);
00730               if (resample_filter->image->colorspace == CMYKColorspace)
00731                 pixel->index=gamma*MeshInterpolate(&delta,pixels[3].index,
00732                   pixels[2].index,pixels[1].index);
00733             }
00734         }
00735       break;
00736     }
00737     case NearestNeighborInterpolatePixel:
00738     {
00739       MagickPixelPacket
00740         pixels[1];
00741 
00742       GetMagickPixelPacket(resample_filter->image,pixel);
00743       p=GetCacheViewVirtualPixels(resample_filter->view,NearestNeighbor(x),
00744         NearestNeighbor(y),1,1,resample_filter->exception);
00745       if (p == (const PixelPacket *) NULL)
00746         {
00747           status=MagickFalse;
00748           break;
00749         }
00750       indexes=GetCacheViewVirtualIndexQueue(resample_filter->view);
00751       GetMagickPixelPacket(resample_filter->image,pixels);
00752       SetMagickPixelPacket(resample_filter->image,p,indexes,pixel);
00753       break;
00754     }
00755     case SplineInterpolatePixel:
00756     {
00757       long
00758         j,
00759         n;
00760 
00761       MagickPixelPacket
00762         pixels[16];
00763 
00764       MagickRealType
00765         alpha[16],
00766         dx,
00767         dy,
00768         gamma;
00769 
00770       PointInfo
00771         delta;
00772 
00773       GetMagickPixelPacket(resample_filter->image,pixel);
00774       p=GetCacheViewVirtualPixels(resample_filter->view,(long) floor(x)-1,(long)
00775         floor(y)-1,4,4,resample_filter->exception);
00776       if (p == (const PixelPacket *) NULL)
00777         {
00778           status=MagickFalse;
00779           break;
00780         }
00781       indexes=GetCacheViewVirtualIndexQueue(resample_filter->view);
00782       n=0;
00783       delta.x=x-floor(x);
00784       delta.y=y-floor(y);
00785       for (i=(-1); i < 3L; i++)
00786       {
00787         dy=CubicWeightingFunction((MagickRealType) i-delta.y);
00788         for (j=(-1); j < 3L; j++)
00789         {
00790           GetMagickPixelPacket(resample_filter->image,pixels+n);
00791           SetMagickPixelPacket(resample_filter->image,p,indexes+n,pixels+n);
00792           alpha[n]=1.0;
00793           if (resample_filter->image->matte != MagickFalse)
00794             {
00795               alpha[n]=QuantumScale*((MagickRealType) QuantumRange-p->opacity);
00796               pixels[n].red*=alpha[n];
00797               pixels[n].green*=alpha[n];
00798               pixels[n].blue*=alpha[n];
00799               if (resample_filter->image->colorspace == CMYKColorspace)
00800                 pixels[n].index*=alpha[n];
00801             }
00802           dx=CubicWeightingFunction(delta.x-(MagickRealType) j);
00803           gamma=alpha[n];
00804           gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00805           pixel->red+=gamma*dx*dy*pixels[n].red;
00806           pixel->green+=gamma*dx*dy*pixels[n].green;
00807           pixel->blue+=gamma*dx*dy*pixels[n].blue;
00808           if (resample_filter->image->matte != MagickFalse)
00809             pixel->opacity+=dx*dy*pixels[n].opacity;
00810           if (resample_filter->image->colorspace == CMYKColorspace)
00811             pixel->index+=gamma*dx*dy*pixels[n].index;
00812           n++;
00813           p++;
00814         }
00815       }
00816       break;
00817     }
00818   }
00819   return(status);
00820 }
00821 
00822 /*
00823 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00824 %                                                                             %
00825 %                                                                             %
00826 %                                                                             %
00827 %   R e s a m p l e P i x e l C o l o r                                       %
00828 %                                                                             %
00829 %                                                                             %
00830 %                                                                             %
00831 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00832 %
00833 %  ResamplePixelColor() samples the pixel values surrounding the location
00834 %  given using an elliptical weighted average, at the scale previously
00835 %  calculated, and in the most efficent manner possible for the
00836 %  VirtualPixelMethod setting.
00837 %
00838 %  The format of the ResamplePixelColor method is:
00839 %
00840 %     MagickBooleanType ResamplePixelColor(ResampleFilter *resample_filter,
00841 %       const double u0,const double v0,MagickPixelPacket *pixel)
00842 %
00843 %  A description of each parameter follows:
00844 %
00845 %    o resample_filter: the resample filter.
00846 %
00847 %    o u0,v0: A double representing the center of the area to resample,
00848 %        The distortion transformed transformed x,y coordinate.
00849 %
00850 %    o pixel: the resampled pixel is returned here.
00851 %
00852 */
00853 MagickExport MagickBooleanType ResamplePixelColor(
00854   ResampleFilter *resample_filter,const double u0,const double v0,
00855   MagickPixelPacket *pixel)
00856 {
00857   MagickBooleanType
00858     status;
00859 
00860   long u,v, uw,v1,v2, hit;
00861   double u1;
00862   double U,V,Q,DQ,DDQ;
00863   double divisor_c,divisor_m;
00864   register double weight;
00865   register const PixelPacket *pixels;
00866   register const IndexPacket *indexes;
00867   assert(resample_filter != (ResampleFilter *) NULL);
00868   assert(resample_filter->signature == MagickSignature);
00869 
00870   if ( resample_filter->do_interpolate ) {
00871     status=InterpolateResampleFilter(resample_filter,
00872       resample_filter->interpolate,u0,v0,pixel);
00873     return(status);
00874   }
00875 
00876   /*
00877     Does resample area Miss the image?
00878     And is that area a simple solid color - then return that color
00879   */
00880   status=MagickTrue;
00881   GetMagickPixelPacket(resample_filter->image,pixel);
00882   hit = 0;
00883   switch ( resample_filter->virtual_pixel ) {
00884     case BackgroundVirtualPixelMethod:
00885     case ConstantVirtualPixelMethod:
00886     case TransparentVirtualPixelMethod:
00887     case BlackVirtualPixelMethod:
00888     case GrayVirtualPixelMethod:
00889     case WhiteVirtualPixelMethod:
00890     case MaskVirtualPixelMethod:
00891       if ( resample_filter->limit_reached
00892            || u0 + resample_filter->sqrtC < 0.0
00893            || u0 - resample_filter->sqrtC > (double) resample_filter->image->columns
00894            || v0 + resample_filter->sqrtA < 0.0
00895            || v0 - resample_filter->sqrtA > (double) resample_filter->image->rows
00896            )
00897         hit++;
00898       break;
00899 
00900     case UndefinedVirtualPixelMethod:
00901     case EdgeVirtualPixelMethod:
00902       if (    ( u0 + resample_filter->sqrtC < 0.0 && v0 + resample_filter->sqrtA < 0.0 )
00903            || ( u0 + resample_filter->sqrtC < 0.0
00904                 && v0 - resample_filter->sqrtA > (double) resample_filter->image->rows )
00905            || ( u0 - resample_filter->sqrtC > (double) resample_filter->image->columns
00906                 && v0 + resample_filter->sqrtA < 0.0 )
00907            || ( u0 - resample_filter->sqrtC > (double) resample_filter->image->columns
00908                 && v0 - resample_