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