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