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 */
98 ResizeWeightingFunctionType
107 Forward declaractions.
111 BesselOrderOne(double),
112 Sinc(const double, const ResizeFilter *),
113 SincFast(const double, const ResizeFilter *);
116 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
120 + F i l t e r F u n c t i o n s %
124 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
126 % These are the various filter and windowing functions that are provided.
128 % They are internal to this module only. See AcquireResizeFilterInfo() for
129 % details of the access to these functions, via the GetResizeFilterSupport()
130 % and GetResizeFilterWeight() API interface.
132 % The individual filter functions have this format...
134 % static MagickRealtype *FilterName(const double x,const double support)
136 % A description of each parameter follows:
138 % o x: the distance from the sampling point generally in the range of 0 to
139 % support. The GetResizeFilterWeight() ensures this a positive value.
141 % o resize_filter: current filter information. This allows function to
142 % access support, and possibly other pre-calculated information defining
147 static double Blackman(const double x,
148 const ResizeFilter *magick_unused(resize_filter))
151 Blackman: 2nd order cosine windowing function:
152 0.42 + 0.5 cos(pi x) + 0.08 cos(2pi x)
154 Refactored by Chantal Racette and Nicolas Robidoux to one trig call and
157 const double cosine=cos((double) (MagickPI*x));
158 return(0.34+cosine*(0.5+cosine*0.16));
161 static double Bohman(const double x,
162 const ResizeFilter *magick_unused(resize_filter))
165 Bohman: 2rd Order cosine windowing function:
166 (1-x) cos(pi x) + sin(pi x) / pi.
168 Refactored by Nicolas Robidoux to one trig call, one sqrt call, and 7 flops,
169 taking advantage of the fact that the support of Bohman is 1.0 (so that we
170 know that sin(pi x) >= 0).
172 const double cosine=cos((double) (MagickPI*x));
173 const double sine=sqrt(1.0-cosine*cosine);
174 return((1.0-x)*cosine+(1.0/MagickPI)*sine);
177 static double Box(const double magick_unused(x),
178 const ResizeFilter *magick_unused(resize_filter))
181 A Box filter is a equal weighting function (all weights equal).
182 DO NOT LIMIT results by support or resize point sampling will work
183 as it requests points beyond its normal 0.0 support size.
188 static double Cosine(const double x,
189 const ResizeFilter *magick_unused(resize_filter))
192 Cosine window function:
195 return((double)cos((double) (MagickPI2*x)));
198 static double CubicBC(const double x,const ResizeFilter *resize_filter)
201 Cubic Filters using B,C determined values:
202 Mitchell-Netravali B = 1/3 C = 1/3 "Balanced" cubic spline filter
203 Catmull-Rom B = 0 C = 1/2 Interpolatory and exact on linears
204 Spline B = 1 C = 0 B-Spline Gaussian approximation
205 Hermite B = 0 C = 0 B-Spline interpolator
207 See paper by Mitchell and Netravali, Reconstruction Filters in Computer
208 Graphics Computer Graphics, Volume 22, Number 4, August 1988
209 http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/
212 Coefficents are determined from B,C values:
213 P0 = ( 6 - 2*B )/6 = coeff[0]
215 P2 = (-18 +12*B + 6*C )/6 = coeff[1]
216 P3 = ( 12 - 9*B - 6*C )/6 = coeff[2]
217 Q0 = ( 8*B +24*C )/6 = coeff[3]
218 Q1 = ( -12*B -48*C )/6 = coeff[4]
219 Q2 = ( 6*B +30*C )/6 = coeff[5]
220 Q3 = ( - 1*B - 6*C )/6 = coeff[6]
222 which are used to define the filter:
224 P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
225 Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x < 2
227 which ensures function is continuous in value and derivative (slope).
230 return(resize_filter->coefficient[0]+x*(x*
231 (resize_filter->coefficient[1]+x*resize_filter->coefficient[2])));
233 return(resize_filter->coefficient[3]+x*(resize_filter->coefficient[4]+x*
234 (resize_filter->coefficient[5]+x*resize_filter->coefficient[6])));
238 static double Gaussian(const double x,const ResizeFilter *resize_filter)
241 Gaussian with a sigma = 1/2 (or as user specified)
243 Gaussian Formula (1D) ...
244 exp( -(x^2)/((2.0*sigma^2) ) / (sqrt(2*PI)*sigma^2))
246 Gaussian Formula (2D) ...
247 exp( -(x^2+y^2)/(2.0*sigma^2) ) / (PI*sigma^2) )
249 exp( -(r^2)/(2.0*sigma^2) ) / (PI*sigma^2) )
251 Note that it is only a change from 1-d to radial form is in the
252 normalization multiplier which is not needed or used when Gaussian is used
255 The constants are pre-calculated...
258 coeff[1]=1.0/(2.0*sigma^2);
259 coeff[2]=1.0/(sqrt(2*PI)*sigma^2);
261 exp( -coeff[1]*(x^2)) ) * coeff[2];
263 However the multiplier coeff[1] is need, the others are informative only.
265 This separates the gaussian 'sigma' value from the 'blur/support'
266 settings allowing for its use in special 'small sigma' gaussians,
267 without the filter 'missing' pixels because the support becomes too
270 return(exp((double)(-resize_filter->coefficient[1]*x*x)));
273 static double Hann(const double x,
274 const ResizeFilter *magick_unused(resize_filter))
277 Cosine window function:
280 const double cosine=cos((double) (MagickPI*x));
281 return(0.5+0.5*cosine);
284 static double Hamming(const double x,
285 const ResizeFilter *magick_unused(resize_filter))
288 Offset cosine window function:
291 const double cosine=cos((double) (MagickPI*x));
292 return(0.54+0.46*cosine);
295 static double Jinc(const double x,
296 const ResizeFilter *magick_unused(resize_filter))
299 See Pratt "Digital Image Processing" p.97 for Jinc/Bessel functions.
300 http://mathworld.wolfram.com/JincFunction.html and page 11 of
301 http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
303 The original "zoom" program by Paul Heckbert called this "Bessel". But
304 really it is more accurately named "Jinc".
307 return(0.5*MagickPI);
308 return(BesselOrderOne(MagickPI*x)/x);
311 static double Kaiser(const double x,const ResizeFilter *resize_filter)
314 Kaiser Windowing Function (bessel windowing)
316 I0( beta * sqrt( 1-x^2) ) / IO(0)
318 Beta (coeff[0]) is a free value from 5 to 8 (defaults to 6.5).
319 However it is typically defined in terms of Alpha*PI
321 The normalization factor (coeff[1]) is not actually needed,
322 but without it the filters has a large value at x=0 making it
323 difficult to compare the function with other windowing functions.
325 return(resize_filter->coefficient[1]*I0(resize_filter->coefficient[0]*
326 sqrt((double) (1.0-x*x))));
329 static double Lagrange(const double x,const ResizeFilter *resize_filter)
342 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the lagrange
343 function and depends on the overall support window size of the filter. That
344 is: for a support of 2, it gives a lagrange-4 (piecewise cubic function).
346 "n" identifies the piece of the piecewise polynomial.
348 See Survey: Interpolation Methods, IEEE Transactions on Medical Imaging,
349 Vol 18, No 11, November 1999, p1049-1075, -- Equation 27 on p1064.
351 if (x > resize_filter->support)
353 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
354 n=(ssize_t) (resize_filter->window_support+x);
356 for (i=0; i < order; i++)
358 value*=(n-i-x)/(n-i);
362 static double Quadratic(const double x,
363 const ResizeFilter *magick_unused(resize_filter))
366 2rd order (quadratic) B-Spline approximation of Gaussian.
371 return(0.5*(x-1.5)*(x-1.5));
375 static double Sinc(const double x,
376 const ResizeFilter *magick_unused(resize_filter))
379 Scaled sinc(x) function using a trig call:
380 sinc(x) == sin(pi x)/(pi x).
384 const double alpha=(double) (MagickPI*x);
385 return(sin((double) alpha)/alpha);
387 return((double) 1.0);
390 static double SincFast(const double x,
391 const ResizeFilter *magick_unused(resize_filter))
394 Approximations of the sinc function sin(pi x)/(pi x) over the interval
395 [-4,4] constructed by Nicolas Robidoux and Chantal Racette with funding
396 from the Natural Sciences and Engineering Research Council of Canada.
398 Although the approximations are polynomials (for low order of
399 approximation) and quotients of polynomials (for higher order of
400 approximation) and consequently are similar in form to Taylor polynomials /
401 Pade approximants, the approximations are computed with a completely
404 Summary: These approximations are "the best" in terms of bang (accuracy)
405 for the buck (flops). More specifically: Among the polynomial quotients
406 that can be computed using a fixed number of flops (with a given "+ - * /
407 budget"), the chosen polynomial quotient is the one closest to the
408 approximated function with respect to maximum absolute relative error over
411 The Remez algorithm, as implemented in the boost library's minimax package,
412 is the key to the construction: http://www.boost.org/doc/libs/1_36_0/libs/
413 math/doc/sf_and_dist/html/math_toolkit/backgrounders/remez.html
415 If outside of the interval of approximation, use the standard trig formula.
419 const double alpha=(double) (MagickPI*x);
420 return(sin((double) alpha)/alpha);
424 The approximations only depend on x^2 (sinc is an even function).
426 const double xx = x*x;
427 #if MAGICKCORE_QUANTUM_DEPTH <= 8
429 Maximum absolute relative error 6.3e-6 < 1/2^17.
431 const double c0 = 0.173610016489197553621906385078711564924e-2L;
432 const double c1 = -0.384186115075660162081071290162149315834e-3L;
433 const double c2 = 0.393684603287860108352720146121813443561e-4L;
434 const double c3 = -0.248947210682259168029030370205389323899e-5L;
435 const double c4 = 0.107791837839662283066379987646635416692e-6L;
436 const double c5 = -0.324874073895735800961260474028013982211e-8L;
437 const double c6 = 0.628155216606695311524920882748052490116e-10L;
438 const double c7 = -0.586110644039348333520104379959307242711e-12L;
440 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
441 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
442 #elif MAGICKCORE_QUANTUM_DEPTH <= 16
444 Max. abs. rel. error 2.2e-8 < 1/2^25.
446 const double c0 = 0.173611107357320220183368594093166520811e-2L;
447 const double c1 = -0.384240921114946632192116762889211361285e-3L;
448 const double c2 = 0.394201182359318128221229891724947048771e-4L;
449 const double c3 = -0.250963301609117217660068889165550534856e-5L;
450 const double c4 = 0.111902032818095784414237782071368805120e-6L;
451 const double c5 = -0.372895101408779549368465614321137048875e-8L;
452 const double c6 = 0.957694196677572570319816780188718518330e-10L;
453 const double c7 = -0.187208577776590710853865174371617338991e-11L;
454 const double c8 = 0.253524321426864752676094495396308636823e-13L;
455 const double c9 = -0.177084805010701112639035485248501049364e-15L;
457 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
458 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
461 Max. abs. rel. error 1.2e-12 < 1/2^39.
463 const double c0 = 0.173611111110910715186413700076827593074e-2L;
464 const double c1 = -0.289105544717893415815859968653611245425e-3L;
465 const double c2 = 0.206952161241815727624413291940849294025e-4L;
466 const double c3 = -0.834446180169727178193268528095341741698e-6L;
467 const double c4 = 0.207010104171026718629622453275917944941e-7L;
468 const double c5 = -0.319724784938507108101517564300855542655e-9L;
469 const double c6 = 0.288101675249103266147006509214934493930e-11L;
470 const double c7 = -0.118218971804934245819960233886876537953e-13L;
472 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
473 const double d0 = 1.0L;
474 const double d1 = 0.547981619622284827495856984100563583948e-1L;
475 const double d2 = 0.134226268835357312626304688047086921806e-2L;
476 const double d3 = 0.178994697503371051002463656833597608689e-4L;
477 const double d4 = 0.114633394140438168641246022557689759090e-6L;
478 const double q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
479 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
484 static double Triangle(const double x,
485 const ResizeFilter *magick_unused(resize_filter))
488 1st order (linear) B-Spline, bilinear interpolation, Tent 1D filter, or
489 a Bartlett 2D Cone filter. Also used as a Bartlett Windowing function
497 static double Welch(const double x,
498 const ResizeFilter *magick_unused(resize_filter))
501 Welch parabolic windowing filter.
509 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
513 + A c q u i r e R e s i z e F i l t e r %
517 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
519 % AcquireResizeFilter() allocates the ResizeFilter structure. Choose from
522 % FIR (Finite impulse Response) Filters
523 % Box Triangle Quadratic
524 % Spline Hermite Catrom
527 % IIR (Infinite impulse Response) Filters
528 % Gaussian Sinc Jinc (Bessel)
530 % Windowed Sinc/Jinc Filters
531 % Blackman Bohman Lanczos
532 % Hann Hamming Cosine
533 % Kaiser Welch Parzen
536 % Special Purpose Filters
537 % Cubic SincFast LanczosSharp Lanczos2 Lanczos2Sharp
538 % Robidoux RobidouxSharp
540 % The users "-filter" selection is used to lookup the default 'expert'
541 % settings for that filter from a internal table. However any provided
542 % 'expert' settings (see below) may override this selection.
544 % FIR filters are used as is, and are limited to that filters support window
545 % (unless over-ridden). 'Gaussian' while classed as an IIR filter, is also
546 % simply clipped by its support size (currently 1.5 or approximately 3*sigma
547 % as recommended by many references)
549 % The special a 'cylindrical' filter flag will promote the default 4-lobed
550 % Windowed Sinc filter to a 3-lobed Windowed Jinc equivalent, which is better
551 % suited to this style of image resampling. This typically happens when using
552 % such a filter for images distortions.
556 % Directly requesting 'Sinc', 'Jinc' function as a filter will force the use
557 % of function without any windowing, or promotion for cylindrical usage. This
558 % is not recommended, except by image processing experts, especially as part
559 % of expert option filter function selection.
561 % Two forms of the 'Sinc' function are available: Sinc and SincFast. Sinc is
562 % computed using the traditional sin(pi*x)/(pi*x); it is selected if the user
563 % specifically specifies the use of a Sinc filter. SincFast uses highly
564 % accurate (and fast) polynomial (low Q) and rational (high Q) approximations,
565 % and will be used by default in most cases.
567 % The Lanczos filter is a special 3-lobed Sinc-windowed Sinc filter (promoted
568 % to Jinc-windowed Jinc for cylindrical (Elliptical Weighted Average) use).
569 % The Sinc version is the most popular windowed filter.
571 % LanczosSharp is a slightly sharpened (blur=0.9812505644269356 < 1) form of
572 % the Lanczos filter, specifically designed for EWA distortion (as a
573 % Jinc-Jinc); it can also be used as a slightly sharper orthogonal Lanczos
574 % (Sinc-Sinc) filter. The chosen blur value comes as close as possible to
575 % satisfying the following condition without changing the character of the
576 % corresponding EWA filter:
578 % 'No-Op' Vertical and Horizontal Line Preservation Condition: Images with
579 % only vertical or horizontal features are preserved when performing 'no-op"
580 % with EWA distortion.
582 % The Lanczos2 and Lanczos2Sharp filters are 2-lobe versions of the Lanczos
583 % filters. The 'sharp' version uses a blur factor of 0.9549963639785485,
584 % again chosen because the resulting EWA filter comes as close as possible to
585 % satisfying the above condition.
587 % Robidoux is another filter tuned for EWA. It is the Keys cubic filter
588 % defined by B=(228 - 108 sqrt(2))/199. Robidoux satisfies the "'No-Op'
589 % Vertical and Horizontal Line Preservation Condition" exactly, and it
590 % moderately blurs high frequency 'pixel-hash' patterns under no-op. It turns
591 % out to be close to both Mitchell and Lanczos2Sharp. For example, its first
592 % crossing is at (36 sqrt(2) + 123)/(72 sqrt(2) + 47), almost the same as the
593 % first crossing of Mitchell and Lanczos2Sharp.
595 % RodidouxSharp is a slightly sharper version of Rodidoux, some believe it
596 % is too sharp. It is designed to minimize the maximum possible change in
597 % a pixel value which is at one of the extremes (e.g., 0 or 255) under no-op
598 % conditions. Amazingly Mitchell falls roughly between Rodidoux and
599 % RodidouxSharp, though this seems to have been pure coincidence.
603 % These artifact "defines" are not recommended for production use without
604 % expert knowledge of resampling, filtering, and the effects they have on the
605 % resulting resampled (resized or distorted) image.
607 % They can be used to override any and all filter default, and it is
608 % recommended you make good use of "filter:verbose" to make sure that the
609 % overall effect of your selection (before and after) is as expected.
611 % "filter:verbose" controls whether to output the exact results of the
612 % filter selections made, as well as plotting data for graphing the
613 % resulting filter over the filters support range.
615 % "filter:filter" select the main function associated with this filter
616 % name, as the weighting function of the filter. This can be used to
617 % set a windowing function as a weighting function, for special
618 % purposes, such as graphing.
620 % If a "filter:window" operation has not been provided, a 'Box'
621 % windowing function will be set to denote that no windowing function is
624 % "filter:window" Select this windowing function for the filter. While any
625 % filter could be used as a windowing function, using the 'first lobe' of
626 % that filter over the whole support window, using a non-windowing
627 % function is not advisible. If no weighting filter function is specified
628 % a 'SincFast' filter is used.
630 % "filter:lobes" Number of lobes to use for the Sinc/Jinc filter. This a
631 % simpler method of setting filter support size that will correctly
632 % handle the Sinc/Jinc switch for an operators filtering requirements.
633 % Only integers should be given.
635 % "filter:support" Set the support size for filtering to the size given.
636 % This not recommended for Sinc/Jinc windowed filters (lobes should be
637 % used instead). This will override any 'filter:lobes' option.
639 % "filter:win-support" Scale windowing function to this size instead. This
640 % causes the windowing (or self-windowing Lagrange filter) to act is if
641 % the support window it much much larger than what is actually supplied
642 % to the calling operator. The filter however is still clipped to the
643 % real support size given, by the support range supplied to the caller.
644 % If unset this will equal the normal filter support size.
646 % "filter:blur" Scale the filter and support window by this amount. A value
647 % of > 1 will generally result in a more blurred image with more ringing
648 % effects, while a value <1 will sharpen the resulting image with more
651 % "filter:sigma" The sigma value to use for the Gaussian filter only.
652 % Defaults to '1/2'. Using a different sigma effectively provides a
653 % method of using the filter as a 'blur' convolution. Particularly when
654 % using it for Distort.
657 % "filter:c" Override the preset B,C values for a Cubic filter.
658 % If only one of these are given it is assumes to be a 'Keys' type of
659 % filter such that B+2C=1, where Keys 'alpha' value = C.
663 % Set a true un-windowed Sinc filter with 10 lobes (very slow):
664 % -define filter:filter=Sinc
665 % -define filter:lobes=8
667 % Set an 8 lobe Lanczos (Sinc or Jinc) filter:
669 % -define filter:lobes=8
671 % The format of the AcquireResizeFilter method is:
673 % ResizeFilter *AcquireResizeFilter(const Image *image,
674 % const FilterTypes filter_type,const MagickBooleanType cylindrical,
675 % ExceptionInfo *exception)
677 % A description of each parameter follows:
679 % o image: the image.
681 % o filter: the filter type, defining a preset filter, window and support.
682 % The artifact settings listed above will override those selections.
684 % o blur: blur the filter by this amount, use 1.0 if unknown. Image
685 % artifact "filter:blur" will override this API call usage, including any
686 % internal change (such as for cylindrical usage).
688 % o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical (radial)
691 % o exception: return any errors or warnings in this structure.
694 MagickPrivate ResizeFilter *AcquireResizeFilter(const Image *image,
695 const FilterTypes filter,const MagickBooleanType cylindrical,
696 ExceptionInfo *exception)
710 register ResizeFilter
714 Table Mapping given Filter, into Weighting and Windowing functions.
715 A 'Box' windowing function means its a simble non-windowed filter.
716 An 'SincFast' filter function could be upgraded to a 'Jinc' filter if a
717 "cylindrical" is requested, unless a 'Sinc' or 'SincFast' filter was
718 specifically requested by the user.
720 WARNING: The order of this table must match the order of the FilterTypes
721 enumeration specified in "resample.h", or the filter names will not match
722 the filter being setup.
724 You can check filter setups with the "filter:verbose" expert setting.
731 } const mapping[SentinelFilter] =
733 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
734 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
735 { BoxFilter, BoxFilter }, /* Box averaging filter */
736 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
737 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
738 { SincFastFilter, HannFilter }, /* Hann -- cosine-sinc */
739 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
740 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
741 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
742 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approx */
743 { CubicFilter, BoxFilter }, /* General Cubic Filter, Spline */
744 { CatromFilter, BoxFilter }, /* Cubic-Keys interpolator */
745 { MitchellFilter, BoxFilter }, /* 'Ideal' Cubic-Keys filter */
746 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
747 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
748 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
749 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
750 { LanczosFilter, WelchFilter }, /* Welch -- parabolic (3 lobe) */
751 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
752 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
753 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
754 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing */
755 { LanczosFilter, LanczosFilter }, /* Lanczos Sinc-Sinc filters */
756 { LanczosSharpFilter, LanczosSharpFilter }, /* | these require */
757 { Lanczos2Filter, Lanczos2Filter }, /* | special handling */
758 { Lanczos2SharpFilter, Lanczos2SharpFilter },
759 { RobidouxFilter, BoxFilter }, /* Cubic Keys tuned for EWA */
760 { RobidouxSharpFilter, BoxFilter }, /* Sharper Cubic Keys for EWA */
761 { LanczosFilter, CosineFilter }, /* Cosine window (3 lobes) */
762 { SplineFilter, BoxFilter }, /* Spline Cubic Filter */
763 { LanczosRadiusFilter, LanczosFilter }, /* Lanczos with integer radius */
766 Table mapping the filter/window from the above table to an actual function.
767 The default support size for that filter as a weighting function, the range
768 to scale with to use that function as a sinc windowing function, (typ 1.0).
770 Note that the filter_type -> function is 1 to 1 except for Sinc(),
771 SincFast(), and CubicBC() functions, which may have multiple filter to
772 function associations.
774 See "filter:verbose" handling below for the function -> filter mapping.
779 (*function)(const double,const ResizeFilter*),
780 support, /* Default lobes/support size of the weighting filter. */
781 scale, /* Support when function used as a windowing function
782 Typically equal to the location of the first zero crossing. */
783 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
784 } const filters[SentinelFilter] =
786 /* .--- support window (if used as a Weighting Function)
787 | .--- first crossing (if used as a Windowing Function)
788 | | .--- B value for Cubic Function
789 | | | .---- C value for Cubic Function
791 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
792 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
793 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
794 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
795 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
796 { Hann, 1.0, 1.0, 0.0, 0.0 }, /* Hann, cosine window */
797 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
798 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
799 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
800 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
801 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* General Cubic Filter */
802 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
803 { CubicBC, 2.0, 8.0/7.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
804 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
805 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
806 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
807 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
808 { Welch, 1.0, 1.0, 0.0, 0.0 }, /* Welch (parabolic window) */
809 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
810 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
811 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
812 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
813 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
814 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, Sharpened */
815 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos, 2-lobed */
816 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2, sharpened */
817 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
818 { CubicBC, 2.0, 1.1685777620836932,
819 0.37821575509399867, 0.31089212245300067 },
820 /* RobidouxSharp: Sharper version of Robidoux */
821 { CubicBC, 2.0, 1.105822933719019,
822 0.2620145123990142, 0.3689927438004929 },
823 { Cosine, 1.0, 1.0, 0.0, 0.0 }, /* Low level cosine window */
824 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
825 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, Interger Radius */
828 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
829 function being used as a filter. It is used by the "filter:lobes" expert
830 setting and for 'lobes' for Jinc functions in the previous table. This way
831 users do not have to deal with the highly irrational lobe sizes of the Jinc
835 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
836 using Jv-function with v=1, then dividing by PI.
860 Allocate resize filter.
862 assert(image != (const Image *) NULL);
863 assert(image->signature == MagickSignature);
864 if (image->debug != MagickFalse)
865 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
866 assert(UndefinedFilter < filter && filter < SentinelFilter);
867 assert(exception != (ExceptionInfo *) NULL);
868 assert(exception->signature == MagickSignature);
869 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
870 if (resize_filter == (ResizeFilter *) NULL)
871 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
872 (void) ResetMagickMemory(resize_filter,0,sizeof(*resize_filter));
874 Defaults for the requested filter.
876 filter_type=mapping[filter].filter;
877 window_type=mapping[filter].window;
878 resize_filter->blur=1.0;
879 /* Promote 1D Windowed Sinc Filters to a 2D Windowed Jinc filters */
880 if ( IfMagickTrue(cylindrical) && (filter_type == SincFastFilter) &&
881 (filter != SincFastFilter))
882 filter_type=JincFilter; /* 1D Windowed Sinc => 2D Windowed Jinc filters */
884 /* Expert filter setting override */
885 artifact=GetImageArtifact(image,"filter:filter");
886 if (artifact != (const char *) NULL)
891 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
892 if ((UndefinedFilter < option) && (option < SentinelFilter))
893 { /* Raw filter request - no window function. */
894 filter_type=(FilterTypes) option;
895 window_type=BoxFilter;
897 /* Filter override with a specific window function. */
898 artifact=GetImageArtifact(image,"filter:window");
899 if (artifact != (const char *) NULL)
901 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
902 if ((UndefinedFilter < option) && (option < SentinelFilter))
903 window_type=(FilterTypes) option;
908 /* Window specified, but no filter function? Assume Sinc/Jinc. */
909 artifact=GetImageArtifact(image,"filter:window");
910 if (artifact != (const char *) NULL)
915 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact);
916 if ((UndefinedFilter < option) && (option < SentinelFilter))
918 filter_type= IfMagickTrue(cylindrical) ? JincFilter
920 window_type=(FilterTypes) option;
925 /* Assign the real functions to use for the filters selected. */
926 resize_filter->filter=filters[filter_type].function;
927 resize_filter->support=filters[filter_type].support;
928 resize_filter->window=filters[window_type].function;
929 resize_filter->scale=filters[window_type].scale;
930 resize_filter->signature=MagickSignature;
932 /* Filter Modifications for orthogonal/cylindrical usage */
933 if (cylindrical != MagickFalse)
937 /* Support for Cylindrical Box should be sqrt(2)/2 */
938 resize_filter->support=(double) MagickSQ1_2;
941 case LanczosSharpFilter:
943 case Lanczos2SharpFilter:
944 case LanczosRadiusFilter:
945 resize_filter->filter=filters[JincFilter].function;
946 resize_filter->window=filters[JincFilter].function;
947 resize_filter->scale=filters[JincFilter].scale;
948 /* number of lobes (support window size) remain unchanged */
953 /* Global Sharpening (regardless of orthoginal/cylindrical) */
956 case LanczosSharpFilter:
957 resize_filter->blur *= 0.9812505644269356;
959 case Lanczos2SharpFilter:
960 resize_filter->blur *= 0.9549963639785485;
962 /* case LanczosRadius: blur adjust is done after lobes */
968 Expert Option Modifications.
971 /* User Gaussian Sigma Override - no support change */
972 if ((resize_filter->filter == Gaussian) ||
973 (resize_filter->window == Gaussian) ) {
974 value=0.5; /* guassian sigma default, half pixel */
975 artifact=GetImageArtifact(image,"filter:sigma");
976 if (artifact != (const char *) NULL)
977 value=StringToDouble(artifact,(char **) NULL);
978 /* Define coefficents for Gaussian */
979 resize_filter->coefficient[0]=value; /* note sigma too */
980 resize_filter->coefficient[1]=PerceptibleReciprocal(2.0*value*value); /* sigma scaling */
981 resize_filter->coefficient[2]=PerceptibleReciprocal(Magick2PI*value*value);
982 /* normalization - not actually needed or used! */
984 resize_filter->support *= 2*value; /* increase support linearly */
987 /* User Kaiser Alpha Override - no support change */
988 if ((resize_filter->filter == Kaiser) ||
989 (resize_filter->window == Kaiser) ) {
990 value=6.5; /* default beta value for Kaiser bessel windowing function */
991 artifact=GetImageArtifact(image,"filter:alpha"); /* FUTURE: depreciate */
992 if (artifact != (const char *) NULL)
993 value=StringToDouble(artifact,(char **) NULL);
994 artifact=GetImageArtifact(image,"filter:kaiser-beta");
995 if (artifact != (const char *) NULL)
996 value=StringToDouble(artifact,(char **) NULL);
997 artifact=GetImageArtifact(image,"filter:kaiser-alpha");
998 if (artifact != (const char *) NULL)
999 value=StringToDouble(artifact,(char **) NULL)*MagickPI;
1000 /* Define coefficents for Kaiser Windowing Function */
1001 resize_filter->coefficient[0]=value; /* alpha */
1002 resize_filter->coefficient[1]=PerceptibleReciprocal(I0(value));
1006 /* Support Overrides */
1007 artifact=GetImageArtifact(image,"filter:lobes");
1008 if (artifact != (const char *) NULL)
1013 lobes=(ssize_t) StringToLong(artifact);
1016 resize_filter->support=(double) lobes;
1018 if (resize_filter->filter == Jinc)
1021 Convert a Jinc function lobes value to a real support value.
1023 if (resize_filter->support > 16)
1024 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
1026 resize_filter->support=jinc_zeros[((long) resize_filter->support)-1];
1028 Blur this filter so support is a integer value (lobes dependant).
1030 if (filter_type == LanczosRadiusFilter)
1031 resize_filter->blur*=floor(resize_filter->support)/
1032 resize_filter->support;
1035 Expert blur override.
1037 artifact=GetImageArtifact(image,"filter:blur");
1038 if (artifact != (const char *) NULL)
1039 resize_filter->blur*=StringToDouble(artifact,(char **) NULL);
1040 if (resize_filter->blur < MagickEpsilon)
1041 resize_filter->blur=(double) MagickEpsilon;
1043 Expert override of the support setting.
1045 artifact=GetImageArtifact(image,"filter:support");
1046 if (artifact != (const char *) NULL)
1047 resize_filter->support=fabs(StringToDouble(artifact,(char **) NULL));
1049 Scale windowing function separately to the support 'clipping' window
1050 that calling operator is planning to actually use. (Expert override)
1052 resize_filter->window_support=resize_filter->support; /* default */
1053 artifact=GetImageArtifact(image,"filter:win-support");
1054 if (artifact != (const char *) NULL)
1055 resize_filter->window_support=fabs(StringToDouble(artifact,(char **) NULL));
1057 Adjust window function scaling to match windowing support for weighting
1058 function. This avoids a division on every filter call.
1060 resize_filter->scale/=resize_filter->window_support;
1062 * Set Cubic Spline B,C values, calculate Cubic coefficients.
1066 if ((resize_filter->filter == CubicBC) ||
1067 (resize_filter->window == CubicBC) )
1069 B=filters[filter_type].B;
1070 C=filters[filter_type].C;
1071 if (filters[window_type].function == CubicBC)
1073 B=filters[window_type].B;
1074 C=filters[window_type].C;
1076 artifact=GetImageArtifact(image,"filter:b");
1077 if (artifact != (const char *) NULL)
1079 B=StringToDouble(artifact,(char **) NULL);
1080 C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
1081 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
1082 if (artifact != (const char *) NULL)
1083 C=StringToDouble(artifact,(char **) NULL);
1087 artifact=GetImageArtifact(image,"filter:c");
1088 if (artifact != (const char *) NULL)
1090 C=StringToDouble(artifact,(char **) NULL);
1091 B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
1099 Convert B,C values into Cubic Coefficents. See CubicBC().
1101 resize_filter->coefficient[0]=1.0-(1.0/3.0)*B;
1102 resize_filter->coefficient[1]=-3.0+twoB+C;
1103 resize_filter->coefficient[2]=2.0-1.5*B-C;
1104 resize_filter->coefficient[3]=(4.0/3.0)*B+4.0*C;
1105 resize_filter->coefficient[4]=-8.0*C-twoB;
1106 resize_filter->coefficient[5]=B+5.0*C;
1107 resize_filter->coefficient[6]=(-1.0/6.0)*B-C;
1112 Expert Option Request for verbose details of the resulting filter.
1114 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1118 if (IfStringTrue(GetImageArtifact(image,"filter:verbose")))
1125 Set the weighting function properly when the weighting function
1126 may not exactly match the filter of the same name. EG: a Point
1127 filter is really uses a Box weighting function with a different
1128 support than is typically used.
1130 if (resize_filter->filter == Box) filter_type=BoxFilter;
1131 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1132 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1133 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1134 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1135 if (resize_filter->window == Box) window_type=BoxFilter;
1136 if (resize_filter->window == Sinc) window_type=SincFilter;
1137 if (resize_filter->window == SincFast) window_type=SincFastFilter;
1138 if (resize_filter->window == Jinc) window_type=JincFilter;
1139 if (resize_filter->window == CubicBC) window_type=CubicFilter;
1141 Report Filter Details.
1143 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1144 (void) FormatLocaleFile(stdout,
1145 "# Resampling Filter (for graphing)\n#\n");
1146 (void) FormatLocaleFile(stdout,"# filter = %s\n",
1147 CommandOptionToMnemonic(MagickFilterOptions,filter_type));
1148 (void) FormatLocaleFile(stdout,"# window = %s\n",
1149 CommandOptionToMnemonic(MagickFilterOptions,window_type));
1150 (void) FormatLocaleFile(stdout,"# support = %.*g\n",
1151 GetMagickPrecision(),(double) resize_filter->support);
1152 (void) FormatLocaleFile(stdout,"# window-support = %.*g\n",
1153 GetMagickPrecision(),(double) resize_filter->window_support);
1154 (void) FormatLocaleFile(stdout,"# scale-blur = %.*g\n",
1155 GetMagickPrecision(),(double)resize_filter->blur);
1156 if ((filter_type == GaussianFilter) || (window_type == GaussianFilter))
1157 (void) FormatLocaleFile(stdout,"# gaussian-sigma = %.*g\n",
1158 GetMagickPrecision(),(double)resize_filter->coefficient[0]);
1159 if ( filter_type == KaiserFilter || window_type == KaiserFilter )
1160 (void) FormatLocaleFile(stdout,"# kaiser-beta = %.*g\n",
1161 GetMagickPrecision(),(double)resize_filter->coefficient[0]);
1162 (void) FormatLocaleFile(stdout,"# practical-support = %.*g\n",
1163 GetMagickPrecision(), (double)support);
1164 if ( filter_type == CubicFilter || window_type == CubicFilter )
1165 (void) FormatLocaleFile(stdout,"# B,C = %.*g,%.*g\n",
1166 GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
1167 (void) FormatLocaleFile(stdout,"\n");
1169 Output values of resulting filter graph -- for graphing filter result.
1171 for (x=0.0; x <= support; x+=0.01f)
1172 (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",x,
1173 GetMagickPrecision(),(double)
1174 GetResizeFilterWeight(resize_filter,x));
1176 A final value so gnuplot can graph the 'stop' properly.
1178 (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",support,
1179 GetMagickPrecision(),0.0);
1181 /* Output the above once only for each image - remove setting */
1182 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1183 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1186 return(resize_filter);
1190 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1194 % A d a p t i v e R e s i z e I m a g e %
1198 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1200 % AdaptiveResizeImage() adaptively resize image with pixel resampling.
1202 % This is shortcut function for a fast interpolative resize using mesh
1203 % interpolation. It works well for small resizes of less than +/- 50%
1204 % of the original image size. For larger resizing on images a full
1205 % filtered and slower resize function should be used instead.
1207 % The format of the AdaptiveResizeImage method is:
1209 % Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1210 % const size_t rows,ExceptionInfo *exception)
1212 % A description of each parameter follows:
1214 % o image: the image.
1216 % o columns: the number of columns in the resized image.
1218 % o rows: the number of rows in the resized image.
1220 % o exception: return any errors or warnings in this structure.
1223 MagickExport Image *AdaptiveResizeImage(const Image *image,
1224 const size_t columns,const size_t rows,ExceptionInfo *exception)
1229 resize_image=InterpolativeResizeImage(image,columns,rows,MeshInterpolatePixel,
1231 return(resize_image);
1235 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1239 + B e s s e l O r d e r O n e %
1243 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245 % BesselOrderOne() computes the Bessel function of x of the first kind of
1246 % order 0. This is used to create the Jinc() filter function below.
1248 % Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1254 % j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1256 % where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1258 % cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1259 % = 1/sqrt(2) * (sin(x) - cos(x))
1260 % sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1261 % = -1/sqrt(2) * (sin(x) + cos(x))
1263 % The format of the BesselOrderOne method is:
1265 % double BesselOrderOne(double x)
1267 % A description of each parameter follows:
1269 % o x: double value.
1274 static double I0(double x)
1285 Zeroth order Bessel function of the first kind.
1290 for (i=2; t > MagickEpsilon; i++)
1293 t*=y/((double) i*i);
1299 static double J1(double x)
1311 0.581199354001606143928050809e+21,
1312 -0.6672106568924916298020941484e+20,
1313 0.2316433580634002297931815435e+19,
1314 -0.3588817569910106050743641413e+17,
1315 0.2908795263834775409737601689e+15,
1316 -0.1322983480332126453125473247e+13,
1317 0.3413234182301700539091292655e+10,
1318 -0.4695753530642995859767162166e+7,
1319 0.270112271089232341485679099e+4
1323 0.11623987080032122878585294e+22,
1324 0.1185770712190320999837113348e+20,
1325 0.6092061398917521746105196863e+17,
1326 0.2081661221307607351240184229e+15,
1327 0.5243710262167649715406728642e+12,
1328 0.1013863514358673989967045588e+10,
1329 0.1501793594998585505921097578e+7,
1330 0.1606931573481487801970916749e+4,
1336 for (i=7; i >= 0; i--)
1345 static double P1(double x)
1357 0.352246649133679798341724373e+5,
1358 0.62758845247161281269005675e+5,
1359 0.313539631109159574238669888e+5,
1360 0.49854832060594338434500455e+4,
1361 0.2111529182853962382105718e+3,
1362 0.12571716929145341558495e+1
1366 0.352246649133679798068390431e+5,
1367 0.626943469593560511888833731e+5,
1368 0.312404063819041039923015703e+5,
1369 0.4930396490181088979386097e+4,
1370 0.2030775189134759322293574e+3,
1376 for (i=4; i >= 0; i--)
1378 p=p*(8.0/x)*(8.0/x)+Pone[i];
1379 q=q*(8.0/x)*(8.0/x)+Qone[i];
1385 static double Q1(double x)
1397 0.3511751914303552822533318e+3,
1398 0.7210391804904475039280863e+3,
1399 0.4259873011654442389886993e+3,
1400 0.831898957673850827325226e+2,
1401 0.45681716295512267064405e+1,
1402 0.3532840052740123642735e-1
1406 0.74917374171809127714519505e+4,
1407 0.154141773392650970499848051e+5,
1408 0.91522317015169922705904727e+4,
1409 0.18111867005523513506724158e+4,
1410 0.1038187585462133728776636e+3,
1416 for (i=4; i >= 0; i--)
1418 p=p*(8.0/x)*(8.0/x)+Pone[i];
1419 q=q*(8.0/x)*(8.0/x)+Qone[i];
1424 static double BesselOrderOne(double x)
1437 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1438 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1446 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1450 + D e s t r o y R e s i z e F i l t e r %
1454 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1456 % DestroyResizeFilter() destroy the resize filter.
1458 % The format of the DestroyResizeFilter method is:
1460 % ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1462 % A description of each parameter follows:
1464 % o resize_filter: the resize filter.
1467 MagickPrivate ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1469 assert(resize_filter != (ResizeFilter *) NULL);
1470 assert(resize_filter->signature == MagickSignature);
1471 resize_filter->signature=(~MagickSignature);
1472 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1473 return(resize_filter);
1477 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1481 + G e t R e s i z e F i l t e r S u p p o r t %
1485 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1487 % GetResizeFilterSupport() return the current support window size for this
1488 % filter. Note that this may have been enlarged by filter:blur factor.
1490 % The format of the GetResizeFilterSupport method is:
1492 % double GetResizeFilterSupport(const ResizeFilter *resize_filter)
1494 % A description of each parameter follows:
1496 % o filter: Image filter to use.
1500 MagickPrivate double *GetResizeFilterCoefficient(
1501 const ResizeFilter *resize_filter)
1503 assert(resize_filter != (ResizeFilter *) NULL);
1504 assert(resize_filter->signature == MagickSignature);
1505 return((double *) resize_filter->coefficient);
1508 MagickPrivate double GetResizeFilterBlur(
1509 const ResizeFilter *resize_filter)
1511 assert(resize_filter != (ResizeFilter *) NULL);
1512 assert(resize_filter->signature == MagickSignature);
1513 return(resize_filter->blur);
1516 MagickPrivate double GetResizeFilterScale(
1517 const ResizeFilter *resize_filter)
1519 assert(resize_filter != (ResizeFilter *) NULL);
1520 assert(resize_filter->signature == MagickSignature);
1521 return(resize_filter->scale);
1524 MagickPrivate double GetResizeFilterWindowSupport(
1525 const ResizeFilter *resize_filter)
1527 assert(resize_filter != (ResizeFilter *) NULL);
1528 assert(resize_filter->signature == MagickSignature);
1529 return(resize_filter->window_support);
1532 MagickPrivate ResizeWeightingFunctionType GetResizeFilterWeightingType(
1533 const ResizeFilter *resize_filter)
1535 assert(resize_filter != (ResizeFilter *) NULL);
1536 assert(resize_filter->signature == MagickSignature);
1537 return(resize_filter->filterWeightingType);
1540 MagickPrivate ResizeWeightingFunctionType GetResizeFilterWindowWeightingType(
1541 const ResizeFilter *resize_filter)
1543 assert(resize_filter != (ResizeFilter *) NULL);
1544 assert(resize_filter->signature == MagickSignature);
1545 return(resize_filter->windowWeightingType);
1548 MagickPrivate double GetResizeFilterSupport(
1549 const ResizeFilter *resize_filter)
1551 assert(resize_filter != (ResizeFilter *) NULL);
1552 assert(resize_filter->signature == MagickSignature);
1553 return(resize_filter->support*resize_filter->blur);
1557 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1561 + G e t R e s i z e F i l t e r W e i g h t %
1565 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1567 % GetResizeFilterWeight evaluates the specified resize filter at the point x
1568 % which usally lies between zero and the filters current 'support' and
1569 % returns the weight of the filter function at that point.
1571 % The format of the GetResizeFilterWeight method is:
1573 % double GetResizeFilterWeight(const ResizeFilter *resize_filter,
1576 % A description of each parameter follows:
1578 % o filter: the filter type.
1583 MagickPrivate double GetResizeFilterWeight(const ResizeFilter *resize_filter,
1592 Windowing function - scale the weighting filter by this amount.
1594 assert(resize_filter != (ResizeFilter *) NULL);
1595 assert(resize_filter->signature == MagickSignature);
1596 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
1597 if ((resize_filter->window_support < MagickEpsilon) ||
1598 (resize_filter->window == Box))
1599 scale=1.0; /* Point or Box Filter -- avoid division by zero */
1602 scale=resize_filter->scale;
1603 scale=resize_filter->window(x_blur*scale,resize_filter);
1605 weight=scale*resize_filter->filter(x_blur,resize_filter);
1610 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1614 % I n t e r p o l a t i v e R e s i z e I m a g e %
1618 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1620 % InterpolativeResizeImage() resizes an image using the specified
1621 % interpolation method.
1623 % The format of the InterpolativeResizeImage method is:
1625 % Image *InterpolativeResizeImage(const Image *image,const size_t columns,
1626 % const size_t rows,const PixelInterpolateMethod method,
1627 % ExceptionInfo *exception)
1629 % A description of each parameter follows:
1631 % o image: the image.
1633 % o columns: the number of columns in the resized image.
1635 % o rows: the number of rows in the resized image.
1637 % o method: the pixel interpolation method.
1639 % o exception: return any errors or warnings in this structure.
1642 MagickExport Image *InterpolativeResizeImage(const Image *image,
1643 const size_t columns,const size_t rows,const PixelInterpolateMethod method,
1644 ExceptionInfo *exception)
1646 #define InterpolativeResizeImageTag "Resize/Image"
1668 Interpolatively resize image.
1670 assert(image != (const Image *) NULL);
1671 assert(image->signature == MagickSignature);
1672 if (image->debug != MagickFalse)
1673 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1674 assert(exception != (ExceptionInfo *) NULL);
1675 assert(exception->signature == MagickSignature);
1676 if ((columns == 0) || (rows == 0))
1677 return((Image *) NULL);
1678 if ((columns == image->columns) && (rows == image->rows))
1679 return(CloneImage(image,0,0,MagickTrue,exception));
1680 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1681 if (resize_image == (Image *) NULL)
1682 return((Image *) NULL);
1683 if (SetImageStorageClass(resize_image,DirectClass,exception) == MagickFalse)
1685 resize_image=DestroyImage(resize_image);
1686 return((Image *) NULL);
1690 image_view=AcquireVirtualCacheView(image,exception);
1691 resize_view=AcquireAuthenticCacheView(resize_image,exception);
1692 scale.x=(double) image->columns/resize_image->columns;
1693 scale.y=(double) image->rows/resize_image->rows;
1694 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1695 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1696 magick_threads(image,resize_image,resize_image->rows,1)
1698 for (y=0; y < (ssize_t) resize_image->rows; y++)
1709 if (status == MagickFalse)
1711 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1713 if (q == (Quantum *) NULL)
1715 offset.y=((double) y+0.5)*scale.y-0.5;
1716 for (x=0; x < (ssize_t) resize_image->columns; x++)
1721 if (GetPixelReadMask(resize_image,q) == 0)
1723 q+=GetPixelChannels(resize_image);
1726 for (i=0; i < (ssize_t) GetPixelChannels(resize_image); i++)
1735 channel=GetPixelChannelChannel(image,i);
1736 traits=GetPixelChannelTraits(image,channel);
1737 resize_traits=GetPixelChannelTraits(resize_image,channel);
1738 if ((traits == UndefinedPixelTrait) ||
1739 (resize_traits == UndefinedPixelTrait))
1741 offset.x=((double) x+0.5)*scale.x-0.5;
1742 status=InterpolatePixelChannels(image,image_view,resize_image,method,
1743 offset.x,offset.y,q,exception);
1745 q+=GetPixelChannels(resize_image);
1747 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1749 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1754 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1755 #pragma omp critical (MagickCore_InterpolativeResizeImage)
1757 proceed=SetImageProgress(image,InterpolativeResizeImageTag,progress++,
1759 if (proceed == MagickFalse)
1763 resize_view=DestroyCacheView(resize_view);
1764 image_view=DestroyCacheView(image_view);
1765 if (status == MagickFalse)
1766 resize_image=DestroyImage(resize_image);
1767 return(resize_image);
1769 #if defined(MAGICKCORE_LQR_DELEGATE)
1772 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1776 % L i q u i d R e s c a l e I m a g e %
1780 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1782 % LiquidRescaleImage() rescales image with seam carving.
1784 % The format of the LiquidRescaleImage method is:
1786 % Image *LiquidRescaleImage(const Image *image,const size_t columns,
1787 % const size_t rows,const double delta_x,const double rigidity,
1788 % ExceptionInfo *exception)
1790 % A description of each parameter follows:
1792 % o image: the image.
1794 % o columns: the number of columns in the rescaled image.
1796 % o rows: the number of rows in the rescaled image.
1798 % o delta_x: maximum seam transversal step (0 means straight seams).
1800 % o rigidity: introduce a bias for non-straight seams (typically 0).
1802 % o exception: return any errors or warnings in this structure.
1805 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1806 const size_t rows,const double delta_x,const double rigidity,
1807 ExceptionInfo *exception)
1809 #define LiquidRescaleImageTag "Rescale/Image"
1845 Liquid rescale image.
1847 assert(image != (const Image *) NULL);
1848 assert(image->signature == MagickSignature);
1849 if (image->debug != MagickFalse)
1850 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1851 assert(exception != (ExceptionInfo *) NULL);
1852 assert(exception->signature == MagickSignature);
1853 if ((columns == 0) || (rows == 0))
1854 return((Image *) NULL);
1855 if ((columns == image->columns) && (rows == image->rows))
1856 return(CloneImage(image,0,0,MagickTrue,exception));
1857 if ((columns <= 2) || (rows <= 2))
1858 return(ResizeImage(image,columns,rows,image->filter,exception));
1859 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1869 Honor liquid resize size limitations.
1871 for (width=image->columns; columns >= (2*width-1); width*=2) ;
1872 for (height=image->rows; rows >= (2*height-1); height*=2) ;
1873 resize_image=ResizeImage(image,width,height,image->filter,exception);
1874 if (resize_image == (Image *) NULL)
1875 return((Image *) NULL);
1876 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1877 rigidity,exception);
1878 resize_image=DestroyImage(resize_image);
1879 return(rescale_image);
1881 pixel_info=AcquireVirtualMemory(image->columns,image->rows*
1882 GetPixelChannels(image)*sizeof(*pixels));
1883 if (pixel_info == (MemoryInfo *) NULL)
1884 return((Image *) NULL);
1885 pixels=(gfloat *) GetVirtualMemoryBlob(pixel_info);
1888 image_view=AcquireVirtualCacheView(image,exception);
1889 for (y=0; y < (ssize_t) image->rows; y++)
1891 register const Quantum
1897 if (status == MagickFalse)
1899 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1900 if (p == (const Quantum *) NULL)
1905 for (x=0; x < (ssize_t) image->columns; x++)
1910 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1911 *q++=QuantumScale*p[i];
1912 p+=GetPixelChannels(image);
1915 image_view=DestroyCacheView(image_view);
1916 carver=lqr_carver_new_ext(pixels,(int) image->columns,(int) image->rows,
1917 (int) GetPixelChannels(image),LQR_COLDEPTH_32F);
1918 if (carver == (LqrCarver *) NULL)
1920 pixel_info=RelinquishVirtualMemory(pixel_info);
1921 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1923 lqr_carver_set_preserve_input_image(carver);
1924 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1925 lqr_status=lqr_carver_resize(carver,(int) columns,(int) rows);
1927 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1928 lqr_carver_get_height(carver),MagickTrue,exception);
1929 if (rescale_image == (Image *) NULL)
1931 pixel_info=RelinquishVirtualMemory(pixel_info);
1932 return((Image *) NULL);
1934 if (SetImageStorageClass(rescale_image,DirectClass,exception) == MagickFalse)
1936 pixel_info=RelinquishVirtualMemory(pixel_info);
1937 rescale_image=DestroyImage(rescale_image);
1938 return((Image *) NULL);
1940 rescale_view=AcquireAuthenticCacheView(rescale_image,exception);
1941 (void) lqr_carver_scan_reset(carver);
1942 while (lqr_carver_scan_ext(carver,&x_offset,&y_offset,(void **) &packet) != 0)
1950 q=QueueCacheViewAuthenticPixels(rescale_view,x_offset,y_offset,1,1,
1952 if (q == (Quantum *) NULL)
1954 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1963 channel=GetPixelChannelChannel(image,i);
1964 traits=GetPixelChannelTraits(image,channel);
1965 rescale_traits=GetPixelChannelTraits(rescale_image,channel);
1966 if ((traits == UndefinedPixelTrait) ||
1967 (rescale_traits == UndefinedPixelTrait))
1969 SetPixelChannel(rescale_image,channel,ClampToQuantum(QuantumRange*
1972 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
1975 rescale_view=DestroyCacheView(rescale_view);
1976 pixel_info=RelinquishVirtualMemory(pixel_info);
1977 lqr_carver_destroy(carver);
1978 return(rescale_image);
1981 MagickExport Image *LiquidRescaleImage(const Image *image,
1982 const size_t magick_unused(columns),const size_t magick_unused(rows),
1983 const double magick_unused(delta_x),const double magick_unused(rigidity),
1984 ExceptionInfo *exception)
1986 assert(image != (const Image *) NULL);
1987 assert(image->signature == MagickSignature);
1988 if (image->debug != MagickFalse)
1989 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1990 assert(exception != (ExceptionInfo *) NULL);
1991 assert(exception->signature == MagickSignature);
1992 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1993 "DelegateLibrarySupportNotBuiltIn","'%s' (LQR)",image->filename);
1994 return((Image *) NULL);
1999 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2003 % M a g n i f y I m a g e %
2007 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2009 % MagnifyImage() doubles the size of the image with a pixel art scaling
2012 % The format of the MagnifyImage method is:
2014 % Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
2016 % A description of each parameter follows:
2018 % o image: the image.
2020 % o exception: return any errors or warnings in this structure.
2023 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
2025 #define MagnifyImageTag "Magnify/Image"
2044 Initialize magnified image attributes.
2046 assert(image != (const Image *) NULL);
2047 assert(image->signature == MagickSignature);
2048 if (image->debug != MagickFalse)
2049 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2050 assert(exception != (ExceptionInfo *) NULL);
2051 assert(exception->signature == MagickSignature);
2052 magnify_image=CloneImage(image,2*image->columns,2*image->rows,MagickTrue,
2054 if (magnify_image == (Image *) NULL)
2055 return((Image *) NULL);
2061 image_view=AcquireVirtualCacheView(image,exception);
2062 magnify_view=AcquireAuthenticCacheView(magnify_image,exception);
2063 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2064 #pragma omp parallel for schedule(static,4) shared(progress,status) \
2065 magick_threads(image,magnify_image,image->rows,1)
2067 for (y=0; y < (ssize_t) image->rows; y++)
2075 if (status == MagickFalse)
2077 q=QueueCacheViewAuthenticPixels(magnify_view,0,2*y,magnify_image->columns,2,
2079 if (q == (Quantum *) NULL)
2085 Magnify this row of pixels.
2087 for (x=0; x < (ssize_t) image->columns; x++)
2092 register const Quantum
2104 p=GetCacheViewVirtualPixels(image_view,x-1,y-1,3,3,exception);
2105 if (p == (const Quantum *) NULL)
2110 channels=GetPixelChannels(image);
2111 for (i=0; i < 9; i++)
2112 intensity[i]=GetPixelIntensity(image,p+i*channels);
2114 if ((fabs(intensity[1]-intensity[7]) < MagickEpsilon) ||
2115 (fabs(intensity[3]-intensity[5]) < MagickEpsilon))
2120 for (i=0; i < (ssize_t) channels; i++)
2121 r[i]=p[4*channels+i];
2122 r+=GetPixelChannels(magnify_image);
2123 for (i=0; i < (ssize_t) channels; i++)
2124 r[i]=p[4*channels+i];
2125 r+=(magnify_image->columns-1)*GetPixelChannels(magnify_image);
2126 for (i=0; i < (ssize_t) channels; i++)
2127 r[i]=p[4*channels+i];
2128 r+=GetPixelChannels(magnify_image);
2129 for (i=0; i < (ssize_t) channels; i++)
2130 r[i]=p[4*channels+i];
2135 Selectively clone pixel.
2137 if (fabs(intensity[1]-intensity[3]) < MagickEpsilon)
2138 for (i=0; i < (ssize_t) channels; i++)
2139 r[i]=p[3*channels+i];
2141 for (i=0; i < (ssize_t) channels; i++)
2142 r[i]=p[4*channels+i];
2143 r+=GetPixelChannels(magnify_image);
2144 if (fabs(intensity[1]-intensity[5]) < MagickEpsilon)
2145 for (i=0; i < (ssize_t) channels; i++)
2146 r[i]=p[5*channels+i];
2148 for (i=0; i < (ssize_t) channels; i++)
2149 r[i]=p[4*channels+i];
2150 r+=(magnify_image->columns-1)*GetPixelChannels(magnify_image);
2151 if (fabs(intensity[3]-intensity[7]) < MagickEpsilon)
2152 for (i=0; i < (ssize_t) channels; i++)
2153 r[i]=p[3*channels+i];
2155 for (i=0; i < (ssize_t) channels; i++)
2156 r[i]=p[4*channels+i];
2157 r+=GetPixelChannels(magnify_image);
2158 if (fabs(intensity[5]-intensity[7]) < MagickEpsilon)
2159 for (i=0; i < (ssize_t) channels; i++)
2160 r[i]=p[5*channels+i];
2162 for (i=0; i < (ssize_t) channels; i++)
2163 r[i]=p[4*channels+i];
2165 q+=2*GetPixelChannels(magnify_image);
2167 if (SyncCacheViewAuthenticPixels(magnify_view,exception) == MagickFalse)
2169 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2174 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2175 #pragma omp critical (MagickCore_MagnifyImage)
2177 proceed=SetImageProgress(image,MagnifyImageTag,progress++,image->rows);
2178 if (proceed == MagickFalse)
2182 magnify_view=DestroyCacheView(magnify_view);
2183 image_view=DestroyCacheView(image_view);
2184 if (status == MagickFalse)
2185 magnify_image=DestroyImage(magnify_image);
2186 return(magnify_image);
2190 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2194 % M i n i f y I m a g e %
2198 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2200 % MinifyImage() is a convenience method that scales an image proportionally to
2203 % The format of the MinifyImage method is:
2205 % Image *MinifyImage(const Image *image,ExceptionInfo *exception)
2207 % A description of each parameter follows:
2209 % o image: the image.
2211 % o exception: return any errors or warnings in this structure.
2214 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
2219 assert(image != (Image *) NULL);
2220 assert(image->signature == MagickSignature);
2221 if (image->debug != MagickFalse)
2222 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2223 assert(exception != (ExceptionInfo *) NULL);
2224 assert(exception->signature == MagickSignature);
2225 minify_image=ResizeImage(image,image->columns/2,image->rows/2,SplineFilter,
2227 return(minify_image);
2231 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2235 % R e s a m p l e I m a g e %
2239 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2241 % ResampleImage() resize image in terms of its pixel size, so that when
2242 % displayed at the given resolution it will be the same size in terms of
2243 % real world units as the original image at the original resolution.
2245 % The format of the ResampleImage method is:
2247 % Image *ResampleImage(Image *image,const double x_resolution,
2248 % const double y_resolution,const FilterTypes filter,
2249 % ExceptionInfo *exception)
2251 % A description of each parameter follows:
2253 % o image: the image to be resized to fit the given resolution.
2255 % o x_resolution: the new image x resolution.
2257 % o y_resolution: the new image y resolution.
2259 % o filter: Image filter to use.
2261 % o exception: return any errors or warnings in this structure.
2264 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
2265 const double y_resolution,const FilterTypes filter,ExceptionInfo *exception)
2267 #define ResampleImageTag "Resample/Image"
2277 Initialize sampled image attributes.
2279 assert(image != (const Image *) NULL);
2280 assert(image->signature == MagickSignature);
2281 if (image->debug != MagickFalse)
2282 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2283 assert(exception != (ExceptionInfo *) NULL);
2284 assert(exception->signature == MagickSignature);
2285 width=(size_t) (x_resolution*image->columns/(image->resolution.x == 0.0 ?
2286 72.0 : image->resolution.x)+0.5);
2287 height=(size_t) (y_resolution*image->rows/(image->resolution.y == 0.0 ?
2288 72.0 : image->resolution.y)+0.5);
2289 resample_image=ResizeImage(image,width,height,filter,exception);
2290 if (resample_image != (Image *) NULL)
2292 resample_image->resolution.x=x_resolution;
2293 resample_image->resolution.y=y_resolution;
2295 return(resample_image);
2299 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2303 % R e s i z e I m a g e %
2307 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2309 % ResizeImage() scales an image to the desired dimensions, using the given
2310 % filter (see AcquireFilterInfo()).
2312 % If an undefined filter is given the filter defaults to Mitchell for a
2313 % colormapped image, a image with a matte channel, or if the image is
2314 % enlarged. Otherwise the filter defaults to a Lanczos.
2316 % ResizeImage() was inspired by Paul Heckbert's "zoom" program.
2318 % The format of the ResizeImage method is:
2320 % Image *ResizeImage(Image *image,const size_t columns,const size_t rows,
2321 % const FilterTypes filter,ExceptionInfo *exception)
2323 % A description of each parameter follows:
2325 % o image: the image.
2327 % o columns: the number of columns in the scaled image.
2329 % o rows: the number of rows in the scaled image.
2331 % o filter: Image filter to use.
2333 % o exception: return any errors or warnings in this structure.
2337 typedef struct _ContributionInfo
2346 static ContributionInfo **DestroyContributionThreadSet(
2347 ContributionInfo **contribution)
2352 assert(contribution != (ContributionInfo **) NULL);
2353 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
2354 if (contribution[i] != (ContributionInfo *) NULL)
2355 contribution[i]=(ContributionInfo *) RelinquishAlignedMemory(
2357 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
2358 return(contribution);
2361 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
2372 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
2373 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
2374 sizeof(*contribution));
2375 if (contribution == (ContributionInfo **) NULL)
2376 return((ContributionInfo **) NULL);
2377 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
2378 for (i=0; i < (ssize_t) number_threads; i++)
2380 contribution[i]=(ContributionInfo *) MagickAssumeAligned(
2381 AcquireAlignedMemory(count,sizeof(**contribution)));
2382 if (contribution[i] == (ContributionInfo *) NULL)
2383 return(DestroyContributionThreadSet(contribution));
2385 return(contribution);
2388 static inline double MagickMax(const double x,const double y)
2395 static inline double MagickMin(const double x,const double y)
2402 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2403 const Image *image,Image *resize_image,const double x_factor,
2404 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2406 #define ResizeImageTag "Resize/Image"
2416 **restrict contributions;
2429 Apply filter to resize horizontally from image to resize image.
2431 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
2432 support=scale*GetResizeFilterSupport(resize_filter);
2433 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2434 if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
2435 return(MagickFalse);
2439 Support too small even for nearest neighbour: Reduce to point sampling.
2441 support=(double) 0.5;
2444 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2445 if (contributions == (ContributionInfo **) NULL)
2447 (void) ThrowMagickException(exception,GetMagickModule(),
2448 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2449 return(MagickFalse);
2452 scale=PerceptibleReciprocal(scale);
2453 image_view=AcquireVirtualCacheView(image,exception);
2454 resize_view=AcquireAuthenticCacheView(resize_image,exception);
2455 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2456 #pragma omp parallel for schedule(static,4) shared(status) \
2457 magick_threads(image,resize_image,resize_image->columns,1)
2459 for (x=0; x < (ssize_t) resize_image->columns; x++)
2465 register const Quantum
2468 register ContributionInfo
2469 *restrict contribution;
2482 if (status == MagickFalse)
2484 bisect=(double) (x+0.5)/x_factor+MagickEpsilon;
2485 start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
2486 stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->columns);
2488 contribution=contributions[GetOpenMPThreadId()];
2489 for (n=0; n < (stop-start); n++)
2491 contribution[n].pixel=start+n;
2492 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2493 ((double) (start+n)-bisect+0.5));
2494 density+=contribution[n].weight;
2496 if ((density != 0.0) && (density != 1.0))
2504 density=PerceptibleReciprocal(density);
2505 for (i=0; i < n; i++)
2506 contribution[i].weight*=density;
2508 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2509 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
2510 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2512 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2517 for (y=0; y < (ssize_t) resize_image->rows; y++)
2522 for (i=0; i < (ssize_t) GetPixelChannels(resize_image); i++)
2542 channel=GetPixelChannelChannel(image,i);
2543 traits=GetPixelChannelTraits(image,channel);
2544 resize_traits=GetPixelChannelTraits(resize_image,channel);
2545 if ((traits == UndefinedPixelTrait) ||
2546 (resize_traits == UndefinedPixelTrait))
2548 if (((resize_traits & CopyPixelTrait) != 0) ||
2549 (GetPixelReadMask(resize_image,q) == 0))
2551 j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
2553 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2554 (contribution[j-start].pixel-contribution[0].pixel);
2555 SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
2560 if ((resize_traits & BlendPixelTrait) == 0)
2565 for (j=0; j < n; j++)
2567 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2568 (contribution[j].pixel-contribution[0].pixel);
2569 alpha=contribution[j].weight;
2570 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2572 SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
2579 for (j=0; j < n; j++)
2581 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2582 (contribution[j].pixel-contribution[0].pixel);
2583 alpha=contribution[j].weight*QuantumScale*
2584 GetPixelAlpha(image,p+k*GetPixelChannels(image));
2585 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2588 gamma=PerceptibleReciprocal(gamma);
2589 SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
2591 q+=GetPixelChannels(resize_image);
2593 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2595 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2600 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2601 #pragma omp critical (MagickCore_HorizontalFilter)
2603 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2604 if (proceed == MagickFalse)
2608 resize_view=DestroyCacheView(resize_view);
2609 image_view=DestroyCacheView(image_view);
2610 contributions=DestroyContributionThreadSet(contributions);
2614 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2615 const Image *image,Image *resize_image,const double y_factor,
2616 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2626 **restrict contributions;
2639 Apply filter to resize vertically from image to resize image.
2641 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
2642 support=scale*GetResizeFilterSupport(resize_filter);
2643 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2644 if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse)
2645 return(MagickFalse);
2649 Support too small even for nearest neighbour: Reduce to point sampling.
2651 support=(double) 0.5;
2654 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2655 if (contributions == (ContributionInfo **) NULL)
2657 (void) ThrowMagickException(exception,GetMagickModule(),
2658 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2659 return(MagickFalse);
2662 scale=PerceptibleReciprocal(scale);
2663 image_view=AcquireVirtualCacheView(image,exception);
2664 resize_view=AcquireAuthenticCacheView(resize_image,exception);
2665 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2666 #pragma omp parallel for schedule(static,4) shared(status) \
2667 magick_threads(image,resize_image,resize_image->rows,1)
2669 for (y=0; y < (ssize_t) resize_image->rows; y++)
2675 register const Quantum
2678 register ContributionInfo
2679 *restrict contribution;
2692 if (status == MagickFalse)
2694 bisect=(double) (y+0.5)/y_factor+MagickEpsilon;
2695 start=(ssize_t) MagickMax(bisect-support+0.5,0.0);
2696 stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->rows);
2698 contribution=contributions[GetOpenMPThreadId()];
2699 for (n=0; n < (stop-start); n++)
2701 contribution[n].pixel=start+n;
2702 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2703 ((double) (start+n)-bisect+0.5));
2704 density+=contribution[n].weight;
2706 if ((density != 0.0) && (density != 1.0))
2714 density=PerceptibleReciprocal(density);
2715 for (i=0; i < n; i++)
2716 contribution[i].weight*=density;
2718 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
2719 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2721 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2723 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2728 for (x=0; x < (ssize_t) resize_image->columns; x++)
2733 for (i=0; i < (ssize_t) GetPixelChannels(resize_image); i++)
2753 channel=GetPixelChannelChannel(image,i);
2754 traits=GetPixelChannelTraits(image,channel);
2755 resize_traits=GetPixelChannelTraits(resize_image,channel);
2756 if ((traits == UndefinedPixelTrait) ||
2757 (resize_traits == UndefinedPixelTrait))
2759 if (((resize_traits & CopyPixelTrait) != 0) ||
2760 (GetPixelReadMask(resize_image,q) == 0))
2762 j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double)
2764 k=(ssize_t) ((contribution[j-start].pixel-contribution[0].pixel)*
2766 SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i],
2771 if ((resize_traits & BlendPixelTrait) == 0)
2776 for (j=0; j < n; j++)
2778 k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
2780 alpha=contribution[j].weight;
2781 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2783 SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q);
2787 for (j=0; j < n; j++)
2789 k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)*
2791 alpha=contribution[j].weight*QuantumScale*GetPixelAlpha(image,p+k*
2792 GetPixelChannels(image));
2793 pixel+=alpha*p[k*GetPixelChannels(image)+i];
2796 gamma=PerceptibleReciprocal(gamma);
2797 SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q);
2799 q+=GetPixelChannels(resize_image);
2801 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2803 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2808 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2809 #pragma omp critical (MagickCore_VerticalFilter)
2811 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2812 if (proceed == MagickFalse)
2816 resize_view=DestroyCacheView(resize_view);
2817 image_view=DestroyCacheView(image_view);
2818 contributions=DestroyContributionThreadSet(contributions);
2822 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2823 const size_t rows,const FilterTypes filter,ExceptionInfo *exception)
2849 Acquire resize image.
2851 assert(image != (Image *) NULL);
2852 assert(image->signature == MagickSignature);
2853 if (image->debug != MagickFalse)
2854 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2855 assert(exception != (ExceptionInfo *) NULL);
2856 assert(exception->signature == MagickSignature);
2857 if ((columns == 0) || (rows == 0))
2858 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2859 if ((columns == image->columns) && (rows == image->rows) &&
2860 (filter == UndefinedFilter))
2861 return(CloneImage(image,0,0,MagickTrue,exception));
2862 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2863 if (resize_image == (Image *) NULL)
2864 return(resize_image);
2866 Acquire resize filter.
2868 x_factor=(double) columns/(double) image->columns;
2869 y_factor=(double) rows/(double) image->rows;
2870 if (x_factor > y_factor)
2871 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2873 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2874 if (filter_image == (Image *) NULL)
2875 return(DestroyImage(resize_image));
2876 filter_type=LanczosFilter;
2877 if (filter != UndefinedFilter)
2880 if ((x_factor == 1.0) && (y_factor == 1.0))
2881 filter_type=PointFilter;
2883 if ((image->storage_class == PseudoClass) ||
2884 (image->alpha_trait == BlendPixelTrait) ||
2885 ((x_factor*y_factor) > 1.0))
2886 filter_type=MitchellFilter;
2887 resize_filter=AcquireResizeFilter(image,filter_type,MagickFalse,exception);
2892 if (x_factor > y_factor)
2894 span=(MagickSizeType) (filter_image->columns+rows);
2895 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
2897 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
2898 span,&offset,exception);
2902 span=(MagickSizeType) (filter_image->rows+columns);
2903 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
2905 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
2906 span,&offset,exception);
2911 filter_image=DestroyImage(filter_image);
2912 resize_filter=DestroyResizeFilter(resize_filter);
2913 if (status == MagickFalse)
2915 resize_image=DestroyImage(resize_image);
2916 return((Image *) NULL);
2918 resize_image->type=image->type;
2919 return(resize_image);
2923 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2927 % S a m p l e I m a g e %
2931 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2933 % SampleImage() scales an image to the desired dimensions with pixel
2934 % sampling. Unlike other scaling methods, this method does not introduce
2935 % any additional color into the scaled image.
2937 % The format of the SampleImage method is:
2939 % Image *SampleImage(const Image *image,const size_t columns,
2940 % const size_t rows,ExceptionInfo *exception)
2942 % A description of each parameter follows:
2944 % o image: the image.
2946 % o columns: the number of columns in the sampled image.
2948 % o rows: the number of rows in the sampled image.
2950 % o exception: return any errors or warnings in this structure.
2953 MagickExport Image *SampleImage(const Image *image,const size_t columns,
2954 const size_t rows,ExceptionInfo *exception)
2956 #define SampleImageTag "Sample/Image"
2982 Initialize sampled image attributes.
2984 assert(image != (const Image *) NULL);
2985 assert(image->signature == MagickSignature);
2986 if (image->debug != MagickFalse)
2987 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2988 assert(exception != (ExceptionInfo *) NULL);
2989 assert(exception->signature == MagickSignature);
2990 if ((columns == 0) || (rows == 0))
2991 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2992 if ((columns == image->columns) && (rows == image->rows))
2993 return(CloneImage(image,0,0,MagickTrue,exception));
2994 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2995 if (sample_image == (Image *) NULL)
2996 return((Image *) NULL);
2998 Set the sampling offset, default is in the mid-point of sample regions.
3000 sample_offset.x=sample_offset.y=0.5-MagickEpsilon;
3005 value=GetImageArtifact(image,"sample:offset");
3006 if (value != (char *) NULL)
3014 (void) ParseGeometry(value,&geometry_info);
3015 flags=ParseGeometry(value,&geometry_info);
3016 sample_offset.x=sample_offset.y=geometry_info.rho/100.0-MagickEpsilon;
3017 if ((flags & SigmaValue) != 0)
3018 sample_offset.y=geometry_info.sigma/100.0-MagickEpsilon;
3022 Allocate scan line buffer and column offset buffers.
3024 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
3026 if (x_offset == (ssize_t *) NULL)
3028 sample_image=DestroyImage(sample_image);
3029 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3031 for (x=0; x < (ssize_t) sample_image->columns; x++)
3032 x_offset[x]=(ssize_t) ((((double) x+sample_offset.x)*image->columns)/
3033 sample_image->columns);
3039 image_view=AcquireVirtualCacheView(image,exception);
3040 sample_view=AcquireAuthenticCacheView(sample_image,exception);
3041 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3042 #pragma omp parallel for schedule(static,4) shared(status) \
3043 magick_threads(image,sample_image,1,1)
3045 for (y=0; y < (ssize_t) sample_image->rows; y++)
3047 register const Quantum
3059 if (status == MagickFalse)
3061 y_offset=(ssize_t) ((((double) y+sample_offset.y)*image->rows)/
3062 sample_image->rows);
3063 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
3065 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
3067 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3075 for (x=0; x < (ssize_t) sample_image->columns; x++)
3080 if (GetPixelReadMask(sample_image,q) == 0)
3082 q+=GetPixelChannels(sample_image);
3085 for (i=0; i < (ssize_t) GetPixelChannels(sample_image); i++)
3094 channel=GetPixelChannelChannel(image,i);
3095 traits=GetPixelChannelTraits(image,channel);
3096 sample_traits=GetPixelChannelTraits(sample_image,channel);
3097 if ((traits == UndefinedPixelTrait) ||
3098 (sample_traits == UndefinedPixelTrait))
3100 SetPixelChannel(sample_image,channel,p[x_offset[x]*GetPixelChannels(
3103 q+=GetPixelChannels(sample_image);
3105 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
3107 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3112 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3113 #pragma omp critical (MagickCore_SampleImage)
3115 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
3116 if (proceed == MagickFalse)
3120 image_view=DestroyCacheView(image_view);
3121 sample_view=DestroyCacheView(sample_view);
3122 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
3123 sample_image->type=image->type;
3124 if (status == MagickFalse)
3125 sample_image=DestroyImage(sample_image);
3126 return(sample_image);
3130 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3134 % S c a l e I m a g e %
3138 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3140 % ScaleImage() changes the size of an image to the given dimensions.
3142 % The format of the ScaleImage method is:
3144 % Image *ScaleImage(const Image *image,const size_t columns,
3145 % const size_t rows,ExceptionInfo *exception)
3147 % A description of each parameter follows:
3149 % o image: the image.
3151 % o columns: the number of columns in the scaled image.
3153 % o rows: the number of rows in the scaled image.
3155 % o exception: return any errors or warnings in this structure.
3158 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
3159 const size_t rows,ExceptionInfo *exception)
3161 #define ScaleImageTag "Scale/Image"
3170 pixel[CompositePixelChannel],
3205 Initialize scaled image attributes.
3207 assert(image != (const Image *) NULL);
3208 assert(image->signature == MagickSignature);
3209 if (image->debug != MagickFalse)
3210 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3211 assert(exception != (ExceptionInfo *) NULL);
3212 assert(exception->signature == MagickSignature);
3213 if ((columns == 0) || (rows == 0))
3214 return((Image *) NULL);
3215 if ((columns == image->columns) && (rows == image->rows))
3216 return(CloneImage(image,0,0,MagickTrue,exception));
3217 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
3218 if (scale_image == (Image *) NULL)
3219 return((Image *) NULL);
3220 if (SetImageStorageClass(scale_image,DirectClass,exception) == MagickFalse)
3222 scale_image=DestroyImage(scale_image);
3223 return((Image *) NULL);
3228 x_vector=(double *) AcquireQuantumMemory((size_t) image->columns,
3229 GetPixelChannels(image)*sizeof(*x_vector));
3231 if (image->rows != scale_image->rows)
3232 scanline=(double *) AcquireQuantumMemory((size_t) image->columns,
3233 GetPixelChannels(image)*sizeof(*scanline));
3234 scale_scanline=(double *) AcquireQuantumMemory((size_t)
3235 scale_image->columns,MaxPixelChannels*sizeof(*scale_scanline));
3236 y_vector=(double *) AcquireQuantumMemory((size_t) image->columns,
3237 GetPixelChannels(image)*sizeof(*y_vector));
3238 if ((scanline == (double *) NULL) ||
3239 (scale_scanline == (double *) NULL) ||
3240 (x_vector == (double *) NULL) ||
3241 (y_vector == (double *) NULL))
3243 scale_image=DestroyImage(scale_image);
3244 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3250 next_row=MagickTrue;
3252 scale.y=(double) scale_image->rows/(double) image->rows;
3253 for (i=0; i < (ssize_t) (GetPixelChannels(image)*image->columns); i++)
3257 image_view=AcquireVirtualCacheView(image,exception);
3258 scale_view=AcquireAuthenticCacheView(scale_image,exception);
3259 for (y=0; y < (ssize_t) scale_image->rows; y++)
3261 register const Quantum
3270 if (status == MagickFalse)
3272 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
3274 if (q == (Quantum *) NULL)
3280 if (scale_image->rows == image->rows)
3283 Read a new scanline.
3285 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
3287 if (p == (const Quantum *) NULL)
3292 for (x=0; x < (ssize_t) image->columns; x++)
3294 if (GetPixelReadMask(image,p) == 0)
3296 p+=GetPixelChannels(image);
3299 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3307 channel=GetPixelChannelChannel(image,i);
3308 traits=GetPixelChannelTraits(image,channel);
3309 if ((traits & BlendPixelTrait) == 0)
3311 x_vector[x*GetPixelChannels(image)+i]=(double) p[i];
3314 alpha=QuantumScale*GetPixelAlpha(image,p);
3315 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
3317 p+=GetPixelChannels(image);
3325 while (scale.y < span.y)
3327 if ((next_row != MagickFalse) &&
3328 (number_rows < (ssize_t) image->rows))
3331 Read a new scanline.
3333 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
3335 if (p == (const Quantum *) NULL)
3340 for (x=0; x < (ssize_t) image->columns; x++)
3342 if (GetPixelReadMask(image,p) == 0)
3344 p+=GetPixelChannels(image);
3347 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3355 channel=GetPixelChannelChannel(image,i);
3356 traits=GetPixelChannelTraits(image,channel);
3357 if ((traits & BlendPixelTrait) == 0)
3359 x_vector[x*GetPixelChannels(image)+i]=(double)
3363 alpha=QuantumScale*GetPixelAlpha(image,p);
3364 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
3366 p+=GetPixelChannels(image);
3370 for (x=0; x < (ssize_t) image->columns; x++)
3371 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3372 y_vector[x*GetPixelChannels(image)+i]+=scale.y*
3373 x_vector[x*GetPixelChannels(image)+i];
3375 scale.y=(double) scale_image->rows/(double) image->rows;
3376 next_row=MagickTrue;
3378 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
3381 Read a new scanline.
3383 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1,
3385 if (p == (const Quantum *) NULL)
3390 for (x=0; x < (ssize_t) image->columns; x++)
3392 if (GetPixelReadMask(image,p) == 0)
3394 p+=GetPixelChannels(image);
3397 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3405 channel=GetPixelChannelChannel(image,i);
3406 traits=GetPixelChannelTraits(image,channel);
3407 if ((traits & BlendPixelTrait) == 0)
3409 x_vector[x*GetPixelChannels(image)+i]=(double) p[i];
3412 alpha=QuantumScale*GetPixelAlpha(image,p);
3413 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i];
3415 p+=GetPixelChannels(image);
3418 next_row=MagickFalse;
3420 for (x=0; x < (ssize_t) image->columns; x++)
3422 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3424 pixel[i]=y_vector[x*GetPixelChannels(image)+i]+span.y*
3425 x_vector[x*GetPixelChannels(image)+i];
3426 scanline[x*GetPixelChannels(image)+i]=pixel[i];
3427 y_vector[x*GetPixelChannels(image)+i]=0.0;
3433 scale.y=(double) scale_image->rows/(double) image->rows;
3434 next_row=MagickTrue;
3438 if (scale_image->columns == image->columns)
3441 Transfer scanline to scaled image.
3443 for (x=0; x < (ssize_t) scale_image->columns; x++)
3445 if (GetPixelReadMask(scale_image,q) == 0)
3447 q+=GetPixelChannels(scale_image);
3450 for (i=0; i < (ssize_t) GetPixelChannels(scale_image); i++)
3455 channel=GetPixelChannelChannel(scale_image,i);
3456 traits=GetPixelChannelTraits(image,channel);
3457 scale_traits=GetPixelChannelTraits(scale_image,channel);
3458 if ((traits == UndefinedPixelTrait) ||
3459 (scale_traits == UndefinedPixelTrait))
3461 offset=GetPixelChannelOffset(image,channel);
3462 if ((traits & BlendPixelTrait) == 0)
3464 SetPixelChannel(scale_image,channel,ClampToQuantum(
3465 scanline[x*GetPixelChannels(image)+offset]),q);
3468 alpha=QuantumScale*scanline[x*GetPixelChannels(image)+
3469 GetPixelChannelChannel(image,AlphaPixelChannel)];
3470 gamma=PerceptibleReciprocal(alpha);
3471 SetPixelChannel(scale_image,channel,ClampToQuantum(gamma*scanline[
3472 x*GetPixelChannels(image)+offset]),q);
3474 q+=GetPixelChannels(scale_image);
3485 next_column=MagickFalse;
3488 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3490 for (x=0; x < (ssize_t) image->columns; x++)
3492 scale.x=(double) scale_image->columns/(double) image->columns;
3493 while (scale.x >= span.x)
3495 if (next_column != MagickFalse)
3497 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3501 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3509 channel=GetPixelChannelChannel(image,i);
3510 traits=GetPixelChannelTraits(image,channel);
3511 if (traits == UndefinedPixelTrait)
3513 pixel[i]+=span.x*scanline[x*GetPixelChannels(image)+i];
3514 scale_scanline[n*MaxPixelChannels+channel]=pixel[i];
3518 next_column=MagickTrue;
3522 if (next_column != MagickFalse)
3524 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3527 next_column=MagickFalse;
3529 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3530 pixel[i]+=scale.x*scanline[x*GetPixelChannels(image)+i];
3536 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3537 pixel[i]+=span.x*scanline[(x-1)*GetPixelChannels(image)+i];
3539 if ((next_column == MagickFalse) &&
3540 ((ssize_t) n < (ssize_t) scale_image->columns))
3541 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3543 channel=GetPixelChannelChannel(image,i);
3544 scale_scanline[n*MaxPixelChannels+channel]=pixel[i];
3547 Transfer scanline to scaled image.
3549 for (x=0; x < (ssize_t) scale_image->columns; x++)
3551 if (GetPixelReadMask(scale_image,q) == 0)
3553 q+=GetPixelChannels(scale_image);
3556 for (i=0; i < (ssize_t) GetPixelChannels(scale_image); i++)
3558 channel=GetPixelChannelChannel(scale_image,i);
3559 traits=GetPixelChannelTraits(image,channel);
3560 scale_traits=GetPixelChannelTraits(scale_image,channel);
3561 if ((traits == UndefinedPixelTrait) ||
3562 (scale_traits == UndefinedPixelTrait))
3564 if ((traits & BlendPixelTrait) == 0)
3566 SetPixelChannel(scale_image,channel,ClampToQuantum(
3567 scale_scanline[x*MaxPixelChannels+channel]),q);
3570 SetPixelChannel(scale_image,channel,ClampToQuantum(scale_scanline[
3571 x*MaxPixelChannels+channel]),q);
3573 q+=GetPixelChannels(scale_image);
3576 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
3581 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3583 if (proceed == MagickFalse)
3589 scale_view=DestroyCacheView(scale_view);
3590 image_view=DestroyCacheView(image_view);
3592 Free allocated memory.
3594 y_vector=(double *) RelinquishMagickMemory(y_vector);
3595 scale_scanline=(double *) RelinquishMagickMemory(scale_scanline);
3596 if (scale_image->rows != image->rows)
3597 scanline=(double *) RelinquishMagickMemory(scanline);
3598 x_vector=(double *) RelinquishMagickMemory(x_vector);
3599 scale_image->type=image->type;
3600 if (status == MagickFalse)
3601 scale_image=DestroyImage(scale_image);
3602 return(scale_image);
3606 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3610 % T h u m b n a i l I m a g e %
3614 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3616 % ThumbnailImage() changes the size of an image to the given dimensions and
3617 % removes any associated profiles. The goal is to produce small low cost
3618 % thumbnail images suited for display on the Web.
3620 % The format of the ThumbnailImage method is:
3622 % Image *ThumbnailImage(const Image *image,const size_t columns,
3623 % const size_t rows,ExceptionInfo *exception)
3625 % A description of each parameter follows:
3627 % o image: the image.
3629 % o columns: the number of columns in the scaled image.
3631 % o rows: the number of rows in the scaled image.
3633 % o exception: return any errors or warnings in this structure.
3636 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3637 const size_t rows,ExceptionInfo *exception)
3639 #define SampleFactor 5
3642 value[MaxTextExtent];
3660 assert(image != (Image *) NULL);
3661 assert(image->signature == MagickSignature);
3662 if (image->debug != MagickFalse)
3663 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3664 assert(exception != (ExceptionInfo *) NULL);
3665 assert(exception->signature == MagickSignature);
3666 x_factor=(double) columns/(double) image->columns;
3667 y_factor=(double) rows/(double) image->rows;
3668 if ((x_factor*y_factor) > 0.1)
3669 thumbnail_image=ResizeImage(image,columns,rows,image->filter,exception);
3671 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
3672 thumbnail_image=ResizeImage(image,columns,rows,image->filter,exception);
3678 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3680 if (sample_image == (Image *) NULL)
3681 return((Image *) NULL);
3682 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3684 sample_image=DestroyImage(sample_image);
3686 if (thumbnail_image == (Image *) NULL)
3687 return(thumbnail_image);
3688 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3689 if (thumbnail_image->alpha_trait != BlendPixelTrait)
3690 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel,exception);
3691 thumbnail_image->depth=8;
3692 thumbnail_image->interlace=NoInterlace;
3694 Strip all profiles except color profiles.
3696 ResetImageProfileIterator(thumbnail_image);
3697 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3699 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3701 (void) DeleteImageProfile(thumbnail_image,name);
3702 ResetImageProfileIterator(thumbnail_image);
3704 name=GetNextImageProfile(thumbnail_image);
3706 (void) DeleteImageProperty(thumbnail_image,"comment");
3707 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3708 if (strstr(image->magick_filename,"//") == (char *) NULL)
3709 (void) FormatLocaleString(value,MaxTextExtent,"file://%s",
3710 image->magick_filename);
3711 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value,exception);
3712 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3713 if ( IfMagickTrue(GetPathAttributes(image->filename,&attributes)) )
3715 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3716 attributes.st_mtime);
3717 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value,exception);
3719 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3720 attributes.st_mtime);
3721 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
3722 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
3723 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value,exception);
3724 (void) FormatLocaleString(value,MaxTextExtent,"image/%s",image->magick);
3726 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value,exception);
3727 (void) SetImageProperty(thumbnail_image,"software",GetMagickVersion(&version),
3729 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3730 image->magick_columns);
3731 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value,
3733 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3734 image->magick_rows);
3735 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Height",value,
3737 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
3738 GetImageListLength(image));
3739 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value,
3741 return(thumbnail_image);