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