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