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