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-2012 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/pixel-private.h"
65 #include "MagickCore/quantum-private.h"
66 #include "MagickCore/resample.h"
67 #include "MagickCore/resample-private.h"
68 #include "MagickCore/resize.h"
69 #include "MagickCore/resize-private.h"
70 #include "MagickCore/resource_.h"
71 #include "MagickCore/string_.h"
72 #include "MagickCore/string-private.h"
73 #include "MagickCore/thread-private.h"
74 #include "MagickCore/token.h"
75 #include "MagickCore/utility.h"
76 #include "MagickCore/utility-private.h"
77 #include "MagickCore/version.h"
78 #if defined(MAGICKCORE_LQR_DELEGATE)
88 (*filter)(const MagickRealType,const ResizeFilter *),
89 (*window)(const MagickRealType,const ResizeFilter *),
90 support, /* filter region of support - the filter support limit */
91 window_support, /* window support, usally equal to support (expert only) */
92 scale, /* dimension scaling to fit window support (usally 1.0) */
93 blur, /* x-scale (blur-sharpen) */
94 coefficient[7]; /* cubic coefficents for BC-cubic spline filters */
101 Forward declaractions.
103 static MagickRealType
104 I0(MagickRealType x),
105 BesselOrderOne(MagickRealType),
106 Sinc(const MagickRealType, const ResizeFilter *),
107 SincFast(const MagickRealType, const ResizeFilter *);
110 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114 + F i l t e r F u n c t i o n s %
118 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
120 % These are the various filter and windowing functions that are provided.
122 % They are internal to this module only. See AcquireResizeFilterInfo() for
123 % details of the access to these functions, via the GetResizeFilterSupport()
124 % and GetResizeFilterWeight() API interface.
126 % The individual filter functions have this format...
128 % static MagickRealtype *FilterName(const MagickRealType x,
129 % const MagickRealType support)
131 % A description of each parameter follows:
133 % o x: the distance from the sampling point generally in the range of 0 to
134 % support. The GetResizeFilterWeight() ensures this a positive value.
136 % o resize_filter: current filter information. This allows function to
137 % access support, and possibly other pre-calculated information defining
142 static MagickRealType Blackman(const MagickRealType x,
143 const ResizeFilter *magick_unused(resize_filter))
146 Blackman: 2nd order cosine windowing function:
147 0.42 + 0.5 cos(pi x) + 0.08 cos(2pi x)
149 Refactored by Chantal Racette and Nicolas Robidoux to one trig call and
152 const MagickRealType cosine=cos((double) (MagickPI*x));
153 return(0.34+cosine*(0.5+cosine*0.16));
156 static MagickRealType Bohman(const MagickRealType x,
157 const ResizeFilter *magick_unused(resize_filter))
160 Bohman: 2rd Order cosine windowing function:
161 (1-x) cos(pi x) + sin(pi x) / pi.
163 Refactored by Nicolas Robidoux to one trig call, one sqrt call, and 7 flops,
164 taking advantage of the fact that the support of Bohman is 1.0 (so that we
165 know that sin(pi x) >= 0).
167 const MagickRealType cosine=cos((double) (MagickPI*x));
168 const MagickRealType sine=sqrt(1.0-cosine*cosine);
169 return((1.0-x)*cosine+(1.0/MagickPI)*sine);
172 static MagickRealType Box(const MagickRealType magick_unused(x),
173 const ResizeFilter *magick_unused(resize_filter))
176 A Box filter is a equal weighting function (all weights equal).
177 DO NOT LIMIT results by support or resize point sampling will work
178 as it requests points beyond its normal 0.0 support size.
183 static MagickRealType Cosine(const MagickRealType x,
184 const ResizeFilter *magick_unused(resize_filter))
187 Cosine window function:
190 return((MagickRealType)cos((double) (MagickPI2*x)));
193 static MagickRealType CubicBC(const MagickRealType x,
194 const ResizeFilter *resize_filter)
197 Cubic Filters using B,C determined values:
198 Mitchell-Netravali B = 1/3 C = 1/3 "Balanced" cubic spline filter
199 Catmull-Rom B = 0 C = 1/2 Interpolatory and exact on linears
200 Cubic B-Spline B = 1 C = 0 Spline approximation of Gaussian
201 Hermite B = 0 C = 0 Spline with small support (= 1)
203 See paper by Mitchell and Netravali, Reconstruction Filters in Computer
204 Graphics Computer Graphics, Volume 22, Number 4, August 1988
205 http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/
208 Coefficents are determined from B,C values:
209 P0 = ( 6 - 2*B )/6 = coeff[0]
211 P2 = (-18 +12*B + 6*C )/6 = coeff[1]
212 P3 = ( 12 - 9*B - 6*C )/6 = coeff[2]
213 Q0 = ( 8*B +24*C )/6 = coeff[3]
214 Q1 = ( -12*B -48*C )/6 = coeff[4]
215 Q2 = ( 6*B +30*C )/6 = coeff[5]
216 Q3 = ( - 1*B - 6*C )/6 = coeff[6]
218 which are used to define the filter:
220 P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
221 Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x < 2
223 which ensures function is continuous in value and derivative (slope).
226 return(resize_filter->coefficient[0]+x*(x*
227 (resize_filter->coefficient[1]+x*resize_filter->coefficient[2])));
229 return(resize_filter->coefficient[3]+x*(resize_filter->coefficient[4]+x*
230 (resize_filter->coefficient[5]+x*resize_filter->coefficient[6])));
234 static MagickRealType Gaussian(const MagickRealType x,
235 const ResizeFilter *resize_filter)
238 Gaussian with a sigma = 1/2 (or as user specified)
240 Gaussian Formula (1D) ...
241 exp( -(x^2)/((2.0*sigma^2) ) / (sqrt(2*PI)*sigma^2))
243 Gaussian Formula (2D) ...
244 exp( -(x^2+y^2)/(2.0*sigma^2) ) / (PI*sigma^2) )
246 exp( -(r^2)/(2.0*sigma^2) ) / (PI*sigma^2) )
248 Note that it is only a change from 1-d to radial form is in the
249 normalization multiplier which is not needed or used when Gaussian is used
252 The constants are pre-calculated...
255 coeff[1]=1.0/(2.0*sigma^2);
256 coeff[2]=1.0/(sqrt(2*PI)*sigma^2);
258 exp( -coeff[1]*(x^2)) ) * coeff[2];
260 However the multiplier coeff[1] is need, the others are informative only.
262 This separates the gaussian 'sigma' value from the 'blur/support'
263 settings allowing for its use in special 'small sigma' gaussians,
264 without the filter 'missing' pixels because the support becomes too
267 return(exp((double)(-resize_filter->coefficient[1]*x*x)));
270 static MagickRealType Hanning(const MagickRealType x,
271 const ResizeFilter *magick_unused(resize_filter))
274 Cosine window function:
277 const MagickRealType cosine=cos((double) (MagickPI*x));
278 return(0.5+0.5*cosine);
281 static MagickRealType Hamming(const MagickRealType x,
282 const ResizeFilter *magick_unused(resize_filter))
285 Offset cosine window function:
288 const MagickRealType cosine=cos((double) (MagickPI*x));
289 return(0.54+0.46*cosine);
292 static MagickRealType Jinc(const MagickRealType x,
293 const ResizeFilter *magick_unused(resize_filter))
296 See Pratt "Digital Image Processing" p.97 for Jinc/Bessel functions.
297 http://mathworld.wolfram.com/JincFunction.html and page 11 of
298 http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
300 The original "zoom" program by Paul Heckbert called this "Bessel". But
301 really it is more accurately named "Jinc".
304 return(0.5*MagickPI);
305 return(BesselOrderOne(MagickPI*x)/x);
308 static MagickRealType Kaiser(const MagickRealType x,
309 const ResizeFilter *resize_filter)
312 Kaiser Windowing Function (bessel windowing)
314 I0( beta * sqrt( 1-x^2) ) / IO(0)
316 Beta (coeff[0]) is a free value from 5 to 8 (defaults to 6.5).
317 However it is typically defined in terms of Alpha*PI
319 The normalization factor (coeff[1]) is not actually needed,
320 but without it the filters has a large value at x=0 making it
321 difficult to compare the function with other windowing functions.
323 return(resize_filter->coefficient[1]*
324 I0(resize_filter->coefficient[0]*sqrt((double) (1.0-x*x))));
327 static MagickRealType Lagrange(const MagickRealType x,
328 const ResizeFilter *resize_filter)
341 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the lagrange
342 function and depends on the overall support window size of the filter. That
343 is: for a support of 2, it gives a lagrange-4 (piecewise cubic function).
345 "n" identifies the piece of the piecewise polynomial.
347 See Survey: Interpolation Methods, IEEE Transactions on Medical Imaging,
348 Vol 18, No 11, November 1999, p1049-1075, -- Equation 27 on p1064.
350 if (x > resize_filter->support)
352 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
353 n=(ssize_t) (resize_filter->window_support+x);
355 for (i=0; i < order; i++)
357 value*=(n-i-x)/(n-i);
361 static MagickRealType Quadratic(const MagickRealType x,
362 const ResizeFilter *magick_unused(resize_filter))
365 2rd order (quadratic) B-Spline approximation of Gaussian.
370 return(0.5*(x-1.5)*(x-1.5));
374 static MagickRealType Sinc(const MagickRealType x,
375 const ResizeFilter *magick_unused(resize_filter))
378 Scaled sinc(x) function using a trig call:
379 sinc(x) == sin(pi x)/(pi x).
383 const MagickRealType alpha=(MagickRealType) (MagickPI*x);
384 return(sin((double) alpha)/alpha);
386 return((MagickRealType) 1.0);
389 static MagickRealType SincFast(const MagickRealType x,
390 const ResizeFilter *magick_unused(resize_filter))
393 Approximations of the sinc function sin(pi x)/(pi x) over the interval
394 [-4,4] constructed by Nicolas Robidoux and Chantal Racette with funding
395 from the Natural Sciences and Engineering Research Council of Canada.
397 Although the approximations are polynomials (for low order of
398 approximation) and quotients of polynomials (for higher order of
399 approximation) and consequently are similar in form to Taylor polynomials /
400 Pade approximants, the approximations are computed with a completely
403 Summary: These approximations are "the best" in terms of bang (accuracy)
404 for the buck (flops). More specifically: Among the polynomial quotients
405 that can be computed using a fixed number of flops (with a given "+ - * /
406 budget"), the chosen polynomial quotient is the one closest to the
407 approximated function with respect to maximum absolute relative error over
410 The Remez algorithm, as implemented in the boost library's minimax package,
411 is the key to the construction: http://www.boost.org/doc/libs/1_36_0/libs/
412 math/doc/sf_and_dist/html/math_toolkit/backgrounders/remez.html
414 If outside of the interval of approximation, use the standard trig formula.
418 const MagickRealType alpha=(MagickRealType) (MagickPI*x);
419 return(sin((double) alpha)/alpha);
423 The approximations only depend on x^2 (sinc is an even function).
425 const MagickRealType xx = x*x;
426 #if MAGICKCORE_QUANTUM_DEPTH <= 8
428 Maximum absolute relative error 6.3e-6 < 1/2^17.
430 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
431 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
432 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
433 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
434 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
435 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
436 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
437 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
438 const MagickRealType p =
439 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
440 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
441 #elif MAGICKCORE_QUANTUM_DEPTH <= 16
443 Max. abs. rel. error 2.2e-8 < 1/2^25.
445 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
446 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
447 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
448 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
449 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
450 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
451 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
452 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
453 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
454 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
455 const MagickRealType p =
456 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
457 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
460 Max. abs. rel. error 1.2e-12 < 1/2^39.
462 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
463 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
464 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
465 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
466 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
467 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
468 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
469 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
470 const MagickRealType p =
471 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
472 const MagickRealType d0 = 1.0L;
473 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
474 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
475 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
476 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
477 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
478 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
483 static MagickRealType Triangle(const MagickRealType x,
484 const ResizeFilter *magick_unused(resize_filter))
487 1st order (linear) B-Spline, bilinear interpolation, Tent 1D filter, or
488 a Bartlett 2D Cone filter. Also used as a Bartlett Windowing function
496 static MagickRealType Welsh(const MagickRealType x,
497 const ResizeFilter *magick_unused(resize_filter))
500 Welsh parabolic windowing filter.
508 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
512 + A c q u i r e R e s i z e F i l t e r %
516 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
518 % AcquireResizeFilter() allocates the ResizeFilter structure. Choose from
521 % FIR (Finite impulse Response) Filters
522 % Box Triangle Quadratic
523 % Cubic Hermite Catrom
526 % IIR (Infinite impulse Response) Filters
527 % Gaussian Sinc Jinc (Bessel)
529 % Windowed Sinc/Jinc Filters
530 % Blackman Hanning Hamming
533 % Special purpose Filters
534 % SincFast LanczosSharp Lanczos2 Lanczos2Sharp
535 % Robidoux RobidouxSharp
537 % The users "-filter" selection is used to lookup the default 'expert'
538 % settings for that filter from a internal table. However any provided
539 % 'expert' settings (see below) may override this selection.
541 % FIR filters are used as is, and are limited to that filters support window
542 % (unless over-ridden). 'Gaussian' while classed as an IIR filter, is also
543 % simply clipped by its support size (currently 1.5 or approximatally 3*sigma
544 % as recommended by many references)
546 % The special a 'cylindrical' filter flag will promote the default 4-lobed
547 % Windowed Sinc filter to a 3-lobed Windowed Jinc equivalent, which is better
548 % suited to this style of image resampling. This typically happens when using
549 % such a filter for images distortions.
553 % Directly requesting 'Sinc', 'Jinc' function as a filter will force the use
554 % of function without any windowing, or promotion for cylindrical usage. This
555 % is not recommended, except by image processing experts, especially as part
556 % of expert option filter function selection.
558 % Two forms of the 'Sinc' function are available: Sinc and SincFast. Sinc is
559 % computed using the traditional sin(pi*x)/(pi*x); it is selected if the user
560 % specifically specifies the use of a Sinc filter. SincFast uses highly
561 % accurate (and fast) polynomial (low Q) and rational (high Q) approximations,
562 % and will be used by default in most cases.
564 % The Lanczos filter is a special 3-lobed Sinc-windowed Sinc filter (promoted
565 % to Jinc-windowed Jinc for cylindrical (Elliptical Weighted Average) use).
566 % The Sinc version is the most popular windowed filter.
568 % LanczosSharp is a slightly sharpened (blur=0.9812505644269356 < 1) form of
569 % the Lanczos filter, specifically designed for EWA distortion (as a
570 % Jinc-Jinc); it can also be used as a slightly sharper orthogonal Lanczos
571 % (Sinc-Sinc) filter. The chosen blur value comes as close as possible to
572 % satisfying the following condition without changing the character of the
573 % corresponding EWA filter:
575 % 'No-Op' Vertical and Horizontal Line Preservation Condition: Images with
576 % only vertical or horizontal features are preserved when performing 'no-op"
577 % with EWA distortion.
579 % The Lanczos2 and Lanczos2Sharp filters are 2-lobe versions of the Lanczos
580 % filters. The 'sharp' version uses a blur factor of 0.9549963639785485,
581 % again chosen because the resulting EWA filter comes as close as possible to
582 % satisfying the above condition.
584 % Robidoux is another filter tuned for EWA. It is the Keys cubic filter
585 % defined by B=(228 - 108 sqrt(2))/199. Robidoux satisfies the "'No-Op'
586 % Vertical and Horizontal Line Preservation Condition" exactly, and it
587 % moderately blurs high frequency 'pixel-hash' patterns under no-op. It turns
588 % out to be close to both Mitchell and Lanczos2Sharp. For example, its first
589 % crossing is at (36 sqrt(2) + 123)/(72 sqrt(2) + 47), almost the same as the
590 % first crossing of Mitchell and Lanczos2Sharp.
592 % RodidouxSharp is a slightly sharper version of Rodidoux, some believe it
593 % is too sharp. It is designed to minimize the maximum possible change in
594 % a pixel value which is at one of the extremes (e.g., 0 or 255) under no-op
595 % conditions. Amazingly Mitchell falls roughly between Rodidoux and
596 % RodidouxSharp, though this seems to have been pure coincidence.
600 % These artifact "defines" are not recommended for production use without
601 % expert knowledge of resampling, filtering, and the effects they have on the
602 % resulting resampled (resize ro distorted) image.
604 % They can be used to override any and all filter default, and it is
605 % recommended you make good use of "filter:verbose" to make sure that the
606 % overall effect of your selection (before and after) is as expected.
608 % "filter:verbose" controls whether to output the exact results of the
609 % filter selections made, as well as plotting data for graphing the
610 % resulting filter over the filters support range.
612 % "filter:filter" select the main function associated with this filter
613 % name, as the weighting function of the filter. This can be used to
614 % set a windowing function as a weighting function, for special
615 % purposes, such as graphing.
617 % If a "filter:window" operation has not been provided, a 'Box'
618 % windowing function will be set to denote that no windowing function is
621 % "filter:window" Select this windowing function for the filter. While any
622 % filter could be used as a windowing function, using the 'first lobe' of
623 % that filter over the whole support window, using a non-windowing
624 % function is not advisible. If no weighting filter function is specifed
625 % a 'SincFast' filter is used.
627 % "filter:lobes" Number of lobes to use for the Sinc/Jinc filter. This a
628 % simpler method of setting filter support size that will correctly
629 % handle the Sinc/Jinc switch for an operators filtering requirements.
630 % Only integers should be given.
632 % "filter:support" Set the support size for filtering to the size given.
633 % This not recommended for Sinc/Jinc windowed filters (lobes should be
634 % used instead). This will override any 'filter:lobes' option.
636 % "filter:win-support" Scale windowing function to this size instead. This
637 % causes the windowing (or self-windowing Lagrange filter) to act is if
638 % the support window it much much larger than what is actually supplied
639 % to the calling operator. The filter however is still clipped to the
640 % real support size given, by the support range suppiled to the caller.
641 % If unset this will equal the normal filter support size.
643 % "filter:blur" Scale the filter and support window by this amount. A value
644 % > 1 will generally result in a more burred image with more ringing
645 % effects, while a value <1 will sharpen the resulting image with more
648 % "filter:sigma" The sigma value to use for the Gaussian filter only.
649 % Defaults to '1/2'. Using a different sigma effectively provides a
650 % method of using the filter as a 'blur' convolution. Particularly when
651 % using it for Distort.
654 % "filter:c" Override the preset B,C values for a Cubic type of filter.
655 % If only one of these are given it is assumes to be a 'Keys' type of
656 % filter such that B+2C=1, where Keys 'alpha' value = C.
660 % Set a true un-windowed Sinc filter with 10 lobes (very slow):
661 % -define filter:filter=Sinc
662 % -define filter:lobes=8
664 % Set an 8 lobe Lanczos (Sinc or Jinc) filter:
666 % -define filter:lobes=8
668 % The format of the AcquireResizeFilter method is:
670 % ResizeFilter *AcquireResizeFilter(const Image *image,
671 % const FilterTypes filter_type,const MagickBooleanType cylindrical,
672 % ExceptionInfo *exception)
674 % A description of each parameter follows:
676 % o image: the image.
678 % o filter: the filter type, defining a preset filter, window and support.
679 % The artifact settings listed above will override those selections.
681 % o blur: blur the filter by this amount, use 1.0 if unknown. Image
682 % artifact "filter:blur" will override this API call usage, including any
683 % internal change (such as for cylindrical usage).
685 % o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical (radial)
688 % o exception: return any errors or warnings in this structure.
691 MagickPrivate ResizeFilter *AcquireResizeFilter(const Image *image,
692 const FilterTypes filter,const MagickBooleanType cylindrical,
693 ExceptionInfo *exception)
707 register ResizeFilter
711 Table Mapping given Filter, into Weighting and Windowing functions.
712 A 'Box' windowing function means its a simble non-windowed filter.
713 An 'SincFast' filter function could be upgraded to a 'Jinc' filter if a
714 "cylindrical" is requested, unless a 'Sinc' or 'SincFast' filter was
715 specifically requested by the user.
717 WARNING: The order of this table must match the order of the FilterTypes
718 enumeration specified in "resample.h", or the filter names will not match
719 the filter being setup.
721 You can check filter setups with the "filter:verbose" expert setting.
728 } const mapping[SentinelFilter] =
730 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
731 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
732 { BoxFilter, BoxFilter }, /* Box averaging filter */
733 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
734 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
735 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
736 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
737 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
738 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
739 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approx */
740 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
741 { CatromFilter, BoxFilter }, /* Cubic-Keys interpolator */
742 { MitchellFilter, BoxFilter }, /* 'Ideal' Cubic-Keys filter */
743 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
744 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
745 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
746 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
747 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
748 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
749 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
750 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
751 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing */
752 { LanczosFilter, LanczosFilter }, /* Lanczos Sinc-Sinc filters */
753 { LanczosSharpFilter, LanczosSharpFilter }, /* | these require */
754 { Lanczos2Filter, Lanczos2Filter }, /* | special handling */
755 { Lanczos2SharpFilter, Lanczos2SharpFilter },
756 { RobidouxFilter, BoxFilter }, /* Cubic Keys tuned for EWA */
757 { RobidouxSharpFilter, BoxFilter }, /* Sharper Cubic Keys for EWA */
758 { SincFastFilter, CosineFilter }, /* low level cosine window */
761 Table mapping the filter/window from the above table to an actual function.
762 The default support size for that filter as a weighting function, the range
763 to scale with to use that function as a sinc windowing function, (typ 1.0).
765 Note that the filter_type -> function is 1 to 1 except for Sinc(),
766 SincFast(), and CubicBC() functions, which may have multiple filter to
767 function associations.
769 See "filter:verbose" handling below for the function -> filter mapping.
774 (*function)(const MagickRealType,const ResizeFilter*),
775 support, /* Default lobes/support size of the weighting filter. */
776 scale, /* Support when function used as a windowing function
777 Typically equal to the location of the first zero crossing. */
778 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
779 } const filters[SentinelFilter] =
781 /* .--- support window
782 | .--- first crossing (if used as a Windowing function)
783 | | .--- B value for Cubic Function
784 | | | .---- C value for Cubic Function
786 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
787 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
788 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
789 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
790 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
791 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
792 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
793 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
794 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
795 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
796 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
797 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
798 { CubicBC, 2.0, 8.0/7.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
799 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
800 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
801 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
802 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
803 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
804 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
805 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
806 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
807 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
808 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
809 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* lanczos, Sharpened */
810 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos, 2-lobed */
811 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2, sharpened */
812 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
813 { CubicBC, 2.0, 1.1685777620836932,
814 0.37821575509399867, 0.31089212245300067 },
815 /* RobidouxSharp: Sharper version of Robidoux */
816 { CubicBC, 2.0, 1.105822933719019,
817 0.2620145123990142, 0.3689927438004929 },
818 { Cosine, 1.0, 1.0, 0.0, 0.0 } /* Low level cosine window */
821 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
822 function being used as a filter. It is used by the "filter:lobes" expert
823 setting and for 'lobes' for Jinc functions in the previous table. This way
824 users do not have to deal with the highly irrational lobe sizes of the Jinc
828 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
829 using Jv-function with v=1, then dividing by PI.
831 static MagickRealType
853 Allocate resize filter.
855 assert(image != (const Image *) NULL);
856 assert(image->signature == MagickSignature);
857 if( IfMagickTrue(image->debug) )
858 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
859 assert(UndefinedFilter < filter && filter < SentinelFilter);
860 assert(exception != (ExceptionInfo *) NULL);
861 assert(exception->signature == MagickSignature);
862 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
863 if (resize_filter == (ResizeFilter *) NULL)
864 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
865 (void) ResetMagickMemory(resize_filter,0,sizeof(*resize_filter));
867 Defaults for the requested filter.
869 filter_type=mapping[filter].filter;
870 window_type=mapping[filter].window;
871 resize_filter->blur=1.0;
872 /* Promote 1D Windowed Sinc Filters to a 2D Windowed Jinc filters */
873 if( IfMagickTrue(cylindrical) && (filter_type == SincFastFilter) &&
874 (filter != SincFastFilter))
875 filter_type=JincFilter; /* 1D Windowed Sinc => 2D Windowed Jinc filters */
877 /* Expert filter setting override */
878 artifact=GetImageArtifact(image,"filter:filter");
879 if (artifact != (const char *) NULL)
884 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
885 if ((UndefinedFilter < option) && (option < SentinelFilter))
886 { /* Raw filter request - no window function. */
887 filter_type=(FilterTypes) option;
888 window_type=BoxFilter;
890 /* Filter override with a specific window function. */
891 artifact=GetImageArtifact(image,"filter:window");
892 if (artifact != (const char *) NULL)
894 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
895 if ((UndefinedFilter < option) && (option < SentinelFilter))
896 window_type=(FilterTypes) option;
901 /* Window specified, but no filter function? Assume Sinc/Jinc. */
902 artifact=GetImageArtifact(image,"filter:window");
903 if (artifact != (const char *) NULL)
908 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
909 if ((UndefinedFilter < option) && (option < SentinelFilter))
911 filter_type= IfMagickTrue(cylindrical) ? JincFilter
913 window_type=(FilterTypes) option;
918 /* Assign the real functions to use for the filters selected. */
919 resize_filter->filter=filters[filter_type].function;
920 resize_filter->support=filters[filter_type].support;
921 resize_filter->window=filters[window_type].function;
922 resize_filter->scale=filters[window_type].scale;
923 resize_filter->signature=MagickSignature;
925 /* Filter Modifications for orthogonal/cylindrical usage */
926 if (cylindrical != MagickFalse)
930 /* Support for Cylindrical Box should be sqrt(2)/2 */
931 resize_filter->support=(MagickRealType) MagickSQ1_2;
934 case LanczosSharpFilter:
936 case Lanczos2SharpFilter:
937 resize_filter->filter=filters[JincFilter].function;
938 resize_filter->window=filters[JincFilter].function;
939 resize_filter->scale=filters[JincFilter].scale;
940 /* number of lobes (support window size) remain unchanged */
945 /* Global Sharpening (regardless of orthoginal/cylindrical) */
948 case LanczosSharpFilter:
949 resize_filter->blur *= 0.9812505644269356;
951 case Lanczos2SharpFilter:
952 resize_filter->blur *= 0.9549963639785485;
959 Expert Option Modifications.
962 /* User Gaussian Sigma Override - no support change */
963 if ((resize_filter->filter == Gaussian) ||
964 (resize_filter->window == Gaussian) ) {
965 value=0.5; /* guassian sigma default, half pixel */
966 artifact=GetImageArtifact(image,"filter:sigma");
967 if (artifact != (const char *) NULL)
968 value=StringToDouble(artifact,(char **) NULL);
969 /* Define coefficents for Gaussian */
970 resize_filter->coefficient[0]=value; /* note sigma too */
971 resize_filter->coefficient[1]=MagickEpsilonReciprocal(2.0*value*value); /* sigma scaling */
972 resize_filter->coefficient[2]=MagickEpsilonReciprocal(Magick2PI*value*value);
973 /* normalization - not actually needed or used! */
975 resize_filter->support *= value/0.5; /* increase support */
978 /* User Kaiser Alpha Override - no support change */
979 if ((resize_filter->filter == Kaiser) ||
980 (resize_filter->window == Kaiser) ) {
981 value=6.5; /* default beta value for Kaiser bessel windowing function */
982 artifact=GetImageArtifact(image,"filter:alpha"); /* FUTURE: depreciate */
983 if (artifact != (const char *) NULL)
984 value=StringToDouble(artifact,(char **) NULL);
985 artifact=GetImageArtifact(image,"filter:kaiser-beta");
986 if (artifact != (const char *) NULL)
987 value=StringToDouble(artifact,(char **) NULL);
988 artifact=GetImageArtifact(image,"filter:kaiser-alpha");
989 if (artifact != (const char *) NULL)
990 value=StringToDouble(artifact,(char **) NULL)*MagickPI;
991 /* Define coefficents for Kaiser Windowing Function */
992 resize_filter->coefficient[0]=value; /* alpha */
993 resize_filter->coefficient[1]=MagickEpsilonReciprocal(I0(value)); /* normalization */
997 artifact=GetImageArtifact(image,"filter:blur");
998 if (artifact != (const char *) NULL)
999 resize_filter->blur*=StringToDouble(artifact,(char **) NULL);
1000 if (resize_filter->blur < MagickEpsilon)
1001 resize_filter->blur=(MagickRealType) MagickEpsilon;
1003 /* Support Overrides */
1004 artifact=GetImageArtifact(image,"filter:lobes");
1005 if (artifact != (const char *) NULL)
1010 lobes=(ssize_t) StringToLong(artifact);
1013 resize_filter->support=(MagickRealType) lobes;
1015 /* Convert a Jinc function lobes value to a real support value */
1016 if (resize_filter->filter == Jinc)
1018 if (resize_filter->support > 16)
1019 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
1021 resize_filter->support=jinc_zeros[((long)resize_filter->support)-1];
1023 /* expert override of the support setting */
1024 artifact=GetImageArtifact(image,"filter:support");
1025 if (artifact != (const char *) NULL)
1026 resize_filter->support=fabs(StringToDouble(artifact,(char **) NULL));
1028 Scale windowing function separately to the support 'clipping'
1029 window that calling operator is planning to actually use. (Expert
1032 resize_filter->window_support=resize_filter->support; /* default */
1033 artifact=GetImageArtifact(image,"filter:win-support");
1034 if (artifact != (const char *) NULL)
1035 resize_filter->window_support=fabs(StringToDouble(artifact,(char **) NULL));
1037 Adjust window function scaling to match windowing support for
1038 weighting function. This avoids a division on every filter call.
1040 resize_filter->scale/=resize_filter->window_support;
1043 * Set Cubic Spline B,C values, calculate Cubic coefficients.
1047 if ((resize_filter->filter == CubicBC) ||
1048 (resize_filter->window == CubicBC) )
1050 B=filters[filter_type].B;
1051 C=filters[filter_type].C;
1052 if (filters[window_type].function == CubicBC)
1054 B=filters[window_type].B;
1055 C=filters[window_type].C;
1057 artifact=GetImageArtifact(image,"filter:b");
1058 if (artifact != (const char *) NULL)
1060 B=StringToDouble(artifact,(char **) NULL);
1061 C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
1062 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
1063 if (artifact != (const char *) NULL)
1064 C=StringToDouble(artifact,(char **) NULL);
1068 artifact=GetImageArtifact(image,"filter:c");
1069 if (artifact != (const char *) NULL)
1071 C=StringToDouble(artifact,(char **) NULL);
1072 B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
1075 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
1077 const double twoB = B+B;
1078 resize_filter->coefficient[0]=1.0-(1.0/3.0)*B;
1079 resize_filter->coefficient[1]=-3.0+twoB+C;
1080 resize_filter->coefficient[2]=2.0-1.5*B-C;
1081 resize_filter->coefficient[3]=(4.0/3.0)*B+4.0*C;
1082 resize_filter->coefficient[4]=-8.0*C-twoB;
1083 resize_filter->coefficient[5]=B+5.0*C;
1084 resize_filter->coefficient[6]=(-1.0/6.0)*B-C;
1089 Expert Option Request for verbose details of the resulting filter.
1091 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1095 if (IfStringTrue(GetImageArtifact(image,"filter:verbose")))
1102 Set the weighting function properly when the weighting
1103 function may not exactly match the filter of the same name.
1104 EG: a Point filter is really uses a Box weighting function
1105 with a different support than is typically used.
1107 if (resize_filter->filter == Box) filter_type=BoxFilter;
1108 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1109 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1110 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1111 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1112 if (resize_filter->window == Box) window_type=BoxFilter;
1113 if (resize_filter->window == Sinc) window_type=SincFilter;
1114 if (resize_filter->window == SincFast) window_type=SincFastFilter;
1115 if (resize_filter->window == Jinc) window_type=JincFilter;
1116 if (resize_filter->window == CubicBC) window_type=CubicFilter;
1118 Report Filter Details.
1120 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1121 (void) FormatLocaleFile(stdout,"# Resize Filter (for graphing)\n#\n");
1122 (void) FormatLocaleFile(stdout,"# filter = %s\n",
1123 CommandOptionToMnemonic(MagickFilterOptions,filter_type));
1124 (void) FormatLocaleFile(stdout,"# window = %s\n",
1125 CommandOptionToMnemonic(MagickFilterOptions,window_type));
1126 (void) FormatLocaleFile(stdout,"# support = %.*g\n",
1127 GetMagickPrecision(),(double) resize_filter->support);
1128 (void) FormatLocaleFile(stdout,"# window-support = %.*g\n",
1129 GetMagickPrecision(),(double) resize_filter->window_support);
1130 (void) FormatLocaleFile(stdout,"# scale-blur = %.*g\n",
1131 GetMagickPrecision(), (double)resize_filter->blur);
1132 if ( filter_type == GaussianFilter || window_type == GaussianFilter )
1133 (void) FormatLocaleFile(stdout,"# gaussian-sigma = %.*g\n",
1134 GetMagickPrecision(), (double)resize_filter->coefficient[0]);
1135 if ( filter_type == KaiserFilter || window_type == KaiserFilter )
1136 (void) FormatLocaleFile(stdout,"# kaiser-beta = %.*g\n",
1137 GetMagickPrecision(),
1138 (double)resize_filter->coefficient[0]);
1139 (void) FormatLocaleFile(stdout,"# practical-support = %.*g\n",
1140 GetMagickPrecision(), (double)support);
1141 if ( filter_type == CubicFilter || window_type == CubicFilter )
1142 (void) FormatLocaleFile(stdout,"# B,C = %.*g,%.*g\n",
1143 GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
1144 (void) FormatLocaleFile(stdout,"\n");
1146 Output values of resulting filter graph -- for graphing
1149 for (x=0.0; x <= support; x+=0.01f)
1150 (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1151 (double) GetResizeFilterWeight(resize_filter,x));
1152 /* A final value so gnuplot can graph the 'stop' properly. */
1153 (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",support,
1154 GetMagickPrecision(),0.0);
1156 /* Output the above once only for each image - remove setting */
1157 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1158 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1161 return(resize_filter);
1165 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1169 % A d a p t i v e R e s i z e I m a g e %
1173 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1175 % AdaptiveResizeImage() adaptively resize image with pixel resampling.
1177 % This is shortcut function for a fast interpolative resize using mesh
1178 % interpolation. It works well for small resizes of less than +/- 50%
1179 % of the original image size. For larger resizing on images a full
1180 % filtered and slower resize function should be used instead.
1182 % The format of the AdaptiveResizeImage method is:
1184 % Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1185 % const size_t rows, ExceptionInfo *exception)
1187 % A description of each parameter follows:
1189 % o image: the image.
1191 % o columns: the number of columns in the resized image.
1193 % o rows: the number of rows in the resized image.
1195 % o exception: return any errors or warnings in this structure.
1198 MagickExport Image *AdaptiveResizeImage(const Image *image,
1199 const size_t columns,const size_t rows,ExceptionInfo *exception)
1204 resize_image=InterpolativeResizeImage(image,columns,rows,MeshInterpolatePixel,
1206 return(resize_image);
1210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1214 + B e s s e l O r d e r O n e %
1218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1220 % BesselOrderOne() computes the Bessel function of x of the first kind of
1221 % order 0. This is used to create the Jinc() filter function below.
1223 % Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1229 % j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1231 % where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1233 % cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1234 % = 1/sqrt(2) * (sin(x) - cos(x))
1235 % sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1236 % = -1/sqrt(2) * (sin(x) + cos(x))
1238 % The format of the BesselOrderOne method is:
1240 % MagickRealType BesselOrderOne(MagickRealType x)
1242 % A description of each parameter follows:
1244 % o x: MagickRealType value.
1249 static MagickRealType I0(MagickRealType x)
1260 Zeroth order Bessel function of the first kind.
1265 for (i=2; t > MagickEpsilon; i++)
1268 t*=y/((MagickRealType) i*i);
1274 static MagickRealType J1(MagickRealType x)
1286 0.581199354001606143928050809e+21,
1287 -0.6672106568924916298020941484e+20,
1288 0.2316433580634002297931815435e+19,
1289 -0.3588817569910106050743641413e+17,
1290 0.2908795263834775409737601689e+15,
1291 -0.1322983480332126453125473247e+13,
1292 0.3413234182301700539091292655e+10,
1293 -0.4695753530642995859767162166e+7,
1294 0.270112271089232341485679099e+4
1298 0.11623987080032122878585294e+22,
1299 0.1185770712190320999837113348e+20,
1300 0.6092061398917521746105196863e+17,
1301 0.2081661221307607351240184229e+15,
1302 0.5243710262167649715406728642e+12,
1303 0.1013863514358673989967045588e+10,
1304 0.1501793594998585505921097578e+7,
1305 0.1606931573481487801970916749e+4,
1311 for (i=7; i >= 0; i--)
1320 static MagickRealType P1(MagickRealType x)
1332 0.352246649133679798341724373e+5,
1333 0.62758845247161281269005675e+5,
1334 0.313539631109159574238669888e+5,
1335 0.49854832060594338434500455e+4,
1336 0.2111529182853962382105718e+3,
1337 0.12571716929145341558495e+1
1341 0.352246649133679798068390431e+5,
1342 0.626943469593560511888833731e+5,
1343 0.312404063819041039923015703e+5,
1344 0.4930396490181088979386097e+4,
1345 0.2030775189134759322293574e+3,
1351 for (i=4; i >= 0; i--)
1353 p=p*(8.0/x)*(8.0/x)+Pone[i];
1354 q=q*(8.0/x)*(8.0/x)+Qone[i];
1360 static MagickRealType Q1(MagickRealType x)
1372 0.3511751914303552822533318e+3,
1373 0.7210391804904475039280863e+3,
1374 0.4259873011654442389886993e+3,
1375 0.831898957673850827325226e+2,
1376 0.45681716295512267064405e+1,
1377 0.3532840052740123642735e-1
1381 0.74917374171809127714519505e+4,
1382 0.154141773392650970499848051e+5,
1383 0.91522317015169922705904727e+4,
1384 0.18111867005523513506724158e+4,
1385 0.1038187585462133728776636e+3,
1391 for (i=4; i >= 0; i--)
1393 p=p*(8.0/x)*(8.0/x)+Pone[i];
1394 q=q*(8.0/x)*(8.0/x)+Qone[i];
1399 static MagickRealType BesselOrderOne(MagickRealType x)
1412 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1413 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1421 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1425 + D e s t r o y R e s i z e F i l t e r %
1429 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1431 % DestroyResizeFilter() destroy the resize filter.
1433 % The format of the DestroyResizeFilter method is:
1435 % ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1437 % A description of each parameter follows:
1439 % o resize_filter: the resize filter.
1442 MagickPrivate ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1444 assert(resize_filter != (ResizeFilter *) NULL);
1445 assert(resize_filter->signature == MagickSignature);
1446 resize_filter->signature=(~MagickSignature);
1447 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1448 return(resize_filter);
1452 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1456 + G e t R e s i z e F i l t e r S u p p o r t %
1460 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1462 % GetResizeFilterSupport() return the current support window size for this
1463 % filter. Note that this may have been enlarged by filter:blur factor.
1465 % The format of the GetResizeFilterSupport method is:
1467 % MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1469 % A description of each parameter follows:
1471 % o filter: Image filter to use.
1474 MagickPrivate MagickRealType GetResizeFilterSupport(
1475 const ResizeFilter *resize_filter)
1477 assert(resize_filter != (ResizeFilter *) NULL);
1478 assert(resize_filter->signature == MagickSignature);
1479 return(resize_filter->support*resize_filter->blur);
1483 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1487 + G e t R e s i z e F i l t e r W e i g h t %
1491 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1493 % GetResizeFilterWeight evaluates the specified resize filter at the point x
1494 % which usally lies between zero and the filters current 'support' and
1495 % returns the weight of the filter function at that point.
1497 % The format of the GetResizeFilterWeight method is:
1499 % MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1500 % const MagickRealType x)
1502 % A description of each parameter follows:
1504 % o filter: the filter type.
1509 MagickPrivate MagickRealType GetResizeFilterWeight(
1510 const ResizeFilter *resize_filter,const MagickRealType x)
1518 Windowing function - scale the weighting filter by this amount.
1520 assert(resize_filter != (ResizeFilter *) NULL);
1521 assert(resize_filter->signature == MagickSignature);
1522 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
1523 if ((resize_filter->window_support < MagickEpsilon) ||
1524 (resize_filter->window == Box))
1525 scale=1.0; /* Point or Box Filter -- avoid division by zero */
1528 scale=resize_filter->scale;
1529 scale=resize_filter->window(x_blur*scale,resize_filter);
1531 weight=scale*resize_filter->filter(x_blur,resize_filter);
1536 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1540 % I n t e r p o l a t i v e R e s i z e I m a g e %
1544 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1546 % InterpolativeResizeImage() resizes an image using the specified
1547 % interpolation method.
1549 % The format of the InterpolativeResizeImage method is:
1551 % Image *InterpolativeResizeImage(const Image *image,const size_t columns,
1552 % const size_t rows,const PixelInterpolateMethod method,
1553 % ExceptionInfo *exception)
1555 % A description of each parameter follows:
1557 % o image: the image.
1559 % o columns: the number of columns in the resized image.
1561 % o rows: the number of rows in the resized image.
1563 % o method: the pixel interpolation method.
1565 % o exception: return any errors or warnings in this structure.
1568 MagickExport Image *InterpolativeResizeImage(const Image *image,
1569 const size_t columns,const size_t rows,const PixelInterpolateMethod method,
1570 ExceptionInfo *exception)
1572 #define InterpolativeResizeImageTag "Resize/Image"
1594 Interpolatively resize image.
1596 assert(image != (const Image *) NULL);
1597 assert(image->signature == MagickSignature);
1598 if( IfMagickTrue(image->debug) )
1599 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1600 assert(exception != (ExceptionInfo *) NULL);
1601 assert(exception->signature == MagickSignature);
1602 if ((columns == 0) || (rows == 0))
1603 return((Image *) NULL);
1604 if ((columns == image->columns) && (rows == image->rows))
1605 return(CloneImage(image,0,0,MagickTrue,exception));
1606 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1607 if (resize_image == (Image *) NULL)
1608 return((Image *) NULL);
1609 if( IfMagickFalse(SetImageStorageClass(resize_image,DirectClass,exception)) )
1611 resize_image=DestroyImage(resize_image);
1612 return((Image *) NULL);
1616 image_view=AcquireVirtualCacheView(image,exception);
1617 resize_view=AcquireAuthenticCacheView(resize_image,exception);
1618 scale.x=(double) image->columns/resize_image->columns;
1619 scale.y=(double) image->rows/resize_image->rows;
1620 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1621 #pragma omp parallel for schedule(static) shared(progress,status) \
1622 dynamic_number_threads(image,image->columns,image->rows,1)
1624 for (y=0; y < (ssize_t) resize_image->rows; y++)
1635 if( IfMagickFalse(status) )
1637 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1639 if (q == (Quantum *) NULL)
1641 offset.y=((MagickRealType) y+0.5)*scale.y-0.5;
1642 for (x=0; x < (ssize_t) resize_image->columns; x++)
1647 if (GetPixelMask(resize_image,q) != 0)
1649 q+=GetPixelChannels(resize_image);
1652 for (i=0; i < (ssize_t) GetPixelChannels(resize_image); i++)
1661 channel=GetPixelChannelMapChannel(image,i);
1662 traits=GetPixelChannelMapTraits(image,channel);
1663 resize_traits=GetPixelChannelMapTraits(resize_image,channel);
1664 if ((traits == UndefinedPixelTrait) ||
1665 (resize_traits == UndefinedPixelTrait))
1667 offset.x=((MagickRealType) x+0.5)*scale.x-0.5;
1668 status=InterpolatePixelChannels(image,image_view,resize_image,method,
1669 offset.x,offset.y,q,exception);
1671 q+=GetPixelChannels(resize_image);
1673 if( IfMagickFalse(SyncCacheViewAuthenticPixels(resize_view,exception)) )
1675 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1680 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1681 #pragma omp critical (MagickCore_InterpolativeResizeImage)
1683 proceed=SetImageProgress(image,InterpolativeResizeImageTag,progress++,
1685 if( IfMagickFalse(proceed) )
1689 resize_view=DestroyCacheView(resize_view);
1690 image_view=DestroyCacheView(image_view);
1691 if( IfMagickFalse(status) )
1692 resize_image=DestroyImage(resize_image);
1693 return(resize_image);
1695 #if defined(MAGICKCORE_LQR_DELEGATE)
1698 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1702 % L i q u i d R e s c a l e I m a g e %
1706 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1708 % LiquidRescaleImage() rescales image with seam carving.
1710 % The format of the LiquidRescaleImage method is:
1712 % Image *LiquidRescaleImage(const Image *image,const size_t columns,
1713 % const size_t rows,const double delta_x,const double rigidity,
1714 % ExceptionInfo *exception)
1716 % A description of each parameter follows:
1718 % o image: the image.
1720 % o columns: the number of columns in the rescaled image.
1722 % o rows: the number of rows in the rescaled image.
1724 % o delta_x: maximum seam transversal step (0 means straight seams).
1726 % o rigidity: introduce a bias for non-straight seams (typically 0).
1728 % o exception: return any errors or warnings in this structure.
1731 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1732 const size_t rows,const double delta_x,const double rigidity,
1733 ExceptionInfo *exception)
1735 #define LiquidRescaleImageTag "Rescale/Image"
1768 Liquid rescale image.
1770 assert(image != (const Image *) NULL);
1771 assert(image->signature == MagickSignature);
1772 if( IfMagickTrue(image->debug) )
1773 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1774 assert(exception != (ExceptionInfo *) NULL);
1775 assert(exception->signature == MagickSignature);
1776 if ((columns == 0) || (rows == 0))
1777 return((Image *) NULL);
1778 if ((columns == image->columns) && (rows == image->rows))
1779 return(CloneImage(image,0,0,MagickTrue,exception));
1780 if ((columns <= 2) || (rows <= 2))
1781 return(ResizeImage(image,columns,rows,image->filter,exception));
1782 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1792 Honor liquid resize size limitations.
1794 for (width=image->columns; columns >= (2*width-1); width*=2);
1795 for (height=image->rows; rows >= (2*height-1); height*=2);
1796 resize_image=ResizeImage(image,width,height,image->filter,exception);
1797 if (resize_image == (Image *) NULL)
1798 return((Image *) NULL);
1799 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1800 rigidity,exception);
1801 resize_image=DestroyImage(resize_image);
1802 return(rescale_image);
1804 pixels=(gfloat *) AcquireQuantumMemory(image->columns,image->rows*
1805 GetPixelChannels(image)*sizeof(*pixels));
1806 if (pixels == (gfloat *) NULL)
1807 return((Image *) NULL);
1810 image_view=AcquireVirtualCacheView(image,exception);
1811 for (y=0; y < (ssize_t) image->rows; y++)
1813 register const Quantum
1819 if( IfMagickFalse(status) )
1821 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1822 if (p == (const Quantum *) NULL)
1827 for (x=0; x < (ssize_t) image->columns; x++)
1832 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1833 *q++=QuantumScale*p[i];
1834 p+=GetPixelChannels(image);
1837 image_view=DestroyCacheView(image_view);
1838 carver=lqr_carver_new_ext(pixels,image->columns,image->rows,
1839 GetPixelChannels(image),LQR_COLDEPTH_32F);
1840 if (carver == (LqrCarver *) NULL)
1842 pixels=(gfloat *) RelinquishMagickMemory(pixels);
1843 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1845 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1846 lqr_status=lqr_carver_resize(carver,columns,rows);
1848 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1849 lqr_carver_get_height(carver),MagickTrue,exception);
1850 if (rescale_image == (Image *) NULL)
1852 pixels=(gfloat *) RelinquishMagickMemory(pixels);
1853 return((Image *) NULL);
1855 if( IfMagickFalse(SetImageStorageClass(rescale_image,DirectClass,exception)) )
1857 pixels=(gfloat *) RelinquishMagickMemory(pixels);
1858 rescale_image=DestroyImage(rescale_image);
1859 return((Image *) NULL);
1861 rescale_view=AcquireAuthenticCacheView(rescale_image,exception);
1862 (void) lqr_carver_scan_reset(carver);
1863 while (lqr_carver_scan_ext(carver,&x_offset,&y_offset,(void **) &packet) != 0)
1871 q=QueueCacheViewAuthenticPixels(rescale_view,x_offset,y_offset,1,1,
1873 if (q == (Quantum *) NULL)
1875 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1884 channel=GetPixelChannelMapChannel(image,i);
1885 traits=GetPixelChannelMapTraits(image,channel);
1886 rescale_traits=GetPixelChannelMapTraits(rescale_image,channel);
1887 if ((traits == UndefinedPixelTrait) ||
1888 (rescale_traits == UndefinedPixelTrait))
1890 SetPixelChannel(rescale_image,channel,ClampToQuantum(QuantumRange*
1893 if( IfMagickFalse(SyncCacheViewAuthenticPixels(rescale_view,exception)) )
1896 rescale_view=DestroyCacheView(rescale_view);
1897 lqr_carver_destroy(carver);
1898 return(rescale_image);
1901 MagickExport Image *LiquidRescaleImage(const Image *image,
1902 const size_t magick_unused(columns),const size_t magick_unused(rows),
1903 const double magick_unused(delta_x),const double magick_unused(rigidity),
1904 ExceptionInfo *exception)
1906 assert(image != (const Image *) NULL);
1907 assert(image->signature == MagickSignature);
1908 if( IfMagickTrue(image->debug) )
1909 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1910 assert(exception != (ExceptionInfo *) NULL);
1911 assert(exception->signature == MagickSignature);
1912 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1913 "DelegateLibrarySupportNotBuiltIn","'%s' (LQR)",image->filename);
1914 return((Image *) NULL);
1919 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1923 % M a g n i f y I m a g e %
1927 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1929 % MagnifyImage() is a convenience method that scales an image proportionally
1930 % to twice its size.
1932 % The format of the MagnifyImage method is:
1934 % Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1936 % A description of each parameter follows:
1938 % o image: the image.
1940 % o exception: return any errors or warnings in this structure.
1943 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1948 assert(image != (Image *) NULL);
1949 assert(image->signature == MagickSignature);
1950 if( IfMagickTrue(image->debug) )
1951 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1952 assert(exception != (ExceptionInfo *) NULL);
1953 assert(exception->signature == MagickSignature);
1954 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1956 return(magnify_image);
1960 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1964 % M i n i f y I m a g e %
1968 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1970 % MinifyImage() is a convenience method that scales an image proportionally to
1973 % The format of the MinifyImage method is:
1975 % Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1977 % A description of each parameter follows:
1979 % o image: the image.
1981 % o exception: return any errors or warnings in this structure.
1984 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1989 assert(image != (Image *) NULL);
1990 assert(image->signature == MagickSignature);
1991 if( IfMagickTrue(image->debug) )
1992 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1993 assert(exception != (ExceptionInfo *) NULL);
1994 assert(exception->signature == MagickSignature);
1995 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1997 return(minify_image);
2001 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2005 % R e s a m p l e I m a g e %
2009 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2011 % ResampleImage() resize image in terms of its pixel size, so that when
2012 % displayed at the given resolution it will be the same size in terms of
2013 % real world units as the original image at the original resolution.
2015 % The format of the ResampleImage method is:
2017 % Image *ResampleImage(Image *image,const double x_resolution,
2018 % const double y_resolution,const FilterTypes filter,
2019 % ExceptionInfo *exception)
2021 % A description of each parameter follows:
2023 % o image: the image to be resized to fit the given resolution.
2025 % o x_resolution: the new image x resolution.
2027 % o y_resolution: the new image y resolution.
2029 % o filter: Image filter to use.
2031 % o exception: return any errors or warnings in this structure.
2034 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
2035 const double y_resolution,const FilterTypes filter,ExceptionInfo *exception)
2037 #define ResampleImageTag "Resample/Image"
2047 Initialize sampled image attributes.
2049 assert(image != (const Image *) NULL);
2050 assert(image->signature == MagickSignature);
2051 if( IfMagickTrue(image->debug) )
2052 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2053 assert(exception != (ExceptionInfo *) NULL);
2054 assert(exception->signature == MagickSignature);
2055 width=(size_t) (x_resolution*image->columns/(image->resolution.x == 0.0 ?
2056 72.0 : image->resolution.x)+0.5);
2057 height=(size_t) (y_resolution*image->rows/(image->resolution.y == 0.0 ?
2058 72.0 : image->resolution.y)+0.5);
2059 resample_image=ResizeImage(image,width,height,filter,exception);
2060 if (resample_image != (Image *) NULL)
2062 resample_image->resolution.x=x_resolution;
2063 resample_image->resolution.y=y_resolution;
2065 return(resample_image);
2069 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2073 % R e s i z e I m a g e %
2077 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2079 % ResizeImage() scales an image to the desired dimensions, using the given
2080 % filter (see AcquireFilterInfo()).
2082 % If an undefined filter is given the filter defaults to Mitchell for a
2083 % colormapped image, a image with a matte channel, or if the image is
2084 % enlarged. Otherwise the filter defaults to a Lanczos.
2086 % ResizeImage() was inspired by Paul Heckbert's "zoom" program.
2088 % The format of the ResizeImage method is:
2090 % Image *ResizeImage(Image *image,const size_t columns,
2091 % const size_t rows,const FilterTypes filter,const double blur,
2092 % ExceptionInfo *exception)
2094 % A description of each parameter follows:
2096 % o image: the image.
2098 % o columns: the number of columns in the scaled image.
2100 % o rows: the number of rows in the scaled image.
2102 % o filter: Image filter to use.
2104 % o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
2107 % o exception: return any errors or warnings in this structure.
2111 typedef struct _ContributionInfo
2120 static ContributionInfo **DestroyContributionThreadSet(
2121 ContributionInfo **contribution)
2126 assert(contribution != (ContributionInfo **) NULL);
2127 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
2128 if (contribution[i] != (ContributionInfo *) NULL)
2129 contribution[i]=(ContributionInfo *) RelinquishAlignedMemory(
2131 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
2132 return(contribution);
2135 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
2146 number_threads=GetOpenMPMaximumThreads();
2147 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
2148 sizeof(*contribution));
2149 if (contribution == (ContributionInfo **) NULL)
2150 return((ContributionInfo **) NULL);
2151 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
2152 for (i=0; i < (ssize_t) number_threads; i++)
2154 contribution[i]=(ContributionInfo *) AcquireAlignedMemory(count,
2155 sizeof(**contribution));
2156 if (contribution[i] == (ContributionInfo *) NULL)
2157 return(DestroyContributionThreadSet(contribution));
2159 return(contribution);
2162 static inline double MagickMax(const double x,const double y)
2169 static inline double MagickMin(const double x,const double y)
2176 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2177 const Image *image,Image *resize_image,const MagickRealType x_factor,
2178 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2180 #define ResizeImageTag "Resize/Image"
2190 **restrict contributions;
2203 Apply filter to resize horizontally from image to resize image.
2205 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
2206 support=scale*GetResizeFilterSupport(resize_filter);
2207 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2208 if( IfMagickFalse(SetImageStorageClass(resize_image,storage_class,exception)) )
2209 return(MagickFalse);
2213 Support too small even for nearest neighbour: Reduce to point sampling.
2215 support=(MagickRealType) 0.5;
2218 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2219 if (contributions == (ContributionInfo **) NULL)
2221 (void) ThrowMagickException(exception,GetMagickModule(),
2222 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
2223 return(MagickFalse);
2226 scale=MagickEpsilonReciprocal(scale);
2227 image_view=AcquireVirtualCacheView(image,exception);
2228 resize_view=AcquireAuthenticCacheView(resize_image,exception);
2229 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2230 #pragma omp parallel for schedule(static,4) shared(status) \
2231 dynamic_number_threads(image,image->columns,image->rows,1)
2233 for (x=0; x < (ssize_t) resize_image->columns; x++)
2239 register const Quantum
2242 register ContributionInfo
2243 *restrict contribution;
2256 if( IfMagickFalse(status) )
2258 bisect=(MagickRealType) (x+0.5)/x_factor+MagickEpsilon;
2259 start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
2260 stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->columns);
2262 contribution=contributions[GetOpenMPThreadId()];
2263 for (n=0; n < (stop-start); n++)
2265 contribution[n].pixel=start+n;
2266 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2267 ((MagickRealType) (start+n)-bisect+0.5));
2268 density+=contribution[n].weight;
2270 if ((density != 0.0) && (density != 1.0))
2278 density=MagickEpsilonReciprocal(density);
2279 for (i=0; i < n; i++)
2280 contribution[i].weight*=density;
2282 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2283 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
2284 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2286 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2291 for (y=0; y < (ssize_t) resize_image->rows; y++)
2296 if (GetPixelMask(resize_image,q) != 0)
2298 q+=GetPixelChannels(resize_image);
2301 for (i=0; i < (ssize_t) GetPixelChannels(resize_image); i++)
2321 channel=GetPixelChannelMapChannel(image,i);
2322 traits=GetPixelChannelMapTraits(image,channel);
2323 resize_traits=GetPixelChannelMapTraits(resize_image,channel);
2324 if ((traits == UndefinedPixelTrait) ||
2325 (resize_traits == UndefinedPixelTrait))
2327 if ((resize_traits & CopyPixelTrait) != 0)
2329 j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
2331 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2332 (contribution[j-start].pixel-contribution[0].pixel);
2333 SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
2338 if ((resize_traits & BlendPixelTrait) == 0)
2343 for (j=0; j < n; j++)
2345 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2346 (contribution[j].pixel-contribution[0].pixel);
2347 alpha=contribution[j].weight;
2348 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2350 SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
2357 for (j=0; j < n; j++)
2359 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2360 (contribution[j].pixel-contribution[0].pixel);
2361 alpha=contribution[j].weight*QuantumScale*
2362 GetPixelAlpha(image,p+k*GetPixelChannels(image));
2363 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2366 gamma=MagickEpsilonReciprocal(gamma);
2367 SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
2369 q+=GetPixelChannels(resize_image);
2371 if( IfMagickFalse(SyncCacheViewAuthenticPixels(resize_view,exception)) )
2373 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2378 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2379 #pragma omp critical (MagickCore_HorizontalFilter)
2381 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2382 if( IfMagickFalse(proceed) )
2386 resize_view=DestroyCacheView(resize_view);
2387 image_view=DestroyCacheView(image_view);
2388 contributions=DestroyContributionThreadSet(contributions);
2392 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2393 const Image *image,Image *resize_image,const MagickRealType y_factor,
2394 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2404 **restrict contributions;
2420 Apply filter to resize vertically from image to resize image.
2422 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
2423 support=scale*GetResizeFilterSupport(resize_filter);
2424 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2425 if( IfMagickFalse(SetImageStorageClass(resize_image,storage_class,exception)) )
2426 return(MagickFalse);
2430 Support too small even for nearest neighbour: Reduce to point sampling.
2432 support=(MagickRealType) 0.5;
2435 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2436 if (contributions == (ContributionInfo **) NULL)
2438 (void) ThrowMagickException(exception,GetMagickModule(),
2439 ResourceLimitError,"MemoryAllocationFailed","'%s'",image->filename);
2440 return(MagickFalse);
2443 scale=MagickEpsilonReciprocal(scale);
2444 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2445 image_view=AcquireVirtualCacheView(image,exception);
2446 resize_view=AcquireAuthenticCacheView(resize_image,exception);
2447 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2448 #pragma omp parallel for schedule(static,4) shared(status) \
2449 dynamic_number_threads(image,image->columns,image->rows,1)
2451 for (y=0; y < (ssize_t) resize_image->rows; y++)
2457 register const Quantum
2460 register ContributionInfo
2461 *restrict contribution;
2474 if( IfMagickFalse(status) )
2476 bisect=(MagickRealType) (y+0.5)/y_factor+MagickEpsilon;
2477 start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
2478 stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->rows);
2480 contribution=contributions[GetOpenMPThreadId()];
2481 for (n=0; n < (stop-start); n++)
2483 contribution[n].pixel=start+n;
2484 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2485 ((MagickRealType) (start+n)-bisect+0.5));
2486 density+=contribution[n].weight;
2488 if ((density != 0.0) && (density != 1.0))
2496 density=MagickEpsilonReciprocal(density);
2497 for (i=0; i < n; i++)
2498 contribution[i].weight*=density;
2500 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
2501 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2503 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2505 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2510 for (x=0; x < (ssize_t) resize_image->columns; x++)
2515 if (GetPixelMask(resize_image,q) != 0)
2517 q+=GetPixelChannels(resize_image);
2520 for (i=0; i < (ssize_t) GetPixelChannels(resize_image); i++)
2540 channel=GetPixelChannelMapChannel(image,i);
2541 traits=GetPixelChannelMapTraits(image,channel);
2542 resize_traits=GetPixelChannelMapTraits(resize_image,channel);
2543 if ((traits == UndefinedPixelTrait) ||
2544 (resize_traits == UndefinedPixelTrait))
2546 if ((resize_traits & CopyPixelTrait) != 0)
2548 j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
2550 k=(ssize_t) ((contribution[j-start].pixel-contribution[0].pixel)*
2552 SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
2557 if ((resize_traits & BlendPixelTrait) == 0)
2562 for (j=0; j < n; j++)
2564 k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
2566 alpha=contribution[j].weight;
2567 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2569 SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
2573 for (j=0; j < n; j++)
2575 k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
2577 alpha=contribution[j].weight*QuantumScale*
2578 GetPixelAlpha(image,p+k*GetPixelChannels(image));
2579 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2582 gamma=MagickEpsilonReciprocal(gamma);
2583 SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
2585 q+=GetPixelChannels(resize_image);
2587 if( IfMagickFalse(SyncCacheViewAuthenticPixels(resize_view,exception)) )
2589 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2594 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2595 #pragma omp critical (MagickCore_VerticalFilter)
2597 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2598 if( IfMagickFalse(proceed) )
2602 resize_view=DestroyCacheView(resize_view);
2603 image_view=DestroyCacheView(image_view);
2604 contributions=DestroyContributionThreadSet(contributions);
2608 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2609 const size_t rows,const FilterTypes filter,ExceptionInfo *exception)
2611 #define WorkLoadFactor 0.265
2637 Acquire resize image.
2639 assert(image != (Image *) NULL);
2640 assert(image->signature == MagickSignature);
2641 if( IfMagickTrue(image->debug) )
2642 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2643 assert(exception != (ExceptionInfo *) NULL);
2644 assert(exception->signature == MagickSignature);
2645 if ((columns == 0) || (rows == 0))
2646 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2647 if ((columns == image->columns) && (rows == image->rows) &&
2648 (filter == UndefinedFilter))
2649 return(CloneImage(image,0,0,MagickTrue,exception));
2650 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2651 if (resize_image == (Image *) NULL)
2652 return(resize_image);
2654 Acquire resize filter.
2656 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2657 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2658 if ((x_factor*y_factor) > WorkLoadFactor)
2659 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2661 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2662 if (filter_image == (Image *) NULL)
2663 return(DestroyImage(resize_image));
2664 filter_type=LanczosFilter;
2665 if (filter != UndefinedFilter)
2668 if ((x_factor == 1.0) && (y_factor == 1.0))
2669 filter_type=PointFilter;
2671 if ((image->storage_class == PseudoClass) ||
2672 IfMagickTrue(image->matte) ||
2673 ((x_factor*y_factor) > 1.0))
2674 filter_type=MitchellFilter;
2675 resize_filter=AcquireResizeFilter(image,filter_type,MagickFalse,exception);
2680 if ((x_factor*y_factor) > WorkLoadFactor)
2682 span=(MagickSizeType) (filter_image->columns+rows);
2683 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
2685 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
2686 span,&offset,exception);
2690 span=(MagickSizeType) (filter_image->rows+columns);
2691 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
2693 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
2694 span,&offset,exception);
2699 filter_image=DestroyImage(filter_image);
2700 resize_filter=DestroyResizeFilter(resize_filter);
2701 if( IfMagickFalse(status) )
2703 resize_image=DestroyImage(resize_image);
2704 return((Image *) NULL);
2706 resize_image->type=image->type;
2707 return(resize_image);
2711 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2715 % S a m p l e I m a g e %
2719 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2721 % SampleImage() scales an image to the desired dimensions with pixel
2722 % sampling. Unlike other scaling methods, this method does not introduce
2723 % any additional color into the scaled image.
2725 % The format of the SampleImage method is:
2727 % Image *SampleImage(const Image *image,const size_t columns,
2728 % const size_t rows,ExceptionInfo *exception)
2730 % A description of each parameter follows:
2732 % o image: the image.
2734 % o columns: the number of columns in the sampled image.
2736 % o rows: the number of rows in the sampled image.
2738 % o exception: return any errors or warnings in this structure.
2741 MagickExport Image *SampleImage(const Image *image,const size_t columns,
2742 const size_t rows,ExceptionInfo *exception)
2744 #define SampleImageTag "Sample/Image"
2767 Initialize sampled image attributes.
2769 assert(image != (const Image *) NULL);
2770 assert(image->signature == MagickSignature);
2771 if( IfMagickTrue(image->debug) )
2772 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2773 assert(exception != (ExceptionInfo *) NULL);
2774 assert(exception->signature == MagickSignature);
2775 if ((columns == 0) || (rows == 0))
2776 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2777 if ((columns == image->columns) && (rows == image->rows))
2778 return(CloneImage(image,0,0,MagickTrue,exception));
2779 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2780 if (sample_image == (Image *) NULL)
2781 return((Image *) NULL);
2783 Allocate scan line buffer and column offset buffers.
2785 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
2787 if (x_offset == (ssize_t *) NULL)
2789 sample_image=DestroyImage(sample_image);
2790 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2792 for (x=0; x < (ssize_t) sample_image->columns; x++)
2793 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
2794 sample_image->columns);
2800 image_view=AcquireVirtualCacheView(image,exception);
2801 sample_view=AcquireAuthenticCacheView(sample_image,exception);
2802 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2803 #pragma omp parallel for schedule(static) shared(progress,status) \
2804 dynamic_number_threads(image,image->columns,image->rows,1)
2806 for (y=0; y < (ssize_t) sample_image->rows; y++)
2808 register const Quantum
2820 if( IfMagickFalse(status) )
2822 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2823 sample_image->rows);
2824 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2826 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2828 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2836 for (x=0; x < (ssize_t) sample_image->columns; x++)
2841 if (GetPixelMask(sample_image,q) != 0)
2843 q+=GetPixelChannels(sample_image);
2846 for (i=0; i < (ssize_t) GetPixelChannels(sample_image); i++)
2855 channel=GetPixelChannelMapChannel(image,i);
2856 traits=GetPixelChannelMapTraits(image,channel);
2857 sample_traits=GetPixelChannelMapTraits(sample_image,channel);
2858 if ((traits == UndefinedPixelTrait) ||
2859 (sample_traits == UndefinedPixelTrait))
2861 SetPixelChannel(sample_image,channel,p[x_offset[x]*GetPixelChannels(
2864 q+=GetPixelChannels(sample_image);
2866 if( IfMagickFalse(SyncCacheViewAuthenticPixels(sample_view,exception)) )
2868 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2873 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2874 #pragma omp critical (MagickCore_SampleImage)
2876 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2877 if( IfMagickFalse(proceed) )
2881 image_view=DestroyCacheView(image_view);
2882 sample_view=DestroyCacheView(sample_view);
2883 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
2884 sample_image->type=image->type;
2885 return(sample_image);
2889 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2893 % S c a l e I m a g e %
2897 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2899 % ScaleImage() changes the size of an image to the given dimensions.
2901 % The format of the ScaleImage method is:
2903 % Image *ScaleImage(const Image *image,const size_t columns,
2904 % const size_t rows,ExceptionInfo *exception)
2906 % A description of each parameter follows:
2908 % o image: the image.
2910 % o columns: the number of columns in the scaled image.
2912 % o rows: the number of rows in the scaled image.
2914 % o exception: return any errors or warnings in this structure.
2917 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2918 const size_t rows,ExceptionInfo *exception)
2920 #define ScaleImageTag "Scale/Image"
2937 pixel[CompositePixelChannel],
2963 Initialize scaled image attributes.
2965 assert(image != (const Image *) NULL);
2966 assert(image->signature == MagickSignature);
2967 if( IfMagickTrue(image->debug) )
2968 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2969 assert(exception != (ExceptionInfo *) NULL);
2970 assert(exception->signature == MagickSignature);
2971 if ((columns == 0) || (rows == 0))
2972 return((Image *) NULL);
2973 if ((columns == image->columns) && (rows == image->rows))
2974 return(CloneImage(image,0,0,MagickTrue,exception));
2975 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2976 if (scale_image == (Image *) NULL)
2977 return((Image *) NULL);
2978 if( IfMagickFalse(SetImageStorageClass(scale_image,DirectClass,exception)) )
2980 scale_image=DestroyImage(scale_image);
2981 return((Image *) NULL);
2986 x_vector=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
2987 GetPixelChannels(image)*sizeof(*x_vector));
2989 if (image->rows != scale_image->rows)
2990 scanline=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
2991 GetPixelChannels(image)*sizeof(*scanline));
2992 scale_scanline=(MagickRealType *) AcquireQuantumMemory((size_t)
2993 scale_image->columns,MaxPixelChannels*sizeof(*scale_scanline));
2994 y_vector=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns,
2995 GetPixelChannels(image)*sizeof(*y_vector));
2996 if ((scanline == (MagickRealType *) NULL) ||
2997 (scale_scanline == (MagickRealType *) NULL) ||
2998 (x_vector == (MagickRealType *) NULL) ||
2999 (y_vector == (MagickRealType *) NULL))
3001 scale_image=DestroyImage(scale_image);
3002 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3008 next_row=MagickTrue;
3010 scale.y=(double) scale_image->rows/(double) image->rows;
3011 for (i=0; i < (ssize_t) (GetPixelChannels(image)*image->columns); i++)
3014 image_view=AcquireVirtualCacheView(image,exception);
3015 scale_view=AcquireAuthenticCacheView(scale_image,exception);
3016 for (y=0; y < (ssize_t) scale_image->rows; y++)
3018 register const Quantum
3027 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
3029 if (q == (Quantum *) NULL)
3032 if (scale_image->rows == image->rows)
3035 Read a new scanline.
3037 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
3039 if (p == (const Quantum *) NULL)
3041 for (x=0; x < (ssize_t) image->columns; x++)
3043 if (GetPixelMask(image,p) != 0)
3045 p+=GetPixelChannels(image);
3048 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3056 channel=GetPixelChannelMapChannel(image,i);
3057 traits=GetPixelChannelMapTraits(image,channel);
3058 if ((traits & BlendPixelTrait) == 0)
3060 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType) p[i];
3063 alpha=QuantumScale*GetPixelAlpha(image,p);
3064 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
3066 p+=GetPixelChannels(image);
3074 while (scale.y < span.y)
3076 if( IfMagickTrue(next_row) && (number_rows < (ssize_t) image->rows))
3079 Read a new scanline.
3081 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
3083 if (p == (const Quantum *) NULL)
3085 for (x=0; x < (ssize_t) image->columns; x++)
3087 if (GetPixelMask(image,p) != 0)
3089 p+=GetPixelChannels(image);
3092 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3100 channel=GetPixelChannelMapChannel(image,i);
3101 traits=GetPixelChannelMapTraits(image,channel);
3102 if ((traits & BlendPixelTrait) == 0)
3104 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType)
3108 alpha=QuantumScale*GetPixelAlpha(image,p);
3109 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
3111 p+=GetPixelChannels(image);
3115 for (x=0; x < (ssize_t) image->columns; x++)
3116 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3117 y_vector[x*GetPixelChannels(image)+i]+=scale.y*
3118 x_vector[x*GetPixelChannels(image)+i];
3120 scale.y=(double) scale_image->rows/(double) image->rows;
3121 next_row=MagickTrue;
3123 if( IfMagickTrue(next_row) && (number_rows < (ssize_t) image->rows))
3126 Read a new scanline.
3128 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
3130 if (p == (const Quantum *) NULL)
3132 for (x=0; x < (ssize_t) image->columns; x++)
3134 if (GetPixelMask(image,p) != 0)
3136 p+=GetPixelChannels(image);
3139 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3147 channel=GetPixelChannelMapChannel(image,i);
3148 traits=GetPixelChannelMapTraits(image,channel);
3149 if ((traits & BlendPixelTrait) == 0)
3151 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType)
3155 alpha=QuantumScale*GetPixelAlpha(image,p);
3156 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
3158 p+=GetPixelChannels(image);
3161 next_row=MagickFalse;
3163 for (x=0; x < (ssize_t) image->columns; x++)
3165 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3167 pixel[i]=y_vector[x*GetPixelChannels(image)+i]+span.y*
3168 x_vector[x*GetPixelChannels(image)+i];
3169 scanline[x*GetPixelChannels(image)+i]=pixel[i];
3170 y_vector[x*GetPixelChannels(image)+i]=0.0;
3176 scale.y=(double) scale_image->rows/(double) image->rows;
3177 next_row=MagickTrue;
3181 if (scale_image->columns == image->columns)
3184 Transfer scanline to scaled image.
3186 for (x=0; x < (ssize_t) scale_image->columns; x++)
3188 if (GetPixelMask(scale_image,q) != 0)
3190 q+=GetPixelChannels(scale_image);
3193 for (i=0; i < (ssize_t) GetPixelChannels(scale_image); i++)
3198 channel=GetPixelChannelMapChannel(scale_image,i);
3199 traits=GetPixelChannelMapTraits(image,channel);
3200 scale_traits=GetPixelChannelMapTraits(scale_image,channel);
3201 if ((traits == UndefinedPixelTrait) ||
3202 (scale_traits == UndefinedPixelTrait))
3204 offset=GetPixelChannelMapOffset(image,channel);
3205 if ((traits & BlendPixelTrait) == 0)
3207 SetPixelChannel(scale_image,channel,ClampToQuantum(
3208 scanline[x*GetPixelChannels(image)+offset]),q);
3211 alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
3212 GetPixelChannelMapChannel(image,AlphaPixelChannel)];
3213 gamma=MagickEpsilonReciprocal(alpha);
3214 SetPixelChannel(scale_image,channel,ClampToQuantum(gamma*scanline[
3215 x*GetPixelChannels(image)+offset]),q);
3217 q+=GetPixelChannels(scale_image);
3228 next_column=MagickFalse;
3231 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3233 for (x=0; x < (ssize_t) image->columns; x++)
3235 scale.x=(double) scale_image->columns/(double) image->columns;
3236 while (scale.x >= span.x)
3238 if( IfMagickTrue(next_column) )
3240 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3244 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3252 channel=GetPixelChannelMapChannel(image,i);
3253 traits=GetPixelChannelMapTraits(image,channel);
3254 if (traits == UndefinedPixelTrait)
3256 pixel[i]+=span.x*scanline[x*GetPixelChannels(image)+i];
3257 scale_scanline[n*MaxPixelChannels+channel]=pixel[i];
3261 next_column=MagickTrue;
3265 if( IfMagickTrue(next_column) )
3267 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3270 next_column=MagickFalse;
3272 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3273 pixel[i]+=scale.x*scanline[x*GetPixelChannels(image)+i];
3279 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3280 pixel[i]+=span.x*scanline[(x-1)*GetPixelChannels(image)+i];
3282 if( IfMagickFalse(next_column) &&
3283 ((ssize_t) n < (ssize_t) scale_image->columns))
3284 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3286 channel=GetPixelChannelMapChannel(image,i);
3287 scale_scanline[n*MaxPixelChannels+channel]=pixel[i];
3290 Transfer scanline to scaled image.
3292 for (x=0; x < (ssize_t) scale_image->columns; x++)
3294 if (GetPixelMask(scale_image,q) != 0)
3296 q+=GetPixelChannels(scale_image);
3299 for (i=0; i < (ssize_t) GetPixelChannels(scale_image); i++)
3301 channel=GetPixelChannelMapChannel(scale_image,i);
3302 traits=GetPixelChannelMapTraits(image,channel);
3303 scale_traits=GetPixelChannelMapTraits(scale_image,channel);
3304 if ((traits == UndefinedPixelTrait) ||
3305 (scale_traits == UndefinedPixelTrait))
3307 if ((traits & BlendPixelTrait) == 0)
3309 SetPixelChannel(scale_image,channel,ClampToQuantum(
3310 scale_scanline[x*MaxPixelChannels+channel]),q);
3313 alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
3314 GetPixelChannelMapChannel(image,AlphaPixelChannel)];
3315 gamma=MagickEpsilonReciprocal(alpha);
3316 SetPixelChannel(scale_image,channel,ClampToQuantum(gamma*
3317 scale_scanline[x*MaxPixelChannels+channel]),q);
3319 q+=GetPixelChannels(scale_image);
3322 if( IfMagickFalse(SyncCacheViewAuthenticPixels(scale_view,exception)) )
3324 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3326 if( IfMagickFalse(proceed) )
3329 scale_view=DestroyCacheView(scale_view);
3330 image_view=DestroyCacheView(image_view);
3332 Free allocated memory.
3334 y_vector=(MagickRealType *) RelinquishMagickMemory(y_vector);
3335 scale_scanline=(MagickRealType *) RelinquishMagickMemory(scale_scanline);
3336 if (scale_image->rows != image->rows)
3337 scanline=(MagickRealType *) RelinquishMagickMemory(scanline);
3338 x_vector=(MagickRealType *) RelinquishMagickMemory(x_vector);
3339 scale_image->type=image->type;
3340 return(scale_image);
3344 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3348 % T h u m b n a i l I m a g e %
3352 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3354 % ThumbnailImage() changes the size of an image to the given dimensions and
3355 % removes any associated profiles. The goal is to produce small low cost
3356 % thumbnail images suited for display on the Web.
3358 % The format of the ThumbnailImage method is:
3360 % Image *ThumbnailImage(const Image *image,const size_t columns,
3361 % const size_t rows,ExceptionInfo *exception)
3363 % A description of each parameter follows:
3365 % o image: the image.
3367 % o columns: the number of columns in the scaled image.
3369 % o rows: the number of rows in the scaled image.
3371 % o exception: return any errors or warnings in this structure.
3374 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3375 const size_t rows,ExceptionInfo *exception)
3377 #define SampleFactor 5
3380 value[MaxTextExtent];
3398 assert(image != (Image *) NULL);
3399 assert(image->signature == MagickSignature);
3400 if( IfMagickTrue(image->debug) )
3401 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3402 assert(exception != (ExceptionInfo *) NULL);
3403 assert(exception->signature == MagickSignature);
3404 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3405 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3406 if ((x_factor*y_factor) > 0.1)
3407 thumbnail_image=ResizeImage(image,columns,rows,image->filter,exception);
3409 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
3410 thumbnail_image=ResizeImage(image,columns,rows,image->filter,exception);
3416 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3418 if (sample_image == (Image *) NULL)
3419 return((Image *) NULL);
3420 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3422 sample_image=DestroyImage(sample_image);
3424 if (thumbnail_image == (Image *) NULL)
3425 return(thumbnail_image);
3426 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3427 if( IfMagickFalse(thumbnail_image->matte) )
3428 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel,exception);
3429 thumbnail_image->depth=8;
3430 thumbnail_image->interlace=NoInterlace;
3432 Strip all profiles except color profiles.
3434 ResetImageProfileIterator(thumbnail_image);
3435 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3437 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3439 (void) DeleteImageProfile(thumbnail_image,name);
3440 ResetImageProfileIterator(thumbnail_image);
3442 name=GetNextImageProfile(thumbnail_image);
3444 (void) DeleteImageProperty(thumbnail_image,"comment");
3445 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3446 if (strstr(image->magick_filename,"//") == (char *) NULL)
3447 (void) FormatLocaleString(value,MaxTextExtent,"file://%s",
3448 image->magick_filename);
3449 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value,exception);
3450 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3451 if( IfMagickTrue(GetPathAttributes(image->filename,&attributes)) )
3453 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3454 attributes.st_mtime);
3455 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value,exception);
3457 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3458 attributes.st_mtime);
3459 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
3460 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
3461 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value,exception);
3462 (void) FormatLocaleString(value,MaxTextExtent,"image/%s",image->magick);
3464 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value,exception);
3465 (void) SetImageProperty(thumbnail_image,"software",GetMagickVersion(&version),
3467 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3468 image->magick_columns);
3469 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value,
3471 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3472 image->magick_rows);
3473 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Height",value,
3475 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3476 GetImageListLength(image));
3477 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value,
3479 return(thumbnail_image);