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 equivelent, 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=StringToDouble(artifact);
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 *= StringToDouble(artifact);
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(StringToDouble(artifact));
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(StringToDouble(artifact));
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=StringToDouble(artifact);
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=StringToDouble(artifact);
1005 artifact=GetImageArtifact(image,"filter:c");
1006 if (artifact != (const char *) NULL)
1008 C=StringToDouble(artifact);
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) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
1060 (void) fprintf(stdout,"# filter = %s\n",
1061 CommandOptionToMnemonic(MagickFilterOptions,filter_type));
1062 (void) fprintf(stdout,"# window = %s\n",
1063 CommandOptionToMnemonic(MagickFilterOptions, window_type));
1064 (void) fprintf(stdout,"# support = %.*g\n",
1065 GetMagickPrecision(),(double) resize_filter->support);
1066 (void) fprintf(stdout,"# win-support = %.*g\n",
1067 GetMagickPrecision(),(double) resize_filter->window_support);
1068 (void) fprintf(stdout,"# scale_blur = %.*g\n",
1069 GetMagickPrecision(), (double)resize_filter->blur);
1070 if ( filter_type == GaussianFilter )
1071 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",
1072 GetMagickPrecision(), (double)sigma);
1073 (void) fprintf(stdout,"# practical_support = %.*g\n",
1074 GetMagickPrecision(), (double)support);
1075 if ( filter_type == CubicFilter || window_type == CubicFilter )
1076 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",
1077 GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
1078 (void) fprintf(stdout,"\n");
1080 Output values of resulting filter graph -- for graphing
1083 for (x=0.0; x <= support; x+=0.01f)
1084 (void) fprintf(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) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
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);
1849 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1850 lqr_carver_get_height(carver),MagickTrue,exception);
1851 if (rescale_image == (Image *) NULL)
1853 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1854 return((Image *) NULL);
1856 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1858 InheritException(exception,&rescale_image->exception);
1859 rescale_image=DestroyImage(rescale_image);
1860 return((Image *) NULL);
1862 GetMagickPixelPacket(rescale_image,&pixel);
1863 (void) lqr_carver_scan_reset(carver);
1864 rescale_view=AcquireCacheView(rescale_image);
1865 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1867 register IndexPacket
1868 *restrict rescale_indexes;
1870 register PixelPacket
1873 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
1874 if (q == (PixelPacket *) NULL)
1876 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
1877 pixel.red=QuantumRange*(packet[0]/255.0);
1878 pixel.green=QuantumRange*(packet[1]/255.0);
1879 pixel.blue=QuantumRange*(packet[2]/255.0);
1880 if (image->colorspace != CMYKColorspace)
1882 if (image->matte == MagickFalse)
1883 pixel.opacity=QuantumRange*(packet[3]/255.0);
1887 pixel.index=QuantumRange*(packet[3]/255.0);
1888 if (image->matte == MagickFalse)
1889 pixel.opacity=QuantumRange*(packet[4]/255.0);
1891 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
1892 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
1895 rescale_view=DestroyCacheView(rescale_view);
1897 Relinquish resources.
1899 lqr_carver_destroy(carver);
1900 return(rescale_image);
1903 MagickExport Image *LiquidRescaleImage(const Image *image,
1904 const size_t magick_unused(columns),const size_t magick_unused(rows),
1905 const double magick_unused(delta_x),const double magick_unused(rigidity),
1906 ExceptionInfo *exception)
1908 assert(image != (const Image *) NULL);
1909 assert(image->signature == MagickSignature);
1910 if (image->debug != MagickFalse)
1911 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1912 assert(exception != (ExceptionInfo *) NULL);
1913 assert(exception->signature == MagickSignature);
1914 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1915 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1916 return((Image *) NULL);
1921 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1925 % R e s i z e I m a g e %
1929 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1931 % ResizeImage() scales an image to the desired dimensions, using the given
1932 % filter (see AcquireFilterInfo()).
1934 % If an undefined filter is given the filter defaults to Mitchell for a
1935 % colormapped image, a image with a matte channel, or if the image is
1936 % enlarged. Otherwise the filter defaults to a Lanczos.
1938 % ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1940 % The format of the ResizeImage method is:
1942 % Image *ResizeImage(Image *image,const size_t columns,
1943 % const size_t rows,const FilterTypes filter,const double blur,
1944 % ExceptionInfo *exception)
1946 % A description of each parameter follows:
1948 % o image: the image.
1950 % o columns: the number of columns in the scaled image.
1952 % o rows: the number of rows in the scaled image.
1954 % o filter: Image filter to use.
1956 % o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1959 % o exception: return any errors or warnings in this structure.
1963 typedef struct _ContributionInfo
1972 static ContributionInfo **DestroyContributionThreadSet(
1973 ContributionInfo **contribution)
1978 assert(contribution != (ContributionInfo **) NULL);
1979 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
1980 if (contribution[i] != (ContributionInfo *) NULL)
1981 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1983 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
1984 return(contribution);
1987 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1998 number_threads=GetOpenMPMaximumThreads();
1999 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
2000 sizeof(*contribution));
2001 if (contribution == (ContributionInfo **) NULL)
2002 return((ContributionInfo **) NULL);
2003 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
2004 for (i=0; i < (ssize_t) number_threads; i++)
2006 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2007 sizeof(**contribution));
2008 if (contribution[i] == (ContributionInfo *) NULL)
2009 return(DestroyContributionThreadSet(contribution));
2011 return(contribution);
2014 static inline double MagickMax(const double x,const double y)
2021 static inline double MagickMin(const double x,const double y)
2028 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2029 const Image *image,Image *resize_image,const MagickRealType x_factor,
2030 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2032 #define ResizeImageTag "Resize/Image"
2042 **restrict contributions;
2058 Apply filter to resize horizontally from image to resize image.
2060 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
2061 support=scale*GetResizeFilterSupport(resize_filter);
2062 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2063 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2065 InheritException(exception,&resize_image->exception);
2066 return(MagickFalse);
2071 Support too small even for nearest neighbour: Reduce to point
2074 support=(MagickRealType) 0.5;
2077 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2078 if (contributions == (ContributionInfo **) NULL)
2080 (void) ThrowMagickException(exception,GetMagickModule(),
2081 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2082 return(MagickFalse);
2086 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2087 image_view=AcquireCacheView(image);
2088 resize_view=AcquireCacheView(resize_image);
2089 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2090 #pragma omp parallel for shared(status)
2092 for (x=0; x < (ssize_t) resize_image->columns; x++)
2098 register const IndexPacket
2101 register const PixelPacket
2104 register ContributionInfo
2105 *restrict contribution;
2107 register IndexPacket
2108 *restrict resize_indexes;
2110 register PixelPacket
2121 if (status == MagickFalse)
2123 center=(MagickRealType) (x+0.5)/x_factor;
2124 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2125 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
2127 contribution=contributions[GetOpenMPThreadId()];
2128 for (n=0; n < (stop-start); n++)
2130 contribution[n].pixel=start+n;
2131 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2132 ((MagickRealType) (start+n)-center+0.5));
2133 density+=contribution[n].weight;
2135 if ((density != 0.0) && (density != 1.0))
2143 density=1.0/density;
2144 for (i=0; i < n; i++)
2145 contribution[i].weight*=density;
2147 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2148 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
2149 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2151 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2156 indexes=GetCacheViewVirtualIndexQueue(image_view);
2157 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2158 for (y=0; y < (ssize_t) resize_image->rows; y++)
2173 if (image->matte == MagickFalse)
2175 for (i=0; i < n; i++)
2177 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2178 (contribution[i].pixel-contribution[0].pixel);
2179 alpha=contribution[i].weight;
2180 pixel.red+=alpha*(p+j)->red;
2181 pixel.green+=alpha*(p+j)->green;
2182 pixel.blue+=alpha*(p+j)->blue;
2183 pixel.opacity+=alpha*(p+j)->opacity;
2185 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2186 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2187 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2188 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2189 if ((image->colorspace == CMYKColorspace) &&
2190 (resize_image->colorspace == CMYKColorspace))
2192 for (i=0; i < n; i++)
2194 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2195 (contribution[i].pixel-contribution[0].pixel);
2196 alpha=contribution[i].weight;
2197 pixel.index+=alpha*indexes[j];
2199 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
2208 for (i=0; i < n; i++)
2210 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2211 (contribution[i].pixel-contribution[0].pixel);
2212 alpha=contribution[i].weight*QuantumScale*
2213 GetAlphaPixelComponent(p+j);
2214 pixel.red+=alpha*(p+j)->red;
2215 pixel.green+=alpha*(p+j)->green;
2216 pixel.blue+=alpha*(p+j)->blue;
2217 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2220 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2221 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2222 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2223 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2224 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2225 if ((image->colorspace == CMYKColorspace) &&
2226 (resize_image->colorspace == CMYKColorspace))
2228 for (i=0; i < n; i++)
2230 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2231 (contribution[i].pixel-contribution[0].pixel);
2232 alpha=contribution[i].weight*QuantumScale*
2233 GetAlphaPixelComponent(p+j);
2234 pixel.index+=alpha*indexes[j];
2236 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2237 GetIndexPixelComponent(&pixel));
2240 if ((resize_image->storage_class == PseudoClass) &&
2241 (image->storage_class == PseudoClass))
2243 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
2245 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2246 (contribution[i-start].pixel-contribution[0].pixel);
2247 resize_indexes[y]=indexes[j];
2251 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2253 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2258 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2259 #pragma omp critical (MagickCore_HorizontalFilter)
2261 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2262 if (proceed == MagickFalse)
2266 resize_view=DestroyCacheView(resize_view);
2267 image_view=DestroyCacheView(image_view);
2268 contributions=DestroyContributionThreadSet(contributions);
2272 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2273 const Image *image,Image *resize_image,const MagickRealType y_factor,
2274 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2284 **restrict contributions;
2300 Apply filter to resize vertically from image to resize image.
2302 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
2303 support=scale*GetResizeFilterSupport(resize_filter);
2304 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2305 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2307 InheritException(exception,&resize_image->exception);
2308 return(MagickFalse);
2313 Support too small even for nearest neighbour: Reduce to point
2316 support=(MagickRealType) 0.5;
2319 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2320 if (contributions == (ContributionInfo **) NULL)
2322 (void) ThrowMagickException(exception,GetMagickModule(),
2323 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2324 return(MagickFalse);
2328 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2329 image_view=AcquireCacheView(image);
2330 resize_view=AcquireCacheView(resize_image);
2331 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2332 #pragma omp parallel for shared(status)
2334 for (y=0; y < (ssize_t) resize_image->rows; y++)
2340 register const IndexPacket
2343 register const PixelPacket
2346 register ContributionInfo
2347 *restrict contribution;
2349 register IndexPacket
2350 *restrict resize_indexes;
2352 register PixelPacket
2363 if (status == MagickFalse)
2365 center=(MagickRealType) (y+0.5)/y_factor;
2366 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2367 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
2369 contribution=contributions[GetOpenMPThreadId()];
2370 for (n=0; n < (stop-start); n++)
2372 contribution[n].pixel=start+n;
2373 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2374 ((MagickRealType) (start+n)-center+0.5));
2375 density+=contribution[n].weight;
2377 if ((density != 0.0) && (density != 1.0))
2385 density=1.0/density;
2386 for (i=0; i < n; i++)
2387 contribution[i].weight*=density;
2389 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
2390 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2392 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2394 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2399 indexes=GetCacheViewVirtualIndexQueue(image_view);
2400 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2401 for (x=0; x < (ssize_t) resize_image->columns; x++)
2416 if (image->matte == MagickFalse)
2418 for (i=0; i < n; i++)
2420 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2422 alpha=contribution[i].weight;
2423 pixel.red+=alpha*(p+j)->red;
2424 pixel.green+=alpha*(p+j)->green;
2425 pixel.blue+=alpha*(p+j)->blue;
2426 pixel.opacity+=alpha*(p+j)->opacity;
2428 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2429 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2430 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2431 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2432 if ((image->colorspace == CMYKColorspace) &&
2433 (resize_image->colorspace == CMYKColorspace))
2435 for (i=0; i < n; i++)
2437 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2439 alpha=contribution[i].weight;
2440 pixel.index+=alpha*indexes[j];
2442 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
2451 for (i=0; i < n; i++)
2453 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2455 alpha=contribution[i].weight*QuantumScale*
2456 GetAlphaPixelComponent(p+j);
2457 pixel.red+=alpha*(p+j)->red;
2458 pixel.green+=alpha*(p+j)->green;
2459 pixel.blue+=alpha*(p+j)->blue;
2460 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2463 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2464 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2465 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2466 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2467 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2468 if ((image->colorspace == CMYKColorspace) &&
2469 (resize_image->colorspace == CMYKColorspace))
2471 for (i=0; i < n; i++)
2473 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2475 alpha=contribution[i].weight*QuantumScale*
2476 GetAlphaPixelComponent(p+j);
2477 pixel.index+=alpha*indexes[j];
2479 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2480 GetIndexPixelComponent(&pixel));
2483 if ((resize_image->storage_class == PseudoClass) &&
2484 (image->storage_class == PseudoClass))
2486 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
2488 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
2490 resize_indexes[x]=indexes[j];
2494 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2496 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2501 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2502 #pragma omp critical (MagickCore_VerticalFilter)
2504 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2505 if (proceed == MagickFalse)
2509 resize_view=DestroyCacheView(resize_view);
2510 image_view=DestroyCacheView(image_view);
2511 contributions=DestroyContributionThreadSet(contributions);
2515 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2516 const size_t rows,const FilterTypes filter,const double blur,
2517 ExceptionInfo *exception)
2519 #define WorkLoadFactor 0.265
2545 Acquire resize image.
2547 assert(image != (Image *) NULL);
2548 assert(image->signature == MagickSignature);
2549 if (image->debug != MagickFalse)
2550 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2551 assert(exception != (ExceptionInfo *) NULL);
2552 assert(exception->signature == MagickSignature);
2553 if ((columns == 0) || (rows == 0))
2554 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2555 if ((columns == image->columns) && (rows == image->rows) &&
2556 (filter == UndefinedFilter) && (blur == 1.0))
2557 return(CloneImage(image,0,0,MagickTrue,exception));
2558 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2559 if (resize_image == (Image *) NULL)
2560 return(resize_image);
2562 Acquire resize filter.
2564 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2565 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2566 if ((x_factor*y_factor) > WorkLoadFactor)
2567 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2569 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2570 if (filter_image == (Image *) NULL)
2571 return(DestroyImage(resize_image));
2572 filter_type=LanczosFilter;
2573 if (filter != UndefinedFilter)
2576 if ((x_factor == 1.0) && (y_factor == 1.0))
2577 filter_type=PointFilter;
2579 if ((image->storage_class == PseudoClass) ||
2580 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2581 filter_type=MitchellFilter;
2582 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2588 if ((x_factor*y_factor) > WorkLoadFactor)
2590 span=(MagickSizeType) (filter_image->columns+rows);
2591 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
2593 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
2594 span,&offset,exception);
2598 span=(MagickSizeType) (filter_image->rows+columns);
2599 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
2601 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
2602 span,&offset,exception);
2607 filter_image=DestroyImage(filter_image);
2608 resize_filter=DestroyResizeFilter(resize_filter);
2609 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2610 return((Image *) NULL);
2611 resize_image->type=image->type;
2612 return(resize_image);
2616 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2620 % S a m p l e I m a g e %
2624 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2626 % SampleImage() scales an image to the desired dimensions with pixel
2627 % sampling. Unlike other scaling methods, this method does not introduce
2628 % any additional color into the scaled image.
2630 % The format of the SampleImage method is:
2632 % Image *SampleImage(const Image *image,const size_t columns,
2633 % const size_t rows,ExceptionInfo *exception)
2635 % A description of each parameter follows:
2637 % o image: the image.
2639 % o columns: the number of columns in the sampled image.
2641 % o rows: the number of rows in the sampled image.
2643 % o exception: return any errors or warnings in this structure.
2646 MagickExport Image *SampleImage(const Image *image,const size_t columns,
2647 const size_t rows,ExceptionInfo *exception)
2649 #define SampleImageTag "Sample/Image"
2672 Initialize sampled image attributes.
2674 assert(image != (const Image *) NULL);
2675 assert(image->signature == MagickSignature);
2676 if (image->debug != MagickFalse)
2677 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2678 assert(exception != (ExceptionInfo *) NULL);
2679 assert(exception->signature == MagickSignature);
2680 if ((columns == 0) || (rows == 0))
2681 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2682 if ((columns == image->columns) && (rows == image->rows))
2683 return(CloneImage(image,0,0,MagickTrue,exception));
2684 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2685 if (sample_image == (Image *) NULL)
2686 return((Image *) NULL);
2688 Allocate scan line buffer and column offset buffers.
2690 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
2692 if (x_offset == (ssize_t *) NULL)
2694 sample_image=DestroyImage(sample_image);
2695 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2697 for (x=0; x < (ssize_t) sample_image->columns; x++)
2698 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
2699 sample_image->columns);
2705 image_view=AcquireCacheView(image);
2706 sample_view=AcquireCacheView(sample_image);
2707 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2708 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2710 for (y=0; y < (ssize_t) sample_image->rows; y++)
2712 register const IndexPacket
2715 register const PixelPacket
2718 register IndexPacket
2719 *restrict sample_indexes;
2721 register PixelPacket
2730 if (status == MagickFalse)
2732 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2733 sample_image->rows);
2734 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2736 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2738 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2743 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2744 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2748 for (x=0; x < (ssize_t) sample_image->columns; x++)
2749 *q++=p[x_offset[x]];
2750 if ((image->storage_class == PseudoClass) ||
2751 (image->colorspace == CMYKColorspace))
2752 for (x=0; x < (ssize_t) sample_image->columns; x++)
2753 sample_indexes[x]=indexes[x_offset[x]];
2754 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2756 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2761 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2762 #pragma omp critical (MagickCore_SampleImage)
2764 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2765 if (proceed == MagickFalse)
2769 image_view=DestroyCacheView(image_view);
2770 sample_view=DestroyCacheView(sample_view);
2771 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
2772 sample_image->type=image->type;
2773 return(sample_image);
2777 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2781 % S c a l e I m a g e %
2785 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2787 % ScaleImage() changes the size of an image to the given dimensions.
2789 % The format of the ScaleImage method is:
2791 % Image *ScaleImage(const Image *image,const size_t columns,
2792 % const size_t rows,ExceptionInfo *exception)
2794 % A description of each parameter follows:
2796 % o image: the image.
2798 % o columns: the number of columns in the scaled image.
2800 % o rows: the number of rows in the scaled image.
2802 % o exception: return any errors or warnings in this structure.
2805 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2806 const size_t rows,ExceptionInfo *exception)
2808 #define ScaleImageTag "Scale/Image"
2842 Initialize scaled image attributes.
2844 assert(image != (const Image *) NULL);
2845 assert(image->signature == MagickSignature);
2846 if (image->debug != MagickFalse)
2847 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2848 assert(exception != (ExceptionInfo *) NULL);
2849 assert(exception->signature == MagickSignature);
2850 if ((columns == 0) || (rows == 0))
2851 return((Image *) NULL);
2852 if ((columns == image->columns) && (rows == image->rows))
2853 return(CloneImage(image,0,0,MagickTrue,exception));
2854 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2855 if (scale_image == (Image *) NULL)
2856 return((Image *) NULL);
2857 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2859 InheritException(exception,&scale_image->exception);
2860 scale_image=DestroyImage(scale_image);
2861 return((Image *) NULL);
2866 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2869 if (image->rows != scale_image->rows)
2870 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2872 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2873 scale_image->columns,sizeof(*scale_scanline));
2874 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2876 if ((scanline == (MagickPixelPacket *) NULL) ||
2877 (scale_scanline == (MagickPixelPacket *) NULL) ||
2878 (x_vector == (MagickPixelPacket *) NULL) ||
2879 (y_vector == (MagickPixelPacket *) NULL))
2881 scale_image=DestroyImage(scale_image);
2882 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2888 next_row=MagickTrue;
2890 scale.y=(double) scale_image->rows/(double) image->rows;
2891 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2893 GetMagickPixelPacket(image,&pixel);
2894 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2896 image_view=AcquireCacheView(image);
2897 scale_view=AcquireCacheView(scale_image);
2898 for (y=0; y < (ssize_t) scale_image->rows; y++)
2900 register const IndexPacket
2903 register const PixelPacket
2906 register IndexPacket
2907 *restrict scale_indexes;
2909 register MagickPixelPacket
2913 register PixelPacket
2919 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2921 if (q == (PixelPacket *) NULL)
2923 scale_indexes=GetCacheViewAuthenticIndexQueue(scale_view);
2924 if (scale_image->rows == image->rows)
2927 Read a new scanline.
2929 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2931 if (p == (const PixelPacket *) NULL)
2933 indexes=GetCacheViewVirtualIndexQueue(image_view);
2934 for (x=0; x < (ssize_t) image->columns; x++)
2936 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2937 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2938 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
2939 if (image->matte != MagickFalse)
2940 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
2941 if (indexes != (IndexPacket *) NULL)
2942 x_vector[x].index=(MagickRealType) indexes[x];
2951 while (scale.y < span.y)
2953 if ((next_row != MagickFalse) &&
2954 (number_rows < (ssize_t) image->rows))
2957 Read a new scanline.
2959 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2961 if (p == (const PixelPacket *) NULL)
2963 indexes=GetCacheViewVirtualIndexQueue(image_view);
2964 for (x=0; x < (ssize_t) image->columns; x++)
2966 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2967 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2968 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
2969 if (image->matte != MagickFalse)
2970 x_vector[x].opacity=(MagickRealType)
2971 GetOpacityPixelComponent(p);
2972 if (indexes != (IndexPacket *) NULL)
2973 x_vector[x].index=(MagickRealType) indexes[x];
2978 for (x=0; x < (ssize_t) image->columns; x++)
2980 y_vector[x].red+=scale.y*x_vector[x].red;
2981 y_vector[x].green+=scale.y*x_vector[x].green;
2982 y_vector[x].blue+=scale.y*x_vector[x].blue;
2983 if (scale_image->matte != MagickFalse)
2984 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2985 if (scale_indexes != (IndexPacket *) NULL)
2986 y_vector[x].index+=scale.y*x_vector[x].index;
2989 scale.y=(double) scale_image->rows/(double) image->rows;
2990 next_row=MagickTrue;
2992 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
2995 Read a new scanline.
2997 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2999 if (p == (const PixelPacket *) NULL)
3001 indexes=GetCacheViewVirtualIndexQueue(image_view);
3002 for (x=0; x < (ssize_t) image->columns; x++)
3004 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
3005 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
3006 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
3007 if (image->matte != MagickFalse)
3008 x_vector[x].opacity=(MagickRealType)
3009 GetOpacityPixelComponent(p);
3010 if (indexes != (IndexPacket *) NULL)
3011 x_vector[x].index=(MagickRealType) indexes[x];
3015 next_row=MagickFalse;
3018 for (x=0; x < (ssize_t) image->columns; x++)
3020 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3021 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3022 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3023 if (image->matte != MagickFalse)
3024 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3025 if (scale_indexes != (IndexPacket *) NULL)
3026 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3028 s->green=pixel.green;
3030 if (scale_image->matte != MagickFalse)
3031 s->opacity=pixel.opacity;
3032 if (scale_indexes != (IndexPacket *) NULL)
3033 s->index=pixel.index;
3040 scale.y=(double) scale_image->rows/(double) image->rows;
3041 next_row=MagickTrue;
3045 if (scale_image->columns == image->columns)
3048 Transfer scanline to scaled image.
3051 for (x=0; x < (ssize_t) scale_image->columns; x++)
3053 q->red=ClampToQuantum(s->red);
3054 q->green=ClampToQuantum(s->green);
3055 q->blue=ClampToQuantum(s->blue);
3056 if (scale_image->matte != MagickFalse)
3057 q->opacity=ClampToQuantum(s->opacity);
3058 if (scale_indexes != (IndexPacket *) NULL)
3059 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
3070 next_column=MagickFalse;
3074 for (x=0; x < (ssize_t) image->columns; x++)
3076 scale.x=(double) scale_image->columns/(double) image->columns;
3077 while (scale.x >= span.x)
3079 if (next_column != MagickFalse)
3084 pixel.red+=span.x*s->red;
3085 pixel.green+=span.x*s->green;
3086 pixel.blue+=span.x*s->blue;
3087 if (image->matte != MagickFalse)
3088 pixel.opacity+=span.x*s->opacity;
3089 if (scale_indexes != (IndexPacket *) NULL)
3090 pixel.index+=span.x*s->index;
3092 t->green=pixel.green;
3094 if (scale_image->matte != MagickFalse)
3095 t->opacity=pixel.opacity;
3096 if (scale_indexes != (IndexPacket *) NULL)
3097 t->index=pixel.index;
3100 next_column=MagickTrue;
3104 if (next_column != MagickFalse)
3107 next_column=MagickFalse;
3110 pixel.red+=scale.x*s->red;
3111 pixel.green+=scale.x*s->green;
3112 pixel.blue+=scale.x*s->blue;
3113 if (scale_image->matte != MagickFalse)
3114 pixel.opacity+=scale.x*s->opacity;
3115 if (scale_indexes != (IndexPacket *) NULL)
3116 pixel.index+=scale.x*s->index;
3124 pixel.red+=span.x*s->red;
3125 pixel.green+=span.x*s->green;
3126 pixel.blue+=span.x*s->blue;
3127 if (scale_image->matte != MagickFalse)
3128 pixel.opacity+=span.x*s->opacity;
3129 if (scale_indexes != (IndexPacket *) NULL)
3130 pixel.index+=span.x*s->index;
3132 if ((next_column == MagickFalse) &&
3133 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
3136 t->green=pixel.green;
3138 if (scale_image->matte != MagickFalse)
3139 t->opacity=pixel.opacity;
3140 if (scale_indexes != (IndexPacket *) NULL)
3141 t->index=pixel.index;
3144 Transfer scanline to scaled image.
3147 for (x=0; x < (ssize_t) scale_image->columns; x++)
3149 q->red=ClampToQuantum(t->red);
3150 q->green=ClampToQuantum(t->green);
3151 q->blue=ClampToQuantum(t->blue);
3152 if (scale_image->matte != MagickFalse)
3153 q->opacity=ClampToQuantum(t->opacity);
3154 if (scale_indexes != (IndexPacket *) NULL)
3155 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
3160 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
3162 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3164 if (proceed == MagickFalse)
3167 scale_view=DestroyCacheView(scale_view);
3168 image_view=DestroyCacheView(image_view);
3170 Free allocated memory.
3172 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3173 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3174 if (scale_image->rows != image->rows)
3175 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3176 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3177 scale_image->type=image->type;
3178 return(scale_image);
3182 THIS IS NOT USED -- to be removed
3184 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3188 + S e t R e s i z e F i l t e r S u p p o r t %
3192 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3194 % SetResizeFilterSupport() specifies which IR filter to use to window
3196 % The format of the SetResizeFilterSupport method is:
3198 % void SetResizeFilterSupport(ResizeFilter *resize_filter,
3199 % const MagickRealType support)
3201 % A description of each parameter follows:
3203 % o resize_filter: the resize filter.
3205 % o support: the filter spport radius.
3208 MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3209 const MagickRealType support)
3211 assert(resize_filter != (ResizeFilter *) NULL);
3212 assert(resize_filter->signature == MagickSignature);
3213 resize_filter->support=support;
3218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3222 % T h u m b n a i l I m a g e %
3226 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3228 % ThumbnailImage() changes the size of an image to the given dimensions and
3229 % removes any associated profiles. The goal is to produce small low cost
3230 % thumbnail images suited for display on the Web.
3232 % The format of the ThumbnailImage method is:
3234 % Image *ThumbnailImage(const Image *image,const size_t columns,
3235 % const size_t rows,ExceptionInfo *exception)
3237 % A description of each parameter follows:
3239 % o image: the image.
3241 % o columns: the number of columns in the scaled image.
3243 % o rows: the number of rows in the scaled image.
3245 % o exception: return any errors or warnings in this structure.
3248 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3249 const size_t rows,ExceptionInfo *exception)
3251 #define SampleFactor 5
3254 value[MaxTextExtent];
3272 assert(image != (Image *) NULL);
3273 assert(image->signature == MagickSignature);
3274 if (image->debug != MagickFalse)
3275 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3276 assert(exception != (ExceptionInfo *) NULL);
3277 assert(exception->signature == MagickSignature);
3278 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3279 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3280 if ((x_factor*y_factor) > 0.1)
3281 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3284 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
3285 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3286 image->blur,exception);
3292 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3294 if (sample_image == (Image *) NULL)
3295 return((Image *) NULL);
3296 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3297 image->blur,exception);
3298 sample_image=DestroyImage(sample_image);
3300 if (thumbnail_image == (Image *) NULL)
3301 return(thumbnail_image);
3302 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3303 if (thumbnail_image->matte == MagickFalse)
3304 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3305 thumbnail_image->depth=8;
3306 thumbnail_image->interlace=NoInterlace;
3308 Strip all profiles except color profiles.
3310 ResetImageProfileIterator(thumbnail_image);
3311 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3313 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3315 (void) DeleteImageProfile(thumbnail_image,name);
3316 ResetImageProfileIterator(thumbnail_image);
3318 name=GetNextImageProfile(thumbnail_image);
3320 (void) DeleteImageProperty(thumbnail_image,"comment");
3321 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3322 if (strstr(image->magick_filename,"//") == (char *) NULL)
3323 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
3324 image->magick_filename);
3325 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3326 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3327 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3329 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3330 attributes.st_mtime);
3331 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3333 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3334 attributes.st_mtime);
3335 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
3336 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
3337 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3338 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3340 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3341 (void) SetImageProperty(thumbnail_image,"software",
3342 GetMagickVersion(&version));
3343 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3344 image->magick_columns);
3345 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
3346 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3347 image->magick_rows);
3348 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
3349 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3350 GetImageListLength(image));
3351 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3352 return(thumbnail_image);