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