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