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

Revision 7591, 116.7 KB checked in by anthony, 13 months ago (diff)

Add RobidouxSharp? filter

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