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 *magick_unused(resize_filter))
292 #define I0A (1.0/I0(Alpha))
295 Kaiser Windowing Function (bessel windowing): Alpha is a free value
296 from 5 to 8 (currently hardcoded to 6.5). Future: make alpha the IOA
297 pre-calculation, an 'expert' setting.
299 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
302 static MagickRealType Lagrange(const MagickRealType x,
303 const ResizeFilter *resize_filter)
316 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the lagrange
317 function and depends on the overall support window size of the filter. That
318 is: for a support of 2, it gives a lagrange-4 (piecewise cubic function).
320 "n" identifies the piece of the piecewise polynomial.
322 See Survey: Interpolation Methods, IEEE Transactions on Medical Imaging,
323 Vol 18, No 11, November 1999, p1049-1075, -- Equation 27 on p1064.
325 if (x > resize_filter->support)
327 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
328 n=(ssize_t) (resize_filter->window_support+x);
330 for (i=0; i < order; i++)
332 value*=(n-i-x)/(n-i);
336 static MagickRealType Quadratic(const MagickRealType x,
337 const ResizeFilter *magick_unused(resize_filter))
340 2rd order (quadratic) B-Spline approximation of Gaussian.
345 return(0.5*(x-1.5)*(x-1.5));
349 static MagickRealType Sinc(const MagickRealType x,
350 const ResizeFilter *magick_unused(resize_filter))
353 Scaled sinc(x) function using a trig call:
354 sinc(x) == sin(pi x)/(pi x).
358 const MagickRealType alpha=(MagickRealType) (MagickPIL*x);
359 return(sin((double) alpha)/alpha);
361 return((MagickRealType) 1.0);
364 static MagickRealType SincFast(const MagickRealType x,
365 const ResizeFilter *magick_unused(resize_filter))
368 Approximations of the sinc function sin(pi x)/(pi x) over the interval
369 [-4,4] constructed by Nicolas Robidoux and Chantal Racette with funding
370 from the Natural Sciences and Engineering Research Council of Canada.
372 Although the approximations are polynomials (for low order of
373 approximation) and quotients of polynomials (for higher order of
374 approximation) and consequently are similar in form to Taylor polynomials /
375 Pade approximants, the approximations are computed with a completely
378 Summary: These approximations are "the best" in terms of bang (accuracy)
379 for the buck (flops). More specifically: Among the polynomial quotients
380 that can be computed using a fixed number of flops (with a given "+ - * /
381 budget"), the chosen polynomial quotient is the one closest to the
382 approximated function with respect to maximum absolute relative error over
385 The Remez algorithm, as implemented in the boost library's minimax package,
386 is the key to the construction: http://www.boost.org/doc/libs/1_36_0/libs/
387 math/doc/sf_and_dist/html/math_toolkit/backgrounders/remez.html
389 If outside of the interval of approximation, use the standard trig formula.
393 const MagickRealType alpha=(MagickRealType) (MagickPIL*x);
394 return(sin((double) alpha)/alpha);
398 The approximations only depend on x^2 (sinc is an even function).
400 const MagickRealType xx = x*x;
401 #if MAGICKCORE_QUANTUM_DEPTH <= 8
403 Maximum absolute relative error 6.3e-6 < 1/2^17.
405 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
406 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
407 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
408 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
409 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
410 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
411 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
412 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
413 const MagickRealType p =
414 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
415 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
416 #elif MAGICKCORE_QUANTUM_DEPTH <= 16
418 Max. abs. rel. error 2.2e-8 < 1/2^25.
420 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
421 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
422 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
423 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
424 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
425 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
426 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
427 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
428 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
429 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
430 const MagickRealType p =
431 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
432 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
435 Max. abs. rel. error 1.2e-12 < 1/2^39.
437 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
438 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
439 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
440 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
441 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
442 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
443 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
444 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
445 const MagickRealType p =
446 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
447 const MagickRealType d0 = 1.0L;
448 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
449 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
450 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
451 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
452 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
453 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
458 static MagickRealType Triangle(const MagickRealType x,
459 const ResizeFilter *magick_unused(resize_filter))
462 1st order (linear) B-Spline, bilinear interpolation, Tent 1D filter, or
463 a Bartlett 2D Cone filter. Also used as a Bartlett Windowing function
471 static MagickRealType Welsh(const MagickRealType x,
472 const ResizeFilter *magick_unused(resize_filter))
475 Welsh parabolic windowing filter.
483 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
487 + A c q u i r e R e s i z e F i l t e r %
491 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
493 % AcquireResizeFilter() allocates the ResizeFilter structure. Choose from
496 % FIR (Finite impulse Response) Filters
497 % Box Triangle Quadratic Cubic Hermite Catrom Mitchell
499 % IIR (Infinite impulse Response) Filters
500 % Gaussian Sinc Jinc (Bessel)
502 % Windowed Sinc/Jinc Filters
503 % Blackman Hanning Hamming Kaiser Lanczos
505 % Special purpose Filters
506 % SincFast LanczosSharp Lanczos2D Lanczos2DSharp Robidoux
508 % The users "-filter" selection is used to lookup the default 'expert'
509 % settings for that filter from a internal table. However any provided
510 % 'expert' settings (see below) may override this selection.
512 % FIR filters are used as is, and are limited to that filters support window
513 % (unless over-ridden). 'Gaussian' while classed as an IIR filter, is also
514 % simply clipped by its support size (currently 1.5 or approximatally 3*sigma
515 % as recommended by many references)
517 % The special a 'cylindrical' filter flag will promote the default 4-lobed
518 % Windowed Sinc filter to a 3-lobed Windowed Jinc equivalent, which is better
519 % suited to this style of image resampling. This typically happens when using
520 % such a filter for images distortions.
522 % Directly requesting 'Sinc', 'Jinc' function as a filter will force the use
523 % of function without any windowing, or promotion for cylindrical usage. This
524 % is not recommended, except by image processing experts, especially as part
525 % of expert option filter function selection.
527 % Two forms of the 'Sinc' function are available: Sinc and SincFast. Sinc is
528 % computed using the traditional sin(pi*x)/(pi*x); it is selected if the user
529 % specifically specifies the use of a Sinc filter. SincFast uses highly
530 % accurate (and fast) polynomial (low Q) and rational (high Q) approximations,
531 % and will be used by default in most cases.
533 % The Lanczos filter is a special 3-lobed Sinc-windowed Sinc filter (promoted
534 % to Jinc-windowed Jinc for cylindrical (Elliptical Weighted Average) use).
535 % The Sinc version is the most popular windowed filter.
537 % LanczosSharp is a slightly sharpened (blur=0.9812505644269356 < 1) form of
538 % the Lanczos filter, specifically designed for EWA distortion (as a
539 % Jinc-Jinc); it can also be used as a slightly sharper orthogonal Lanczos
540 % (Sinc-Sinc) filter. The chosen blur value comes as close as possible to
541 % satisfying the following condition without changing the character of the
542 % corresponding EWA filter:
544 % 'No-Op' Vertical and Horizontal Line Preservation Condition: Images with
545 % only vertical or horizontal features are preserved when performing 'no-op"
546 % with EWA distortion.
548 % The Lanczos2 and Lanczos2Sharp filters are 2-lobe versions of the Lanczos
549 % filters. The 'sharp' version uses a blur factor of 0.9549963639785485,
550 % again chosen because the resulting EWA filter comes as close as possible to
551 % satisfying the above condition.
553 % Robidoux is another filter tuned for EWA. It is the Keys cubic filter
554 % defined by B=(228 - 108 sqrt(2))/199. Robidoux satisfies the "'No-Op'
555 % Vertical and Horizontal Line Preservation Condition" exactly, and it
556 % moderately blurs high frequency 'pixel-hash' patterns under no-op. It turns
557 % out to be close to both Mitchell and Lanczos2Sharp. For example, its first
558 % crossing is at (36 sqrt(2) + 123)/(72 sqrt(2) + 47), almost the same as the
559 % first crossing of Mitchell and Lanczos2Sharp.
563 % These artifact "defines" are not recommended for production use without
564 % expert knowledge of resampling, filtering, and the effects they have on the
565 % resulting resampled (resize ro distorted) image.
567 % They can be used to override any and all filter default, and it is
568 % recommended you make good use of "filter:verbose" to make sure that the
569 % overall effect of your selection (before and after) is as expected.
571 % "filter:verbose" controls whether to output the exact results of the
572 % filter selections made, as well as plotting data for graphing the
573 % resulting filter over the filters support range.
575 % "filter:filter" select the main function associated with this filter
576 % name, as the weighting function of the filter. This can be used to
577 % set a windowing function as a weighting function, for special
578 % purposes, such as graphing.
580 % If a "filter:window" operation has not been provided, a 'Box'
581 % windowing function will be set to denote that no windowing function is
584 % "filter:window" Select this windowing function for the filter. While any
585 % filter could be used as a windowing function, using the 'first lobe' of
586 % that filter over the whole support window, using a non-windowing
587 % function is not advisible. If no weighting filter function is specifed
588 % a 'SincFast' filter is used.
590 % "filter:lobes" Number of lobes to use for the Sinc/Jinc filter. This a
591 % simpler method of setting filter support size that will correctly
592 % handle the Sinc/Jinc switch for an operators filtering requirements.
593 % Only integers should be given.
595 % "filter:support" Set the support size for filtering to the size given.
596 % This not recommended for Sinc/Jinc windowed filters (lobes should be
597 % used instead). This will override any 'filter:lobes' option.
599 % "filter:win-support" Scale windowing function to this size instead. This
600 % causes the windowing (or self-windowing Lagrange filter) to act is if
601 % the support window it much much larger than what is actually supplied
602 % to the calling operator. The filter however is still clipped to the
603 % real support size given, by the support range suppiled to the caller.
604 % If unset this will equal the normal filter support size.
606 % "filter:blur" Scale the filter and support window by this amount. A value
607 % > 1 will generally result in a more burred image with more ringing
608 % effects, while a value <1 will sharpen the resulting image with more
611 % "filter:sigma" The sigma value to use for the Gaussian filter only.
612 % Defaults to '1/2'. Using a different sigma effectively provides a
613 % method of using the filter as a 'blur' convolution. Particularly when
614 % using it for Distort.
617 % "filter:c" Override the preset B,C values for a Cubic type of filter.
618 % If only one of these are given it is assumes to be a 'Keys' type of
619 % filter such that B+2C=1, where Keys 'alpha' value = C.
623 % Set a true un-windowed Sinc filter with 10 lobes (very slow):
624 % -define filter:filter=Sinc
625 % -define filter:lobes=8
627 % Set an 8 lobe Lanczos (Sinc or Jinc) filter:
629 % -define filter:lobes=8
631 % The format of the AcquireResizeFilter method is:
633 % ResizeFilter *AcquireResizeFilter(const Image *image,
634 % const FilterTypes filter_type, const MagickBooleanType radial,
635 % ExceptionInfo *exception)
637 % A description of each parameter follows:
639 % o image: the image.
641 % o filter: the filter type, defining a preset filter, window and support.
642 % The artifact settings listed above will override those selections.
644 % o blur: blur the filter by this amount, use 1.0 if unknown. Image
645 % artifact "filter:blur" will override this API call usage, including any
646 % internal change (such as for cylindrical usage).
648 % o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical (radial)
651 % o exception: return any errors or warnings in this structure.
654 MagickPrivate ResizeFilter *AcquireResizeFilter(const Image *image,
655 const FilterTypes filter,const MagickRealType blur,
656 const MagickBooleanType cylindrical,ExceptionInfo *exception)
670 register ResizeFilter
674 Table Mapping given Filter, into Weighting and Windowing functions. A
675 'Box' windowing function means its a simble non-windowed filter. An
676 'SincFast' filter function could be upgraded to a 'Jinc' filter if a
677 "cylindrical", unless a 'Sinc' or 'SincFast' filter was specifically
680 WARNING: The order of this tabel must match the order of the FilterTypes
681 enumeration specified in "resample.h", or the filter names will not match
682 the filter being setup.
684 You can check filter setups with the "filter:verbose" setting.
691 } const mapping[SentinelFilter] =
693 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
694 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
695 { BoxFilter, BoxFilter }, /* Box averaging filter */
696 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
697 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
698 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
699 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
700 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
701 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
702 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approx */
703 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
704 { CatromFilter, BoxFilter }, /* Cubic-Keys interpolator */
705 { MitchellFilter, BoxFilter }, /* 'Ideal' Cubic-Keys filter */
706 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
707 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
708 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
709 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
710 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
711 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
712 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
713 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
714 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing */
715 { LanczosFilter, LanczosFilter }, /* Lanczos Sinc-Sinc filters */
716 { LanczosSharpFilter, LanczosSharpFilter }, /* | these require */
717 { Lanczos2Filter, Lanczos2Filter }, /* | special handling */
718 { Lanczos2SharpFilter, Lanczos2SharpFilter },
719 { RobidouxFilter, BoxFilter }, /* Cubic Keys tuned for EWA */
722 Table mapping the filter/window from the above table to an actual function.
723 The default support size for that filter as a weighting function, the range
724 to scale with to use that function as a sinc windowing function, (typ 1.0).
726 Note that the filter_type -> function is 1 to 1 except for Sinc(),
727 SincFast(), and CubicBC() functions, which may have multiple filter to
728 function associations.
730 See "filter:verbose" handling below for the function -> filter mapping.
735 (*function)(const MagickRealType,const ResizeFilter*),
736 lobes, /* Default lobes/support size of the weighting filter. */
737 scale, /* Support when function used as a windowing function. Typically
738 equal to the location of the first zero crossing. */
740 C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
741 } const filters[SentinelFilter] =
743 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
744 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
745 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
746 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
747 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
748 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
749 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
750 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
751 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
752 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
753 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
754 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
755 { CubicBC, 2.0, 8.0/7.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
756 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
757 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
758 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
759 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
760 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
761 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
762 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
763 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
764 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
765 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
766 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* lanczos, Sharpened */
767 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos, 2-lobed */
768 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2, sharpened */
769 { CubicBC, 2.0, 1.1685777620836932, 0.37821575509399867,
770 0.31089212245300067 }
771 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
774 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
775 function being used as a filter. It is used by the "filter:lobes" expert
776 setting and for 'lobes' for Jinc functions in the previous table. This way
777 users do not have to deal with the highly irrational lobe sizes of the Jinc
780 Values taken from http://cose.math.bas.bg/webMathematica/webComputing/
781 BesselZeros.jsp using Jv-function with v=1, then dividing by PI.
783 static MagickRealType
805 Allocate resize filter.
807 assert(image != (const Image *) NULL);
808 assert(image->signature == MagickSignature);
809 if (image->debug != MagickFalse)
810 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
811 assert(UndefinedFilter < filter && filter < SentinelFilter);
812 assert(exception != (ExceptionInfo *) NULL);
813 assert(exception->signature == MagickSignature);
814 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
815 if (resize_filter == (ResizeFilter *) NULL)
816 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
817 (void) ResetMagickMemory(resize_filter,0,sizeof(*resize_filter));
819 Defaults for the requested filter.
821 filter_type=mapping[filter].filter;
822 window_type=mapping[filter].window;
823 resize_filter->blur = blur; /* function argument blur factor */
824 sigma=0.5; /* gaussian sigma of half a pixel by default */
825 if ((cylindrical != MagickFalse) && (filter_type == SincFastFilter) &&
826 (filter != SincFastFilter))
827 filter_type=JincFilter; /* 1D Windowed Sinc => 2D Windowed Jinc filters */
828 artifact=GetImageArtifact(image,"filter:filter");
829 if (artifact != (const char *) NULL)
835 Expert filter setting override.
837 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
838 if ((UndefinedFilter < option) && (option < SentinelFilter))
841 Raw filter request - no window function.
843 filter_type=(FilterTypes) option;
844 window_type=BoxFilter;
846 artifact=GetImageArtifact(image,"filter:window");
847 if (artifact != (const char *) NULL)
850 Filter override with a specific window function.
852 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
853 if ((UndefinedFilter < option) && (option < SentinelFilter))
854 window_type=(FilterTypes) option;
859 artifact=GetImageArtifact(image,"filter:window");
860 if (artifact != (const char *) NULL)
866 Window specified, but no filter function? Assume Sinc/Jinc.
868 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
869 if ((UndefinedFilter < option) && (option < SentinelFilter))
871 filter_type=cylindrical != MagickFalse ? JincFilter :
873 window_type=(FilterTypes) option;
878 Assign the real functions to use for the filters selected.
880 resize_filter->filter=filters[filter_type].function;
881 resize_filter->support=filters[filter_type].lobes;
882 resize_filter->window=filters[window_type].function;
883 resize_filter->scale=filters[window_type].scale;
884 resize_filter->signature=MagickSignature;
886 Filter Modifications for orthogonal/cylindrical usage.
888 if (cylindrical != MagickFalse)
894 Support for Cylindrical Box should be sqrt(2)/2.
896 resize_filter->support=(MagickRealType) MagickSQ1_2;
900 case LanczosSharpFilter:
902 case Lanczos2SharpFilter:
905 Number of lobes (support window size) remain unchanged.
907 resize_filter->filter=filters[JincFilter].function;
908 resize_filter->window=filters[JincFilter].function;
909 resize_filter->scale=filters[JincFilter].scale;
916 Global Sharpening (regardless of orthoginal/cylindrical).
920 case LanczosSharpFilter:
922 resize_filter->blur*=0.9812505644269356;
925 case Lanczos2SharpFilter:
927 resize_filter->blur*=0.9549963639785485;
933 artifact=GetImageArtifact(image,"filter:sigma");
934 if (artifact != (const char *) NULL)
935 sigma=StringToDouble(artifact,(char **) NULL); /* override sigma */
939 Define coefficents for Gaussian.
941 resize_filter->coefficient[0]=1.0/(2.0*sigma*sigma);
942 resize_filter->coefficient[1]=(MagickRealType) (1.0/(Magick2PI*sigma*
943 sigma)); /* normalization multiplier - unneeded for filters */
945 artifact=GetImageArtifact(image,"filter:blur");
946 if (artifact != (const char *) NULL)
947 resize_filter->blur*=StringToDouble(artifact,(char **) NULL); /* override blur */
948 if (resize_filter->blur < MagickEpsilon)
949 resize_filter->blur=(MagickRealType) MagickEpsilon;
950 artifact=GetImageArtifact(image,"filter:lobes");
951 if (artifact != (const char *) NULL)
959 lobes=(ssize_t) StringToLong(artifact);
962 resize_filter->support=(MagickRealType) lobes;
964 if (resize_filter->filter == Jinc)
967 Convert a Jinc function lobes value to a real support value.
969 if (resize_filter->support > 16)
970 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
972 resize_filter->support=jinc_zeros[((long)resize_filter->support)-1];
974 artifact=GetImageArtifact(image,"filter:support");
975 if (artifact != (const char *) NULL)
976 resize_filter->support=fabs(StringToDouble(artifact,(char **) NULL)); /* override support */
978 Scale windowing function separately to the support 'clipping' window that
979 calling operator is planning to actually use (expert override).
981 resize_filter->window_support=resize_filter->support; /* default */
982 artifact=GetImageArtifact(image,"filter:win-support");
983 if (artifact != (const char *) NULL)
984 resize_filter->window_support=fabs(StringToDouble(artifact,(char **) NULL));
986 Adjust window function scaling to match windowing support for weighting
987 function. This avoids a division on every filter call.
989 resize_filter->scale/=resize_filter->window_support;
991 Set Cubic Spline B,C values, calculate cubic coefficients.
995 if ((filters[filter_type].function == CubicBC) ||
996 (filters[window_type].function == CubicBC))
998 B=filters[filter_type].B;
999 C=filters[filter_type].C;
1000 if (filters[window_type].function == CubicBC)
1002 B=filters[window_type].B;
1003 C=filters[window_type].C;
1005 artifact=GetImageArtifact(image,"filter:b");
1006 if (artifact != (const char *) NULL)
1008 B=StringToDouble(artifact,(char **) NULL);
1009 C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
1010 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
1011 if (artifact != (const char *) NULL)
1012 C=StringToDouble(artifact,(char **) NULL);
1016 artifact=GetImageArtifact(image,"filter:c");
1017 if (artifact != (const char *) NULL)
1019 C=StringToDouble(artifact,(char **) NULL);
1020 B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
1028 Convert B,C values into Cubic Coefficents. See CubicBC().
1031 resize_filter->coefficient[0]=1.0-(1.0/3.0)*B;
1032 resize_filter->coefficient[1]=-3.0+B_squared+C;
1033 resize_filter->coefficient[2]=2.0-1.5*B-C;
1034 resize_filter->coefficient[3]=(4.0/3.0)*B+4.0*C;
1035 resize_filter->coefficient[4]=-8.0*C-B_squared;
1036 resize_filter->coefficient[5]=B+5.0*C;
1037 resize_filter->coefficient[6]=(-1.0/6.0)*B-C;
1041 Expert option request for verbose details of the resulting filter.
1043 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1047 artifact=GetImageArtifact(image,"filter:verbose");
1048 if (IsMagickTrue(artifact) != MagickFalse)
1055 Set the weighting function properly when the weighting function may
1056 not exactly match the filter of the same name. EG: a point filter
1057 really uses a box weighting function with a different support than is
1060 if (resize_filter->filter == Box)
1061 filter_type=BoxFilter;
1062 if (resize_filter->filter == Sinc)
1063 filter_type=SincFilter;
1064 if (resize_filter->filter == SincFast)
1065 filter_type=SincFastFilter;
1066 if (resize_filter->filter == Jinc)
1067 filter_type=JincFilter;
1068 if (resize_filter->filter == CubicBC)
1069 filter_type=CubicFilter;
1070 if (resize_filter->window == Box)
1071 window_type=BoxFilter;
1072 if (resize_filter->window == Sinc)
1073 window_type=SincFilter;
1074 if (resize_filter->window == SincFast)
1075 window_type=SincFastFilter;
1076 if (resize_filter->window == Jinc)
1077 window_type=JincFilter;
1078 if (resize_filter->window == CubicBC)
1079 window_type=CubicFilter;
1081 Report filter details.
1083 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1084 (void) FormatLocaleFile(stdout,"# Resize Filter (for graphing)\n#\n");
1085 (void) FormatLocaleFile(stdout,"# filter = %s\n",
1086 CommandOptionToMnemonic(MagickFilterOptions,filter_type));
1087 (void) FormatLocaleFile(stdout,"# window = %s\n",
1088 CommandOptionToMnemonic(MagickFilterOptions, window_type));
1089 (void) FormatLocaleFile(stdout,"# support = %.*g\n",
1090 GetMagickPrecision(),(double) resize_filter->support);
1091 (void) FormatLocaleFile(stdout,"# win-support = %.*g\n",
1092 GetMagickPrecision(),(double) resize_filter->window_support);
1093 (void) FormatLocaleFile(stdout,"# scale_blur = %.*g\n",
1094 GetMagickPrecision(), (double)resize_filter->blur);
1095 if (filter_type == GaussianFilter)
1096 (void) FormatLocaleFile(stdout,"# gaussian_sigma = %.*g\n",
1097 GetMagickPrecision(), (double)sigma);
1098 (void) FormatLocaleFile(stdout,"# practical_support = %.*g\n",
1099 GetMagickPrecision(), (double)support);
1100 if ( filter_type == CubicFilter || window_type == CubicFilter )
1101 (void) FormatLocaleFile(stdout,"# B,C = %.*g,%.*g\n",
1102 GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
1103 (void) FormatLocaleFile(stdout,"\n");
1105 Output values of resulting filter graph -- for graphing filter result.
1107 for (x=0.0; x <= support; x+=0.01f)
1108 (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",x,
1109 GetMagickPrecision(),(double) GetResizeFilterWeight(resize_filter,
1112 A final value so gnuplot can graph the 'stop' properly.
1114 (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",support,
1115 GetMagickPrecision(),0.0);
1118 Output the above once only for each image - remove setting.
1120 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1121 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1124 return(resize_filter);
1128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1132 % A d a p t i v e R e s i z e I m a g e %
1136 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1138 % AdaptiveResizeImage() adaptively resize image with pixel resampling.
1140 % The format of the AdaptiveResizeImage method is:
1142 % Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1143 % const size_t rows,const PixelInterpolateMethod method,
1144 % ExceptionInfo *exception)
1146 % A description of each parameter follows:
1148 % o image: the image.
1150 % o columns: the number of columns in the resized image.
1152 % o rows: the number of rows in the resized image.
1154 % o method: the pixel interpolation method.
1156 % o exception: return any errors or warnings in this structure.
1159 MagickExport Image *AdaptiveResizeImage(const Image *image,
1160 const size_t columns,const size_t rows,const PixelInterpolateMethod method,
1161 ExceptionInfo *exception)
1163 #define AdaptiveResizeImageTag "Resize/Image"
1182 Adaptively resize image.
1184 assert(image != (const Image *) NULL);
1185 assert(image->signature == MagickSignature);
1186 if (image->debug != MagickFalse)
1187 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1188 assert(exception != (ExceptionInfo *) NULL);
1189 assert(exception->signature == MagickSignature);
1190 if ((columns == 0) || (rows == 0))
1191 return((Image *) NULL);
1192 if ((columns == image->columns) && (rows == image->rows))
1193 return(CloneImage(image,0,0,MagickTrue,exception));
1194 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1195 if (resize_image == (Image *) NULL)
1196 return((Image *) NULL);
1197 if (SetImageStorageClass(resize_image,DirectClass,exception) == MagickFalse)
1199 resize_image=DestroyImage(resize_image);
1200 return((Image *) NULL);
1204 image_view=AcquireCacheView(image);
1205 resize_view=AcquireCacheView(resize_image);
1206 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1207 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
1209 for (y=0; y < (ssize_t) resize_image->rows; y++)
1220 if (status == MagickFalse)
1222 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1224 if (q == (Quantum *) NULL)
1226 offset.y=((MagickRealType) (y+0.5)*image->rows/resize_image->rows);
1227 for (x=0; x < (ssize_t) resize_image->columns; x++)
1229 offset.x=((MagickRealType) (x+0.5)*image->columns/resize_image->columns);
1230 status=InterpolatePixelChannels(image,image_view,resize_image,
1231 method,offset.x-0.5,offset.y-0.5,q,exception);
1232 q+=GetPixelChannels(resize_image);
1234 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1236 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1241 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1242 #pragma omp critical (MagickCore_AdaptiveResizeImage)
1244 proceed=SetImageProgress(image,AdaptiveResizeImageTag,progress++,
1246 if (proceed == MagickFalse)
1250 resize_view=DestroyCacheView(resize_view);
1251 image_view=DestroyCacheView(image_view);
1252 if (status == MagickFalse)
1253 resize_image=DestroyImage(resize_image);
1254 return(resize_image);
1258 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1262 + B e s s e l O r d e r O n e %
1266 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1268 % BesselOrderOne() computes the Bessel function of x of the first kind of
1269 % order 0. This is used to create the Jinc() filter function below.
1271 % Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1277 % j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1279 % where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1281 % cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1282 % = 1/sqrt(2) * (sin(x) - cos(x))
1283 % sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1284 % = -1/sqrt(2) * (sin(x) + cos(x))
1286 % The format of the BesselOrderOne method is:
1288 % MagickRealType BesselOrderOne(MagickRealType x)
1290 % A description of each parameter follows:
1292 % o x: MagickRealType value.
1297 static MagickRealType I0(MagickRealType x)
1308 Zeroth order Bessel function of the first kind.
1313 for (i=2; t > MagickEpsilon; i++)
1316 t*=y/((MagickRealType) i*i);
1322 static MagickRealType J1(MagickRealType x)
1334 0.581199354001606143928050809e+21,
1335 -0.6672106568924916298020941484e+20,
1336 0.2316433580634002297931815435e+19,
1337 -0.3588817569910106050743641413e+17,
1338 0.2908795263834775409737601689e+15,
1339 -0.1322983480332126453125473247e+13,
1340 0.3413234182301700539091292655e+10,
1341 -0.4695753530642995859767162166e+7,
1342 0.270112271089232341485679099e+4
1346 0.11623987080032122878585294e+22,
1347 0.1185770712190320999837113348e+20,
1348 0.6092061398917521746105196863e+17,
1349 0.2081661221307607351240184229e+15,
1350 0.5243710262167649715406728642e+12,
1351 0.1013863514358673989967045588e+10,
1352 0.1501793594998585505921097578e+7,
1353 0.1606931573481487801970916749e+4,
1359 for (i=7; i >= 0; i--)
1368 static MagickRealType P1(MagickRealType x)
1380 0.352246649133679798341724373e+5,
1381 0.62758845247161281269005675e+5,
1382 0.313539631109159574238669888e+5,
1383 0.49854832060594338434500455e+4,
1384 0.2111529182853962382105718e+3,
1385 0.12571716929145341558495e+1
1389 0.352246649133679798068390431e+5,
1390 0.626943469593560511888833731e+5,
1391 0.312404063819041039923015703e+5,
1392 0.4930396490181088979386097e+4,
1393 0.2030775189134759322293574e+3,
1399 for (i=4; i >= 0; i--)
1401 p=p*(8.0/x)*(8.0/x)+Pone[i];
1402 q=q*(8.0/x)*(8.0/x)+Qone[i];
1408 static MagickRealType Q1(MagickRealType x)
1420 0.3511751914303552822533318e+3,
1421 0.7210391804904475039280863e+3,
1422 0.4259873011654442389886993e+3,
1423 0.831898957673850827325226e+2,
1424 0.45681716295512267064405e+1,
1425 0.3532840052740123642735e-1
1429 0.74917374171809127714519505e+4,
1430 0.154141773392650970499848051e+5,
1431 0.91522317015169922705904727e+4,
1432 0.18111867005523513506724158e+4,
1433 0.1038187585462133728776636e+3,
1439 for (i=4; i >= 0; i--)
1441 p=p*(8.0/x)*(8.0/x)+Pone[i];
1442 q=q*(8.0/x)*(8.0/x)+Qone[i];
1447 static MagickRealType BesselOrderOne(MagickRealType x)
1460 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1461 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1469 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1473 + D e s t r o y R e s i z e F i l t e r %
1477 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1479 % DestroyResizeFilter() destroy the resize filter.
1481 % The format of the DestroyResizeFilter method is:
1483 % ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1485 % A description of each parameter follows:
1487 % o resize_filter: the resize filter.
1490 MagickPrivate ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1492 assert(resize_filter != (ResizeFilter *) NULL);
1493 assert(resize_filter->signature == MagickSignature);
1494 resize_filter->signature=(~MagickSignature);
1495 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1496 return(resize_filter);
1500 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1504 + G e t R e s i z e F i l t e r S u p p o r t %
1508 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1510 % GetResizeFilterSupport() return the current support window size for this
1511 % filter. Note that this may have been enlarged by filter:blur factor.
1513 % The format of the GetResizeFilterSupport method is:
1515 % MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1517 % A description of each parameter follows:
1519 % o filter: Image filter to use.
1522 MagickPrivate MagickRealType GetResizeFilterSupport(
1523 const ResizeFilter *resize_filter)
1525 assert(resize_filter != (ResizeFilter *) NULL);
1526 assert(resize_filter->signature == MagickSignature);
1527 return(resize_filter->support*resize_filter->blur);
1531 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1535 + G e t R e s i z e F i l t e r W e i g h t %
1539 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1541 % GetResizeFilterWeight evaluates the specified resize filter at the point x
1542 % which usally lies between zero and the filters current 'support' and
1543 % returns the weight of the filter function at that point.
1545 % The format of the GetResizeFilterWeight method is:
1547 % MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1548 % const MagickRealType x)
1550 % A description of each parameter follows:
1552 % o filter: the filter type.
1557 MagickPrivate MagickRealType GetResizeFilterWeight(
1558 const ResizeFilter *resize_filter,const MagickRealType x)
1566 Windowing function - scale the weighting filter by this amount.
1568 assert(resize_filter != (ResizeFilter *) NULL);
1569 assert(resize_filter->signature == MagickSignature);
1570 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
1571 if ((resize_filter->window_support < MagickEpsilon) ||
1572 (resize_filter->window == Box))
1573 scale=1.0; /* Point or Box Filter -- avoid division by zero */
1576 scale=resize_filter->scale;
1577 scale=resize_filter->window(x_blur*scale,resize_filter);
1579 weight=scale*resize_filter->filter(x_blur,resize_filter);
1582 #if defined(MAGICKCORE_LQR_DELEGATE)
1585 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1589 % L i q u i d R e s c a l e I m a g e %
1593 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1595 % LiquidRescaleImage() rescales image with seam carving.
1597 % The format of the LiquidRescaleImage method is:
1599 % Image *LiquidRescaleImage(const Image *image,const size_t columns,
1600 % const size_t rows,const double delta_x,const double rigidity,
1601 % ExceptionInfo *exception)
1603 % A description of each parameter follows:
1605 % o image: the image.
1607 % o columns: the number of columns in the rescaled image.
1609 % o rows: the number of rows in the rescaled image.
1611 % o delta_x: maximum seam transversal step (0 means straight seams).
1613 % o rigidity: introduce a bias for non-straight seams (typically 0).
1615 % o exception: return any errors or warnings in this structure.
1618 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1619 const size_t rows,const double delta_x,const double rigidity,
1620 ExceptionInfo *exception)
1622 #define LiquidRescaleImageTag "Rescale/Image"
1655 Liquid rescale image.
1657 assert(image != (const Image *) NULL);
1658 assert(image->signature == MagickSignature);
1659 if (image->debug != MagickFalse)
1660 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1661 assert(exception != (ExceptionInfo *) NULL);
1662 assert(exception->signature == MagickSignature);
1663 if ((columns == 0) || (rows == 0))
1664 return((Image *) NULL);
1665 if ((columns == image->columns) && (rows == image->rows))
1666 return(CloneImage(image,0,0,MagickTrue,exception));
1667 if ((columns <= 2) || (rows <= 2))
1668 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
1669 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1679 Honor liquid resize size limitations.
1681 for (width=image->columns; columns >= (2*width-1); width*=2);
1682 for (height=image->rows; rows >= (2*height-1); height*=2);
1683 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1685 if (resize_image == (Image *) NULL)
1686 return((Image *) NULL);
1687 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1688 rigidity,exception);
1689 resize_image=DestroyImage(resize_image);
1690 return(rescale_image);
1692 pixels=(gfloat *) AcquireQuantumMemory(image->columns,image->rows*
1693 GetPixelChannels(image)*sizeof(*pixels));
1694 if (pixels == (gfloat *) NULL)
1695 return((Image *) NULL);
1698 image_view=AcquireCacheView(image);
1699 for (y=0; y < (ssize_t) image->rows; y++)
1701 register const Quantum
1707 if (status == MagickFalse)
1709 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1710 if (p == (const Quantum *) NULL)
1715 for (x=0; x < (ssize_t) image->columns; x++)
1720 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1721 *q++=QuantumScale*p[i];
1722 p+=GetPixelChannels(image);
1725 image_view=DestroyCacheView(image_view);
1726 carver=lqr_carver_new_ext(pixels,image->columns,image->rows,
1727 GetPixelChannels(image),LQR_COLDEPTH_32F);
1728 if (carver == (LqrCarver *) NULL)
1730 pixels=(gfloat *) RelinquishMagickMemory(pixels);
1731 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1733 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1734 lqr_status=lqr_carver_resize(carver,columns,rows);
1736 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1737 lqr_carver_get_height(carver),MagickTrue,exception);
1738 if (rescale_image == (Image *) NULL)
1740 pixels=(gfloat *) RelinquishMagickMemory(pixels);
1741 return((Image *) NULL);
1743 if (SetImageStorageClass(rescale_image,DirectClass,exception) == MagickFalse)
1745 pixels=(gfloat *) RelinquishMagickMemory(pixels);
1746 rescale_image=DestroyImage(rescale_image);
1747 return((Image *) NULL);
1749 rescale_view=AcquireCacheView(rescale_image);
1750 (void) lqr_carver_scan_reset(carver);
1751 while (lqr_carver_scan_ext(carver,&x_offset,&y_offset,(void **) &packet) != 0)
1759 q=QueueCacheViewAuthenticPixels(rescale_view,x_offset,y_offset,1,1,
1761 if (q == (Quantum *) NULL)
1763 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1772 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1773 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
1774 rescale_traits=GetPixelChannelMapTraits(rescale_image,channel);
1775 if ((traits == UndefinedPixelTrait) ||
1776 (rescale_traits == UndefinedPixelTrait))
1778 SetPixelChannel(rescale_image,channel,ClampToQuantum(QuantumRange*
1781 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
1784 rescale_view=DestroyCacheView(rescale_view);
1785 lqr_carver_destroy(carver);
1786 return(rescale_image);
1789 MagickExport Image *LiquidRescaleImage(const Image *image,
1790 const size_t magick_unused(columns),const size_t magick_unused(rows),
1791 const double magick_unused(delta_x),const double magick_unused(rigidity),
1792 ExceptionInfo *exception)
1794 assert(image != (const Image *) NULL);
1795 assert(image->signature == MagickSignature);
1796 if (image->debug != MagickFalse)
1797 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1798 assert(exception != (ExceptionInfo *) NULL);
1799 assert(exception->signature == MagickSignature);
1800 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1801 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1802 return((Image *) NULL);
1807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1811 % M a g n i f y I m a g e %
1815 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1817 % MagnifyImage() is a convenience method that scales an image proportionally
1818 % to twice its size.
1820 % The format of the MagnifyImage method is:
1822 % Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1824 % A description of each parameter follows:
1826 % o image: the image.
1828 % o exception: return any errors or warnings in this structure.
1831 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1836 assert(image != (Image *) NULL);
1837 assert(image->signature == MagickSignature);
1838 if (image->debug != MagickFalse)
1839 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1840 assert(exception != (ExceptionInfo *) NULL);
1841 assert(exception->signature == MagickSignature);
1842 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1844 return(magnify_image);
1848 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1852 % M i n i f y I m a g e %
1856 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1858 % MinifyImage() is a convenience method that scales an image proportionally to
1861 % The format of the MinifyImage method is:
1863 % Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1865 % A description of each parameter follows:
1867 % o image: the image.
1869 % o exception: return any errors or warnings in this structure.
1872 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1877 assert(image != (Image *) NULL);
1878 assert(image->signature == MagickSignature);
1879 if (image->debug != MagickFalse)
1880 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1881 assert(exception != (ExceptionInfo *) NULL);
1882 assert(exception->signature == MagickSignature);
1883 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,1.0,
1885 return(minify_image);
1889 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1893 % R e s a m p l e I m a g e %
1897 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1899 % ResampleImage() resize image in terms of its pixel size, so that when
1900 % displayed at the given resolution it will be the same size in terms of
1901 % real world units as the original image at the original resolution.
1903 % The format of the ResampleImage method is:
1905 % Image *ResampleImage(Image *image,const double x_resolution,
1906 % const double y_resolution,const FilterTypes filter,const double blur,
1907 % ExceptionInfo *exception)
1909 % A description of each parameter follows:
1911 % o image: the image to be resized to fit the given resolution.
1913 % o x_resolution: the new image x resolution.
1915 % o y_resolution: the new image y resolution.
1917 % o filter: Image filter to use.
1919 % o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1922 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1923 const double y_resolution,const FilterTypes filter,const double blur,
1924 ExceptionInfo *exception)
1926 #define ResampleImageTag "Resample/Image"
1936 Initialize sampled image attributes.
1938 assert(image != (const Image *) NULL);
1939 assert(image->signature == MagickSignature);
1940 if (image->debug != MagickFalse)
1941 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1942 assert(exception != (ExceptionInfo *) NULL);
1943 assert(exception->signature == MagickSignature);
1944 width=(size_t) (x_resolution*image->columns/(image->resolution.x == 0.0 ?
1945 72.0 : image->resolution.x)+0.5);
1946 height=(size_t) (y_resolution*image->rows/(image->resolution.y == 0.0 ?
1947 72.0 : image->resolution.y)+0.5);
1948 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1949 if (resample_image != (Image *) NULL)
1951 resample_image->resolution.x=x_resolution;
1952 resample_image->resolution.y=y_resolution;
1954 return(resample_image);
1958 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1962 % R e s i z e I m a g e %
1966 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1968 % ResizeImage() scales an image to the desired dimensions, using the given
1969 % filter (see AcquireFilterInfo()).
1971 % If an undefined filter is given the filter defaults to Mitchell for a
1972 % colormapped image, a image with a matte channel, or if the image is
1973 % enlarged. Otherwise the filter defaults to a Lanczos.
1975 % ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1977 % The format of the ResizeImage method is:
1979 % Image *ResizeImage(Image *image,const size_t columns,
1980 % const size_t rows,const FilterTypes filter,const double blur,
1981 % ExceptionInfo *exception)
1983 % A description of each parameter follows:
1985 % o image: the image.
1987 % o columns: the number of columns in the scaled image.
1989 % o rows: the number of rows in the scaled image.
1991 % o filter: Image filter to use.
1993 % o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1996 % o exception: return any errors or warnings in this structure.
2000 typedef struct _ContributionInfo
2009 static ContributionInfo **DestroyContributionThreadSet(
2010 ContributionInfo **contribution)
2015 assert(contribution != (ContributionInfo **) NULL);
2016 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
2017 if (contribution[i] != (ContributionInfo *) NULL)
2018 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
2020 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
2021 return(contribution);
2024 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
2035 number_threads=GetOpenMPMaximumThreads();
2036 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
2037 sizeof(*contribution));
2038 if (contribution == (ContributionInfo **) NULL)
2039 return((ContributionInfo **) NULL);
2040 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
2041 for (i=0; i < (ssize_t) number_threads; i++)
2043 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2044 sizeof(**contribution));
2045 if (contribution[i] == (ContributionInfo *) NULL)
2046 return(DestroyContributionThreadSet(contribution));
2048 return(contribution);
2051 static inline double MagickMax(const double x,const double y)
2058 static inline double MagickMin(const double x,const double y)
2065 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2066 const Image *image,Image *resize_image,const MagickRealType x_factor,
2067 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2069 #define ResizeImageTag "Resize/Image"
2079 **restrict contributions;
2092 Apply filter to resize horizontally from image to resize image.
2094 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
2095 support=scale*GetResizeFilterSupport(resize_filter);
2096 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2097 if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
2098 return(MagickFalse);
2102 Support too small even for nearest neighbour: Reduce to point sampling.
2104 support=(MagickRealType) 0.5;
2107 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2108 if (contributions == (ContributionInfo **) NULL)
2110 (void) ThrowMagickException(exception,GetMagickModule(),
2111 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2112 return(MagickFalse);
2116 image_view=AcquireCacheView(image);
2117 resize_view=AcquireCacheView(resize_image);
2118 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2119 #pragma omp parallel for shared(status)
2121 for (x=0; x < (ssize_t) resize_image->columns; x++)
2127 register const Quantum
2130 register ContributionInfo
2131 *restrict contribution;
2144 if (status == MagickFalse)
2146 bisect=(MagickRealType) (x+0.5)/x_factor;
2147 start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
2148 stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->columns);
2150 contribution=contributions[GetOpenMPThreadId()];
2151 for (n=0; n < (stop-start); n++)
2153 contribution[n].pixel=start+n;
2154 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2155 ((MagickRealType) (start+n)-bisect+0.5));
2156 density+=contribution[n].weight;
2158 if ((density != 0.0) && (density != 1.0))
2166 density=1.0/density;
2167 for (i=0; i < n; i++)
2168 contribution[i].weight*=density;
2170 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2171 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
2172 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2174 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2179 for (y=0; y < (ssize_t) resize_image->rows; y++)
2184 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2204 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2205 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
2206 resize_traits=GetPixelChannelMapTraits(resize_image,channel);
2207 if ((traits == UndefinedPixelTrait) ||
2208 (resize_traits == UndefinedPixelTrait))
2210 if ((resize_traits & CopyPixelTrait) != 0)
2212 j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
2214 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2215 (contribution[j-start].pixel-contribution[0].pixel);
2216 SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
2221 if ((resize_traits & BlendPixelTrait) == 0)
2226 for (j=0; j < n; j++)
2228 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2229 (contribution[j].pixel-contribution[0].pixel);
2230 alpha=contribution[j].weight;
2231 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2233 SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
2240 for (j=0; j < n; j++)
2242 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2243 (contribution[j].pixel-contribution[0].pixel);
2244 alpha=contribution[j].weight*QuantumScale*
2245 GetPixelAlpha(image,p+k*GetPixelChannels(image));
2246 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2249 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2250 SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
2252 q+=GetPixelChannels(resize_image);
2254 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2256 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2261 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2262 #pragma omp critical (MagickCore_HorizontalFilter)
2264 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2265 if (proceed == MagickFalse)
2269 resize_view=DestroyCacheView(resize_view);
2270 image_view=DestroyCacheView(image_view);
2271 contributions=DestroyContributionThreadSet(contributions);
2275 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2276 const Image *image,Image *resize_image,const MagickRealType y_factor,
2277 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2287 **restrict contributions;
2303 Apply filter to resize vertically from image to resize image.
2305 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
2306 support=scale*GetResizeFilterSupport(resize_filter);
2307 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2308 if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
2309 return(MagickFalse);
2313 Support too small even for nearest neighbour: Reduce to point sampling.
2315 support=(MagickRealType) 0.5;
2318 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2319 if (contributions == (ContributionInfo **) NULL)
2321 (void) ThrowMagickException(exception,GetMagickModule(),
2322 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2323 return(MagickFalse);
2327 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2328 image_view=AcquireCacheView(image);
2329 resize_view=AcquireCacheView(resize_image);
2330 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2331 #pragma omp parallel for shared(status)
2333 for (y=0; y < (ssize_t) resize_image->rows; y++)
2339 register const Quantum
2342 register ContributionInfo
2343 *restrict contribution;
2356 if (status == MagickFalse)
2358 bisect=(MagickRealType) (y+0.5)/y_factor;
2359 start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
2360 stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->rows);
2362 contribution=contributions[GetOpenMPThreadId()];
2363 for (n=0; n < (stop-start); n++)
2365 contribution[n].pixel=start+n;
2366 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2367 ((MagickRealType) (start+n)-bisect+0.5));
2368 density+=contribution[n].weight;
2370 if ((density != 0.0) && (density != 1.0))
2378 density=1.0/density;
2379 for (i=0; i < n; i++)
2380 contribution[i].weight*=density;
2382 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
2383 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2385 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2387 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2392 for (x=0; x < (ssize_t) resize_image->columns; x++)
2397 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2417 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2418 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
2419 resize_traits=GetPixelChannelMapTraits(resize_image,channel);
2420 if ((traits == UndefinedPixelTrait) ||
2421 (resize_traits == UndefinedPixelTrait))
2423 if ((resize_traits & CopyPixelTrait) != 0)
2425 j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
2427 k=(ssize_t) ((contribution[j-start].pixel-contribution[0].pixel)*
2429 SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
2434 if ((resize_traits & BlendPixelTrait) == 0)
2439 for (j=0; j < n; j++)
2441 k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
2443 alpha=contribution[j].weight;
2444 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2446 SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
2450 for (j=0; j < n; j++)
2452 k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
2454 alpha=contribution[j].weight*QuantumScale*
2455 GetPixelAlpha(image,p+k*GetPixelChannels(image));
2456 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2459 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2460 SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
2462 q+=GetPixelChannels(resize_image);
2464 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2466 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2471 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2472 #pragma omp critical (MagickCore_VerticalFilter)
2474 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2475 if (proceed == MagickFalse)
2479 resize_view=DestroyCacheView(resize_view);
2480 image_view=DestroyCacheView(image_view);
2481 contributions=DestroyContributionThreadSet(contributions);
2485 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2486 const size_t rows,const FilterTypes filter,const double blur,
2487 ExceptionInfo *exception)
2489 #define WorkLoadFactor 0.265
2515 Acquire resize image.
2517 assert(image != (Image *) NULL);
2518 assert(image->signature == MagickSignature);
2519 if (image->debug != MagickFalse)
2520 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2521 assert(exception != (ExceptionInfo *) NULL);
2522 assert(exception->signature == MagickSignature);
2523 if ((columns == 0) || (rows == 0))
2524 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2525 if ((columns == image->columns) && (rows == image->rows) &&
2526 (filter == UndefinedFilter) && (blur == 1.0))
2527 return(CloneImage(image,0,0,MagickTrue,exception));
2528 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2529 if (resize_image == (Image *) NULL)
2530 return(resize_image);
2532 Acquire resize filter.
2534 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2535 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2536 if ((x_factor*y_factor) > WorkLoadFactor)
2537 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2539 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2540 if (filter_image == (Image *) NULL)
2541 return(DestroyImage(resize_image));
2542 filter_type=LanczosFilter;
2543 if (filter != UndefinedFilter)
2546 if ((x_factor == 1.0) && (y_factor == 1.0))
2547 filter_type=PointFilter;
2549 if ((image->storage_class == PseudoClass) ||
2550 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2551 filter_type=MitchellFilter;
2552 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2558 if ((x_factor*y_factor) > WorkLoadFactor)
2560 span=(MagickSizeType) (filter_image->columns+rows);
2561 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
2563 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
2564 span,&offset,exception);
2568 span=(MagickSizeType) (filter_image->rows+columns);
2569 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
2571 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
2572 span,&offset,exception);
2577 filter_image=DestroyImage(filter_image);
2578 resize_filter=DestroyResizeFilter(resize_filter);
2579 if (status == MagickFalse)
2581 resize_image=DestroyImage(resize_image);
2582 return((Image *) NULL);
2584 resize_image->type=image->type;
2585 return(resize_image);
2589 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2593 % S a m p l e I m a g e %
2597 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2599 % SampleImage() scales an image to the desired dimensions with pixel
2600 % sampling. Unlike other scaling methods, this method does not introduce
2601 % any additional color into the scaled image.
2603 % The format of the SampleImage method is:
2605 % Image *SampleImage(const Image *image,const size_t columns,
2606 % const size_t rows,ExceptionInfo *exception)
2608 % A description of each parameter follows:
2610 % o image: the image.
2612 % o columns: the number of columns in the sampled image.
2614 % o rows: the number of rows in the sampled image.
2616 % o exception: return any errors or warnings in this structure.
2619 MagickExport Image *SampleImage(const Image *image,const size_t columns,
2620 const size_t rows,ExceptionInfo *exception)
2622 #define SampleImageTag "Sample/Image"
2645 Initialize sampled image attributes.
2647 assert(image != (const Image *) NULL);
2648 assert(image->signature == MagickSignature);
2649 if (image->debug != MagickFalse)
2650 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2651 assert(exception != (ExceptionInfo *) NULL);
2652 assert(exception->signature == MagickSignature);
2653 if ((columns == 0) || (rows == 0))
2654 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2655 if ((columns == image->columns) && (rows == image->rows))
2656 return(CloneImage(image,0,0,MagickTrue,exception));
2657 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2658 if (sample_image == (Image *) NULL)
2659 return((Image *) NULL);
2661 Allocate scan line buffer and column offset buffers.
2663 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
2665 if (x_offset == (ssize_t *) NULL)
2667 sample_image=DestroyImage(sample_image);
2668 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2670 for (x=0; x < (ssize_t) sample_image->columns; x++)
2671 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
2672 sample_image->columns);
2678 image_view=AcquireCacheView(image);
2679 sample_view=AcquireCacheView(sample_image);
2680 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2681 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
2683 for (y=0; y < (ssize_t) sample_image->rows; y++)
2685 register const Quantum
2697 if (status == MagickFalse)
2699 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2700 sample_image->rows);
2701 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2703 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2705 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2713 for (x=0; x < (ssize_t) sample_image->columns; x++)
2718 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2727 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2728 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
2729 sample_traits=GetPixelChannelMapTraits(sample_image,channel);
2730 if ((traits == UndefinedPixelTrait) ||
2731 (sample_traits == UndefinedPixelTrait))
2733 SetPixelChannel(sample_image,channel,p[x_offset[x]*GetPixelChannels(
2736 q+=GetPixelChannels(sample_image);
2738 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2740 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2745 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2746 #pragma omp critical (MagickCore_SampleImage)
2748 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2749 if (proceed == MagickFalse)
2753 image_view=DestroyCacheView(image_view);
2754 sample_view=DestroyCacheView(sample_view);
2755 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
2756 sample_image->type=image->type;
2757 return(sample_image);
2761 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2765 % S c a l e I m a g e %
2769 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2771 % ScaleImage() changes the size of an image to the given dimensions.
2773 % The format of the ScaleImage method is:
2775 % Image *ScaleImage(const Image *image,const size_t columns,
2776 % const size_t rows,ExceptionInfo *exception)
2778 % A description of each parameter follows:
2780 % o image: the image.
2782 % o columns: the number of columns in the scaled image.
2784 % o rows: the number of rows in the scaled image.
2786 % o exception: return any errors or warnings in this structure.
2789 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2790 const size_t rows,ExceptionInfo *exception)
2792 #define ScaleImageTag "Scale/Image"
2809 pixel[CompositePixelChannel],
2835 Initialize scaled image attributes.
2837 assert(image != (const Image *) NULL);
2838 assert(image->signature == MagickSignature);
2839 if (image->debug != MagickFalse)
2840 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2841 assert(exception != (ExceptionInfo *) NULL);
2842 assert(exception->signature == MagickSignature);
2843 if ((columns == 0) || (rows == 0))
2844 return((Image *) NULL);
2845 if ((columns == image->columns) && (rows == image->rows))
2846 return(CloneImage(image,0,0,MagickTrue,exception));
2847 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2848 if (scale_image == (Image *) NULL)
2849 return((Image *) NULL);
2850 if (SetImageStorageClass(scale_image,DirectClass,exception) == MagickFalse)
2852 scale_image=DestroyImage(scale_image);
2853 return((Image *) NULL);
2858 x_vector=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
2859 GetPixelChannels(image)*sizeof(*x_vector));
2861 if (image->rows != scale_image->rows)
2862 scanline=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
2863 GetPixelChannels(image)*sizeof(*scanline));
2864 scale_scanline=(MagickRealType *) AcquireQuantumMemory((size_t)
2865 scale_image->columns,GetPixelChannels(scale_image)*sizeof(*scale_scanline));
2866 y_vector=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
2867 GetPixelChannels(image)*sizeof(*y_vector));
2868 if ((scanline == (MagickRealType *) NULL) ||
2869 (scale_scanline == (MagickRealType *) NULL) ||
2870 (x_vector == (MagickRealType *) NULL) ||
2871 (y_vector == (MagickRealType *) NULL))
2873 scale_image=DestroyImage(scale_image);
2874 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2880 next_row=MagickTrue;
2882 scale.y=(double) scale_image->rows/(double) image->rows;
2883 for (i=0; i < (ssize_t) (GetPixelChannels(image)*image->columns); i++)
2886 image_view=AcquireCacheView(image);
2887 scale_view=AcquireCacheView(scale_image);
2888 for (y=0; y < (ssize_t) scale_image->rows; y++)
2890 register const Quantum
2899 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2901 if (q == (Quantum *) NULL)
2904 if (scale_image->rows == image->rows)
2907 Read a new scanline.
2909 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
2911 if (p == (const Quantum *) NULL)
2913 for (x=0; x < (ssize_t) image->columns; x++)
2915 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2917 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2918 if ((traits & BlendPixelTrait) == 0)
2920 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType) p[i];
2923 alpha=QuantumScale*GetPixelAlpha(image,p);
2924 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
2926 p+=GetPixelChannels(image);
2934 while (scale.y < span.y)
2936 if ((next_row != MagickFalse) &&
2937 (number_rows < (ssize_t) image->rows))
2940 Read a new scanline.
2942 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
2944 if (p == (const Quantum *) NULL)
2946 for (x=0; x < (ssize_t) image->columns; x++)
2948 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2950 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2951 if ((traits & BlendPixelTrait) == 0)
2953 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType)
2957 alpha=QuantumScale*GetPixelAlpha(image,p);
2958 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
2960 p+=GetPixelChannels(image);
2964 for (x=0; x < (ssize_t) image->columns; x++)
2965 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2966 y_vector[x*GetPixelChannels(image)+i]+=scale.y*
2967 x_vector[x*GetPixelChannels(image)+i];
2969 scale.y=(double) scale_image->rows/(double) image->rows;
2970 next_row=MagickTrue;
2972 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
2975 Read a new scanline.
2977 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
2979 if (p == (const Quantum *) NULL)
2981 for (x=0; x < (ssize_t) image->columns; x++)
2983 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2985 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2986 if ((traits & BlendPixelTrait) == 0)
2988 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType) p[i];
2991 alpha=QuantumScale*GetPixelAlpha(image,p);
2992 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
2994 p+=GetPixelChannels(image);
2997 next_row=MagickFalse;
2999 for (x=0; x < (ssize_t) image->columns; x++)
3001 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3003 pixel[i]=y_vector[x*GetPixelChannels(image)+i]+span.y*
3004 x_vector[x*GetPixelChannels(image)+i];
3005 scanline[x*GetPixelChannels(image)+i]=pixel[i];
3006 y_vector[x*GetPixelChannels(image)+i]=0.0;
3012 scale.y=(double) scale_image->rows/(double) image->rows;
3013 next_row=MagickTrue;
3017 if (scale_image->columns == image->columns)
3020 Transfer scanline to scaled image.
3022 for (x=0; x < (ssize_t) scale_image->columns; x++)
3024 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3026 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3027 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3028 scale_traits=GetPixelChannelMapTraits(scale_image,channel);
3029 if ((traits == UndefinedPixelTrait) ||
3030 (scale_traits == UndefinedPixelTrait))
3032 if ((scale_traits & BlendPixelTrait) == 0)
3034 SetPixelChannel(scale_image,channel,ClampToQuantum(scanline[
3035 x*GetPixelChannels(image)+i]),q);
3038 alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
3039 GetPixelChannelMapChannel(image,AlphaPixelChannel)];
3040 gamma=1.0/(fabs((double) alpha) <= MagickEpsilon ? 1.0 : alpha);
3041 SetPixelChannel(scale_image,channel,ClampToQuantum(gamma*scanline[
3042 x*GetPixelChannels(image)+i]),q);
3044 q+=GetPixelChannels(scale_image);
3055 next_column=MagickFalse;
3058 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3060 for (x=0; x < (ssize_t) image->columns; x++)
3062 scale.x=(double) scale_image->columns/(double) image->columns;
3063 while (scale.x >= span.x)
3065 if (next_column != MagickFalse)
3067 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3071 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3073 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3074 if (traits == UndefinedPixelTrait)
3076 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3077 pixel[i]+=span.x*scanline[x*GetPixelChannels(image)+i];
3078 scale_scanline[n*GetPixelChannels(scale_image)+channel]=pixel[i];
3082 next_column=MagickTrue;
3086 if (next_column != MagickFalse)
3088 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3091 next_column=MagickFalse;
3093 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3094 pixel[i]+=scale.x*scanline[x*GetPixelChannels(image)+i];
3100 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3101 pixel[i]+=span.x*scanline[(x-1)*GetPixelChannels(image)+i];
3103 if ((next_column == MagickFalse) &&
3104 ((ssize_t) n < (ssize_t) scale_image->columns))
3105 for (i=0; i < (ssize_t) GetPixelChannels(scale_image); i++)
3106 scale_scanline[n*GetPixelChannels(scale_image)+i]=pixel[i];
3108 Transfer scanline to scaled image.
3110 for (x=0; x < (ssize_t) scale_image->columns; x++)
3112 for (i=0; i < (ssize_t) GetPixelChannels(scale_image); i++)
3114 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3115 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3116 scale_traits=GetPixelChannelMapTraits(scale_image,channel);
3117 if ((traits == UndefinedPixelTrait) ||
3118 (scale_traits == UndefinedPixelTrait))
3120 if ((scale_traits & BlendPixelTrait) == 0)
3122 SetPixelChannel(scale_image,channel,ClampToQuantum(
3123 scale_scanline[x*GetPixelChannels(scale_image)+channel]),q);
3126 alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
3127 GetPixelChannelMapChannel(image,AlphaPixelChannel)];
3128 gamma=1.0/(fabs((double) alpha) <= MagickEpsilon ? 1.0 : alpha);
3129 SetPixelChannel(scale_image,channel,ClampToQuantum(gamma*
3130 scale_scanline[x*GetPixelChannels(scale_image)+channel]),q);
3132 q+=GetPixelChannels(scale_image);
3135 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
3137 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3139 if (proceed == MagickFalse)
3142 scale_view=DestroyCacheView(scale_view);
3143 image_view=DestroyCacheView(image_view);
3145 Free allocated memory.
3147 y_vector=(MagickRealType *) RelinquishMagickMemory(y_vector);
3148 scale_scanline=(MagickRealType *) RelinquishMagickMemory(scale_scanline);
3149 if (scale_image->rows != image->rows)
3150 scanline=(MagickRealType *) RelinquishMagickMemory(scanline);
3151 x_vector=(MagickRealType *) RelinquishMagickMemory(x_vector);
3152 scale_image->type=image->type;
3153 return(scale_image);
3157 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3161 % T h u m b n a i l I m a g e %
3165 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3167 % ThumbnailImage() changes the size of an image to the given dimensions and
3168 % removes any associated profiles. The goal is to produce small low cost
3169 % thumbnail images suited for display on the Web.
3171 % The format of the ThumbnailImage method is:
3173 % Image *ThumbnailImage(const Image *image,const size_t columns,
3174 % const size_t rows,ExceptionInfo *exception)
3176 % A description of each parameter follows:
3178 % o image: the image.
3180 % o columns: the number of columns in the scaled image.
3182 % o rows: the number of rows in the scaled image.
3184 % o exception: return any errors or warnings in this structure.
3187 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3188 const size_t rows,ExceptionInfo *exception)
3190 #define SampleFactor 5
3193 value[MaxTextExtent];
3211 assert(image != (Image *) NULL);
3212 assert(image->signature == MagickSignature);
3213 if (image->debug != MagickFalse)
3214 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3215 assert(exception != (ExceptionInfo *) NULL);
3216 assert(exception->signature == MagickSignature);
3217 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3218 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3219 if ((x_factor*y_factor) > 0.1)
3220 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3223 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
3224 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3225 image->blur,exception);
3231 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3233 if (sample_image == (Image *) NULL)
3234 return((Image *) NULL);
3235 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3236 image->blur,exception);
3237 sample_image=DestroyImage(sample_image);
3239 if (thumbnail_image == (Image *) NULL)
3240 return(thumbnail_image);
3241 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3242 if (thumbnail_image->matte == MagickFalse)
3243 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel,exception);
3244 thumbnail_image->depth=8;
3245 thumbnail_image->interlace=NoInterlace;
3247 Strip all profiles except color profiles.
3249 ResetImageProfileIterator(thumbnail_image);
3250 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3252 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3254 (void) DeleteImageProfile(thumbnail_image,name);
3255 ResetImageProfileIterator(thumbnail_image);
3257 name=GetNextImageProfile(thumbnail_image);
3259 (void) DeleteImageProperty(thumbnail_image,"comment");
3260 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3261 if (strstr(image->magick_filename,"//") == (char *) NULL)
3262 (void) FormatLocaleString(value,MaxTextExtent,"file://%s",
3263 image->magick_filename);
3264 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value,exception);
3265 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3266 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3268 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3269 attributes.st_mtime);
3270 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value,exception);
3272 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3273 attributes.st_mtime);
3274 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
3275 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
3276 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value,exception);
3277 (void) FormatLocaleString(value,MaxTextExtent,"image/%s",image->magick);
3279 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value,exception);
3280 (void) SetImageProperty(thumbnail_image,"software",GetMagickVersion(&version),
3282 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3283 image->magick_columns);
3284 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value,
3286 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3287 image->magick_rows);
3288 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value,
3290 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3291 GetImageListLength(image));
3292 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value,
3294 return(thumbnail_image);