• 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-2021 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(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   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   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*=PerceptibleReciprocal(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   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   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   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   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(x)-
1505     cos(x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin(x)+cos(x))));
1506   if (p < 0.0)
1507     q=(-q);
1508   return(q);
1509 }
1510 
1511 /*
1512 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1513 %                                                                             %
1514 %                                                                             %
1515 %                                                                             %
1516 +   D e s t r o y R e s i z e F i l t e r                                     %
1517 %                                                                             %
1518 %                                                                             %
1519 %                                                                             %
1520 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1521 %
1522 %  DestroyResizeFilter() destroy the resize filter.
1523 %
1524 %  The format of the DestroyResizeFilter method is:
1525 %
1526 %      ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1527 %
1528 %  A description of each parameter follows:
1529 %
1530 %    o resize_filter: the resize filter.
1531 %
1532 */
DestroyResizeFilter(ResizeFilter * resize_filter)1533 MagickPrivate ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1534 {
1535   assert(resize_filter != (ResizeFilter *) NULL);
1536   assert(resize_filter->signature == MagickCoreSignature);
1537   resize_filter->signature=(~MagickCoreSignature);
1538   resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1539   return(resize_filter);
1540 }
1541 
1542 /*
1543 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1544 %                                                                             %
1545 %                                                                             %
1546 %                                                                             %
1547 +   G e t R e s i z e F i l t e r S u p p o r t                               %
1548 %                                                                             %
1549 %                                                                             %
1550 %                                                                             %
1551 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1552 %
1553 %  GetResizeFilterSupport() return the current support window size for this
1554 %  filter.  Note that this may have been enlarged by filter:blur factor.
1555 %
1556 %  The format of the GetResizeFilterSupport method is:
1557 %
1558 %      double GetResizeFilterSupport(const ResizeFilter *resize_filter)
1559 %
1560 %  A description of each parameter follows:
1561 %
1562 %    o filter: Image filter to use.
1563 %
1564 */
1565 
GetResizeFilterCoefficient(const ResizeFilter * resize_filter)1566 MagickPrivate double *GetResizeFilterCoefficient(
1567   const ResizeFilter *resize_filter)
1568 {
1569   assert(resize_filter != (ResizeFilter *) NULL);
1570   assert(resize_filter->signature == MagickCoreSignature);
1571   return((double *) resize_filter->coefficient);
1572 }
1573 
GetResizeFilterBlur(const ResizeFilter * resize_filter)1574 MagickPrivate double GetResizeFilterBlur(const ResizeFilter *resize_filter)
1575 {
1576   assert(resize_filter != (ResizeFilter *) NULL);
1577   assert(resize_filter->signature == MagickCoreSignature);
1578   return(resize_filter->blur);
1579 }
1580 
GetResizeFilterScale(const ResizeFilter * resize_filter)1581 MagickPrivate double GetResizeFilterScale(const ResizeFilter *resize_filter)
1582 {
1583   assert(resize_filter != (ResizeFilter *) NULL);
1584   assert(resize_filter->signature == MagickCoreSignature);
1585   return(resize_filter->scale);
1586 }
1587 
GetResizeFilterWindowSupport(const ResizeFilter * resize_filter)1588 MagickPrivate double GetResizeFilterWindowSupport(
1589   const ResizeFilter *resize_filter)
1590 {
1591   assert(resize_filter != (ResizeFilter *) NULL);
1592   assert(resize_filter->signature == MagickCoreSignature);
1593   return(resize_filter->window_support);
1594 }
1595 
GetResizeFilterWeightingType(const ResizeFilter * resize_filter)1596 MagickPrivate ResizeWeightingFunctionType GetResizeFilterWeightingType(
1597   const ResizeFilter *resize_filter)
1598 {
1599   assert(resize_filter != (ResizeFilter *) NULL);
1600   assert(resize_filter->signature == MagickCoreSignature);
1601   return(resize_filter->filterWeightingType);
1602 }
1603 
GetResizeFilterWindowWeightingType(const ResizeFilter * resize_filter)1604 MagickPrivate ResizeWeightingFunctionType GetResizeFilterWindowWeightingType(
1605   const ResizeFilter *resize_filter)
1606 {
1607   assert(resize_filter != (ResizeFilter *) NULL);
1608   assert(resize_filter->signature == MagickCoreSignature);
1609   return(resize_filter->windowWeightingType);
1610 }
1611 
GetResizeFilterSupport(const ResizeFilter * resize_filter)1612 MagickPrivate double GetResizeFilterSupport(const ResizeFilter *resize_filter)
1613 {
1614   assert(resize_filter != (ResizeFilter *) NULL);
1615   assert(resize_filter->signature == MagickCoreSignature);
1616   return(resize_filter->support*resize_filter->blur);
1617 }
1618 
1619 /*
1620 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1621 %                                                                             %
1622 %                                                                             %
1623 %                                                                             %
1624 +   G e t R e s i z e F i l t e r W e i g h t                                 %
1625 %                                                                             %
1626 %                                                                             %
1627 %                                                                             %
1628 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1629 %
1630 %  GetResizeFilterWeight evaluates the specified resize filter at the point x
1631 %  which usally lies between zero and the filters current 'support' and
1632 %  returns the weight of the filter function at that point.
1633 %
1634 %  The format of the GetResizeFilterWeight method is:
1635 %
1636 %      double GetResizeFilterWeight(const ResizeFilter *resize_filter,
1637 %        const double x)
1638 %
1639 %  A description of each parameter follows:
1640 %
1641 %    o filter: the filter type.
1642 %
1643 %    o x: the point.
1644 %
1645 */
GetResizeFilterWeight(const ResizeFilter * resize_filter,const double x)1646 MagickPrivate double GetResizeFilterWeight(const ResizeFilter *resize_filter,
1647   const double x)
1648 {
1649   double
1650     scale,
1651     weight,
1652     x_blur;
1653 
1654   /*
1655     Windowing function - scale the weighting filter by this amount.
1656   */
1657   assert(resize_filter != (ResizeFilter *) NULL);
1658   assert(resize_filter->signature == MagickCoreSignature);
1659   x_blur=fabs((double) x)*PerceptibleReciprocal(resize_filter->blur);  /* X offset with blur scaling */
1660   if ((resize_filter->window_support < MagickEpsilon) ||
1661       (resize_filter->window == Box))
1662     scale=1.0;  /* Point or Box Filter -- avoid division by zero */
1663   else
1664     {
1665       scale=resize_filter->scale;
1666       scale=resize_filter->window(x_blur*scale,resize_filter);
1667     }
1668   weight=scale*resize_filter->filter(x_blur,resize_filter);
1669   return(weight);
1670 }
1671 
1672 /*
1673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1674 %                                                                             %
1675 %                                                                             %
1676 %                                                                             %
1677 %   I n t e r p o l a t i v e R e s i z e I m a g e                           %
1678 %                                                                             %
1679 %                                                                             %
1680 %                                                                             %
1681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1682 %
1683 %  InterpolativeResizeImage() resizes an image using the specified
1684 %  interpolation method.
1685 %
1686 %  The format of the InterpolativeResizeImage method is:
1687 %
1688 %      Image *InterpolativeResizeImage(const Image *image,const size_t columns,
1689 %        const size_t rows,const PixelInterpolateMethod method,
1690 %        ExceptionInfo *exception)
1691 %
1692 %  A description of each parameter follows:
1693 %
1694 %    o image: the image.
1695 %
1696 %    o columns: the number of columns in the resized image.
1697 %
1698 %    o rows: the number of rows in the resized image.
1699 %
1700 %    o method: the pixel interpolation method.
1701 %
1702 %    o exception: return any errors or warnings in this structure.
1703 %
1704 */
InterpolativeResizeImage(const Image * image,const size_t columns,const size_t rows,const PixelInterpolateMethod method,ExceptionInfo * exception)1705 MagickExport Image *InterpolativeResizeImage(const Image *image,
1706   const size_t columns,const size_t rows,const PixelInterpolateMethod method,
1707   ExceptionInfo *exception)
1708 {
1709 #define InterpolativeResizeImageTag  "Resize/Image"
1710 
1711   CacheView
1712     *image_view,
1713     *resize_view;
1714 
1715   Image
1716     *resize_image;
1717 
1718   MagickBooleanType
1719     status;
1720 
1721   MagickOffsetType
1722     progress;
1723 
1724   PointInfo
1725     scale;
1726 
1727   ssize_t
1728     y;
1729 
1730   /*
1731     Interpolatively resize image.
1732   */
1733   assert(image != (const Image *) NULL);
1734   assert(image->signature == MagickCoreSignature);
1735   if (image->debug != MagickFalse)
1736     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1737   assert(exception != (ExceptionInfo *) NULL);
1738   assert(exception->signature == MagickCoreSignature);
1739   if ((columns == 0) || (rows == 0))
1740     ThrowImageException(ImageError,"NegativeOrZeroImageSize");
1741   if ((columns == image->columns) && (rows == image->rows))
1742     return(CloneImage(image,0,0,MagickTrue,exception));
1743   resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1744   if (resize_image == (Image *) NULL)
1745     return((Image *) NULL);
1746   if (SetImageStorageClass(resize_image,DirectClass,exception) == MagickFalse)
1747     {
1748       resize_image=DestroyImage(resize_image);
1749       return((Image *) NULL);
1750     }
1751   status=MagickTrue;
1752   progress=0;
1753   image_view=AcquireVirtualCacheView(image,exception);
1754   resize_view=AcquireAuthenticCacheView(resize_image,exception);
1755   scale.x=(double) image->columns/resize_image->columns;
1756   scale.y=(double) image->rows/resize_image->rows;
1757 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1758   #pragma omp parallel for schedule(static) shared(progress,status) \
1759     magick_number_threads(image,resize_image,resize_image->rows,1)
1760 #endif
1761   for (y=0; y < (ssize_t) resize_image->rows; y++)
1762   {
1763     PointInfo
1764       offset;
1765 
1766     Quantum
1767       *magick_restrict q;
1768 
1769     ssize_t
1770       x;
1771 
1772     if (status == MagickFalse)
1773       continue;
1774     q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1775       exception);
1776     if (q == (Quantum *) NULL)
1777       continue;
1778     offset.y=((double) y+0.5)*scale.y-0.5;
1779     for (x=0; x < (ssize_t) resize_image->columns; x++)
1780     {
1781       ssize_t
1782         i;
1783 
1784       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1785       {
1786         PixelChannel
1787           channel;
1788 
1789         PixelTrait
1790           resize_traits,
1791           traits;
1792 
1793         channel=GetPixelChannelChannel(image,i);
1794         traits=GetPixelChannelTraits(image,channel);
1795         resize_traits=GetPixelChannelTraits(resize_image,channel);
1796         if ((traits == UndefinedPixelTrait) ||
1797             (resize_traits == UndefinedPixelTrait))
1798           continue;
1799         offset.x=((double) x+0.5)*scale.x-0.5;
1800         status=InterpolatePixelChannels(image,image_view,resize_image,method,
1801           offset.x,offset.y,q,exception);
1802         if (status == MagickFalse)
1803           break;
1804       }
1805       q+=GetPixelChannels(resize_image);
1806     }
1807     if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1808       status=MagickFalse;
1809     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1810       {
1811         MagickBooleanType
1812           proceed;
1813 
1814 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1815         #pragma omp atomic
1816 #endif
1817         progress++;
1818         proceed=SetImageProgress(image,InterpolativeResizeImageTag,progress,
1819           image->rows);
1820         if (proceed == MagickFalse)
1821           status=MagickFalse;
1822       }
1823   }
1824   resize_view=DestroyCacheView(resize_view);
1825   image_view=DestroyCacheView(image_view);
1826   if (status == MagickFalse)
1827     resize_image=DestroyImage(resize_image);
1828   return(resize_image);
1829 }
1830 #if defined(MAGICKCORE_LQR_DELEGATE)
1831 
1832 /*
1833 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1834 %                                                                             %
1835 %                                                                             %
1836 %                                                                             %
1837 %   L i q u i d R e s c a l e I m a g e                                       %
1838 %                                                                             %
1839 %                                                                             %
1840 %                                                                             %
1841 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1842 %
1843 %  LiquidRescaleImage() rescales image with seam carving.
1844 %
1845 %  The format of the LiquidRescaleImage method is:
1846 %
1847 %      Image *LiquidRescaleImage(const Image *image,const size_t columns,
1848 %        const size_t rows,const double delta_x,const double rigidity,
1849 %        ExceptionInfo *exception)
1850 %
1851 %  A description of each parameter follows:
1852 %
1853 %    o image: the image.
1854 %
1855 %    o columns: the number of columns in the rescaled image.
1856 %
1857 %    o rows: the number of rows in the rescaled image.
1858 %
1859 %    o delta_x: maximum seam transversal step (0 means straight seams).
1860 %
1861 %    o rigidity: introduce a bias for non-straight seams (typically 0).
1862 %
1863 %    o exception: return any errors or warnings in this structure.
1864 %
1865 */
LiquidRescaleImage(const Image * image,const size_t columns,const size_t rows,const double delta_x,const double rigidity,ExceptionInfo * exception)1866 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1867   const size_t rows,const double delta_x,const double rigidity,
1868   ExceptionInfo *exception)
1869 {
1870 #define LiquidRescaleImageTag  "Rescale/Image"
1871 
1872   CacheView
1873     *image_view,
1874     *rescale_view;
1875 
1876   gfloat
1877     *packet,
1878     *pixels;
1879 
1880   Image
1881     *rescale_image;
1882 
1883   int
1884     x_offset,
1885     y_offset;
1886 
1887   LqrCarver
1888     *carver;
1889 
1890   LqrRetVal
1891     lqr_status;
1892 
1893   MagickBooleanType
1894     status;
1895 
1896   MemoryInfo
1897     *pixel_info;
1898 
1899   gfloat
1900     *q;
1901 
1902   ssize_t
1903     y;
1904 
1905   /*
1906     Liquid rescale image.
1907   */
1908   assert(image != (const Image *) NULL);
1909   assert(image->signature == MagickCoreSignature);
1910   if (image->debug != MagickFalse)
1911     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1912   assert(exception != (ExceptionInfo *) NULL);
1913   assert(exception->signature == MagickCoreSignature);
1914   if ((columns == 0) || (rows == 0))
1915     ThrowImageException(ImageError,"NegativeOrZeroImageSize");
1916   if ((columns == image->columns) && (rows == image->rows))
1917     return(CloneImage(image,0,0,MagickTrue,exception));
1918   if ((columns <= 2) || (rows <= 2))
1919     return(ResizeImage(image,columns,rows,image->filter,exception));
1920   pixel_info=AcquireVirtualMemory(image->columns,image->rows*MaxPixelChannels*
1921     sizeof(*pixels));
1922   if (pixel_info == (MemoryInfo *) NULL)
1923     return((Image *) NULL);
1924   pixels=(gfloat *) GetVirtualMemoryBlob(pixel_info);
1925   status=MagickTrue;
1926   q=pixels;
1927   image_view=AcquireVirtualCacheView(image,exception);
1928   for (y=0; y < (ssize_t) image->rows; y++)
1929   {
1930     const Quantum
1931       *magick_restrict p;
1932 
1933     ssize_t
1934       x;
1935 
1936     if (status == MagickFalse)
1937       continue;
1938     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1939     if (p == (const Quantum *) NULL)
1940       {
1941         status=MagickFalse;
1942         continue;
1943       }
1944     for (x=0; x < (ssize_t) image->columns; x++)
1945     {
1946       ssize_t
1947         i;
1948 
1949       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1950         *q++=QuantumScale*p[i];
1951       p+=GetPixelChannels(image);
1952     }
1953   }
1954   image_view=DestroyCacheView(image_view);
1955   carver=lqr_carver_new_ext(pixels,(int) image->columns,(int) image->rows,
1956     (int) GetPixelChannels(image),LQR_COLDEPTH_32F);
1957   if (carver == (LqrCarver *) NULL)
1958     {
1959       pixel_info=RelinquishVirtualMemory(pixel_info);
1960       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1961     }
1962   lqr_carver_set_preserve_input_image(carver);
1963   lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1964   lqr_status=lqr_carver_resize(carver,(int) columns,(int) rows);
1965   (void) lqr_status;
1966   rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1967     lqr_carver_get_height(carver),MagickTrue,exception);
1968   if (rescale_image == (Image *) NULL)
1969     {
1970       pixel_info=RelinquishVirtualMemory(pixel_info);
1971       return((Image *) NULL);
1972     }
1973   if (SetImageStorageClass(rescale_image,DirectClass,exception) == MagickFalse)
1974     {
1975       pixel_info=RelinquishVirtualMemory(pixel_info);
1976       rescale_image=DestroyImage(rescale_image);
1977       return((Image *) NULL);
1978     }
1979   rescale_view=AcquireAuthenticCacheView(rescale_image,exception);
1980   (void) lqr_carver_scan_reset(carver);
1981   while (lqr_carver_scan_ext(carver,&x_offset,&y_offset,(void **) &packet) != 0)
1982   {
1983     Quantum
1984       *magick_restrict p;
1985 
1986     ssize_t
1987       i;
1988 
1989     p=QueueCacheViewAuthenticPixels(rescale_view,x_offset,y_offset,1,1,
1990       exception);
1991     if (p == (Quantum *) NULL)
1992       break;
1993     for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1994     {
1995       PixelChannel
1996         channel;
1997 
1998       PixelTrait
1999         rescale_traits,
2000         traits;
2001 
2002       channel=GetPixelChannelChannel(image,i);
2003       traits=GetPixelChannelTraits(image,channel);
2004       rescale_traits=GetPixelChannelTraits(rescale_image,channel);
2005       if ((traits == UndefinedPixelTrait) ||
2006           (rescale_traits == UndefinedPixelTrait))
2007         continue;
2008       SetPixelChannel(rescale_image,channel,ClampToQuantum(QuantumRange*
2009         packet[i]),p);
2010     }
2011     if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
2012       break;
2013   }
2014   rescale_view=DestroyCacheView(rescale_view);
2015   pixel_info=RelinquishVirtualMemory(pixel_info);
2016   lqr_carver_destroy(carver);
2017   return(rescale_image);
2018 }
2019 #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)2020 MagickExport Image *LiquidRescaleImage(const Image *image,
2021   const size_t magick_unused(columns),const size_t magick_unused(rows),
2022   const double magick_unused(delta_x),const double magick_unused(rigidity),
2023   ExceptionInfo *exception)
2024 {
2025   assert(image != (const Image *) NULL);
2026   assert(image->signature == MagickCoreSignature);
2027   if (image->debug != MagickFalse)
2028     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2029   assert(exception != (ExceptionInfo *) NULL);
2030   assert(exception->signature == MagickCoreSignature);
2031   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
2032     "DelegateLibrarySupportNotBuiltIn","'%s' (LQR)",image->filename);
2033   return((Image *) NULL);
2034 }
2035 #endif
2036 
2037 /*
2038 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2039 %                                                                             %
2040 %                                                                             %
2041 %                                                                             %
2042 %   M a g n i f y I m a g e                                                   %
2043 %                                                                             %
2044 %                                                                             %
2045 %                                                                             %
2046 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2047 %
2048 %  MagnifyImage() doubles the size of the image with a pixel art scaling
2049 %  algorithm.
2050 %
2051 %  The format of the MagnifyImage method is:
2052 %
2053 %      Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
2054 %
2055 %  A description of each parameter follows:
2056 %
2057 %    o image: the image.
2058 %
2059 %    o exception: return any errors or warnings in this structure.
2060 %
2061 */
2062 
CopyPixels(const Quantum * source,const ssize_t source_offset,Quantum * destination,const ssize_t destination_offset,const size_t channels)2063 static inline void CopyPixels(const Quantum *source,const ssize_t source_offset,
2064   Quantum *destination,const ssize_t destination_offset,const size_t channels)
2065 {
2066   ssize_t
2067     i;
2068 
2069   for (i=0; i < (ssize_t) channels; i++)
2070     destination[channels*destination_offset+i]=source[source_offset*channels+i];
2071 }
2072 
MixPixels(const Quantum * source,const ssize_t * source_offset,const size_t source_size,Quantum * destination,const ssize_t destination_offset,const size_t channels)2073 static inline void MixPixels(const Quantum *source,const ssize_t *source_offset,
2074   const size_t source_size,Quantum *destination,
2075   const ssize_t destination_offset,const size_t channels)
2076 {
2077   ssize_t
2078     sum;
2079 
2080   ssize_t
2081     i;
2082 
2083   for (i=0; i < (ssize_t) channels; i++)
2084   {
2085     ssize_t
2086       j;
2087 
2088     sum=0;
2089     for (j=0; j < (ssize_t) source_size; j++)
2090       sum+=source[source_offset[j]*channels+i];
2091     destination[channels*destination_offset+i]=(Quantum) (sum/source_size);
2092   }
2093 }
2094 
Mix2Pixels(const Quantum * source,const ssize_t source_offset1,const ssize_t source_offset2,Quantum * destination,const ssize_t destination_offset,const size_t channels)2095 static inline void Mix2Pixels(const Quantum *source,
2096   const ssize_t source_offset1,const ssize_t source_offset2,
2097   Quantum *destination,const ssize_t destination_offset,const size_t channels)
2098 {
2099   const ssize_t
2100     offsets[2] = { source_offset1, source_offset2 };
2101 
2102   MixPixels(source,offsets,2,destination,destination_offset,channels);
2103 }
2104 
PixelsEqual(const Quantum * source1,ssize_t offset1,const Quantum * source2,ssize_t offset2,const size_t channels)2105 static inline int PixelsEqual(const Quantum *source1,ssize_t offset1,
2106   const Quantum *source2,ssize_t offset2,const size_t channels)
2107 {
2108   ssize_t
2109     i;
2110 
2111   offset1*=channels;
2112   offset2*=channels;
2113   for (i=0; i < (ssize_t) channels; i++)
2114     if (source1[offset1+i] != source2[offset2+i])
2115       return(0);
2116   return(1);
2117 }
2118 
Eagle2X(const Image * source,const Quantum * pixels,Quantum * result,const size_t channels)2119 static inline void Eagle2X(const Image *source,const Quantum *pixels,
2120   Quantum *result,const size_t channels)
2121 {
2122   ssize_t
2123     i;
2124 
2125   (void) source;
2126   for (i=0; i < 4; i++)
2127     CopyPixels(pixels,4,result,i,channels);
2128   if (PixelsEqual(pixels,0,pixels,1,channels) &&
2129       PixelsEqual(pixels,1,pixels,3,channels))
2130     CopyPixels(pixels,0,result,0,channels);
2131   if (PixelsEqual(pixels,1,pixels,2,channels) &&
2132       PixelsEqual(pixels,2,pixels,5,channels))
2133     CopyPixels(pixels,2,result,1,channels);
2134   if (PixelsEqual(pixels,3,pixels,6,channels) &&
2135       PixelsEqual(pixels,6,pixels,7,channels))
2136     CopyPixels(pixels,6,result,2,channels);
2137   if (PixelsEqual(pixels,5,pixels,8,channels) &&
2138       PixelsEqual(pixels,8,pixels,7,channels))
2139     CopyPixels(pixels,8,result,3,channels);
2140 }
2141 
Hq2XHelper(const unsigned int rule,const Quantum * source,Quantum * destination,const ssize_t destination_offset,const size_t channels,const ssize_t e,const ssize_t a,const ssize_t b,const ssize_t d,const ssize_t f,const ssize_t h)2142 static void Hq2XHelper(const unsigned int rule,const Quantum *source,
2143   Quantum *destination,const ssize_t destination_offset,const size_t channels,
2144   const ssize_t e,const ssize_t a,const ssize_t b,const ssize_t d,
2145   const ssize_t f,const ssize_t h)
2146 {
2147 #define caseA(N,A,B,C,D) \
2148   case N: \
2149   { \
2150     const ssize_t \
2151       offsets[4] = { A, B, C, D }; \
2152  \
2153     MixPixels(source,offsets,4,destination,destination_offset,channels);\
2154     break; \
2155   }
2156 #define caseB(N,A,B,C,D,E,F,G,H) \
2157   case N: \
2158   { \
2159     const ssize_t \
2160       offsets[8] = { A, B, C, D, E, F, G, H }; \
2161  \
2162     MixPixels(source,offsets,8,destination,destination_offset,channels);\
2163     break; \
2164   }
2165 
2166   switch (rule)
2167   {
2168     case 0:
2169     {
2170       CopyPixels(source,e,destination,destination_offset,channels);
2171       break;
2172     }
2173     caseA(1,e,e,e,a)
2174     caseA(2,e,e,e,d)
2175     caseA(3,e,e,e,b)
2176     caseA(4,e,e,d,b)
2177     caseA(5,e,e,a,b)
2178     caseA(6,e,e,a,d)
2179     caseB(7,e,e,e,e,e,b,b,d)
2180     caseB(8,e,e,e,e,e,d,d,b)
2181     caseB(9,e,e,e,e,e,e,d,b)
2182     caseB(10,e,e,d,d,d,b,b,b)
2183     case 11:
2184     {
2185       const ssize_t
2186         offsets[16] = { e, e, e, e, e, e, e, e, e, e, e, e, e, e, d, b };
2187 
2188       MixPixels(source,offsets,16,destination,destination_offset,channels);
2189       break;
2190     }
2191     case 12:
2192     {
2193       if (PixelsEqual(source,b,source,d,channels))
2194         {
2195           const ssize_t
2196             offsets[4] = { e, e, d, b };
2197 
2198           MixPixels(source,offsets,4,destination,destination_offset,channels);
2199         }
2200       else
2201         CopyPixels(source,e,destination,destination_offset,channels);
2202       break;
2203     }
2204     case 13:
2205     {
2206       if (PixelsEqual(source,b,source,d,channels))
2207         {
2208           const ssize_t
2209             offsets[8] = { e, e, d, d, d, b, b, b };
2210 
2211           MixPixels(source,offsets,8,destination,destination_offset,channels);
2212         }
2213       else
2214         CopyPixels(source,e,destination,destination_offset,channels);
2215       break;
2216     }
2217     case 14:
2218     {
2219       if (PixelsEqual(source,b,source,d,channels))
2220         {
2221           const ssize_t
2222             offsets[16] = { e, e, e, e, e, e, e, e, e, e, e, e, e, e, d, b };
2223 
2224           MixPixels(source,offsets,16,destination,destination_offset,channels);
2225         }
2226       else
2227         CopyPixels(source,e,destination,destination_offset,channels);
2228       break;
2229     }
2230     case 15:
2231     {
2232       if (PixelsEqual(source,b,source,d,channels))
2233         {
2234           const ssize_t
2235             offsets[4] = { e, e, d, b };
2236 
2237           MixPixels(source,offsets,4,destination,destination_offset,channels);
2238         }
2239       else
2240         {
2241           const ssize_t
2242             offsets[4] = { e, e, e, a };
2243 
2244           MixPixels(source,offsets,4,destination,destination_offset,channels);
2245         }
2246       break;
2247     }
2248     case 16:
2249     {
2250       if (PixelsEqual(source,b,source,d,channels))
2251         {
2252           const ssize_t
2253             offsets[8] = { e, e, e, e, e, e, d, b };
2254 
2255           MixPixels(source,offsets,8,destination,destination_offset,channels);
2256         }
2257       else
2258         {
2259           const ssize_t
2260             offsets[4] = { e, e, e, a };
2261 
2262           MixPixels(source,offsets,4,destination,destination_offset,channels);
2263         }
2264       break;
2265     }
2266     case 17:
2267     {
2268       if (PixelsEqual(source,b,source,d,channels))
2269         {
2270           const ssize_t
2271             offsets[8] = { e, e, d, d, d, b, b, b };
2272 
2273           MixPixels(source,offsets,8,destination,destination_offset,channels);
2274         }
2275       else
2276         {
2277           const ssize_t
2278             offsets[4] = { e, e, e, a };
2279 
2280           MixPixels(source,offsets,4,destination,destination_offset,channels);
2281         }
2282       break;
2283     }
2284     case 18:
2285     {
2286       if (PixelsEqual(source,b,source,f,channels))
2287         {
2288           const ssize_t
2289             offsets[8] = { e, e, e, e, e, b, b, d };
2290 
2291           MixPixels(source,offsets,8,destination,destination_offset,channels);
2292         }
2293       else
2294         {
2295           const ssize_t
2296             offsets[4] = { e, e, e, d };
2297 
2298           MixPixels(source,offsets,4,destination,destination_offset,channels);
2299         }
2300       break;
2301     }
2302     default:
2303     {
2304       if (PixelsEqual(source,d,source,h,channels))
2305         {
2306           const ssize_t
2307             offsets[8] = { e, e, e, e, e, d, d, b };
2308 
2309           MixPixels(source,offsets,8,destination,destination_offset,channels);
2310         }
2311       else
2312         {
2313           const ssize_t
2314             offsets[4] = { e, e, e, b };
2315 
2316           MixPixels(source,offsets,4,destination,destination_offset,channels);
2317         }
2318       break;
2319     }
2320   }
2321   #undef caseA
2322   #undef caseB
2323 }
2324 
Hq2XPatternToNumber(const int * pattern)2325 static inline unsigned int Hq2XPatternToNumber(const int *pattern)
2326 {
2327   ssize_t
2328     i;
2329 
2330   unsigned int
2331     result,
2332     order;
2333 
2334   result=0;
2335   order=1;
2336   for (i=7; i >= 0; i--)
2337   {
2338     result+=order*pattern[i];
2339     order*=2;
2340   }
2341   return(result);
2342 }
2343 
Hq2X(const Image * source,const Quantum * pixels,Quantum * result,const size_t channels)2344 static inline void Hq2X(const Image *source,const Quantum *pixels,
2345   Quantum *result,const size_t channels)
2346 {
2347   static const unsigned int
2348     Hq2XTable[] =
2349     {
2350       4, 4, 6,  2, 4, 4, 6,  2, 5,  3, 15, 12, 5,  3, 17, 13,
2351       4, 4, 6, 18, 4, 4, 6, 18, 5,  3, 12, 12, 5,  3,  1, 12,
2352       4, 4, 6,  2, 4, 4, 6,  2, 5,  3, 17, 13, 5,  3, 16, 14,
2353       4, 4, 6, 18, 4, 4, 6, 18, 5,  3, 16, 12, 5,  3,  1, 14,
2354       4, 4, 6,  2, 4, 4, 6,  2, 5, 19, 12, 12, 5, 19, 16, 12,
2355       4, 4, 6,  2, 4, 4, 6,  2, 5,  3, 16, 12, 5,  3, 16, 12,
2356       4, 4, 6,  2, 4, 4, 6,  2, 5, 19,  1, 12, 5, 19,  1, 14,
2357       4, 4, 6,  2, 4, 4, 6, 18, 5,  3, 16, 12, 5, 19,  1, 14,
2358       4, 4, 6,  2, 4, 4, 6,  2, 5,  3, 15, 12, 5,  3, 17, 13,
2359       4, 4, 6,  2, 4, 4, 6,  2, 5,  3, 16, 12, 5,  3, 16, 12,
2360       4, 4, 6,  2, 4, 4, 6,  2, 5,  3, 17, 13, 5,  3, 16, 14,
2361       4, 4, 6,  2, 4, 4, 6,  2, 5,  3, 16, 13, 5,  3,  1, 14,
2362       4, 4, 6,  2, 4, 4, 6,  2, 5,  3, 16, 12, 5,  3, 16, 13,
2363       4, 4, 6,  2, 4, 4, 6,  2, 5,  3, 16, 12, 5,  3,  1, 12,
2364       4, 4, 6,  2, 4, 4, 6,  2, 5,  3, 16, 12, 5,  3,  1, 14,
2365       4, 4, 6,  2, 4, 4, 6,  2, 5,  3,  1, 12, 5,  3,  1, 14
2366     };
2367 
2368   const int
2369     pattern1[] =
2370     {
2371       !PixelsEqual(pixels,4,pixels,8,channels),
2372       !PixelsEqual(pixels,4,pixels,7,channels),
2373       !PixelsEqual(pixels,4,pixels,6,channels),
2374       !PixelsEqual(pixels,4,pixels,5,channels),
2375       !PixelsEqual(pixels,4,pixels,3,channels),
2376       !PixelsEqual(pixels,4,pixels,2,channels),
2377       !PixelsEqual(pixels,4,pixels,1,channels),
2378       !PixelsEqual(pixels,4,pixels,0,channels)
2379     };
2380 
2381 #define Rotated(p) p[2], p[4], p[7], p[1], p[6], p[0], p[3], p[5]
2382   const int pattern2[] = { Rotated(pattern1) };
2383   const int pattern3[] = { Rotated(pattern2) };
2384   const int pattern4[] = { Rotated(pattern3) };
2385 #undef Rotated
2386   (void) source;
2387   Hq2XHelper(Hq2XTable[Hq2XPatternToNumber(pattern1)],pixels,result,0,
2388     channels,4,0,1,3,5,7);
2389   Hq2XHelper(Hq2XTable[Hq2XPatternToNumber(pattern2)],pixels,result,1,
2390     channels,4,2,5,1,7,3);
2391   Hq2XHelper(Hq2XTable[Hq2XPatternToNumber(pattern3)],pixels,result,3,
2392     channels,4,8,7,5,3,1);
2393   Hq2XHelper(Hq2XTable[Hq2XPatternToNumber(pattern4)],pixels,result,2,
2394     channels,4,6,3,7,1,5);
2395 }
2396 
Fish2X(const Image * source,const Quantum * pixels,Quantum * result,const size_t channels)2397 static void Fish2X(const Image *source,const Quantum *pixels,Quantum *result,
2398   const size_t channels)
2399 {
2400 #define Corner(A,B,C,D) \
2401   { \
2402     if (intensities[B] > intensities[A]) \
2403       { \
2404         ssize_t    \
2405           offsets[3] = { B, C, D }; \
2406  \
2407         MixPixels(pixels,offsets,3,result,3,channels); \
2408       } \
2409     else \
2410       { \
2411         ssize_t    \
2412           offsets[3] = { A, B, C }; \
2413  \
2414         MixPixels(pixels,offsets,3,result,3,channels); \
2415       } \
2416   }
2417 
2418 #define Line(A,B,C,D) \
2419   { \
2420     if (intensities[C] > intensities[A]) \
2421       Mix2Pixels(pixels,C,D,result,3,channels); \
2422     else \
2423       Mix2Pixels(pixels,A,B,result,3,channels); \
2424   }
2425 
2426   MagickFloatType
2427     intensities[9];
2428 
2429   int
2430     ae,
2431     bd,
2432     ab,
2433     ad,
2434     be,
2435     de;
2436 
2437   ssize_t
2438     i;
2439 
2440   ssize_t
2441     offsets[4] = { 0, 1, 3, 4 };
2442 
2443   for (i=0; i < 9; i++)
2444     intensities[i]=GetPixelIntensity(source,pixels + i*channels);
2445   CopyPixels(pixels,0,result,0,channels);
2446   CopyPixels(pixels,(ssize_t) (intensities[0] > intensities[1] ? 0 : 1),result,
2447     1,channels);
2448   CopyPixels(pixels,(ssize_t) (intensities[0] > intensities[3] ? 0 : 3),result,
2449     2,channels);
2450   ae=PixelsEqual(pixels,0,pixels,4,channels);
2451   bd=PixelsEqual(pixels,1,pixels,3,channels);
2452   ab=PixelsEqual(pixels,0,pixels,1,channels);
2453   de=PixelsEqual(pixels,3,pixels,4,channels);
2454   ad=PixelsEqual(pixels,0,pixels,3,channels);
2455   be=PixelsEqual(pixels,1,pixels,4,channels);
2456   if (ae && bd && ab)
2457     {
2458       CopyPixels(pixels,0,result,3,channels);
2459       return;
2460     }
2461   if (ad && de && !ab)
2462     {
2463       Corner(1,0,4,3)
2464       return;
2465     }
2466   if (be && de && !ab)
2467     {
2468       Corner(0,1,3,4)
2469       return;
2470     }
2471   if (ad && ab && !be)
2472     {
2473       Corner(4,3,1,0)
2474       return;
2475     }
2476   if (ab && be && !ad)
2477     {
2478       Corner(3,0,4,1)
2479       return;
2480     }
2481   if (ae && (!bd || intensities[1] > intensities[0]))
2482     {
2483       Mix2Pixels(pixels,0,4,result,3,channels);
2484       return;
2485     }
2486   if (bd && (!ae || intensities[0] > intensities[1]))
2487     {
2488       Mix2Pixels(pixels,1,3,result,3,channels);
2489       return;
2490     }
2491   if (ab)
2492     {
2493       Line(0,1,3,4)
2494       return;
2495     }
2496   if (de)
2497     {
2498       Line(3,4,0,1)
2499       return;
2500     }
2501   if (ad)
2502     {
2503       Line(0,3,1,4)
2504       return;
2505     }
2506   if (be)
2507     {
2508       Line(1,4,0,3)
2509       return;
2510     }
2511   MixPixels(pixels,offsets,4,result,3,channels);
2512 #undef Corner
2513 #undef Line
2514 }
2515 
Xbr2X(const Image * source,const Quantum * pixels,Quantum * result,const size_t channels)2516 static void Xbr2X(const Image *source,const Quantum *pixels,Quantum *result,
2517   const size_t channels)
2518 {
2519 #define WeightVar(M,N) const int w_##M##_##N = \
2520   PixelsEqual(pixels,M,pixels,N,channels) ? 0 : 1;
2521 
2522   WeightVar(12,11)
2523   WeightVar(12,7)
2524   WeightVar(12,13)
2525   WeightVar(12,17)
2526   WeightVar(12,16)
2527   WeightVar(12,8)
2528   WeightVar(6,10)
2529   WeightVar(6,2)
2530   WeightVar(11,7)
2531   WeightVar(11,17)
2532   WeightVar(11,5)
2533   WeightVar(7,13)
2534   WeightVar(7,1)
2535   WeightVar(12,6)
2536   WeightVar(12,18)
2537   WeightVar(8,14)
2538   WeightVar(8,2)
2539   WeightVar(13,17)
2540   WeightVar(13,9)
2541   WeightVar(7,3)
2542   WeightVar(16,10)
2543   WeightVar(16,22)
2544   WeightVar(17,21)
2545   WeightVar(11,15)
2546   WeightVar(18,14)
2547   WeightVar(18,22)
2548   WeightVar(17,23)
2549   WeightVar(17,19)
2550 #undef WeightVar
2551 
2552   if (
2553     w_12_16 + w_12_8 + w_6_10 + w_6_2 + (4 * w_11_7) <
2554     w_11_17 + w_11_5 + w_7_13 + w_7_1 + (4 * w_12_6)
2555   )
2556     Mix2Pixels(pixels,(ssize_t) (w_12_11 <= w_12_7 ? 11 : 7),12,result,0,
2557       channels);
2558   else
2559     CopyPixels(pixels,12,result,0,channels);
2560   if (
2561     w_12_18 + w_12_6 + w_8_14 + w_8_2 + (4 * w_7_13) <
2562     w_13_17 + w_13_9 + w_11_7 + w_7_3 + (4 * w_12_8)
2563   )
2564     Mix2Pixels(pixels,(ssize_t) (w_12_7 <= w_12_13 ? 7 : 13),12,result,1,
2565       channels);
2566   else
2567     CopyPixels(pixels,12,result,1,channels);
2568   if (
2569     w_12_6 + w_12_18 + w_16_10 + w_16_22 + (4 * w_11_17) <
2570     w_11_7 + w_11_15 + w_13_17 + w_17_21 + (4 * w_12_16)
2571   )
2572     Mix2Pixels(pixels,(ssize_t) (w_12_11 <= w_12_17 ? 11 : 17),12,result,2,
2573       channels);
2574   else
2575     CopyPixels(pixels,12,result,2,channels);
2576   if (
2577     w_12_8 + w_12_16 + w_18_14 + w_18_22 + (4 * w_13_17) <
2578     w_11_17 + w_17_23 + w_17_19 + w_7_13 + (4 * w_12_18)
2579   )
2580     Mix2Pixels(pixels,(ssize_t) (w_12_13 <= w_12_17 ? 13 : 17),12,result,3,
2581       channels);
2582   else
2583     CopyPixels(pixels,12,result,3,channels);
2584 }
2585 
Scale2X(const Image * source,const Quantum * pixels,Quantum * result,const size_t channels)2586 static void Scale2X(const Image *source,const Quantum *pixels,Quantum *result,
2587   const size_t channels)
2588 {
2589   if (PixelsEqual(pixels,1,pixels,7,channels) ||
2590       PixelsEqual(pixels,3,pixels,5,channels))
2591     {
2592       ssize_t
2593         i;
2594 
2595       for (i=0; i < 4; i++)
2596         CopyPixels(pixels,4,result,i,channels);
2597       return;
2598     }
2599     if (PixelsEqual(pixels,1,pixels,3,channels))
2600       CopyPixels(pixels,3,result,0,channels);
2601     else
2602       CopyPixels(pixels,4,result,0,channels);
2603     if (PixelsEqual(pixels,1,pixels,5,channels))
2604       CopyPixels(pixels,5,result,1,channels);
2605     else
2606       CopyPixels(pixels,4,result,1,channels);
2607     if (PixelsEqual(pixels,3,pixels,7,channels))
2608       CopyPixels(pixels,3,result,2,channels);
2609     else
2610       CopyPixels(pixels,4,result,2,channels);
2611     if (PixelsEqual(pixels,5,pixels,7,channels))
2612       CopyPixels(pixels,5,result,3,channels);
2613     else
2614       CopyPixels(pixels,4,result,3,channels);
2615 }
2616 
Epbx2X(const Image * source,const Quantum * pixels,Quantum * result,const size_t channels)2617 static void Epbx2X(const Image *source,const Quantum *pixels,
2618   Quantum *result,const size_t channels)
2619 {
2620 #define HelperCond(a,b,c,d,e,f,g) ( \
2621   PixelsEqual(pixels,a,pixels,b,channels) && ( \
2622     PixelsEqual(pixels,c,pixels,d,channels) || \
2623     PixelsEqual(pixels,c,pixels,e,channels) || \
2624     PixelsEqual(pixels,a,pixels,f,channels) || \
2625     PixelsEqual(pixels,b,pixels,g,channels) \
2626     ) \
2627   )
2628 
2629   ssize_t
2630     i;
2631 
2632   for (i=0; i < 4; i++)
2633     CopyPixels(pixels,4,result,i,channels);
2634   if (
2635     !PixelsEqual(pixels,3,pixels,5,channels) &&
2636     !PixelsEqual(pixels,1,pixels,7,channels) &&
2637     (
2638       PixelsEqual(pixels,4,pixels,3,channels) ||
2639       PixelsEqual(pixels,4,pixels,7,channels) ||
2640       PixelsEqual(pixels,4,pixels,5,channels) ||
2641       PixelsEqual(pixels,4,pixels,1,channels) ||
2642       (
2643         (
2644           !PixelsEqual(pixels,0,pixels,8,channels) ||
2645            PixelsEqual(pixels,4,pixels,6,channels) ||
2646            PixelsEqual(pixels,3,pixels,2,channels)
2647         ) &&
2648         (
2649           !PixelsEqual(pixels,6,pixels,2,channels) ||
2650            PixelsEqual(pixels,4,pixels,0,channels) ||
2651            PixelsEqual(pixels,4,pixels,8,channels)
2652         )
2653       )
2654     )
2655   )
2656     {
2657       if (HelperCond(1,3,4,0,8,2,6))
2658         Mix2Pixels(pixels,1,3,result,0,channels);
2659       if (HelperCond(5,1,4,2,6,8,0))
2660         Mix2Pixels(pixels,5,1,result,1,channels);
2661       if (HelperCond(3,7,4,6,2,0,8))
2662         Mix2Pixels(pixels,3,7,result,2,channels);
2663       if (HelperCond(7,5,4,8,0,6,2))
2664         Mix2Pixels(pixels,7,5,result,3,channels);
2665     }
2666 
2667 #undef HelperCond
2668 }
2669 
Eagle3X(const Image * source,const Quantum * pixels,Quantum * result,const size_t channels)2670 static inline void Eagle3X(const Image *source,const Quantum *pixels,
2671   Quantum *result,const size_t channels)
2672 {
2673   ssize_t
2674     corner_tl,
2675     corner_tr,
2676     corner_bl,
2677     corner_br;
2678 
2679   corner_tl=PixelsEqual(pixels,0,pixels,1,channels) &&
2680     PixelsEqual(pixels,0,pixels,3,channels);
2681   corner_tr=PixelsEqual(pixels,1,pixels,2,channels) &&
2682     PixelsEqual(pixels,2,pixels,5,channels);
2683   corner_bl=PixelsEqual(pixels,3,pixels,6,channels) &&
2684     PixelsEqual(pixels,6,pixels,7,channels);
2685   corner_br=PixelsEqual(pixels,5,pixels,7,channels) &&
2686     PixelsEqual(pixels,7,pixels,8,channels);
2687   CopyPixels(pixels,(ssize_t) (corner_tl ? 0 : 4),result,0,channels);
2688   if (corner_tl && corner_tr)
2689     Mix2Pixels(pixels,0,2,result,1,channels);
2690   else
2691     CopyPixels(pixels,4,result,1,channels);
2692   CopyPixels(pixels,(ssize_t) (corner_tr ? 1 : 4),result,2,channels);
2693   if (corner_tl && corner_bl)
2694     Mix2Pixels(pixels,0,6,result,3,channels);
2695   else
2696     CopyPixels(pixels,4,result,3,channels);
2697   CopyPixels(pixels,4,result,4,channels);
2698   if (corner_tr && corner_br)
2699     Mix2Pixels(pixels,2,8,result,5,channels);
2700   else
2701     CopyPixels(pixels,4,result,5,channels);
2702   CopyPixels(pixels,(ssize_t) (corner_bl ? 3 : 4),result,6,channels);
2703   if (corner_bl && corner_br)
2704     Mix2Pixels(pixels,6,8,result,7,channels);
2705   else
2706     CopyPixels(pixels,4,result,7,channels);
2707   CopyPixels(pixels,(ssize_t) (corner_br ? 5 : 4),result,8,channels);
2708 }
2709 
Eagle3XB(const Image * source,const Quantum * pixels,Quantum * result,const size_t channels)2710 static inline void Eagle3XB(const Image *source,const Quantum *pixels,
2711   Quantum *result,const size_t channels)
2712 {
2713   ssize_t
2714     corner_tl,
2715     corner_tr,
2716     corner_bl,
2717     corner_br;
2718 
2719   corner_tl=PixelsEqual(pixels,0,pixels,1,channels) &&
2720     PixelsEqual(pixels,0,pixels,3,channels);
2721   corner_tr=PixelsEqual(pixels,1,pixels,2,channels) &&
2722     PixelsEqual(pixels,2,pixels,5,channels);
2723   corner_bl=PixelsEqual(pixels,3,pixels,6,channels) &&
2724     PixelsEqual(pixels,6,pixels,7,channels);
2725   corner_br=PixelsEqual(pixels,5,pixels,7,channels) &&
2726     PixelsEqual(pixels,7,pixels,8,channels);
2727   CopyPixels(pixels,(ssize_t) (corner_tl ? 0 : 4),result,0,channels);
2728   CopyPixels(pixels,4,result,1,channels);
2729   CopyPixels(pixels,(ssize_t) (corner_tr ? 1 : 4),result,2,channels);
2730   CopyPixels(pixels,4,result,3,channels);
2731   CopyPixels(pixels,4,result,4,channels);
2732   CopyPixels(pixels,4,result,5,channels);
2733   CopyPixels(pixels,(ssize_t) (corner_bl ? 3 : 4),result,6,channels);
2734   CopyPixels(pixels,4,result,7,channels);
2735   CopyPixels(pixels,(ssize_t) (corner_br ? 5 : 4),result,8,channels);
2736 }
2737 
Scale3X(const Image * source,const Quantum * pixels,Quantum * result,const size_t channels)2738 static inline void Scale3X(const Image *source,const Quantum *pixels,
2739   Quantum *result,const size_t channels)
2740 {
2741   if (!PixelsEqual(pixels,1,pixels,7,channels) &&
2742       !PixelsEqual(pixels,3,pixels,5,channels))
2743     {
2744       if (PixelsEqual(pixels,3,pixels,1,channels))
2745         CopyPixels(pixels,3,result,0,channels);
2746       else
2747         CopyPixels(pixels,4,result,0,channels);
2748 
2749       if (
2750         (
2751           PixelsEqual(pixels,3,pixels,1,channels) &&
2752           !PixelsEqual(pixels,4,pixels,2,channels)
2753         ) ||
2754         (
2755           PixelsEqual(pixels,5,pixels,1,channels) &&
2756           !PixelsEqual(pixels,4,pixels,0,channels)
2757         )
2758       )
2759         CopyPixels(pixels,1,result,1,channels);
2760       else
2761         CopyPixels(pixels,4,result,1,channels);
2762       if (PixelsEqual(pixels,5,pixels,1,channels))
2763         CopyPixels(pixels,5,result,2,channels);
2764       else
2765         CopyPixels(pixels,4,result,2,channels);
2766       if (
2767         (
2768           PixelsEqual(pixels,3,pixels,1,channels) &&
2769           !PixelsEqual(pixels,4,pixels,6,channels)
2770         ) ||
2771         (
2772           PixelsEqual(pixels,3,pixels,7,channels) &&
2773           !PixelsEqual(pixels,4,pixels,0,channels)
2774         )
2775       )
2776         CopyPixels(pixels,3,result,3,channels);
2777       else
2778         CopyPixels(pixels,4,result,3,channels);
2779       CopyPixels(pixels,4,result,4,channels);
2780       if (
2781         (
2782           PixelsEqual(pixels,5,pixels,1,channels) &&
2783           !PixelsEqual(pixels,4,pixels,8,channels)
2784         ) ||
2785         (
2786           PixelsEqual(pixels,5,pixels,7,channels) &&
2787           !PixelsEqual(pixels,4,pixels,2,channels)
2788         )
2789       )
2790         CopyPixels(pixels,5,result,5,channels);
2791       else
2792         CopyPixels(pixels,4,result,5,channels);
2793       if (PixelsEqual(pixels,3,pixels,7,channels))
2794         CopyPixels(pixels,3,result,6,channels);
2795       else
2796         CopyPixels(pixels,4,result,6,channels);
2797       if (
2798         (
2799           PixelsEqual(pixels,3,pixels,7,channels) &&
2800           !PixelsEqual(pixels,4,pixels,8,channels)
2801         ) ||
2802         (
2803           PixelsEqual(pixels,5,pixels,7,channels) &&
2804           !PixelsEqual(pixels,4,pixels,6,channels)
2805         )
2806       )
2807         CopyPixels(pixels,7,result,7,channels);
2808       else
2809         CopyPixels(pixels,4,result,7,channels);
2810       if (PixelsEqual(pixels,5,pixels,7,channels))
2811         CopyPixels(pixels,5,result,8,channels);
2812       else
2813         CopyPixels(pixels,4,result,8,channels);
2814     }
2815   else
2816     {
2817       ssize_t
2818         i;
2819 
2820       for (i=0; i < 9; i++)
2821         CopyPixels(pixels,4,result,i,channels);
2822     }
2823 }
2824 
MagnifyImage(const Image * image,ExceptionInfo * exception)2825 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
2826 {
2827 #define MagnifyImageTag  "Magnify/Image"
2828 
2829   CacheView
2830     *image_view,
2831     *magnify_view;
2832 
2833   const char
2834     *option;
2835 
2836   Image
2837     *source_image,
2838     *magnify_image;
2839 
2840   MagickBooleanType
2841     status;
2842 
2843   MagickOffsetType
2844     progress;
2845 
2846   OffsetInfo
2847     offset;
2848 
2849   RectangleInfo
2850     rectangle;
2851 
2852   ssize_t
2853     y;
2854 
2855   unsigned char
2856     magnification,
2857     width;
2858 
2859   void
2860     (*scaling_method)(const Image *,const Quantum *,Quantum *,size_t);
2861 
2862   /*
2863     Initialize magnified image attributes.
2864   */
2865   assert(image != (const Image *) NULL);
2866   assert(image->signature == MagickCoreSignature);
2867   if (image->debug != MagickFalse)
2868     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2869   assert(exception != (ExceptionInfo *) NULL);
2870   assert(exception->signature == MagickCoreSignature);
2871   option=GetImageOption(image->image_info,"magnify:method");
2872   if (option == (char *) NULL)
2873     option="scale2x";
2874   scaling_method=Scale2X;
2875   magnification=1;
2876   width=1;
2877   switch (*option)
2878   {
2879     case 'e':
2880     {
2881       if (LocaleCompare(option,"eagle2x") == 0)
2882         {
2883           scaling_method=Eagle2X;
2884           magnification=2;
2885           width=3;
2886           break;
2887         }
2888       if (LocaleCompare(option,"eagle3x") == 0)
2889         {
2890           scaling_method=Eagle3X;
2891           magnification=3;
2892           width=3;
2893           break;
2894         }
2895       if (LocaleCompare(option,"eagle3xb") == 0)
2896         {
2897           scaling_method=Eagle3XB;
2898           magnification=3;
2899           width=3;
2900           break;
2901         }
2902       if (LocaleCompare(option,"epbx2x") == 0)
2903         {
2904           scaling_method=Epbx2X;
2905           magnification=2;
2906           width=3;
2907           break;
2908         }
2909       break;
2910     }
2911     case 'f':
2912     {
2913       if (LocaleCompare(option,"fish2x") == 0)
2914         {
2915           scaling_method=Fish2X;
2916           magnification=2;
2917           width=3;
2918           break;
2919         }
2920       break;
2921     }
2922     case 'h':
2923     {
2924       if (LocaleCompare(option,"hq2x") == 0)
2925         {
2926           scaling_method=Hq2X;
2927           magnification=2;
2928           width=3;
2929           break;
2930         }
2931       break;
2932     }
2933     case 's':
2934     {
2935       if (LocaleCompare(option,"scale2x") == 0)
2936         {
2937           scaling_method=Scale2X;
2938           magnification=2;
2939           width=3;
2940           break;
2941         }
2942       if (LocaleCompare(option,"scale3x") == 0)
2943         {
2944           scaling_method=Scale3X;
2945           magnification=3;
2946           width=3;
2947           break;
2948         }
2949       break;
2950     }
2951     case 'x':
2952     {
2953       if (LocaleCompare(option,"xbr2x") == 0)
2954         {
2955           scaling_method=Xbr2X;
2956           magnification=2;
2957           width=5;
2958         }
2959       break;
2960     }
2961     default:
2962       break;
2963   }
2964   /*
2965     Make a working copy of the source image and convert it to RGB colorspace.
2966   */
2967   source_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2968     exception);
2969   if (source_image == (Image *) NULL)
2970     return((Image *) NULL);
2971   offset.x=0;
2972   offset.y=0;
2973   rectangle.x=0;
2974   rectangle.y=0;
2975   rectangle.width=image->columns;
2976   rectangle.height=image->rows;
2977   (void) CopyImagePixels(source_image,image,&rectangle,&offset,exception);
2978   (void) SetImageColorspace(source_image,RGBColorspace,exception);
2979   magnify_image=CloneImage(source_image,magnification*source_image->columns,
2980     magnification*source_image->rows,MagickTrue,exception);
2981   if (magnify_image == (Image *) NULL)
2982     {
2983       source_image=DestroyImage(source_image);
2984       return((Image *) NULL);
2985     }
2986   /*
2987     Magnify the image.
2988   */
2989   status=MagickTrue;
2990   progress=0;
2991   image_view=AcquireVirtualCacheView(source_image,exception);
2992   magnify_view=AcquireAuthenticCacheView(magnify_image,exception);
2993 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2994   #pragma omp parallel for schedule(static) shared(progress,status) \
2995     magick_number_threads(source_image,magnify_image,source_image->rows,1)
2996 #endif
2997   for (y=0; y < (ssize_t) source_image->rows; y++)
2998   {
2999     Quantum
3000       r[128]; /* to hold result pixels */
3001 
3002     Quantum
3003       *magick_restrict q;
3004 
3005     ssize_t
3006       x;
3007 
3008     if (status == MagickFalse)
3009       continue;
3010     q=QueueCacheViewAuthenticPixels(magnify_view,0,magnification*y,
3011       magnify_image->columns,magnification,exception);
3012     if (q == (Quantum *) NULL)
3013       {
3014         status=MagickFalse;
3015         continue;
3016       }
3017     /*
3018       Magnify this row of pixels.
3019     */
3020     for (x=0; x < (ssize_t) source_image->columns; x++)
3021     {
3022       const Quantum
3023         *magick_restrict p;
3024 
3025       size_t
3026         channels;
3027 
3028       ssize_t
3029         i;
3030 
3031       ssize_t
3032         j;
3033 
3034       p=GetCacheViewVirtualPixels(image_view,x-width/2,y-width/2,width,width,
3035         exception);
3036       channels=GetPixelChannels(source_image);
3037       scaling_method(source_image,p,r,channels);
3038       /*
3039         Copy the result pixels into the final image.
3040       */
3041       for (j=0; j < (ssize_t) magnification; j++)
3042         for (i=0; i < (ssize_t) (channels*magnification); i++)
3043           q[j*channels*magnify_image->columns+i]=r[j*magnification*channels+i];
3044       q+=magnification*GetPixelChannels(magnify_image);
3045     }
3046     if (SyncCacheViewAuthenticPixels(magnify_view,exception) == MagickFalse)
3047       status=MagickFalse;
3048     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3049       {
3050         MagickBooleanType
3051           proceed;
3052 
3053 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3054         #pragma omp atomic
3055 #endif
3056         progress++;
3057         proceed=SetImageProgress(image,MagnifyImageTag,progress,image->rows);
3058         if (proceed == MagickFalse)
3059           status=MagickFalse;
3060       }
3061   }
3062   magnify_view=DestroyCacheView(magnify_view);
3063   image_view=DestroyCacheView(image_view);
3064   source_image=DestroyImage(source_image);
3065   if (status == MagickFalse)
3066     magnify_image=DestroyImage(magnify_image);
3067   return(magnify_image);
3068 }
3069 
3070 /*
3071 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3072 %                                                                             %
3073 %                                                                             %
3074 %                                                                             %
3075 %   M i n i f y I m a g e                                                     %
3076 %                                                                             %
3077 %                                                                             %
3078 %                                                                             %
3079 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3080 %
3081 %  MinifyImage() is a convenience method that scales an image proportionally to
3082 %  half its size.
3083 %
3084 %  The format of the MinifyImage method is:
3085 %
3086 %      Image *MinifyImage(const Image *image,ExceptionInfo *exception)
3087 %
3088 %  A description of each parameter follows:
3089 %
3090 %    o image: the image.
3091 %
3092 %    o exception: return any errors or warnings in this structure.
3093 %
3094 */
MinifyImage(const Image * image,ExceptionInfo * exception)3095 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
3096 {
3097   Image
3098     *minify_image;
3099 
3100   assert(image != (Image *) NULL);
3101   assert(image->signature == MagickCoreSignature);
3102   if (image->debug != MagickFalse)
3103     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3104   assert(exception != (ExceptionInfo *) NULL);
3105   assert(exception->signature == MagickCoreSignature);
3106   minify_image=ResizeImage(image,image->columns/2,image->rows/2,SplineFilter,
3107     exception);
3108   return(minify_image);
3109 }
3110 
3111 /*
3112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3113 %                                                                             %
3114 %                                                                             %
3115 %                                                                             %
3116 %   R e s a m p l e I m a g e                                                 %
3117 %                                                                             %
3118 %                                                                             %
3119 %                                                                             %
3120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3121 %
3122 %  ResampleImage() resize image in terms of its pixel size, so that when
3123 %  displayed at the given resolution it will be the same size in terms of
3124 %  real world units as the original image at the original resolution.
3125 %
3126 %  The format of the ResampleImage method is:
3127 %
3128 %      Image *ResampleImage(Image *image,const double x_resolution,
3129 %        const double y_resolution,const FilterType filter,
3130 %        ExceptionInfo *exception)
3131 %
3132 %  A description of each parameter follows:
3133 %
3134 %    o image: the image to be resized to fit the given resolution.
3135 %
3136 %    o x_resolution: the new image x resolution.
3137 %
3138 %    o y_resolution: the new image y resolution.
3139 %
3140 %    o filter: Image filter to use.
3141 %
3142 %    o exception: return any errors or warnings in this structure.
3143 %
3144 */
ResampleImage(const Image * image,const double x_resolution,const double y_resolution,const FilterType filter,ExceptionInfo * exception)3145 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
3146   const double y_resolution,const FilterType filter,ExceptionInfo *exception)
3147 {
3148 #define ResampleImageTag  "Resample/Image"
3149 
3150   Image
3151     *resample_image;
3152 
3153   size_t
3154     height,
3155     width;
3156 
3157   /*
3158     Initialize sampled image attributes.
3159   */
3160   assert(image != (const Image *) NULL);
3161   assert(image->signature == MagickCoreSignature);
3162   if (image->debug != MagickFalse)
3163     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3164   assert(exception != (ExceptionInfo *) NULL);
3165   assert(exception->signature == MagickCoreSignature);
3166   width=(size_t) (x_resolution*image->columns/(image->resolution.x == 0.0 ?
3167     DefaultResolution : image->resolution.x)+0.5);
3168   height=(size_t) (y_resolution*image->rows/(image->resolution.y == 0.0 ?
3169     DefaultResolution : image->resolution.y)+0.5);
3170   resample_image=ResizeImage(image,width,height,filter,exception);
3171   if (resample_image != (Image *) NULL)
3172     {
3173       resample_image->resolution.x=x_resolution;
3174       resample_image->resolution.y=y_resolution;
3175     }
3176   return(resample_image);
3177 }
3178 
3179 /*
3180 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3181 %                                                                             %
3182 %                                                                             %
3183 %                                                                             %
3184 %   R e s i z e I m a g e                                                     %
3185 %                                                                             %
3186 %                                                                             %
3187 %                                                                             %
3188 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3189 %
3190 %  ResizeImage() scales an image to the desired dimensions, using the given
3191 %  filter (see AcquireFilterInfo()).
3192 %
3193 %  If an undefined filter is given the filter defaults to Mitchell for a
3194 %  colormapped image, a image with a matte channel, or if the image is
3195 %  enlarged.  Otherwise the filter defaults to a Lanczos.
3196 %
3197 %  ResizeImage() was inspired by Paul Heckbert's "zoom" program.
3198 %
3199 %  The format of the ResizeImage method is:
3200 %
3201 %      Image *ResizeImage(Image *image,const size_t columns,const size_t rows,
3202 %        const FilterType filter,ExceptionInfo *exception)
3203 %
3204 %  A description of each parameter follows:
3205 %
3206 %    o image: the image.
3207 %
3208 %    o columns: the number of columns in the scaled image.
3209 %
3210 %    o rows: the number of rows in the scaled image.
3211 %
3212 %    o filter: Image filter to use.
3213 %
3214 %    o exception: return any errors or warnings in this structure.
3215 %
3216 */
3217 
3218 typedef struct _ContributionInfo
3219 {
3220   double
3221     weight;
3222 
3223   ssize_t
3224     pixel;
3225 } ContributionInfo;
3226 
DestroyContributionThreadSet(ContributionInfo ** contribution)3227 static ContributionInfo **DestroyContributionThreadSet(
3228   ContributionInfo **contribution)
3229 {
3230   ssize_t
3231     i;
3232 
3233   assert(contribution != (ContributionInfo **) NULL);
3234   for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
3235     if (contribution[i] != (ContributionInfo *) NULL)
3236       contribution[i]=(ContributionInfo *) RelinquishAlignedMemory(
3237         contribution[i]);
3238   contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
3239   return(contribution);
3240 }
3241 
AcquireContributionThreadSet(const size_t count)3242 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
3243 {
3244   ssize_t
3245     i;
3246 
3247   ContributionInfo
3248     **contribution;
3249 
3250   size_t
3251     number_threads;
3252 
3253   number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
3254   contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
3255     sizeof(*contribution));
3256   if (contribution == (ContributionInfo **) NULL)
3257     return((ContributionInfo **) NULL);
3258   (void) memset(contribution,0,number_threads*sizeof(*contribution));
3259   for (i=0; i < (ssize_t) number_threads; i++)
3260   {
3261     contribution[i]=(ContributionInfo *) MagickAssumeAligned(
3262       AcquireAlignedMemory(count,sizeof(**contribution)));
3263     if (contribution[i] == (ContributionInfo *) NULL)
3264       return(DestroyContributionThreadSet(contribution));
3265   }
3266   return(contribution);
3267 }
3268 
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)3269 static MagickBooleanType HorizontalFilter(
3270   const ResizeFilter *magick_restrict resize_filter,
3271   const Image *magick_restrict image,Image *magick_restrict resize_image,
3272   const double x_factor,const MagickSizeType span,
3273   MagickOffsetType *magick_restrict progress,ExceptionInfo *exception)
3274 {
3275 #define ResizeImageTag  "Resize/Image"
3276 
3277   CacheView
3278     *image_view,
3279     *resize_view;
3280 
3281   ClassType
3282     storage_class;
3283 
3284   ContributionInfo
3285     **magick_restrict contributions;
3286 
3287   MagickBooleanType
3288     status;
3289 
3290   double
3291     scale,
3292     support;
3293 
3294   ssize_t
3295     x;
3296 
3297   /*
3298     Apply filter to resize horizontally from image to resize image.
3299   */
3300   scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
3301   support=scale*GetResizeFilterSupport(resize_filter);
3302   storage_class=support > 0.5 ? DirectClass : image->storage_class;
3303   if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
3304     return(MagickFalse);
3305   if (support < 0.5)
3306     {
3307       /*
3308         Support too small even for nearest neighbour: Reduce to point sampling.
3309       */
3310       support=(double) 0.5;
3311       scale=1.0;
3312     }
3313   contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
3314   if (contributions == (ContributionInfo **) NULL)
3315     {
3316       (void) ThrowMagickException(exception,GetMagickModule(),
3317         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
3318       return(MagickFalse);
3319     }
3320   status=MagickTrue;
3321   scale=PerceptibleReciprocal(scale);
3322   image_view=AcquireVirtualCacheView(image,exception);
3323   resize_view=AcquireAuthenticCacheView(resize_image,exception);
3324 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3325   #pragma omp parallel for schedule(static) shared(progress,status) \
3326     magick_number_threads(image,resize_image,resize_image->columns,1)
3327 #endif
3328   for (x=0; x < (ssize_t) resize_image->columns; x++)
3329   {
3330     const int
3331       id = GetOpenMPThreadId();
3332 
3333     double
3334       bisect,
3335       density;
3336 
3337     const Quantum
3338       *magick_restrict p;
3339 
3340     ContributionInfo
3341       *magick_restrict contribution;
3342 
3343     Quantum
3344       *magick_restrict q;
3345 
3346     ssize_t
3347       y;
3348 
3349     ssize_t
3350       n,
3351       start,
3352       stop;
3353 
3354     if (status == MagickFalse)
3355       continue;
3356     bisect=(double) (x+0.5)/x_factor+MagickEpsilon;
3357     start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
3358     stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->columns);
3359     density=0.0;
3360     contribution=contributions[id];
3361     for (n=0; n < (stop-start); n++)
3362     {
3363       contribution[n].pixel=start+n;
3364       contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
3365         ((double) (start+n)-bisect+0.5));
3366       density+=contribution[n].weight;
3367     }
3368     if (n == 0)
3369       continue;
3370     if ((density != 0.0) && (density != 1.0))
3371       {
3372         ssize_t
3373           i;
3374 
3375         /*
3376           Normalize.
3377         */
3378         density=PerceptibleReciprocal(density);
3379         for (i=0; i < n; i++)
3380           contribution[i].weight*=density;
3381       }
3382     p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
3383       (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
3384     q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
3385       exception);
3386     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3387       {
3388         status=MagickFalse;
3389         continue;
3390       }
3391     for (y=0; y < (ssize_t) resize_image->rows; y++)
3392     {
3393       ssize_t
3394         i;
3395 
3396       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3397       {
3398         double
3399           alpha,
3400           gamma,
3401           pixel;
3402 
3403         PixelChannel
3404           channel;
3405 
3406         PixelTrait
3407           resize_traits,
3408           traits;
3409 
3410         ssize_t
3411           j;
3412 
3413         ssize_t
3414           k;
3415 
3416         channel=GetPixelChannelChannel(image,i);
3417         traits=GetPixelChannelTraits(image,channel);
3418         resize_traits=GetPixelChannelTraits(resize_image,channel);
3419         if ((traits == UndefinedPixelTrait) ||
3420             (resize_traits == UndefinedPixelTrait))
3421           continue;
3422         if (((resize_traits & CopyPixelTrait) != 0) ||
3423             (GetPixelWriteMask(resize_image,q) <= (QuantumRange/2)))
3424           {
3425             j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
3426               stop-1.0)+0.5);
3427             k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
3428               (contribution[j-start].pixel-contribution[0].pixel);
3429             SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
3430               q);
3431             continue;
3432           }
3433         pixel=0.0;
3434         if ((resize_traits & BlendPixelTrait) == 0)
3435           {
3436             /*
3437               No alpha blending.
3438             */
3439             for (j=0; j < n; j++)
3440             {
3441               k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
3442                 (contribution[j].pixel-contribution[0].pixel);
3443               alpha=contribution[j].weight;
3444               pixel+=alpha*p[k*GetPixelChannels(image)+i];
3445             }
3446             SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
3447             continue;
3448           }
3449         /*
3450           Alpha blending.
3451         */
3452         gamma=0.0;
3453         for (j=0; j < n; j++)
3454         {
3455           k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
3456             (contribution[j].pixel-contribution[0].pixel);
3457           alpha=contribution[j].weight*QuantumScale*
3458             GetPixelAlpha(image,p+k*GetPixelChannels(image));
3459           pixel+=alpha*p[k*GetPixelChannels(image)+i];
3460           gamma+=alpha;
3461         }
3462         gamma=PerceptibleReciprocal(gamma);
3463         SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
3464       }
3465       q+=GetPixelChannels(resize_image);
3466     }
3467     if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
3468       status=MagickFalse;
3469     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3470       {
3471         MagickBooleanType
3472           proceed;
3473 
3474 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3475         #pragma omp atomic
3476 #endif
3477         (*progress)++;
3478         proceed=SetImageProgress(image,ResizeImageTag,*progress,span);
3479         if (proceed == MagickFalse)
3480           status=MagickFalse;
3481       }
3482   }
3483   resize_view=DestroyCacheView(resize_view);
3484   image_view=DestroyCacheView(image_view);
3485   contributions=DestroyContributionThreadSet(contributions);
3486   return(status);
3487 }
3488 
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)3489 static MagickBooleanType VerticalFilter(
3490   const ResizeFilter *magick_restrict resize_filter,
3491   const Image *magick_restrict image,Image *magick_restrict resize_image,
3492   const double y_factor,const MagickSizeType span,
3493   MagickOffsetType *magick_restrict progress,ExceptionInfo *exception)
3494 {
3495   CacheView
3496     *image_view,
3497     *resize_view;
3498 
3499   ClassType
3500     storage_class;
3501 
3502   ContributionInfo
3503     **magick_restrict contributions;
3504 
3505   double
3506     scale,
3507     support;
3508 
3509   MagickBooleanType
3510     status;
3511 
3512   ssize_t
3513     y;
3514 
3515   /*
3516     Apply filter to resize vertically from image to resize image.
3517   */
3518   scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
3519   support=scale*GetResizeFilterSupport(resize_filter);
3520   storage_class=support > 0.5 ? DirectClass : image->storage_class;
3521   if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
3522     return(MagickFalse);
3523   if (support < 0.5)
3524     {
3525       /*
3526         Support too small even for nearest neighbour: Reduce to point sampling.
3527       */
3528       support=(double) 0.5;
3529       scale=1.0;
3530     }
3531   contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
3532   if (contributions == (ContributionInfo **) NULL)
3533     {
3534       (void) ThrowMagickException(exception,GetMagickModule(),
3535         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
3536       return(MagickFalse);
3537     }
3538   status=MagickTrue;
3539   scale=PerceptibleReciprocal(scale);
3540   image_view=AcquireVirtualCacheView(image,exception);
3541   resize_view=AcquireAuthenticCacheView(resize_image,exception);
3542 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3543   #pragma omp parallel for schedule(static) shared(progress,status) \
3544     magick_number_threads(image,resize_image,resize_image->rows,1)
3545 #endif
3546   for (y=0; y < (ssize_t) resize_image->rows; y++)
3547   {
3548     const int
3549       id = GetOpenMPThreadId();
3550 
3551     double
3552       bisect,
3553       density;
3554 
3555     const Quantum
3556       *magick_restrict p;
3557 
3558     ContributionInfo
3559       *magick_restrict contribution;
3560 
3561     Quantum
3562       *magick_restrict q;
3563 
3564     ssize_t
3565       x;
3566 
3567     ssize_t
3568       n,
3569       start,
3570       stop;
3571 
3572     if (status == MagickFalse)
3573       continue;
3574     bisect=(double) (y+0.5)/y_factor+MagickEpsilon;
3575     start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
3576     stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->rows);
3577     density=0.0;
3578     contribution=contributions[id];
3579     for (n=0; n < (stop-start); n++)
3580     {
3581       contribution[n].pixel=start+n;
3582       contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
3583         ((double) (start+n)-bisect+0.5));
3584       density+=contribution[n].weight;
3585     }
3586     if (n == 0)
3587       continue;
3588     if ((density != 0.0) && (density != 1.0))
3589       {
3590         ssize_t
3591           i;
3592 
3593         /*
3594           Normalize.
3595         */
3596         density=PerceptibleReciprocal(density);
3597         for (i=0; i < n; i++)
3598           contribution[i].weight*=density;
3599       }
3600     p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
3601       image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
3602       exception);
3603     q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
3604       exception);
3605     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3606       {
3607         status=MagickFalse;
3608         continue;
3609       }
3610     for (x=0; x < (ssize_t) resize_image->columns; x++)
3611     {
3612       ssize_t
3613         i;
3614 
3615       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3616       {
3617         double
3618           alpha,
3619           gamma,
3620           pixel;
3621 
3622         PixelChannel
3623           channel;
3624 
3625         PixelTrait
3626           resize_traits,
3627           traits;
3628 
3629         ssize_t
3630           j;
3631 
3632         ssize_t
3633           k;
3634 
3635         channel=GetPixelChannelChannel(image,i);
3636         traits=GetPixelChannelTraits(image,channel);
3637         resize_traits=GetPixelChannelTraits(resize_image,channel);
3638         if ((traits == UndefinedPixelTrait) ||
3639             (resize_traits == UndefinedPixelTrait))
3640           continue;
3641         if (((resize_traits & CopyPixelTrait) != 0) ||
3642             (GetPixelWriteMask(resize_image,q) <= (QuantumRange/2)))
3643           {
3644             j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
3645               stop-1.0)+0.5);
3646             k=(ssize_t) ((contribution[j-start].pixel-contribution[0].pixel)*
3647               image->columns+x);
3648             SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
3649               q);
3650             continue;
3651           }
3652         pixel=0.0;
3653         if ((resize_traits & BlendPixelTrait) == 0)
3654           {
3655             /*
3656               No alpha blending.
3657             */
3658             for (j=0; j < n; j++)
3659             {
3660               k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
3661                 image->columns+x);
3662               alpha=contribution[j].weight;
3663               pixel+=alpha*p[k*GetPixelChannels(image)+i];
3664             }
3665             SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
3666             continue;
3667           }
3668         gamma=0.0;
3669         for (j=0; j < n; j++)
3670         {
3671           k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
3672             image->columns+x);
3673           alpha=contribution[j].weight*QuantumScale*GetPixelAlpha(image,p+k*
3674             GetPixelChannels(image));
3675           pixel+=alpha*p[k*GetPixelChannels(image)+i];
3676           gamma+=alpha;
3677         }
3678         gamma=PerceptibleReciprocal(gamma);
3679         SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
3680       }
3681       q+=GetPixelChannels(resize_image);
3682     }
3683     if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
3684       status=MagickFalse;
3685     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3686       {
3687         MagickBooleanType
3688           proceed;
3689 
3690 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3691         #pragma omp atomic
3692 #endif
3693         (*progress)++;
3694         proceed=SetImageProgress(image,ResizeImageTag,*progress,span);
3695         if (proceed == MagickFalse)
3696           status=MagickFalse;
3697       }
3698   }
3699   resize_view=DestroyCacheView(resize_view);
3700   image_view=DestroyCacheView(image_view);
3701   contributions=DestroyContributionThreadSet(contributions);
3702   return(status);
3703 }
3704 
ResizeImage(const Image * image,const size_t columns,const size_t rows,const FilterType filter,ExceptionInfo * exception)3705 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
3706   const size_t rows,const FilterType filter,ExceptionInfo *exception)
3707 {
3708   double
3709     x_factor,
3710     y_factor;
3711 
3712   FilterType
3713     filter_type;
3714 
3715   Image
3716     *filter_image,
3717     *resize_image;
3718 
3719   MagickOffsetType
3720     offset;
3721 
3722   MagickSizeType
3723     span;
3724 
3725   MagickStatusType
3726     status;
3727 
3728   ResizeFilter
3729     *resize_filter;
3730 
3731   /*
3732     Acquire resize image.
3733   */
3734   assert(image != (Image *) NULL);
3735   assert(image->signature == MagickCoreSignature);
3736   if (image->debug != MagickFalse)
3737     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3738   assert(exception != (ExceptionInfo *) NULL);
3739   assert(exception->signature == MagickCoreSignature);
3740   if ((columns == 0) || (rows == 0))
3741     ThrowImageException(ImageError,"NegativeOrZeroImageSize");
3742   if ((columns == image->columns) && (rows == image->rows) &&
3743       (filter == UndefinedFilter))
3744     return(CloneImage(image,0,0,MagickTrue,exception));
3745   /*
3746     Acquire resize filter.
3747   */
3748   x_factor=(double) columns/(double) image->columns;
3749   y_factor=(double) rows/(double) image->rows;
3750   filter_type=LanczosFilter;
3751   if (filter != UndefinedFilter)
3752     filter_type=filter;
3753   else
3754     if ((x_factor == 1.0) && (y_factor == 1.0))
3755       filter_type=PointFilter;
3756     else
3757       if ((image->storage_class == PseudoClass) ||
3758           (image->alpha_trait != UndefinedPixelTrait) ||
3759           ((x_factor*y_factor) > 1.0))
3760         filter_type=MitchellFilter;
3761   resize_filter=AcquireResizeFilter(image,filter_type,MagickFalse,exception);
3762 #if defined(MAGICKCORE_OPENCL_SUPPORT)
3763   resize_image=AccelerateResizeImage(image,columns,rows,resize_filter,
3764     exception);
3765   if (resize_image != (Image *) NULL)
3766     {
3767       resize_filter=DestroyResizeFilter(resize_filter);
3768       return(resize_image);
3769     }
3770 #endif
3771   resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
3772   if (resize_image == (Image *) NULL)
3773     {
3774       resize_filter=DestroyResizeFilter(resize_filter);
3775       return(resize_image);
3776     }
3777   if (x_factor > y_factor)
3778     filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
3779   else
3780     filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
3781   if (filter_image == (Image *) NULL)
3782     {
3783       resize_filter=DestroyResizeFilter(resize_filter);
3784       return(DestroyImage(resize_image));
3785     }
3786   /*
3787     Resize image.
3788   */
3789   offset=0;
3790   if (x_factor > y_factor)
3791     {
3792       span=(MagickSizeType) (filter_image->columns+rows);
3793       status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
3794         &offset,exception);
3795       status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
3796         span,&offset,exception);
3797     }
3798   else
3799     {
3800       span=(MagickSizeType) (filter_image->rows+columns);
3801       status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
3802         &offset,exception);
3803       status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
3804         span,&offset,exception);
3805     }
3806   /*
3807     Free resources.
3808   */
3809   filter_image=DestroyImage(filter_image);
3810   resize_filter=DestroyResizeFilter(resize_filter);
3811   if (status == MagickFalse)
3812     {
3813       resize_image=DestroyImage(resize_image);
3814       return((Image *) NULL);
3815     }
3816   resize_image->type=image->type;
3817   return(resize_image);
3818 }
3819 
3820 /*
3821 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3822 %                                                                             %
3823 %                                                                             %
3824 %                                                                             %
3825 %   S a m p l e I m a g e                                                     %
3826 %                                                                             %
3827 %                                                                             %
3828 %                                                                             %
3829 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3830 %
3831 %  SampleImage() scales an image to the desired dimensions with pixel
3832 %  sampling.  Unlike other scaling methods, this method does not introduce
3833 %  any additional color into the scaled image.
3834 %
3835 %  The format of the SampleImage method is:
3836 %
3837 %      Image *SampleImage(const Image *image,const size_t columns,
3838 %        const size_t rows,ExceptionInfo *exception)
3839 %
3840 %  A description of each parameter follows:
3841 %
3842 %    o image: the image.
3843 %
3844 %    o columns: the number of columns in the sampled image.
3845 %
3846 %    o rows: the number of rows in the sampled image.
3847 %
3848 %    o exception: return any errors or warnings in this structure.
3849 %
3850 */
SampleImage(const Image * image,const size_t columns,const size_t rows,ExceptionInfo * exception)3851 MagickExport Image *SampleImage(const Image *image,const size_t columns,
3852   const size_t rows,ExceptionInfo *exception)
3853 {
3854 #define SampleImageTag  "Sample/Image"
3855 
3856   CacheView
3857     *image_view,
3858     *sample_view;
3859 
3860   Image
3861     *sample_image;
3862 
3863   MagickBooleanType
3864     status;
3865 
3866   MagickOffsetType
3867     progress;
3868 
3869   ssize_t
3870     x1;
3871 
3872   ssize_t
3873     *x_offset,
3874     y;
3875 
3876   PointInfo
3877     sample_offset;
3878 
3879   /*
3880     Initialize sampled image attributes.
3881   */
3882   assert(image != (const Image *) NULL);
3883   assert(image->signature == MagickCoreSignature);
3884   if (image->debug != MagickFalse)
3885     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3886   assert(exception != (ExceptionInfo *) NULL);
3887   assert(exception->signature == MagickCoreSignature);
3888   if ((columns == 0) || (rows == 0))
3889     ThrowImageException(ImageError,"NegativeOrZeroImageSize");
3890   if ((columns == image->columns) && (rows == image->rows))
3891     return(CloneImage(image,0,0,MagickTrue,exception));
3892   sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
3893   if (sample_image == (Image *) NULL)
3894     return((Image *) NULL);
3895   /*
3896     Set the sampling offset, default is in the mid-point of sample regions.
3897   */
3898   sample_offset.x=sample_offset.y=0.5-MagickEpsilon;
3899   {
3900     const char
3901       *value;
3902 
3903     value=GetImageArtifact(image,"sample:offset");
3904     if (value != (char *) NULL)
3905       {
3906         GeometryInfo
3907           geometry_info;
3908 
3909         MagickStatusType
3910           flags;
3911 
3912         (void) ParseGeometry(value,&geometry_info);
3913         flags=ParseGeometry(value,&geometry_info);
3914         sample_offset.x=sample_offset.y=geometry_info.rho/100.0-MagickEpsilon;
3915         if ((flags & SigmaValue) != 0)
3916           sample_offset.y=geometry_info.sigma/100.0-MagickEpsilon;
3917       }
3918   }
3919   /*
3920     Allocate scan line buffer and column offset buffers.
3921   */
3922   x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
3923     sizeof(*x_offset));
3924   if (x_offset == (ssize_t *) NULL)
3925     {
3926       sample_image=DestroyImage(sample_image);
3927       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3928     }
3929   for (x1=0; x1 < (ssize_t) sample_image->columns; x1++)
3930     x_offset[x1]=(ssize_t) ((((double) x1+sample_offset.x)*image->columns)/
3931       sample_image->columns);
3932   /*
3933     Sample each row.
3934   */
3935   status=MagickTrue;
3936   progress=0;
3937   image_view=AcquireVirtualCacheView(image,exception);
3938   sample_view=AcquireAuthenticCacheView(sample_image,exception);
3939 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3940   #pragma omp parallel for schedule(static) shared(status) \
3941     magick_number_threads(image,sample_image,sample_image->rows,1)
3942 #endif
3943   for (y=0; y < (ssize_t) sample_image->rows; y++)
3944   {
3945     const Quantum
3946       *magick_restrict p;
3947 
3948     Quantum
3949       *magick_restrict q;
3950 
3951     ssize_t
3952       x;
3953 
3954     ssize_t
3955       y_offset;
3956 
3957     if (status == MagickFalse)
3958       continue;
3959     y_offset=(ssize_t) ((((double) y+sample_offset.y)*image->rows)/
3960       sample_image->rows);
3961     p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
3962       exception);
3963     q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
3964       exception);
3965     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3966       {
3967         status=MagickFalse;
3968         continue;
3969       }
3970     /*
3971       Sample each column.
3972     */
3973     for (x=0; x < (ssize_t) sample_image->columns; x++)
3974     {
3975       ssize_t
3976         i;
3977 
3978       if (GetPixelWriteMask(sample_image,q) <= (QuantumRange/2))
3979         {
3980           q+=GetPixelChannels(sample_image);
3981           continue;
3982         }
3983       for (i=0; i < (ssize_t) GetPixelChannels(sample_image); i++)
3984       {
3985         PixelChannel
3986           channel;
3987 
3988         PixelTrait
3989           image_traits,
3990           traits;
3991 
3992         channel=GetPixelChannelChannel(sample_image,i);
3993         traits=GetPixelChannelTraits(sample_image,channel);
3994         image_traits=GetPixelChannelTraits(image,channel);
3995         if ((traits == UndefinedPixelTrait) ||
3996             (image_traits == UndefinedPixelTrait))
3997           continue;
3998         SetPixelChannel(sample_image,channel,p[x_offset[x]*GetPixelChannels(
3999           image)+i],q);
4000       }
4001       q+=GetPixelChannels(sample_image);
4002     }
4003     if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
4004       status=MagickFalse;
4005     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4006       {
4007         MagickBooleanType
4008           proceed;
4009 
4010         proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
4011         if (proceed == MagickFalse)
4012           status=MagickFalse;
4013       }
4014   }
4015   image_view=DestroyCacheView(image_view);
4016   sample_view=DestroyCacheView(sample_view);
4017   x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
4018   sample_image->type=image->type;
4019   if (status == MagickFalse)
4020     sample_image=DestroyImage(sample_image);
4021   return(sample_image);
4022 }
4023 
4024 /*
4025 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4026 %                                                                             %
4027 %                                                                             %
4028 %                                                                             %
4029 %   S c a l e I m a g e                                                       %
4030 %                                                                             %
4031 %                                                                             %
4032 %                                                                             %
4033 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4034 %
4035 %  ScaleImage() changes the size of an image to the given dimensions.
4036 %
4037 %  The format of the ScaleImage method is:
4038 %
4039 %      Image *ScaleImage(const Image *image,const size_t columns,
4040 %        const size_t rows,ExceptionInfo *exception)
4041 %
4042 %  A description of each parameter follows:
4043 %
4044 %    o image: the image.
4045 %
4046 %    o columns: the number of columns in the scaled image.
4047 %
4048 %    o rows: the number of rows in the scaled image.
4049 %
4050 %    o exception: return any errors or warnings in this structure.
4051 %
4052 */
ScaleImage(const Image * image,const size_t columns,const size_t rows,ExceptionInfo * exception)4053 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
4054   const size_t rows,ExceptionInfo *exception)
4055 {
4056 #define ScaleImageTag  "Scale/Image"
4057 
4058   CacheView
4059     *image_view,
4060     *scale_view;
4061 
4062   double
4063     alpha,
4064     pixel[CompositePixelChannel],
4065     *scale_scanline,
4066     *scanline,
4067     *x_vector,
4068     *y_vector;
4069 
4070   Image
4071     *scale_image;
4072 
4073   MagickBooleanType
4074     next_column,
4075     next_row,
4076     proceed,
4077     status;
4078 
4079   PixelTrait
4080     scale_traits;
4081 
4082   PointInfo
4083     scale,
4084     span;
4085 
4086   ssize_t
4087     i;
4088 
4089   ssize_t
4090     n,
4091     number_rows,
4092     y;
4093 
4094   /*
4095     Initialize scaled image attributes.
4096   */
4097   assert(image != (const Image *) NULL);
4098   assert(image->signature == MagickCoreSignature);
4099   if (image->debug != MagickFalse)
4100     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4101   assert(exception != (ExceptionInfo *) NULL);
4102   assert(exception->signature == MagickCoreSignature);
4103   if ((columns == 0) || (rows == 0))
4104     ThrowImageException(ImageError,"NegativeOrZeroImageSize");
4105   if ((columns == image->columns) && (rows == image->rows))
4106     return(CloneImage(image,0,0,MagickTrue,exception));
4107   scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
4108   if (scale_image == (Image *) NULL)
4109     return((Image *) NULL);
4110   if (SetImageStorageClass(scale_image,DirectClass,exception) == MagickFalse)
4111     {
4112       scale_image=DestroyImage(scale_image);
4113       return((Image *) NULL);
4114     }
4115   /*
4116     Allocate memory.
4117   */
4118   x_vector=(double *) AcquireQuantumMemory((size_t) image->columns,
4119     MaxPixelChannels*sizeof(*x_vector));
4120   scanline=x_vector;
4121   if (image->rows != scale_image->rows)
4122     scanline=(double *) AcquireQuantumMemory((size_t) image->columns,
4123       MaxPixelChannels*sizeof(*scanline));
4124   scale_scanline=(double *) AcquireQuantumMemory((size_t) scale_image->columns,
4125     MaxPixelChannels*sizeof(*scale_scanline));
4126   y_vector=(double *) AcquireQuantumMemory((size_t) image->columns,
4127     MaxPixelChannels*sizeof(*y_vector));
4128   if ((scanline == (double *) NULL) || (scale_scanline == (double *) NULL) ||
4129       (x_vector == (double *) NULL) || (y_vector == (double *) NULL))
4130     {
4131       if ((image->rows != scale_image->rows) && (scanline != (double *) NULL))
4132         scanline=(double *) RelinquishMagickMemory(scanline);
4133       if (scale_scanline != (double *) NULL)
4134         scale_scanline=(double *) RelinquishMagickMemory(scale_scanline);
4135       if (x_vector != (double *) NULL)
4136         x_vector=(double *) RelinquishMagickMemory(x_vector);
4137       if (y_vector != (double *) NULL)
4138         y_vector=(double *) RelinquishMagickMemory(y_vector);
4139       scale_image=DestroyImage(scale_image);
4140       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4141     }
4142   /*
4143     Scale image.
4144   */
4145   number_rows=0;
4146   next_row=MagickTrue;
4147   span.y=1.0;
4148   scale.y=(double) scale_image->rows/(double) image->rows;
4149   (void) memset(y_vector,0,(size_t) MaxPixelChannels*image->columns*
4150     sizeof(*y_vector));
4151   n=0;
4152   status=MagickTrue;
4153   image_view=AcquireVirtualCacheView(image,exception);
4154   scale_view=AcquireAuthenticCacheView(scale_image,exception);
4155   for (y=0; y < (ssize_t) scale_image->rows; y++)
4156   {
4157     const Quantum
4158       *magick_restrict p;
4159 
4160     Quantum
4161       *magick_restrict q;
4162 
4163     ssize_t
4164       x;
4165 
4166     if (status == MagickFalse)
4167       break;
4168     q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
4169       exception);
4170     if (q == (Quantum *) NULL)
4171       {
4172         status=MagickFalse;
4173         break;
4174       }
4175     alpha=1.0;
4176     if (scale_image->rows == image->rows)
4177       {
4178         /*
4179           Read a new scanline.
4180         */
4181         p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
4182           exception);
4183         if (p == (const Quantum *) NULL)
4184           {
4185             status=MagickFalse;
4186             break;
4187           }
4188         for (x=0; x < (ssize_t) image->columns; x++)
4189         {
4190           if (GetPixelWriteMask(image,p) <= (QuantumRange/2))
4191             {
4192               p+=GetPixelChannels(image);
4193               continue;
4194             }
4195           if (image->alpha_trait != UndefinedPixelTrait)
4196             alpha=QuantumScale*GetPixelAlpha(image,p);
4197           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4198           {
4199             PixelChannel channel = GetPixelChannelChannel(image,i);
4200             PixelTrait traits = GetPixelChannelTraits(image,channel);
4201             if ((traits & BlendPixelTrait) == 0)
4202               {
4203                 x_vector[x*GetPixelChannels(image)+i]=(double) p[i];
4204                 continue;
4205               }
4206             x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
4207           }
4208           p+=GetPixelChannels(image);
4209         }
4210       }
4211     else
4212       {
4213         /*
4214           Scale Y direction.
4215         */
4216         while (scale.y < span.y)
4217         {
4218           if ((next_row != MagickFalse) &&
4219               (number_rows < (ssize_t) image->rows))
4220             {
4221               /*
4222                 Read a new scanline.
4223               */
4224               p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
4225                 exception);
4226               if (p == (const Quantum *) NULL)
4227                 {
4228                   status=MagickFalse;
4229                   break;
4230                 }
4231               for (x=0; x < (ssize_t) image->columns; x++)
4232               {
4233                 if (GetPixelWriteMask(image,p) <= (QuantumRange/2))
4234                   {
4235                     p+=GetPixelChannels(image);
4236                     continue;
4237                   }
4238                 if (image->alpha_trait != UndefinedPixelTrait)
4239                   alpha=QuantumScale*GetPixelAlpha(image,p);
4240                 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4241                 {
4242                   PixelChannel channel = GetPixelChannelChannel(image,i);
4243                   PixelTrait traits = GetPixelChannelTraits(image,channel);
4244                   if ((traits & BlendPixelTrait) == 0)
4245                     {
4246                       x_vector[x*GetPixelChannels(image)+i]=(double) p[i];
4247                       continue;
4248                     }
4249                   x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
4250                 }
4251                 p+=GetPixelChannels(image);
4252               }
4253               number_rows++;
4254             }
4255           for (x=0; x < (ssize_t) image->columns; x++)
4256             for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4257               y_vector[x*GetPixelChannels(image)+i]+=scale.y*
4258                 x_vector[x*GetPixelChannels(image)+i];
4259           span.y-=scale.y;
4260           scale.y=(double) scale_image->rows/(double) image->rows;
4261           next_row=MagickTrue;
4262         }
4263         if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
4264           {
4265             /*
4266               Read a new scanline.
4267             */
4268             p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
4269               exception);
4270             if (p == (const Quantum *) NULL)
4271               {
4272                 status=MagickFalse;
4273                 break;
4274               }
4275             for (x=0; x < (ssize_t) image->columns; x++)
4276             {
4277               if (GetPixelWriteMask(image,p) <= (QuantumRange/2))
4278                 {
4279                   p+=GetPixelChannels(image);
4280                   continue;
4281                 }
4282               if (image->alpha_trait != UndefinedPixelTrait)
4283                 alpha=QuantumScale*GetPixelAlpha(image,p);
4284               for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4285               {
4286                 PixelChannel channel = GetPixelChannelChannel(image,i);
4287                 PixelTrait traits = GetPixelChannelTraits(image,channel);
4288                 if ((traits & BlendPixelTrait) == 0)
4289                   {
4290                     x_vector[x*GetPixelChannels(image)+i]=(double) p[i];
4291                     continue;
4292                   }
4293                 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
4294               }
4295               p+=GetPixelChannels(image);
4296             }
4297             number_rows++;
4298             next_row=MagickFalse;
4299           }
4300         for (x=0; x < (ssize_t) image->columns; x++)
4301         {
4302           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4303           {
4304             pixel[i]=y_vector[x*GetPixelChannels(image)+i]+span.y*
4305               x_vector[x*GetPixelChannels(image)+i];
4306             scanline[x*GetPixelChannels(image)+i]=pixel[i];
4307             y_vector[x*GetPixelChannels(image)+i]=0.0;
4308           }
4309         }
4310         scale.y-=span.y;
4311         if (scale.y <= 0)
4312           {
4313             scale.y=(double) scale_image->rows/(double) image->rows;
4314             next_row=MagickTrue;
4315           }
4316         span.y=1.0;
4317       }
4318     if (scale_image->columns == image->columns)
4319       {
4320         /*
4321           Transfer scanline to scaled image.
4322         */
4323         for (x=0; x < (ssize_t) scale_image->columns; x++)
4324         {
4325           if (GetPixelWriteMask(scale_image,q) <= (QuantumRange/2))
4326             {
4327               q+=GetPixelChannels(scale_image);
4328               continue;
4329             }
4330           if (image->alpha_trait != UndefinedPixelTrait)
4331             {
4332               alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
4333                 GetPixelChannelOffset(image,AlphaPixelChannel)];
4334               alpha=PerceptibleReciprocal(alpha);
4335             }
4336           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4337           {
4338             PixelChannel channel = GetPixelChannelChannel(image,i);
4339             PixelTrait traits = GetPixelChannelTraits(image,channel);
4340             scale_traits=GetPixelChannelTraits(scale_image,channel);
4341             if ((traits == UndefinedPixelTrait) ||
4342                 (scale_traits == UndefinedPixelTrait))
4343               continue;
4344             if ((traits & BlendPixelTrait) == 0)
4345               {
4346                 SetPixelChannel(scale_image,channel,ClampToQuantum(
4347                   scanline[x*GetPixelChannels(image)+i]),q);
4348                 continue;
4349               }
4350             SetPixelChannel(scale_image,channel,ClampToQuantum(alpha*scanline[
4351               x*GetPixelChannels(image)+i]),q);
4352           }
4353           q+=GetPixelChannels(scale_image);
4354         }
4355       }
4356     else
4357       {
4358         ssize_t
4359           t;
4360 
4361         /*
4362           Scale X direction.
4363         */
4364         for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4365           pixel[i]=0.0;
4366         next_column=MagickFalse;
4367         span.x=1.0;
4368         t=0;
4369         for (x=0; x < (ssize_t) image->columns; x++)
4370         {
4371           scale.x=(double) scale_image->columns/(double) image->columns;
4372           while (scale.x >= span.x)
4373           {
4374             if (next_column != MagickFalse)
4375               {
4376                 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4377                   pixel[i]=0.0;
4378                 t++;
4379               }
4380             for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4381             {
4382               PixelChannel channel = GetPixelChannelChannel(image,i);
4383               PixelTrait traits = GetPixelChannelTraits(image,channel);
4384               if (traits == UndefinedPixelTrait)
4385                 continue;
4386               pixel[i]+=span.x*scanline[x*GetPixelChannels(image)+i];
4387               scale_scanline[t*GetPixelChannels(image)+i]=pixel[i];
4388             }
4389             scale.x-=span.x;
4390             span.x=1.0;
4391             next_column=MagickTrue;
4392           }
4393           if (scale.x > 0)
4394             {
4395               if (next_column != MagickFalse)
4396                 {
4397                   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4398                     pixel[i]=0.0;
4399                   next_column=MagickFalse;
4400                   t++;
4401                 }
4402               for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4403                 pixel[i]+=scale.x*scanline[x*GetPixelChannels(image)+i];
4404               span.x-=scale.x;
4405             }
4406         }
4407       if (span.x > 0)
4408         {
4409           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4410             pixel[i]+=span.x*scanline[(x-1)*GetPixelChannels(image)+i];
4411         }
4412       if ((next_column == MagickFalse) && (t < (ssize_t) scale_image->columns))
4413         for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4414           scale_scanline[t*GetPixelChannels(image)+i]=pixel[i];
4415       /*
4416         Transfer scanline to scaled image.
4417       */
4418       for (x=0; x < (ssize_t) scale_image->columns; x++)
4419       {
4420         if (GetPixelWriteMask(scale_image,q) <= (QuantumRange/2))
4421           {
4422             q+=GetPixelChannels(scale_image);
4423             continue;
4424           }
4425         if (image->alpha_trait != UndefinedPixelTrait)
4426           {
4427             alpha=QuantumScale*scale_scanline[x*GetPixelChannels(image)+
4428               GetPixelChannelOffset(image,AlphaPixelChannel)];
4429             alpha=PerceptibleReciprocal(alpha);
4430           }
4431         for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4432         {
4433           PixelChannel channel = GetPixelChannelChannel(image,i);
4434           PixelTrait traits = GetPixelChannelTraits(image,channel);
4435           scale_traits=GetPixelChannelTraits(scale_image,channel);
4436           if ((traits == UndefinedPixelTrait) ||
4437               (scale_traits == UndefinedPixelTrait))
4438             continue;
4439           if ((traits & BlendPixelTrait) == 0)
4440             {
4441               SetPixelChannel(scale_image,channel,ClampToQuantum(
4442                 scale_scanline[x*GetPixelChannels(image)+i]),q);
4443               continue;
4444             }
4445           SetPixelChannel(scale_image,channel,ClampToQuantum(alpha*
4446             scale_scanline[x*GetPixelChannels(image)+i]),q);
4447         }
4448         q+=GetPixelChannels(scale_image);
4449       }
4450     }
4451     if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
4452       {
4453         status=MagickFalse;
4454         break;
4455       }
4456     proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
4457       image->rows);
4458     if (proceed == MagickFalse)
4459       {
4460         status=MagickFalse;
4461         break;
4462       }
4463   }
4464   scale_view=DestroyCacheView(scale_view);
4465   image_view=DestroyCacheView(image_view);
4466   /*
4467     Free allocated memory.
4468   */
4469   y_vector=(double *) RelinquishMagickMemory(y_vector);
4470   scale_scanline=(double *) RelinquishMagickMemory(scale_scanline);
4471   if (scale_image->rows != image->rows)
4472     scanline=(double *) RelinquishMagickMemory(scanline);
4473   x_vector=(double *) RelinquishMagickMemory(x_vector);
4474   scale_image->type=image->type;
4475   if (status == MagickFalse)
4476     scale_image=DestroyImage(scale_image);
4477   return(scale_image);
4478 }
4479 
4480 /*
4481 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4482 %                                                                             %
4483 %                                                                             %
4484 %                                                                             %
4485 %   T h u m b n a i l I m a g e                                               %
4486 %                                                                             %
4487 %                                                                             %
4488 %                                                                             %
4489 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4490 %
4491 %  ThumbnailImage() changes the size of an image to the given dimensions and
4492 %  removes any associated profiles.  The goal is to produce small low cost
4493 %  thumbnail images suited for display on the Web.
4494 %
4495 %  The format of the ThumbnailImage method is:
4496 %
4497 %      Image *ThumbnailImage(const Image *image,const size_t columns,
4498 %        const size_t rows,ExceptionInfo *exception)
4499 %
4500 %  A description of each parameter follows:
4501 %
4502 %    o image: the image.
4503 %
4504 %    o columns: the number of columns in the scaled image.
4505 %
4506 %    o rows: the number of rows in the scaled image.
4507 %
4508 %    o exception: return any errors or warnings in this structure.
4509 %
4510 */
ThumbnailImage(const Image * image,const size_t columns,const size_t rows,ExceptionInfo * exception)4511 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
4512   const size_t rows,ExceptionInfo *exception)
4513 {
4514 #define SampleFactor  5
4515 
4516   char
4517     filename[MagickPathExtent],
4518     value[MagickPathExtent];
4519 
4520   const char
4521     *name;
4522 
4523   Image
4524     *thumbnail_image;
4525 
4526   double
4527     x_factor,
4528     y_factor;
4529 
4530   struct stat
4531     attributes;
4532 
4533   assert(image != (Image *) NULL);
4534   assert(image->signature == MagickCoreSignature);
4535   if (image->debug != MagickFalse)
4536     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4537   assert(exception != (ExceptionInfo *) NULL);
4538   assert(exception->signature == MagickCoreSignature);
4539   x_factor=(double) columns/(double) image->columns;
4540   y_factor=(double) rows/(double) image->rows;
4541   if ((x_factor*y_factor) > 0.1)
4542     thumbnail_image=ResizeImage(image,columns,rows,image->filter,exception);
4543   else
4544     if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
4545       thumbnail_image=ResizeImage(image,columns,rows,image->filter,exception);
4546     else
4547       {
4548         Image
4549           *sample_image;
4550 
4551         sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
4552           exception);
4553         if (sample_image == (Image *) NULL)
4554           return((Image *) NULL);
4555         thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
4556           exception);
4557         sample_image=DestroyImage(sample_image);
4558       }
4559   if (thumbnail_image == (Image *) NULL)
4560     return(thumbnail_image);
4561   (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
4562   if (thumbnail_image->alpha_trait == UndefinedPixelTrait)
4563     (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel,exception);
4564   thumbnail_image->depth=8;
4565   thumbnail_image->interlace=NoInterlace;
4566   /*
4567     Strip all profiles except color profiles.
4568   */
4569   ResetImageProfileIterator(thumbnail_image);
4570   for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
4571   {
4572     if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
4573      {
4574        (void) DeleteImageProfile(thumbnail_image,name);
4575        ResetImageProfileIterator(thumbnail_image);
4576      }
4577     name=GetNextImageProfile(thumbnail_image);
4578   }
4579   (void) DeleteImageProperty(thumbnail_image,"comment");
4580   (void) CopyMagickString(value,image->magick_filename,MagickPathExtent);
4581   if (strstr(image->magick_filename,"//") == (char *) NULL)
4582     (void) FormatLocaleString(value,MagickPathExtent,"file://%s",
4583       image->magick_filename);
4584   (void) SetImageProperty(thumbnail_image,"Thumb::URI",value,exception);
4585   GetPathComponent(image->magick_filename,TailPath,filename);
4586   (void) CopyMagickString(value,filename,MagickPathExtent);
4587   if ( GetPathAttributes(image->filename,&attributes) != MagickFalse )
4588     (void) FormatImageProperty(thumbnail_image,"Thumb::MTime","%.20g",(double)
4589       attributes.st_mtime);
4590   (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
4591     attributes.st_mtime);
4592   (void) FormatMagickSize(GetBlobSize(image),MagickFalse,"B",MagickPathExtent,
4593     value);
4594   (void) SetImageProperty(thumbnail_image,"Thumb::Size",value,exception);
4595   (void) FormatLocaleString(value,MagickPathExtent,"image/%s",image->magick);
4596   LocaleLower(value);
4597   (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value,exception);
4598   (void) SetImageProperty(thumbnail_image,"software",MagickAuthoritativeURL,
4599     exception);
4600   (void) FormatImageProperty(thumbnail_image,"Thumb::Image::Width","%.20g",
4601     (double) image->magick_columns);
4602   (void) FormatImageProperty(thumbnail_image,"Thumb::Image::Height","%.20g",
4603     (double) image->magick_rows);
4604   (void) FormatImageProperty(thumbnail_image,"Thumb::Document::Pages","%.20g",
4605     (double) GetImageListLength(image));
4606   return(thumbnail_image);
4607 }
4608