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=InterpretLocaleValue(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*=InterpretLocaleValue(artifact,
948 (char **) NULL); /* override blur */
949 if (resize_filter->blur < MagickEpsilon)
950 resize_filter->blur=(MagickRealType) MagickEpsilon;
951 artifact=GetImageArtifact(image,"filter:lobes");
952 if (artifact != (const char *) NULL)
957 ssize_t lobes=(ssize_t) StringToLong(artifact);
960 resize_filter->support=(MagickRealType) lobes;
962 if (resize_filter->filter == Jinc)
965 Convert a Jinc function lobes value to a real support value.
967 if (resize_filter->support > 16)
968 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
970 resize_filter->support=jinc_zeros[((long)resize_filter->support)-1];
972 artifact=GetImageArtifact(image,"filter:support");
973 if (artifact != (const char *) NULL)
974 resize_filter->support=fabs(InterpretLocaleValue(artifact,
975 (char **) NULL)); /* override support */
977 Scale windowing function separately to the support 'clipping' window that
978 calling operator is planning to actually use (expert override).
980 resize_filter->window_support=resize_filter->support; /* default */
981 artifact=GetImageArtifact(image,"filter:win-support");
982 if (artifact != (const char *) NULL)
983 resize_filter->window_support=fabs(InterpretLocaleValue(artifact,
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=InterpretLocaleValue(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=InterpretLocaleValue(artifact,(char **) NULL);
1016 artifact=GetImageArtifact(image,"filter:c");
1017 if (artifact != (const char *) NULL)
1019 C=InterpretLocaleValue(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 q[channel]=ClampToQuantum(QuantumRange*packet[i]);
1780 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
1783 rescale_view=DestroyCacheView(rescale_view);
1784 lqr_carver_destroy(carver);
1785 return(rescale_image);
1788 MagickExport Image *LiquidRescaleImage(const Image *image,
1789 const size_t magick_unused(columns),const size_t magick_unused(rows),
1790 const double magick_unused(delta_x),const double magick_unused(rigidity),
1791 ExceptionInfo *exception)
1793 assert(image != (const Image *) NULL);
1794 assert(image->signature == MagickSignature);
1795 if (image->debug != MagickFalse)
1796 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1797 assert(exception != (ExceptionInfo *) NULL);
1798 assert(exception->signature == MagickSignature);
1799 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1800 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1801 return((Image *) NULL);
1806 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1810 % M a g n i f y I m a g e %
1814 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1816 % MagnifyImage() is a convenience method that scales an image proportionally
1817 % to twice its size.
1819 % The format of the MagnifyImage method is:
1821 % Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1823 % A description of each parameter follows:
1825 % o image: the image.
1827 % o exception: return any errors or warnings in this structure.
1830 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1835 assert(image != (Image *) NULL);
1836 assert(image->signature == MagickSignature);
1837 if (image->debug != MagickFalse)
1838 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1839 assert(exception != (ExceptionInfo *) NULL);
1840 assert(exception->signature == MagickSignature);
1841 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1843 return(magnify_image);
1847 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1851 % M i n i f y I m a g e %
1855 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1857 % MinifyImage() is a convenience method that scales an image proportionally to
1860 % The format of the MinifyImage method is:
1862 % Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1864 % A description of each parameter follows:
1866 % o image: the image.
1868 % o exception: return any errors or warnings in this structure.
1871 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1876 assert(image != (Image *) NULL);
1877 assert(image->signature == MagickSignature);
1878 if (image->debug != MagickFalse)
1879 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1880 assert(exception != (ExceptionInfo *) NULL);
1881 assert(exception->signature == MagickSignature);
1882 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,1.0,
1884 return(minify_image);
1888 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1892 % R e s a m p l e I m a g e %
1896 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1898 % ResampleImage() resize image in terms of its pixel size, so that when
1899 % displayed at the given resolution it will be the same size in terms of
1900 % real world units as the original image at the original resolution.
1902 % The format of the ResampleImage method is:
1904 % Image *ResampleImage(Image *image,const double x_resolution,
1905 % const double y_resolution,const FilterTypes filter,const double blur,
1906 % ExceptionInfo *exception)
1908 % A description of each parameter follows:
1910 % o image: the image to be resized to fit the given resolution.
1912 % o x_resolution: the new image x resolution.
1914 % o y_resolution: the new image y resolution.
1916 % o filter: Image filter to use.
1918 % o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1921 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1922 const double y_resolution,const FilterTypes filter,const double blur,
1923 ExceptionInfo *exception)
1925 #define ResampleImageTag "Resample/Image"
1935 Initialize sampled image attributes.
1937 assert(image != (const Image *) NULL);
1938 assert(image->signature == MagickSignature);
1939 if (image->debug != MagickFalse)
1940 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1941 assert(exception != (ExceptionInfo *) NULL);
1942 assert(exception->signature == MagickSignature);
1943 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1944 72.0 : image->x_resolution)+0.5);
1945 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1946 72.0 : image->y_resolution)+0.5);
1947 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1948 if (resample_image != (Image *) NULL)
1950 resample_image->x_resolution=x_resolution;
1951 resample_image->y_resolution=y_resolution;
1953 return(resample_image);
1957 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1961 % R e s i z e I m a g e %
1965 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1967 % ResizeImage() scales an image to the desired dimensions, using the given
1968 % filter (see AcquireFilterInfo()).
1970 % If an undefined filter is given the filter defaults to Mitchell for a
1971 % colormapped image, a image with a matte channel, or if the image is
1972 % enlarged. Otherwise the filter defaults to a Lanczos.
1974 % ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1976 % The format of the ResizeImage method is:
1978 % Image *ResizeImage(Image *image,const size_t columns,
1979 % const size_t rows,const FilterTypes filter,const double blur,
1980 % ExceptionInfo *exception)
1982 % A description of each parameter follows:
1984 % o image: the image.
1986 % o columns: the number of columns in the scaled image.
1988 % o rows: the number of rows in the scaled image.
1990 % o filter: Image filter to use.
1992 % o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1995 % o exception: return any errors or warnings in this structure.
1999 typedef struct _ContributionInfo
2008 static ContributionInfo **DestroyContributionThreadSet(
2009 ContributionInfo **contribution)
2014 assert(contribution != (ContributionInfo **) NULL);
2015 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
2016 if (contribution[i] != (ContributionInfo *) NULL)
2017 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
2019 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
2020 return(contribution);
2023 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
2034 number_threads=GetOpenMPMaximumThreads();
2035 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
2036 sizeof(*contribution));
2037 if (contribution == (ContributionInfo **) NULL)
2038 return((ContributionInfo **) NULL);
2039 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
2040 for (i=0; i < (ssize_t) number_threads; i++)
2042 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2043 sizeof(**contribution));
2044 if (contribution[i] == (ContributionInfo *) NULL)
2045 return(DestroyContributionThreadSet(contribution));
2047 return(contribution);
2050 static inline double MagickMax(const double x,const double y)
2057 static inline double MagickMin(const double x,const double y)
2064 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2065 const Image *image,Image *resize_image,const MagickRealType x_factor,
2066 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2068 #define ResizeImageTag "Resize/Image"
2078 **restrict contributions;
2091 Apply filter to resize horizontally from image to resize image.
2093 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
2094 support=scale*GetResizeFilterSupport(resize_filter);
2095 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2096 if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
2097 return(MagickFalse);
2101 Support too small even for nearest neighbour: Reduce to point sampling.
2103 support=(MagickRealType) 0.5;
2106 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2107 if (contributions == (ContributionInfo **) NULL)
2109 (void) ThrowMagickException(exception,GetMagickModule(),
2110 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2111 return(MagickFalse);
2115 image_view=AcquireCacheView(image);
2116 resize_view=AcquireCacheView(resize_image);
2117 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2118 #pragma omp parallel for shared(status)
2120 for (x=0; x < (ssize_t) resize_image->columns; x++)
2126 register const Quantum
2129 register ContributionInfo
2130 *restrict contribution;
2143 if (status == MagickFalse)
2145 bisect=(MagickRealType) (x+0.5)/x_factor;
2146 start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
2147 stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->columns);
2149 contribution=contributions[GetOpenMPThreadId()];
2150 for (n=0; n < (stop-start); n++)
2152 contribution[n].pixel=start+n;
2153 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2154 ((MagickRealType) (start+n)-bisect+0.5));
2155 density+=contribution[n].weight;
2157 if ((density != 0.0) && (density != 1.0))
2165 density=1.0/density;
2166 for (i=0; i < n; i++)
2167 contribution[i].weight*=density;
2169 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2170 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
2171 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2173 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2178 for (y=0; y < (ssize_t) resize_image->rows; y++)
2183 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2203 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2204 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
2205 resize_traits=GetPixelChannelMapTraits(resize_image,channel);
2206 if ((traits == UndefinedPixelTrait) ||
2207 (resize_traits == UndefinedPixelTrait))
2209 if ((resize_traits & CopyPixelTrait) != 0)
2211 j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
2213 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2214 (contribution[j-start].pixel-contribution[0].pixel);
2215 q[channel]=p[k*GetPixelChannels(image)+i];
2219 if ((resize_traits & BlendPixelTrait) == 0)
2224 for (j=0; j < n; j++)
2226 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2227 (contribution[j].pixel-contribution[0].pixel);
2228 alpha=contribution[j].weight;
2229 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2231 q[channel]=ClampToQuantum(pixel);
2238 for (j=0; j < n; j++)
2240 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2241 (contribution[j].pixel-contribution[0].pixel);
2242 alpha=contribution[j].weight*QuantumScale*
2243 GetPixelAlpha(image,p+k*GetPixelChannels(image));
2244 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2247 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2248 q[channel]=ClampToQuantum(gamma*pixel);
2250 q+=GetPixelChannels(resize_image);
2252 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2254 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2259 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2260 #pragma omp critical (MagickCore_HorizontalFilter)
2262 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2263 if (proceed == MagickFalse)
2267 resize_view=DestroyCacheView(resize_view);
2268 image_view=DestroyCacheView(image_view);
2269 contributions=DestroyContributionThreadSet(contributions);
2273 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2274 const Image *image,Image *resize_image,const MagickRealType y_factor,
2275 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2285 **restrict contributions;
2301 Apply filter to resize vertically from image to resize image.
2303 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
2304 support=scale*GetResizeFilterSupport(resize_filter);
2305 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2306 if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
2307 return(MagickFalse);
2311 Support too small even for nearest neighbour: Reduce to point sampling.
2313 support=(MagickRealType) 0.5;
2316 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2317 if (contributions == (ContributionInfo **) NULL)
2319 (void) ThrowMagickException(exception,GetMagickModule(),
2320 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2321 return(MagickFalse);
2325 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2326 image_view=AcquireCacheView(image);
2327 resize_view=AcquireCacheView(resize_image);
2328 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2329 #pragma omp parallel for shared(status)
2331 for (y=0; y < (ssize_t) resize_image->rows; y++)
2337 register const Quantum
2340 register ContributionInfo
2341 *restrict contribution;
2354 if (status == MagickFalse)
2356 bisect=(MagickRealType) (y+0.5)/y_factor;
2357 start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
2358 stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->rows);
2360 contribution=contributions[GetOpenMPThreadId()];
2361 for (n=0; n < (stop-start); n++)
2363 contribution[n].pixel=start+n;
2364 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2365 ((MagickRealType) (start+n)-bisect+0.5));
2366 density+=contribution[n].weight;
2368 if ((density != 0.0) && (density != 1.0))
2376 density=1.0/density;
2377 for (i=0; i < n; i++)
2378 contribution[i].weight*=density;
2380 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
2381 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2383 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2385 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2390 for (x=0; x < (ssize_t) resize_image->columns; x++)
2395 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2415 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2416 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
2417 resize_traits=GetPixelChannelMapTraits(resize_image,channel);
2418 if ((traits == UndefinedPixelTrait) ||
2419 (resize_traits == UndefinedPixelTrait))
2421 if ((resize_traits & CopyPixelTrait) != 0)
2423 j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
2425 k=(ssize_t) ((contribution[j-start].pixel-contribution[0].pixel)*
2427 q[channel]=p[k*GetPixelChannels(image)+i];
2431 if ((resize_traits & BlendPixelTrait) == 0)
2436 for (j=0; j < n; j++)
2438 k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
2440 alpha=contribution[j].weight;
2441 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2443 q[channel]=ClampToQuantum(pixel);
2447 for (j=0; j < n; j++)
2449 k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
2451 alpha=contribution[j].weight*QuantumScale*
2452 GetPixelAlpha(image,p+k*GetPixelChannels(image));
2453 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2456 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2457 q[channel]=ClampToQuantum(gamma*pixel);
2459 q+=GetPixelChannels(resize_image);
2461 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2463 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2468 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2469 #pragma omp critical (MagickCore_VerticalFilter)
2471 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2472 if (proceed == MagickFalse)
2476 resize_view=DestroyCacheView(resize_view);
2477 image_view=DestroyCacheView(image_view);
2478 contributions=DestroyContributionThreadSet(contributions);
2482 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2483 const size_t rows,const FilterTypes filter,const double blur,
2484 ExceptionInfo *exception)
2486 #define WorkLoadFactor 0.265
2512 Acquire resize image.
2514 assert(image != (Image *) NULL);
2515 assert(image->signature == MagickSignature);
2516 if (image->debug != MagickFalse)
2517 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2518 assert(exception != (ExceptionInfo *) NULL);
2519 assert(exception->signature == MagickSignature);
2520 if ((columns == 0) || (rows == 0))
2521 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2522 if ((columns == image->columns) && (rows == image->rows) &&
2523 (filter == UndefinedFilter) && (blur == 1.0))
2524 return(CloneImage(image,0,0,MagickTrue,exception));
2525 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2526 if (resize_image == (Image *) NULL)
2527 return(resize_image);
2529 Acquire resize filter.
2531 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2532 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2533 if ((x_factor*y_factor) > WorkLoadFactor)
2534 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2536 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2537 if (filter_image == (Image *) NULL)
2538 return(DestroyImage(resize_image));
2539 filter_type=LanczosFilter;
2540 if (filter != UndefinedFilter)
2543 if ((x_factor == 1.0) && (y_factor == 1.0))
2544 filter_type=PointFilter;
2546 if ((image->storage_class == PseudoClass) ||
2547 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2548 filter_type=MitchellFilter;
2549 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2555 if ((x_factor*y_factor) > WorkLoadFactor)
2557 span=(MagickSizeType) (filter_image->columns+rows);
2558 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
2560 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
2561 span,&offset,exception);
2565 span=(MagickSizeType) (filter_image->rows+columns);
2566 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
2568 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
2569 span,&offset,exception);
2574 filter_image=DestroyImage(filter_image);
2575 resize_filter=DestroyResizeFilter(resize_filter);
2576 if (status == MagickFalse)
2578 resize_image=DestroyImage(resize_image);
2579 return((Image *) NULL);
2581 resize_image->type=image->type;
2582 return(resize_image);
2586 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2590 % S a m p l e I m a g e %
2594 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2596 % SampleImage() scales an image to the desired dimensions with pixel
2597 % sampling. Unlike other scaling methods, this method does not introduce
2598 % any additional color into the scaled image.
2600 % The format of the SampleImage method is:
2602 % Image *SampleImage(const Image *image,const size_t columns,
2603 % const size_t rows,ExceptionInfo *exception)
2605 % A description of each parameter follows:
2607 % o image: the image.
2609 % o columns: the number of columns in the sampled image.
2611 % o rows: the number of rows in the sampled image.
2613 % o exception: return any errors or warnings in this structure.
2616 MagickExport Image *SampleImage(const Image *image,const size_t columns,
2617 const size_t rows,ExceptionInfo *exception)
2619 #define SampleImageTag "Sample/Image"
2642 Initialize sampled image attributes.
2644 assert(image != (const Image *) NULL);
2645 assert(image->signature == MagickSignature);
2646 if (image->debug != MagickFalse)
2647 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2648 assert(exception != (ExceptionInfo *) NULL);
2649 assert(exception->signature == MagickSignature);
2650 if ((columns == 0) || (rows == 0))
2651 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2652 if ((columns == image->columns) && (rows == image->rows))
2653 return(CloneImage(image,0,0,MagickTrue,exception));
2654 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2655 if (sample_image == (Image *) NULL)
2656 return((Image *) NULL);
2658 Allocate scan line buffer and column offset buffers.
2660 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
2662 if (x_offset == (ssize_t *) NULL)
2664 sample_image=DestroyImage(sample_image);
2665 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2667 for (x=0; x < (ssize_t) sample_image->columns; x++)
2668 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
2669 sample_image->columns);
2675 image_view=AcquireCacheView(image);
2676 sample_view=AcquireCacheView(sample_image);
2677 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2678 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
2680 for (y=0; y < (ssize_t) sample_image->rows; y++)
2682 register const Quantum
2694 if (status == MagickFalse)
2696 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2697 sample_image->rows);
2698 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2700 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2702 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2710 for (x=0; x < (ssize_t) sample_image->columns; x++)
2715 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2724 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2725 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
2726 sample_traits=GetPixelChannelMapTraits(sample_image,channel);
2727 if ((traits == UndefinedPixelTrait) ||
2728 (sample_traits == UndefinedPixelTrait))
2730 q[channel]=p[x_offset[x]*GetPixelChannels(image)+i];
2732 q+=GetPixelChannels(sample_image);
2734 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2736 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2741 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2742 #pragma omp critical (MagickCore_SampleImage)
2744 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2745 if (proceed == MagickFalse)
2749 image_view=DestroyCacheView(image_view);
2750 sample_view=DestroyCacheView(sample_view);
2751 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
2752 sample_image->type=image->type;
2753 return(sample_image);
2757 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2761 % S c a l e I m a g e %
2765 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2767 % ScaleImage() changes the size of an image to the given dimensions.
2769 % The format of the ScaleImage method is:
2771 % Image *ScaleImage(const Image *image,const size_t columns,
2772 % const size_t rows,ExceptionInfo *exception)
2774 % A description of each parameter follows:
2776 % o image: the image.
2778 % o columns: the number of columns in the scaled image.
2780 % o rows: the number of rows in the scaled image.
2782 % o exception: return any errors or warnings in this structure.
2785 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2786 const size_t rows,ExceptionInfo *exception)
2788 #define ScaleImageTag "Scale/Image"
2805 pixel[MaxPixelChannels],
2831 Initialize scaled image attributes.
2833 assert(image != (const Image *) NULL);
2834 assert(image->signature == MagickSignature);
2835 if (image->debug != MagickFalse)
2836 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2837 assert(exception != (ExceptionInfo *) NULL);
2838 assert(exception->signature == MagickSignature);
2839 if ((columns == 0) || (rows == 0))
2840 return((Image *) NULL);
2841 if ((columns == image->columns) && (rows == image->rows))
2842 return(CloneImage(image,0,0,MagickTrue,exception));
2843 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2844 if (scale_image == (Image *) NULL)
2845 return((Image *) NULL);
2846 if (SetImageStorageClass(scale_image,DirectClass,exception) == MagickFalse)
2848 scale_image=DestroyImage(scale_image);
2849 return((Image *) NULL);
2854 x_vector=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
2855 GetPixelChannels(image)*sizeof(*x_vector));
2857 if (image->rows != scale_image->rows)
2858 scanline=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
2859 GetPixelChannels(image)*sizeof(*scanline));
2860 scale_scanline=(MagickRealType *) AcquireQuantumMemory((size_t)
2861 scale_image->columns,GetPixelChannels(scale_image)*sizeof(*scale_scanline));
2862 y_vector=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
2863 GetPixelChannels(image)*sizeof(*y_vector));
2864 if ((scanline == (MagickRealType *) NULL) ||
2865 (scale_scanline == (MagickRealType *) NULL) ||
2866 (x_vector == (MagickRealType *) NULL) ||
2867 (y_vector == (MagickRealType *) NULL))
2869 scale_image=DestroyImage(scale_image);
2870 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2876 next_row=MagickTrue;
2878 scale.y=(double) scale_image->rows/(double) image->rows;
2879 for (i=0; i < (ssize_t) (GetPixelChannels(image)*image->columns); i++)
2882 image_view=AcquireCacheView(image);
2883 scale_view=AcquireCacheView(scale_image);
2884 for (y=0; y < (ssize_t) scale_image->rows; y++)
2886 register const Quantum
2895 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2897 if (q == (Quantum *) NULL)
2900 if (scale_image->rows == image->rows)
2903 Read a new scanline.
2905 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
2907 if (p == (const Quantum *) NULL)
2909 for (x=0; x < (ssize_t) image->columns; x++)
2911 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2913 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2914 if ((traits & BlendPixelTrait) == 0)
2916 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType) p[i];
2919 alpha=QuantumScale*GetPixelAlpha(image,p);
2920 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
2922 p+=GetPixelChannels(image);
2930 while (scale.y < span.y)
2932 if ((next_row != MagickFalse) &&
2933 (number_rows < (ssize_t) image->rows))
2936 Read a new scanline.
2938 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
2940 if (p == (const Quantum *) NULL)
2942 for (x=0; x < (ssize_t) image->columns; x++)
2944 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2946 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2947 if ((traits & BlendPixelTrait) == 0)
2949 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType)
2953 alpha=QuantumScale*GetPixelAlpha(image,p);
2954 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
2956 p+=GetPixelChannels(image);
2960 for (x=0; x < (ssize_t) image->columns; x++)
2961 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2962 y_vector[x*GetPixelChannels(image)+i]+=scale.y*
2963 x_vector[x*GetPixelChannels(image)+i];
2965 scale.y=(double) scale_image->rows/(double) image->rows;
2966 next_row=MagickTrue;
2968 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
2971 Read a new scanline.
2973 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
2975 if (p == (const Quantum *) NULL)
2977 for (x=0; x < (ssize_t) image->columns; x++)
2979 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2981 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2982 if ((traits & BlendPixelTrait) == 0)
2984 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType) p[i];
2987 alpha=QuantumScale*GetPixelAlpha(image,p);
2988 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
2990 p+=GetPixelChannels(image);
2993 next_row=MagickFalse;
2995 for (x=0; x < (ssize_t) image->columns; x++)
2997 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2999 pixel[i]=y_vector[x*GetPixelChannels(image)+i]+span.y*
3000 x_vector[x*GetPixelChannels(image)+i];
3001 scanline[x*GetPixelChannels(image)+i]=pixel[i];
3002 y_vector[x*GetPixelChannels(image)+i]=0.0;
3008 scale.y=(double) scale_image->rows/(double) image->rows;
3009 next_row=MagickTrue;
3013 if (scale_image->columns == image->columns)
3016 Transfer scanline to scaled image.
3018 for (x=0; x < (ssize_t) scale_image->columns; x++)
3020 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3022 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3023 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3024 scale_traits=GetPixelChannelMapTraits(scale_image,channel);
3025 if ((traits == UndefinedPixelTrait) ||
3026 (scale_traits == UndefinedPixelTrait))
3028 if ((scale_traits & BlendPixelTrait) == 0)
3030 q[channel]=ClampToQuantum(scanline[x*
3031 GetPixelChannels(image)+i]);
3034 alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
3035 GetPixelChannelMapChannel(image,AlphaPixelChannel)];
3036 gamma=1.0/(fabs((double) alpha) <= MagickEpsilon ? 1.0 : alpha);
3037 q[channel]=ClampToQuantum(gamma*scanline[x*GetPixelChannels(image)+
3040 q+=GetPixelChannels(scale_image);
3051 next_column=MagickFalse;
3054 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3056 for (x=0; x < (ssize_t) image->columns; x++)
3058 scale.x=(double) scale_image->columns/(double) image->columns;
3059 while (scale.x >= span.x)
3061 if (next_column != MagickFalse)
3063 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3067 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3069 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3070 if (traits == UndefinedPixelTrait)
3072 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3073 pixel[i]+=span.x*scanline[x*GetPixelChannels(image)+i];
3074 scale_scanline[n*GetPixelChannels(scale_image)+channel]=pixel[i];
3078 next_column=MagickTrue;
3082 if (next_column != MagickFalse)
3084 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3087 next_column=MagickFalse;
3089 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3090 pixel[i]+=scale.x*scanline[x*GetPixelChannels(image)+i];
3096 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3097 pixel[i]+=span.x*scanline[(x-1)*GetPixelChannels(image)+i];
3099 if ((next_column == MagickFalse) &&
3100 ((ssize_t) n < (ssize_t) scale_image->columns))
3101 for (i=0; i < (ssize_t) GetPixelChannels(scale_image); i++)
3102 scale_scanline[n*GetPixelChannels(scale_image)+i]=pixel[i];
3104 Transfer scanline to scaled image.
3106 for (x=0; x < (ssize_t) scale_image->columns; x++)
3108 for (i=0; i < (ssize_t) GetPixelChannels(scale_image); i++)
3110 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3111 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3112 scale_traits=GetPixelChannelMapTraits(scale_image,channel);
3113 if ((traits == UndefinedPixelTrait) ||
3114 (scale_traits == UndefinedPixelTrait))
3116 if ((scale_traits & BlendPixelTrait) == 0)
3118 q[channel]=ClampToQuantum(scale_scanline[x*
3119 GetPixelChannels(scale_image)+channel]);
3122 alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
3123 GetPixelChannelMapChannel(image,AlphaPixelChannel)];
3124 gamma=1.0/(fabs((double) alpha) <= MagickEpsilon ? 1.0 : alpha);
3125 q[channel]=ClampToQuantum(gamma*scale_scanline[
3126 x*GetPixelChannels(scale_image)+channel]);
3128 q+=GetPixelChannels(scale_image);
3131 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
3133 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3135 if (proceed == MagickFalse)
3138 scale_view=DestroyCacheView(scale_view);
3139 image_view=DestroyCacheView(image_view);
3141 Free allocated memory.
3143 y_vector=(MagickRealType *) RelinquishMagickMemory(y_vector);
3144 scale_scanline=(MagickRealType *) RelinquishMagickMemory(scale_scanline);
3145 if (scale_image->rows != image->rows)
3146 scanline=(MagickRealType *) RelinquishMagickMemory(scanline);
3147 x_vector=(MagickRealType *) RelinquishMagickMemory(x_vector);
3148 scale_image->type=image->type;
3149 return(scale_image);
3153 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3157 % T h u m b n a i l I m a g e %
3161 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3163 % ThumbnailImage() changes the size of an image to the given dimensions and
3164 % removes any associated profiles. The goal is to produce small low cost
3165 % thumbnail images suited for display on the Web.
3167 % The format of the ThumbnailImage method is:
3169 % Image *ThumbnailImage(const Image *image,const size_t columns,
3170 % const size_t rows,ExceptionInfo *exception)
3172 % A description of each parameter follows:
3174 % o image: the image.
3176 % o columns: the number of columns in the scaled image.
3178 % o rows: the number of rows in the scaled image.
3180 % o exception: return any errors or warnings in this structure.
3183 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3184 const size_t rows,ExceptionInfo *exception)
3186 #define SampleFactor 5
3189 value[MaxTextExtent];
3207 assert(image != (Image *) NULL);
3208 assert(image->signature == MagickSignature);
3209 if (image->debug != MagickFalse)
3210 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3211 assert(exception != (ExceptionInfo *) NULL);
3212 assert(exception->signature == MagickSignature);
3213 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3214 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3215 if ((x_factor*y_factor) > 0.1)
3216 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3219 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
3220 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3221 image->blur,exception);
3227 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3229 if (sample_image == (Image *) NULL)
3230 return((Image *) NULL);
3231 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3232 image->blur,exception);
3233 sample_image=DestroyImage(sample_image);
3235 if (thumbnail_image == (Image *) NULL)
3236 return(thumbnail_image);
3237 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3238 if (thumbnail_image->matte == MagickFalse)
3239 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel,exception);
3240 thumbnail_image->depth=8;
3241 thumbnail_image->interlace=NoInterlace;
3243 Strip all profiles except color profiles.
3245 ResetImageProfileIterator(thumbnail_image);
3246 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3248 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3250 (void) DeleteImageProfile(thumbnail_image,name);
3251 ResetImageProfileIterator(thumbnail_image);
3253 name=GetNextImageProfile(thumbnail_image);
3255 (void) DeleteImageProperty(thumbnail_image,"comment");
3256 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3257 if (strstr(image->magick_filename,"//") == (char *) NULL)
3258 (void) FormatLocaleString(value,MaxTextExtent,"file://%s",
3259 image->magick_filename);
3260 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3261 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3262 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3264 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3265 attributes.st_mtime);
3266 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3268 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3269 attributes.st_mtime);
3270 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
3271 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
3272 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3273 (void) FormatLocaleString(value,MaxTextExtent,"image/%s",image->magick);
3275 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3276 (void) SetImageProperty(thumbnail_image,"software",
3277 GetMagickVersion(&version));
3278 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3279 image->magick_columns);
3280 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
3281 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3282 image->magick_rows);
3283 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
3284 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3285 GetImageListLength(image));
3286 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3287 return(thumbnail_image);