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

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