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-2010 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,GetResizeFilterWeight(resize_filter,x));
00751       (void) printf("%5.2lf\t%lf\n",support,0.0);
00752     }
00753   return(resize_filter);
00754 }
00755 
00756 /*
00757 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00758 %                                                                             %
00759 %                                                                             %
00760 %                                                                             %
00761 %   A d a p t i v e R e s i z e I m a g e                                     %
00762 %                                                                             %
00763 %                                                                             %
00764 %                                                                             %
00765 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00766 %
00767 %  AdaptiveResizeImage() adaptively resize image with pixel resampling.
00768 %
00769 %  The format of the AdaptiveResizeImage method is:
00770 %
00771 %      Image *AdaptiveResizeImage(const Image *image,
00772 %        const unsigned long columns,const unsigned long rows,
00773 %        ExceptionInfo *exception)
00774 %
00775 %  A description of each parameter follows:
00776 %
00777 %    o image: the image.
00778 %
00779 %    o columns: the number of columns in the resized image.
00780 %
00781 %    o rows: the number of rows in the resized image.
00782 %
00783 %    o exception: return any errors or warnings in this structure.
00784 %
00785 */
00786 MagickExport Image *AdaptiveResizeImage(const Image *image,
00787   const unsigned long columns,const unsigned long rows,ExceptionInfo *exception)
00788 {
00789 #define AdaptiveResizeImageTag  "Resize/Image"
00790 
00791   Image
00792     *resize_image;
00793 
00794   long
00795     y;
00796 
00797   MagickBooleanType
00798     proceed;
00799 
00800   MagickPixelPacket
00801     pixel;
00802 
00803   PointInfo
00804     offset;
00805 
00806   ResampleFilter
00807     *resample_filter;
00808 
00809   ViewInfo
00810     *resize_view;
00811 
00812   /*
00813     Adaptively resize image.
00814   */
00815   assert(image != (const Image *) NULL);
00816   assert(image->signature == MagickSignature);
00817   if (image->debug != MagickFalse)
00818     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00819   assert(exception != (ExceptionInfo *) NULL);
00820   assert(exception->signature == MagickSignature);
00821   if ((columns == 0) || (rows == 0))
00822     return((Image *) NULL);
00823   if ((columns == image->columns) && (rows == image->rows))
00824     return(CloneImage(image,0,0,MagickTrue,exception));
00825   resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
00826   if (resize_image == (Image *) NULL)
00827     return((Image *) NULL);
00828   if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
00829     {
00830       InheritException(exception,&resize_image->exception);
00831       resize_image=DestroyImage(resize_image);
00832       return((Image *) NULL);
00833     }
00834   GetMagickPixelPacket(image,&pixel);
00835   resample_filter=AcquireResampleFilter(image,exception);
00836   if (image->interpolate == UndefinedInterpolatePixel)
00837     (void) SetResampleFilterInterpolateMethod(resample_filter,
00838       MeshInterpolatePixel);
00839   resize_view=AcquireCacheView(resize_image);
00840   for (y=0; y < (long) resize_image->rows; y++)
00841   {
00842     register IndexPacket
00843       *__restrict resize_indexes;
00844 
00845     register long
00846       x;
00847 
00848     register PixelPacket
00849       *__restrict q;
00850 
00851     q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
00852       exception);
00853     if (q == (PixelPacket *) NULL)
00854       break;
00855     resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
00856     offset.y=((MagickRealType) y*image->rows/resize_image->rows);
00857     for (x=0; x < (long) resize_image->columns; x++)
00858     {
00859       offset.x=((MagickRealType) x*image->columns/resize_image->columns);
00860       (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
00861         &pixel);
00862       SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
00863       q++;
00864     }
00865     if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
00866       break;
00867     proceed=SetImageProgress(image,AdaptiveResizeImageTag,y,image->rows);
00868     if (proceed == MagickFalse)
00869       break;
00870   }
00871   resample_filter=DestroyResampleFilter(resample_filter);
00872   resize_view=DestroyCacheView(resize_view);
00873   return(resize_image);
00874 }
00875 
00876 /*
00877 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00878 %                                                                             %
00879 %                                                                             %
00880 %                                                                             %
00881 +   B e s s e l O r d e r O n e                                               %
00882 %                                                                             %
00883 %                                                                             %
00884 %                                                                             %
00885 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00886 %
00887 %  BesselOrderOne() computes the Bessel function of x of the first kind of
00888 %  order 0:
00889 %
00890 %    Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
00891 %
00892 %       j1(x) = x*j1(x);
00893 %
00894 %    For x in (8,inf)
00895 %
00896 %       j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
00897 %
00898 %    where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
00899 %
00900 %       cos(x1) =  cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
00901 %               =  1/sqrt(2) * (sin(x) - cos(x))
00902 %       sin(x1) =  sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
00903 %               = -1/sqrt(2) * (sin(x) + cos(x))
00904 %
00905 %  The format of the BesselOrderOne method is:
00906 %
00907 %      MagickRealType BesselOrderOne(MagickRealType x)
00908 %
00909 %  A description of each parameter follows:
00910 %
00911 %    o x: MagickRealType value.
00912 %
00913 */
00914 
00915 #undef I0
00916 static MagickRealType I0(MagickRealType x)
00917 {
00918   MagickRealType
00919     sum,
00920     t,
00921     y;
00922 
00923   register long
00924     i;
00925 
00926   /*
00927     Zeroth order Bessel function of the first kind.
00928   */
00929   sum=1.0;
00930   y=x*x/4.0;
00931   t=y;
00932   for (i=2; t > MagickEpsilon; i++)
00933   {
00934     sum+=t;
00935     t*=y/((MagickRealType) i*i);
00936   }
00937   return(sum);
00938 }
00939 
00940 #undef J1
00941 static MagickRealType J1(MagickRealType x)
00942 {
00943   MagickRealType
00944     p,
00945     q;
00946 
00947   register long
00948     i;
00949 
00950   static const double
00951     Pone[] =
00952     {
00953        0.581199354001606143928050809e+21,
00954       -0.6672106568924916298020941484e+20,
00955        0.2316433580634002297931815435e+19,
00956       -0.3588817569910106050743641413e+17,
00957        0.2908795263834775409737601689e+15,
00958       -0.1322983480332126453125473247e+13,
00959        0.3413234182301700539091292655e+10,
00960       -0.4695753530642995859767162166e+7,
00961        0.270112271089232341485679099e+4
00962     },
00963     Qone[] =
00964     {
00965       0.11623987080032122878585294e+22,
00966       0.1185770712190320999837113348e+20,
00967       0.6092061398917521746105196863e+17,
00968       0.2081661221307607351240184229e+15,
00969       0.5243710262167649715406728642e+12,
00970       0.1013863514358673989967045588e+10,
00971       0.1501793594998585505921097578e+7,
00972       0.1606931573481487801970916749e+4,
00973       0.1e+1
00974     };
00975 
00976   p=Pone[8];
00977   q=Qone[8];
00978   for (i=7; i >= 0; i--)
00979   {
00980     p=p*x*x+Pone[i];
00981     q=q*x*x+Qone[i];
00982   }
00983   return(p/q);
00984 }
00985 
00986 #undef P1
00987 static MagickRealType P1(MagickRealType x)
00988 {
00989   MagickRealType
00990     p,
00991     q;
00992 
00993   register long
00994     i;
00995 
00996   static const double
00997     Pone[] =
00998     {
00999       0.352246649133679798341724373e+5,
01000       0.62758845247161281269005675e+5,
01001       0.313539631109159574238669888e+5,
01002       0.49854832060594338434500455e+4,
01003       0.2111529182853962382105718e+3,
01004       0.12571716929145341558495e+1
01005     },
01006     Qone[] =
01007     {
01008       0.352246649133679798068390431e+5,
01009       0.626943469593560511888833731e+5,
01010       0.312404063819041039923015703e+5,
01011       0.4930396490181088979386097e+4,
01012       0.2030775189134759322293574e+3,
01013       0.1e+1
01014     };
01015 
01016   p=Pone[5];
01017   q=Qone[5];
01018   for (i=4; i >= 0; i--)
01019   {
01020     p=p*(8.0/x)*(8.0/x)+Pone[i];
01021     q=q*(8.0/x)*(8.0/x)+Qone[i];
01022   }
01023   return(p/q);
01024 }
01025 
01026 #undef Q1
01027 static MagickRealType Q1(MagickRealType x)
01028 {
01029   MagickRealType
01030     p,
01031     q;
01032 
01033   register long
01034     i;
01035 
01036   static const double
01037     Pone[] =
01038     {
01039       0.3511751914303552822533318e+3,
01040       0.7210391804904475039280863e+3,
01041       0.4259873011654442389886993e+3,
01042       0.831898957673850827325226e+2,
01043       0.45681716295512267064405e+1,
01044       0.3532840052740123642735e-1
01045     },
01046     Qone[] =
01047     {
01048       0.74917374171809127714519505e+4,
01049       0.154141773392650970499848051e+5,
01050       0.91522317015169922705904727e+4,
01051       0.18111867005523513506724158e+4,
01052       0.1038187585462133728776636e+3,
01053       0.1e+1
01054     };
01055 
01056   p=Pone[5];
01057   q=Qone[5];
01058   for (i=4; i >= 0; i--)
01059   {
01060     p=p*(8.0/x)*(8.0/x)+Pone[i];
01061     q=q*(8.0/x)*(8.0/x)+Qone[i];
01062   }
01063   return(p/q);
01064 }
01065 
01066 static MagickRealType BesselOrderOne(MagickRealType x)
01067 {
01068   MagickRealType
01069     p,
01070     q;
01071 
01072   if (x == 0.0)
01073     return(0.0);
01074   p=x;
01075   if (x < 0.0)
01076     x=(-x);
01077   if (x < 8.0)
01078     return(p*J1(x));
01079   q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
01080     cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
01081     cos((double) x))));
01082   if (p < 0.0)
01083     q=(-q);
01084   return(q);
01085 }
01086 
01087 /*
01088 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01089 %                                                                             %
01090 %                                                                             %
01091 %                                                                             %
01092 +   D e s t r o y R e s i z e F i l t e r                                     %
01093 %                                                                             %
01094 %                                                                             %
01095 %                                                                             %
01096 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01097 %
01098 %  DestroyResizeFilter() destroy the resize filter.
01099 %
01100 %  The format of the AcquireResizeFilter method is:
01101 %
01102 %      ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
01103 %
01104 %  A description of each parameter follows:
01105 %
01106 %    o resize_filter: the resize filter.
01107 %
01108 */
01109 MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
01110 {
01111   assert(resize_filter != (ResizeFilter *) NULL);
01112   assert(resize_filter->signature == MagickSignature);
01113   resize_filter->signature=(~MagickSignature);
01114   resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
01115   return(resize_filter);
01116 }
01117 
01118 /*
01119 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01120 %                                                                             %
01121 %                                                                             %
01122 %                                                                             %
01123 +   G e t R e s i z e F i l t e r S u p p o r t                               %
01124 %                                                                             %
01125 %                                                                             %
01126 %                                                                             %
01127 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01128 %
01129 %  GetResizeFilterSupport() return the current support window size for this
01130 %  filter.  Note that this may have been enlarged by filter:blur factor.
01131 %
01132 %  The format of the GetResizeFilterSupport method is:
01133 %
01134 %      MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
01135 %
01136 %  A description of each parameter follows:
01137 %
01138 %    o filter: Image filter to use.
01139 %
01140 */
01141 MagickExport MagickRealType GetResizeFilterSupport(
01142   const ResizeFilter *resize_filter)
01143 {
01144   assert(resize_filter != (ResizeFilter *) NULL);
01145   assert(resize_filter->signature == MagickSignature);
01146   return(resize_filter->support*resize_filter->blur);
01147 }
01148 
01149 /*
01150 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01151 %                                                                             %
01152 %                                                                             %
01153 %                                                                             %
01154 +   G e t R e s i z e F i l t e r W e i g h t                                 %
01155 %                                                                             %
01156 %                                                                             %
01157 %                                                                             %
01158 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01159 %
01160 %  GetResizeFilterWeight evaluates the specified resize filter at the point x
01161 %  which usally lies between zero and the filters current 'support' and
01162 %  returns the weight of the filter function at that point.
01163 %
01164 %  The format of the GetResizeFilterWeight method is:
01165 %
01166 %      MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
01167 %        const MagickRealType x)
01168 %
01169 %  A description of each parameter follows:
01170 %
01171 %    o filter: the filter type.
01172 %
01173 %    o x: the point.
01174 %
01175 */
01176 MagickExport MagickRealType GetResizeFilterWeight(
01177   const ResizeFilter *resize_filter,const MagickRealType x)
01178 {
01179   MagickRealType
01180     blur,
01181     scale;
01182 
01183   /*
01184     Windowing function - scale the weighting filter by this amount.
01185   */
01186   assert(resize_filter != (ResizeFilter *) NULL);
01187   assert(resize_filter->signature == MagickSignature);
01188   blur=fabs(x)/resize_filter->blur;  /* X offset with blur scaling */
01189   if ((resize_filter->window_support < MagickEpsilon) ||
01190       (resize_filter->window == Box))
01191     scale=1.0;  /* Point/Box Filter -- avoid division by zero */
01192   else
01193     {
01194       scale=resize_filter->scale/resize_filter->window_support;
01195       scale=resize_filter->window(blur*scale,resize_filter);
01196     }
01197   return(scale*resize_filter->filter(blur,resize_filter));
01198 }
01199 
01200 /*
01201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01202 %                                                                             %
01203 %                                                                             %
01204 %                                                                             %
01205 %   M a g n i f y I m a g e                                                   %
01206 %                                                                             %
01207 %                                                                             %
01208 %                                                                             %
01209 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01210 %
01211 %  MagnifyImage() is a convenience method that scales an image proportionally
01212 %  to twice its size.
01213 %
01214 %  The format of the MagnifyImage method is:
01215 %
01216 %      Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
01217 %
01218 %  A description of each parameter follows:
01219 %
01220 %    o image: the image.
01221 %
01222 %    o exception: return any errors or warnings in this structure.
01223 %
01224 */
01225 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
01226 {
01227   Image
01228     *magnify_image;
01229 
01230   assert(image != (Image *) NULL);
01231   assert(image->signature == MagickSignature);
01232   if (image->debug != MagickFalse)
01233     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01234   assert(exception != (ExceptionInfo *) NULL);
01235   assert(exception->signature == MagickSignature);
01236   magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
01237     1.0,exception);
01238   return(magnify_image);
01239 }
01240 
01241 /*
01242 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01243 %                                                                             %
01244 %                                                                             %
01245 %                                                                             %
01246 %   M i n i f y I m a g e                                                     %
01247 %                                                                             %
01248 %                                                                             %
01249 %                                                                             %
01250 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01251 %
01252 %  MinifyImage() is a convenience method that scales an image proportionally
01253 %  to half its size.
01254 %
01255 %  The format of the MinifyImage method is:
01256 %
01257 %      Image *MinifyImage(const Image *image,ExceptionInfo *exception)
01258 %
01259 %  A description of each parameter follows:
01260 %
01261 %    o image: the image.
01262 %
01263 %    o exception: return any errors or warnings in this structure.
01264 %
01265 */
01266 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
01267 {
01268   Image
01269     *minify_image;
01270 
01271   assert(image != (Image *) NULL);
01272   assert(image->signature == MagickSignature);
01273   if (image->debug != MagickFalse)
01274     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01275   assert(exception != (ExceptionInfo *) NULL);
01276   assert(exception->signature == MagickSignature);
01277   minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
01278     1.0,exception);
01279   return(minify_image);
01280 }
01281 
01282 /*
01283 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01284 %                                                                             %
01285 %                                                                             %
01286 %                                                                             %
01287 %   R e s a m p l e I m a g e                                                 %
01288 %                                                                             %
01289 %                                                                             %
01290 %                                                                             %
01291 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01292 %
01293 %  ResampleImage() resize image in terms of its pixel size, so that when
01294 %  displayed at the given resolution it will be the same size in terms of
01295 %  real world units as the original image at the original resolution.
01296 %
01297 %  The format of the ResampleImage method is:
01298 %
01299 %      Image *ResampleImage(Image *image,const double x_resolution,
01300 %        const double y_resolution,const FilterTypes filter,const double blur,
01301 %        ExceptionInfo *exception)
01302 %
01303 %  A description of each parameter follows:
01304 %
01305 %    o image: the image to be resized to fit the given resolution.
01306 %
01307 %    o x_resolution: the new image x resolution.
01308 %
01309 %    o y_resolution: the new image y resolution.
01310 %
01311 %    o filter: Image filter to use.
01312 %
01313 %    o blur: the blur factor where > 1 is blurry, < 1 is sharp.
01314 %
01315 */
01316 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
01317   const double y_resolution,const FilterTypes filter,const double blur,
01318   ExceptionInfo *exception)
01319 {
01320 #define ResampleImageTag  "Resample/Image"
01321 
01322   Image
01323     *resample_image;
01324 
01325   unsigned long
01326     height,
01327     width;
01328 
01329   /*
01330     Initialize sampled image attributes.
01331   */
01332   assert(image != (const Image *) NULL);
01333   assert(image->signature == MagickSignature);
01334   if (image->debug != MagickFalse)
01335     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01336   assert(exception != (ExceptionInfo *) NULL);
01337   assert(exception->signature == MagickSignature);
01338   width=(unsigned long) (x_resolution*image->columns/
01339     (image->x_resolution == 0.0 ? 72.0 : image->x_resolution)+0.5);
01340   height=(unsigned long) (y_resolution*image->rows/
01341     (image->y_resolution == 0.0 ? 72.0 : image->y_resolution)+0.5);
01342   resample_image=ResizeImage(image,width,height,filter,blur,exception);
01343   if (resample_image != (Image *) NULL)
01344     {
01345       resample_image->x_resolution=x_resolution;
01346       resample_image->y_resolution=y_resolution;
01347     }
01348   return(resample_image);
01349 }
01350 #if defined(MAGICKCORE_LQR_DELEGATE)
01351 
01352 /*
01353 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01354 %                                                                             %
01355 %                                                                             %
01356 %                                                                             %
01357 %   L i q u i d R e s c a l e I m a g e                                       %
01358 %                                                                             %
01359 %                                                                             %
01360 %                                                                             %
01361 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01362 %
01363 %  LiquidRescaleImage() rescales image with seam carving.
01364 %
01365 %  The format of the LiquidRescaleImage method is:
01366 %
01367 %      Image *LiquidRescaleImage(const Image *image,
01368 %        const unsigned long columns,const unsigned long rows,
01369 %        const double delta_x,const double rigidity,ExceptionInfo *exception)
01370 %
01371 %  A description of each parameter follows:
01372 %
01373 %    o image: the image.
01374 %
01375 %    o columns: the number of columns in the rescaled image.
01376 %
01377 %    o rows: the number of rows in the rescaled image.
01378 %
01379 %    o delta_x: maximum seam transversal step (0 means straight seams).
01380 %
01381 %    o rigidity: introduce a bias for non-straight seams (typically 0).
01382 %
01383 %    o exception: return any errors or warnings in this structure.
01384 %
01385 */
01386 MagickExport Image *LiquidRescaleImage(const Image *image,
01387   const unsigned long columns,const unsigned long rows,
01388   const double delta_x,const double rigidity,ExceptionInfo *exception)
01389 {
01390 #define LiquidRescaleImageTag  "Rescale/Image"
01391 
01392   const char
01393     *map;
01394 
01395   guchar
01396     *packet;
01397 
01398   Image
01399     *rescale_image;
01400 
01401   int
01402     x,
01403     y;
01404 
01405   LqrCarver
01406     *carver;
01407 
01408   LqrRetVal
01409     lqr_status;
01410 
01411   MagickBooleanType
01412     status;
01413 
01414   MagickPixelPacket
01415     pixel;
01416 
01417   unsigned char
01418     *pixels;
01419 
01420   /*
01421     Liquid rescale image.
01422   */
01423   assert(image != (const Image *) NULL);
01424   assert(image->signature == MagickSignature);
01425   if (image->debug != MagickFalse)
01426     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01427   assert(exception != (ExceptionInfo *) NULL);
01428   assert(exception->signature == MagickSignature);
01429   if ((columns == 0) || (rows == 0))
01430     return((Image *) NULL);
01431   if ((columns == image->columns) && (rows == image->rows))
01432     return(CloneImage(image,0,0,MagickTrue,exception));
01433   if ((columns <= 2) || (rows <= 2))
01434     return(ZoomImage(image,columns,rows,exception));
01435   if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
01436     {
01437       Image
01438         *resize_image;
01439 
01440       unsigned long
01441         height,
01442         width;
01443 
01444       /*
01445         Honor liquid resize size limitations.
01446       */
01447       for (width=image->columns; columns >= (2*width-1); width*=2);
01448       for (height=image->rows; rows >= (2*height-1); height*=2);
01449       resize_image=ResizeImage(image,width,height,image->filter,image->blur,
01450         exception);
01451       if (resize_image == (Image *) NULL)
01452         return((Image *) NULL);
01453       rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
01454         rigidity,exception);
01455       resize_image=DestroyImage(resize_image);
01456       return(rescale_image);
01457     }
01458   map="RGB";
01459   if (image->matte == MagickFalse)
01460     map="RGBA";
01461   if (image->colorspace == CMYKColorspace)
01462     {
01463       map="CMYK";
01464       if (image->matte == MagickFalse)
01465         map="CMYKA";
01466     }
01467   pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
01468     strlen(map)*sizeof(*pixels));
01469   if (pixels == (unsigned char *) NULL)
01470     return((Image *) NULL);
01471   status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
01472     pixels,exception);
01473   if (status == MagickFalse)
01474     {
01475       pixels=(unsigned char *) RelinquishMagickMemory(pixels);
01476       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01477     }
01478   carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
01479   if (carver == (LqrCarver *) NULL)
01480     {
01481       pixels=(unsigned char *) RelinquishMagickMemory(pixels);
01482       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01483     }
01484   lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
01485   lqr_status=lqr_carver_resize(carver,columns,rows);
01486   rescale_image=CloneImage(image,lqr_carver_get_width(carver),
01487     lqr_carver_get_height(carver),MagickTrue,exception);
01488   if (rescale_image == (Image *) NULL)
01489     {
01490       pixels=(unsigned char *) RelinquishMagickMemory(pixels);
01491       return((Image *) NULL);
01492     }
01493   if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
01494     {
01495       InheritException(exception,&rescale_image->exception);
01496       rescale_image=DestroyImage(rescale_image);
01497       return((Image *) NULL);
01498     }
01499   GetMagickPixelPacket(rescale_image,&pixel);
01500   (void) lqr_carver_scan_reset(carver);
01501   while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
01502   {
01503     register IndexPacket
01504       *__restrict rescale_indexes;
01505 
01506     register PixelPacket
01507       *__restrict q;
01508 
01509     q=QueueAuthenticPixels(rescale_image,x,y,1,1,exception);
01510     if (q == (PixelPacket *) NULL)
01511       break;
01512     rescale_indexes=GetAuthenticIndexQueue(rescale_image);
01513     pixel.red=QuantumRange*(packet[0]/255.0);
01514     pixel.green=QuantumRange*(packet[1]/255.0);
01515     pixel.blue=QuantumRange*(packet[2]/255.0);
01516     if (image->colorspace != CMYKColorspace)
01517       {
01518         if (image->matte == MagickFalse)
01519           pixel.opacity=QuantumRange*(packet[3]/255.0);
01520       }
01521     else
01522       {
01523         pixel.index=QuantumRange*(packet[3]/255.0);
01524         if (image->matte == MagickFalse)
01525           pixel.opacity=QuantumRange*(packet[4]/255.0);
01526       }
01527     SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
01528     if (SyncAuthenticPixels(rescale_image,exception) == MagickFalse)
01529       break;
01530   }
01531   /*
01532     Relinquish resources.
01533   */
01534   lqr_carver_destroy(carver);
01535   return(rescale_image);
01536 }
01537 #else
01538 MagickExport Image *LiquidRescaleImage(const Image *image,
01539   const unsigned long magick_unused(columns),
01540   const unsigned long magick_unused(rows),const double magick_unused(delta_x),
01541   const double magick_unused(rigidity),ExceptionInfo *exception)
01542 {
01543   assert(image != (const Image *) NULL);
01544   assert(image->signature == MagickSignature);
01545   if (image->debug != MagickFalse)
01546     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01547   assert(exception != (ExceptionInfo *) NULL);
01548   assert(exception->signature == MagickSignature);
01549   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
01550     "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
01551   return((Image *) NULL);
01552 }
01553 #endif
01554 
01555 /*
01556 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01557 %                                                                             %
01558 %                                                                             %
01559 %                                                                             %
01560 %   R e s i z e I m a g e                                                     %
01561 %                                                                             %
01562 %                                                                             %
01563 %                                                                             %
01564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01565 %
01566 %  ResizeImage() scales an image to the desired dimensions, using the given
01567 %  filter (see AcquireFilterInfo() ).
01568 %
01569 %  If an undefined filter is given the filter defaults to Mitchell for a
01570 %  colormapped image, a image with a matte channel, or if the image is
01571 %  enlarged.  Otherwise the filter defaults to a Lanczos.
01572 %
01573 %  ResizeImage() was inspired by Paul Heckbert's "zoom" program.
01574 %
01575 %  The format of the ResizeImage method is:
01576 %
01577 %      Image *ResizeImage(Image *image,const unsigned long columns,
01578 %        const unsigned long rows,const FilterTypes filter,const double blur,
01579 %        ExceptionInfo *exception)
01580 %
01581 %  A description of each parameter follows:
01582 %
01583 %    o image: the image.
01584 %
01585 %    o columns: the number of columns in the scaled image.
01586 %
01587 %    o rows: the number of rows in the scaled image.
01588 %
01589 %    o filter: Image filter to use.
01590 %
01591 %    o blur: the blur factor where > 1 is blurry, < 1 is sharp.
01592 %            Typically set this to 1.0.
01593 %
01594 %    o exception: return any errors or warnings in this structure.
01595 %
01596 */
01597 
01598 typedef struct _ContributionInfo
01599 {
01600   MagickRealType
01601     weight;
01602 
01603   long
01604     pixel;
01605 } ContributionInfo;
01606 
01607 static ContributionInfo **DestroyContributionThreadSet(
01608   ContributionInfo **contribution)
01609 {
01610   register long
01611     i;
01612 
01613   assert(contribution != (ContributionInfo **) NULL);
01614   for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
01615     if (contribution[i] != (ContributionInfo *) NULL)
01616       contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
01617         contribution[i]);
01618   return((ContributionInfo **) RelinquishMagickMemory(contribution));
01619 }
01620 
01621 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
01622 {
01623   register long
01624     i;
01625 
01626   ContributionInfo
01627     **contribution;
01628 
01629   unsigned long
01630     number_threads;
01631 
01632   number_threads=GetOpenMPMaximumThreads();
01633   contribution=(ContributionInfo **) AcquireCachelineMemory(number_threads*
01634     sizeof(*contribution));
01635   if (contribution == (ContributionInfo **) NULL)
01636     return((ContributionInfo **) NULL);
01637   (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
01638   for (i=0; i < (long) number_threads; i++)
01639   {
01640     contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
01641       sizeof(**contribution));
01642     if (contribution[i] == (ContributionInfo *) NULL)
01643       return(DestroyContributionThreadSet(contribution));
01644   }
01645   return(contribution);
01646 }
01647 
01648 static inline double MagickMax(const double x,const double y)
01649 {
01650   if (x > y)
01651     return(x);
01652   return(y);
01653 }
01654 
01655 static inline double MagickMin(const double x,const double y)
01656 {
01657   if (x < y)
01658     return(x);
01659   return(y);
01660 }
01661 
01662 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
01663   const Image *image,Image *resize_image,const MagickRealType x_factor,
01664   const MagickSizeType span,MagickOffsetType *quantum,ExceptionInfo *exception)
01665 {
01666 #define ResizeImageTag  "Resize/Image"
01667 
01668   ClassType
01669     storage_class;
01670 
01671   ContributionInfo
01672     **contributions;
01673 
01674   long
01675     x;
01676 
01677   MagickBooleanType
01678     status;
01679 
01680   MagickPixelPacket
01681     zero;
01682 
01683   MagickRealType
01684     scale,
01685     support;
01686 
01687   ViewInfo
01688     *image_view,
01689     *resize_view;
01690 
01691   /*
01692     Apply filter to resize horizontally from image to resize image.
01693   */
01694   scale=MagickMax(1.0/x_factor,1.0);
01695   support=scale*GetResizeFilterSupport(resize_filter);
01696   storage_class=support > 0.5 ? DirectClass : image->storage_class;
01697   if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
01698     {
01699       InheritException(exception,&resize_image->exception);
01700       return(MagickFalse);
01701     }
01702   if (support < 0.5)
01703     {
01704       /*
01705         Support too small even for nearest neighbour:  reduce to point sampling.
01706       */
01707       support=(MagickRealType) 0.5;
01708       scale=1.0;
01709     }
01710   contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
01711   if (contributions == (ContributionInfo **) NULL)
01712     {
01713       (void) ThrowMagickException(exception,GetMagickModule(),
01714         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
01715       return(MagickFalse);
01716     }
01717   status=MagickTrue;
01718   scale=1.0/scale;
01719   (void) ResetMagickMemory(&zero,0,sizeof(zero));
01720   image_view=AcquireCacheView(image);
01721   resize_view=AcquireCacheView(resize_image);
01722 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01723   #pragma omp parallel for shared(status)
01724 #endif
01725   for (x=0; x < (long) resize_image->columns; x++)
01726   {
01727     long
01728       n,
01729       start,
01730       stop;
01731 
01732     MagickRealType
01733       center,
01734       density;
01735 
01736     register const IndexPacket
01737       *__restrict indexes;
01738 
01739     register const PixelPacket
01740       *__restrict p;
01741 
01742     register ContributionInfo
01743       *__restrict contribution;
01744 
01745     register IndexPacket
01746       *__restrict resize_indexes;
01747 
01748     register long
01749       y;
01750 
01751     register PixelPacket
01752       *__restrict q;
01753 
01754     if (status == MagickFalse)
01755       continue;
01756     center=(MagickRealType) (x+0.5)/x_factor;
01757     start=(long) (MagickMax(center-support-MagickEpsilon,0.0)+0.5);
01758     stop=(long) (MagickMin(center+support,(double) image->columns)+0.5);
01759     density=0.0;
01760     contribution=contributions[GetOpenMPThreadId()];
01761     for (n=0; n < (stop-start); n++)
01762     {
01763       contribution[n].pixel=start+n;
01764       contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
01765         ((MagickRealType) (start+n)-center+0.5));
01766       density+=contribution[n].weight;
01767     }
01768     if ((density != 0.0) && (density != 1.0))
01769       {
01770         register long
01771           i;
01772 
01773         /*
01774           Normalize.
01775         */
01776         density=1.0/density;
01777         for (i=0; i < n; i++)
01778           contribution[i].weight*=density;
01779       }
01780     p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,
01781       (unsigned long) (contribution[n-1].pixel-contribution[0].pixel+1),
01782       image->rows,exception);
01783     q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
01784       exception);
01785     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
01786       {
01787         status=MagickFalse;
01788         continue;
01789       }
01790     indexes=GetCacheViewVirtualIndexQueue(image_view);
01791     resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
01792     for (y=0; y < (long) resize_image->rows; y++)
01793     {
01794       long
01795         j;
01796 
01797       MagickPixelPacket
01798         pixel;
01799 
01800       MagickRealType
01801         alpha;
01802 
01803       register long
01804         i;
01805 
01806       pixel=zero;
01807       if (image->matte == MagickFalse)
01808         {
01809           for (i=0; i < n; i++)
01810           {
01811             j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
01812               (contribution[i].pixel-contribution[0].pixel);
01813             alpha=contribution[i].weight;
01814             pixel.red+=alpha*(p+j)->red;
01815             pixel.green+=alpha*(p+j)->green;
01816             pixel.blue+=alpha*(p+j)->blue;
01817             pixel.opacity+=alpha*(p+j)->opacity;
01818           }
01819           q->red=RoundToQuantum(pixel.red);
01820           q->green=RoundToQuantum(pixel.green);
01821           q->blue=RoundToQuantum(pixel.blue);
01822           q->opacity=RoundToQuantum(pixel.opacity);
01823           if ((image->colorspace == CMYKColorspace) &&
01824               (resize_image->colorspace == CMYKColorspace))
01825             {
01826               for (i=0; i < n; i++)
01827               {
01828                 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
01829                   (contribution[i].pixel-contribution[0].pixel);
01830                 alpha=contribution[i].weight;
01831                 pixel.index+=alpha*indexes[j];
01832               }
01833               resize_indexes[y]=(IndexPacket) RoundToQuantum(pixel.index);
01834             }
01835         }
01836       else
01837         {
01838           MagickRealType
01839             gamma;
01840 
01841           gamma=0.0;
01842           for (i=0; i < n; i++)
01843           {
01844             j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
01845               (contribution[i].pixel-contribution[0].pixel);
01846             alpha=contribution[i].weight*QuantumScale*((MagickRealType)
01847               QuantumRange-(p+j)->opacity);
01848             pixel.red+=alpha*(p+j)->red;
01849             pixel.green+=alpha*(p+j)->green;
01850             pixel.blue+=alpha*(p+j)->blue;
01851             pixel.opacity+=contribution[i].weight*(p+j)->opacity;
01852             gamma+=alpha;
01853           }
01854           gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
01855           q->red=RoundToQuantum(gamma*pixel.red);
01856           q->green=RoundToQuantum(gamma*pixel.green);
01857           q->blue=RoundToQuantum(gamma*pixel.blue);
01858           q->opacity=RoundToQuantum(pixel.opacity);
01859           if ((image->colorspace == CMYKColorspace) &&
01860               (resize_image->colorspace == CMYKColorspace))
01861             {
01862               for (i=0; i < n; i++)
01863               {
01864                 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
01865                   (contribution[i].pixel-contribution[0].pixel);
01866                 alpha=contribution[i].weight*QuantumScale*((MagickRealType)
01867                   QuantumRange-(p+j)->opacity);
01868                 gamma+=alpha;
01869               }
01870               resize_indexes[y]=(IndexPacket) RoundToQuantum(gamma*pixel.index);
01871             }
01872         }
01873       if ((resize_image->storage_class == PseudoClass) &&
01874           (image->storage_class == PseudoClass))
01875         {
01876           i=(long) (MagickMin(MagickMax(center,(double) start),(double) stop-
01877             1.0)+0.5);
01878           j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
01879             (contribution[i-start].pixel-contribution[0].pixel);
01880           resize_indexes[y]=indexes[j];
01881         }
01882       q++;
01883     }
01884     if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
01885       status=MagickFalse;
01886     if (image->progress_monitor != (MagickProgressMonitor) NULL)
01887       {
01888         MagickBooleanType
01889           proceed;
01890 
01891 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01892   #pragma omp critical (MagickCore_HorizontalFilter)
01893 #endif
01894         proceed=SetImageProgress(image,ResizeImageTag,(*quantum)++,span);
01895         if (proceed == MagickFalse)
01896           status=MagickFalse;
01897       }
01898   }
01899   resize_view=DestroyCacheView(resize_view);
01900   image_view=DestroyCacheView(image_view);
01901   contributions=DestroyContributionThreadSet(contributions);
01902   return(status);
01903 }
01904 
01905 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
01906   const Image *image,Image *resize_image,const MagickRealType y_factor,
01907   const MagickSizeType span,MagickOffsetType *quantum,ExceptionInfo *exception)
01908 {
01909   ClassType
01910     storage_class;
01911 
01912   ContributionInfo
01913     **contributions;
01914 
01915   long
01916     y;
01917 
01918   MagickBooleanType
01919     status;
01920 
01921   MagickPixelPacket
01922     zero;
01923 
01924   MagickRealType
01925     scale,
01926     support;
01927 
01928   ViewInfo
01929     *image_view,
01930     *resize_view;
01931 
01932   /*
01933     Apply filter to resize vertically from image to resize_image.
01934   */
01935   scale=MagickMax(1.0/y_factor,1.0);
01936   support=scale*GetResizeFilterSupport(resize_filter);
01937   storage_class=support > 0.5 ? DirectClass : image->storage_class;
01938   if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
01939     {
01940       InheritException(exception,&resize_image->exception);
01941       return(MagickFalse);
01942     }
01943   if (support < 0.5)
01944     {
01945       /*
01946         Support too small even for nearest neighbour:  reduce to point sampling.
01947       */
01948       support=(MagickRealType) 0.5;
01949       scale=1.0;
01950     }
01951   contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
01952   if (contributions == (ContributionInfo **) NULL)
01953     {
01954       (void) ThrowMagickException(exception,GetMagickModule(),
01955         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
01956       return(MagickFalse);
01957     }
01958   status=MagickTrue;
01959   scale=1.0/scale;
01960   (void) ResetMagickMemory(&zero,0,sizeof(zero));
01961   image_view=AcquireCacheView(image);
01962   resize_view=AcquireCacheView(resize_image);
01963 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01964   #pragma omp parallel for shared(status)
01965 #endif
01966   for (y=0; y < (long) resize_image->rows; y++)
01967   {
01968     long
01969       n,
01970       start,
01971       stop;
01972 
01973     MagickRealType
01974       center,
01975       density;
01976 
01977     register const IndexPacket
01978       *__restrict indexes;
01979 
01980     register const PixelPacket
01981       *__restrict p;
01982 
01983     register ContributionInfo
01984       *__restrict contribution;
01985 
01986     register IndexPacket
01987       *__restrict resize_indexes;
01988 
01989     register long
01990       x;
01991 
01992     register PixelPacket
01993       *__restrict q;
01994 
01995     if (status == MagickFalse)
01996       continue;
01997     center=(MagickRealType) (y+0.5)/y_factor;
01998     start=(long) (MagickMax(center-support-MagickEpsilon,0.0)+0.5);
01999     stop=(long) (MagickMin(center+support,(double) image->rows)+0.5);
02000     density=0.0;
02001     contribution=contributions[GetOpenMPThreadId()];
02002     for (n=0; n < (stop-start); n++)
02003     {
02004       contribution[n].pixel=start+n;
02005       contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
02006         ((MagickRealType) (start+n)-center+0.5));
02007       density+=contribution[n].weight;
02008     }
02009     if ((density != 0.0) && (density != 1.0))
02010       {
02011         register long
02012           i;
02013 
02014         /*
02015           Normalize.
02016         */
02017         density=1.0/density;
02018         for (i=0; i < n; i++)
02019           contribution[i].weight*=density;
02020       }
02021     p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
02022       image->columns,(unsigned long) (contribution[n-1].pixel-
02023       contribution[0].pixel+1),exception);
02024     q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
02025       exception);
02026     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
02027       {
02028         status=MagickFalse;
02029         continue;
02030       }
02031     indexes=GetCacheViewVirtualIndexQueue(image_view);
02032     resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
02033     for (x=0; x < (long) resize_image->columns; x++)
02034     {
02035       long
02036         j;
02037 
02038       MagickPixelPacket
02039         pixel;
02040 
02041       MagickRealType
02042         alpha;
02043 
02044       register long
02045         i;
02046 
02047       pixel=zero;
02048       if (image->matte == MagickFalse)
02049         {
02050           for (i=0; i < n; i++)
02051           {
02052             j=(long) ((contribution[i].pixel-contribution[0].pixel)*
02053               image->columns+x);
02054             alpha=contribution[i].weight;
02055             pixel.red+=alpha*(p+j)->red;
02056             pixel.green+=alpha*(p+j)->green;
02057             pixel.blue+=alpha*(p+j)->blue;
02058             pixel.opacity+=alpha*(p+j)->opacity;
02059           }
02060           q->red=RoundToQuantum(pixel.red);
02061           q->green=RoundToQuantum(pixel.green);
02062           q->blue=RoundToQuantum(pixel.blue);
02063           q->opacity=RoundToQuantum(pixel.opacity);
02064           if ((image->colorspace == CMYKColorspace) &&
02065               (resize_image->colorspace == CMYKColorspace))
02066             {
02067               for (i=0; i < n; i++)
02068               {
02069                 j=(long) ((contribution[i].pixel-contribution[0].pixel)*
02070                   image->columns+x);
02071                 alpha=contribution[i].weight;
02072                 pixel.index+=alpha*indexes[j];
02073               }
02074               resize_indexes[x]=(IndexPacket) RoundToQuantum(pixel.index);
02075             }
02076         }
02077       else
02078         {
02079           MagickRealType
02080             gamma;
02081 
02082           gamma=0.0;
02083           for (i=0; i < n; i++)
02084           {
02085             j=(long) ((contribution[i].pixel-contribution[0].pixel)*
02086               image->columns+x);
02087             alpha=contribution[i].weight*QuantumScale*((MagickRealType)
02088               QuantumRange-(p+j)->opacity);
02089             pixel.red+=alpha*(p+j)->red;
02090             pixel.green+=alpha*(p+j)->green;
02091             pixel.blue+=alpha*(p+j)->blue;
02092             pixel.opacity+=contribution[i].weight*(p+j)->opacity;
02093             gamma+=alpha;
02094           }
02095           gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
02096           q->red=RoundToQuantum(gamma*pixel.red);
02097           q->green=RoundToQuantum(gamma*pixel.green);
02098           q->blue=RoundToQuantum(gamma*pixel.blue);
02099           q->opacity=RoundToQuantum(pixel.opacity);
02100           if ((image->colorspace == CMYKColorspace) &&
02101               (resize_image->colorspace == CMYKColorspace))
02102             {
02103               for (i=0; i < n; i++)
02104               {
02105                 j=(long) ((contribution[i].pixel-contribution[0].pixel)*
02106                   image->columns+x);
02107                 alpha=contribution[i].weight*QuantumScale*((MagickRealType)
02108                   QuantumRange-(p+j)->opacity);
02109                 pixel.index+=alpha*indexes[j];
02110               }
02111               resize_indexes[x]=(IndexPacket) RoundToQuantum(gamma*pixel.index);
02112             }
02113         }
02114       if ((resize_image->storage_class == PseudoClass) &&
02115           (image->storage_class == PseudoClass))
02116         {
02117           i=(long) (MagickMin(MagickMax(center,(double) start),(double) stop-
02118             1.0)+0.5);
02119           j=(long) ((contribution[i-start].pixel-contribution[0].pixel)*
02120             image->columns+x);
02121           resize_indexes[x]=indexes[j];
02122         }
02123       q++;
02124     }
02125     if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
02126       status=MagickFalse;
02127     if (image->progress_monitor != (MagickProgressMonitor) NULL)
02128       {
02129         MagickBooleanType
02130           proceed;
02131 
02132 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02133   #pragma omp critical (MagickCore_VerticalFilter)
02134 #endif
02135         proceed=SetImageProgress(image,ResizeImageTag,(*quantum)++,span);
02136         if (proceed == MagickFalse)
02137           status=MagickFalse;
02138       }
02139   }
02140   resize_view=DestroyCacheView(resize_view);
02141   image_view=DestroyCacheView(image_view);
02142   contributions=DestroyContributionThreadSet(contributions);
02143   return(status);
02144 }
02145 
02146 MagickExport Image *ResizeImage(const Image *image,const unsigned long columns,
02147   const unsigned long rows,const FilterTypes filter,const double blur,
02148   ExceptionInfo *exception)
02149 {
02150 #define WorkLoadFactor  0.265
02151 
02152   FilterTypes
02153     filter_type;
02154 
02155   Image
02156     *filter_image,
02157     *resize_image;
02158 
02159   MagickRealType
02160     x_factor,
02161     y_factor;
02162 
02163   MagickSizeType
02164     span;
02165 
02166   MagickStatusType
02167     status;
02168 
02169   ResizeFilter
02170     *resize_filter;
02171 
02172   MagickOffsetType
02173     quantum;
02174 
02175   /*
02176     Acquire resize image.
02177   */
02178   assert(image != (Image *) NULL);
02179   assert(image->signature == MagickSignature);
02180   if (image->debug != MagickFalse)
02181     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02182   assert(exception != (ExceptionInfo *) NULL);
02183   assert(exception->signature == MagickSignature);
02184   if ((columns == 0) || (rows == 0))
02185     ThrowImageException(ImageError,"NegativeOrZeroImageSize");
02186   if ((columns == image->columns) && (rows == image->rows) &&
02187       (filter == UndefinedFilter) && (blur == 1.0))
02188     return(CloneImage(image,0,0,MagickTrue,exception));
02189   resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
02190   if (resize_image == (Image *) NULL)
02191     return(resize_image);
02192   /*
02193     Acquire resize filter.
02194   */
02195   x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
02196   y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
02197   if ((x_factor*y_factor) > WorkLoadFactor)
02198     filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
02199   else
02200     filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
02201   if (filter_image == (Image *) NULL)
02202     return(DestroyImage(resize_image));
02203   filter_type=LanczosFilter;
02204   if (filter != UndefinedFilter)
02205     filter_type=filter;
02206   else
02207     if ((x_factor == 1.0) && (y_factor == 1.0))
02208       filter_type=PointFilter;
02209     else
02210       if ((image->storage_class == PseudoClass) ||
02211           (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
02212         filter_type=MitchellFilter;
02213   resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
02214     exception);
02215   /*
02216     Resize image.
02217   */
02218   quantum=0;
02219   if ((x_factor*y_factor) > WorkLoadFactor)
02220     {
02221       span=(MagickSizeType) (filter_image->columns+rows);
02222       status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
02223         &quantum,exception);
02224       status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
02225         span,&quantum,exception);
02226     }
02227   else
02228     {
02229       span=(MagickSizeType) (filter_image->rows+columns);
02230       status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
02231         &quantum,exception);
02232       status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
02233         span,&quantum,exception);
02234     }
02235   /*
02236     Free resources.
02237   */
02238   filter_image=DestroyImage(filter_image);
02239   resize_filter=DestroyResizeFilter(resize_filter);
02240   if ((status == MagickFalse) || (resize_image == (Image *) NULL))
02241     return((Image *) NULL);
02242   resize_image->type=image->type;
02243   return(resize_image);
02244 }
02245 
02246 /*
02247 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02248 %                                                                             %
02249 %                                                                             %
02250 %                                                                             %
02251 %   S a m p l e I m a g e                                                     %
02252 %                                                                             %
02253 %                                                                             %
02254 %                                                                             %
02255 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02256 %
02257 %  SampleImage() scales an image to the desired dimensions with pixel
02258 %  sampling.  Unlike other scaling methods, this method does not introduce
02259 %  any additional color into the scaled image.
02260 %
02261 %  The format of the SampleImage method is:
02262 %
02263 %      Image *SampleImage(const Image *image,const unsigned long columns,
02264 %        const unsigned long rows,ExceptionInfo *exception)
02265 %
02266 %  A description of each parameter follows:
02267 %
02268 %    o image: the image.
02269 %
02270 %    o columns: the number of columns in the sampled image.
02271 %
02272 %    o rows: the number of rows in the sampled image.
02273 %
02274 %    o exception: return any errors or warnings in this structure.
02275 %
02276 */
02277 MagickExport Image *SampleImage(const Image *image,const unsigned long columns,
02278   const unsigned long rows,ExceptionInfo *exception)
02279 {
02280 #define SampleImageTag  "Sample/Image"
02281 
02282   Image
02283     *sample_image;
02284 
02285   long
02286     progress,
02287     *x_offset,
02288     y;
02289 
02290   MagickBooleanType
02291     status;
02292 
02293   register long
02294     x;
02295 
02296   ViewInfo
02297     *image_view,
02298     *sample_view;
02299 
02300   /*
02301     Initialize sampled image attributes.
02302   */
02303   assert(image != (const Image *) NULL);
02304   assert(image->signature == MagickSignature);
02305   if (image->debug != MagickFalse)
02306     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02307   assert(exception != (ExceptionInfo *) NULL);
02308   assert(exception->signature == MagickSignature);
02309   if ((columns == 0) || (rows == 0))
02310     ThrowImageException(ImageError,"NegativeOrZeroImageSize");
02311   if ((columns == image->columns) && (rows == image->rows))
02312     return(CloneImage(image,0,0,MagickTrue,exception));
02313   sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
02314   if (sample_image == (Image *) NULL)
02315     return((Image *) NULL);
02316   /*
02317     Allocate scan line buffer and column offset buffers.
02318   */
02319   x_offset=(long *) AcquireQuantumMemory((size_t) sample_image->columns,
02320     sizeof(*x_offset));
02321   if (x_offset == (long *) NULL)
02322     {
02323       sample_image=DestroyImage(sample_image);
02324       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02325     }
02326   for (x=0; x < (long) sample_image->columns; x++)
02327     x_offset[x]=(long) (((MagickRealType) x+0.5)*image->columns/
02328       sample_image->columns);
02329   /*
02330     Sample each row.
02331   */
02332   status=MagickTrue;
02333   progress=0;
02334   image_view=AcquireCacheView(image);
02335   sample_view=AcquireCacheView(sample_image);
02336 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02337   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
02338 #endif
02339   for (y=0; y < (long) sample_image->rows; y++)
02340   {
02341     long
02342       y_offset;
02343 
02344     register const IndexPacket
02345       *__restrict indexes;
02346 
02347     register const PixelPacket
02348       *__restrict p;
02349 
02350     register IndexPacket
02351       *__restrict sample_indexes;
02352 
02353     register long
02354       x;
02355 
02356     register PixelPacket
02357       *__restrict q;
02358 
02359     if (status == MagickFalse)
02360       continue;
02361     y_offset=(long) (((MagickRealType) y+0.5)*image->rows/sample_image->rows);
02362     p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
02363       exception);
02364     q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
02365       exception);
02366     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
02367       {
02368         status=MagickFalse;
02369         continue;
02370       }
02371     indexes=GetCacheViewAuthenticIndexQueue(image_view);
02372     sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
02373     /*
02374       Sample each column.
02375     */
02376     for (x=0; x < (long) sample_image->columns; x++)
02377       *q++=p[x_offset[x]];
02378     if ((image->storage_class == PseudoClass) ||
02379         (image->colorspace == CMYKColorspace))
02380       for (x=0; x < (long) sample_image->columns; x++)
02381         sample_indexes[x]=indexes[x_offset[x]];
02382     if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
02383       status=MagickFalse;
02384     if (image->progress_monitor != (MagickProgressMonitor) NULL)
02385       {
02386         MagickBooleanType
02387           proceed;
02388 
02389 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02390   #pragma omp critical (MagickCore_SampleImage)
02391 #endif
02392         proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
02393         if (proceed == MagickFalse)
02394           status=MagickFalse;
02395       }
02396   }
02397   image_view=DestroyCacheView(image_view);
02398   sample_view=DestroyCacheView(sample_view);
02399   x_offset=(long *) RelinquishMagickMemory(x_offset);
02400   sample_image->type=image->type;
02401   return(sample_image);
02402 }
02403 
02404 /*
02405 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02406 %                                                                             %
02407 %                                                                             %
02408 %                                                                             %
02409 %   S c a l e I m a g e                                                       %
02410 %                                                                             %
02411 %                                                                             %
02412 %                                                                             %
02413 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02414 %
02415 %  ScaleImage() changes the size of an image to the given dimensions.
02416 %
02417 %  The format of the ScaleImage method is:
02418 %
02419 %      Image *ScaleImage(const Image *image,const unsigned long columns,
02420 %        const unsigned long rows,ExceptionInfo *exception)
02421 %
02422 %  A description of each parameter follows:
02423 %
02424 %    o image: the image.
02425 %
02426 %    o columns: the number of columns in the scaled image.
02427 %
02428 %    o rows: the number of rows in the scaled image.
02429 %
02430 %    o exception: return any errors or warnings in this structure.
02431 %
02432 */
02433 MagickExport Image *ScaleImage(const Image *image,const unsigned long columns,
02434   const unsigned long rows,ExceptionInfo *exception)
02435 {
02436 #define ScaleImageTag  "Scale/Image"
02437 
02438   Image
02439     *scale_image;
02440 
02441   long
02442     number_rows,
02443     y;
02444 
02445   MagickBooleanType
02446     next_column,
02447     next_row,
02448     proceed;
02449 
02450   MagickPixelPacket
02451     pixel,
02452     *scale_scanline,
02453     *scanline,
02454     *x_vector,
02455     *y_vector,
02456     zero;
02457 
02458   MagickRealType
02459     alpha,
02460     gamma;
02461 
02462   PointInfo
02463     scale,
02464     span;
02465 
02466   register long
02467     i;
02468 
02469   /*
02470     Initialize scaled image attributes.
02471   */
02472   assert(image != (const Image *) NULL);
02473   assert(image->signature == MagickSignature);
02474   if (image->debug != MagickFalse)
02475     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02476   assert(exception != (ExceptionInfo *) NULL);
02477   assert(exception->signature == MagickSignature);
02478   if ((columns == 0) || (rows == 0))
02479     return((Image *) NULL);
02480   if ((columns == image->columns) && (rows == image->rows))
02481     return(CloneImage(image,0,0,MagickTrue,exception));
02482   scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
02483   if (scale_image == (Image *) NULL)
02484     return((Image *) NULL);
02485   if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
02486     {
02487       InheritException(exception,&scale_image->exception);
02488       scale_image=DestroyImage(scale_image);
02489       return((Image *) NULL);
02490     }
02491   /*
02492     Allocate memory.
02493   */
02494   x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
02495     sizeof(*x_vector));
02496   scanline=x_vector;
02497   if (image->rows != scale_image->rows)
02498     scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
02499       sizeof(*scanline));
02500   scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
02501     scale_image->columns,sizeof(*scale_scanline));
02502   y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
02503     sizeof(*y_vector));
02504   if ((scanline == (MagickPixelPacket *) NULL) ||
02505       (scale_scanline == (MagickPixelPacket *) NULL) ||
02506       (x_vector == (MagickPixelPacket *) NULL) ||
02507       (y_vector == (MagickPixelPacket *) NULL))
02508     {
02509       scale_image=DestroyImage(scale_image);
02510       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02511     }
02512   /*
02513     Scale image.
02514   */
02515   number_rows=0;
02516   next_row=MagickTrue;
02517   span.y=1.0;
02518   scale.y=(double) scale_image->rows/(double) image->rows;
02519   (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
02520     sizeof(*y_vector));
02521   GetMagickPixelPacket(image,&pixel);
02522   (void) ResetMagickMemory(&zero,0,sizeof(zero));
02523   i=0;
02524   for (y=0; y < (long) scale_image->rows; y++)
02525   {
02526     register const IndexPacket
02527       *__restrict indexes;
02528 
02529     register const PixelPacket
02530       *__restrict p;
02531 
02532     register IndexPacket
02533       *__restrict scale_indexes;
02534 
02535     register long
02536       x;
02537 
02538     register MagickPixelPacket
02539       *__restrict s,
02540       *__restrict t;
02541 
02542     register PixelPacket
02543       *__restrict q;
02544 
02545     q=QueueAuthenticPixels(scale_image,0,y,scale_image->columns,1,exception);
02546     if (q == (PixelPacket *) NULL)
02547       break;
02548     scale_indexes=GetAuthenticIndexQueue(scale_image);
02549     if (scale_image->rows == image->rows)
02550       {
02551         /*
02552           Read a new scanline.
02553         */
02554         p=GetVirtualPixels(image,0,i++,image->columns,1,exception);
02555         if (p == (const PixelPacket *) NULL)
02556           break;
02557         indexes=GetVirtualIndexQueue(image);
02558         for (x=0; x < (long) image->columns; x++)
02559         {
02560           x_vector[x].red=(MagickRealType) p->red;
02561           x_vector[x].green=(MagickRealType) p->green;
02562           x_vector[x].blue=(MagickRealType) p->blue;
02563           if (image->matte != MagickFalse)
02564             x_vector[x].opacity=(MagickRealType) p->opacity;
02565           if (indexes != (IndexPacket *) NULL)
02566             x_vector[x].index=(MagickRealType) indexes[x];
02567           p++;
02568         }
02569       }
02570     else
02571       {
02572         /*
02573           Scale Y direction.
02574         */
02575         while (scale.y < span.y)
02576         {
02577           if ((next_row != MagickFalse) && (number_rows < (long) image->rows))
02578             {
02579               /*
02580                 Read a new scanline.
02581               */
02582               p=GetVirtualPixels(image,0,i++,image->columns,1,exception);
02583               if (p == (const PixelPacket *) NULL)
02584                 break;
02585               indexes=GetVirtualIndexQueue(image);
02586               for (x=0; x < (long) image->columns; x++)
02587               {
02588                 x_vector[x].red=(MagickRealType) p->red;
02589                 x_vector[x].green=(MagickRealType) p->green;
02590                 x_vector[x].blue=(MagickRealType) p->blue;
02591                 if (image->matte != MagickFalse)
02592                   x_vector[x].opacity=(MagickRealType) p->opacity;
02593                 if (indexes != (IndexPacket *) NULL)
02594                   x_vector[x].index=(MagickRealType) indexes[x];
02595                 p++;
02596               }
02597               number_rows++;
02598             }
02599           for (x=0; x < (long) image->columns; x++)
02600           {
02601             y_vector[x].red+=scale.y*x_vector[x].red;
02602             y_vector[x].green+=scale.y*x_vector[x].green;
02603             y_vector[x].blue+=scale.y*x_vector[x].blue;
02604             if (scale_image->matte != MagickFalse)
02605               y_vector[x].opacity+=scale.y*x_vector[x].opacity;
02606             if (scale_indexes != (IndexPacket *) NULL)
02607               y_vector[x].index+=scale.y*x_vector[x].index;
02608           }
02609           span.y-=scale.y;
02610           scale.y=(double) scale_image->rows/(double) image->rows;
02611           next_row=MagickTrue;
02612         }
02613         if ((next_row != MagickFalse) && (number_rows < (long) image->rows))
02614           {
02615             /*
02616               Read a new scanline.
02617             */
02618             p=GetVirtualPixels(image,0,i++,image->columns,1,exception);
02619             if (p == (const PixelPacket *) NULL)
02620               break;
02621             indexes=GetVirtualIndexQueue(image);
02622             for (x=0; x < (long) image->columns; x++)
02623             {
02624               x_vector[x].red=(MagickRealType) p->red;
02625               x_vector[x].green=(MagickRealType) p->green;
02626               x_vector[x].blue=(MagickRealType) p->blue;
02627               if (image->matte != MagickFalse)
02628                 x_vector[x].opacity=(MagickRealType) p->opacity;
02629               if (indexes != (IndexPacket *) NULL)
02630                 x_vector[x].index=(MagickRealType) indexes[x];
02631               p++;
02632             }
02633             number_rows++;
02634             next_row=MagickFalse;
02635           }
02636         s=scanline;
02637         for (x=0; x < (long) image->columns; x++)
02638         {
02639           pixel.red=y_vector[x].red+span.y*x_vector[x].red;
02640           pixel.green=y_vector[x].green+span.y*x_vector[x].green;
02641           pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
02642           if (image->matte != MagickFalse)
02643             pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
02644           if (scale_indexes != (IndexPacket *) NULL)
02645             pixel.index=y_vector[x].index+span.y*x_vector[x].index;
02646           s->red=pixel.red;
02647           s->green=pixel.green;
02648           s->blue=pixel.blue;
02649           if (scale_image->matte != MagickFalse)
02650             s->opacity=pixel.opacity;
02651           if (scale_indexes != (IndexPacket *) NULL)
02652             s->index=pixel.index;
02653           s++;
02654           y_vector[x]=zero;
02655         }
02656         scale.y-=span.y;
02657         if (scale.y <= 0)
02658           {
02659             scale.y=(double) scale_image->rows/(double) image->rows;
02660             next_row=MagickTrue;
02661           }
02662         span.y=1.0;
02663       }
02664     if (scale_image->columns == image->columns)
02665       {
02666         /*
02667           Transfer scanline to scaled image.
02668         */
02669         s=scanline;
02670         for (x=0; x < (long) scale_image->columns; x++)
02671         {
02672           q->red=RoundToQuantum(s->red);
02673           q->green=RoundToQuantum(s->green);
02674           q->blue=RoundToQuantum(s->blue);
02675           if (scale_image->matte != MagickFalse)
02676             q->opacity=RoundToQuantum(s->opacity);
02677           if (scale_indexes != (IndexPacket *) NULL)
02678             scale_indexes[x]=(IndexPacket) RoundToQuantum(s->index);
02679           q++;
02680           s++;
02681         }
02682       }
02683     else
02684       {
02685         /*
02686           Scale X direction.
02687         */
02688         pixel=zero;
02689         next_column=MagickFalse;
02690         span.x=1.0;
02691         s=scanline;
02692         t=scale_scanline;
02693         for (x=0; x < (long) image->columns; x++)
02694         {
02695           scale.x=(double) scale_image->columns/(double) image->columns;
02696           while (scale.x >= span.x)
02697           {
02698             if (next_column != MagickFalse)
02699               {
02700                 pixel=zero;
02701                 t++;
02702               }
02703             pixel.red+=span.x*s->red;
02704             pixel.green+=span.x*s->green;
02705             pixel.blue+=span.x*s->blue;
02706             if (image->matte != MagickFalse)
02707               pixel.opacity+=span.x*s->opacity;
02708             if (scale_indexes != (IndexPacket *) NULL)
02709               pixel.index+=span.x*s->index;
02710             t->red=pixel.red;
02711             t->green=pixel.green;
02712             t->blue=pixel.blue;
02713             if (scale_image->matte != MagickFalse)
02714               t->opacity=pixel.opacity;
02715             if (scale_indexes != (IndexPacket *) NULL)
02716               t->index=pixel.index;
02717             scale.x-=span.x;
02718             span.x=1.0;
02719             next_column=MagickTrue;
02720           }
02721         if (scale.x > 0)
02722           {
02723             if (next_column != MagickFalse)
02724               {
02725                 pixel=zero;
02726                 next_column=MagickFalse;
02727                 t++;
02728               }
02729             pixel.red+=scale.x*s->red;
02730             pixel.green+=scale.x*s->green;
02731             pixel.blue+=scale.x*s->blue;
02732             if (scale_image->matte != MagickFalse)
02733               pixel.opacity+=scale.x*s->opacity;
02734             if (scale_indexes != (IndexPacket *) NULL)
02735               pixel.index+=scale.x*s->index;
02736             span.x-=scale.x;
02737           }
02738         s++;
02739       }
02740       if (span.x > 0)
02741         {
02742           s--;
02743           pixel.red+=span.x*s->red;
02744           pixel.green+=span.x*s->green;
02745           pixel.blue+=span.x*s->blue;
02746           if (scale_image->matte != MagickFalse)
02747             pixel.opacity+=span.x*s->opacity;
02748           if (scale_indexes != (IndexPacket *) NULL)
02749             pixel.index+=span.x*s->index;
02750         }
02751       if ((next_column == MagickFalse) &&
02752           ((long) (t-scale_scanline) < (long) scale_image->columns))
02753         {
02754           t->red=pixel.red;
02755           t->green=pixel.green;
02756           t->blue=pixel.blue;
02757           if (scale_image->matte != MagickFalse)
02758             t->opacity=pixel.opacity;
02759           if (scale_indexes != (IndexPacket *) NULL)
02760             t->index=pixel.index;
02761         }
02762       /*
02763         Transfer scanline to scaled image.
02764       */
02765       t=scale_scanline;
02766       for (x=0; x < (long) scale_image->columns; x++)
02767       {
02768         alpha=1.0;
02769         if (image->matte != MagickFalse)
02770           alpha=(MagickRealType) (QuantumScale*(QuantumRange-t->opacity));
02771         gamma=1.0/(fabs((double) alpha) <= MagickEpsilon ? 1.0 : alpha);
02772         q->red=RoundToQuantum(gamma*t->red);
02773         q->green=RoundToQuantum(gamma*t->green);
02774         q->blue=RoundToQuantum(gamma*t->blue);
02775         if (scale_image->matte != MagickFalse)
02776           q->opacity=RoundToQuantum(t->opacity);
02777         if (scale_indexes != (IndexPacket *) NULL)
02778           scale_indexes[x]=(IndexPacket) RoundToQuantum(gamma*t->index);
02779         t++;
02780         q++;
02781       }
02782     }
02783     if (SyncAuthenticPixels(scale_image,exception) == MagickFalse)
02784       break;
02785     proceed=SetImageProgress(image,ScaleImageTag,y,image->rows);
02786     if (proceed == MagickFalse)
02787       break;
02788   }
02789   /*
02790     Free allocated memory.
02791   */
02792   y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
02793   scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
02794   if (scale_image->rows != image->rows)
02795     scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
02796   x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
02797   scale_image->type=image->type;
02798   return(scale_image);
02799 }
02800 
02801 /*
02802 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02803 %                                                                             %
02804 %                                                                             %
02805 %                                                                             %
02806 +   S e t R e s i z e F i l t e r S u p p o r t                               %
02807 %                                                                             %
02808 %                                                                             %
02809 %                                                                             %
02810 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02811 %
02812 %  SetResizeFilterSupport() specifies which IR filter to use to window
02813 %
02814 %  The format of the SetResizeFilterSupport method is:
02815 %
02816 %      void SetResizeFilterSupport(ResizeFilter *resize_filter,
02817 %        const MagickRealType support)
02818 %
02819 %  A description of each parameter follows:
02820 %
02821 %    o resize_filter: the resize filter.
02822 %
02823 %    o support: the filter spport radius.
02824 %
02825 */
02826 MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
02827   const MagickRealType support)
02828 {
02829   assert(resize_filter != (ResizeFilter *) NULL);
02830   assert(resize_filter->signature == MagickSignature);
02831   resize_filter->support=support;
02832 }
02833 
02834 /*
02835 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02836 %                                                                             %
02837 %                                                                             %
02838 %                                                                             %
02839 %   T h u m b n a i l I m a g e                                               %
02840 %                                                                             %
02841 %                                                                             %
02842 %                                                                             %
02843 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02844 %
02845 %  ThumbnailImage() changes the size of an image to the given dimensions and
02846 %  removes any associated profiles.  The goal is to produce small low cost
02847 %  thumbnail images suited for display on the Web.
02848 %
02849 %  The format of the ThumbnailImage method is:
02850 %
02851 %      Image *ThumbnailImage(const Image *image,const unsigned long columns,
02852 %        const unsigned long rows,ExceptionInfo *exception)
02853 %
02854 %  A description of each parameter follows:
02855 %
02856 %    o image: the image.
02857 %
02858 %    o columns: the number of columns in the scaled image.
02859 %
02860 %    o rows: the number of rows in the scaled image.
02861 %
02862 %    o exception: return any errors or warnings in this structure.
02863 %
02864 */
02865 MagickExport Image *ThumbnailImage(const Image *image,
02866   const unsigned long columns,const unsigned long rows,ExceptionInfo *exception)
02867 {
02868 #define SampleFactor  5
02869 
02870   char
02871     value[MaxTextExtent];
02872 
02873   const char
02874     *attribute;
02875 
02876   Image
02877     *thumbnail_image;
02878 
02879   MagickRealType
02880     x_factor,
02881     y_factor;
02882 
02883   struct stat
02884     attributes;
02885 
02886   unsigned long
02887     version;
02888 
02889   assert(image != (Image *) NULL);
02890   assert(image->signature == MagickSignature);
02891   if (image->debug != MagickFalse)
02892     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02893   assert(exception != (ExceptionInfo *) NULL);
02894   assert(exception->signature == MagickSignature);
02895   x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
02896   y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
02897   if ((x_factor*y_factor) > 0.1)
02898     {
02899       thumbnail_image=ZoomImage(image,columns,rows,exception);
02900       if (thumbnail_image != (Image *) NULL)
02901         (void) StripImage(thumbnail_image);
02902       return(thumbnail_image);
02903     }
02904   if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
02905     thumbnail_image=ZoomImage(image,columns,rows,exception);
02906   else
02907     {
02908       Image
02909         *sample_image;
02910 
02911       sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
02912         exception);
02913       if (sample_image == (Image *) NULL)
02914         return((Image *) NULL);
02915       thumbnail_image=ZoomImage(sample_image,columns,rows,exception);
02916       sample_image=DestroyImage(sample_image);
02917     }
02918   if (thumbnail_image == (Image *) NULL)
02919     return(thumbnail_image);
02920   (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
02921   if (thumbnail_image->matte == MagickFalse)
02922     (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
02923   thumbnail_image->depth=8;
02924   thumbnail_image->interlace=NoInterlace;
02925   (void) StripImage(thumbnail_image);
02926   (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
02927   if (strstr(image->magick_filename,"///") == (char *) NULL)
02928     (void) FormatMagickString(value,MaxTextExtent,"file:///%s",
02929       image->magick_filename);
02930   (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
02931   (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
02932   if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
02933     {
02934       (void) FormatMagickString(value,MaxTextExtent,"%ld",(long)
02935         attributes.st_mtime);
02936       (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
02937     }
02938   (void) FormatMagickString(value,MaxTextExtent,"%ld",(long)
02939     attributes.st_mtime);
02940   (void) FormatMagickSize(GetBlobSize(image),value);
02941   (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
02942   (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
02943   LocaleLower(value);
02944   (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
02945   attribute=GetImageProperty(image,"comment");
02946   if ((attribute != (const char *) NULL) &&
02947       (value != (char *) NULL))
02948     (void) SetImageProperty(thumbnail_image,"description",value);
02949   (void) SetImageProperty(thumbnail_image,"software",
02950     GetMagickVersion(&version));
02951   (void) FormatMagickString(value,MaxTextExtent,"%lu",image->magick_columns);
02952   (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
02953   (void) FormatMagickString(value,MaxTextExtent,"%lu",image->magick_rows);
02954   (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
02955   (void) FormatMagickString(value,MaxTextExtent,"%lu",
02956     GetImageListLength(image));
02957   (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
02958   return(thumbnail_image);
02959 }
02960 
02961 /*
02962 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02963 %                                                                             %
02964 %                                                                             %
02965 %                                                                             %
02966 %   Z o o m I m a g e                                                         %
02967 %                                                                             %
02968 %                                                                             %
02969 %                                                                             %
02970 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02971 %
02972 %  ZoomImage() creates a new image that is a scaled size of an existing one.
02973 %  It allocates the memory necessary for the new Image structure and returns a
02974 %  pointer to the new image.  The Point filter gives fast pixel replication,
02975 %  Triangle is equivalent to bi-linear interpolation, and Mitchel giver slower,
02976 %  very high-quality results.  See Graphic Gems III for details on this
02977 %  algorithm.
02978 %
02979 %  The filter member of the Image structure specifies which image filter to
02980 %  use. Blur specifies the blur factor where > 1 is blurry, < 1 is sharp.
02981 %
02982 %  The format of the ZoomImage method is:
02983 %
02984 %      Image *ZoomImage(const Image *image,const unsigned long columns,
02985 %        const unsigned long rows,ExceptionInfo *exception)
02986 %
02987 %  A description of each parameter follows:
02988 %
02989 %    o image: the image.
02990 %
02991 %    o columns: An integer that specifies the number of columns in the zoom
02992 %      image.
02993 %
02994 %    o rows: An integer that specifies the number of rows in the scaled
02995 %      image.
02996 %
02997 %    o exception: return any errors or warnings in this structure.
02998 %
02999 */
03000 MagickExport Image *ZoomImage(const Image *image,const unsigned long columns,
03001   const unsigned long rows,ExceptionInfo *exception)
03002 {
03003   Image
03004     *zoom_image;
03005 
03006   assert(image != (const Image *) NULL);
03007   assert(image->signature == MagickSignature);
03008   if (image->debug != MagickFalse)
03009     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03010   assert(exception != (ExceptionInfo *) NULL);
03011   assert(exception->signature == MagickSignature);
03012   zoom_image=ResizeImage(image,columns,rows,image->filter,image->blur,
03013     exception);
03014   return(zoom_image);
03015 }

Generated on Thu Jul 2 12:03:21 2009 for MagickCore by  doxygen 1.5.8