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