2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % RRRR EEEEE SSSSS IIIII ZZZZZ EEEEE %
8 % RRRR EEE SSS I ZZZ EEE %
10 % R R EEEEE SSSSS IIIII ZZZZZ EEEEE %
13 % MagickCore Image Resize Methods %
20 % Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42 #include "magick/studio.h"
43 #include "magick/artifact.h"
44 #include "magick/blob.h"
45 #include "magick/cache.h"
46 #include "magick/cache-view.h"
47 #include "magick/color.h"
48 #include "magick/color-private.h"
49 #include "magick/draw.h"
50 #include "magick/exception.h"
51 #include "magick/exception-private.h"
52 #include "magick/gem.h"
53 #include "magick/image.h"
54 #include "magick/image-private.h"
55 #include "magick/list.h"
56 #include "magick/memory_.h"
57 #include "magick/magick.h"
58 #include "magick/pixel-private.h"
59 #include "magick/property.h"
60 #include "magick/monitor.h"
61 #include "magick/monitor-private.h"
62 #include "magick/pixel.h"
63 #include "magick/option.h"
64 #include "magick/resample.h"
65 #include "magick/resample-private.h"
66 #include "magick/resize.h"
67 #include "magick/resize-private.h"
68 #include "magick/string_.h"
69 #include "magick/string-private.h"
70 #include "magick/thread-private.h"
71 #include "magick/utility.h"
72 #include "magick/version.h"
73 #if defined(MAGICKCORE_LQR_DELEGATE)
83 (*filter)(const MagickRealType,const ResizeFilter *),
84 (*window)(const MagickRealType,const ResizeFilter *),
85 support, /* filter region of support - the filter support limit */
86 window_support, /* window support, usally equal to support (expert only) */
87 scale, /* dimension scaling to fit window support (usally 1.0) */
88 blur, /* x-scale (blur-sharpen) */
89 coefficient[7]; /* cubic coefficents for BC-cubic spline filters */
96 Forward declaractions.
100 BesselOrderOne(MagickRealType),
101 Sinc(const MagickRealType, const ResizeFilter *),
102 SincFast(const MagickRealType, const ResizeFilter *);
105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
109 + F i l t e r F u n c t i o n s %
113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
115 % These are the various filter and windowing functions that are provided.
117 % They are internal to this module only. See AcquireResizeFilterInfo() for
118 % details of the access to these functions, via the GetResizeFilterSupport()
119 % and GetResizeFilterWeight() API interface.
121 % The individual filter functions have this format...
123 % static MagickRealtype *FilterName(const MagickRealType x,
124 % const MagickRealType support)
126 % A description of each parameter follows:
128 % o x: the distance from the sampling point generally in the range of 0 to
129 % support. The GetResizeFilterWeight() ensures this a positive value.
131 % o resize_filter: current filter information. This allows function to
132 % access support, and possibly other pre-calculated information defining
137 #define MagickPIL ((MagickRealType) 3.14159265358979323846264338327950288420L)
139 static MagickRealType Jinc(const MagickRealType x,
140 const ResizeFilter *magick_unused(resize_filter))
143 See Pratt "Digital Image Processing" p.97 for Jinc/Bessel functions.
144 http://mathworld.wolfram.com/JincFunction.html and page 11 of
145 http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
147 The original "zoom" program by Paul Heckbert called this "Bessel".
148 But really it is more accurately named "Jinc".
151 return(0.5*MagickPIL);
152 return(BesselOrderOne(MagickPIL*x)/x);
155 static MagickRealType Blackman(const MagickRealType x,
156 const ResizeFilter *magick_unused(resize_filter))
159 Blackman: 2nd order cosine windowing function:
160 0.42 + 0.5 cos(pi x) + 0.08 cos(2pi x)
161 Refactored by Chantal Racette and Nicolas Robidoux to one trig
164 const MagickRealType cospix = cos((double) (MagickPIL*x));
165 return(0.34+cospix*(0.5+cospix*0.16));
168 static MagickRealType Bohman(const MagickRealType x,
169 const ResizeFilter *magick_unused(resize_filter))
172 Bohman: 2rd Order cosine windowing function:
173 (1-x) cos(pi x) + sin(pi x) / pi.
174 Refactored by Nicolas Robidoux to one trig call, one sqrt call,
175 and 7 flops, taking advantage of the fact that the support of
176 Bohman is 1 (so that we know that sin(pi x) >= 0).
178 const double cospix = cos((double) (MagickPIL*x));
179 const double sinpix = sqrt(1.0-cospix*cospix);
180 return((1.0-x)*cospix+(1.0/MagickPIL)*sinpix);
183 static MagickRealType Box(const MagickRealType magick_unused(x),
184 const ResizeFilter *magick_unused(resize_filter))
187 A Box filter is a equal weighting function (all weights equal).
188 DO NOT LIMIT results by support or resize point sampling will work
189 as it requests points beyond its normal 0.0 support size.
194 static MagickRealType CubicBC(const MagickRealType x,
195 const ResizeFilter *resize_filter)
198 Cubic Filters using B,C determined values:
199 Mitchell-Netravali B= 1/3 C= 1/3 "Balanced" cubic spline filter
200 Catmull-Rom B= 0 C= 1/2 Interpolatory and exact on linears
201 Cubic B-Spline B= 1 C= 0 Spline approximation of Gaussian
202 Hermite B= 0 C= 0 Spline with small support (= 1)
204 See paper by Mitchell and Netravali, Reconstruction Filters in Computer
205 Graphics Computer Graphics, Volume 22, Number 4, August 1988
206 http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/
209 Coefficents are determined from B,C values:
210 P0 = ( 6 - 2*B )/6 = coeff[0]
212 P2 = (-18 +12*B + 6*C )/6 = coeff[1]
213 P3 = ( 12 - 9*B - 6*C )/6 = coeff[2]
214 Q0 = ( 8*B +24*C )/6 = coeff[3]
215 Q1 = ( -12*B -48*C )/6 = coeff[4]
216 Q2 = ( 6*B +30*C )/6 = coeff[5]
217 Q3 = ( - 1*B - 6*C )/6 = coeff[6]
219 which are used to define the filter:
221 P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
222 Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x < 2
224 which ensures function is continuous in value and derivative
228 return(resize_filter->coefficient[0]+x*(x*
229 (resize_filter->coefficient[1]+x*resize_filter->coefficient[2])));
231 return(resize_filter->coefficient[3]+x*(resize_filter->coefficient[4]+x*
232 (resize_filter->coefficient[5]+x*resize_filter->coefficient[6])));
236 static MagickRealType Gaussian(const MagickRealType x,
237 const ResizeFilter *resize_filter)
240 Gaussian with a fixed sigma = 1/2
242 Gaussian Formula (1D) ...
243 exp( -(x^2)/((2.0*sigma^2) ) / sqrt(2*PI)sigma^2))
244 The constants are pre-calculated...
245 exp( -coeff[0]*(x^2)) ) * coeff[1]
246 However the multiplier coefficent (1) is not needed and not used.
248 Gaussian Formula (2D) ...
249 exp( -(x^2)/((2.0*sigma^2) ) / (PI*sigma^2) )
250 Note that it is only a change in the normalization multiplier
251 which is not needed or used when gausian is used as a filter.
253 This separates the gaussian 'sigma' value from the 'blur/support'
254 settings allowing for its use in special 'small sigma' gaussians,
255 without the filter 'missing' pixels because the support becomes too
258 return(exp((double)(-resize_filter->coefficient[0]*x*x)));
261 static MagickRealType Hanning(const MagickRealType x,
262 const ResizeFilter *magick_unused(resize_filter))
265 Cosine window function:
268 const MagickRealType cospix = cos((double) (MagickPIL*x));
269 return(0.5+0.5*cospix);
272 static MagickRealType Hamming(const MagickRealType x,
273 const ResizeFilter *magick_unused(resize_filter))
276 Offset cosine window function:
279 const MagickRealType cospix = cos((double) (MagickPIL*x));
280 return(0.54+0.46*cospix);
283 static MagickRealType Kaiser(const MagickRealType x,
284 const ResizeFilter *magick_unused(resize_filter))
287 #define I0A (1.0/I0(Alpha))
290 Kaiser Windowing Function (bessel windowing): Alpha is a free
291 value from 5 to 8 (currently hardcoded to 6.5).
292 Future: make alpha the IOA pre-calculation, an 'expert' setting.
294 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
297 static MagickRealType Lagrange(const MagickRealType x,
298 const ResizeFilter *resize_filter)
311 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the
312 lagrange function and depends on the overall support window size
313 of the filter. That is: for a support of 2, it gives a lagrange-4
314 (piecewise cubic function).
316 "n" identifies the piece of the piecewise polynomial.
318 See Survey: Interpolation Methods, IEEE Transactions on Medical
319 Imaging, Vol 18, No 11, November 1999, p1049-1075, -- Equation 27
322 if (x > resize_filter->support)
324 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
325 /*n=(ssize_t)((1.0*order)/2.0+x); -- which piece does x belong to */
326 n = (ssize_t)(resize_filter->window_support + x);
328 for (i=0; i < order; i++)
330 value*=(n-i-x)/(n-i);
334 static MagickRealType Quadratic(const MagickRealType x,
335 const ResizeFilter *magick_unused(resize_filter))
338 2rd order (quadratic) B-Spline approximation of Gaussian.
343 return(0.5*(x-1.5)*(x-1.5));
347 static MagickRealType Sinc(const MagickRealType x,
348 const ResizeFilter *magick_unused(resize_filter))
351 Scaled sinc(x) function using a trig call:
352 sinc(x) == sin(pi x)/(pi x).
356 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
357 return(sin((double) pix)/pix);
359 return((MagickRealType) 1.0);
362 static MagickRealType SincFast(const MagickRealType x,
363 const ResizeFilter *magick_unused(resize_filter))
366 Approximations of the sinc function sin(pi x)/(pi x) over the
367 interval [-4,4] constructed by Nicolas Robidoux and Chantal
368 Racette with funding from the Natural Sciences and Engineering
369 Research Council of Canada.
371 Although the approximations are polynomials (for low order of
372 approximation) and quotients of polynomials (for higher order of
373 approximation) and consequently are similar in form to Taylor
374 polynomials/Pade approximants, the approximations are computed
375 with a completely different technique.
377 Summary: These approximations are "the best" in terms of bang
378 (accuracy) for the buck (flops). More specifically: Among the
379 polynomial quotients that can be computed using a fixed number of
380 flops (with a given "+ - * / budget"), the chosen polynomial
381 quotient is the one closest to the approximated function with
382 respect to maximum absolute relative error over the given
385 The Remez algorithm, as implemented in the boost library's minimax
386 package, is the key to the construction:
387 http://www.boost.org/doc/libs/1_36_0/libs/math/doc/...
388 ...sf_and_dist/html/math_toolkit/backgrounders/remez.html
391 If outside of the interval of approximation, use the standard trig
396 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
397 return(sin((double) pix)/pix);
401 The approximations only depend on x^2 (sinc is an even
404 const MagickRealType xx = x*x;
405 #if MAGICKCORE_QUANTUM_DEPTH <= 8
407 Maximum absolute relative error 6.3e-6 < 1/2^17.
409 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
410 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
411 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
412 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
413 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
414 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
415 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
416 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
417 const MagickRealType p =
418 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
419 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
420 #elif MAGICKCORE_QUANTUM_DEPTH <= 16
422 Max. abs. rel. error 2.2e-8 < 1/2^25.
424 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
425 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
426 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
427 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
428 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
429 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
430 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
431 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
432 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
433 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
434 const MagickRealType p =
435 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
436 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
439 Max. abs. rel. error 1.2e-12 < 1/2^39.
441 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
442 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
443 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
444 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
445 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
446 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
447 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
448 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
449 const MagickRealType p =
450 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
451 const MagickRealType d0 = 1.0L;
452 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
453 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
454 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
455 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
456 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
457 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
462 static MagickRealType Triangle(const MagickRealType x,
463 const ResizeFilter *magick_unused(resize_filter))
466 1st order (linear) B-Spline, bilinear interpolation, Tent 1D
467 filter, or a Bartlett 2D Cone filter. Also used as a
468 Bartlett Windowing function for Sinc().
475 static MagickRealType Welsh(const MagickRealType x,
476 const ResizeFilter *magick_unused(resize_filter))
479 Welsh parabolic windowing filter.
487 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
491 + A c q u i r e R e s i z e F i l t e r %
495 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
497 % AcquireResizeFilter() allocates the ResizeFilter structure. Choose from
500 % FIR (Finite impulse Response) Filters
501 % Box Triangle Quadratic
502 % Cubic Hermite Catrom
505 % IIR (Infinite impulse Response) Filters
506 % Gaussian Sinc Jinc (Bessel)
508 % Windowed Sinc/Jinc Filters
509 % Blackman Hanning Hamming
512 % Special purpose Filters
513 % SincFast LanczosSharp Lanczos2D Lanczos2DSharp Robidoux
515 % The users "-filter" selection is used to lookup the default 'expert'
516 % settings for that filter from a internal table. However any provided
517 % 'expert' settings (see below) may override this selection.
519 % FIR filters are used as is, and are limited to that filters support window
520 % (unless over-ridden). 'Gaussian' while classed as an IIR filter, is also
521 % simply clipped by its support size (currently 1.5 or approximatally 3*sigma
522 % as recommended by many references)
524 % The special a 'cylindrical' filter flag will promote the default 4-lobed
525 % Windowed Sinc filter to a 3-lobed Windowed Jinc equivalent, which is better
526 % suited to this style of image resampling. This typically happens when using
527 % such a filter for images distortions.
529 % Directly requesting 'Sinc', 'Jinc' function as a filter will force the use
530 % of function without any windowing, or promotion for cylindrical usage. This
531 % is not recommended, except by image processing experts, especially as part
532 % of expert option filter function selection.
534 % Two forms of the 'Sinc' function are available: Sinc and SincFast. Sinc is
535 % computed using the traditional sin(pi*x)/(pi*x); it is selected if the user
536 % specifically specifies the use of a Sinc filter. SincFast uses highly
537 % accurate (and fast) polynomial (low Q) and rational (high Q) approximations,
538 % and will be used by default in most cases.
540 % The Lanczos filter is a special 3-lobed Sinc-windowed Sinc filter (promoted
541 % to Jinc-windowed Jinc for cylindrical (Elliptical Weighted Average) use).
542 % The Sinc version is the most popular windowed filter.
544 % LanczosSharp is a slightly sharpened (blur=0.9812505644269356 < 1) form of
545 % the Lanczos filter, specifically designed for EWA distortion (as a
546 % Jinc-Jinc); it can also be used as a slightly sharper orthogonal Lanczos
547 % (Sinc-Sinc) filter. The chosen blur value comes as close as possible to
548 % satisfying the following condition without changing the character of the
549 % corresponding EWA filter:
551 % 'No-Op' Vertical and Horizontal Line Preservation Condition: Images with
552 % only vertical or horizontal features are preserved when performing 'no-op"
553 % with EWA distortion.
555 % The Lanczos2 and Lanczos2Sharp filters are 2-lobe versions of the Lanczos
556 % filters. The 'sharp' version uses a blur factor of 0.9549963639785485,
557 % again chosen because the resulting EWA filter comes as close as possible to
558 % satisfying the above condition.
560 % Robidoux is another filter tuned for EWA. It is the Keys cubic filter
561 % defined by B=(228 - 108 sqrt(2))/199. Robidoux satisfies the "'No-Op'
562 % Vertical and Horizontal Line Preservation Condition" exactly, and it
563 % moderately blurs high frequency 'pixel-hash' patterns under no-op. It turns
564 % out to be close to both Mitchell and Lanczos2Sharp. For example, its first
565 % crossing is at (36 sqrt(2) + 123)/(72 sqrt(2) + 47), almost the same as the
566 % first crossing of Mitchell and Lanczos2Sharp.
570 % These artifact "defines" are not recommended for production use without
571 % expert knowledge of resampling, filtering, and the effects they have on the
572 % resulting resampled (resize ro distorted) image.
574 % They can be used to override any and all filter default, and it is
575 % recommended you make good use of "filter:verbose" to make sure that the
576 % overall effect of your selection (before and after) is as expected.
578 % "filter:verbose" controls whether to output the exact results of the
579 % filter selections made, as well as plotting data for graphing the
580 % resulting filter over the filters support range.
582 % "filter:filter" select the main function associated with this filter
583 % name, as the weighting function of the filter. This can be used to
584 % set a windowing function as a weighting function, for special
585 % purposes, such as graphing.
587 % If a "filter:window" operation has not been provided, a 'Box'
588 % windowing function will be set to denote that no windowing function is
591 % "filter:window" Select this windowing function for the filter. While any
592 % filter could be used as a windowing function, using the 'first lobe' of
593 % that filter over the whole support window, using a non-windowing
594 % function is not advisible. If no weighting filter function is specifed
595 % a 'SincFast' filter is used.
597 % "filter:lobes" Number of lobes to use for the Sinc/Jinc filter. This a
598 % simpler method of setting filter support size that will correctly
599 % handle the Sinc/Jinc switch for an operators filtering requirements.
600 % Only integers should be given.
602 % "filter:support" Set the support size for filtering to the size given.
603 % This not recommended for Sinc/Jinc windowed filters (lobes should be
604 % used instead). This will override any 'filter:lobes' option.
606 % "filter:win-support" Scale windowing function to this size instead. This
607 % causes the windowing (or self-windowing Lagrange filter) to act is if
608 % the support window it much much larger than what is actually supplied
609 % to the calling operator. The filter however is still clipped to the
610 % real support size given, by the support range suppiled to the caller.
611 % If unset this will equal the normal filter support size.
613 % "filter:blur" Scale the filter and support window by this amount. A value
614 % > 1 will generally result in a more burred image with more ringing
615 % effects, while a value <1 will sharpen the resulting image with more
618 % "filter:sigma" The sigma value to use for the Gaussian filter only.
619 % Defaults to '1/2'. Using a different sigma effectively provides a
620 % method of using the filter as a 'blur' convolution. Particularly when
621 % using it for Distort.
624 % "filter:c" Override the preset B,C values for a Cubic type of filter.
625 % If only one of these are given it is assumes to be a 'Keys' type of
626 % filter such that B+2C=1, where Keys 'alpha' value = C.
630 % Set a true un-windowed Sinc filter with 10 lobes (very slow):
631 % -define filter:filter=Sinc
632 % -define filter:lobes=8
634 % Set an 8 lobe Lanczos (Sinc or Jinc) filter:
636 % -define filter:lobes=8
638 % The format of the AcquireResizeFilter method is:
640 % ResizeFilter *AcquireResizeFilter(const Image *image,
641 % const FilterTypes filter_type, const MagickBooleanType radial,
642 % ExceptionInfo *exception)
644 % A description of each parameter follows:
646 % o image: the image.
648 % o filter: the filter type, defining a preset filter, window and support.
649 % The artifact settings listed above will override those selections.
651 % o blur: blur the filter by this amount, use 1.0 if unknown. Image
652 % artifact "filter:blur" will override this API call usage, including any
653 % internal change (such as for cylindrical usage).
655 % o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical (radial)
658 % o exception: return any errors or warnings in this structure.
661 MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
662 const FilterTypes filter,const MagickRealType blur,
663 const MagickBooleanType cylindrical,ExceptionInfo *exception)
677 register ResizeFilter
681 Table Mapping given Filter, into Weighting and Windowing functions. A
682 'Box' windowing function means its a simble non-windowed filter. An
683 'SincFast' filter function could be upgraded to a 'Jinc' filter if a
684 "cylindrical", unless a 'Sinc' or 'SincFast' filter was specifically
687 WARNING: The order of this tabel must match the order of the FilterTypes
688 enumeration specified in "resample.h", or the filter names will not match
689 the filter being setup.
691 You can check filter setups with the "filter:verbose" setting.
698 } const mapping[SentinelFilter] =
700 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
701 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
702 { BoxFilter, BoxFilter }, /* Box averaging filter */
703 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
704 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
705 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
706 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
707 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
708 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
709 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approx */
710 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
711 { CatromFilter, BoxFilter }, /* Cubic-Keys interpolator */
712 { MitchellFilter, BoxFilter }, /* 'Ideal' Cubic-Keys filter */
713 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
714 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
715 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
716 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
717 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
718 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
719 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
720 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
721 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing */
722 { LanczosFilter, LanczosFilter }, /* Lanczos Sinc-Sinc filters */
723 { LanczosSharpFilter, LanczosSharpFilter }, /* | these require */
724 { Lanczos2Filter, Lanczos2Filter }, /* | special handling */
725 { Lanczos2SharpFilter,Lanczos2SharpFilter },
726 { RobidouxFilter, BoxFilter }, /* Cubic Keys tuned for EWA */
729 Table mapping the filter/window from the above table to an actual function.
730 The default support size for that filter as a weighting function, the range
731 to scale with to use that function as a sinc windowing function, (typ 1.0).
733 Note that the filter_type -> function is 1 to 1 except for Sinc(),
734 SincFast(), and CubicBC() functions, which may have multiple filter to
735 function associations.
737 See "filter:verbose" handling below for the function -> filter mapping.
742 (*function)(const MagickRealType, const ResizeFilter*),
743 lobes, /* Default lobes/support size of the weighting filter. */
744 scale, /* Support when function used as a windowing function
745 Typically equal to the location of the first zero crossing. */
746 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
747 } const filters[SentinelFilter] =
749 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
750 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
751 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
752 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
753 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
754 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
755 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
756 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
757 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
758 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
759 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
760 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
761 { CubicBC, 2.0, 8.0/7.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
762 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
763 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
764 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
765 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
766 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
767 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
768 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
769 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
770 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
771 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
772 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* lanczos, Sharpened */
773 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos, 2-lobed */
774 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2, sharpened */
775 { CubicBC, 2.0, 1.1685777620836932,
776 0.37821575509399867, 0.31089212245300067 }
777 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
780 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
781 function being used as a filter. It is used by the "filter:lobes" expert
782 setting and for 'lobes' for Jinc functions in the previous table. This way
783 users do not have to deal with the highly irrational lobe sizes of the Jinc
787 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp using
788 Jv-function with v=1, then dividing by PI.
790 static MagickRealType
812 Allocate resize filter.
814 assert(image != (const Image *) NULL);
815 assert(image->signature == MagickSignature);
816 if (image->debug != MagickFalse)
817 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
818 assert(UndefinedFilter < filter && filter < SentinelFilter);
819 assert(exception != (ExceptionInfo *) NULL);
820 assert(exception->signature == MagickSignature);
821 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
822 if (resize_filter == (ResizeFilter *) NULL)
823 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
825 Defaults for the requested filter.
827 filter_type=mapping[filter].filter;
828 window_type=mapping[filter].window;
829 resize_filter->blur = blur; /* function argument blur factor */
830 sigma = 0.5; /* guassian sigma of half a pixel by default */
831 /* Promote 1D Windowed Sinc Filters to a 2D Windowed Jinc filters */
832 if (cylindrical != MagickFalse && filter_type == SincFastFilter
833 && filter != SincFastFilter )
834 filter_type=JincFilter;
836 /* Expert filter setting override */
837 artifact=GetImageArtifact(image,"filter:filter");
838 if (artifact != (const char *) NULL)
843 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
844 if ((UndefinedFilter < option) && (option < SentinelFilter))
845 { /* Raw filter request - no window function. */
846 filter_type=(FilterTypes) option;
847 window_type=BoxFilter;
849 /* Filter override with a specific window function. */
850 artifact=GetImageArtifact(image,"filter:window");
851 if (artifact != (const char *) NULL)
853 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
854 if ((UndefinedFilter < option) && (option < SentinelFilter))
855 window_type=(FilterTypes) option;
860 /* Window specified, but no filter function? Assume Sinc/Jinc. */
861 artifact=GetImageArtifact(image,"filter:window");
862 if (artifact != (const char *) NULL)
867 option=ParseCommandOption(MagickFilterOptions,MagickFalse,
869 if ((UndefinedFilter < option) && (option < SentinelFilter))
871 filter_type=cylindrical != MagickFalse ?
872 JincFilter : SincFastFilter;
873 window_type=(FilterTypes) option;
878 /* Assign the real functions to use for the filters selected. */
879 resize_filter->filter=filters[filter_type].function;
880 resize_filter->support=filters[filter_type].lobes;
881 resize_filter->window=filters[window_type].function;
882 resize_filter->scale=filters[window_type].scale;
883 resize_filter->signature=MagickSignature;
885 /* Filter Modifications for orthogonal/cylindrical usage */
886 if (cylindrical != MagickFalse)
890 /* Support for Cylindrical Box should be sqrt(2)/2 */
891 resize_filter->support=(MagickRealType) MagickSQ1_2;
894 case LanczosSharpFilter:
896 case Lanczos2SharpFilter:
897 resize_filter->filter=filters[JincFilter].function;
898 resize_filter->window=filters[JincFilter].function;
899 resize_filter->scale=filters[JincFilter].scale;
900 /* number of lobes (support window size) remain unchanged */
905 /* Global Sharpening (regardless of orthoginal/cylindrical) */
908 case LanczosSharpFilter:
909 resize_filter->blur *= 0.9812505644269356;
911 case Lanczos2SharpFilter:
912 resize_filter->blur *= 0.9549963639785485;
919 ** Other Expert Option Modifications
922 /* User Sigma Override - no support change */
923 artifact=GetImageArtifact(image,"filter:sigma");
924 if (artifact != (const char *) NULL)
925 sigma=InterpretLocaleValue(artifact,(char **) NULL);
926 /* Define coefficents for Gaussian */
927 if ( GaussianFilter ) {
928 resize_filter->coefficient[0]=1.0/(2.0*sigma*sigma);
929 resize_filter->coefficient[1]=(MagickRealType) (1.0/(Magick2PI*sigma*
930 sigma)); /* Normalization Multiplier - unneeded for filters */
934 artifact=GetImageArtifact(image,"filter:blur");
935 if (artifact != (const char *) NULL)
936 resize_filter->blur *= InterpretLocaleValue(artifact,(char **) NULL);
937 if (resize_filter->blur < MagickEpsilon)
938 resize_filter->blur=(MagickRealType) MagickEpsilon;
940 /* Support Overrides */
941 artifact=GetImageArtifact(image,"filter:lobes");
942 if (artifact != (const char *) NULL)
947 lobes=(ssize_t) StringToLong(artifact);
950 resize_filter->support=(MagickRealType) lobes;
952 /* Convert a Jinc function lobes value to a real support value */
953 if (resize_filter->filter == Jinc)
955 if (resize_filter->support > 16)
956 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
958 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
960 /* expert override of the support setting */
961 artifact=GetImageArtifact(image,"filter:support");
962 if (artifact != (const char *) NULL)
963 resize_filter->support=fabs(InterpretLocaleValue(artifact,(char **) NULL));
965 Scale windowing function separatally to the support 'clipping'
966 window that calling operator is planning to actually use. (Expert
969 resize_filter->window_support=resize_filter->support; /* default */
970 artifact=GetImageArtifact(image,"filter:win-support");
971 if (artifact != (const char *) NULL)
972 resize_filter->window_support=fabs(InterpretLocaleValue(artifact,(char **) NULL));
974 Adjust window function scaling to match windowing support for
975 weighting function. This avoids a division on every filter call.
977 resize_filter->scale /= resize_filter->window_support;
980 * Set Cubic Spline B,C values, calculate Cubic coefficients.
984 if ((filters[filter_type].function == CubicBC) ||
985 (filters[window_type].function == CubicBC))
987 B=filters[filter_type].B;
988 C=filters[filter_type].C;
989 if (filters[window_type].function == CubicBC)
991 B=filters[window_type].B;
992 C=filters[window_type].C;
994 artifact=GetImageArtifact(image,"filter:b");
995 if (artifact != (const char *) NULL)
997 B=InterpretLocaleValue(artifact,(char **) NULL);
998 C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
999 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
1000 if (artifact != (const char *) NULL)
1001 C=InterpretLocaleValue(artifact,(char **) NULL);
1005 artifact=GetImageArtifact(image,"filter:c");
1006 if (artifact != (const char *) NULL)
1008 C=InterpretLocaleValue(artifact,(char **) NULL);
1009 B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
1012 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
1014 const double twoB = B+B;
1015 resize_filter->coefficient[0]=1.0-(1.0/3.0)*B;
1016 resize_filter->coefficient[1]=-3.0+twoB+C;
1017 resize_filter->coefficient[2]=2.0-1.5*B-C;
1018 resize_filter->coefficient[3]=(4.0/3.0)*B+4.0*C;
1019 resize_filter->coefficient[4]=-8.0*C-twoB;
1020 resize_filter->coefficient[5]=B+5.0*C;
1021 resize_filter->coefficient[6]=(-1.0/6.0)*B-C;
1026 Expert Option Request for verbose details of the resulting filter.
1028 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1032 artifact=GetImageArtifact(image,"filter:verbose");
1033 if (IsMagickTrue(artifact))
1040 Set the weighting function properly when the weighting
1041 function may not exactly match the filter of the same name.
1042 EG: a Point filter is really uses a Box weighting function
1043 with a different support than is typically used.
1045 if (resize_filter->filter == Box) filter_type=BoxFilter;
1046 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1047 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1048 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1049 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1050 if (resize_filter->window == Box) window_type=BoxFilter;
1051 if (resize_filter->window == Sinc) window_type=SincFilter;
1052 if (resize_filter->window == SincFast) window_type=SincFastFilter;
1053 if (resize_filter->window == Jinc) window_type=JincFilter;
1054 if (resize_filter->window == CubicBC) window_type=CubicFilter;
1056 Report Filter Details.
1058 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1059 (void) FormatLocaleFile(stdout,"# Resize Filter (for graphing)\n#\n");
1060 (void) FormatLocaleFile(stdout,"# filter = %s\n",
1061 CommandOptionToMnemonic(MagickFilterOptions,filter_type));
1062 (void) FormatLocaleFile(stdout,"# window = %s\n",
1063 CommandOptionToMnemonic(MagickFilterOptions, window_type));
1064 (void) FormatLocaleFile(stdout,"# support = %.*g\n",
1065 GetMagickPrecision(),(double) resize_filter->support);
1066 (void) FormatLocaleFile(stdout,"# win-support = %.*g\n",
1067 GetMagickPrecision(),(double) resize_filter->window_support);
1068 (void) FormatLocaleFile(stdout,"# scale_blur = %.*g\n",
1069 GetMagickPrecision(), (double)resize_filter->blur);
1070 if ( filter_type == GaussianFilter )
1071 (void) FormatLocaleFile(stdout,"# gaussian_sigma = %.*g\n",
1072 GetMagickPrecision(), (double)sigma);
1073 (void) FormatLocaleFile(stdout,"# practical_support = %.*g\n",
1074 GetMagickPrecision(), (double)support);
1075 if ( filter_type == CubicFilter || window_type == CubicFilter )
1076 (void) FormatLocaleFile(stdout,"# B,C = %.*g,%.*g\n",
1077 GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
1078 (void) FormatLocaleFile(stdout,"\n");
1080 Output values of resulting filter graph -- for graphing
1083 for (x=0.0; x <= support; x+=0.01f)
1084 (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1085 (double) GetResizeFilterWeight(resize_filter,x));
1086 /* A final value so gnuplot can graph the 'stop' properly. */
1087 (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",support,
1088 GetMagickPrecision(),0.0);
1090 /* Output the above once only for each image - remove setting */
1091 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1092 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1095 return(resize_filter);
1099 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1103 % A d a p t i v e R e s i z e I m a g e %
1107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1109 % AdaptiveResizeImage() adaptively resize image with pixel resampling.
1111 % The format of the AdaptiveResizeImage method is:
1113 % Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1114 % const size_t rows,ExceptionInfo *exception)
1116 % A description of each parameter follows:
1118 % o image: the image.
1120 % o columns: the number of columns in the resized image.
1122 % o rows: the number of rows in the resized image.
1124 % o exception: return any errors or warnings in this structure.
1127 MagickExport Image *AdaptiveResizeImage(const Image *image,
1128 const size_t columns,const size_t rows,ExceptionInfo *exception)
1130 #define AdaptiveResizeImageTag "Resize/Image"
1149 Adaptively resize image.
1151 assert(image != (const Image *) NULL);
1152 assert(image->signature == MagickSignature);
1153 if (image->debug != MagickFalse)
1154 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1155 assert(exception != (ExceptionInfo *) NULL);
1156 assert(exception->signature == MagickSignature);
1157 if ((columns == 0) || (rows == 0))
1158 return((Image *) NULL);
1159 if ((columns == image->columns) && (rows == image->rows))
1160 return(CloneImage(image,0,0,MagickTrue,exception));
1161 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1162 if (resize_image == (Image *) NULL)
1163 return((Image *) NULL);
1164 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1166 InheritException(exception,&resize_image->exception);
1167 resize_image=DestroyImage(resize_image);
1168 return((Image *) NULL);
1172 image_view=AcquireCacheView(image);
1173 resize_view=AcquireCacheView(resize_image);
1174 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1175 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
1177 for (y=0; y < (ssize_t) resize_image->rows; y++)
1185 register IndexPacket
1186 *restrict resize_indexes;
1188 register PixelPacket
1194 if (status == MagickFalse)
1196 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1198 if (q == (PixelPacket *) NULL)
1200 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1201 offset.y=((MagickRealType) (y+0.5)*image->rows/resize_image->rows);
1202 GetMagickPixelPacket(image,&pixel);
1203 for (x=0; x < (ssize_t) resize_image->columns; x++)
1205 offset.x=((MagickRealType) (x+0.5)*image->columns/resize_image->columns);
1206 (void) InterpolateMagickPixelPacket(image,image_view,
1207 MeshInterpolatePixel,offset.x-0.5,offset.y-0.5,&pixel,exception);
1208 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1211 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1213 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1218 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1219 #pragma omp critical (MagickCore_AdaptiveResizeImage)
1221 proceed=SetImageProgress(image,AdaptiveResizeImageTag,progress++,
1223 if (proceed == MagickFalse)
1227 resize_view=DestroyCacheView(resize_view);
1228 image_view=DestroyCacheView(image_view);
1229 if (status == MagickFalse)
1230 resize_image=DestroyImage(resize_image);
1231 return(resize_image);
1235 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1239 + B e s s e l O r d e r O n e %
1243 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245 % BesselOrderOne() computes the Bessel function of x of the first kind of
1246 % order 0. This is used to create the Jinc() filter function below.
1248 % Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1254 % j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1256 % where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1258 % cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1259 % = 1/sqrt(2) * (sin(x) - cos(x))
1260 % sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1261 % = -1/sqrt(2) * (sin(x) + cos(x))
1263 % The format of the BesselOrderOne method is:
1265 % MagickRealType BesselOrderOne(MagickRealType x)
1267 % A description of each parameter follows:
1269 % o x: MagickRealType value.
1274 static MagickRealType I0(MagickRealType x)
1285 Zeroth order Bessel function of the first kind.
1290 for (i=2; t > MagickEpsilon; i++)
1293 t*=y/((MagickRealType) i*i);
1299 static MagickRealType J1(MagickRealType x)
1311 0.581199354001606143928050809e+21,
1312 -0.6672106568924916298020941484e+20,
1313 0.2316433580634002297931815435e+19,
1314 -0.3588817569910106050743641413e+17,
1315 0.2908795263834775409737601689e+15,
1316 -0.1322983480332126453125473247e+13,
1317 0.3413234182301700539091292655e+10,
1318 -0.4695753530642995859767162166e+7,
1319 0.270112271089232341485679099e+4
1323 0.11623987080032122878585294e+22,
1324 0.1185770712190320999837113348e+20,
1325 0.6092061398917521746105196863e+17,
1326 0.2081661221307607351240184229e+15,
1327 0.5243710262167649715406728642e+12,
1328 0.1013863514358673989967045588e+10,
1329 0.1501793594998585505921097578e+7,
1330 0.1606931573481487801970916749e+4,
1336 for (i=7; i >= 0; i--)
1345 static MagickRealType P1(MagickRealType x)
1357 0.352246649133679798341724373e+5,
1358 0.62758845247161281269005675e+5,
1359 0.313539631109159574238669888e+5,
1360 0.49854832060594338434500455e+4,
1361 0.2111529182853962382105718e+3,
1362 0.12571716929145341558495e+1
1366 0.352246649133679798068390431e+5,
1367 0.626943469593560511888833731e+5,
1368 0.312404063819041039923015703e+5,
1369 0.4930396490181088979386097e+4,
1370 0.2030775189134759322293574e+3,
1376 for (i=4; i >= 0; i--)
1378 p=p*(8.0/x)*(8.0/x)+Pone[i];
1379 q=q*(8.0/x)*(8.0/x)+Qone[i];
1385 static MagickRealType Q1(MagickRealType x)
1397 0.3511751914303552822533318e+3,
1398 0.7210391804904475039280863e+3,
1399 0.4259873011654442389886993e+3,
1400 0.831898957673850827325226e+2,
1401 0.45681716295512267064405e+1,
1402 0.3532840052740123642735e-1
1406 0.74917374171809127714519505e+4,
1407 0.154141773392650970499848051e+5,
1408 0.91522317015169922705904727e+4,
1409 0.18111867005523513506724158e+4,
1410 0.1038187585462133728776636e+3,
1416 for (i=4; i >= 0; i--)
1418 p=p*(8.0/x)*(8.0/x)+Pone[i];
1419 q=q*(8.0/x)*(8.0/x)+Qone[i];
1424 static MagickRealType BesselOrderOne(MagickRealType x)
1437 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1438 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1446 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1450 + D e s t r o y R e s i z e F i l t e r %
1454 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1456 % DestroyResizeFilter() destroy the resize filter.
1458 % The format of the DestroyResizeFilter method is:
1460 % ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1462 % A description of each parameter follows:
1464 % o resize_filter: the resize filter.
1467 MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1469 assert(resize_filter != (ResizeFilter *) NULL);
1470 assert(resize_filter->signature == MagickSignature);
1471 resize_filter->signature=(~MagickSignature);
1472 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1473 return(resize_filter);
1477 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1481 + G e t R e s i z e F i l t e r S u p p o r t %
1485 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1487 % GetResizeFilterSupport() return the current support window size for this
1488 % filter. Note that this may have been enlarged by filter:blur factor.
1490 % The format of the GetResizeFilterSupport method is:
1492 % MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1494 % A description of each parameter follows:
1496 % o filter: Image filter to use.
1499 MagickExport MagickRealType GetResizeFilterSupport(
1500 const ResizeFilter *resize_filter)
1502 assert(resize_filter != (ResizeFilter *) NULL);
1503 assert(resize_filter->signature == MagickSignature);
1504 return(resize_filter->support*resize_filter->blur);
1508 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1512 + G e t R e s i z e F i l t e r W e i g h t %
1516 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1518 % GetResizeFilterWeight evaluates the specified resize filter at the point x
1519 % which usally lies between zero and the filters current 'support' and
1520 % returns the weight of the filter function at that point.
1522 % The format of the GetResizeFilterWeight method is:
1524 % MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1525 % const MagickRealType x)
1527 % A description of each parameter follows:
1529 % o filter: the filter type.
1534 MagickExport MagickRealType GetResizeFilterWeight(
1535 const ResizeFilter *resize_filter,const MagickRealType x)
1543 Windowing function - scale the weighting filter by this amount.
1545 assert(resize_filter != (ResizeFilter *) NULL);
1546 assert(resize_filter->signature == MagickSignature);
1547 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
1548 if ((resize_filter->window_support < MagickEpsilon) ||
1549 (resize_filter->window == Box))
1550 scale=1.0; /* Point or Box Filter -- avoid division by zero */
1553 scale=resize_filter->scale;
1554 scale=resize_filter->window(x_blur*scale,resize_filter);
1556 weight=scale*resize_filter->filter(x_blur,resize_filter);
1561 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1565 % M a g n i f y I m a g e %
1569 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1571 % MagnifyImage() is a convenience method that scales an image proportionally
1572 % to twice its size.
1574 % The format of the MagnifyImage method is:
1576 % Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1578 % A description of each parameter follows:
1580 % o image: the image.
1582 % o exception: return any errors or warnings in this structure.
1585 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1590 assert(image != (Image *) NULL);
1591 assert(image->signature == MagickSignature);
1592 if (image->debug != MagickFalse)
1593 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1594 assert(exception != (ExceptionInfo *) NULL);
1595 assert(exception->signature == MagickSignature);
1596 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1598 return(magnify_image);
1602 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1606 % M i n i f y I m a g e %
1610 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1612 % MinifyImage() is a convenience method that scales an image proportionally
1615 % The format of the MinifyImage method is:
1617 % Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1619 % A description of each parameter follows:
1621 % o image: the image.
1623 % o exception: return any errors or warnings in this structure.
1626 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1631 assert(image != (Image *) NULL);
1632 assert(image->signature == MagickSignature);
1633 if (image->debug != MagickFalse)
1634 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1635 assert(exception != (ExceptionInfo *) NULL);
1636 assert(exception->signature == MagickSignature);
1637 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,1.0,
1639 return(minify_image);
1643 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1647 % R e s a m p l e I m a g e %
1651 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1653 % ResampleImage() resize image in terms of its pixel size, so that when
1654 % displayed at the given resolution it will be the same size in terms of
1655 % real world units as the original image at the original resolution.
1657 % The format of the ResampleImage method is:
1659 % Image *ResampleImage(Image *image,const double x_resolution,
1660 % const double y_resolution,const FilterTypes filter,const double blur,
1661 % ExceptionInfo *exception)
1663 % A description of each parameter follows:
1665 % o image: the image to be resized to fit the given resolution.
1667 % o x_resolution: the new image x resolution.
1669 % o y_resolution: the new image y resolution.
1671 % o filter: Image filter to use.
1673 % o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1676 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1677 const double y_resolution,const FilterTypes filter,const double blur,
1678 ExceptionInfo *exception)
1680 #define ResampleImageTag "Resample/Image"
1690 Initialize sampled image attributes.
1692 assert(image != (const Image *) NULL);
1693 assert(image->signature == MagickSignature);
1694 if (image->debug != MagickFalse)
1695 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1696 assert(exception != (ExceptionInfo *) NULL);
1697 assert(exception->signature == MagickSignature);
1698 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1699 72.0 : image->x_resolution)+0.5);
1700 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1701 72.0 : image->y_resolution)+0.5);
1702 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1703 if (resample_image != (Image *) NULL)
1705 resample_image->x_resolution=x_resolution;
1706 resample_image->y_resolution=y_resolution;
1708 return(resample_image);
1710 #if defined(MAGICKCORE_LQR_DELEGATE)
1713 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1717 % L i q u i d R e s c a l e I m a g e %
1721 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1723 % LiquidRescaleImage() rescales image with seam carving.
1725 % The format of the LiquidRescaleImage method is:
1727 % Image *LiquidRescaleImage(const Image *image,
1728 % const size_t columns,const size_t rows,
1729 % const double delta_x,const double rigidity,ExceptionInfo *exception)
1731 % A description of each parameter follows:
1733 % o image: the image.
1735 % o columns: the number of columns in the rescaled image.
1737 % o rows: the number of rows in the rescaled image.
1739 % o delta_x: maximum seam transversal step (0 means straight seams).
1741 % o rigidity: introduce a bias for non-straight seams (typically 0).
1743 % o exception: return any errors or warnings in this structure.
1746 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1747 const size_t rows,const double delta_x,const double rigidity,
1748 ExceptionInfo *exception)
1750 #define LiquidRescaleImageTag "Rescale/Image"
1784 Liquid rescale image.
1786 assert(image != (const Image *) NULL);
1787 assert(image->signature == MagickSignature);
1788 if (image->debug != MagickFalse)
1789 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1790 assert(exception != (ExceptionInfo *) NULL);
1791 assert(exception->signature == MagickSignature);
1792 if ((columns == 0) || (rows == 0))
1793 return((Image *) NULL);
1794 if ((columns == image->columns) && (rows == image->rows))
1795 return(CloneImage(image,0,0,MagickTrue,exception));
1796 if ((columns <= 2) || (rows <= 2))
1797 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
1798 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1808 Honor liquid resize size limitations.
1810 for (width=image->columns; columns >= (2*width-1); width*=2);
1811 for (height=image->rows; rows >= (2*height-1); height*=2);
1812 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1814 if (resize_image == (Image *) NULL)
1815 return((Image *) NULL);
1816 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1817 rigidity,exception);
1818 resize_image=DestroyImage(resize_image);
1819 return(rescale_image);
1822 if (image->matte == MagickFalse)
1824 if (image->colorspace == CMYKColorspace)
1827 if (image->matte == MagickFalse)
1830 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1831 strlen(map)*sizeof(*pixels));
1832 if (pixels == (unsigned char *) NULL)
1833 return((Image *) NULL);
1834 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1836 if (status == MagickFalse)
1838 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1839 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1841 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1842 if (carver == (LqrCarver *) NULL)
1844 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1845 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1847 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1848 lqr_status=lqr_carver_resize(carver,columns,rows);
1850 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1851 lqr_carver_get_height(carver),MagickTrue,exception);
1852 if (rescale_image == (Image *) NULL)
1854 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1855 return((Image *) NULL);
1857 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1859 InheritException(exception,&rescale_image->exception);
1860 rescale_image=DestroyImage(rescale_image);
1861 return((Image *) NULL);
1863 GetMagickPixelPacket(rescale_image,&pixel);
1864 (void) lqr_carver_scan_reset(carver);
1865 rescale_view=AcquireCacheView(rescale_image);
1866 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1868 register IndexPacket
1869 *restrict rescale_indexes;
1871 register PixelPacket
1874 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
1875 if (q == (PixelPacket *) NULL)
1877 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
1878 pixel.red=QuantumRange*(packet[0]/255.0);
1879 pixel.green=QuantumRange*(packet[1]/255.0);
1880 pixel.blue=QuantumRange*(packet[2]/255.0);
1881 if (image->colorspace != CMYKColorspace)
1883 if (image->matte == MagickFalse)
1884 pixel.opacity=QuantumRange*(packet[3]/255.0);
1888 pixel.index=QuantumRange*(packet[3]/255.0);
1889 if (image->matte == MagickFalse)
1890 pixel.opacity=QuantumRange*(packet[4]/255.0);
1892 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
1893 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
1896 rescale_view=DestroyCacheView(rescale_view);
1898 Relinquish resources.
1900 lqr_carver_destroy(carver);
1901 return(rescale_image);
1904 MagickExport Image *LiquidRescaleImage(const Image *image,
1905 const size_t magick_unused(columns),const size_t magick_unused(rows),
1906 const double magick_unused(delta_x),const double magick_unused(rigidity),
1907 ExceptionInfo *exception)
1909 assert(image != (const Image *) NULL);
1910 assert(image->signature == MagickSignature);
1911 if (image->debug != MagickFalse)
1912 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1913 assert(exception != (ExceptionInfo *) NULL);
1914 assert(exception->signature == MagickSignature);
1915 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1916 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1917 return((Image *) NULL);
1922 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1926 % R e s i z e I m a g e %
1930 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1932 % ResizeImage() scales an image to the desired dimensions, using the given
1933 % filter (see AcquireFilterInfo()).
1935 % If an undefined filter is given the filter defaults to Mitchell for a
1936 % colormapped image, a image with a matte channel, or if the image is
1937 % enlarged. Otherwise the filter defaults to a Lanczos.
1939 % ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1941 % The format of the ResizeImage method is:
1943 % Image *ResizeImage(Image *image,const size_t columns,
1944 % const size_t rows,const FilterTypes filter,const double blur,
1945 % ExceptionInfo *exception)
1947 % A description of each parameter follows:
1949 % o image: the image.
1951 % o columns: the number of columns in the scaled image.
1953 % o rows: the number of rows in the scaled image.
1955 % o filter: Image filter to use.
1957 % o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1960 % o exception: return any errors or warnings in this structure.
1964 typedef struct _ContributionInfo
1973 static ContributionInfo **DestroyContributionThreadSet(
1974 ContributionInfo **contribution)
1979 assert(contribution != (ContributionInfo **) NULL);
1980 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
1981 if (contribution[i] != (ContributionInfo *) NULL)
1982 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1984 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
1985 return(contribution);
1988 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1999 number_threads=GetOpenMPMaximumThreads();
2000 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
2001 sizeof(*contribution));
2002 if (contribution == (ContributionInfo **) NULL)
2003 return((ContributionInfo **) NULL);
2004 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
2005 for (i=0; i < (ssize_t) number_threads; i++)
2007 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2008 sizeof(**contribution));
2009 if (contribution[i] == (ContributionInfo *) NULL)
2010 return(DestroyContributionThreadSet(contribution));
2012 return(contribution);
2015 static inline double MagickMax(const double x,const double y)
2022 static inline double MagickMin(const double x,const double y)
2029 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2030 const Image *image,Image *resize_image,const MagickRealType x_factor,
2031 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2033 #define ResizeImageTag "Resize/Image"
2043 **restrict contributions;
2059 Apply filter to resize horizontally from image to resize image.
2061 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
2062 support=scale*GetResizeFilterSupport(resize_filter);
2063 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2064 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2066 InheritException(exception,&resize_image->exception);
2067 return(MagickFalse);
2072 Support too small even for nearest neighbour: Reduce to point
2075 support=(MagickRealType) 0.5;
2078 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2079 if (contributions == (ContributionInfo **) NULL)
2081 (void) ThrowMagickException(exception,GetMagickModule(),
2082 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2083 return(MagickFalse);
2087 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2088 image_view=AcquireCacheView(image);
2089 resize_view=AcquireCacheView(resize_image);
2090 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2091 #pragma omp parallel for shared(status)
2093 for (x=0; x < (ssize_t) resize_image->columns; x++)
2099 register const IndexPacket
2102 register const PixelPacket
2105 register ContributionInfo
2106 *restrict contribution;
2108 register IndexPacket
2109 *restrict resize_indexes;
2111 register PixelPacket
2122 if (status == MagickFalse)
2124 center=(MagickRealType) (x+0.5)/x_factor;
2125 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2126 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
2128 contribution=contributions[GetOpenMPThreadId()];
2129 for (n=0; n < (stop-start); n++)
2131 contribution[n].pixel=start+n;
2132 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2133 ((MagickRealType) (start+n)-center+0.5));
2134 density+=contribution[n].weight;
2136 if ((density != 0.0) && (density != 1.0))
2144 density=1.0/density;
2145 for (i=0; i < n; i++)
2146 contribution[i].weight*=density;
2148 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2149 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
2150 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2152 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2157 indexes=GetCacheViewVirtualIndexQueue(image_view);
2158 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2159 for (y=0; y < (ssize_t) resize_image->rows; y++)
2174 if (image->matte == MagickFalse)
2176 for (i=0; i < n; i++)
2178 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2179 (contribution[i].pixel-contribution[0].pixel);
2180 alpha=contribution[i].weight;
2181 pixel.red+=alpha*GetRedPixelComponent(p+j);
2182 pixel.green+=alpha*GetGreenPixelComponent(p+j);
2183 pixel.blue+=alpha*GetBluePixelComponent(p+j);
2184 pixel.opacity+=alpha*GetOpacityPixelComponent(p+j);
2186 SetRedPixelComponent(q,ClampToQuantum(pixel.red));
2187 SetGreenPixelComponent(q,ClampToQuantum(pixel.green));
2188 SetBluePixelComponent(q,ClampToQuantum(pixel.blue));
2189 SetOpacityPixelComponent(q,ClampToQuantum(pixel.opacity));
2190 if ((image->colorspace == CMYKColorspace) &&
2191 (resize_image->colorspace == CMYKColorspace))
2193 for (i=0; i < n; i++)
2195 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2196 (contribution[i].pixel-contribution[0].pixel);
2197 alpha=contribution[i].weight;
2198 pixel.index+=alpha*GetIndexPixelComponent(indexes+j);
2200 SetIndexPixelComponent(resize_indexes+y,ClampToQuantum(
2210 for (i=0; i < n; i++)
2212 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2213 (contribution[i].pixel-contribution[0].pixel);
2214 alpha=contribution[i].weight*QuantumScale*
2215 GetAlphaPixelComponent(p+j);
2216 pixel.red+=alpha*GetRedPixelComponent(p+j);
2217 pixel.green+=alpha*GetGreenPixelComponent(p+j);
2218 pixel.blue+=alpha*GetBluePixelComponent(p+j);
2219 pixel.opacity+=contribution[i].weight*GetOpacityPixelComponent(p+j);
2222 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2223 SetRedPixelComponent(q,ClampToQuantum(gamma*pixel.red));
2224 SetGreenPixelComponent(q,ClampToQuantum(gamma*pixel.green));
2225 SetBluePixelComponent(q,ClampToQuantum(gamma*pixel.blue));
2226 SetOpacityPixelComponent(q,ClampToQuantum(pixel.opacity));
2227 if ((image->colorspace == CMYKColorspace) &&
2228 (resize_image->colorspace == CMYKColorspace))
2230 for (i=0; i < n; i++)
2232 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2233 (contribution[i].pixel-contribution[0].pixel);
2234 alpha=contribution[i].weight*QuantumScale*
2235 GetAlphaPixelComponent(p+j);
2236 pixel.index+=alpha*GetIndexPixelComponent(indexes+j);
2238 SetIndexPixelComponent(resize_indexes+y,ClampToQuantum(gamma*
2242 if ((resize_image->storage_class == PseudoClass) &&
2243 (image->storage_class == PseudoClass))
2245 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
2247 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2248 (contribution[i-start].pixel-contribution[0].pixel);
2249 SetIndexPixelComponent(resize_indexes+y,GetIndexPixelComponent(
2254 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2256 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2261 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2262 #pragma omp critical (MagickCore_HorizontalFilter)
2264 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2265 if (proceed == MagickFalse)
2269 resize_view=DestroyCacheView(resize_view);
2270 image_view=DestroyCacheView(image_view);
2271 contributions=DestroyContributionThreadSet(contributions);
2275 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2276 const Image *image,Image *resize_image,const MagickRealType y_factor,
2277 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2287 **restrict contributions;
2303 Apply filter to resize vertically from image to resize image.
2305 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
2306 support=scale*GetResizeFilterSupport(resize_filter);
2307 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2308 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2310 InheritException(exception,&resize_image->exception);
2311 return(MagickFalse);
2316 Support too small even for nearest neighbour: Reduce to point
2319 support=(MagickRealType) 0.5;
2322 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2323 if (contributions == (ContributionInfo **) NULL)
2325 (void) ThrowMagickException(exception,GetMagickModule(),
2326 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2327 return(MagickFalse);
2331 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2332 image_view=AcquireCacheView(image);
2333 resize_view=AcquireCacheView(resize_image);
2334 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2335 #pragma omp parallel for shared(status)
2337 for (y=0; y < (ssize_t) resize_image->rows; y++)
2343 register const IndexPacket
2346 register const PixelPacket
2349 register ContributionInfo
2350 *restrict contribution;
2352 register IndexPacket
2353 *restrict resize_indexes;
2355 register PixelPacket
2366 if (status == MagickFalse)
2368 center=(MagickRealType) (y+0.5)/y_factor;
2369 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2370 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
2372 contribution=contributions[GetOpenMPThreadId()];
2373 for (n=0; n < (stop-start); n++)
2375 contribution[n].pixel=start+n;
2376 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2377 ((MagickRealType) (start+n)-center+0.5));
2378 density+=contribution[n].weight;
2380 if ((density != 0.0) && (density != 1.0))
2388 density=1.0/density;
2389 for (i=0; i < n; i++)
2390 contribution[i].weight*=density;
2392 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
2393 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2395 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2397 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2402 indexes=GetCacheViewVirtualIndexQueue(image_view);
2403 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2404 for (x=0; x < (ssize_t) resize_image->columns; x++)
2419 if (image->matte == MagickFalse)
2421 for (i=0; i < n; i++)
2423 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2425 alpha=contribution[i].weight;
2426 pixel.red+=alpha*GetRedPixelComponent(p+j);
2427 pixel.green+=alpha*GetGreenPixelComponent(p+j);
2428 pixel.blue+=alpha*GetBluePixelComponent(p+j);
2429 pixel.opacity+=alpha*GetOpacityPixelComponent(p+j);
2431 SetRedPixelComponent(q,ClampToQuantum(pixel.red));
2432 SetGreenPixelComponent(q,ClampToQuantum(pixel.green));
2433 SetBluePixelComponent(q,ClampToQuantum(pixel.blue));
2434 SetOpacityPixelComponent(q,ClampToQuantum(pixel.opacity));
2435 if ((image->colorspace == CMYKColorspace) &&
2436 (resize_image->colorspace == CMYKColorspace))
2438 for (i=0; i < n; i++)
2440 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2442 alpha=contribution[i].weight;
2443 pixel.index+=alpha*GetIndexPixelComponent(indexes+j);
2445 SetIndexPixelComponent(resize_indexes+x,ClampToQuantum(
2455 for (i=0; i < n; i++)
2457 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2459 alpha=contribution[i].weight*QuantumScale*
2460 GetAlphaPixelComponent(p+j);
2461 pixel.red+=alpha*GetRedPixelComponent(p+j);
2462 pixel.green+=alpha*GetGreenPixelComponent(p+j);
2463 pixel.blue+=alpha*GetBluePixelComponent(p+j);
2464 pixel.opacity+=contribution[i].weight*GetOpacityPixelComponent(p+j);
2467 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2468 SetRedPixelComponent(q,ClampToQuantum(gamma*pixel.red));
2469 SetGreenPixelComponent(q,ClampToQuantum(gamma*pixel.green));
2470 SetBluePixelComponent(q,ClampToQuantum(gamma*pixel.blue));
2471 SetOpacityPixelComponent(q,ClampToQuantum(pixel.opacity));
2472 if ((image->colorspace == CMYKColorspace) &&
2473 (resize_image->colorspace == CMYKColorspace))
2475 for (i=0; i < n; i++)
2477 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2479 alpha=contribution[i].weight*QuantumScale*
2480 GetAlphaPixelComponent(p+j);
2481 pixel.index+=alpha*GetIndexPixelComponent(indexes+j);
2483 SetIndexPixelComponent(resize_indexes+x,ClampToQuantum(gamma*
2487 if ((resize_image->storage_class == PseudoClass) &&
2488 (image->storage_class == PseudoClass))
2490 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
2492 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
2494 SetIndexPixelComponent(resize_indexes+x,
2495 GetIndexPixelComponent(indexes+j));
2499 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2501 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2506 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2507 #pragma omp critical (MagickCore_VerticalFilter)
2509 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2510 if (proceed == MagickFalse)
2514 resize_view=DestroyCacheView(resize_view);
2515 image_view=DestroyCacheView(image_view);
2516 contributions=DestroyContributionThreadSet(contributions);
2520 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2521 const size_t rows,const FilterTypes filter,const double blur,
2522 ExceptionInfo *exception)
2524 #define WorkLoadFactor 0.265
2550 Acquire resize image.
2552 assert(image != (Image *) NULL);
2553 assert(image->signature == MagickSignature);
2554 if (image->debug != MagickFalse)
2555 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2556 assert(exception != (ExceptionInfo *) NULL);
2557 assert(exception->signature == MagickSignature);
2558 if ((columns == 0) || (rows == 0))
2559 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2560 if ((columns == image->columns) && (rows == image->rows) &&
2561 (filter == UndefinedFilter) && (blur == 1.0))
2562 return(CloneImage(image,0,0,MagickTrue,exception));
2563 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2564 if (resize_image == (Image *) NULL)
2565 return(resize_image);
2567 Acquire resize filter.
2569 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2570 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2571 if ((x_factor*y_factor) > WorkLoadFactor)
2572 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2574 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2575 if (filter_image == (Image *) NULL)
2576 return(DestroyImage(resize_image));
2577 filter_type=LanczosFilter;
2578 if (filter != UndefinedFilter)
2581 if ((x_factor == 1.0) && (y_factor == 1.0))
2582 filter_type=PointFilter;
2584 if ((image->storage_class == PseudoClass) ||
2585 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2586 filter_type=MitchellFilter;
2587 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2593 if ((x_factor*y_factor) > WorkLoadFactor)
2595 span=(MagickSizeType) (filter_image->columns+rows);
2596 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
2598 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
2599 span,&offset,exception);
2603 span=(MagickSizeType) (filter_image->rows+columns);
2604 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
2606 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
2607 span,&offset,exception);
2612 filter_image=DestroyImage(filter_image);
2613 resize_filter=DestroyResizeFilter(resize_filter);
2614 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2615 return((Image *) NULL);
2616 resize_image->type=image->type;
2617 return(resize_image);
2621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2625 % S a m p l e I m a g e %
2629 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2631 % SampleImage() scales an image to the desired dimensions with pixel
2632 % sampling. Unlike other scaling methods, this method does not introduce
2633 % any additional color into the scaled image.
2635 % The format of the SampleImage method is:
2637 % Image *SampleImage(const Image *image,const size_t columns,
2638 % const size_t rows,ExceptionInfo *exception)
2640 % A description of each parameter follows:
2642 % o image: the image.
2644 % o columns: the number of columns in the sampled image.
2646 % o rows: the number of rows in the sampled image.
2648 % o exception: return any errors or warnings in this structure.
2651 MagickExport Image *SampleImage(const Image *image,const size_t columns,
2652 const size_t rows,ExceptionInfo *exception)
2654 #define SampleImageTag "Sample/Image"
2677 Initialize sampled image attributes.
2679 assert(image != (const Image *) NULL);
2680 assert(image->signature == MagickSignature);
2681 if (image->debug != MagickFalse)
2682 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2683 assert(exception != (ExceptionInfo *) NULL);
2684 assert(exception->signature == MagickSignature);
2685 if ((columns == 0) || (rows == 0))
2686 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2687 if ((columns == image->columns) && (rows == image->rows))
2688 return(CloneImage(image,0,0,MagickTrue,exception));
2689 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2690 if (sample_image == (Image *) NULL)
2691 return((Image *) NULL);
2693 Allocate scan line buffer and column offset buffers.
2695 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
2697 if (x_offset == (ssize_t *) NULL)
2699 sample_image=DestroyImage(sample_image);
2700 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2702 for (x=0; x < (ssize_t) sample_image->columns; x++)
2703 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
2704 sample_image->columns);
2710 image_view=AcquireCacheView(image);
2711 sample_view=AcquireCacheView(sample_image);
2712 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2713 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2715 for (y=0; y < (ssize_t) sample_image->rows; y++)
2717 register const IndexPacket
2720 register const PixelPacket
2723 register IndexPacket
2724 *restrict sample_indexes;
2726 register PixelPacket
2735 if (status == MagickFalse)
2737 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2738 sample_image->rows);
2739 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2741 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2743 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2748 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2749 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2753 for (x=0; x < (ssize_t) sample_image->columns; x++)
2754 *q++=p[x_offset[x]];
2755 if ((image->storage_class == PseudoClass) ||
2756 (image->colorspace == CMYKColorspace))
2757 for (x=0; x < (ssize_t) sample_image->columns; x++)
2758 SetIndexPixelComponent(sample_indexes+x,
2759 GetIndexPixelComponent(indexes+x_offset[x]));
2760 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2762 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2767 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2768 #pragma omp critical (MagickCore_SampleImage)
2770 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2771 if (proceed == MagickFalse)
2775 image_view=DestroyCacheView(image_view);
2776 sample_view=DestroyCacheView(sample_view);
2777 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
2778 sample_image->type=image->type;
2779 return(sample_image);
2783 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2787 % S c a l e I m a g e %
2791 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2793 % ScaleImage() changes the size of an image to the given dimensions.
2795 % The format of the ScaleImage method is:
2797 % Image *ScaleImage(const Image *image,const size_t columns,
2798 % const size_t rows,ExceptionInfo *exception)
2800 % A description of each parameter follows:
2802 % o image: the image.
2804 % o columns: the number of columns in the scaled image.
2806 % o rows: the number of rows in the scaled image.
2808 % o exception: return any errors or warnings in this structure.
2811 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2812 const size_t rows,ExceptionInfo *exception)
2814 #define ScaleImageTag "Scale/Image"
2851 Initialize scaled image attributes.
2853 assert(image != (const Image *) NULL);
2854 assert(image->signature == MagickSignature);
2855 if (image->debug != MagickFalse)
2856 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2857 assert(exception != (ExceptionInfo *) NULL);
2858 assert(exception->signature == MagickSignature);
2859 if ((columns == 0) || (rows == 0))
2860 return((Image *) NULL);
2861 if ((columns == image->columns) && (rows == image->rows))
2862 return(CloneImage(image,0,0,MagickTrue,exception));
2863 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2864 if (scale_image == (Image *) NULL)
2865 return((Image *) NULL);
2866 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2868 InheritException(exception,&scale_image->exception);
2869 scale_image=DestroyImage(scale_image);
2870 return((Image *) NULL);
2875 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2878 if (image->rows != scale_image->rows)
2879 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2881 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2882 scale_image->columns,sizeof(*scale_scanline));
2883 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2885 if ((scanline == (MagickPixelPacket *) NULL) ||
2886 (scale_scanline == (MagickPixelPacket *) NULL) ||
2887 (x_vector == (MagickPixelPacket *) NULL) ||
2888 (y_vector == (MagickPixelPacket *) NULL))
2890 scale_image=DestroyImage(scale_image);
2891 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2897 next_row=MagickTrue;
2899 scale.y=(double) scale_image->rows/(double) image->rows;
2900 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2902 GetMagickPixelPacket(image,&pixel);
2903 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2905 image_view=AcquireCacheView(image);
2906 scale_view=AcquireCacheView(scale_image);
2907 for (y=0; y < (ssize_t) scale_image->rows; y++)
2909 register const IndexPacket
2912 register const PixelPacket
2915 register IndexPacket
2916 *restrict scale_indexes;
2918 register MagickPixelPacket
2922 register PixelPacket
2928 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2930 if (q == (PixelPacket *) NULL)
2933 scale_indexes=GetCacheViewAuthenticIndexQueue(scale_view);
2934 if (scale_image->rows == image->rows)
2937 Read a new scanline.
2939 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2941 if (p == (const PixelPacket *) NULL)
2943 indexes=GetCacheViewVirtualIndexQueue(image_view);
2944 for (x=0; x < (ssize_t) image->columns; x++)
2946 if (image->matte != MagickFalse)
2947 alpha=QuantumScale*GetAlphaPixelComponent(p);
2948 x_vector[x].red=(MagickRealType) (alpha*GetRedPixelComponent(p));
2949 x_vector[x].green=(MagickRealType) (alpha*GetGreenPixelComponent(p));
2950 x_vector[x].blue=(MagickRealType) (alpha*GetBluePixelComponent(p));
2951 if (image->matte != MagickFalse)
2952 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
2953 if (indexes != (IndexPacket *) NULL)
2954 x_vector[x].index=(MagickRealType) (alpha*
2955 GetIndexPixelComponent(indexes+x));
2964 while (scale.y < span.y)
2966 if ((next_row != MagickFalse) &&
2967 (number_rows < (ssize_t) image->rows))
2970 Read a new scanline.
2972 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2974 if (p == (const PixelPacket *) NULL)
2976 indexes=GetCacheViewVirtualIndexQueue(image_view);
2977 for (x=0; x < (ssize_t) image->columns; x++)
2979 if (image->matte != MagickFalse)
2980 alpha=QuantumScale*GetAlphaPixelComponent(p);
2981 x_vector[x].red=(MagickRealType) (alpha*
2982 GetRedPixelComponent(p));
2983 x_vector[x].green=(MagickRealType) (alpha*
2984 GetGreenPixelComponent(p));
2985 x_vector[x].blue=(MagickRealType) (alpha*
2986 GetBluePixelComponent(p));
2987 if (image->matte != MagickFalse)
2988 x_vector[x].opacity=(MagickRealType)
2989 GetOpacityPixelComponent(p);
2990 if (indexes != (IndexPacket *) NULL)
2991 x_vector[x].index=(MagickRealType) (alpha*
2992 GetIndexPixelComponent(indexes+x));
2997 for (x=0; x < (ssize_t) image->columns; x++)
2999 y_vector[x].red+=scale.y*x_vector[x].red;
3000 y_vector[x].green+=scale.y*x_vector[x].green;
3001 y_vector[x].blue+=scale.y*x_vector[x].blue;
3002 if (scale_image->matte != MagickFalse)
3003 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
3004 if (scale_indexes != (IndexPacket *) NULL)
3005 y_vector[x].index+=scale.y*x_vector[x].index;
3008 scale.y=(double) scale_image->rows/(double) image->rows;
3009 next_row=MagickTrue;
3011 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
3014 Read a new scanline.
3016 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
3018 if (p == (const PixelPacket *) NULL)
3020 indexes=GetCacheViewVirtualIndexQueue(image_view);
3021 for (x=0; x < (ssize_t) image->columns; x++)
3023 if (image->matte != MagickFalse)
3024 alpha=QuantumScale*GetAlphaPixelComponent(p);
3025 x_vector[x].red=(MagickRealType) (alpha*
3026 GetRedPixelComponent(p));
3027 x_vector[x].green=(MagickRealType) (alpha*
3028 GetGreenPixelComponent(p));
3029 x_vector[x].blue=(MagickRealType) (alpha*
3030 GetBluePixelComponent(p));
3031 if (image->matte != MagickFalse)
3032 x_vector[x].opacity=(MagickRealType)
3033 GetOpacityPixelComponent(p);
3034 if (indexes != (IndexPacket *) NULL)
3035 x_vector[x].index=(MagickRealType) (alpha*
3036 GetIndexPixelComponent(indexes+x));
3040 next_row=MagickFalse;
3043 for (x=0; x < (ssize_t) image->columns; x++)
3045 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3046 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3047 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3048 if (image->matte != MagickFalse)
3049 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3050 if (scale_indexes != (IndexPacket *) NULL)
3051 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3053 s->green=pixel.green;
3055 if (scale_image->matte != MagickFalse)
3056 s->opacity=pixel.opacity;
3057 if (scale_indexes != (IndexPacket *) NULL)
3058 s->index=pixel.index;
3065 scale.y=(double) scale_image->rows/(double) image->rows;
3066 next_row=MagickTrue;
3070 if (scale_image->columns == image->columns)
3073 Transfer scanline to scaled image.
3076 for (x=0; x < (ssize_t) scale_image->columns; x++)
3078 if (scale_image->matte != MagickFalse)
3079 alpha=QuantumScale*(QuantumRange-s->opacity);
3080 alpha=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
3081 SetRedPixelComponent(q,ClampToQuantum(alpha*s->red));
3082 SetGreenPixelComponent(q,ClampToQuantum(alpha*s->green));
3083 SetBluePixelComponent(q,ClampToQuantum(alpha*s->blue));
3084 if (scale_image->matte != MagickFalse)
3085 SetOpacityPixelComponent(q,ClampToQuantum(s->opacity));
3086 if (scale_indexes != (IndexPacket *) NULL)
3087 SetIndexPixelComponent(scale_indexes+x,ClampToQuantum(alpha*
3099 next_column=MagickFalse;
3103 for (x=0; x < (ssize_t) image->columns; x++)
3105 scale.x=(double) scale_image->columns/(double) image->columns;
3106 while (scale.x >= span.x)
3108 if (next_column != MagickFalse)
3113 pixel.red+=span.x*s->red;
3114 pixel.green+=span.x*s->green;
3115 pixel.blue+=span.x*s->blue;
3116 if (image->matte != MagickFalse)
3117 pixel.opacity+=span.x*s->opacity;
3118 if (scale_indexes != (IndexPacket *) NULL)
3119 pixel.index+=span.x*s->index;
3121 t->green=pixel.green;
3123 if (scale_image->matte != MagickFalse)
3124 t->opacity=pixel.opacity;
3125 if (scale_indexes != (IndexPacket *) NULL)
3126 t->index=pixel.index;
3129 next_column=MagickTrue;
3133 if (next_column != MagickFalse)
3136 next_column=MagickFalse;
3139 pixel.red+=scale.x*s->red;
3140 pixel.green+=scale.x*s->green;
3141 pixel.blue+=scale.x*s->blue;
3142 if (scale_image->matte != MagickFalse)
3143 pixel.opacity+=scale.x*s->opacity;
3144 if (scale_indexes != (IndexPacket *) NULL)
3145 pixel.index+=scale.x*s->index;
3153 pixel.red+=span.x*s->red;
3154 pixel.green+=span.x*s->green;
3155 pixel.blue+=span.x*s->blue;
3156 if (scale_image->matte != MagickFalse)
3157 pixel.opacity+=span.x*s->opacity;
3158 if (scale_indexes != (IndexPacket *) NULL)
3159 pixel.index+=span.x*s->index;
3161 if ((next_column == MagickFalse) &&
3162 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
3165 t->green=pixel.green;
3167 if (scale_image->matte != MagickFalse)
3168 t->opacity=pixel.opacity;
3169 if (scale_indexes != (IndexPacket *) NULL)
3170 t->index=pixel.index;
3173 Transfer scanline to scaled image.
3176 for (x=0; x < (ssize_t) scale_image->columns; x++)
3178 if (scale_image->matte != MagickFalse)
3179 alpha=QuantumScale*(QuantumRange-s->opacity);
3180 alpha=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
3181 SetRedPixelComponent(q,ClampToQuantum(alpha*t->red));
3182 SetGreenPixelComponent(q,ClampToQuantum(alpha*t->green));
3183 SetBluePixelComponent(q,ClampToQuantum(alpha*t->blue));
3184 if (scale_image->matte != MagickFalse)
3185 SetOpacityPixelComponent(q,ClampToQuantum(t->opacity));
3186 if (scale_indexes != (IndexPacket *) NULL)
3187 SetIndexPixelComponent(scale_indexes+x,ClampToQuantum(alpha*
3193 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
3195 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3197 if (proceed == MagickFalse)
3200 scale_view=DestroyCacheView(scale_view);
3201 image_view=DestroyCacheView(image_view);
3203 Free allocated memory.
3205 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3206 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3207 if (scale_image->rows != image->rows)
3208 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3209 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3210 scale_image->type=image->type;
3211 return(scale_image);
3215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3219 % T h u m b n a i l I m a g e %
3223 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3225 % ThumbnailImage() changes the size of an image to the given dimensions and
3226 % removes any associated profiles. The goal is to produce small low cost
3227 % thumbnail images suited for display on the Web.
3229 % The format of the ThumbnailImage method is:
3231 % Image *ThumbnailImage(const Image *image,const size_t columns,
3232 % const size_t rows,ExceptionInfo *exception)
3234 % A description of each parameter follows:
3236 % o image: the image.
3238 % o columns: the number of columns in the scaled image.
3240 % o rows: the number of rows in the scaled image.
3242 % o exception: return any errors or warnings in this structure.
3245 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3246 const size_t rows,ExceptionInfo *exception)
3248 #define SampleFactor 5
3251 value[MaxTextExtent];
3269 assert(image != (Image *) NULL);
3270 assert(image->signature == MagickSignature);
3271 if (image->debug != MagickFalse)
3272 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3273 assert(exception != (ExceptionInfo *) NULL);
3274 assert(exception->signature == MagickSignature);
3275 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3276 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3277 if ((x_factor*y_factor) > 0.1)
3278 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3281 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
3282 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3283 image->blur,exception);
3289 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3291 if (sample_image == (Image *) NULL)
3292 return((Image *) NULL);
3293 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3294 image->blur,exception);
3295 sample_image=DestroyImage(sample_image);
3297 if (thumbnail_image == (Image *) NULL)
3298 return(thumbnail_image);
3299 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3300 if (thumbnail_image->matte == MagickFalse)
3301 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3302 thumbnail_image->depth=8;
3303 thumbnail_image->interlace=NoInterlace;
3305 Strip all profiles except color profiles.
3307 ResetImageProfileIterator(thumbnail_image);
3308 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3310 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3312 (void) DeleteImageProfile(thumbnail_image,name);
3313 ResetImageProfileIterator(thumbnail_image);
3315 name=GetNextImageProfile(thumbnail_image);
3317 (void) DeleteImageProperty(thumbnail_image,"comment");
3318 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3319 if (strstr(image->magick_filename,"//") == (char *) NULL)
3320 (void) FormatLocaleString(value,MaxTextExtent,"file://%s",
3321 image->magick_filename);
3322 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3323 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3324 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3326 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3327 attributes.st_mtime);
3328 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3330 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3331 attributes.st_mtime);
3332 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
3333 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
3334 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3335 (void) FormatLocaleString(value,MaxTextExtent,"image/%s",image->magick);
3337 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3338 (void) SetImageProperty(thumbnail_image,"software",
3339 GetMagickVersion(&version));
3340 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3341 image->magick_columns);
3342 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
3343 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3344 image->magick_rows);
3345 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
3346 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3347 GetImageListLength(image));
3348 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3349 return(thumbnail_image);