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