MagickCore  6.9.12-67
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*
2954  PerceptibleReciprocal((double) image->columns));
2955  y_factor=(MagickRealType) (rows*
2956  PerceptibleReciprocal((double) image->rows));
2957  filter_type=LanczosFilter;
2958  if (filter != UndefinedFilter)
2959  filter_type=filter;
2960  else
2961  if ((x_factor == 1.0) && (y_factor == 1.0))
2962  filter_type=PointFilter;
2963  else
2964  if ((image->storage_class == PseudoClass) ||
2965  (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2966  filter_type=MitchellFilter;
2967  resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2968  exception);
2969 #if defined(MAGICKCORE_OPENCL_SUPPORT)
2970  resize_image=AccelerateResizeImage(image,columns,rows,resize_filter,
2971  exception);
2972  if (resize_image != NULL)
2973  {
2974  resize_filter=DestroyResizeFilter(resize_filter);
2975  return(resize_image);
2976  }
2977 #endif
2978  resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2979  if (resize_image == (Image *) NULL)
2980  {
2981  resize_filter=DestroyResizeFilter(resize_filter);
2982  return(resize_image);
2983  }
2984  if (x_factor > y_factor)
2985  filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2986  else
2987  filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2988  if (filter_image == (Image *) NULL)
2989  {
2990  resize_filter=DestroyResizeFilter(resize_filter);
2991  return(DestroyImage(resize_image));
2992  }
2993  /*
2994  Resize image.
2995  */
2996  offset=0;
2997  if (x_factor > y_factor)
2998  {
2999  span=(MagickSizeType) (filter_image->columns+rows);
3000  status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
3001  &offset,exception);
3002  status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
3003  span,&offset,exception);
3004  }
3005  else
3006  {
3007  span=(MagickSizeType) (filter_image->rows+columns);
3008  status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
3009  &offset,exception);
3010  status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
3011  span,&offset,exception);
3012  }
3013  /*
3014  Free resources.
3015  */
3016  filter_image=DestroyImage(filter_image);
3017  resize_filter=DestroyResizeFilter(resize_filter);
3018  if (status == MagickFalse)
3019  {
3020  resize_image=DestroyImage(resize_image);
3021  return((Image *) NULL);
3022  }
3023  resize_image->type=image->type;
3024  return(resize_image);
3025 }
3026 
3027 /*
3028 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3029 % %
3030 % %
3031 % %
3032 % S a m p l e I m a g e %
3033 % %
3034 % %
3035 % %
3036 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3037 %
3038 % SampleImage() scales an image to the desired dimensions with pixel
3039 % sampling. Unlike other scaling methods, this method does not introduce
3040 % any additional color into the scaled image.
3041 %
3042 % The format of the SampleImage method is:
3043 %
3044 % Image *SampleImage(const Image *image,const size_t columns,
3045 % const size_t rows,ExceptionInfo *exception)
3046 %
3047 % A description of each parameter follows:
3048 %
3049 % o image: the image.
3050 %
3051 % o columns: the number of columns in the sampled image.
3052 %
3053 % o rows: the number of rows in the sampled image.
3054 %
3055 % o exception: return any errors or warnings in this structure.
3056 %
3057 */
3058 MagickExport Image *SampleImage(const Image *image,const size_t columns,
3059  const size_t rows,ExceptionInfo *exception)
3060 {
3061 #define SampleImageTag "Sample/Image"
3062 
3063  CacheView
3064  *image_view,
3065  *sample_view;
3066 
3067  Image
3068  *sample_image;
3069 
3070  MagickBooleanType
3071  status;
3072 
3073  MagickOffsetType
3074  progress;
3075 
3076  ssize_t
3077  x;
3078 
3079  ssize_t
3080  *x_offset,
3081  y;
3082 
3083  PointInfo
3084  sample_offset;
3085 
3086  /*
3087  Initialize sampled image attributes.
3088  */
3089  assert(image != (const Image *) NULL);
3090  assert(image->signature == MagickCoreSignature);
3091  assert(exception != (ExceptionInfo *) NULL);
3092  assert(exception->signature == MagickCoreSignature);
3093  if (IsEventLogging() != MagickFalse)
3094  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3095  if ((columns == 0) || (rows == 0))
3096  ThrowImageException(ImageError,"NegativeOrZeroImageSize");
3097  if ((columns == image->columns) && (rows == image->rows))
3098  return(CloneImage(image,0,0,MagickTrue,exception));
3099  sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
3100  if (sample_image == (Image *) NULL)
3101  return((Image *) NULL);
3102  /*
3103  Check for posible user defined sampling offset Artifact
3104  The default sampling offset is in the mid-point of sample regions.
3105  */
3106  sample_offset.x=sample_offset.y=0.5-MagickEpsilon;
3107  {
3108  const char
3109  *value;
3110 
3111  value=GetImageArtifact(image,"sample:offset");
3112  if (value != (char *) NULL)
3113  {
3114  GeometryInfo
3115  geometry_info;
3116  MagickStatusType
3117  flags;
3118 
3119  (void) ParseGeometry(value,&geometry_info);
3120  flags=ParseGeometry(value,&geometry_info);
3121  sample_offset.x=sample_offset.y=geometry_info.rho/100.0-MagickEpsilon;
3122  if ((flags & SigmaValue) != 0)
3123  sample_offset.y=geometry_info.sigma/100.0-MagickEpsilon;
3124  }
3125  }
3126  /*
3127  Allocate scan line buffer and column offset buffers.
3128  */
3129  x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
3130  sizeof(*x_offset));
3131  if (x_offset == (ssize_t *) NULL)
3132  {
3133  sample_image=DestroyImage(sample_image);
3134  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3135  }
3136  for (x=0; x < (ssize_t) sample_image->columns; x++)
3137  x_offset[x]=(ssize_t) ((((double) x+sample_offset.x)*image->columns)/
3138  sample_image->columns);
3139  /*
3140  Sample each row.
3141  */
3142  status=MagickTrue;
3143  progress=0;
3144  image_view=AcquireVirtualCacheView(image,exception);
3145  sample_view=AcquireAuthenticCacheView(sample_image,exception);
3146 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3147  #pragma omp parallel for schedule(static) shared(status) \
3148  magick_number_threads(image,sample_image,sample_image->rows,1)
3149 #endif
3150  for (y=0; y < (ssize_t) sample_image->rows; y++)
3151  {
3152  const IndexPacket
3153  *magick_restrict indexes;
3154 
3155  const PixelPacket
3156  *magick_restrict p;
3157 
3158  IndexPacket
3159  *magick_restrict sample_indexes;
3160 
3161  PixelPacket
3162  *magick_restrict q;
3163 
3164  ssize_t
3165  x;
3166 
3167  ssize_t
3168  y_offset;
3169 
3170  if (status == MagickFalse)
3171  continue;
3172  y_offset=(ssize_t) ((((double) y+sample_offset.y)*image->rows)/
3173  sample_image->rows);
3174  p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
3175  exception);
3176  q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
3177  exception);
3178  if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3179  {
3180  status=MagickFalse;
3181  continue;
3182  }
3183  indexes=GetCacheViewAuthenticIndexQueue(image_view);
3184  sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
3185  /*
3186  Sample each column.
3187  */
3188  for (x=0; x < (ssize_t) sample_image->columns; x++)
3189  *q++=p[x_offset[x]];
3190  if ((image->storage_class == PseudoClass) ||
3191  (image->colorspace == CMYKColorspace))
3192  for (x=0; x < (ssize_t) sample_image->columns; x++)
3193  SetPixelIndex(sample_indexes+x,GetPixelIndex(indexes+x_offset[x]));
3194  if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
3195  status=MagickFalse;
3196  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3197  {
3198  MagickBooleanType
3199  proceed;
3200 
3201 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3202  #pragma omp atomic
3203 #endif
3204  progress++;
3205  proceed=SetImageProgress(image,SampleImageTag,progress,image->rows);
3206  if (proceed == MagickFalse)
3207  status=MagickFalse;
3208  }
3209  }
3210  image_view=DestroyCacheView(image_view);
3211  sample_view=DestroyCacheView(sample_view);
3212  x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
3213  sample_image->type=image->type;
3214  if (status == MagickFalse)
3215  sample_image=DestroyImage(sample_image);
3216  return(sample_image);
3217 }
3218 
3219 /*
3220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3221 % %
3222 % %
3223 % %
3224 % S c a l e I m a g e %
3225 % %
3226 % %
3227 % %
3228 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3229 %
3230 % ScaleImage() changes the size of an image to the given dimensions.
3231 %
3232 % The format of the ScaleImage method is:
3233 %
3234 % Image *ScaleImage(const Image *image,const size_t columns,
3235 % const size_t rows,ExceptionInfo *exception)
3236 %
3237 % A description of each parameter follows:
3238 %
3239 % o image: the image.
3240 %
3241 % o columns: the number of columns in the scaled image.
3242 %
3243 % o rows: the number of rows in the scaled image.
3244 %
3245 % o exception: return any errors or warnings in this structure.
3246 %
3247 */
3248 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
3249  const size_t rows,ExceptionInfo *exception)
3250 {
3251 #define ScaleImageTag "Scale/Image"
3252 
3253  CacheView
3254  *image_view,
3255  *scale_view;
3256 
3257  Image
3258  *scale_image;
3259 
3260  MagickBooleanType
3261  next_column,
3262  next_row,
3263  proceed,
3264  status;
3265 
3267  pixel,
3268  *scale_scanline,
3269  *scanline,
3270  *x_vector,
3271  *y_vector,
3272  zero;
3273 
3274  MagickRealType
3275  alpha;
3276 
3277  PointInfo
3278  scale,
3279  span;
3280 
3281  ssize_t
3282  i;
3283 
3284  ssize_t
3285  number_rows,
3286  y;
3287 
3288  /*
3289  Initialize scaled image attributes.
3290  */
3291  assert(image != (const Image *) NULL);
3292  assert(image->signature == MagickCoreSignature);
3293  assert(exception != (ExceptionInfo *) NULL);
3294  assert(exception->signature == MagickCoreSignature);
3295  if (IsEventLogging() != MagickFalse)
3296  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3297  if ((columns == 0) || (rows == 0))
3298  return((Image *) NULL);
3299  if ((columns == image->columns) && (rows == image->rows))
3300  return(CloneImage(image,0,0,MagickTrue,exception));
3301  scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
3302  if (scale_image == (Image *) NULL)
3303  return((Image *) NULL);
3304  if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
3305  {
3306  InheritException(exception,&scale_image->exception);
3307  scale_image=DestroyImage(scale_image);
3308  return((Image *) NULL);
3309  }
3310  /*
3311  Allocate memory.
3312  */
3313  x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
3314  sizeof(*x_vector));
3315  scanline=x_vector;
3316  if (image->rows != scale_image->rows)
3317  scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
3318  sizeof(*scanline));
3319  scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
3320  scale_image->columns,sizeof(*scale_scanline));
3321  y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
3322  sizeof(*y_vector));
3323  if ((scanline == (MagickPixelPacket *) NULL) ||
3324  (scale_scanline == (MagickPixelPacket *) NULL) ||
3325  (x_vector == (MagickPixelPacket *) NULL) ||
3326  (y_vector == (MagickPixelPacket *) NULL))
3327  {
3328  if ((image->rows != scale_image->rows) &&
3329  (scanline != (MagickPixelPacket *) NULL))
3330  scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3331  if (scale_scanline != (MagickPixelPacket *) NULL)
3332  scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(
3333  scale_scanline);
3334  if (x_vector != (MagickPixelPacket *) NULL)
3335  x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3336  if (y_vector != (MagickPixelPacket *) NULL)
3337  y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3338  scale_image=DestroyImage(scale_image);
3339  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3340  }
3341  /*
3342  Scale image.
3343  */
3344  number_rows=0;
3345  next_row=MagickTrue;
3346  span.y=1.0;
3347  scale.y=(double) scale_image->rows/(double) image->rows;
3348  (void) memset(y_vector,0,(size_t) image->columns*
3349  sizeof(*y_vector));
3350  GetMagickPixelPacket(image,&pixel);
3351  (void) memset(&zero,0,sizeof(zero));
3352  i=0;
3353  status=MagickTrue;
3354  image_view=AcquireVirtualCacheView(image,exception);
3355  scale_view=AcquireAuthenticCacheView(scale_image,exception);
3356  for (y=0; y < (ssize_t) scale_image->rows; y++)
3357  {
3358  const IndexPacket
3359  *magick_restrict indexes;
3360 
3361  const PixelPacket
3362  *magick_restrict p;
3363 
3364  IndexPacket
3365  *magick_restrict scale_indexes;
3366 
3368  *magick_restrict s,
3369  *magick_restrict t;
3370 
3371  PixelPacket
3372  *magick_restrict q;
3373 
3374  ssize_t
3375  x;
3376 
3377  if (status == MagickFalse)
3378  break;
3379  q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
3380  exception);
3381  if (q == (PixelPacket *) NULL)
3382  {
3383  status=MagickFalse;
3384  break;
3385  }
3386  alpha=1.0;
3387  scale_indexes=GetCacheViewAuthenticIndexQueue(scale_view);
3388  if (scale_image->rows == image->rows)
3389  {
3390  /*
3391  Read a new scanline.
3392  */
3393  p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
3394  exception);
3395  if (p == (const PixelPacket *) NULL)
3396  {
3397  status=MagickFalse;
3398  break;
3399  }
3400  indexes=GetCacheViewVirtualIndexQueue(image_view);
3401  for (x=0; x < (ssize_t) image->columns; x++)
3402  {
3403  if (image->matte != MagickFalse)
3404  alpha=QuantumScale*GetPixelAlpha(p);
3405  x_vector[x].red=(MagickRealType) (alpha*GetPixelRed(p));
3406  x_vector[x].green=(MagickRealType) (alpha*GetPixelGreen(p));
3407  x_vector[x].blue=(MagickRealType) (alpha*GetPixelBlue(p));
3408  if (image->matte != MagickFalse)
3409  x_vector[x].opacity=(MagickRealType) GetPixelOpacity(p);
3410  if (indexes != (IndexPacket *) NULL)
3411  x_vector[x].index=(MagickRealType) (alpha*GetPixelIndex(indexes+x));
3412  p++;
3413  }
3414  }
3415  else
3416  {
3417  /*
3418  Scale Y direction.
3419  */
3420  while (scale.y < span.y)
3421  {
3422  if ((next_row != MagickFalse) &&
3423  (number_rows < (ssize_t) image->rows))
3424  {
3425  /*
3426  Read a new scanline.
3427  */
3428  p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
3429  exception);
3430  if (p == (const PixelPacket *) NULL)
3431  {
3432  status=MagickFalse;
3433  break;
3434  }
3435  indexes=GetCacheViewVirtualIndexQueue(image_view);
3436  for (x=0; x < (ssize_t) image->columns; x++)
3437  {
3438  if (image->matte != MagickFalse)
3439  alpha=QuantumScale*GetPixelAlpha(p);
3440  x_vector[x].red=(MagickRealType) (alpha*GetPixelRed(p));
3441  x_vector[x].green=(MagickRealType) (alpha*GetPixelGreen(p));
3442  x_vector[x].blue=(MagickRealType) (alpha*GetPixelBlue(p));
3443  if (image->matte != MagickFalse)
3444  x_vector[x].opacity=(MagickRealType) GetPixelOpacity(p);
3445  if (indexes != (IndexPacket *) NULL)
3446  x_vector[x].index=(MagickRealType) (alpha*
3447  GetPixelIndex(indexes+x));
3448  p++;
3449  }
3450  number_rows++;
3451  }
3452  for (x=0; x < (ssize_t) image->columns; x++)
3453  {
3454  y_vector[x].red+=scale.y*x_vector[x].red;
3455  y_vector[x].green+=scale.y*x_vector[x].green;
3456  y_vector[x].blue+=scale.y*x_vector[x].blue;
3457  if (scale_image->matte != MagickFalse)
3458  y_vector[x].opacity+=scale.y*x_vector[x].opacity;
3459  if (scale_indexes != (IndexPacket *) NULL)
3460  y_vector[x].index+=scale.y*x_vector[x].index;
3461  }
3462  span.y-=scale.y;
3463  scale.y=(double) scale_image->rows/(double) image->rows;
3464  next_row=MagickTrue;
3465  }
3466  if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
3467  {
3468  /*
3469  Read a new scanline.
3470  */
3471  p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
3472  exception);
3473  if (p == (const PixelPacket *) NULL)
3474  {
3475  status=MagickFalse;
3476  break;
3477  }
3478  indexes=GetCacheViewVirtualIndexQueue(image_view);
3479  for (x=0; x < (ssize_t) image->columns; x++)
3480  {
3481  if (image->matte != MagickFalse)
3482  alpha=QuantumScale*GetPixelAlpha(p);
3483  x_vector[x].red=(MagickRealType) (alpha*GetPixelRed(p));
3484  x_vector[x].green=(MagickRealType) (alpha*GetPixelGreen(p));
3485  x_vector[x].blue=(MagickRealType) (alpha*GetPixelBlue(p));
3486  if (image->matte != MagickFalse)
3487  x_vector[x].opacity=(MagickRealType) GetPixelOpacity(p);
3488  if (indexes != (IndexPacket *) NULL)
3489  x_vector[x].index=(MagickRealType) (alpha*
3490  GetPixelIndex(indexes+x));
3491  p++;
3492  }
3493  number_rows++;
3494  next_row=MagickFalse;
3495  }
3496  s=scanline;
3497  for (x=0; x < (ssize_t) image->columns; x++)
3498  {
3499  pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3500  pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3501  pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3502  if (image->matte != MagickFalse)
3503  pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3504  if (scale_indexes != (IndexPacket *) NULL)
3505  pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3506  s->red=pixel.red;
3507  s->green=pixel.green;
3508  s->blue=pixel.blue;
3509  if (scale_image->matte != MagickFalse)
3510  s->opacity=pixel.opacity;
3511  if (scale_indexes != (IndexPacket *) NULL)
3512  s->index=pixel.index;
3513  s++;
3514  y_vector[x]=zero;
3515  }
3516  scale.y-=span.y;
3517  if (scale.y <= 0)
3518  {
3519  scale.y=(double) scale_image->rows/(double) image->rows;
3520  next_row=MagickTrue;
3521  }
3522  span.y=1.0;
3523  }
3524  if (scale_image->columns == image->columns)
3525  {
3526  /*
3527  Transfer scanline to scaled image.
3528  */
3529  s=scanline;
3530  for (x=0; x < (ssize_t) scale_image->columns; x++)
3531  {
3532  if (scale_image->matte != MagickFalse)
3533  alpha=QuantumScale*GetPixelAlpha(s);
3534  alpha=PerceptibleReciprocal(alpha);
3535  SetPixelRed(q,ClampToQuantum(alpha*s->red));
3536  SetPixelGreen(q,ClampToQuantum(alpha*s->green));
3537  SetPixelBlue(q,ClampToQuantum(alpha*s->blue));
3538  if (scale_image->matte != MagickFalse)
3539  SetPixelOpacity(q,ClampToQuantum(s->opacity));
3540  if (scale_indexes != (IndexPacket *) NULL)
3541  SetPixelIndex(scale_indexes+x,ClampToQuantum(alpha*s->index));
3542  q++;
3543  s++;
3544  }
3545  }
3546  else
3547  {
3548  /*
3549  Scale X direction.
3550  */
3551  pixel=zero;
3552  next_column=MagickFalse;
3553  span.x=1.0;
3554  s=scanline;
3555  t=scale_scanline;
3556  for (x=0; x < (ssize_t) image->columns; x++)
3557  {
3558  scale.x=(double) scale_image->columns/(double) image->columns;
3559  while (scale.x >= span.x)
3560  {
3561  if (next_column != MagickFalse)
3562  {
3563  pixel=zero;
3564  t++;
3565  }
3566  pixel.red+=span.x*s->red;
3567  pixel.green+=span.x*s->green;
3568  pixel.blue+=span.x*s->blue;
3569  if (image->matte != MagickFalse)
3570  pixel.opacity+=span.x*s->opacity;
3571  if (scale_indexes != (IndexPacket *) NULL)
3572  pixel.index+=span.x*s->index;
3573  t->red=pixel.red;
3574  t->green=pixel.green;
3575  t->blue=pixel.blue;
3576  if (scale_image->matte != MagickFalse)
3577  t->opacity=pixel.opacity;
3578  if (scale_indexes != (IndexPacket *) NULL)
3579  t->index=pixel.index;
3580  scale.x-=span.x;
3581  span.x=1.0;
3582  next_column=MagickTrue;
3583  }
3584  if (scale.x > 0)
3585  {
3586  if (next_column != MagickFalse)
3587  {
3588  pixel=zero;
3589  next_column=MagickFalse;
3590  t++;
3591  }
3592  pixel.red+=scale.x*s->red;
3593  pixel.green+=scale.x*s->green;
3594  pixel.blue+=scale.x*s->blue;
3595  if (scale_image->matte != MagickFalse)
3596  pixel.opacity+=scale.x*s->opacity;
3597  if (scale_indexes != (IndexPacket *) NULL)
3598  pixel.index+=scale.x*s->index;
3599  span.x-=scale.x;
3600  }
3601  s++;
3602  }
3603  if (span.x > 0)
3604  {
3605  s--;
3606  pixel.red+=span.x*s->red;
3607  pixel.green+=span.x*s->green;
3608  pixel.blue+=span.x*s->blue;
3609  if (scale_image->matte != MagickFalse)
3610  pixel.opacity+=span.x*s->opacity;
3611  if (scale_indexes != (IndexPacket *) NULL)
3612  pixel.index+=span.x*s->index;
3613  }
3614  if ((next_column == MagickFalse) &&
3615  ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
3616  {
3617  t->red=pixel.red;
3618  t->green=pixel.green;
3619  t->blue=pixel.blue;
3620  if (scale_image->matte != MagickFalse)
3621  t->opacity=pixel.opacity;
3622  if (scale_indexes != (IndexPacket *) NULL)
3623  t->index=pixel.index;
3624  }
3625  /*
3626  Transfer scanline to scaled image.
3627  */
3628  t=scale_scanline;
3629  for (x=0; x < (ssize_t) scale_image->columns; x++)
3630  {
3631  if (scale_image->matte != MagickFalse)
3632  alpha=QuantumScale*GetPixelAlpha(t);
3633  alpha=PerceptibleReciprocal(alpha);
3634  SetPixelRed(q,ClampToQuantum(alpha*t->red));
3635  SetPixelGreen(q,ClampToQuantum(alpha*t->green));
3636  SetPixelBlue(q,ClampToQuantum(alpha*t->blue));
3637  if (scale_image->matte != MagickFalse)
3638  SetPixelOpacity(q,ClampToQuantum(t->opacity));
3639  if (scale_indexes != (IndexPacket *) NULL)
3640  SetPixelIndex(scale_indexes+x,ClampToQuantum(alpha*t->index));
3641  t++;
3642  q++;
3643  }
3644  }
3645  if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
3646  {
3647  status=MagickFalse;
3648  break;
3649  }
3650  proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3651  image->rows);
3652  if (proceed == MagickFalse)
3653  {
3654  status=MagickFalse;
3655  break;
3656  }
3657  }
3658  scale_view=DestroyCacheView(scale_view);
3659  image_view=DestroyCacheView(image_view);
3660  /*
3661  Free allocated memory.
3662  */
3663  y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3664  scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3665  if (scale_image->rows != image->rows)
3666  scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3667  x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3668  scale_image->type=image->type;
3669  if (status == MagickFalse)
3670  scale_image=DestroyImage(scale_image);
3671  return(scale_image);
3672 }
3673 
3674 /*
3675 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3676 % %
3677 % %
3678 % %
3679 % T h u m b n a i l I m a g e %
3680 % %
3681 % %
3682 % %
3683 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3684 %
3685 % ThumbnailImage() changes the size of an image to the given dimensions and
3686 % removes any associated profiles. The goal is to produce small low cost
3687 % thumbnail images suited for display on the Web.
3688 %
3689 % The format of the ThumbnailImage method is:
3690 %
3691 % Image *ThumbnailImage(const Image *image,const size_t columns,
3692 % const size_t rows,ExceptionInfo *exception)
3693 %
3694 % A description of each parameter follows:
3695 %
3696 % o image: the image.
3697 %
3698 % o columns: the number of columns in the scaled image.
3699 %
3700 % o rows: the number of rows in the scaled image.
3701 %
3702 % o exception: return any errors or warnings in this structure.
3703 %
3704 */
3705 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3706  const size_t rows,ExceptionInfo *exception)
3707 {
3708 #define SampleFactor 5
3709 
3710  char
3711  filename[MaxTextExtent],
3712  value[MaxTextExtent];
3713 
3714  const char
3715  *name;
3716 
3717  Image
3718  *thumbnail_image;
3719 
3720  struct stat
3721  attributes;
3722 
3723  assert(image != (Image *) NULL);
3724  assert(image->signature == MagickCoreSignature);
3725  assert(exception != (ExceptionInfo *) NULL);
3726  assert(exception->signature == MagickCoreSignature);
3727  if (IsEventLogging() != MagickFalse)
3728  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3729  thumbnail_image=CloneImage(image,0,0,MagickTrue,exception);
3730  if (thumbnail_image == (Image *) NULL)
3731  return(thumbnail_image);
3732  if ((columns != image->columns) || (rows != image->rows))
3733  {
3734  Image
3735  *clone_image = thumbnail_image;
3736 
3737  ssize_t
3738  x_factor,
3739  y_factor;
3740 
3741  x_factor=(ssize_t) image->columns/columns;
3742  y_factor=(ssize_t) image->rows/rows;
3743  if ((x_factor > 4) && (y_factor > 4))
3744  {
3745  thumbnail_image=SampleImage(clone_image,4*columns,4*rows,exception);
3746  if (thumbnail_image != (Image *) NULL)
3747  {
3748  clone_image=DestroyImage(clone_image);
3749  clone_image=thumbnail_image;
3750  }
3751  }
3752  if ((x_factor > 2) && (y_factor > 2))
3753  {
3754  thumbnail_image=ResizeImage(clone_image,2*columns,2*rows,BoxFilter,
3755  1.0,exception);
3756  if (thumbnail_image != (Image *) NULL)
3757  {
3758  clone_image=DestroyImage(clone_image);
3759  clone_image=thumbnail_image;
3760  }
3761  }
3762  thumbnail_image=ResizeImage(clone_image,columns,rows,image->filter ==
3763  UndefinedFilter ? LanczosSharpFilter : image->filter,1.0,exception);
3764  clone_image=DestroyImage(clone_image);
3765  if (thumbnail_image == (Image *) NULL)
3766  return(thumbnail_image);
3767  }
3768  (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3769  thumbnail_image->depth=8;
3770  thumbnail_image->interlace=NoInterlace;
3771  /*
3772  Strip all profiles except color profiles.
3773  */
3774  ResetImageProfileIterator(thumbnail_image);
3775  for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3776  {
3777  if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3778  {
3779  (void) DeleteImageProfile(thumbnail_image,name);
3780  ResetImageProfileIterator(thumbnail_image);
3781  }
3782  name=GetNextImageProfile(thumbnail_image);
3783  }
3784  (void) DeleteImageProperty(thumbnail_image,"comment");
3785  (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3786  if (strstr(image->magick_filename,"//") == (char *) NULL)
3787  (void) FormatLocaleString(value,MaxTextExtent,"file://%s",
3788  image->magick_filename);
3789  (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3790  GetPathComponent(image->magick_filename,TailPath,filename);
3791  (void) CopyMagickString(value,filename,MaxTextExtent);
3792  if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3793  {
3794  (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3795  attributes.st_mtime);
3796  (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3797  }
3798  (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3799  attributes.st_mtime);
3800  (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
3801  (void) ConcatenateMagickString(value,"B",MaxTextExtent);
3802  (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3803  (void) FormatLocaleString(value,MaxTextExtent,"image/%s",image->magick);
3804  LocaleLower(value);
3805  (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3806  (void) SetImageProperty(thumbnail_image,"software",MagickAuthoritativeURL);
3807  (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3808  image->magick_columns);
3809  (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
3810  (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3811  image->magick_rows);
3812  (void) SetImageProperty(thumbnail_image,"Thumb::Image::Height",value);
3813  (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3814  GetImageListLength(image));
3815  (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3816  return(thumbnail_image);
3817 }
Definition: image.h:152