source: ImageMagick/branches/ImageMagick-6/magick/resize.c @ 8157

Revision 8157, 120.4 KB checked in by anthony, 12 months ago (diff)

Rename of 'bicubic' interpolation to more precise 'catrom'.

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