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