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  }
2201  else
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  }
2214  else
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  }
2227  else
2228  CopyPixels(source,e,destination,destination_offset,channels);
2229  break;
2230  }
2231  case 15:
2232  {
2233  if (PixelsEqual(source,b,source,d,channels))
2234  {
2235  const ssize_t
2236  offsets[4] = { e, e, d, b };
2237 
2238  MixPixels(source,offsets,4,destination,destination_offset,channels);
2239  }
2240  else
2241  {
2242  const ssize_t
2243  offsets[4] = { e, e, e, a };
2244 
2245  MixPixels(source,offsets,4,destination,destination_offset,channels);
2246  }
2247  break;
2248  }
2249  case 16:
2250  {
2251  if (PixelsEqual(source,b,source,d,channels))
2252  {
2253  const ssize_t
2254  offsets[8] = { e, e, e, e, e, e, d, b };
2255 
2256  MixPixels(source,offsets,8,destination,destination_offset,channels);
2257  }
2258  else
2259  {
2260  const ssize_t
2261  offsets[4] = { e, e, e, a };
2262 
2263  MixPixels(source,offsets,4,destination,destination_offset,channels);
2264  }
2265  break;
2266  }
2267  case 17:
2268  {
2269  if (PixelsEqual(source,b,source,d,channels))
2270  {
2271  const ssize_t
2272  offsets[8] = { e, e, d, d, d, b, b, b };
2273 
2274  MixPixels(source,offsets,8,destination,destination_offset,channels);
2275  }
2276  else
2277  {
2278  const ssize_t
2279  offsets[4] = { e, e, e, a };
2280 
2281  MixPixels(source,offsets,4,destination,destination_offset,channels);
2282  }
2283  break;
2284  }
2285  case 18:
2286  {
2287  if (PixelsEqual(source,b,source,f,channels))
2288  {
2289  const ssize_t
2290  offsets[8] = { e, e, e, e, e, b, b, d };
2291 
2292  MixPixels(source,offsets,8,destination,destination_offset,channels);
2293  }
2294  else
2295  {
2296  const ssize_t
2297  offsets[4] = { e, e, e, d };
2298 
2299  MixPixels(source,offsets,4,destination,destination_offset,channels);
2300  }
2301  break;
2302  }
2303  default:
2304  {
2305  if (PixelsEqual(source,d,source,h,channels))
2306  {
2307  const ssize_t
2308  offsets[8] = { e, e, e, e, e, d, d, b };
2309 
2310  MixPixels(source,offsets,8,destination,destination_offset,channels);
2311  }
2312  else
2313  {
2314  const ssize_t
2315  offsets[4] = { e, e, e, b };
2316 
2317  MixPixels(source,offsets,4,destination,destination_offset,channels);
2318  }
2319  break;
2320  }
2321  }
2322  #undef caseA
2323  #undef caseB
2324 }
2325 
2326 static inline unsigned int Hq2XPatternToNumber(const int *pattern)
2327 {
2328  ssize_t
2329  i;
2330 
2331  unsigned int
2332  result,
2333  order;
2334 
2335  result=0;
2336  order=1;
2337  for (i=7; i >= 0; i--)
2338  {
2339  result+=order*pattern[i];
2340  order*=2;
2341  }
2342  return(result);
2343 }
2344 
2345 static inline void Hq2X(const Image *source,const Quantum *pixels,
2346  Quantum *result,const size_t channels)
2347 {
2348  static const unsigned int
2349  Hq2XTable[] =
2350  {
2351  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13,
2352  4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 12, 12, 5, 3, 1, 12,
2353  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14,
2354  4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 16, 12, 5, 3, 1, 14,
2355  4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 12, 12, 5, 19, 16, 12,
2356  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12,
2357  4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 1, 12, 5, 19, 1, 14,
2358  4, 4, 6, 2, 4, 4, 6, 18, 5, 3, 16, 12, 5, 19, 1, 14,
2359  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13,
2360  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12,
2361  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14,
2362  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 13, 5, 3, 1, 14,
2363  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 13,
2364  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 12,
2365  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 14,
2366  4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 1, 12, 5, 3, 1, 14
2367  };
2368 
2369  const int
2370  pattern1[] =
2371  {
2372  !PixelsEqual(pixels,4,pixels,8,channels),
2373  !PixelsEqual(pixels,4,pixels,7,channels),
2374  !PixelsEqual(pixels,4,pixels,6,channels),
2375  !PixelsEqual(pixels,4,pixels,5,channels),
2376  !PixelsEqual(pixels,4,pixels,3,channels),
2377  !PixelsEqual(pixels,4,pixels,2,channels),
2378  !PixelsEqual(pixels,4,pixels,1,channels),
2379  !PixelsEqual(pixels,4,pixels,0,channels)
2380  };
2381 
2382 #define Rotated(p) p[2], p[4], p[7], p[1], p[6], p[0], p[3], p[5]
2383  const int pattern2[] = { Rotated(pattern1) };
2384  const int pattern3[] = { Rotated(pattern2) };
2385  const int pattern4[] = { Rotated(pattern3) };
2386 #undef Rotated
2387 
2388  Hq2XHelper(Hq2XTable[Hq2XPatternToNumber(pattern1)],pixels,result,0,
2389  channels,4,0,1,3,5,7);
2390  Hq2XHelper(Hq2XTable[Hq2XPatternToNumber(pattern2)],pixels,result,1,
2391  channels,4,2,5,1,7,3);
2392  Hq2XHelper(Hq2XTable[Hq2XPatternToNumber(pattern3)],pixels,result,3,
2393  channels,4,8,7,5,3,1);
2394  Hq2XHelper(Hq2XTable[Hq2XPatternToNumber(pattern4)],pixels,result,2,
2395  channels,4,6,3,7,1,5);
2396 }
2397 
2398 static void Fish2X(const Image *source,const Quantum *pixels,Quantum *result,
2399  const size_t channels)
2400 {
2401 #define Corner(A,B,C,D) \
2402  { \
2403  if (intensities[B] > intensities[A]) \
2404  { \
2405  ssize_t \
2406  offsets[3] = { B, C, D }; \
2407  \
2408  MixPixels(pixels,offsets,3,result,3,channels); \
2409  } \
2410  else \
2411  { \
2412  ssize_t \
2413  offsets[3] = { A, B, C }; \
2414  \
2415  MixPixels(pixels,offsets,3,result,3,channels); \
2416  } \
2417  }
2418 
2419 #define Line(A,B,C,D) \
2420  { \
2421  if (intensities[C] > intensities[A]) \
2422  Mix2Pixels(pixels,C,D,result,3,channels); \
2423  else \
2424  Mix2Pixels(pixels,A,B,result,3,channels); \
2425  }
2426 
2428  intensities[9];
2429 
2430  int
2431  ae,
2432  bd,
2433  ab,
2434  ad,
2435  be,
2436  de;
2437 
2438  register ssize_t
2439  i;
2440 
2441  ssize_t
2442  offsets[4] = { 0, 1, 3, 4 };
2443 
2444  for (i=0; i < 9; i++)
2445  intensities[i]=GetPixelIntensity(source,pixels + i*channels);
2446  CopyPixels(pixels,0,result,0,channels);
2447  CopyPixels(pixels,(ssize_t) (intensities[0] > intensities[1] ? 0 : 1),result,
2448  1,channels);
2449  CopyPixels(pixels,(ssize_t) (intensities[0] > intensities[3] ? 0 : 3),result,
2450  2,channels);
2451  ae=PixelsEqual(pixels,0,pixels,4,channels);
2452  bd=PixelsEqual(pixels,1,pixels,3,channels);
2453  ab=PixelsEqual(pixels,0,pixels,1,channels);
2454  de=PixelsEqual(pixels,3,pixels,4,channels);
2455  ad=PixelsEqual(pixels,0,pixels,3,channels);
2456  be=PixelsEqual(pixels,1,pixels,4,channels);
2457  if (ae && bd && ab)
2458  {
2459  CopyPixels(pixels,0,result,3,channels);
2460  return;
2461  }
2462  if (ad && de && !ab)
2463  {
2464  Corner(1,0,4,3)
2465  return;
2466  }
2467  if (be && de && !ab)
2468  {
2469  Corner(0,1,3,4)
2470  return;
2471  }
2472  if (ad && ab && !be)
2473  {
2474  Corner(4,3,1,0)
2475  return;
2476  }
2477  if (ab && be && !ad)
2478  {
2479  Corner(3,0,4,1)
2480  return;
2481  }
2482  if (ae && (!bd || intensities[1] > intensities[0]))
2483  {
2484  Mix2Pixels(pixels,0,4,result,3,channels);
2485  return;
2486  }
2487  if (bd && (!ae || intensities[0] > intensities[1]))
2488  {
2489  Mix2Pixels(pixels,1,3,result,3,channels);
2490  return;
2491  }
2492  if (ab)
2493  {
2494  Line(0,1,3,4)
2495  return;
2496  }
2497  if (de)
2498  {
2499  Line(3,4,0,1)
2500  return;
2501  }
2502  if (ad)
2503  {
2504  Line(0,3,1,4)
2505  return;
2506  }
2507  if (be)
2508  {
2509  Line(1,4,0,3)
2510  return;
2511  }
2512  MixPixels(pixels,offsets,4,result,3,channels);
2513 #undef Corner
2514 #undef Line
2515 }
2516 
2517 static void Xbr2X(const Image *source,const Quantum *pixels,Quantum *result,
2518  const size_t channels)
2519 {
2520 #define WeightVar(M,N) const int w_##M##_##N = \
2521  PixelsEqual(pixels,M,pixels,N,channels) ? 0 : 1;
2522 
2523  WeightVar(12,11)
2524  WeightVar(12,7)
2525  WeightVar(12,13)
2526  WeightVar(12,17)
2527  WeightVar(12,16)
2528  WeightVar(12,8)
2529  WeightVar(6,10)
2530  WeightVar(6,2)
2531  WeightVar(11,7)
2532  WeightVar(11,17)
2533  WeightVar(11,5)
2534  WeightVar(7,13)
2535  WeightVar(7,1)
2536  WeightVar(12,6)
2537  WeightVar(12,18)
2538  WeightVar(8,14)
2539  WeightVar(8,2)
2540  WeightVar(13,17)
2541  WeightVar(13,9)
2542  WeightVar(7,3)
2543  WeightVar(16,10)
2544  WeightVar(16,22)
2545  WeightVar(17,21)
2546  WeightVar(11,15)
2547  WeightVar(18,14)
2548  WeightVar(18,22)
2549  WeightVar(17,23)
2550  WeightVar(17,19)
2551 #undef WeightVar
2552 
2553  if (
2554  w_12_16 + w_12_8 + w_6_10 + w_6_2 + (4 * w_11_7) <
2555  w_11_17 + w_11_5 + w_7_13 + w_7_1 + (4 * w_12_6)
2556  )
2557  Mix2Pixels(pixels,(ssize_t) (w_12_11 <= w_12_7 ? 11 : 7),12,result,0,
2558  channels);
2559  else
2560  CopyPixels(pixels,12,result,0,channels);
2561  if (
2562  w_12_18 + w_12_6 + w_8_14 + w_8_2 + (4 * w_7_13) <
2563  w_13_17 + w_13_9 + w_11_7 + w_7_3 + (4 * w_12_8)
2564  )
2565  Mix2Pixels(pixels,(ssize_t) (w_12_7 <= w_12_13 ? 7 : 13),12,result,1,
2566  channels);
2567  else
2568  CopyPixels(pixels,12,result,1,channels);
2569  if (
2570  w_12_6 + w_12_18 + w_16_10 + w_16_22 + (4 * w_11_17) <
2571  w_11_7 + w_11_15 + w_13_17 + w_17_21 + (4 * w_12_16)
2572  )
2573  Mix2Pixels(pixels,(ssize_t) (w_12_11 <= w_12_17 ? 11 : 17),12,result,2,
2574  channels);
2575  else
2576  CopyPixels(pixels,12,result,2,channels);
2577  if (
2578  w_12_8 + w_12_16 + w_18_14 + w_18_22 + (4 * w_13_17) <
2579  w_11_17 + w_17_23 + w_17_19 + w_7_13 + (4 * w_12_18)
2580  )
2581  Mix2Pixels(pixels,(ssize_t) (w_12_13 <= w_12_17 ? 13 : 17),12,result,3,
2582  channels);
2583  else
2584  CopyPixels(pixels,12,result,3,channels);
2585 }
2586 
2587 static void Scale2X(const Image *source,const Quantum *pixels,Quantum *result,
2588  const size_t channels)
2589 {
2590  if (PixelsEqual(pixels,1,pixels,7,channels) ||
2591  PixelsEqual(pixels,3,pixels,5,channels))
2592  {
2593  register ssize_t
2594  i;
2595 
2596  for (i=0; i < 4; i++)
2597  CopyPixels(pixels,4,result,i,channels);
2598  return;
2599  }
2600  if (PixelsEqual(pixels,1,pixels,3,channels))
2601  CopyPixels(pixels,3,result,0,channels);
2602  else
2603  CopyPixels(pixels,4,result,0,channels);
2604  if (PixelsEqual(pixels,1,pixels,5,channels))
2605  CopyPixels(pixels,5,result,1,channels);
2606  else
2607  CopyPixels(pixels,4,result,1,channels);
2608  if (PixelsEqual(pixels,3,pixels,7,channels))
2609  CopyPixels(pixels,3,result,2,channels);
2610  else
2611  CopyPixels(pixels,4,result,2,channels);
2612  if (PixelsEqual(pixels,5,pixels,7,channels))
2613  CopyPixels(pixels,5,result,3,channels);
2614  else
2615  CopyPixels(pixels,4,result,3,channels);
2616 }
2617 
2618 static void Epbx2X(const Image *source,const Quantum *pixels,
2619  Quantum *result,const size_t channels)
2620 {
2621 #define HelperCond(a,b,c,d,e,f,g) ( \
2622  PixelsEqual(pixels,a,pixels,b,channels) && ( \
2623  PixelsEqual(pixels,c,pixels,d,channels) || \
2624  PixelsEqual(pixels,c,pixels,e,channels) || \
2625  PixelsEqual(pixels,a,pixels,f,channels) || \
2626  PixelsEqual(pixels,b,pixels,g,channels) \
2627  ) \
2628  )
2629 
2630  register ssize_t
2631  i;
2632 
2633  for (i=0; i < 4; i++)
2634  CopyPixels(pixels,4,result,i,channels);
2635  if (
2636  !PixelsEqual(pixels,3,pixels,5,channels) &&
2637  !PixelsEqual(pixels,1,pixels,7,channels) &&
2638  (
2639  PixelsEqual(pixels,4,pixels,3,channels) ||
2640  PixelsEqual(pixels,4,pixels,7,channels) ||
2641  PixelsEqual(pixels,4,pixels,5,channels) ||
2642  PixelsEqual(pixels,4,pixels,1,channels) ||
2643  (
2644  (
2645  !PixelsEqual(pixels,0,pixels,8,channels) ||
2646  PixelsEqual(pixels,4,pixels,6,channels) ||
2647  PixelsEqual(pixels,3,pixels,2,channels)
2648  ) &&
2649  (
2650  !PixelsEqual(pixels,6,pixels,2,channels) ||
2651  PixelsEqual(pixels,4,pixels,0,channels) ||
2652  PixelsEqual(pixels,4,pixels,8,channels)
2653  )
2654  )
2655  )
2656  )
2657  {
2658  if (HelperCond(1,3,4,0,8,2,6))
2659  Mix2Pixels(pixels,1,3,result,0,channels);
2660  if (HelperCond(5,1,4,2,6,8,0))
2661  Mix2Pixels(pixels,5,1,result,1,channels);
2662  if (HelperCond(3,7,4,6,2,0,8))
2663  Mix2Pixels(pixels,3,7,result,2,channels);
2664  if (HelperCond(7,5,4,8,0,6,2))
2665  Mix2Pixels(pixels,7,5,result,3,channels);
2666  }
2667 
2668 #undef HelperCond
2669 }
2670 
2671 static inline void Eagle3X(const Image *source,const Quantum *pixels,
2672  Quantum *result,const size_t channels)
2673 {
2674  ssize_t
2675  corner_tl,
2676  corner_tr,
2677  corner_bl,
2678  corner_br;
2679 
2680  corner_tl=PixelsEqual(pixels,0,pixels,1,channels) &&
2681  PixelsEqual(pixels,0,pixels,3,channels);
2682  corner_tr=PixelsEqual(pixels,1,pixels,2,channels) &&
2683  PixelsEqual(pixels,2,pixels,5,channels);
2684  corner_bl=PixelsEqual(pixels,3,pixels,6,channels) &&
2685  PixelsEqual(pixels,6,pixels,7,channels);
2686  corner_br=PixelsEqual(pixels,5,pixels,7,channels) &&
2687  PixelsEqual(pixels,7,pixels,8,channels);
2688  CopyPixels(pixels,(ssize_t) (corner_tl ? 0 : 4),result,0,channels);
2689  if (corner_tl && corner_tr)
2690  Mix2Pixels(pixels,0,2,result,1,channels);
2691  else
2692  CopyPixels(pixels,4,result,1,channels);
2693  CopyPixels(pixels,(ssize_t) (corner_tr ? 1 : 4),result,2,channels);
2694  if (corner_tl && corner_bl)
2695  Mix2Pixels(pixels,0,6,result,3,channels);
2696  else
2697  CopyPixels(pixels,4,result,3,channels);
2698  CopyPixels(pixels,4,result,4,channels);
2699  if (corner_tr && corner_br)
2700  Mix2Pixels(pixels,2,8,result,5,channels);
2701  else
2702  CopyPixels(pixels,4,result,5,channels);
2703  CopyPixels(pixels,(ssize_t) (corner_bl ? 3 : 4),result,6,channels);
2704  if (corner_bl && corner_br)
2705  Mix2Pixels(pixels,6,8,result,7,channels);
2706  else
2707  CopyPixels(pixels,4,result,7,channels);
2708  CopyPixels(pixels,(ssize_t) (corner_br ? 5 : 4),result,8,channels);
2709 }
2710 
2711 static inline void Eagle3XB(const Image *source,const Quantum *pixels,
2712  Quantum *result,const size_t channels)
2713 {
2714  ssize_t
2715  corner_tl,
2716  corner_tr,
2717  corner_bl,
2718  corner_br;
2719 
2720  corner_tl=PixelsEqual(pixels,0,pixels,1,channels) &&
2721  PixelsEqual(pixels,0,pixels,3,channels);
2722  corner_tr=PixelsEqual(pixels,1,pixels,2,channels) &&
2723  PixelsEqual(pixels,2,pixels,5,channels);
2724  corner_bl=PixelsEqual(pixels,3,pixels,6,channels) &&
2725  PixelsEqual(pixels,6,pixels,7,channels);
2726  corner_br=PixelsEqual(pixels,5,pixels,7,channels) &&
2727  PixelsEqual(pixels,7,pixels,8,channels);
2728  CopyPixels(pixels,(ssize_t) (corner_tl ? 0 : 4),result,0,channels);
2729  CopyPixels(pixels,4,result,1,channels);
2730  CopyPixels(pixels,(ssize_t) (corner_tr ? 1 : 4),result,2,channels);
2731  CopyPixels(pixels,4,result,3,channels);
2732  CopyPixels(pixels,4,result,4,channels);
2733  CopyPixels(pixels,4,result,5,channels);
2734  CopyPixels(pixels,(ssize_t) (corner_bl ? 3 : 4),result,6,channels);
2735  CopyPixels(pixels,4,result,7,channels);
2736  CopyPixels(pixels,(ssize_t) (corner_br ? 5 : 4),result,8,channels);
2737 }
2738 
2739 static inline void Scale3X(const Image *source,const Quantum *pixels,
2740  Quantum *result,const size_t channels)
2741 {
2742  if (!PixelsEqual(pixels,1,pixels,7,channels) &&
2743  !PixelsEqual(pixels,3,pixels,5,channels))
2744  {
2745  if (PixelsEqual(pixels,3,pixels,1,channels))
2746  CopyPixels(pixels,3,result,0,channels);
2747  else
2748  CopyPixels(pixels,4,result,0,channels);
2749 
2750  if (
2751  (
2752  PixelsEqual(pixels,3,pixels,1,channels) &&
2753  !PixelsEqual(pixels,4,pixels,2,channels)
2754  ) ||
2755  (
2756  PixelsEqual(pixels,5,pixels,1,channels) &&
2757  !PixelsEqual(pixels,4,pixels,0,channels)
2758  )
2759  )
2760  CopyPixels(pixels,1,result,1,channels);
2761  else
2762  CopyPixels(pixels,4,result,1,channels);
2763  if (PixelsEqual(pixels,5,pixels,1,channels))
2764  CopyPixels(pixels,5,result,2,channels);
2765  else
2766  CopyPixels(pixels,4,result,2,channels);
2767  if (
2768  (
2769  PixelsEqual(pixels,3,pixels,1,channels) &&
2770  !PixelsEqual(pixels,4,pixels,6,channels)
2771  ) ||
2772  (
2773  PixelsEqual(pixels,3,pixels,7,channels) &&
2774  !PixelsEqual(pixels,4,pixels,0,channels)
2775  )
2776  )
2777  CopyPixels(pixels,3,result,3,channels);
2778  else
2779  CopyPixels(pixels,4,result,3,channels);
2780  CopyPixels(pixels,4,result,4,channels);
2781  if (
2782  (
2783  PixelsEqual(pixels,5,pixels,1,channels) &&
2784  !PixelsEqual(pixels,4,pixels,8,channels)
2785  ) ||
2786  (
2787  PixelsEqual(pixels,5,pixels,7,channels) &&
2788  !PixelsEqual(pixels,4,pixels,2,channels)
2789  )
2790  )
2791  CopyPixels(pixels,5,result,5,channels);
2792  else
2793  CopyPixels(pixels,4,result,5,channels);
2794  if (PixelsEqual(pixels,3,pixels,7,channels))
2795  CopyPixels(pixels,3,result,6,channels);
2796  else
2797  CopyPixels(pixels,4,result,6,channels);
2798  if (
2799  (
2800  PixelsEqual(pixels,3,pixels,7,channels) &&
2801  !PixelsEqual(pixels,4,pixels,8,channels)
2802  ) ||
2803  (
2804  PixelsEqual(pixels,5,pixels,7,channels) &&
2805  !PixelsEqual(pixels,4,pixels,6,channels)
2806  )
2807  )
2808  CopyPixels(pixels,7,result,7,channels);
2809  else
2810  CopyPixels(pixels,4,result,7,channels);
2811  if (PixelsEqual(pixels,5,pixels,7,channels))
2812  CopyPixels(pixels,5,result,8,channels);
2813  else
2814  CopyPixels(pixels,4,result,8,channels);
2815  }
2816  else
2817  {
2818  register ssize_t
2819  i;
2820 
2821  for (i=0; i < 9; i++)
2822  CopyPixels(pixels,4,result,i,channels);
2823  }
2824 }
2825 
2827 {
2828 #define MagnifyImageTag "Magnify/Image"
2829 
2830  CacheView
2831  *image_view,
2832  *magnify_view;
2833 
2834  const char
2835  *option;
2836 
2837  Image
2838  *source_image,
2839  *magnify_image;
2840 
2842  status;
2843 
2845  progress;
2846 
2847  OffsetInfo
2848  offset;
2849 
2851  rectangle;
2852 
2853  ssize_t
2854  y;
2855 
2856  unsigned char
2857  magnification,
2858  width;
2859 
2860  void
2861  (*scaling_method)(const Image *,const Quantum *,Quantum *,size_t);
2862 
2863  /*
2864  Initialize magnified image attributes.
2865  */
2866  assert(image != (const Image *) NULL);
2867  assert(image->signature == MagickCoreSignature);
2868  if (image->debug != MagickFalse)
2869  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2870  assert(exception != (ExceptionInfo *) NULL);
2871  assert(exception->signature == MagickCoreSignature);
2872  option=GetImageOption(image->image_info,"magnify:method");
2873  if (option == (char *) NULL)
2874  option="scale2x";
2875  scaling_method=Scale2X;
2876  magnification=1;
2877  width=1;
2878  switch (*option)
2879  {
2880  case 'e':
2881  {
2882  if (LocaleCompare(option,"eagle2x") == 0)
2883  {
2884  scaling_method=Eagle2X;
2885  magnification=2;
2886  width=3;
2887  break;
2888  }
2889  if (LocaleCompare(option,"eagle3x") == 0)
2890  {
2891  scaling_method=Eagle3X;
2892  magnification=3;
2893  width=3;
2894  break;
2895  }
2896  if (LocaleCompare(option,"eagle3xb") == 0)
2897  {
2898  scaling_method=Eagle3XB;
2899  magnification=3;
2900  width=3;
2901  break;
2902  }
2903  if (LocaleCompare(option,"epbx2x") == 0)
2904  {
2905  scaling_method=Epbx2X;
2906  magnification=2;
2907  width=3;
2908  break;
2909  }
2910  break;
2911  }
2912  case 'f':
2913  {
2914  if (LocaleCompare(option,"fish2x") == 0)
2915  {
2916  scaling_method=Fish2X;
2917  magnification=2;
2918  width=3;
2919  break;
2920  }
2921  break;
2922  }
2923  case 'h':
2924  {
2925  if (LocaleCompare(option,"hq2x") == 0)
2926  {
2927  scaling_method=Hq2X;
2928  magnification=2;
2929  width=3;
2930  break;
2931  }
2932  break;
2933  }
2934  case 's':
2935  {
2936  if (LocaleCompare(option,"scale2x") == 0)
2937  {
2938  scaling_method=Scale2X;
2939  magnification=2;
2940  width=3;
2941  break;
2942  }
2943  if (LocaleCompare(option,"scale3x") == 0)
2944  {
2945  scaling_method=Scale3X;
2946  magnification=3;
2947  width=3;
2948  break;
2949  }
2950  break;
2951  }
2952  case 'x':
2953  {
2954  if (LocaleCompare(option,"xbr2x") == 0)
2955  {
2956  scaling_method=Xbr2X;
2957  magnification=2;
2958  width=5;
2959  }
2960  break;
2961  }
2962  default:
2963  break;
2964  }
2965  /*
2966  Make a working copy of the source image and convert it to RGB colorspace.
2967  */
2968  source_image=CloneImage(image,image->columns,image->rows,MagickTrue,
2969  exception);
2970  offset.x=0;
2971  offset.y=0;
2972  rectangle.x=0;
2973  rectangle.y=0;
2974  rectangle.width=image->columns;
2975  rectangle.height=image->rows;
2976  (void) CopyImagePixels(source_image,image,&rectangle,&offset,exception);
2977  (void) SetImageColorspace(source_image,RGBColorspace,exception);
2978  magnify_image=CloneImage(source_image,magnification*source_image->columns,
2979  magnification*source_image->rows,MagickTrue,exception);
2980  if (magnify_image == (Image *) NULL)
2981  return((Image *) NULL);
2982  /*
2983  Magnify the image.
2984  */
2985  status=MagickTrue;
2986  progress=0;
2987  image_view=AcquireVirtualCacheView(source_image,exception);
2988  magnify_view=AcquireAuthenticCacheView(magnify_image,exception);
2989 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2990  #pragma omp parallel for schedule(static) shared(progress,status) \
2991  magick_number_threads(source_image,magnify_image,source_image->rows,1)
2992 #endif
2993  for (y=0; y < (ssize_t) source_image->rows; y++)
2994  {
2995  Quantum
2996  r[128]; /* to hold result pixels */
2997 
2998  register Quantum
2999  *magick_restrict q;
3000 
3001  register ssize_t
3002  x;
3003 
3004  if (status == MagickFalse)
3005  continue;
3006  q=QueueCacheViewAuthenticPixels(magnify_view,0,magnification*y,
3007  magnify_image->columns,magnification,exception);
3008  if (q == (Quantum *) NULL)
3009  {
3010  status=MagickFalse;
3011  continue;
3012  }
3013  /*
3014  Magnify this row of pixels.
3015  */
3016  for (x=0; x < (ssize_t) source_image->columns; x++)
3017  {
3018  register const Quantum
3019  *magick_restrict p;
3020 
3021  size_t
3022  channels;
3023 
3024  register ssize_t
3025  i;
3026 
3027  ssize_t
3028  j;
3029 
3030  p=GetCacheViewVirtualPixels(image_view,x-width/2,y-width/2,width,width,
3031  exception);
3032  channels=GetPixelChannels(source_image);
3033  scaling_method(source_image,p,r,channels);
3034  /*
3035  Copy the result pixels into the final image.
3036  */
3037  for (j=0; j < (ssize_t) magnification; j++)
3038  for (i=0; i < (ssize_t) (channels*magnification); i++)
3039  q[j*channels*magnify_image->columns+i]=r[j*magnification*channels+i];
3040  q+=magnification*GetPixelChannels(magnify_image);
3041  }
3042  if (SyncCacheViewAuthenticPixels(magnify_view,exception) == MagickFalse)
3043  status=MagickFalse;
3044  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3045  {
3047  proceed;
3048 
3049 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3050  #pragma omp atomic
3051 #endif
3052  progress++;
3053  proceed=SetImageProgress(image,MagnifyImageTag,progress,image->rows);
3054  if (proceed == MagickFalse)
3055  status=MagickFalse;
3056  }
3057  }
3058  magnify_view=DestroyCacheView(magnify_view);
3059  image_view=DestroyCacheView(image_view);
3060  source_image=DestroyImage(source_image);
3061  if (status == MagickFalse)
3062  magnify_image=DestroyImage(magnify_image);
3063  return(magnify_image);
3064 }
3065 
3066 /*
3067 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3068 % %
3069 % %
3070 % %
3071 % M i n i f y I m a g e %
3072 % %
3073 % %
3074 % %
3075 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3076 %
3077 % MinifyImage() is a convenience method that scales an image proportionally to
3078 % half its size.
3079 %
3080 % The format of the MinifyImage method is:
3081 %
3082 % Image *MinifyImage(const Image *image,ExceptionInfo *exception)
3083 %
3084 % A description of each parameter follows:
3085 %
3086 % o image: the image.
3087 %
3088 % o exception: return any errors or warnings in this structure.
3089 %
3090 */
3092 {
3093  Image
3094  *minify_image;
3095 
3096  assert(image != (Image *) NULL);
3097  assert(image->signature == MagickCoreSignature);
3098  if (image->debug != MagickFalse)
3099  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3100  assert(exception != (ExceptionInfo *) NULL);
3101  assert(exception->signature == MagickCoreSignature);
3102  minify_image=ResizeImage(image,image->columns/2,image->rows/2,SplineFilter,
3103  exception);
3104  return(minify_image);
3105 }
3106 
3107 /*
3108 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3109 % %
3110 % %
3111 % %
3112 % R e s a m p l e I m a g e %
3113 % %
3114 % %
3115 % %
3116 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3117 %
3118 % ResampleImage() resize image in terms of its pixel size, so that when
3119 % displayed at the given resolution it will be the same size in terms of
3120 % real world units as the original image at the original resolution.
3121 %
3122 % The format of the ResampleImage method is:
3123 %
3124 % Image *ResampleImage(Image *image,const double x_resolution,
3125 % const double y_resolution,const FilterType filter,
3126 % ExceptionInfo *exception)
3127 %
3128 % A description of each parameter follows:
3129 %
3130 % o image: the image to be resized to fit the given resolution.
3131 %
3132 % o x_resolution: the new image x resolution.
3133 %
3134 % o y_resolution: the new image y resolution.
3135 %
3136 % o filter: Image filter to use.
3137 %
3138 % o exception: return any errors or warnings in this structure.
3139 %
3140 */
3141 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
3142  const double y_resolution,const FilterType filter,ExceptionInfo *exception)
3143 {
3144 #define ResampleImageTag "Resample/Image"
3145 
3146  Image
3147  *resample_image;
3148 
3149  size_t
3150  height,
3151  width;
3152 
3153  /*
3154  Initialize sampled image attributes.
3155  */
3156  assert(image != (const Image *) NULL);
3157  assert(image->signature == MagickCoreSignature);
3158  if (image->debug != MagickFalse)
3159  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3160  assert(exception != (ExceptionInfo *) NULL);
3161  assert(exception->signature == MagickCoreSignature);
3162  width=(size_t) (x_resolution*image->columns/(image->resolution.x == 0.0 ?
3163  72.0 : image->resolution.x)+0.5);
3164  height=(size_t) (y_resolution*image->rows/(image->resolution.y == 0.0 ?
3165  72.0 : image->resolution.y)+0.5);
3166  resample_image=ResizeImage(image,width,height,filter,exception);
3167  if (resample_image != (Image *) NULL)
3168  {
3169  resample_image->resolution.x=x_resolution;
3170  resample_image->resolution.y=y_resolution;
3171  }
3172  return(resample_image);
3173 }
3174 
3175 /*
3176 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3177 % %
3178 % %
3179 % %
3180 % R e s i z e I m a g e %
3181 % %
3182 % %
3183 % %
3184 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3185 %
3186 % ResizeImage() scales an image to the desired dimensions, using the given
3187 % filter (see AcquireFilterInfo()).
3188 %
3189 % If an undefined filter is given the filter defaults to Mitchell for a
3190 % colormapped image, a image with a matte channel, or if the image is
3191 % enlarged. Otherwise the filter defaults to a Lanczos.
3192 %
3193 % ResizeImage() was inspired by Paul Heckbert's "zoom" program.
3194 %
3195 % The format of the ResizeImage method is:
3196 %
3197 % Image *ResizeImage(Image *image,const size_t columns,const size_t rows,
3198 % const FilterType filter,ExceptionInfo *exception)
3199 %
3200 % A description of each parameter follows:
3201 %
3202 % o image: the image.
3203 %
3204 % o columns: the number of columns in the scaled image.
3205 %
3206 % o rows: the number of rows in the scaled image.
3207 %
3208 % o filter: Image filter to use.
3209 %
3210 % o exception: return any errors or warnings in this structure.
3211 %
3212 */
3213 
3214 typedef struct _ContributionInfo
3215 {
3216  double
3218 
3219  ssize_t
3222 
3224  ContributionInfo **contribution)
3225 {
3226  register ssize_t
3227  i;
3228 
3229  assert(contribution != (ContributionInfo **) NULL);
3230  for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
3231  if (contribution[i] != (ContributionInfo *) NULL)
3232  contribution[i]=(ContributionInfo *) RelinquishAlignedMemory(
3233  contribution[i]);
3234  contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
3235  return(contribution);
3236 }
3237 
3239 {
3240  register ssize_t
3241  i;
3242 
3244  **contribution;
3245 
3246  size_t
3247  number_threads;
3248 
3249  number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
3250  contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
3251  sizeof(*contribution));
3252  if (contribution == (ContributionInfo **) NULL)
3253  return((ContributionInfo **) NULL);
3254  (void) memset(contribution,0,number_threads*sizeof(*contribution));
3255  for (i=0; i < (ssize_t) number_threads; i++)
3256  {
3257  contribution[i]=(ContributionInfo *) MagickAssumeAligned(
3258  AcquireAlignedMemory(count,sizeof(**contribution)));
3259  if (contribution[i] == (ContributionInfo *) NULL)
3260  return(DestroyContributionThreadSet(contribution));
3261  }
3262  return(contribution);
3263 }
3264 
3266  const ResizeFilter *magick_restrict resize_filter,
3267  const Image *magick_restrict image,Image *magick_restrict resize_image,
3268  const double x_factor,const MagickSizeType span,
3269  MagickOffsetType *magick_restrict progress,ExceptionInfo *exception)
3270 {
3271 #define ResizeImageTag "Resize/Image"
3272 
3273  CacheView
3274  *image_view,
3275  *resize_view;
3276 
3277  ClassType
3278  storage_class;
3279 
3281  **magick_restrict contributions;
3282 
3284  status;
3285 
3286  double
3287  scale,
3288  support;
3289 
3290  ssize_t
3291  x;
3292 
3293  /*
3294  Apply filter to resize horizontally from image to resize image.
3295  */
3296  scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
3297  support=scale*GetResizeFilterSupport(resize_filter);
3298  storage_class=support > 0.5 ? DirectClass : image->storage_class;
3299  if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
3300  return(MagickFalse);
3301  if (support < 0.5)
3302  {
3303  /*
3304  Support too small even for nearest neighbour: Reduce to point sampling.
3305  */
3306  support=(double) 0.5;
3307  scale=1.0;
3308  }
3309  contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
3310  if (contributions == (ContributionInfo **) NULL)
3311  {
3312  (void) ThrowMagickException(exception,GetMagickModule(),
3313  ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
3314  return(MagickFalse);
3315  }
3316  status=MagickTrue;
3317  scale=PerceptibleReciprocal(scale);
3318  image_view=AcquireVirtualCacheView(image,exception);
3319  resize_view=AcquireAuthenticCacheView(resize_image,exception);
3320 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3321  #pragma omp parallel for schedule(static) shared(progress,status) \
3322  magick_number_threads(image,resize_image,resize_image->columns,1)
3323 #endif
3324  for (x=0; x < (ssize_t) resize_image->columns; x++)
3325  {
3326  const int
3327  id = GetOpenMPThreadId();
3328 
3329  double
3330  bisect,
3331  density;
3332 
3333  register const Quantum
3334  *magick_restrict p;
3335 
3336  register ContributionInfo
3337  *magick_restrict contribution;
3338 
3339  register Quantum
3340  *magick_restrict q;
3341 
3342  register ssize_t
3343  y;
3344 
3345  ssize_t
3346  n,
3347  start,
3348  stop;
3349 
3350  if (status == MagickFalse)
3351  continue;
3352  bisect=(double) (x+0.5)/x_factor+MagickEpsilon;
3353  start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
3354  stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->columns);
3355  density=0.0;
3356  contribution=contributions[id];
3357  for (n=0; n < (stop-start); n++)
3358  {
3359  contribution[n].pixel=start+n;
3360  contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
3361  ((double) (start+n)-bisect+0.5));
3362  density+=contribution[n].weight;
3363  }
3364  if (n == 0)
3365  continue;
3366  if ((density != 0.0) && (density != 1.0))
3367  {
3368  register ssize_t
3369  i;
3370 
3371  /*
3372  Normalize.
3373  */
3374  density=PerceptibleReciprocal(density);
3375  for (i=0; i < n; i++)
3376  contribution[i].weight*=density;
3377  }
3378  p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
3379  (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
3380  q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
3381  exception);
3382  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3383  {
3384  status=MagickFalse;
3385  continue;
3386  }
3387  for (y=0; y < (ssize_t) resize_image->rows; y++)
3388  {
3389  register ssize_t
3390  i;
3391 
3392  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3393  {
3394  double
3395  alpha,
3396  gamma,
3397  pixel;
3398 
3399  PixelChannel
3400  channel;
3401 
3402  PixelTrait
3403  resize_traits,
3404  traits;
3405 
3406  register ssize_t
3407  j;
3408 
3409  ssize_t
3410  k;
3411 
3412  channel=GetPixelChannelChannel(image,i);
3413  traits=GetPixelChannelTraits(image,channel);
3414  resize_traits=GetPixelChannelTraits(resize_image,channel);
3415  if ((traits == UndefinedPixelTrait) ||
3416  (resize_traits == UndefinedPixelTrait))
3417  continue;
3418  if (((resize_traits & CopyPixelTrait) != 0) ||
3419  (GetPixelWriteMask(resize_image,q) <= (QuantumRange/2)))
3420  {
3421  j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
3422  stop-1.0)+0.5);
3423  k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
3424  (contribution[j-start].pixel-contribution[0].pixel);
3425  SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
3426  q);
3427  continue;
3428  }
3429  pixel=0.0;
3430  if ((resize_traits & BlendPixelTrait) == 0)
3431  {
3432  /*
3433  No alpha blending.
3434  */
3435  for (j=0; j < n; j++)
3436  {
3437  k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
3438  (contribution[j].pixel-contribution[0].pixel);
3439  alpha=contribution[j].weight;
3440  pixel+=alpha*p[k*GetPixelChannels(image)+i];
3441  }
3442  SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
3443  continue;
3444  }
3445  /*
3446  Alpha blending.
3447  */
3448  gamma=0.0;
3449  for (j=0; j < n; j++)
3450  {
3451  k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
3452  (contribution[j].pixel-contribution[0].pixel);
3453  alpha=contribution[j].weight*QuantumScale*
3454  GetPixelAlpha(image,p+k*GetPixelChannels(image));
3455  pixel+=alpha*p[k*GetPixelChannels(image)+i];
3456  gamma+=alpha;
3457  }
3458  gamma=PerceptibleReciprocal(gamma);
3459  SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
3460  }
3461  q+=GetPixelChannels(resize_image);
3462  }
3463  if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
3464  status=MagickFalse;
3465  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3466  {
3468  proceed;
3469 
3470 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3471  #pragma omp atomic
3472 #endif
3473  (*progress)++;
3474  proceed=SetImageProgress(image,ResizeImageTag,*progress,span);
3475  if (proceed == MagickFalse)
3476  status=MagickFalse;
3477  }
3478  }
3479  resize_view=DestroyCacheView(resize_view);
3480  image_view=DestroyCacheView(image_view);
3481  contributions=DestroyContributionThreadSet(contributions);
3482  return(status);
3483 }
3484 
3486  const ResizeFilter *magick_restrict resize_filter,
3487  const Image *magick_restrict image,Image *magick_restrict resize_image,
3488  const double y_factor,const MagickSizeType span,
3489  MagickOffsetType *magick_restrict progress,ExceptionInfo *exception)
3490 {
3491  CacheView
3492  *image_view,
3493  *resize_view;
3494 
3495  ClassType
3496  storage_class;
3497 
3499  **magick_restrict contributions;
3500 
3501  double
3502  scale,
3503  support;
3504 
3506  status;
3507 
3508  ssize_t
3509  y;
3510 
3511  /*
3512  Apply filter to resize vertically from image to resize image.
3513  */
3514  scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
3515  support=scale*GetResizeFilterSupport(resize_filter);
3516  storage_class=support > 0.5 ? DirectClass : image->storage_class;
3517  if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
3518  return(MagickFalse);
3519  if (support < 0.5)
3520  {
3521  /*
3522  Support too small even for nearest neighbour: Reduce to point sampling.
3523  */
3524  support=(double) 0.5;
3525  scale=1.0;
3526  }
3527  contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
3528  if (contributions == (ContributionInfo **) NULL)
3529  {
3530  (void) ThrowMagickException(exception,GetMagickModule(),
3531  ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
3532  return(MagickFalse);
3533  }
3534  status=MagickTrue;
3535  scale=PerceptibleReciprocal(scale);
3536  image_view=AcquireVirtualCacheView(image,exception);
3537  resize_view=AcquireAuthenticCacheView(resize_image,exception);
3538 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3539  #pragma omp parallel for schedule(static) shared(progress,status) \
3540  magick_number_threads(image,resize_image,resize_image->rows,1)
3541 #endif
3542  for (y=0; y < (ssize_t) resize_image->rows; y++)
3543  {
3544  const int
3545  id = GetOpenMPThreadId();
3546 
3547  double
3548  bisect,
3549  density;
3550 
3551  register const Quantum
3552  *magick_restrict p;
3553 
3554  register ContributionInfo
3555  *magick_restrict contribution;
3556 
3557  register Quantum
3558  *magick_restrict q;
3559 
3560  register ssize_t
3561  x;
3562 
3563  ssize_t
3564  n,
3565  start,
3566  stop;
3567 
3568  if (status == MagickFalse)
3569  continue;
3570  bisect=(double) (y+0.5)/y_factor+MagickEpsilon;
3571  start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
3572  stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->rows);
3573  density=0.0;
3574  contribution=contributions[id];
3575  for (n=0; n < (stop-start); n++)
3576  {
3577  contribution[n].pixel=start+n;
3578  contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
3579  ((double) (start+n)-bisect+0.5));
3580  density+=contribution[n].weight;
3581  }
3582  if (n == 0)
3583  continue;
3584  if ((density != 0.0) && (density != 1.0))
3585  {
3586  register ssize_t
3587  i;
3588 
3589  /*
3590  Normalize.
3591  */
3592  density=PerceptibleReciprocal(density);
3593  for (i=0; i < n; i++)
3594  contribution[i].weight*=density;
3595  }
3596  p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
3597  image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
3598  exception);
3599  q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
3600  exception);
3601  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3602  {
3603  status=MagickFalse;
3604  continue;
3605  }
3606  for (x=0; x < (ssize_t) resize_image->columns; x++)
3607  {
3608  register ssize_t
3609  i;
3610 
3611  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3612  {
3613  double
3614  alpha,
3615  gamma,
3616  pixel;
3617 
3618  PixelChannel
3619  channel;
3620 
3621  PixelTrait
3622  resize_traits,
3623  traits;
3624 
3625  register ssize_t
3626  j;
3627 
3628  ssize_t
3629  k;
3630 
3631  channel=GetPixelChannelChannel(image,i);
3632  traits=GetPixelChannelTraits(image,channel);
3633  resize_traits=GetPixelChannelTraits(resize_image,channel);
3634  if ((traits == UndefinedPixelTrait) ||
3635  (resize_traits == UndefinedPixelTrait))
3636  continue;
3637  if (((resize_traits & CopyPixelTrait) != 0) ||
3638  (GetPixelWriteMask(resize_image,q) <= (QuantumRange/2)))
3639  {
3640  j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
3641  stop-1.0)+0.5);
3642  k=(ssize_t) ((contribution[j-start].pixel-contribution[0].pixel)*
3643  image->columns+x);
3644  SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
3645  q);
3646  continue;
3647  }
3648  pixel=0.0;
3649  if ((resize_traits & BlendPixelTrait) == 0)
3650  {
3651  /*
3652  No alpha blending.
3653  */
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;
3659  pixel+=alpha*p[k*GetPixelChannels(image)+i];
3660  }
3661  SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
3662  continue;
3663  }
3664  gamma=0.0;
3665  for (j=0; j < n; j++)
3666  {
3667  k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
3668  image->columns+x);
3669  alpha=contribution[j].weight*QuantumScale*GetPixelAlpha(image,p+k*
3670  GetPixelChannels(image));
3671  pixel+=alpha*p[k*GetPixelChannels(image)+i];
3672  gamma+=alpha;
3673  }
3674  gamma=PerceptibleReciprocal(gamma);
3675  SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
3676  }
3677  q+=GetPixelChannels(resize_image);
3678  }
3679  if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
3680  status=MagickFalse;
3681  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3682  {
3684  proceed;
3685 
3686 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3687  #pragma omp atomic
3688 #endif
3689  (*progress)++;
3690  proceed=SetImageProgress(image,ResizeImageTag,*progress,span);
3691  if (proceed == MagickFalse)
3692  status=MagickFalse;
3693  }
3694  }
3695  resize_view=DestroyCacheView(resize_view);
3696  image_view=DestroyCacheView(image_view);
3697  contributions=DestroyContributionThreadSet(contributions);
3698  return(status);
3699 }
3700 
3701 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
3702  const size_t rows,const FilterType filter,ExceptionInfo *exception)
3703 {
3704  double
3705  x_factor,
3706  y_factor;
3707 
3708  FilterType
3709  filter_type;
3710 
3711  Image
3712  *filter_image,
3713  *resize_image;
3714 
3716  offset;
3717 
3719  span;
3720 
3722  status;
3723 
3724  ResizeFilter
3725  *resize_filter;
3726 
3727  /*
3728  Acquire resize image.
3729  */
3730  assert(image != (Image *) NULL);
3731  assert(image->signature == MagickCoreSignature);
3732  if (image->debug != MagickFalse)
3733  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3734  assert(exception != (ExceptionInfo *) NULL);
3735  assert(exception->signature == MagickCoreSignature);
3736  if ((columns == 0) || (rows == 0))
3737  ThrowImageException(ImageError,"NegativeOrZeroImageSize");
3738  if ((columns == image->columns) && (rows == image->rows) &&
3739  (filter == UndefinedFilter))
3740  return(CloneImage(image,0,0,MagickTrue,exception));
3741  /*
3742  Acquire resize filter.
3743  */
3744  x_factor=(double) columns/(double) image->columns;
3745  y_factor=(double) rows/(double) image->rows;
3746  filter_type=LanczosFilter;
3747  if (filter != UndefinedFilter)
3748  filter_type=filter;
3749  else
3750  if ((x_factor == 1.0) && (y_factor == 1.0))
3751  filter_type=PointFilter;
3752  else
3753  if ((image->storage_class == PseudoClass) ||
3754  (image->alpha_trait != UndefinedPixelTrait) ||
3755  ((x_factor*y_factor) > 1.0))
3756  filter_type=MitchellFilter;
3757  resize_filter=AcquireResizeFilter(image,filter_type,MagickFalse,exception);
3758 #if defined(MAGICKCORE_OPENCL_SUPPORT)
3759  resize_image=AccelerateResizeImage(image,columns,rows,resize_filter,
3760  exception);
3761  if (resize_image != (Image *) NULL)
3762  {
3763  resize_filter=DestroyResizeFilter(resize_filter);
3764  return(resize_image);
3765  }
3766 #endif
3767  resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
3768  if (resize_image == (Image *) NULL)
3769  {
3770  resize_filter=DestroyResizeFilter(resize_filter);
3771  return(resize_image);
3772  }
3773  if (x_factor > y_factor)
3774  filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
3775  else
3776  filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
3777  if (filter_image == (Image *) NULL)
3778  {
3779  resize_filter=DestroyResizeFilter(resize_filter);
3780  return(DestroyImage(resize_image));
3781  }
3782  /*
3783  Resize image.
3784  */
3785  offset=0;
3786  if (x_factor > y_factor)
3787  {
3788  span=(MagickSizeType) (filter_image->columns+rows);
3789  status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
3790  &offset,exception);
3791  status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
3792  span,&offset,exception);
3793  }
3794  else
3795  {
3796  span=(MagickSizeType) (filter_image->rows+columns);
3797  status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
3798  &offset,exception);
3799  status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
3800  span,&offset,exception);
3801  }
3802  /*
3803  Free resources.
3804  */
3805  filter_image=DestroyImage(filter_image);
3806  resize_filter=DestroyResizeFilter(resize_filter);
3807  if (status == MagickFalse)
3808  {
3809  resize_image=DestroyImage(resize_image);
3810  return((Image *) NULL);
3811  }
3812  resize_image->type=image->type;
3813  return(resize_image);
3814 }
3815 
3816 /*
3817 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3818 % %
3819 % %
3820 % %
3821 % S a m p l e I m a g e %
3822 % %
3823 % %
3824 % %
3825 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3826 %
3827 % SampleImage() scales an image to the desired dimensions with pixel
3828 % sampling. Unlike other scaling methods, this method does not introduce
3829 % any additional color into the scaled image.
3830 %
3831 % The format of the SampleImage method is:
3832 %
3833 % Image *SampleImage(const Image *image,const size_t columns,
3834 % const size_t rows,ExceptionInfo *exception)
3835 %
3836 % A description of each parameter follows:
3837 %
3838 % o image: the image.
3839 %
3840 % o columns: the number of columns in the sampled image.
3841 %
3842 % o rows: the number of rows in the sampled image.
3843 %
3844 % o exception: return any errors or warnings in this structure.
3845 %
3846 */
3847 MagickExport Image *SampleImage(const Image *image,const size_t columns,
3848  const size_t rows,ExceptionInfo *exception)
3849 {
3850 #define SampleImageTag "Sample/Image"
3851 
3852  CacheView
3853  *image_view,
3854  *sample_view;
3855 
3856  Image
3857  *sample_image;
3858 
3860  status;
3861 
3863  progress;
3864 
3865  register ssize_t
3866  x1;
3867 
3868  ssize_t
3869  *x_offset,
3870  y;
3871 
3872  PointInfo
3873  sample_offset;
3874 
3875  /*
3876  Initialize sampled image attributes.
3877  */
3878  assert(image != (const Image *) NULL);
3879  assert(image->signature == MagickCoreSignature);
3880  if (image->debug != MagickFalse)
3881  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3882  assert(exception != (ExceptionInfo *) NULL);
3883  assert(exception->signature == MagickCoreSignature);
3884  if ((columns == 0) || (rows == 0))
3885  ThrowImageException(ImageError,"NegativeOrZeroImageSize");
3886  if ((columns == image->columns) && (rows == image->rows))
3887  return(CloneImage(image,0,0,MagickTrue,exception));
3888  sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
3889  if (sample_image == (Image *) NULL)
3890  return((Image *) NULL);
3891  /*
3892  Set the sampling offset, default is in the mid-point of sample regions.
3893  */
3894  sample_offset.x=sample_offset.y=0.5-MagickEpsilon;
3895  {
3896  const char
3897  *value;
3898 
3899  value=GetImageArtifact(image,"sample:offset");
3900  if (value != (char *) NULL)
3901  {
3902  GeometryInfo
3903  geometry_info;
3904 
3906  flags;
3907 
3908  (void) ParseGeometry(value,&geometry_info);
3909  flags=ParseGeometry(value,&geometry_info);
3910  sample_offset.x=sample_offset.y=geometry_info.rho/100.0-MagickEpsilon;
3911  if ((flags & SigmaValue) != 0)
3912  sample_offset.y=geometry_info.sigma/100.0-MagickEpsilon;
3913  }
3914  }
3915  /*
3916  Allocate scan line buffer and column offset buffers.
3917  */
3918  x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
3919  sizeof(*x_offset));
3920  if (x_offset == (ssize_t *) NULL)
3921  {
3922  sample_image=DestroyImage(sample_image);
3923  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3924  }
3925  for (x1=0; x1 < (ssize_t) sample_image->columns; x1++)
3926  x_offset[x1]=(ssize_t) ((((double) x1+sample_offset.x)*image->columns)/
3927  sample_image->columns);
3928  /*
3929  Sample each row.
3930  */
3931  status=MagickTrue;
3932  progress=0;
3933  image_view=AcquireVirtualCacheView(image,exception);
3934  sample_view=AcquireAuthenticCacheView(sample_image,exception);
3935 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3936  #pragma omp parallel for schedule(static) shared(status) \
3937  magick_number_threads(image,sample_image,sample_image->rows,1)
3938 #endif
3939  for (y=0; y < (ssize_t) sample_image->rows; y++)
3940  {
3941  register const Quantum
3942  *magick_restrict p;
3943 
3944  register Quantum
3945  *magick_restrict q;
3946 
3947  register ssize_t
3948  x;
3949 
3950  ssize_t
3951  y_offset;
3952 
3953  if (status == MagickFalse)
3954  continue;
3955  y_offset=(ssize_t) ((((double) y+sample_offset.y)*image->rows)/
3956  sample_image->rows);
3957  p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
3958  exception);
3959  q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
3960  exception);
3961  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3962  {
3963  status=MagickFalse;
3964  continue;
3965  }
3966  /*
3967  Sample each column.
3968  */
3969  for (x=0; x < (ssize_t) sample_image->columns; x++)
3970  {
3971  register ssize_t
3972  i;
3973 
3974  if (GetPixelWriteMask(sample_image,q) <= (QuantumRange/2))
3975  {
3976  q+=GetPixelChannels(sample_image);
3977  continue;
3978  }
3979  for (i=0; i < (ssize_t) GetPixelChannels(sample_image); i++)
3980  {
3981  PixelChannel
3982  channel;
3983 
3984  PixelTrait
3985  image_traits,
3986  traits;
3987 
3988  channel=GetPixelChannelChannel(sample_image,i);
3989  traits=GetPixelChannelTraits(sample_image,channel);
3990  image_traits=GetPixelChannelTraits(image,channel);
3991  if ((traits == UndefinedPixelTrait) ||
3992  (image_traits == UndefinedPixelTrait))
3993  continue;
3994  SetPixelChannel(sample_image,channel,p[x_offset[x]*GetPixelChannels(
3995  image)+i],q);
3996  }
3997  q+=GetPixelChannels(sample_image);
3998  }
3999  if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
4000  status=MagickFalse;
4001  if (image->progress_monitor != (MagickProgressMonitor) NULL)
4002  {
4004  proceed;
4005 
4006  proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
4007  if (proceed == MagickFalse)
4008  status=MagickFalse;
4009  }
4010  }
4011  image_view=DestroyCacheView(image_view);
4012  sample_view=DestroyCacheView(sample_view);
4013  x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
4014  sample_image->type=image->type;
4015  if (status == MagickFalse)
4016  sample_image=DestroyImage(sample_image);
4017  return(sample_image);
4018 }
4019 
4020 /*
4021 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4022 % %
4023 % %
4024 % %
4025 % S c a l e I m a g e %
4026 % %
4027 % %
4028 % %
4029 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4030 %
4031 % ScaleImage() changes the size of an image to the given dimensions.
4032 %
4033 % The format of the ScaleImage method is:
4034 %
4035 % Image *ScaleImage(const Image *image,const size_t columns,
4036 % const size_t rows,ExceptionInfo *exception)
4037 %
4038 % A description of each parameter follows:
4039 %
4040 % o image: the image.
4041 %
4042 % o columns: the number of columns in the scaled image.
4043 %
4044 % o rows: the number of rows in the scaled image.
4045 %
4046 % o exception: return any errors or warnings in this structure.
4047 %
4048 */
4049 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
4050  const size_t rows,ExceptionInfo *exception)
4051 {
4052 #define ScaleImageTag "Scale/Image"
4053 
4054  CacheView
4055  *image_view,
4056  *scale_view;
4057 
4058  double
4059  alpha,
4060  pixel[CompositePixelChannel],
4061  *scale_scanline,
4062  *scanline,
4063  *x_vector,
4064  *y_vector;
4065 
4066  Image
4067  *scale_image;
4068 
4070  next_column,
4071  next_row,
4072  proceed,
4073  status;
4074 
4075  PixelTrait
4076  scale_traits;
4077 
4078  PointInfo
4079  scale,
4080  span;
4081 
4082  register ssize_t
4083  i;
4084 
4085  ssize_t
4086  n,
4087  number_rows,
4088  y;
4089 
4090  /*
4091  Initialize scaled image attributes.
4092  */
4093  assert(image != (const Image *) NULL);
4094  assert(image->signature == MagickCoreSignature);
4095  if (image->debug != MagickFalse)
4096  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4097  assert(exception != (ExceptionInfo *) NULL);
4098  assert(exception->signature == MagickCoreSignature);
4099  if ((columns == 0) || (rows == 0))
4100  ThrowImageException(ImageError,"NegativeOrZeroImageSize");
4101  if ((columns == image->columns) && (rows == image->rows))
4102  return(CloneImage(image,0,0,MagickTrue,exception));
4103  scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
4104  if (scale_image == (Image *) NULL)
4105  return((Image *) NULL);
4106  if (SetImageStorageClass(scale_image,DirectClass,exception) == MagickFalse)
4107  {
4108  scale_image=DestroyImage(scale_image);
4109  return((Image *) NULL);
4110  }
4111  /*
4112  Allocate memory.
4113  */
4114  x_vector=(double *) AcquireQuantumMemory((size_t) image->columns,
4115  MaxPixelChannels*sizeof(*x_vector));
4116  scanline=x_vector;
4117  if (image->rows != scale_image->rows)
4118  scanline=(double *) AcquireQuantumMemory((size_t) image->columns,
4119  MaxPixelChannels*sizeof(*scanline));
4120  scale_scanline=(double *) AcquireQuantumMemory((size_t) scale_image->columns,
4121  MaxPixelChannels*sizeof(*scale_scanline));
4122  y_vector=(double *) AcquireQuantumMemory((size_t) image->columns,
4123  MaxPixelChannels*sizeof(*y_vector));
4124  if ((scanline == (double *) NULL) || (scale_scanline == (double *) NULL) ||
4125  (x_vector == (double *) NULL) || (y_vector == (double *) NULL))
4126  {
4127  if ((image->rows != scale_image->rows) && (scanline != (double *) NULL))
4128  scanline=(double *) RelinquishMagickMemory(scanline);
4129  if (scale_scanline != (double *) NULL)
4130  scale_scanline=(double *) RelinquishMagickMemory(scale_scanline);
4131  if (x_vector != (double *) NULL)
4132  x_vector=(double *) RelinquishMagickMemory(x_vector);
4133  if (y_vector != (double *) NULL)
4134  y_vector=(double *) RelinquishMagickMemory(y_vector);
4135  scale_image=DestroyImage(scale_image);
4136  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4137  }
4138  /*
4139  Scale image.
4140  */
4141  number_rows=0;
4142  next_row=MagickTrue;
4143  span.y=1.0;
4144  scale.y=(double) scale_image->rows/(double) image->rows;
4145  (void) memset(y_vector,0,(size_t) MaxPixelChannels*image->columns*
4146  sizeof(*y_vector));
4147  n=0;
4148  status=MagickTrue;
4149  image_view=AcquireVirtualCacheView(image,exception);
4150  scale_view=AcquireAuthenticCacheView(scale_image,exception);
4151  for (y=0; y < (ssize_t) scale_image->rows; y++)
4152  {
4153  register const Quantum
4154  *magick_restrict p;
4155 
4156  register Quantum
4157  *magick_restrict q;
4158 
4159  register ssize_t
4160  x;
4161 
4162  if (status == MagickFalse)
4163  break;
4164  q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
4165  exception);
4166  if (q == (Quantum *) NULL)
4167  {
4168  status=MagickFalse;
4169  break;
4170  }
4171  alpha=1.0;
4172  if (scale_image->rows == image->rows)
4173  {
4174  /*
4175  Read a new scanline.
4176  */
4177  p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
4178  exception);
4179  if (p == (const Quantum *) NULL)
4180  {
4181  status=MagickFalse;
4182  break;
4183  }
4184  for (x=0; x < (ssize_t) image->columns; x++)
4185  {
4186  if (GetPixelWriteMask(image,p) <= (QuantumRange/2))
4187  {
4188  p+=GetPixelChannels(image);
4189  continue;
4190  }
4191  if (image->alpha_trait != UndefinedPixelTrait)
4192  alpha=QuantumScale*GetPixelAlpha(image,p);
4193  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4194  {
4195  PixelChannel channel = GetPixelChannelChannel(image,i);
4196  PixelTrait traits = GetPixelChannelTraits(image,channel);
4197  if ((traits & BlendPixelTrait) == 0)
4198  {
4199  x_vector[x*GetPixelChannels(image)+i]=(double) p[i];
4200  continue;
4201  }
4202  x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
4203  }
4204  p+=GetPixelChannels(image);
4205  }
4206  }
4207  else
4208  {
4209  /*
4210  Scale Y direction.
4211  */
4212  while (scale.y < span.y)
4213  {
4214  if ((next_row != MagickFalse) &&
4215  (number_rows < (ssize_t) image->rows))
4216  {
4217  /*
4218  Read a new scanline.
4219  */
4220  p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
4221  exception);
4222  if (p == (const Quantum *) NULL)
4223  {
4224  status=MagickFalse;
4225  break;
4226  }
4227  for (x=0; x < (ssize_t) image->columns; x++)
4228  {
4229  if (GetPixelWriteMask(image,p) <= (QuantumRange/2))
4230  {
4231  p+=GetPixelChannels(image);
4232  continue;
4233  }
4234  if (image->alpha_trait != UndefinedPixelTrait)
4235  alpha=QuantumScale*GetPixelAlpha(image,p);
4236  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4237  {
4238  PixelChannel channel = GetPixelChannelChannel(image,i);
4239  PixelTrait traits = GetPixelChannelTraits(image,channel);
4240  if ((traits & BlendPixelTrait) == 0)
4241  {
4242  x_vector[x*GetPixelChannels(image)+i]=(double) p[i];
4243  continue;
4244  }
4245  x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
4246  }
4247  p+=GetPixelChannels(image);
4248  }
4249  number_rows++;
4250  }
4251  for (x=0; x < (ssize_t) image->columns; x++)
4252  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4253  y_vector[x*GetPixelChannels(image)+i]+=scale.y*
4254  x_vector[x*GetPixelChannels(image)+i];
4255  span.y-=scale.y;
4256  scale.y=(double) scale_image->rows/(double) image->rows;
4257  next_row=MagickTrue;
4258  }
4259  if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
4260  {
4261  /*
4262  Read a new scanline.
4263  */
4264  p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
4265  exception);
4266  if (p == (const Quantum *) NULL)
4267  {
4268  status=MagickFalse;
4269  break;
4270  }
4271  for (x=0; x < (ssize_t) image->columns; x++)
4272  {
4273  if (GetPixelWriteMask(image,p) <= (QuantumRange/2))
4274  {
4275  p+=GetPixelChannels(image);
4276  continue;
4277  }
4278  if (image->alpha_trait != UndefinedPixelTrait)
4279  alpha=QuantumScale*GetPixelAlpha(image,p);
4280  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4281  {
4282  PixelChannel channel = GetPixelChannelChannel(image,i);
4283  PixelTrait traits = GetPixelChannelTraits(image,channel);
4284  if ((traits & BlendPixelTrait) == 0)
4285  {
4286  x_vector[x*GetPixelChannels(image)+i]=(double) p[i];
4287  continue;
4288  }
4289  x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
4290  }
4291  p+=GetPixelChannels(image);
4292  }
4293  number_rows++;
4294  next_row=MagickFalse;
4295  }
4296  for (x=0; x < (ssize_t) image->columns; x++)
4297  {
4298  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4299  {
4300  pixel[i]=y_vector[x*GetPixelChannels(image)+i]+span.y*
4301  x_vector[x*GetPixelChannels(image)+i];
4302  scanline[x*GetPixelChannels(image)+i]=pixel[i];
4303  y_vector[x*GetPixelChannels(image)+i]=0.0;
4304  }
4305  }
4306  scale.y-=span.y;
4307  if (scale.y <= 0)
4308  {
4309  scale.y=(double) scale_image->rows/(double) image->rows;
4310  next_row=MagickTrue;
4311  }
4312  span.y=1.0;
4313  }
4314  if (scale_image->columns == image->columns)
4315  {
4316  /*
4317  Transfer scanline to scaled image.
4318  */
4319  for (x=0; x < (ssize_t) scale_image->columns; x++)
4320  {
4321  if (GetPixelWriteMask(scale_image,q) <= (QuantumRange/2))
4322  {
4323  q+=GetPixelChannels(scale_image);
4324  continue;
4325  }
4326  if (image->alpha_trait != UndefinedPixelTrait)
4327  {
4328  alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
4330  alpha=PerceptibleReciprocal(alpha);
4331  }
4332  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4333  {
4334  PixelChannel channel = GetPixelChannelChannel(image,i);
4335  PixelTrait traits = GetPixelChannelTraits(image,channel);
4336  scale_traits=GetPixelChannelTraits(scale_image,channel);
4337  if ((traits == UndefinedPixelTrait) ||
4338  (scale_traits == UndefinedPixelTrait))
4339  continue;
4340  if ((traits & BlendPixelTrait) == 0)
4341  {
4342  SetPixelChannel(scale_image,channel,ClampToQuantum(
4343  scanline[x*GetPixelChannels(image)+i]),q);
4344  continue;
4345  }
4346  SetPixelChannel(scale_image,channel,ClampToQuantum(alpha*scanline[
4347  x*GetPixelChannels(image)+i]),q);
4348  }
4349  q+=GetPixelChannels(scale_image);
4350  }
4351  }
4352  else
4353  {
4354  ssize_t
4355  t;
4356 
4357  /*
4358  Scale X direction.
4359  */
4360  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4361  pixel[i]=0.0;
4362  next_column=MagickFalse;
4363  span.x=1.0;
4364  t=0;
4365  for (x=0; x < (ssize_t) image->columns; x++)
4366  {
4367  scale.x=(double) scale_image->columns/(double) image->columns;
4368  while (scale.x >= span.x)
4369  {
4370  if (next_column != MagickFalse)
4371  {
4372  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4373  pixel[i]=0.0;
4374  t++;
4375  }
4376  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4377  {
4378  PixelChannel channel = GetPixelChannelChannel(image,i);
4379  PixelTrait traits = GetPixelChannelTraits(image,channel);
4380  if (traits == UndefinedPixelTrait)
4381  continue;
4382  pixel[i]+=span.x*scanline[x*GetPixelChannels(image)+i];
4383  scale_scanline[t*GetPixelChannels(image)+i]=pixel[i];
4384  }
4385  scale.x-=span.x;
4386  span.x=1.0;
4387  next_column=MagickTrue;
4388  }
4389  if (scale.x > 0)
4390  {
4391  if (next_column != MagickFalse)
4392  {
4393  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4394  pixel[i]=0.0;
4395  next_column=MagickFalse;
4396  t++;
4397  }
4398  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4399  pixel[i]+=scale.x*scanline[x*GetPixelChannels(image)+i];
4400  span.x-=scale.x;
4401  }
4402  }
4403  if (span.x > 0)
4404  {
4405  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4406  pixel[i]+=span.x*scanline[(x-1)*GetPixelChannels(image)+i];
4407  }
4408  if ((next_column == MagickFalse) && (t < (ssize_t) scale_image->columns))
4409  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4410  scale_scanline[t*GetPixelChannels(image)+i]=pixel[i];
4411  /*
4412  Transfer scanline to scaled image.
4413  */
4414  for (x=0; x < (ssize_t) scale_image->columns; x++)
4415  {
4416  if (GetPixelWriteMask(scale_image,q) <= (QuantumRange/2))
4417  {
4418  q+=GetPixelChannels(scale_image);
4419  continue;
4420  }
4421  if (image->alpha_trait != UndefinedPixelTrait)
4422  {
4423  alpha=QuantumScale*scale_scanline[x*GetPixelChannels(image)+
4425  alpha=PerceptibleReciprocal(alpha);
4426  }
4427  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4428  {
4429  PixelChannel channel = GetPixelChannelChannel(image,i);
4430  PixelTrait traits = GetPixelChannelTraits(image,channel);
4431  scale_traits=GetPixelChannelTraits(scale_image,channel);
4432  if ((traits == UndefinedPixelTrait) ||
4433  (scale_traits == UndefinedPixelTrait))
4434  continue;
4435  if ((traits & BlendPixelTrait) == 0)
4436  {
4437  SetPixelChannel(scale_image,channel,ClampToQuantum(
4438  scale_scanline[x*GetPixelChannels(image)+i]),q);
4439  continue;
4440  }
4441  SetPixelChannel(scale_image,channel,ClampToQuantum(alpha*
4442  scale_scanline[x*GetPixelChannels(image)+i]),q);
4443  }
4444  q+=GetPixelChannels(scale_image);
4445  }
4446  }
4447  if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
4448  {
4449  status=MagickFalse;
4450  break;
4451  }
4453  image->rows);
4454  if (proceed == MagickFalse)
4455  {
4456  status=MagickFalse;
4457  break;
4458  }
4459  }
4460  scale_view=DestroyCacheView(scale_view);
4461  image_view=DestroyCacheView(image_view);
4462  /*
4463  Free allocated memory.
4464  */
4465  y_vector=(double *) RelinquishMagickMemory(y_vector);
4466  scale_scanline=(double *) RelinquishMagickMemory(scale_scanline);
4467  if (scale_image->rows != image->rows)
4468  scanline=(double *) RelinquishMagickMemory(scanline);
4469  x_vector=(double *) RelinquishMagickMemory(x_vector);
4470  scale_image->type=image->type;
4471  if (status == MagickFalse)
4472  scale_image=DestroyImage(scale_image);
4473  return(scale_image);
4474 }
4475 
4476 /*
4477 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4478 % %
4479 % %
4480 % %
4481 % T h u m b n a i l I m a g e %
4482 % %
4483 % %
4484 % %
4485 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4486 %
4487 % ThumbnailImage() changes the size of an image to the given dimensions and
4488 % removes any associated profiles. The goal is to produce small low cost
4489 % thumbnail images suited for display on the Web.
4490 %
4491 % The format of the ThumbnailImage method is:
4492 %
4493 % Image *ThumbnailImage(const Image *image,const size_t columns,
4494 % const size_t rows,ExceptionInfo *exception)
4495 %
4496 % A description of each parameter follows:
4497 %
4498 % o image: the image.
4499 %
4500 % o columns: the number of columns in the scaled image.
4501 %
4502 % o rows: the number of rows in the scaled image.
4503 %
4504 % o exception: return any errors or warnings in this structure.
4505 %
4506 */
4507 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
4508  const size_t rows,ExceptionInfo *exception)
4509 {
4510 #define SampleFactor 5
4511 
4512  char
4513  filename[MagickPathExtent],
4514  value[MagickPathExtent];
4515 
4516  const char
4517  *name;
4518 
4519  Image
4520  *thumbnail_image;
4521 
4522  double
4523  x_factor,
4524  y_factor;
4525 
4526  struct stat
4527  attributes;
4528 
4529  assert(image != (Image *) NULL);
4530  assert(image->signature == MagickCoreSignature);
4531  if (image->debug != MagickFalse)
4532  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4533  assert(exception != (ExceptionInfo *) NULL);
4534  assert(exception->signature == MagickCoreSignature);
4535  x_factor=(double) columns/(double) image->columns;
4536  y_factor=(double) rows/(double) image->rows;
4537  if ((x_factor*y_factor) > 0.1)
4538  thumbnail_image=ResizeImage(image,columns,rows,image->filter,exception);
4539  else
4540  if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
4541  thumbnail_image=ResizeImage(image,columns,rows,image->filter,exception);
4542  else
4543  {
4544  Image
4545  *sample_image;
4546 
4547  sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
4548  exception);
4549  if (sample_image == (Image *) NULL)
4550  return((Image *) NULL);
4551  thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
4552  exception);
4553  sample_image=DestroyImage(sample_image);
4554  }
4555  if (thumbnail_image == (Image *) NULL)
4556  return(thumbnail_image);
4557  (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
4558  if (thumbnail_image->alpha_trait == UndefinedPixelTrait)
4559  (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel,exception);
4560  thumbnail_image->depth=8;
4561  thumbnail_image->interlace=NoInterlace;
4562  /*
4563  Strip all profiles except color profiles.
4564  */
4565  ResetImageProfileIterator(thumbnail_image);
4566  for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
4567  {
4568  if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
4569  {
4570  (void) DeleteImageProfile(thumbnail_image,name);
4571  ResetImageProfileIterator(thumbnail_image);
4572  }
4573  name=GetNextImageProfile(thumbnail_image);
4574  }
4575  (void) DeleteImageProperty(thumbnail_image,"comment");
4576  (void) CopyMagickString(value,image->magick_filename,MagickPathExtent);
4577  if (strstr(image->magick_filename,"//") == (char *) NULL)
4578  (void) FormatLocaleString(value,MagickPathExtent,"file://%s",
4579  image->magick_filename);
4580  (void) SetImageProperty(thumbnail_image,"Thumb::URI",value,exception);
4581  GetPathComponent(image->magick_filename,TailPath,filename);
4582  (void) CopyMagickString(value,filename,MagickPathExtent);
4583  if ( GetPathAttributes(image->filename,&attributes) != MagickFalse )
4584  {
4585  (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
4586  attributes.st_mtime);
4587  (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value,exception);
4588  }
4589  (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
4590  attributes.st_mtime);
4592  value);
4593  (void) SetImageProperty(thumbnail_image,"Thumb::Size",value,exception);
4594  (void) FormatLocaleString(value,MagickPathExtent,"image/%s",image->magick);
4595  LocaleLower(value);
4596  (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value,exception);
4597  (void) SetImageProperty(thumbnail_image,"software",MagickAuthoritativeURL,
4598  exception);
4599  (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
4600  image->magick_columns);
4601  (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value,
4602  exception);
4603  (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
4604  image->magick_rows);
4605  (void) SetImageProperty(thumbnail_image,"Thumb::Image::Height",value,
4606  exception);
4607  (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
4608  GetImageListLength(image));
4609  (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value,
4610  exception);
4611  return(thumbnail_image);
4612 }
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:3701
#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:2671
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:3265
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:2517
MagickExport ssize_t ParseCommandOption(const CommandOption option, const MagickBooleanType list, const char *options)
Definition: option.c:2968
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:2618
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:3238
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:3847
static void Scale2X(const Image *source, const Quantum *pixels, Quantum *result, const size_t channels)
Definition: resize.c:2587
ssize_t pixel
Definition: resize.c:3220
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:2303
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:4507
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:3485
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:2739
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:2681
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:3223
static Quantum GetPixelWriteMask(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
MagickExport Image * MinifyImage(const Image *image, ExceptionInfo *exception)
Definition: resize.c:3091
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:2326
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:945
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:2615
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:3141
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:2826
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:2398
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:1364
#define MagickExport
static void Hq2X(const Image *source, const Quantum *pixels, Quantum *result, const size_t channels)
Definition: resize.c:2345
MagickExport Image * ScaleImage(const Image *image, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: resize.c:4049
static void Eagle3XB(const Image *source, const Quantum *pixels, Quantum *result, const size_t channels)
Definition: resize.c:2711
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