Home | History | Annotate | Download | only in MagickCore
      1 /*
      2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      3 %                                                                             %
      4 %                                                                             %
      5 %                                                                             %
      6 %                 RRRR   EEEEE  SSSSS  IIIII  ZZZZZ  EEEEE                    %
      7 %                 R   R  E      SS       I       ZZ  E                        %
      8 %                 RRRR   EEE     SSS     I     ZZZ   EEE                      %
      9 %                 R R    E         SS    I    ZZ     E                        %
     10 %                 R  R   EEEEE  SSSSS  IIIII  ZZZZZ  EEEEE                    %
     11 %                                                                             %
     12 %                                                                             %
     13 %                      MagickCore Image Resize Methods                        %
     14 %                                                                             %
     15 %                              Software Design                                %
     16 %                                   Cristy                                    %
     17 %                                 July 1992                                   %
     18 %                                                                             %
     19 %                                                                             %
     20 %  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
     21 %  dedicated to making software imaging solutions freely available.           %
     22 %                                                                             %
     23 %  You may not use this file except in compliance with the License.  You may  %
     24 %  obtain a copy of the License at                                            %
     25 %                                                                             %
     26 %    http://www.imagemagick.org/script/license.php                            %
     27 %                                                                             %
     28 %  Unless required by applicable law or agreed to in writing, software        %
     29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
     30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
     31 %  See the License for the specific language governing permissions and        %
     32 %  limitations under the License.                                             %
     33 %                                                                             %
     34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     35 %
     36 %
     37 */
     38 
     39 /*
     41   Include declarations.
     42 */
     43 #include "MagickCore/studio.h"
     44 #include "MagickCore/accelerate-private.h"
     45 #include "MagickCore/artifact.h"
     46 #include "MagickCore/blob.h"
     47 #include "MagickCore/cache.h"
     48 #include "MagickCore/cache-view.h"
     49 #include "MagickCore/channel.h"
     50 #include "MagickCore/color.h"
     51 #include "MagickCore/color-private.h"
     52 #include "MagickCore/draw.h"
     53 #include "MagickCore/exception.h"
     54 #include "MagickCore/exception-private.h"
     55 #include "MagickCore/gem.h"
     56 #include "MagickCore/image.h"
     57 #include "MagickCore/image-private.h"
     58 #include "MagickCore/list.h"
     59 #include "MagickCore/memory_.h"
     60 #include "MagickCore/memory-private.h"
     61 #include "MagickCore/magick.h"
     62 #include "MagickCore/pixel-accessor.h"
     63 #include "MagickCore/property.h"
     64 #include "MagickCore/monitor.h"
     65 #include "MagickCore/monitor-private.h"
     66 #include "MagickCore/nt-base-private.h"
     67 #include "MagickCore/option.h"
     68 #include "MagickCore/pixel.h"
     69 #include "MagickCore/pixel-private.h"
     70 #include "MagickCore/quantum-private.h"
     71 #include "MagickCore/resample.h"
     72 #include "MagickCore/resample-private.h"
     73 #include "MagickCore/resize.h"
     74 #include "MagickCore/resize-private.h"
     75 #include "MagickCore/resource_.h"
     76 #include "MagickCore/string_.h"
     77 #include "MagickCore/string-private.h"
     78 #include "MagickCore/thread-private.h"
     79 #include "MagickCore/token.h"
     80 #include "MagickCore/utility.h"
     81 #include "MagickCore/utility-private.h"
     82 #include "MagickCore/version.h"
     83 #if defined(MAGICKCORE_LQR_DELEGATE)
     84 #include <lqr.h>
     85 #endif
     86 
     87 /*
     89   Typedef declarations.
     90 */
     91 struct _ResizeFilter
     92 {
     93   double
     94     (*filter)(const double,const ResizeFilter *),
     95     (*window)(const double,const ResizeFilter *),
     96     support,        /* filter region of support - the filter support limit */
     97     window_support, /* window support, usally equal to support (expert only) */
     98     scale,          /* dimension scaling to fit window support (usally 1.0) */
     99     blur,           /* x-scale (blur-sharpen) */
    100     coefficient[7]; /* cubic coefficents for BC-cubic filters */
    101 
    102   ResizeWeightingFunctionType
    103     filterWeightingType,
    104     windowWeightingType;
    105 
    106   size_t
    107     signature;
    108 };
    109 
    110 /*
    112   Forward declaractions.
    113 */
    114 static double
    115   I0(double x),
    116   BesselOrderOne(double),
    117   Sinc(const double, const ResizeFilter *),
    118   SincFast(const double, const ResizeFilter *);
    119 
    120 /*
    122 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    123 %                                                                             %
    124 %                                                                             %
    125 %                                                                             %
    126 +   F i l t e r F u n c t i o n s                                             %
    127 %                                                                             %
    128 %                                                                             %
    129 %                                                                             %
    130 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    131 %
    132 %  These are the various filter and windowing functions that are provided.
    133 %
    134 %  They are internal to this module only.  See AcquireResizeFilterInfo() for
    135 %  details of the access to these functions, via the GetResizeFilterSupport()
    136 %  and GetResizeFilterWeight() API interface.
    137 %
    138 %  The individual filter functions have this format...
    139 %
    140 %     static MagickRealtype *FilterName(const double x,const double support)
    141 %
    142 %  A description of each parameter follows:
    143 %
    144 %    o x: the distance from the sampling point generally in the range of 0 to
    145 %      support.  The GetResizeFilterWeight() ensures this a positive value.
    146 %
    147 %    o resize_filter: current filter information.  This allows function to
    148 %      access support, and possibly other pre-calculated information defining
    149 %      the functions.
    150 %
    151 */
    152 
    153 static double Blackman(const double x,
    154   const ResizeFilter *magick_unused(resize_filter))
    155 {
    156   /*
    157     Blackman: 2nd order cosine windowing function:
    158       0.42 + 0.5 cos(pi x) + 0.08 cos(2pi x)
    159 
    160     Refactored by Chantal Racette and Nicolas Robidoux to one trig call and
    161     five flops.
    162   */
    163   const double cosine=cos((double) (MagickPI*x));
    164   magick_unreferenced(resize_filter);
    165   return(0.34+cosine*(0.5+cosine*0.16));
    166 }
    167 
    168 static double Bohman(const double x,
    169   const ResizeFilter *magick_unused(resize_filter))
    170 {
    171   /*
    172     Bohman: 2rd Order cosine windowing function:
    173       (1-x) cos(pi x) + sin(pi x) / pi.
    174 
    175     Refactored by Nicolas Robidoux to one trig call, one sqrt call, and 7 flops,
    176     taking advantage of the fact that the support of Bohman is 1.0 (so that we
    177     know that sin(pi x) >= 0).
    178   */
    179   const double cosine=cos((double) (MagickPI*x));
    180   const double sine=sqrt(1.0-cosine*cosine);
    181   magick_unreferenced(resize_filter);
    182   return((1.0-x)*cosine+(1.0/MagickPI)*sine);
    183 }
    184 
    185 static double Box(const double magick_unused(x),
    186   const ResizeFilter *magick_unused(resize_filter))
    187 {
    188   magick_unreferenced(x);
    189   magick_unreferenced(resize_filter);
    190 
    191   /*
    192     A Box filter is a equal weighting function (all weights equal).
    193     DO NOT LIMIT results by support or resize point sampling will work
    194     as it requests points beyond its normal 0.0 support size.
    195   */
    196   return(1.0);
    197 }
    198 
    199 static double Cosine(const double x,
    200   const ResizeFilter *magick_unused(resize_filter))
    201 {
    202   magick_unreferenced(resize_filter);
    203 
    204   /*
    205     Cosine window function:
    206       cos((pi/2)*x).
    207   */
    208   return((double)cos((double) (MagickPI2*x)));
    209 }
    210 
    211 static double CubicBC(const double x,const ResizeFilter *resize_filter)
    212 {
    213   /*
    214     Cubic Filters using B,C determined values:
    215        Mitchell-Netravali  B = 1/3 C = 1/3  "Balanced" cubic spline filter
    216        Catmull-Rom         B = 0   C = 1/2  Interpolatory and exact on linears
    217        Spline              B = 1   C = 0    B-Spline Gaussian approximation
    218        Hermite             B = 0   C = 0    B-Spline interpolator
    219 
    220     See paper by Mitchell and Netravali, Reconstruction Filters in Computer
    221     Graphics Computer Graphics, Volume 22, Number 4, August 1988
    222     http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/
    223     Mitchell.pdf.
    224 
    225     Coefficents are determined from B,C values:
    226        P0 = (  6 - 2*B       )/6 = coeff[0]
    227        P1 =         0
    228        P2 = (-18 +12*B + 6*C )/6 = coeff[1]
    229        P3 = ( 12 - 9*B - 6*C )/6 = coeff[2]
    230        Q0 = (      8*B +24*C )/6 = coeff[3]
    231        Q1 = (    -12*B -48*C )/6 = coeff[4]
    232        Q2 = (      6*B +30*C )/6 = coeff[5]
    233        Q3 = (    - 1*B - 6*C )/6 = coeff[6]
    234 
    235     which are used to define the filter:
    236 
    237        P0 + P1*x + P2*x^2 + P3*x^3      0 <= x < 1
    238        Q0 + Q1*x + Q2*x^2 + Q3*x^3      1 <= x < 2
    239 
    240     which ensures function is continuous in value and derivative (slope).
    241   */
    242   if (x < 1.0)
    243     return(resize_filter->coefficient[0]+x*(x*
    244       (resize_filter->coefficient[1]+x*resize_filter->coefficient[2])));
    245   if (x < 2.0)
    246     return(resize_filter->coefficient[3]+x*(resize_filter->coefficient[4]+x*
    247       (resize_filter->coefficient[5]+x*resize_filter->coefficient[6])));
    248   return(0.0);
    249 }
    250 
    251 static double Gaussian(const double x,const ResizeFilter *resize_filter)
    252 {
    253   /*
    254     Gaussian with a sigma = 1/2 (or as user specified)
    255 
    256     Gaussian Formula (1D) ...
    257         exp( -(x^2)/((2.0*sigma^2) ) / (sqrt(2*PI)*sigma^2))
    258 
    259     Gaussian Formula (2D) ...
    260         exp( -(x^2+y^2)/(2.0*sigma^2) ) / (PI*sigma^2) )
    261     or for radius
    262         exp( -(r^2)/(2.0*sigma^2) ) / (PI*sigma^2) )
    263 
    264     Note that it is only a change from 1-d to radial form is in the
    265     normalization multiplier which is not needed or used when Gaussian is used
    266     as a filter.
    267 
    268     The constants are pre-calculated...
    269 
    270         coeff[0]=sigma;
    271         coeff[1]=1.0/(2.0*sigma^2);
    272         coeff[2]=1.0/(sqrt(2*PI)*sigma^2);
    273 
    274         exp( -coeff[1]*(x^2)) ) * coeff[2];
    275 
    276     However the multiplier coeff[1] is need, the others are informative only.
    277 
    278     This separates the gaussian 'sigma' value from the 'blur/support'
    279     settings allowing for its use in special 'small sigma' gaussians,
    280     without the filter 'missing' pixels because the support becomes too
    281     small.
    282   */
    283   return(exp((double)(-resize_filter->coefficient[1]*x*x)));
    284 }
    285 
    286 static double Hann(const double x,
    287   const ResizeFilter *magick_unused(resize_filter))
    288 {
    289   /*
    290     Cosine window function:
    291       0.5+0.5*cos(pi*x).
    292   */
    293   const double cosine=cos((double) (MagickPI*x));
    294   magick_unreferenced(resize_filter);
    295   return(0.5+0.5*cosine);
    296 }
    297 
    298 static double Hamming(const double x,
    299   const ResizeFilter *magick_unused(resize_filter))
    300 {
    301   /*
    302     Offset cosine window function:
    303      .54 + .46 cos(pi x).
    304   */
    305   const double cosine=cos((double) (MagickPI*x));
    306   magick_unreferenced(resize_filter);
    307   return(0.54+0.46*cosine);
    308 }
    309 
    310 static double Jinc(const double x,
    311   const ResizeFilter *magick_unused(resize_filter))
    312 {
    313   magick_unreferenced(resize_filter);
    314 
    315   /*
    316     See Pratt "Digital Image Processing" p.97 for Jinc/Bessel functions.
    317     http://mathworld.wolfram.com/JincFunction.html and page 11 of
    318     http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
    319 
    320     The original "zoom" program by Paul Heckbert called this "Bessel".  But
    321     really it is more accurately named "Jinc".
    322   */
    323   if (x == 0.0)
    324     return(0.5*MagickPI);
    325   return(BesselOrderOne(MagickPI*x)/x);
    326 }
    327 
    328 static double Kaiser(const double x,const ResizeFilter *resize_filter)
    329 {
    330   /*
    331     Kaiser Windowing Function (bessel windowing)
    332 
    333        I0( beta * sqrt( 1-x^2) ) / IO(0)
    334 
    335     Beta (coeff[0]) is a free value from 5 to 8 (defaults to 6.5).
    336     However it is typically defined in terms of Alpha*PI
    337 
    338     The normalization factor (coeff[1]) is not actually needed,
    339     but without it the filters has a large value at x=0 making it
    340     difficult to compare the function with other windowing functions.
    341   */
    342   return(resize_filter->coefficient[1]*I0(resize_filter->coefficient[0]*
    343     sqrt((double) (1.0-x*x))));
    344 }
    345 
    346 static double Lagrange(const double x,const ResizeFilter *resize_filter)
    347 {
    348   double
    349     value;
    350 
    351   register ssize_t
    352     i;
    353 
    354   ssize_t
    355     n,
    356     order;
    357 
    358   /*
    359     Lagrange piecewise polynomial fit of sinc: N is the 'order' of the lagrange
    360     function and depends on the overall support window size of the filter. That
    361     is: for a support of 2, it gives a lagrange-4 (piecewise cubic function).
    362 
    363     "n" identifies the piece of the piecewise polynomial.
    364 
    365     See Survey: Interpolation Methods, IEEE Transactions on Medical Imaging,
    366     Vol 18, No 11, November 1999, p1049-1075, -- Equation 27 on p1064.
    367   */
    368   if (x > resize_filter->support)
    369     return(0.0);
    370   order=(ssize_t) (2.0*resize_filter->window_support);  /* number of pieces */
    371   n=(ssize_t) (resize_filter->window_support+x);
    372   value=1.0f;
    373   for (i=0; i < order; i++)
    374     if (i != n)
    375       value*=(n-i-x)/(n-i);
    376   return(value);
    377 }
    378 
    379 static double Quadratic(const double x,
    380   const ResizeFilter *magick_unused(resize_filter))
    381 {
    382   magick_unreferenced(resize_filter);
    383 
    384   /*
    385     2rd order (quadratic) B-Spline approximation of Gaussian.
    386   */
    387   if (x < 0.5)
    388     return(0.75-x*x);
    389   if (x < 1.5)
    390     return(0.5*(x-1.5)*(x-1.5));
    391   return(0.0);
    392 }
    393 
    394 static double Sinc(const double x,
    395   const ResizeFilter *magick_unused(resize_filter))
    396 {
    397   magick_unreferenced(resize_filter);
    398 
    399   /*
    400     Scaled sinc(x) function using a trig call:
    401       sinc(x) == sin(pi x)/(pi x).
    402   */
    403   if (x != 0.0)
    404     {
    405       const double alpha=(double) (MagickPI*x);
    406       return(sin((double) alpha)/alpha);
    407     }
    408   return((double) 1.0);
    409 }
    410 
    411 static double SincFast(const double x,
    412   const ResizeFilter *magick_unused(resize_filter))
    413 {
    414   magick_unreferenced(resize_filter);
    415 
    416   /*
    417     Approximations of the sinc function sin(pi x)/(pi x) over the interval
    418     [-4,4] constructed by Nicolas Robidoux and Chantal Racette with funding
    419     from the Natural Sciences and Engineering Research Council of Canada.
    420 
    421     Although the approximations are polynomials (for low order of
    422     approximation) and quotients of polynomials (for higher order of
    423     approximation) and consequently are similar in form to Taylor polynomials /
    424     Pade approximants, the approximations are computed with a completely
    425     different technique.
    426 
    427     Summary: These approximations are "the best" in terms of bang (accuracy)
    428     for the buck (flops). More specifically: Among the polynomial quotients
    429     that can be computed using a fixed number of flops (with a given "+ - * /
    430     budget"), the chosen polynomial quotient is the one closest to the
    431     approximated function with respect to maximum absolute relative error over
    432     the given interval.
    433 
    434     The Remez algorithm, as implemented in the boost library's minimax package,
    435     is the key to the construction: http://www.boost.org/doc/libs/1_36_0/libs/
    436     math/doc/sf_and_dist/html/math_toolkit/backgrounders/remez.html
    437 
    438     If outside of the interval of approximation, use the standard trig formula.
    439   */
    440   if (x > 4.0)
    441     {
    442       const double alpha=(double) (MagickPI*x);
    443       return(sin((double) alpha)/alpha);
    444     }
    445   {
    446     /*
    447       The approximations only depend on x^2 (sinc is an even function).
    448     */
    449     const double xx = x*x;
    450 #if MAGICKCORE_QUANTUM_DEPTH <= 8
    451     /*
    452       Maximum absolute relative error 6.3e-6 < 1/2^17.
    453     */
    454     const double c0 = 0.173610016489197553621906385078711564924e-2L;
    455     const double c1 = -0.384186115075660162081071290162149315834e-3L;
    456     const double c2 = 0.393684603287860108352720146121813443561e-4L;
    457     const double c3 = -0.248947210682259168029030370205389323899e-5L;
    458     const double c4 = 0.107791837839662283066379987646635416692e-6L;
    459     const double c5 = -0.324874073895735800961260474028013982211e-8L;
    460     const double c6 = 0.628155216606695311524920882748052490116e-10L;
    461     const double c7 = -0.586110644039348333520104379959307242711e-12L;
    462     const double p =
    463       c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
    464     return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
    465 #elif MAGICKCORE_QUANTUM_DEPTH <= 16
    466     /*
    467       Max. abs. rel. error 2.2e-8 < 1/2^25.
    468     */
    469     const double c0 = 0.173611107357320220183368594093166520811e-2L;
    470     const double c1 = -0.384240921114946632192116762889211361285e-3L;
    471     const double c2 = 0.394201182359318128221229891724947048771e-4L;
    472     const double c3 = -0.250963301609117217660068889165550534856e-5L;
    473     const double c4 = 0.111902032818095784414237782071368805120e-6L;
    474     const double c5 = -0.372895101408779549368465614321137048875e-8L;
    475     const double c6 = 0.957694196677572570319816780188718518330e-10L;
    476     const double c7 = -0.187208577776590710853865174371617338991e-11L;
    477     const double c8 = 0.253524321426864752676094495396308636823e-13L;
    478     const double c9 = -0.177084805010701112639035485248501049364e-15L;
    479     const double p =
    480       c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
    481     return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
    482 #else
    483     /*
    484       Max. abs. rel. error 1.2e-12 < 1/2^39.
    485     */
    486     const double c0 = 0.173611111110910715186413700076827593074e-2L;
    487     const double c1 = -0.289105544717893415815859968653611245425e-3L;
    488     const double c2 = 0.206952161241815727624413291940849294025e-4L;
    489     const double c3 = -0.834446180169727178193268528095341741698e-6L;
    490     const double c4 = 0.207010104171026718629622453275917944941e-7L;
    491     const double c5 = -0.319724784938507108101517564300855542655e-9L;
    492     const double c6 = 0.288101675249103266147006509214934493930e-11L;
    493     const double c7 = -0.118218971804934245819960233886876537953e-13L;
    494     const double p =
    495       c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
    496     const double d0 = 1.0L;
    497     const double d1 = 0.547981619622284827495856984100563583948e-1L;
    498     const double d2 = 0.134226268835357312626304688047086921806e-2L;
    499     const double d3 = 0.178994697503371051002463656833597608689e-4L;
    500     const double d4 = 0.114633394140438168641246022557689759090e-6L;
    501     const double q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
    502     return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
    503 #endif
    504   }
    505 }
    506 
    507 static double Triangle(const double x,
    508   const ResizeFilter *magick_unused(resize_filter))
    509 {
    510   magick_unreferenced(resize_filter);
    511 
    512   /*
    513     1st order (linear) B-Spline, bilinear interpolation, Tent 1D filter, or
    514     a Bartlett 2D Cone filter.  Also used as a Bartlett Windowing function
    515     for Sinc().
    516   */
    517   if (x < 1.0)
    518     return(1.0-x);
    519   return(0.0);
    520 }
    521 
    522 static double Welch(const double x,
    523   const ResizeFilter *magick_unused(resize_filter))
    524 {
    525   magick_unreferenced(resize_filter);
    526 
    527   /*
    528     Welch parabolic windowing filter.
    529   */
    530   if (x < 1.0)
    531     return(1.0-x*x);
    532   return(0.0);
    533 }
    534 
    535 /*
    537 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    538 %                                                                             %
    539 %                                                                             %
    540 %                                                                             %
    541 +   A c q u i r e R e s i z e F i l t e r                                     %
    542 %                                                                             %
    543 %                                                                             %
    544 %                                                                             %
    545 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    546 %
    547 %  AcquireResizeFilter() allocates the ResizeFilter structure.  Choose from
    548 %  these filters:
    549 %
    550 %  FIR (Finite impulse Response) Filters
    551 %      Box         Triangle   Quadratic
    552 %      Spline      Hermite    Catrom
    553 %      Mitchell
    554 %
    555 %  IIR (Infinite impulse Response) Filters
    556 %      Gaussian     Sinc        Jinc (Bessel)
    557 %
    558 %  Windowed Sinc/Jinc Filters
    559 %      Blackman    Bohman     Lanczos
    560 %      Hann        Hamming    Cosine
    561 %      Kaiser      Welch      Parzen
    562 %      Bartlett
    563 %
    564 %  Special Purpose Filters
    565 %      Cubic  SincFast  LanczosSharp  Lanczos2  Lanczos2Sharp
    566 %      Robidoux RobidouxSharp
    567 %
    568 %  The users "-filter" selection is used to lookup the default 'expert'
    569 %  settings for that filter from a internal table.  However any provided
    570 %  'expert' settings (see below) may override this selection.
    571 %
    572 %  FIR filters are used as is, and are limited to that filters support window
    573 %  (unless over-ridden).  'Gaussian' while classed as an IIR filter, is also
    574 %  simply clipped by its support size (currently 1.5 or approximately 3*sigma
    575 %  as recommended by many references)
    576 %
    577 %  The special a 'cylindrical' filter flag will promote the default 4-lobed
    578 %  Windowed Sinc filter to a 3-lobed Windowed Jinc equivalent, which is better
    579 %  suited to this style of image resampling. This typically happens when using
    580 %  such a filter for images distortions.
    581 %
    582 %  SPECIFIC FILTERS:
    583 %
    584 %  Directly requesting 'Sinc', 'Jinc' function as a filter will force the use
    585 %  of function without any windowing, or promotion for cylindrical usage.  This
    586 %  is not recommended, except by image processing experts, especially as part
    587 %  of expert option filter function selection.
    588 %
    589 %  Two forms of the 'Sinc' function are available: Sinc and SincFast.  Sinc is
    590 %  computed using the traditional sin(pi*x)/(pi*x); it is selected if the user
    591 %  specifically specifies the use of a Sinc filter. SincFast uses highly
    592 %  accurate (and fast) polynomial (low Q) and rational (high Q) approximations,
    593 %  and will be used by default in most cases.
    594 %
    595 %  The Lanczos filter is a special 3-lobed Sinc-windowed Sinc filter (promoted
    596 %  to Jinc-windowed Jinc for cylindrical (Elliptical Weighted Average) use).
    597 %  The Sinc version is the most popular windowed filter.
    598 %
    599 %  LanczosSharp is a slightly sharpened (blur=0.9812505644269356 < 1) form of
    600 %  the Lanczos filter, specifically designed for EWA distortion (as a
    601 %  Jinc-Jinc); it can also be used as a slightly sharper orthogonal Lanczos
    602 %  (Sinc-Sinc) filter. The chosen blur value comes as close as possible to
    603 %  satisfying the following condition without changing the character of the
    604 %  corresponding EWA filter:
    605 %
    606 %    'No-Op' Vertical and Horizontal Line Preservation Condition: Images with
    607 %    only vertical or horizontal features are preserved when performing 'no-op"
    608 %    with EWA distortion.
    609 %
    610 %  The Lanczos2 and Lanczos2Sharp filters are 2-lobe versions of the Lanczos
    611 %  filters.  The 'sharp' version uses a blur factor of 0.9549963639785485,
    612 %  again chosen because the resulting EWA filter comes as close as possible to
    613 %  satisfying the above condition.
    614 %
    615 %  Robidoux is another filter tuned for EWA. It is the Keys cubic filter
    616 %  defined by B=(228 - 108 sqrt(2))/199. Robidoux satisfies the "'No-Op'
    617 %  Vertical and Horizontal Line Preservation Condition" exactly, and it
    618 %  moderately blurs high frequency 'pixel-hash' patterns under no-op.  It turns
    619 %  out to be close to both Mitchell and Lanczos2Sharp.  For example, its first
    620 %  crossing is at (36 sqrt(2) + 123)/(72 sqrt(2) + 47), almost the same as the
    621 %  first crossing of Mitchell and Lanczos2Sharp.
    622 %
    623 %  RodidouxSharp is a slightly sharper version of Rodidoux, some believe it
    624 %  is too sharp.  It is designed to minimize the maximum possible change in
    625 %  a pixel value which is at one of the extremes (e.g., 0 or 255) under no-op
    626 %  conditions.  Amazingly Mitchell falls roughly between Rodidoux and
    627 %  RodidouxSharp, though this seems to have been pure coincidence.
    628 %
    629 %  'EXPERT' OPTIONS:
    630 %
    631 %  These artifact "defines" are not recommended for production use without
    632 %  expert knowledge of resampling, filtering, and the effects they have on the
    633 %  resulting resampled (resized or distorted) image.
    634 %
    635 %  They can be used to override any and all filter default, and it is
    636 %  recommended you make good use of "filter:verbose" to make sure that the
    637 %  overall effect of your selection (before and after) is as expected.
    638 %
    639 %    "filter:verbose"  controls whether to output the exact results of the
    640 %       filter selections made, as well as plotting data for graphing the
    641 %       resulting filter over the filters support range.
    642 %
    643 %    "filter:filter"  select the main function associated with this filter
    644 %       name, as the weighting function of the filter.  This can be used to
    645 %       set a windowing function as a weighting function, for special
    646 %       purposes, such as graphing.
    647 %
    648 %       If a "filter:window" operation has not been provided, a 'Box'
    649 %       windowing function will be set to denote that no windowing function is
    650 %       being used.
    651 %
    652 %    "filter:window"  Select this windowing function for the filter. While any
    653 %       filter could be used as a windowing function, using the 'first lobe' of
    654 %       that filter over the whole support window, using a non-windowing
    655 %       function is not advisible. If no weighting filter function is specified
    656 %       a 'SincFast' filter is used.
    657 %
    658 %    "filter:lobes"  Number of lobes to use for the Sinc/Jinc filter.  This a
    659 %       simpler method of setting filter support size that will correctly
    660 %       handle the Sinc/Jinc switch for an operators filtering requirements.
    661 %       Only integers should be given.
    662 %
    663 %    "filter:support" Set the support size for filtering to the size given.
    664 %       This not recommended for Sinc/Jinc windowed filters (lobes should be
    665 %       used instead).  This will override any 'filter:lobes' option.
    666 %
    667 %    "filter:win-support" Scale windowing function to this size instead.  This
    668 %       causes the windowing (or self-windowing Lagrange filter) to act is if
    669 %       the support window it much much larger than what is actually supplied
    670 %       to the calling operator.  The filter however is still clipped to the
    671 %       real support size given, by the support range supplied to the caller.
    672 %       If unset this will equal the normal filter support size.
    673 %
    674 %    "filter:blur" Scale the filter and support window by this amount.  A value
    675 %       of > 1 will generally result in a more blurred image with more ringing
    676 %       effects, while a value <1 will sharpen the resulting image with more
    677 %       aliasing effects.
    678 %
    679 %    "filter:sigma" The sigma value to use for the Gaussian filter only.
    680 %       Defaults to '1/2'.  Using a different sigma effectively provides a
    681 %       method of using the filter as a 'blur' convolution.  Particularly when
    682 %       using it for Distort.
    683 %
    684 %    "filter:b"
    685 %    "filter:c" Override the preset B,C values for a Cubic filter.
    686 %       If only one of these are given it is assumes to be a 'Keys' type of
    687 %       filter such that B+2C=1, where Keys 'alpha' value = C.
    688 %
    689 %  Examples:
    690 %
    691 %  Set a true un-windowed Sinc filter with 10 lobes (very slow):
    692 %     -define filter:filter=Sinc
    693 %     -define filter:lobes=8
    694 %
    695 %  Set an 8 lobe Lanczos (Sinc or Jinc) filter:
    696 %     -filter Lanczos
    697 %     -define filter:lobes=8
    698 %
    699 %  The format of the AcquireResizeFilter method is:
    700 %
    701 %      ResizeFilter *AcquireResizeFilter(const Image *image,
    702 %        const FilterType filter_type,const MagickBooleanType cylindrical,
    703 %        ExceptionInfo *exception)
    704 %
    705 %  A description of each parameter follows:
    706 %
    707 %    o image: the image.
    708 %
    709 %    o filter: the filter type, defining a preset filter, window and support.
    710 %      The artifact settings listed above will override those selections.
    711 %
    712 %    o blur: blur the filter by this amount, use 1.0 if unknown.  Image
    713 %      artifact "filter:blur" will override this API call usage, including any
    714 %      internal change (such as for cylindrical usage).
    715 %
    716 %    o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical (radial)
    717 %      filter (Jinc).
    718 %
    719 %    o exception: return any errors or warnings in this structure.
    720 %
    721 */
    722 MagickPrivate ResizeFilter *AcquireResizeFilter(const Image *image,
    723   const FilterType filter,const MagickBooleanType cylindrical,
    724   ExceptionInfo *exception)
    725 {
    726   const char
    727     *artifact;
    728 
    729   FilterType
    730     filter_type,
    731     window_type;
    732 
    733   double
    734     B,
    735     C,
    736     value;
    737 
    738   register ResizeFilter
    739     *resize_filter;
    740 
    741   /*
    742     Table Mapping given Filter, into Weighting and Windowing functions.
    743     A 'Box' windowing function means its a simble non-windowed filter.
    744     An 'SincFast' filter function could be upgraded to a 'Jinc' filter if a
    745     "cylindrical" is requested, unless a 'Sinc' or 'SincFast' filter was
    746     specifically requested by the user.
    747 
    748     WARNING: The order of this table must match the order of the FilterType
    749     enumeration specified in "resample.h", or the filter names will not match
    750     the filter being setup.
    751 
    752     You can check filter setups with the "filter:verbose" expert setting.
    753   */
    754   static struct
    755   {
    756     FilterType
    757       filter,
    758       window;
    759   } const mapping[SentinelFilter] =
    760   {
    761     { UndefinedFilter,     BoxFilter      },  /* Undefined (default to Box)   */
    762     { PointFilter,         BoxFilter      },  /* SPECIAL: Nearest neighbour   */
    763     { BoxFilter,           BoxFilter      },  /* Box averaging filter         */
    764     { TriangleFilter,      BoxFilter      },  /* Linear interpolation filter  */
    765     { HermiteFilter,       BoxFilter      },  /* Hermite interpolation filter */
    766     { SincFastFilter,      HannFilter     },  /* Hann -- cosine-sinc          */
    767     { SincFastFilter,      HammingFilter  },  /* Hamming --   '' variation    */
    768     { SincFastFilter,      BlackmanFilter },  /* Blackman -- 2*cosine-sinc    */
    769     { GaussianFilter,      BoxFilter      },  /* Gaussian blur filter         */
    770     { QuadraticFilter,     BoxFilter      },  /* Quadratic Gaussian approx    */
    771     { CubicFilter,         BoxFilter      },  /* General Cubic Filter, Spline */
    772     { CatromFilter,        BoxFilter      },  /* Cubic-Keys interpolator      */
    773     { MitchellFilter,      BoxFilter      },  /* 'Ideal' Cubic-Keys filter    */
    774     { JincFilter,          BoxFilter      },  /* Raw 3-lobed Jinc function    */
    775     { SincFilter,          BoxFilter      },  /* Raw 4-lobed Sinc function    */
    776     { SincFastFilter,      BoxFilter      },  /* Raw fast sinc ("Pade"-type)  */
    777     { SincFastFilter,      KaiserFilter   },  /* Kaiser -- square root-sinc   */
    778     { LanczosFilter,       WelchFilter    },  /* Welch -- parabolic (3 lobe)  */
    779     { SincFastFilter,      CubicFilter    },  /* Parzen -- cubic-sinc         */
    780     { SincFastFilter,      BohmanFilter   },  /* Bohman -- 2*cosine-sinc      */
    781     { SincFastFilter,      TriangleFilter },  /* Bartlett -- triangle-sinc    */
    782     { LagrangeFilter,      BoxFilter      },  /* Lagrange self-windowing      */
    783     { LanczosFilter,       LanczosFilter  },  /* Lanczos Sinc-Sinc filters    */
    784     { LanczosSharpFilter,  LanczosSharpFilter }, /* | these require */
    785     { Lanczos2Filter,      Lanczos2Filter },     /* | special handling */
    786     { Lanczos2SharpFilter, Lanczos2SharpFilter },
    787     { RobidouxFilter,      BoxFilter      },  /* Cubic Keys tuned for EWA     */
    788     { RobidouxSharpFilter, BoxFilter      },  /* Sharper Cubic Keys for EWA   */
    789     { LanczosFilter,       CosineFilter   },  /* Cosine window (3 lobes)      */
    790     { SplineFilter,        BoxFilter      },  /* Spline Cubic Filter          */
    791     { LanczosRadiusFilter, LanczosFilter  },  /* Lanczos with integer radius  */
    792   };
    793   /*
    794     Table mapping the filter/window from the above table to an actual function.
    795     The default support size for that filter as a weighting function, the range
    796     to scale with to use that function as a sinc windowing function, (typ 1.0).
    797 
    798     Note that the filter_type -> function is 1 to 1 except for Sinc(),
    799     SincFast(), and CubicBC() functions, which may have multiple filter to
    800     function associations.
    801 
    802     See "filter:verbose" handling below for the function -> filter mapping.
    803   */
    804   static struct
    805   {
    806     double
    807       (*function)(const double,const ResizeFilter*),
    808       support, /* Default lobes/support size of the weighting filter. */
    809       scale,   /* Support when function used as a windowing function
    810                  Typically equal to the location of the first zero crossing. */
    811       B,C;     /* BC-spline coefficients, ignored if not a CubicBC filter. */
    812     ResizeWeightingFunctionType weightingFunctionType;
    813   } const filters[SentinelFilter] =
    814   {
    815     /*            .---  support window (if used as a Weighting Function)
    816                   |    .--- first crossing (if used as a Windowing Function)
    817                   |    |    .--- B value for Cubic Function
    818                   |    |    |    .---- C value for Cubic Function
    819                   |    |    |    |                                    */
    820     { Box,       0.5, 0.5, 0.0, 0.0, BoxWeightingFunction },      /* Undefined (default to Box)  */
    821     { Box,       0.0, 0.5, 0.0, 0.0, BoxWeightingFunction },      /* Point (special handling)    */
    822     { Box,       0.5, 0.5, 0.0, 0.0, BoxWeightingFunction },      /* Box                         */
    823     { Triangle,  1.0, 1.0, 0.0, 0.0, TriangleWeightingFunction }, /* Triangle                    */
    824     { CubicBC,   1.0, 1.0, 0.0, 0.0, CubicBCWeightingFunction },  /* Hermite (cubic  B=C=0)      */
    825     { Hann,      1.0, 1.0, 0.0, 0.0, HannWeightingFunction },     /* Hann, cosine window         */
    826     { Hamming,   1.0, 1.0, 0.0, 0.0, HammingWeightingFunction },  /* Hamming, '' variation       */
    827     { Blackman,  1.0, 1.0, 0.0, 0.0, BlackmanWeightingFunction }, /* Blackman, 2*cosine window   */
    828     { Gaussian,  2.0, 1.5, 0.0, 0.0, GaussianWeightingFunction }, /* Gaussian                    */
    829     { Quadratic, 1.5, 1.5, 0.0, 0.0, QuadraticWeightingFunction },/* Quadratic gaussian          */
    830     { CubicBC,   2.0, 2.0, 1.0, 0.0, CubicBCWeightingFunction },  /* General Cubic Filter        */
    831     { CubicBC,   2.0, 1.0, 0.0, 0.5, CubicBCWeightingFunction },  /* Catmull-Rom    (B=0,C=1/2)  */
    832     { CubicBC,   2.0, 8.0/7.0, 1./3., 1./3., CubicBCWeightingFunction }, /* Mitchell   (B=C=1/3)    */
    833     { Jinc,      3.0, 1.2196698912665045, 0.0, 0.0, JincWeightingFunction }, /* Raw 3-lobed Jinc */
    834     { Sinc,      4.0, 1.0, 0.0, 0.0, SincWeightingFunction },     /* Raw 4-lobed Sinc            */
    835     { SincFast,  4.0, 1.0, 0.0, 0.0, SincFastWeightingFunction }, /* Raw fast sinc ("Pade"-type) */
    836     { Kaiser,    1.0, 1.0, 0.0, 0.0, KaiserWeightingFunction },   /* Kaiser (square root window) */
    837     { Welch,     1.0, 1.0, 0.0, 0.0, WelchWeightingFunction },    /* Welch (parabolic window)    */
    838     { CubicBC,   2.0, 2.0, 1.0, 0.0, CubicBCWeightingFunction },  /* Parzen (B-Spline window)    */
    839     { Bohman,    1.0, 1.0, 0.0, 0.0, BohmanWeightingFunction },   /* Bohman, 2*Cosine window     */
    840     { Triangle,  1.0, 1.0, 0.0, 0.0, TriangleWeightingFunction }, /* Bartlett (triangle window)  */
    841     { Lagrange,  2.0, 1.0, 0.0, 0.0, LagrangeWeightingFunction }, /* Lagrange sinc approximation */
    842     { SincFast,  3.0, 1.0, 0.0, 0.0, SincFastWeightingFunction }, /* Lanczos, 3-lobed Sinc-Sinc  */
    843     { SincFast,  3.0, 1.0, 0.0, 0.0, SincFastWeightingFunction }, /* Lanczos, Sharpened          */
    844     { SincFast,  2.0, 1.0, 0.0, 0.0, SincFastWeightingFunction }, /* Lanczos, 2-lobed            */
    845     { SincFast,  2.0, 1.0, 0.0, 0.0, SincFastWeightingFunction }, /* Lanczos2, sharpened         */
    846     /* Robidoux: Keys cubic close to Lanczos2D sharpened */
    847     { CubicBC,   2.0, 1.1685777620836932,
    848                             0.37821575509399867, 0.31089212245300067, CubicBCWeightingFunction },
    849     /* RobidouxSharp: Sharper version of Robidoux */
    850     { CubicBC,   2.0, 1.105822933719019,
    851                             0.2620145123990142,  0.3689927438004929, CubicBCWeightingFunction },
    852     { Cosine,    1.0, 1.0, 0.0, 0.0, CosineWeightingFunction },   /* Low level cosine window     */
    853     { CubicBC,   2.0, 2.0, 1.0, 0.0, CubicBCWeightingFunction },  /* Cubic B-Spline (B=1,C=0)    */
    854     { SincFast,  3.0, 1.0, 0.0, 0.0, SincFastWeightingFunction }, /* Lanczos, Interger Radius    */
    855   };
    856   /*
    857     The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
    858     function being used as a filter. It is used by the "filter:lobes" expert
    859     setting and for 'lobes' for Jinc functions in the previous table. This way
    860     users do not have to deal with the highly irrational lobe sizes of the Jinc
    861     filter.
    862 
    863     Values taken from
    864     http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
    865     using Jv-function with v=1, then dividing by PI.
    866   */
    867   static double
    868     jinc_zeros[16] =
    869     {
    870       1.2196698912665045,
    871       2.2331305943815286,
    872       3.2383154841662362,
    873       4.2410628637960699,
    874       5.2427643768701817,
    875       6.2439216898644877,
    876       7.2447598687199570,
    877       8.2453949139520427,
    878       9.2458926849494673,
    879       10.246293348754916,
    880       11.246622794877883,
    881       12.246898461138105,
    882       13.247132522181061,
    883       14.247333735806849,
    884       15.247508563037300,
    885       16.247661874700962
    886    };
    887 
    888   /*
    889     Allocate resize filter.
    890   */
    891   assert(image != (const Image *) NULL);
    892   assert(image->signature == MagickCoreSignature);
    893   if (image->debug != MagickFalse)
    894     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    895   assert(UndefinedFilter < filter && filter < SentinelFilter);
    896   assert(exception != (ExceptionInfo *) NULL);
    897   assert(exception->signature == MagickCoreSignature);
    898   resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
    899   if (resize_filter == (ResizeFilter *) NULL)
    900     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
    901   (void) ResetMagickMemory(resize_filter,0,sizeof(*resize_filter));
    902   /*
    903     Defaults for the requested filter.
    904   */
    905   filter_type=mapping[filter].filter;
    906   window_type=mapping[filter].window;
    907   resize_filter->blur=1.0;
    908   /* Promote 1D Windowed Sinc Filters to a 2D Windowed Jinc filters */
    909   if ( cylindrical != MagickFalse && (filter_type == SincFastFilter) &&
    910       (filter != SincFastFilter))
    911     filter_type=JincFilter;  /* 1D Windowed Sinc => 2D Windowed Jinc filters */
    912 
    913   /* Expert filter setting override */
    914   artifact=GetImageArtifact(image,"filter:filter");
    915   if (IsStringTrue(artifact) != MagickFalse)
    916     {
    917       ssize_t
    918         option;
    919 
    920       option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
    921       if ((UndefinedFilter < option) && (option < SentinelFilter))
    922         { /* Raw filter request - no window function. */
    923           filter_type=(FilterType) option;
    924           window_type=BoxFilter;
    925         }
    926       /* Filter override with a specific window function. */
    927       artifact=GetImageArtifact(image,"filter:window");
    928       if (artifact != (const char *) NULL)
    929         {
    930           option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
    931           if ((UndefinedFilter < option) && (option < SentinelFilter))
    932             window_type=(FilterType) option;
    933         }
    934     }
    935   else
    936     {
    937       /* Window specified, but no filter function?  Assume Sinc/Jinc. */
    938       artifact=GetImageArtifact(image,"filter:window");
    939       if (artifact != (const char *) NULL)
    940         {
    941           ssize_t
    942             option;
    943 
    944           option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
    945           if ((UndefinedFilter < option) && (option < SentinelFilter))
    946             {
    947               filter_type= cylindrical != MagickFalse ? JincFilter
    948                                                      : SincFastFilter;
    949               window_type=(FilterType) option;
    950             }
    951         }
    952     }
    953 
    954   /* Assign the real functions to use for the filters selected. */
    955   resize_filter->filter=filters[filter_type].function;
    956   resize_filter->support=filters[filter_type].support;
    957   resize_filter->filterWeightingType=filters[filter_type].weightingFunctionType;
    958   resize_filter->window=filters[window_type].function;
    959   resize_filter->windowWeightingType=filters[window_type].weightingFunctionType;
    960   resize_filter->scale=filters[window_type].scale;
    961   resize_filter->signature=MagickCoreSignature;
    962 
    963   /* Filter Modifications for orthogonal/cylindrical usage */
    964   if (cylindrical != MagickFalse)
    965     switch (filter_type)
    966     {
    967       case BoxFilter:
    968         /* Support for Cylindrical Box should be sqrt(2)/2 */
    969         resize_filter->support=(double) MagickSQ1_2;
    970         break;
    971       case LanczosFilter:
    972       case LanczosSharpFilter:
    973       case Lanczos2Filter:
    974       case Lanczos2SharpFilter:
    975       case LanczosRadiusFilter:
    976         resize_filter->filter=filters[JincFilter].function;
    977         resize_filter->window=filters[JincFilter].function;
    978         resize_filter->scale=filters[JincFilter].scale;
    979         /* number of lobes (support window size) remain unchanged */
    980         break;
    981       default:
    982         break;
    983     }
    984   /* Global Sharpening (regardless of orthoginal/cylindrical) */
    985   switch (filter_type)
    986   {
    987     case LanczosSharpFilter:
    988       resize_filter->blur *= 0.9812505644269356;
    989       break;
    990     case Lanczos2SharpFilter:
    991       resize_filter->blur *= 0.9549963639785485;
    992       break;
    993     /* case LanczosRadius:  blur adjust is done after lobes */
    994     default:
    995       break;
    996   }
    997 
    998   /*
    999     Expert Option Modifications.
   1000   */
   1001 
   1002   /* User Gaussian Sigma Override - no support change */
   1003   if ((resize_filter->filter == Gaussian) ||
   1004       (resize_filter->window == Gaussian) ) {
   1005     value=0.5;    /* guassian sigma default, half pixel */
   1006     artifact=GetImageArtifact(image,"filter:sigma");
   1007     if (artifact != (const char *) NULL)
   1008       value=StringToDouble(artifact,(char **) NULL);
   1009     /* Define coefficents for Gaussian */
   1010     resize_filter->coefficient[0]=value;                 /* note sigma too */
   1011     resize_filter->coefficient[1]=PerceptibleReciprocal(2.0*value*value); /* sigma scaling */
   1012     resize_filter->coefficient[2]=PerceptibleReciprocal(Magick2PI*value*value);
   1013        /* normalization - not actually needed or used! */
   1014     if ( value > 0.5 )
   1015       resize_filter->support *= 2*value;  /* increase support linearly */
   1016   }
   1017 
   1018   /* User Kaiser Alpha Override - no support change */
   1019   if ((resize_filter->filter == Kaiser) ||
   1020       (resize_filter->window == Kaiser) ) {
   1021     value=6.5; /* default beta value for Kaiser bessel windowing function */
   1022     artifact=GetImageArtifact(image,"filter:alpha");  /* FUTURE: depreciate */
   1023     if (artifact != (const char *) NULL)
   1024       value=StringToDouble(artifact,(char **) NULL);
   1025     artifact=GetImageArtifact(image,"filter:kaiser-beta");
   1026     if (artifact != (const char *) NULL)
   1027       value=StringToDouble(artifact,(char **) NULL);
   1028     artifact=GetImageArtifact(image,"filter:kaiser-alpha");
   1029     if (artifact != (const char *) NULL)
   1030       value=StringToDouble(artifact,(char **) NULL)*MagickPI;
   1031     /* Define coefficents for Kaiser Windowing Function */
   1032     resize_filter->coefficient[0]=value;         /* alpha */
   1033     resize_filter->coefficient[1]=PerceptibleReciprocal(I0(value));
   1034       /* normalization */
   1035   }
   1036 
   1037   /* Support Overrides */
   1038   artifact=GetImageArtifact(image,"filter:lobes");
   1039   if (artifact != (const char *) NULL)
   1040     {
   1041       ssize_t
   1042         lobes;
   1043 
   1044       lobes=(ssize_t) StringToLong(artifact);
   1045       if (lobes < 1)
   1046         lobes=1;
   1047       resize_filter->support=(double) lobes;
   1048     }
   1049   if (resize_filter->filter == Jinc)
   1050     {
   1051       /*
   1052         Convert a Jinc function lobes value to a real support value.
   1053       */
   1054       if (resize_filter->support > 16)
   1055         resize_filter->support=jinc_zeros[15];  /* largest entry in table */
   1056       else
   1057         resize_filter->support=jinc_zeros[((long) resize_filter->support)-1];
   1058       /*
   1059         Blur this filter so support is a integer value (lobes dependant).
   1060       */
   1061       if (filter_type == LanczosRadiusFilter)
   1062         resize_filter->blur*=floor(resize_filter->support)/
   1063           resize_filter->support;
   1064     }
   1065   /*
   1066     Expert blur override.
   1067   */
   1068   artifact=GetImageArtifact(image,"filter:blur");
   1069   if (artifact != (const char *) NULL)
   1070     resize_filter->blur*=StringToDouble(artifact,(char **) NULL);
   1071   if (resize_filter->blur < MagickEpsilon)
   1072     resize_filter->blur=(double) MagickEpsilon;
   1073   /*
   1074     Expert override of the support setting.
   1075   */
   1076   artifact=GetImageArtifact(image,"filter:support");
   1077   if (artifact != (const char *) NULL)
   1078     resize_filter->support=fabs(StringToDouble(artifact,(char **) NULL));
   1079   /*
   1080     Scale windowing function separately to the support 'clipping' window
   1081     that calling operator is planning to actually use. (Expert override)
   1082   */
   1083   resize_filter->window_support=resize_filter->support; /* default */
   1084   artifact=GetImageArtifact(image,"filter:win-support");
   1085   if (artifact != (const char *) NULL)
   1086     resize_filter->window_support=fabs(StringToDouble(artifact,(char **) NULL));
   1087   /*
   1088     Adjust window function scaling to match windowing support for weighting
   1089     function.  This avoids a division on every filter call.
   1090   */
   1091   resize_filter->scale/=resize_filter->window_support;
   1092   /*
   1093    * Set Cubic Spline B,C values, calculate Cubic coefficients.
   1094   */
   1095   B=0.0;
   1096   C=0.0;
   1097   if ((resize_filter->filter == CubicBC) ||
   1098       (resize_filter->window == CubicBC) )
   1099     {
   1100       B=filters[filter_type].B;
   1101       C=filters[filter_type].C;
   1102       if (filters[window_type].function == CubicBC)
   1103         {
   1104           B=filters[window_type].B;
   1105           C=filters[window_type].C;
   1106         }
   1107       artifact=GetImageArtifact(image,"filter:b");
   1108       if (artifact != (const char *) NULL)
   1109         {
   1110           B=StringToDouble(artifact,(char **) NULL);
   1111           C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
   1112           artifact=GetImageArtifact(image,"filter:c"); /* user C override */
   1113           if (artifact != (const char *) NULL)
   1114             C=StringToDouble(artifact,(char **) NULL);
   1115         }
   1116       else
   1117         {
   1118           artifact=GetImageArtifact(image,"filter:c");
   1119           if (artifact != (const char *) NULL)
   1120             {
   1121               C=StringToDouble(artifact,(char **) NULL);
   1122               B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
   1123             }
   1124         }
   1125       {
   1126         const double
   1127           twoB = B+B;
   1128 
   1129         /*
   1130           Convert B,C values into Cubic Coefficents. See CubicBC().
   1131         */
   1132         resize_filter->coefficient[0]=1.0-(1.0/3.0)*B;
   1133         resize_filter->coefficient[1]=-3.0+twoB+C;
   1134         resize_filter->coefficient[2]=2.0-1.5*B-C;
   1135         resize_filter->coefficient[3]=(4.0/3.0)*B+4.0*C;
   1136         resize_filter->coefficient[4]=-8.0*C-twoB;
   1137         resize_filter->coefficient[5]=B+5.0*C;
   1138         resize_filter->coefficient[6]=(-1.0/6.0)*B-C;
   1139       }
   1140     }
   1141 
   1142   /*
   1143     Expert Option Request for verbose details of the resulting filter.
   1144   */
   1145 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1146   #pragma omp master
   1147   {
   1148 #endif
   1149     if (IsStringTrue(GetImageArtifact(image,"filter:verbose")) != MagickFalse)
   1150       {
   1151         double
   1152           support,
   1153           x;
   1154 
   1155         /*
   1156           Set the weighting function properly when the weighting function
   1157           may not exactly match the filter of the same name.  EG: a Point
   1158           filter is really uses a Box weighting function with a different
   1159           support than is typically used.
   1160         */
   1161         if (resize_filter->filter == Box)       filter_type=BoxFilter;
   1162         if (resize_filter->filter == Sinc)      filter_type=SincFilter;
   1163         if (resize_filter->filter == SincFast)  filter_type=SincFastFilter;
   1164         if (resize_filter->filter == Jinc)      filter_type=JincFilter;
   1165         if (resize_filter->filter == CubicBC)   filter_type=CubicFilter;
   1166         if (resize_filter->window == Box)       window_type=BoxFilter;
   1167         if (resize_filter->window == Sinc)      window_type=SincFilter;
   1168         if (resize_filter->window == SincFast)  window_type=SincFastFilter;
   1169         if (resize_filter->window == Jinc)      window_type=JincFilter;
   1170         if (resize_filter->window == CubicBC)   window_type=CubicFilter;
   1171         /*
   1172           Report Filter Details.
   1173         */
   1174         support=GetResizeFilterSupport(resize_filter);  /* practical_support */
   1175         (void) FormatLocaleFile(stdout,
   1176           "# Resampling Filter (for graphing)\n#\n");
   1177         (void) FormatLocaleFile(stdout,"# filter = %s\n",
   1178           CommandOptionToMnemonic(MagickFilterOptions,filter_type));
   1179         (void) FormatLocaleFile(stdout,"# window = %s\n",
   1180           CommandOptionToMnemonic(MagickFilterOptions,window_type));
   1181         (void) FormatLocaleFile(stdout,"# support = %.*g\n",
   1182           GetMagickPrecision(),(double) resize_filter->support);
   1183         (void) FormatLocaleFile(stdout,"# window-support = %.*g\n",
   1184           GetMagickPrecision(),(double) resize_filter->window_support);
   1185         (void) FormatLocaleFile(stdout,"# scale-blur = %.*g\n",
   1186           GetMagickPrecision(),(double)resize_filter->blur);
   1187         if ((filter_type == GaussianFilter) || (window_type == GaussianFilter))
   1188           (void) FormatLocaleFile(stdout,"# gaussian-sigma = %.*g\n",
   1189             GetMagickPrecision(),(double)resize_filter->coefficient[0]);
   1190         if ( filter_type == KaiserFilter || window_type == KaiserFilter )
   1191           (void) FormatLocaleFile(stdout,"# kaiser-beta = %.*g\n",
   1192             GetMagickPrecision(),(double)resize_filter->coefficient[0]);
   1193         (void) FormatLocaleFile(stdout,"# practical-support = %.*g\n",
   1194           GetMagickPrecision(), (double)support);
   1195         if ( filter_type == CubicFilter || window_type == CubicFilter )
   1196           (void) FormatLocaleFile(stdout,"# B,C = %.*g,%.*g\n",
   1197             GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
   1198         (void) FormatLocaleFile(stdout,"\n");
   1199         /*
   1200           Output values of resulting filter graph -- for graphing filter result.
   1201         */
   1202         for (x=0.0; x <= support; x+=0.01f)
   1203           (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",x,
   1204             GetMagickPrecision(),(double)
   1205             GetResizeFilterWeight(resize_filter,x));
   1206         /*
   1207           A final value so gnuplot can graph the 'stop' properly.
   1208         */
   1209         (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",support,
   1210           GetMagickPrecision(),0.0);
   1211       }
   1212       /* Output the above once only for each image - remove setting */
   1213     (void) DeleteImageArtifact((Image *) image,"filter:verbose");
   1214 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1215   }
   1216 #endif
   1217   return(resize_filter);
   1218 }
   1219 
   1220 /*
   1222 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1223 %                                                                             %
   1224 %                                                                             %
   1225 %                                                                             %
   1226 %   A d a p t i v e R e s i z e I m a g e                                     %
   1227 %                                                                             %
   1228 %                                                                             %
   1229 %                                                                             %
   1230 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1231 %
   1232 %  AdaptiveResizeImage() adaptively resize image with pixel resampling.
   1233 %
   1234 %  This is shortcut function for a fast interpolative resize using mesh
   1235 %  interpolation.  It works well for small resizes of less than +/- 50%
   1236 %  of the original image size.  For larger resizing on images a full
   1237 %  filtered and slower resize function should be used instead.
   1238 %
   1239 %  The format of the AdaptiveResizeImage method is:
   1240 %
   1241 %      Image *AdaptiveResizeImage(const Image *image,const size_t columns,
   1242 %        const size_t rows,ExceptionInfo *exception)
   1243 %
   1244 %  A description of each parameter follows:
   1245 %
   1246 %    o image: the image.
   1247 %
   1248 %    o columns: the number of columns in the resized image.
   1249 %
   1250 %    o rows: the number of rows in the resized image.
   1251 %
   1252 %    o exception: return any errors or warnings in this structure.
   1253 %
   1254 */
   1255 MagickExport Image *AdaptiveResizeImage(const Image *image,
   1256   const size_t columns,const size_t rows,ExceptionInfo *exception)
   1257 {
   1258   Image
   1259     *resize_image;
   1260 
   1261   resize_image=InterpolativeResizeImage(image,columns,rows,MeshInterpolatePixel,
   1262     exception);
   1263   return(resize_image);
   1264 }
   1265 
   1266 /*
   1268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1269 %                                                                             %
   1270 %                                                                             %
   1271 %                                                                             %
   1272 +   B e s s e l O r d e r O n e                                               %
   1273 %                                                                             %
   1274 %                                                                             %
   1275 %                                                                             %
   1276 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1277 %
   1278 %  BesselOrderOne() computes the Bessel function of x of the first kind of
   1279 %  order 0.  This is used to create the Jinc() filter function below.
   1280 %
   1281 %    Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
   1282 %
   1283 %       j1(x) = x*j1(x);
   1284 %
   1285 %    For x in (8,inf)
   1286 %
   1287 %       j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
   1288 %
   1289 %    where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
   1290 %
   1291 %       cos(x1) =  cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
   1292 %               =  1/sqrt(2) * (sin(x) - cos(x))
   1293 %       sin(x1) =  sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
   1294 %               = -1/sqrt(2) * (sin(x) + cos(x))
   1295 %
   1296 %  The format of the BesselOrderOne method is:
   1297 %
   1298 %      double BesselOrderOne(double x)
   1299 %
   1300 %  A description of each parameter follows:
   1301 %
   1302 %    o x: double value.
   1303 %
   1304 */
   1305 
   1306 #undef I0
   1307 static double I0(double x)
   1308 {
   1309   double
   1310     sum,
   1311     t,
   1312     y;
   1313 
   1314   register ssize_t
   1315     i;
   1316 
   1317   /*
   1318     Zeroth order Bessel function of the first kind.
   1319   */
   1320   sum=1.0;
   1321   y=x*x/4.0;
   1322   t=y;
   1323   for (i=2; t > MagickEpsilon; i++)
   1324   {
   1325     sum+=t;
   1326     t*=y/((double) i*i);
   1327   }
   1328   return(sum);
   1329 }
   1330 
   1331 #undef J1
   1332 static double J1(double x)
   1333 {
   1334   double
   1335     p,
   1336     q;
   1337 
   1338   register ssize_t
   1339     i;
   1340 
   1341   static const double
   1342     Pone[] =
   1343     {
   1344        0.581199354001606143928050809e+21,
   1345       -0.6672106568924916298020941484e+20,
   1346        0.2316433580634002297931815435e+19,
   1347       -0.3588817569910106050743641413e+17,
   1348        0.2908795263834775409737601689e+15,
   1349       -0.1322983480332126453125473247e+13,
   1350        0.3413234182301700539091292655e+10,
   1351       -0.4695753530642995859767162166e+7,
   1352        0.270112271089232341485679099e+4
   1353     },
   1354     Qone[] =
   1355     {
   1356       0.11623987080032122878585294e+22,
   1357       0.1185770712190320999837113348e+20,
   1358       0.6092061398917521746105196863e+17,
   1359       0.2081661221307607351240184229e+15,
   1360       0.5243710262167649715406728642e+12,
   1361       0.1013863514358673989967045588e+10,
   1362       0.1501793594998585505921097578e+7,
   1363       0.1606931573481487801970916749e+4,
   1364       0.1e+1
   1365     };
   1366 
   1367   p=Pone[8];
   1368   q=Qone[8];
   1369   for (i=7; i >= 0; i--)
   1370   {
   1371     p=p*x*x+Pone[i];
   1372     q=q*x*x+Qone[i];
   1373   }
   1374   return(p/q);
   1375 }
   1376 
   1377 #undef P1
   1378 static double P1(double x)
   1379 {
   1380   double
   1381     p,
   1382     q;
   1383 
   1384   register ssize_t
   1385     i;
   1386 
   1387   static const double
   1388     Pone[] =
   1389     {
   1390       0.352246649133679798341724373e+5,
   1391       0.62758845247161281269005675e+5,
   1392       0.313539631109159574238669888e+5,
   1393       0.49854832060594338434500455e+4,
   1394       0.2111529182853962382105718e+3,
   1395       0.12571716929145341558495e+1
   1396     },
   1397     Qone[] =
   1398     {
   1399       0.352246649133679798068390431e+5,
   1400       0.626943469593560511888833731e+5,
   1401       0.312404063819041039923015703e+5,
   1402       0.4930396490181088979386097e+4,
   1403       0.2030775189134759322293574e+3,
   1404       0.1e+1
   1405     };
   1406 
   1407   p=Pone[5];
   1408   q=Qone[5];
   1409   for (i=4; i >= 0; i--)
   1410   {
   1411     p=p*(8.0/x)*(8.0/x)+Pone[i];
   1412     q=q*(8.0/x)*(8.0/x)+Qone[i];
   1413   }
   1414   return(p/q);
   1415 }
   1416 
   1417 #undef Q1
   1418 static double Q1(double x)
   1419 {
   1420   double
   1421     p,
   1422     q;
   1423 
   1424   register ssize_t
   1425     i;
   1426 
   1427   static const double
   1428     Pone[] =
   1429     {
   1430       0.3511751914303552822533318e+3,
   1431       0.7210391804904475039280863e+3,
   1432       0.4259873011654442389886993e+3,
   1433       0.831898957673850827325226e+2,
   1434       0.45681716295512267064405e+1,
   1435       0.3532840052740123642735e-1
   1436     },
   1437     Qone[] =
   1438     {
   1439       0.74917374171809127714519505e+4,
   1440       0.154141773392650970499848051e+5,
   1441       0.91522317015169922705904727e+4,
   1442       0.18111867005523513506724158e+4,
   1443       0.1038187585462133728776636e+3,
   1444       0.1e+1
   1445     };
   1446 
   1447   p=Pone[5];
   1448   q=Qone[5];
   1449   for (i=4; i >= 0; i--)
   1450   {
   1451     p=p*(8.0/x)*(8.0/x)+Pone[i];
   1452     q=q*(8.0/x)*(8.0/x)+Qone[i];
   1453   }
   1454   return(p/q);
   1455 }
   1456 
   1457 static double BesselOrderOne(double x)
   1458 {
   1459   double
   1460     p,
   1461     q;
   1462 
   1463   if (x == 0.0)
   1464     return(0.0);
   1465   p=x;
   1466   if (x < 0.0)
   1467     x=(-x);
   1468   if (x < 8.0)
   1469     return(p*J1(x));
   1470   q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
   1471     cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
   1472     cos((double) x))));
   1473   if (p < 0.0)
   1474     q=(-q);
   1475   return(q);
   1476 }
   1477 
   1478 /*
   1480 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1481 %                                                                             %
   1482 %                                                                             %
   1483 %                                                                             %
   1484 +   D e s t r o y R e s i z e F i l t e r                                     %
   1485 %                                                                             %
   1486 %                                                                             %
   1487 %                                                                             %
   1488 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1489 %
   1490 %  DestroyResizeFilter() destroy the resize filter.
   1491 %
   1492 %  The format of the DestroyResizeFilter method is:
   1493 %
   1494 %      ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
   1495 %
   1496 %  A description of each parameter follows:
   1497 %
   1498 %    o resize_filter: the resize filter.
   1499 %
   1500 */
   1501 MagickPrivate ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
   1502 {
   1503   assert(resize_filter != (ResizeFilter *) NULL);
   1504   assert(resize_filter->signature == MagickCoreSignature);
   1505   resize_filter->signature=(~MagickCoreSignature);
   1506   resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
   1507   return(resize_filter);
   1508 }
   1509 
   1510 /*
   1512 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1513 %                                                                             %
   1514 %                                                                             %
   1515 %                                                                             %
   1516 +   G e t R e s i z e F i l t e r S u p p o r t                               %
   1517 %                                                                             %
   1518 %                                                                             %
   1519 %                                                                             %
   1520 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1521 %
   1522 %  GetResizeFilterSupport() return the current support window size for this
   1523 %  filter.  Note that this may have been enlarged by filter:blur factor.
   1524 %
   1525 %  The format of the GetResizeFilterSupport method is:
   1526 %
   1527 %      double GetResizeFilterSupport(const ResizeFilter *resize_filter)
   1528 %
   1529 %  A description of each parameter follows:
   1530 %
   1531 %    o filter: Image filter to use.
   1532 %
   1533 */
   1534 
   1535 MagickPrivate double *GetResizeFilterCoefficient(
   1536   const ResizeFilter *resize_filter)
   1537 {
   1538   assert(resize_filter != (ResizeFilter *) NULL);
   1539   assert(resize_filter->signature == MagickCoreSignature);
   1540   return((double *) resize_filter->coefficient);
   1541 }
   1542 
   1543 MagickPrivate double GetResizeFilterBlur(const ResizeFilter *resize_filter)
   1544 {
   1545   assert(resize_filter != (ResizeFilter *) NULL);
   1546   assert(resize_filter->signature == MagickCoreSignature);
   1547   return(resize_filter->blur);
   1548 }
   1549 
   1550 MagickPrivate double GetResizeFilterScale(const ResizeFilter *resize_filter)
   1551 {
   1552   assert(resize_filter != (ResizeFilter *) NULL);
   1553   assert(resize_filter->signature == MagickCoreSignature);
   1554   return(resize_filter->scale);
   1555 }
   1556 
   1557 MagickPrivate double GetResizeFilterWindowSupport(
   1558   const ResizeFilter *resize_filter)
   1559 {
   1560   assert(resize_filter != (ResizeFilter *) NULL);
   1561   assert(resize_filter->signature == MagickCoreSignature);
   1562   return(resize_filter->window_support);
   1563 }
   1564 
   1565 MagickPrivate ResizeWeightingFunctionType GetResizeFilterWeightingType(
   1566   const ResizeFilter *resize_filter)
   1567 {
   1568   assert(resize_filter != (ResizeFilter *) NULL);
   1569   assert(resize_filter->signature == MagickCoreSignature);
   1570   return(resize_filter->filterWeightingType);
   1571 }
   1572 
   1573 MagickPrivate ResizeWeightingFunctionType GetResizeFilterWindowWeightingType(
   1574   const ResizeFilter *resize_filter)
   1575 {
   1576   assert(resize_filter != (ResizeFilter *) NULL);
   1577   assert(resize_filter->signature == MagickCoreSignature);
   1578   return(resize_filter->windowWeightingType);
   1579 }
   1580 
   1581 MagickPrivate double GetResizeFilterSupport(const ResizeFilter *resize_filter)
   1582 {
   1583   assert(resize_filter != (ResizeFilter *) NULL);
   1584   assert(resize_filter->signature == MagickCoreSignature);
   1585   return(resize_filter->support*resize_filter->blur);
   1586 }
   1587 
   1588 /*
   1590 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1591 %                                                                             %
   1592 %                                                                             %
   1593 %                                                                             %
   1594 +   G e t R e s i z e F i l t e r W e i g h t                                 %
   1595 %                                                                             %
   1596 %                                                                             %
   1597 %                                                                             %
   1598 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1599 %
   1600 %  GetResizeFilterWeight evaluates the specified resize filter at the point x
   1601 %  which usally lies between zero and the filters current 'support' and
   1602 %  returns the weight of the filter function at that point.
   1603 %
   1604 %  The format of the GetResizeFilterWeight method is:
   1605 %
   1606 %      double GetResizeFilterWeight(const ResizeFilter *resize_filter,
   1607 %        const double x)
   1608 %
   1609 %  A description of each parameter follows:
   1610 %
   1611 %    o filter: the filter type.
   1612 %
   1613 %    o x: the point.
   1614 %
   1615 */
   1616 MagickPrivate double GetResizeFilterWeight(const ResizeFilter *resize_filter,
   1617   const double x)
   1618 {
   1619   double
   1620     scale,
   1621     weight,
   1622     x_blur;
   1623 
   1624   /*
   1625     Windowing function - scale the weighting filter by this amount.
   1626   */
   1627   assert(resize_filter != (ResizeFilter *) NULL);
   1628   assert(resize_filter->signature == MagickCoreSignature);
   1629   x_blur=fabs((double) x)/resize_filter->blur;  /* X offset with blur scaling */
   1630   if ((resize_filter->window_support < MagickEpsilon) ||
   1631       (resize_filter->window == Box))
   1632     scale=1.0;  /* Point or Box Filter -- avoid division by zero */
   1633   else
   1634     {
   1635       scale=resize_filter->scale;
   1636       scale=resize_filter->window(x_blur*scale,resize_filter);
   1637     }
   1638   weight=scale*resize_filter->filter(x_blur,resize_filter);
   1639   return(weight);
   1640 }
   1641 
   1642 /*
   1644 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1645 %                                                                             %
   1646 %                                                                             %
   1647 %                                                                             %
   1648 %   I n t e r p o l a t i v e R e s i z e I m a g e                           %
   1649 %                                                                             %
   1650 %                                                                             %
   1651 %                                                                             %
   1652 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1653 %
   1654 %  InterpolativeResizeImage() resizes an image using the specified
   1655 %  interpolation method.
   1656 %
   1657 %  The format of the InterpolativeResizeImage method is:
   1658 %
   1659 %      Image *InterpolativeResizeImage(const Image *image,const size_t columns,
   1660 %        const size_t rows,const PixelInterpolateMethod method,
   1661 %        ExceptionInfo *exception)
   1662 %
   1663 %  A description of each parameter follows:
   1664 %
   1665 %    o image: the image.
   1666 %
   1667 %    o columns: the number of columns in the resized image.
   1668 %
   1669 %    o rows: the number of rows in the resized image.
   1670 %
   1671 %    o method: the pixel interpolation method.
   1672 %
   1673 %    o exception: return any errors or warnings in this structure.
   1674 %
   1675 */
   1676 MagickExport Image *InterpolativeResizeImage(const Image *image,
   1677   const size_t columns,const size_t rows,const PixelInterpolateMethod method,
   1678   ExceptionInfo *exception)
   1679 {
   1680 #define InterpolativeResizeImageTag  "Resize/Image"
   1681 
   1682   CacheView
   1683     *image_view,
   1684     *resize_view;
   1685 
   1686   Image
   1687     *resize_image;
   1688 
   1689   MagickBooleanType
   1690     status;
   1691 
   1692   MagickOffsetType
   1693     progress;
   1694 
   1695   PointInfo
   1696     scale;
   1697 
   1698   ssize_t
   1699     y;
   1700 
   1701   /*
   1702     Interpolatively resize image.
   1703   */
   1704   assert(image != (const Image *) NULL);
   1705   assert(image->signature == MagickCoreSignature);
   1706   if (image->debug != MagickFalse)
   1707     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1708   assert(exception != (ExceptionInfo *) NULL);
   1709   assert(exception->signature == MagickCoreSignature);
   1710   if ((columns == 0) || (rows == 0))
   1711     ThrowImageException(ImageError,"NegativeOrZeroImageSize");
   1712   if ((columns == image->columns) && (rows == image->rows))
   1713     return(CloneImage(image,0,0,MagickTrue,exception));
   1714   resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
   1715   if (resize_image == (Image *) NULL)
   1716     return((Image *) NULL);
   1717   if (SetImageStorageClass(resize_image,DirectClass,exception) == MagickFalse)
   1718     {
   1719       resize_image=DestroyImage(resize_image);
   1720       return((Image *) NULL);
   1721     }
   1722   status=MagickTrue;
   1723   progress=0;
   1724   image_view=AcquireVirtualCacheView(image,exception);
   1725   resize_view=AcquireAuthenticCacheView(resize_image,exception);
   1726   scale.x=(double) image->columns/resize_image->columns;
   1727   scale.y=(double) image->rows/resize_image->rows;
   1728 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1729   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   1730     magick_threads(image,resize_image,resize_image->rows,1)
   1731 #endif
   1732   for (y=0; y < (ssize_t) resize_image->rows; y++)
   1733   {
   1734     PointInfo
   1735       offset;
   1736 
   1737     register Quantum
   1738       *magick_restrict q;
   1739 
   1740     register ssize_t
   1741       x;
   1742 
   1743     if (status == MagickFalse)
   1744       continue;
   1745     q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
   1746       exception);
   1747     if (q == (Quantum *) NULL)
   1748       continue;
   1749     offset.y=((double) y+0.5)*scale.y-0.5;
   1750     for (x=0; x < (ssize_t) resize_image->columns; x++)
   1751     {
   1752       register ssize_t
   1753         i;
   1754 
   1755       if (GetPixelReadMask(resize_image,q) == 0)
   1756         {
   1757           q+=GetPixelChannels(resize_image);
   1758           continue;
   1759         }
   1760       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   1761       {
   1762         PixelChannel
   1763           channel;
   1764 
   1765         PixelTrait
   1766           resize_traits,
   1767           traits;
   1768 
   1769         channel=GetPixelChannelChannel(image,i);
   1770         traits=GetPixelChannelTraits(image,channel);
   1771         resize_traits=GetPixelChannelTraits(resize_image,channel);
   1772         if ((traits == UndefinedPixelTrait) ||
   1773             (resize_traits == UndefinedPixelTrait))
   1774           continue;
   1775         offset.x=((double) x+0.5)*scale.x-0.5;
   1776         status=InterpolatePixelChannels(image,image_view,resize_image,method,
   1777           offset.x,offset.y,q,exception);
   1778       }
   1779       q+=GetPixelChannels(resize_image);
   1780     }
   1781     if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
   1782       status=MagickFalse;
   1783     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   1784       {
   1785         MagickBooleanType
   1786           proceed;
   1787 
   1788 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1789         #pragma omp critical (MagickCore_InterpolativeResizeImage)
   1790 #endif
   1791         proceed=SetImageProgress(image,InterpolativeResizeImageTag,progress++,
   1792           image->rows);
   1793         if (proceed == MagickFalse)
   1794           status=MagickFalse;
   1795       }
   1796   }
   1797   resize_view=DestroyCacheView(resize_view);
   1798   image_view=DestroyCacheView(image_view);
   1799   if (status == MagickFalse)
   1800     resize_image=DestroyImage(resize_image);
   1801   return(resize_image);
   1802 }
   1803 #if defined(MAGICKCORE_LQR_DELEGATE)
   1804 
   1805 /*
   1807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1808 %                                                                             %
   1809 %                                                                             %
   1810 %                                                                             %
   1811 %   L i q u i d R e s c a l e I m a g e                                       %
   1812 %                                                                             %
   1813 %                                                                             %
   1814 %                                                                             %
   1815 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1816 %
   1817 %  LiquidRescaleImage() rescales image with seam carving.
   1818 %
   1819 %  The format of the LiquidRescaleImage method is:
   1820 %
   1821 %      Image *LiquidRescaleImage(const Image *image,const size_t columns,
   1822 %        const size_t rows,const double delta_x,const double rigidity,
   1823 %        ExceptionInfo *exception)
   1824 %
   1825 %  A description of each parameter follows:
   1826 %
   1827 %    o image: the image.
   1828 %
   1829 %    o columns: the number of columns in the rescaled image.
   1830 %
   1831 %    o rows: the number of rows in the rescaled image.
   1832 %
   1833 %    o delta_x: maximum seam transversal step (0 means straight seams).
   1834 %
   1835 %    o rigidity: introduce a bias for non-straight seams (typically 0).
   1836 %
   1837 %    o exception: return any errors or warnings in this structure.
   1838 %
   1839 */
   1840 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
   1841   const size_t rows,const double delta_x,const double rigidity,
   1842   ExceptionInfo *exception)
   1843 {
   1844 #define LiquidRescaleImageTag  "Rescale/Image"
   1845 
   1846   CacheView
   1847     *image_view,
   1848     *rescale_view;
   1849 
   1850   gfloat
   1851     *packet,
   1852     *pixels;
   1853 
   1854   Image
   1855     *rescale_image;
   1856 
   1857   int
   1858     x_offset,
   1859     y_offset;
   1860 
   1861   LqrCarver
   1862     *carver;
   1863 
   1864   LqrRetVal
   1865     lqr_status;
   1866 
   1867   MagickBooleanType
   1868     status;
   1869 
   1870   MemoryInfo
   1871     *pixel_info;
   1872 
   1873   register gfloat
   1874     *q;
   1875 
   1876   ssize_t
   1877     y;
   1878 
   1879   /*
   1880     Liquid rescale image.
   1881   */
   1882   assert(image != (const Image *) NULL);
   1883   assert(image->signature == MagickCoreSignature);
   1884   if (image->debug != MagickFalse)
   1885     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1886   assert(exception != (ExceptionInfo *) NULL);
   1887   assert(exception->signature == MagickCoreSignature);
   1888   if ((columns == 0) || (rows == 0))
   1889     ThrowImageException(ImageError,"NegativeOrZeroImageSize");
   1890   if ((columns == image->columns) && (rows == image->rows))
   1891     return(CloneImage(image,0,0,MagickTrue,exception));
   1892   if ((columns <= 2) || (rows <= 2))
   1893     return(ResizeImage(image,columns,rows,image->filter,exception));
   1894   pixel_info=AcquireVirtualMemory(image->columns,image->rows*
   1895     GetPixelChannels(image)*sizeof(*pixels));
   1896   if (pixel_info == (MemoryInfo *) NULL)
   1897     return((Image *) NULL);
   1898   pixels=(gfloat *) GetVirtualMemoryBlob(pixel_info);
   1899   status=MagickTrue;
   1900   q=pixels;
   1901   image_view=AcquireVirtualCacheView(image,exception);
   1902   for (y=0; y < (ssize_t) image->rows; y++)
   1903   {
   1904     register const Quantum
   1905       *magick_restrict p;
   1906 
   1907     register ssize_t
   1908       x;
   1909 
   1910     if (status == MagickFalse)
   1911       continue;
   1912     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
   1913     if (p == (const Quantum *) NULL)
   1914       {
   1915         status=MagickFalse;
   1916         continue;
   1917       }
   1918     for (x=0; x < (ssize_t) image->columns; x++)
   1919     {
   1920       register ssize_t
   1921         i;
   1922 
   1923       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   1924         *q++=QuantumScale*p[i];
   1925       p+=GetPixelChannels(image);
   1926     }
   1927   }
   1928   image_view=DestroyCacheView(image_view);
   1929   carver=lqr_carver_new_ext(pixels,(int) image->columns,(int) image->rows,
   1930     (int) GetPixelChannels(image),LQR_COLDEPTH_32F);
   1931   if (carver == (LqrCarver *) NULL)
   1932     {
   1933       pixel_info=RelinquishVirtualMemory(pixel_info);
   1934       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
   1935     }
   1936   lqr_carver_set_preserve_input_image(carver);
   1937   lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
   1938   lqr_status=lqr_carver_resize(carver,(int) columns,(int) rows);
   1939   (void) lqr_status;
   1940   rescale_image=CloneImage(image,lqr_carver_get_width(carver),
   1941     lqr_carver_get_height(carver),MagickTrue,exception);
   1942   if (rescale_image == (Image *) NULL)
   1943     {
   1944       pixel_info=RelinquishVirtualMemory(pixel_info);
   1945       return((Image *) NULL);
   1946     }
   1947   if (SetImageStorageClass(rescale_image,DirectClass,exception) == MagickFalse)
   1948     {
   1949       pixel_info=RelinquishVirtualMemory(pixel_info);
   1950       rescale_image=DestroyImage(rescale_image);
   1951       return((Image *) NULL);
   1952     }
   1953   rescale_view=AcquireAuthenticCacheView(rescale_image,exception);
   1954   (void) lqr_carver_scan_reset(carver);
   1955   while (lqr_carver_scan_ext(carver,&x_offset,&y_offset,(void **) &packet) != 0)
   1956   {
   1957     register Quantum
   1958       *magick_restrict q;
   1959 
   1960     register ssize_t
   1961       i;
   1962 
   1963     q=QueueCacheViewAuthenticPixels(rescale_view,x_offset,y_offset,1,1,
   1964       exception);
   1965     if (q == (Quantum *) NULL)
   1966       break;
   1967     for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   1968     {
   1969       PixelChannel
   1970         channel;
   1971 
   1972       PixelTrait
   1973         rescale_traits,
   1974         traits;
   1975 
   1976       channel=GetPixelChannelChannel(image,i);
   1977       traits=GetPixelChannelTraits(image,channel);
   1978       rescale_traits=GetPixelChannelTraits(rescale_image,channel);
   1979       if ((traits == UndefinedPixelTrait) ||
   1980           (rescale_traits == UndefinedPixelTrait))
   1981         continue;
   1982       SetPixelChannel(rescale_image,channel,ClampToQuantum(QuantumRange*
   1983         packet[i]),q);
   1984     }
   1985     if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
   1986       break;
   1987   }
   1988   rescale_view=DestroyCacheView(rescale_view);
   1989   pixel_info=RelinquishVirtualMemory(pixel_info);
   1990   lqr_carver_destroy(carver);
   1991   return(rescale_image);
   1992 }
   1993 #else
   1994 MagickExport Image *LiquidRescaleImage(const Image *image,
   1995   const size_t magick_unused(columns),const size_t magick_unused(rows),
   1996   const double magick_unused(delta_x),const double magick_unused(rigidity),
   1997   ExceptionInfo *exception)
   1998 {
   1999   assert(image != (const Image *) NULL);
   2000   assert(image->signature == MagickCoreSignature);
   2001   if (image->debug != MagickFalse)
   2002     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   2003   assert(exception != (ExceptionInfo *) NULL);
   2004   assert(exception->signature == MagickCoreSignature);
   2005   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
   2006     "DelegateLibrarySupportNotBuiltIn","'%s' (LQR)",image->filename);
   2007   return((Image *) NULL);
   2008 }
   2009 #endif
   2010 
   2011 /*
   2013 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2014 %                                                                             %
   2015 %                                                                             %
   2016 %                                                                             %
   2017 %   M a g n i f y I m a g e                                                   %
   2018 %                                                                             %
   2019 %                                                                             %
   2020 %                                                                             %
   2021 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2022 %
   2023 %  MagnifyImage() doubles the size of the image with a pixel art scaling
   2024 %  algorithm.
   2025 %
   2026 %  The format of the MagnifyImage method is:
   2027 %
   2028 %      Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
   2029 %
   2030 %  A description of each parameter follows:
   2031 %
   2032 %    o image: the image.
   2033 %
   2034 %    o exception: return any errors or warnings in this structure.
   2035 %
   2036 */
   2037 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
   2038 {
   2039 #define MagnifyImageTag  "Magnify/Image"
   2040 
   2041   CacheView
   2042     *image_view,
   2043     *magnify_view;
   2044 
   2045   Image
   2046     *magnify_image;
   2047 
   2048   MagickBooleanType
   2049     status;
   2050 
   2051   MagickOffsetType
   2052     progress;
   2053 
   2054   ssize_t
   2055     y;
   2056 
   2057   /*
   2058     Initialize magnified image attributes.
   2059   */
   2060   assert(image != (const Image *) NULL);
   2061   assert(image->signature == MagickCoreSignature);
   2062   if (image->debug != MagickFalse)
   2063     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   2064   assert(exception != (ExceptionInfo *) NULL);
   2065   assert(exception->signature == MagickCoreSignature);
   2066   magnify_image=CloneImage(image,2*image->columns,2*image->rows,MagickTrue,
   2067     exception);
   2068   if (magnify_image == (Image *) NULL)
   2069     return((Image *) NULL);
   2070   /*
   2071     Magnify image.
   2072   */
   2073   status=MagickTrue;
   2074   progress=0;
   2075   image_view=AcquireVirtualCacheView(image,exception);
   2076   magnify_view=AcquireAuthenticCacheView(magnify_image,exception);
   2077 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2078   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   2079     magick_threads(image,magnify_image,image->rows,1)
   2080 #endif
   2081   for (y=0; y < (ssize_t) image->rows; y++)
   2082   {
   2083     register Quantum
   2084       *magick_restrict q;
   2085 
   2086     register ssize_t
   2087       x;
   2088 
   2089     if (status == MagickFalse)
   2090       continue;
   2091     q=QueueCacheViewAuthenticPixels(magnify_view,0,2*y,magnify_image->columns,2,
   2092       exception);
   2093     if (q == (Quantum *) NULL)
   2094       {
   2095         status=MagickFalse;
   2096         continue;
   2097       }
   2098     /*
   2099       Magnify this row of pixels.
   2100     */
   2101     for (x=0; x < (ssize_t) image->columns; x++)
   2102     {
   2103       MagickRealType
   2104         intensity[9];
   2105 
   2106       register const Quantum
   2107         *magick_restrict p;
   2108 
   2109       register Quantum
   2110         *magick_restrict r;
   2111 
   2112       register ssize_t
   2113         i;
   2114 
   2115       size_t
   2116         channels;
   2117 
   2118       p=GetCacheViewVirtualPixels(image_view,x-1,y-1,3,3,exception);
   2119       if (p == (const Quantum *) NULL)
   2120         {
   2121           status=MagickFalse;
   2122           continue;
   2123         }
   2124       channels=GetPixelChannels(image);
   2125       for (i=0; i < 9; i++)
   2126         intensity[i]=GetPixelIntensity(image,p+i*channels);
   2127       r=q;
   2128       if ((fabs(intensity[1]-intensity[7]) < MagickEpsilon) ||
   2129           (fabs(intensity[3]-intensity[5]) < MagickEpsilon))
   2130         {
   2131           /*
   2132             Clone center pixel.
   2133           */
   2134           for (i=0; i < (ssize_t) channels; i++)
   2135             r[i]=p[4*channels+i];
   2136           r+=GetPixelChannels(magnify_image);
   2137           for (i=0; i < (ssize_t) channels; i++)
   2138             r[i]=p[4*channels+i];
   2139           r+=GetPixelChannels(magnify_image)*(magnify_image->columns-1);
   2140           for (i=0; i < (ssize_t) channels; i++)
   2141             r[i]=p[4*channels+i];
   2142           r+=GetPixelChannels(magnify_image);
   2143           for (i=0; i < (ssize_t) channels; i++)
   2144             r[i]=p[4*channels+i];
   2145         }
   2146       else
   2147         {
   2148           /*
   2149             Selectively clone pixel.
   2150           */
   2151           if (fabs(intensity[1]-intensity[3]) < MagickEpsilon)
   2152             for (i=0; i < (ssize_t) channels; i++)
   2153               r[i]=p[3*channels+i];
   2154           else
   2155             for (i=0; i < (ssize_t) channels; i++)
   2156               r[i]=p[4*channels+i];
   2157           r+=GetPixelChannels(magnify_image);
   2158           if (fabs(intensity[1]-intensity[5]) < MagickEpsilon)
   2159             for (i=0; i < (ssize_t) channels; i++)
   2160               r[i]=p[5*channels+i];
   2161           else
   2162             for (i=0; i < (ssize_t) channels; i++)
   2163               r[i]=p[4*channels+i];
   2164           r+=GetPixelChannels(magnify_image)*(magnify_image->columns-1);
   2165           if (fabs(intensity[3]-intensity[7]) < MagickEpsilon)
   2166             for (i=0; i < (ssize_t) channels; i++)
   2167               r[i]=p[3*channels+i];
   2168           else
   2169             for (i=0; i < (ssize_t) channels; i++)
   2170               r[i]=p[4*channels+i];
   2171           r+=GetPixelChannels(magnify_image);
   2172           if (fabs(intensity[5]-intensity[7]) < MagickEpsilon)
   2173             for (i=0; i < (ssize_t) channels; i++)
   2174               r[i]=p[5*channels+i];
   2175           else
   2176             for (i=0; i < (ssize_t) channels; i++)
   2177               r[i]=p[4*channels+i];
   2178         }
   2179       q+=2*GetPixelChannels(magnify_image);
   2180     }
   2181     if (SyncCacheViewAuthenticPixels(magnify_view,exception) == MagickFalse)
   2182       status=MagickFalse;
   2183     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   2184       {
   2185         MagickBooleanType
   2186           proceed;
   2187 
   2188 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2189         #pragma omp critical (MagickCore_MagnifyImage)
   2190 #endif
   2191         proceed=SetImageProgress(image,MagnifyImageTag,progress++,image->rows);
   2192         if (proceed == MagickFalse)
   2193           status=MagickFalse;
   2194       }
   2195   }
   2196   magnify_view=DestroyCacheView(magnify_view);
   2197   image_view=DestroyCacheView(image_view);
   2198   if (status == MagickFalse)
   2199     magnify_image=DestroyImage(magnify_image);
   2200   return(magnify_image);
   2201 }
   2202 
   2203 /*
   2205 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2206 %                                                                             %
   2207 %                                                                             %
   2208 %                                                                             %
   2209 %   M i n i f y I m a g e                                                     %
   2210 %                                                                             %
   2211 %                                                                             %
   2212 %                                                                             %
   2213 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2214 %
   2215 %  MinifyImage() is a convenience method that scales an image proportionally to
   2216 %  half its size.
   2217 %
   2218 %  The format of the MinifyImage method is:
   2219 %
   2220 %      Image *MinifyImage(const Image *image,ExceptionInfo *exception)
   2221 %
   2222 %  A description of each parameter follows:
   2223 %
   2224 %    o image: the image.
   2225 %
   2226 %    o exception: return any errors or warnings in this structure.
   2227 %
   2228 */
   2229 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
   2230 {
   2231   Image
   2232     *minify_image;
   2233 
   2234   assert(image != (Image *) NULL);
   2235   assert(image->signature == MagickCoreSignature);
   2236   if (image->debug != MagickFalse)
   2237     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   2238   assert(exception != (ExceptionInfo *) NULL);
   2239   assert(exception->signature == MagickCoreSignature);
   2240   minify_image=ResizeImage(image,image->columns/2,image->rows/2,SplineFilter,
   2241     exception);
   2242   return(minify_image);
   2243 }
   2244 
   2245 /*
   2247 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2248 %                                                                             %
   2249 %                                                                             %
   2250 %                                                                             %
   2251 %   R e s a m p l e I m a g e                                                 %
   2252 %                                                                             %
   2253 %                                                                             %
   2254 %                                                                             %
   2255 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2256 %
   2257 %  ResampleImage() resize image in terms of its pixel size, so that when
   2258 %  displayed at the given resolution it will be the same size in terms of
   2259 %  real world units as the original image at the original resolution.
   2260 %
   2261 %  The format of the ResampleImage method is:
   2262 %
   2263 %      Image *ResampleImage(Image *image,const double x_resolution,
   2264 %        const double y_resolution,const FilterType filter,
   2265 %        ExceptionInfo *exception)
   2266 %
   2267 %  A description of each parameter follows:
   2268 %
   2269 %    o image: the image to be resized to fit the given resolution.
   2270 %
   2271 %    o x_resolution: the new image x resolution.
   2272 %
   2273 %    o y_resolution: the new image y resolution.
   2274 %
   2275 %    o filter: Image filter to use.
   2276 %
   2277 %    o exception: return any errors or warnings in this structure.
   2278 %
   2279 */
   2280 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
   2281   const double y_resolution,const FilterType filter,ExceptionInfo *exception)
   2282 {
   2283 #define ResampleImageTag  "Resample/Image"
   2284 
   2285   Image
   2286     *resample_image;
   2287 
   2288   size_t
   2289     height,
   2290     width;
   2291 
   2292   /*
   2293     Initialize sampled image attributes.
   2294   */
   2295   assert(image != (const Image *) NULL);
   2296   assert(image->signature == MagickCoreSignature);
   2297   if (image->debug != MagickFalse)
   2298     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   2299   assert(exception != (ExceptionInfo *) NULL);
   2300   assert(exception->signature == MagickCoreSignature);
   2301   width=(size_t) (x_resolution*image->columns/(image->resolution.x == 0.0 ?
   2302     72.0 : image->resolution.x)+0.5);
   2303   height=(size_t) (y_resolution*image->rows/(image->resolution.y == 0.0 ?
   2304     72.0 : image->resolution.y)+0.5);
   2305   resample_image=ResizeImage(image,width,height,filter,exception);
   2306   if (resample_image != (Image *) NULL)
   2307     {
   2308       resample_image->resolution.x=x_resolution;
   2309       resample_image->resolution.y=y_resolution;
   2310     }
   2311   return(resample_image);
   2312 }
   2313 
   2314 /*
   2316 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2317 %                                                                             %
   2318 %                                                                             %
   2319 %                                                                             %
   2320 %   R e s i z e I m a g e                                                     %
   2321 %                                                                             %
   2322 %                                                                             %
   2323 %                                                                             %
   2324 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2325 %
   2326 %  ResizeImage() scales an image to the desired dimensions, using the given
   2327 %  filter (see AcquireFilterInfo()).
   2328 %
   2329 %  If an undefined filter is given the filter defaults to Mitchell for a
   2330 %  colormapped image, a image with a matte channel, or if the image is
   2331 %  enlarged.  Otherwise the filter defaults to a Lanczos.
   2332 %
   2333 %  ResizeImage() was inspired by Paul Heckbert's "zoom" program.
   2334 %
   2335 %  The format of the ResizeImage method is:
   2336 %
   2337 %      Image *ResizeImage(Image *image,const size_t columns,const size_t rows,
   2338 %        const FilterType filter,ExceptionInfo *exception)
   2339 %
   2340 %  A description of each parameter follows:
   2341 %
   2342 %    o image: the image.
   2343 %
   2344 %    o columns: the number of columns in the scaled image.
   2345 %
   2346 %    o rows: the number of rows in the scaled image.
   2347 %
   2348 %    o filter: Image filter to use.
   2349 %
   2350 %    o exception: return any errors or warnings in this structure.
   2351 %
   2352 */
   2353 
   2354 typedef struct _ContributionInfo
   2355 {
   2356   double
   2357     weight;
   2358 
   2359   ssize_t
   2360     pixel;
   2361 } ContributionInfo;
   2362 
   2363 static ContributionInfo **DestroyContributionThreadSet(
   2364   ContributionInfo **contribution)
   2365 {
   2366   register ssize_t
   2367     i;
   2368 
   2369   assert(contribution != (ContributionInfo **) NULL);
   2370   for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
   2371     if (contribution[i] != (ContributionInfo *) NULL)
   2372       contribution[i]=(ContributionInfo *) RelinquishAlignedMemory(
   2373         contribution[i]);
   2374   contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
   2375   return(contribution);
   2376 }
   2377 
   2378 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
   2379 {
   2380   register ssize_t
   2381     i;
   2382 
   2383   ContributionInfo
   2384     **contribution;
   2385 
   2386   size_t
   2387     number_threads;
   2388 
   2389   number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
   2390   contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
   2391     sizeof(*contribution));
   2392   if (contribution == (ContributionInfo **) NULL)
   2393     return((ContributionInfo **) NULL);
   2394   (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
   2395   for (i=0; i < (ssize_t) number_threads; i++)
   2396   {
   2397     contribution[i]=(ContributionInfo *) MagickAssumeAligned(
   2398       AcquireAlignedMemory(count,sizeof(**contribution)));
   2399     if (contribution[i] == (ContributionInfo *) NULL)
   2400       return(DestroyContributionThreadSet(contribution));
   2401   }
   2402   return(contribution);
   2403 }
   2404 
   2405 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
   2406   const Image *image,Image *resize_image,const double x_factor,
   2407   const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
   2408 {
   2409 #define ResizeImageTag  "Resize/Image"
   2410 
   2411   CacheView
   2412     *image_view,
   2413     *resize_view;
   2414 
   2415   ClassType
   2416     storage_class;
   2417 
   2418   ContributionInfo
   2419     **magick_restrict contributions;
   2420 
   2421   MagickBooleanType
   2422     status;
   2423 
   2424   double
   2425     scale,
   2426     support;
   2427 
   2428   ssize_t
   2429     x;
   2430 
   2431   /*
   2432     Apply filter to resize horizontally from image to resize image.
   2433   */
   2434   scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
   2435   support=scale*GetResizeFilterSupport(resize_filter);
   2436   storage_class=support > 0.5 ? DirectClass : image->storage_class;
   2437   if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
   2438     return(MagickFalse);
   2439   if (support < 0.5)
   2440     {
   2441       /*
   2442         Support too small even for nearest neighbour: Reduce to point sampling.
   2443       */
   2444       support=(double) 0.5;
   2445       scale=1.0;
   2446     }
   2447   contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
   2448   if (contributions == (ContributionInfo **) NULL)
   2449     {
   2450       (void) ThrowMagickException(exception,GetMagickModule(),
   2451         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
   2452       return(MagickFalse);
   2453     }
   2454   status=MagickTrue;
   2455   scale=PerceptibleReciprocal(scale);
   2456   image_view=AcquireVirtualCacheView(image,exception);
   2457   resize_view=AcquireAuthenticCacheView(resize_image,exception);
   2458 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2459   #pragma omp parallel for schedule(static,4) shared(status) \
   2460     magick_threads(image,resize_image,resize_image->columns,1)
   2461 #endif
   2462   for (x=0; x < (ssize_t) resize_image->columns; x++)
   2463   {
   2464     const int
   2465       id = GetOpenMPThreadId();
   2466 
   2467     double
   2468       bisect,
   2469       density;
   2470 
   2471     register const Quantum
   2472       *magick_restrict p;
   2473 
   2474     register ContributionInfo
   2475       *magick_restrict contribution;
   2476 
   2477     register Quantum
   2478       *magick_restrict q;
   2479 
   2480     register ssize_t
   2481       y;
   2482 
   2483     ssize_t
   2484       n,
   2485       start,
   2486       stop;
   2487 
   2488     if (status == MagickFalse)
   2489       continue;
   2490     bisect=(double) (x+0.5)/x_factor+MagickEpsilon;
   2491     start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
   2492     stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->columns);
   2493     density=0.0;
   2494     contribution=contributions[id];
   2495     for (n=0; n < (stop-start); n++)
   2496     {
   2497       contribution[n].pixel=start+n;
   2498       contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
   2499         ((double) (start+n)-bisect+0.5));
   2500       density+=contribution[n].weight;
   2501     }
   2502     if (n == 0)
   2503       continue;
   2504     if ((density != 0.0) && (density != 1.0))
   2505       {
   2506         register ssize_t
   2507           i;
   2508 
   2509         /*
   2510           Normalize.
   2511         */
   2512         density=PerceptibleReciprocal(density);
   2513         for (i=0; i < n; i++)
   2514           contribution[i].weight*=density;
   2515       }
   2516     p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
   2517       (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
   2518     q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
   2519       exception);
   2520     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   2521       {
   2522         status=MagickFalse;
   2523         continue;
   2524       }
   2525     for (y=0; y < (ssize_t) resize_image->rows; y++)
   2526     {
   2527       register ssize_t
   2528         i;
   2529 
   2530       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   2531       {
   2532         double
   2533           alpha,
   2534           gamma,
   2535           pixel;
   2536 
   2537         PixelChannel
   2538           channel;
   2539 
   2540         PixelTrait
   2541           resize_traits,
   2542           traits;
   2543 
   2544         register ssize_t
   2545           j;
   2546 
   2547         ssize_t
   2548           k;
   2549 
   2550         channel=GetPixelChannelChannel(image,i);
   2551         traits=GetPixelChannelTraits(image,channel);
   2552         resize_traits=GetPixelChannelTraits(resize_image,channel);
   2553         if ((traits == UndefinedPixelTrait) ||
   2554             (resize_traits == UndefinedPixelTrait))
   2555           continue;
   2556         if (((resize_traits & CopyPixelTrait) != 0) ||
   2557             (GetPixelReadMask(resize_image,q) == 0))
   2558           {
   2559             j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
   2560               stop-1.0)+0.5);
   2561             k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
   2562               (contribution[j-start].pixel-contribution[0].pixel);
   2563             SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
   2564               q);
   2565             continue;
   2566           }
   2567         pixel=0.0;
   2568         if ((resize_traits & BlendPixelTrait) == 0)
   2569           {
   2570             /*
   2571               No alpha blending.
   2572             */
   2573             for (j=0; j < n; j++)
   2574             {
   2575               k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
   2576                 (contribution[j].pixel-contribution[0].pixel);
   2577               alpha=contribution[j].weight;
   2578               pixel+=alpha*p[k*GetPixelChannels(image)+i];
   2579             }
   2580             SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
   2581             continue;
   2582           }
   2583         /*
   2584           Alpha blending.
   2585         */
   2586         gamma=0.0;
   2587         for (j=0; j < n; j++)
   2588         {
   2589           k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
   2590             (contribution[j].pixel-contribution[0].pixel);
   2591           alpha=contribution[j].weight*QuantumScale*
   2592             GetPixelAlpha(image,p+k*GetPixelChannels(image));
   2593           pixel+=alpha*p[k*GetPixelChannels(image)+i];
   2594           gamma+=alpha;
   2595         }
   2596         gamma=PerceptibleReciprocal(gamma);
   2597         SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
   2598       }
   2599       q+=GetPixelChannels(resize_image);
   2600     }
   2601     if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
   2602       status=MagickFalse;
   2603     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   2604       {
   2605         MagickBooleanType
   2606           proceed;
   2607 
   2608 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2609         #pragma omp critical (MagickCore_HorizontalFilter)
   2610 #endif
   2611         proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
   2612         if (proceed == MagickFalse)
   2613           status=MagickFalse;
   2614       }
   2615   }
   2616   resize_view=DestroyCacheView(resize_view);
   2617   image_view=DestroyCacheView(image_view);
   2618   contributions=DestroyContributionThreadSet(contributions);
   2619   return(status);
   2620 }
   2621 
   2622 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
   2623   const Image *image,Image *resize_image,const double y_factor,
   2624   const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
   2625 {
   2626   CacheView
   2627     *image_view,
   2628     *resize_view;
   2629 
   2630   ClassType
   2631     storage_class;
   2632 
   2633   ContributionInfo
   2634     **magick_restrict contributions;
   2635 
   2636   double
   2637     scale,
   2638     support;
   2639 
   2640   MagickBooleanType
   2641     status;
   2642 
   2643   ssize_t
   2644     y;
   2645 
   2646   /*
   2647     Apply filter to resize vertically from image to resize image.
   2648   */
   2649   scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
   2650   support=scale*GetResizeFilterSupport(resize_filter);
   2651   storage_class=support > 0.5 ? DirectClass : image->storage_class;
   2652   if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
   2653     return(MagickFalse);
   2654   if (support < 0.5)
   2655     {
   2656       /*
   2657         Support too small even for nearest neighbour: Reduce to point sampling.
   2658       */
   2659       support=(double) 0.5;
   2660       scale=1.0;
   2661     }
   2662   contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
   2663   if (contributions == (ContributionInfo **) NULL)
   2664     {
   2665       (void) ThrowMagickException(exception,GetMagickModule(),
   2666         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
   2667       return(MagickFalse);
   2668     }
   2669   status=MagickTrue;
   2670   scale=PerceptibleReciprocal(scale);
   2671   image_view=AcquireVirtualCacheView(image,exception);
   2672   resize_view=AcquireAuthenticCacheView(resize_image,exception);
   2673 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2674   #pragma omp parallel for schedule(static,4) shared(status) \
   2675     magick_threads(image,resize_image,resize_image->rows,1)
   2676 #endif
   2677   for (y=0; y < (ssize_t) resize_image->rows; y++)
   2678   {
   2679     const int
   2680       id = GetOpenMPThreadId();
   2681 
   2682     double
   2683       bisect,
   2684       density;
   2685 
   2686     register const Quantum
   2687       *magick_restrict p;
   2688 
   2689     register ContributionInfo
   2690       *magick_restrict contribution;
   2691 
   2692     register Quantum
   2693       *magick_restrict q;
   2694 
   2695     register ssize_t
   2696       x;
   2697 
   2698     ssize_t
   2699       n,
   2700       start,
   2701       stop;
   2702 
   2703     if (status == MagickFalse)
   2704       continue;
   2705     bisect=(double) (y+0.5)/y_factor+MagickEpsilon;
   2706     start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
   2707     stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->rows);
   2708     density=0.0;
   2709     contribution=contributions[id];
   2710     for (n=0; n < (stop-start); n++)
   2711     {
   2712       contribution[n].pixel=start+n;
   2713       contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
   2714         ((double) (start+n)-bisect+0.5));
   2715       density+=contribution[n].weight;
   2716     }
   2717     if (n == 0)
   2718       continue;
   2719     if ((density != 0.0) && (density != 1.0))
   2720       {
   2721         register ssize_t
   2722           i;
   2723 
   2724         /*
   2725           Normalize.
   2726         */
   2727         density=PerceptibleReciprocal(density);
   2728         for (i=0; i < n; i++)
   2729           contribution[i].weight*=density;
   2730       }
   2731     p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
   2732       image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
   2733       exception);
   2734     q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
   2735       exception);
   2736     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   2737       {
   2738         status=MagickFalse;
   2739         continue;
   2740       }
   2741     for (x=0; x < (ssize_t) resize_image->columns; x++)
   2742     {
   2743       register ssize_t
   2744         i;
   2745 
   2746       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   2747       {
   2748         double
   2749           alpha,
   2750           gamma,
   2751           pixel;
   2752 
   2753         PixelChannel
   2754           channel;
   2755 
   2756         PixelTrait
   2757           resize_traits,
   2758           traits;
   2759 
   2760         register ssize_t
   2761           j;
   2762 
   2763         ssize_t
   2764           k;
   2765 
   2766         channel=GetPixelChannelChannel(image,i);
   2767         traits=GetPixelChannelTraits(image,channel);
   2768         resize_traits=GetPixelChannelTraits(resize_image,channel);
   2769         if ((traits == UndefinedPixelTrait) ||
   2770             (resize_traits == UndefinedPixelTrait))
   2771           continue;
   2772         if (((resize_traits & CopyPixelTrait) != 0) ||
   2773             (GetPixelReadMask(resize_image,q) == 0))
   2774           {
   2775             j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
   2776               stop-1.0)+0.5);
   2777             k=(ssize_t) ((contribution[j-start].pixel-contribution[0].pixel)*
   2778               image->columns+x);
   2779             SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
   2780               q);
   2781             continue;
   2782           }
   2783         pixel=0.0;
   2784         if ((resize_traits & BlendPixelTrait) == 0)
   2785           {
   2786             /*
   2787               No alpha blending.
   2788             */
   2789             for (j=0; j < n; j++)
   2790             {
   2791               k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
   2792                 image->columns+x);
   2793               alpha=contribution[j].weight;
   2794               pixel+=alpha*p[k*GetPixelChannels(image)+i];
   2795             }
   2796             SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
   2797             continue;
   2798           }
   2799         gamma=0.0;
   2800         for (j=0; j < n; j++)
   2801         {
   2802           k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
   2803             image->columns+x);
   2804           alpha=contribution[j].weight*QuantumScale*GetPixelAlpha(image,p+k*
   2805             GetPixelChannels(image));
   2806           pixel+=alpha*p[k*GetPixelChannels(image)+i];
   2807           gamma+=alpha;
   2808         }
   2809         gamma=PerceptibleReciprocal(gamma);
   2810         SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
   2811       }
   2812       q+=GetPixelChannels(resize_image);
   2813     }
   2814     if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
   2815       status=MagickFalse;
   2816     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   2817       {
   2818         MagickBooleanType
   2819           proceed;
   2820 
   2821 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2822         #pragma omp critical (MagickCore_VerticalFilter)
   2823 #endif
   2824         proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
   2825         if (proceed == MagickFalse)
   2826           status=MagickFalse;
   2827       }
   2828   }
   2829   resize_view=DestroyCacheView(resize_view);
   2830   image_view=DestroyCacheView(image_view);
   2831   contributions=DestroyContributionThreadSet(contributions);
   2832   return(status);
   2833 }
   2834 
   2835 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
   2836   const size_t rows,const FilterType filter,ExceptionInfo *exception)
   2837 {
   2838   double
   2839     x_factor,
   2840     y_factor;
   2841 
   2842   FilterType
   2843     filter_type;
   2844 
   2845   Image
   2846     *filter_image,
   2847     *resize_image;
   2848 
   2849   MagickOffsetType
   2850     offset;
   2851 
   2852   MagickSizeType
   2853     span;
   2854 
   2855   MagickStatusType
   2856     status;
   2857 
   2858   ResizeFilter
   2859     *resize_filter;
   2860 
   2861   /*
   2862     Acquire resize image.
   2863   */
   2864   assert(image != (Image *) NULL);
   2865   assert(image->signature == MagickCoreSignature);
   2866   if (image->debug != MagickFalse)
   2867     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   2868   assert(exception != (ExceptionInfo *) NULL);
   2869   assert(exception->signature == MagickCoreSignature);
   2870   if ((columns == 0) || (rows == 0))
   2871     ThrowImageException(ImageError,"NegativeOrZeroImageSize");
   2872   if ((columns == image->columns) && (rows == image->rows) &&
   2873       (filter == UndefinedFilter))
   2874     return(CloneImage(image,0,0,MagickTrue,exception));
   2875   /*
   2876     Acquire resize filter.
   2877   */
   2878   x_factor=(double) columns/(double) image->columns;
   2879   y_factor=(double) rows/(double) image->rows;
   2880   filter_type=LanczosFilter;
   2881   if (filter != UndefinedFilter)
   2882     filter_type=filter;
   2883   else
   2884     if ((x_factor == 1.0) && (y_factor == 1.0))
   2885       filter_type=PointFilter;
   2886     else
   2887       if ((image->storage_class == PseudoClass) ||
   2888           (image->alpha_trait != UndefinedPixelTrait) ||
   2889           ((x_factor*y_factor) > 1.0))
   2890         filter_type=MitchellFilter;
   2891   resize_filter=AcquireResizeFilter(image,filter_type,MagickFalse,exception);
   2892 #if defined(MAGICKCORE_OPENCL_SUPPORT)
   2893   resize_image=AccelerateResizeImage(image,columns,rows,resize_filter,
   2894     exception);
   2895   if (resize_image != (Image *) NULL)
   2896     {
   2897       resize_filter=DestroyResizeFilter(resize_filter);
   2898       return(resize_image);
   2899     }
   2900 #endif
   2901   resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
   2902   if (resize_image == (Image *) NULL)
   2903     {
   2904       resize_filter=DestroyResizeFilter(resize_filter);
   2905       return(resize_image);
   2906     }
   2907   if (x_factor > y_factor)
   2908     filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
   2909   else
   2910     filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
   2911   if (filter_image == (Image *) NULL)
   2912     {
   2913       resize_filter=DestroyResizeFilter(resize_filter);
   2914       return(DestroyImage(resize_image));
   2915     }
   2916   /*
   2917     Resize image.
   2918   */
   2919   offset=0;
   2920   if (x_factor > y_factor)
   2921     {
   2922       span=(MagickSizeType) (filter_image->columns+rows);
   2923       status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
   2924         &offset,exception);
   2925       status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
   2926         span,&offset,exception);
   2927     }
   2928   else
   2929     {
   2930       span=(MagickSizeType) (filter_image->rows+columns);
   2931       status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
   2932         &offset,exception);
   2933       status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
   2934         span,&offset,exception);
   2935     }
   2936   /*
   2937     Free resources.
   2938   */
   2939   filter_image=DestroyImage(filter_image);
   2940   resize_filter=DestroyResizeFilter(resize_filter);
   2941   if (status == MagickFalse)
   2942     {
   2943       resize_image=DestroyImage(resize_image);
   2944       return((Image *) NULL);
   2945     }
   2946   resize_image->type=image->type;
   2947   return(resize_image);
   2948 }
   2949 
   2950 /*
   2952 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2953 %                                                                             %
   2954 %                                                                             %
   2955 %                                                                             %
   2956 %   S a m p l e I m a g e                                                     %
   2957 %                                                                             %
   2958 %                                                                             %
   2959 %                                                                             %
   2960 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2961 %
   2962 %  SampleImage() scales an image to the desired dimensions with pixel
   2963 %  sampling.  Unlike other scaling methods, this method does not introduce
   2964 %  any additional color into the scaled image.
   2965 %
   2966 %  The format of the SampleImage method is:
   2967 %
   2968 %      Image *SampleImage(const Image *image,const size_t columns,
   2969 %        const size_t rows,ExceptionInfo *exception)
   2970 %
   2971 %  A description of each parameter follows:
   2972 %
   2973 %    o image: the image.
   2974 %
   2975 %    o columns: the number of columns in the sampled image.
   2976 %
   2977 %    o rows: the number of rows in the sampled image.
   2978 %
   2979 %    o exception: return any errors or warnings in this structure.
   2980 %
   2981 */
   2982 MagickExport Image *SampleImage(const Image *image,const size_t columns,
   2983   const size_t rows,ExceptionInfo *exception)
   2984 {
   2985 #define SampleImageTag  "Sample/Image"
   2986 
   2987   CacheView
   2988     *image_view,
   2989     *sample_view;
   2990 
   2991   Image
   2992     *sample_image;
   2993 
   2994   MagickBooleanType
   2995     status;
   2996 
   2997   MagickOffsetType
   2998     progress;
   2999 
   3000   register ssize_t
   3001     x;
   3002 
   3003   ssize_t
   3004     *x_offset,
   3005     y;
   3006 
   3007   PointInfo
   3008     sample_offset;
   3009 
   3010   /*
   3011     Initialize sampled image attributes.
   3012   */
   3013   assert(image != (const Image *) NULL);
   3014   assert(image->signature == MagickCoreSignature);
   3015   if (image->debug != MagickFalse)
   3016     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   3017   assert(exception != (ExceptionInfo *) NULL);
   3018   assert(exception->signature == MagickCoreSignature);
   3019   if ((columns == 0) || (rows == 0))
   3020     ThrowImageException(ImageError,"NegativeOrZeroImageSize");
   3021   if ((columns == image->columns) && (rows == image->rows))
   3022     return(CloneImage(image,0,0,MagickTrue,exception));
   3023   sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
   3024   if (sample_image == (Image *) NULL)
   3025     return((Image *) NULL);
   3026   /*
   3027     Set the sampling offset, default is in the mid-point of sample regions.
   3028   */
   3029   sample_offset.x=sample_offset.y=0.5-MagickEpsilon;
   3030   {
   3031     const char
   3032       *value;
   3033 
   3034     value=GetImageArtifact(image,"sample:offset");
   3035     if (value != (char *) NULL)
   3036       {
   3037         GeometryInfo
   3038           geometry_info;
   3039 
   3040         MagickStatusType
   3041           flags;
   3042 
   3043         (void) ParseGeometry(value,&geometry_info);
   3044         flags=ParseGeometry(value,&geometry_info);
   3045         sample_offset.x=sample_offset.y=geometry_info.rho/100.0-MagickEpsilon;
   3046         if ((flags & SigmaValue) != 0)
   3047           sample_offset.y=geometry_info.sigma/100.0-MagickEpsilon;
   3048       }
   3049   }
   3050   /*
   3051     Allocate scan line buffer and column offset buffers.
   3052   */
   3053   x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
   3054     sizeof(*x_offset));
   3055   if (x_offset == (ssize_t *) NULL)
   3056     {
   3057       sample_image=DestroyImage(sample_image);
   3058       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
   3059     }
   3060   for (x=0; x < (ssize_t) sample_image->columns; x++)
   3061     x_offset[x]=(ssize_t) ((((double) x+sample_offset.x)*image->columns)/
   3062       sample_image->columns);
   3063   /*
   3064     Sample each row.
   3065   */
   3066   status=MagickTrue;
   3067   progress=0;
   3068   image_view=AcquireVirtualCacheView(image,exception);
   3069   sample_view=AcquireAuthenticCacheView(sample_image,exception);
   3070 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   3071   #pragma omp parallel for schedule(static,4) shared(status) \
   3072     magick_threads(image,sample_image,1,1)
   3073 #endif
   3074   for (y=0; y < (ssize_t) sample_image->rows; y++)
   3075   {
   3076     register const Quantum
   3077       *magick_restrict p;
   3078 
   3079     register Quantum
   3080       *magick_restrict q;
   3081 
   3082     register ssize_t
   3083       x;
   3084 
   3085     ssize_t
   3086       y_offset;
   3087 
   3088     if (status == MagickFalse)
   3089       continue;
   3090     y_offset=(ssize_t) ((((double) y+sample_offset.y)*image->rows)/
   3091       sample_image->rows);
   3092     p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
   3093       exception);
   3094     q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
   3095       exception);
   3096     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   3097       {
   3098         status=MagickFalse;
   3099         continue;
   3100       }
   3101     /*
   3102       Sample each column.
   3103     */
   3104     for (x=0; x < (ssize_t) sample_image->columns; x++)
   3105     {
   3106       register ssize_t
   3107         i;
   3108 
   3109       if (GetPixelReadMask(sample_image,q) == 0)
   3110         {
   3111           q+=GetPixelChannels(sample_image);
   3112           continue;
   3113         }
   3114       for (i=0; i < (ssize_t) GetPixelChannels(sample_image); i++)
   3115       {
   3116         PixelChannel
   3117           channel;
   3118 
   3119         PixelTrait
   3120           sample_traits,
   3121           traits;
   3122 
   3123         channel=GetPixelChannelChannel(image,i);
   3124         traits=GetPixelChannelTraits(image,channel);
   3125         sample_traits=GetPixelChannelTraits(sample_image,channel);
   3126         if ((traits == UndefinedPixelTrait) ||
   3127             (sample_traits == UndefinedPixelTrait))
   3128           continue;
   3129         SetPixelChannel(sample_image,channel,p[x_offset[x]*GetPixelChannels(
   3130           image)+i],q);
   3131       }
   3132       q+=GetPixelChannels(sample_image);
   3133     }
   3134     if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
   3135       status=MagickFalse;
   3136     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   3137       {
   3138         MagickBooleanType
   3139           proceed;
   3140 
   3141 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   3142         #pragma omp critical (MagickCore_SampleImage)
   3143 #endif
   3144         proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
   3145         if (proceed == MagickFalse)
   3146           status=MagickFalse;
   3147       }
   3148   }
   3149   image_view=DestroyCacheView(image_view);
   3150   sample_view=DestroyCacheView(sample_view);
   3151   x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
   3152   sample_image->type=image->type;
   3153   if (status == MagickFalse)
   3154     sample_image=DestroyImage(sample_image);
   3155   return(sample_image);
   3156 }
   3157 
   3158 /*
   3160 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3161 %                                                                             %
   3162 %                                                                             %
   3163 %                                                                             %
   3164 %   S c a l e I m a g e                                                       %
   3165 %                                                                             %
   3166 %                                                                             %
   3167 %                                                                             %
   3168 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3169 %
   3170 %  ScaleImage() changes the size of an image to the given dimensions.
   3171 %
   3172 %  The format of the ScaleImage method is:
   3173 %
   3174 %      Image *ScaleImage(const Image *image,const size_t columns,
   3175 %        const size_t rows,ExceptionInfo *exception)
   3176 %
   3177 %  A description of each parameter follows:
   3178 %
   3179 %    o image: the image.
   3180 %
   3181 %    o columns: the number of columns in the scaled image.
   3182 %
   3183 %    o rows: the number of rows in the scaled image.
   3184 %
   3185 %    o exception: return any errors or warnings in this structure.
   3186 %
   3187 */
   3188 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
   3189   const size_t rows,ExceptionInfo *exception)
   3190 {
   3191 #define ScaleImageTag  "Scale/Image"
   3192 
   3193   CacheView
   3194     *image_view,
   3195     *scale_view;
   3196 
   3197   double
   3198     alpha,
   3199     pixel[CompositePixelChannel],
   3200     *scale_scanline,
   3201     *scanline,
   3202     *x_vector,
   3203     *y_vector;
   3204 
   3205   Image
   3206     *scale_image;
   3207 
   3208   MagickBooleanType
   3209     next_column,
   3210     next_row,
   3211     proceed,
   3212     status;
   3213 
   3214   PixelChannel
   3215     channel;
   3216 
   3217   PixelTrait
   3218     scale_traits,
   3219     traits;
   3220 
   3221   PointInfo
   3222     scale,
   3223     span;
   3224 
   3225   register ssize_t
   3226     i;
   3227 
   3228   ssize_t
   3229     n,
   3230     number_rows,
   3231     y;
   3232 
   3233   /*
   3234     Initialize scaled image attributes.
   3235   */
   3236   assert(image != (const Image *) NULL);
   3237   assert(image->signature == MagickCoreSignature);
   3238   if (image->debug != MagickFalse)
   3239     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   3240   assert(exception != (ExceptionInfo *) NULL);
   3241   assert(exception->signature == MagickCoreSignature);
   3242   if ((columns == 0) || (rows == 0))
   3243     ThrowImageException(ImageError,"NegativeOrZeroImageSize");
   3244   if ((columns == image->columns) && (rows == image->rows))
   3245     return(CloneImage(image,0,0,MagickTrue,exception));
   3246   scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
   3247   if (scale_image == (Image *) NULL)
   3248     return((Image *) NULL);
   3249   if (SetImageStorageClass(scale_image,DirectClass,exception) == MagickFalse)
   3250     {
   3251       scale_image=DestroyImage(scale_image);
   3252       return((Image *) NULL);
   3253     }
   3254   /*
   3255     Allocate memory.
   3256   */
   3257   x_vector=(double *) AcquireQuantumMemory((size_t) image->columns,
   3258     GetPixelChannels(image)*sizeof(*x_vector));
   3259   scanline=x_vector;
   3260   if (image->rows != scale_image->rows)
   3261     scanline=(double *) AcquireQuantumMemory((size_t) image->columns,
   3262       GetPixelChannels(image)*sizeof(*scanline));
   3263   scale_scanline=(double *) AcquireQuantumMemory((size_t)
   3264     scale_image->columns,GetPixelChannels(image)*sizeof(*scale_scanline));
   3265   y_vector=(double *) AcquireQuantumMemory((size_t) image->columns,
   3266     GetPixelChannels(image)*sizeof(*y_vector));
   3267   if ((scanline == (double *) NULL) ||
   3268       (scale_scanline == (double *) NULL) ||
   3269       (x_vector == (double *) NULL) ||
   3270       (y_vector == (double *) NULL))
   3271     {
   3272       scale_image=DestroyImage(scale_image);
   3273       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
   3274     }
   3275   /*
   3276     Scale image.
   3277   */
   3278   number_rows=0;
   3279   next_row=MagickTrue;
   3280   span.y=1.0;
   3281   scale.y=(double) scale_image->rows/(double) image->rows;
   3282   (void) ResetMagickMemory(y_vector,0,(size_t) GetPixelChannels(image)*
   3283     image->columns*sizeof(*y_vector));
   3284   n=0;
   3285   status=MagickTrue;
   3286   image_view=AcquireVirtualCacheView(image,exception);
   3287   scale_view=AcquireAuthenticCacheView(scale_image,exception);
   3288   for (y=0; y < (ssize_t) scale_image->rows; y++)
   3289   {
   3290     register const Quantum
   3291       *magick_restrict p;
   3292 
   3293     register Quantum
   3294       *magick_restrict q;
   3295 
   3296     register ssize_t
   3297       x;
   3298 
   3299     if (status == MagickFalse)
   3300       break;
   3301     q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
   3302       exception);
   3303     if (q == (Quantum *) NULL)
   3304       {
   3305         status=MagickFalse;
   3306         break;
   3307       }
   3308     alpha=1.0;
   3309     if (scale_image->rows == image->rows)
   3310       {
   3311         /*
   3312           Read a new scanline.
   3313         */
   3314         p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
   3315           exception);
   3316         if (p == (const Quantum *) NULL)
   3317           {
   3318             status=MagickFalse;
   3319             break;
   3320           }
   3321         for (x=0; x < (ssize_t) image->columns; x++)
   3322         {
   3323           if (GetPixelReadMask(image,p) == 0)
   3324             {
   3325               p+=GetPixelChannels(image);
   3326               continue;
   3327             }
   3328           if (image->alpha_trait != UndefinedPixelTrait)
   3329             alpha=QuantumScale*GetPixelAlpha(image,p);
   3330           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3331           {
   3332             PixelChannel channel=GetPixelChannelChannel(image,i);
   3333             PixelTrait traits=GetPixelChannelTraits(image,channel);
   3334             if ((traits & BlendPixelTrait) == 0)
   3335               {
   3336                 x_vector[x*GetPixelChannels(image)+i]=(double) p[i];
   3337                 continue;
   3338               }
   3339             x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
   3340           }
   3341           p+=GetPixelChannels(image);
   3342         }
   3343       }
   3344     else
   3345       {
   3346         /*
   3347           Scale Y direction.
   3348         */
   3349         while (scale.y < span.y)
   3350         {
   3351           if ((next_row != MagickFalse) &&
   3352               (number_rows < (ssize_t) image->rows))
   3353             {
   3354               /*
   3355                 Read a new scanline.
   3356               */
   3357               p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
   3358                 exception);
   3359               if (p == (const Quantum *) NULL)
   3360                 {
   3361                   status=MagickFalse;
   3362                   break;
   3363                 }
   3364               for (x=0; x < (ssize_t) image->columns; x++)
   3365               {
   3366                 if (GetPixelReadMask(image,p) == 0)
   3367                   {
   3368                     p+=GetPixelChannels(image);
   3369                     continue;
   3370                   }
   3371                 if (image->alpha_trait != UndefinedPixelTrait)
   3372                   alpha=QuantumScale*GetPixelAlpha(image,p);
   3373                 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3374                 {
   3375                   PixelChannel channel=GetPixelChannelChannel(image,i);
   3376                   PixelTrait traits=GetPixelChannelTraits(image,channel);
   3377                   if ((traits & BlendPixelTrait) == 0)
   3378                     {
   3379                       x_vector[x*GetPixelChannels(image)+i]=(double) p[i];
   3380                       continue;
   3381                     }
   3382                   x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
   3383                 }
   3384                 p+=GetPixelChannels(image);
   3385               }
   3386               number_rows++;
   3387             }
   3388           for (x=0; x < (ssize_t) image->columns; x++)
   3389             for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3390               y_vector[x*GetPixelChannels(image)+i]+=scale.y*
   3391                 x_vector[x*GetPixelChannels(image)+i];
   3392           span.y-=scale.y;
   3393           scale.y=(double) scale_image->rows/(double) image->rows;
   3394           next_row=MagickTrue;
   3395         }
   3396         if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
   3397           {
   3398             /*
   3399               Read a new scanline.
   3400             */
   3401             p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
   3402               exception);
   3403             if (p == (const Quantum *) NULL)
   3404               {
   3405                 status=MagickFalse;
   3406                 break;
   3407               }
   3408             for (x=0; x < (ssize_t) image->columns; x++)
   3409             {
   3410               if (GetPixelReadMask(image,p) == 0)
   3411                 {
   3412                   p+=GetPixelChannels(image);
   3413                   continue;
   3414                 }
   3415               if (image->alpha_trait != UndefinedPixelTrait)
   3416                 alpha=QuantumScale*GetPixelAlpha(image,p);
   3417               for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3418               {
   3419                 PixelChannel channel=GetPixelChannelChannel(image,i);
   3420                 PixelTrait traits=GetPixelChannelTraits(image,channel);
   3421                 if ((traits & BlendPixelTrait) == 0)
   3422                   {
   3423                     x_vector[x*GetPixelChannels(image)+i]=(double) p[i];
   3424                     continue;
   3425                   }
   3426                 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
   3427               }
   3428               p+=GetPixelChannels(image);
   3429             }
   3430             number_rows++;
   3431             next_row=MagickFalse;
   3432           }
   3433         for (x=0; x < (ssize_t) image->columns; x++)
   3434         {
   3435           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3436           {
   3437             pixel[i]=y_vector[x*GetPixelChannels(image)+i]+span.y*
   3438               x_vector[x*GetPixelChannels(image)+i];
   3439             scanline[x*GetPixelChannels(image)+i]=pixel[i];
   3440             y_vector[x*GetPixelChannels(image)+i]=0.0;
   3441           }
   3442         }
   3443         scale.y-=span.y;
   3444         if (scale.y <= 0)
   3445           {
   3446             scale.y=(double) scale_image->rows/(double) image->rows;
   3447             next_row=MagickTrue;
   3448           }
   3449         span.y=1.0;
   3450       }
   3451     if (scale_image->columns == image->columns)
   3452       {
   3453         /*
   3454           Transfer scanline to scaled image.
   3455         */
   3456         for (x=0; x < (ssize_t) scale_image->columns; x++)
   3457         {
   3458           if (GetPixelReadMask(scale_image,q) == 0)
   3459             {
   3460               q+=GetPixelChannels(scale_image);
   3461               continue;
   3462             }
   3463           if (image->alpha_trait != UndefinedPixelTrait)
   3464             {
   3465               alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
   3466                 GetPixelChannelOffset(image,AlphaPixelChannel)];
   3467               alpha=PerceptibleReciprocal(alpha);
   3468             }
   3469           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3470           {
   3471             channel=GetPixelChannelChannel(image,i);
   3472             traits=GetPixelChannelTraits(image,channel);
   3473             scale_traits=GetPixelChannelTraits(scale_image,channel);
   3474             if ((traits == UndefinedPixelTrait) ||
   3475                 (scale_traits == UndefinedPixelTrait))
   3476               continue;
   3477             if ((traits & BlendPixelTrait) == 0)
   3478               {
   3479                 SetPixelChannel(scale_image,channel,ClampToQuantum(
   3480                   scanline[x*GetPixelChannels(image)+i]),q);
   3481                 continue;
   3482               }
   3483             SetPixelChannel(scale_image,channel,ClampToQuantum(alpha*scanline[
   3484               x*GetPixelChannels(image)+i]),q);
   3485           }
   3486           q+=GetPixelChannels(scale_image);
   3487         }
   3488       }
   3489     else
   3490       {
   3491         ssize_t
   3492           t;
   3493 
   3494         /*
   3495           Scale X direction.
   3496         */
   3497         for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3498           pixel[i]=0.0;
   3499         next_column=MagickFalse;
   3500         span.x=1.0;
   3501         t=0;
   3502         for (x=0; x < (ssize_t) image->columns; x++)
   3503         {
   3504           scale.x=(double) scale_image->columns/(double) image->columns;
   3505           while (scale.x >= span.x)
   3506           {
   3507             if (next_column != MagickFalse)
   3508               {
   3509                 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3510                   pixel[i]=0.0;
   3511                 t++;
   3512               }
   3513             for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3514             {
   3515               PixelChannel channel=GetPixelChannelChannel(image,i);
   3516               PixelTrait traits=GetPixelChannelTraits(image,channel);
   3517               if (traits == UndefinedPixelTrait)
   3518                 continue;
   3519               pixel[i]+=span.x*scanline[x*GetPixelChannels(image)+i];
   3520               scale_scanline[t*GetPixelChannels(image)+i]=pixel[i];
   3521             }
   3522             scale.x-=span.x;
   3523             span.x=1.0;
   3524             next_column=MagickTrue;
   3525           }
   3526           if (scale.x > 0)
   3527             {
   3528               if (next_column != MagickFalse)
   3529                 {
   3530                   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3531                     pixel[i]=0.0;
   3532                   next_column=MagickFalse;
   3533                   t++;
   3534                 }
   3535               for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3536                 pixel[i]+=scale.x*scanline[x*GetPixelChannels(image)+i];
   3537               span.x-=scale.x;
   3538             }
   3539         }
   3540       if (span.x > 0)
   3541         {
   3542           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3543             pixel[i]+=span.x*scanline[(x-1)*GetPixelChannels(image)+i];
   3544         }
   3545       if ((next_column == MagickFalse) &&
   3546           (t < (ssize_t) scale_image->columns))
   3547         for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3548           scale_scanline[t*GetPixelChannels(image)+i]=pixel[i];
   3549       /*
   3550         Transfer scanline to scaled image.
   3551       */
   3552       for (x=0; x < (ssize_t) scale_image->columns; x++)
   3553       {
   3554         if (GetPixelReadMask(scale_image,q) == 0)
   3555           {
   3556             q+=GetPixelChannels(scale_image);
   3557             continue;
   3558           }
   3559         if (image->alpha_trait != UndefinedPixelTrait)
   3560           {
   3561             alpha=QuantumScale*scale_scanline[x*GetPixelChannels(image)+
   3562               GetPixelChannelOffset(image,AlphaPixelChannel)];
   3563             alpha=PerceptibleReciprocal(alpha);
   3564           }
   3565         for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3566         {
   3567           PixelChannel channel=GetPixelChannelChannel(image,i);
   3568           PixelTrait traits=GetPixelChannelTraits(image,channel);
   3569           scale_traits=GetPixelChannelTraits(scale_image,channel);
   3570           if ((traits == UndefinedPixelTrait) ||
   3571               (scale_traits == UndefinedPixelTrait))
   3572             continue;
   3573           if ((traits & BlendPixelTrait) == 0)
   3574             {
   3575               SetPixelChannel(scale_image,channel,ClampToQuantum(
   3576                 scale_scanline[x*GetPixelChannels(image)+i]),q);
   3577               continue;
   3578             }
   3579           SetPixelChannel(scale_image,channel,ClampToQuantum(alpha*
   3580             scale_scanline[x*GetPixelChannels(image)+i]),q);
   3581         }
   3582         q+=GetPixelChannels(scale_image);
   3583       }
   3584     }
   3585     if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
   3586       {
   3587         status=MagickFalse;
   3588         break;
   3589       }
   3590     proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
   3591       image->rows);
   3592     if (proceed == MagickFalse)
   3593       {
   3594         status=MagickFalse;
   3595         break;
   3596       }
   3597   }
   3598   scale_view=DestroyCacheView(scale_view);
   3599   image_view=DestroyCacheView(image_view);
   3600   /*
   3601     Free allocated memory.
   3602   */
   3603   y_vector=(double *) RelinquishMagickMemory(y_vector);
   3604   scale_scanline=(double *) RelinquishMagickMemory(scale_scanline);
   3605   if (scale_image->rows != image->rows)
   3606     scanline=(double *) RelinquishMagickMemory(scanline);
   3607   x_vector=(double *) RelinquishMagickMemory(x_vector);
   3608   scale_image->type=image->type;
   3609   if (status == MagickFalse)
   3610     scale_image=DestroyImage(scale_image);
   3611   return(scale_image);
   3612 }
   3613 
   3614 /*
   3616 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3617 %                                                                             %
   3618 %                                                                             %
   3619 %                                                                             %
   3620 %   T h u m b n a i l I m a g e                                               %
   3621 %                                                                             %
   3622 %                                                                             %
   3623 %                                                                             %
   3624 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3625 %
   3626 %  ThumbnailImage() changes the size of an image to the given dimensions and
   3627 %  removes any associated profiles.  The goal is to produce small low cost
   3628 %  thumbnail images suited for display on the Web.
   3629 %
   3630 %  The format of the ThumbnailImage method is:
   3631 %
   3632 %      Image *ThumbnailImage(const Image *image,const size_t columns,
   3633 %        const size_t rows,ExceptionInfo *exception)
   3634 %
   3635 %  A description of each parameter follows:
   3636 %
   3637 %    o image: the image.
   3638 %
   3639 %    o columns: the number of columns in the scaled image.
   3640 %
   3641 %    o rows: the number of rows in the scaled image.
   3642 %
   3643 %    o exception: return any errors or warnings in this structure.
   3644 %
   3645 */
   3646 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
   3647   const size_t rows,ExceptionInfo *exception)
   3648 {
   3649 #define SampleFactor  5
   3650 
   3651   char
   3652     value[MagickPathExtent];
   3653 
   3654   const char
   3655     *name;
   3656 
   3657   Image
   3658     *thumbnail_image;
   3659 
   3660   double
   3661     x_factor,
   3662     y_factor;
   3663 
   3664   size_t
   3665     version;
   3666 
   3667   struct stat
   3668     attributes;
   3669 
   3670   assert(image != (Image *) NULL);
   3671   assert(image->signature == MagickCoreSignature);
   3672   if (image->debug != MagickFalse)
   3673     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   3674   assert(exception != (ExceptionInfo *) NULL);
   3675   assert(exception->signature == MagickCoreSignature);
   3676   x_factor=(double) columns/(double) image->columns;
   3677   y_factor=(double) rows/(double) image->rows;
   3678   if ((x_factor*y_factor) > 0.1)
   3679     thumbnail_image=ResizeImage(image,columns,rows,image->filter,exception);
   3680   else
   3681     if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
   3682       thumbnail_image=ResizeImage(image,columns,rows,image->filter,exception);
   3683     else
   3684       {
   3685         Image
   3686           *sample_image;
   3687 
   3688         sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
   3689           exception);
   3690         if (sample_image == (Image *) NULL)
   3691           return((Image *) NULL);
   3692         thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
   3693           exception);
   3694         sample_image=DestroyImage(sample_image);
   3695       }
   3696   if (thumbnail_image == (Image *) NULL)
   3697     return(thumbnail_image);
   3698   (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
   3699   if (thumbnail_image->alpha_trait == UndefinedPixelTrait)
   3700     (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel,exception);
   3701   thumbnail_image->depth=8;
   3702   thumbnail_image->interlace=NoInterlace;
   3703   /*
   3704     Strip all profiles except color profiles.
   3705   */
   3706   ResetImageProfileIterator(thumbnail_image);
   3707   for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
   3708   {
   3709     if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
   3710      {
   3711        (void) DeleteImageProfile(thumbnail_image,name);
   3712        ResetImageProfileIterator(thumbnail_image);
   3713      }
   3714     name=GetNextImageProfile(thumbnail_image);
   3715   }
   3716   (void) DeleteImageProperty(thumbnail_image,"comment");
   3717   (void) CopyMagickString(value,image->magick_filename,MagickPathExtent);
   3718   if (strstr(image->magick_filename,"//") == (char *) NULL)
   3719     (void) FormatLocaleString(value,MagickPathExtent,"file://%s",
   3720       image->magick_filename);
   3721   (void) SetImageProperty(thumbnail_image,"Thumb::URI",value,exception);
   3722   (void) CopyMagickString(value,image->magick_filename,MagickPathExtent);
   3723   if ( GetPathAttributes(image->filename,&attributes) != MagickFalse )
   3724     {
   3725       (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   3726         attributes.st_mtime);
   3727       (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value,exception);
   3728     }
   3729   (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   3730     attributes.st_mtime);
   3731   (void) FormatMagickSize(GetBlobSize(image),MagickFalse,"B",MagickPathExtent,
   3732     value);
   3733   (void) SetImageProperty(thumbnail_image,"Thumb::Size",value,exception);
   3734   (void) FormatLocaleString(value,MagickPathExtent,"image/%s",image->magick);
   3735   LocaleLower(value);
   3736   (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value,exception);
   3737   (void) SetImageProperty(thumbnail_image,"software",GetMagickVersion(&version),
   3738     exception);
   3739   (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   3740     image->magick_columns);
   3741   (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value,
   3742     exception);
   3743   (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   3744     image->magick_rows);
   3745   (void) SetImageProperty(thumbnail_image,"Thumb::Image::Height",value,
   3746     exception);
   3747   (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
   3748     GetImageListLength(image));
   3749   (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value,
   3750     exception);
   3751   return(thumbnail_image);
   3752 }
   3753