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