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