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