MagickCore  6.9.12-62
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-2021 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/script/license.php %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 
39 /*
40  Include declarations.
41 */
42 #include "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, usally equal to support (expert only) */
94  scale, /* dimension scaling to fit window support (usally 1.0) */
95  blur, /* x-scale (blur-sharpen) */
96  coefficient[7]; /* cubic coefficents for BC-cubic filters */
97 
98  ResizeWeightingFunctionType
99  filterWeightingType,
100  windowWeightingType;
101 
102  size_t
103  signature;
104 };
105 
106 /*
107  Forward declaractions.
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  Coefficents are determined from B,C values:
221  P0 = ( 6 - 2*B )/6 = coeff[0]
222  P1 = 0
223  P2 = (-18 +12*B + 6*C )/6 = coeff[1]
224  P3 = ( 12 - 9*B - 6*C )/6 = coeff[2]
225  Q0 = ( 8*B +24*C )/6 = coeff[3]
226  Q1 = ( -12*B -48*C )/6 = coeff[4]
227  Q2 = ( 6*B +30*C )/6 = coeff[5]
228  Q3 = ( - 1*B - 6*C )/6 = coeff[6]
229 
230  which are used to define the filter:
231 
232  P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
233  Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x < 2
234 
235  which ensures function is continuous in value and derivative (slope).
236  */
237  if (x < 1.0)
238  return(resize_filter->coefficient[0]+x*(x*
239  (resize_filter->coefficient[1]+x*resize_filter->coefficient[2])));
240  if (x < 2.0)
241  return(resize_filter->coefficient[3]+x*(resize_filter->coefficient[4]+x*
242  (resize_filter->coefficient[5]+x*resize_filter->coefficient[6])));
243  return(0.0);
244 }
245 
246 static 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 advisible. 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 simble 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 orthoginal/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; /* guassian sigma default, half pixel */
1006  artifact=GetImageArtifact(image,"filter:sigma");
1007  if (artifact != (const char *) NULL)
1008  value=StringToDouble(artifact,(char **) NULL);
1009  /* Define coefficents 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 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1138  #pragma omp master
1139  {
1140 #endif
1141  artifact=GetImageArtifact(image,"filter:verbose");
1142  if (IsMagickTrue(artifact) != MagickFalse)
1143  {
1144  double
1145  support,
1146  x;
1147 
1148  /*
1149  Set the weighting function properly when the weighting
1150  function may not exactly match the filter of the same name.
1151  EG: a Point filter is really uses a Box weighting function
1152  with a different support than is typically used.
1153  */
1154  if (resize_filter->filter == Box) filter_type=BoxFilter;
1155  if (resize_filter->filter == Sinc) filter_type=SincFilter;
1156  if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1157  if (resize_filter->filter == Jinc) filter_type=JincFilter;
1158  if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1159  if (resize_filter->window == Box) window_type=BoxFilter;
1160  if (resize_filter->window == Sinc) window_type=SincFilter;
1161  if (resize_filter->window == SincFast) window_type=SincFastFilter;
1162  if (resize_filter->window == Jinc) window_type=JincFilter;
1163  if (resize_filter->window == CubicBC) window_type=CubicFilter;
1164  /*
1165  Report Filter Details.
1166  */
1167  support=GetResizeFilterSupport(resize_filter); /* practical_support */
1168  (void) FormatLocaleFile(stdout,"# Resampling Filter (for graphing)\n#\n");
1169  (void) FormatLocaleFile(stdout,"# filter = %s\n",
1170  CommandOptionToMnemonic(MagickFilterOptions,filter_type));
1171  (void) FormatLocaleFile(stdout,"# window = %s\n",
1172  CommandOptionToMnemonic(MagickFilterOptions,window_type));
1173  (void) FormatLocaleFile(stdout,"# support = %.*g\n",
1174  GetMagickPrecision(),(double) resize_filter->support);
1175  (void) FormatLocaleFile(stdout,"# window-support = %.*g\n",
1176  GetMagickPrecision(),(double) resize_filter->window_support);
1177  (void) FormatLocaleFile(stdout,"# scale-blur = %.*g\n",
1178  GetMagickPrecision(), (double)resize_filter->blur);
1179  if ( filter_type == GaussianFilter || window_type == GaussianFilter )
1180  (void) FormatLocaleFile(stdout,"# gaussian-sigma = %.*g\n",
1181  GetMagickPrecision(), (double)resize_filter->coefficient[0]);
1182  if ( filter_type == KaiserFilter || window_type == KaiserFilter )
1183  (void) FormatLocaleFile(stdout,"# kaiser-beta = %.*g\n",
1184  GetMagickPrecision(),
1185  (double)resize_filter->coefficient[0]);
1186  (void) FormatLocaleFile(stdout,"# practical-support = %.*g\n",
1187  GetMagickPrecision(), (double)support);
1188  if ( filter_type == CubicFilter || window_type == CubicFilter )
1189  (void) FormatLocaleFile(stdout,"# B,C = %.*g,%.*g\n",
1190  GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
1191  (void) FormatLocaleFile(stdout,"\n");
1192  /*
1193  Output values of resulting filter graph -- for graphing
1194  filter result.
1195  */
1196  for (x=0.0; x <= support; x+=0.01f)
1197  (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1198  (double) GetResizeFilterWeight(resize_filter,x));
1199  /* A final value so gnuplot can graph the 'stop' properly. */
1200  (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",support,
1201  GetMagickPrecision(),0.0);
1202  }
1203  /* Output the above once only for each image - remove setting */
1204  (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1205 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1206  }
1207 #endif
1208  return(resize_filter);
1209 }
1210 
1211 /*
1212 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1213 % %
1214 % %
1215 % %
1216 % A d a p t i v e R e s i z e I m a g e %
1217 % %
1218 % %
1219 % %
1220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1221 %
1222 % AdaptiveResizeImage() adaptively resize image with pixel resampling.
1223 %
1224 % This is shortcut function for a fast interpolative resize using mesh
1225 % interpolation. It works well for small resizes of less than +/- 50%
1226 % of the original image size. For larger resizing on images a full
1227 % filtered and slower resize function should be used instead.
1228 %
1229 % The format of the AdaptiveResizeImage method is:
1230 %
1231 % Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1232 % const size_t rows,ExceptionInfo *exception)
1233 %
1234 % A description of each parameter follows:
1235 %
1236 % o image: the image.
1237 %
1238 % o columns: the number of columns in the resized image.
1239 %
1240 % o rows: the number of rows in the resized image.
1241 %
1242 % o exception: return any errors or warnings in this structure.
1243 %
1244 */
1245 MagickExport Image *AdaptiveResizeImage(const Image *image,
1246  const size_t columns,const size_t rows,ExceptionInfo *exception)
1247 {
1248  return(InterpolativeResizeImage(image,columns,rows,MeshInterpolatePixel,
1249  exception));
1250 }
1251 
1252 /*
1253 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1254 % %
1255 % %
1256 % %
1257 + B e s s e l O r d e r O n e %
1258 % %
1259 % %
1260 % %
1261 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1262 %
1263 % BesselOrderOne() computes the Bessel function of x of the first kind of
1264 % order 0. This is used to create the Jinc() filter function below.
1265 %
1266 % Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1267 %
1268 % j1(x) = x*j1(x);
1269 %
1270 % For x in (8,inf)
1271 %
1272 % j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1273 %
1274 % where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1275 %
1276 % cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1277 % = 1/sqrt(2) * (sin(x) - cos(x))
1278 % sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1279 % = -1/sqrt(2) * (sin(x) + cos(x))
1280 %
1281 % The format of the BesselOrderOne method is:
1282 %
1283 % MagickRealType BesselOrderOne(MagickRealType x)
1284 %
1285 % A description of each parameter follows:
1286 %
1287 % o x: MagickRealType value.
1288 %
1289 */
1290 
1291 #undef I0
1292 static MagickRealType I0(MagickRealType x)
1293 {
1294  MagickRealType
1295  sum,
1296  t,
1297  y;
1298 
1299  ssize_t
1300  i;
1301 
1302  /*
1303  Zeroth order Bessel function of the first kind.
1304  */
1305  sum=1.0;
1306  y=x*x/4.0;
1307  t=y;
1308  for (i=2; t > MagickEpsilon; i++)
1309  {
1310  sum+=t;
1311  t*=y/((MagickRealType) i*i);
1312  }
1313  return(sum);
1314 }
1315 
1316 #undef J1
1317 static MagickRealType J1(MagickRealType x)
1318 {
1319  MagickRealType
1320  p,
1321  q;
1322 
1323  ssize_t
1324  i;
1325 
1326  static const double
1327  Pone[] =
1328  {
1329  0.581199354001606143928050809e+21,
1330  -0.6672106568924916298020941484e+20,
1331  0.2316433580634002297931815435e+19,
1332  -0.3588817569910106050743641413e+17,
1333  0.2908795263834775409737601689e+15,
1334  -0.1322983480332126453125473247e+13,
1335  0.3413234182301700539091292655e+10,
1336  -0.4695753530642995859767162166e+7,
1337  0.270112271089232341485679099e+4
1338  },
1339  Qone[] =
1340  {
1341  0.11623987080032122878585294e+22,
1342  0.1185770712190320999837113348e+20,
1343  0.6092061398917521746105196863e+17,
1344  0.2081661221307607351240184229e+15,
1345  0.5243710262167649715406728642e+12,
1346  0.1013863514358673989967045588e+10,
1347  0.1501793594998585505921097578e+7,
1348  0.1606931573481487801970916749e+4,
1349  0.1e+1
1350  };
1351 
1352  p=Pone[8];
1353  q=Qone[8];
1354  for (i=7; i >= 0; i--)
1355  {
1356  p=p*x*x+Pone[i];
1357  q=q*x*x+Qone[i];
1358  }
1359  return(p/q);
1360 }
1361 
1362 #undef P1
1363 static MagickRealType P1(MagickRealType x)
1364 {
1365  MagickRealType
1366  p,
1367  q;
1368 
1369  ssize_t
1370  i;
1371 
1372  static const double
1373  Pone[] =
1374  {
1375  0.352246649133679798341724373e+5,
1376  0.62758845247161281269005675e+5,
1377  0.313539631109159574238669888e+5,
1378  0.49854832060594338434500455e+4,
1379  0.2111529182853962382105718e+3,
1380  0.12571716929145341558495e+1
1381  },
1382  Qone[] =
1383  {
1384  0.352246649133679798068390431e+5,
1385  0.626943469593560511888833731e+5,
1386  0.312404063819041039923015703e+5,
1387  0.4930396490181088979386097e+4,
1388  0.2030775189134759322293574e+3,
1389  0.1e+1
1390  };
1391 
1392  p=Pone[5];
1393  q=Qone[5];
1394  for (i=4; i >= 0; i--)
1395  {
1396  p=p*(8.0/x)*(8.0/x)+Pone[i];
1397  q=q*(8.0/x)*(8.0/x)+Qone[i];
1398  }
1399  return(p/q);
1400 }
1401 
1402 #undef Q1
1403 static MagickRealType Q1(MagickRealType x)
1404 {
1405  MagickRealType
1406  p,
1407  q;
1408 
1409  ssize_t
1410  i;
1411 
1412  static const double
1413  Pone[] =
1414  {
1415  0.3511751914303552822533318e+3,
1416  0.7210391804904475039280863e+3,
1417  0.4259873011654442389886993e+3,
1418  0.831898957673850827325226e+2,
1419  0.45681716295512267064405e+1,
1420  0.3532840052740123642735e-1
1421  },
1422  Qone[] =
1423  {
1424  0.74917374171809127714519505e+4,
1425  0.154141773392650970499848051e+5,
1426  0.91522317015169922705904727e+4,
1427  0.18111867005523513506724158e+4,
1428  0.1038187585462133728776636e+3,
1429  0.1e+1
1430  };
1431 
1432  p=Pone[5];
1433  q=Qone[5];
1434  for (i=4; i >= 0; i--)
1435  {
1436  p=p*(8.0/x)*(8.0/x)+Pone[i];
1437  q=q*(8.0/x)*(8.0/x)+Qone[i];
1438  }
1439  return(p/q);
1440 }
1441 
1442 static MagickRealType BesselOrderOne(MagickRealType x)
1443 {
1444  MagickRealType
1445  p,
1446  q;
1447 
1448  if (x == 0.0)
1449  return(0.0);
1450  p=x;
1451  if (x < 0.0)
1452  x=(-x);
1453  if (x < 8.0)
1454  return(p*J1(x));
1455  q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1456  cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1457  cos((double) x))));
1458  if (p < 0.0)
1459  q=(-q);
1460  return(q);
1461 }
1462 
1463 /*
1464 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1465 % %
1466 % %
1467 % %
1468 + D e s t r o y R e s i z e F i l t e r %
1469 % %
1470 % %
1471 % %
1472 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1473 %
1474 % DestroyResizeFilter() destroy the resize filter.
1475 %
1476 % The format of the DestroyResizeFilter method is:
1477 %
1478 % ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1479 %
1480 % A description of each parameter follows:
1481 %
1482 % o resize_filter: the resize filter.
1483 %
1484 */
1485 MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1486 {
1487  assert(resize_filter != (ResizeFilter *) NULL);
1488  assert(resize_filter->signature == MagickCoreSignature);
1489  resize_filter->signature=(~MagickCoreSignature);
1490  resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1491  return(resize_filter);
1492 }
1493 
1494 /*
1495 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1496 % %
1497 % %
1498 % %
1499 + G e t R e s i z e F i l t e r S u p p o r t %
1500 % %
1501 % %
1502 % %
1503 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1504 %
1505 % GetResizeFilterSupport() return the current support window size for this
1506 % filter. Note that this may have been enlarged by filter:blur factor.
1507 %
1508 % The format of the GetResizeFilterSupport method is:
1509 %
1510 % MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1511 %
1512 % A description of each parameter follows:
1513 %
1514 % o filter: Image filter to use.
1515 %
1516 */
1517 
1518 MagickExport MagickRealType *GetResizeFilterCoefficient(
1519  const ResizeFilter *resize_filter)
1520 {
1521  assert(resize_filter != (ResizeFilter *) NULL);
1522  assert(resize_filter->signature == MagickCoreSignature);
1523  return((MagickRealType *) resize_filter->coefficient);
1524 }
1525 
1526 MagickExport MagickRealType GetResizeFilterBlur(
1527  const ResizeFilter *resize_filter)
1528 {
1529  assert(resize_filter != (ResizeFilter *) NULL);
1530  assert(resize_filter->signature == MagickCoreSignature);
1531  return(resize_filter->blur);
1532 }
1533 
1534 MagickExport MagickRealType GetResizeFilterScale(
1535  const ResizeFilter *resize_filter)
1536 {
1537  assert(resize_filter != (ResizeFilter *) NULL);
1538  assert(resize_filter->signature == MagickCoreSignature);
1539  return(resize_filter->scale);
1540 }
1541 
1542 MagickExport MagickRealType GetResizeFilterWindowSupport(
1543  const ResizeFilter *resize_filter)
1544 {
1545  assert(resize_filter != (ResizeFilter *) NULL);
1546  assert(resize_filter->signature == MagickCoreSignature);
1547  return(resize_filter->window_support);
1548 }
1549 
1550 MagickExport ResizeWeightingFunctionType GetResizeFilterWeightingType(
1551  const ResizeFilter *resize_filter)
1552 {
1553  assert(resize_filter != (ResizeFilter *) NULL);
1554  assert(resize_filter->signature == MagickCoreSignature);
1555  return(resize_filter->filterWeightingType);
1556 }
1557 
1558 MagickExport ResizeWeightingFunctionType GetResizeFilterWindowWeightingType(
1559  const ResizeFilter *resize_filter)
1560 {
1561  assert(resize_filter != (ResizeFilter *) NULL);
1562  assert(resize_filter->signature == MagickCoreSignature);
1563  return(resize_filter->windowWeightingType);
1564 }
1565 
1566 MagickExport MagickRealType GetResizeFilterSupport(
1567  const ResizeFilter *resize_filter)
1568 {
1569  assert(resize_filter != (ResizeFilter *) NULL);
1570  assert(resize_filter->signature == MagickCoreSignature);
1571  return(resize_filter->support*resize_filter->blur);
1572 }
1573 
1574 /*
1575 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1576 % %
1577 % %
1578 % %
1579 + G e t R e s i z e F i l t e r W e i g h t %
1580 % %
1581 % %
1582 % %
1583 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1584 %
1585 % GetResizeFilterWeight evaluates the specified resize filter at the point x
1586 % which usally lies between zero and the filters current 'support' and
1587 % returns the weight of the filter function at that point.
1588 %
1589 % The format of the GetResizeFilterWeight method is:
1590 %
1591 % MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1592 % const MagickRealType x)
1593 %
1594 % A description of each parameter follows:
1595 %
1596 % o filter: the filter type.
1597 %
1598 % o x: the point.
1599 %
1600 */
1601 MagickExport MagickRealType GetResizeFilterWeight(
1602  const ResizeFilter *resize_filter,const MagickRealType x)
1603 {
1604  MagickRealType
1605  scale,
1606  weight,
1607  x_blur;
1608 
1609  /*
1610  Windowing function - scale the weighting filter by this amount.
1611  */
1612  assert(resize_filter != (ResizeFilter *) NULL);
1613  assert(resize_filter->signature == MagickCoreSignature);
1614  x_blur=fabs((double) x)*PerceptibleReciprocal(resize_filter->blur); /* X offset with blur scaling */
1615  if ((resize_filter->window_support < MagickEpsilon) ||
1616  (resize_filter->window == Box))
1617  scale=1.0; /* Point or Box Filter -- avoid division by zero */
1618  else
1619  {
1620  scale=resize_filter->scale;
1621  scale=resize_filter->window(x_blur*scale,resize_filter);
1622  }
1623  weight=scale*resize_filter->filter(x_blur,resize_filter);
1624  return(weight);
1625 }
1626 
1627 /*
1628 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1629 % %
1630 % %
1631 % %
1632 % I n t e r p o l a t i v e R e s i z e I m a g e %
1633 % %
1634 % %
1635 % %
1636 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1637 %
1638 % InterpolativeResizeImage() resizes an image using the specified
1639 % interpolation method.
1640 %
1641 % The format of the InterpolativeResizeImage method is:
1642 %
1643 % Image *InterpolativeResizeImage(const Image *image,const size_t columns,
1644 % const size_t rows,const InterpolatePixelMethod method,
1645 % ExceptionInfo *exception)
1646 %
1647 % A description of each parameter follows:
1648 %
1649 % o image: the image.
1650 %
1651 % o columns: the number of columns in the resized image.
1652 %
1653 % o rows: the number of rows in the resized image.
1654 %
1655 % o method: the pixel interpolation method.
1656 %
1657 % o exception: return any errors or warnings in this structure.
1658 %
1659 */
1660 MagickExport Image *InterpolativeResizeImage(const Image *image,
1661  const size_t columns,const size_t rows,const InterpolatePixelMethod method,
1662  ExceptionInfo *exception)
1663 {
1664 #define InterpolativeResizeImageTag "Resize/Image"
1665 
1666  CacheView
1667  *image_view,
1668  *resize_view;
1669 
1670  Image
1671  *resize_image;
1672 
1673  MagickBooleanType
1674  status;
1675 
1676  MagickOffsetType
1677  progress;
1678 
1679  PointInfo
1680  scale;
1681 
1682  ssize_t
1683  y;
1684 
1685  /*
1686  Interpolatively resize image.
1687  */
1688  assert(image != (const Image *) NULL);
1689  assert(image->signature == MagickCoreSignature);
1690  assert(exception != (ExceptionInfo *) NULL);
1691  assert(exception->signature == MagickCoreSignature);
1692  if (IsEventLogging() != MagickFalse)
1693  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1694  if ((columns == 0) || (rows == 0))
1695  return((Image *) NULL);
1696  if ((columns == image->columns) && (rows == image->rows))
1697  return(CloneImage(image,0,0,MagickTrue,exception));
1698  resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1699  if (resize_image == (Image *) NULL)
1700  return((Image *) NULL);
1701  if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1702  {
1703  InheritException(exception,&resize_image->exception);
1704  resize_image=DestroyImage(resize_image);
1705  return((Image *) NULL);
1706  }
1707  status=MagickTrue;
1708  progress=0;
1709  image_view=AcquireVirtualCacheView(image,exception);
1710  resize_view=AcquireAuthenticCacheView(resize_image,exception);
1711  scale.x=(double) image->columns/resize_image->columns;
1712  scale.y=(double) image->rows/resize_image->rows;
1713 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1714  #pragma omp parallel for schedule(static) shared(progress,status) \
1715  magick_number_threads(image,resize_image,resize_image->rows,1)
1716 #endif
1717  for (y=0; y < (ssize_t) resize_image->rows; y++)
1718  {
1720  pixel;
1721 
1722  PointInfo
1723  offset;
1724 
1725  IndexPacket
1726  *magick_restrict resize_indexes;
1727 
1728  PixelPacket
1729  *magick_restrict q;
1730 
1731  ssize_t
1732  x;
1733 
1734  if (status == MagickFalse)
1735  continue;
1736  q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1737  exception);
1738  if (q == (PixelPacket *) NULL)
1739  continue;
1740  resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1741  GetMagickPixelPacket(image,&pixel);
1742  offset.y=((MagickRealType) y+0.5)*scale.y-0.5;
1743  for (x=0; x < (ssize_t) resize_image->columns; x++)
1744  {
1745  offset.x=((MagickRealType) x+0.5)*scale.x-0.5;
1746  status=InterpolateMagickPixelPacket(image,image_view,method,offset.x,
1747  offset.y,&pixel,exception);
1748  if (status == MagickFalse)
1749  break;
1750  SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1751  q++;
1752  }
1753  if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1754  continue;
1755  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1756  {
1757  MagickBooleanType
1758  proceed;
1759 
1760 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1761  #pragma omp atomic
1762 #endif
1763  progress++;
1764  proceed=SetImageProgress(image,InterpolativeResizeImageTag,progress,
1765  image->rows);
1766  if (proceed == MagickFalse)
1767  status=MagickFalse;
1768  }
1769  }
1770  resize_view=DestroyCacheView(resize_view);
1771  image_view=DestroyCacheView(image_view);
1772  if (status == MagickFalse)
1773  resize_image=DestroyImage(resize_image);
1774  return(resize_image);
1775 }
1776 #if defined(MAGICKCORE_LQR_DELEGATE)
1777 
1778 /*
1779 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1780 % %
1781 % %
1782 % %
1783 % L i q u i d R e s c a l e I m a g e %
1784 % %
1785 % %
1786 % %
1787 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1788 %
1789 % LiquidRescaleImage() rescales image with seam carving.
1790 %
1791 % The format of the LiquidRescaleImage method is:
1792 %
1793 % Image *LiquidRescaleImage(const Image *image,
1794 % const size_t columns,const size_t rows,
1795 % const double delta_x,const double rigidity,ExceptionInfo *exception)
1796 %
1797 % A description of each parameter follows:
1798 %
1799 % o image: the image.
1800 %
1801 % o columns: the number of columns in the rescaled image.
1802 %
1803 % o rows: the number of rows in the rescaled image.
1804 %
1805 % o delta_x: maximum seam transversal step (0 means straight seams).
1806 %
1807 % o rigidity: introduce a bias for non-straight seams (typically 0).
1808 %
1809 % o exception: return any errors or warnings in this structure.
1810 %
1811 */
1812 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1813  const size_t rows,const double delta_x,const double rigidity,
1814  ExceptionInfo *exception)
1815 {
1816 #define LiquidRescaleImageTag "Rescale/Image"
1817 
1818  CacheView
1819  *rescale_view;
1820 
1821  const char
1822  *map;
1823 
1824  guchar
1825  *packet;
1826 
1827  Image
1828  *rescale_image;
1829 
1830  int
1831  x,
1832  y;
1833 
1834  LqrCarver
1835  *carver;
1836 
1837  LqrRetVal
1838  lqr_status;
1839 
1840  MagickBooleanType
1841  status;
1842 
1844  pixel;
1845 
1846  MemoryInfo
1847  *pixel_info;
1848 
1849  unsigned char
1850  *pixels;
1851 
1852  /*
1853  Liquid rescale image.
1854  */
1855  assert(image != (const Image *) NULL);
1856  assert(image->signature == MagickCoreSignature);
1857  assert(exception != (ExceptionInfo *) NULL);
1858  assert(exception->signature == MagickCoreSignature);
1859  if (IsEventLogging() != MagickFalse)
1860  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1861  if ((columns == 0) || (rows == 0))
1862  return((Image *) NULL);
1863  if ((columns == image->columns) && (rows == image->rows))
1864  return(CloneImage(image,0,0,MagickTrue,exception));
1865  if ((columns <= 2) || (rows <= 2))
1866  return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
1867  map="RGB";
1868  if (image->matte != MagickFalse)
1869  map="RGBA";
1870  if (image->colorspace == CMYKColorspace)
1871  {
1872  map="CMYK";
1873  if (image->matte != MagickFalse)
1874  map="CMYKA";
1875  }
1876  pixel_info=AcquireVirtualMemory(image->columns,image->rows*strlen(map)*
1877  sizeof(*pixels));
1878  if (pixel_info == (MemoryInfo *) NULL)
1879  return((Image *) NULL);
1880  pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
1881  status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1882  pixels,exception);
1883  if (status == MagickFalse)
1884  {
1885  pixel_info=RelinquishVirtualMemory(pixel_info);
1886  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1887  }
1888  carver=lqr_carver_new(pixels,(int) image->columns,(int) image->rows,
1889  (int) strlen(map));
1890  if (carver == (LqrCarver *) NULL)
1891  {
1892  pixel_info=RelinquishVirtualMemory(pixel_info);
1893  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1894  }
1895  lqr_carver_set_preserve_input_image(carver);
1896  lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1897  lqr_status=lqr_carver_resize(carver,(int) columns,(int) rows);
1898  (void) lqr_status;
1899  rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1900  lqr_carver_get_height(carver),MagickTrue,exception);
1901  if (rescale_image == (Image *) NULL)
1902  {
1903  pixel_info=RelinquishVirtualMemory(pixel_info);
1904  return((Image *) NULL);
1905  }
1906  if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1907  {
1908  InheritException(exception,&rescale_image->exception);
1909  rescale_image=DestroyImage(rescale_image);
1910  return((Image *) NULL);
1911  }
1912  GetMagickPixelPacket(rescale_image,&pixel);
1913  (void) lqr_carver_scan_reset(carver);
1914  rescale_view=AcquireAuthenticCacheView(rescale_image,exception);
1915  while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1916  {
1917  IndexPacket
1918  *magick_restrict rescale_indexes;
1919 
1920  PixelPacket
1921  *magick_restrict q;
1922 
1923  q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
1924  if (q == (PixelPacket *) NULL)
1925  break;
1926  rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
1927  pixel.red=QuantumRange*(packet[0]/255.0);
1928  pixel.green=QuantumRange*(packet[1]/255.0);
1929  pixel.blue=QuantumRange*(packet[2]/255.0);
1930  if (image->colorspace != CMYKColorspace)
1931  {
1932  if (image->matte != MagickFalse)
1933  pixel.opacity=QuantumRange-QuantumRange*(packet[3]/255.0);
1934  }
1935  else
1936  {
1937  pixel.index=QuantumRange*(packet[3]/255.0);
1938  if (image->matte != MagickFalse)
1939  pixel.opacity=QuantumRange-QuantumRange*(packet[4]/255.0);
1940  }
1941  SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
1942  if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
1943  break;
1944  }
1945  rescale_view=DestroyCacheView(rescale_view);
1946  /*
1947  Relinquish resources.
1948  */
1949  pixel_info=RelinquishVirtualMemory(pixel_info);
1950  lqr_carver_destroy(carver);
1951  return(rescale_image);
1952 }
1953 #else
1954 MagickExport Image *LiquidRescaleImage(const Image *image,
1955  const size_t magick_unused(columns),const size_t magick_unused(rows),
1956  const double magick_unused(delta_x),const double magick_unused(rigidity),
1957  ExceptionInfo *exception)
1958 {
1959  assert(image != (const Image *) NULL);
1960  assert(image->signature == MagickCoreSignature);
1961  assert(exception != (ExceptionInfo *) NULL);
1962  assert(exception->signature == MagickCoreSignature);
1963  if (IsEventLogging() != MagickFalse)
1964  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1965  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1966  "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1967  return((Image *) NULL);
1968 }
1969 #endif
1970 
1971 /*
1972 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1973 % %
1974 % %
1975 % %
1976 % M a g n i f y I m a g e %
1977 % %
1978 % %
1979 % %
1980 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1981 %
1982 % MagnifyImage() doubles the size of the image with a pixel art scaling
1983 % algorithm.
1984 %
1985 % The format of the MagnifyImage method is:
1986 %
1987 % Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1988 %
1989 % A description of each parameter follows:
1990 %
1991 % o image: the image.
1992 %
1993 % o exception: return any errors or warnings in this structure.
1994 %
1995 */
1996 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1997 {
1998 #define MagnifyImageTag "Magnify/Image"
1999 
2000  CacheView
2001  *image_view,
2002  *magnify_view;
2003 
2004  Image
2005  *magnify_image;
2006 
2007  MagickBooleanType
2008  status;
2009 
2010  MagickOffsetType
2011  progress;
2012 
2013  ssize_t
2014  y;
2015 
2016  /*
2017  Initialize magnified image attributes.
2018  */
2019  assert(image != (const Image *) NULL);
2020  assert(image->signature == MagickCoreSignature);
2021  assert(exception != (ExceptionInfo *) NULL);
2022  assert(exception->signature == MagickCoreSignature);
2023  if (IsEventLogging() != MagickFalse)
2024  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2025  magnify_image=CloneImage(image,2*image->columns,2*image->rows,MagickTrue,
2026  exception);
2027  if (magnify_image == (Image *) NULL)
2028  return((Image *) NULL);
2029  /*
2030  Magnify image.
2031  */
2032  status=MagickTrue;
2033  progress=0;
2034  image_view=AcquireVirtualCacheView(image,exception);
2035  magnify_view=AcquireAuthenticCacheView(magnify_image,exception);
2036 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2037  #pragma omp parallel for schedule(static) shared(progress,status) \
2038  magick_number_threads(image,magnify_image,image->rows,1)
2039 #endif
2040  for (y=0; y < (ssize_t) image->rows; y++)
2041  {
2042  IndexPacket
2043  *magick_restrict magnify_indexes;
2044 
2045  PixelPacket
2046  *magick_restrict q;
2047 
2048  ssize_t
2049  x;
2050 
2051  if (status == MagickFalse)
2052  continue;
2053  q=QueueCacheViewAuthenticPixels(magnify_view,0,2*y,magnify_image->columns,2,
2054  exception);
2055  if (q == (PixelPacket *) NULL)
2056  {
2057  status=MagickFalse;
2058  continue;
2059  }
2060  magnify_indexes=GetCacheViewAuthenticIndexQueue(magnify_view);
2061  for (x=0; x < (ssize_t) image->columns; x++)
2062  {
2063  const IndexPacket
2064  *magick_restrict indexes;
2065 
2066  const PixelPacket
2067  *magick_restrict p;
2068 
2069  MagickRealType
2070  intensity[9];
2071 
2072  PixelPacket
2073  *magick_restrict r;
2074 
2075  ssize_t
2076  i;
2077 
2078  /*
2079  Magnify this row of pixels.
2080  */
2081  p=GetCacheViewVirtualPixels(image_view,x-1,y-1,3,3,exception);
2082  if (p == (const PixelPacket *) NULL)
2083  {
2084  status=MagickFalse;
2085  continue;
2086  }
2087  indexes=GetCacheViewVirtualIndexQueue(image_view);
2088  for (i=0; i < 9; i++)
2089  intensity[i]=GetPixelIntensity(image,p+i);
2090  r=q;
2091  if ((fabs((double) (intensity[1]-intensity[7])) < MagickEpsilon) ||
2092  (fabs((double) (intensity[3]-intensity[5])) < MagickEpsilon))
2093  {
2094  /*
2095  Clone center pixel.
2096  */
2097  *r=p[4];
2098  r++;
2099  *r=p[4];
2100  r+=(magnify_image->columns-1);
2101  *r=p[4];
2102  r++;
2103  *r=p[4];
2104  }
2105  else
2106  {
2107  /*
2108  Selectively clone pixel.
2109  */
2110  if (fabs((double) (intensity[1]-intensity[3])) < MagickEpsilon)
2111  *r=p[3];
2112  else
2113  *r=p[4];
2114  r++;
2115  if (fabs((double) (intensity[1]-intensity[5])) < MagickEpsilon)
2116  *r=p[5];
2117  else
2118  *r=p[4];
2119  r+=(magnify_image->columns-1);
2120  if (fabs((double) (intensity[3]-intensity[7])) < MagickEpsilon)
2121  *r=p[3];
2122  else
2123  *r=p[4];
2124  r++;
2125  if (fabs((double) (intensity[5]-intensity[7])) < MagickEpsilon)
2126  *r=p[5];
2127  else
2128  *r=p[4];
2129  }
2130  if (indexes != (const IndexPacket *) NULL)
2131  {
2132  IndexPacket
2133  *r;
2134 
2135  /*
2136  Magnify the colormap indexes.
2137  */
2138  r=magnify_indexes;
2139  if ((fabs((double) (intensity[1]-intensity[7])) < MagickEpsilon) ||
2140  (fabs((double) (intensity[3]-intensity[5])) < MagickEpsilon))
2141  {
2142  /*
2143  Clone center pixel.
2144  */
2145  *r=indexes[4];
2146  r++;
2147  *r=indexes[4];
2148  r+=(magnify_image->columns-1);
2149  *r=indexes[4];
2150  r++;
2151  *r=indexes[4];
2152  }
2153  else
2154  {
2155  /*
2156  Selectively clone pixel.
2157  */
2158  if (fabs((double) (intensity[1]-intensity[3])) < MagickEpsilon)
2159  *r=indexes[3];
2160  else
2161  *r=indexes[4];
2162  r++;
2163  if (fabs((double) (intensity[1]-intensity[5])) < MagickEpsilon)
2164  *r=indexes[5];
2165  else
2166  *r=indexes[4];
2167  r+=(magnify_image->columns-1);
2168  if (fabs((double) (intensity[3]-intensity[7])) < MagickEpsilon)
2169  *r=indexes[3];
2170  else
2171  *r=indexes[4];
2172  r++;
2173  if (fabs((double) (intensity[5]-intensity[7])) < MagickEpsilon)
2174  *r=indexes[5];
2175  else
2176  *r=indexes[4];
2177  }
2178  magnify_indexes+=2;
2179  }
2180  q+=2;
2181  }
2182  if (SyncCacheViewAuthenticPixels(magnify_view,exception) == MagickFalse)
2183  status=MagickFalse;
2184  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2185  {
2186  MagickBooleanType
2187  proceed;
2188 
2189 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2190  #pragma omp atomic
2191 #endif
2192  progress++;
2193  proceed=SetImageProgress(image,MagnifyImageTag,progress,image->rows);
2194  if (proceed == MagickFalse)
2195  status=MagickFalse;
2196  }
2197  }
2198  magnify_view=DestroyCacheView(magnify_view);
2199  image_view=DestroyCacheView(image_view);
2200  if (status == MagickFalse)
2201  magnify_image=DestroyImage(magnify_image);
2202  return(magnify_image);
2203 }
2204 
2205 /*
2206 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2207 % %
2208 % %
2209 % %
2210 % M i n i f y I m a g e %
2211 % %
2212 % %
2213 % %
2214 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2215 %
2216 % MinifyImage() is a convenience method that scales an image proportionally to
2217 % half its size.
2218 %
2219 % The format of the MinifyImage method is:
2220 %
2221 % Image *MinifyImage(const Image *image,ExceptionInfo *exception)
2222 %
2223 % A description of each parameter follows:
2224 %
2225 % o image: the image.
2226 %
2227 % o exception: return any errors or warnings in this structure.
2228 %
2229 */
2230 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
2231 {
2232  Image
2233  *minify_image;
2234 
2235  assert(image != (Image *) NULL);
2236  assert(image->signature == MagickCoreSignature);
2237  assert(exception != (ExceptionInfo *) NULL);
2238  assert(exception->signature == MagickCoreSignature);
2239  if (IsEventLogging() != MagickFalse)
2240  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2241  minify_image=ResizeImage(image,image->columns/2,image->rows/2,SplineFilter,
2242  1.0,exception);
2243  return(minify_image);
2244 }
2245 
2246 /*
2247 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2248 % %
2249 % %
2250 % %
2251 % R e s a m p l e I m a g e %
2252 % %
2253 % %
2254 % %
2255 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2256 %
2257 % ResampleImage() resize image in terms of its pixel size, so that when
2258 % displayed at the given resolution it will be the same size in terms of
2259 % real world units as the original image at the original resolution.
2260 %
2261 % The format of the ResampleImage method is:
2262 %
2263 % Image *ResampleImage(Image *image,const double x_resolution,
2264 % const double y_resolution,const FilterTypes filter,const double blur,
2265 % ExceptionInfo *exception)
2266 %
2267 % A description of each parameter follows:
2268 %
2269 % o image: the image to be resized to fit the given resolution.
2270 %
2271 % o x_resolution: the new image x resolution.
2272 %
2273 % o y_resolution: the new image y resolution.
2274 %
2275 % o filter: Image filter to use.
2276 %
2277 % o blur: the blur factor where > 1 is blurry, < 1 is sharp.
2278 %
2279 */
2280 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
2281  const double y_resolution,const FilterTypes filter,const double blur,
2282  ExceptionInfo *exception)
2283 {
2284 #define ResampleImageTag "Resample/Image"
2285 
2286  Image
2287  *resample_image;
2288 
2289  size_t
2290  height,
2291  width;
2292 
2293  /*
2294  Initialize sampled image attributes.
2295  */
2296  assert(image != (const Image *) NULL);
2297  assert(image->signature == MagickCoreSignature);
2298  assert(exception != (ExceptionInfo *) NULL);
2299  assert(exception->signature == MagickCoreSignature);
2300  if (IsEventLogging() != MagickFalse)
2301  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2302  width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
2303  DefaultResolution : image->x_resolution)+0.5);
2304  height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
2305  DefaultResolution : image->y_resolution)+0.5);
2306  resample_image=ResizeImage(image,width,height,filter,blur,exception);
2307  if (resample_image != (Image *) NULL)
2308  {
2309  resample_image->x_resolution=x_resolution;
2310  resample_image->y_resolution=y_resolution;
2311  }
2312  return(resample_image);
2313 }
2314 
2315 /*
2316 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2317 % %
2318 % %
2319 % %
2320 % R e s i z e I m a g e %
2321 % %
2322 % %
2323 % %
2324 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2325 %
2326 % ResizeImage() scales an image to the desired dimensions, using the given
2327 % filter (see AcquireFilterInfo()).
2328 %
2329 % If an undefined filter is given the filter defaults to Mitchell for a
2330 % colormapped image, a image with a matte channel, or if the image is
2331 % enlarged. Otherwise the filter defaults to a Lanczos.
2332 %
2333 % ResizeImage() was inspired by Paul Heckbert's "zoom" program.
2334 %
2335 % The format of the ResizeImage method is:
2336 %
2337 % Image *ResizeImage(Image *image,const size_t columns,
2338 % const size_t rows,const FilterTypes filter,const double blur,
2339 % ExceptionInfo *exception)
2340 %
2341 % A description of each parameter follows:
2342 %
2343 % o image: the image.
2344 %
2345 % o columns: the number of columns in the scaled image.
2346 %
2347 % o rows: the number of rows in the scaled image.
2348 %
2349 % o filter: Image filter to use.
2350 %
2351 % o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
2352 % this to 1.0.
2353 %
2354 % o exception: return any errors or warnings in this structure.
2355 %
2356 */
2357 
2358 typedef struct _ContributionInfo
2359 {
2360  MagickRealType
2361  weight;
2362 
2363  ssize_t
2364  pixel;
2366 
2367 static ContributionInfo **DestroyContributionTLS(
2368  ContributionInfo **contribution)
2369 {
2370  ssize_t
2371  i;
2372 
2373  assert(contribution != (ContributionInfo **) NULL);
2374  for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
2375  if (contribution[i] != (ContributionInfo *) NULL)
2376  contribution[i]=(ContributionInfo *) RelinquishAlignedMemory(
2377  contribution[i]);
2378  contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
2379  return(contribution);
2380 }
2381 
2382 static ContributionInfo **AcquireContributionTLS(const size_t count)
2383 {
2384  ssize_t
2385  i;
2386 
2388  **contribution;
2389 
2390  size_t
2391  number_threads;
2392 
2393  number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
2394  contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
2395  sizeof(*contribution));
2396  if (contribution == (ContributionInfo **) NULL)
2397  return((ContributionInfo **) NULL);
2398  (void) memset(contribution,0,number_threads*sizeof(*contribution));
2399  for (i=0; i < (ssize_t) number_threads; i++)
2400  {
2401  contribution[i]=(ContributionInfo *) MagickAssumeAligned(
2402  AcquireAlignedMemory(count,sizeof(**contribution)));
2403  if (contribution[i] == (ContributionInfo *) NULL)
2404  return(DestroyContributionTLS(contribution));
2405  }
2406  return(contribution);
2407 }
2408 
2409 static MagickBooleanType HorizontalFilter(
2410  const ResizeFilter *magick_restrict resize_filter,
2411  const Image *magick_restrict image,Image *magick_restrict resize_image,
2412  const MagickRealType x_factor,const MagickSizeType span,
2413  MagickOffsetType *magick_restrict offset,ExceptionInfo *exception)
2414 {
2415 #define ResizeImageTag "Resize/Image"
2416 
2417  CacheView
2418  *image_view,
2419  *resize_view;
2420 
2421  ClassType
2422  storage_class;
2423 
2425  **magick_restrict contributions;
2426 
2427  MagickBooleanType
2428  status;
2429 
2431  zero;
2432 
2433  MagickRealType
2434  scale,
2435  support;
2436 
2437  ssize_t
2438  x;
2439 
2440  /*
2441  Apply filter to resize horizontally from image to resize image.
2442  */
2443  scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
2444  support=scale*GetResizeFilterSupport(resize_filter);
2445  storage_class=support > 0.5 ? DirectClass : image->storage_class;
2446  if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2447  {
2448  InheritException(exception,&resize_image->exception);
2449  return(MagickFalse);
2450  }
2451  if (support < 0.5)
2452  {
2453  /*
2454  Support too small even for nearest neighbour: Reduce to point
2455  sampling.
2456  */
2457  support=(MagickRealType) 0.5;
2458  scale=1.0;
2459  }
2460  contributions=AcquireContributionTLS((size_t) (2.0*support+3.0));
2461  if (contributions == (ContributionInfo **) NULL)
2462  {
2463  (void) ThrowMagickException(exception,GetMagickModule(),
2464  ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2465  return(MagickFalse);
2466  }
2467  status=MagickTrue;
2468  scale=PerceptibleReciprocal(scale);
2469  (void) memset(&zero,0,sizeof(zero));
2470  image_view=AcquireVirtualCacheView(image,exception);
2471  resize_view=AcquireAuthenticCacheView(resize_image,exception);
2472 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2473  #pragma omp parallel for schedule(static) shared(status,offset) \
2474  magick_number_threads(image,resize_image,resize_image->columns,1)
2475 #endif
2476  for (x=0; x < (ssize_t) resize_image->columns; x++)
2477  {
2478  const int
2479  id = GetOpenMPThreadId();
2480 
2481  MagickRealType
2482  bisect,
2483  density;
2484 
2485  const IndexPacket
2486  *magick_restrict indexes;
2487 
2488  const PixelPacket
2489  *magick_restrict p;
2490 
2492  *magick_restrict contribution;
2493 
2494  IndexPacket
2495  *magick_restrict resize_indexes;
2496 
2497  PixelPacket
2498  *magick_restrict q;
2499 
2500  ssize_t
2501  y;
2502 
2503  ssize_t
2504  n,
2505  start,
2506  stop;
2507 
2508  if (status == MagickFalse)
2509  continue;
2510  bisect=(MagickRealType) (x+0.5)/x_factor+MagickEpsilon;
2511  start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
2512  stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->columns);
2513  density=0.0;
2514  contribution=contributions[id];
2515  for (n=0; n < (stop-start); n++)
2516  {
2517  contribution[n].pixel=start+n;
2518  contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2519  ((MagickRealType) (start+n)-bisect+0.5));
2520  density+=contribution[n].weight;
2521  }
2522  if (n == 0)
2523  continue;
2524  if ((density != 0.0) && (density != 1.0))
2525  {
2526  ssize_t
2527  i;
2528 
2529  /*
2530  Normalize.
2531  */
2532  density=PerceptibleReciprocal(density);
2533  for (i=0; i < n; i++)
2534  contribution[i].weight*=density;
2535  }
2536  p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2537  (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
2538  q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2539  exception);
2540  if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2541  {
2542  status=MagickFalse;
2543  continue;
2544  }
2545  indexes=GetCacheViewVirtualIndexQueue(image_view);
2546  resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2547  for (y=0; y < (ssize_t) resize_image->rows; y++)
2548  {
2550  pixel;
2551 
2552  MagickRealType
2553  alpha;
2554 
2555  ssize_t
2556  i;
2557 
2558  ssize_t
2559  j;
2560 
2561  pixel=zero;
2562  if (image->matte == MagickFalse)
2563  {
2564  for (i=0; i < n; i++)
2565  {
2566  j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2567  (contribution[i].pixel-contribution[0].pixel);
2568  alpha=contribution[i].weight;
2569  pixel.red+=alpha*GetPixelRed(p+j);
2570  pixel.green+=alpha*GetPixelGreen(p+j);
2571  pixel.blue+=alpha*GetPixelBlue(p+j);
2572  pixel.opacity+=alpha*GetPixelOpacity(p+j);
2573  }
2574  SetPixelRed(q,ClampToQuantum(pixel.red));
2575  SetPixelGreen(q,ClampToQuantum(pixel.green));
2576  SetPixelBlue(q,ClampToQuantum(pixel.blue));
2577  SetPixelOpacity(q,ClampToQuantum(pixel.opacity));
2578  if ((image->colorspace == CMYKColorspace) &&
2579  (resize_image->colorspace == CMYKColorspace))
2580  {
2581  for (i=0; i < n; i++)
2582  {
2583  j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2584  (contribution[i].pixel-contribution[0].pixel);
2585  alpha=contribution[i].weight;
2586  pixel.index+=alpha*GetPixelIndex(indexes+j);
2587  }
2588  SetPixelIndex(resize_indexes+y,ClampToQuantum(pixel.index));
2589  }
2590  }
2591  else
2592  {
2593  double
2594  gamma;
2595 
2596  gamma=0.0;
2597  for (i=0; i < n; i++)
2598  {
2599  j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2600  (contribution[i].pixel-contribution[0].pixel);
2601  alpha=contribution[i].weight*QuantumScale*GetPixelAlpha(p+j);
2602  pixel.red+=alpha*GetPixelRed(p+j);
2603  pixel.green+=alpha*GetPixelGreen(p+j);
2604  pixel.blue+=alpha*GetPixelBlue(p+j);
2605  pixel.opacity+=contribution[i].weight*GetPixelOpacity(p+j);
2606  gamma+=alpha;
2607  }
2608  gamma=PerceptibleReciprocal(gamma);
2609  SetPixelRed(q,ClampToQuantum(gamma*pixel.red));
2610  SetPixelGreen(q,ClampToQuantum(gamma*pixel.green));
2611  SetPixelBlue(q,ClampToQuantum(gamma*pixel.blue));
2612  SetPixelOpacity(q,ClampToQuantum(pixel.opacity));
2613  if ((image->colorspace == CMYKColorspace) &&
2614  (resize_image->colorspace == CMYKColorspace))
2615  {
2616  for (i=0; i < n; i++)
2617  {
2618  j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2619  (contribution[i].pixel-contribution[0].pixel);
2620  alpha=contribution[i].weight*QuantumScale*GetPixelAlpha(p+j);
2621  pixel.index+=alpha*GetPixelIndex(indexes+j);
2622  }
2623  SetPixelIndex(resize_indexes+y,ClampToQuantum(gamma*pixel.index));
2624  }
2625  }
2626  if ((resize_image->storage_class == PseudoClass) &&
2627  (image->storage_class == PseudoClass))
2628  {
2629  i=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double) stop-
2630  1.0)+0.5);
2631  j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2632  (contribution[i-start].pixel-contribution[0].pixel);
2633  SetPixelIndex(resize_indexes+y,GetPixelIndex(indexes+j));
2634  }
2635  q++;
2636  }
2637  if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2638  status=MagickFalse;
2639  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2640  {
2641  MagickBooleanType
2642  proceed;
2643 
2644 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2645  #pragma omp atomic
2646 #endif
2647  (*offset)++;
2648  proceed=SetImageProgress(image,ResizeImageTag,*offset,span);
2649  if (proceed == MagickFalse)
2650  status=MagickFalse;
2651  }
2652  }
2653  resize_view=DestroyCacheView(resize_view);
2654  image_view=DestroyCacheView(image_view);
2655  contributions=DestroyContributionTLS(contributions);
2656  return(status);
2657 }
2658 
2659 static MagickBooleanType VerticalFilter(
2660  const ResizeFilter *magick_restrict resize_filter,
2661  const Image *magick_restrict image,Image *magick_restrict resize_image,
2662  const MagickRealType y_factor,const MagickSizeType span,
2663  MagickOffsetType *magick_restrict offset,ExceptionInfo *exception)
2664 {
2665  CacheView
2666  *image_view,
2667  *resize_view;
2668 
2669  ClassType
2670  storage_class;
2671 
2673  **magick_restrict contributions;
2674 
2675  MagickBooleanType
2676  status;
2677 
2679  zero;
2680 
2681  MagickRealType
2682  scale,
2683  support;
2684 
2685  ssize_t
2686  y;
2687 
2688  /*
2689  Apply filter to resize vertically from image to resize image.
2690  */
2691  scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
2692  support=scale*GetResizeFilterSupport(resize_filter);
2693  storage_class=support > 0.5 ? DirectClass : image->storage_class;
2694  if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2695  {
2696  InheritException(exception,&resize_image->exception);
2697  return(MagickFalse);
2698  }
2699  if (support < 0.5)
2700  {
2701  /*
2702  Support too small even for nearest neighbour: Reduce to point
2703  sampling.
2704  */
2705  support=(MagickRealType) 0.5;
2706  scale=1.0;
2707  }
2708  contributions=AcquireContributionTLS((size_t) (2.0*support+3.0));
2709  if (contributions == (ContributionInfo **) NULL)
2710  {
2711  (void) ThrowMagickException(exception,GetMagickModule(),
2712  ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2713  return(MagickFalse);
2714  }
2715  status=MagickTrue;
2716  scale=PerceptibleReciprocal(scale);
2717  (void) memset(&zero,0,sizeof(zero));
2718  image_view=AcquireVirtualCacheView(image,exception);
2719  resize_view=AcquireAuthenticCacheView(resize_image,exception);
2720 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2721  #pragma omp parallel for schedule(static) shared(status,offset) \
2722  magick_number_threads(image,resize_image,resize_image->rows,1)
2723 #endif
2724  for (y=0; y < (ssize_t) resize_image->rows; y++)
2725  {
2726  const int
2727  id = GetOpenMPThreadId();
2728 
2729  MagickRealType
2730  bisect,
2731  density;
2732 
2733  const IndexPacket
2734  *magick_restrict indexes;
2735 
2736  const PixelPacket
2737  *magick_restrict p;
2738 
2740  *magick_restrict contribution;
2741 
2742  IndexPacket
2743  *magick_restrict resize_indexes;
2744 
2745  PixelPacket
2746  *magick_restrict q;
2747 
2748  ssize_t
2749  x;
2750 
2751  ssize_t
2752  n,
2753  start,
2754  stop;
2755 
2756  if (status == MagickFalse)
2757  continue;
2758  bisect=(MagickRealType) (y+0.5)/y_factor+MagickEpsilon;
2759  start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
2760  stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->rows);
2761  density=0.0;
2762  contribution=contributions[id];
2763  for (n=0; n < (stop-start); n++)
2764  {
2765  contribution[n].pixel=start+n;
2766  contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2767  ((MagickRealType) (start+n)-bisect+0.5));
2768  density+=contribution[n].weight;
2769  }
2770  if (n == 0)
2771  continue;
2772  if ((density != 0.0) && (density != 1.0))
2773  {
2774  ssize_t
2775  i;
2776 
2777  /*
2778  Normalize.
2779  */
2780  density=PerceptibleReciprocal(density);
2781  for (i=0; i < n; i++)
2782  contribution[i].weight*=density;
2783  }
2784  p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
2785  image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2786  exception);
2787  q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2788  exception);
2789  if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2790  {
2791  status=MagickFalse;
2792  continue;
2793  }
2794  indexes=GetCacheViewVirtualIndexQueue(image_view);
2795  resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2796  for (x=0; x < (ssize_t) resize_image->columns; x++)
2797  {
2799  pixel;
2800 
2801  MagickRealType
2802  alpha;
2803 
2804  ssize_t
2805  i;
2806 
2807  ssize_t
2808  j;
2809 
2810  pixel=zero;
2811  if (image->matte == MagickFalse)
2812  {
2813  for (i=0; i < n; i++)
2814  {
2815  j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2816  image->columns+x);
2817  alpha=contribution[i].weight;
2818  pixel.red+=alpha*GetPixelRed(p+j);
2819  pixel.green+=alpha*GetPixelGreen(p+j);
2820  pixel.blue+=alpha*GetPixelBlue(p+j);
2821  pixel.opacity+=alpha*GetPixelOpacity(p+j);
2822  }
2823  SetPixelRed(q,ClampToQuantum(pixel.red));
2824  SetPixelGreen(q,ClampToQuantum(pixel.green));
2825  SetPixelBlue(q,ClampToQuantum(pixel.blue));
2826  SetPixelOpacity(q,ClampToQuantum(pixel.opacity));
2827  if ((image->colorspace == CMYKColorspace) &&
2828  (resize_image->colorspace == CMYKColorspace))
2829  {
2830  for (i=0; i < n; i++)
2831  {
2832  j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2833  image->columns+x);
2834  alpha=contribution[i].weight;
2835  pixel.index+=alpha*GetPixelIndex(indexes+j);
2836  }
2837  SetPixelIndex(resize_indexes+x,ClampToQuantum(pixel.index));
2838  }
2839  }
2840  else
2841  {
2842  double
2843  gamma;
2844 
2845  gamma=0.0;
2846  for (i=0; i < n; i++)
2847  {
2848  j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2849  image->columns+x);
2850  alpha=contribution[i].weight*QuantumScale*GetPixelAlpha(p+j);
2851  pixel.red+=alpha*GetPixelRed(p+j);
2852  pixel.green+=alpha*GetPixelGreen(p+j);
2853  pixel.blue+=alpha*GetPixelBlue(p+j);
2854  pixel.opacity+=contribution[i].weight*GetPixelOpacity(p+j);
2855  gamma+=alpha;
2856  }
2857  gamma=PerceptibleReciprocal(gamma);
2858  SetPixelRed(q,ClampToQuantum(gamma*pixel.red));
2859  SetPixelGreen(q,ClampToQuantum(gamma*pixel.green));
2860  SetPixelBlue(q,ClampToQuantum(gamma*pixel.blue));
2861  SetPixelOpacity(q,ClampToQuantum(pixel.opacity));
2862  if ((image->colorspace == CMYKColorspace) &&
2863  (resize_image->colorspace == CMYKColorspace))
2864  {
2865  for (i=0; i < n; i++)
2866  {
2867  j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2868  image->columns+x);
2869  alpha=contribution[i].weight*QuantumScale*GetPixelAlpha(p+j);
2870  pixel.index+=alpha*GetPixelIndex(indexes+j);
2871  }
2872  SetPixelIndex(resize_indexes+x,ClampToQuantum(gamma*pixel.index));
2873  }
2874  }
2875  if ((resize_image->storage_class == PseudoClass) &&
2876  (image->storage_class == PseudoClass))
2877  {
2878  i=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double) stop-
2879  1.0)+0.5);
2880  j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
2881  image->columns+x);
2882  SetPixelIndex(resize_indexes+x,GetPixelIndex(indexes+j));
2883  }
2884  q++;
2885  }
2886  if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2887  status=MagickFalse;
2888  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2889  {
2890  MagickBooleanType
2891  proceed;
2892 
2893 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2894  #pragma omp atomic
2895 #endif
2896  (*offset)++;
2897  proceed=SetImageProgress(image,ResizeImageTag,*offset,span);
2898  if (proceed == MagickFalse)
2899  status=MagickFalse;
2900  }
2901  }
2902  resize_view=DestroyCacheView(resize_view);
2903  image_view=DestroyCacheView(image_view);
2904  contributions=DestroyContributionTLS(contributions);
2905  return(status);
2906 }
2907 
2908 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2909  const size_t rows,const FilterTypes filter,const double blur,
2910  ExceptionInfo *exception)
2911 {
2912  FilterTypes
2913  filter_type;
2914 
2915  Image
2916  *filter_image,
2917  *resize_image;
2918 
2919  MagickOffsetType
2920  offset;
2921 
2922  MagickRealType
2923  x_factor,
2924  y_factor;
2925 
2926  MagickSizeType
2927  span;
2928 
2929  MagickStatusType
2930  status;
2931 
2932  ResizeFilter
2933  *resize_filter;
2934 
2935  /*
2936  Acquire resize image.
2937  */
2938  assert(image != (Image *) NULL);
2939  assert(image->signature == MagickCoreSignature);
2940  assert(exception != (ExceptionInfo *) NULL);
2941  assert(exception->signature == MagickCoreSignature);
2942  if (IsEventLogging() != MagickFalse)
2943  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2944  if ((columns == 0) || (rows == 0))
2945  ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2946  if ((columns == image->columns) && (rows == image->rows) &&
2947  (filter == UndefinedFilter) && (blur == 1.0))
2948  return(CloneImage(image,0,0,MagickTrue,exception));
2949 
2950  /*
2951  Acquire resize filter.
2952  */
2953  x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2954  y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2955 
2956  filter_type=LanczosFilter;
2957  if (filter != UndefinedFilter)
2958  filter_type=filter;
2959  else
2960  if ((x_factor == 1.0) && (y_factor == 1.0))
2961  filter_type=PointFilter;
2962  else
2963  if ((image->storage_class == PseudoClass) ||
2964  (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2965  filter_type=MitchellFilter;
2966  resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2967  exception);
2968 #if defined(MAGICKCORE_OPENCL_SUPPORT)
2969  resize_image=AccelerateResizeImage(image,columns,rows,resize_filter,
2970  exception);
2971  if (resize_image != NULL)
2972  {
2973  resize_filter=DestroyResizeFilter(resize_filter);
2974  return(resize_image);
2975  }
2976 #endif
2977  resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2978  if (resize_image == (Image *) NULL)
2979  {
2980  resize_filter=DestroyResizeFilter(resize_filter);
2981  return(resize_image);
2982  }
2983  if (x_factor > y_factor)
2984  filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2985  else
2986  filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2987  if (filter_image == (Image *) NULL)
2988  {
2989  resize_filter=DestroyResizeFilter(resize_filter);
2990  return(DestroyImage(resize_image));
2991  }
2992  /*
2993  Resize image.
2994  */
2995  offset=0;
2996  if (x_factor > y_factor)
2997  {
2998  span=(MagickSizeType) (filter_image->columns+rows);
2999  status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
3000  &offset,exception);
3001  status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
3002  span,&offset,exception);
3003  }
3004  else
3005  {
3006  span=(MagickSizeType) (filter_image->rows+columns);
3007  status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
3008  &offset,exception);
3009  status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
3010  span,&offset,exception);
3011  }
3012  /*
3013  Free resources.
3014  */
3015  filter_image=DestroyImage(filter_image);
3016  resize_filter=DestroyResizeFilter(resize_filter);
3017  if (status == MagickFalse)
3018  {
3019  resize_image=DestroyImage(resize_image);
3020  return((Image *) NULL);
3021  }
3022  resize_image->type=image->type;
3023  return(resize_image);
3024 }
3025 
3026 /*
3027 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3028 % %
3029 % %
3030 % %
3031 % S a m p l e I m a g e %
3032 % %
3033 % %
3034 % %
3035 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3036 %
3037 % SampleImage() scales an image to the desired dimensions with pixel
3038 % sampling. Unlike other scaling methods, this method does not introduce
3039 % any additional color into the scaled image.
3040 %
3041 % The format of the SampleImage method is:
3042 %
3043 % Image *SampleImage(const Image *image,const size_t columns,
3044 % const size_t rows,ExceptionInfo *exception)
3045 %
3046 % A description of each parameter follows:
3047 %
3048 % o image: the image.
3049 %
3050 % o columns: the number of columns in the sampled image.
3051 %
3052 % o rows: the number of rows in the sampled image.
3053 %
3054 % o exception: return any errors or warnings in this structure.
3055 %
3056 */
3057 MagickExport Image *SampleImage(const Image *image,const size_t columns,
3058  const size_t rows,ExceptionInfo *exception)
3059 {
3060 #define SampleImageTag "Sample/Image"
3061 
3062  CacheView
3063  *image_view,
3064  *sample_view;
3065 
3066  Image
3067  *sample_image;
3068 
3069  MagickBooleanType
3070  status;
3071 
3072  MagickOffsetType
3073  progress;
3074 
3075  ssize_t
3076  x;
3077 
3078  ssize_t
3079  *x_offset,
3080  y;
3081 
3082  PointInfo
3083  sample_offset;
3084 
3085  /*
3086  Initialize sampled image attributes.
3087  */
3088  assert(image != (const Image *) NULL);
3089  assert(image->signature == MagickCoreSignature);
3090  assert(exception != (ExceptionInfo *) NULL);
3091  assert(exception->signature == MagickCoreSignature);
3092  if (IsEventLogging() != MagickFalse)
3093  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3094  if ((columns == 0) || (rows == 0))
3095  ThrowImageException(ImageError,"NegativeOrZeroImageSize");
3096  if ((columns == image->columns) && (rows == image->rows))
3097  return(CloneImage(image,0,0,MagickTrue,exception));
3098  sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
3099  if (sample_image == (Image *) NULL)
3100  return((Image *) NULL);
3101  /*
3102  Check for posible user defined sampling offset Artifact
3103  The default sampling offset is in the mid-point of sample regions.
3104  */
3105  sample_offset.x=sample_offset.y=0.5-MagickEpsilon;
3106  {
3107  const char
3108  *value;
3109 
3110  value=GetImageArtifact(image,"sample:offset");
3111  if (value != (char *) NULL)
3112  {
3113  GeometryInfo
3114  geometry_info;
3115  MagickStatusType
3116  flags;
3117 
3118  (void) ParseGeometry(value,&geometry_info);
3119  flags=ParseGeometry(value,&geometry_info);
3120  sample_offset.x=sample_offset.y=geometry_info.rho/100.0-MagickEpsilon;
3121  if ((flags & SigmaValue) != 0)
3122  sample_offset.y=geometry_info.sigma/100.0-MagickEpsilon;
3123  }
3124  }
3125  /*
3126  Allocate scan line buffer and column offset buffers.
3127  */
3128  x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
3129  sizeof(*x_offset));
3130  if (x_offset == (ssize_t *) NULL)
3131  {
3132  sample_image=DestroyImage(sample_image);
3133  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3134  }
3135  for (x=0; x < (ssize_t) sample_image->columns; x++)
3136  x_offset[x]=(ssize_t) ((((double) x+sample_offset.x)*image->columns)/
3137  sample_image->columns);
3138  /*
3139  Sample each row.
3140  */
3141  status=MagickTrue;
3142  progress=0;
3143  image_view=AcquireVirtualCacheView(image,exception);
3144  sample_view=AcquireAuthenticCacheView(sample_image,exception);
3145 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3146  #pragma omp parallel for schedule(static) shared(status) \
3147  magick_number_threads(image,sample_image,sample_image->rows,1)
3148 #endif
3149  for (y=0; y < (ssize_t) sample_image->rows; y++)
3150  {
3151  const IndexPacket
3152  *magick_restrict indexes;
3153 
3154  const PixelPacket
3155  *magick_restrict p;
3156 
3157  IndexPacket
3158  *magick_restrict sample_indexes;
3159 
3160  PixelPacket
3161  *magick_restrict q;
3162 
3163  ssize_t
3164  x;
3165 
3166  ssize_t
3167  y_offset;
3168 
3169  if (status == MagickFalse)
3170  continue;
3171  y_offset=(ssize_t) ((((double) y+sample_offset.y)*image->rows)/
3172  sample_image->rows);
3173  p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
3174  exception);
3175  q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
3176  exception);
3177  if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3178  {
3179  status=MagickFalse;
3180  continue;
3181  }
3182  indexes=GetCacheViewAuthenticIndexQueue(image_view);
3183  sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
3184  /*
3185  Sample each column.
3186  */
3187  for (x=0; x < (ssize_t) sample_image->columns; x++)
3188  *q++=p[x_offset[x]];
3189  if ((image->storage_class == PseudoClass) ||
3190  (image->colorspace == CMYKColorspace))
3191  for (x=0; x < (ssize_t) sample_image->columns; x++)
3192  SetPixelIndex(sample_indexes+x,GetPixelIndex(indexes+x_offset[x]));
3193  if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
3194  status=MagickFalse;
3195  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3196  {
3197  MagickBooleanType
3198  proceed;
3199 
3200 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3201  #pragma omp atomic
3202 #endif
3203  progress++;
3204  proceed=SetImageProgress(image,SampleImageTag,progress,image->rows);
3205  if (proceed == MagickFalse)
3206  status=MagickFalse;
3207  }
3208  }
3209  image_view=DestroyCacheView(image_view);
3210  sample_view=DestroyCacheView(sample_view);
3211  x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
3212  sample_image->type=image->type;
3213  if (status == MagickFalse)
3214  sample_image=DestroyImage(sample_image);
3215  return(sample_image);
3216 }
3217 
3218 /*
3219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3220 % %
3221 % %
3222 % %
3223 % S c a l e I m a g e %
3224 % %
3225 % %
3226 % %
3227 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3228 %
3229 % ScaleImage() changes the size of an image to the given dimensions.
3230 %
3231 % The format of the ScaleImage method is:
3232 %
3233 % Image *ScaleImage(const Image *image,const size_t columns,
3234 % const size_t rows,ExceptionInfo *exception)
3235 %
3236 % A description of each parameter follows:
3237 %
3238 % o image: the image.
3239 %
3240 % o columns: the number of columns in the scaled image.
3241 %
3242 % o rows: the number of rows in the scaled image.
3243 %
3244 % o exception: return any errors or warnings in this structure.
3245 %
3246 */
3247 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
3248  const size_t rows,ExceptionInfo *exception)
3249 {
3250 #define ScaleImageTag "Scale/Image"
3251 
3252  CacheView
3253  *image_view,
3254  *scale_view;
3255 
3256  Image
3257  *scale_image;
3258 
3259  MagickBooleanType
3260  next_column,
3261  next_row,
3262  proceed,
3263  status;
3264 
3266  pixel,
3267  *scale_scanline,
3268  *scanline,
3269  *x_vector,
3270  *y_vector,
3271  zero;
3272 
3273  MagickRealType
3274  alpha;
3275 
3276  PointInfo
3277  scale,
3278  span;
3279 
3280  ssize_t
3281  i;
3282 
3283  ssize_t
3284  number_rows,
3285  y;
3286 
3287  /*
3288  Initialize scaled image attributes.
3289  */
3290  assert(image != (const Image *) NULL);
3291  assert(image->signature == MagickCoreSignature);
3292  assert(exception != (ExceptionInfo *) NULL);
3293  assert(exception->signature == MagickCoreSignature);
3294  if (IsEventLogging() != MagickFalse)
3295  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3296  if ((columns == 0) || (rows == 0))
3297  return((Image *) NULL);
3298  if ((columns == image->columns) && (rows == image->rows))
3299  return(CloneImage(image,0,0,MagickTrue,exception));
3300  scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
3301  if (scale_image == (Image *) NULL)
3302  return((Image *) NULL);
3303  if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
3304  {
3305  InheritException(exception,&scale_image->exception);
3306  scale_image=DestroyImage(scale_image);
3307  return((Image *) NULL);
3308  }
3309  /*
3310  Allocate memory.
3311  */
3312  x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
3313  sizeof(*x_vector));
3314  scanline=x_vector;
3315  if (image->rows != scale_image->rows)
3316  scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
3317  sizeof(*scanline));
3318  scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
3319  scale_image->columns,sizeof(*scale_scanline));
3320  y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
3321  sizeof(*y_vector));
3322  if ((scanline == (MagickPixelPacket *) NULL) ||
3323  (scale_scanline == (MagickPixelPacket *) NULL) ||
3324  (x_vector == (MagickPixelPacket *) NULL) ||
3325  (y_vector == (MagickPixelPacket *) NULL))
3326  {
3327  if ((image->rows != scale_image->rows) &&
3328  (scanline != (MagickPixelPacket *) NULL))
3329  scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3330  if (scale_scanline != (MagickPixelPacket *) NULL)
3331  scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(
3332  scale_scanline);
3333  if (x_vector != (MagickPixelPacket *) NULL)
3334  x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3335  if (y_vector != (MagickPixelPacket *) NULL)
3336  y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3337  scale_image=DestroyImage(scale_image);
3338  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3339  }
3340  /*
3341  Scale image.
3342  */
3343  number_rows=0;
3344  next_row=MagickTrue;
3345  span.y=1.0;
3346  scale.y=(double) scale_image->rows/(double) image->rows;
3347  (void) memset(y_vector,0,(size_t) image->columns*
3348  sizeof(*y_vector));
3349  GetMagickPixelPacket(image,&pixel);
3350  (void) memset(&zero,0,sizeof(zero));
3351  i=0;
3352  status=MagickTrue;
3353  image_view=AcquireVirtualCacheView(image,exception);
3354  scale_view=AcquireAuthenticCacheView(scale_image,exception);
3355  for (y=0; y < (ssize_t) scale_image->rows; y++)
3356  {
3357  const IndexPacket
3358  *magick_restrict indexes;
3359 
3360  const PixelPacket
3361  *magick_restrict p;
3362 
3363  IndexPacket
3364  *magick_restrict scale_indexes;
3365 
3367  *magick_restrict s,
3368  *magick_restrict t;
3369 
3370  PixelPacket
3371  *magick_restrict q;
3372 
3373  ssize_t
3374  x;
3375 
3376  if (status == MagickFalse)
3377  break;
3378  q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
3379  exception);
3380  if (q == (PixelPacket *) NULL)
3381  {
3382  status=MagickFalse;
3383  break;
3384  }
3385  alpha=1.0;
3386  scale_indexes=GetCacheViewAuthenticIndexQueue(scale_view);
3387  if (scale_image->rows == image->rows)
3388  {
3389  /*
3390  Read a new scanline.
3391  */
3392  p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
3393  exception);
3394  if (p == (const PixelPacket *) NULL)
3395  {
3396  status=MagickFalse;
3397  break;
3398  }
3399  indexes=GetCacheViewVirtualIndexQueue(image_view);
3400  for (x=0; x < (ssize_t) image->columns; x++)
3401  {
3402  if (image->matte != MagickFalse)
3403  alpha=QuantumScale*GetPixelAlpha(p);
3404  x_vector[x].red=(MagickRealType) (alpha*GetPixelRed(p));
3405  x_vector[x].green=(MagickRealType) (alpha*GetPixelGreen(p));
3406  x_vector[x].blue=(MagickRealType) (alpha*GetPixelBlue(p));
3407  if (image->matte != MagickFalse)
3408  x_vector[x].opacity=(MagickRealType) GetPixelOpacity(p);
3409  if (indexes != (IndexPacket *) NULL)
3410  x_vector[x].index=(MagickRealType) (alpha*GetPixelIndex(indexes+x));
3411  p++;
3412  }
3413  }
3414  else
3415  {
3416  /*
3417  Scale Y direction.
3418  */
3419  while (scale.y < span.y)
3420  {
3421  if ((next_row != MagickFalse) &&
3422  (number_rows < (ssize_t) image->rows))
3423  {
3424  /*
3425  Read a new scanline.
3426  */
3427  p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
3428  exception);
3429  if (p == (const PixelPacket *) NULL)
3430  {
3431  status=MagickFalse;
3432  break;
3433  }
3434  indexes=GetCacheViewVirtualIndexQueue(image_view);
3435  for (x=0; x < (ssize_t) image->columns; x++)
3436  {
3437  if (image->matte != MagickFalse)
3438  alpha=QuantumScale*GetPixelAlpha(p);
3439  x_vector[x].red=(MagickRealType) (alpha*GetPixelRed(p));
3440  x_vector[x].green=(MagickRealType) (alpha*GetPixelGreen(p));
3441  x_vector[x].blue=(MagickRealType) (alpha*GetPixelBlue(p));
3442  if (image->matte != MagickFalse)
3443  x_vector[x].opacity=(MagickRealType) GetPixelOpacity(p);
3444  if (indexes != (IndexPacket *) NULL)
3445  x_vector[x].index=(MagickRealType) (alpha*
3446  GetPixelIndex(indexes+x));
3447  p++;
3448  }
3449  number_rows++;
3450  }
3451  for (x=0; x < (ssize_t) image->columns; x++)
3452  {
3453  y_vector[x].red+=scale.y*x_vector[x].red;
3454  y_vector[x].green+=scale.y*x_vector[x].green;
3455  y_vector[x].blue+=scale.y*x_vector[x].blue;
3456  if (scale_image->matte != MagickFalse)
3457  y_vector[x].opacity+=scale.y*x_vector[x].opacity;
3458  if (scale_indexes != (IndexPacket *) NULL)
3459  y_vector[x].index+=scale.y*x_vector[x].index;
3460  }
3461  span.y-=scale.y;
3462  scale.y=(double) scale_image->rows/(double) image->rows;
3463  next_row=MagickTrue;
3464  }
3465  if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
3466  {
3467  /*
3468  Read a new scanline.
3469  */
3470  p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
3471  exception);
3472  if (p == (const PixelPacket *) NULL)
3473  {
3474  status=MagickFalse;
3475  break;
3476  }
3477  indexes=GetCacheViewVirtualIndexQueue(image_view);
3478  for (x=0; x < (ssize_t) image->columns; x++)
3479  {
3480  if (image->matte != MagickFalse)
3481  alpha=QuantumScale*GetPixelAlpha(p);
3482  x_vector[x].red=(MagickRealType) (alpha*GetPixelRed(p));
3483  x_vector[x].green=(MagickRealType) (alpha*GetPixelGreen(p));
3484  x_vector[x].blue=(MagickRealType) (alpha*GetPixelBlue(p));
3485  if (image->matte != MagickFalse)
3486  x_vector[x].opacity=(MagickRealType) GetPixelOpacity(p);
3487  if (indexes != (IndexPacket *) NULL)
3488  x_vector[x].index=(MagickRealType) (alpha*
3489  GetPixelIndex(indexes+x));
3490  p++;
3491  }
3492  number_rows++;
3493  next_row=MagickFalse;
3494  }
3495  s=scanline;
3496  for (x=0; x < (ssize_t) image->columns; x++)
3497  {
3498  pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3499  pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3500  pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3501  if (image->matte != MagickFalse)
3502  pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3503  if (scale_indexes != (IndexPacket *) NULL)
3504  pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3505  s->red=pixel.red;
3506  s->green=pixel.green;
3507  s->blue=pixel.blue;
3508  if (scale_image->matte != MagickFalse)
3509  s->opacity=pixel.opacity;
3510  if (scale_indexes != (IndexPacket *) NULL)
3511  s->index=pixel.index;
3512  s++;
3513  y_vector[x]=zero;
3514  }
3515  scale.y-=span.y;
3516  if (scale.y <= 0)
3517  {
3518  scale.y=(double) scale_image->rows/(double) image->rows;
3519  next_row=MagickTrue;
3520  }
3521  span.y=1.0;
3522  }
3523  if (scale_image->columns == image->columns)
3524  {
3525  /*
3526  Transfer scanline to scaled image.
3527  */
3528  s=scanline;
3529  for (x=0; x < (ssize_t) scale_image->columns; x++)
3530  {
3531  if (scale_image->matte != MagickFalse)
3532  alpha=QuantumScale*GetPixelAlpha(s);
3533  alpha=PerceptibleReciprocal(alpha);
3534  SetPixelRed(q,ClampToQuantum(alpha*s->red));
3535  SetPixelGreen(q,ClampToQuantum(alpha*s->green));
3536  SetPixelBlue(q,ClampToQuantum(alpha*s->blue));
3537  if (scale_image->matte != MagickFalse)
3538  SetPixelOpacity(q,ClampToQuantum(s->opacity));
3539  if (scale_indexes != (IndexPacket *) NULL)
3540  SetPixelIndex(scale_indexes+x,ClampToQuantum(alpha*s->index));
3541  q++;
3542  s++;
3543  }
3544  }
3545  else
3546  {
3547  /*
3548  Scale X direction.
3549  */
3550  pixel=zero;
3551  next_column=MagickFalse;
3552  span.x=1.0;
3553  s=scanline;
3554  t=scale_scanline;
3555  for (x=0; x < (ssize_t) image->columns; x++)
3556  {
3557  scale.x=(double) scale_image->columns/(double) image->columns;
3558  while (scale.x >= span.x)
3559  {
3560  if (next_column != MagickFalse)
3561  {
3562  pixel=zero;
3563  t++;
3564  }
3565  pixel.red+=span.x*s->red;
3566  pixel.green+=span.x*s->green;
3567  pixel.blue+=span.x*s->blue;
3568  if (image->matte != MagickFalse)
3569  pixel.opacity+=span.x*s->opacity;
3570  if (scale_indexes != (IndexPacket *) NULL)
3571  pixel.index+=span.x*s->index;
3572  t->red=pixel.red;
3573  t->green=pixel.green;
3574  t->blue=pixel.blue;
3575  if (scale_image->matte != MagickFalse)
3576  t->opacity=pixel.opacity;
3577  if (scale_indexes != (IndexPacket *) NULL)
3578  t->index=pixel.index;
3579  scale.x-=span.x;
3580  span.x=1.0;
3581  next_column=MagickTrue;
3582  }
3583  if (scale.x > 0)
3584  {
3585  if (next_column != MagickFalse)
3586  {
3587  pixel=zero;
3588  next_column=MagickFalse;
3589  t++;
3590  }
3591  pixel.red+=scale.x*s->red;
3592  pixel.green+=scale.x*s->green;
3593  pixel.blue+=scale.x*s->blue;
3594  if (scale_image->matte != MagickFalse)
3595  pixel.opacity+=scale.x*s->opacity;
3596  if (scale_indexes != (IndexPacket *) NULL)
3597  pixel.index+=scale.x*s->index;
3598  span.x-=scale.x;
3599  }
3600  s++;
3601  }
3602  if (span.x > 0)
3603  {
3604  s--;
3605  pixel.red+=span.x*s->red;
3606  pixel.green+=span.x*s->green;
3607  pixel.blue+=span.x*s->blue;
3608  if (scale_image->matte != MagickFalse)
3609  pixel.opacity+=span.x*s->opacity;
3610  if (scale_indexes != (IndexPacket *) NULL)
3611  pixel.index+=span.x*s->index;
3612  }
3613  if ((next_column == MagickFalse) &&
3614  ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
3615  {
3616  t->red=pixel.red;
3617  t->green=pixel.green;
3618  t->blue=pixel.blue;
3619  if (scale_image->matte != MagickFalse)
3620  t->opacity=pixel.opacity;
3621  if (scale_indexes != (IndexPacket *) NULL)
3622  t->index=pixel.index;
3623  }
3624  /*
3625  Transfer scanline to scaled image.
3626  */
3627  t=scale_scanline;
3628  for (x=0; x < (ssize_t) scale_image->columns; x++)
3629  {
3630  if (scale_image->matte != MagickFalse)
3631  alpha=QuantumScale*GetPixelAlpha(t);
3632  alpha=PerceptibleReciprocal(alpha);
3633  SetPixelRed(q,ClampToQuantum(alpha*t->red));
3634  SetPixelGreen(q,ClampToQuantum(alpha*t->green));
3635  SetPixelBlue(q,ClampToQuantum(alpha*t->blue));
3636  if (scale_image->matte != MagickFalse)
3637  SetPixelOpacity(q,ClampToQuantum(t->opacity));
3638  if (scale_indexes != (IndexPacket *) NULL)
3639  SetPixelIndex(scale_indexes+x,ClampToQuantum(alpha*t->index));
3640  t++;
3641  q++;
3642  }
3643  }
3644  if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
3645  {
3646  status=MagickFalse;
3647  break;
3648  }
3649  proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3650  image->rows);
3651  if (proceed == MagickFalse)
3652  {
3653  status=MagickFalse;
3654  break;
3655  }
3656  }
3657  scale_view=DestroyCacheView(scale_view);
3658  image_view=DestroyCacheView(image_view);
3659  /*
3660  Free allocated memory.
3661  */
3662  y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3663  scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3664  if (scale_image->rows != image->rows)
3665  scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3666  x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3667  scale_image->type=image->type;
3668  if (status == MagickFalse)
3669  scale_image=DestroyImage(scale_image);
3670  return(scale_image);
3671 }
3672 
3673 /*
3674 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3675 % %
3676 % %
3677 % %
3678 % T h u m b n a i l I m a g e %
3679 % %
3680 % %
3681 % %
3682 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3683 %
3684 % ThumbnailImage() changes the size of an image to the given dimensions and
3685 % removes any associated profiles. The goal is to produce small low cost
3686 % thumbnail images suited for display on the Web.
3687 %
3688 % The format of the ThumbnailImage method is:
3689 %
3690 % Image *ThumbnailImage(const Image *image,const size_t columns,
3691 % const size_t rows,ExceptionInfo *exception)
3692 %
3693 % A description of each parameter follows:
3694 %
3695 % o image: the image.
3696 %
3697 % o columns: the number of columns in the scaled image.
3698 %
3699 % o rows: the number of rows in the scaled image.
3700 %
3701 % o exception: return any errors or warnings in this structure.
3702 %
3703 */
3704 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3705  const size_t rows,ExceptionInfo *exception)
3706 {
3707 #define SampleFactor 5
3708 
3709  char
3710  filename[MaxTextExtent],
3711  value[MaxTextExtent];
3712 
3713  const char
3714  *name;
3715 
3716  Image
3717  *thumbnail_image;
3718 
3719  struct stat
3720  attributes;
3721 
3722  assert(image != (Image *) NULL);
3723  assert(image->signature == MagickCoreSignature);
3724  assert(exception != (ExceptionInfo *) NULL);
3725  assert(exception->signature == MagickCoreSignature);
3726  if (IsEventLogging() != MagickFalse)
3727  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3728  thumbnail_image=CloneImage(image,0,0,MagickTrue,exception);
3729  if (thumbnail_image == (Image *) NULL)
3730  return(thumbnail_image);
3731  if ((columns != image->columns) || (rows != image->rows))
3732  {
3733  Image
3734  *clone_image = thumbnail_image;
3735 
3736  ssize_t
3737  x_factor,
3738  y_factor;
3739 
3740  x_factor=(ssize_t) image->columns/columns;
3741  y_factor=(ssize_t) image->rows/rows;
3742  if ((x_factor > 4) && (y_factor > 4))
3743  {
3744  thumbnail_image=SampleImage(clone_image,4*columns,4*rows,exception);
3745  if (thumbnail_image != (Image *) NULL)
3746  {
3747  clone_image=DestroyImage(clone_image);
3748  clone_image=thumbnail_image;
3749  }
3750  }
3751  if ((x_factor > 2) && (y_factor > 2))
3752  {
3753  thumbnail_image=ResizeImage(clone_image,2*columns,2*rows,BoxFilter,
3754  1.0,exception);
3755  if (thumbnail_image != (Image *) NULL)
3756  {
3757  clone_image=DestroyImage(clone_image);
3758  clone_image=thumbnail_image;
3759  }
3760  }
3761  thumbnail_image=ResizeImage(clone_image,columns,rows,image->filter ==
3762  UndefinedFilter ? LanczosSharpFilter : image->filter,1.0,exception);
3763  clone_image=DestroyImage(clone_image);
3764  if (thumbnail_image == (Image *) NULL)
3765  return(thumbnail_image);
3766  }
3767  (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3768  thumbnail_image->depth=8;
3769  thumbnail_image->interlace=NoInterlace;
3770  /*
3771  Strip all profiles except color profiles.
3772  */
3773  ResetImageProfileIterator(thumbnail_image);
3774  for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3775  {
3776  if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3777  {
3778  (void) DeleteImageProfile(thumbnail_image,name);
3779  ResetImageProfileIterator(thumbnail_image);
3780  }
3781  name=GetNextImageProfile(thumbnail_image);
3782  }
3783  (void) DeleteImageProperty(thumbnail_image,"comment");
3784  (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3785  if (strstr(image->magick_filename,"//") == (char *) NULL)
3786  (void) FormatLocaleString(value,MaxTextExtent,"file://%s",
3787  image->magick_filename);
3788  (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3789  GetPathComponent(image->magick_filename,TailPath,filename);
3790  (void) CopyMagickString(value,filename,MaxTextExtent);
3791  if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3792  {
3793  (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3794  attributes.st_mtime);
3795  (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3796  }
3797  (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3798  attributes.st_mtime);
3799  (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
3800  (void) ConcatenateMagickString(value,"B",MaxTextExtent);
3801  (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3802  (void) FormatLocaleString(value,MaxTextExtent,"image/%s",image->magick);
3803  LocaleLower(value);
3804  (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3805  (void) SetImageProperty(thumbnail_image,"software",MagickAuthoritativeURL);
3806  (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3807  image->magick_columns);
3808  (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
3809  (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3810  image->magick_rows);
3811  (void) SetImageProperty(thumbnail_image,"Thumb::Image::Height",value);
3812  (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3813  GetImageListLength(image));
3814  (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3815  return(thumbnail_image);
3816 }
Definition: image.h:152