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