resize.c

Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                                                                             %
00006 %                 RRRR   EEEEE  SSSSS  IIIII  ZZZZZ  EEEEE                    %
00007 %                 R   R  E      SS       I       ZZ  E                        %
00008 %                 RRRR   EEE     SSS     I     ZZZ   EEE                      %
00009 %                 R R    E         SS    I    ZZ     E                        %
00010 %                 R  R   EEEEE  SSSSS  IIIII  ZZZZZ  EEEEE                    %
00011 %                                                                             %
00012 %                                                                             %
00013 %                      MagickCore Image Resize Methods                        %
00014 %                                                                             %
00015 %                              Software Design                                %
00016 %                                John Cristy                                  %
00017 %                                 July 1992                                   %
00018 %                                                                             %
00019 %                                                                             %
00020 %  Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization      %
00021 %  dedicated to making software imaging solutions freely available.           %
00022 %                                                                             %
00023 %  You may not use this file except in compliance with the License.  You may  %
00024 %  obtain a copy of the License at                                            %
00025 %                                                                             %
00026 %    http://www.imagemagick.org/script/license.php                            %
00027 %                                                                             %
00028 %  Unless required by applicable law or agreed to in writing, software        %
00029 %  distributed under the License is distributed on an "AS IS" BASIS,          %
00030 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
00031 %  See the License for the specific language governing permissions and        %
00032 %  limitations under the License.                                             %
00033 %                                                                             %
00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00035 %
00036 %
00037 */
00038 
00039 /*
00040   Include declarations.
00041 */
00042 #include "magick/studio.h"
00043 #include "magick/artifact.h"
00044 #include "magick/blob.h"
00045 #include "magick/cache.h"
00046 #include "magick/cache-view.h"
00047 #include "magick/color.h"
00048 #include "magick/color-private.h"
00049 #include "magick/draw.h"
00050 #include "magick/exception.h"
00051 #include "magick/exception-private.h"
00052 #include "magick/gem.h"
00053 #include "magick/image.h"
00054 #include "magick/image-private.h"
00055 #include "magick/list.h"
00056 #include "magick/memory_.h"
00057 #include "magick/pixel-private.h"
00058 #include "magick/property.h"
00059 #include "magick/monitor.h"
00060 #include "magick/monitor-private.h"
00061 #include "magick/pixel.h"
00062 #include "magick/option.h"
00063 #include "magick/resample.h"
00064 #include "magick/resize.h"
00065 #include "magick/resize-private.h"
00066 #include "magick/string_.h"
00067 #include "magick/thread-private.h"
00068 #include "magick/utility.h"
00069 #include "magick/version.h"
00070 #if defined(MAGICKCORE_LQR_DELEGATE)
00071 #include <lqr.h>
00072 #endif
00073 
00074 /*
00075   Typedef declarations.
00076 */
00077 struct _ResizeFilter
00078 {
00079   MagickRealType
00080     (*filter)(const MagickRealType,const ResizeFilter *),
00081     (*window)(const MagickRealType,const ResizeFilter *),
00082     support,   /* filter region of support - the filter support limit */
00083     window_support,  /* window support, usally equal to support (expert only) */
00084     scale,     /* dimension to scale to fit window support (usally 1.0) */
00085     blur,      /* x-scale (blur-sharpen) */
00086     cubic[8];  /* cubic coefficents for smooth Cubic filters */
00087 
00088   unsigned long
00089     signature;
00090 };
00091 
00092 /*
00093   Forward declaractions.
00094 */
00095 static MagickRealType
00096   I0(MagickRealType x),
00097   BesselOrderOne(MagickRealType);
00098 
00099 /*
00100 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00101 %                                                                             %
00102 %                                                                             %
00103 %                                                                             %
00104 +   F i l t e r F u n c t i o n s                                             %
00105 %                                                                             %
00106 %                                                                             %
00107 %                                                                             %
00108 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00109 %
00110 %  These are the various filter and windowing functions that are provided,
00111 %
00112 %  They are all internal to this module only.  See AcquireResizeFilterInfo()
00113 %  for details of the access to these functions, via the
00114 %  GetResizeFilterSupport() and GetResizeFilterWeight() API interface.
00115 %
00116 %  The individual filter functions have this format...
00117 %
00118 %     static MagickRealtype *FilterName(const MagickRealType x,
00119 %        const MagickRealType support)
00120 %
00121 %    o x: the distance from the sampling point
00122 %         generally in the range of  0 to support
00123 %         The GetResizeFilterWeight() ensures this a positive value.
00124 %
00125 %    o resize_filter: Current Filter Information
00126 %        This allows function to access support, and posibly other
00127 %        pre-calculated information defineding the functions.
00128 %
00129 */
00130 
00131 static MagickRealType Bessel(const MagickRealType x,
00132   const ResizeFilter *magick_unused(resize_filter))
00133 {
00134   /*
00135     See Pratt "Digital Image Processing" p.97 for Bessel functions
00136 
00137     This function actually a X-scaled Jinc(x) function.
00138       http://mathworld.wolfram.com/JincFunction.html
00139     And on page 11 of...
00140       http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
00141   */
00142   if (x == 0.0)
00143     return((MagickRealType) (MagickPI/4.0));
00144   return(BesselOrderOne(MagickPI*x)/(2.0*x));
00145 }
00146 
00147 static MagickRealType Blackman(const MagickRealType x,
00148      const ResizeFilter *magick_unused(resize_filter))
00149 {
00150   /*
00151     Blackman: 2rd Order cosine windowing function.
00152   */
00153   return(0.42+0.5*cos(MagickPI*(double) x)+0.08*cos(2.0*MagickPI*(double) x));
00154 }
00155 
00156 static MagickRealType Bohman(const MagickRealType x,
00157      const ResizeFilter *magick_unused(resize_filter))
00158 {
00159   /*
00160     Bohman: 2rd Order cosine windowing function.
00161   */
00162   return((1-x)*cos(MagickPI*(double) x)+sin(MagickPI*(double) x)/MagickPI);
00163 }
00164 
00165 static MagickRealType Box(const MagickRealType magick_unused(x),
00166   const ResizeFilter *magick_unused(resize_filter))
00167 {
00168   /*
00169     Just return 1.0, filter will still be clipped by its support window.
00170   */
00171   return(1.0);
00172 }
00173 
00174 static MagickRealType CubicBC(const MagickRealType x,
00175   const ResizeFilter *resize_filter)
00176 {
00177   /*
00178     Cubic Filters using B,C determined values:
00179 
00180     Mitchell-Netravali  B=1/3 C=1/3   Qualitively ideal Cubic Filter
00181     Catmull-Rom         B= 0  C=1/2   Cublic Interpolation Function
00182     Cubic B-Spline      B= 1  C= 0    Spline Approximation of Gaussian
00183     Hermite             B= 0  C= 0    Quadratic Spline (support = 1)
00184 
00185     See paper by Mitchell and Netravali,
00186       Reconstruction Filters in Computer Graphics
00187       Computer Graphics, Volume 22, Number 4, August 1988
00188         http://www.cs.utexas.edu/users/fussell/courses/cs384g/
00189                  lectures/mitchell/Mitchell.pdf
00190 
00191     Coefficents are determined from B,C values
00192        P0 = (  6 - 2*B       )/6
00193        P1 =         0
00194        P2 = (-18 +12*B + 6*C )/6
00195        P3 = ( 12 - 9*B - 6*C )/6
00196        Q0 = (      8*B +24*C )/6
00197        Q1 = (    -12*B -48*C )/6
00198        Q2 = (      6*B +30*C )/6
00199        Q3 = (    - 1*B - 6*C )/6
00200 
00201     Which is used to define the filter...
00202        P0 + P1*x + P2*x^2 + P3*x^3      0 <= x < 1
00203        Q0 + Q1*x + Q2*x^2 + Q3*x^3      1 <= x <= 2
00204 
00205     Which ensures function is continuous in value and derivative (slope).
00206   */
00207   if (x < 1.0)
00208     return(resize_filter->cubic[0]+x*(resize_filter->cubic[1]+x*
00209       (resize_filter->cubic[2]+x*resize_filter->cubic[3])));
00210   if (x < 2.0)
00211     return(resize_filter->cubic[4] +x*(resize_filter->cubic[5]+x*
00212       (resize_filter->cubic[6] +x*resize_filter->cubic[7])));
00213   return(0.0);
00214 }
00215 
00216 static MagickRealType Gaussian(const MagickRealType x,
00217   const ResizeFilter *magick_unused(resize_filter))
00218 {
00219   return(exp((double) (-2.0*x*x))*sqrt(2.0/MagickPI));
00220 }
00221 
00222 static MagickRealType Hanning(const MagickRealType x,
00223   const ResizeFilter *magick_unused(resize_filter))
00224 {
00225   /*
00226     A Cosine windowing function.
00227   */
00228   return(0.5+0.5*cos(MagickPI*(double) x));
00229 }
00230 
00231 static MagickRealType Hamming(const MagickRealType x,
00232   const ResizeFilter *magick_unused(resize_filter))
00233 {
00234   /*
00235     A offset Cosine windowing function.
00236   */
00237   return(0.54+0.46*cos(MagickPI*(double) x));
00238 }
00239 
00240 static MagickRealType Kaiser(const MagickRealType x,
00241   const ResizeFilter *magick_unused(resize_filter))
00242 {
00243 #define Alpha  6.5
00244 #define I0A  (1.0/I0(Alpha))
00245 
00246   /*
00247     Kaiser Windowing Function (bessel windowing):
00248       Alpha is a free value  from 5 to 8 (currently hardcoded to 6.5)
00249       Future: make alphand the IOA pre-calculation, a 'expert' setting.
00250   */
00251   return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
00252 }
00253 
00254 static MagickRealType Lagrange(const MagickRealType x,
00255   const ResizeFilter *resize_filter)
00256 {
00257   long
00258     n,
00259     order;
00260 
00261   MagickRealType
00262     value;
00263 
00264   register long
00265     i;
00266 
00267   /*
00268     Lagrange Piece-Wise polynomial fit of Sinc:
00269       N is the 'order' of the lagrange function and depends on
00270       the overall support window size of the filter. That is for
00271       a support of 2, gives a lagrange-4 or piece-wise cubic functions
00272 
00273       Note that n is the specific piece of the piece-wise function to calculate.
00274 
00275       See Survey: Interpolation Methods, IEEE Transactions on Medical Imaging,
00276       Vol 18, No 11, November 1999, p1049-1075, -- Equation 27 on p1064
00277   */
00278   if (x > resize_filter->support)
00279     return(0.0);
00280   order=(long) (2.0*resize_filter->window_support);  /* number of pieces */
00281   n=(long) ((1.0*order)/2.0+x);  /* which piece does x belong to */
00282   value=1.0f;
00283   for (i=0; i < order; i++)
00284     if (i != n)
00285       value*=(n-i-x)/(n-i);
00286   return(value);
00287 }
00288 
00289 static MagickRealType Quadratic(const MagickRealType x,
00290   const ResizeFilter *magick_unused(resize_filter))
00291 {
00292   /*
00293     2rd order (quadratic) B-Spline approximation of Gaussian.
00294   */
00295   if (x < 0.5)
00296     return(0.75-x*x);
00297   if (x < 1.5)
00298     return(0.5*(x-1.5)*(x-1.5));
00299   return(0.0);
00300 }
00301 
00302 static MagickRealType Sinc(const MagickRealType x,
00303   const ResizeFilter *magick_unused(resize_filter))
00304 {
00305   /*
00306     This function actually a X-scaled Sinc(x) function.
00307   */
00308   if (x == 0.0)
00309     return(1.0);
00310   return(sin(MagickPI*(double) x)/(MagickPI*(double) x));
00311 }
00312 
00313 static MagickRealType Triangle(const MagickRealType x,
00314   const ResizeFilter *magick_unused(resize_filter))
00315 {
00316   /*
00317     1rd order (linear) B-Spline,  bilinear interpolation,
00318     Tent 1D filter, or a Bartlett 2D Cone filter
00319   */
00320   if (x < 1.0)
00321     return(1.0-x);
00322   return(0.0);
00323 }
00324 
00325 static MagickRealType Welsh(const MagickRealType x,
00326   const ResizeFilter *magick_unused(resize_filter))
00327 {
00328   /*
00329     Welsh parabolic windowing filter.
00330   */
00331   if (x <  1.0)
00332     return(1.0-x*x);
00333   return(0.0);
00334 }
00335 
00336 /*
00337 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00338 %                                                                             %
00339 %                                                                             %
00340 %                                                                             %
00341 +   A c q u i r e R e s i z e F i l t e r                                     %
00342 %                                                                             %
00343 %                                                                             %
00344 %                                                                             %
00345 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00346 %
00347 %  AcquireResizeFilter() allocates the ResizeFilter structure.  Choose from
00348 %  these filters:
00349 %
00350 %  FIR (Finite impulse Response) Filters
00351 %      Box         Triangle   Quadratic
00352 %      Cubic       Hermite    Catrom
00353 %      Mitchell
00354 %
00355 %  IIR (Infinite impulse Response) Filters
00356 %      Gaussian     Sinc        Bessel
00357 %
00358 %  Windowed Sinc/Bessel Method
00359 %      Blackman     Hanning     Hamming
00360 %      Kaiser       Lancos (Sinc)
00361 %
00362 %  FIR filters are used as is, and are limited by that filters support window
00363 %  (unless over-ridden).  'Gaussian' while classed as an IIR filter, is also
00364 %  simply clipped by its support size (1.5).
00365 %
00366 %  Requesting a windowed filter will return either a windowed Sinc, for a one
00367 %  dimentional orthogonal filtering method, such as ResizeImage(), or a
00368 %  windowed Bessel for image operations requiring a two dimentional
00369 %  cylindrical filtering method, such a DistortImage().  Which function is
00370 %  is used set by the "cylindrical" boolean argument.
00371 %
00372 %  Directly requesting 'Sinc' or 'Bessel' will force the use of that filter
00373 %  function, with a default 'Blackman' windowing method.  This not however
00374 %  recommended as it removes the correct filter selection for different
00375 %  filtering image operations.  Selecting a window filtering method is better.
00376 %
00377 %  Lanczos is purely special case of a Sinc windowed Sinc, but defulting to
00378 %  a 3 lobe support, rather that the default 4 lobe support.
00379 %
00380 %  Special options can be used to override specific, or all the filter
00381 %  settings.   However doing so is not advisible unless you have expert
00382 %  knowledge of the use of resampling filtered techniques. Extreme caution is
00383 %  advised.
00384 %
00385 %    "filter:filter"    Select this function as the filter.
00386 %        If a "filter:window" operation is not provided, then no windowing
00387 %        will be performed on the selected filter, (support clipped)
00388 %
00389 %        This can be used to force the use of a windowing method as filter,
00390 %        request a 'Sinc' filter in a radially filtered operation, or the
00391 %        'Bessel' filter for a othogonal filtered operation.
00392 %
00393 %    "filter:window"   Select this windowing function for the filter.
00394 %        While any filter could be used as a windowing function,
00395 %        using that filters first lobe over the whole support window,
00396 %        using a non-windowing method is not advisible.
00397 %
00398 %    "filter:lobes"    Number of lobes to use for the Sinc/Bessel filter.
00399 %        This a simper method of setting filter support size that will
00400 %        correctly handle the Sinc/Bessel switch for an operators filtering
00401 %        requirements.
00402 %
00403 %    "filter:support"  Set the support size for filtering to the size given
00404 %        This not recomented for Sinc/Bessel windowed filters, but is
00405 %        used for simple filters like FIR filters, and the Gaussian Filter.
00406 %        This will override any 'filter:lobes' option.
00407 %
00408 %    "filter:blur"     Scale the filter and support window by this amount.
00409 %        A value >1 will generally result in a more burred image with
00410 %        more ringing effects, while a value <1 will sharpen the
00411 %        resulting image with more aliasing and Morie effects.
00412 %
00413 %    "filter:win-support"  Scale windowing function to this size instead.
00414 %        This causes the windowing (or self-windowing Lagrange filter)
00415 %        to act is if the support winodw it much much larger than what
00416 %        is actually supplied to the calling operator.  The filter however
00417 %        is still clipped to the real support size given.  If unset this
00418 %        will equal the normal filter support size.
00419 %
00420 %    "filter:b"
00421 %    "filter:c"    Override the preset B,C values for a Cubic type of filter
00422 %         If only one of these are given it is assumes to be a 'Keys'
00423 %         type of filter such that B+2C=1, where Keys 'alpha' value = C
00424 %
00425 %    "filter:verbose"   Output verbose plotting data for graphing the
00426 %         resulting filter over the whole support range (with blur effect).
00427 %
00428 %  Set a true un-windowed Sinc filter with 10 lobes (very slow)
00429 %     -set option:filter:filter  Sinc
00430 %     -set option:filter:lobes   8
00431 %
00432 %  For example force an 8 lobe Lanczos (Sinc or Bessel) filter...
00433 %     -filter Lanczos
00434 %     -set option:filter:lobes   8
00435 %
00436 %  The format of the AcquireResizeFilter method is:
00437 %
00438 %      ResizeFilter *AcquireResizeFilter(const Image *image,
00439 %        const FilterTypes filter_type, const MagickBooleanType radial,
00440 %        ExceptionInfo *exception)
00441 %
00442 %    o image: the image.
00443 %
00444 %    o filter: the filter type, defining a preset filter, window and support.
00445 %
00446 %    o blur: blur the filter by this amount, use 1.0 if unknown.
00447 %            Image artifact "filter:blur"  will override this old usage
00448 %
00449 %    o radial: 1D orthogonal filter (Sinc) or 2D radial filter (Bessel)
00450 %
00451 %    o exception: return any errors or warnings in this structure.
00452 %
00453 */
00454 MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
00455   const FilterTypes filter, const MagickRealType blur,
00456   const MagickBooleanType cylindrical,ExceptionInfo *exception)
00457 {
00458   const char
00459     *artifact;
00460 
00461   FilterTypes
00462     filter_type,
00463     window_type;
00464 
00465   long
00466     filter_artifact;
00467 
00468   MagickRealType
00469     B,
00470     C;
00471 
00472   register ResizeFilter
00473     *resize_filter;
00474 
00475   /*
00476     Table Mapping given Filter, into  Weighting and Windowing functions.
00477     A 'Box' windowing function means its a simble non-windowed filter.
00478     A 'Sinc' filter function (must be windowed) could be upgraded to a
00479     'Bessel' filter if a "cylindrical" filter is requested, unless a "Sinc"
00480     filter specifically request.
00481   */
00482   static struct
00483   {
00484     FilterTypes
00485       filter,
00486       window;
00487   } const mapping[SentinelFilter] =
00488   {
00489     { UndefinedFilter, BoxFilter },  /* undefined */
00490     { PointFilter,     BoxFilter },  /* special, nearest-neighbour filter */
00491     { BoxFilter,       BoxFilter },  /* Box averaging Filter */
00492     { TriangleFilter,  BoxFilter },  /* Linear Interpolation Filter */
00493     { HermiteFilter,   BoxFilter },      /* Hermite interpolation filter */
00494     { SincFilter,      HanningFilter },  /* Hanning -- Cosine-Sinc */
00495     { SincFilter,      HammingFilter },  /* Hamming --  '' variation */
00496     { SincFilter,      BlackmanFilter }, /* Blackman -- 2*Cosine-Sinc */
00497     { GaussianFilter,  BoxFilter },      /* Gaussain Blurring filter */
00498     { QuadraticFilter, BoxFilter },      /* Quadratic Gaussian approximation */
00499     { CubicFilter,     BoxFilter },      /* Cubic Gaussian approximation */
00500     { CatromFilter,    BoxFilter },      /* Cubic Interpolator */
00501     { MitchellFilter,  BoxFilter },      /* 'ideal' Cubic Filter */
00502     { LanczosFilter,   SincFilter },     /* Special, 3 lobed Sinc-Sinc */
00503     { BesselFilter,    BlackmanFilter }, /* 3 lobed bessel -specific request */
00504     { SincFilter,      BlackmanFilter }, /* 4 lobed sinc - specific request */
00505     { SincFilter,      KaiserFilter },   /* Kaiser --  SqRoot-Sinc */
00506     { SincFilter,      WelshFilter },    /* Welsh -- Parabolic-Sinc */
00507     { SincFilter,      CubicFilter },    /* Parzen -- Cubic-Sinc */
00508     { LagrangeFilter,  BoxFilter },      /* Lagrange self-windowing filter */
00509     { SincFilter,      BohmanFilter },   /* Bohman -- 2*Cosine-Sinc */
00510     { SincFilter,      TriangleFilter }  /* Bartlett -- Triangle-Sinc */
00511   };
00512   /*
00513     Table maping the filter/window function from the above table to the actual
00514     filter/window function call to use.  The default support size for that
00515     filter as a weighting function, and the point to scale when that function
00516     is used as a windowing function (typ 1.0).
00517   */
00518   static struct
00519   {
00520     MagickRealType
00521       (*function)(const MagickRealType, const ResizeFilter*),
00522       support,  /* default support size for function as a filter */
00523       scale,    /* size windowing function, for scaling windowing function */
00524       B,
00525       C;        /* Cubic Filter factors for a CubicBC function, else ignored */
00526   } const filters[SentinelFilter] =
00527   {
00528     { Box,       0.0f,  0.5f, 0.0f, 0.0f }, /* Undefined */
00529     { Box,       0.0f,  0.5f, 0.0f, 0.0f }, /* Point */
00530     { Box,       0.5f,  0.5f, 0.0f, 0.0f }, /* Box */
00531     { Triangle,  1.0f,  1.0f, 0.0f, 0.0f }, /* Triangle */
00532     { CubicBC,   1.0f,  1.0f, 0.0f, 0.0f }, /* Hermite, Cubic B=C=0 */
00533     { Hanning,   1.0f,  1.0f, 0.0f, 0.0f }, /* Hanning, Cosine window */
00534     { Hamming,   1.0f,  1.0f, 0.0f, 0.0f }, /* Hamming, '' variation */
00535     { Blackman,  1.0f,  1.0f, 0.0f, 0.0f }, /* Blackman, 2*cos window */
00536     { Gaussian,  1.5f,  1.5f, 0.0f, 0.0f }, /* Gaussian */
00537     { Quadratic, 1.5f,  1.5f, 0.0f, 0.0f }, /* Quadratic Gaussian */
00538     { CubicBC,   2.0f,  2.0f, 1.0f, 0.0f }, /* B-Spline of Gaussian B=1 C=0 */
00539     { CubicBC,   2.0f,  1.0f, 0.0f, 0.5f }, /* Catmull-Rom  B=0 C=1/2 */
00540     { CubicBC,   2.0f,  1.0f, 1.0f/3.0f, 1.0f/3.0f }, /* Mitchel B=C=1/3 */
00541     { Sinc,      3.0f,  1.0f, 0.0f, 0.0f }, /* Lanczos, 3 lobed Sinc-Sinc */
00542     { Bessel,    3.2383f,1.2197f,.0f,.0f }, /* 3 lobed Blackman-Bessel */
00543     { Sinc,      4.0f,  1.0f, 0.0f, 0.0f }, /* 4 lobed Blackman-Sinc   */
00544     { Kaiser,    1.0f,  1.0f, 0.0f, 0.0f }, /* Kaiser, sq-root windowing */
00545     { Welsh,     1.0f,  1.0f, 0.0f, 0.0f }, /* Welsh, Parabolic windowing */
00546     { CubicBC,   2.0f,  2.0f, 1.0f, 0.0f }, /* Parzen, B-Spline windowing */
00547     { Lagrange,  2.0f,  1.0f, 0.0f, 0.0f }, /* Lagrangian Filter */
00548     { Bohman,    1.0f,  1.0f, 0.0f, 0.0f }, /* Bohman, 2*Cosine windowing */
00549     { Triangle,  1.0f,  1.0f, 0.0f, 0.0f }  /* Bartlett, Triangle windowing */
00550   };
00551   /*
00552     The known zero crossings of the Bessel() or the Jinc(x*PI) function
00553     Found by using
00554       http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
00555     for Jv-function with v=1,  then dividing X-roots by PI (tabled below)
00556   */
00557   static MagickRealType
00558     bessel_zeros[16] =
00559     {
00560       1.21966989126651f,
00561       2.23313059438153f,
00562       3.23831548416624f,
00563       4.24106286379607f,
00564       5.24276437687019f,
00565       6.24392168986449f,
00566       7.24475986871996f,
00567       8.24539491395205f,
00568       9.24589268494948f,
00569       10.2462933487549f,
00570       11.2466227948779f,
00571       12.2468984611381f,
00572       13.2471325221811f,
00573       14.2473337358069f,
00574       15.2475085630373f,
00575       16.247661874701f
00576    };
00577 
00578   assert(image != (const Image *) NULL);
00579   assert(image->signature == MagickSignature);
00580   if (image->debug != MagickFalse)
00581     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00582   assert(UndefinedFilter < filter && filter < SentinelFilter);
00583   assert(exception != (ExceptionInfo *) NULL);
00584   assert(exception->signature == MagickSignature);
00585 
00586   resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
00587   if (resize_filter == (ResizeFilter *) NULL)
00588     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00589 
00590   /* defaults for the requested filter */
00591   filter_type = mapping[filter].filter;
00592   window_type = mapping[filter].window;
00593 
00594 
00595   /* Filter blur -- scaling both filter and support window */
00596   resize_filter->blur = blur;
00597   artifact=GetImageArtifact(image,"filter:blur");
00598   if (artifact != (const char *) NULL)
00599     resize_filter->blur = atof(artifact);
00600   if ( resize_filter->blur < MagickEpsilon )
00601     resize_filter->blur = (MagickRealType) MagickEpsilon;
00602 
00603   /* Modifications for Cylindrical filter use */
00604   if ( cylindrical != MagickFalse && filter != SincFilter ) {
00605     /* promote 1D Sinc Filter to a 2D Bessel filter */
00606     if ( filter_type == SincFilter )
00607       filter_type = BesselFilter;
00608     /* Prompote Lanczos (Sinc-Sinc) to Lanczos (Bessel-Bessel) */
00609     else if ( filter_type == LanczosFilter ) {
00610       filter_type = BesselFilter;
00611       window_type = BesselFilter;
00612     }
00613    /* Blur other filters appropriatally correct cylindrical usage */
00614     else if ( filter_type == GaussianFilter )
00615       /* Gaussian is scaled by  4*ln(2) and not 4*sqrt(2/MagickPI)
00616          - according to Paul Heckbert's paper on EWA resampling */
00617       resize_filter->blur *= 2.0*log(2.0)/sqrt(2.0/MagickPI);
00618     else if ( filter_type != BesselFilter )
00619       /* filters with a 1.0 zero root crossing by the first bessel_zero */
00620       resize_filter->blur *= bessel_zeros[0];
00621   }
00622 
00623   /* Override Filter Selection */
00624   artifact=GetImageArtifact(image,"filter:filter");
00625   if (artifact != (const char *) NULL) {
00626     /* raw filter request - no window function */
00627     filter_artifact=ParseMagickOption(MagickFilterOptions,
00628          MagickFalse,artifact);
00629     if ( UndefinedFilter < filter_artifact &&
00630              filter_artifact < SentinelFilter ) {
00631       filter_type = (FilterTypes) filter_artifact;
00632       window_type = BoxFilter;
00633     }
00634     /* Lanczos is nor a real filter but a self windowing Sinc/Bessel */
00635     if ( filter_artifact == LanczosFilter ) {
00636       filter_type = (cylindrical!=MagickFalse) ? BesselFilter : LanczosFilter;
00637       window_type = (cylindrical!=MagickFalse) ? BesselFilter : SincFilter;
00638     }
00639     /* Filter overwide with a specific window function? */
00640     artifact=GetImageArtifact(image,"filter:window");
00641     if (artifact != (const char *) NULL) {
00642       filter_artifact=ParseMagickOption(MagickFilterOptions,
00643             MagickFalse,artifact);
00644       if ( UndefinedFilter < filter_artifact &&
00645                filter_artifact < SentinelFilter ) {
00646         if ( filter_artifact != LanczosFilter )
00647           window_type = (FilterTypes) filter_artifact;
00648         else
00649           window_type = (cylindrical!=MagickFalse) ? BesselFilter : SincFilter;
00650       }
00651     }
00652   }
00653   else {
00654     /* window specified, but no filter function?  Assume Sinc/Bessel */
00655     artifact=GetImageArtifact(image,"filter:window");
00656     if (artifact != (const char *) NULL) {
00657       filter_artifact=ParseMagickOption(MagickFilterOptions,MagickFalse,
00658         artifact);
00659       if ( UndefinedFilter < filter_artifact &&
00660                filter_artifact < SentinelFilter ) {
00661         filter_type = (cylindrical!=MagickFalse) ? BesselFilter : SincFilter;
00662         if ( filter_artifact != LanczosFilter )
00663           window_type = (FilterTypes) filter_artifact;
00664         else
00665           window_type = filter_type;
00666       }
00667     }
00668   }
00669 
00670   resize_filter->filter   = filters[filter_type].function;
00671   resize_filter->support  = filters[filter_type].support;
00672   resize_filter->window   = filters[window_type].function;
00673   resize_filter->scale    = filters[window_type].scale;
00674   resize_filter->signature=MagickSignature;
00675 
00676   /* Filter support overrides */
00677   artifact=GetImageArtifact(image,"filter:lobes");
00678   if (artifact != (const char *) NULL) {
00679     long lobes = atol(artifact);
00680     if ( lobes < 1  ) lobes = 1;
00681     resize_filter->support = (MagickRealType) lobes;
00682     if ( filter_type == BesselFilter ) {
00683       if ( lobes > 16 ) lobes = 16;
00684       resize_filter->support = bessel_zeros[lobes-1];
00685     }
00686   }
00687   artifact=GetImageArtifact(image,"filter:support");
00688   if (artifact != (const char *) NULL)
00689     resize_filter->support = fabs(atof(artifact));
00690 
00691   /* Scale windowing function separatally to the support 'clipping' window
00692      that calling operator is planning to actually use. - Expert Use Only
00693   */
00694   resize_filter->window_support = resize_filter->support;
00695   artifact=GetImageArtifact(image,"filter:win-support");
00696   if (artifact != (const char *) NULL)
00697     resize_filter->window_support = fabs(atof(artifact));
00698 
00699   /* Set Cubic Spline B,C values, calculate Cubic coefficents */
00700   B=0.0;
00701   C=0.0;
00702   if ( filters[filter_type].function == CubicBC
00703        || filters[window_type].function == CubicBC ) {
00704     if ( filters[filter_type].function == CubicBC ) {
00705       B=filters[filter_type].B;
00706       C=filters[filter_type].C;
00707     }
00708     else if ( filters[window_type].function == CubicBC ) {
00709       B=filters[window_type].B;
00710       C=filters[window_type].C;
00711     }
00712     artifact=GetImageArtifact(image,"filter:b");
00713     if (artifact != (const char *) NULL) {
00714       B=atof(artifact);
00715       C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter */
00716       artifact=GetImageArtifact(image,"filter:c");
00717       if (artifact != (const char *) NULL)
00718         C=atof(artifact);
00719     }
00720     else {
00721       artifact=GetImageArtifact(image,"filter:c");
00722       if (artifact != (const char *) NULL) {
00723         C=atof(artifact);
00724         B=1.0-2.0*C;  /* Calculate B as if it is a Keys cubic filter */
00725       }
00726     }
00727     /* Convert B,C values into Cubic Coefficents - See CubicBC() */
00728     resize_filter->cubic[0]=(  6.0 -2.0*B       )/6.0;
00729     resize_filter->cubic[1]=0.0;
00730     resize_filter->cubic[2]=(-18.0+12.0*B+ 6.0*C)/6.0;
00731     resize_filter->cubic[3]=( 12.0- 9.0*B- 6.0*C)/6.0;
00732     resize_filter->cubic[4]=(       8.0*B+24.0*C)/6.0;
00733     resize_filter->cubic[5]=(     -12.0*B-48.0*C)/6.0;
00734     resize_filter->cubic[6]=(       6.0*B+30.0*C)/6.0;
00735     resize_filter->cubic[7]=(     - 1.0*B- 6.0*C)/6.0;
00736   }
00737   artifact=GetImageArtifact(image,"filter:verbose");
00738   if (artifact != (const char *) NULL)
00739     {
00740       double
00741         support,
00742         x;
00743 
00744       /*
00745         Output filter graph -- for graphing filter result.
00746       */
00747       support=GetResizeFilterSupport(resize_filter);
00748       (void) printf("# support = %lg\n",support);
00749       for (x=0.0; x <= support; x+=0.01f)
00750         (void) printf("%5.2lf\t%lf\n",x,(double) GetResizeFilterWeight(
00751           resize_filter,x));
00752       (void) printf("%5.2lf\t%lf\n",support,0.0);
00753     }
00754   return(resize_filter);
00755 }
00756 
00757 /*
00758 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00759 %                                                                             %
00760 %                                                                             %
00761 %                                                                             %
00762 %   A d a p t i v e R e s i z e I m a g e                                     %
00763 %                                                                             %
00764 %                                                                             %
00765 %                                                                             %
00766 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00767 %
00768 %  AdaptiveResizeImage() adaptively resize image with pixel resampling.
00769 %
00770 %  The format of the AdaptiveResizeImage method is:
00771 %
00772 %      Image *AdaptiveResizeImage(const Image *image,
00773 %        const unsigned long columns,const unsigned long rows,
00774 %        ExceptionInfo *exception)
00775 %
00776 %  A description of each parameter follows:
00777 %
00778 %    o image: the image.
00779 %
00780 %    o columns: the number of columns in the resized image.
00781 %
00782 %    o rows: the number of rows in the resized image.
00783 %
00784 %    o exception: return any errors or warnings in this structure.
00785 %
00786 */
00787 MagickExport Image *AdaptiveResizeImage(const Image *image,
00788   const unsigned long columns,const unsigned long rows,ExceptionInfo *exception)
00789 {
00790 #define AdaptiveResizeImageTag  "Resize/Image"
00791 
00792   Image
00793     *resize_image;
00794 
00795   long
00796     y;
00797 
00798   MagickBooleanType
00799     proceed;
00800 
00801   MagickPixelPacket
00802     pixel;
00803 
00804   PointInfo
00805     offset;
00806 
00807   ResampleFilter
00808     *resample_filter;
00809 
00810   CacheView
00811     *resize_view;
00812 
00813   /*
00814     Adaptively resize image.
00815   */
00816   assert(image != (const Image *) NULL);
00817   assert(image->signature == MagickSignature);
00818   if (image->debug != MagickFalse)
00819     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00820   assert(exception != (ExceptionInfo *) NULL);
00821   assert(exception->signature == MagickSignature);
00822   if ((columns == 0) || (rows == 0))
00823     return((Image *) NULL);
00824   if ((columns == image->columns) && (rows == image->rows))
00825     return(CloneImage(image,0,0,MagickTrue,exception));
00826   resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
00827   if (resize_image == (Image *) NULL)
00828     return((Image *) NULL);
00829   if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
00830     {
00831       InheritException(exception,&resize_image->exception);
00832       resize_image=DestroyImage(resize_image);
00833       return((Image *) NULL);
00834     }
00835   GetMagickPixelPacket(image,&pixel);
00836   resample_filter=AcquireResampleFilter(image,exception);
00837   if (image->interpolate == UndefinedInterpolatePixel)
00838     (void) SetResampleFilterInterpolateMethod(resample_filter,
00839       MeshInterpolatePixel);
00840   resize_view=AcquireCacheView(resize_image);
00841   for (y=0; y < (long) resize_image->rows; y++)
00842   {
00843     register IndexPacket
00844       *__restrict resize_indexes;
00845 
00846     register long
00847       x;
00848 
00849     register PixelPacket
00850       *__restrict q;
00851 
00852     q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
00853       exception);
00854     if (q == (PixelPacket *) NULL)
00855       break;
00856     resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
00857     offset.y=((MagickRealType) y*image->rows/resize_image->rows);
00858     for (x=0; x < (long) resize_image->columns; x++)
00859     {
00860       offset.x=((MagickRealType) x*image->columns/resize_image->columns);
00861       (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
00862         &pixel);
00863       SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
00864       q++;
00865     }
00866     if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
00867       break;
00868     proceed=SetImageProgress(image,AdaptiveResizeImageTag,y,image->rows);
00869     if (proceed == MagickFalse)
00870       break;
00871   }
00872   resample_filter=DestroyResampleFilter(resample_filter);
00873   resize_view=DestroyCacheView(resize_view);
00874   return(resize_image);
00875 }
00876 
00877 /*
00878 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00879 %                                                                             %
00880 %                                                                             %
00881 %                                                                             %
00882 +   B e s s e l O r d e r O n e                                               %
00883 %                                                                             %
00884 %                                                                             %
00885 %                                                                             %
00886 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00887 %
00888 %  BesselOrderOne() computes the Bessel function of x of the first kind of
00889 %  order 0:
00890 %
00891 %    Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
00892 %
00893 %       j1(x) = x*j1(x);
00894 %
00895 %    For x in (8,inf)
00896 %
00897 %       j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
00898 %
00899 %    where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
00900 %
00901 %       cos(x1) =  cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
00902 %               =  1/sqrt(2) * (sin(x) - cos(x))
00903 %       sin(x1) =  sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
00904 %               = -1/sqrt(2) * (sin(x) + cos(x))
00905 %
00906 %  The format of the BesselOrderOne method is:
00907 %
00908 %      MagickRealType BesselOrderOne(MagickRealType x)
00909 %
00910 %  A description of each parameter follows:
00911 %
00912 %    o x: MagickRealType value.
00913 %
00914 */
00915 
00916 #undef I0
00917 static MagickRealType I0(MagickRealType x)
00918 {
00919   MagickRealType
00920     sum,
00921     t,
00922     y;
00923 
00924   register long
00925     i;
00926 
00927   /*
00928     Zeroth order Bessel function of the first kind.
00929   */
00930   sum=1.0;
00931   y=x*x/4.0;
00932   t=y;
00933   for (i=2; t > MagickEpsilon; i++)
00934   {
00935     sum+=t;
00936     t*=y/((MagickRealType) i*i);
00937   }
00938   return(sum);
00939 }
00940 
00941 #undef J1
00942 static MagickRealType J1(MagickRealType x)
00943 {
00944   MagickRealType
00945     p,
00946     q;
00947 
00948   register long
00949     i;
00950 
00951   static const double
00952     Pone[] =
00953     {
00954        0.581199354001606143928050809e+21,
00955       -0.6672106568924916298020941484e+20,
00956        0.2316433580634002297931815435e+19,
00957       -0.3588817569910106050743641413e+17,
00958        0.2908795263834775409737601689e+15,
00959       -0.1322983480332126453125473247e+13,
00960        0.3413234182301700539091292655e+10,
00961       -0.4695753530642995859767162166e+7,
00962        0.270112271089232341485679099e+4
00963     },
00964     Qone[] =
00965     {
00966       0.11623987080032122878585294e+22,
00967       0.1185770712190320999837113348e+20,
00968       0.6092061398917521746105196863e+17,
00969       0.2081661221307607351240184229e+15,
00970       0.5243710262167649715406728642e+12,
00971       0.1013863514358673989967045588e+10,
00972       0.1501793594998585505921097578e+7,
00973       0.1606931573481487801970916749e+4,
00974       0.1e+1
00975     };
00976 
00977   p=Pone[8];
00978   q=Qone[8];
00979   for (i=7; i >= 0; i--)
00980   {
00981     p=p*x*x+Pone[i];
00982     q=q*x*x+Qone[i];
00983   }
00984   return(p/q);
00985 }
00986 
00987 #undef P1
00988 static MagickRealType P1(MagickRealType x)
00989 {
00990   MagickRealType
00991     p,
00992     q;
00993 
00994   register long
00995     i;
00996 
00997   static const double
00998     Pone[] =
00999     {
01000       0.352246649133679798341724373e+5,
01001       0.62758845247161281269005675e+5,
01002       0.313539631109159574238669888e+5,
01003       0.49854832060594338434500455e+4,
01004       0.2111529182853962382105718e+3,
01005       0.12571716929145341558495e+1
01006     },
01007     Qone[] =
01008     {
01009       0.352246649133679798068390431e+5,
01010       0.626943469593560511888833731e+5,
01011       0.312404063819041039923015703e+5,
01012       0.4930396490181088979386097e+4,
01013       0.2030775189134759322293574e+3,
01014       0.1e+1
01015     };
01016 
01017   p=Pone[5];
01018   q=Qone[5];
01019   for (i=4; i >= 0; i--)
01020   {
01021     p=p*(8.0/x)*(8.0/x)+Pone[i];
01022     q=q*(8.0/x)*(8.0/x)+Qone[i];
01023   }
01024   return(p/q);
01025 }
01026 
01027 #undef Q1
01028 static MagickRealType Q1(MagickRealType x)
01029 {
01030   MagickRealType
01031     p,
01032     q;
01033 
01034   register long
01035     i;
01036 
01037   static const double
01038     Pone[] =
01039     {
01040       0.3511751914303552822533318e+3,
01041       0.7210391804904475039280863e+3,
01042       0.4259873011654442389886993e+3,
01043       0.831898957673850827325226e+2,
01044       0.45681716295512267064405e+1,
01045       0.3532840052740123642735e-1
01046     },
01047     Qone[] =
01048     {
01049       0.74917374171809127714519505e+4,
01050       0.154141773392650970499848051e+5,
01051       0.91522317015169922705904727e+4,
01052       0.18111867005523513506724158e+4,
01053       0.1038187585462133728776636e+3,
01054       0.1e+1
01055     };
01056 
01057   p=Pone[5];
01058   q=Qone[5];
01059   for (i=4; i >= 0; i--)
01060   {
01061     p=p*(8.0/x)*(8.0/x)+Pone[i];
01062     q=q*(8.0/x)*(8.0/x)+Qone[i];
01063   }
01064   return(p/q);
01065 }
01066 
01067 static MagickRealType BesselOrderOne(MagickRealType x)
01068 {
01069   MagickRealType
01070     p,
01071     q;
01072 
01073   if (x == 0.0)
01074     return(0.0);
01075   p=x;
01076   if (x < 0.0)
01077     x=(-x);
01078   if (x < 8.0)
01079     return(p*J1(x));
01080   q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
01081     cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
01082     cos((double) x))));
01083   if (p < 0.0)
01084     q=(-q);
01085   return(q);
01086 }
01087 
01088 /*
01089 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01090 %                                                                             %
01091 %                                                                             %
01092 %                                                                             %
01093 +   D e s t r o y R e s i z e F i l t e r                                     %
01094 %                                                                             %
01095 %                                                                             %
01096 %                                                                             %
01097 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01098 %
01099 %  DestroyResizeFilter() destroy the resize filter.
01100 %
01101 %  The format of the AcquireResizeFilter method is:
01102 %
01103 %      ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
01104 %
01105 %  A description of each parameter follows:
01106 %
01107 %    o resize_filter: the resize filter.
01108 %
01109 */
01110 MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
01111 {
01112   assert(resize_filter != (ResizeFilter *) NULL);
01113   assert(resize_filter->signature == MagickSignature);
01114   resize_filter->signature=(~MagickSignature);
01115   resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
01116   return(resize_filter);
01117 }
01118 
01119 /*
01120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01121 %                                                                             %
01122 %                                                                             %
01123 %                                                                             %
01124 +   G e t R e s i z e F i l t e r S u p p o r t                               %
01125 %                                                                             %
01126 %                                                                             %
01127 %                                                                             %
01128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01129 %
01130 %  GetResizeFilterSupport() return the current support window size for this
01131 %  filter.  Note that this may have been enlarged by filter:blur factor.
01132 %
01133 %  The format of the GetResizeFilterSupport method is:
01134 %
01135 %      MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
01136 %
01137 %  A description of each parameter follows:
01138 %
01139 %    o filter: Image filter to use.
01140 %
01141 */
01142 MagickExport MagickRealType GetResizeFilterSupport(
01143   const ResizeFilter *resize_filter)
01144 {
01145   assert(resize_filter != (ResizeFilter *) NULL);
01146   assert(resize_filter->signature == MagickSignature);
01147   return(resize_filter->support*resize_filter->blur);
01148 }
01149 
01150 /*
01151 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01152 %                                                                             %
01153 %                                                                             %
01154 %                                                                             %
01155 +   G e t R e s i z e F i l t e r W e i g h t                                 %
01156 %                                                                             %
01157 %                                                                             %
01158 %                                                                             %
01159 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01160 %
01161 %  GetResizeFilterWeight evaluates the specified resize filter at the point x
01162 %  which usally lies between zero and the filters current 'support' and
01163 %  returns the weight of the filter function at that point.
01164 %
01165 %  The format of the GetResizeFilterWeight method is:
01166 %
01167 %      MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
01168 %        const MagickRealType x)
01169 %
01170 %  A description of each parameter follows:
01171 %
01172 %    o filter: the filter type.
01173 %
01174 %    o x: the point.
01175 %
01176 */
01177 MagickExport MagickRealType GetResizeFilterWeight(
01178   const ResizeFilter *resize_filter,const MagickRealType x)
01179 {
01180   MagickRealType
01181     blur,
01182     scale;
01183 
01184   /*
01185     Windowing function - scale the weighting filter by this amount.
01186   */
01187   assert(resize_filter != (ResizeFilter *) NULL);
01188   assert(resize_filter->signature == MagickSignature);
01189   blur=fabs(x)/resize_filter->blur;  /* X offset with blur scaling */
01190   if ((resize_filter->window_support < MagickEpsilon) ||
01191       (resize_filter->window == Box))
01192     scale=1.0;  /* Point/Box Filter -- avoid division by zero */
01193   else
01194     {
01195       scale=resize_filter->scale/resize_filter->window_support;
01196       scale=resize_filter->window(blur*scale,resize_filter);
01197     }
01198   return(scale*resize_filter->filter(blur,resize_filter));
01199 }
01200 
01201 /*
01202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01203 %                                                                             %
01204 %                                                                             %
01205 %                                                                             %
01206 %   M a g n i f y I m a g e                                                   %
01207 %                                                                             %
01208 %                                                                             %
01209 %                                                                             %
01210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01211 %
01212 %  MagnifyImage() is a convenience method that scales an image proportionally
01213 %  to twice its size.
01214 %
01215 %  The format of the MagnifyImage method is:
01216 %
01217 %      Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
01218 %
01219 %  A description of each parameter follows:
01220 %
01221 %    o image: the image.
01222 %
01223 %    o exception: return any errors or warnings in this structure.
01224 %
01225 */
01226 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
01227 {
01228   Image
01229     *magnify_image;
01230 
01231   assert(image != (Image *) NULL);
01232   assert(image->signature == MagickSignature);
01233   if (image->debug != MagickFalse)
01234     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01235   assert(exception != (ExceptionInfo *) NULL);
01236   assert(exception->signature == MagickSignature);
01237   magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
01238     1.0,exception);
01239   return(magnify_image);
01240 }
01241 
01242 /*
01243 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01244 %                                                                             %
01245 %                                                                             %
01246 %                                                                             %
01247 %   M i n i f y I m a g e                                                     %
01248 %                                                                             %
01249 %                                                                             %
01250 %                                                                             %
01251 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01252 %
01253 %  MinifyImage() is a convenience method that scales an image proportionally
01254 %  to half its size.
01255 %
01256 %  The format of the MinifyImage method is:
01257 %
01258 %      Image *MinifyImage(const Image *image,ExceptionInfo *exception)
01259 %
01260 %  A description of each parameter follows:
01261 %
01262 %    o image: the image.
01263 %
01264 %    o exception: return any errors or warnings in this structure.
01265 %
01266 */
01267 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
01268 {
01269   Image
01270     *minify_image;
01271 
01272   assert(image != (Image *) NULL);
01273   assert(image->signature == MagickSignature);
01274   if (image->debug != MagickFalse)
01275     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01276   assert(exception != (ExceptionInfo *) NULL);
01277   assert(exception->signature == MagickSignature);
01278   minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
01279     1.0,exception);
01280   return(minify_image);
01281 }
01282 
01283 /*
01284 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01285 %                                                                             %
01286 %                                                                             %
01287 %                                                                             %
01288 %   R e s a m p l e I m a g e                                                 %
01289 %                                                                             %
01290 %                                                                             %
01291 %                                                                             %
01292 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01293 %
01294 %  ResampleImage() resize image in terms of its pixel size, so that when
01295 %  displayed at the given resolution it will be the same size in terms of
01296 %  real world units as the original image at the original resolution.
01297 %
01298 %  The format of the ResampleImage method is:
01299 %
01300 %      Image *ResampleImage(Image *image,const double x_resolution,
01301 %        const double y_resolution,const FilterTypes filter,const double blur,
01302 %        ExceptionInfo *exception)
01303 %
01304 %  A description of each parameter follows:
01305 %
01306 %    o image: the image to be resized to fit the given resolution.
01307 %
01308 %    o x_resolution: the new image x resolution.
01309 %
01310 %    o y_resolution: the new image y resolution.
01311 %
01312 %    o filter: Image filter to use.
01313 %
01314 %    o blur: the blur factor where > 1 is blurry, < 1 is sharp.
01315 %
01316 */
01317 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
01318   const double y_resolution,const FilterTypes filter,const double blur,
01319   ExceptionInfo *exception)
01320 {
01321 #define ResampleImageTag  "Resample/Image"
01322 
01323   Image
01324     *resample_image;
01325 
01326   unsigned long
01327     height,
01328     width;
01329 
01330   /*
01331     Initialize sampled image attributes.
01332   */
01333   assert(image != (const Image *) NULL);
01334   assert(image->signature == MagickSignature);
01335   if (image->debug != MagickFalse)
01336     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01337   assert(exception != (ExceptionInfo *) NULL);
01338   assert(exception->signature == MagickSignature);
01339   width=(unsigned long) (x_resolution*image->columns/
01340     (image->x_resolution == 0.0 ? 72.0 : image->x_resolution)+0.5);
01341   height=(unsigned long) (y_resolution*image->rows/
01342     (image->y_resolution == 0.0 ? 72.0 : image->y_resolution)+0.5);
01343   resample_image=ResizeImage(image,width,height,filter,blur,exception);
01344   if (resample_image != (Image *) NULL)
01345     {
01346       resample_image->x_resolution=x_resolution;
01347       resample_image->y_resolution=y_resolution;
01348     }
01349   return(resample_image);
01350 }
01351 #if defined(MAGICKCORE_LQR_DELEGATE)
01352 
01353 /*
01354 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01355 %                                                                             %
01356 %                                                                             %
01357 %                                                                             %
01358 %   L i q u i d R e s c a l e I m a g e                                       %
01359 %                                                                             %
01360 %                                                                             %
01361 %                                                                             %
01362 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01363 %
01364 %  LiquidRescaleImage() rescales image with seam carving.
01365 %
01366 %  The format of the LiquidRescaleImage method is:
01367 %
01368 %      Image *LiquidRescaleImage(const Image *image,
01369 %        const unsigned long columns,const unsigned long rows,
01370 %        const double delta_x,const double rigidity,ExceptionInfo *exception)
01371 %
01372 %  A description of each parameter follows:
01373 %
01374 %    o image: the image.
01375 %
01376 %    o columns: the number of columns in the rescaled image.
01377 %
01378 %    o rows: the number of rows in the rescaled image.
01379 %
01380 %    o delta_x: maximum seam transversal step (0 means straight seams).
01381 %
01382 %    o rigidity: introduce a bias for non-straight seams (typically 0).
01383 %
01384 %    o exception: return any errors or warnings in this structure.
01385 %
01386 */
01387 MagickExport Image *LiquidRescaleImage(const Image *image,
01388   const unsigned long columns,const unsigned long rows,
01389   const double delta_x,const double rigidity,ExceptionInfo *exception)
01390 {
01391 #define LiquidRescaleImageTag  "Rescale/Image"
01392 
01393   const char
01394     *map;
01395 
01396   guchar
01397     *packet;
01398 
01399   Image
01400     *rescale_image;
01401 
01402   int
01403     x,
01404     y;
01405 
01406   LqrCarver
01407     *carver;
01408 
01409   LqrRetVal
01410     lqr_status;
01411 
01412   MagickBooleanType
01413     status;
01414 
01415   MagickPixelPacket
01416     pixel;
01417 
01418   unsigned char
01419     *pixels;
01420 
01421   /*
01422     Liquid rescale image.
01423   */
01424   assert(image != (const Image *) NULL);
01425   assert(image->signature == MagickSignature);
01426   if (image->debug != MagickFalse)
01427     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01428   assert(exception != (ExceptionInfo *) NULL);
01429   assert(exception->signature == MagickSignature);
01430   if ((columns == 0) || (rows == 0))
01431     return((Image *) NULL);
01432   if ((columns == image->columns) && (rows == image->rows))
01433     return(CloneImage(image,0,0,MagickTrue,exception));
01434   if ((columns <= 2) || (rows <= 2))
01435     return(ZoomImage(image,columns,rows,exception));
01436   if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
01437     {
01438       Image
01439         *resize_image;
01440 
01441       unsigned long
01442         height,
01443         width;
01444 
01445       /*
01446         Honor liquid resize size limitations.
01447       */
01448       for (width=image->columns; columns >= (2*width-1); width*=2);
01449       for (height=image->rows; rows >= (2*height-1); height*=2);
01450       resize_image=ResizeImage(image,width,height,image->filter,image->blur,
01451         exception);
01452       if (resize_image == (Image *) NULL)
01453         return((Image *) NULL);
01454       rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
01455         rigidity,exception);
01456       resize_image=DestroyImage(resize_image);
01457       return(rescale_image);
01458     }
01459   map="RGB";
01460   if (image->matte == MagickFalse)
01461     map="RGBA";
01462   if (image->colorspace == CMYKColorspace)
01463     {
01464       map="CMYK";
01465       if (image->matte == MagickFalse)
01466         map="CMYKA";
01467     }
01468   pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
01469     strlen(map)*sizeof(*pixels));
01470   if (pixels == (unsigned char *) NULL)
01471     return((Image *) NULL);
01472   status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
01473     pixels,exception);
01474   if (status == MagickFalse)
01475     {
01476       pixels=(unsigned char *) RelinquishMagickMemory(pixels);
01477       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01478     }
01479   carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
01480   if (carver == (LqrCarver *) NULL)
01481     {
01482       pixels=(unsigned char *) RelinquishMagickMemory(pixels);
01483       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01484     }
01485   lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
01486   lqr_status=lqr_carver_resize(carver,columns,rows);
01487   rescale_image=CloneImage(image,lqr_carver_get_width(carver),
01488     lqr_carver_get_height(carver),MagickTrue,exception);
01489   if (rescale_image == (Image *) NULL)
01490     {
01491       pixels=(unsigned char *) RelinquishMagickMemory(pixels);
01492       return((Image *) NULL);
01493     }
01494   if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
01495     {
01496       InheritException(exception,&rescale_image->exception);
01497       rescale_image=DestroyImage(rescale_image);
01498       return((Image *) NULL);
01499     }
01500   GetMagickPixelPacket(rescale_image,&pixel);
01501   (void) lqr_carver_scan_reset(carver);
01502   while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
01503   {
01504     register IndexPacket
01505       *__restrict rescale_indexes;
01506 
01507     register PixelPacket
01508       *__restrict q;
01509 
01510     q=QueueAuthenticPixels(rescale_image,x,y,1,1,exception);
01511     if (q == (PixelPacket *) NULL)
01512       break;
01513     rescale_indexes=GetAuthenticIndexQueue(rescale_image);
01514     pixel.red=QuantumRange*(packet[0]/255.0);
01515     pixel.green=QuantumRange*(packet[1]/255.0);
01516     pixel.blue=QuantumRange*(packet[2]/255.0);
01517     if (image->colorspace != CMYKColorspace)
01518       {
01519         if (image->matte == MagickFalse)
01520           pixel.opacity=QuantumRange*(packet[3]/255.0);
01521       }
01522     else
01523       {
01524         pixel.index=QuantumRange*(packet[3]/255.0);
01525         if (image->matte == MagickFalse)
01526           pixel.opacity=QuantumRange*(packet[4]/255.0);
01527       }
01528     SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
01529     if (SyncAuthenticPixels(rescale_image,exception) == MagickFalse)
01530       break;
01531   }
01532   /*
01533     Relinquish resources.
01534   */
01535   lqr_carver_destroy(carver);
01536   return(rescale_image);
01537 }
01538 #else
01539 MagickExport Image *LiquidRescaleImage(const Image *image,
01540   const unsigned long magick_unused(columns),
01541   const unsigned long magick_unused(rows),const double magick_unused(delta_x),
01542   const double magick_unused(rigidity),ExceptionInfo *exception)
01543 {
01544   assert(image != (const Image *) NULL);
01545   assert(image->signature == MagickSignature);
01546   if (image->debug != MagickFalse)
01547     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01548   assert(exception != (ExceptionInfo *) NULL);
01549   assert(exception->signature == MagickSignature);
01550   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
01551     "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
01552   return((Image *) NULL);
01553 }
01554 #endif
01555 
01556 /*
01557 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01558 %                                                                             %
01559 %                                                                             %
01560 %                                                                             %
01561 %   R e s i z e I m a g e                                                     %
01562 %                                                                             %
01563 %                                                                             %
01564 %                                                                             %
01565 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01566 %
01567 %  ResizeImage() scales an image to the desired dimensions, using the given
01568 %  filter (see AcquireFilterInfo() ).
01569 %
01570 %  If an undefined filter is given the filter defaults to Mitchell for a
01571 %  colormapped image, a image with a matte channel, or if the image is
01572 %  enlarged.  Otherwise the filter defaults to a Lanczos.
01573 %
01574 %  ResizeImage() was inspired by Paul Heckbert's "zoom" program.
01575 %
01576 %  The format of the ResizeImage method is:
01577 %
01578 %      Image *ResizeImage(Image *image,const unsigned long columns,
01579 %        const unsigned long rows,const FilterTypes filter,const double blur,
01580 %        ExceptionInfo *exception)
01581 %
01582 %  A description of each parameter follows:
01583 %
01584 %    o image: the image.
01585 %
01586 %    o columns: the number of columns in the scaled image.
01587 %
01588 %    o rows: the number of rows in the scaled image.
01589 %
01590 %    o filter: Image filter to use.
01591 %
01592 %    o blur: the blur factor where > 1 is blurry, < 1 is sharp.
01593 %            Typically set this to 1.0.
01594 %
01595 %    o exception: return any errors or warnings in this structure.
01596 %
01597 */
01598 
01599 typedef struct _ContributionInfo
01600 {
01601   MagickRealType
01602     weight;
01603 
01604   long
01605     pixel;
01606 } ContributionInfo;
01607 
01608 static ContributionInfo **DestroyContributionThreadSet(
01609   ContributionInfo **contribution)
01610 {
01611   register long
01612     i;
01613 
01614   assert(contribution != (ContributionInfo **) NULL);
01615   for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
01616     if (contribution[i] != (ContributionInfo *) NULL)
01617       contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
01618         contribution[i]);
01619   contribution=(ContributionInfo **) RelinquishAlignedMemory(contribution);
01620   return(contribution);
01621 }
01622 
01623 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
01624 {
01625   register long
01626     i;
01627 
01628   ContributionInfo
01629     **contribution;
01630 
01631   unsigned long
01632     number_threads;
01633 
01634   number_threads=GetOpenMPMaximumThreads();
01635   contribution=(ContributionInfo **) AcquireAlignedMemory(number_threads,
01636     sizeof(*contribution));
01637   if (contribution == (ContributionInfo **) NULL)
01638     return((ContributionInfo **) NULL);
01639   (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
01640   for (i=0; i < (long) number_threads; i++)
01641   {
01642     contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
01643       sizeof(**contribution));
01644     if (contribution[i] == (ContributionInfo *) NULL)
01645       return(DestroyContributionThreadSet(contribution));
01646   }
01647   return(contribution);
01648 }
01649 
01650 static inline double MagickMax(const double x,const double y)
01651 {
01652   if (x > y)
01653     return(x);
01654   return(y);
01655 }
01656 
01657 static inline double MagickMin(const double x,const double y)
01658 {
01659   if (x < y)
01660     return(x);
01661   return(y);
01662 }
01663 
01664 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
01665   const Image *image,Image *resize_image,const MagickRealType x_factor,
01666   const MagickSizeType span,MagickOffsetType *quantum,ExceptionInfo *exception)
01667 {
01668 #define ResizeImageTag  "Resize/Image"
01669 
01670   ClassType
01671     storage_class;
01672 
01673   ContributionInfo
01674     **contributions;
01675 
01676   long
01677     x;
01678 
01679   MagickBooleanType
01680     status;
01681 
01682   MagickPixelPacket
01683     zero;
01684 
01685   MagickRealType
01686     scale,
01687     support;
01688 
01689   CacheView
01690     *image_view,
01691     *resize_view;
01692 
01693   /*
01694     Apply filter to resize horizontally from image to resize image.
01695   */
01696   scale=MagickMax(1.0/x_factor,1.0);
01697   support=scale*GetResizeFilterSupport(resize_filter);
01698   storage_class=support > 0.5 ? DirectClass : image->storage_class;
01699   if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
01700     {
01701       InheritException(exception,&resize_image->exception);
01702       return(MagickFalse);
01703     }
01704   if (support < 0.5)
01705     {
01706       /*
01707         Support too small even for nearest neighbour:  reduce to point sampling.
01708       */
01709       support=(MagickRealType) 0.5;
01710       scale=1.0;
01711     }
01712   contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
01713   if (contributions == (ContributionInfo **) NULL)
01714     {
01715       (void) ThrowMagickException(exception,GetMagickModule(),
01716         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
01717       return(MagickFalse);
01718     }
01719   status=MagickTrue;
01720   scale=1.0/scale;
01721   (void) ResetMagickMemory(&zero,0,sizeof(zero));
01722   image_view=AcquireCacheView(image);
01723   resize_view=AcquireCacheView(resize_image);
01724 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01725   #pragma omp parallel for shared(status)
01726 #endif
01727   for (x=0; x < (long) resize_image->columns; x++)
01728   {
01729     long
01730       n,
01731       start,
01732       stop;
01733 
01734     MagickRealType
01735       center,
01736       density;
01737 
01738     register const IndexPacket
01739       *__restrict indexes;
01740 
01741     register const PixelPacket
01742       *__restrict p;
01743 
01744     register ContributionInfo
01745       *__restrict contribution;
01746 
01747     register IndexPacket
01748       *__restrict resize_indexes;
01749 
01750     register long
01751       y;
01752 
01753     register PixelPacket
01754       *__restrict q;
01755 
01756     if (status == MagickFalse)
01757       continue;
01758     center=(MagickRealType) (x+0.5)/x_factor;
01759     start=(long) (MagickMax(center-support-MagickEpsilon,0.0)+0.5);
01760     stop=(long) (MagickMin(center+support,(double) image->columns)+0.5);
01761     density=0.0;
01762     contribution=contributions[GetOpenMPThreadId()];
01763     for (n=0; n < (stop-start); n++)
01764     {
01765       contribution[n].pixel=start+n;
01766       contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
01767         ((MagickRealType) (start+n)-center+0.5));
01768       density+=contribution[n].weight;
01769     }
01770     if ((density != 0.0) && (density != 1.0))
01771       {
01772         register long
01773           i;
01774 
01775         /*
01776           Normalize.
01777         */
01778         density=1.0/density;
01779         for (i=0; i < n; i++)
01780           contribution[i].weight*=density;
01781       }
01782     p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,
01783       (unsigned long) (contribution[n-1].pixel-contribution[0].pixel+1),
01784       image->rows,exception);
01785     q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
01786       exception);
01787     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
01788       {
01789         status=MagickFalse;
01790         continue;
01791       }
01792     indexes=GetCacheViewVirtualIndexQueue(image_view);
01793     resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
01794     for (y=0; y < (long) resize_image->rows; y++)
01795     {
01796       long
01797         j;
01798 
01799       MagickPixelPacket
01800         pixel;
01801 
01802       MagickRealType
01803         alpha;
01804 
01805       register long
01806         i;
01807 
01808       pixel=zero;
01809       if (image->matte == MagickFalse)
01810         {
01811           for (i=0; i < n; i++)
01812           {
01813             j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
01814               (contribution[i].pixel-contribution[0].pixel);
01815             alpha=contribution[i].weight;
01816             pixel.red+=alpha*(p+j)->red;
01817             pixel.green+=alpha*(p+j)->green;
01818             pixel.blue+=alpha*(p+j)->blue;
01819             pixel.opacity+=alpha*(p+j)->opacity;
01820           }
01821           q->red=RoundToQuantum(pixel.red);
01822           q->green=RoundToQuantum(pixel.green);
01823           q->blue=RoundToQuantum(pixel.blue);
01824           q->opacity=RoundToQuantum(pixel.opacity);
01825           if ((image->colorspace == CMYKColorspace) &&
01826               (resize_image->colorspace == CMYKColorspace))
01827             {
01828               for (i=0; i < n; i++)
01829               {
01830                 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
01831                   (contribution[i].pixel-contribution[0].pixel);
01832                 alpha=contribution[i].weight;
01833                 pixel.index+=alpha*indexes[j];
01834               }
01835               resize_indexes[y]=(IndexPacket) RoundToQuantum(pixel.index);
01836             }
01837         }
01838       else
01839         {
01840           MagickRealType
01841             gamma;
01842 
01843           gamma=0.0;
01844           for (i=0; i < n; i++)
01845           {
01846             j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
01847               (contribution[i].pixel-contribution[0].pixel);
01848             alpha=contribution[i].weight*QuantumScale*((MagickRealType)
01849               QuantumRange-(p+j)->opacity);
01850             pixel.red+=alpha*(p+j)->red;
01851             pixel.green+=alpha*(p+j)->green;
01852             pixel.blue+=alpha*(p+j)->blue;
01853             pixel.opacity+=contribution[i].weight*(p+j)->opacity;
01854             gamma+=alpha;
01855           }
01856           gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
01857           q->red=RoundToQuantum(gamma*pixel.red);
01858           q->green=RoundToQuantum(gamma*pixel.green);
01859           q->blue=RoundToQuantum(gamma*pixel.blue);
01860           q->opacity=RoundToQuantum(pixel.opacity);
01861           if ((image->colorspace == CMYKColorspace) &&
01862               (resize_image->colorspace == CMYKColorspace))
01863             {
01864               for (i=0; i < n; i++)
01865               {
01866                 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
01867                   (contribution[i].pixel-contribution[0].pixel);
01868                 alpha=contribution[i].weight*QuantumScale*((MagickRealType)
01869                   QuantumRange-(p+j)->opacity);
01870                 pixel.index+=alpha*indexes[j];
01871               }
01872               resize_indexes[y]=(IndexPacket) RoundToQuantum(gamma*pixel.index);
01873             }
01874         }
01875       if ((resize_image->storage_class == PseudoClass) &&
01876           (image->storage_class == PseudoClass))
01877         {
01878           i=(long) (MagickMin(MagickMax(center,(double) start),(double) stop-
01879             1.0)+0.5);
01880           j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
01881             (contribution[i-start].pixel-contribution[0].pixel);
01882           resize_indexes[y]=indexes[j];
01883         }
01884       q++;
01885     }
01886     if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
01887       status=MagickFalse;
01888     if (image->progress_monitor != (MagickProgressMonitor) NULL)
01889       {
01890         MagickBooleanType
01891           proceed;
01892 
01893 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01894   #pragma omp critical (MagickCore_HorizontalFilter)
01895 #endif
01896         proceed=SetImageProgress(image,ResizeImageTag,(*quantum)++,span);
01897         if (proceed == MagickFalse)
01898           status=MagickFalse;
01899       }
01900   }
01901   resize_view=DestroyCacheView(resize_view);
01902   image_view=DestroyCacheView(image_view);
01903   contributions=DestroyContributionThreadSet(contributions);
01904   return(status);
01905 }
01906 
01907 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
01908   const Image *image,Image *resize_image,const MagickRealType y_factor,
01909   const MagickSizeType span,MagickOffsetType *quantum,ExceptionInfo *exception)
01910 {
01911   ClassType
01912     storage_class;
01913 
01914   ContributionInfo
01915     **contributions;
01916 
01917   long
01918     y;
01919 
01920   MagickBooleanType
01921     status;
01922 
01923   MagickPixelPacket
01924     zero;
01925 
01926   MagickRealType
01927     scale,
01928     support;
01929 
01930   CacheView
01931     *image_view,
01932     *resize_view;
01933 
01934   /*
01935     Apply filter to resize vertically from image to resize_image.
01936   */
01937   scale=MagickMax(1.0/y_factor,1.0);
01938   support=scale*GetResizeFilterSupport(resize_filter);
01939   storage_class=support > 0.5 ? DirectClass : image->storage_class;
01940   if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
01941     {
01942       InheritException(exception,&resize_image->exception);
01943       return(MagickFalse);
01944     }
01945   if (support < 0.5)
01946     {
01947       /*
01948         Support too small even for nearest neighbour:  reduce to point sampling.
01949       */
01950       support=(MagickRealType) 0.5;
01951       scale=1.0;
01952     }
01953   contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
01954   if (contributions == (ContributionInfo **) NULL)
01955     {
01956       (void) ThrowMagickException(exception,GetMagickModule(),
01957         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
01958       return(MagickFalse);
01959     }
01960   status=MagickTrue;
01961   scale=1.0/scale;
01962   (void) ResetMagickMemory(&zero,0,sizeof(zero));
01963   image_view=AcquireCacheView(image);
01964   resize_view=AcquireCacheView(resize_image);
01965 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01966   #pragma omp parallel for shared(status)
01967 #endif
01968   for (y=0; y < (long) resize_image->rows; y++)
01969   {
01970     long
01971       n,
01972       start,
01973       stop;
01974 
01975     MagickRealType
01976       center,
01977       density;
01978 
01979     register const IndexPacket
01980       *__restrict indexes;
01981 
01982     register const PixelPacket
01983       *__restrict p;
01984 
01985     register ContributionInfo
01986       *__restrict contribution;
01987 
01988     register IndexPacket
01989       *__restrict resize_indexes;
01990 
01991     register long
01992       x;
01993 
01994     register PixelPacket
01995       *__restrict q;
01996 
01997     if (status == MagickFalse)
01998       continue;
01999     center=(MagickRealType) (y+0.5)/y_factor;
02000     start=(long) (MagickMax(center-support-MagickEpsilon,0.0)+0.5);
02001     stop=(long) (MagickMin(center+support,(double) image->rows)+0.5);
02002     density=0.0;
02003     contribution=contributions[GetOpenMPThreadId()];
02004     for (n=0; n < (stop-start); n++)
02005     {
02006       contribution[n].pixel=start+n;
02007       contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
02008         ((MagickRealType) (start+n)-center+0.5));
02009       density+=contribution[n].weight;
02010     }
02011     if ((density != 0.0) && (density != 1.0))
02012       {
02013         register long
02014           i;
02015 
02016         /*
02017           Normalize.
02018         */
02019         density=1.0/density;
02020         for (i=0; i < n; i++)
02021           contribution[i].weight*=density;
02022       }
02023     p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
02024       image->columns,(unsigned long) (contribution[n-1].pixel-
02025       contribution[0].pixel+1),exception);
02026     q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
02027       exception);
02028     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
02029       {
02030         status=MagickFalse;
02031         continue;
02032       }
02033     indexes=GetCacheViewVirtualIndexQueue(image_view);
02034     resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
02035     for (x=0; x < (long) resize_image->columns; x++)
02036     {
02037       long
02038         j;
02039 
02040       MagickPixelPacket
02041         pixel;
02042 
02043       MagickRealType
02044         alpha;
02045 
02046       register long
02047         i;
02048 
02049       pixel=zero;
02050       if (image->matte == MagickFalse)
02051         {
02052           for (i=0; i < n; i++)
02053           {
02054             j=(long) ((contribution[i].pixel-contribution[0].pixel)*
02055               image->columns+x);
02056             alpha=contribution[i].weight;
02057             pixel.red+=alpha*(p+j)->red;
02058             pixel.green+=alpha*(p+j)->green;
02059             pixel.blue+=alpha*(p+j)->blue;
02060             pixel.opacity+=alpha*(p+j)->opacity;
02061           }
02062           q->red=RoundToQuantum(pixel.red);
02063           q->green=RoundToQuantum(pixel.green);
02064           q->blue=RoundToQuantum(pixel.blue);
02065           q->opacity=RoundToQuantum(pixel.opacity);
02066           if ((image->colorspace == CMYKColorspace) &&
02067               (resize_image->colorspace == CMYKColorspace))
02068             {
02069               for (i=0; i < n; i++)
02070               {
02071                 j=(long) ((contribution[i].pixel-contribution[0].pixel)*
02072                   image->columns+x);
02073                 alpha=contribution[i].weight;
02074                 pixel.index+=alpha*indexes[j];
02075               }
02076               resize_indexes[x]=(IndexPacket) RoundToQuantum(pixel.index);
02077             }
02078         }
02079       else
02080         {
02081           MagickRealType
02082             gamma;
02083 
02084           gamma=0.0;
02085           for (i=0; i < n; i++)
02086           {
02087             j=(long) ((contribution[i].pixel-contribution[0].pixel)*
02088               image->columns+x);
02089             alpha=contribution[i].weight*QuantumScale*((MagickRealType)
02090               QuantumRange-(p+j)->opacity);
02091             pixel.red+=alpha*(p+j)->red;
02092             pixel.green+=alpha*(p+j)->green;
02093             pixel.blue+=alpha*(p+j)->blue;
02094             pixel.opacity+=contribution[i].weight*(p+j)->opacity;
02095             gamma+=alpha;
02096           }
02097           gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
02098           q->red=RoundToQuantum(gamma*pixel.red);
02099           q->green=RoundToQuantum(gamma*pixel.green);
02100           q->blue=RoundToQuantum(gamma*pixel.blue);
02101           q->opacity=RoundToQuantum(pixel.opacity);
02102           if ((image->colorspace == CMYKColorspace) &&
02103               (resize_image->colorspace == CMYKColorspace))
02104             {
02105               for (i=0; i < n; i++)
02106               {
02107                 j=(long) ((contribution[i].pixel-contribution[0].pixel)*
02108                   image->columns+x);
02109                 alpha=contribution[i].weight*QuantumScale*((MagickRealType)
02110                   QuantumRange-(p+j)->opacity);
02111                 pixel.index+=alpha*indexes[j];
02112               }
02113               resize_indexes[x]=(IndexPacket) RoundToQuantum(gamma*pixel.index);
02114             }
02115         }
02116       if ((resize_image->storage_class == PseudoClass) &&
02117           (image->storage_class == PseudoClass))
02118         {
02119           i=(long) (MagickMin(MagickMax(center,(double) start),(double) stop-
02120             1.0)+0.5);
02121           j=(long) ((contribution[i-start].pixel-contribution[0].pixel)*
02122             image->columns+x);
02123           resize_indexes[x]=indexes[j];
02124         }
02125       q++;
02126     }
02127     if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
02128       status=MagickFalse;
02129     if (image->progress_monitor != (MagickProgressMonitor) NULL)
02130       {
02131         MagickBooleanType
02132           proceed;
02133 
02134 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02135   #pragma omp critical (MagickCore_VerticalFilter)
02136 #endif
02137         proceed=SetImageProgress(image,ResizeImageTag,(*quantum)++,span);
02138         if (proceed == MagickFalse)
02139           status=MagickFalse;
02140       }
02141   }
02142   resize_view=DestroyCacheView(resize_view);
02143   image_view=DestroyCacheView(image_view);
02144   contributions=DestroyContributionThreadSet(contributions);
02145   return(status);
02146 }
02147 
02148 MagickExport Image *ResizeImage(const Image *image,const unsigned long columns,
02149   const unsigned long rows,const FilterTypes filter,const double blur,
02150   ExceptionInfo *exception)
02151 {
02152 #define WorkLoadFactor  0.265
02153 
02154   FilterTypes
02155     filter_type;
02156 
02157   Image
02158     *filter_image,
02159     *resize_image;
02160 
02161   MagickRealType
02162     x_factor,
02163     y_factor;
02164 
02165   MagickSizeType
02166     span;
02167 
02168   MagickStatusType
02169     status;
02170 
02171   ResizeFilter
02172     *resize_filter;
02173 
02174   MagickOffsetType
02175     quantum;
02176 
02177   /*
02178     Acquire resize image.
02179   */
02180   assert(image != (Image *) NULL);
02181   assert(image->signature == MagickSignature);
02182   if (image->debug != MagickFalse)
02183     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02184   assert(exception != (ExceptionInfo *) NULL);
02185   assert(exception->signature == MagickSignature);
02186   if ((columns == 0) || (rows == 0))
02187     ThrowImageException(ImageError,"NegativeOrZeroImageSize");
02188   if ((columns == image->columns) && (rows == image->rows) &&
02189       (filter == UndefinedFilter) && (blur == 1.0))
02190     return(CloneImage(image,0,0,MagickTrue,exception));
02191   resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
02192   if (resize_image == (Image *) NULL)
02193     return(resize_image);
02194   /*
02195     Acquire resize filter.
02196   */
02197   x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
02198   y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
02199   if ((x_factor*y_factor) > WorkLoadFactor)
02200     filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
02201   else
02202     filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
02203   if (filter_image == (Image *) NULL)
02204     return(DestroyImage(resize_image));
02205   filter_type=LanczosFilter;
02206   if (filter != UndefinedFilter)
02207     filter_type=filter;
02208   else
02209     if ((x_factor == 1.0) && (y_factor == 1.0))
02210       filter_type=PointFilter;
02211     else
02212       if ((image->storage_class == PseudoClass) ||
02213           (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
02214         filter_type=MitchellFilter;
02215   resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
02216     exception);
02217   /*
02218     Resize image.
02219   */
02220   quantum=0;
02221   if ((x_factor*y_factor) > WorkLoadFactor)
02222     {
02223       span=(MagickSizeType) (filter_image->columns+rows);
02224       status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
02225         &quantum,exception);
02226       status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
02227         span,&quantum,exception);
02228     }
02229   else
02230     {
02231       span=(MagickSizeType) (filter_image->rows+columns);
02232       status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
02233         &quantum,exception);
02234       status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
02235         span,&quantum,exception);
02236     }
02237   /*
02238     Free resources.
02239   */
02240   filter_image=DestroyImage(filter_image);
02241   resize_filter=DestroyResizeFilter(resize_filter);
02242   if ((status == MagickFalse) || (resize_image == (Image *) NULL))
02243     return((Image *) NULL);
02244   resize_image->type=image->type;
02245   return(resize_image);
02246 }
02247 
02248 /*
02249 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02250 %                                                                             %
02251 %                                                                             %
02252 %                                                                             %
02253 %   S a m p l e I m a g e                                                     %
02254 %                                                                             %
02255 %                                                                             %
02256 %                                                                             %
02257 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02258 %
02259 %  SampleImage() scales an image to the desired dimensions with pixel
02260 %  sampling.  Unlike other scaling methods, this method does not introduce
02261 %  any additional color into the scaled image.
02262 %
02263 %  The format of the SampleImage method is:
02264 %
02265 %      Image *SampleImage(const Image *image,const unsigned long columns,
02266 %        const unsigned long rows,ExceptionInfo *exception)
02267 %
02268 %  A description of each parameter follows:
02269 %
02270 %    o image: the image.
02271 %
02272 %    o columns: the number of columns in the sampled image.
02273 %
02274 %    o rows: the number of rows in the sampled image.
02275 %
02276 %    o exception: return any errors or warnings in this structure.
02277 %
02278 */
02279 MagickExport Image *SampleImage(const Image *image,const unsigned long columns,
02280   const unsigned long rows,ExceptionInfo *exception)
02281 {
02282 #define SampleImageTag  "Sample/Image"
02283 
02284   Image
02285     *sample_image;
02286 
02287   long
02288     progress,
02289     *x_offset,
02290     y;
02291 
02292   MagickBooleanType
02293     status;
02294 
02295   register long
02296     x;
02297 
02298   CacheView
02299     *image_view,
02300     *sample_view;
02301 
02302   /*
02303     Initialize sampled image attributes.
02304   */
02305   assert(image != (const Image *) NULL);
02306   assert(image->signature == MagickSignature);
02307   if (image->debug != MagickFalse)
02308     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02309   assert(exception != (ExceptionInfo *) NULL);
02310   assert(exception->signature == MagickSignature);
02311   if ((columns == 0) || (rows == 0))
02312     ThrowImageException(ImageError,"NegativeOrZeroImageSize");
02313   if ((columns == image->columns) && (rows == image->rows))
02314     return(CloneImage(image,0,0,MagickTrue,exception));
02315   sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
02316   if (sample_image == (Image *) NULL)
02317     return((Image *) NULL);
02318   /*
02319     Allocate scan line buffer and column offset buffers.
02320   */
02321   x_offset=(long *) AcquireQuantumMemory((size_t) sample_image->columns,
02322     sizeof(*x_offset));
02323   if (x_offset == (long *) NULL)
02324     {
02325       sample_image=DestroyImage(sample_image);
02326       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02327     }
02328   for (x=0; x < (long) sample_image->columns; x++)
02329     x_offset[x]=(long) (((MagickRealType) x+0.5)*image->columns/
02330       sample_image->columns);
02331   /*
02332     Sample each row.
02333   */
02334   status=MagickTrue;
02335   progress=0;
02336   image_view=AcquireCacheView(image);
02337   sample_view=AcquireCacheView(sample_image);
02338 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02339   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
02340 #endif
02341   for (y=0; y < (long) sample_image->rows; y++)
02342   {
02343     long
02344       y_offset;
02345 
02346     register const IndexPacket
02347       *__restrict indexes;
02348 
02349     register const PixelPacket
02350       *__restrict p;
02351 
02352     register IndexPacket
02353       *__restrict sample_indexes;
02354 
02355     register long
02356       x;
02357 
02358     register PixelPacket
02359       *__restrict q;
02360 
02361     if (status == MagickFalse)
02362       continue;
02363     y_offset=(long) (((MagickRealType) y+0.5)*image->rows/sample_image->rows);
02364     p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
02365       exception);
02366     q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
02367       exception);
02368     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
02369       {
02370         status=MagickFalse;
02371         continue;
02372       }
02373     indexes=GetCacheViewAuthenticIndexQueue(image_view);
02374     sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
02375     /*
02376       Sample each column.
02377     */
02378     for (x=0; x < (long) sample_image->columns; x++)
02379       *q++=p[x_offset[x]];
02380     if ((image->storage_class == PseudoClass) ||
02381         (image->colorspace == CMYKColorspace))
02382       for (x=0; x < (long) sample_image->columns; x++)
02383         sample_indexes[x]=indexes[x_offset[x]];
02384     if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
02385       status=MagickFalse;
02386     if (image->progress_monitor != (MagickProgressMonitor) NULL)
02387       {
02388         MagickBooleanType
02389           proceed;
02390 
02391 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02392         #pragma omp critical (MagickCore_SampleImage)
02393 #endif
02394         proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
02395         if (proceed == MagickFalse)
02396           status=MagickFalse;
02397       }
02398   }
02399   image_view=DestroyCacheView(image_view);
02400   sample_view=DestroyCacheView(sample_view);
02401   x_offset=(long *) RelinquishMagickMemory(x_offset);
02402   sample_image->type=image->type;
02403   return(sample_image);
02404 }
02405 
02406 /*
02407 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02408 %                                                                             %
02409 %                                                                             %
02410 %                                                                             %
02411 %   S c a l e I m a g e                                                       %
02412 %                                                                             %
02413 %                                                                             %
02414 %                                                                             %
02415 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02416 %
02417 %  ScaleImage() changes the size of an image to the given dimensions.
02418 %
02419 %  The format of the ScaleImage method is:
02420 %
02421 %      Image *ScaleImage(const Image *image,const unsigned long columns,
02422 %        const unsigned long rows,ExceptionInfo *exception)
02423 %
02424 %  A description of each parameter follows:
02425 %
02426 %    o image: the image.
02427 %
02428 %    o columns: the number of columns in the scaled image.
02429 %
02430 %    o rows: the number of rows in the scaled image.
02431 %
02432 %    o exception: return any errors or warnings in this structure.
02433 %
02434 */
02435 MagickExport Image *ScaleImage(const Image *image,const unsigned long columns,
02436   const unsigned long rows,ExceptionInfo *exception)
02437 {
02438 #define ScaleImageTag  "Scale/Image"
02439 
02440   Image
02441     *scale_image;
02442 
02443   long
02444     number_rows,
02445     y;
02446 
02447   MagickBooleanType
02448     next_column,
02449     next_row,
02450     proceed;
02451 
02452   MagickPixelPacket
02453     pixel,
02454     *scale_scanline,
02455     *scanline,
02456     *x_vector,
02457     *y_vector,
02458     zero;
02459 
02460   PointInfo
02461     scale,
02462     span;
02463 
02464   register long
02465     i;
02466 
02467   /*
02468     Initialize scaled image attributes.
02469   */
02470   assert(image != (const Image *) NULL);
02471   assert(image->signature == MagickSignature);
02472   if (image->debug != MagickFalse)
02473     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02474   assert(exception != (ExceptionInfo *) NULL);
02475   assert(exception->signature == MagickSignature);
02476   if ((columns == 0) || (rows == 0))
02477     return((Image *) NULL);
02478   if ((columns == image->columns) && (rows == image->rows))
02479     return(CloneImage(image,0,0,MagickTrue,exception));
02480   scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
02481   if (scale_image == (Image *) NULL)
02482     return((Image *) NULL);
02483   if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
02484     {
02485       InheritException(exception,&scale_image->exception);
02486       scale_image=DestroyImage(scale_image);
02487       return((Image *) NULL);
02488     }
02489   /*
02490     Allocate memory.
02491   */
02492   x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
02493     sizeof(*x_vector));
02494   scanline=x_vector;
02495   if (image->rows != scale_image->rows)
02496     scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
02497       sizeof(*scanline));
02498   scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
02499     scale_image->columns,sizeof(*scale_scanline));
02500   y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
02501     sizeof(*y_vector));
02502   if ((scanline == (MagickPixelPacket *) NULL) ||
02503       (scale_scanline == (MagickPixelPacket *) NULL) ||
02504       (x_vector == (MagickPixelPacket *) NULL) ||
02505       (y_vector == (MagickPixelPacket *) NULL))
02506     {
02507       scale_image=DestroyImage(scale_image);
02508       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02509     }
02510   /*
02511     Scale image.
02512   */
02513   number_rows=0;
02514   next_row=MagickTrue;
02515   span.y=1.0;
02516   scale.y=(double) scale_image->rows/(double) image->rows;
02517   (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
02518     sizeof(*y_vector));
02519   GetMagickPixelPacket(image,&pixel);
02520   (void) ResetMagickMemory(&zero,0,sizeof(zero));
02521   i=0;
02522   for (y=0; y < (long) scale_image->rows; y++)
02523   {
02524     register const IndexPacket
02525       *__restrict indexes;
02526 
02527     register const PixelPacket
02528       *__restrict p;
02529 
02530     register IndexPacket
02531       *__restrict scale_indexes;
02532 
02533     register long
02534       x;
02535 
02536     register MagickPixelPacket
02537       *__restrict s,
02538       *__restrict t;
02539 
02540     register PixelPacket
02541       *__restrict q;
02542 
02543     q=QueueAuthenticPixels(scale_image,0,y,scale_image->columns,1,exception);
02544     if (q == (PixelPacket *) NULL)
02545       break;
02546     scale_indexes=GetAuthenticIndexQueue(scale_image);
02547     if (scale_image->rows == image->rows)
02548       {
02549         /*
02550           Read a new scanline.
02551         */
02552         p=GetVirtualPixels(image,0,i++,image->columns,1,exception);
02553         if (p == (const PixelPacket *) NULL)
02554           break;
02555         indexes=GetVirtualIndexQueue(image);
02556         for (x=0; x < (long) image->columns; x++)
02557         {
02558           x_vector[x].red=(MagickRealType) p->red;
02559           x_vector[x].green=(MagickRealType) p->green;
02560           x_vector[x].blue=(MagickRealType) p->blue;
02561           if (image->matte != MagickFalse)
02562             x_vector[x].opacity=(MagickRealType) p->opacity;
02563           if (indexes != (IndexPacket *) NULL)
02564             x_vector[x].index=(MagickRealType) indexes[x];
02565           p++;
02566         }
02567       }
02568     else
02569       {
02570         /*
02571           Scale Y direction.
02572         */
02573         while (scale.y < span.y)
02574         {
02575           if ((next_row != MagickFalse) && (number_rows < (long) image->rows))
02576             {
02577               /*
02578                 Read a new scanline.
02579               */
02580               p=GetVirtualPixels(image,0,i++,image->columns,1,exception);
02581               if (p == (const PixelPacket *) NULL)
02582                 break;
02583               indexes=GetVirtualIndexQueue(image);
02584               for (x=0; x < (long) image->columns; x++)
02585               {
02586                 x_vector[x].red=(MagickRealType) p->red;
02587                 x_vector[x].green=(MagickRealType) p->green;
02588                 x_vector[x].blue=(MagickRealType) p->blue;
02589                 if (image->matte != MagickFalse)
02590                   x_vector[x].opacity=(MagickRealType) p->opacity;
02591                 if (indexes != (IndexPacket *) NULL)
02592                   x_vector[x].index=(MagickRealType) indexes[x];
02593                 p++;
02594               }
02595               number_rows++;
02596             }
02597           for (x=0; x < (long) image->columns; x++)
02598           {
02599             y_vector[x].red+=scale.y*x_vector[x].red;
02600             y_vector[x].green+=scale.y*x_vector[x].green;
02601             y_vector[x].blue+=scale.y*x_vector[x].blue;
02602             if (scale_image->matte != MagickFalse)
02603               y_vector[x].opacity+=scale.y*x_vector[x].opacity;
02604             if (scale_indexes != (IndexPacket *) NULL)
02605               y_vector[x].index+=scale.y*x_vector[x].index;
02606           }
02607           span.y-=scale.y;
02608           scale.y=(double) scale_image->rows/(double) image->rows;
02609           next_row=MagickTrue;
02610         }
02611         if ((next_row != MagickFalse) && (number_rows < (long) image->rows))
02612           {
02613             /*
02614               Read a new scanline.
02615             */
02616             p=GetVirtualPixels(image,0,i++,image->columns,1,exception);
02617             if (p == (const PixelPacket *) NULL)
02618               break;
02619             indexes=GetVirtualIndexQueue(image);
02620             for (x=0; x < (long) image->columns; x++)
02621             {
02622               x_vector[x].red=(MagickRealType) p->red;
02623               x_vector[x].green=(MagickRealType) p->green;
02624               x_vector[x].blue=(MagickRealType) p->blue;
02625               if (image->matte != MagickFalse)
02626                 x_vector[x].opacity=(MagickRealType) p->opacity;
02627               if (indexes != (IndexPacket *) NULL)
02628                 x_vector[x].index=(MagickRealType) indexes[x];
02629               p++;
02630             }
02631             number_rows++;
02632             next_row=MagickFalse;
02633           }
02634         s=scanline;
02635         for (x=0; x < (long) image->columns; x++)
02636         {
02637           pixel.red=y_vector[x].red+span.y*x_vector[x].red;
02638           pixel.green=y_vector[x].green+span.y*x_vector[x].green;
02639           pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
02640           if (image->matte != MagickFalse)
02641             pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
02642           if (scale_indexes != (IndexPacket *) NULL)
02643             pixel.index=y_vector[x].index+span.y*x_vector[x].index;
02644           s->red=pixel.red;
02645           s->green=pixel.green;
02646           s->blue=pixel.blue;
02647           if (scale_image->matte != MagickFalse)
02648             s->opacity=pixel.opacity;
02649           if (scale_indexes != (IndexPacket *) NULL)
02650             s->index=pixel.index;
02651           s++;
02652           y_vector[x]=zero;
02653         }
02654         scale.y-=span.y;
02655         if (scale.y <= 0)
02656           {
02657             scale.y=(double) scale_image->rows/(double) image->rows;
02658             next_row=MagickTrue;
02659           }
02660         span.y=1.0;
02661       }
02662     if (scale_image->columns == image->columns)
02663       {
02664         /*
02665           Transfer scanline to scaled image.
02666         */
02667         s=scanline;
02668         for (x=0; x < (long) scale_image->columns; x++)
02669         {
02670           q->red=RoundToQuantum(s->red);
02671           q->green=RoundToQuantum(s->green);
02672           q->blue=RoundToQuantum(s->blue);
02673           if (scale_image->matte != MagickFalse)
02674             q->opacity=RoundToQuantum(s->opacity);
02675           if (scale_indexes != (IndexPacket *) NULL)
02676             scale_indexes[x]=(IndexPacket) RoundToQuantum(s->index);
02677           q++;
02678           s++;
02679         }
02680       }
02681     else
02682       {
02683         /*
02684           Scale X direction.
02685         */
02686         pixel=zero;
02687         next_column=MagickFalse;
02688         span.x=1.0;
02689         s=scanline;
02690         t=scale_scanline;
02691         for (x=0; x < (long) image->columns; x++)
02692         {
02693           scale.x=(double) scale_image->columns/(double) image->columns;
02694           while (scale.x >= span.x)
02695           {
02696             if (next_column != MagickFalse)
02697               {
02698                 pixel=zero;
02699                 t++;
02700               }
02701             pixel.red+=span.x*s->red;
02702             pixel.green+=span.x*s->green;
02703             pixel.blue+=span.x*s->blue;
02704             if (image->matte != MagickFalse)
02705               pixel.opacity+=span.x*s->opacity;
02706             if (scale_indexes != (IndexPacket *) NULL)
02707               pixel.index+=span.x*s->index;
02708             t->red=pixel.red;
02709             t->green=pixel.green;
02710             t->blue=pixel.blue;
02711             if (scale_image->matte != MagickFalse)
02712               t->opacity=pixel.opacity;
02713             if (scale_indexes != (IndexPacket *) NULL)
02714               t->index=pixel.index;
02715             scale.x-=span.x;
02716             span.x=1.0;
02717             next_column=MagickTrue;
02718           }
02719         if (scale.x > 0)
02720           {
02721             if (next_column != MagickFalse)
02722               {
02723                 pixel=zero;
02724                 next_column=MagickFalse;
02725                 t++;
02726               }
02727             pixel.red+=scale.x*s->red;
02728             pixel.green+=scale.x*s->green;
02729             pixel.blue+=scale.x*s->blue;
02730             if (scale_image->matte != MagickFalse)
02731               pixel.opacity+=scale.x*s->opacity;
02732             if (scale_indexes != (IndexPacket *) NULL)
02733               pixel.index+=scale.x*s->index;
02734             span.x-=scale.x;
02735           }
02736         s++;
02737       }
02738       if (span.x > 0)
02739         {
02740           s--;
02741           pixel.red+=span.x*s->red;
02742           pixel.green+=span.x*s->green;
02743           pixel.blue+=span.x*s->blue;
02744           if (scale_image->matte != MagickFalse)
02745             pixel.opacity+=span.x*s->opacity;
02746           if (scale_indexes != (IndexPacket *) NULL)
02747             pixel.index+=span.x*s->index;
02748         }
02749       if ((next_column == MagickFalse) &&
02750           ((long) (t-scale_scanline) < (long) scale_image->columns))
02751         {
02752           t->red=pixel.red;
02753           t->green=pixel.green;
02754           t->blue=pixel.blue;
02755           if (scale_image->matte != MagickFalse)
02756             t->opacity=pixel.opacity;
02757           if (scale_indexes != (IndexPacket *) NULL)
02758             t->index=pixel.index;
02759         }
02760       /*
02761         Transfer scanline to scaled image.
02762       */
02763       t=scale_scanline;
02764       for (x=0; x < (long) scale_image->columns; x++)
02765       {
02766         q->red=RoundToQuantum(t->red);
02767         q->green=RoundToQuantum(t->green);
02768         q->blue=RoundToQuantum(t->blue);
02769         if (scale_image->matte != MagickFalse)
02770           q->opacity=RoundToQuantum(t->opacity);
02771         if (scale_indexes != (IndexPacket *) NULL)
02772           scale_indexes[x]=(IndexPacket) RoundToQuantum(t->index);
02773         t++;
02774         q++;
02775       }
02776     }
02777     if (SyncAuthenticPixels(scale_image,exception) == MagickFalse)
02778       break;
02779     proceed=SetImageProgress(image,ScaleImageTag,y,image->rows);
02780     if (proceed == MagickFalse)
02781       break;
02782   }
02783   /*
02784     Free allocated memory.
02785   */
02786   y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
02787   scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
02788   if (scale_image->rows != image->rows)
02789     scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
02790   x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
02791   scale_image->type=image->type;
02792   return(scale_image);
02793 }
02794 
02795 /*
02796 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02797 %                                                                             %
02798 %                                                                             %
02799 %                                                                             %
02800 +   S e t R e s i z e F i l t e r S u p p o r t                               %
02801 %                                                                             %
02802 %                                                                             %
02803 %                                                                             %
02804 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02805 %
02806 %  SetResizeFilterSupport() specifies which IR filter to use to window
02807 %
02808 %  The format of the SetResizeFilterSupport method is:
02809 %
02810 %      void SetResizeFilterSupport(ResizeFilter *resize_filter,
02811 %        const MagickRealType support)
02812 %
02813 %  A description of each parameter follows:
02814 %
02815 %    o resize_filter: the resize filter.
02816 %
02817 %    o support: the filter spport radius.
02818 %
02819 */
02820 MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
02821   const MagickRealType support)
02822 {
02823   assert(resize_filter != (ResizeFilter *) NULL);
02824   assert(resize_filter->signature == MagickSignature);
02825   resize_filter->support=support;
02826 }
02827 
02828 /*
02829 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02830 %                                                                             %
02831 %                                                                             %
02832 %                                                                             %
02833 %   T h u m b n a i l I m a g e                                               %
02834 %                                                                             %
02835 %                                                                             %
02836 %                                                                             %
02837 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02838 %
02839 %  ThumbnailImage() changes the size of an image to the given dimensions and
02840 %  removes any associated profiles.  The goal is to produce small low cost
02841 %  thumbnail images suited for display on the Web.
02842 %
02843 %  The format of the ThumbnailImage method is:
02844 %
02845 %      Image *ThumbnailImage(const Image *image,const unsigned long columns,
02846 %        const unsigned long rows,ExceptionInfo *exception)
02847 %
02848 %  A description of each parameter follows:
02849 %
02850 %    o image: the image.
02851 %
02852 %    o columns: the number of columns in the scaled image.
02853 %
02854 %    o rows: the number of rows in the scaled image.
02855 %
02856 %    o exception: return any errors or warnings in this structure.
02857 %
02858 */
02859 MagickExport Image *ThumbnailImage(const Image *image,
02860   const unsigned long columns,const unsigned long rows,ExceptionInfo *exception)
02861 {
02862 #define SampleFactor  5
02863 
02864   char
02865     value[MaxTextExtent];
02866 
02867   const char
02868     *name;
02869 
02870   Image
02871     *thumbnail_image;
02872 
02873   MagickRealType
02874     x_factor,
02875     y_factor;
02876 
02877   struct stat
02878     attributes;
02879 
02880   unsigned long
02881     version;
02882 
02883   assert(image != (Image *) NULL);
02884   assert(image->signature == MagickSignature);
02885   if (image->debug != MagickFalse)
02886     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02887   assert(exception != (ExceptionInfo *) NULL);
02888   assert(exception->signature == MagickSignature);
02889   x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
02890   y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
02891   if ((x_factor*y_factor) > 0.1)
02892     thumbnail_image=ZoomImage(image,columns,rows,exception);
02893   else
02894     if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
02895       thumbnail_image=ZoomImage(image,columns,rows,exception);
02896     else
02897       {
02898         Image
02899           *sample_image;
02900 
02901         sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
02902           exception);
02903         if (sample_image == (Image *) NULL)
02904           return((Image *) NULL);
02905         thumbnail_image=ZoomImage(sample_image,columns,rows,exception);
02906         sample_image=DestroyImage(sample_image);
02907       }
02908   if (thumbnail_image == (Image *) NULL)
02909     return(thumbnail_image);
02910   (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
02911   if (thumbnail_image->matte == MagickFalse)
02912     (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
02913   thumbnail_image->depth=8;
02914   thumbnail_image->interlace=NoInterlace;
02915   /*
02916     Strip all profiles except color profiles.
02917   */
02918   ResetImageProfileIterator(thumbnail_image);
02919   for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
02920   {
02921     if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
02922      {
02923        DeleteImageProfile(thumbnail_image,name);
02924        ResetImageProfileIterator(thumbnail_image);
02925      }
02926     name=GetNextImageProfile(thumbnail_image);
02927   }
02928   (void) DeleteImageProperty(thumbnail_image,"comment");
02929   (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
02930   if (strstr(image->magick_filename,"//") == (char *) NULL)
02931     (void) FormatMagickString(value,MaxTextExtent,"file://%s",
02932       image->magick_filename);
02933   (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
02934   (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
02935   if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
02936     {
02937       (void) FormatMagickString(value,MaxTextExtent,"%ld",(long)
02938         attributes.st_mtime);
02939       (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
02940     }
02941   (void) FormatMagickString(value,MaxTextExtent,"%ld",(long)
02942     attributes.st_mtime);
02943   (void) FormatMagickSize(GetBlobSize(image),value);
02944   (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
02945   (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
02946   LocaleLower(value);
02947   (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
02948   (void) SetImageProperty(thumbnail_image,"software",
02949     GetMagickVersion(&version));
02950   (void) FormatMagickString(value,MaxTextExtent,"%lu",image->magick_columns);
02951   (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
02952   (void) FormatMagickString(value,MaxTextExtent,"%lu",image->magick_rows);
02953   (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
02954   (void) FormatMagickString(value,MaxTextExtent,"%lu",
02955     GetImageListLength(image));
02956   (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
02957   return(thumbnail_image);
02958 }
02959 
02960 /*
02961 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02962 %                                                                             %
02963 %                                                                             %
02964 %                                                                             %
02965 %   Z o o m I m a g e                                                         %
02966 %                                                                             %
02967 %                                                                             %
02968 %                                                                             %
02969 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02970 %
02971 %  ZoomImage() creates a new image that is a scaled size of an existing one.
02972 %  It allocates the memory necessary for the new Image structure and returns a
02973 %  pointer to the new image.  The Point filter gives fast pixel replication,
02974 %  Triangle is equivalent to bi-linear interpolation, and Mitchel giver slower,
02975 %  very high-quality results.  See Graphic Gems III for details on this
02976 %  algorithm.
02977 %
02978 %  The filter member of the Image structure specifies which image filter to
02979 %  use. Blur specifies the blur factor where > 1 is blurry, < 1 is sharp.
02980 %
02981 %  The format of the ZoomImage method is:
02982 %
02983 %      Image *ZoomImage(const Image *image,const unsigned long columns,
02984 %        const unsigned long rows,ExceptionInfo *exception)
02985 %
02986 %  A description of each parameter follows:
02987 %
02988 %    o image: the image.
02989 %
02990 %    o columns: An integer that specifies the number of columns in the zoom
02991 %      image.
02992 %
02993 %    o rows: An integer that specifies the number of rows in the scaled
02994 %      image.
02995 %
02996 %    o exception: return any errors or warnings in this structure.
02997 %
02998 */
02999 MagickExport Image *ZoomImage(const Image *image,const unsigned long columns,
03000   const unsigned long rows,ExceptionInfo *exception)
03001 {
03002   Image
03003     *zoom_image;
03004 
03005   assert(image != (const Image *) NULL);
03006   assert(image->signature == MagickSignature);
03007   if (image->debug != MagickFalse)
03008     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03009   assert(exception != (ExceptionInfo *) NULL);
03010   assert(exception->signature == MagickSignature);
03011   zoom_image=ResizeImage(image,columns,rows,image->filter,image->blur,
03012     exception);
03013   return(zoom_image);
03014 }

Generated on 19 Nov 2009 for MagickCore by  doxygen 1.6.1