source: ImageMagick/trunk/MagickCore/resize.c @ 7845

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