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-2014 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/cache-view.h"
47 #include "MagickCore/channel.h"
48 #include "MagickCore/color.h"
49 #include "MagickCore/color-private.h"
50 #include "MagickCore/draw.h"
51 #include "MagickCore/exception.h"
52 #include "MagickCore/exception-private.h"
53 #include "MagickCore/gem.h"
54 #include "MagickCore/image.h"
55 #include "MagickCore/image-private.h"
56 #include "MagickCore/list.h"
57 #include "MagickCore/memory_.h"
58 #include "MagickCore/memory-private.h"
59 #include "MagickCore/magick.h"
60 #include "MagickCore/pixel-accessor.h"
61 #include "MagickCore/property.h"
62 #include "MagickCore/monitor.h"
63 #include "MagickCore/monitor-private.h"
64 #include "MagickCore/option.h"
65 #include "MagickCore/pixel.h"
66 #include "MagickCore/pixel-private.h"
67 #include "MagickCore/quantum-private.h"
68 #include "MagickCore/resample.h"
69 #include "MagickCore/resample-private.h"
70 #include "MagickCore/resize.h"
71 #include "MagickCore/resize-private.h"
72 #include "MagickCore/resource_.h"
73 #include "MagickCore/string_.h"
74 #include "MagickCore/string-private.h"
75 #include "MagickCore/thread-private.h"
76 #include "MagickCore/token.h"
77 #include "MagickCore/utility.h"
78 #include "MagickCore/utility-private.h"
79 #include "MagickCore/version.h"
80 #if defined(MAGICKCORE_LQR_DELEGATE)
90 (*filter)(const double,const ResizeFilter *),
91 (*window)(const double,const ResizeFilter *),
92 support, /* filter region of support - the filter support limit */
93 window_support, /* window support, usally equal to support (expert only) */
94 scale, /* dimension scaling to fit window support (usally 1.0) */
95 blur, /* x-scale (blur-sharpen) */
96 coefficient[7]; /* cubic coefficents for BC-cubic filters */
103 Forward declaractions.
107 BesselOrderOne(double),
108 Sinc(const double, const ResizeFilter *),
109 SincFast(const double, const ResizeFilter *);
112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
116 + F i l t e r F u n c t i o n s %
120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
122 % These are the various filter and windowing functions that are provided.
124 % They are internal to this module only. See AcquireResizeFilterInfo() for
125 % details of the access to these functions, via the GetResizeFilterSupport()
126 % and GetResizeFilterWeight() API interface.
128 % The individual filter functions have this format...
130 % static MagickRealtype *FilterName(const double x,
131 % const double support)
133 % A description of each parameter follows:
135 % o x: the distance from the sampling point generally in the range of 0 to
136 % support. The GetResizeFilterWeight() ensures this a positive value.
138 % o resize_filter: current filter information. This allows function to
139 % access support, and possibly other pre-calculated information defining
144 static double Blackman(const double x,
145 const ResizeFilter *magick_unused(resize_filter))
148 Blackman: 2nd order cosine windowing function:
149 0.42 + 0.5 cos(pi x) + 0.08 cos(2pi x)
151 Refactored by Chantal Racette and Nicolas Robidoux to one trig call and
154 const double cosine=cos((double) (MagickPI*x));
155 return(0.34+cosine*(0.5+cosine*0.16));
158 static double Bohman(const double x,
159 const ResizeFilter *magick_unused(resize_filter))
162 Bohman: 2rd Order cosine windowing function:
163 (1-x) cos(pi x) + sin(pi x) / pi.
165 Refactored by Nicolas Robidoux to one trig call, one sqrt call, and 7 flops,
166 taking advantage of the fact that the support of Bohman is 1.0 (so that we
167 know that sin(pi x) >= 0).
169 const double cosine=cos((double) (MagickPI*x));
170 const double sine=sqrt(1.0-cosine*cosine);
171 return((1.0-x)*cosine+(1.0/MagickPI)*sine);
174 static double Box(const double magick_unused(x),
175 const ResizeFilter *magick_unused(resize_filter))
178 A Box filter is a equal weighting function (all weights equal).
179 DO NOT LIMIT results by support or resize point sampling will work
180 as it requests points beyond its normal 0.0 support size.
185 static double Cosine(const double x,
186 const ResizeFilter *magick_unused(resize_filter))
189 Cosine window function:
192 return((double)cos((double) (MagickPI2*x)));
195 static double CubicBC(const double x,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 Spline B = 1 C = 0 B-Spline Gaussian approximation
202 Hermite B = 0 C = 0 B-Spline interpolator
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 (slope).
227 return(resize_filter->coefficient[0]+x*(x*
228 (resize_filter->coefficient[1]+x*resize_filter->coefficient[2])));
230 return(resize_filter->coefficient[3]+x*(resize_filter->coefficient[4]+x*
231 (resize_filter->coefficient[5]+x*resize_filter->coefficient[6])));
235 static double Gaussian(const double x,const ResizeFilter *resize_filter)
238 Gaussian with a sigma = 1/2 (or as user specified)
240 Gaussian Formula (1D) ...
241 exp( -(x^2)/((2.0*sigma^2) ) / (sqrt(2*PI)*sigma^2))
243 Gaussian Formula (2D) ...
244 exp( -(x^2+y^2)/(2.0*sigma^2) ) / (PI*sigma^2) )
246 exp( -(r^2)/(2.0*sigma^2) ) / (PI*sigma^2) )
248 Note that it is only a change from 1-d to radial form is in the
249 normalization multiplier which is not needed or used when Gaussian is used
252 The constants are pre-calculated...
255 coeff[1]=1.0/(2.0*sigma^2);
256 coeff[2]=1.0/(sqrt(2*PI)*sigma^2);
258 exp( -coeff[1]*(x^2)) ) * coeff[2];
260 However the multiplier coeff[1] is need, the others are informative only.
262 This separates the gaussian 'sigma' value from the 'blur/support'
263 settings allowing for its use in special 'small sigma' gaussians,
264 without the filter 'missing' pixels because the support becomes too
267 return(exp((double)(-resize_filter->coefficient[1]*x*x)));
270 static double Hann(const double x,
271 const ResizeFilter *magick_unused(resize_filter))
274 Cosine window function:
277 const double cosine=cos((double) (MagickPI*x));
278 return(0.5+0.5*cosine);
281 static double Hamming(const double x,
282 const ResizeFilter *magick_unused(resize_filter))
285 Offset cosine window function:
288 const double cosine=cos((double) (MagickPI*x));
289 return(0.54+0.46*cosine);
292 static double Jinc(const double x,
293 const ResizeFilter *magick_unused(resize_filter))
296 See Pratt "Digital Image Processing" p.97 for Jinc/Bessel functions.
297 http://mathworld.wolfram.com/JincFunction.html and page 11 of
298 http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
300 The original "zoom" program by Paul Heckbert called this "Bessel". But
301 really it is more accurately named "Jinc".
304 return(0.5*MagickPI);
305 return(BesselOrderOne(MagickPI*x)/x);
308 static double Kaiser(const double x,const ResizeFilter *resize_filter)
311 Kaiser Windowing Function (bessel windowing)
313 I0( beta * sqrt( 1-x^2) ) / IO(0)
315 Beta (coeff[0]) is a free value from 5 to 8 (defaults to 6.5).
316 However it is typically defined in terms of Alpha*PI
318 The normalization factor (coeff[1]) is not actually needed,
319 but without it the filters has a large value at x=0 making it
320 difficult to compare the function with other windowing functions.
322 return(resize_filter->coefficient[1]*I0(resize_filter->coefficient[0]*
323 sqrt((double) (1.0-x*x))));
326 static double Lagrange(const double x,const ResizeFilter *resize_filter)
339 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the lagrange
340 function and depends on the overall support window size of the filter. That
341 is: for a support of 2, it gives a lagrange-4 (piecewise cubic function).
343 "n" identifies the piece of the piecewise polynomial.
345 See Survey: Interpolation Methods, IEEE Transactions on Medical Imaging,
346 Vol 18, No 11, November 1999, p1049-1075, -- Equation 27 on p1064.
348 if (x > resize_filter->support)
350 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
351 n=(ssize_t) (resize_filter->window_support+x);
353 for (i=0; i < order; i++)
355 value*=(n-i-x)/(n-i);
359 static double Quadratic(const double x,
360 const ResizeFilter *magick_unused(resize_filter))
363 2rd order (quadratic) B-Spline approximation of Gaussian.
368 return(0.5*(x-1.5)*(x-1.5));
372 static double Sinc(const double x,
373 const ResizeFilter *magick_unused(resize_filter))
376 Scaled sinc(x) function using a trig call:
377 sinc(x) == sin(pi x)/(pi x).
381 const double alpha=(double) (MagickPI*x);
382 return(sin((double) alpha)/alpha);
384 return((double) 1.0);
387 static double SincFast(const double x,
388 const ResizeFilter *magick_unused(resize_filter))
391 Approximations of the sinc function sin(pi x)/(pi x) over the interval
392 [-4,4] constructed by Nicolas Robidoux and Chantal Racette with funding
393 from the Natural Sciences and Engineering Research Council of Canada.
395 Although the approximations are polynomials (for low order of
396 approximation) and quotients of polynomials (for higher order of
397 approximation) and consequently are similar in form to Taylor polynomials /
398 Pade approximants, the approximations are computed with a completely
401 Summary: These approximations are "the best" in terms of bang (accuracy)
402 for the buck (flops). More specifically: Among the polynomial quotients
403 that can be computed using a fixed number of flops (with a given "+ - * /
404 budget"), the chosen polynomial quotient is the one closest to the
405 approximated function with respect to maximum absolute relative error over
408 The Remez algorithm, as implemented in the boost library's minimax package,
409 is the key to the construction: http://www.boost.org/doc/libs/1_36_0/libs/
410 math/doc/sf_and_dist/html/math_toolkit/backgrounders/remez.html
412 If outside of the interval of approximation, use the standard trig formula.
416 const double alpha=(double) (MagickPI*x);
417 return(sin((double) alpha)/alpha);
421 The approximations only depend on x^2 (sinc is an even function).
423 const double xx = x*x;
424 #if MAGICKCORE_QUANTUM_DEPTH <= 8
426 Maximum absolute relative error 6.3e-6 < 1/2^17.
428 const double c0 = 0.173610016489197553621906385078711564924e-2L;
429 const double c1 = -0.384186115075660162081071290162149315834e-3L;
430 const double c2 = 0.393684603287860108352720146121813443561e-4L;
431 const double c3 = -0.248947210682259168029030370205389323899e-5L;
432 const double c4 = 0.107791837839662283066379987646635416692e-6L;
433 const double c5 = -0.324874073895735800961260474028013982211e-8L;
434 const double c6 = 0.628155216606695311524920882748052490116e-10L;
435 const double c7 = -0.586110644039348333520104379959307242711e-12L;
437 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
438 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
439 #elif MAGICKCORE_QUANTUM_DEPTH <= 16
441 Max. abs. rel. error 2.2e-8 < 1/2^25.
443 const double c0 = 0.173611107357320220183368594093166520811e-2L;
444 const double c1 = -0.384240921114946632192116762889211361285e-3L;
445 const double c2 = 0.394201182359318128221229891724947048771e-4L;
446 const double c3 = -0.250963301609117217660068889165550534856e-5L;
447 const double c4 = 0.111902032818095784414237782071368805120e-6L;
448 const double c5 = -0.372895101408779549368465614321137048875e-8L;
449 const double c6 = 0.957694196677572570319816780188718518330e-10L;
450 const double c7 = -0.187208577776590710853865174371617338991e-11L;
451 const double c8 = 0.253524321426864752676094495396308636823e-13L;
452 const double c9 = -0.177084805010701112639035485248501049364e-15L;
454 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
455 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
458 Max. abs. rel. error 1.2e-12 < 1/2^39.
460 const double c0 = 0.173611111110910715186413700076827593074e-2L;
461 const double c1 = -0.289105544717893415815859968653611245425e-3L;
462 const double c2 = 0.206952161241815727624413291940849294025e-4L;
463 const double c3 = -0.834446180169727178193268528095341741698e-6L;
464 const double c4 = 0.207010104171026718629622453275917944941e-7L;
465 const double c5 = -0.319724784938507108101517564300855542655e-9L;
466 const double c6 = 0.288101675249103266147006509214934493930e-11L;
467 const double c7 = -0.118218971804934245819960233886876537953e-13L;
469 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
470 const double d0 = 1.0L;
471 const double d1 = 0.547981619622284827495856984100563583948e-1L;
472 const double d2 = 0.134226268835357312626304688047086921806e-2L;
473 const double d3 = 0.178994697503371051002463656833597608689e-4L;
474 const double d4 = 0.114633394140438168641246022557689759090e-6L;
475 const double q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
476 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
481 static double Triangle(const double x,
482 const ResizeFilter *magick_unused(resize_filter))
485 1st order (linear) B-Spline, bilinear interpolation, Tent 1D filter, or
486 a Bartlett 2D Cone filter. Also used as a Bartlett Windowing function
494 static double Welch(const double x,
495 const ResizeFilter *magick_unused(resize_filter))
498 Welch parabolic windowing filter.
506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
510 + A c q u i r e R e s i z e F i l t e r %
514 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
516 % AcquireResizeFilter() allocates the ResizeFilter structure. Choose from
519 % FIR (Finite impulse Response) Filters
520 % Box Triangle Quadratic
521 % Spline Hermite Catrom
524 % IIR (Infinite impulse Response) Filters
525 % Gaussian Sinc Jinc (Bessel)
527 % Windowed Sinc/Jinc Filters
528 % Blackman Bohman Lanczos
529 % Hann Hamming Cosine
530 % Kaiser Welch Parzen
533 % Special Purpose Filters
534 % Cubic SincFast LanczosSharp Lanczos2 Lanczos2Sharp
535 % Robidoux RobidouxSharp
537 % The users "-filter" selection is used to lookup the default 'expert'
538 % settings for that filter from a internal table. However any provided
539 % 'expert' settings (see below) may override this selection.
541 % FIR filters are used as is, and are limited to that filters support window
542 % (unless over-ridden). 'Gaussian' while classed as an IIR filter, is also
543 % simply clipped by its support size (currently 1.5 or approximately 3*sigma
544 % as recommended by many references)
546 % The special a 'cylindrical' filter flag will promote the default 4-lobed
547 % Windowed Sinc filter to a 3-lobed Windowed Jinc equivalent, which is better
548 % suited to this style of image resampling. This typically happens when using
549 % such a filter for images distortions.
553 % Directly requesting 'Sinc', 'Jinc' function as a filter will force the use
554 % of function without any windowing, or promotion for cylindrical usage. This
555 % is not recommended, except by image processing experts, especially as part
556 % of expert option filter function selection.
558 % Two forms of the 'Sinc' function are available: Sinc and SincFast. Sinc is
559 % computed using the traditional sin(pi*x)/(pi*x); it is selected if the user
560 % specifically specifies the use of a Sinc filter. SincFast uses highly
561 % accurate (and fast) polynomial (low Q) and rational (high Q) approximations,
562 % and will be used by default in most cases.
564 % The Lanczos filter is a special 3-lobed Sinc-windowed Sinc filter (promoted
565 % to Jinc-windowed Jinc for cylindrical (Elliptical Weighted Average) use).
566 % The Sinc version is the most popular windowed filter.
568 % LanczosSharp is a slightly sharpened (blur=0.9812505644269356 < 1) form of
569 % the Lanczos filter, specifically designed for EWA distortion (as a
570 % Jinc-Jinc); it can also be used as a slightly sharper orthogonal Lanczos
571 % (Sinc-Sinc) filter. The chosen blur value comes as close as possible to
572 % satisfying the following condition without changing the character of the
573 % corresponding EWA filter:
575 % 'No-Op' Vertical and Horizontal Line Preservation Condition: Images with
576 % only vertical or horizontal features are preserved when performing 'no-op"
577 % with EWA distortion.
579 % The Lanczos2 and Lanczos2Sharp filters are 2-lobe versions of the Lanczos
580 % filters. The 'sharp' version uses a blur factor of 0.9549963639785485,
581 % again chosen because the resulting EWA filter comes as close as possible to
582 % satisfying the above condition.
584 % Robidoux is another filter tuned for EWA. It is the Keys cubic filter
585 % defined by B=(228 - 108 sqrt(2))/199. Robidoux satisfies the "'No-Op'
586 % Vertical and Horizontal Line Preservation Condition" exactly, and it
587 % moderately blurs high frequency 'pixel-hash' patterns under no-op. It turns
588 % out to be close to both Mitchell and Lanczos2Sharp. For example, its first
589 % crossing is at (36 sqrt(2) + 123)/(72 sqrt(2) + 47), almost the same as the
590 % first crossing of Mitchell and Lanczos2Sharp.
592 % RodidouxSharp is a slightly sharper version of Rodidoux, some believe it
593 % is too sharp. It is designed to minimize the maximum possible change in
594 % a pixel value which is at one of the extremes (e.g., 0 or 255) under no-op
595 % conditions. Amazingly Mitchell falls roughly between Rodidoux and
596 % RodidouxSharp, though this seems to have been pure coincidence.
600 % These artifact "defines" are not recommended for production use without
601 % expert knowledge of resampling, filtering, and the effects they have on the
602 % resulting resampled (resized or distorted) image.
604 % They can be used to override any and all filter default, and it is
605 % recommended you make good use of "filter:verbose" to make sure that the
606 % overall effect of your selection (before and after) is as expected.
608 % "filter:verbose" controls whether to output the exact results of the
609 % filter selections made, as well as plotting data for graphing the
610 % resulting filter over the filters support range.
612 % "filter:filter" select the main function associated with this filter
613 % name, as the weighting function of the filter. This can be used to
614 % set a windowing function as a weighting function, for special
615 % purposes, such as graphing.
617 % If a "filter:window" operation has not been provided, a 'Box'
618 % windowing function will be set to denote that no windowing function is
621 % "filter:window" Select this windowing function for the filter. While any
622 % filter could be used as a windowing function, using the 'first lobe' of
623 % that filter over the whole support window, using a non-windowing
624 % function is not advisible. If no weighting filter function is specified
625 % a 'SincFast' filter is used.
627 % "filter:lobes" Number of lobes to use for the Sinc/Jinc filter. This a
628 % simpler method of setting filter support size that will correctly
629 % handle the Sinc/Jinc switch for an operators filtering requirements.
630 % Only integers should be given.
632 % "filter:support" Set the support size for filtering to the size given.
633 % This not recommended for Sinc/Jinc windowed filters (lobes should be
634 % used instead). This will override any 'filter:lobes' option.
636 % "filter:win-support" Scale windowing function to this size instead. This
637 % causes the windowing (or self-windowing Lagrange filter) to act is if
638 % the support window it much much larger than what is actually supplied
639 % to the calling operator. The filter however is still clipped to the
640 % real support size given, by the support range supplied to the caller.
641 % If unset this will equal the normal filter support size.
643 % "filter:blur" Scale the filter and support window by this amount. A value
644 % of > 1 will generally result in a more blurred image with more ringing
645 % effects, while a value <1 will sharpen the resulting image with more
648 % "filter:sigma" The sigma value to use for the Gaussian filter only.
649 % Defaults to '1/2'. Using a different sigma effectively provides a
650 % method of using the filter as a 'blur' convolution. Particularly when
651 % using it for Distort.
654 % "filter:c" Override the preset B,C values for a Cubic filter.
655 % If only one of these are given it is assumes to be a 'Keys' type of
656 % filter such that B+2C=1, where Keys 'alpha' value = C.
660 % Set a true un-windowed Sinc filter with 10 lobes (very slow):
661 % -define filter:filter=Sinc
662 % -define filter:lobes=8
664 % Set an 8 lobe Lanczos (Sinc or Jinc) filter:
666 % -define filter:lobes=8
668 % The format of the AcquireResizeFilter method is:
670 % ResizeFilter *AcquireResizeFilter(const Image *image,
671 % const FilterTypes filter_type,const MagickBooleanType cylindrical,
672 % ExceptionInfo *exception)
674 % A description of each parameter follows:
676 % o image: the image.
678 % o filter: the filter type, defining a preset filter, window and support.
679 % The artifact settings listed above will override those selections.
681 % o blur: blur the filter by this amount, use 1.0 if unknown. Image
682 % artifact "filter:blur" will override this API call usage, including any
683 % internal change (such as for cylindrical usage).
685 % o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical (radial)
688 % o exception: return any errors or warnings in this structure.
691 MagickPrivate ResizeFilter *AcquireResizeFilter(const Image *image,
692 const FilterTypes filter,const MagickBooleanType cylindrical,
693 ExceptionInfo *exception)
707 register ResizeFilter
711 Table Mapping given Filter, into Weighting and Windowing functions.
712 A 'Box' windowing function means its a simble non-windowed filter.
713 An 'SincFast' filter function could be upgraded to a 'Jinc' filter if a
714 "cylindrical" is requested, unless a 'Sinc' or 'SincFast' filter was
715 specifically requested by the user.
717 WARNING: The order of this table must match the order of the FilterTypes
718 enumeration specified in "resample.h", or the filter names will not match
719 the filter being setup.
721 You can check filter setups with the "filter:verbose" expert setting.
728 } const mapping[SentinelFilter] =
730 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
731 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
732 { BoxFilter, BoxFilter }, /* Box averaging filter */
733 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
734 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
735 { SincFastFilter, HannFilter }, /* Hann -- cosine-sinc */
736 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
737 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
738 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
739 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approx */
740 { CubicFilter, BoxFilter }, /* General Cubic Filter, Spline */
741 { CatromFilter, BoxFilter }, /* Cubic-Keys interpolator */
742 { MitchellFilter, BoxFilter }, /* 'Ideal' Cubic-Keys filter */
743 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
744 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
745 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
746 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
747 { LanczosFilter, WelchFilter }, /* Welch -- parabolic (3 lobe) */
748 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
749 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
750 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
751 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing */
752 { LanczosFilter, LanczosFilter }, /* Lanczos Sinc-Sinc filters */
753 { LanczosSharpFilter, LanczosSharpFilter }, /* | these require */
754 { Lanczos2Filter, Lanczos2Filter }, /* | special handling */
755 { Lanczos2SharpFilter, Lanczos2SharpFilter },
756 { RobidouxFilter, BoxFilter }, /* Cubic Keys tuned for EWA */
757 { RobidouxSharpFilter, BoxFilter }, /* Sharper Cubic Keys for EWA */
758 { LanczosFilter, CosineFilter }, /* Cosine window (3 lobes) */
759 { SplineFilter, BoxFilter }, /* Spline Cubic Filter */
760 { LanczosRadiusFilter, LanczosFilter }, /* Lanczos with integer radius */
763 Table mapping the filter/window from the above table to an actual function.
764 The default support size for that filter as a weighting function, the range
765 to scale with to use that function as a sinc windowing function, (typ 1.0).
767 Note that the filter_type -> function is 1 to 1 except for Sinc(),
768 SincFast(), and CubicBC() functions, which may have multiple filter to
769 function associations.
771 See "filter:verbose" handling below for the function -> filter mapping.
776 (*function)(const double,const ResizeFilter*),
777 support, /* Default lobes/support size of the weighting filter. */
778 scale, /* Support when function used as a windowing function
779 Typically equal to the location of the first zero crossing. */
780 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
781 } const filters[SentinelFilter] =
783 /* .--- support window (if used as a Weighting Function)
784 | .--- first crossing (if used as a Windowing Function)
785 | | .--- B value for Cubic Function
786 | | | .---- C value for Cubic Function
788 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
789 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
790 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
791 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
792 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
793 { Hann, 1.0, 1.0, 0.0, 0.0 }, /* Hann, cosine window */
794 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
795 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
796 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
797 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
798 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* General Cubic Filter */
799 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
800 { CubicBC, 2.0, 8.0/7.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
801 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
802 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
803 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
804 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
805 { Welch, 1.0, 1.0, 0.0, 0.0 }, /* Welch (parabolic window) */
806 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
807 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
808 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
809 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
810 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
811 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, Sharpened */
812 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos, 2-lobed */
813 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2, sharpened */
814 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
815 { CubicBC, 2.0, 1.1685777620836932,
816 0.37821575509399867, 0.31089212245300067 },
817 /* RobidouxSharp: Sharper version of Robidoux */
818 { CubicBC, 2.0, 1.105822933719019,
819 0.2620145123990142, 0.3689927438004929 },
820 { Cosine, 1.0, 1.0, 0.0, 0.0 }, /* Low level cosine window */
821 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
822 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, Interger Radius */
825 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
826 function being used as a filter. It is used by the "filter:lobes" expert
827 setting and for 'lobes' for Jinc functions in the previous table. This way
828 users do not have to deal with the highly irrational lobe sizes of the Jinc
832 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
833 using Jv-function with v=1, then dividing by PI.
857 Allocate resize filter.
859 assert(image != (const Image *) NULL);
860 assert(image->signature == MagickSignature);
861 if (image->debug != MagickFalse)
862 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
863 assert(UndefinedFilter < filter && filter < SentinelFilter);
864 assert(exception != (ExceptionInfo *) NULL);
865 assert(exception->signature == MagickSignature);
866 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
867 if (resize_filter == (ResizeFilter *) NULL)
868 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
869 (void) ResetMagickMemory(resize_filter,0,sizeof(*resize_filter));
871 Defaults for the requested filter.
873 filter_type=mapping[filter].filter;
874 window_type=mapping[filter].window;
875 resize_filter->blur=1.0;
876 /* Promote 1D Windowed Sinc Filters to a 2D Windowed Jinc filters */
877 if ( IfMagickTrue(cylindrical) && (filter_type == SincFastFilter) &&
878 (filter != SincFastFilter))
879 filter_type=JincFilter; /* 1D Windowed Sinc => 2D Windowed Jinc filters */
881 /* Expert filter setting override */
882 artifact=GetImageArtifact(image,"filter:filter");
883 if (artifact != (const char *) NULL)
888 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
889 if ((UndefinedFilter < option) && (option < SentinelFilter))
890 { /* Raw filter request - no window function. */
891 filter_type=(FilterTypes) option;
892 window_type=BoxFilter;
894 /* Filter override with a specific window function. */
895 artifact=GetImageArtifact(image,"filter:window");
896 if (artifact != (const char *) NULL)
898 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
899 if ((UndefinedFilter < option) && (option < SentinelFilter))
900 window_type=(FilterTypes) option;
905 /* Window specified, but no filter function? Assume Sinc/Jinc. */
906 artifact=GetImageArtifact(image,"filter:window");
907 if (artifact != (const char *) NULL)
912 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
913 if ((UndefinedFilter < option) && (option < SentinelFilter))
915 filter_type= IfMagickTrue(cylindrical) ? JincFilter
917 window_type=(FilterTypes) option;
922 /* Assign the real functions to use for the filters selected. */
923 resize_filter->filter=filters[filter_type].function;
924 resize_filter->support=filters[filter_type].support;
925 resize_filter->window=filters[window_type].function;
926 resize_filter->scale=filters[window_type].scale;
927 resize_filter->signature=MagickSignature;
929 /* Filter Modifications for orthogonal/cylindrical usage */
930 if (cylindrical != MagickFalse)
934 /* Support for Cylindrical Box should be sqrt(2)/2 */
935 resize_filter->support=(double) MagickSQ1_2;
938 case LanczosSharpFilter:
940 case Lanczos2SharpFilter:
941 case LanczosRadiusFilter:
942 resize_filter->filter=filters[JincFilter].function;
943 resize_filter->window=filters[JincFilter].function;
944 resize_filter->scale=filters[JincFilter].scale;
945 /* number of lobes (support window size) remain unchanged */
950 /* Global Sharpening (regardless of orthoginal/cylindrical) */
953 case LanczosSharpFilter:
954 resize_filter->blur *= 0.9812505644269356;
956 case Lanczos2SharpFilter:
957 resize_filter->blur *= 0.9549963639785485;
959 /* case LanczosRadius: blur adjust is done after lobes */
965 Expert Option Modifications.
968 /* User Gaussian Sigma Override - no support change */
969 if ((resize_filter->filter == Gaussian) ||
970 (resize_filter->window == Gaussian) ) {
971 value=0.5; /* guassian sigma default, half pixel */
972 artifact=GetImageArtifact(image,"filter:sigma");
973 if (artifact != (const char *) NULL)
974 value=StringToDouble(artifact,(char **) NULL);
975 /* Define coefficents for Gaussian */
976 resize_filter->coefficient[0]=value; /* note sigma too */
977 resize_filter->coefficient[1]=PerceptibleReciprocal(2.0*value*value); /* sigma scaling */
978 resize_filter->coefficient[2]=PerceptibleReciprocal(Magick2PI*value*value);
979 /* normalization - not actually needed or used! */
981 resize_filter->support *= 2*value; /* increase support linearly */
984 /* User Kaiser Alpha Override - no support change */
985 if ((resize_filter->filter == Kaiser) ||
986 (resize_filter->window == Kaiser) ) {
987 value=6.5; /* default beta value for Kaiser bessel windowing function */
988 artifact=GetImageArtifact(image,"filter:alpha"); /* FUTURE: depreciate */
989 if (artifact != (const char *) NULL)
990 value=StringToDouble(artifact,(char **) NULL);
991 artifact=GetImageArtifact(image,"filter:kaiser-beta");
992 if (artifact != (const char *) NULL)
993 value=StringToDouble(artifact,(char **) NULL);
994 artifact=GetImageArtifact(image,"filter:kaiser-alpha");
995 if (artifact != (const char *) NULL)
996 value=StringToDouble(artifact,(char **) NULL)*MagickPI;
997 /* Define coefficents for Kaiser Windowing Function */
998 resize_filter->coefficient[0]=value; /* alpha */
999 resize_filter->coefficient[1]=PerceptibleReciprocal(I0(value));
1003 /* Support Overrides */
1004 artifact=GetImageArtifact(image,"filter:lobes");
1005 if (artifact != (const char *) NULL)
1010 lobes=(ssize_t) StringToLong(artifact);
1013 resize_filter->support=(double) lobes;
1015 if (resize_filter->filter == Jinc)
1018 Convert a Jinc function lobes value to a real support value.
1020 if (resize_filter->support > 16)
1021 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
1023 resize_filter->support=jinc_zeros[((long) resize_filter->support)-1];
1025 Blur this filter so support is a integer value (lobes dependant).
1027 if (filter_type == LanczosRadiusFilter)
1028 resize_filter->blur*=floor(resize_filter->support)/
1029 resize_filter->support;
1032 Expert blur override.
1034 artifact=GetImageArtifact(image,"filter:blur");
1035 if (artifact != (const char *) NULL)
1036 resize_filter->blur*=StringToDouble(artifact,(char **) NULL);
1037 if (resize_filter->blur < MagickEpsilon)
1038 resize_filter->blur=(double) MagickEpsilon;
1040 Expert override of the support setting.
1042 artifact=GetImageArtifact(image,"filter:support");
1043 if (artifact != (const char *) NULL)
1044 resize_filter->support=fabs(StringToDouble(artifact,(char **) NULL));
1046 Scale windowing function separately to the support 'clipping' window
1047 that calling operator is planning to actually use. (Expert override)
1049 resize_filter->window_support=resize_filter->support; /* default */
1050 artifact=GetImageArtifact(image,"filter:win-support");
1051 if (artifact != (const char *) NULL)
1052 resize_filter->window_support=fabs(StringToDouble(artifact,(char **) NULL));
1054 Adjust window function scaling to match windowing support for weighting
1055 function. This avoids a division on every filter call.
1057 resize_filter->scale/=resize_filter->window_support;
1059 * Set Cubic Spline B,C values, calculate Cubic coefficients.
1063 if ((resize_filter->filter == CubicBC) ||
1064 (resize_filter->window == CubicBC) )
1066 B=filters[filter_type].B;
1067 C=filters[filter_type].C;
1068 if (filters[window_type].function == CubicBC)
1070 B=filters[window_type].B;
1071 C=filters[window_type].C;
1073 artifact=GetImageArtifact(image,"filter:b");
1074 if (artifact != (const char *) NULL)
1076 B=StringToDouble(artifact,(char **) NULL);
1077 C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
1078 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
1079 if (artifact != (const char *) NULL)
1080 C=StringToDouble(artifact,(char **) NULL);
1084 artifact=GetImageArtifact(image,"filter:c");
1085 if (artifact != (const char *) NULL)
1087 C=StringToDouble(artifact,(char **) NULL);
1088 B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
1096 Convert B,C values into Cubic Coefficents. See CubicBC().
1098 resize_filter->coefficient[0]=1.0-(1.0/3.0)*B;
1099 resize_filter->coefficient[1]=-3.0+twoB+C;
1100 resize_filter->coefficient[2]=2.0-1.5*B-C;
1101 resize_filter->coefficient[3]=(4.0/3.0)*B+4.0*C;
1102 resize_filter->coefficient[4]=-8.0*C-twoB;
1103 resize_filter->coefficient[5]=B+5.0*C;
1104 resize_filter->coefficient[6]=(-1.0/6.0)*B-C;
1109 Expert Option Request for verbose details of the resulting filter.
1111 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1115 if (IfStringTrue(GetImageArtifact(image,"filter:verbose")))
1122 Set the weighting function properly when the weighting function
1123 may not exactly match the filter of the same name. EG: a Point
1124 filter is really uses a Box weighting function with a different
1125 support than is typically used.
1127 if (resize_filter->filter == Box) filter_type=BoxFilter;
1128 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1129 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1130 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1131 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1132 if (resize_filter->window == Box) window_type=BoxFilter;
1133 if (resize_filter->window == Sinc) window_type=SincFilter;
1134 if (resize_filter->window == SincFast) window_type=SincFastFilter;
1135 if (resize_filter->window == Jinc) window_type=JincFilter;
1136 if (resize_filter->window == CubicBC) window_type=CubicFilter;
1138 Report Filter Details.
1140 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1141 (void) FormatLocaleFile(stdout,
1142 "# Resampling Filter (for graphing)\n#\n");
1143 (void) FormatLocaleFile(stdout,"# filter = %s\n",
1144 CommandOptionToMnemonic(MagickFilterOptions,filter_type));
1145 (void) FormatLocaleFile(stdout,"# window = %s\n",
1146 CommandOptionToMnemonic(MagickFilterOptions,window_type));
1147 (void) FormatLocaleFile(stdout,"# support = %.*g\n",
1148 GetMagickPrecision(),(double) resize_filter->support);
1149 (void) FormatLocaleFile(stdout,"# window-support = %.*g\n",
1150 GetMagickPrecision(),(double) resize_filter->window_support);
1151 (void) FormatLocaleFile(stdout,"# scale-blur = %.*g\n",
1152 GetMagickPrecision(),(double)resize_filter->blur);
1153 if ((filter_type == GaussianFilter) || (window_type == GaussianFilter))
1154 (void) FormatLocaleFile(stdout,"# gaussian-sigma = %.*g\n",
1155 GetMagickPrecision(),(double)resize_filter->coefficient[0]);
1156 if ( filter_type == KaiserFilter || window_type == KaiserFilter )
1157 (void) FormatLocaleFile(stdout,"# kaiser-beta = %.*g\n",
1158 GetMagickPrecision(),(double)resize_filter->coefficient[0]);
1159 (void) FormatLocaleFile(stdout,"# practical-support = %.*g\n",
1160 GetMagickPrecision(), (double)support);
1161 if ( filter_type == CubicFilter || window_type == CubicFilter )
1162 (void) FormatLocaleFile(stdout,"# B,C = %.*g,%.*g\n",
1163 GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
1164 (void) FormatLocaleFile(stdout,"\n");
1166 Output values of resulting filter graph -- for graphing filter result.
1168 for (x=0.0; x <= support; x+=0.01f)
1169 (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",x,
1170 GetMagickPrecision(),(double)
1171 GetResizeFilterWeight(resize_filter,x));
1173 A final value so gnuplot can graph the 'stop' properly.
1175 (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",support,
1176 GetMagickPrecision(),0.0);
1178 /* Output the above once only for each image - remove setting */
1179 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1180 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1183 return(resize_filter);
1187 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1191 % A d a p t i v e R e s i z e I m a g e %
1195 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1197 % AdaptiveResizeImage() adaptively resize image with pixel resampling.
1199 % This is shortcut function for a fast interpolative resize using mesh
1200 % interpolation. It works well for small resizes of less than +/- 50%
1201 % of the original image size. For larger resizing on images a full
1202 % filtered and slower resize function should be used instead.
1204 % The format of the AdaptiveResizeImage method is:
1206 % Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1207 % const size_t rows,ExceptionInfo *exception)
1209 % A description of each parameter follows:
1211 % o image: the image.
1213 % o columns: the number of columns in the resized image.
1215 % o rows: the number of rows in the resized image.
1217 % o exception: return any errors or warnings in this structure.
1220 MagickExport Image *AdaptiveResizeImage(const Image *image,
1221 const size_t columns,const size_t rows,ExceptionInfo *exception)
1226 resize_image=InterpolativeResizeImage(image,columns,rows,MeshInterpolatePixel,
1228 return(resize_image);
1232 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1236 + B e s s e l O r d e r O n e %
1240 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1242 % BesselOrderOne() computes the Bessel function of x of the first kind of
1243 % order 0. This is used to create the Jinc() filter function below.
1245 % Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1251 % j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1253 % where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1255 % cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1256 % = 1/sqrt(2) * (sin(x) - cos(x))
1257 % sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1258 % = -1/sqrt(2) * (sin(x) + cos(x))
1260 % The format of the BesselOrderOne method is:
1262 % double BesselOrderOne(double x)
1264 % A description of each parameter follows:
1266 % o x: double value.
1271 static double I0(double x)
1282 Zeroth order Bessel function of the first kind.
1287 for (i=2; t > MagickEpsilon; i++)
1290 t*=y/((double) i*i);
1296 static double J1(double x)
1308 0.581199354001606143928050809e+21,
1309 -0.6672106568924916298020941484e+20,
1310 0.2316433580634002297931815435e+19,
1311 -0.3588817569910106050743641413e+17,
1312 0.2908795263834775409737601689e+15,
1313 -0.1322983480332126453125473247e+13,
1314 0.3413234182301700539091292655e+10,
1315 -0.4695753530642995859767162166e+7,
1316 0.270112271089232341485679099e+4
1320 0.11623987080032122878585294e+22,
1321 0.1185770712190320999837113348e+20,
1322 0.6092061398917521746105196863e+17,
1323 0.2081661221307607351240184229e+15,
1324 0.5243710262167649715406728642e+12,
1325 0.1013863514358673989967045588e+10,
1326 0.1501793594998585505921097578e+7,
1327 0.1606931573481487801970916749e+4,
1333 for (i=7; i >= 0; i--)
1342 static double P1(double x)
1354 0.352246649133679798341724373e+5,
1355 0.62758845247161281269005675e+5,
1356 0.313539631109159574238669888e+5,
1357 0.49854832060594338434500455e+4,
1358 0.2111529182853962382105718e+3,
1359 0.12571716929145341558495e+1
1363 0.352246649133679798068390431e+5,
1364 0.626943469593560511888833731e+5,
1365 0.312404063819041039923015703e+5,
1366 0.4930396490181088979386097e+4,
1367 0.2030775189134759322293574e+3,
1373 for (i=4; i >= 0; i--)
1375 p=p*(8.0/x)*(8.0/x)+Pone[i];
1376 q=q*(8.0/x)*(8.0/x)+Qone[i];
1382 static double Q1(double x)
1394 0.3511751914303552822533318e+3,
1395 0.7210391804904475039280863e+3,
1396 0.4259873011654442389886993e+3,
1397 0.831898957673850827325226e+2,
1398 0.45681716295512267064405e+1,
1399 0.3532840052740123642735e-1
1403 0.74917374171809127714519505e+4,
1404 0.154141773392650970499848051e+5,
1405 0.91522317015169922705904727e+4,
1406 0.18111867005523513506724158e+4,
1407 0.1038187585462133728776636e+3,
1413 for (i=4; i >= 0; i--)
1415 p=p*(8.0/x)*(8.0/x)+Pone[i];
1416 q=q*(8.0/x)*(8.0/x)+Qone[i];
1421 static double BesselOrderOne(double x)
1434 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1435 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1447 + D e s t r o y R e s i z e F i l t e r %
1451 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1453 % DestroyResizeFilter() destroy the resize filter.
1455 % The format of the DestroyResizeFilter method is:
1457 % ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1459 % A description of each parameter follows:
1461 % o resize_filter: the resize filter.
1464 MagickPrivate ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1466 assert(resize_filter != (ResizeFilter *) NULL);
1467 assert(resize_filter->signature == MagickSignature);
1468 resize_filter->signature=(~MagickSignature);
1469 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1470 return(resize_filter);
1474 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1478 + G e t R e s i z e F i l t e r S u p p o r t %
1482 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1484 % GetResizeFilterSupport() return the current support window size for this
1485 % filter. Note that this may have been enlarged by filter:blur factor.
1487 % The format of the GetResizeFilterSupport method is:
1489 % double GetResizeFilterSupport(const ResizeFilter *resize_filter)
1491 % A description of each parameter follows:
1493 % o filter: Image filter to use.
1496 MagickPrivate double GetResizeFilterSupport(
1497 const ResizeFilter *resize_filter)
1499 assert(resize_filter != (ResizeFilter *) NULL);
1500 assert(resize_filter->signature == MagickSignature);
1501 return(resize_filter->support*resize_filter->blur);
1505 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1509 + G e t R e s i z e F i l t e r W e i g h t %
1513 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1515 % GetResizeFilterWeight evaluates the specified resize filter at the point x
1516 % which usally lies between zero and the filters current 'support' and
1517 % returns the weight of the filter function at that point.
1519 % The format of the GetResizeFilterWeight method is:
1521 % double GetResizeFilterWeight(const ResizeFilter *resize_filter,
1524 % A description of each parameter follows:
1526 % o filter: the filter type.
1531 MagickPrivate double GetResizeFilterWeight(const ResizeFilter *resize_filter,
1540 Windowing function - scale the weighting filter by this amount.
1542 assert(resize_filter != (ResizeFilter *) NULL);
1543 assert(resize_filter->signature == MagickSignature);
1544 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
1545 if ((resize_filter->window_support < MagickEpsilon) ||
1546 (resize_filter->window == Box))
1547 scale=1.0; /* Point or Box Filter -- avoid division by zero */
1550 scale=resize_filter->scale;
1551 scale=resize_filter->window(x_blur*scale,resize_filter);
1553 weight=scale*resize_filter->filter(x_blur,resize_filter);
1558 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1562 % I n t e r p o l a t i v e R e s i z e I m a g e %
1566 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1568 % InterpolativeResizeImage() resizes an image using the specified
1569 % interpolation method.
1571 % The format of the InterpolativeResizeImage method is:
1573 % Image *InterpolativeResizeImage(const Image *image,const size_t columns,
1574 % const size_t rows,const PixelInterpolateMethod method,
1575 % ExceptionInfo *exception)
1577 % A description of each parameter follows:
1579 % o image: the image.
1581 % o columns: the number of columns in the resized image.
1583 % o rows: the number of rows in the resized image.
1585 % o method: the pixel interpolation method.
1587 % o exception: return any errors or warnings in this structure.
1590 MagickExport Image *InterpolativeResizeImage(const Image *image,
1591 const size_t columns,const size_t rows,const PixelInterpolateMethod method,
1592 ExceptionInfo *exception)
1594 #define InterpolativeResizeImageTag "Resize/Image"
1616 Interpolatively resize image.
1618 assert(image != (const Image *) NULL);
1619 assert(image->signature == MagickSignature);
1620 if (image->debug != MagickFalse)
1621 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1622 assert(exception != (ExceptionInfo *) NULL);
1623 assert(exception->signature == MagickSignature);
1624 if ((columns == 0) || (rows == 0))
1625 return((Image *) NULL);
1626 if ((columns == image->columns) && (rows == image->rows))
1627 return(CloneImage(image,0,0,MagickTrue,exception));
1628 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1629 if (resize_image == (Image *) NULL)
1630 return((Image *) NULL);
1631 if (SetImageStorageClass(resize_image,DirectClass,exception) == MagickFalse)
1633 resize_image=DestroyImage(resize_image);
1634 return((Image *) NULL);
1638 image_view=AcquireVirtualCacheView(image,exception);
1639 resize_view=AcquireAuthenticCacheView(resize_image,exception);
1640 scale.x=(double) image->columns/resize_image->columns;
1641 scale.y=(double) image->rows/resize_image->rows;
1642 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1643 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1644 magick_threads(image,resize_image,resize_image->rows,1)
1646 for (y=0; y < (ssize_t) resize_image->rows; y++)
1657 if (status == MagickFalse)
1659 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1661 if (q == (Quantum *) NULL)
1663 offset.y=((double) y+0.5)*scale.y-0.5;
1664 for (x=0; x < (ssize_t) resize_image->columns; x++)
1669 if (GetPixelReadMask(resize_image,q) == 0)
1671 q+=GetPixelChannels(resize_image);
1674 for (i=0; i < (ssize_t) GetPixelChannels(resize_image); i++)
1683 channel=GetPixelChannelChannel(image,i);
1684 traits=GetPixelChannelTraits(image,channel);
1685 resize_traits=GetPixelChannelTraits(resize_image,channel);
1686 if ((traits == UndefinedPixelTrait) ||
1687 (resize_traits == UndefinedPixelTrait))
1689 offset.x=((double) x+0.5)*scale.x-0.5;
1690 status=InterpolatePixelChannels(image,image_view,resize_image,method,
1691 offset.x,offset.y,q,exception);
1693 q+=GetPixelChannels(resize_image);
1695 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1697 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1702 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1703 #pragma omp critical (MagickCore_InterpolativeResizeImage)
1705 proceed=SetImageProgress(image,InterpolativeResizeImageTag,progress++,
1707 if (proceed == MagickFalse)
1711 resize_view=DestroyCacheView(resize_view);
1712 image_view=DestroyCacheView(image_view);
1713 if (status == MagickFalse)
1714 resize_image=DestroyImage(resize_image);
1715 return(resize_image);
1717 #if defined(MAGICKCORE_LQR_DELEGATE)
1720 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1724 % L i q u i d R e s c a l e I m a g e %
1728 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1730 % LiquidRescaleImage() rescales image with seam carving.
1732 % The format of the LiquidRescaleImage method is:
1734 % Image *LiquidRescaleImage(const Image *image,const size_t columns,
1735 % const size_t rows,const double delta_x,const double rigidity,
1736 % ExceptionInfo *exception)
1738 % A description of each parameter follows:
1740 % o image: the image.
1742 % o columns: the number of columns in the rescaled image.
1744 % o rows: the number of rows in the rescaled image.
1746 % o delta_x: maximum seam transversal step (0 means straight seams).
1748 % o rigidity: introduce a bias for non-straight seams (typically 0).
1750 % o exception: return any errors or warnings in this structure.
1753 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1754 const size_t rows,const double delta_x,const double rigidity,
1755 ExceptionInfo *exception)
1757 #define LiquidRescaleImageTag "Rescale/Image"
1793 Liquid rescale image.
1795 assert(image != (const Image *) NULL);
1796 assert(image->signature == MagickSignature);
1797 if (image->debug != MagickFalse)
1798 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1799 assert(exception != (ExceptionInfo *) NULL);
1800 assert(exception->signature == MagickSignature);
1801 if ((columns == 0) || (rows == 0))
1802 return((Image *) NULL);
1803 if ((columns == image->columns) && (rows == image->rows))
1804 return(CloneImage(image,0,0,MagickTrue,exception));
1805 if ((columns <= 2) || (rows <= 2))
1806 return(ResizeImage(image,columns,rows,image->filter,exception));
1807 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1817 Honor liquid resize size limitations.
1819 for (width=image->columns; columns >= (2*width-1); width*=2) ;
1820 for (height=image->rows; rows >= (2*height-1); height*=2) ;
1821 resize_image=ResizeImage(image,width,height,image->filter,exception);
1822 if (resize_image == (Image *) NULL)
1823 return((Image *) NULL);
1824 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1825 rigidity,exception);
1826 resize_image=DestroyImage(resize_image);
1827 return(rescale_image);
1829 pixel_info=AcquireVirtualMemory(image->columns,image->rows*
1830 GetPixelChannels(image)*sizeof(*pixels));
1831 if (pixel_info == (MemoryInfo *) NULL)
1832 return((Image *) NULL);
1833 pixels=(gfloat *) GetVirtualMemoryBlob(pixel_info);
1836 image_view=AcquireVirtualCacheView(image,exception);
1837 for (y=0; y < (ssize_t) image->rows; y++)
1839 register const Quantum
1845 if (status == MagickFalse)
1847 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1848 if (p == (const Quantum *) NULL)
1853 for (x=0; x < (ssize_t) image->columns; x++)
1858 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1859 *q++=QuantumScale*p[i];
1860 p+=GetPixelChannels(image);
1863 image_view=DestroyCacheView(image_view);
1864 carver=lqr_carver_new_ext(pixels,(int) image->columns,(int) image->rows,
1865 (int) GetPixelChannels(image),LQR_COLDEPTH_32F);
1866 if (carver == (LqrCarver *) NULL)
1868 pixel_info=RelinquishVirtualMemory(pixel_info);
1869 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1871 lqr_carver_set_preserve_input_image(carver);
1872 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1873 lqr_status=lqr_carver_resize(carver,(int) columns,(int) rows);
1875 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1876 lqr_carver_get_height(carver),MagickTrue,exception);
1877 if (rescale_image == (Image *) NULL)
1879 pixel_info=RelinquishVirtualMemory(pixel_info);
1880 return((Image *) NULL);
1882 if (SetImageStorageClass(rescale_image,DirectClass,exception) == MagickFalse)
1884 pixel_info=RelinquishVirtualMemory(pixel_info);
1885 rescale_image=DestroyImage(rescale_image);
1886 return((Image *) NULL);
1888 rescale_view=AcquireAuthenticCacheView(rescale_image,exception);
1889 (void) lqr_carver_scan_reset(carver);
1890 while (lqr_carver_scan_ext(carver,&x_offset,&y_offset,(void **) &packet) != 0)
1898 q=QueueCacheViewAuthenticPixels(rescale_view,x_offset,y_offset,1,1,
1900 if (q == (Quantum *) NULL)
1902 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1911 channel=GetPixelChannelChannel(image,i);
1912 traits=GetPixelChannelTraits(image,channel);
1913 rescale_traits=GetPixelChannelTraits(rescale_image,channel);
1914 if ((traits == UndefinedPixelTrait) ||
1915 (rescale_traits == UndefinedPixelTrait))
1917 SetPixelChannel(rescale_image,channel,ClampToQuantum(QuantumRange*
1920 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
1923 rescale_view=DestroyCacheView(rescale_view);
1924 pixel_info=RelinquishVirtualMemory(pixel_info);
1925 lqr_carver_destroy(carver);
1926 return(rescale_image);
1929 MagickExport Image *LiquidRescaleImage(const Image *image,
1930 const size_t magick_unused(columns),const size_t magick_unused(rows),
1931 const double magick_unused(delta_x),const double magick_unused(rigidity),
1932 ExceptionInfo *exception)
1934 assert(image != (const Image *) NULL);
1935 assert(image->signature == MagickSignature);
1936 if (image->debug != MagickFalse)
1937 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1938 assert(exception != (ExceptionInfo *) NULL);
1939 assert(exception->signature == MagickSignature);
1940 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1941 "DelegateLibrarySupportNotBuiltIn","'%s' (LQR)",image->filename);
1942 return((Image *) NULL);
1947 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1951 % M a g n i f y I m a g e %
1955 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1957 % MagnifyImage() doubles the size of the image with a pixel art scaling
1960 % The format of the MagnifyImage method is:
1962 % Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1964 % A description of each parameter follows:
1966 % o image: the image.
1968 % o exception: return any errors or warnings in this structure.
1971 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1973 #define MagnifyImageTag "Magnify/Image"
1992 Initialize magnified image attributes.
1994 assert(image != (const Image *) NULL);
1995 assert(image->signature == MagickSignature);
1996 if (image->debug != MagickFalse)
1997 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1998 assert(exception != (ExceptionInfo *) NULL);
1999 assert(exception->signature == MagickSignature);
2000 magnify_image=CloneImage(image,2*image->columns,2*image->rows,MagickTrue,
2002 if (magnify_image == (Image *) NULL)
2003 return((Image *) NULL);
2009 image_view=AcquireVirtualCacheView(image,exception);
2010 magnify_view=AcquireAuthenticCacheView(magnify_image,exception);
2011 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2012 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2013 magick_threads(image,magnify_image,image->rows,1)
2015 for (y=0; y < (ssize_t) image->rows; y++)
2023 if (status == MagickFalse)
2025 q=QueueCacheViewAuthenticPixels(magnify_view,0,2*y,magnify_image->columns,2,
2027 if (q == (Quantum *) NULL)
2033 Magnify this row of pixels.
2035 for (x=0; x < (ssize_t) image->columns; x++)
2040 register const Quantum
2052 p=GetCacheViewVirtualPixels(image_view,x-1,y-1,3,3,exception);
2053 if (p == (const Quantum *) NULL)
2058 channels=GetPixelChannels(image);
2059 for (i=0; i < 9; i++)
2060 intensity[i]=GetPixelIntensity(image,p+i*channels);
2062 if ((fabs(intensity[1]-intensity[7]) < MagickEpsilon) ||
2063 (fabs(intensity[3]-intensity[5]) < MagickEpsilon))
2068 for (i=0; i < (ssize_t) channels; i++)
2069 r[i]=p[4*channels+i];
2070 r+=GetPixelChannels(magnify_image);
2071 for (i=0; i < (ssize_t) channels; i++)
2072 r[i]=p[4*channels+i];
2073 r+=(magnify_image->columns-1)*GetPixelChannels(magnify_image);
2074 for (i=0; i < (ssize_t) channels; i++)
2075 r[i]=p[4*channels+i];
2076 r+=GetPixelChannels(magnify_image);
2077 for (i=0; i < (ssize_t) channels; i++)
2078 r[i]=p[4*channels+i];
2083 Selectively clone pixel.
2085 if (fabs(intensity[1]-intensity[3]) < MagickEpsilon)
2086 for (i=0; i < (ssize_t) channels; i++)
2087 r[i]=p[3*channels+i];
2089 for (i=0; i < (ssize_t) channels; i++)
2090 r[i]=p[4*channels+i];
2091 r+=GetPixelChannels(magnify_image);
2092 if (fabs(intensity[1]-intensity[5]) < MagickEpsilon)
2093 for (i=0; i < (ssize_t) channels; i++)
2094 r[i]=p[5*channels+i];
2096 for (i=0; i < (ssize_t) channels; i++)
2097 r[i]=p[4*channels+i];
2098 r+=(magnify_image->columns-1)*GetPixelChannels(magnify_image);
2099 if (fabs(intensity[3]-intensity[7]) < MagickEpsilon)
2100 for (i=0; i < (ssize_t) channels; i++)
2101 r[i]=p[3*channels+i];
2103 for (i=0; i < (ssize_t) channels; i++)
2104 r[i]=p[4*channels+i];
2105 r+=GetPixelChannels(magnify_image);
2106 if (fabs(intensity[5]-intensity[7]) < MagickEpsilon)
2107 for (i=0; i < (ssize_t) channels; i++)
2108 r[i]=p[5*channels+i];
2110 for (i=0; i < (ssize_t) channels; i++)
2111 r[i]=p[4*channels+i];
2113 q+=2*GetPixelChannels(magnify_image);
2115 if (SyncCacheViewAuthenticPixels(magnify_view,exception) == MagickFalse)
2117 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2122 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2123 #pragma omp critical (MagickCore_MagnifyImage)
2125 proceed=SetImageProgress(image,MagnifyImageTag,progress++,image->rows);
2126 if (proceed == MagickFalse)
2130 magnify_view=DestroyCacheView(magnify_view);
2131 image_view=DestroyCacheView(image_view);
2132 if (status == MagickFalse)
2133 magnify_image=DestroyImage(magnify_image);
2134 return(magnify_image);
2138 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2142 % M i n i f y I m a g e %
2146 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2148 % MinifyImage() is a convenience method that scales an image proportionally to
2151 % The format of the MinifyImage method is:
2153 % Image *MinifyImage(const Image *image,ExceptionInfo *exception)
2155 % A description of each parameter follows:
2157 % o image: the image.
2159 % o exception: return any errors or warnings in this structure.
2162 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
2167 assert(image != (Image *) NULL);
2168 assert(image->signature == MagickSignature);
2169 if (image->debug != MagickFalse)
2170 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2171 assert(exception != (ExceptionInfo *) NULL);
2172 assert(exception->signature == MagickSignature);
2173 minify_image=ResizeImage(image,image->columns/2,image->rows/2,SplineFilter,
2175 return(minify_image);
2179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2183 % R e s a m p l e I m a g e %
2187 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2189 % ResampleImage() resize image in terms of its pixel size, so that when
2190 % displayed at the given resolution it will be the same size in terms of
2191 % real world units as the original image at the original resolution.
2193 % The format of the ResampleImage method is:
2195 % Image *ResampleImage(Image *image,const double x_resolution,
2196 % const double y_resolution,const FilterTypes filter,
2197 % ExceptionInfo *exception)
2199 % A description of each parameter follows:
2201 % o image: the image to be resized to fit the given resolution.
2203 % o x_resolution: the new image x resolution.
2205 % o y_resolution: the new image y resolution.
2207 % o filter: Image filter to use.
2209 % o exception: return any errors or warnings in this structure.
2212 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
2213 const double y_resolution,const FilterTypes filter,ExceptionInfo *exception)
2215 #define ResampleImageTag "Resample/Image"
2225 Initialize sampled image attributes.
2227 assert(image != (const Image *) NULL);
2228 assert(image->signature == MagickSignature);
2229 if (image->debug != MagickFalse)
2230 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2231 assert(exception != (ExceptionInfo *) NULL);
2232 assert(exception->signature == MagickSignature);
2233 width=(size_t) (x_resolution*image->columns/(image->resolution.x == 0.0 ?
2234 72.0 : image->resolution.x)+0.5);
2235 height=(size_t) (y_resolution*image->rows/(image->resolution.y == 0.0 ?
2236 72.0 : image->resolution.y)+0.5);
2237 resample_image=ResizeImage(image,width,height,filter,exception);
2238 if (resample_image != (Image *) NULL)
2240 resample_image->resolution.x=x_resolution;
2241 resample_image->resolution.y=y_resolution;
2243 return(resample_image);
2247 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2251 % R e s i z e I m a g e %
2255 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2257 % ResizeImage() scales an image to the desired dimensions, using the given
2258 % filter (see AcquireFilterInfo()).
2260 % If an undefined filter is given the filter defaults to Mitchell for a
2261 % colormapped image, a image with a matte channel, or if the image is
2262 % enlarged. Otherwise the filter defaults to a Lanczos.
2264 % ResizeImage() was inspired by Paul Heckbert's "zoom" program.
2266 % The format of the ResizeImage method is:
2268 % Image *ResizeImage(Image *image,const size_t columns,const size_t rows,
2269 % const FilterTypes filter,ExceptionInfo *exception)
2271 % A description of each parameter follows:
2273 % o image: the image.
2275 % o columns: the number of columns in the scaled image.
2277 % o rows: the number of rows in the scaled image.
2279 % o filter: Image filter to use.
2281 % o exception: return any errors or warnings in this structure.
2285 typedef struct _ContributionInfo
2294 static ContributionInfo **DestroyContributionThreadSet(
2295 ContributionInfo **contribution)
2300 assert(contribution != (ContributionInfo **) NULL);
2301 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
2302 if (contribution[i] != (ContributionInfo *) NULL)
2303 contribution[i]=(ContributionInfo *) RelinquishAlignedMemory(
2305 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
2306 return(contribution);
2309 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
2320 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
2321 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
2322 sizeof(*contribution));
2323 if (contribution == (ContributionInfo **) NULL)
2324 return((ContributionInfo **) NULL);
2325 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
2326 for (i=0; i < (ssize_t) number_threads; i++)
2328 contribution[i]=(ContributionInfo *) MagickAssumeAligned(
2329 AcquireAlignedMemory(count,sizeof(**contribution)));
2330 if (contribution[i] == (ContributionInfo *) NULL)
2331 return(DestroyContributionThreadSet(contribution));
2333 return(contribution);
2336 static inline double MagickMax(const double x,const double y)
2343 static inline double MagickMin(const double x,const double y)
2350 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2351 const Image *image,Image *resize_image,const double x_factor,
2352 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2354 #define ResizeImageTag "Resize/Image"
2364 **restrict contributions;
2377 Apply filter to resize horizontally from image to resize image.
2379 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
2380 support=scale*GetResizeFilterSupport(resize_filter);
2381 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2382 if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
2383 return(MagickFalse);
2387 Support too small even for nearest neighbour: Reduce to point sampling.
2389 support=(double) 0.5;
2392 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2393 if (contributions == (ContributionInfo **) NULL)
2395 (void) ThrowMagickException(exception,GetMagickModule(),
2396 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2397 return(MagickFalse);
2400 scale=PerceptibleReciprocal(scale);
2401 image_view=AcquireVirtualCacheView(image,exception);
2402 resize_view=AcquireAuthenticCacheView(resize_image,exception);
2403 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2404 #pragma omp parallel for schedule(static,4) shared(status) \
2405 magick_threads(image,resize_image,resize_image->columns,1)
2407 for (x=0; x < (ssize_t) resize_image->columns; x++)
2413 register const Quantum
2416 register ContributionInfo
2417 *restrict contribution;
2430 if (status == MagickFalse)
2432 bisect=(double) (x+0.5)/x_factor+MagickEpsilon;
2433 start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
2434 stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->columns);
2436 contribution=contributions[GetOpenMPThreadId()];
2437 for (n=0; n < (stop-start); n++)
2439 contribution[n].pixel=start+n;
2440 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2441 ((double) (start+n)-bisect+0.5));
2442 density+=contribution[n].weight;
2444 if ((density != 0.0) && (density != 1.0))
2452 density=PerceptibleReciprocal(density);
2453 for (i=0; i < n; i++)
2454 contribution[i].weight*=density;
2456 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2457 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
2458 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2460 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2465 for (y=0; y < (ssize_t) resize_image->rows; y++)
2470 for (i=0; i < (ssize_t) GetPixelChannels(resize_image); i++)
2490 channel=GetPixelChannelChannel(image,i);
2491 traits=GetPixelChannelTraits(image,channel);
2492 resize_traits=GetPixelChannelTraits(resize_image,channel);
2493 if ((traits == UndefinedPixelTrait) ||
2494 (resize_traits == UndefinedPixelTrait))
2496 if (((resize_traits & CopyPixelTrait) != 0) ||
2497 (GetPixelReadMask(resize_image,q) == 0))
2499 j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
2501 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2502 (contribution[j-start].pixel-contribution[0].pixel);
2503 SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
2508 if ((resize_traits & BlendPixelTrait) == 0)
2513 for (j=0; j < n; j++)
2515 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2516 (contribution[j].pixel-contribution[0].pixel);
2517 alpha=contribution[j].weight;
2518 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2520 SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
2527 for (j=0; j < n; j++)
2529 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2530 (contribution[j].pixel-contribution[0].pixel);
2531 alpha=contribution[j].weight*QuantumScale*
2532 GetPixelAlpha(image,p+k*GetPixelChannels(image));
2533 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2536 gamma=PerceptibleReciprocal(gamma);
2537 SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
2539 q+=GetPixelChannels(resize_image);
2541 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2543 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2548 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2549 #pragma omp critical (MagickCore_HorizontalFilter)
2551 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2552 if (proceed == MagickFalse)
2556 resize_view=DestroyCacheView(resize_view);
2557 image_view=DestroyCacheView(image_view);
2558 contributions=DestroyContributionThreadSet(contributions);
2562 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2563 const Image *image,Image *resize_image,const double y_factor,
2564 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2574 **restrict contributions;
2587 Apply filter to resize vertically from image to resize image.
2589 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
2590 support=scale*GetResizeFilterSupport(resize_filter);
2591 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2592 if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
2593 return(MagickFalse);
2597 Support too small even for nearest neighbour: Reduce to point sampling.
2599 support=(double) 0.5;
2602 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2603 if (contributions == (ContributionInfo **) NULL)
2605 (void) ThrowMagickException(exception,GetMagickModule(),
2606 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2607 return(MagickFalse);
2610 scale=PerceptibleReciprocal(scale);
2611 image_view=AcquireVirtualCacheView(image,exception);
2612 resize_view=AcquireAuthenticCacheView(resize_image,exception);
2613 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2614 #pragma omp parallel for schedule(static,4) shared(status) \
2615 magick_threads(image,resize_image,resize_image->rows,1)
2617 for (y=0; y < (ssize_t) resize_image->rows; y++)
2623 register const Quantum
2626 register ContributionInfo
2627 *restrict contribution;
2640 if (status == MagickFalse)
2642 bisect=(double) (y+0.5)/y_factor+MagickEpsilon;
2643 start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
2644 stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->rows);
2646 contribution=contributions[GetOpenMPThreadId()];
2647 for (n=0; n < (stop-start); n++)
2649 contribution[n].pixel=start+n;
2650 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2651 ((double) (start+n)-bisect+0.5));
2652 density+=contribution[n].weight;
2654 if ((density != 0.0) && (density != 1.0))
2662 density=PerceptibleReciprocal(density);
2663 for (i=0; i < n; i++)
2664 contribution[i].weight*=density;
2666 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
2667 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2669 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2671 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2676 for (x=0; x < (ssize_t) resize_image->columns; x++)
2681 for (i=0; i < (ssize_t) GetPixelChannels(resize_image); i++)
2701 channel=GetPixelChannelChannel(image,i);
2702 traits=GetPixelChannelTraits(image,channel);
2703 resize_traits=GetPixelChannelTraits(resize_image,channel);
2704 if ((traits == UndefinedPixelTrait) ||
2705 (resize_traits == UndefinedPixelTrait))
2707 if (((resize_traits & CopyPixelTrait) != 0) ||
2708 (GetPixelReadMask(resize_image,q) == 0))
2710 j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
2712 k=(ssize_t) ((contribution[j-start].pixel-contribution[0].pixel)*
2714 SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
2719 if ((resize_traits & BlendPixelTrait) == 0)
2724 for (j=0; j < n; j++)
2726 k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
2728 alpha=contribution[j].weight;
2729 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2731 SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
2735 for (j=0; j < n; j++)
2737 k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
2739 alpha=contribution[j].weight*QuantumScale*GetPixelAlpha(image,p+k*
2740 GetPixelChannels(image));
2741 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2744 gamma=PerceptibleReciprocal(gamma);
2745 SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
2747 q+=GetPixelChannels(resize_image);
2749 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2751 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2756 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2757 #pragma omp critical (MagickCore_VerticalFilter)
2759 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2760 if (proceed == MagickFalse)
2764 resize_view=DestroyCacheView(resize_view);
2765 image_view=DestroyCacheView(image_view);
2766 contributions=DestroyContributionThreadSet(contributions);
2770 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2771 const size_t rows,const FilterTypes filter,ExceptionInfo *exception)
2797 Acquire resize image.
2799 assert(image != (Image *) NULL);
2800 assert(image->signature == MagickSignature);
2801 if (image->debug != MagickFalse)
2802 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2803 assert(exception != (ExceptionInfo *) NULL);
2804 assert(exception->signature == MagickSignature);
2805 if ((columns == 0) || (rows == 0))
2806 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2807 if ((columns == image->columns) && (rows == image->rows) &&
2808 (filter == UndefinedFilter))
2809 return(CloneImage(image,0,0,MagickTrue,exception));
2810 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2811 if (resize_image == (Image *) NULL)
2812 return(resize_image);
2814 Acquire resize filter.
2816 x_factor=(double) columns/(double) image->columns;
2817 y_factor=(double) rows/(double) image->rows;
2818 if (x_factor > y_factor)
2819 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2821 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2822 if (filter_image == (Image *) NULL)
2823 return(DestroyImage(resize_image));
2824 filter_type=LanczosFilter;
2825 if (filter != UndefinedFilter)
2828 if ((x_factor == 1.0) && (y_factor == 1.0))
2829 filter_type=PointFilter;
2831 if ((image->storage_class == PseudoClass) ||
2832 (image->alpha_trait == BlendPixelTrait) ||
2833 ((x_factor*y_factor) > 1.0))
2834 filter_type=MitchellFilter;
2835 resize_filter=AcquireResizeFilter(image,filter_type,MagickFalse,exception);
2840 if (x_factor > y_factor)
2842 span=(MagickSizeType) (filter_image->columns+rows);
2843 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
2845 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
2846 span,&offset,exception);
2850 span=(MagickSizeType) (filter_image->rows+columns);
2851 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
2853 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
2854 span,&offset,exception);
2859 filter_image=DestroyImage(filter_image);
2860 resize_filter=DestroyResizeFilter(resize_filter);
2861 if (status == MagickFalse)
2863 resize_image=DestroyImage(resize_image);
2864 return((Image *) NULL);
2866 resize_image->type=image->type;
2867 return(resize_image);
2871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2875 % S a m p l e I m a g e %
2879 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2881 % SampleImage() scales an image to the desired dimensions with pixel
2882 % sampling. Unlike other scaling methods, this method does not introduce
2883 % any additional color into the scaled image.
2885 % The format of the SampleImage method is:
2887 % Image *SampleImage(const Image *image,const size_t columns,
2888 % const size_t rows,ExceptionInfo *exception)
2890 % A description of each parameter follows:
2892 % o image: the image.
2894 % o columns: the number of columns in the sampled image.
2896 % o rows: the number of rows in the sampled image.
2898 % o exception: return any errors or warnings in this structure.
2901 MagickExport Image *SampleImage(const Image *image,const size_t columns,
2902 const size_t rows,ExceptionInfo *exception)
2904 #define SampleImageTag "Sample/Image"
2930 Initialize sampled image attributes.
2932 assert(image != (const Image *) NULL);
2933 assert(image->signature == MagickSignature);
2934 if (image->debug != MagickFalse)
2935 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2936 assert(exception != (ExceptionInfo *) NULL);
2937 assert(exception->signature == MagickSignature);
2938 if ((columns == 0) || (rows == 0))
2939 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2940 if ((columns == image->columns) && (rows == image->rows))
2941 return(CloneImage(image,0,0,MagickTrue,exception));
2942 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2943 if (sample_image == (Image *) NULL)
2944 return((Image *) NULL);
2946 Set the sampling offset, default is in the mid-point of sample regions.
2948 sample_offset.x=sample_offset.y=0.5-MagickEpsilon;
2953 value=GetImageArtifact(image,"sample:offset");
2954 if (value != (char *) NULL)
2962 (void) ParseGeometry(value,&geometry_info);
2963 flags=ParseGeometry(value,&geometry_info);
2964 sample_offset.x=sample_offset.y=geometry_info.rho/100.0-MagickEpsilon;
2965 if ((flags & SigmaValue) != 0)
2966 sample_offset.y=geometry_info.sigma/100.0-MagickEpsilon;
2970 Allocate scan line buffer and column offset buffers.
2972 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
2974 if (x_offset == (ssize_t *) NULL)
2976 sample_image=DestroyImage(sample_image);
2977 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2979 for (x=0; x < (ssize_t) sample_image->columns; x++)
2980 x_offset[x]=(ssize_t) ((((double) x+sample_offset.x)*image->columns)/
2981 sample_image->columns);
2987 image_view=AcquireVirtualCacheView(image,exception);
2988 sample_view=AcquireAuthenticCacheView(sample_image,exception);
2989 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2990 #pragma omp parallel for schedule(static,4) shared(status) \
2991 magick_threads(image,sample_image,1,1)
2993 for (y=0; y < (ssize_t) sample_image->rows; y++)
2995 register const Quantum
3007 if (status == MagickFalse)
3009 y_offset=(ssize_t) ((((double) y+sample_offset.y)*image->rows)/
3010 sample_image->rows);
3011 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
3013 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
3015 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3023 for (x=0; x < (ssize_t) sample_image->columns; x++)
3028 if (GetPixelReadMask(sample_image,q) == 0)
3030 q+=GetPixelChannels(sample_image);
3033 for (i=0; i < (ssize_t) GetPixelChannels(sample_image); i++)
3042 channel=GetPixelChannelChannel(image,i);
3043 traits=GetPixelChannelTraits(image,channel);
3044 sample_traits=GetPixelChannelTraits(sample_image,channel);
3045 if ((traits == UndefinedPixelTrait) ||
3046 (sample_traits == UndefinedPixelTrait))
3048 SetPixelChannel(sample_image,channel,p[x_offset[x]*GetPixelChannels(
3051 q+=GetPixelChannels(sample_image);
3053 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
3055 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3060 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3061 #pragma omp critical (MagickCore_SampleImage)
3063 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
3064 if (proceed == MagickFalse)
3068 image_view=DestroyCacheView(image_view);
3069 sample_view=DestroyCacheView(sample_view);
3070 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
3071 sample_image->type=image->type;
3072 if (status == MagickFalse)
3073 sample_image=DestroyImage(sample_image);
3074 return(sample_image);
3078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3082 % S c a l e I m a g e %
3086 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3088 % ScaleImage() changes the size of an image to the given dimensions.
3090 % The format of the ScaleImage method is:
3092 % Image *ScaleImage(const Image *image,const size_t columns,
3093 % const size_t rows,ExceptionInfo *exception)
3095 % A description of each parameter follows:
3097 % o image: the image.
3099 % o columns: the number of columns in the scaled image.
3101 % o rows: the number of rows in the scaled image.
3103 % o exception: return any errors or warnings in this structure.
3106 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
3107 const size_t rows,ExceptionInfo *exception)
3109 #define ScaleImageTag "Scale/Image"
3118 pixel[CompositePixelChannel],
3153 Initialize scaled image attributes.
3155 assert(image != (const Image *) NULL);
3156 assert(image->signature == MagickSignature);
3157 if (image->debug != MagickFalse)
3158 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3159 assert(exception != (ExceptionInfo *) NULL);
3160 assert(exception->signature == MagickSignature);
3161 if ((columns == 0) || (rows == 0))
3162 return((Image *) NULL);
3163 if ((columns == image->columns) && (rows == image->rows))
3164 return(CloneImage(image,0,0,MagickTrue,exception));
3165 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
3166 if (scale_image == (Image *) NULL)
3167 return((Image *) NULL);
3168 if (SetImageStorageClass(scale_image,DirectClass,exception) == MagickFalse)
3170 scale_image=DestroyImage(scale_image);
3171 return((Image *) NULL);
3176 x_vector=(double *) AcquireQuantumMemory((size_t) image->columns,
3177 GetPixelChannels(image)*sizeof(*x_vector));
3179 if (image->rows != scale_image->rows)
3180 scanline=(double *) AcquireQuantumMemory((size_t) image->columns,
3181 GetPixelChannels(image)*sizeof(*scanline));
3182 scale_scanline=(double *) AcquireQuantumMemory((size_t)
3183 scale_image->columns,MaxPixelChannels*sizeof(*scale_scanline));
3184 y_vector=(double *) AcquireQuantumMemory((size_t) image->columns,
3185 GetPixelChannels(image)*sizeof(*y_vector));
3186 if ((scanline == (double *) NULL) ||
3187 (scale_scanline == (double *) NULL) ||
3188 (x_vector == (double *) NULL) ||
3189 (y_vector == (double *) NULL))
3191 scale_image=DestroyImage(scale_image);
3192 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3198 next_row=MagickTrue;
3200 scale.y=(double) scale_image->rows/(double) image->rows;
3201 for (i=0; i < (ssize_t) (GetPixelChannels(image)*image->columns); i++)
3205 image_view=AcquireVirtualCacheView(image,exception);
3206 scale_view=AcquireAuthenticCacheView(scale_image,exception);
3207 for (y=0; y < (ssize_t) scale_image->rows; y++)
3209 register const Quantum
3218 if (status == MagickFalse)
3220 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
3222 if (q == (Quantum *) NULL)
3228 if (scale_image->rows == image->rows)
3231 Read a new scanline.
3233 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
3235 if (p == (const Quantum *) NULL)
3240 for (x=0; x < (ssize_t) image->columns; x++)
3242 if (GetPixelReadMask(image,p) == 0)
3244 p+=GetPixelChannels(image);
3247 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3255 channel=GetPixelChannelChannel(image,i);
3256 traits=GetPixelChannelTraits(image,channel);
3257 if ((traits & BlendPixelTrait) == 0)
3259 x_vector[x*GetPixelChannels(image)+i]=(double) p[i];
3262 alpha=QuantumScale*GetPixelAlpha(image,p);
3263 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
3265 p+=GetPixelChannels(image);
3273 while (scale.y < span.y)
3275 if ((next_row != MagickFalse) &&
3276 (number_rows < (ssize_t) image->rows))
3279 Read a new scanline.
3281 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
3283 if (p == (const Quantum *) NULL)
3288 for (x=0; x < (ssize_t) image->columns; x++)
3290 if (GetPixelReadMask(image,p) == 0)
3292 p+=GetPixelChannels(image);
3295 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3303 channel=GetPixelChannelChannel(image,i);
3304 traits=GetPixelChannelTraits(image,channel);
3305 if ((traits & BlendPixelTrait) == 0)
3307 x_vector[x*GetPixelChannels(image)+i]=(double)
3311 alpha=QuantumScale*GetPixelAlpha(image,p);
3312 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
3314 p+=GetPixelChannels(image);
3318 for (x=0; x < (ssize_t) image->columns; x++)
3319 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3320 y_vector[x*GetPixelChannels(image)+i]+=scale.y*
3321 x_vector[x*GetPixelChannels(image)+i];
3323 scale.y=(double) scale_image->rows/(double) image->rows;
3324 next_row=MagickTrue;
3326 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
3329 Read a new scanline.
3331 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
3333 if (p == (const Quantum *) NULL)
3338 for (x=0; x < (ssize_t) image->columns; x++)
3340 if (GetPixelReadMask(image,p) == 0)
3342 p+=GetPixelChannels(image);
3345 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3353 channel=GetPixelChannelChannel(image,i);
3354 traits=GetPixelChannelTraits(image,channel);
3355 if ((traits & BlendPixelTrait) == 0)
3357 x_vector[x*GetPixelChannels(image)+i]=(double) p[i];
3360 alpha=QuantumScale*GetPixelAlpha(image,p);
3361 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
3363 p+=GetPixelChannels(image);
3366 next_row=MagickFalse;
3368 for (x=0; x < (ssize_t) image->columns; x++)
3370 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3372 pixel[i]=y_vector[x*GetPixelChannels(image)+i]+span.y*
3373 x_vector[x*GetPixelChannels(image)+i];
3374 scanline[x*GetPixelChannels(image)+i]=pixel[i];
3375 y_vector[x*GetPixelChannels(image)+i]=0.0;
3381 scale.y=(double) scale_image->rows/(double) image->rows;
3382 next_row=MagickTrue;
3386 if (scale_image->columns == image->columns)
3389 Transfer scanline to scaled image.
3391 for (x=0; x < (ssize_t) scale_image->columns; x++)
3393 if (GetPixelReadMask(scale_image,q) == 0)
3395 q+=GetPixelChannels(scale_image);
3398 for (i=0; i < (ssize_t) GetPixelChannels(scale_image); i++)
3403 channel=GetPixelChannelChannel(scale_image,i);
3404 traits=GetPixelChannelTraits(image,channel);
3405 scale_traits=GetPixelChannelTraits(scale_image,channel);
3406 if ((traits == UndefinedPixelTrait) ||
3407 (scale_traits == UndefinedPixelTrait))
3409 offset=GetPixelChannelOffset(image,channel);
3410 if ((traits & BlendPixelTrait) == 0)
3412 SetPixelChannel(scale_image,channel,ClampToQuantum(
3413 scanline[x*GetPixelChannels(image)+offset]),q);
3416 alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
3417 GetPixelChannelChannel(image,AlphaPixelChannel)];
3418 gamma=PerceptibleReciprocal(alpha);
3419 SetPixelChannel(scale_image,channel,ClampToQuantum(gamma*scanline[
3420 x*GetPixelChannels(image)+offset]),q);
3422 q+=GetPixelChannels(scale_image);
3433 next_column=MagickFalse;
3436 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3438 for (x=0; x < (ssize_t) image->columns; x++)
3440 scale.x=(double) scale_image->columns/(double) image->columns;
3441 while (scale.x >= span.x)
3443 if (next_column != MagickFalse)
3445 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3449 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3457 channel=GetPixelChannelChannel(image,i);
3458 traits=GetPixelChannelTraits(image,channel);
3459 if (traits == UndefinedPixelTrait)
3461 pixel[i]+=span.x*scanline[x*GetPixelChannels(image)+i];
3462 scale_scanline[n*MaxPixelChannels+channel]=pixel[i];
3466 next_column=MagickTrue;
3470 if (next_column != MagickFalse)
3472 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3475 next_column=MagickFalse;
3477 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3478 pixel[i]+=scale.x*scanline[x*GetPixelChannels(image)+i];
3484 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3485 pixel[i]+=span.x*scanline[(x-1)*GetPixelChannels(image)+i];
3487 if ((next_column == MagickFalse) &&
3488 ((ssize_t) n < (ssize_t) scale_image->columns))
3489 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3491 channel=GetPixelChannelChannel(image,i);
3492 scale_scanline[n*MaxPixelChannels+channel]=pixel[i];
3495 Transfer scanline to scaled image.
3497 for (x=0; x < (ssize_t) scale_image->columns; x++)
3499 if (GetPixelReadMask(scale_image,q) == 0)
3501 q+=GetPixelChannels(scale_image);
3504 for (i=0; i < (ssize_t) GetPixelChannels(scale_image); i++)
3506 channel=GetPixelChannelChannel(scale_image,i);
3507 traits=GetPixelChannelTraits(image,channel);
3508 scale_traits=GetPixelChannelTraits(scale_image,channel);
3509 if ((traits == UndefinedPixelTrait) ||
3510 (scale_traits == UndefinedPixelTrait))
3512 if ((traits & BlendPixelTrait) == 0)
3514 SetPixelChannel(scale_image,channel,ClampToQuantum(
3515 scale_scanline[x*MaxPixelChannels+channel]),q);
3518 SetPixelChannel(scale_image,channel,ClampToQuantum(scale_scanline[
3519 x*MaxPixelChannels+channel]),q);
3521 q+=GetPixelChannels(scale_image);
3524 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
3529 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3531 if (proceed == MagickFalse)
3537 scale_view=DestroyCacheView(scale_view);
3538 image_view=DestroyCacheView(image_view);
3540 Free allocated memory.
3542 y_vector=(double *) RelinquishMagickMemory(y_vector);
3543 scale_scanline=(double *) RelinquishMagickMemory(scale_scanline);
3544 if (scale_image->rows != image->rows)
3545 scanline=(double *) RelinquishMagickMemory(scanline);
3546 x_vector=(double *) RelinquishMagickMemory(x_vector);
3547 scale_image->type=image->type;
3548 if (status == MagickFalse)
3549 scale_image=DestroyImage(scale_image);
3550 return(scale_image);
3554 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3558 % T h u m b n a i l I m a g e %
3562 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3564 % ThumbnailImage() changes the size of an image to the given dimensions and
3565 % removes any associated profiles. The goal is to produce small low cost
3566 % thumbnail images suited for display on the Web.
3568 % The format of the ThumbnailImage method is:
3570 % Image *ThumbnailImage(const Image *image,const size_t columns,
3571 % const size_t rows,ExceptionInfo *exception)
3573 % A description of each parameter follows:
3575 % o image: the image.
3577 % o columns: the number of columns in the scaled image.
3579 % o rows: the number of rows in the scaled image.
3581 % o exception: return any errors or warnings in this structure.
3584 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3585 const size_t rows,ExceptionInfo *exception)
3587 #define SampleFactor 5
3590 value[MaxTextExtent];
3608 assert(image != (Image *) NULL);
3609 assert(image->signature == MagickSignature);
3610 if (image->debug != MagickFalse)
3611 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3612 assert(exception != (ExceptionInfo *) NULL);
3613 assert(exception->signature == MagickSignature);
3614 x_factor=(double) columns/(double) image->columns;
3615 y_factor=(double) rows/(double) image->rows;
3616 if ((x_factor*y_factor) > 0.1)
3617 thumbnail_image=ResizeImage(image,columns,rows,image->filter,exception);
3619 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
3620 thumbnail_image=ResizeImage(image,columns,rows,image->filter,exception);
3626 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3628 if (sample_image == (Image *) NULL)
3629 return((Image *) NULL);
3630 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3632 sample_image=DestroyImage(sample_image);
3634 if (thumbnail_image == (Image *) NULL)
3635 return(thumbnail_image);
3636 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3637 if (thumbnail_image->alpha_trait != BlendPixelTrait)
3638 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel,exception);
3639 thumbnail_image->depth=8;
3640 thumbnail_image->interlace=NoInterlace;
3642 Strip all profiles except color profiles.
3644 ResetImageProfileIterator(thumbnail_image);
3645 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3647 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3649 (void) DeleteImageProfile(thumbnail_image,name);
3650 ResetImageProfileIterator(thumbnail_image);
3652 name=GetNextImageProfile(thumbnail_image);
3654 (void) DeleteImageProperty(thumbnail_image,"comment");
3655 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3656 if (strstr(image->magick_filename,"//") == (char *) NULL)
3657 (void) FormatLocaleString(value,MaxTextExtent,"file://%s",
3658 image->magick_filename);
3659 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value,exception);
3660 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3661 if ( IfMagickTrue(GetPathAttributes(image->filename,&attributes)) )
3663 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3664 attributes.st_mtime);
3665 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value,exception);
3667 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3668 attributes.st_mtime);
3669 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
3670 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
3671 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value,exception);
3672 (void) FormatLocaleString(value,MaxTextExtent,"image/%s",image->magick);
3674 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value,exception);
3675 (void) SetImageProperty(thumbnail_image,"software",GetMagickVersion(&version),
3677 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3678 image->magick_columns);
3679 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value,
3681 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3682 image->magick_rows);
3683 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Height",value,
3685 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3686 GetImageListLength(image));
3687 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value,
3689 return(thumbnail_image);