MagickCore  6.7.5
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-2012 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 "MagickCore/studio.h"
00043 #include "MagickCore/artifact.h"
00044 #include "MagickCore/blob.h"
00045 #include "MagickCore/cache.h"
00046 #include "MagickCore/cache-view.h"
00047 #include "MagickCore/color.h"
00048 #include "MagickCore/color-private.h"
00049 #include "MagickCore/draw.h"
00050 #include "MagickCore/exception.h"
00051 #include "MagickCore/exception-private.h"
00052 #include "MagickCore/gem.h"
00053 #include "MagickCore/image.h"
00054 #include "MagickCore/image-private.h"
00055 #include "MagickCore/list.h"
00056 #include "MagickCore/memory_.h"
00057 #include "MagickCore/magick.h"
00058 #include "MagickCore/pixel-accessor.h"
00059 #include "MagickCore/property.h"
00060 #include "MagickCore/monitor.h"
00061 #include "MagickCore/monitor-private.h"
00062 #include "MagickCore/option.h"
00063 #include "MagickCore/pixel.h"
00064 #include "MagickCore/quantum-private.h"
00065 #include "MagickCore/resample.h"
00066 #include "MagickCore/resample-private.h"
00067 #include "MagickCore/resize.h"
00068 #include "MagickCore/resize-private.h"
00069 #include "MagickCore/string_.h"
00070 #include "MagickCore/string-private.h"
00071 #include "MagickCore/thread-private.h"
00072 #include "MagickCore/utility.h"
00073 #include "MagickCore/utility-private.h"
00074 #include "MagickCore/version.h"
00075 #if defined(MAGICKCORE_LQR_DELEGATE)
00076 #include <lqr.h>
00077 #endif
00078 
00079 /*
00080   Typedef declarations.
00081 */
00082 struct _ResizeFilter
00083 {
00084   MagickRealType
00085     (*filter)(const MagickRealType,const ResizeFilter *),
00086     (*window)(const MagickRealType,const ResizeFilter *),
00087     support,        /* filter region of support - the filter support limit */
00088     window_support, /* window support, usally equal to support (expert only) */
00089     scale,          /* dimension scaling to fit window support (usally 1.0) */
00090     blur,           /* x-scale (blur-sharpen) */
00091     coefficient[7]; /* cubic coefficents for BC-cubic spline filters */
00092 
00093   size_t
00094     signature;
00095 };
00096 
00097 /*
00098   Forward declaractions.
00099 */
00100 static MagickRealType
00101   I0(MagickRealType x),
00102   BesselOrderOne(MagickRealType),
00103   Sinc(const MagickRealType, const ResizeFilter *),
00104   SincFast(const MagickRealType, const ResizeFilter *);
00105 
00106 /*
00107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00108 %                                                                             %
00109 %                                                                             %
00110 %                                                                             %
00111 +   F i l t e r F u n c t i o n s                                             %
00112 %                                                                             %
00113 %                                                                             %
00114 %                                                                             %
00115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00116 %
00117 %  These are the various filter and windowing functions that are provided.
00118 %
00119 %  They are internal to this module only.  See AcquireResizeFilterInfo() for
00120 %  details of the access to these functions, via the GetResizeFilterSupport()
00121 %  and GetResizeFilterWeight() API interface.
00122 %
00123 %  The individual filter functions have this format...
00124 %
00125 %     static MagickRealtype *FilterName(const MagickRealType x,
00126 %        const MagickRealType support)
00127 %
00128 %  A description of each parameter follows:
00129 %
00130 %    o x: the distance from the sampling point generally in the range of 0 to
00131 %      support.  The GetResizeFilterWeight() ensures this a positive value.
00132 %
00133 %    o resize_filter: current filter information.  This allows function to
00134 %      access support, and possibly other pre-calculated information defining
00135 %      the functions.
00136 %
00137 */
00138 
00139 #define MagickPIL ((MagickRealType) 3.14159265358979323846264338327950288420L)
00140 
00141 static MagickRealType Blackman(const MagickRealType x,
00142   const ResizeFilter *magick_unused(resize_filter))
00143 {
00144   /*
00145     Blackman: 2nd order cosine windowing function:
00146       0.42 + 0.5 cos(pi x) + 0.08 cos(2pi x)
00147 
00148     Refactored by Chantal Racette and Nicolas Robidoux to one trig call and
00149     five flops.
00150   */
00151   const MagickRealType cosine=cos((double) (MagickPIL*x));
00152   return(0.34+cosine*(0.5+cosine*0.16));
00153 }
00154 
00155 static MagickRealType Bohman(const MagickRealType x,
00156   const ResizeFilter *magick_unused(resize_filter))
00157 {
00158   /*
00159     Bohman: 2rd Order cosine windowing function:
00160       (1-x) cos(pi x) + sin(pi x) / pi.
00161 
00162     Refactored by Nicolas Robidoux to one trig call, one sqrt call, and 7 flops,
00163     taking advantage of the fact that the support of Bohman is 1.0 (so that we
00164     know that sin(pi x) >= 0).
00165   */
00166   const MagickRealType cosine=cos((double) (MagickPIL*x));
00167   const MagickRealType sine=sqrt(1.0-cosine*cosine);
00168   return((1.0-x)*cosine+(1.0/MagickPIL)*sine);
00169 }
00170 
00171 static MagickRealType Box(const MagickRealType magick_unused(x),
00172   const ResizeFilter *magick_unused(resize_filter))
00173 {
00174   /*
00175     A Box filter is a equal weighting function (all weights equal).  DO NOT
00176     LIMIT results by support or resize point sampling will work as it requests
00177     points beyond its normal 0.0 support size.
00178   */
00179   return(1.0);
00180 }
00181 
00182 static MagickRealType CubicBC(const MagickRealType x,
00183   const ResizeFilter *resize_filter)
00184 {
00185   /*
00186     Cubic Filters using B,C determined values:
00187        Mitchell-Netravali  B= 1/3 C= 1/3  "Balanced" cubic spline filter
00188        Catmull-Rom         B= 0   C= 1/2  Interpolatory and exact on linears
00189        Cubic B-Spline      B= 1   C= 0    Spline approximation of Gaussian
00190        Hermite             B= 0   C= 0    Spline with small support (= 1)
00191 
00192     See paper by Mitchell and Netravali, Reconstruction Filters in Computer
00193     Graphics Computer Graphics, Volume 22, Number 4, August 1988
00194     http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/
00195     Mitchell.pdf.
00196 
00197     Coefficents are determined from B,C values:
00198        P0 = (  6 - 2*B       )/6 = coeff[0]
00199        P1 =         0
00200        P2 = (-18 +12*B + 6*C )/6 = coeff[1]
00201        P3 = ( 12 - 9*B - 6*C )/6 = coeff[2]
00202        Q0 = (      8*B +24*C )/6 = coeff[3]
00203        Q1 = (    -12*B -48*C )/6 = coeff[4]
00204        Q2 = (      6*B +30*C )/6 = coeff[5]
00205        Q3 = (    - 1*B - 6*C )/6 = coeff[6]
00206 
00207     which are used to define the filter:
00208 
00209        P0 + P1*x + P2*x^2 + P3*x^3      0 <= x < 1
00210        Q0 + Q1*x + Q2*x^2 + Q3*x^3      1 <= x < 2
00211 
00212     which ensures function is continuous in value and derivative (slope).
00213   */
00214   if (x < 1.0)
00215     return(resize_filter->coefficient[0]+x*(x*(resize_filter->coefficient[1]+x*
00216       resize_filter->coefficient[2])));
00217   if (x < 2.0)
00218     return(resize_filter->coefficient[3]+x*(resize_filter->coefficient[4]+x*
00219       (resize_filter->coefficient[5]+x*resize_filter->coefficient[6])));
00220   return(0.0);
00221 }
00222 
00223 static MagickRealType Gaussian(const MagickRealType x,
00224   const ResizeFilter *resize_filter)
00225 {
00226   /*
00227     Gaussian with a fixed sigma = 1/2
00228 
00229     Gaussian Formula (1D) ...
00230        exp( -(x^2)/((2.0*sigma^2) ) / sqrt(2*PI)sigma^2))
00231 
00232     The constants are pre-calculated...
00233        exp( -coeff[0]*(x^2)) ) * coeff[1]
00234 
00235     However the multiplier coefficent (1) is not needed and not used.
00236 
00237     Gaussian Formula (2D) ...
00238        exp( -(x^2)/((2.0*sigma^2) ) / (PI*sigma^2) )
00239 
00240     Note that it is only a change in the normalization multiplier which is
00241     not needed or used when gausian is used as a filter.
00242 
00243     This separates the gaussian 'sigma' value from the 'blur/support'
00244     settings allowing for its use in special 'small sigma' gaussians,
00245     without the filter 'missing' pixels because the support becomes too small.
00246   */
00247   return(exp((double)(-resize_filter->coefficient[0]*x*x)));
00248 }
00249 
00250 static MagickRealType Hanning(const MagickRealType x,
00251   const ResizeFilter *magick_unused(resize_filter))
00252 {
00253   /*
00254     Cosine window function:
00255       0.5+0.5*cos(pi*x).
00256   */
00257   const MagickRealType cosine=cos((double) (MagickPIL*x));
00258   return(0.5+0.5*cosine);
00259 }
00260 
00261 static MagickRealType Hamming(const MagickRealType x,
00262   const ResizeFilter *magick_unused(resize_filter))
00263 {
00264   /*
00265     Offset cosine window function:
00266      .54 + .46 cos(pi x).
00267   */
00268   const MagickRealType cosine=cos((double) (MagickPIL*x));
00269   return(0.54+0.46*cosine);
00270 }
00271 
00272 static MagickRealType Jinc(const MagickRealType x,
00273   const ResizeFilter *magick_unused(resize_filter))
00274 {
00275   /*
00276     See Pratt "Digital Image Processing" p.97 for Jinc/Bessel functions.
00277     http://mathworld.wolfram.com/JincFunction.html and page 11 of
00278     http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
00279 
00280     The original "zoom" program by Paul Heckbert called this "Bessel".  But
00281     really it is more accurately named "Jinc".
00282   */
00283   if (x == 0.0)
00284     return(0.5*MagickPIL);
00285   return(BesselOrderOne(MagickPIL*x)/x);
00286 }
00287 
00288 static MagickRealType Kaiser(const MagickRealType x,
00289   const ResizeFilter *resize_filter)
00290 {
00291   /*
00292     Kaiser Windowing Function (bessel windowing)
00293     Alpha (c[0]) is a free value from 5 to 8 (defaults to 6.5).
00294     A scaling factor (c[1]) is not needed as filter is normalized
00295   */
00296   return(resize_filter->coefficient[1]*
00297               I0(resize_filter->coefficient[0]*sqrt((double) (1.0-x*x))));
00298 }
00299 
00300 static MagickRealType Lagrange(const MagickRealType x,
00301   const ResizeFilter *resize_filter)
00302 {
00303   MagickRealType
00304     value;
00305 
00306   register ssize_t
00307     i;
00308 
00309   ssize_t
00310     n,
00311     order;
00312 
00313   /*
00314     Lagrange piecewise polynomial fit of sinc: N is the 'order' of the lagrange
00315     function and depends on the overall support window size of the filter. That
00316     is: for a support of 2, it gives a lagrange-4 (piecewise cubic function).
00317 
00318     "n" identifies the piece of the piecewise polynomial.
00319 
00320     See Survey: Interpolation Methods, IEEE Transactions on Medical Imaging,
00321     Vol 18, No 11, November 1999, p1049-1075, -- Equation 27 on p1064.
00322   */
00323   if (x > resize_filter->support)
00324     return(0.0);
00325   order=(ssize_t) (2.0*resize_filter->window_support);  /* number of pieces */
00326   n=(ssize_t) (resize_filter->window_support+x);
00327   value=1.0f;
00328   for (i=0; i < order; i++)
00329     if (i != n)
00330       value*=(n-i-x)/(n-i);
00331   return(value);
00332 }
00333 
00334 static MagickRealType Quadratic(const MagickRealType x,
00335   const ResizeFilter *magick_unused(resize_filter))
00336 {
00337   /*
00338     2rd order (quadratic) B-Spline approximation of Gaussian.
00339   */
00340   if (x < 0.5)
00341     return(0.75-x*x);
00342   if (x < 1.5)
00343     return(0.5*(x-1.5)*(x-1.5));
00344   return(0.0);
00345 }
00346 
00347 static MagickRealType Sinc(const MagickRealType x,
00348   const ResizeFilter *magick_unused(resize_filter))
00349 {
00350   /*
00351     Scaled sinc(x) function using a trig call:
00352       sinc(x) == sin(pi x)/(pi x).
00353   */
00354   if (x != 0.0)
00355     {
00356       const MagickRealType alpha=(MagickRealType) (MagickPIL*x);
00357       return(sin((double) alpha)/alpha);
00358     }
00359   return((MagickRealType) 1.0);
00360 }
00361 
00362 static MagickRealType SincFast(const MagickRealType x,
00363   const ResizeFilter *magick_unused(resize_filter))
00364 {
00365   /*
00366     Approximations of the sinc function sin(pi x)/(pi x) over the interval
00367     [-4,4] constructed by Nicolas Robidoux and Chantal Racette with funding
00368     from the Natural Sciences and Engineering Research Council of Canada.
00369 
00370     Although the approximations are polynomials (for low order of
00371     approximation) and quotients of polynomials (for higher order of
00372     approximation) and consequently are similar in form to Taylor polynomials /
00373     Pade approximants, the approximations are computed with a completely
00374     different technique.
00375 
00376     Summary: These approximations are "the best" in terms of bang (accuracy)
00377     for the buck (flops). More specifically: Among the polynomial quotients
00378     that can be computed using a fixed number of flops (with a given "+ - * /
00379     budget"), the chosen polynomial quotient is the one closest to the
00380     approximated function with respect to maximum absolute relative error over
00381     the given interval.
00382 
00383     The Remez algorithm, as implemented in the boost library's minimax package,
00384     is the key to the construction: http://www.boost.org/doc/libs/1_36_0/libs/
00385     math/doc/sf_and_dist/html/math_toolkit/backgrounders/remez.html
00386 
00387     If outside of the interval of approximation, use the standard trig formula.
00388   */
00389   if (x > 4.0)
00390     {
00391       const MagickRealType alpha=(MagickRealType) (MagickPIL*x);
00392       return(sin((double) alpha)/alpha);
00393     }
00394   {
00395     /*
00396       The approximations only depend on x^2 (sinc is an even function).
00397     */
00398     const MagickRealType xx = x*x;
00399 #if MAGICKCORE_QUANTUM_DEPTH <= 8
00400     /*
00401       Maximum absolute relative error 6.3e-6 < 1/2^17.
00402     */
00403     const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
00404     const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
00405     const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
00406     const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
00407     const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
00408     const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
00409     const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
00410     const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
00411     const MagickRealType p =
00412       c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
00413     return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
00414 #elif MAGICKCORE_QUANTUM_DEPTH <= 16
00415     /*
00416       Max. abs. rel. error 2.2e-8 < 1/2^25.
00417     */
00418     const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
00419     const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
00420     const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
00421     const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
00422     const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
00423     const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
00424     const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
00425     const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
00426     const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
00427     const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
00428     const MagickRealType p =
00429       c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
00430     return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
00431 #else
00432     /*
00433       Max. abs. rel. error 1.2e-12 < 1/2^39.
00434     */
00435     const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
00436     const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
00437     const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
00438     const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
00439     const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
00440     const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
00441     const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
00442     const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
00443     const MagickRealType p =
00444       c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
00445     const MagickRealType d0 = 1.0L;
00446     const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
00447     const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
00448     const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
00449     const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
00450     const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
00451     return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
00452 #endif
00453   }
00454 }
00455 
00456 static MagickRealType Triangle(const MagickRealType x,
00457   const ResizeFilter *magick_unused(resize_filter))
00458 {
00459   /*
00460     1st order (linear) B-Spline, bilinear interpolation, Tent 1D filter, or
00461     a Bartlett 2D Cone filter.  Also used as a Bartlett Windowing function
00462     for Sinc().
00463   */
00464   if (x < 1.0)
00465     return(1.0-x);
00466   return(0.0);
00467 }
00468 
00469 static MagickRealType Welsh(const MagickRealType x,
00470   const ResizeFilter *magick_unused(resize_filter))
00471 {
00472   /*
00473     Welsh parabolic windowing filter.
00474   */
00475   if (x < 1.0)
00476     return(1.0-x*x);
00477   return(0.0);
00478 }
00479 
00480 /*
00481 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00482 %                                                                             %
00483 %                                                                             %
00484 %                                                                             %
00485 +   A c q u i r e R e s i z e F i l t e r                                     %
00486 %                                                                             %
00487 %                                                                             %
00488 %                                                                             %
00489 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00490 %
00491 %  AcquireResizeFilter() allocates the ResizeFilter structure.  Choose from
00492 %  these filters:
00493 %
00494 %  FIR (Finite impulse Response) Filters
00495 %      Box  Triangle Quadratic  Cubic  Hermite  Catrom  Mitchell
00496 %
00497 %  IIR (Infinite impulse Response) Filters
00498 %      Gaussian  Sinc  Jinc (Bessel)
00499 %
00500 %  Windowed Sinc/Jinc Filters
00501 %      Blackman  Hanning  Hamming  Kaiser Lanczos
00502 %
00503 %  Special purpose Filters
00504 %      SincFast  LanczosSharp  Lanczos2D  Lanczos2DSharp  Robidoux
00505 %
00506 %  The users "-filter" selection is used to lookup the default 'expert'
00507 %  settings for that filter from a internal table.  However any provided
00508 %  'expert' settings (see below) may override this selection.
00509 %
00510 %  FIR filters are used as is, and are limited to that filters support window
00511 %  (unless over-ridden).  'Gaussian' while classed as an IIR filter, is also
00512 %  simply clipped by its support size (currently 1.5 or approximatally 3*sigma
00513 %  as recommended by many references)
00514 %
00515 %  The special a 'cylindrical' filter flag will promote the default 4-lobed
00516 %  Windowed Sinc filter to a 3-lobed Windowed Jinc equivalent, which is better
00517 %  suited to this style of image resampling. This typically happens when using
00518 %  such a filter for images distortions.
00519 %
00520 %  Directly requesting 'Sinc', 'Jinc' function as a filter will force the use
00521 %  of function without any windowing, or promotion for cylindrical usage.  This
00522 %  is not recommended, except by image processing experts, especially as part
00523 %  of expert option filter function selection.
00524 %
00525 %  Two forms of the 'Sinc' function are available: Sinc and SincFast.  Sinc is
00526 %  computed using the traditional sin(pi*x)/(pi*x); it is selected if the user
00527 %  specifically specifies the use of a Sinc filter. SincFast uses highly
00528 %  accurate (and fast) polynomial (low Q) and rational (high Q) approximations,
00529 %  and will be used by default in most cases.
00530 %
00531 %  The Lanczos filter is a special 3-lobed Sinc-windowed Sinc filter (promoted
00532 %  to Jinc-windowed Jinc for cylindrical (Elliptical Weighted Average) use).
00533 %  The Sinc version is the most popular windowed filter.
00534 %
00535 %  LanczosSharp is a slightly sharpened (blur=0.9812505644269356 < 1) form of
00536 %  the Lanczos filter, specifically designed for EWA distortion (as a
00537 %  Jinc-Jinc); it can also be used as a slightly sharper orthogonal Lanczos
00538 %  (Sinc-Sinc) filter. The chosen blur value comes as close as possible to
00539 %  satisfying the following condition without changing the character of the
00540 %  corresponding EWA filter:
00541 %
00542 %    'No-Op' Vertical and Horizontal Line Preservation Condition: Images with
00543 %    only vertical or horizontal features are preserved when performing 'no-op"
00544 %    with EWA distortion.
00545 %
00546 %  The Lanczos2 and Lanczos2Sharp filters are 2-lobe versions of the Lanczos
00547 %  filters.  The 'sharp' version uses a blur factor of 0.9549963639785485,
00548 %  again chosen because the resulting EWA filter comes as close as possible to
00549 %  satisfying the above condition.
00550 %
00551 %  Robidoux is another filter tuned for EWA. It is the Keys cubic filter
00552 %  defined by B=(228 - 108 sqrt(2))/199. Robidoux satisfies the "'No-Op'
00553 %  Vertical and Horizontal Line Preservation Condition" exactly, and it
00554 %  moderately blurs high frequency 'pixel-hash' patterns under no-op.  It turns
00555 %  out to be close to both Mitchell and Lanczos2Sharp.  For example, its first
00556 %  crossing is at (36 sqrt(2) + 123)/(72 sqrt(2) + 47), almost the same as the
00557 %  first crossing of Mitchell and Lanczos2Sharp.
00558 %
00559 %  'EXPERT' OPTIONS:
00560 %
00561 %  These artifact "defines" are not recommended for production use without
00562 %  expert knowledge of resampling, filtering, and the effects they have on the
00563 %  resulting resampled (resize ro distorted) image.
00564 %
00565 %  They can be used to override any and all filter default, and it is
00566 %  recommended you make good use of "filter:verbose" to make sure that the
00567 %  overall effect of your selection (before and after) is as expected.
00568 %
00569 %    "filter:verbose"  controls whether to output the exact results of the
00570 %       filter selections made, as well as plotting data for graphing the
00571 %       resulting filter over the filters support range.
00572 %
00573 %    "filter:filter"  select the main function associated with this filter
00574 %       name, as the weighting function of the filter.  This can be used to
00575 %       set a windowing function as a weighting function, for special
00576 %       purposes, such as graphing.
00577 %
00578 %       If a "filter:window" operation has not been provided, a 'Box'
00579 %       windowing function will be set to denote that no windowing function is
00580 %       being used.
00581 %
00582 %    "filter:window"  Select this windowing function for the filter. While any
00583 %       filter could be used as a windowing function, using the 'first lobe' of
00584 %       that filter over the whole support window, using a non-windowing
00585 %       function is not advisible. If no weighting filter function is specifed
00586 %       a 'SincFast' filter is used.
00587 %
00588 %    "filter:lobes"  Number of lobes to use for the Sinc/Jinc filter.  This a
00589 %       simpler method of setting filter support size that will correctly
00590 %       handle the Sinc/Jinc switch for an operators filtering requirements.
00591 %       Only integers should be given.
00592 %
00593 %    "filter:support" Set the support size for filtering to the size given.
00594 %       This not recommended for Sinc/Jinc windowed filters (lobes should be
00595 %       used instead).  This will override any 'filter:lobes' option.
00596 %
00597 %    "filter:win-support" Scale windowing function to this size instead.  This
00598 %       causes the windowing (or self-windowing Lagrange filter) to act is if
00599 %       the support window it much much larger than what is actually supplied
00600 %       to the calling operator.  The filter however is still clipped to the
00601 %       real support size given, by the support range suppiled to the caller.
00602 %       If unset this will equal the normal filter support size.
00603 %
00604 %    "filter:blur" Scale the filter and support window by this amount.  A value
00605 %       > 1 will generally result in a more burred image with more ringing
00606 %       effects, while a value <1 will sharpen the resulting image with more
00607 %       aliasing effects.
00608 %
00609 %    "filter:sigma" The sigma value to use for the Gaussian filter only.
00610 %       Defaults to '1/2'.  Using a different sigma effectively provides a
00611 %       method of using the filter as a 'blur' convolution.  Particularly when
00612 %       using it for Distort.
00613 %
00614 %    "filter:b"
00615 %    "filter:c" Override the preset B,C values for a Cubic type of filter.
00616 %       If only one of these are given it is assumes to be a 'Keys' type of
00617 %       filter such that B+2C=1, where Keys 'alpha' value = C.
00618 %
00619 %  Examples:
00620 %
00621 %  Set a true un-windowed Sinc filter with 10 lobes (very slow):
00622 %     -define filter:filter=Sinc
00623 %     -define filter:lobes=8
00624 %
00625 %  Set an 8 lobe Lanczos (Sinc or Jinc) filter:
00626 %     -filter Lanczos
00627 %     -define filter:lobes=8
00628 %
00629 %  The format of the AcquireResizeFilter method is:
00630 %
00631 %      ResizeFilter *AcquireResizeFilter(const Image *image,
00632 %        const FilterTypes filter_type, const MagickBooleanType radial,
00633 %        ExceptionInfo *exception)
00634 %
00635 %  A description of each parameter follows:
00636 %
00637 %    o image: the image.
00638 %
00639 %    o filter: the filter type, defining a preset filter, window and support.
00640 %      The artifact settings listed above will override those selections.
00641 %
00642 %    o blur: blur the filter by this amount, use 1.0 if unknown.  Image
00643 %      artifact "filter:blur" will override this API call usage, including any
00644 %      internal change (such as for cylindrical usage).
00645 %
00646 %    o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical (radial)
00647 %      filter (Jinc).
00648 %
00649 %    o exception: return any errors or warnings in this structure.
00650 %
00651 */
00652 MagickPrivate ResizeFilter *AcquireResizeFilter(const Image *image,
00653   const FilterTypes filter,const MagickRealType blur,
00654   const MagickBooleanType cylindrical,ExceptionInfo *exception)
00655 {
00656   const char
00657     *artifact;
00658 
00659   FilterTypes
00660     filter_type,
00661     window_type;
00662 
00663   MagickRealType
00664     B,
00665     C,
00666     value;
00667 
00668   register ResizeFilter
00669     *resize_filter;
00670 
00671   /*
00672     Table Mapping given Filter, into Weighting and Windowing functions. A
00673     'Box' windowing function means its a simble non-windowed filter.  An
00674     'SincFast' filter function could be upgraded to a 'Jinc' filter if a
00675     "cylindrical", unless a 'Sinc' or 'SincFast' filter was specifically
00676     requested.
00677 
00678     WARNING: The order of this tabel must match the order of the FilterTypes
00679     enumeration specified in "resample.h", or the filter names will not match
00680     the filter being setup.
00681 
00682     You can check filter setups with the "filter:verbose" setting.
00683   */
00684   static struct
00685   {
00686     FilterTypes
00687       filter,
00688       window;
00689   } const mapping[SentinelFilter] =
00690   {
00691     { UndefinedFilter,     BoxFilter      },  /* Undefined (default to Box)   */
00692     { PointFilter,         BoxFilter      },  /* SPECIAL: Nearest neighbour   */
00693     { BoxFilter,           BoxFilter      },  /* Box averaging filter         */
00694     { TriangleFilter,      BoxFilter      },  /* Linear interpolation filter  */
00695     { HermiteFilter,       BoxFilter      },  /* Hermite interpolation filter */
00696     { SincFastFilter,      HanningFilter  },  /* Hanning -- cosine-sinc       */
00697     { SincFastFilter,      HammingFilter  },  /* Hamming --      '' variation */
00698     { SincFastFilter,      BlackmanFilter },  /* Blackman -- 2*cosine-sinc    */
00699     { GaussianFilter,      BoxFilter      },  /* Gaussian blur filter         */
00700     { QuadraticFilter,     BoxFilter      },  /* Quadratic Gaussian approx    */
00701     { CubicFilter,         BoxFilter      },  /* Cubic B-Spline               */
00702     { CatromFilter,        BoxFilter      },  /* Cubic-Keys interpolator      */
00703     { MitchellFilter,      BoxFilter      },  /* 'Ideal' Cubic-Keys filter    */
00704     { JincFilter,          BoxFilter      },  /* Raw 3-lobed Jinc function    */
00705     { SincFilter,          BoxFilter      },  /* Raw 4-lobed Sinc function    */
00706     { SincFastFilter,      BoxFilter      },  /* Raw fast sinc ("Pade"-type)  */
00707     { SincFastFilter,      KaiserFilter   },  /* Kaiser -- square root-sinc   */
00708     { SincFastFilter,      WelshFilter    },  /* Welsh -- parabolic-sinc      */
00709     { SincFastFilter,      CubicFilter    },  /* Parzen -- cubic-sinc         */
00710     { SincFastFilter,      BohmanFilter   },  /* Bohman -- 2*cosine-sinc      */
00711     { SincFastFilter,      TriangleFilter },  /* Bartlett -- triangle-sinc    */
00712     { LagrangeFilter,      BoxFilter      },  /* Lagrange self-windowing      */
00713     { LanczosFilter,       LanczosFilter  },  /* Lanczos Sinc-Sinc filters    */
00714     { LanczosSharpFilter,  LanczosSharpFilter }, /* | these require */
00715     { Lanczos2Filter,      Lanczos2Filter },     /* | special handling */
00716     { Lanczos2SharpFilter, Lanczos2SharpFilter },
00717     { RobidouxFilter,      BoxFilter      },  /* Cubic Keys tuned for EWA     */
00718   };
00719   /*
00720     Table mapping the filter/window from the above table to an actual function.
00721     The default support size for that filter as a weighting function, the range
00722     to scale with to use that function as a sinc windowing function, (typ 1.0).
00723 
00724     Note that the filter_type -> function is 1 to 1 except for Sinc(),
00725     SincFast(), and CubicBC() functions, which may have multiple filter to
00726     function associations.
00727 
00728     See "filter:verbose" handling below for the function -> filter mapping.
00729   */
00730   static struct
00731   {
00732     MagickRealType
00733       (*function)(const MagickRealType,const ResizeFilter*),
00734       lobes,  /* Default lobes/support size of the weighting filter. */
00735       scale, /* Support when function used as a windowing function
00736                 Typically equal to the location of the first zero crossing. */
00737       B,C;   /* BC-spline coefficients, ignored if not a CubicBC filter. */
00738   } const filters[SentinelFilter] =
00739   {
00740     { Box,       0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box)  */
00741     { Box,       0.0, 0.5, 0.0, 0.0 }, /* Point (special handling)    */
00742     { Box,       0.5, 0.5, 0.0, 0.0 }, /* Box                         */
00743     { Triangle,  1.0, 1.0, 0.0, 0.0 }, /* Triangle                    */
00744     { CubicBC,   1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic  B=C=0)      */
00745     { Hanning,   1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window      */
00746     { Hamming,   1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation       */
00747     { Blackman,  1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window   */
00748     { Gaussian,  2.0, 1.5, 0.0, 0.0 }, /* Gaussian                    */
00749     { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian          */
00750     { CubicBC,   2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0)    */
00751     { CubicBC,   2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom    (B=0,C=1/2)  */
00752     { CubicBC,   2.0, 8.0/7.0, 1./3., 1./3. }, /* Mitchell   (B=C=1/3)    */
00753     { Jinc,      3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
00754     { Sinc,      4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc            */
00755     { SincFast,  4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
00756     { Kaiser,    1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
00757     { Welsh,     1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window)    */
00758     { CubicBC,   2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window)    */
00759     { Bohman,    1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window     */
00760     { Triangle,  1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window)  */
00761     { Lagrange,  2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
00762     { SincFast,  3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc  */
00763     { SincFast,  3.0, 1.0, 0.0, 0.0 }, /* lanczos, Sharpened          */
00764     { SincFast,  2.0, 1.0, 0.0, 0.0 }, /* Lanczos, 2-lobed            */
00765     { SincFast,  2.0, 1.0, 0.0, 0.0 }, /* Lanczos2, sharpened         */
00766     { CubicBC,   2.0, 1.1685777620836932, 0.37821575509399867,
00767                  0.31089212245300067 }
00768                  /* Robidoux: Keys cubic close to Lanczos2D sharpened */
00769   };
00770   /*
00771     The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
00772     function being used as a filter. It is used by the "filter:lobes" expert
00773     setting and for 'lobes' for Jinc functions in the previous table. This way
00774     users do not have to deal with the highly irrational lobe sizes of the Jinc
00775     filter.
00776 
00777     Values taken from http://cose.math.bas.bg/webMathematica/webComputing/
00778     BesselZeros.jsp using Jv-function with v=1, then dividing by PI.
00779   */
00780   static MagickRealType
00781     jinc_zeros[16] =
00782     {
00783       1.2196698912665045,
00784       2.2331305943815286,
00785       3.2383154841662362,
00786       4.2410628637960699,
00787       5.2427643768701817,
00788       6.2439216898644877,
00789       7.2447598687199570,
00790       8.2453949139520427,
00791       9.2458926849494673,
00792       10.246293348754916,
00793       11.246622794877883,
00794       12.246898461138105,
00795       13.247132522181061,
00796       14.247333735806849,
00797       15.247508563037300,
00798       16.247661874700962
00799    };
00800 
00801   /*
00802     Allocate resize filter.
00803   */
00804   assert(image != (const Image *) NULL);
00805   assert(image->signature == MagickSignature);
00806   if (image->debug != MagickFalse)
00807     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00808   assert(UndefinedFilter < filter && filter < SentinelFilter);
00809   assert(exception != (ExceptionInfo *) NULL);
00810   assert(exception->signature == MagickSignature);
00811   resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
00812   if (resize_filter == (ResizeFilter *) NULL)
00813     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00814   (void) ResetMagickMemory(resize_filter,0,sizeof(*resize_filter));
00815   /*
00816     Defaults for the requested filter.
00817   */
00818   filter_type=mapping[filter].filter;
00819   window_type=mapping[filter].window;
00820   resize_filter->blur = blur;   /* function argument blur factor */
00821   /* Promote 1D Windowed Sinc Filters to a 2D Windowed Jinc filters */
00822   if ((cylindrical != MagickFalse) && (filter_type == SincFastFilter) &&
00823       (filter != SincFastFilter))
00824     filter_type=JincFilter;  /* 1D Windowed Sinc => 2D Windowed Jinc filters */
00825   /* Expert filter setting override */
00826   artifact=GetImageArtifact(image,"filter:filter");
00827   if (artifact != (const char *) NULL)
00828     {
00829       ssize_t
00830         option;
00831 
00832       option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
00833       if ((UndefinedFilter < option) && (option < SentinelFilter))
00834         {
00835           /*
00836             Raw filter request - no window function.
00837           */
00838           filter_type=(FilterTypes) option;
00839           window_type=BoxFilter;
00840         }
00841       /*
00842         Filter override with a specific window function.
00843       */
00844       artifact=GetImageArtifact(image,"filter:window");
00845       if (artifact != (const char *) NULL)
00846         {
00847           option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
00848           if ((UndefinedFilter < option) && (option < SentinelFilter))
00849             window_type=(FilterTypes) option;
00850         }
00851     }
00852   else
00853     {
00854       /*
00855         Window specified, but no filter function?  Assume Sinc/Jinc.
00856       */
00857       artifact=GetImageArtifact(image,"filter:window");
00858       if (artifact != (const char *) NULL)
00859         {
00860           ssize_t
00861             option;
00862 
00863           option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
00864           if ((UndefinedFilter < option) && (option < SentinelFilter))
00865             {
00866               filter_type=cylindrical != MagickFalse ? JincFilter :
00867                  SincFastFilter;
00868               window_type=(FilterTypes) option;
00869             }
00870         }
00871     }
00872   /*
00873     Assign the real functions to use for the filters selected.
00874   */
00875   resize_filter->filter=filters[filter_type].function;
00876   resize_filter->support=filters[filter_type].lobes;
00877   resize_filter->window=filters[window_type].function;
00878   resize_filter->scale=filters[window_type].scale;
00879   resize_filter->signature=MagickSignature;
00880   /*
00881     Filter Modifications for orthogonal/cylindrical usage.
00882   */
00883   if (cylindrical != MagickFalse)
00884     switch (filter_type)
00885     {
00886       case BoxFilter:
00887       {
00888         /*
00889           Support for Cylindrical Box should be sqrt(2)/2.
00890         */
00891         resize_filter->support=(MagickRealType) MagickSQ1_2;
00892         break;
00893       }
00894       case LanczosFilter:
00895       case LanczosSharpFilter:
00896       case Lanczos2Filter:
00897       case Lanczos2SharpFilter:
00898       {
00899         /*
00900           Number of lobes (support window size) remain unchanged.
00901         */
00902         resize_filter->filter=filters[JincFilter].function;
00903         resize_filter->window=filters[JincFilter].function;
00904         resize_filter->scale=filters[JincFilter].scale;
00905         break;
00906       }
00907       default:
00908         break;
00909     }
00910   /*
00911     Global Sharpening (regardless of orthoginal/cylindrical).
00912   */
00913   switch (filter_type)
00914   {
00915     case LanczosSharpFilter:
00916     {
00917       resize_filter->blur*=0.9812505644269356;
00918       break;
00919     }
00920     case Lanczos2SharpFilter:
00921     {
00922       resize_filter->blur*=0.9549963639785485;
00923       break;
00924     }
00925     default:
00926       break;
00927   }
00928   /*
00929     Expert Option Modifications.
00930   */
00931   /* User Gaussian Sigma Override - no support change */
00932   value=0.5;    /* guassian sigma default, half pixel */
00933   if ( GaussianFilter ) {
00934   artifact=GetImageArtifact(image,"filter:sigma");
00935   if (artifact != (const char *) NULL)
00936       value=StringToDouble(artifact,(char **) NULL);
00937     /* Define coefficents for Gaussian */
00938     resize_filter->coefficient[0]=1.0/(2.0*value*value); /* X scaling */
00939     resize_filter->coefficient[1]=(MagickRealType) (1.0/(Magick2PI*value*
00940       value)); /* normalization */
00941   }
00942   /* User Kaiser Alpha Override - no support change */
00943   if ( KaiserFilter ) {
00944     value=6.5; /* default alpha value for Kaiser bessel windowing function */
00945     artifact=GetImageArtifact(image,"filter:alpha");
00946     if (artifact != (const char *) NULL)
00947       value=StringToDouble(artifact,(char **) NULL);
00948     /* Define coefficents for Kaiser Windowing Function */
00949     resize_filter->coefficient[0]=value;         /* X scaling */
00950     resize_filter->coefficient[1]=1.0/I0(value); /* normalization */
00951   }
00952 
00953   /* Blur Override */
00954   artifact=GetImageArtifact(image,"filter:blur");
00955   if (artifact != (const char *) NULL)
00956     resize_filter->blur*=StringToDouble(artifact,(char **) NULL);
00957   if (resize_filter->blur < MagickEpsilon)
00958     resize_filter->blur=(MagickRealType) MagickEpsilon;
00959 
00960   /* Support Overrides */
00961   artifact=GetImageArtifact(image,"filter:lobes");
00962   if (artifact != (const char *) NULL)
00963     {
00964       ssize_t
00965         lobes;
00966 
00967       lobes=(ssize_t) StringToLong(artifact);
00968       if (lobes < 1)
00969         lobes=1;
00970       resize_filter->support=(MagickRealType) lobes;
00971     }
00972   /* Convert a Jinc function lobes value to a real support value */
00973   if (resize_filter->filter == Jinc)
00974     {
00975       if (resize_filter->support > 16)
00976         resize_filter->support=jinc_zeros[15];  /* largest entry in table */
00977       else
00978         resize_filter->support=jinc_zeros[((long)resize_filter->support)-1];
00979     }
00980   /* expert override of the support setting */
00981   artifact=GetImageArtifact(image,"filter:support");
00982   if (artifact != (const char *) NULL)
00983     resize_filter->support=fabs(StringToDouble(artifact,(char **) NULL));
00984   /*
00985     Scale windowing function separately to the support 'clipping'
00986     window that calling operator is planning to actually use. (Expert
00987     override)
00988   */
00989   resize_filter->window_support=resize_filter->support; /* default */
00990   artifact=GetImageArtifact(image,"filter:win-support");
00991   if (artifact != (const char *) NULL)
00992     resize_filter->window_support=fabs(StringToDouble(artifact,(char **) NULL));
00993   /*
00994     Adjust window function scaling to match windowing support for
00995     weighting function.  This avoids a division on every filter call.
00996   */
00997   resize_filter->scale/=resize_filter->window_support;
00998 
00999   /*
01000    * Set Cubic Spline B,C values, calculate Cubic coefficients.
01001   */
01002   B=0.0;
01003   C=0.0;
01004   if ((filters[filter_type].function == CubicBC) ||
01005       (filters[window_type].function == CubicBC))
01006     {
01007       B=filters[filter_type].B;
01008       C=filters[filter_type].C;
01009       if (filters[window_type].function == CubicBC)
01010         {
01011           B=filters[window_type].B;
01012           C=filters[window_type].C;
01013         }
01014       artifact=GetImageArtifact(image,"filter:b");
01015       if (artifact != (const char *) NULL)
01016         {
01017           B=StringToDouble(artifact,(char **) NULL);
01018           C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
01019           artifact=GetImageArtifact(image,"filter:c"); /* user C override */
01020           if (artifact != (const char *) NULL)
01021             C=StringToDouble(artifact,(char **) NULL);
01022         }
01023       else
01024         {
01025           artifact=GetImageArtifact(image,"filter:c");
01026           if (artifact != (const char *) NULL)
01027             {
01028               C=StringToDouble(artifact,(char **) NULL);
01029               B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
01030             }
01031         }
01032       /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
01033      {
01034         double
01035           B_squared;
01036 
01037         B_squared=B+B;
01038         resize_filter->coefficient[0]=1.0-(1.0/3.0)*B;
01039         resize_filter->coefficient[1]=-3.0+B_squared+C;
01040         resize_filter->coefficient[2]=2.0-1.5*B-C;
01041         resize_filter->coefficient[3]=(4.0/3.0)*B+4.0*C;
01042         resize_filter->coefficient[4]=-8.0*C-B_squared;
01043         resize_filter->coefficient[5]=B+5.0*C;
01044         resize_filter->coefficient[6]=(-1.0/6.0)*B-C;
01045      }
01046     }
01047 
01048   /*
01049     Expert Option Request for verbose details of the resulting filter.
01050   */
01051 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01052   #pragma omp master
01053   {
01054 #endif
01055     artifact=GetImageArtifact(image,"filter:verbose");
01056     if (IsMagickTrue(artifact) != MagickFalse)
01057       {
01058         double
01059           support,
01060           x;
01061 
01062         /*
01063           Set the weighting function properly when the weighting
01064           function may not exactly match the filter of the same name.
01065           EG: a Point filter is really uses a Box weighting function
01066           with a different support than is typically used.
01067         */
01068         if (resize_filter->filter == Box)       filter_type=BoxFilter;
01069         if (resize_filter->filter == Sinc)      filter_type=SincFilter;
01070         if (resize_filter->filter == SincFast)  filter_type=SincFastFilter;
01071         if (resize_filter->filter == Jinc)      filter_type=JincFilter;
01072         if (resize_filter->filter == CubicBC)   filter_type=CubicFilter;
01073         if (resize_filter->window == Box)       window_type=BoxFilter;
01074         if (resize_filter->window == Sinc)      window_type=SincFilter;
01075         if (resize_filter->window == SincFast)  window_type=SincFastFilter;
01076         if (resize_filter->window == Jinc)      window_type=JincFilter;
01077         if (resize_filter->window == CubicBC)   window_type=CubicFilter;
01078         /*
01079           Report Filter Details.
01080         */
01081         support=GetResizeFilterSupport(resize_filter);  /* practical_support */
01082         (void) FormatLocaleFile(stdout,"# Resize Filter (for graphing)\n#\n");
01083         (void) FormatLocaleFile(stdout,"# filter = %s\n",
01084           CommandOptionToMnemonic(MagickFilterOptions,filter_type));
01085         (void) FormatLocaleFile(stdout,"# window = %s\n",
01086           CommandOptionToMnemonic(MagickFilterOptions, window_type));
01087         (void) FormatLocaleFile(stdout,"# support = %.*g\n",
01088           GetMagickPrecision(),(double) resize_filter->support);
01089         (void) FormatLocaleFile(stdout,"# win-support = %.*g\n",
01090           GetMagickPrecision(),(double) resize_filter->window_support);
01091         (void) FormatLocaleFile(stdout,"# scale_blur = %.*g\n",
01092           GetMagickPrecision(), (double)resize_filter->blur);
01093         if (filter_type == GaussianFilter)
01094           (void) FormatLocaleFile(stdout,"# gaussian_sigma = %.*g\n",
01095             GetMagickPrecision(), (double)value);
01096         if ( filter_type == KaiserFilter )
01097           (void) FormatLocaleFile(stdout,"# kaiser_alpha = %.*g\n",
01098             GetMagickPrecision(), (double)value);
01099         (void) FormatLocaleFile(stdout,"# practical_support = %.*g\n",
01100            GetMagickPrecision(), (double)support);
01101         if ( filter_type == CubicFilter || window_type == CubicFilter )
01102           (void) FormatLocaleFile(stdout,"# B,C = %.*g,%.*g\n",
01103             GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
01104         (void) FormatLocaleFile(stdout,"\n");
01105         /*
01106           Output values of resulting filter graph -- for graphing
01107           filter result.
01108         */
01109         for (x=0.0; x <= support; x+=0.01f)
01110           (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",x,
01111             GetMagickPrecision(),(double) GetResizeFilterWeight(resize_filter,
01112             x));
01113         /* A final value so gnuplot can graph the 'stop' properly. */
01114         (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",support,
01115           GetMagickPrecision(),0.0);
01116       }
01117       /* Output the above once only for each image - remove setting */
01118     (void) DeleteImageArtifact((Image *) image,"filter:verbose");
01119 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01120   }
01121 #endif
01122   return(resize_filter);
01123 }
01124 
01125 /*
01126 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01127 %                                                                             %
01128 %                                                                             %
01129 %                                                                             %
01130 %   A d a p t i v e R e s i z e I m a g e                                     %
01131 %                                                                             %
01132 %                                                                             %
01133 %                                                                             %
01134 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01135 %
01136 %  AdaptiveResizeImage() adaptively resize image with pixel resampling.
01137 %
01138 %  This is shortcut function for a fast interpolative resize using mesh
01139 %  interpolation.  It works well for small resizes of less than +/- 50%
01140 %  of the original image size.  For larger resizing on images a full
01141 %  filtered and slower resize function should be used instead.
01142 %
01143 %  The format of the AdaptiveResizeImage method is:
01144 %
01145 %      Image *AdaptiveResizeImage(const Image *image,const size_t columns,
01146 %        const size_t rows, ExceptionInfo *exception)
01147 %
01148 %  A description of each parameter follows:
01149 %
01150 %    o image: the image.
01151 %
01152 %    o columns: the number of columns in the resized image.
01153 %
01154 %    o rows: the number of rows in the resized image.
01155 %
01156 %    o exception: return any errors or warnings in this structure.
01157 %
01158 */
01159 MagickExport Image *AdaptiveResizeImage(const Image *image,
01160   const size_t columns,const size_t rows,ExceptionInfo *exception)
01161 {
01162   Image
01163     *resize_image;
01164 
01165   resize_image=InterpolativeResizeImage(image,columns,rows,MeshInterpolatePixel,
01166     exception);
01167   return(resize_image);
01168 }
01169 
01170 /*
01171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01172 %                                                                             %
01173 %                                                                             %
01174 %                                                                             %
01175 +   B e s s e l O r d e r O n e                                               %
01176 %                                                                             %
01177 %                                                                             %
01178 %                                                                             %
01179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01180 %
01181 %  BesselOrderOne() computes the Bessel function of x of the first kind of
01182 %  order 0.  This is used to create the Jinc() filter function below.
01183 %
01184 %    Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
01185 %
01186 %       j1(x) = x*j1(x);
01187 %
01188 %    For x in (8,inf)
01189 %
01190 %       j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
01191 %
01192 %    where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
01193 %
01194 %       cos(x1) =  cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
01195 %               =  1/sqrt(2) * (sin(x) - cos(x))
01196 %       sin(x1) =  sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
01197 %               = -1/sqrt(2) * (sin(x) + cos(x))
01198 %
01199 %  The format of the BesselOrderOne method is:
01200 %
01201 %      MagickRealType BesselOrderOne(MagickRealType x)
01202 %
01203 %  A description of each parameter follows:
01204 %
01205 %    o x: MagickRealType value.
01206 %
01207 */
01208 
01209 #undef I0
01210 static MagickRealType I0(MagickRealType x)
01211 {
01212   MagickRealType
01213     sum,
01214     t,
01215     y;
01216 
01217   register ssize_t
01218     i;
01219 
01220   /*
01221     Zeroth order Bessel function of the first kind.
01222   */
01223   sum=1.0;
01224   y=x*x/4.0;
01225   t=y;
01226   for (i=2; t > MagickEpsilon; i++)
01227   {
01228     sum+=t;
01229     t*=y/((MagickRealType) i*i);
01230   }
01231   return(sum);
01232 }
01233 
01234 #undef J1
01235 static MagickRealType J1(MagickRealType x)
01236 {
01237   MagickRealType
01238     p,
01239     q;
01240 
01241   register ssize_t
01242     i;
01243 
01244   static const double
01245     Pone[] =
01246     {
01247        0.581199354001606143928050809e+21,
01248       -0.6672106568924916298020941484e+20,
01249        0.2316433580634002297931815435e+19,
01250       -0.3588817569910106050743641413e+17,
01251        0.2908795263834775409737601689e+15,
01252       -0.1322983480332126453125473247e+13,
01253        0.3413234182301700539091292655e+10,
01254       -0.4695753530642995859767162166e+7,
01255        0.270112271089232341485679099e+4
01256     },
01257     Qone[] =
01258     {
01259       0.11623987080032122878585294e+22,
01260       0.1185770712190320999837113348e+20,
01261       0.6092061398917521746105196863e+17,
01262       0.2081661221307607351240184229e+15,
01263       0.5243710262167649715406728642e+12,
01264       0.1013863514358673989967045588e+10,
01265       0.1501793594998585505921097578e+7,
01266       0.1606931573481487801970916749e+4,
01267       0.1e+1
01268     };
01269 
01270   p=Pone[8];
01271   q=Qone[8];
01272   for (i=7; i >= 0; i--)
01273   {
01274     p=p*x*x+Pone[i];
01275     q=q*x*x+Qone[i];
01276   }
01277   return(p/q);
01278 }
01279 
01280 #undef P1
01281 static MagickRealType P1(MagickRealType x)
01282 {
01283   MagickRealType
01284     p,
01285     q;
01286 
01287   register ssize_t
01288     i;
01289 
01290   static const double
01291     Pone[] =
01292     {
01293       0.352246649133679798341724373e+5,
01294       0.62758845247161281269005675e+5,
01295       0.313539631109159574238669888e+5,
01296       0.49854832060594338434500455e+4,
01297       0.2111529182853962382105718e+3,
01298       0.12571716929145341558495e+1
01299     },
01300     Qone[] =
01301     {
01302       0.352246649133679798068390431e+5,
01303       0.626943469593560511888833731e+5,
01304       0.312404063819041039923015703e+5,
01305       0.4930396490181088979386097e+4,
01306       0.2030775189134759322293574e+3,
01307       0.1e+1
01308     };
01309 
01310   p=Pone[5];
01311   q=Qone[5];
01312   for (i=4; i >= 0; i--)
01313   {
01314     p=p*(8.0/x)*(8.0/x)+Pone[i];
01315     q=q*(8.0/x)*(8.0/x)+Qone[i];
01316   }
01317   return(p/q);
01318 }
01319 
01320 #undef Q1
01321 static MagickRealType Q1(MagickRealType x)
01322 {
01323   MagickRealType
01324     p,
01325     q;
01326 
01327   register ssize_t
01328     i;
01329 
01330   static const double
01331     Pone[] =
01332     {
01333       0.3511751914303552822533318e+3,
01334       0.7210391804904475039280863e+3,
01335       0.4259873011654442389886993e+3,
01336       0.831898957673850827325226e+2,
01337       0.45681716295512267064405e+1,
01338       0.3532840052740123642735e-1
01339     },
01340     Qone[] =
01341     {
01342       0.74917374171809127714519505e+4,
01343       0.154141773392650970499848051e+5,
01344       0.91522317015169922705904727e+4,
01345       0.18111867005523513506724158e+4,
01346       0.1038187585462133728776636e+3,
01347       0.1e+1
01348     };
01349 
01350   p=Pone[5];
01351   q=Qone[5];
01352   for (i=4; i >= 0; i--)
01353   {
01354     p=p*(8.0/x)*(8.0/x)+Pone[i];
01355     q=q*(8.0/x)*(8.0/x)+Qone[i];
01356   }
01357   return(p/q);
01358 }
01359 
01360 static MagickRealType BesselOrderOne(MagickRealType x)
01361 {
01362   MagickRealType
01363     p,
01364     q;
01365 
01366   if (x == 0.0)
01367     return(0.0);
01368   p=x;
01369   if (x < 0.0)
01370     x=(-x);
01371   if (x < 8.0)
01372     return(p*J1(x));
01373   q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
01374     cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
01375     cos((double) x))));
01376   if (p < 0.0)
01377     q=(-q);
01378   return(q);
01379 }
01380 
01381 /*
01382 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01383 %                                                                             %
01384 %                                                                             %
01385 %                                                                             %
01386 +   D e s t r o y R e s i z e F i l t e r                                     %
01387 %                                                                             %
01388 %                                                                             %
01389 %                                                                             %
01390 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01391 %
01392 %  DestroyResizeFilter() destroy the resize filter.
01393 %
01394 %  The format of the DestroyResizeFilter method is:
01395 %
01396 %      ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
01397 %
01398 %  A description of each parameter follows:
01399 %
01400 %    o resize_filter: the resize filter.
01401 %
01402 */
01403 MagickPrivate ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
01404 {
01405   assert(resize_filter != (ResizeFilter *) NULL);
01406   assert(resize_filter->signature == MagickSignature);
01407   resize_filter->signature=(~MagickSignature);
01408   resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
01409   return(resize_filter);
01410 }
01411 
01412 /*
01413 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01414 %                                                                             %
01415 %                                                                             %
01416 %                                                                             %
01417 +   G e t R e s i z e F i l t e r S u p p o r t                               %
01418 %                                                                             %
01419 %                                                                             %
01420 %                                                                             %
01421 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01422 %
01423 %  GetResizeFilterSupport() return the current support window size for this
01424 %  filter.  Note that this may have been enlarged by filter:blur factor.
01425 %
01426 %  The format of the GetResizeFilterSupport method is:
01427 %
01428 %      MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
01429 %
01430 %  A description of each parameter follows:
01431 %
01432 %    o filter: Image filter to use.
01433 %
01434 */
01435 MagickPrivate MagickRealType GetResizeFilterSupport(
01436   const ResizeFilter *resize_filter)
01437 {
01438   assert(resize_filter != (ResizeFilter *) NULL);
01439   assert(resize_filter->signature == MagickSignature);
01440   return(resize_filter->support*resize_filter->blur);
01441 }
01442 
01443 /*
01444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01445 %                                                                             %
01446 %                                                                             %
01447 %                                                                             %
01448 +   G e t R e s i z e F i l t e r W e i g h t                                 %
01449 %                                                                             %
01450 %                                                                             %
01451 %                                                                             %
01452 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01453 %
01454 %  GetResizeFilterWeight evaluates the specified resize filter at the point x
01455 %  which usally lies between zero and the filters current 'support' and
01456 %  returns the weight of the filter function at that point.
01457 %
01458 %  The format of the GetResizeFilterWeight method is:
01459 %
01460 %      MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
01461 %        const MagickRealType x)
01462 %
01463 %  A description of each parameter follows:
01464 %
01465 %    o filter: the filter type.
01466 %
01467 %    o x: the point.
01468 %
01469 */
01470 MagickPrivate MagickRealType GetResizeFilterWeight(
01471   const ResizeFilter *resize_filter,const MagickRealType x)
01472 {
01473   MagickRealType
01474     scale,
01475     weight,
01476     x_blur;
01477 
01478   /*
01479     Windowing function - scale the weighting filter by this amount.
01480   */
01481   assert(resize_filter != (ResizeFilter *) NULL);
01482   assert(resize_filter->signature == MagickSignature);
01483   x_blur=fabs((double) x)/resize_filter->blur;  /* X offset with blur scaling */
01484   if ((resize_filter->window_support < MagickEpsilon) ||
01485       (resize_filter->window == Box))
01486     scale=1.0;  /* Point or Box Filter -- avoid division by zero */
01487   else
01488     {
01489       scale=resize_filter->scale;
01490       scale=resize_filter->window(x_blur*scale,resize_filter);
01491     }
01492   weight=scale*resize_filter->filter(x_blur,resize_filter);
01493   return(weight);
01494 }
01495 
01496 /*
01497 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01498 %                                                                             %
01499 %                                                                             %
01500 %                                                                             %
01501 %   I n t e r p o l a t i v e R e s i z e I m a g e                           %
01502 %                                                                             %
01503 %                                                                             %
01504 %                                                                             %
01505 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01506 %
01507 %  InterpolativeResizeImage() resizes an image using the specified
01508 %  interpolation method.
01509 %
01510 %  The format of the InterpolativeResizeImage method is:
01511 %
01512 %      Image *InterpolativeResizeImage(const Image *image,const size_t columns,
01513 %        const size_t rows,const PixelInterpolateMethod method,
01514 %        ExceptionInfo *exception)
01515 %
01516 %  A description of each parameter follows:
01517 %
01518 %    o image: the image.
01519 %
01520 %    o columns: the number of columns in the resized image.
01521 %
01522 %    o rows: the number of rows in the resized image.
01523 %
01524 %    o method: the pixel interpolation method.
01525 %
01526 %    o exception: return any errors or warnings in this structure.
01527 %
01528 */
01529 MagickExport Image *InterpolativeResizeImage(const Image *image,
01530   const size_t columns,const size_t rows,const PixelInterpolateMethod method,
01531   ExceptionInfo *exception)
01532 {
01533 #define InterpolativeResizeImageTag  "Resize/Image"
01534 
01535   CacheView
01536     *image_view,
01537     *resize_view;
01538 
01539   Image
01540     *resize_image;
01541 
01542   MagickBooleanType
01543     status;
01544 
01545   MagickOffsetType
01546     progress;
01547 
01548   PointInfo
01549     scale;
01550 
01551   ssize_t
01552     y;
01553 
01554   /*
01555     Interpolatively resize image.
01556   */
01557   assert(image != (const Image *) NULL);
01558   assert(image->signature == MagickSignature);
01559   if (image->debug != MagickFalse)
01560     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01561   assert(exception != (ExceptionInfo *) NULL);
01562   assert(exception->signature == MagickSignature);
01563   if ((columns == 0) || (rows == 0))
01564     return((Image *) NULL);
01565   if ((columns == image->columns) && (rows == image->rows))
01566     return(CloneImage(image,0,0,MagickTrue,exception));
01567   resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
01568   if (resize_image == (Image *) NULL)
01569     return((Image *) NULL);
01570   if (SetImageStorageClass(resize_image,DirectClass,exception) == MagickFalse)
01571     {
01572       resize_image=DestroyImage(resize_image);
01573       return((Image *) NULL);
01574     }
01575   status=MagickTrue;
01576   progress=0;
01577   image_view=AcquireCacheView(image);
01578   resize_view=AcquireCacheView(resize_image);
01579   scale.x=(double) image->columns/resize_image->columns;
01580   scale.y=(double) image->rows/resize_image->rows;
01581 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01582   #pragma omp parallel for schedule(static) shared(progress,status)
01583 #endif
01584   for (y=0; y < (ssize_t) resize_image->rows; y++)
01585   {
01586     PointInfo
01587       offset;
01588 
01589     register Quantum
01590       *restrict q;
01591 
01592     register ssize_t
01593       x;
01594 
01595     if (status == MagickFalse)
01596       continue;
01597     q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
01598       exception);
01599     if (q == (Quantum *) NULL)
01600       continue;
01601     offset.y=((MagickRealType) y+0.5)*scale.y-0.5;
01602     for (x=0; x < (ssize_t) resize_image->columns; x++)
01603     {
01604       register ssize_t
01605         i;
01606 
01607       if (GetPixelMask(resize_image,q) != 0)
01608         {
01609           q+=GetPixelChannels(resize_image);
01610           continue;
01611         }
01612       for (i=0; i < (ssize_t) GetPixelChannels(resize_image); i++)
01613       {
01614         PixelChannel
01615           channel;
01616 
01617         PixelTrait
01618           resize_traits,
01619           traits;
01620 
01621         channel=GetPixelChannelMapChannel(image,i);
01622         traits=GetPixelChannelMapTraits(image,channel);
01623         resize_traits=GetPixelChannelMapTraits(resize_image,channel);
01624         if ((traits == UndefinedPixelTrait) ||
01625             (resize_traits == UndefinedPixelTrait))
01626           continue;
01627         offset.x=((MagickRealType) x+0.5)*scale.x-0.5;
01628         status=InterpolatePixelChannels(image,image_view,resize_image,method,
01629           offset.x,offset.y,q,exception);
01630       }
01631       q+=GetPixelChannels(resize_image);
01632     }
01633     if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
01634       continue;
01635     if (image->progress_monitor != (MagickProgressMonitor) NULL)
01636       {
01637         MagickBooleanType
01638           proceed;
01639 
01640 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01641   #pragma omp critical (MagickCore_InterpolativeResizeImage)
01642 #endif
01643         proceed=SetImageProgress(image,InterpolativeResizeImageTag,progress++,
01644           image->rows);
01645         if (proceed == MagickFalse)
01646           status=MagickFalse;
01647       }
01648   }
01649   resize_view=DestroyCacheView(resize_view);
01650   image_view=DestroyCacheView(image_view);
01651   if (status == MagickFalse)
01652     resize_image=DestroyImage(resize_image);
01653   return(resize_image);
01654 }
01655 #if defined(MAGICKCORE_LQR_DELEGATE)
01656 
01657 /*
01658 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01659 %                                                                             %
01660 %                                                                             %
01661 %                                                                             %
01662 %   L i q u i d R e s c a l e I m a g e                                       %
01663 %                                                                             %
01664 %                                                                             %
01665 %                                                                             %
01666 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01667 %
01668 %  LiquidRescaleImage() rescales image with seam carving.
01669 %
01670 %  The format of the LiquidRescaleImage method is:
01671 %
01672 %      Image *LiquidRescaleImage(const Image *image,const size_t columns,
01673 %        const size_t rows,const double delta_x,const double rigidity,
01674 %        ExceptionInfo *exception)
01675 %
01676 %  A description of each parameter follows:
01677 %
01678 %    o image: the image.
01679 %
01680 %    o columns: the number of columns in the rescaled image.
01681 %
01682 %    o rows: the number of rows in the rescaled image.
01683 %
01684 %    o delta_x: maximum seam transversal step (0 means straight seams).
01685 %
01686 %    o rigidity: introduce a bias for non-straight seams (typically 0).
01687 %
01688 %    o exception: return any errors or warnings in this structure.
01689 %
01690 */
01691 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
01692   const size_t rows,const double delta_x,const double rigidity,
01693   ExceptionInfo *exception)
01694 {
01695 #define LiquidRescaleImageTag  "Rescale/Image"
01696 
01697   CacheView
01698     *image_view,
01699     *rescale_view;
01700 
01701   gfloat
01702     *packet,
01703     *pixels;
01704 
01705   Image
01706     *rescale_image;
01707 
01708   int
01709     x_offset,
01710     y_offset;
01711 
01712   LqrCarver
01713     *carver;
01714 
01715   LqrRetVal
01716     lqr_status;
01717 
01718   MagickBooleanType
01719     status;
01720 
01721   register gfloat
01722     *q;
01723 
01724   ssize_t
01725     y;
01726 
01727   /*
01728     Liquid rescale image.
01729   */
01730   assert(image != (const Image *) NULL);
01731   assert(image->signature == MagickSignature);
01732   if (image->debug != MagickFalse)
01733     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01734   assert(exception != (ExceptionInfo *) NULL);
01735   assert(exception->signature == MagickSignature);
01736   if ((columns == 0) || (rows == 0))
01737     return((Image *) NULL);
01738   if ((columns == image->columns) && (rows == image->rows))
01739     return(CloneImage(image,0,0,MagickTrue,exception));
01740   if ((columns <= 2) || (rows <= 2))
01741     return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
01742   if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
01743     {
01744       Image
01745         *resize_image;
01746 
01747       size_t
01748         height,
01749         width;
01750 
01751       /*
01752         Honor liquid resize size limitations.
01753       */
01754       for (width=image->columns; columns >= (2*width-1); width*=2);
01755       for (height=image->rows; rows >= (2*height-1); height*=2);
01756       resize_image=ResizeImage(image,width,height,image->filter,image->blur,
01757         exception);
01758       if (resize_image == (Image *) NULL)
01759         return((Image *) NULL);
01760       rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
01761         rigidity,exception);
01762       resize_image=DestroyImage(resize_image);
01763       return(rescale_image);
01764     }
01765   pixels=(gfloat *) AcquireQuantumMemory(image->columns,image->rows*
01766     GetPixelChannels(image)*sizeof(*pixels));
01767   if (pixels == (gfloat *) NULL)
01768     return((Image *) NULL);
01769   status=MagickTrue;
01770   q=pixels;
01771   image_view=AcquireCacheView(image);
01772   for (y=0; y < (ssize_t) image->rows; y++)
01773   {
01774     register const Quantum
01775       *restrict p;
01776 
01777     register ssize_t
01778       x;
01779 
01780     if (status == MagickFalse)
01781       continue;
01782     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
01783     if (p == (const Quantum *) NULL)
01784       {
01785         status=MagickFalse;
01786         continue;
01787       }
01788     for (x=0; x < (ssize_t) image->columns; x++)
01789     {
01790       register ssize_t
01791         i;
01792 
01793       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
01794         *q++=QuantumScale*p[i];
01795       p+=GetPixelChannels(image);
01796     }
01797   }
01798   image_view=DestroyCacheView(image_view);
01799   carver=lqr_carver_new_ext(pixels,image->columns,image->rows,
01800     GetPixelChannels(image),LQR_COLDEPTH_32F);
01801   if (carver == (LqrCarver *) NULL)
01802     {
01803       pixels=(gfloat *) RelinquishMagickMemory(pixels);
01804       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01805     }
01806   lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
01807   lqr_status=lqr_carver_resize(carver,columns,rows);
01808   (void) lqr_status;
01809   rescale_image=CloneImage(image,lqr_carver_get_width(carver),
01810     lqr_carver_get_height(carver),MagickTrue,exception);
01811   if (rescale_image == (Image *) NULL)
01812     {
01813       pixels=(gfloat *) RelinquishMagickMemory(pixels);
01814       return((Image *) NULL);
01815     }
01816   if (SetImageStorageClass(rescale_image,DirectClass,exception) == MagickFalse)
01817     {
01818       pixels=(gfloat *) RelinquishMagickMemory(pixels);
01819       rescale_image=DestroyImage(rescale_image);
01820       return((Image *) NULL);
01821     }
01822   rescale_view=AcquireCacheView(rescale_image);
01823   (void) lqr_carver_scan_reset(carver);
01824   while (lqr_carver_scan_ext(carver,&x_offset,&y_offset,(void **) &packet) != 0)
01825   {
01826     register Quantum
01827       *restrict q;
01828 
01829     register ssize_t
01830       i;
01831 
01832     q=QueueCacheViewAuthenticPixels(rescale_view,x_offset,y_offset,1,1,
01833       exception);
01834     if (q == (Quantum *) NULL)
01835       break;
01836     for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
01837     {
01838       PixelChannel
01839         channel;
01840 
01841       PixelTrait
01842         rescale_traits,
01843         traits;
01844 
01845       channel=GetPixelChannelMapChannel(image,i);
01846       traits=GetPixelChannelMapTraits(image,channel);
01847       rescale_traits=GetPixelChannelMapTraits(rescale_image,channel);
01848       if ((traits == UndefinedPixelTrait) ||
01849           (rescale_traits == UndefinedPixelTrait))
01850         continue;
01851       SetPixelChannel(rescale_image,channel,ClampToQuantum(QuantumRange*
01852         packet[i]),q);
01853     }
01854     if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
01855       break;
01856   }
01857   rescale_view=DestroyCacheView(rescale_view);
01858   lqr_carver_destroy(carver);
01859   return(rescale_image);
01860 }
01861 #else
01862 MagickExport Image *LiquidRescaleImage(const Image *image,
01863   const size_t magick_unused(columns),const size_t magick_unused(rows),
01864   const double magick_unused(delta_x),const double magick_unused(rigidity),
01865   ExceptionInfo *exception)
01866 {
01867   assert(image != (const Image *) NULL);
01868   assert(image->signature == MagickSignature);
01869   if (image->debug != MagickFalse)
01870     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01871   assert(exception != (ExceptionInfo *) NULL);
01872   assert(exception->signature == MagickSignature);
01873   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
01874     "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
01875   return((Image *) NULL);
01876 }
01877 #endif
01878 
01879 /*
01880 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01881 %                                                                             %
01882 %                                                                             %
01883 %                                                                             %
01884 %   M a g n i f y I m a g e                                                   %
01885 %                                                                             %
01886 %                                                                             %
01887 %                                                                             %
01888 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01889 %
01890 %  MagnifyImage() is a convenience method that scales an image proportionally
01891 %  to twice its size.
01892 %
01893 %  The format of the MagnifyImage method is:
01894 %
01895 %      Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
01896 %
01897 %  A description of each parameter follows:
01898 %
01899 %    o image: the image.
01900 %
01901 %    o exception: return any errors or warnings in this structure.
01902 %
01903 */
01904 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
01905 {
01906   Image
01907     *magnify_image;
01908 
01909   assert(image != (Image *) NULL);
01910   assert(image->signature == MagickSignature);
01911   if (image->debug != MagickFalse)
01912     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01913   assert(exception != (ExceptionInfo *) NULL);
01914   assert(exception->signature == MagickSignature);
01915   magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
01916     1.0,exception);
01917   return(magnify_image);
01918 }
01919 
01920 /*
01921 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01922 %                                                                             %
01923 %                                                                             %
01924 %                                                                             %
01925 %   M i n i f y I m a g e                                                     %
01926 %                                                                             %
01927 %                                                                             %
01928 %                                                                             %
01929 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01930 %
01931 %  MinifyImage() is a convenience method that scales an image proportionally to
01932 %  half its size.
01933 %
01934 %  The format of the MinifyImage method is:
01935 %
01936 %      Image *MinifyImage(const Image *image,ExceptionInfo *exception)
01937 %
01938 %  A description of each parameter follows:
01939 %
01940 %    o image: the image.
01941 %
01942 %    o exception: return any errors or warnings in this structure.
01943 %
01944 */
01945 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
01946 {
01947   Image
01948     *minify_image;
01949 
01950   assert(image != (Image *) NULL);
01951   assert(image->signature == MagickSignature);
01952   if (image->debug != MagickFalse)
01953     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01954   assert(exception != (ExceptionInfo *) NULL);
01955   assert(exception->signature == MagickSignature);
01956   minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,1.0,
01957     exception);
01958   return(minify_image);
01959 }
01960 
01961 /*
01962 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01963 %                                                                             %
01964 %                                                                             %
01965 %                                                                             %
01966 %   R e s a m p l e I m a g e                                                 %
01967 %                                                                             %
01968 %                                                                             %
01969 %                                                                             %
01970 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01971 %
01972 %  ResampleImage() resize image in terms of its pixel size, so that when
01973 %  displayed at the given resolution it will be the same size in terms of
01974 %  real world units as the original image at the original resolution.
01975 %
01976 %  The format of the ResampleImage method is:
01977 %
01978 %      Image *ResampleImage(Image *image,const double x_resolution,
01979 %        const double y_resolution,const FilterTypes filter,const double blur,
01980 %        ExceptionInfo *exception)
01981 %
01982 %  A description of each parameter follows:
01983 %
01984 %    o image: the image to be resized to fit the given resolution.
01985 %
01986 %    o x_resolution: the new image x resolution.
01987 %
01988 %    o y_resolution: the new image y resolution.
01989 %
01990 %    o filter: Image filter to use.
01991 %
01992 %    o blur: the blur factor where > 1 is blurry, < 1 is sharp.
01993 %
01994 */
01995 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
01996   const double y_resolution,const FilterTypes filter,const double blur,
01997   ExceptionInfo *exception)
01998 {
01999 #define ResampleImageTag  "Resample/Image"
02000 
02001   Image
02002     *resample_image;
02003 
02004   size_t
02005     height,
02006     width;
02007 
02008   /*
02009     Initialize sampled image attributes.
02010   */
02011   assert(image != (const Image *) NULL);
02012   assert(image->signature == MagickSignature);
02013   if (image->debug != MagickFalse)
02014     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02015   assert(exception != (ExceptionInfo *) NULL);
02016   assert(exception->signature == MagickSignature);
02017   width=(size_t) (x_resolution*image->columns/(image->resolution.x == 0.0 ?
02018     72.0 : image->resolution.x)+0.5);
02019   height=(size_t) (y_resolution*image->rows/(image->resolution.y == 0.0 ?
02020     72.0 : image->resolution.y)+0.5);
02021   resample_image=ResizeImage(image,width,height,filter,blur,exception);
02022   if (resample_image != (Image *) NULL)
02023     {
02024       resample_image->resolution.x=x_resolution;
02025       resample_image->resolution.y=y_resolution;
02026     }
02027   return(resample_image);
02028 }
02029 
02030 /*
02031 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02032 %                                                                             %
02033 %                                                                             %
02034 %                                                                             %
02035 %   R e s i z e I m a g e                                                     %
02036 %                                                                             %
02037 %                                                                             %
02038 %                                                                             %
02039 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02040 %
02041 %  ResizeImage() scales an image to the desired dimensions, using the given
02042 %  filter (see AcquireFilterInfo()).
02043 %
02044 %  If an undefined filter is given the filter defaults to Mitchell for a
02045 %  colormapped image, a image with a matte channel, or if the image is
02046 %  enlarged.  Otherwise the filter defaults to a Lanczos.
02047 %
02048 %  ResizeImage() was inspired by Paul Heckbert's "zoom" program.
02049 %
02050 %  The format of the ResizeImage method is:
02051 %
02052 %      Image *ResizeImage(Image *image,const size_t columns,
02053 %        const size_t rows,const FilterTypes filter,const double blur,
02054 %        ExceptionInfo *exception)
02055 %
02056 %  A description of each parameter follows:
02057 %
02058 %    o image: the image.
02059 %
02060 %    o columns: the number of columns in the scaled image.
02061 %
02062 %    o rows: the number of rows in the scaled image.
02063 %
02064 %    o filter: Image filter to use.
02065 %
02066 %    o blur: the blur factor where > 1 is blurry, < 1 is sharp.  Typically set
02067 %      this to 1.0.
02068 %
02069 %    o exception: return any errors or warnings in this structure.
02070 %
02071 */
02072 
02073 typedef struct _ContributionInfo
02074 {
02075   MagickRealType
02076     weight;
02077 
02078   ssize_t
02079     pixel;
02080 } ContributionInfo;
02081 
02082 static ContributionInfo **DestroyContributionThreadSet(
02083   ContributionInfo **contribution)
02084 {
02085   register ssize_t
02086     i;
02087 
02088   assert(contribution != (ContributionInfo **) NULL);
02089   for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
02090     if (contribution[i] != (ContributionInfo *) NULL)
02091       contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
02092         contribution[i]);
02093   contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
02094   return(contribution);
02095 }
02096 
02097 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
02098 {
02099   register ssize_t
02100     i;
02101 
02102   ContributionInfo
02103     **contribution;
02104 
02105   size_t
02106     number_threads;
02107 
02108   number_threads=GetOpenMPMaximumThreads();
02109   contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
02110     sizeof(*contribution));
02111   if (contribution == (ContributionInfo **) NULL)
02112     return((ContributionInfo **) NULL);
02113   (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
02114   for (i=0; i < (ssize_t) number_threads; i++)
02115   {
02116     contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
02117       sizeof(**contribution));
02118     if (contribution[i] == (ContributionInfo *) NULL)
02119       return(DestroyContributionThreadSet(contribution));
02120   }
02121   return(contribution);
02122 }
02123 
02124 static inline double MagickMax(const double x,const double y)
02125 {
02126   if (x > y)
02127     return(x);
02128   return(y);
02129 }
02130 
02131 static inline double MagickMin(const double x,const double y)
02132 {
02133   if (x < y)
02134     return(x);
02135   return(y);
02136 }
02137 
02138 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
02139   const Image *image,Image *resize_image,const MagickRealType x_factor,
02140   const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
02141 {
02142 #define ResizeImageTag  "Resize/Image"
02143 
02144   CacheView
02145     *image_view,
02146     *resize_view;
02147 
02148   ClassType
02149     storage_class;
02150 
02151   ContributionInfo
02152     **restrict contributions;
02153 
02154   MagickBooleanType
02155     status;
02156 
02157   MagickRealType
02158     scale,
02159     support;
02160 
02161   ssize_t
02162     x;
02163 
02164   /*
02165     Apply filter to resize horizontally from image to resize image.
02166   */
02167   scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
02168   support=scale*GetResizeFilterSupport(resize_filter);
02169   storage_class=support > 0.5 ? DirectClass : image->storage_class;
02170   if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
02171     return(MagickFalse);
02172   if (support < 0.5)
02173     {
02174       /*
02175         Support too small even for nearest neighbour: Reduce to point sampling.
02176       */
02177       support=(MagickRealType) 0.5;
02178       scale=1.0;
02179     }
02180   contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
02181   if (contributions == (ContributionInfo **) NULL)
02182     {
02183       (void) ThrowMagickException(exception,GetMagickModule(),
02184         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
02185       return(MagickFalse);
02186     }
02187   status=MagickTrue;
02188   scale=1.0/scale;
02189   image_view=AcquireCacheView(image);
02190   resize_view=AcquireCacheView(resize_image);
02191 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02192   #pragma omp parallel for schedule(static,4) shared(status)
02193 #endif
02194   for (x=0; x < (ssize_t) resize_image->columns; x++)
02195   {
02196     MagickRealType
02197       bisect,
02198       density;
02199 
02200     register const Quantum
02201       *restrict p;
02202 
02203     register ContributionInfo
02204       *restrict contribution;
02205 
02206     register Quantum
02207       *restrict q;
02208 
02209     register ssize_t
02210       y;
02211 
02212     ssize_t
02213       n,
02214       start,
02215       stop;
02216 
02217     if (status == MagickFalse)
02218       continue;
02219     bisect=(MagickRealType) (x+0.5)/x_factor;
02220     start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
02221     stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->columns);
02222     density=0.0;
02223     contribution=contributions[GetOpenMPThreadId()];
02224     for (n=0; n < (stop-start); n++)
02225     {
02226       contribution[n].pixel=start+n;
02227       contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
02228         ((MagickRealType) (start+n)-bisect+0.5));
02229       density+=contribution[n].weight;
02230     }
02231     if ((density != 0.0) && (density != 1.0))
02232       {
02233         register ssize_t
02234           i;
02235 
02236         /*
02237           Normalize.
02238         */
02239         density=1.0/density;
02240         for (i=0; i < n; i++)
02241           contribution[i].weight*=density;
02242       }
02243     p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
02244       (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
02245     q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
02246       exception);
02247     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
02248       {
02249         status=MagickFalse;
02250         continue;
02251       }
02252     for (y=0; y < (ssize_t) resize_image->rows; y++)
02253     {
02254       register ssize_t
02255         i;
02256 
02257       for (i=0; i < (ssize_t) GetPixelChannels(resize_image); i++)
02258       {
02259         MagickRealType
02260           alpha,
02261           gamma,
02262           pixel;
02263 
02264         PixelChannel
02265           channel;
02266 
02267         PixelTrait
02268           resize_traits,
02269           traits;
02270 
02271         register ssize_t
02272           j;
02273 
02274         ssize_t
02275           k;
02276 
02277         channel=GetPixelChannelMapChannel(image,i);
02278         traits=GetPixelChannelMapTraits(image,channel);
02279         resize_traits=GetPixelChannelMapTraits(resize_image,channel);
02280         if ((traits == UndefinedPixelTrait) ||
02281             (resize_traits == UndefinedPixelTrait))
02282           continue;
02283         if (((resize_traits & CopyPixelTrait) != 0) ||
02284             (GetPixelMask(resize_image,q) != 0))
02285           {
02286             j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
02287               stop-1.0)+0.5);
02288             k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
02289               (contribution[j-start].pixel-contribution[0].pixel);
02290             SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
02291               q);
02292             continue;
02293           }
02294         pixel=0.0;
02295         if ((resize_traits & BlendPixelTrait) == 0)
02296           {
02297             /*
02298               No alpha blending.
02299             */
02300             for (j=0; j < n; j++)
02301             {
02302               k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
02303                 (contribution[j].pixel-contribution[0].pixel);
02304               alpha=contribution[j].weight;
02305               pixel+=alpha*p[k*GetPixelChannels(image)+i];
02306             }
02307             SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
02308             continue;
02309           }
02310         /*
02311           Alpha blending.
02312         */
02313         gamma=0.0;
02314         for (j=0; j < n; j++)
02315         {
02316           k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
02317             (contribution[j].pixel-contribution[0].pixel);
02318           alpha=contribution[j].weight*QuantumScale*
02319             GetPixelAlpha(image,p+k*GetPixelChannels(image));
02320           pixel+=alpha*p[k*GetPixelChannels(image)+i];
02321           gamma+=alpha;
02322         }
02323         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
02324         SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
02325       }
02326       q+=GetPixelChannels(resize_image);
02327     }
02328     if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
02329       status=MagickFalse;
02330     if (image->progress_monitor != (MagickProgressMonitor) NULL)
02331       {
02332         MagickBooleanType
02333           proceed;
02334 
02335 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02336   #pragma omp critical (MagickCore_HorizontalFilter)
02337 #endif
02338         proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
02339         if (proceed == MagickFalse)
02340           status=MagickFalse;
02341       }
02342   }
02343   resize_view=DestroyCacheView(resize_view);
02344   image_view=DestroyCacheView(image_view);
02345   contributions=DestroyContributionThreadSet(contributions);
02346   return(status);
02347 }
02348 
02349 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
02350   const Image *image,Image *resize_image,const MagickRealType y_factor,
02351   const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
02352 {
02353   CacheView
02354     *image_view,
02355     *resize_view;
02356 
02357   ClassType
02358     storage_class;
02359 
02360   ContributionInfo
02361     **restrict contributions;
02362 
02363   MagickBooleanType
02364     status;
02365 
02366   PixelInfo
02367     zero;
02368 
02369   MagickRealType
02370     scale,
02371     support;
02372 
02373   ssize_t
02374     y;
02375 
02376   /*
02377     Apply filter to resize vertically from image to resize image.
02378   */
02379   scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
02380   support=scale*GetResizeFilterSupport(resize_filter);
02381   storage_class=support > 0.5 ? DirectClass : image->storage_class;
02382   if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
02383     return(MagickFalse);
02384   if (support < 0.5)
02385     {
02386       /*
02387         Support too small even for nearest neighbour: Reduce to point sampling.
02388       */
02389       support=(MagickRealType) 0.5;
02390       scale=1.0;
02391     }
02392   contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
02393   if (contributions == (ContributionInfo **) NULL)
02394     {
02395       (void) ThrowMagickException(exception,GetMagickModule(),
02396         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
02397       return(MagickFalse);
02398     }
02399   status=MagickTrue;
02400   scale=1.0/scale;
02401   (void) ResetMagickMemory(&zero,0,sizeof(zero));
02402   image_view=AcquireCacheView(image);
02403   resize_view=AcquireCacheView(resize_image);
02404 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02405   #pragma omp parallel for schedule(static,4) shared(status)
02406 #endif
02407   for (y=0; y < (ssize_t) resize_image->rows; y++)
02408   {
02409     MagickRealType
02410       bisect,
02411       density;
02412 
02413     register const Quantum
02414       *restrict p;
02415 
02416     register ContributionInfo
02417       *restrict contribution;
02418 
02419     register Quantum
02420       *restrict q;
02421 
02422     register ssize_t
02423       x;
02424 
02425     ssize_t
02426       n,
02427       start,
02428       stop;
02429 
02430     if (status == MagickFalse)
02431       continue;
02432     bisect=(MagickRealType) (y+0.5)/y_factor;
02433     start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
02434     stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->rows);
02435     density=0.0;
02436     contribution=contributions[GetOpenMPThreadId()];
02437     for (n=0; n < (stop-start); n++)
02438     {
02439       contribution[n].pixel=start+n;
02440       contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
02441         ((MagickRealType) (start+n)-bisect+0.5));
02442       density+=contribution[n].weight;
02443     }
02444     if ((density != 0.0) && (density != 1.0))
02445       {
02446         register ssize_t
02447           i;
02448 
02449         /*
02450           Normalize.
02451         */
02452         density=1.0/density;
02453         for (i=0; i < n; i++)
02454           contribution[i].weight*=density;
02455       }
02456     p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
02457       image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
02458       exception);
02459     q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
02460       exception);
02461     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
02462       {
02463         status=MagickFalse;
02464         continue;
02465       }
02466     for (x=0; x < (ssize_t) resize_image->columns; x++)
02467     {
02468       register ssize_t
02469         i;
02470 
02471       for (i=0; i < (ssize_t) GetPixelChannels(resize_image); i++)
02472       {
02473         MagickRealType
02474           alpha,
02475           gamma,
02476           pixel;
02477 
02478         PixelChannel
02479           channel;
02480 
02481         PixelTrait
02482           resize_traits,
02483           traits;
02484 
02485         register ssize_t
02486           j;
02487 
02488         ssize_t
02489           k;
02490 
02491         channel=GetPixelChannelMapChannel(image,i);
02492         traits=GetPixelChannelMapTraits(image,channel);
02493         resize_traits=GetPixelChannelMapTraits(resize_image,channel);
02494         if ((traits == UndefinedPixelTrait) ||
02495             (resize_traits == UndefinedPixelTrait))
02496           continue;
02497         if (((resize_traits & CopyPixelTrait) != 0) ||
02498             (GetPixelMask(resize_image,q) != 0))
02499           {
02500             j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
02501               stop-1.0)+0.5);
02502             k=(ssize_t) ((contribution[j-start].pixel-contribution[0].pixel)*
02503               image->columns+x);
02504             SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
02505               q);
02506             continue;
02507           }
02508         pixel=0.0;
02509         if ((resize_traits & BlendPixelTrait) == 0)
02510           {
02511             /*
02512               No alpha blending.
02513             */
02514             for (j=0; j < n; j++)
02515             {
02516               k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
02517                 image->columns+x);
02518               alpha=contribution[j].weight;
02519               pixel+=alpha*p[k*GetPixelChannels(image)+i];
02520             }
02521             SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
02522             continue;
02523           }
02524         gamma=0.0;
02525         for (j=0; j < n; j++)
02526         {
02527           k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
02528             image->columns+x);
02529           alpha=contribution[j].weight*QuantumScale*
02530             GetPixelAlpha(image,p+k*GetPixelChannels(image));
02531           pixel+=alpha*p[k*GetPixelChannels(image)+i];
02532           gamma+=alpha;
02533         }
02534         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
02535         SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
02536       }
02537       q+=GetPixelChannels(resize_image);
02538     }
02539     if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
02540       status=MagickFalse;
02541     if (image->progress_monitor != (MagickProgressMonitor) NULL)
02542       {
02543         MagickBooleanType
02544           proceed;
02545 
02546 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02547   #pragma omp critical (MagickCore_VerticalFilter)
02548 #endif
02549         proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
02550         if (proceed == MagickFalse)
02551           status=MagickFalse;
02552       }
02553   }
02554   resize_view=DestroyCacheView(resize_view);
02555   image_view=DestroyCacheView(image_view);
02556   contributions=DestroyContributionThreadSet(contributions);
02557   return(status);
02558 }
02559 
02560 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
02561   const size_t rows,const FilterTypes filter,const double blur,
02562   ExceptionInfo *exception)
02563 {
02564 #define WorkLoadFactor  0.265
02565 
02566   FilterTypes
02567     filter_type;
02568 
02569   Image
02570     *filter_image,
02571     *resize_image;
02572 
02573   MagickOffsetType
02574     offset;
02575 
02576   MagickRealType
02577     x_factor,
02578     y_factor;
02579 
02580   MagickSizeType
02581     span;
02582 
02583   MagickStatusType
02584     status;
02585 
02586   ResizeFilter
02587     *resize_filter;
02588 
02589   /*
02590     Acquire resize image.
02591   */
02592   assert(image != (Image *) NULL);
02593   assert(image->signature == MagickSignature);
02594   if (image->debug != MagickFalse)
02595     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02596   assert(exception != (ExceptionInfo *) NULL);
02597   assert(exception->signature == MagickSignature);
02598   if ((columns == 0) || (rows == 0))
02599     ThrowImageException(ImageError,"NegativeOrZeroImageSize");
02600   if ((columns == image->columns) && (rows == image->rows) &&
02601       (filter == UndefinedFilter) && (blur == 1.0))
02602     return(CloneImage(image,0,0,MagickTrue,exception));
02603   resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
02604   if (resize_image == (Image *) NULL)
02605     return(resize_image);
02606   /*
02607     Acquire resize filter.
02608   */
02609   x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
02610   y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
02611   if ((x_factor*y_factor) > WorkLoadFactor)
02612     filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
02613   else
02614     filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
02615   if (filter_image == (Image *) NULL)
02616     return(DestroyImage(resize_image));
02617   filter_type=LanczosFilter;
02618   if (filter != UndefinedFilter)
02619     filter_type=filter;
02620   else
02621     if ((x_factor == 1.0) && (y_factor == 1.0))
02622       filter_type=PointFilter;
02623     else
02624       if ((image->storage_class == PseudoClass) ||
02625           (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
02626         filter_type=MitchellFilter;
02627   resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
02628     exception);
02629   /*
02630     Resize image.
02631   */
02632   offset=0;
02633   if ((x_factor*y_factor) > WorkLoadFactor)
02634     {
02635       span=(MagickSizeType) (filter_image->columns+rows);
02636       status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
02637         &offset,exception);
02638       status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
02639         span,&offset,exception);
02640     }
02641   else
02642     {
02643       span=(MagickSizeType) (filter_image->rows+columns);
02644       status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
02645         &offset,exception);
02646       status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
02647         span,&offset,exception);
02648     }
02649   /*
02650     Free resources.
02651   */
02652   filter_image=DestroyImage(filter_image);
02653   resize_filter=DestroyResizeFilter(resize_filter);
02654   if (status == MagickFalse)
02655     {
02656       resize_image=DestroyImage(resize_image);
02657       return((Image *) NULL);
02658     }
02659   resize_image->type=image->type;
02660   return(resize_image);
02661 }
02662 
02663 /*
02664 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02665 %                                                                             %
02666 %                                                                             %
02667 %                                                                             %
02668 %   S a m p l e I m a g e                                                     %
02669 %                                                                             %
02670 %                                                                             %
02671 %                                                                             %
02672 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02673 %
02674 %  SampleImage() scales an image to the desired dimensions with pixel
02675 %  sampling.  Unlike other scaling methods, this method does not introduce
02676 %  any additional color into the scaled image.
02677 %
02678 %  The format of the SampleImage method is:
02679 %
02680 %      Image *SampleImage(const Image *image,const size_t columns,
02681 %        const size_t rows,ExceptionInfo *exception)
02682 %
02683 %  A description of each parameter follows:
02684 %
02685 %    o image: the image.
02686 %
02687 %    o columns: the number of columns in the sampled image.
02688 %
02689 %    o rows: the number of rows in the sampled image.
02690 %
02691 %    o exception: return any errors or warnings in this structure.
02692 %
02693 */
02694 MagickExport Image *SampleImage(const Image *image,const size_t columns,
02695   const size_t rows,ExceptionInfo *exception)
02696 {
02697 #define SampleImageTag  "Sample/Image"
02698 
02699   CacheView
02700     *image_view,
02701     *sample_view;
02702 
02703   Image
02704     *sample_image;
02705 
02706   MagickBooleanType
02707     status;
02708 
02709   MagickOffsetType
02710     progress;
02711 
02712   register ssize_t
02713     x;
02714 
02715   ssize_t
02716     *x_offset,
02717     y;
02718 
02719   /*
02720     Initialize sampled image attributes.
02721   */
02722   assert(image != (const Image *) NULL);
02723   assert(image->signature == MagickSignature);
02724   if (image->debug != MagickFalse)
02725     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02726   assert(exception != (ExceptionInfo *) NULL);
02727   assert(exception->signature == MagickSignature);
02728   if ((columns == 0) || (rows == 0))
02729     ThrowImageException(ImageError,"NegativeOrZeroImageSize");
02730   if ((columns == image->columns) && (rows == image->rows))
02731     return(CloneImage(image,0,0,MagickTrue,exception));
02732   sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
02733   if (sample_image == (Image *) NULL)
02734     return((Image *) NULL);
02735   /*
02736     Allocate scan line buffer and column offset buffers.
02737   */
02738   x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
02739     sizeof(*x_offset));
02740   if (x_offset == (ssize_t *) NULL)
02741     {
02742       sample_image=DestroyImage(sample_image);
02743       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02744     }
02745   for (x=0; x < (ssize_t) sample_image->columns; x++)
02746     x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
02747       sample_image->columns);
02748   /*
02749     Sample each row.
02750   */
02751   status=MagickTrue;
02752   progress=0;
02753   image_view=AcquireCacheView(image);
02754   sample_view=AcquireCacheView(sample_image);
02755 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02756   #pragma omp parallel for schedule(static) shared(progress,status)
02757 #endif
02758   for (y=0; y < (ssize_t) sample_image->rows; y++)
02759   {
02760     register const Quantum
02761       *restrict p;
02762 
02763     register Quantum
02764       *restrict q;
02765 
02766     register ssize_t
02767       x;
02768 
02769     ssize_t
02770       y_offset;
02771 
02772     if (status == MagickFalse)
02773       continue;
02774     y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
02775       sample_image->rows);
02776     p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
02777       exception);
02778     q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
02779       exception);
02780     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
02781       {
02782         status=MagickFalse;
02783         continue;
02784       }
02785     /*
02786       Sample each column.
02787     */
02788     for (x=0; x < (ssize_t) sample_image->columns; x++)
02789     {
02790       register ssize_t
02791         i;
02792 
02793       if (GetPixelMask(sample_image,q) != 0)
02794         {
02795           q+=GetPixelChannels(sample_image);
02796           continue;
02797         }
02798       for (i=0; i < (ssize_t) GetPixelChannels(sample_image); i++)
02799       {
02800         PixelChannel
02801           channel;
02802 
02803         PixelTrait
02804           sample_traits,
02805           traits;
02806 
02807         channel=GetPixelChannelMapChannel(image,i);
02808         traits=GetPixelChannelMapTraits(image,channel);
02809         sample_traits=GetPixelChannelMapTraits(sample_image,channel);
02810         if ((traits == UndefinedPixelTrait) ||
02811             (sample_traits == UndefinedPixelTrait))
02812           continue;
02813         SetPixelChannel(sample_image,channel,p[x_offset[x]*GetPixelChannels(
02814           image)+i],q);
02815       }
02816       q+=GetPixelChannels(sample_image);
02817     }
02818     if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
02819       status=MagickFalse;
02820     if (image->progress_monitor != (MagickProgressMonitor) NULL)
02821       {
02822         MagickBooleanType
02823           proceed;
02824 
02825 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02826         #pragma omp critical (MagickCore_SampleImage)
02827 #endif
02828         proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
02829         if (proceed == MagickFalse)
02830           status=MagickFalse;
02831       }
02832   }
02833   image_view=DestroyCacheView(image_view);
02834   sample_view=DestroyCacheView(sample_view);
02835   x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
02836   sample_image->type=image->type;
02837   return(sample_image);
02838 }
02839 
02840 /*
02841 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02842 %                                                                             %
02843 %                                                                             %
02844 %                                                                             %
02845 %   S c a l e I m a g e                                                       %
02846 %                                                                             %
02847 %                                                                             %
02848 %                                                                             %
02849 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02850 %
02851 %  ScaleImage() changes the size of an image to the given dimensions.
02852 %
02853 %  The format of the ScaleImage method is:
02854 %
02855 %      Image *ScaleImage(const Image *image,const size_t columns,
02856 %        const size_t rows,ExceptionInfo *exception)
02857 %
02858 %  A description of each parameter follows:
02859 %
02860 %    o image: the image.
02861 %
02862 %    o columns: the number of columns in the scaled image.
02863 %
02864 %    o rows: the number of rows in the scaled image.
02865 %
02866 %    o exception: return any errors or warnings in this structure.
02867 %
02868 */
02869 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
02870   const size_t rows,ExceptionInfo *exception)
02871 {
02872 #define ScaleImageTag  "Scale/Image"
02873 
02874   CacheView
02875     *image_view,
02876     *scale_view;
02877 
02878   Image
02879     *scale_image;
02880 
02881   MagickBooleanType
02882     next_column,
02883     next_row,
02884     proceed;
02885 
02886   MagickRealType
02887     alpha,
02888     gamma,
02889     pixel[CompositePixelChannel],
02890     *scale_scanline,
02891     *scanline,
02892     *x_vector,
02893     *y_vector;
02894 
02895   PixelChannel
02896     channel;
02897 
02898   PixelTrait
02899     scale_traits,
02900     traits;
02901 
02902   PointInfo
02903     scale,
02904     span;
02905 
02906   register ssize_t
02907     i;
02908 
02909   ssize_t
02910     n,
02911     number_rows,
02912     y;
02913 
02914   /*
02915     Initialize scaled image attributes.
02916   */
02917   assert(image != (const Image *) NULL);
02918   assert(image->signature == MagickSignature);
02919   if (image->debug != MagickFalse)
02920     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02921   assert(exception != (ExceptionInfo *) NULL);
02922   assert(exception->signature == MagickSignature);
02923   if ((columns == 0) || (rows == 0))
02924     return((Image *) NULL);
02925   if ((columns == image->columns) && (rows == image->rows))
02926     return(CloneImage(image,0,0,MagickTrue,exception));
02927   scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
02928   if (scale_image == (Image *) NULL)
02929     return((Image *) NULL);
02930   if (SetImageStorageClass(scale_image,DirectClass,exception) == MagickFalse)
02931     {
02932       scale_image=DestroyImage(scale_image);
02933       return((Image *) NULL);
02934     }
02935   /*
02936     Allocate memory.
02937   */
02938   x_vector=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
02939     GetPixelChannels(image)*sizeof(*x_vector));
02940   scanline=x_vector;
02941   if (image->rows != scale_image->rows)
02942     scanline=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
02943       GetPixelChannels(image)*sizeof(*scanline));
02944   scale_scanline=(MagickRealType *) AcquireQuantumMemory((size_t)
02945     scale_image->columns,MaxPixelChannels*sizeof(*scale_scanline));
02946   y_vector=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
02947     GetPixelChannels(image)*sizeof(*y_vector));
02948   if ((scanline == (MagickRealType *) NULL) ||
02949       (scale_scanline == (MagickRealType *) NULL) ||
02950       (x_vector == (MagickRealType *) NULL) ||
02951       (y_vector == (MagickRealType *) NULL))
02952     {
02953       scale_image=DestroyImage(scale_image);
02954       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02955     }
02956   /*
02957     Scale image.
02958   */
02959   number_rows=0;
02960   next_row=MagickTrue;
02961   span.y=1.0;
02962   scale.y=(double) scale_image->rows/(double) image->rows;
02963   for (i=0; i < (ssize_t) (GetPixelChannels(image)*image->columns); i++)
02964     y_vector[i]=0.0;
02965   n=0;
02966   image_view=AcquireCacheView(image);
02967   scale_view=AcquireCacheView(scale_image);
02968   for (y=0; y < (ssize_t) scale_image->rows; y++)
02969   {
02970     register const Quantum
02971       *restrict p;
02972 
02973     register Quantum
02974       *restrict q;
02975 
02976     register ssize_t
02977       x;
02978 
02979     q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
02980       exception);
02981     if (q == (Quantum *) NULL)
02982       break;
02983     alpha=1.0;
02984     if (scale_image->rows == image->rows)
02985       {
02986         /*
02987           Read a new scanline.
02988         */
02989         p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
02990           exception);
02991         if (p == (const Quantum *) NULL)
02992           break;
02993         for (x=0; x < (ssize_t) image->columns; x++)
02994         {
02995           if (GetPixelMask(image,p) != 0)
02996             {
02997               p+=GetPixelChannels(image);
02998               continue;
02999             }
03000           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
03001           {
03002             PixelChannel
03003               channel;
03004 
03005             PixelTrait
03006               traits;
03007 
03008             channel=GetPixelChannelMapChannel(image,i);
03009             traits=GetPixelChannelMapTraits(image,channel);
03010             if ((traits & BlendPixelTrait) == 0)
03011               {
03012                 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType) p[i];
03013                 continue;
03014               }
03015             alpha=QuantumScale*GetPixelAlpha(image,p);
03016             x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
03017           }
03018           p+=GetPixelChannels(image);
03019         }
03020       }
03021     else
03022       {
03023         /*
03024           Scale Y direction.
03025         */
03026         while (scale.y < span.y)
03027         {
03028           if ((next_row != MagickFalse) &&
03029               (number_rows < (ssize_t) image->rows))
03030             {
03031               /*
03032                 Read a new scanline.
03033               */
03034               p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
03035                 exception);
03036               if (p == (const Quantum *) NULL)
03037                 break;
03038               for (x=0; x < (ssize_t) image->columns; x++)
03039               {
03040                 if (GetPixelMask(image,p) != 0)
03041                   {
03042                     p+=GetPixelChannels(image);
03043                     continue;
03044                   }
03045                 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
03046                 {
03047                   PixelChannel
03048                     channel;
03049 
03050                   PixelTrait
03051                     traits;
03052 
03053                   channel=GetPixelChannelMapChannel(image,i);
03054                   traits=GetPixelChannelMapTraits(image,channel);
03055                   if ((traits & BlendPixelTrait) == 0)
03056                     {
03057                       x_vector[x*GetPixelChannels(image)+i]=(MagickRealType)
03058                         p[i];
03059                       continue;
03060                     }
03061                   alpha=QuantumScale*GetPixelAlpha(image,p);
03062                   x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
03063                 }
03064                 p+=GetPixelChannels(image);
03065               }
03066               number_rows++;
03067             }
03068           for (x=0; x < (ssize_t) image->columns; x++)
03069             for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
03070               y_vector[x*GetPixelChannels(image)+i]+=scale.y*
03071                 x_vector[x*GetPixelChannels(image)+i];
03072           span.y-=scale.y;
03073           scale.y=(double) scale_image->rows/(double) image->rows;
03074           next_row=MagickTrue;
03075         }
03076         if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
03077           {
03078             /*
03079               Read a new scanline.
03080             */
03081             p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
03082               exception);
03083             if (p == (const Quantum *) NULL)
03084               break;
03085             for (x=0; x < (ssize_t) image->columns; x++)
03086             {
03087               if (GetPixelMask(image,p) != 0)
03088                 {
03089                   p+=GetPixelChannels(image);
03090                   continue;
03091                 }
03092               for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
03093               {
03094                 PixelChannel
03095                   channel;
03096 
03097                 PixelTrait
03098                   traits;
03099 
03100                 channel=GetPixelChannelMapChannel(image,i);
03101                 traits=GetPixelChannelMapTraits(image,channel);
03102                 if ((traits & BlendPixelTrait) == 0)
03103                   {
03104                     x_vector[x*GetPixelChannels(image)+i]=(MagickRealType)
03105                       p[i];
03106                     continue;
03107                   }
03108                 alpha=QuantumScale*GetPixelAlpha(image,p);
03109                 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
03110               }
03111               p+=GetPixelChannels(image);
03112             }
03113             number_rows++;
03114             next_row=MagickFalse;
03115           }
03116         for (x=0; x < (ssize_t) image->columns; x++)
03117         {
03118           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
03119           {
03120             pixel[i]=y_vector[x*GetPixelChannels(image)+i]+span.y*
03121               x_vector[x*GetPixelChannels(image)+i];
03122             scanline[x*GetPixelChannels(image)+i]=pixel[i];
03123             y_vector[x*GetPixelChannels(image)+i]=0.0;
03124           }
03125         }
03126         scale.y-=span.y;
03127         if (scale.y <= 0)
03128           {
03129             scale.y=(double) scale_image->rows/(double) image->rows;
03130             next_row=MagickTrue;
03131           }
03132         span.y=1.0;
03133       }
03134     if (scale_image->columns == image->columns)
03135       {
03136         /*
03137           Transfer scanline to scaled image.
03138         */
03139         for (x=0; x < (ssize_t) scale_image->columns; x++)
03140         {
03141           if (GetPixelMask(scale_image,q) != 0)
03142             {
03143               q+=GetPixelChannels(scale_image);
03144               continue;
03145             }
03146           for (i=0; i < (ssize_t) GetPixelChannels(scale_image); i++)
03147           {
03148             ssize_t
03149               offset;
03150 
03151             channel=GetPixelChannelMapChannel(scale_image,i);
03152             traits=GetPixelChannelMapTraits(image,channel);
03153             scale_traits=GetPixelChannelMapTraits(scale_image,channel);
03154             if ((traits == UndefinedPixelTrait) ||
03155                 (scale_traits == UndefinedPixelTrait))
03156               continue;
03157             offset=GetPixelChannelMapOffset(image,channel);
03158             if ((traits & BlendPixelTrait) == 0)
03159               {
03160                 SetPixelChannel(scale_image,channel,ClampToQuantum(
03161                   scanline[x*GetPixelChannels(image)+offset]),q);
03162                 continue;
03163               }
03164             alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
03165               GetPixelChannelMapChannel(image,AlphaPixelChannel)];
03166             gamma=1.0/(fabs((double) alpha) <= MagickEpsilon ? 1.0 : alpha);
03167             SetPixelChannel(scale_image,channel,ClampToQuantum(gamma*scanline[
03168               x*GetPixelChannels(image)+offset]),q);
03169           }
03170           q+=GetPixelChannels(scale_image);
03171         }
03172       }
03173     else
03174       {
03175         ssize_t
03176           n;
03177 
03178         /*
03179           Scale X direction.
03180         */
03181         next_column=MagickFalse;
03182         n=0;
03183         span.x=1.0;
03184         for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
03185           pixel[i]=0.0;
03186         for (x=0; x < (ssize_t) image->columns; x++)
03187         {
03188           scale.x=(double) scale_image->columns/(double) image->columns;
03189           while (scale.x >= span.x)
03190           {
03191             if (next_column != MagickFalse)
03192               {
03193                 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
03194                   pixel[i]=0.0;
03195                 n++;
03196               }
03197             for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
03198             {
03199               PixelChannel
03200                 channel;
03201 
03202               PixelTrait
03203                 traits;
03204 
03205               channel=GetPixelChannelMapChannel(image,i);
03206               traits=GetPixelChannelMapTraits(image,channel);
03207               if (traits == UndefinedPixelTrait)
03208                 continue;
03209               pixel[i]+=span.x*scanline[x*GetPixelChannels(image)+i];
03210               scale_scanline[n*MaxPixelChannels+channel]=pixel[i];
03211             }
03212             scale.x-=span.x;
03213             span.x=1.0;
03214             next_column=MagickTrue;
03215           }
03216         if (scale.x > 0)
03217           {
03218             if (next_column != MagickFalse)
03219               {
03220                 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
03221                   pixel[i]=0.0;
03222                 n++;
03223                 next_column=MagickFalse;
03224               }
03225             for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
03226               pixel[i]+=scale.x*scanline[x*GetPixelChannels(image)+i];
03227             span.x-=scale.x;
03228           }
03229       }
03230       if (span.x > 0)
03231         {
03232           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
03233             pixel[i]+=span.x*scanline[(x-1)*GetPixelChannels(image)+i];
03234         }
03235       if ((next_column == MagickFalse) &&
03236           ((ssize_t) n < (ssize_t) scale_image->columns))
03237         for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
03238         {
03239           channel=GetPixelChannelMapChannel(image,i);
03240           scale_scanline[n*MaxPixelChannels+channel]=pixel[i];
03241         }
03242       /*
03243         Transfer scanline to scaled image.
03244       */
03245       for (x=0; x < (ssize_t) scale_image->columns; x++)
03246       {
03247         if (GetPixelMask(scale_image,q) != 0)
03248           {
03249             q+=GetPixelChannels(scale_image);
03250             continue;
03251           }
03252         for (i=0; i < (ssize_t) GetPixelChannels(scale_image); i++)
03253         {
03254           channel=GetPixelChannelMapChannel(scale_image,i);
03255           traits=GetPixelChannelMapTraits(image,channel);
03256           scale_traits=GetPixelChannelMapTraits(scale_image,channel);
03257           if ((traits == UndefinedPixelTrait) ||
03258               (scale_traits == UndefinedPixelTrait))
03259             continue;
03260           if ((traits & BlendPixelTrait) == 0)
03261             {
03262               SetPixelChannel(scale_image,channel,ClampToQuantum(
03263                 scale_scanline[x*MaxPixelChannels+channel]),q);
03264               continue;
03265             }
03266           alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
03267             GetPixelChannelMapChannel(image,AlphaPixelChannel)];
03268           gamma=1.0/(fabs((double) alpha) <= MagickEpsilon ? 1.0 : alpha);
03269           SetPixelChannel(scale_image,channel,ClampToQuantum(gamma*
03270             scale_scanline[x*MaxPixelChannels+channel]),q);
03271         }
03272         q+=GetPixelChannels(scale_image);
03273       }
03274     }
03275     if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
03276       break;
03277     proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
03278       image->rows);
03279     if (proceed == MagickFalse)
03280       break;
03281   }
03282   scale_view=DestroyCacheView(scale_view);
03283   image_view=DestroyCacheView(image_view);
03284   /*
03285     Free allocated memory.
03286   */
03287   y_vector=(MagickRealType *) RelinquishMagickMemory(y_vector);
03288   scale_scanline=(MagickRealType *) RelinquishMagickMemory(scale_scanline);
03289   if (scale_image->rows != image->rows)
03290     scanline=(MagickRealType *) RelinquishMagickMemory(scanline);
03291   x_vector=(MagickRealType *) RelinquishMagickMemory(x_vector);
03292   scale_image->type=image->type;
03293   return(scale_image);
03294 }
03295 
03296 /*
03297 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03298 %                                                                             %
03299 %                                                                             %
03300 %                                                                             %
03301 %   T h u m b n a i l I m a g e                                               %
03302 %                                                                             %
03303 %                                                                             %
03304 %                                                                             %
03305 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03306 %
03307 %  ThumbnailImage() changes the size of an image to the given dimensions and
03308 %  removes any associated profiles.  The goal is to produce small low cost
03309 %  thumbnail images suited for display on the Web.
03310 %
03311 %  The format of the ThumbnailImage method is:
03312 %
03313 %      Image *ThumbnailImage(const Image *image,const size_t columns,
03314 %        const size_t rows,ExceptionInfo *exception)
03315 %
03316 %  A description of each parameter follows:
03317 %
03318 %    o image: the image.
03319 %
03320 %    o columns: the number of columns in the scaled image.
03321 %
03322 %    o rows: the number of rows in the scaled image.
03323 %
03324 %    o exception: return any errors or warnings in this structure.
03325 %
03326 */
03327 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
03328   const size_t rows,ExceptionInfo *exception)
03329 {
03330 #define SampleFactor  5
03331 
03332   char
03333     value[MaxTextExtent];
03334 
03335   const char
03336     *name;
03337 
03338   Image
03339     *thumbnail_image;
03340 
03341   MagickRealType
03342     x_factor,
03343     y_factor;
03344 
03345   size_t
03346     version;
03347 
03348   struct stat
03349     attributes;
03350 
03351   assert(image != (Image *) NULL);
03352   assert(image->signature == MagickSignature);
03353   if (image->debug != MagickFalse)
03354     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03355   assert(exception != (ExceptionInfo *) NULL);
03356   assert(exception->signature == MagickSignature);
03357   x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
03358   y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
03359   if ((x_factor*y_factor) > 0.1)
03360     thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
03361       exception);
03362   else
03363     if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
03364       thumbnail_image=ResizeImage(image,columns,rows,image->filter,
03365         image->blur,exception);
03366     else
03367       {
03368         Image
03369           *sample_image;
03370 
03371         sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
03372           exception);
03373         if (sample_image == (Image *) NULL)
03374           return((Image *) NULL);
03375         thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
03376           image->blur,exception);
03377         sample_image=DestroyImage(sample_image);
03378       }
03379   if (thumbnail_image == (Image *) NULL)
03380     return(thumbnail_image);
03381   (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
03382   if (thumbnail_image->matte == MagickFalse)
03383     (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel,exception);
03384   thumbnail_image->depth=8;
03385   thumbnail_image->interlace=NoInterlace;
03386   /*
03387     Strip all profiles except color profiles.
03388   */
03389   ResetImageProfileIterator(thumbnail_image);
03390   for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
03391   {
03392     if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
03393      {
03394        (void) DeleteImageProfile(thumbnail_image,name);
03395        ResetImageProfileIterator(thumbnail_image);
03396      }
03397     name=GetNextImageProfile(thumbnail_image);
03398   }
03399   (void) DeleteImageProperty(thumbnail_image,"comment");
03400   (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
03401   if (strstr(image->magick_filename,"//") == (char *) NULL)
03402     (void) FormatLocaleString(value,MaxTextExtent,"file://%s",
03403       image->magick_filename);
03404   (void) SetImageProperty(thumbnail_image,"Thumb::URI",value,exception);
03405   (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
03406   if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
03407     {
03408       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
03409         attributes.st_mtime);
03410       (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value,exception);
03411     }
03412   (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
03413     attributes.st_mtime);
03414   (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
03415   (void) ConcatenateMagickString(value,"B",MaxTextExtent);
03416   (void) SetImageProperty(thumbnail_image,"Thumb::Size",value,exception);
03417   (void) FormatLocaleString(value,MaxTextExtent,"image/%s",image->magick);
03418   LocaleLower(value);
03419   (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value,exception);
03420   (void) SetImageProperty(thumbnail_image,"software",GetMagickVersion(&version),
03421     exception);
03422   (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
03423     image->magick_columns);
03424   (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value,
03425     exception);
03426   (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
03427     image->magick_rows);
03428   (void) SetImageProperty(thumbnail_image,"Thumb::Image::Height",value,
03429     exception);
03430   (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
03431     GetImageListLength(image));
03432   (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value,
03433     exception);
03434   return(thumbnail_image);
03435 }