MagickCore  7.0.3
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-2019 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/script/license.php %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 
39 /*
40  Include declarations.
41 */
42 #include "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) memset(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) 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  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1786  {
1787  PixelChannel
1788  channel;
1789 
1790  PixelTrait
1791  resize_traits,
1792  traits;
1793 
1794  channel=GetPixelChannelChannel(image,i);
1795  traits=GetPixelChannelTraits(image,channel);
1796  resize_traits=GetPixelChannelTraits(resize_image,channel);
1797  if ((traits == UndefinedPixelTrait) ||
1798  (resize_traits == UndefinedPixelTrait))
1799  continue;
1800  offset.x=((double) x+0.5)*scale.x-0.5;
1801  status=InterpolatePixelChannels(image,image_view,resize_image,method,
1802  offset.x,offset.y,q,exception);
1803  if (status == MagickFalse)
1804  break;
1805  }
1806  q+=GetPixelChannels(resize_image);
1807  }
1808  if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1809  status=MagickFalse;
1810  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1811  {
1813  proceed;
1814 
1815 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1816  #pragma omp atomic
1817 #endif
1818  progress++;
1819  proceed=SetImageProgress(image,InterpolativeResizeImageTag,progress,
1820  image->rows);
1821  if (proceed == MagickFalse)
1822  status=MagickFalse;
1823  }
1824  }
1825  resize_view=DestroyCacheView(resize_view);
1826  image_view=DestroyCacheView(image_view);
1827  if (status == MagickFalse)
1828  resize_image=DestroyImage(resize_image);
1829  return(resize_image);
1830 }
1831 #if defined(MAGICKCORE_LQR_DELEGATE)
1832 
1833 /*
1834 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1835 % %
1836 % %
1837 % %
1838 % L i q u i d R e s c a l e I m a g e %
1839 % %
1840 % %
1841 % %
1842 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1843 %
1844 % LiquidRescaleImage() rescales image with seam carving.
1845 %
1846 % The format of the LiquidRescaleImage method is:
1847 %
1848 % Image *LiquidRescaleImage(const Image *image,const size_t columns,
1849 % const size_t rows,const double delta_x,const double rigidity,
1850 % ExceptionInfo *exception)
1851 %
1852 % A description of each parameter follows:
1853 %
1854 % o image: the image.
1855 %
1856 % o columns: the number of columns in the rescaled image.
1857 %
1858 % o rows: the number of rows in the rescaled image.
1859 %
1860 % o delta_x: maximum seam transversal step (0 means straight seams).
1861 %
1862 % o rigidity: introduce a bias for non-straight seams (typically 0).
1863 %
1864 % o exception: return any errors or warnings in this structure.
1865 %
1866 */
1867 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1868  const size_t rows,const double delta_x,const double rigidity,
1869  ExceptionInfo *exception)
1870 {
1871 #define LiquidRescaleImageTag "Rescale/Image"
1872 
1873  CacheView
1874  *image_view,
1875  *rescale_view;
1876 
1877  gfloat
1878  *packet,
1879  *pixels;
1880 
1881  Image
1882  *rescale_image;
1883 
1884  int
1885  x_offset,
1886  y_offset;
1887 
1888  LqrCarver
1889  *carver;
1890 
1891  LqrRetVal
1892  lqr_status;
1893 
1895  status;
1896 
1897  MemoryInfo
1898  *pixel_info;
1899 
1900  register gfloat
1901  *q;
1902 
1903  ssize_t
1904  y;
1905 
1906  /*
1907  Liquid rescale image.
1908  */
1909  assert(image != (const Image *) NULL);
1910  assert(image->signature == MagickCoreSignature);
1911  if (image->debug != MagickFalse)
1912  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1913  assert(exception != (ExceptionInfo *) NULL);
1914  assert(exception->signature == MagickCoreSignature);
1915  if ((columns == 0) || (rows == 0))
1916  ThrowImageException(ImageError,"NegativeOrZeroImageSize");
1917  if ((columns == image->columns) && (rows == image->rows))
1918  return(CloneImage(image,0,0,MagickTrue,exception));
1919  if ((columns <= 2) || (rows <= 2))
1920  return(ResizeImage(image,columns,rows,image->filter,exception));
1921  pixel_info=AcquireVirtualMemory(image->columns,image->rows*MaxPixelChannels*
1922  sizeof(*pixels));
1923  if (pixel_info == (MemoryInfo *) NULL)
1924  return((Image *) NULL);
1925  pixels=(gfloat *) GetVirtualMemoryBlob(pixel_info);
1926  status=MagickTrue;
1927  q=pixels;
1928  image_view=AcquireVirtualCacheView(image,exception);
1929  for (y=0; y < (ssize_t) image->rows; y++)
1930  {
1931  register const Quantum
1932  *magick_restrict p;
1933 
1934  register ssize_t
1935  x;
1936 
1937  if (status == MagickFalse)
1938  continue;
1939  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1940  if (p == (const Quantum *) NULL)
1941  {
1942  status=MagickFalse;
1943  continue;
1944  }
1945  for (x=0; x < (ssize_t) image->columns; x++)
1946  {
1947  register ssize_t
1948  i;
1949 
1950  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1951  *q++=QuantumScale*p[i];
1952  p+=GetPixelChannels(image);
1953  }
1954  }
1955  image_view=DestroyCacheView(image_view);
1956  carver=lqr_carver_new_ext(pixels,(int) image->columns,(int) image->rows,
1957  (int) GetPixelChannels(image),LQR_COLDEPTH_32F);
1958  if (carver == (LqrCarver *) NULL)
1959  {
1960  pixel_info=RelinquishVirtualMemory(pixel_info);
1961  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1962  }
1963  lqr_carver_set_preserve_input_image(carver);
1964  lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1965  lqr_status=lqr_carver_resize(carver,(int) columns,(int) rows);
1966  (void) lqr_status;
1967  rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1968  lqr_carver_get_height(carver),MagickTrue,exception);
1969  if (rescale_image == (Image *) NULL)
1970  {
1971  pixel_info=RelinquishVirtualMemory(pixel_info);
1972  return((Image *) NULL);
1973  }
1974  if (SetImageStorageClass(rescale_image,DirectClass,exception) == MagickFalse)
1975  {
1976  pixel_info=RelinquishVirtualMemory(pixel_info);
1977  rescale_image=DestroyImage(rescale_image);
1978  return((Image *) NULL);
1979  }
1980  rescale_view=AcquireAuthenticCacheView(rescale_image,exception);
1981  (void) lqr_carver_scan_reset(carver);
1982  while (lqr_carver_scan_ext(carver,&x_offset,&y_offset,(void **) &packet) != 0)
1983  {
1984  register Quantum
1985  *magick_restrict p;
1986 
1987  register ssize_t
1988  i;
1989 
1990  p=QueueCacheViewAuthenticPixels(rescale_view,x_offset,y_offset,1,1,
1991  exception);
1992  if (p == (Quantum *) NULL)
1993  break;
1994  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1995  {
1996  PixelChannel
1997  channel;
1998 
1999  PixelTrait
2000  rescale_traits,
2001  traits;
2002 
2003  channel=GetPixelChannelChannel(image,i);
2004  traits=GetPixelChannelTraits(image,channel);
2005  rescale_traits=GetPixelChannelTraits(rescale_image,channel);
2006  if ((traits == UndefinedPixelTrait) ||
2007  (rescale_traits == UndefinedPixelTrait))
2008  continue;
2009  SetPixelChannel(rescale_image,channel,ClampToQuantum(QuantumRange*
2010  packet[i]),p);
2011  }
2012  if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
2013  break;
2014  }
2015  rescale_view=DestroyCacheView(rescale_view);
2016  pixel_info=RelinquishVirtualMemory(pixel_info);
2017  lqr_carver_destroy(carver);
2018  return(rescale_image);
2019 }
2020 #else
2022  const size_t magick_unused(columns),const size_t magick_unused(rows),
2023  const double magick_unused(delta_x),const double magick_unused(rigidity),
2024  ExceptionInfo *exception)
2025 {
2026  assert(image != (const Image *) NULL);
2027  assert(image->signature == MagickCoreSignature);
2028  if (image->debug != MagickFalse)
2029  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2030  assert(exception != (ExceptionInfo *) NULL);
2031  assert(exception->signature == MagickCoreSignature);
2033  "DelegateLibrarySupportNotBuiltIn","'%s' (LQR)",image->filename);
2034  return((Image *) NULL);
2035 }
2036 #endif
2037 
2038 /*
2039 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2040 % %
2041 % %
2042 % %
2043 % M a g n i f y I m a g e %
2044 % %
2045 % %
2046 % %
2047 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2048 %
2049 % MagnifyImage() doubles the size of the image with a pixel art scaling
2050 % algorithm.
2051 %
2052 % The format of the MagnifyImage method is:
2053 %
2054 % Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
2055 %
2056 % A description of each parameter follows:
2057 %
2058 % o image: the image.
2059 %
2060 % o exception: return any errors or warnings in this structure.
2061 %
2062 */
2063 
2064 static inline void CopyPixels(const Quantum *source,const ssize_t source_offset,
2065  Quantum *destination,const ssize_t destination_offset,const size_t channels)
2066 {
2067  register ssize_t
2068  i;
2069 
2070  for (i=0; i < (ssize_t) channels; i++)
2071  destination[channels*destination_offset+i]=source[source_offset*channels+i];
2072 }
2073 
2074 static inline void MixPixels(const Quantum *source,const ssize_t *source_offset,
2075  const size_t source_size,Quantum *destination,
2076  const ssize_t destination_offset,const size_t channels)
2077 {
2078  ssize_t
2079  sum;
2080 
2081  register ssize_t
2082  i;
2083 
2084  for (i=0; i < (ssize_t) channels; i++)
2085  {
2086  register ssize_t
2087  j;
2088 
2089  sum=0;
2090  for (j=0; j < (ssize_t) source_size; j++)
2091  sum+=source[source_offset[j]*channels+i];
2092  destination[channels*destination_offset+i]=(Quantum) (sum/source_size);
2093  }
2094 }
2095 
2096 static inline void Mix2Pixels(const Quantum *source,
2097  const ssize_t source_offset1,const ssize_t source_offset2,
2098  Quantum *destination,const ssize_t destination_offset,const size_t channels)
2099 {
2100  const ssize_t
2101  offsets[2] = { source_offset1, source_offset2 };
2102 
2103  MixPixels(source,offsets,2,destination,destination_offset,channels);
2104 }
2105 
2106 static inline int PixelsEqual(const Quantum *source1,ssize_t offset1,
2107  const Quantum *source2,ssize_t offset2,const size_t channels)
2108 {
2109  register ssize_t
2110  i;
2111 
2112  offset1*=channels;
2113  offset2*=channels;
2114  for (i=0; i < (ssize_t) channels; i++)
2115  if (source1[offset1+i] != source2[offset2+i])
2116  return(0);
2117  return(1);
2118 }
2119 
2120 static inline void Eagle2X(const Image *source,const Quantum *pixels,
2121  Quantum *result,const size_t channels)
2122 {
2123  ssize_t
2124  i;
2125 
2126  (void) source;
2127  for (i=0; i < 4; i++)
2128  CopyPixels(pixels,4,result,i,channels);
2129  if (PixelsEqual(pixels,0,pixels,1,channels) &&
2130  PixelsEqual(pixels,1,pixels,3,channels))
2131  CopyPixels(pixels,0,result,0,channels);
2132  if (PixelsEqual(pixels,1,pixels,2,channels) &&
2133  PixelsEqual(pixels,2,pixels,5,channels))
2134  CopyPixels(pixels,2,result,1,channels);
2135  if (PixelsEqual(pixels,3,pixels,6,channels) &&
2136  PixelsEqual(pixels,6,pixels,7,channels))
2137  CopyPixels(pixels,6,result,2,channels);
2138  if (PixelsEqual(pixels,5,pixels,8,channels) &&
2139  PixelsEqual(pixels,8,pixels,7,channels))
2140  CopyPixels(pixels,8,result,3,channels);
2141 }
2142 
2143 static void Hq2XHelper(const unsigned int rule,const Quantum *source,
2144  Quantum *destination,const ssize_t destination_offset,const size_t channels,
2145  const ssize_t e,const ssize_t a,const ssize_t b,const ssize_t d,
2146  const ssize_t f,const ssize_t h)
2147 {
2148 #define caseA(N,A,B,C,D) \
2149  case N: \
2150  { \
2151  const ssize_t \
2152  offsets[4] = { A, B, C, D }; \
2153  \
2154  MixPixels(source,offsets,4,destination,destination_offset,channels);\
2155  break; \
2156  }
2157 #define caseB(N,A,B,C,D,E,F,G,H) \
2158  case N: \
2159  { \
2160  const ssize_t \
2161  offsets[8] = { A, B, C, D, E, F, G, H }; \
2162  \
2163  MixPixels(source,offsets,8,destination,destination_offset,channels);\
2164  break; \
2165  }
2166 
2167  switch (rule)
2168  {
2169  case 0:
2170  {
2171  CopyPixels(source,e,destination,destination_offset,channels);
2172  break;
2173  }
2174  caseA(1,e,e,e,a)
2175  caseA(2,e,e,e,d)
2176  caseA(3,e,e,e,b)
2177  caseA(4,e,e,d,b)
2178  caseA(5,e,e,a,b)
2179  caseA(6,e,e,a,d)
2180  caseB(7,e,e,e,e,e,b,b,d)
2181  caseB(8,e,e,e,e,e,d,d,b)
2182  caseB(9,e,e,e,e,e,e,d,b)
2183  caseB(10,e,e,d,d,d,b,b,b)
2184  case 11:
2185  {
2186  const ssize_t
2187  offsets[16] = { e, e, e, e, e, e, e, e, e, e, e, e, e, e, d, b };
2188 
2189  MixPixels(source,offsets,16,destination,destination_offset,channels);
2190  break;
2191  }
2192  case 12:
2193  {
2194  if (PixelsEqual(source,b,source,d,channels))
2195  {
2196  const ssize_t
2197  offsets[4] = { e, e, d, b };
2198 
2199  MixPixels(source,offsets,4,destination,destination_offset,channels);
2200  break;
2201  }
2202  CopyPixels(source,e,destination,destination_offset,channels);
2203  break;
2204  }
2205  case 13:
2206  {
2207  if (PixelsEqual(source,b,source,d,channels))
2208  {
2209  const ssize_t
2210  offsets[8] = { e, e, d, d, d, b, b, b };
2211 
2212  MixPixels(source,offsets,8,destination,destination_offset,channels);
2213  break;
2214  }
2215  CopyPixels(source,e,destination,destination_offset,channels);
2216  break;
2217  }
2218  case 14:
2219  {
2220  if (PixelsEqual(source,b,source,d,channels))
2221  {
2222  const ssize_t
2223  offsets[16] = { e, e, e, e, e, e, e, e, e, e, e, e, e, e, d, b };
2224 
2225  MixPixels(source,offsets,16,destination,destination_offset,channels);
2226  break;
2227  }
2228  CopyPixels(source,e,destination,destination_offset,channels);
2229  break;
2230  }
2231  case 15:
2232  {
2233  const ssize_t
2234  offsets[4] = { e, e, e, a };
2235 
2236  if (PixelsEqual(source,b,source,d,channels))
2237  {
2238  const ssize_t
2239  offsets[4] = { e, e, d, b };
2240 
2241  MixPixels(source,offsets,4,destination,destination_offset,channels);
2242  break;
2243  }
2244  MixPixels(source,offsets,4,destination,destination_offset,channels);
2245  break;
2246  }
2247  case 16:
2248  {
2249  const ssize_t
2250  offsets[4] = { e, e, e, a };
2251 
2252  if (PixelsEqual(source,b,source,d,channels))
2253  {
2254  const ssize_t
2255  offsets[8] = { e, e, e, e, e, e, d, b };
2256 
2257  MixPixels(source,offsets,8,destination,destination_offset,channels);
2258  break;
2259  }
2260  MixPixels(source,offsets,4,destination,destination_offset,channels);
2261  break;
2262  }
2263  case 17:
2264  {
2265  const ssize_t
2266  offsets[4] = { e, e, e, a };
2267 
2268  if (PixelsEqual(source,b,source,d,channels))
2269  {
2270  const ssize_t
2271  offsets[8] = { e, e, d, d, d, b, b, b };
2272 
2273  MixPixels(source,offsets,8,destination,destination_offset,channels);
2274  break;
2275  }
2276  MixPixels(source,offsets,4,destination,destination_offset,channels);
2277  break;
2278  }
2279  case 18:
2280  {
2281  const ssize_t
2282  offsets[4] = { e, e, e, d };
2283 
2284  if (PixelsEqual(source,b,source,f,channels))
2285  {
2286  const ssize_t
2287  offsets[8] = { e, e, e, e, e, b, b, d };
2288 
2289  MixPixels(source,offsets,8,destination,destination_offset,channels);
2290  break;
2291  }
2292  MixPixels(source,offsets,4,destination,destination_offset,channels);
2293  break;
2294  }
2295  default:
2296  {
2297  const ssize_t
2298  offsets[4] = { e, e, e, b };
2299 
2300  if (PixelsEqual(source,d,source,h,channels))
2301  {
2302  const ssize_t
2303  offsets[8] = { e, e, e, e, e, d, d, b };
2304  MixPixels(source,offsets,8,destination,destination_offset,channels);
2305  break;
2306  }
2307  MixPixels(source,offsets,4,destination,destination_offset,channels);
2308  break;
2309  }
2310  }
2311  #undef caseA
2312  #undef caseB
2313 }
2314 
2315 static inline unsigned int Hq2XPatternToNumber(const int *pattern)
2316 {
2317  ssize_t
2318  i;
2319 
2320  unsigned int
2321  result,
2322  order;
2323 
2324  result=0;
2325  order=1;
2326  for (i=7; i >= 0; i--)
2327  {
2328  result+=order*pattern[i];
2329  order*=2;
2330  }
2331  return(result);
2332 }
2333 
2334 static inline void Hq2X(const Image *source,const Quantum *pixels,
2335  Quantum *result,const size_t channels)
2336 {
2337  static const unsigned int
2338  Hq2XTable[] =
2339  {
2340  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13,
2341  4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 12, 12, 5, 3, 1, 12,
2342  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14,
2343  4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 16, 12, 5, 3, 1, 14,
2344  4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 12, 12, 5, 19, 16, 12,
2345  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12,
2346  4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 1, 12, 5, 19, 1, 14,
2347  4, 4, 6, 2, 4, 4, 6, 18, 5, 3, 16, 12, 5, 19, 1, 14,
2348  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13,
2349  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12,
2350  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14,
2351  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 13, 5, 3, 1, 14,
2352  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 13,
2353  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 12,
2354  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 14,
2355  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 1, 12, 5, 3, 1, 14
2356  };
2357 
2358  const int
2359  pattern1[] =
2360  {
2361  !PixelsEqual(pixels,4,pixels,8,channels),
2362  !PixelsEqual(pixels,4,pixels,7,channels),
2363  !PixelsEqual(pixels,4,pixels,6,channels),
2364  !PixelsEqual(pixels,4,pixels,5,channels),
2365  !PixelsEqual(pixels,4,pixels,3,channels),
2366  !PixelsEqual(pixels,4,pixels,2,channels),
2367  !PixelsEqual(pixels,4,pixels,1,channels),
2368  !PixelsEqual(pixels,4,pixels,0,channels)
2369  };
2370 
2371 #define Rotated(p) p[2], p[4], p[7], p[1], p[6], p[0], p[3], p[5]
2372  const int pattern2[] = { Rotated(pattern1) };
2373  const int pattern3[] = { Rotated(pattern2) };
2374  const int pattern4[] = { Rotated(pattern3) };
2375 #undef Rotated
2376 
2377  Hq2XHelper(Hq2XTable[Hq2XPatternToNumber(pattern1)],pixels,result,0,
2378  channels,4,0,1,3,5,7);
2379  Hq2XHelper(Hq2XTable[Hq2XPatternToNumber(pattern2)],pixels,result,1,
2380  channels,4,2,5,1,7,3);
2381  Hq2XHelper(Hq2XTable[Hq2XPatternToNumber(pattern3)],pixels,result,3,
2382  channels,4,8,7,5,3,1);
2383  Hq2XHelper(Hq2XTable[Hq2XPatternToNumber(pattern4)],pixels,result,2,
2384  channels,4,6,3,7,1,5);
2385 }
2386 
2387 static void Fish2X(const Image *source,const Quantum *pixels,Quantum *result,
2388  const size_t channels)
2389 {
2390 #define Corner(A,B,C,D) \
2391  { \
2392  if (intensities[B] > intensities[A]) \
2393  { \
2394  ssize_t \
2395  offsets[3] = { B, C, D }; \
2396  \
2397  MixPixels(pixels,offsets,3,result,3,channels); \
2398  } \
2399  else \
2400  { \
2401  ssize_t \
2402  offsets[3] = { A, B, C }; \
2403  \
2404  MixPixels(pixels,offsets,3,result,3,channels); \
2405  } \
2406  }
2407 
2408 #define Line(A,B,C,D) \
2409  { \
2410  if (intensities[C] > intensities[A]) \
2411  Mix2Pixels(pixels,C,D,result,3,channels); \
2412  else \
2413  Mix2Pixels(pixels,A,B,result,3,channels); \
2414  }
2415 
2417  intensities[9];
2418 
2419  int
2420  ae,
2421  bd,
2422  ab,
2423  ad,
2424  be,
2425  de;
2426 
2427  register ssize_t
2428  i;
2429 
2430  ssize_t
2431  offsets[4] = { 0, 1, 3, 4 };
2432 
2433  for (i=0; i < 9; i++)
2434  intensities[i]=GetPixelIntensity(source,pixels + i*channels);
2435  CopyPixels(pixels,0,result,0,channels);
2436  CopyPixels(pixels,(ssize_t) (intensities[0] > intensities[1] ? 0 : 1),result,
2437  1,channels);
2438  CopyPixels(pixels,(ssize_t) (intensities[0] > intensities[3] ? 0 : 3),result,
2439  2,channels);
2440  ae=PixelsEqual(pixels,0,pixels,4,channels);
2441  bd=PixelsEqual(pixels,1,pixels,3,channels);
2442  ab=PixelsEqual(pixels,0,pixels,1,channels);
2443  de=PixelsEqual(pixels,3,pixels,4,channels);
2444  ad=PixelsEqual(pixels,0,pixels,3,channels);
2445  be=PixelsEqual(pixels,1,pixels,4,channels);
2446  if (ae && bd && ab)
2447  {
2448  CopyPixels(pixels,0,result,3,channels);
2449  return;
2450  }
2451  if (ad && de && !ab)
2452  {
2453  Corner(1,0,4,3)
2454  return;
2455  }
2456  if (be && de && !ab)
2457  {
2458  Corner(0,1,3,4)
2459  return;
2460  }
2461  if (ad && ab && !be)
2462  {
2463  Corner(4,3,1,0)
2464  return;
2465  }
2466  if (ab && be && !ad)
2467  {
2468  Corner(3,0,4,1)
2469  return;
2470  }
2471  if (ae && (!bd || intensities[1] > intensities[0]))
2472  {
2473  Mix2Pixels(pixels,0,4,result,3,channels);
2474  return;
2475  }
2476  if (bd && (!ae || intensities[0] > intensities[1]))
2477  {
2478  Mix2Pixels(pixels,1,3,result,3,channels);
2479  return;
2480  }
2481  if (ab)
2482  {
2483  Line(0,1,3,4)
2484  return;
2485  }
2486  if (de)
2487  {
2488  Line(3,4,0,1)
2489  return;
2490  }
2491  if (ad)
2492  {
2493  Line(0,3,1,4)
2494  return;
2495  }
2496  if (be)
2497  {
2498  Line(1,4,0,3)
2499  return;
2500  }
2501  MixPixels(pixels,offsets,4,result,3,channels);
2502 #undef Corner
2503 #undef Line
2504 }
2505 
2506 static void Xbr2X(const Image *source,const Quantum *pixels,Quantum *result,
2507  const size_t channels)
2508 {
2509 #define WeightVar(M,N) const int w_##M##_##N = \
2510  PixelsEqual(pixels,M,pixels,N,channels) ? 0 : 1;
2511 
2512  WeightVar(12,11)
2513  WeightVar(12,7)
2514  WeightVar(12,13)
2515  WeightVar(12,17)
2516  WeightVar(12,16)
2517  WeightVar(12,8)
2518  WeightVar(6,10)
2519  WeightVar(6,2)
2520  WeightVar(11,7)
2521  WeightVar(11,17)
2522  WeightVar(11,5)
2523  WeightVar(7,13)
2524  WeightVar(7,1)
2525  WeightVar(12,6)
2526  WeightVar(12,18)
2527  WeightVar(8,14)
2528  WeightVar(8,2)
2529  WeightVar(13,17)
2530  WeightVar(13,9)
2531  WeightVar(7,3)
2532  WeightVar(16,10)
2533  WeightVar(16,22)
2534  WeightVar(17,21)
2535  WeightVar(11,15)
2536  WeightVar(18,14)
2537  WeightVar(18,22)
2538  WeightVar(17,23)
2539  WeightVar(17,19)
2540 #undef WeightVar
2541 
2542  if (
2543  w_12_16 + w_12_8 + w_6_10 + w_6_2 + (4 * w_11_7) <
2544  w_11_17 + w_11_5 + w_7_13 + w_7_1 + (4 * w_12_6)
2545  )
2546  Mix2Pixels(pixels,(ssize_t) (w_12_11 <= w_12_7 ? 11 : 7),12,result,0,
2547  channels);
2548  else
2549  CopyPixels(pixels,12,result,0,channels);
2550  if (
2551  w_12_18 + w_12_6 + w_8_14 + w_8_2 + (4 * w_7_13) <
2552  w_13_17 + w_13_9 + w_11_7 + w_7_3 + (4 * w_12_8)
2553  )
2554  Mix2Pixels(pixels,(ssize_t) (w_12_7 <= w_12_13 ? 7 : 13),12,result,1,
2555  channels);
2556  else
2557  CopyPixels(pixels,12,result,1,channels);
2558  if (
2559  w_12_6 + w_12_18 + w_16_10 + w_16_22 + (4 * w_11_17) <
2560  w_11_7 + w_11_15 + w_13_17 + w_17_21 + (4 * w_12_16)
2561  )
2562  Mix2Pixels(pixels,(ssize_t) (w_12_11 <= w_12_17 ? 11 : 17),12,result,2,
2563  channels);
2564  else
2565  CopyPixels(pixels,12,result,2,channels);
2566  if (
2567  w_12_8 + w_12_16 + w_18_14 + w_18_22 + (4 * w_13_17) <
2568  w_11_17 + w_17_23 + w_17_19 + w_7_13 + (4 * w_12_18)
2569  )
2570  Mix2Pixels(pixels,(ssize_t) (w_12_13 <= w_12_17 ? 13 : 17),12,result,3,
2571  channels);
2572  else
2573  CopyPixels(pixels,12,result,3,channels);
2574 }
2575 
2576 static void Scale2X(const Image *source,const Quantum *pixels,Quantum *result,
2577  const size_t channels)
2578 {
2579  if (PixelsEqual(pixels,1,pixels,7,channels) ||
2580  PixelsEqual(pixels,3,pixels,5,channels))
2581  {
2582  register ssize_t
2583  i;
2584 
2585  for (i=0; i < 4; i++)
2586  CopyPixels(pixels,4,result,i,channels);
2587  return;
2588  }
2589  if (PixelsEqual(pixels,1,pixels,3,channels))
2590  CopyPixels(pixels,3,result,0,channels);
2591  else
2592  CopyPixels(pixels,4,result,0,channels);
2593  if (PixelsEqual(pixels,1,pixels,5,channels))
2594  CopyPixels(pixels,5,result,1,channels);
2595  else
2596  CopyPixels(pixels,4,result,1,channels);
2597  if (PixelsEqual(pixels,3,pixels,7,channels))
2598  CopyPixels(pixels,3,result,2,channels);
2599  else
2600  CopyPixels(pixels,4,result,2,channels);
2601  if (PixelsEqual(pixels,5,pixels,7,channels))
2602  CopyPixels(pixels,5,result,3,channels);
2603  else
2604  CopyPixels(pixels,4,result,3,channels);
2605 }
2606 
2607 static void Epbx2X(const Image *source,const Quantum *pixels,
2608  Quantum *result,const size_t channels)
2609 {
2610 #define HelperCond(a,b,c,d,e,f,g) ( \
2611  PixelsEqual(pixels,a,pixels,b,channels) && ( \
2612  PixelsEqual(pixels,c,pixels,d,channels) || \
2613  PixelsEqual(pixels,c,pixels,e,channels) || \
2614  PixelsEqual(pixels,a,pixels,f,channels) || \
2615  PixelsEqual(pixels,b,pixels,g,channels) \
2616  ) \
2617  )
2618 
2619  register ssize_t
2620  i;
2621 
2622  for (i=0; i < 4; i++)
2623  CopyPixels(pixels,4,result,i,channels);
2624  if (
2625  !PixelsEqual(pixels,3,pixels,5,channels) &&
2626  !PixelsEqual(pixels,1,pixels,7,channels) &&
2627  (
2628  PixelsEqual(pixels,4,pixels,3,channels) ||
2629  PixelsEqual(pixels,4,pixels,7,channels) ||
2630  PixelsEqual(pixels,4,pixels,5,channels) ||
2631  PixelsEqual(pixels,4,pixels,1,channels) ||
2632  (
2633  (
2634  !PixelsEqual(pixels,0,pixels,8,channels) ||
2635  PixelsEqual(pixels,4,pixels,6,channels) ||
2636  PixelsEqual(pixels,3,pixels,2,channels)
2637  ) &&
2638  (
2639  !PixelsEqual(pixels,6,pixels,2,channels) ||
2640  PixelsEqual(pixels,4,pixels,0,channels) ||
2641  PixelsEqual(pixels,4,pixels,8,channels)
2642  )
2643  )
2644  )
2645  )
2646  {
2647  if (HelperCond(1,3,4,0,8,2,6))
2648  Mix2Pixels(pixels,1,3,result,0,channels);
2649  if (HelperCond(5,1,4,2,6,8,0))
2650  Mix2Pixels(pixels,5,1,result,1,channels);
2651  if (HelperCond(3,7,4,6,2,0,8))
2652  Mix2Pixels(pixels,3,7,result,2,channels);
2653  if (HelperCond(7,5,4,8,0,6,2))
2654  Mix2Pixels(pixels,7,5,result,3,channels);
2655  }
2656 
2657 #undef HelperCond
2658 }
2659 
2660 static inline void Eagle3X(const Image *source,const Quantum *pixels,
2661  Quantum *result,const size_t channels)
2662 {
2663  ssize_t
2664  corner_tl,
2665  corner_tr,
2666  corner_bl,
2667  corner_br;
2668 
2669  corner_tl=PixelsEqual(pixels,0,pixels,1,channels) &&
2670  PixelsEqual(pixels,0,pixels,3,channels);
2671  corner_tr=PixelsEqual(pixels,1,pixels,2,channels) &&
2672  PixelsEqual(pixels,2,pixels,5,channels);
2673  corner_bl=PixelsEqual(pixels,3,pixels,6,channels) &&
2674  PixelsEqual(pixels,6,pixels,7,channels);
2675  corner_br=PixelsEqual(pixels,5,pixels,7,channels) &&
2676  PixelsEqual(pixels,7,pixels,8,channels);
2677  CopyPixels(pixels,(ssize_t) (corner_tl ? 0 : 4),result,0,channels);
2678  if (corner_tl && corner_tr)
2679  Mix2Pixels(pixels,0,2,result,1,channels);
2680  else
2681  CopyPixels(pixels,4,result,1,channels);
2682  CopyPixels(pixels,(ssize_t) (corner_tr ? 1 : 4),result,2,channels);
2683  if (corner_tl && corner_bl)
2684  Mix2Pixels(pixels,0,6,result,3,channels);
2685  else
2686  CopyPixels(pixels,4,result,3,channels);
2687  CopyPixels(pixels,4,result,4,channels);
2688  if (corner_tr && corner_br)
2689  Mix2Pixels(pixels,2,8,result,5,channels);
2690  else
2691  CopyPixels(pixels,4,result,5,channels);
2692  CopyPixels(pixels,(ssize_t) (corner_bl ? 3 : 4),result,6,channels);
2693  if (corner_bl && corner_br)
2694  Mix2Pixels(pixels,6,8,result,7,channels);
2695  else
2696  CopyPixels(pixels,4,result,7,channels);
2697  CopyPixels(pixels,(ssize_t) (corner_br ? 5 : 4),result,8,channels);
2698 }
2699 
2700 static inline void Eagle3XB(const Image *source,const Quantum *pixels,
2701  Quantum *result,const size_t channels)
2702 {
2703  ssize_t
2704  corner_tl,
2705  corner_tr,
2706  corner_bl,
2707  corner_br;
2708 
2709  corner_tl=PixelsEqual(pixels,0,pixels,1,channels) &&
2710  PixelsEqual(pixels,0,pixels,3,channels);
2711  corner_tr=PixelsEqual(pixels,1,pixels,2,channels) &&
2712  PixelsEqual(pixels,2,pixels,5,channels);
2713  corner_bl=PixelsEqual(pixels,3,pixels,6,channels) &&
2714  PixelsEqual(pixels,6,pixels,7,channels);
2715  corner_br=PixelsEqual(pixels,5,pixels,7,channels) &&
2716  PixelsEqual(pixels,7,pixels,8,channels);
2717  CopyPixels(pixels,(ssize_t) (corner_tl ? 0 : 4),result,0,channels);
2718  CopyPixels(pixels,4,result,1,channels);
2719  CopyPixels(pixels,(ssize_t) (corner_tr ? 1 : 4),result,2,channels);
2720  CopyPixels(pixels,4,result,3,channels);
2721  CopyPixels(pixels,4,result,4,channels);
2722  CopyPixels(pixels,4,result,5,channels);
2723  CopyPixels(pixels,(ssize_t) (corner_bl ? 3 : 4),result,6,channels);
2724  CopyPixels(pixels,4,result,7,channels);
2725  CopyPixels(pixels,(ssize_t) (corner_br ? 5 : 4),result,8,channels);
2726 }
2727 
2728 static inline void Scale3X(const Image *source,const Quantum *pixels,
2729  Quantum *result,const size_t channels)
2730 {
2731  if (!PixelsEqual(pixels,1,pixels,7,channels) &&
2732  !PixelsEqual(pixels,3,pixels,5,channels))
2733  {
2734  if (PixelsEqual(pixels,3,pixels,1,channels))
2735  CopyPixels(pixels,3,result,0,channels);
2736  else
2737  CopyPixels(pixels,4,result,0,channels);
2738 
2739  if (
2740  (
2741  PixelsEqual(pixels,3,pixels,1,channels) &&
2742  !PixelsEqual(pixels,4,pixels,2,channels)
2743  ) ||
2744  (
2745  PixelsEqual(pixels,5,pixels,1,channels) &&
2746  !PixelsEqual(pixels,4,pixels,0,channels)
2747  )
2748  )
2749  CopyPixels(pixels,1,result,1,channels);
2750  else
2751  CopyPixels(pixels,4,result,1,channels);
2752  if (PixelsEqual(pixels,5,pixels,1,channels))
2753  CopyPixels(pixels,5,result,2,channels);
2754  else
2755  CopyPixels(pixels,4,result,2,channels);
2756  if (
2757  (
2758  PixelsEqual(pixels,3,pixels,1,channels) &&
2759  !PixelsEqual(pixels,4,pixels,6,channels)
2760  ) ||
2761  (
2762  PixelsEqual(pixels,3,pixels,7,channels) &&
2763  !PixelsEqual(pixels,4,pixels,0,channels)
2764  )
2765  )
2766  CopyPixels(pixels,3,result,3,channels);
2767  else
2768  CopyPixels(pixels,4,result,3,channels);
2769  CopyPixels(pixels,4,result,4,channels);
2770  if (
2771  (
2772  PixelsEqual(pixels,5,pixels,1,channels) &&
2773  !PixelsEqual(pixels,4,pixels,8,channels)
2774  ) ||
2775  (
2776  PixelsEqual(pixels,5,pixels,7,channels) &&
2777  !PixelsEqual(pixels,4,pixels,2,channels)
2778  )
2779  )
2780  CopyPixels(pixels,5,result,5,channels);
2781  else
2782  CopyPixels(pixels,4,result,5,channels);
2783  if (PixelsEqual(pixels,3,pixels,7,channels))
2784  CopyPixels(pixels,3,result,6,channels);
2785  else
2786  CopyPixels(pixels,4,result,6,channels);
2787  if (
2788  (
2789  PixelsEqual(pixels,3,pixels,7,channels) &&
2790  !PixelsEqual(pixels,4,pixels,8,channels)
2791  ) ||
2792  (
2793  PixelsEqual(pixels,5,pixels,7,channels) &&
2794  !PixelsEqual(pixels,4,pixels,6,channels)
2795  )
2796  )
2797  CopyPixels(pixels,7,result,7,channels);
2798  else
2799  CopyPixels(pixels,4,result,7,channels);
2800  if (PixelsEqual(pixels,5,pixels,7,channels))
2801  CopyPixels(pixels,5,result,8,channels);
2802  else
2803  CopyPixels(pixels,4,result,8,channels);
2804  }
2805  else
2806  {
2807  register ssize_t
2808  i;
2809 
2810  for (i=0; i < 9; i++)
2811  CopyPixels(pixels,4,result,i,channels);
2812  }
2813 }
2814 
2816 {
2817 #define MagnifyImageTag "Magnify/Image"
2818 
2819  CacheView
2820  *image_view,
2821  *magnify_view;
2822 
2823  const char
2824  *option;
2825 
2826  Image
2827  *source_image,
2828  *magnify_image;
2829 
2831  status;
2832 
2834  progress;
2835 
2836  OffsetInfo
2837  offset;
2838 
2840  rectangle;
2841 
2842  ssize_t
2843  y;
2844 
2845  unsigned char
2846  magnification,
2847  width;
2848 
2849  void
2850  (*scaling_method)(const Image *,const Quantum *,Quantum *,size_t);
2851 
2852  /*
2853  Initialize magnified image attributes.
2854  */
2855  assert(image != (const Image *) NULL);
2856  assert(image->signature == MagickCoreSignature);
2857  if (image->debug != MagickFalse)
2858  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2859  assert(exception != (ExceptionInfo *) NULL);
2860  assert(exception->signature == MagickCoreSignature);
2861  option=GetImageOption(image->image_info,"magnify:method");
2862  if (option == (char *) NULL)
2863  option="scale2x";
2864  scaling_method=Scale2X;
2865  magnification=1;
2866  width=1;
2867  switch (*option)
2868  {
2869  case 'e':
2870  {
2871  if (LocaleCompare(option,"eagle2x") == 0)
2872  {
2873  scaling_method=Eagle2X;
2874  magnification=2;
2875  width=3;
2876  break;
2877  }
2878  if (LocaleCompare(option,"eagle3x") == 0)
2879  {
2880  scaling_method=Eagle3X;
2881  magnification=3;
2882  width=3;
2883  break;
2884  }
2885  if (LocaleCompare(option,"eagle3xb") == 0)
2886  {
2887  scaling_method=Eagle3XB;
2888  magnification=3;
2889  width=3;
2890  break;
2891  }
2892  if (LocaleCompare(option,"epbx2x") == 0)
2893  {
2894  scaling_method=Epbx2X;
2895  magnification=2;
2896  width=3;
2897  break;
2898  }
2899  break;
2900  }
2901  case 'f':
2902  {
2903  if (LocaleCompare(option,"fish2x") == 0)
2904  {
2905  scaling_method=Fish2X;
2906  magnification=2;
2907  width=3;
2908  break;
2909  }
2910  break;
2911  }
2912  case 'h':
2913  {
2914  if (LocaleCompare(option,"hq2x") == 0)
2915  {
2916  scaling_method=Hq2X;
2917  magnification=2;
2918  width=3;
2919  break;
2920  }
2921  break;
2922  }
2923  case 's':
2924  {
2925  if (LocaleCompare(option,"scale2x") == 0)
2926  {
2927  scaling_method=Scale2X;
2928  magnification=2;
2929  width=3;
2930  break;
2931  }
2932  if (LocaleCompare(option,"scale3x") == 0)
2933  {
2934  scaling_method=Scale3X;
2935  magnification=3;
2936  width=3;
2937  break;
2938  }
2939  break;
2940  }
2941  case 'x':
2942  {
2943  if (LocaleCompare(option,"xbr2x") == 0)
2944  {
2945  scaling_method=Xbr2X;
2946  magnification=2;
2947  width=5;
2948  }
2949  break;
2950  }
2951  default:
2952  break;
2953  }
2954  /*
2955  Make a working copy of the source image and convert it to RGB colorspace.
2956  */
2957  source_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2958  exception);
2959  offset.x=0;
2960  offset.y=0;
2961  rectangle.x=0;
2962  rectangle.y=0;
2963  rectangle.width=image->columns;
2964  rectangle.height=image->rows;
2965  (void) CopyImagePixels(source_image,image,&rectangle,&offset,exception);
2966  (void) SetImageColorspace(source_image,RGBColorspace,exception);
2967  magnify_image=CloneImage(source_image,magnification*source_image->columns,
2968  magnification*source_image->rows,MagickTrue,exception);
2969  if (magnify_image == (Image *) NULL)
2970  return((Image *) NULL);
2971  /*
2972  Magnify the image.
2973  */
2974  status=MagickTrue;
2975  progress=0;
2976  image_view=AcquireVirtualCacheView(source_image,exception);
2977  magnify_view=AcquireAuthenticCacheView(magnify_image,exception);
2978 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2979  #pragma omp parallel for schedule(static) shared(progress,status) \
2980  magick_number_threads(source_image,magnify_image,source_image->rows,1)
2981 #endif
2982  for (y=0; y < (ssize_t) source_image->rows; y++)
2983  {
2984  Quantum
2985  r[128]; /* to hold result pixels */
2986 
2987  register Quantum
2988  *magick_restrict q;
2989 
2990  register ssize_t
2991  x;
2992 
2993  if (status == MagickFalse)
2994  continue;
2995  q=QueueCacheViewAuthenticPixels(magnify_view,0,magnification*y,
2996  magnify_image->columns,magnification,exception);
2997  if (q == (Quantum *) NULL)
2998  {
2999  status=MagickFalse;
3000  continue;
3001  }
3002  /*
3003  Magnify this row of pixels.
3004  */
3005  for (x=0; x < (ssize_t) source_image->columns; x++)
3006  {
3007  register const Quantum
3008  *magick_restrict p;
3009 
3010  size_t
3011  channels;
3012 
3013  register ssize_t
3014  i;
3015 
3016  ssize_t
3017  j;
3018 
3019  p=GetCacheViewVirtualPixels(image_view,x-width/2,y-width/2,width,width,
3020  exception);
3021  channels=GetPixelChannels(source_image);
3022  scaling_method(source_image,p,r,channels);
3023  /*
3024  Copy the result pixels into the final image.
3025  */
3026  for (j=0; j < (ssize_t) magnification; j++)
3027  for (i=0; i < (ssize_t) (channels*magnification); i++)
3028  q[j*channels*magnify_image->columns+i]=r[j*magnification*channels+i];
3029  q+=magnification*GetPixelChannels(magnify_image);
3030  }
3031  if (SyncCacheViewAuthenticPixels(magnify_view,exception) == MagickFalse)
3032  status=MagickFalse;
3033  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3034  {
3036  proceed;
3037 
3038 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3039  #pragma omp atomic
3040 #endif
3041  progress++;
3042  proceed=SetImageProgress(image,MagnifyImageTag,progress,image->rows);
3043  if (proceed == MagickFalse)
3044  status=MagickFalse;
3045  }
3046  }
3047  magnify_view=DestroyCacheView(magnify_view);
3048  image_view=DestroyCacheView(image_view);
3049  source_image=DestroyImage(source_image);
3050  if (status == MagickFalse)
3051  magnify_image=DestroyImage(magnify_image);
3052  return(magnify_image);
3053 }
3054 
3055 /*
3056 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3057 % %
3058 % %
3059 % %
3060 % M i n i f y I m a g e %
3061 % %
3062 % %
3063 % %
3064 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3065 %
3066 % MinifyImage() is a convenience method that scales an image proportionally to
3067 % half its size.
3068 %
3069 % The format of the MinifyImage method is:
3070 %
3071 % Image *MinifyImage(const Image *image,ExceptionInfo *exception)
3072 %
3073 % A description of each parameter follows:
3074 %
3075 % o image: the image.
3076 %
3077 % o exception: return any errors or warnings in this structure.
3078 %
3079 */
3081 {
3082  Image
3083  *minify_image;
3084 
3085  assert(image != (Image *) NULL);
3086  assert(image->signature == MagickCoreSignature);
3087  if (image->debug != MagickFalse)
3088  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3089  assert(exception != (ExceptionInfo *) NULL);
3090  assert(exception->signature == MagickCoreSignature);
3091  minify_image=ResizeImage(image,image->columns/2,image->rows/2,SplineFilter,
3092  exception);
3093  return(minify_image);
3094 }
3095 
3096 /*
3097 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3098 % %
3099 % %
3100 % %
3101 % R e s a m p l e I m a g e %
3102 % %
3103 % %
3104 % %
3105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3106 %
3107 % ResampleImage() resize image in terms of its pixel size, so that when
3108 % displayed at the given resolution it will be the same size in terms of
3109 % real world units as the original image at the original resolution.
3110 %
3111 % The format of the ResampleImage method is:
3112 %
3113 % Image *ResampleImage(Image *image,const double x_resolution,
3114 % const double y_resolution,const FilterType filter,
3115 % ExceptionInfo *exception)
3116 %
3117 % A description of each parameter follows:
3118 %
3119 % o image: the image to be resized to fit the given resolution.
3120 %
3121 % o x_resolution: the new image x resolution.
3122 %
3123 % o y_resolution: the new image y resolution.
3124 %
3125 % o filter: Image filter to use.
3126 %
3127 % o exception: return any errors or warnings in this structure.
3128 %
3129 */
3130 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
3131  const double y_resolution,const FilterType filter,ExceptionInfo *exception)
3132 {
3133 #define ResampleImageTag "Resample/Image"
3134 
3135  Image
3136  *resample_image;
3137 
3138  size_t
3139  height,
3140  width;
3141 
3142  /*
3143  Initialize sampled image attributes.
3144  */
3145  assert(image != (const Image *) NULL);
3146  assert(image->signature == MagickCoreSignature);
3147  if (image->debug != MagickFalse)
3148  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3149  assert(exception != (ExceptionInfo *) NULL);
3150  assert(exception->signature == MagickCoreSignature);
3151  width=(size_t) (x_resolution*image->columns/(image->resolution.x == 0.0 ?
3152  72.0 : image->resolution.x)+0.5);
3153  height=(size_t) (y_resolution*image->rows/(image->resolution.y == 0.0 ?
3154  72.0 : image->resolution.y)+0.5);
3155  resample_image=ResizeImage(image,width,height,filter,exception);
3156  if (resample_image != (Image *) NULL)
3157  {
3158  resample_image->resolution.x=x_resolution;
3159  resample_image->resolution.y=y_resolution;
3160  }
3161  return(resample_image);
3162 }
3163 
3164 /*
3165 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3166 % %
3167 % %
3168 % %
3169 % R e s i z e I m a g e %
3170 % %
3171 % %
3172 % %
3173 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3174 %
3175 % ResizeImage() scales an image to the desired dimensions, using the given
3176 % filter (see AcquireFilterInfo()).
3177 %
3178 % If an undefined filter is given the filter defaults to Mitchell for a
3179 % colormapped image, a image with a matte channel, or if the image is
3180 % enlarged. Otherwise the filter defaults to a Lanczos.
3181 %
3182 % ResizeImage() was inspired by Paul Heckbert's "zoom" program.
3183 %
3184 % The format of the ResizeImage method is:
3185 %
3186 % Image *ResizeImage(Image *image,const size_t columns,const size_t rows,
3187 % const FilterType filter,ExceptionInfo *exception)
3188 %
3189 % A description of each parameter follows:
3190 %
3191 % o image: the image.
3192 %
3193 % o columns: the number of columns in the scaled image.
3194 %
3195 % o rows: the number of rows in the scaled image.
3196 %
3197 % o filter: Image filter to use.
3198 %
3199 % o exception: return any errors or warnings in this structure.
3200 %
3201 */
3202 
3203 typedef struct _ContributionInfo
3204 {
3205  double
3207 
3208  ssize_t
3211 
3213  ContributionInfo **contribution)
3214 {
3215  register ssize_t
3216  i;
3217 
3218  assert(contribution != (ContributionInfo **) NULL);
3219  for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
3220  if (contribution[i] != (ContributionInfo *) NULL)
3221  contribution[i]=(ContributionInfo *) RelinquishAlignedMemory(
3222  contribution[i]);
3223  contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
3224  return(contribution);
3225 }
3226 
3228 {
3229  register ssize_t
3230  i;
3231 
3233  **contribution;
3234 
3235  size_t
3236  number_threads;
3237 
3238  number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
3239  contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
3240  sizeof(*contribution));
3241  if (contribution == (ContributionInfo **) NULL)
3242  return((ContributionInfo **) NULL);
3243  (void) memset(contribution,0,number_threads*sizeof(*contribution));
3244  for (i=0; i < (ssize_t) number_threads; i++)
3245  {
3246  contribution[i]=(ContributionInfo *) MagickAssumeAligned(
3247  AcquireAlignedMemory(count,sizeof(**contribution)));
3248  if (contribution[i] == (ContributionInfo *) NULL)
3249  return(DestroyContributionThreadSet(contribution));
3250  }
3251  return(contribution);
3252 }
3253 
3255  const ResizeFilter *magick_restrict resize_filter,
3256  const Image *magick_restrict image,Image *magick_restrict resize_image,
3257  const double x_factor,const MagickSizeType span,
3258  MagickOffsetType *magick_restrict progress,ExceptionInfo *exception)
3259 {
3260 #define ResizeImageTag "Resize/Image"
3261 
3262  CacheView
3263  *image_view,
3264  *resize_view;
3265 
3266  ClassType
3267  storage_class;
3268 
3270  **magick_restrict contributions;
3271 
3273  status;
3274 
3275  double
3276  scale,
3277  support;
3278 
3279  ssize_t
3280  x;
3281 
3282  /*
3283  Apply filter to resize horizontally from image to resize image.
3284  */
3285  scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
3286  support=scale*GetResizeFilterSupport(resize_filter);
3287  storage_class=support > 0.5 ? DirectClass : image->storage_class;
3288  if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
3289  return(MagickFalse);
3290  if (support < 0.5)
3291  {
3292  /*
3293  Support too small even for nearest neighbour: Reduce to point sampling.
3294  */
3295  support=(double) 0.5;
3296  scale=1.0;
3297  }
3298  contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
3299  if (contributions == (ContributionInfo **) NULL)
3300  {
3301  (void) ThrowMagickException(exception,GetMagickModule(),
3302  ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
3303  return(MagickFalse);
3304  }
3305  status=MagickTrue;
3306  scale=PerceptibleReciprocal(scale);
3307  image_view=AcquireVirtualCacheView(image,exception);
3308  resize_view=AcquireAuthenticCacheView(resize_image,exception);
3309 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3310  #pragma omp parallel for schedule(static) shared(progress,status) \
3311  magick_number_threads(image,resize_image,resize_image->columns,1)
3312 #endif
3313  for (x=0; x < (ssize_t) resize_image->columns; x++)
3314  {
3315  const int
3316  id = GetOpenMPThreadId();
3317 
3318  double
3319  bisect,
3320  density;
3321 
3322  register const Quantum
3323  *magick_restrict p;
3324 
3325  register ContributionInfo
3326  *magick_restrict contribution;
3327 
3328  register Quantum
3329  *magick_restrict q;
3330 
3331  register ssize_t
3332  y;
3333 
3334  ssize_t
3335  n,
3336  start,
3337  stop;
3338 
3339  if (status == MagickFalse)
3340  continue;
3341  bisect=(double) (x+0.5)/x_factor+MagickEpsilon;
3342  start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
3343  stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->columns);
3344  density=0.0;
3345  contribution=contributions[id];
3346  for (n=0; n < (stop-start); n++)
3347  {
3348  contribution[n].pixel=start+n;
3349  contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
3350  ((double) (start+n)-bisect+0.5));
3351  density+=contribution[n].weight;
3352  }
3353  if (n == 0)
3354  continue;
3355  if ((density != 0.0) && (density != 1.0))
3356  {
3357  register ssize_t
3358  i;
3359 
3360  /*
3361  Normalize.
3362  */
3363  density=PerceptibleReciprocal(density);
3364  for (i=0; i < n; i++)
3365  contribution[i].weight*=density;
3366  }
3367  p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
3368  (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
3369  q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
3370  exception);
3371  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3372  {
3373  status=MagickFalse;
3374  continue;
3375  }
3376  for (y=0; y < (ssize_t) resize_image->rows; y++)
3377  {
3378  register ssize_t
3379  i;
3380 
3381  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3382  {
3383  double
3384  alpha,
3385  gamma,
3386  pixel;
3387 
3388  PixelChannel
3389  channel;
3390 
3391  PixelTrait
3392  resize_traits,
3393  traits;
3394 
3395  register ssize_t
3396  j;
3397 
3398  ssize_t
3399  k;
3400 
3401  channel=GetPixelChannelChannel(image,i);
3402  traits=GetPixelChannelTraits(image,channel);
3403  resize_traits=GetPixelChannelTraits(resize_image,channel);
3404  if ((traits == UndefinedPixelTrait) ||
3405  (resize_traits == UndefinedPixelTrait))
3406  continue;
3407  if (((resize_traits & CopyPixelTrait) != 0) ||
3408  (GetPixelWriteMask(resize_image,q) <= (QuantumRange/2)))
3409  {
3410  j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
3411  stop-1.0)+0.5);
3412  k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
3413  (contribution[j-start].pixel-contribution[0].pixel);
3414  SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
3415  q);
3416  continue;
3417  }
3418  pixel=0.0;
3419  if ((resize_traits & BlendPixelTrait) == 0)
3420  {
3421  /*
3422  No alpha blending.
3423  */
3424  for (j=0; j < n; j++)
3425  {
3426  k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
3427  (contribution[j].pixel-contribution[0].pixel);
3428  alpha=contribution[j].weight;
3429  pixel+=alpha*p[k*GetPixelChannels(image)+i];
3430  }
3431  SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
3432  continue;
3433  }
3434  /*
3435  Alpha blending.
3436  */
3437  gamma=0.0;
3438  for (j=0; j < n; j++)
3439  {
3440  k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
3441  (contribution[j].pixel-contribution[0].pixel);
3442  alpha=contribution[j].weight*QuantumScale*
3443  GetPixelAlpha(image,p+k*GetPixelChannels(image));
3444  pixel+=alpha*p[k*GetPixelChannels(image)+i];
3445  gamma+=alpha;
3446  }
3447  gamma=PerceptibleReciprocal(gamma);
3448  SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
3449  }
3450  q+=GetPixelChannels(resize_image);
3451  }
3452  if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
3453  status=MagickFalse;
3454  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3455  {
3457  proceed;
3458 
3459 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3460  #pragma omp atomic
3461 #endif
3462  (*progress)++;
3463  proceed=SetImageProgress(image,ResizeImageTag,*progress,span);
3464  if (proceed == MagickFalse)
3465  status=MagickFalse;
3466  }
3467  }
3468  resize_view=DestroyCacheView(resize_view);
3469  image_view=DestroyCacheView(image_view);
3470  contributions=DestroyContributionThreadSet(contributions);
3471  return(status);
3472 }
3473 
3475  const ResizeFilter *magick_restrict resize_filter,
3476  const Image *magick_restrict image,Image *magick_restrict resize_image,
3477  const double y_factor,const MagickSizeType span,
3478  MagickOffsetType *magick_restrict progress,ExceptionInfo *exception)
3479 {
3480  CacheView
3481  *image_view,
3482  *resize_view;
3483 
3484  ClassType
3485  storage_class;
3486 
3488  **magick_restrict contributions;
3489 
3490  double
3491  scale,
3492  support;
3493 
3495  status;
3496 
3497  ssize_t
3498  y;
3499 
3500  /*
3501  Apply filter to resize vertically from image to resize image.
3502  */
3503  scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
3504  support=scale*GetResizeFilterSupport(resize_filter);
3505  storage_class=support > 0.5 ? DirectClass : image->storage_class;
3506  if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
3507  return(MagickFalse);
3508  if (support < 0.5)
3509  {
3510  /*
3511  Support too small even for nearest neighbour: Reduce to point sampling.
3512  */
3513  support=(double) 0.5;
3514  scale=1.0;
3515  }
3516  contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
3517  if (contributions == (ContributionInfo **) NULL)
3518  {
3519  (void) ThrowMagickException(exception,GetMagickModule(),
3520  ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
3521  return(MagickFalse);
3522  }
3523  status=MagickTrue;
3524  scale=PerceptibleReciprocal(scale);
3525  image_view=AcquireVirtualCacheView(image,exception);
3526  resize_view=AcquireAuthenticCacheView(resize_image,exception);
3527 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3528  #pragma omp parallel for schedule(static) shared(progress,status) \
3529  magick_number_threads(image,resize_image,resize_image->rows,1)
3530 #endif
3531  for (y=0; y < (ssize_t) resize_image->rows; y++)
3532  {
3533  const int
3534  id = GetOpenMPThreadId();
3535 
3536  double
3537  bisect,
3538  density;
3539 
3540  register const Quantum
3541  *magick_restrict p;
3542 
3543  register ContributionInfo
3544  *magick_restrict contribution;
3545 
3546  register Quantum
3547  *magick_restrict q;
3548 
3549  register ssize_t
3550  x;
3551 
3552  ssize_t
3553  n,
3554  start,
3555  stop;
3556 
3557  if (status == MagickFalse)
3558  continue;
3559  bisect=(double) (y+0.5)/y_factor+MagickEpsilon;
3560  start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
3561  stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->rows);
3562  density=0.0;
3563  contribution=contributions[id];
3564  for (n=0; n < (stop-start); n++)
3565  {
3566  contribution[n].pixel=start+n;
3567  contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
3568  ((double) (start+n)-bisect+0.5));
3569  density+=contribution[n].weight;
3570  }
3571  if (n == 0)
3572  continue;
3573  if ((density != 0.0) && (density != 1.0))
3574  {
3575  register ssize_t
3576  i;
3577 
3578  /*
3579  Normalize.
3580  */
3581  density=PerceptibleReciprocal(density);
3582  for (i=0; i < n; i++)
3583  contribution[i].weight*=density;
3584  }
3585  p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
3586  image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
3587  exception);
3588  q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
3589  exception);
3590  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3591  {
3592  status=MagickFalse;
3593  continue;
3594  }
3595  for (x=0; x < (ssize_t) resize_image->columns; x++)
3596  {
3597  register ssize_t
3598  i;
3599 
3600  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3601  {
3602  double
3603  alpha,
3604  gamma,
3605  pixel;
3606 
3607  PixelChannel
3608  channel;
3609 
3610  PixelTrait
3611  resize_traits,
3612  traits;
3613 
3614  register ssize_t
3615  j;
3616 
3617  ssize_t
3618  k;
3619 
3620  channel=GetPixelChannelChannel(image,i);
3621  traits=GetPixelChannelTraits(image,channel);
3622  resize_traits=GetPixelChannelTraits(resize_image,channel);
3623  if ((traits == UndefinedPixelTrait) ||
3624  (resize_traits == UndefinedPixelTrait))
3625  continue;
3626  if (((resize_traits & CopyPixelTrait) != 0) ||
3627  (GetPixelWriteMask(resize_image,q) <= (QuantumRange/2)))
3628  {
3629  j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
3630  stop-1.0)+0.5);
3631  k=(ssize_t) ((contribution[j-start].pixel-contribution[0].pixel)*
3632  image->columns+x);
3633  SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
3634  q);
3635  continue;
3636  }
3637  pixel=0.0;
3638  if ((resize_traits & BlendPixelTrait) == 0)
3639  {
3640  /*
3641  No alpha blending.
3642  */
3643  for (j=0; j < n; j++)
3644  {
3645  k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
3646  image->columns+x);
3647  alpha=contribution[j].weight;
3648  pixel+=alpha*p[k*GetPixelChannels(image)+i];
3649  }
3650  SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
3651  continue;
3652  }
3653  gamma=0.0;
3654  for (j=0; j < n; j++)
3655  {
3656  k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
3657  image->columns+x);
3658  alpha=contribution[j].weight*QuantumScale*GetPixelAlpha(image,p+k*
3659  GetPixelChannels(image));
3660  pixel+=alpha*p[k*GetPixelChannels(image)+i];
3661  gamma+=alpha;
3662  }
3663  gamma=PerceptibleReciprocal(gamma);
3664  SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
3665  }
3666  q+=GetPixelChannels(resize_image);
3667  }
3668  if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
3669  status=MagickFalse;
3670  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3671  {
3673  proceed;
3674 
3675 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3676  #pragma omp atomic
3677 #endif
3678  (*progress)++;
3679  proceed=SetImageProgress(image,ResizeImageTag,*progress,span);
3680  if (proceed == MagickFalse)
3681  status=MagickFalse;
3682  }
3683  }
3684  resize_view=DestroyCacheView(resize_view);
3685  image_view=DestroyCacheView(image_view);
3686  contributions=DestroyContributionThreadSet(contributions);
3687  return(status);
3688 }
3689 
3690 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
3691  const size_t rows,const FilterType filter,ExceptionInfo *exception)
3692 {
3693  double
3694  x_factor,
3695  y_factor;
3696 
3697  FilterType
3698  filter_type;
3699 
3700  Image
3701  *filter_image,
3702  *resize_image;
3703 
3705  offset;
3706 
3708  span;
3709 
3711  status;
3712 
3713  ResizeFilter
3714  *resize_filter;
3715 
3716  /*
3717  Acquire resize image.
3718  */
3719  assert(image != (Image *) NULL);
3720  assert(image->signature == MagickCoreSignature);
3721  if (image->debug != MagickFalse)
3722  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3723  assert(exception != (ExceptionInfo *) NULL);
3724  assert(exception->signature == MagickCoreSignature);
3725  if ((columns == 0) || (rows == 0))
3726  ThrowImageException(ImageError,"NegativeOrZeroImageSize");
3727  if ((columns == image->columns) && (rows == image->rows) &&
3728  (filter == UndefinedFilter))
3729  return(CloneImage(image,0,0,MagickTrue,exception));
3730  /*
3731  Acquire resize filter.
3732  */
3733  x_factor=(double) columns/(double) image->columns;
3734  y_factor=(double) rows/(double) image->rows;
3735  filter_type=LanczosFilter;
3736  if (filter != UndefinedFilter)
3737  filter_type=filter;
3738  else
3739  if ((x_factor == 1.0) && (y_factor == 1.0))
3740  filter_type=PointFilter;
3741  else
3742  if ((image->storage_class == PseudoClass) ||
3743  (image->alpha_trait != UndefinedPixelTrait) ||
3744  ((x_factor*y_factor) > 1.0))
3745  filter_type=MitchellFilter;
3746  resize_filter=AcquireResizeFilter(image,filter_type,MagickFalse,exception);
3747 #if defined(MAGICKCORE_OPENCL_SUPPORT)
3748  resize_image=AccelerateResizeImage(image,columns,rows,resize_filter,
3749  exception);
3750  if (resize_image != (Image *) NULL)
3751  {
3752  resize_filter=DestroyResizeFilter(resize_filter);
3753  return(resize_image);
3754  }
3755 #endif
3756  resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
3757  if (resize_image == (Image *) NULL)
3758  {
3759  resize_filter=DestroyResizeFilter(resize_filter);
3760  return(resize_image);
3761  }
3762  if (x_factor > y_factor)
3763  filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
3764  else
3765  filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
3766  if (filter_image == (Image *) NULL)
3767  {
3768  resize_filter=DestroyResizeFilter(resize_filter);
3769  return(DestroyImage(resize_image));
3770  }
3771  /*
3772  Resize image.
3773  */
3774  offset=0;
3775  if (x_factor > y_factor)
3776  {
3777  span=(MagickSizeType) (filter_image->columns+rows);
3778  status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
3779  &offset,exception);
3780  status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
3781  span,&offset,exception);
3782  }
3783  else
3784  {
3785  span=(MagickSizeType) (filter_image->rows+columns);
3786  status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
3787  &offset,exception);
3788  status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
3789  span,&offset,exception);
3790  }
3791  /*
3792  Free resources.
3793  */
3794  filter_image=DestroyImage(filter_image);
3795  resize_filter=DestroyResizeFilter(resize_filter);
3796  if (status == MagickFalse)
3797  {
3798  resize_image=DestroyImage(resize_image);
3799  return((Image *) NULL);
3800  }
3801  resize_image->type=image->type;
3802  return(resize_image);
3803 }
3804 
3805 /*
3806 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3807 % %
3808 % %
3809 % %
3810 % S a m p l e I m a g e %
3811 % %
3812 % %
3813 % %
3814 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3815 %
3816 % SampleImage() scales an image to the desired dimensions with pixel
3817 % sampling. Unlike other scaling methods, this method does not introduce
3818 % any additional color into the scaled image.
3819 %
3820 % The format of the SampleImage method is:
3821 %
3822 % Image *SampleImage(const Image *image,const size_t columns,
3823 % const size_t rows,ExceptionInfo *exception)
3824 %
3825 % A description of each parameter follows:
3826 %
3827 % o image: the image.
3828 %
3829 % o columns: the number of columns in the sampled image.
3830 %
3831 % o rows: the number of rows in the sampled image.
3832 %
3833 % o exception: return any errors or warnings in this structure.
3834 %
3835 */
3836 MagickExport Image *SampleImage(const Image *image,const size_t columns,
3837  const size_t rows,ExceptionInfo *exception)
3838 {
3839 #define SampleImageTag "Sample/Image"
3840 
3841  CacheView
3842  *image_view,
3843  *sample_view;
3844 
3845  Image
3846  *sample_image;
3847 
3849  status;
3850 
3852  progress;
3853 
3854  register ssize_t
3855  x1;
3856 
3857  ssize_t
3858  *x_offset,
3859  y;
3860 
3861  PointInfo
3862  sample_offset;
3863 
3864  /*
3865  Initialize sampled image attributes.
3866  */
3867  assert(image != (const Image *) NULL);
3868  assert(image->signature == MagickCoreSignature);
3869  if (image->debug != MagickFalse)
3870  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3871  assert(exception != (ExceptionInfo *) NULL);
3872  assert(exception->signature == MagickCoreSignature);
3873  if ((columns == 0) || (rows == 0))
3874  ThrowImageException(ImageError,"NegativeOrZeroImageSize");
3875  if ((columns == image->columns) && (rows == image->rows))
3876  return(CloneImage(image,0,0,MagickTrue,exception));
3877  sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
3878  if (sample_image == (Image *) NULL)
3879  return((Image *) NULL);
3880  /*
3881  Set the sampling offset, default is in the mid-point of sample regions.
3882  */
3883  sample_offset.x=sample_offset.y=0.5-MagickEpsilon;
3884  {
3885  const char
3886  *value;
3887 
3888  value=GetImageArtifact(image,"sample:offset");
3889  if (value != (char *) NULL)
3890  {
3891  GeometryInfo
3892  geometry_info;
3893 
3895  flags;
3896 
3897  (void) ParseGeometry(value,&geometry_info);
3898  flags=ParseGeometry(value,&geometry_info);
3899  sample_offset.x=sample_offset.y=geometry_info.rho/100.0-MagickEpsilon;
3900  if ((flags & SigmaValue) != 0)
3901  sample_offset.y=geometry_info.sigma/100.0-MagickEpsilon;
3902  }
3903  }
3904  /*
3905  Allocate scan line buffer and column offset buffers.
3906  */
3907  x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
3908  sizeof(*x_offset));
3909  if (x_offset == (ssize_t *) NULL)
3910  {
3911  sample_image=DestroyImage(sample_image);
3912  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3913  }
3914  for (x1=0; x1 < (ssize_t) sample_image->columns; x1++)
3915  x_offset[x1]=(ssize_t) ((((double) x1+sample_offset.x)*image->columns)/
3916  sample_image->columns);
3917  /*
3918  Sample each row.
3919  */
3920  status=MagickTrue;
3921  progress=0;
3922  image_view=AcquireVirtualCacheView(image,exception);
3923  sample_view=AcquireAuthenticCacheView(sample_image,exception);
3924 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3925  #pragma omp parallel for schedule(static) shared(status) \
3926  magick_number_threads(image,sample_image,sample_image->rows,1)
3927 #endif
3928  for (y=0; y < (ssize_t) sample_image->rows; y++)
3929  {
3930  register const Quantum
3931  *magick_restrict p;
3932 
3933  register Quantum
3934  *magick_restrict q;
3935 
3936  register ssize_t
3937  x;
3938 
3939  ssize_t
3940  y_offset;
3941 
3942  if (status == MagickFalse)
3943  continue;
3944  y_offset=(ssize_t) ((((double) y+sample_offset.y)*image->rows)/
3945  sample_image->rows);
3946  p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
3947  exception);
3948  q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
3949  exception);
3950  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3951  {
3952  status=MagickFalse;
3953  continue;
3954  }
3955  /*
3956  Sample each column.
3957  */
3958  for (x=0; x < (ssize_t) sample_image->columns; x++)
3959  {
3960  register ssize_t
3961  i;
3962 
3963  if (GetPixelWriteMask(sample_image,q) <= (QuantumRange/2))
3964  {
3965  q+=GetPixelChannels(sample_image);
3966  continue;
3967  }
3968  for (i=0; i < (ssize_t) GetPixelChannels(sample_image); i++)
3969  {
3970  PixelChannel
3971  channel;
3972 
3973  PixelTrait
3974  image_traits,
3975  traits;
3976 
3977  channel=GetPixelChannelChannel(sample_image,i);
3978  traits=GetPixelChannelTraits(sample_image,channel);
3979  image_traits=GetPixelChannelTraits(image,channel);
3980  if ((traits == UndefinedPixelTrait) ||
3981  (image_traits == UndefinedPixelTrait))
3982  continue;
3983  SetPixelChannel(sample_image,channel,p[x_offset[x]*GetPixelChannels(
3984  image)+i],q);
3985  }
3986  q+=GetPixelChannels(sample_image);
3987  }
3988  if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
3989  status=MagickFalse;
3990  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3991  {
3993  proceed;
3994 
3995  proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
3996  if (proceed == MagickFalse)
3997  status=MagickFalse;
3998  }
3999  }
4000  image_view=DestroyCacheView(image_view);
4001  sample_view=DestroyCacheView(sample_view);
4002  x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
4003  sample_image->type=image->type;
4004  if (status == MagickFalse)
4005  sample_image=DestroyImage(sample_image);
4006  return(sample_image);
4007 }
4008 
4009 /*
4010 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4011 % %
4012 % %
4013 % %
4014 % S c a l e I m a g e %
4015 % %
4016 % %
4017 % %
4018 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4019 %
4020 % ScaleImage() changes the size of an image to the given dimensions.
4021 %
4022 % The format of the ScaleImage method is:
4023 %
4024 % Image *ScaleImage(const Image *image,const size_t columns,
4025 % const size_t rows,ExceptionInfo *exception)
4026 %
4027 % A description of each parameter follows:
4028 %
4029 % o image: the image.
4030 %
4031 % o columns: the number of columns in the scaled image.
4032 %
4033 % o rows: the number of rows in the scaled image.
4034 %
4035 % o exception: return any errors or warnings in this structure.
4036 %
4037 */
4038 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
4039  const size_t rows,ExceptionInfo *exception)
4040 {
4041 #define ScaleImageTag "Scale/Image"
4042 
4043  CacheView
4044  *image_view,
4045  *scale_view;
4046 
4047  double
4048  alpha,
4049  pixel[CompositePixelChannel],
4050  *scale_scanline,
4051  *scanline,
4052  *x_vector,
4053  *y_vector;
4054 
4055  Image
4056  *scale_image;
4057 
4059  next_column,
4060  next_row,
4061  proceed,
4062  status;
4063 
4064  PixelTrait
4065  scale_traits;
4066 
4067  PointInfo
4068  scale,
4069  span;
4070 
4071  register ssize_t
4072  i;
4073 
4074  ssize_t
4075  n,
4076  number_rows,
4077  y;
4078 
4079  /*
4080  Initialize scaled image attributes.
4081  */
4082  assert(image != (const Image *) NULL);
4083  assert(image->signature == MagickCoreSignature);
4084  if (image->debug != MagickFalse)
4085  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4086  assert(exception != (ExceptionInfo *) NULL);
4087  assert(exception->signature == MagickCoreSignature);
4088  if ((columns == 0) || (rows == 0))
4089  ThrowImageException(ImageError,"NegativeOrZeroImageSize");
4090  if ((columns == image->columns) && (rows == image->rows))
4091  return(CloneImage(image,0,0,MagickTrue,exception));
4092  scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
4093  if (scale_image == (Image *) NULL)
4094  return((Image *) NULL);
4095  if (SetImageStorageClass(scale_image,DirectClass,exception) == MagickFalse)
4096  {
4097  scale_image=DestroyImage(scale_image);
4098  return((Image *) NULL);
4099  }
4100  /*
4101  Allocate memory.
4102  */
4103  x_vector=(double *) AcquireQuantumMemory((size_t) image->columns,
4104  MaxPixelChannels*sizeof(*x_vector));
4105  scanline=x_vector;
4106  if (image->rows != scale_image->rows)
4107  scanline=(double *) AcquireQuantumMemory((size_t) image->columns,
4108  MaxPixelChannels*sizeof(*scanline));
4109  scale_scanline=(double *) AcquireQuantumMemory((size_t) scale_image->columns,
4110  MaxPixelChannels*sizeof(*scale_scanline));
4111  y_vector=(double *) AcquireQuantumMemory((size_t) image->columns,
4112  MaxPixelChannels*sizeof(*y_vector));
4113  if ((scanline == (double *) NULL) || (scale_scanline == (double *) NULL) ||
4114  (x_vector == (double *) NULL) || (y_vector == (double *) NULL))
4115  {
4116  if ((image->rows != scale_image->rows) && (scanline != (double *) NULL))
4117  scanline=(double *) RelinquishMagickMemory(scanline);
4118  if (scale_scanline != (double *) NULL)
4119  scale_scanline=(double *) RelinquishMagickMemory(scale_scanline);
4120  if (x_vector != (double *) NULL)
4121  x_vector=(double *) RelinquishMagickMemory(x_vector);
4122  if (y_vector != (double *) NULL)
4123  y_vector=(double *) RelinquishMagickMemory(y_vector);
4124  scale_image=DestroyImage(scale_image);
4125  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4126  }
4127  /*
4128  Scale image.
4129  */
4130  number_rows=0;
4131  next_row=MagickTrue;
4132  span.y=1.0;
4133  scale.y=(double) scale_image->rows/(double) image->rows;
4134  (void) memset(y_vector,0,(size_t) MaxPixelChannels*image->columns*
4135  sizeof(*y_vector));
4136  n=0;
4137  status=MagickTrue;
4138  image_view=AcquireVirtualCacheView(image,exception);
4139  scale_view=AcquireAuthenticCacheView(scale_image,exception);
4140  for (y=0; y < (ssize_t) scale_image->rows; y++)
4141  {
4142  register const Quantum
4143  *magick_restrict p;
4144 
4145  register Quantum
4146  *magick_restrict q;
4147 
4148  register ssize_t
4149  x;
4150 
4151  if (status == MagickFalse)
4152  break;
4153  q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
4154  exception);
4155  if (q == (Quantum *) NULL)
4156  {
4157  status=MagickFalse;
4158  break;
4159  }
4160  alpha=1.0;
4161  if (scale_image->rows == image->rows)
4162  {
4163  /*
4164  Read a new scanline.
4165  */
4166  p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
4167  exception);
4168  if (p == (const Quantum *) NULL)
4169  {
4170  status=MagickFalse;
4171  break;
4172  }
4173  for (x=0; x < (ssize_t) image->columns; x++)
4174  {
4175  if (GetPixelWriteMask(image,p) <= (QuantumRange/2))
4176  {
4177  p+=GetPixelChannels(image);
4178  continue;
4179  }
4180  if (image->alpha_trait != UndefinedPixelTrait)
4181  alpha=QuantumScale*GetPixelAlpha(image,p);
4182  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4183  {
4184  PixelChannel channel = GetPixelChannelChannel(image,i);
4185  PixelTrait traits = GetPixelChannelTraits(image,channel);
4186  if ((traits & BlendPixelTrait) == 0)
4187  {
4188  x_vector[x*GetPixelChannels(image)+i]=(double) p[i];
4189  continue;
4190  }
4191  x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
4192  }
4193  p+=GetPixelChannels(image);
4194  }
4195  }
4196  else
4197  {
4198  /*
4199  Scale Y direction.
4200  */
4201  while (scale.y < span.y)
4202  {
4203  if ((next_row != MagickFalse) &&
4204  (number_rows < (ssize_t) image->rows))
4205  {
4206  /*
4207  Read a new scanline.
4208  */
4209  p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
4210  exception);
4211  if (p == (const Quantum *) NULL)
4212  {
4213  status=MagickFalse;
4214  break;
4215  }
4216  for (x=0; x < (ssize_t) image->columns; x++)
4217  {
4218  if (GetPixelWriteMask(image,p) <= (QuantumRange/2))
4219  {
4220  p+=GetPixelChannels(image);
4221  continue;
4222  }
4223  if (image->alpha_trait != UndefinedPixelTrait)
4224  alpha=QuantumScale*GetPixelAlpha(image,p);
4225  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4226  {
4227  PixelChannel channel = GetPixelChannelChannel(image,i);
4228  PixelTrait traits = GetPixelChannelTraits(image,channel);
4229  if ((traits & BlendPixelTrait) == 0)
4230  {
4231  x_vector[x*GetPixelChannels(image)+i]=(double) p[i];
4232  continue;
4233  }
4234  x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
4235  }
4236  p+=GetPixelChannels(image);
4237  }
4238  number_rows++;
4239  }
4240  for (x=0; x < (ssize_t) image->columns; x++)
4241  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4242  y_vector[x*GetPixelChannels(image)+i]+=scale.y*
4243  x_vector[x*GetPixelChannels(image)+i];
4244  span.y-=scale.y;
4245  scale.y=(double) scale_image->rows/(double) image->rows;
4246  next_row=MagickTrue;
4247  }
4248  if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
4249  {
4250  /*
4251  Read a new scanline.
4252  */
4253  p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
4254  exception);
4255  if (p == (const Quantum *) NULL)
4256  {
4257  status=MagickFalse;
4258  break;
4259  }
4260  for (x=0; x < (ssize_t) image->columns; x++)
4261  {
4262  if (GetPixelWriteMask(image,p) <= (QuantumRange/2))
4263  {
4264  p+=GetPixelChannels(image);
4265  continue;
4266  }
4267  if (image->alpha_trait != UndefinedPixelTrait)
4268  alpha=QuantumScale*GetPixelAlpha(image,p);
4269  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4270  {
4271  PixelChannel channel = GetPixelChannelChannel(image,i);
4272  PixelTrait traits = GetPixelChannelTraits(image,channel);
4273  if ((traits & BlendPixelTrait) == 0)
4274  {
4275  x_vector[x*GetPixelChannels(image)+i]=(double) p[i];
4276  continue;
4277  }
4278  x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
4279  }
4280  p+=GetPixelChannels(image);
4281  }
4282  number_rows++;
4283  next_row=MagickFalse;
4284  }
4285  for (x=0; x < (ssize_t) image->columns; x++)
4286  {
4287  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4288  {
4289  pixel[i]=y_vector[x*GetPixelChannels(image)+i]+span.y*
4290  x_vector[x*GetPixelChannels(image)+i];
4291  scanline[x*GetPixelChannels(image)+i]=pixel[i];
4292  y_vector[x*GetPixelChannels(image)+i]=0.0;
4293  }
4294  }
4295  scale.y-=span.y;
4296  if (scale.y <= 0)
4297  {
4298  scale.y=(double) scale_image->rows/(double) image->rows;
4299  next_row=MagickTrue;
4300  }
4301  span.y=1.0;
4302  }
4303  if (scale_image->columns == image->columns)
4304  {
4305  /*
4306  Transfer scanline to scaled image.
4307  */
4308  for (x=0; x < (ssize_t) scale_image->columns; x++)
4309  {
4310  if (GetPixelWriteMask(scale_image,q) <= (QuantumRange/2))
4311  {
4312  q+=GetPixelChannels(scale_image);
4313  continue;
4314  }
4315  if (image->alpha_trait != UndefinedPixelTrait)
4316  {
4317  alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
4319  alpha=PerceptibleReciprocal(alpha);
4320  }
4321  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4322  {
4323  PixelChannel channel = GetPixelChannelChannel(image,i);
4324  PixelTrait traits = GetPixelChannelTraits(image,channel);
4325  scale_traits=GetPixelChannelTraits(scale_image,channel);
4326  if ((traits == UndefinedPixelTrait) ||
4327  (scale_traits == UndefinedPixelTrait))
4328  continue;
4329  if ((traits & BlendPixelTrait) == 0)
4330  {
4331  SetPixelChannel(scale_image,channel,ClampToQuantum(
4332  scanline[x*GetPixelChannels(image)+i]),q);
4333  continue;
4334  }
4335  SetPixelChannel(scale_image,channel,ClampToQuantum(alpha*scanline[
4336  x*GetPixelChannels(image)+i]),q);
4337  }
4338  q+=GetPixelChannels(scale_image);
4339  }
4340  }
4341  else
4342  {
4343  ssize_t
4344  t;
4345 
4346  /*
4347  Scale X direction.
4348  */
4349  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4350  pixel[i]=0.0;
4351  next_column=MagickFalse;
4352  span.x=1.0;
4353  t=0;
4354  for (x=0; x < (ssize_t) image->columns; x++)
4355  {
4356  scale.x=(double) scale_image->columns/(double) image->columns;
4357  while (scale.x >= span.x)
4358  {
4359  if (next_column != MagickFalse)
4360  {
4361  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4362  pixel[i]=0.0;
4363  t++;
4364  }
4365  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4366  {
4367  PixelChannel channel = GetPixelChannelChannel(image,i);
4368  PixelTrait traits = GetPixelChannelTraits(image,channel);
4369  if (traits == UndefinedPixelTrait)
4370  continue;
4371  pixel[i]+=span.x*scanline[x*GetPixelChannels(image)+i];
4372  scale_scanline[t*GetPixelChannels(image)+i]=pixel[i];
4373  }
4374  scale.x-=span.x;
4375  span.x=1.0;
4376  next_column=MagickTrue;
4377  }
4378  if (scale.x > 0)
4379  {
4380  if (next_column != MagickFalse)
4381  {
4382  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4383  pixel[i]=0.0;
4384  next_column=MagickFalse;
4385  t++;
4386  }
4387  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4388  pixel[i]+=scale.x*scanline[x*GetPixelChannels(image)+i];
4389  span.x-=scale.x;
4390  }
4391  }
4392  if (span.x > 0)
4393  {
4394  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4395  pixel[i]+=span.x*scanline[(x-1)*GetPixelChannels(image)+i];
4396  }
4397  if ((next_column == MagickFalse) &&
4398  (t < (ssize_t) scale_image->columns))
4399  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4400  scale_scanline[t*GetPixelChannels(image)+i]=pixel[i];
4401  /*
4402  Transfer scanline to scaled image.
4403  */
4404  for (x=0; x < (ssize_t) scale_image->columns; x++)
4405  {
4406  if (GetPixelWriteMask(scale_image,q) <= (QuantumRange/2))
4407  {
4408  q+=GetPixelChannels(scale_image);
4409  continue;
4410  }
4411  if (image->alpha_trait != UndefinedPixelTrait)
4412  {
4413  alpha=QuantumScale*scale_scanline[x*GetPixelChannels(image)+
4415  alpha=PerceptibleReciprocal(alpha);
4416  }
4417  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4418  {
4419  PixelChannel channel = GetPixelChannelChannel(image,i);
4420  PixelTrait traits = GetPixelChannelTraits(image,channel);
4421  scale_traits=GetPixelChannelTraits(scale_image,channel);
4422  if ((traits == UndefinedPixelTrait) ||
4423  (scale_traits == UndefinedPixelTrait))
4424  continue;
4425  if ((traits & BlendPixelTrait) == 0)
4426  {
4427  SetPixelChannel(scale_image,channel,ClampToQuantum(
4428  scale_scanline[x*GetPixelChannels(image)+i]),q);
4429  continue;
4430  }
4431  SetPixelChannel(scale_image,channel,ClampToQuantum(alpha*
4432  scale_scanline[x*GetPixelChannels(image)+i]),q);
4433  }
4434  q+=GetPixelChannels(scale_image);
4435  }
4436  }
4437  if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
4438  {
4439  status=MagickFalse;
4440  break;
4441  }
4443  image->rows);
4444  if (proceed == MagickFalse)
4445  {
4446  status=MagickFalse;
4447  break;
4448  }
4449  }
4450  scale_view=DestroyCacheView(scale_view);
4451  image_view=DestroyCacheView(image_view);
4452  /*
4453  Free allocated memory.
4454  */
4455  y_vector=(double *) RelinquishMagickMemory(y_vector);
4456  scale_scanline=(double *) RelinquishMagickMemory(scale_scanline);
4457  if (scale_image->rows != image->rows)
4458  scanline=(double *) RelinquishMagickMemory(scanline);
4459  x_vector=(double *) RelinquishMagickMemory(x_vector);
4460  scale_image->type=image->type;
4461  if (status == MagickFalse)
4462  scale_image=DestroyImage(scale_image);
4463  return(scale_image);
4464 }
4465 
4466 /*
4467 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4468 % %
4469 % %
4470 % %
4471 % T h u m b n a i l I m a g e %
4472 % %
4473 % %
4474 % %
4475 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4476 %
4477 % ThumbnailImage() changes the size of an image to the given dimensions and
4478 % removes any associated profiles. The goal is to produce small low cost
4479 % thumbnail images suited for display on the Web.
4480 %
4481 % The format of the ThumbnailImage method is:
4482 %
4483 % Image *ThumbnailImage(const Image *image,const size_t columns,
4484 % const size_t rows,ExceptionInfo *exception)
4485 %
4486 % A description of each parameter follows:
4487 %
4488 % o image: the image.
4489 %
4490 % o columns: the number of columns in the scaled image.
4491 %
4492 % o rows: the number of rows in the scaled image.
4493 %
4494 % o exception: return any errors or warnings in this structure.
4495 %
4496 */
4497 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
4498  const size_t rows,ExceptionInfo *exception)
4499 {
4500 #define SampleFactor 5
4501 
4502  char
4503  filename[MagickPathExtent],
4504  value[MagickPathExtent];
4505 
4506  const char
4507  *name;
4508 
4509  Image
4510  *thumbnail_image;
4511 
4512  double
4513  x_factor,
4514  y_factor;
4515 
4516  struct stat
4517  attributes;
4518 
4519  assert(image != (Image *) NULL);
4520  assert(image->signature == MagickCoreSignature);
4521  if (image->debug != MagickFalse)
4522  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4523  assert(exception != (ExceptionInfo *) NULL);
4524  assert(exception->signature == MagickCoreSignature);
4525  x_factor=(double) columns/(double) image->columns;
4526  y_factor=(double) rows/(double) image->rows;
4527  if ((x_factor*y_factor) > 0.1)
4528  thumbnail_image=ResizeImage(image,columns,rows,image->filter,exception);
4529  else
4530  if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
4531  thumbnail_image=ResizeImage(image,columns,rows,image->filter,exception);
4532  else
4533  {
4534  Image
4535  *sample_image;
4536 
4537  sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
4538  exception);
4539  if (sample_image == (Image *) NULL)
4540  return((Image *) NULL);
4541  thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
4542  exception);
4543  sample_image=DestroyImage(sample_image);
4544  }
4545  if (thumbnail_image == (Image *) NULL)
4546  return(thumbnail_image);
4547  (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
4548  if (thumbnail_image->alpha_trait == UndefinedPixelTrait)
4549  (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel,exception);
4550  thumbnail_image->depth=8;
4551  thumbnail_image->interlace=NoInterlace;
4552  /*
4553  Strip all profiles except color profiles.
4554  */
4555  ResetImageProfileIterator(thumbnail_image);
4556  for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
4557  {
4558  if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
4559  {
4560  (void) DeleteImageProfile(thumbnail_image,name);
4561  ResetImageProfileIterator(thumbnail_image);
4562  }
4563  name=GetNextImageProfile(thumbnail_image);
4564  }
4565  (void) DeleteImageProperty(thumbnail_image,"comment");
4566  (void) CopyMagickString(value,image->magick_filename,MagickPathExtent);
4567  if (strstr(image->magick_filename,"//") == (char *) NULL)
4568  (void) FormatLocaleString(value,MagickPathExtent,"file://%s",
4569  image->magick_filename);
4570  (void) SetImageProperty(thumbnail_image,"Thumb::URI",value,exception);
4571  GetPathComponent(image->magick_filename,TailPath,filename);
4572  (void) CopyMagickString(value,filename,MagickPathExtent);
4573  if ( GetPathAttributes(image->filename,&attributes) != MagickFalse )
4574  {
4575  (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
4576  attributes.st_mtime);
4577  (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value,exception);
4578  }
4579  (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
4580  attributes.st_mtime);
4582  value);
4583  (void) SetImageProperty(thumbnail_image,"Thumb::Size",value,exception);
4584  (void) FormatLocaleString(value,MagickPathExtent,"image/%s",image->magick);
4585  LocaleLower(value);
4586  (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value,exception);
4587  (void) SetImageProperty(thumbnail_image,"software",MagickAuthoritativeURL,
4588  exception);
4589  (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
4590  image->magick_columns);
4591  (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value,
4592  exception);
4593  (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
4594  image->magick_rows);
4595  (void) SetImageProperty(thumbnail_image,"Thumb::Image::Height",value,
4596  exception);
4597  (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
4598  GetImageListLength(image));
4599  (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value,
4600  exception);
4601  return(thumbnail_image);
4602 }
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:3690
#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:1093
#define MagickSQ1_2
Definition: image-private.h:32
static ssize_t GetPixelChannelOffset(const Image *magick_restrict image, const PixelChannel channel)
MagickPrivate double GetResizeFilterWindowSupport(const ResizeFilter *)
static void Eagle3X(const Image *source, const Quantum *pixels, Quantum *result, const size_t channels)
Definition: resize.c:2660
double(*)(*) blur
Definition: resize.c:94
MagickExport MemoryInfo * RelinquishVirtualMemory(MemoryInfo *memory_info)
Definition: memory.c:1136
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 MagickBooleanType HorizontalFilter(const ResizeFilter *magick_restrict resize_filter, const Image *magick_restrict image, Image *magick_restrict resize_image, const double x_factor, const MagickSizeType span, MagickOffsetType *magick_restrict progress, ExceptionInfo *exception)
Definition: resize.c:3254
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
ssize_t y
Definition: geometry.h:116
static double Jinc(const double x, const ResizeFilter *magick_unused(resize_filter))
Definition: resize.c:346
static void Xbr2X(const Image *source, const Quantum *pixels, Quantum *result, const size_t channels)
Definition: resize.c:2506
MagickExport ssize_t ParseCommandOption(const CommandOption option, const MagickBooleanType list, const char *options)
Definition: option.c:2966
static double I0(double x)
Definition: resize.c:1341
static void Mix2Pixels(const Quantum *source, const ssize_t source_offset1, const ssize_t source_offset2, Quantum *destination, const ssize_t destination_offset, const size_t channels)
Definition: resize.c:2096
#define WeightVar(M, N)
MagickExport MagickBooleanType DeleteImageProfile(Image *image, const char *name)
Definition: profile.c:220
PixelInterpolateMethod
Definition: pixel.h:110
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:581
static void Epbx2X(const Image *source, const Quantum *pixels, Quantum *result, const size_t channels)
Definition: resize.c:2607
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:703
MagickExport MagickBooleanType DeleteImageArtifact(Image *image, const char *artifact)
Definition: artifact.c:198
#define MagickAuthoritativeURL
Definition: version.h:53
#define SampleFactor
static double Blackman(const double x, const ResizeFilter *magick_unused(resize_filter))
Definition: resize.c:149
static double Q1(double x)
Definition: resize.c:1452
#define caseA(N, A, B, C, D)
MagickExport MagickBooleanType InterpolatePixelChannels(const Image *magick_restrict source, const CacheView_ *source_view, const Image *magick_restrict destination, const PixelInterpolateMethod method, const double x, const double y, Quantum *pixel, ExceptionInfo *exception)
Definition: pixel.c:4911
MagickExport const char * GetImageArtifact(const Image *image, const char *artifact)
Definition: artifact.c:273
#define ResizeImageTag
MagickPrivate double GetResizeFilterSupport(const ResizeFilter *)
static ContributionInfo ** AcquireContributionThreadSet(const size_t count)
Definition: resize.c:3227
static void MixPixels(const Quantum *source, const ssize_t *source_offset, const size_t source_size, Quantum *destination, const ssize_t destination_offset, const size_t channels)
Definition: resize.c:2074
static double StringToDouble(const char *magick_restrict string, char **magick_restrict sentinal)
static PixelTrait GetPixelChannelTraits(const Image *magick_restrict image, const PixelChannel channel)
MagickPrivate double GetResizeFilterScale(const ResizeFilter *)
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:2021
#define MagickPI
Definition: image-private.h:30
MagickPrivate ResizeFilter * AcquireResizeFilter(const Image *, const FilterType, const MagickBooleanType, ExceptionInfo *)
MagickExport Image * SampleImage(const Image *image, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: resize.c:3836
static void Scale2X(const Image *source, const Quantum *pixels, Quantum *result, const size_t channels)
Definition: resize.c:2576
ssize_t pixel
Definition: resize.c:3209
MagickExport ssize_t FormatLocaleString(char *magick_restrict string, const size_t length, const char *magick_restrict format,...)
Definition: locale.c:499
ResizeWeightingFunctionType windowWeightingType
Definition: resize.c:101
static void * AcquireCriticalMemory(const size_t size)
#define MagickPI2
Definition: image-private.h:31
char magick[MagickPathExtent]
Definition: image.h:319
size_t magick_rows
Definition: image.h:324
MagickExport const Quantum * GetCacheViewVirtualPixels(const CacheView *cache_view, const ssize_t x, const ssize_t y, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: cache-view.c:651
static long StringToLong(const char *magick_restrict value)
double(*)(*) support
Definition: resize.c:94
MagickExport const char * GetImageOption(const ImageInfo *image_info, const char *option)
Definition: option.c:2301
float MagickFloatType
Definition: magick-type.h:40
#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 *)
size_t width
Definition: geometry.h:130
Definition: log.h:52
ssize_t MagickOffsetType
Definition: magick-type.h:129
MagickExport Image * ThumbnailImage(const Image *image, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: resize.c:4497
Definition: image.h:151
static MagickBooleanType VerticalFilter(const ResizeFilter *magick_restrict resize_filter, const Image *magick_restrict image, Image *magick_restrict resize_image, const double y_factor, const MagickSizeType span, MagickOffsetType *magick_restrict progress, ExceptionInfo *exception)
Definition: resize.c:3474
MagickPrivate double GetResizeFilterBlur(const ResizeFilter *)
double x
Definition: geometry.h:123
#define MagickCoreSignature
static void Scale3X(const Image *source, const Quantum *pixels, Quantum *result, const size_t channels)
Definition: resize.c:2728
static void Hq2XHelper(const unsigned int rule, const Quantum *source, Quantum *destination, const ssize_t destination_offset, const size_t channels, const ssize_t e, const ssize_t a, const ssize_t b, const ssize_t d, const ssize_t f, const ssize_t h)
Definition: resize.c:2143
static double Lagrange(const double x, const ResizeFilter *resize_filter)
Definition: resize.c:382
MagickExport void GetPathComponent(const char *path, PathType type, char *component)
Definition: utility.c:1213
MagickExport ssize_t FormatLocaleFile(FILE *file, const char *magick_restrict format,...)
Definition: locale.c:404
MagickExport MagickBooleanType SetImageAlphaChannel(Image *image, const AlphaChannelOption alpha_type, ExceptionInfo *exception)
Definition: channel.c:974
MagickBooleanType
Definition: magick-type.h:158
size_t signature
Definition: resize.c:105
unsigned int MagickStatusType
Definition: magick-type.h:121
static double PerceptibleReciprocal(const double x)
MagickExport void LocaleLower(char *string)
Definition: locale.c:1490
ClassType
Definition: magick-type.h:151
static double BesselOrderOne(double)
Definition: resize.c:1491
MagickExport const char * CommandOptionToMnemonic(const CommandOption option, const ssize_t type)
Definition: option.c:2679
static double Cosine(const double x, const ResizeFilter *magick_unused(resize_filter))
Definition: resize.c:195
struct _ImageInfo * image_info
Definition: image.h:342
#define Magick2PI
Definition: image-private.h:28
static ContributionInfo ** DestroyContributionThreadSet(ContributionInfo **contribution)
Definition: resize.c:3212
static Quantum GetPixelWriteMask(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
MagickExport Image * MinifyImage(const Image *image, ExceptionInfo *exception)
Definition: resize.c:3080
MagickExport void * AcquireQuantumMemory(const size_t count, const size_t quantum)
Definition: memory.c:543
static double P1(double x)
Definition: resize.c:1412
double y
Definition: geometry.h:123
static int GetOpenMPThreadId(void)
MagickExport MagickBooleanType SetImageProperty(Image *image, const char *property, const char *value, ExceptionInfo *exception)
Definition: property.c:4113
#define magick_unused(x)
RectangleInfo page
Definition: image.h:212
size_t magick_columns
Definition: image.h:324
size_t MagickSizeType
Definition: magick-type.h:130
#define MagnifyImageTag
#define MagickPathExtent
static unsigned int Hq2XPatternToNumber(const int *pattern)
Definition: resize.c:2315
MagickExport void * RelinquishAlignedMemory(void *memory)
Definition: memory.c:1032
MagickExport MagickBooleanType IsStringTrue(const char *value)
Definition: string.c:1425
PixelTrait alpha_trait
Definition: image.h:280
MagickExport int GetMagickPrecision(void)
Definition: magick.c:886
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
double(* filter)(const double, const ResizeFilter *)
Definition: resize.c:92
MagickPrivate double GetResizeFilterWeight(const ResizeFilter *, const double)
Definition: resize.c:1647
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:1145
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:1413
#define Corner(A, B, C, D)
size_t signature
Definition: image.h:354
MagickExport MagickSizeType GetMagickResourceLimit(const ResourceType type)
Definition: resource.c:790
#define QuantumScale
Definition: magick-type.h:115
size_t columns
Definition: image.h:172
struct _ContributionInfo ContributionInfo
ssize_t x
Definition: geometry.h:134
static double Quadratic(const double x, const ResizeFilter *magick_unused(resize_filter))
Definition: resize.c:415
size_t height
Definition: geometry.h:130
MagickExport MagickBooleanType SetImageStorageClass(Image *image, const ClassType storage_class, ExceptionInfo *exception)
Definition: image.c:2617
MagickExport size_t CopyMagickString(char *destination, const char *source, const size_t length)
Definition: string.c:755
MagickExport Image * ResampleImage(const Image *image, const double x_resolution, const double y_resolution, const FilterType filter, ExceptionInfo *exception)
Definition: resize.c:3130
PixelChannel
Definition: pixel.h:67
MagickExport void * AcquireAlignedMemory(const size_t count, const size_t quantum)
Definition: memory.c:242
MagickExport MagickBooleanType CopyImagePixels(Image *image, const Image *source_image, const RectangleInfo *geometry, const OffsetInfo *offset, ExceptionInfo *exception)
Definition: image.c:1049
MagickExport char * GetNextImageProfile(const Image *image)
Definition: profile.c:323
#define MagickMax(x, y)
Definition: image-private.h:26
static void CopyPixels(const Quantum *source, const ssize_t source_offset, Quantum *destination, const ssize_t destination_offset, const size_t channels)
Definition: resize.c:2064
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:1435
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
#define caseB(N, A, B, C, D, E, F, G, H)
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:1803
MagickExport Image * MagnifyImage(const Image *image, ExceptionInfo *exception)
Definition: resize.c:2815
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
MagickExport MagickBooleanType SetImageColorspace(Image *image, const ColorspaceType colorspace, ExceptionInfo *exception)
Definition: colorspace.c:1135
static double Sinc(const double, const ResizeFilter *)
static void Fish2X(const Image *source, const Quantum *pixels, Quantum *result, const size_t channels)
Definition: resize.c:2387
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
ResizeWeightingFunctionType
#define InterpolativeResizeImageTag
MagickExport MagickStatusType ParseGeometry(const char *geometry, GeometryInfo *geometry_info)
Definition: geometry.c:853
static void SetPixelChannel(const Image *magick_restrict image, const PixelChannel channel, const Quantum quantum, Quantum *magick_restrict pixel)
static void Eagle2X(const Image *source, const Quantum *pixels, Quantum *result, const size_t channels)
Definition: resize.c:2120
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
ssize_t x
Definition: geometry.h:116
static double Bohman(const double x, const ResizeFilter *magick_unused(resize_filter))
Definition: resize.c:164
MagickExport void * RelinquishMagickMemory(void *memory)
Definition: memory.c:1069
#define MaxPixelChannels
Definition: pixel.h:27
PointInfo resolution
Definition: image.h:209
#define magick_unreferenced(x)
#define Line(A, B, C, D)
static int PixelsEqual(const Quantum *source1, ssize_t offset1, const Quantum *source2, ssize_t offset2, const size_t channels)
Definition: resize.c:2106
MagickBooleanType(* MagickProgressMonitor)(const char *, const MagickOffsetType, const MagickSizeType, void *)
Definition: monitor.h:26
ResizeWeightingFunctionType filterWeightingType
Definition: resize.c:101
#define Rotated(p)
#define MagickPrivate
MagickExport void ResetImageProfileIterator(const Image *image)
Definition: profile.c:1365
#define MagickExport
static void Hq2X(const Image *source, const Quantum *pixels, Quantum *result, const size_t channels)
Definition: resize.c:2334
MagickExport Image * ScaleImage(const Image *image, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: resize.c:4038
static void Eagle3XB(const Image *source, const Quantum *pixels, Quantum *result, const size_t channels)
Definition: resize.c:2700
MagickExport MagickBooleanType SyncCacheViewAuthenticPixels(CacheView *magick_restrict cache_view, ExceptionInfo *exception)
Definition: cache-view.c:1100
ssize_t y
Definition: geometry.h:134
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
MagickPrivate double * GetResizeFilterCoefficient(const ResizeFilter *)
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:134
MagickExport void * GetVirtualMemoryBlob(const MemoryInfo *memory_info)
Definition: memory.c:963
#define HelperCond(a, b, c, d, e, f, g)
MagickPrivate ResizeWeightingFunctionType GetResizeFilterWindowWeightingType(const ResizeFilter *)
Definition: resize.c:1605
MagickExport MagickRealType GetPixelIntensity(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
Definition: pixel.c:2358
MagickExport size_t GetImageListLength(const Image *images)
Definition: list.c:696
MagickPrivate ResizeWeightingFunctionType GetResizeFilterWeightingType(const ResizeFilter *)
MagickPrivate ResizeFilter * DestroyResizeFilter(ResizeFilter *)
Definition: resize.c:1534
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:1181
MagickExport Image * CloneImage(const Image *image, const size_t columns, const size_t rows, const MagickBooleanType detach, ExceptionInfo *exception)
Definition: image.c:796
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
MagickExport MagickBooleanType SetImageProgress(const Image *image, const char *tag, const MagickOffsetType offset, const MagickSizeType extent)
Definition: monitor.c:136
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