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 */
936 if (GaussianFilter != (FilterTypes) NULL)
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,ExceptionInfo *exception)
1145 % A description of each parameter follows:
1147 % o image: the image.
1149 % o columns: the number of columns in the resized image.
1151 % o rows: the number of rows in the resized image.
1153 % o exception: return any errors or warnings in this structure.
1156 MagickExport Image *AdaptiveResizeImage(const Image *image,
1157 const size_t columns,const size_t rows,ExceptionInfo *exception)
1159 #define AdaptiveResizeImageTag "Resize/Image"
1178 Adaptively resize image.
1180 assert(image != (const Image *) NULL);
1181 assert(image->signature == MagickSignature);
1182 if (image->debug != MagickFalse)
1183 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1184 assert(exception != (ExceptionInfo *) NULL);
1185 assert(exception->signature == MagickSignature);
1186 if ((columns == 0) || (rows == 0))
1187 return((Image *) NULL);
1188 if ((columns == image->columns) && (rows == image->rows))
1189 return(CloneImage(image,0,0,MagickTrue,exception));
1190 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1191 if (resize_image == (Image *) NULL)
1192 return((Image *) NULL);
1193 if (SetImageStorageClass(resize_image,DirectClass,exception) == MagickFalse)
1195 resize_image=DestroyImage(resize_image);
1196 return((Image *) NULL);
1200 image_view=AcquireCacheView(image);
1201 resize_view=AcquireCacheView(resize_image);
1202 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1203 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
1205 for (y=0; y < (ssize_t) resize_image->rows; y++)
1216 if (status == MagickFalse)
1218 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1220 if (q == (Quantum *) NULL)
1222 offset.y=((MagickRealType) (y+0.5)*image->rows/resize_image->rows);
1223 for (x=0; x < (ssize_t) resize_image->columns; x++)
1228 offset.x=((MagickRealType) (x+0.5)*image->columns/resize_image->columns);
1229 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1241 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1242 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
1243 resize_traits=GetPixelChannelMapTraits(resize_image,channel);
1244 if ((traits == UndefinedPixelTrait) ||
1245 (resize_traits == UndefinedPixelTrait))
1247 status=InterpolatePixelChannel(image,image_view,(PixelChannel) i,
1248 MeshInterpolatePixel,offset.x-0.5,offset.y-0.5,&pixel,exception);
1249 q[channel]=ClampToQuantum(pixel);
1251 q+=GetPixelChannels(resize_image);
1253 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1255 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1260 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1261 #pragma omp critical (MagickCore_AdaptiveResizeImage)
1263 proceed=SetImageProgress(image,AdaptiveResizeImageTag,progress++,
1265 if (proceed == MagickFalse)
1269 resize_view=DestroyCacheView(resize_view);
1270 image_view=DestroyCacheView(image_view);
1271 if (status == MagickFalse)
1272 resize_image=DestroyImage(resize_image);
1273 return(resize_image);
1277 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1281 + B e s s e l O r d e r O n e %
1285 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1287 % BesselOrderOne() computes the Bessel function of x of the first kind of
1288 % order 0. This is used to create the Jinc() filter function below.
1290 % Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1296 % j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1298 % where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1300 % cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1301 % = 1/sqrt(2) * (sin(x) - cos(x))
1302 % sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1303 % = -1/sqrt(2) * (sin(x) + cos(x))
1305 % The format of the BesselOrderOne method is:
1307 % MagickRealType BesselOrderOne(MagickRealType x)
1309 % A description of each parameter follows:
1311 % o x: MagickRealType value.
1316 static MagickRealType I0(MagickRealType x)
1327 Zeroth order Bessel function of the first kind.
1332 for (i=2; t > MagickEpsilon; i++)
1335 t*=y/((MagickRealType) i*i);
1341 static MagickRealType J1(MagickRealType x)
1353 0.581199354001606143928050809e+21,
1354 -0.6672106568924916298020941484e+20,
1355 0.2316433580634002297931815435e+19,
1356 -0.3588817569910106050743641413e+17,
1357 0.2908795263834775409737601689e+15,
1358 -0.1322983480332126453125473247e+13,
1359 0.3413234182301700539091292655e+10,
1360 -0.4695753530642995859767162166e+7,
1361 0.270112271089232341485679099e+4
1365 0.11623987080032122878585294e+22,
1366 0.1185770712190320999837113348e+20,
1367 0.6092061398917521746105196863e+17,
1368 0.2081661221307607351240184229e+15,
1369 0.5243710262167649715406728642e+12,
1370 0.1013863514358673989967045588e+10,
1371 0.1501793594998585505921097578e+7,
1372 0.1606931573481487801970916749e+4,
1378 for (i=7; i >= 0; i--)
1387 static MagickRealType P1(MagickRealType x)
1399 0.352246649133679798341724373e+5,
1400 0.62758845247161281269005675e+5,
1401 0.313539631109159574238669888e+5,
1402 0.49854832060594338434500455e+4,
1403 0.2111529182853962382105718e+3,
1404 0.12571716929145341558495e+1
1408 0.352246649133679798068390431e+5,
1409 0.626943469593560511888833731e+5,
1410 0.312404063819041039923015703e+5,
1411 0.4930396490181088979386097e+4,
1412 0.2030775189134759322293574e+3,
1418 for (i=4; i >= 0; i--)
1420 p=p*(8.0/x)*(8.0/x)+Pone[i];
1421 q=q*(8.0/x)*(8.0/x)+Qone[i];
1427 static MagickRealType Q1(MagickRealType x)
1439 0.3511751914303552822533318e+3,
1440 0.7210391804904475039280863e+3,
1441 0.4259873011654442389886993e+3,
1442 0.831898957673850827325226e+2,
1443 0.45681716295512267064405e+1,
1444 0.3532840052740123642735e-1
1448 0.74917374171809127714519505e+4,
1449 0.154141773392650970499848051e+5,
1450 0.91522317015169922705904727e+4,
1451 0.18111867005523513506724158e+4,
1452 0.1038187585462133728776636e+3,
1458 for (i=4; i >= 0; i--)
1460 p=p*(8.0/x)*(8.0/x)+Pone[i];
1461 q=q*(8.0/x)*(8.0/x)+Qone[i];
1466 static MagickRealType BesselOrderOne(MagickRealType x)
1479 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1480 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1488 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1492 + D e s t r o y R e s i z e F i l t e r %
1496 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1498 % DestroyResizeFilter() destroy the resize filter.
1500 % The format of the DestroyResizeFilter method is:
1502 % ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1504 % A description of each parameter follows:
1506 % o resize_filter: the resize filter.
1509 MagickPrivate ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1511 assert(resize_filter != (ResizeFilter *) NULL);
1512 assert(resize_filter->signature == MagickSignature);
1513 resize_filter->signature=(~MagickSignature);
1514 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1515 return(resize_filter);
1519 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1523 + G e t R e s i z e F i l t e r S u p p o r t %
1527 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1529 % GetResizeFilterSupport() return the current support window size for this
1530 % filter. Note that this may have been enlarged by filter:blur factor.
1532 % The format of the GetResizeFilterSupport method is:
1534 % MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1536 % A description of each parameter follows:
1538 % o filter: Image filter to use.
1541 MagickPrivate MagickRealType GetResizeFilterSupport(
1542 const ResizeFilter *resize_filter)
1544 assert(resize_filter != (ResizeFilter *) NULL);
1545 assert(resize_filter->signature == MagickSignature);
1546 return(resize_filter->support*resize_filter->blur);
1550 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1554 + G e t R e s i z e F i l t e r W e i g h t %
1558 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1560 % GetResizeFilterWeight evaluates the specified resize filter at the point x
1561 % which usally lies between zero and the filters current 'support' and
1562 % returns the weight of the filter function at that point.
1564 % The format of the GetResizeFilterWeight method is:
1566 % MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1567 % const MagickRealType x)
1569 % A description of each parameter follows:
1571 % o filter: the filter type.
1576 MagickPrivate MagickRealType GetResizeFilterWeight(
1577 const ResizeFilter *resize_filter,const MagickRealType x)
1585 Windowing function - scale the weighting filter by this amount.
1587 assert(resize_filter != (ResizeFilter *) NULL);
1588 assert(resize_filter->signature == MagickSignature);
1589 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
1590 if ((resize_filter->window_support < MagickEpsilon) ||
1591 (resize_filter->window == Box))
1592 scale=1.0; /* Point or Box Filter -- avoid division by zero */
1595 scale=resize_filter->scale;
1596 scale=resize_filter->window(x_blur*scale,resize_filter);
1598 weight=scale*resize_filter->filter(x_blur,resize_filter);
1601 #if defined(MAGICKCORE_LQR_DELEGATE)
1604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1608 % L i q u i d R e s c a l e I m a g e %
1612 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1614 % LiquidRescaleImage() rescales image with seam carving.
1616 % The format of the LiquidRescaleImage method is:
1618 % 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 % A description of each parameter follows:
1624 % o image: the image.
1626 % o columns: the number of columns in the rescaled image.
1628 % o rows: the number of rows in the rescaled image.
1630 % o delta_x: maximum seam transversal step (0 means straight seams).
1632 % o rigidity: introduce a bias for non-straight seams (typically 0).
1634 % o exception: return any errors or warnings in this structure.
1637 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1638 const size_t rows,const double delta_x,const double rigidity,
1639 ExceptionInfo *exception)
1641 #define LiquidRescaleImageTag "Rescale/Image"
1674 Liquid rescale image.
1676 assert(image != (const Image *) NULL);
1677 assert(image->signature == MagickSignature);
1678 if (image->debug != MagickFalse)
1679 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1680 assert(exception != (ExceptionInfo *) NULL);
1681 assert(exception->signature == MagickSignature);
1682 if ((columns == 0) || (rows == 0))
1683 return((Image *) NULL);
1684 if ((columns == image->columns) && (rows == image->rows))
1685 return(CloneImage(image,0,0,MagickTrue,exception));
1686 if ((columns <= 2) || (rows <= 2))
1687 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
1688 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1698 Honor liquid resize size limitations.
1700 for (width=image->columns; columns >= (2*width-1); width*=2);
1701 for (height=image->rows; rows >= (2*height-1); height*=2);
1702 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1704 if (resize_image == (Image *) NULL)
1705 return((Image *) NULL);
1706 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1707 rigidity,exception);
1708 resize_image=DestroyImage(resize_image);
1709 return(rescale_image);
1711 pixels=(gfloat *) AcquireQuantumMemory(image->columns,image->rows*
1712 GetPixelChannels(image)*sizeof(*pixels));
1713 if (pixels == (gfloat *) NULL)
1714 return((Image *) NULL);
1717 image_view=AcquireCacheView(image);
1718 for (y=0; y < (ssize_t) image->rows; y++)
1720 register const Quantum
1726 if (status == MagickFalse)
1728 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1729 if (p == (const Quantum *) NULL)
1734 for (x=0; x < (ssize_t) image->columns; x++)
1739 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1740 *q++=QuantumScale*p[i];
1741 p+=GetPixelChannels(image);
1744 image_view=DestroyCacheView(image_view);
1745 carver=lqr_carver_new_ext(pixels,image->columns,image->rows,
1746 GetPixelChannels(image),LQR_COLDEPTH_32F);
1747 if (carver == (LqrCarver *) NULL)
1749 pixels=(gfloat *) RelinquishMagickMemory(pixels);
1750 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1752 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1753 lqr_status=lqr_carver_resize(carver,columns,rows);
1755 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1756 lqr_carver_get_height(carver),MagickTrue,exception);
1757 if (rescale_image == (Image *) NULL)
1759 pixels=(gfloat *) RelinquishMagickMemory(pixels);
1760 return((Image *) NULL);
1762 if (SetImageStorageClass(rescale_image,DirectClass,exception) == MagickFalse)
1764 pixels=(gfloat *) RelinquishMagickMemory(pixels);
1765 rescale_image=DestroyImage(rescale_image);
1766 return((Image *) NULL);
1768 rescale_view=AcquireCacheView(rescale_image);
1769 (void) lqr_carver_scan_reset(carver);
1770 while (lqr_carver_scan_ext(carver,&x_offset,&y_offset,(void **) &packet) != 0)
1778 q=QueueCacheViewAuthenticPixels(rescale_view,x_offset,y_offset,1,1,
1780 if (q == (Quantum *) NULL)
1782 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1791 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1792 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
1793 rescale_traits=GetPixelChannelMapTraits(rescale_image,channel);
1794 if ((traits == UndefinedPixelTrait) ||
1795 (rescale_traits == UndefinedPixelTrait))
1797 q[channel]=ClampToQuantum(QuantumRange*packet[i]);
1799 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
1802 rescale_view=DestroyCacheView(rescale_view);
1803 lqr_carver_destroy(carver);
1804 return(rescale_image);
1807 MagickExport Image *LiquidRescaleImage(const Image *image,
1808 const size_t magick_unused(columns),const size_t magick_unused(rows),
1809 const double magick_unused(delta_x),const double magick_unused(rigidity),
1810 ExceptionInfo *exception)
1812 assert(image != (const Image *) NULL);
1813 assert(image->signature == MagickSignature);
1814 if (image->debug != MagickFalse)
1815 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1816 assert(exception != (ExceptionInfo *) NULL);
1817 assert(exception->signature == MagickSignature);
1818 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1819 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1820 return((Image *) NULL);
1825 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1829 % M a g n i f y I m a g e %
1833 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1835 % MagnifyImage() is a convenience method that scales an image proportionally
1836 % to twice its size.
1838 % The format of the MagnifyImage method is:
1840 % Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1842 % A description of each parameter follows:
1844 % o image: the image.
1846 % o exception: return any errors or warnings in this structure.
1849 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1854 assert(image != (Image *) NULL);
1855 assert(image->signature == MagickSignature);
1856 if (image->debug != MagickFalse)
1857 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1858 assert(exception != (ExceptionInfo *) NULL);
1859 assert(exception->signature == MagickSignature);
1860 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1862 return(magnify_image);
1866 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1870 % M i n i f y I m a g e %
1874 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1876 % MinifyImage() is a convenience method that scales an image proportionally to
1879 % The format of the MinifyImage method is:
1881 % Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1883 % A description of each parameter follows:
1885 % o image: the image.
1887 % o exception: return any errors or warnings in this structure.
1890 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1895 assert(image != (Image *) NULL);
1896 assert(image->signature == MagickSignature);
1897 if (image->debug != MagickFalse)
1898 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1899 assert(exception != (ExceptionInfo *) NULL);
1900 assert(exception->signature == MagickSignature);
1901 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,1.0,
1903 return(minify_image);
1907 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1911 % R e s a m p l e I m a g e %
1915 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1917 % ResampleImage() resize image in terms of its pixel size, so that when
1918 % displayed at the given resolution it will be the same size in terms of
1919 % real world units as the original image at the original resolution.
1921 % The format of the ResampleImage method is:
1923 % Image *ResampleImage(Image *image,const double x_resolution,
1924 % const double y_resolution,const FilterTypes filter,const double blur,
1925 % ExceptionInfo *exception)
1927 % A description of each parameter follows:
1929 % o image: the image to be resized to fit the given resolution.
1931 % o x_resolution: the new image x resolution.
1933 % o y_resolution: the new image y resolution.
1935 % o filter: Image filter to use.
1937 % o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1940 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1941 const double y_resolution,const FilterTypes filter,const double blur,
1942 ExceptionInfo *exception)
1944 #define ResampleImageTag "Resample/Image"
1954 Initialize sampled image attributes.
1956 assert(image != (const Image *) NULL);
1957 assert(image->signature == MagickSignature);
1958 if (image->debug != MagickFalse)
1959 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1960 assert(exception != (ExceptionInfo *) NULL);
1961 assert(exception->signature == MagickSignature);
1962 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1963 72.0 : image->x_resolution)+0.5);
1964 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1965 72.0 : image->y_resolution)+0.5);
1966 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1967 if (resample_image != (Image *) NULL)
1969 resample_image->x_resolution=x_resolution;
1970 resample_image->y_resolution=y_resolution;
1972 return(resample_image);
1976 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1980 % R e s i z e I m a g e %
1984 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1986 % ResizeImage() scales an image to the desired dimensions, using the given
1987 % filter (see AcquireFilterInfo()).
1989 % If an undefined filter is given the filter defaults to Mitchell for a
1990 % colormapped image, a image with a matte channel, or if the image is
1991 % enlarged. Otherwise the filter defaults to a Lanczos.
1993 % ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1995 % The format of the ResizeImage method is:
1997 % Image *ResizeImage(Image *image,const size_t columns,
1998 % const size_t rows,const FilterTypes filter,const double blur,
1999 % ExceptionInfo *exception)
2001 % A description of each parameter follows:
2003 % o image: the image.
2005 % o columns: the number of columns in the scaled image.
2007 % o rows: the number of rows in the scaled image.
2009 % o filter: Image filter to use.
2011 % o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
2014 % o exception: return any errors or warnings in this structure.
2018 typedef struct _ContributionInfo
2027 static ContributionInfo **DestroyContributionThreadSet(
2028 ContributionInfo **contribution)
2033 assert(contribution != (ContributionInfo **) NULL);
2034 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
2035 if (contribution[i] != (ContributionInfo *) NULL)
2036 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
2038 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
2039 return(contribution);
2042 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
2053 number_threads=GetOpenMPMaximumThreads();
2054 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
2055 sizeof(*contribution));
2056 if (contribution == (ContributionInfo **) NULL)
2057 return((ContributionInfo **) NULL);
2058 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
2059 for (i=0; i < (ssize_t) number_threads; i++)
2061 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2062 sizeof(**contribution));
2063 if (contribution[i] == (ContributionInfo *) NULL)
2064 return(DestroyContributionThreadSet(contribution));
2066 return(contribution);
2069 static inline double MagickMax(const double x,const double y)
2076 static inline double MagickMin(const double x,const double y)
2083 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2084 const Image *image,Image *resize_image,const MagickRealType x_factor,
2085 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2087 #define ResizeImageTag "Resize/Image"
2097 **restrict contributions;
2110 Apply filter to resize horizontally from image to resize image.
2112 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
2113 support=scale*GetResizeFilterSupport(resize_filter);
2114 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2115 if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
2116 return(MagickFalse);
2120 Support too small even for nearest neighbour: Reduce to point sampling.
2122 support=(MagickRealType) 0.5;
2125 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2126 if (contributions == (ContributionInfo **) NULL)
2128 (void) ThrowMagickException(exception,GetMagickModule(),
2129 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2130 return(MagickFalse);
2134 image_view=AcquireCacheView(image);
2135 resize_view=AcquireCacheView(resize_image);
2136 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2137 #pragma omp parallel for shared(status)
2139 for (x=0; x < (ssize_t) resize_image->columns; x++)
2145 register const Quantum
2148 register ContributionInfo
2149 *restrict contribution;
2162 if (status == MagickFalse)
2164 bisect=(MagickRealType) (x+0.5)/x_factor;
2165 start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
2166 stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->columns);
2168 contribution=contributions[GetOpenMPThreadId()];
2169 for (n=0; n < (stop-start); n++)
2171 contribution[n].pixel=start+n;
2172 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2173 ((MagickRealType) (start+n)-bisect+0.5));
2174 density+=contribution[n].weight;
2176 if ((density != 0.0) && (density != 1.0))
2184 density=1.0/density;
2185 for (i=0; i < n; i++)
2186 contribution[i].weight*=density;
2188 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2189 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
2190 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2192 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2197 for (y=0; y < (ssize_t) resize_image->rows; y++)
2202 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2222 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2223 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
2224 resize_traits=GetPixelChannelMapTraits(resize_image,channel);
2225 if ((traits == UndefinedPixelTrait) ||
2226 (resize_traits == UndefinedPixelTrait))
2228 if ((resize_traits & CopyPixelTrait) != 0)
2230 j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
2232 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2233 (contribution[j-start].pixel-contribution[0].pixel);
2234 q[channel]=p[k*GetPixelChannels(image)+i];
2238 if ((resize_traits & BlendPixelTrait) == 0)
2243 for (j=0; j < n; j++)
2245 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2246 (contribution[j].pixel-contribution[0].pixel);
2247 alpha=contribution[j].weight;
2248 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2250 q[channel]=ClampToQuantum(pixel);
2257 for (j=0; j < n; j++)
2259 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2260 (contribution[j].pixel-contribution[0].pixel);
2261 alpha=contribution[j].weight*QuantumScale*
2262 GetPixelAlpha(image,p+k*GetPixelChannels(image));
2263 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2266 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2267 q[channel]=ClampToQuantum(gamma*pixel);
2269 q+=GetPixelChannels(resize_image);
2271 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2273 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2278 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2279 #pragma omp critical (MagickCore_HorizontalFilter)
2281 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2282 if (proceed == MagickFalse)
2286 resize_view=DestroyCacheView(resize_view);
2287 image_view=DestroyCacheView(image_view);
2288 contributions=DestroyContributionThreadSet(contributions);
2292 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2293 const Image *image,Image *resize_image,const MagickRealType y_factor,
2294 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2304 **restrict contributions;
2320 Apply filter to resize vertically from image to resize image.
2322 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
2323 support=scale*GetResizeFilterSupport(resize_filter);
2324 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2325 if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
2326 return(MagickFalse);
2330 Support too small even for nearest neighbour: Reduce to point sampling.
2332 support=(MagickRealType) 0.5;
2335 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2336 if (contributions == (ContributionInfo **) NULL)
2338 (void) ThrowMagickException(exception,GetMagickModule(),
2339 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2340 return(MagickFalse);
2344 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2345 image_view=AcquireCacheView(image);
2346 resize_view=AcquireCacheView(resize_image);
2347 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2348 #pragma omp parallel for shared(status)
2350 for (y=0; y < (ssize_t) resize_image->rows; y++)
2356 register const Quantum
2359 register ContributionInfo
2360 *restrict contribution;
2373 if (status == MagickFalse)
2375 bisect=(MagickRealType) (y+0.5)/y_factor;
2376 start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
2377 stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->rows);
2379 contribution=contributions[GetOpenMPThreadId()];
2380 for (n=0; n < (stop-start); n++)
2382 contribution[n].pixel=start+n;
2383 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2384 ((MagickRealType) (start+n)-bisect+0.5));
2385 density+=contribution[n].weight;
2387 if ((density != 0.0) && (density != 1.0))
2395 density=1.0/density;
2396 for (i=0; i < n; i++)
2397 contribution[i].weight*=density;
2399 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
2400 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2402 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2404 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2409 for (x=0; x < (ssize_t) resize_image->columns; x++)
2414 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2434 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2435 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
2436 resize_traits=GetPixelChannelMapTraits(resize_image,channel);
2437 if ((traits == UndefinedPixelTrait) ||
2438 (resize_traits == UndefinedPixelTrait))
2440 if ((resize_traits & CopyPixelTrait) != 0)
2442 j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
2444 k=(ssize_t) ((contribution[j-start].pixel-contribution[0].pixel)*
2446 q[channel]=p[k*GetPixelChannels(image)+i];
2450 if ((resize_traits & BlendPixelTrait) == 0)
2455 for (j=0; j < n; j++)
2457 k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
2459 alpha=contribution[j].weight;
2460 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2462 q[channel]=ClampToQuantum(pixel);
2466 for (j=0; j < n; j++)
2468 k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
2470 alpha=contribution[j].weight*QuantumScale*
2471 GetPixelAlpha(image,p+k*GetPixelChannels(image));
2472 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2475 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2476 q[channel]=ClampToQuantum(gamma*pixel);
2478 q+=GetPixelChannels(resize_image);
2480 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2482 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2487 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2488 #pragma omp critical (MagickCore_VerticalFilter)
2490 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2491 if (proceed == MagickFalse)
2495 resize_view=DestroyCacheView(resize_view);
2496 image_view=DestroyCacheView(image_view);
2497 contributions=DestroyContributionThreadSet(contributions);
2501 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2502 const size_t rows,const FilterTypes filter,const double blur,
2503 ExceptionInfo *exception)
2505 #define WorkLoadFactor 0.265
2531 Acquire resize image.
2533 assert(image != (Image *) NULL);
2534 assert(image->signature == MagickSignature);
2535 if (image->debug != MagickFalse)
2536 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2537 assert(exception != (ExceptionInfo *) NULL);
2538 assert(exception->signature == MagickSignature);
2539 if ((columns == 0) || (rows == 0))
2540 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2541 if ((columns == image->columns) && (rows == image->rows) &&
2542 (filter == UndefinedFilter) && (blur == 1.0))
2543 return(CloneImage(image,0,0,MagickTrue,exception));
2544 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2545 if (resize_image == (Image *) NULL)
2546 return(resize_image);
2548 Acquire resize filter.
2550 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2551 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2552 if ((x_factor*y_factor) > WorkLoadFactor)
2553 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2555 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2556 if (filter_image == (Image *) NULL)
2557 return(DestroyImage(resize_image));
2558 filter_type=LanczosFilter;
2559 if (filter != UndefinedFilter)
2562 if ((x_factor == 1.0) && (y_factor == 1.0))
2563 filter_type=PointFilter;
2565 if ((image->storage_class == PseudoClass) ||
2566 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2567 filter_type=MitchellFilter;
2568 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2574 if ((x_factor*y_factor) > WorkLoadFactor)
2576 span=(MagickSizeType) (filter_image->columns+rows);
2577 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
2579 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
2580 span,&offset,exception);
2584 span=(MagickSizeType) (filter_image->rows+columns);
2585 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
2587 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
2588 span,&offset,exception);
2593 filter_image=DestroyImage(filter_image);
2594 resize_filter=DestroyResizeFilter(resize_filter);
2595 if (status == MagickFalse)
2597 resize_image=DestroyImage(resize_image);
2598 return((Image *) NULL);
2600 resize_image->type=image->type;
2601 return(resize_image);
2605 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2609 % S a m p l e I m a g e %
2613 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2615 % SampleImage() scales an image to the desired dimensions with pixel
2616 % sampling. Unlike other scaling methods, this method does not introduce
2617 % any additional color into the scaled image.
2619 % The format of the SampleImage method is:
2621 % Image *SampleImage(const Image *image,const size_t columns,
2622 % const size_t rows,ExceptionInfo *exception)
2624 % A description of each parameter follows:
2626 % o image: the image.
2628 % o columns: the number of columns in the sampled image.
2630 % o rows: the number of rows in the sampled image.
2632 % o exception: return any errors or warnings in this structure.
2635 MagickExport Image *SampleImage(const Image *image,const size_t columns,
2636 const size_t rows,ExceptionInfo *exception)
2638 #define SampleImageTag "Sample/Image"
2661 Initialize sampled image attributes.
2663 assert(image != (const Image *) NULL);
2664 assert(image->signature == MagickSignature);
2665 if (image->debug != MagickFalse)
2666 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2667 assert(exception != (ExceptionInfo *) NULL);
2668 assert(exception->signature == MagickSignature);
2669 if ((columns == 0) || (rows == 0))
2670 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2671 if ((columns == image->columns) && (rows == image->rows))
2672 return(CloneImage(image,0,0,MagickTrue,exception));
2673 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2674 if (sample_image == (Image *) NULL)
2675 return((Image *) NULL);
2677 Allocate scan line buffer and column offset buffers.
2679 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
2681 if (x_offset == (ssize_t *) NULL)
2683 sample_image=DestroyImage(sample_image);
2684 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2686 for (x=0; x < (ssize_t) sample_image->columns; x++)
2687 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
2688 sample_image->columns);
2694 image_view=AcquireCacheView(image);
2695 sample_view=AcquireCacheView(sample_image);
2696 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2697 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
2699 for (y=0; y < (ssize_t) sample_image->rows; y++)
2701 register const Quantum
2713 if (status == MagickFalse)
2715 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2716 sample_image->rows);
2717 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2719 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2721 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2729 for (x=0; x < (ssize_t) sample_image->columns; x++)
2734 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2743 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2744 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
2745 sample_traits=GetPixelChannelMapTraits(sample_image,channel);
2746 if ((traits == UndefinedPixelTrait) ||
2747 (sample_traits == UndefinedPixelTrait))
2749 q[channel]=p[x_offset[x]*GetPixelChannels(image)+i];
2751 q+=GetPixelChannels(sample_image);
2753 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2755 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2760 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2761 #pragma omp critical (MagickCore_SampleImage)
2763 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2764 if (proceed == MagickFalse)
2768 image_view=DestroyCacheView(image_view);
2769 sample_view=DestroyCacheView(sample_view);
2770 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
2771 sample_image->type=image->type;
2772 return(sample_image);
2776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2780 % S c a l e I m a g e %
2784 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2786 % ScaleImage() changes the size of an image to the given dimensions.
2788 % The format of the ScaleImage method is:
2790 % Image *ScaleImage(const Image *image,const size_t columns,
2791 % const size_t rows,ExceptionInfo *exception)
2793 % A description of each parameter follows:
2795 % o image: the image.
2797 % o columns: the number of columns in the scaled image.
2799 % o rows: the number of rows in the scaled image.
2801 % o exception: return any errors or warnings in this structure.
2804 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2805 const size_t rows,ExceptionInfo *exception)
2807 #define ScaleImageTag "Scale/Image"
2824 pixel[MaxPixelChannels],
2850 Initialize scaled image attributes.
2852 assert(image != (const Image *) NULL);
2853 assert(image->signature == MagickSignature);
2854 if (image->debug != MagickFalse)
2855 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2856 assert(exception != (ExceptionInfo *) NULL);
2857 assert(exception->signature == MagickSignature);
2858 if ((columns == 0) || (rows == 0))
2859 return((Image *) NULL);
2860 if ((columns == image->columns) && (rows == image->rows))
2861 return(CloneImage(image,0,0,MagickTrue,exception));
2862 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2863 if (scale_image == (Image *) NULL)
2864 return((Image *) NULL);
2865 if (SetImageStorageClass(scale_image,DirectClass,exception) == MagickFalse)
2867 scale_image=DestroyImage(scale_image);
2868 return((Image *) NULL);
2873 x_vector=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
2874 GetPixelChannels(image)*sizeof(*x_vector));
2876 if (image->rows != scale_image->rows)
2877 scanline=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
2878 GetPixelChannels(image)*sizeof(*scanline));
2879 scale_scanline=(MagickRealType *) AcquireQuantumMemory((size_t)
2880 scale_image->columns,GetPixelChannels(scale_image)*sizeof(*scale_scanline));
2881 y_vector=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
2882 GetPixelChannels(image)*sizeof(*y_vector));
2883 if ((scanline == (MagickRealType *) NULL) ||
2884 (scale_scanline == (MagickRealType *) NULL) ||
2885 (x_vector == (MagickRealType *) NULL) ||
2886 (y_vector == (MagickRealType *) NULL))
2888 scale_image=DestroyImage(scale_image);
2889 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2895 next_row=MagickTrue;
2897 scale.y=(double) scale_image->rows/(double) image->rows;
2898 for (i=0; i < (ssize_t) (GetPixelChannels(image)*image->columns); i++)
2901 image_view=AcquireCacheView(image);
2902 scale_view=AcquireCacheView(scale_image);
2903 for (y=0; y < (ssize_t) scale_image->rows; y++)
2905 register const Quantum
2914 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2916 if (q == (Quantum *) NULL)
2919 if (scale_image->rows == image->rows)
2922 Read a new scanline.
2924 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
2926 if (p == (const Quantum *) NULL)
2928 for (x=0; x < (ssize_t) image->columns; x++)
2930 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2932 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2933 if ((traits & BlendPixelTrait) == 0)
2935 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType) p[i];
2938 alpha=QuantumScale*GetPixelAlpha(image,p);
2939 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
2941 p+=GetPixelChannels(image);
2949 while (scale.y < span.y)
2951 if ((next_row != MagickFalse) &&
2952 (number_rows < (ssize_t) image->rows))
2955 Read a new scanline.
2957 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
2959 if (p == (const Quantum *) NULL)
2961 for (x=0; x < (ssize_t) image->columns; x++)
2963 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2965 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2966 if ((traits & BlendPixelTrait) == 0)
2968 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType)
2972 alpha=QuantumScale*GetPixelAlpha(image,p);
2973 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
2975 p+=GetPixelChannels(image);
2979 for (x=0; x < (ssize_t) image->columns; x++)
2980 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2981 y_vector[x*GetPixelChannels(image)+i]+=scale.y*
2982 x_vector[x*GetPixelChannels(image)+i];
2984 scale.y=(double) scale_image->rows/(double) image->rows;
2985 next_row=MagickTrue;
2987 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
2990 Read a new scanline.
2992 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
2994 if (p == (const Quantum *) NULL)
2996 for (x=0; x < (ssize_t) image->columns; x++)
2998 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3000 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3001 if ((traits & BlendPixelTrait) == 0)
3003 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType) p[i];
3006 alpha=QuantumScale*GetPixelAlpha(image,p);
3007 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
3009 p+=GetPixelChannels(image);
3012 next_row=MagickFalse;
3014 for (x=0; x < (ssize_t) image->columns; x++)
3016 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3018 pixel[i]=y_vector[x*GetPixelChannels(image)+i]+span.y*
3019 x_vector[x*GetPixelChannels(image)+i];
3020 scanline[x*GetPixelChannels(image)+i]=pixel[i];
3021 y_vector[x*GetPixelChannels(image)+i]=0.0;
3027 scale.y=(double) scale_image->rows/(double) image->rows;
3028 next_row=MagickTrue;
3032 if (scale_image->columns == image->columns)
3035 Transfer scanline to scaled image.
3037 for (x=0; x < (ssize_t) scale_image->columns; x++)
3039 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3041 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3042 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3043 scale_traits=GetPixelChannelMapTraits(scale_image,channel);
3044 if ((traits == UndefinedPixelTrait) ||
3045 (scale_traits == UndefinedPixelTrait))
3047 if ((scale_traits & BlendPixelTrait) == 0)
3049 q[channel]=ClampToQuantum(scanline[x*
3050 GetPixelChannels(image)+i]);
3053 alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
3054 GetPixelChannelMapChannel(image,AlphaPixelChannel)];
3055 gamma=1.0/(fabs((double) alpha) <= MagickEpsilon ? 1.0 : alpha);
3056 q[channel]=ClampToQuantum(gamma*scanline[x*GetPixelChannels(image)+
3059 q+=GetPixelChannels(scale_image);
3070 next_column=MagickFalse;
3073 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3075 for (x=0; x < (ssize_t) image->columns; x++)
3077 scale.x=(double) scale_image->columns/(double) image->columns;
3078 while (scale.x >= span.x)
3080 if (next_column != MagickFalse)
3082 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3086 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3088 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3089 if (traits == UndefinedPixelTrait)
3091 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3092 pixel[i]+=span.x*scanline[x*GetPixelChannels(image)+i];
3093 scale_scanline[n*GetPixelChannels(scale_image)+channel]=pixel[i];
3097 next_column=MagickTrue;
3101 if (next_column != MagickFalse)
3103 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3106 next_column=MagickFalse;
3108 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3109 pixel[i]+=scale.x*scanline[x*GetPixelChannels(image)+i];
3115 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3116 pixel[i]+=span.x*scanline[(x-1)*GetPixelChannels(image)+i];
3118 if ((next_column == MagickFalse) &&
3119 ((ssize_t) n < (ssize_t) scale_image->columns))
3120 for (i=0; i < (ssize_t) GetPixelChannels(scale_image); i++)
3121 scale_scanline[n*GetPixelChannels(scale_image)+i]=pixel[i];
3123 Transfer scanline to scaled image.
3125 for (x=0; x < (ssize_t) scale_image->columns; x++)
3127 for (i=0; i < (ssize_t) GetPixelChannels(scale_image); i++)
3129 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3130 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3131 scale_traits=GetPixelChannelMapTraits(scale_image,channel);
3132 if ((traits == UndefinedPixelTrait) ||
3133 (scale_traits == UndefinedPixelTrait))
3135 if ((scale_traits & BlendPixelTrait) == 0)
3137 q[channel]=ClampToQuantum(scale_scanline[x*
3138 GetPixelChannels(scale_image)+channel]);
3141 alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
3142 GetPixelChannelMapChannel(image,AlphaPixelChannel)];
3143 gamma=1.0/(fabs((double) alpha) <= MagickEpsilon ? 1.0 : alpha);
3144 q[channel]=ClampToQuantum(gamma*scale_scanline[
3145 x*GetPixelChannels(scale_image)+channel]);
3147 q+=GetPixelChannels(scale_image);
3150 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
3152 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3154 if (proceed == MagickFalse)
3157 scale_view=DestroyCacheView(scale_view);
3158 image_view=DestroyCacheView(image_view);
3160 Free allocated memory.
3162 y_vector=(MagickRealType *) RelinquishMagickMemory(y_vector);
3163 scale_scanline=(MagickRealType *) RelinquishMagickMemory(scale_scanline);
3164 if (scale_image->rows != image->rows)
3165 scanline=(MagickRealType *) RelinquishMagickMemory(scanline);
3166 x_vector=(MagickRealType *) RelinquishMagickMemory(x_vector);
3167 scale_image->type=image->type;
3168 return(scale_image);
3172 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3176 % T h u m b n a i l I m a g e %
3180 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3182 % ThumbnailImage() changes the size of an image to the given dimensions and
3183 % removes any associated profiles. The goal is to produce small low cost
3184 % thumbnail images suited for display on the Web.
3186 % The format of the ThumbnailImage method is:
3188 % Image *ThumbnailImage(const Image *image,const size_t columns,
3189 % const size_t rows,ExceptionInfo *exception)
3191 % A description of each parameter follows:
3193 % o image: the image.
3195 % o columns: the number of columns in the scaled image.
3197 % o rows: the number of rows in the scaled image.
3199 % o exception: return any errors or warnings in this structure.
3202 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3203 const size_t rows,ExceptionInfo *exception)
3205 #define SampleFactor 5
3208 value[MaxTextExtent];
3226 assert(image != (Image *) NULL);
3227 assert(image->signature == MagickSignature);
3228 if (image->debug != MagickFalse)
3229 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3230 assert(exception != (ExceptionInfo *) NULL);
3231 assert(exception->signature == MagickSignature);
3232 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3233 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3234 if ((x_factor*y_factor) > 0.1)
3235 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3238 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
3239 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3240 image->blur,exception);
3246 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3248 if (sample_image == (Image *) NULL)
3249 return((Image *) NULL);
3250 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3251 image->blur,exception);
3252 sample_image=DestroyImage(sample_image);
3254 if (thumbnail_image == (Image *) NULL)
3255 return(thumbnail_image);
3256 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3257 if (thumbnail_image->matte == MagickFalse)
3258 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel,exception);
3259 thumbnail_image->depth=8;
3260 thumbnail_image->interlace=NoInterlace;
3262 Strip all profiles except color profiles.
3264 ResetImageProfileIterator(thumbnail_image);
3265 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3267 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3269 (void) DeleteImageProfile(thumbnail_image,name);
3270 ResetImageProfileIterator(thumbnail_image);
3272 name=GetNextImageProfile(thumbnail_image);
3274 (void) DeleteImageProperty(thumbnail_image,"comment");
3275 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3276 if (strstr(image->magick_filename,"//") == (char *) NULL)
3277 (void) FormatLocaleString(value,MaxTextExtent,"file://%s",
3278 image->magick_filename);
3279 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3280 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3281 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3283 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3284 attributes.st_mtime);
3285 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3287 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3288 attributes.st_mtime);
3289 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
3290 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
3291 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3292 (void) FormatLocaleString(value,MaxTextExtent,"image/%s",image->magick);
3294 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3295 (void) SetImageProperty(thumbnail_image,"software",
3296 GetMagickVersion(&version));
3297 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3298 image->magick_columns);
3299 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
3300 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3301 image->magick_rows);
3302 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
3303 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3304 GetImageListLength(image));
3305 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3306 return(thumbnail_image);