2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % RRRR EEEEE SSSSS IIIII ZZZZZ EEEEE %
8 % RRRR EEE SSS I ZZZ EEE %
10 % R R EEEEE SSSSS IIIII ZZZZZ EEEEE %
13 % MagickCore Image Resize Methods %
20 % Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/cache-view.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/draw.h"
50 #include "MagickCore/exception.h"
51 #include "MagickCore/exception-private.h"
52 #include "MagickCore/gem.h"
53 #include "MagickCore/image.h"
54 #include "MagickCore/image-private.h"
55 #include "MagickCore/list.h"
56 #include "MagickCore/memory_.h"
57 #include "MagickCore/magick.h"
58 #include "MagickCore/pixel-accessor.h"
59 #include "MagickCore/property.h"
60 #include "MagickCore/monitor.h"
61 #include "MagickCore/monitor-private.h"
62 #include "MagickCore/option.h"
63 #include "MagickCore/pixel.h"
64 #include "MagickCore/quantum-private.h"
65 #include "MagickCore/resample.h"
66 #include "MagickCore/resample-private.h"
67 #include "MagickCore/resize.h"
68 #include "MagickCore/resize-private.h"
69 #include "MagickCore/string_.h"
70 #include "MagickCore/string-private.h"
71 #include "MagickCore/thread-private.h"
72 #include "MagickCore/utility.h"
73 #include "MagickCore/utility-private.h"
74 #include "MagickCore/version.h"
75 #if defined(MAGICKCORE_LQR_DELEGATE)
85 (*filter)(const MagickRealType,const ResizeFilter *),
86 (*window)(const MagickRealType,const ResizeFilter *),
87 support, /* filter region of support - the filter support limit */
88 window_support, /* window support, usally equal to support (expert only) */
89 scale, /* dimension scaling to fit window support (usally 1.0) */
90 blur, /* x-scale (blur-sharpen) */
91 coefficient[7]; /* cubic coefficents for BC-cubic spline filters */
98 Forward declaractions.
100 static MagickRealType
101 I0(MagickRealType x),
102 BesselOrderOne(MagickRealType),
103 Sinc(const MagickRealType, const ResizeFilter *),
104 SincFast(const MagickRealType, const ResizeFilter *);
107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
111 + F i l t e r F u n c t i o n s %
115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
117 % These are the various filter and windowing functions that are provided.
119 % They are internal to this module only. See AcquireResizeFilterInfo() for
120 % details of the access to these functions, via the GetResizeFilterSupport()
121 % and GetResizeFilterWeight() API interface.
123 % The individual filter functions have this format...
125 % static MagickRealtype *FilterName(const MagickRealType x,
126 % const MagickRealType support)
128 % A description of each parameter follows:
130 % o x: the distance from the sampling point generally in the range of 0 to
131 % support. The GetResizeFilterWeight() ensures this a positive value.
133 % o resize_filter: current filter information. This allows function to
134 % access support, and possibly other pre-calculated information defining
139 #define MagickPIL ((MagickRealType) 3.14159265358979323846264338327950288420L)
141 static MagickRealType Blackman(const MagickRealType x,
142 const ResizeFilter *magick_unused(resize_filter))
145 Blackman: 2nd order cosine windowing function:
146 0.42 + 0.5 cos(pi x) + 0.08 cos(2pi x)
148 Refactored by Chantal Racette and Nicolas Robidoux to one trig call and
151 const MagickRealType cosine=cos((double) (MagickPIL*x));
152 return(0.34+cosine*(0.5+cosine*0.16));
155 static MagickRealType Bohman(const MagickRealType x,
156 const ResizeFilter *magick_unused(resize_filter))
159 Bohman: 2rd Order cosine windowing function:
160 (1-x) cos(pi x) + sin(pi x) / pi.
162 Refactored by Nicolas Robidoux to one trig call, one sqrt call, and 7 flops,
163 taking advantage of the fact that the support of Bohman is 1.0 (so that we
164 know that sin(pi x) >= 0).
166 const MagickRealType cosine=cos((double) (MagickPIL*x));
167 const MagickRealType sine=sqrt(1.0-cosine*cosine);
168 return((1.0-x)*cosine+(1.0/MagickPIL)*sine);
171 static MagickRealType Box(const MagickRealType magick_unused(x),
172 const ResizeFilter *magick_unused(resize_filter))
175 A Box filter is a equal weighting function (all weights equal). DO NOT
176 LIMIT results by support or resize point sampling will work as it requests
177 points beyond its normal 0.0 support size.
182 static MagickRealType CubicBC(const MagickRealType x,
183 const ResizeFilter *resize_filter)
186 Cubic Filters using B,C determined values:
187 Mitchell-Netravali B= 1/3 C= 1/3 "Balanced" cubic spline filter
188 Catmull-Rom B= 0 C= 1/2 Interpolatory and exact on linears
189 Cubic B-Spline B= 1 C= 0 Spline approximation of Gaussian
190 Hermite B= 0 C= 0 Spline with small support (= 1)
192 See paper by Mitchell and Netravali, Reconstruction Filters in Computer
193 Graphics Computer Graphics, Volume 22, Number 4, August 1988
194 http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/
197 Coefficents are determined from B,C values:
198 P0 = ( 6 - 2*B )/6 = coeff[0]
200 P2 = (-18 +12*B + 6*C )/6 = coeff[1]
201 P3 = ( 12 - 9*B - 6*C )/6 = coeff[2]
202 Q0 = ( 8*B +24*C )/6 = coeff[3]
203 Q1 = ( -12*B -48*C )/6 = coeff[4]
204 Q2 = ( 6*B +30*C )/6 = coeff[5]
205 Q3 = ( - 1*B - 6*C )/6 = coeff[6]
207 which are used to define the filter:
209 P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
210 Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x < 2
212 which ensures function is continuous in value and derivative (slope).
215 return(resize_filter->coefficient[0]+x*(x*(resize_filter->coefficient[1]+x*
216 resize_filter->coefficient[2])));
218 return(resize_filter->coefficient[3]+x*(resize_filter->coefficient[4]+x*
219 (resize_filter->coefficient[5]+x*resize_filter->coefficient[6])));
223 static MagickRealType Gaussian(const MagickRealType x,
224 const ResizeFilter *resize_filter)
227 Gaussian with a fixed sigma = 1/2
229 Gaussian Formula (1D) ...
230 exp( -(x^2)/((2.0*sigma^2) ) / sqrt(2*PI)sigma^2))
232 The constants are pre-calculated...
233 exp( -coeff[0]*(x^2)) ) * coeff[1]
235 However the multiplier coefficent (1) is not needed and not used.
237 Gaussian Formula (2D) ...
238 exp( -(x^2)/((2.0*sigma^2) ) / (PI*sigma^2) )
240 Note that it is only a change in the normalization multiplier which is
241 not needed or used when gausian is used as a filter.
243 This separates the gaussian 'sigma' value from the 'blur/support'
244 settings allowing for its use in special 'small sigma' gaussians,
245 without the filter 'missing' pixels because the support becomes too small.
247 return(exp((double)(-resize_filter->coefficient[0]*x*x)));
250 static MagickRealType Hanning(const MagickRealType x,
251 const ResizeFilter *magick_unused(resize_filter))
254 Cosine window function:
257 const MagickRealType cosine=cos((double) (MagickPIL*x));
258 return(0.5+0.5*cosine);
261 static MagickRealType Hamming(const MagickRealType x,
262 const ResizeFilter *magick_unused(resize_filter))
265 Offset cosine window function:
268 const MagickRealType cosine=cos((double) (MagickPIL*x));
269 return(0.54+0.46*cosine);
272 static MagickRealType Jinc(const MagickRealType x,
273 const ResizeFilter *magick_unused(resize_filter))
276 See Pratt "Digital Image Processing" p.97 for Jinc/Bessel functions.
277 http://mathworld.wolfram.com/JincFunction.html and page 11 of
278 http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
280 The original "zoom" program by Paul Heckbert called this "Bessel". But
281 really it is more accurately named "Jinc".
284 return(0.5*MagickPIL);
285 return(BesselOrderOne(MagickPIL*x)/x);
288 static MagickRealType Kaiser(const MagickRealType x,
289 const ResizeFilter *resize_filter)
292 Kaiser Windowing Function (bessel windowing)
293 Alpha (c[0]) is a free value from 5 to 8 (defaults to 6.5).
294 A scaling factor (c[1]) is not needed as filter is normalized
296 return(resize_filter->coefficient[1]*
297 I0(resize_filter->coefficient[0]*sqrt((double) (1.0-x*x))));
300 static MagickRealType Lagrange(const MagickRealType x,
301 const ResizeFilter *resize_filter)
314 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the lagrange
315 function and depends on the overall support window size of the filter. That
316 is: for a support of 2, it gives a lagrange-4 (piecewise cubic function).
318 "n" identifies the piece of the piecewise polynomial.
320 See Survey: Interpolation Methods, IEEE Transactions on Medical Imaging,
321 Vol 18, No 11, November 1999, p1049-1075, -- Equation 27 on p1064.
323 if (x > resize_filter->support)
325 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
326 n=(ssize_t) (resize_filter->window_support+x);
328 for (i=0; i < order; i++)
330 value*=(n-i-x)/(n-i);
334 static MagickRealType Quadratic(const MagickRealType x,
335 const ResizeFilter *magick_unused(resize_filter))
338 2rd order (quadratic) B-Spline approximation of Gaussian.
343 return(0.5*(x-1.5)*(x-1.5));
347 static MagickRealType Sinc(const MagickRealType x,
348 const ResizeFilter *magick_unused(resize_filter))
351 Scaled sinc(x) function using a trig call:
352 sinc(x) == sin(pi x)/(pi x).
356 const MagickRealType alpha=(MagickRealType) (MagickPIL*x);
357 return(sin((double) alpha)/alpha);
359 return((MagickRealType) 1.0);
362 static MagickRealType SincFast(const MagickRealType x,
363 const ResizeFilter *magick_unused(resize_filter))
366 Approximations of the sinc function sin(pi x)/(pi x) over the interval
367 [-4,4] constructed by Nicolas Robidoux and Chantal Racette with funding
368 from the Natural Sciences and Engineering Research Council of Canada.
370 Although the approximations are polynomials (for low order of
371 approximation) and quotients of polynomials (for higher order of
372 approximation) and consequently are similar in form to Taylor polynomials /
373 Pade approximants, the approximations are computed with a completely
376 Summary: These approximations are "the best" in terms of bang (accuracy)
377 for the buck (flops). More specifically: Among the polynomial quotients
378 that can be computed using a fixed number of flops (with a given "+ - * /
379 budget"), the chosen polynomial quotient is the one closest to the
380 approximated function with respect to maximum absolute relative error over
383 The Remez algorithm, as implemented in the boost library's minimax package,
384 is the key to the construction: http://www.boost.org/doc/libs/1_36_0/libs/
385 math/doc/sf_and_dist/html/math_toolkit/backgrounders/remez.html
387 If outside of the interval of approximation, use the standard trig formula.
391 const MagickRealType alpha=(MagickRealType) (MagickPIL*x);
392 return(sin((double) alpha)/alpha);
396 The approximations only depend on x^2 (sinc is an even function).
398 const MagickRealType xx = x*x;
399 #if MAGICKCORE_QUANTUM_DEPTH <= 8
401 Maximum absolute relative error 6.3e-6 < 1/2^17.
403 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
404 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
405 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
406 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
407 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
408 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
409 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
410 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
411 const MagickRealType p =
412 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
413 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
414 #elif MAGICKCORE_QUANTUM_DEPTH <= 16
416 Max. abs. rel. error 2.2e-8 < 1/2^25.
418 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
419 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
420 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
421 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
422 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
423 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
424 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
425 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
426 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
427 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
428 const MagickRealType p =
429 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
430 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
433 Max. abs. rel. error 1.2e-12 < 1/2^39.
435 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
436 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
437 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
438 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
439 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
440 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
441 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
442 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
443 const MagickRealType p =
444 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
445 const MagickRealType d0 = 1.0L;
446 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
447 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
448 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
449 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
450 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
451 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
456 static MagickRealType Triangle(const MagickRealType x,
457 const ResizeFilter *magick_unused(resize_filter))
460 1st order (linear) B-Spline, bilinear interpolation, Tent 1D filter, or
461 a Bartlett 2D Cone filter. Also used as a Bartlett Windowing function
469 static MagickRealType Welsh(const MagickRealType x,
470 const ResizeFilter *magick_unused(resize_filter))
473 Welsh parabolic windowing filter.
481 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485 + A c q u i r e R e s i z e F i l t e r %
489 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
491 % AcquireResizeFilter() allocates the ResizeFilter structure. Choose from
494 % FIR (Finite impulse Response) Filters
495 % Box Triangle Quadratic Cubic Hermite Catrom Mitchell
497 % IIR (Infinite impulse Response) Filters
498 % Gaussian Sinc Jinc (Bessel)
500 % Windowed Sinc/Jinc Filters
501 % Blackman Hanning Hamming Kaiser Lanczos
503 % Special purpose Filters
504 % SincFast LanczosSharp Lanczos2D Lanczos2DSharp Robidoux
506 % The users "-filter" selection is used to lookup the default 'expert'
507 % settings for that filter from a internal table. However any provided
508 % 'expert' settings (see below) may override this selection.
510 % FIR filters are used as is, and are limited to that filters support window
511 % (unless over-ridden). 'Gaussian' while classed as an IIR filter, is also
512 % simply clipped by its support size (currently 1.5 or approximatally 3*sigma
513 % as recommended by many references)
515 % The special a 'cylindrical' filter flag will promote the default 4-lobed
516 % Windowed Sinc filter to a 3-lobed Windowed Jinc equivalent, which is better
517 % suited to this style of image resampling. This typically happens when using
518 % such a filter for images distortions.
520 % Directly requesting 'Sinc', 'Jinc' function as a filter will force the use
521 % of function without any windowing, or promotion for cylindrical usage. This
522 % is not recommended, except by image processing experts, especially as part
523 % of expert option filter function selection.
525 % Two forms of the 'Sinc' function are available: Sinc and SincFast. Sinc is
526 % computed using the traditional sin(pi*x)/(pi*x); it is selected if the user
527 % specifically specifies the use of a Sinc filter. SincFast uses highly
528 % accurate (and fast) polynomial (low Q) and rational (high Q) approximations,
529 % and will be used by default in most cases.
531 % The Lanczos filter is a special 3-lobed Sinc-windowed Sinc filter (promoted
532 % to Jinc-windowed Jinc for cylindrical (Elliptical Weighted Average) use).
533 % The Sinc version is the most popular windowed filter.
535 % LanczosSharp is a slightly sharpened (blur=0.9812505644269356 < 1) form of
536 % the Lanczos filter, specifically designed for EWA distortion (as a
537 % Jinc-Jinc); it can also be used as a slightly sharper orthogonal Lanczos
538 % (Sinc-Sinc) filter. The chosen blur value comes as close as possible to
539 % satisfying the following condition without changing the character of the
540 % corresponding EWA filter:
542 % 'No-Op' Vertical and Horizontal Line Preservation Condition: Images with
543 % only vertical or horizontal features are preserved when performing 'no-op"
544 % with EWA distortion.
546 % The Lanczos2 and Lanczos2Sharp filters are 2-lobe versions of the Lanczos
547 % filters. The 'sharp' version uses a blur factor of 0.9549963639785485,
548 % again chosen because the resulting EWA filter comes as close as possible to
549 % satisfying the above condition.
551 % Robidoux is another filter tuned for EWA. It is the Keys cubic filter
552 % defined by B=(228 - 108 sqrt(2))/199. Robidoux satisfies the "'No-Op'
553 % Vertical and Horizontal Line Preservation Condition" exactly, and it
554 % moderately blurs high frequency 'pixel-hash' patterns under no-op. It turns
555 % out to be close to both Mitchell and Lanczos2Sharp. For example, its first
556 % crossing is at (36 sqrt(2) + 123)/(72 sqrt(2) + 47), almost the same as the
557 % first crossing of Mitchell and Lanczos2Sharp.
561 % These artifact "defines" are not recommended for production use without
562 % expert knowledge of resampling, filtering, and the effects they have on the
563 % resulting resampled (resize ro distorted) image.
565 % They can be used to override any and all filter default, and it is
566 % recommended you make good use of "filter:verbose" to make sure that the
567 % overall effect of your selection (before and after) is as expected.
569 % "filter:verbose" controls whether to output the exact results of the
570 % filter selections made, as well as plotting data for graphing the
571 % resulting filter over the filters support range.
573 % "filter:filter" select the main function associated with this filter
574 % name, as the weighting function of the filter. This can be used to
575 % set a windowing function as a weighting function, for special
576 % purposes, such as graphing.
578 % If a "filter:window" operation has not been provided, a 'Box'
579 % windowing function will be set to denote that no windowing function is
582 % "filter:window" Select this windowing function for the filter. While any
583 % filter could be used as a windowing function, using the 'first lobe' of
584 % that filter over the whole support window, using a non-windowing
585 % function is not advisible. If no weighting filter function is specifed
586 % a 'SincFast' filter is used.
588 % "filter:lobes" Number of lobes to use for the Sinc/Jinc filter. This a
589 % simpler method of setting filter support size that will correctly
590 % handle the Sinc/Jinc switch for an operators filtering requirements.
591 % Only integers should be given.
593 % "filter:support" Set the support size for filtering to the size given.
594 % This not recommended for Sinc/Jinc windowed filters (lobes should be
595 % used instead). This will override any 'filter:lobes' option.
597 % "filter:win-support" Scale windowing function to this size instead. This
598 % causes the windowing (or self-windowing Lagrange filter) to act is if
599 % the support window it much much larger than what is actually supplied
600 % to the calling operator. The filter however is still clipped to the
601 % real support size given, by the support range suppiled to the caller.
602 % If unset this will equal the normal filter support size.
604 % "filter:blur" Scale the filter and support window by this amount. A value
605 % > 1 will generally result in a more burred image with more ringing
606 % effects, while a value <1 will sharpen the resulting image with more
609 % "filter:sigma" The sigma value to use for the Gaussian filter only.
610 % Defaults to '1/2'. Using a different sigma effectively provides a
611 % method of using the filter as a 'blur' convolution. Particularly when
612 % using it for Distort.
615 % "filter:c" Override the preset B,C values for a Cubic type of filter.
616 % If only one of these are given it is assumes to be a 'Keys' type of
617 % filter such that B+2C=1, where Keys 'alpha' value = C.
621 % Set a true un-windowed Sinc filter with 10 lobes (very slow):
622 % -define filter:filter=Sinc
623 % -define filter:lobes=8
625 % Set an 8 lobe Lanczos (Sinc or Jinc) filter:
627 % -define filter:lobes=8
629 % The format of the AcquireResizeFilter method is:
631 % ResizeFilter *AcquireResizeFilter(const Image *image,
632 % const FilterTypes filter_type, const MagickBooleanType radial,
633 % ExceptionInfo *exception)
635 % A description of each parameter follows:
637 % o image: the image.
639 % o filter: the filter type, defining a preset filter, window and support.
640 % The artifact settings listed above will override those selections.
642 % o blur: blur the filter by this amount, use 1.0 if unknown. Image
643 % artifact "filter:blur" will override this API call usage, including any
644 % internal change (such as for cylindrical usage).
646 % o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical (radial)
649 % o exception: return any errors or warnings in this structure.
652 MagickPrivate ResizeFilter *AcquireResizeFilter(const Image *image,
653 const FilterTypes filter,const MagickRealType blur,
654 const MagickBooleanType cylindrical,ExceptionInfo *exception)
668 register ResizeFilter
672 Table Mapping given Filter, into Weighting and Windowing functions. A
673 'Box' windowing function means its a simble non-windowed filter. An
674 'SincFast' filter function could be upgraded to a 'Jinc' filter if a
675 "cylindrical", unless a 'Sinc' or 'SincFast' filter was specifically
678 WARNING: The order of this tabel must match the order of the FilterTypes
679 enumeration specified in "resample.h", or the filter names will not match
680 the filter being setup.
682 You can check filter setups with the "filter:verbose" setting.
689 } const mapping[SentinelFilter] =
691 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
692 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
693 { BoxFilter, BoxFilter }, /* Box averaging filter */
694 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
695 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
696 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
697 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
698 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
699 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
700 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approx */
701 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
702 { CatromFilter, BoxFilter }, /* Cubic-Keys interpolator */
703 { MitchellFilter, BoxFilter }, /* 'Ideal' Cubic-Keys filter */
704 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
705 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
706 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
707 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
708 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
709 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
710 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
711 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
712 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing */
713 { LanczosFilter, LanczosFilter }, /* Lanczos Sinc-Sinc filters */
714 { LanczosSharpFilter, LanczosSharpFilter }, /* | these require */
715 { Lanczos2Filter, Lanczos2Filter }, /* | special handling */
716 { Lanczos2SharpFilter, Lanczos2SharpFilter },
717 { RobidouxFilter, BoxFilter }, /* Cubic Keys tuned for EWA */
720 Table mapping the filter/window from the above table to an actual function.
721 The default support size for that filter as a weighting function, the range
722 to scale with to use that function as a sinc windowing function, (typ 1.0).
724 Note that the filter_type -> function is 1 to 1 except for Sinc(),
725 SincFast(), and CubicBC() functions, which may have multiple filter to
726 function associations.
728 See "filter:verbose" handling below for the function -> filter mapping.
733 (*function)(const MagickRealType,const ResizeFilter*),
734 lobes, /* Default lobes/support size of the weighting filter. */
735 scale, /* Support when function used as a windowing function
736 Typically equal to the location of the first zero crossing. */
737 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
738 } const filters[SentinelFilter] =
740 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
741 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
742 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
743 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
744 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
745 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
746 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
747 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
748 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
749 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
750 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
751 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
752 { CubicBC, 2.0, 8.0/7.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
753 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
754 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
755 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
756 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
757 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
758 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
759 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
760 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
761 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
762 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
763 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* lanczos, Sharpened */
764 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos, 2-lobed */
765 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2, sharpened */
766 { CubicBC, 2.0, 1.1685777620836932,
767 0.37821575509399867, 0.31089212245300067 }
768 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
771 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
772 function being used as a filter. It is used by the "filter:lobes" expert
773 setting and for 'lobes' for Jinc functions in the previous table. This way
774 users do not have to deal with the highly irrational lobe sizes of the Jinc
777 Values taken from http://cose.math.bas.bg/webMathematica/webComputing/
778 BesselZeros.jsp using Jv-function with v=1, then dividing by PI.
780 static MagickRealType
802 Allocate resize filter.
804 assert(image != (const Image *) NULL);
805 assert(image->signature == MagickSignature);
806 if (image->debug != MagickFalse)
807 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
808 assert(UndefinedFilter < filter && filter < SentinelFilter);
809 assert(exception != (ExceptionInfo *) NULL);
810 assert(exception->signature == MagickSignature);
811 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
812 if (resize_filter == (ResizeFilter *) NULL)
813 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
814 (void) ResetMagickMemory(resize_filter,0,sizeof(*resize_filter));
816 Defaults for the requested filter.
818 filter_type=mapping[filter].filter;
819 window_type=mapping[filter].window;
820 resize_filter->blur = blur; /* function argument blur factor */
821 /* Promote 1D Windowed Sinc Filters to a 2D Windowed Jinc filters */
822 if ((cylindrical != MagickFalse) && (filter_type == SincFastFilter) &&
823 (filter != SincFastFilter))
824 filter_type=JincFilter; /* 1D Windowed Sinc => 2D Windowed Jinc filters */
825 /* Expert filter setting override */
826 artifact=GetImageArtifact(image,"filter:filter");
827 if (artifact != (const char *) NULL)
832 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
833 if ((UndefinedFilter < option) && (option < SentinelFilter))
834 { /* Raw filter request - no window function. */
835 filter_type=(FilterTypes) option;
836 window_type=BoxFilter;
838 /* Filter override with a specific window function. */
839 artifact=GetImageArtifact(image,"filter:window");
840 if (artifact != (const char *) NULL)
842 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
843 if ((UndefinedFilter < option) && (option < SentinelFilter))
844 window_type=(FilterTypes) option;
849 /* Window specified, but no filter function? Assume Sinc/Jinc. */
850 artifact=GetImageArtifact(image,"filter:window");
851 if (artifact != (const char *) NULL)
856 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
857 if ((UndefinedFilter < option) && (option < SentinelFilter))
859 filter_type=cylindrical != MagickFalse ? JincFilter :
861 window_type=(FilterTypes) option;
866 /* Assign the real functions to use for the filters selected. */
867 resize_filter->filter=filters[filter_type].function;
868 resize_filter->support=filters[filter_type].lobes;
869 resize_filter->window=filters[window_type].function;
870 resize_filter->scale=filters[window_type].scale;
871 resize_filter->signature=MagickSignature;
873 /* Filter Modifications for orthogonal/cylindrical usage */
874 if (cylindrical != MagickFalse)
878 /* Support for Cylindrical Box should be sqrt(2)/2 */
879 resize_filter->support=(MagickRealType) MagickSQ1_2;
882 case LanczosSharpFilter:
884 case Lanczos2SharpFilter:
885 resize_filter->filter=filters[JincFilter].function;
886 resize_filter->window=filters[JincFilter].function;
887 resize_filter->scale=filters[JincFilter].scale;
888 /* number of lobes (support window size) remain unchanged */
893 /* Global Sharpening (regardless of orthoginal/cylindrical) */
896 case LanczosSharpFilter:
897 resize_filter->blur*=0.9812505644269356;
899 case Lanczos2SharpFilter:
900 resize_filter->blur*=0.9549963639785485;
907 ** Other Expert Option Modifications
910 /* User Gaussian Sigma Override - no support change */
911 value = 0.5; /* guassian sigma default, half pixel */
912 if ( GaussianFilter ) {
913 artifact=GetImageArtifact(image,"filter:sigma");
914 if (artifact != (const char *) NULL)
915 value=StringToDouble(artifact,(char **) NULL);
916 /* Define coefficents for Gaussian */
917 resize_filter->coefficient[0]=1.0/(2.0*value*value); /* X scaling */
918 resize_filter->coefficient[1]=(MagickRealType) (1.0/(Magick2PI*value*
919 value)); /* normalization */
921 /* User Kaiser Alpha Override - no support change */
922 if ( KaiserFilter ) {
923 value=6.5; /* default alpha value for Kaiser bessel windowing function */
924 artifact=GetImageArtifact(image,"filter:alpha");
925 if (artifact != (const char *) NULL)
926 value=StringToDouble(artifact,(char **) NULL);
927 /* Define coefficents for Kaiser Windowing Function */
928 resize_filter->coefficient[0]=value; /* X scaling */
929 resize_filter->coefficient[1]=1.0/I0(value); /* normalization */
933 artifact=GetImageArtifact(image,"filter:blur");
934 if (artifact != (const char *) NULL)
935 resize_filter->blur*=StringToDouble(artifact,(char **) NULL);
936 if (resize_filter->blur < MagickEpsilon)
937 resize_filter->blur=(MagickRealType) MagickEpsilon;
939 /* Support Overrides */
940 artifact=GetImageArtifact(image,"filter:lobes");
941 if (artifact != (const char *) NULL)
946 lobes=(ssize_t) StringToLong(artifact);
949 resize_filter->support=(MagickRealType) lobes;
951 /* Convert a Jinc function lobes value to a real support value */
952 if (resize_filter->filter == Jinc)
954 if (resize_filter->support > 16)
955 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
957 resize_filter->support=jinc_zeros[((long)resize_filter->support)-1];
959 /* expert override of the support setting */
960 artifact=GetImageArtifact(image,"filter:support");
961 if (artifact != (const char *) NULL)
962 resize_filter->support=fabs(StringToDouble(artifact,(char **) NULL));
964 Scale windowing function separately to the support 'clipping'
965 window that calling operator is planning to actually use. (Expert
968 resize_filter->window_support=resize_filter->support; /* default */
969 artifact=GetImageArtifact(image,"filter:win-support");
970 if (artifact != (const char *) NULL)
971 resize_filter->window_support=fabs(StringToDouble(artifact,(char **) NULL));
973 Adjust window function scaling to match windowing support for
974 weighting function. This avoids a division on every filter call.
976 resize_filter->scale/=resize_filter->window_support;
979 * Set Cubic Spline B,C values, calculate Cubic coefficients.
983 if ((filters[filter_type].function == CubicBC) ||
984 (filters[window_type].function == CubicBC))
986 B=filters[filter_type].B;
987 C=filters[filter_type].C;
988 if (filters[window_type].function == CubicBC)
990 B=filters[window_type].B;
991 C=filters[window_type].C;
993 artifact=GetImageArtifact(image,"filter:b");
994 if (artifact != (const char *) NULL)
996 B=StringToDouble(artifact,(char **) NULL);
997 C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
998 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
999 if (artifact != (const char *) NULL)
1000 C=StringToDouble(artifact,(char **) NULL);
1004 artifact=GetImageArtifact(image,"filter:c");
1005 if (artifact != (const char *) NULL)
1007 C=StringToDouble(artifact,(char **) NULL);
1008 B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
1011 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
1017 resize_filter->coefficient[0]=1.0-(1.0/3.0)*B;
1018 resize_filter->coefficient[1]=-3.0+B_squared+C;
1019 resize_filter->coefficient[2]=2.0-1.5*B-C;
1020 resize_filter->coefficient[3]=(4.0/3.0)*B+4.0*C;
1021 resize_filter->coefficient[4]=-8.0*C-B_squared;
1022 resize_filter->coefficient[5]=B+5.0*C;
1023 resize_filter->coefficient[6]=(-1.0/6.0)*B-C;
1028 Expert Option Request for verbose details of the resulting filter.
1030 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1034 artifact=GetImageArtifact(image,"filter:verbose");
1035 if (IsMagickTrue(artifact) != MagickFalse)
1042 Set the weighting function properly when the weighting
1043 function may not exactly match the filter of the same name.
1044 EG: a Point filter is really uses a Box weighting function
1045 with a different support than is typically used.
1047 if (resize_filter->filter == Box) filter_type=BoxFilter;
1048 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1049 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1050 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1051 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1052 if (resize_filter->window == Box) window_type=BoxFilter;
1053 if (resize_filter->window == Sinc) window_type=SincFilter;
1054 if (resize_filter->window == SincFast) window_type=SincFastFilter;
1055 if (resize_filter->window == Jinc) window_type=JincFilter;
1056 if (resize_filter->window == CubicBC) window_type=CubicFilter;
1058 Report Filter Details.
1060 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1061 (void) FormatLocaleFile(stdout,"# Resize Filter (for graphing)\n#\n");
1062 (void) FormatLocaleFile(stdout,"# filter = %s\n",
1063 CommandOptionToMnemonic(MagickFilterOptions,filter_type));
1064 (void) FormatLocaleFile(stdout,"# window = %s\n",
1065 CommandOptionToMnemonic(MagickFilterOptions, window_type));
1066 (void) FormatLocaleFile(stdout,"# support = %.*g\n",
1067 GetMagickPrecision(),(double) resize_filter->support);
1068 (void) FormatLocaleFile(stdout,"# win-support = %.*g\n",
1069 GetMagickPrecision(),(double) resize_filter->window_support);
1070 (void) FormatLocaleFile(stdout,"# scale_blur = %.*g\n",
1071 GetMagickPrecision(), (double)resize_filter->blur);
1072 if (filter_type == GaussianFilter)
1073 (void) FormatLocaleFile(stdout,"# gaussian_sigma = %.*g\n",
1074 GetMagickPrecision(), (double)value);
1075 if ( filter_type == KaiserFilter )
1076 (void) FormatLocaleFile(stdout,"# kaiser_alpha = %.*g\n",
1077 GetMagickPrecision(), (double)value);
1078 (void) FormatLocaleFile(stdout,"# practical_support = %.*g\n",
1079 GetMagickPrecision(), (double)support);
1080 if ( filter_type == CubicFilter || window_type == CubicFilter )
1081 (void) FormatLocaleFile(stdout,"# B,C = %.*g,%.*g\n",
1082 GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
1083 (void) FormatLocaleFile(stdout,"\n");
1085 Output values of resulting filter graph -- for graphing
1088 for (x=0.0; x <= support; x+=0.01f)
1089 (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1090 (double) GetResizeFilterWeight(resize_filter,x));
1091 /* A final value so gnuplot can graph the 'stop' properly. */
1092 (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",support,
1093 GetMagickPrecision(),0.0);
1095 /* Output the above once only for each image - remove setting */
1096 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1097 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1100 return(resize_filter);
1104 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1108 % A d a p t i v e R e s i z e I m a g e %
1112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1114 % AdaptiveResizeImage() adaptively resize image with pixel resampling.
1116 % The format of the AdaptiveResizeImage method is:
1118 % Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1119 % const size_t rows,const PixelInterpolateMethod method,
1120 % ExceptionInfo *exception)
1122 % A description of each parameter follows:
1124 % o image: the image.
1126 % o columns: the number of columns in the resized image.
1128 % o rows: the number of rows in the resized image.
1130 % o method: the pixel interpolation method.
1132 % o exception: return any errors or warnings in this structure.
1135 MagickExport Image *AdaptiveResizeImage(const Image *image,
1136 const size_t columns,const size_t rows,const PixelInterpolateMethod method,
1137 ExceptionInfo *exception)
1139 #define AdaptiveResizeImageTag "Resize/Image"
1158 Adaptively resize image.
1160 assert(image != (const Image *) NULL);
1161 assert(image->signature == MagickSignature);
1162 if (image->debug != MagickFalse)
1163 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1164 assert(exception != (ExceptionInfo *) NULL);
1165 assert(exception->signature == MagickSignature);
1166 if ((columns == 0) || (rows == 0))
1167 return((Image *) NULL);
1168 if ((columns == image->columns) && (rows == image->rows))
1169 return(CloneImage(image,0,0,MagickTrue,exception));
1170 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1171 if (resize_image == (Image *) NULL)
1172 return((Image *) NULL);
1173 if (SetImageStorageClass(resize_image,DirectClass,exception) == MagickFalse)
1175 resize_image=DestroyImage(resize_image);
1176 return((Image *) NULL);
1180 image_view=AcquireCacheView(image);
1181 resize_view=AcquireCacheView(resize_image);
1182 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1183 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
1185 for (y=0; y < (ssize_t) resize_image->rows; y++)
1196 if (status == MagickFalse)
1198 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1200 if (q == (Quantum *) NULL)
1202 offset.y=((MagickRealType) (y+0.5)*image->rows/resize_image->rows);
1203 for (x=0; x < (ssize_t) resize_image->columns; x++)
1205 offset.x=((MagickRealType) (x+0.5)*image->columns/resize_image->columns);
1206 status=InterpolatePixelChannels(image,image_view,resize_image,
1207 method,offset.x-0.5,offset.y-0.5,q,exception);
1208 q+=GetPixelChannels(resize_image);
1210 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1212 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1217 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1218 #pragma omp critical (MagickCore_AdaptiveResizeImage)
1220 proceed=SetImageProgress(image,AdaptiveResizeImageTag,progress++,
1222 if (proceed == MagickFalse)
1226 resize_view=DestroyCacheView(resize_view);
1227 image_view=DestroyCacheView(image_view);
1228 if (status == MagickFalse)
1229 resize_image=DestroyImage(resize_image);
1230 return(resize_image);
1234 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1238 + B e s s e l O r d e r O n e %
1242 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1244 % BesselOrderOne() computes the Bessel function of x of the first kind of
1245 % order 0. This is used to create the Jinc() filter function below.
1247 % Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1253 % j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1255 % where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1257 % cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1258 % = 1/sqrt(2) * (sin(x) - cos(x))
1259 % sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1260 % = -1/sqrt(2) * (sin(x) + cos(x))
1262 % The format of the BesselOrderOne method is:
1264 % MagickRealType BesselOrderOne(MagickRealType x)
1266 % A description of each parameter follows:
1268 % o x: MagickRealType value.
1273 static MagickRealType I0(MagickRealType x)
1284 Zeroth order Bessel function of the first kind.
1289 for (i=2; t > MagickEpsilon; i++)
1292 t*=y/((MagickRealType) i*i);
1298 static MagickRealType J1(MagickRealType x)
1310 0.581199354001606143928050809e+21,
1311 -0.6672106568924916298020941484e+20,
1312 0.2316433580634002297931815435e+19,
1313 -0.3588817569910106050743641413e+17,
1314 0.2908795263834775409737601689e+15,
1315 -0.1322983480332126453125473247e+13,
1316 0.3413234182301700539091292655e+10,
1317 -0.4695753530642995859767162166e+7,
1318 0.270112271089232341485679099e+4
1322 0.11623987080032122878585294e+22,
1323 0.1185770712190320999837113348e+20,
1324 0.6092061398917521746105196863e+17,
1325 0.2081661221307607351240184229e+15,
1326 0.5243710262167649715406728642e+12,
1327 0.1013863514358673989967045588e+10,
1328 0.1501793594998585505921097578e+7,
1329 0.1606931573481487801970916749e+4,
1335 for (i=7; i >= 0; i--)
1344 static MagickRealType P1(MagickRealType x)
1356 0.352246649133679798341724373e+5,
1357 0.62758845247161281269005675e+5,
1358 0.313539631109159574238669888e+5,
1359 0.49854832060594338434500455e+4,
1360 0.2111529182853962382105718e+3,
1361 0.12571716929145341558495e+1
1365 0.352246649133679798068390431e+5,
1366 0.626943469593560511888833731e+5,
1367 0.312404063819041039923015703e+5,
1368 0.4930396490181088979386097e+4,
1369 0.2030775189134759322293574e+3,
1375 for (i=4; i >= 0; i--)
1377 p=p*(8.0/x)*(8.0/x)+Pone[i];
1378 q=q*(8.0/x)*(8.0/x)+Qone[i];
1384 static MagickRealType Q1(MagickRealType x)
1396 0.3511751914303552822533318e+3,
1397 0.7210391804904475039280863e+3,
1398 0.4259873011654442389886993e+3,
1399 0.831898957673850827325226e+2,
1400 0.45681716295512267064405e+1,
1401 0.3532840052740123642735e-1
1405 0.74917374171809127714519505e+4,
1406 0.154141773392650970499848051e+5,
1407 0.91522317015169922705904727e+4,
1408 0.18111867005523513506724158e+4,
1409 0.1038187585462133728776636e+3,
1415 for (i=4; i >= 0; i--)
1417 p=p*(8.0/x)*(8.0/x)+Pone[i];
1418 q=q*(8.0/x)*(8.0/x)+Qone[i];
1423 static MagickRealType BesselOrderOne(MagickRealType x)
1436 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1437 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1449 + D e s t r o y R e s i z e F i l t e r %
1453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1455 % DestroyResizeFilter() destroy the resize filter.
1457 % The format of the DestroyResizeFilter method is:
1459 % ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1461 % A description of each parameter follows:
1463 % o resize_filter: the resize filter.
1466 MagickPrivate ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1468 assert(resize_filter != (ResizeFilter *) NULL);
1469 assert(resize_filter->signature == MagickSignature);
1470 resize_filter->signature=(~MagickSignature);
1471 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1472 return(resize_filter);
1476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1480 + G e t R e s i z e F i l t e r S u p p o r t %
1484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1486 % GetResizeFilterSupport() return the current support window size for this
1487 % filter. Note that this may have been enlarged by filter:blur factor.
1489 % The format of the GetResizeFilterSupport method is:
1491 % MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1493 % A description of each parameter follows:
1495 % o filter: Image filter to use.
1498 MagickPrivate MagickRealType GetResizeFilterSupport(
1499 const ResizeFilter *resize_filter)
1501 assert(resize_filter != (ResizeFilter *) NULL);
1502 assert(resize_filter->signature == MagickSignature);
1503 return(resize_filter->support*resize_filter->blur);
1507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1511 + G e t R e s i z e F i l t e r W e i g h t %
1515 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1517 % GetResizeFilterWeight evaluates the specified resize filter at the point x
1518 % which usally lies between zero and the filters current 'support' and
1519 % returns the weight of the filter function at that point.
1521 % The format of the GetResizeFilterWeight method is:
1523 % MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1524 % const MagickRealType x)
1526 % A description of each parameter follows:
1528 % o filter: the filter type.
1533 MagickPrivate MagickRealType GetResizeFilterWeight(
1534 const ResizeFilter *resize_filter,const MagickRealType x)
1542 Windowing function - scale the weighting filter by this amount.
1544 assert(resize_filter != (ResizeFilter *) NULL);
1545 assert(resize_filter->signature == MagickSignature);
1546 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
1547 if ((resize_filter->window_support < MagickEpsilon) ||
1548 (resize_filter->window == Box))
1549 scale=1.0; /* Point or Box Filter -- avoid division by zero */
1552 scale=resize_filter->scale;
1553 scale=resize_filter->window(x_blur*scale,resize_filter);
1555 weight=scale*resize_filter->filter(x_blur,resize_filter);
1558 #if defined(MAGICKCORE_LQR_DELEGATE)
1561 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1565 % L i q u i d R e s c a l e I m a g e %
1569 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1571 % LiquidRescaleImage() rescales image with seam carving.
1573 % The format of the LiquidRescaleImage method is:
1575 % Image *LiquidRescaleImage(const Image *image,const size_t columns,
1576 % const size_t rows,const double delta_x,const double rigidity,
1577 % ExceptionInfo *exception)
1579 % A description of each parameter follows:
1581 % o image: the image.
1583 % o columns: the number of columns in the rescaled image.
1585 % o rows: the number of rows in the rescaled image.
1587 % o delta_x: maximum seam transversal step (0 means straight seams).
1589 % o rigidity: introduce a bias for non-straight seams (typically 0).
1591 % o exception: return any errors or warnings in this structure.
1594 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1595 const size_t rows,const double delta_x,const double rigidity,
1596 ExceptionInfo *exception)
1598 #define LiquidRescaleImageTag "Rescale/Image"
1631 Liquid rescale image.
1633 assert(image != (const Image *) NULL);
1634 assert(image->signature == MagickSignature);
1635 if (image->debug != MagickFalse)
1636 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1637 assert(exception != (ExceptionInfo *) NULL);
1638 assert(exception->signature == MagickSignature);
1639 if ((columns == 0) || (rows == 0))
1640 return((Image *) NULL);
1641 if ((columns == image->columns) && (rows == image->rows))
1642 return(CloneImage(image,0,0,MagickTrue,exception));
1643 if ((columns <= 2) || (rows <= 2))
1644 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
1645 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1655 Honor liquid resize size limitations.
1657 for (width=image->columns; columns >= (2*width-1); width*=2);
1658 for (height=image->rows; rows >= (2*height-1); height*=2);
1659 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1661 if (resize_image == (Image *) NULL)
1662 return((Image *) NULL);
1663 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1664 rigidity,exception);
1665 resize_image=DestroyImage(resize_image);
1666 return(rescale_image);
1668 pixels=(gfloat *) AcquireQuantumMemory(image->columns,image->rows*
1669 GetPixelChannels(image)*sizeof(*pixels));
1670 if (pixels == (gfloat *) NULL)
1671 return((Image *) NULL);
1674 image_view=AcquireCacheView(image);
1675 for (y=0; y < (ssize_t) image->rows; y++)
1677 register const Quantum
1683 if (status == MagickFalse)
1685 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1686 if (p == (const Quantum *) NULL)
1691 for (x=0; x < (ssize_t) image->columns; x++)
1696 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1697 *q++=QuantumScale*p[i];
1698 p+=GetPixelChannels(image);
1701 image_view=DestroyCacheView(image_view);
1702 carver=lqr_carver_new_ext(pixels,image->columns,image->rows,
1703 GetPixelChannels(image),LQR_COLDEPTH_32F);
1704 if (carver == (LqrCarver *) NULL)
1706 pixels=(gfloat *) RelinquishMagickMemory(pixels);
1707 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1709 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1710 lqr_status=lqr_carver_resize(carver,columns,rows);
1712 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1713 lqr_carver_get_height(carver),MagickTrue,exception);
1714 if (rescale_image == (Image *) NULL)
1716 pixels=(gfloat *) RelinquishMagickMemory(pixels);
1717 return((Image *) NULL);
1719 if (SetImageStorageClass(rescale_image,DirectClass,exception) == MagickFalse)
1721 pixels=(gfloat *) RelinquishMagickMemory(pixels);
1722 rescale_image=DestroyImage(rescale_image);
1723 return((Image *) NULL);
1725 rescale_view=AcquireCacheView(rescale_image);
1726 (void) lqr_carver_scan_reset(carver);
1727 while (lqr_carver_scan_ext(carver,&x_offset,&y_offset,(void **) &packet) != 0)
1735 q=QueueCacheViewAuthenticPixels(rescale_view,x_offset,y_offset,1,1,
1737 if (q == (Quantum *) NULL)
1739 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1748 channel=GetPixelChannelMapChannel(image,i);
1749 traits=GetPixelChannelMapTraits(image,channel);
1750 rescale_traits=GetPixelChannelMapTraits(rescale_image,channel);
1751 if ((traits == UndefinedPixelTrait) ||
1752 (rescale_traits == UndefinedPixelTrait))
1754 SetPixelChannel(rescale_image,channel,ClampToQuantum(QuantumRange*
1757 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
1760 rescale_view=DestroyCacheView(rescale_view);
1761 lqr_carver_destroy(carver);
1762 return(rescale_image);
1765 MagickExport Image *LiquidRescaleImage(const Image *image,
1766 const size_t magick_unused(columns),const size_t magick_unused(rows),
1767 const double magick_unused(delta_x),const double magick_unused(rigidity),
1768 ExceptionInfo *exception)
1770 assert(image != (const Image *) NULL);
1771 assert(image->signature == MagickSignature);
1772 if (image->debug != MagickFalse)
1773 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1774 assert(exception != (ExceptionInfo *) NULL);
1775 assert(exception->signature == MagickSignature);
1776 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1777 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1778 return((Image *) NULL);
1783 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1787 % M a g n i f y I m a g e %
1791 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1793 % MagnifyImage() is a convenience method that scales an image proportionally
1794 % to twice its size.
1796 % The format of the MagnifyImage method is:
1798 % Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1800 % A description of each parameter follows:
1802 % o image: the image.
1804 % o exception: return any errors or warnings in this structure.
1807 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1812 assert(image != (Image *) NULL);
1813 assert(image->signature == MagickSignature);
1814 if (image->debug != MagickFalse)
1815 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1816 assert(exception != (ExceptionInfo *) NULL);
1817 assert(exception->signature == MagickSignature);
1818 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1820 return(magnify_image);
1824 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1828 % M i n i f y I m a g e %
1832 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1834 % MinifyImage() is a convenience method that scales an image proportionally to
1837 % The format of the MinifyImage method is:
1839 % Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1841 % A description of each parameter follows:
1843 % o image: the image.
1845 % o exception: return any errors or warnings in this structure.
1848 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1853 assert(image != (Image *) NULL);
1854 assert(image->signature == MagickSignature);
1855 if (image->debug != MagickFalse)
1856 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1857 assert(exception != (ExceptionInfo *) NULL);
1858 assert(exception->signature == MagickSignature);
1859 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,1.0,
1861 return(minify_image);
1865 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1869 % R e s a m p l e I m a g e %
1873 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1875 % ResampleImage() resize image in terms of its pixel size, so that when
1876 % displayed at the given resolution it will be the same size in terms of
1877 % real world units as the original image at the original resolution.
1879 % The format of the ResampleImage method is:
1881 % Image *ResampleImage(Image *image,const double x_resolution,
1882 % const double y_resolution,const FilterTypes filter,const double blur,
1883 % ExceptionInfo *exception)
1885 % A description of each parameter follows:
1887 % o image: the image to be resized to fit the given resolution.
1889 % o x_resolution: the new image x resolution.
1891 % o y_resolution: the new image y resolution.
1893 % o filter: Image filter to use.
1895 % o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1898 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1899 const double y_resolution,const FilterTypes filter,const double blur,
1900 ExceptionInfo *exception)
1902 #define ResampleImageTag "Resample/Image"
1912 Initialize sampled image attributes.
1914 assert(image != (const Image *) NULL);
1915 assert(image->signature == MagickSignature);
1916 if (image->debug != MagickFalse)
1917 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1918 assert(exception != (ExceptionInfo *) NULL);
1919 assert(exception->signature == MagickSignature);
1920 width=(size_t) (x_resolution*image->columns/(image->resolution.x == 0.0 ?
1921 72.0 : image->resolution.x)+0.5);
1922 height=(size_t) (y_resolution*image->rows/(image->resolution.y == 0.0 ?
1923 72.0 : image->resolution.y)+0.5);
1924 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1925 if (resample_image != (Image *) NULL)
1927 resample_image->resolution.x=x_resolution;
1928 resample_image->resolution.y=y_resolution;
1930 return(resample_image);
1934 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1938 % R e s i z e I m a g e %
1942 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1944 % ResizeImage() scales an image to the desired dimensions, using the given
1945 % filter (see AcquireFilterInfo()).
1947 % If an undefined filter is given the filter defaults to Mitchell for a
1948 % colormapped image, a image with a matte channel, or if the image is
1949 % enlarged. Otherwise the filter defaults to a Lanczos.
1951 % ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1953 % The format of the ResizeImage method is:
1955 % Image *ResizeImage(Image *image,const size_t columns,
1956 % const size_t rows,const FilterTypes filter,const double blur,
1957 % ExceptionInfo *exception)
1959 % A description of each parameter follows:
1961 % o image: the image.
1963 % o columns: the number of columns in the scaled image.
1965 % o rows: the number of rows in the scaled image.
1967 % o filter: Image filter to use.
1969 % o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1972 % o exception: return any errors or warnings in this structure.
1976 typedef struct _ContributionInfo
1985 static ContributionInfo **DestroyContributionThreadSet(
1986 ContributionInfo **contribution)
1991 assert(contribution != (ContributionInfo **) NULL);
1992 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
1993 if (contribution[i] != (ContributionInfo *) NULL)
1994 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1996 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
1997 return(contribution);
2000 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
2011 number_threads=GetOpenMPMaximumThreads();
2012 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
2013 sizeof(*contribution));
2014 if (contribution == (ContributionInfo **) NULL)
2015 return((ContributionInfo **) NULL);
2016 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
2017 for (i=0; i < (ssize_t) number_threads; i++)
2019 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2020 sizeof(**contribution));
2021 if (contribution[i] == (ContributionInfo *) NULL)
2022 return(DestroyContributionThreadSet(contribution));
2024 return(contribution);
2027 static inline double MagickMax(const double x,const double y)
2034 static inline double MagickMin(const double x,const double y)
2041 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2042 const Image *image,Image *resize_image,const MagickRealType x_factor,
2043 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2045 #define ResizeImageTag "Resize/Image"
2055 **restrict contributions;
2068 Apply filter to resize horizontally from image to resize image.
2070 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
2071 support=scale*GetResizeFilterSupport(resize_filter);
2072 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2073 if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
2074 return(MagickFalse);
2078 Support too small even for nearest neighbour: Reduce to point sampling.
2080 support=(MagickRealType) 0.5;
2083 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2084 if (contributions == (ContributionInfo **) NULL)
2086 (void) ThrowMagickException(exception,GetMagickModule(),
2087 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2088 return(MagickFalse);
2092 image_view=AcquireCacheView(image);
2093 resize_view=AcquireCacheView(resize_image);
2094 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2095 #pragma omp parallel for shared(status)
2097 for (x=0; x < (ssize_t) resize_image->columns; x++)
2103 register const Quantum
2106 register ContributionInfo
2107 *restrict contribution;
2120 if (status == MagickFalse)
2122 bisect=(MagickRealType) (x+0.5)/x_factor;
2123 start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
2124 stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->columns);
2126 contribution=contributions[GetOpenMPThreadId()];
2127 for (n=0; n < (stop-start); n++)
2129 contribution[n].pixel=start+n;
2130 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2131 ((MagickRealType) (start+n)-bisect+0.5));
2132 density+=contribution[n].weight;
2134 if ((density != 0.0) && (density != 1.0))
2142 density=1.0/density;
2143 for (i=0; i < n; i++)
2144 contribution[i].weight*=density;
2146 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2147 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
2148 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2150 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2155 for (y=0; y < (ssize_t) resize_image->rows; y++)
2160 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2180 channel=GetPixelChannelMapChannel(image,i);
2181 traits=GetPixelChannelMapTraits(image,channel);
2182 resize_traits=GetPixelChannelMapTraits(resize_image,channel);
2183 if ((traits == UndefinedPixelTrait) ||
2184 (resize_traits == UndefinedPixelTrait))
2186 if ((resize_traits & CopyPixelTrait) != 0)
2188 j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
2190 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2191 (contribution[j-start].pixel-contribution[0].pixel);
2192 SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
2197 if ((resize_traits & BlendPixelTrait) == 0)
2202 for (j=0; j < n; j++)
2204 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2205 (contribution[j].pixel-contribution[0].pixel);
2206 alpha=contribution[j].weight;
2207 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2209 SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
2216 for (j=0; j < n; j++)
2218 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2219 (contribution[j].pixel-contribution[0].pixel);
2220 alpha=contribution[j].weight*QuantumScale*
2221 GetPixelAlpha(image,p+k*GetPixelChannels(image));
2222 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2225 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2226 SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
2228 q+=GetPixelChannels(resize_image);
2230 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2232 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2237 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2238 #pragma omp critical (MagickCore_HorizontalFilter)
2240 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2241 if (proceed == MagickFalse)
2245 resize_view=DestroyCacheView(resize_view);
2246 image_view=DestroyCacheView(image_view);
2247 contributions=DestroyContributionThreadSet(contributions);
2251 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2252 const Image *image,Image *resize_image,const MagickRealType y_factor,
2253 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2263 **restrict contributions;
2279 Apply filter to resize vertically from image to resize image.
2281 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
2282 support=scale*GetResizeFilterSupport(resize_filter);
2283 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2284 if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
2285 return(MagickFalse);
2289 Support too small even for nearest neighbour: Reduce to point sampling.
2291 support=(MagickRealType) 0.5;
2294 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2295 if (contributions == (ContributionInfo **) NULL)
2297 (void) ThrowMagickException(exception,GetMagickModule(),
2298 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2299 return(MagickFalse);
2303 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2304 image_view=AcquireCacheView(image);
2305 resize_view=AcquireCacheView(resize_image);
2306 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2307 #pragma omp parallel for shared(status)
2309 for (y=0; y < (ssize_t) resize_image->rows; y++)
2315 register const Quantum
2318 register ContributionInfo
2319 *restrict contribution;
2332 if (status == MagickFalse)
2334 bisect=(MagickRealType) (y+0.5)/y_factor;
2335 start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
2336 stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->rows);
2338 contribution=contributions[GetOpenMPThreadId()];
2339 for (n=0; n < (stop-start); n++)
2341 contribution[n].pixel=start+n;
2342 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2343 ((MagickRealType) (start+n)-bisect+0.5));
2344 density+=contribution[n].weight;
2346 if ((density != 0.0) && (density != 1.0))
2354 density=1.0/density;
2355 for (i=0; i < n; i++)
2356 contribution[i].weight*=density;
2358 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
2359 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2361 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2363 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2368 for (x=0; x < (ssize_t) resize_image->columns; x++)
2373 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2393 channel=GetPixelChannelMapChannel(image,i);
2394 traits=GetPixelChannelMapTraits(image,channel);
2395 resize_traits=GetPixelChannelMapTraits(resize_image,channel);
2396 if ((traits == UndefinedPixelTrait) ||
2397 (resize_traits == UndefinedPixelTrait))
2399 if ((resize_traits & CopyPixelTrait) != 0)
2401 j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
2403 k=(ssize_t) ((contribution[j-start].pixel-contribution[0].pixel)*
2405 SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
2410 if ((resize_traits & BlendPixelTrait) == 0)
2415 for (j=0; j < n; j++)
2417 k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
2419 alpha=contribution[j].weight;
2420 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2422 SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
2426 for (j=0; j < n; j++)
2428 k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
2430 alpha=contribution[j].weight*QuantumScale*
2431 GetPixelAlpha(image,p+k*GetPixelChannels(image));
2432 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2435 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2436 SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
2438 q+=GetPixelChannels(resize_image);
2440 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2442 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2447 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2448 #pragma omp critical (MagickCore_VerticalFilter)
2450 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2451 if (proceed == MagickFalse)
2455 resize_view=DestroyCacheView(resize_view);
2456 image_view=DestroyCacheView(image_view);
2457 contributions=DestroyContributionThreadSet(contributions);
2461 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2462 const size_t rows,const FilterTypes filter,const double blur,
2463 ExceptionInfo *exception)
2465 #define WorkLoadFactor 0.265
2491 Acquire resize image.
2493 assert(image != (Image *) NULL);
2494 assert(image->signature == MagickSignature);
2495 if (image->debug != MagickFalse)
2496 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2497 assert(exception != (ExceptionInfo *) NULL);
2498 assert(exception->signature == MagickSignature);
2499 if ((columns == 0) || (rows == 0))
2500 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2501 if ((columns == image->columns) && (rows == image->rows) &&
2502 (filter == UndefinedFilter) && (blur == 1.0))
2503 return(CloneImage(image,0,0,MagickTrue,exception));
2504 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2505 if (resize_image == (Image *) NULL)
2506 return(resize_image);
2508 Acquire resize filter.
2510 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2511 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2512 if ((x_factor*y_factor) > WorkLoadFactor)
2513 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2515 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2516 if (filter_image == (Image *) NULL)
2517 return(DestroyImage(resize_image));
2518 filter_type=LanczosFilter;
2519 if (filter != UndefinedFilter)
2522 if ((x_factor == 1.0) && (y_factor == 1.0))
2523 filter_type=PointFilter;
2525 if ((image->storage_class == PseudoClass) ||
2526 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2527 filter_type=MitchellFilter;
2528 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2534 if ((x_factor*y_factor) > WorkLoadFactor)
2536 span=(MagickSizeType) (filter_image->columns+rows);
2537 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
2539 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
2540 span,&offset,exception);
2544 span=(MagickSizeType) (filter_image->rows+columns);
2545 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
2547 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
2548 span,&offset,exception);
2553 filter_image=DestroyImage(filter_image);
2554 resize_filter=DestroyResizeFilter(resize_filter);
2555 if (status == MagickFalse)
2557 resize_image=DestroyImage(resize_image);
2558 return((Image *) NULL);
2560 resize_image->type=image->type;
2561 return(resize_image);
2565 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2569 % S a m p l e I m a g e %
2573 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2575 % SampleImage() scales an image to the desired dimensions with pixel
2576 % sampling. Unlike other scaling methods, this method does not introduce
2577 % any additional color into the scaled image.
2579 % The format of the SampleImage method is:
2581 % Image *SampleImage(const Image *image,const size_t columns,
2582 % const size_t rows,ExceptionInfo *exception)
2584 % A description of each parameter follows:
2586 % o image: the image.
2588 % o columns: the number of columns in the sampled image.
2590 % o rows: the number of rows in the sampled image.
2592 % o exception: return any errors or warnings in this structure.
2595 MagickExport Image *SampleImage(const Image *image,const size_t columns,
2596 const size_t rows,ExceptionInfo *exception)
2598 #define SampleImageTag "Sample/Image"
2621 Initialize sampled image attributes.
2623 assert(image != (const Image *) NULL);
2624 assert(image->signature == MagickSignature);
2625 if (image->debug != MagickFalse)
2626 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2627 assert(exception != (ExceptionInfo *) NULL);
2628 assert(exception->signature == MagickSignature);
2629 if ((columns == 0) || (rows == 0))
2630 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2631 if ((columns == image->columns) && (rows == image->rows))
2632 return(CloneImage(image,0,0,MagickTrue,exception));
2633 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2634 if (sample_image == (Image *) NULL)
2635 return((Image *) NULL);
2637 Allocate scan line buffer and column offset buffers.
2639 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
2641 if (x_offset == (ssize_t *) NULL)
2643 sample_image=DestroyImage(sample_image);
2644 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2646 for (x=0; x < (ssize_t) sample_image->columns; x++)
2647 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
2648 sample_image->columns);
2654 image_view=AcquireCacheView(image);
2655 sample_view=AcquireCacheView(sample_image);
2656 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2657 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
2659 for (y=0; y < (ssize_t) sample_image->rows; y++)
2661 register const Quantum
2673 if (status == MagickFalse)
2675 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2676 sample_image->rows);
2677 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2679 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2681 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2689 for (x=0; x < (ssize_t) sample_image->columns; x++)
2694 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2703 channel=GetPixelChannelMapChannel(image,i);
2704 traits=GetPixelChannelMapTraits(image,channel);
2705 sample_traits=GetPixelChannelMapTraits(sample_image,channel);
2706 if ((traits == UndefinedPixelTrait) ||
2707 (sample_traits == UndefinedPixelTrait))
2709 SetPixelChannel(sample_image,channel,p[x_offset[x]*GetPixelChannels(
2712 q+=GetPixelChannels(sample_image);
2714 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2716 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2721 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2722 #pragma omp critical (MagickCore_SampleImage)
2724 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2725 if (proceed == MagickFalse)
2729 image_view=DestroyCacheView(image_view);
2730 sample_view=DestroyCacheView(sample_view);
2731 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
2732 sample_image->type=image->type;
2733 return(sample_image);
2737 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2741 % S c a l e I m a g e %
2745 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2747 % ScaleImage() changes the size of an image to the given dimensions.
2749 % The format of the ScaleImage method is:
2751 % Image *ScaleImage(const Image *image,const size_t columns,
2752 % const size_t rows,ExceptionInfo *exception)
2754 % A description of each parameter follows:
2756 % o image: the image.
2758 % o columns: the number of columns in the scaled image.
2760 % o rows: the number of rows in the scaled image.
2762 % o exception: return any errors or warnings in this structure.
2765 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2766 const size_t rows,ExceptionInfo *exception)
2768 #define ScaleImageTag "Scale/Image"
2785 pixel[CompositePixelChannel],
2811 Initialize scaled image attributes.
2813 assert(image != (const Image *) NULL);
2814 assert(image->signature == MagickSignature);
2815 if (image->debug != MagickFalse)
2816 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2817 assert(exception != (ExceptionInfo *) NULL);
2818 assert(exception->signature == MagickSignature);
2819 if ((columns == 0) || (rows == 0))
2820 return((Image *) NULL);
2821 if ((columns == image->columns) && (rows == image->rows))
2822 return(CloneImage(image,0,0,MagickTrue,exception));
2823 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2824 if (scale_image == (Image *) NULL)
2825 return((Image *) NULL);
2826 if (SetImageStorageClass(scale_image,DirectClass,exception) == MagickFalse)
2828 scale_image=DestroyImage(scale_image);
2829 return((Image *) NULL);
2834 x_vector=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
2835 GetPixelChannels(image)*sizeof(*x_vector));
2837 if (image->rows != scale_image->rows)
2838 scanline=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
2839 GetPixelChannels(image)*sizeof(*scanline));
2840 scale_scanline=(MagickRealType *) AcquireQuantumMemory((size_t)
2841 scale_image->columns,GetPixelChannels(image)*sizeof(*scale_scanline));
2842 y_vector=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
2843 GetPixelChannels(image)*sizeof(*y_vector));
2844 if ((scanline == (MagickRealType *) NULL) ||
2845 (scale_scanline == (MagickRealType *) NULL) ||
2846 (x_vector == (MagickRealType *) NULL) ||
2847 (y_vector == (MagickRealType *) NULL))
2849 scale_image=DestroyImage(scale_image);
2850 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2856 next_row=MagickTrue;
2858 scale.y=(double) scale_image->rows/(double) image->rows;
2859 for (i=0; i < (ssize_t) (GetPixelChannels(image)*image->columns); i++)
2862 image_view=AcquireCacheView(image);
2863 scale_view=AcquireCacheView(scale_image);
2864 for (y=0; y < (ssize_t) scale_image->rows; y++)
2866 register const Quantum
2875 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2877 if (q == (Quantum *) NULL)
2880 if (scale_image->rows == image->rows)
2883 Read a new scanline.
2885 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
2887 if (p == (const Quantum *) NULL)
2889 for (x=0; x < (ssize_t) image->columns; x++)
2891 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2899 channel=GetPixelChannelMapChannel(image,i);
2900 traits=GetPixelChannelMapTraits(image,channel);
2901 if ((traits & BlendPixelTrait) == 0)
2903 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType) p[i];
2906 alpha=QuantumScale*GetPixelAlpha(image,p);
2907 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
2909 p+=GetPixelChannels(image);
2917 while (scale.y < span.y)
2919 if ((next_row != MagickFalse) &&
2920 (number_rows < (ssize_t) image->rows))
2923 Read a new scanline.
2925 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
2927 if (p == (const Quantum *) NULL)
2929 for (x=0; x < (ssize_t) image->columns; x++)
2931 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2939 channel=GetPixelChannelMapChannel(image,i);
2940 traits=GetPixelChannelMapTraits(image,channel);
2941 if ((traits & BlendPixelTrait) == 0)
2943 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType)
2947 alpha=QuantumScale*GetPixelAlpha(image,p);
2948 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
2950 p+=GetPixelChannels(image);
2954 for (x=0; x < (ssize_t) image->columns; x++)
2955 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2956 y_vector[x*GetPixelChannels(image)+i]+=scale.y*
2957 x_vector[x*GetPixelChannels(image)+i];
2959 scale.y=(double) scale_image->rows/(double) image->rows;
2960 next_row=MagickTrue;
2962 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
2965 Read a new scanline.
2967 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
2969 if (p == (const Quantum *) NULL)
2971 for (x=0; x < (ssize_t) image->columns; x++)
2973 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2981 channel=GetPixelChannelMapChannel(image,i);
2982 traits=GetPixelChannelMapTraits(image,channel);
2983 if ((traits & BlendPixelTrait) == 0)
2985 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType)
2989 alpha=QuantumScale*GetPixelAlpha(image,p);
2990 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
2992 p+=GetPixelChannels(image);
2995 next_row=MagickFalse;
2997 for (x=0; x < (ssize_t) image->columns; x++)
2999 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3001 pixel[i]=y_vector[x*GetPixelChannels(image)+i]+span.y*
3002 x_vector[x*GetPixelChannels(image)+i];
3003 scanline[x*GetPixelChannels(image)+i]=pixel[i];
3004 y_vector[x*GetPixelChannels(image)+i]=0.0;
3010 scale.y=(double) scale_image->rows/(double) image->rows;
3011 next_row=MagickTrue;
3015 if (scale_image->columns == image->columns)
3018 Transfer scanline to scaled image.
3020 for (x=0; x < (ssize_t) scale_image->columns; x++)
3022 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3024 channel=GetPixelChannelMapChannel(image,i);
3025 traits=GetPixelChannelMapTraits(image,channel);
3026 scale_traits=GetPixelChannelMapTraits(scale_image,channel);
3027 if ((traits == UndefinedPixelTrait) ||
3028 (scale_traits == UndefinedPixelTrait))
3030 if ((scale_traits & BlendPixelTrait) == 0)
3032 SetPixelChannel(scale_image,channel,ClampToQuantum(scanline[
3033 x*GetPixelChannels(image)+i]),q);
3036 alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
3037 GetPixelChannelMapChannel(image,AlphaPixelChannel)];
3038 gamma=1.0/(fabs((double) alpha) <= MagickEpsilon ? 1.0 : alpha);
3039 SetPixelChannel(scale_image,channel,ClampToQuantum(gamma*scanline[
3040 x*GetPixelChannels(image)+i]),q);
3042 q+=GetPixelChannels(scale_image);
3053 next_column=MagickFalse;
3056 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3058 for (x=0; x < (ssize_t) image->columns; x++)
3060 scale.x=(double) scale_image->columns/(double) image->columns;
3061 while (scale.x >= span.x)
3063 if (next_column != MagickFalse)
3065 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3069 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3077 channel=GetPixelChannelMapChannel(image,i);
3078 traits=GetPixelChannelMapTraits(image,channel);
3079 if (traits == UndefinedPixelTrait)
3081 pixel[i]+=span.x*scanline[x*GetPixelChannels(image)+i];
3082 scale_scanline[n*GetPixelChannels(image)+i]=pixel[i];
3086 next_column=MagickTrue;
3090 if (next_column != MagickFalse)
3092 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3095 next_column=MagickFalse;
3097 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3098 pixel[i]+=scale.x*scanline[x*GetPixelChannels(image)+i];
3104 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3105 pixel[i]+=span.x*scanline[(x-1)*GetPixelChannels(image)+i];
3107 if ((next_column == MagickFalse) &&
3108 ((ssize_t) n < (ssize_t) scale_image->columns))
3109 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3110 scale_scanline[n*GetPixelChannels(image)+i]=pixel[i];
3112 Transfer scanline to scaled image.
3114 for (x=0; x < (ssize_t) scale_image->columns; x++)
3116 for (i=0; i < (ssize_t) GetPixelChannels(scale_image); i++)
3118 channel=GetPixelChannelMapChannel(image,i);
3119 traits=GetPixelChannelMapTraits(image,channel);
3120 scale_traits=GetPixelChannelMapTraits(scale_image,channel);
3121 if ((traits == UndefinedPixelTrait) ||
3122 (scale_traits == UndefinedPixelTrait))
3124 if ((scale_traits & BlendPixelTrait) == 0)
3126 SetPixelChannel(scale_image,channel,ClampToQuantum(
3127 scale_scanline[x*GetPixelChannels(image)+i]),q);
3130 alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
3131 GetPixelChannelMapChannel(image,AlphaPixelChannel)];
3132 gamma=1.0/(fabs((double) alpha) <= MagickEpsilon ? 1.0 : alpha);
3133 SetPixelChannel(scale_image,channel,ClampToQuantum(gamma*
3134 scale_scanline[x*GetPixelChannels(image)+i]),q);
3136 q+=GetPixelChannels(scale_image);
3139 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
3141 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3143 if (proceed == MagickFalse)
3146 scale_view=DestroyCacheView(scale_view);
3147 image_view=DestroyCacheView(image_view);
3149 Free allocated memory.
3151 y_vector=(MagickRealType *) RelinquishMagickMemory(y_vector);
3152 scale_scanline=(MagickRealType *) RelinquishMagickMemory(scale_scanline);
3153 if (scale_image->rows != image->rows)
3154 scanline=(MagickRealType *) RelinquishMagickMemory(scanline);
3155 x_vector=(MagickRealType *) RelinquishMagickMemory(x_vector);
3156 scale_image->type=image->type;
3157 return(scale_image);
3161 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3165 % T h u m b n a i l I m a g e %
3169 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3171 % ThumbnailImage() changes the size of an image to the given dimensions and
3172 % removes any associated profiles. The goal is to produce small low cost
3173 % thumbnail images suited for display on the Web.
3175 % The format of the ThumbnailImage method is:
3177 % Image *ThumbnailImage(const Image *image,const size_t columns,
3178 % const size_t rows,ExceptionInfo *exception)
3180 % A description of each parameter follows:
3182 % o image: the image.
3184 % o columns: the number of columns in the scaled image.
3186 % o rows: the number of rows in the scaled image.
3188 % o exception: return any errors or warnings in this structure.
3191 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3192 const size_t rows,ExceptionInfo *exception)
3194 #define SampleFactor 5
3197 value[MaxTextExtent];
3215 assert(image != (Image *) NULL);
3216 assert(image->signature == MagickSignature);
3217 if (image->debug != MagickFalse)
3218 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3219 assert(exception != (ExceptionInfo *) NULL);
3220 assert(exception->signature == MagickSignature);
3221 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3222 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3223 if ((x_factor*y_factor) > 0.1)
3224 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3227 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
3228 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3229 image->blur,exception);
3235 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3237 if (sample_image == (Image *) NULL)
3238 return((Image *) NULL);
3239 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3240 image->blur,exception);
3241 sample_image=DestroyImage(sample_image);
3243 if (thumbnail_image == (Image *) NULL)
3244 return(thumbnail_image);
3245 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3246 if (thumbnail_image->matte == MagickFalse)
3247 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel,exception);
3248 thumbnail_image->depth=8;
3249 thumbnail_image->interlace=NoInterlace;
3251 Strip all profiles except color profiles.
3253 ResetImageProfileIterator(thumbnail_image);
3254 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3256 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3258 (void) DeleteImageProfile(thumbnail_image,name);
3259 ResetImageProfileIterator(thumbnail_image);
3261 name=GetNextImageProfile(thumbnail_image);
3263 (void) DeleteImageProperty(thumbnail_image,"comment");
3264 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3265 if (strstr(image->magick_filename,"//") == (char *) NULL)
3266 (void) FormatLocaleString(value,MaxTextExtent,"file://%s",
3267 image->magick_filename);
3268 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value,exception);
3269 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3270 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3272 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3273 attributes.st_mtime);
3274 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value,exception);
3276 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3277 attributes.st_mtime);
3278 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
3279 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
3280 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value,exception);
3281 (void) FormatLocaleString(value,MaxTextExtent,"image/%s",image->magick);
3283 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value,exception);
3284 (void) SetImageProperty(thumbnail_image,"software",GetMagickVersion(&version),
3286 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3287 image->magick_columns);
3288 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value,
3290 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3291 image->magick_rows);
3292 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value,
3294 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3295 GetImageListLength(image));
3296 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value,
3298 return(thumbnail_image);