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

Revision 7625, 117.3 KB checked in by anthony, 13 months ago (diff)

Fix Gaussian Filter which was causing problems with Variable Blur

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