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-2010 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42 #include "magick/studio.h"
43 #include "magick/artifact.h"
44 #include "magick/blob.h"
45 #include "magick/cache.h"
46 #include "magick/cache-view.h"
47 #include "magick/color.h"
48 #include "magick/color-private.h"
49 #include "magick/draw.h"
50 #include "magick/exception.h"
51 #include "magick/exception-private.h"
52 #include "magick/gem.h"
53 #include "magick/image.h"
54 #include "magick/image-private.h"
55 #include "magick/list.h"
56 #include "magick/memory_.h"
57 #include "magick/magick.h"
58 #include "magick/pixel-private.h"
59 #include "magick/property.h"
60 #include "magick/monitor.h"
61 #include "magick/monitor-private.h"
62 #include "magick/pixel.h"
63 #include "magick/option.h"
64 #include "magick/resample.h"
65 #include "magick/resize.h"
66 #include "magick/resize-private.h"
67 #include "magick/string_.h"
68 #include "magick/string-private.h"
69 #include "magick/thread-private.h"
70 #include "magick/utility.h"
71 #include "magick/version.h"
72 #if defined(MAGICKCORE_LQR_DELEGATE)
82 (*filter)(const MagickRealType,const ResizeFilter *),
83 (*window)(const MagickRealType,const ResizeFilter *),
84 support, /* filter region of support - the filter support limit */
85 window_support, /* window support, usally equal to support (expert only) */
86 scale, /* dimension scaling to fit window support (usally 1.0) */
87 blur, /* x-scale (blur-sharpen) */
88 coeff[8]; /* cubic coefficents for smooth Cubic filters */
95 Forward declaractions.
99 BesselOrderOne(MagickRealType),
100 Sinc(const MagickRealType, const ResizeFilter *),
101 SincFast(const MagickRealType, const ResizeFilter *);
104 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
108 + F i l t e r F u n c t i o n s %
112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114 % These are the various filter and windowing functions that are provided.
116 % They are internal to this module only. See AcquireResizeFilterInfo() for
117 % details of the access to these functions, via the GetResizeFilterSupport()
118 % and GetResizeFilterWeight() API interface.
120 % The individual filter functions have this format...
122 % static MagickRealtype *FilterName(const MagickRealType x,
123 % const MagickRealType support)
125 % A description of each parameter follows:
127 % o x: the distance from the sampling point generally in the range of 0 to
128 % support. The GetResizeFilterWeight() ensures this a positive value.
130 % o resize_filter: current filter information. This allows function to
131 % access support, and possibly other pre-calculated information defining
136 #define MagickPIL ((MagickRealType) 3.14159265358979323846264338327950288420L)
138 static MagickRealType Jinc(const MagickRealType x,
139 const ResizeFilter *magick_unused(resize_filter))
142 See Pratt "Digital Image Processing" p.97 for Jinc/Bessel functions.
143 http://mathworld.wolfram.com/JincFunction.html and page 11 of
144 http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
146 The original "zoom" program by Paul Heckbert called this "Bessel".
147 But really it is more accurately named "Jinc".
150 return(0.5*MagickPIL);
151 return(BesselOrderOne(MagickPIL*x)/x);
154 static MagickRealType Blackman(const MagickRealType x,
155 const ResizeFilter *magick_unused(resize_filter))
158 Blackman: 2nd order cosine windowing function:
159 0.42 + 0.5 cos(pi x) + 0.08 cos(2pi x)
160 Refactored by Chantal Racette and Nicolas Robidoux to one trig
163 const MagickRealType cospix = cos((double) (MagickPIL*x));
164 return(0.34+cospix*(0.5+cospix*0.16));
167 static MagickRealType Bohman(const MagickRealType x,
168 const ResizeFilter *magick_unused(resize_filter))
171 Bohman: 2rd Order cosine windowing function:
172 (1-x) cos(pi x) + sin(pi x) / pi.
173 Refactored by Nicolas Robidoux to one trig call, one sqrt call,
174 and 7 flops, taking advantage of the fact that the support of
175 Bohman is 1 (so that we know that sin(pi x) >= 0).
177 const double cospix = cos((double) (MagickPIL*x));
178 const double sinpix = sqrt(1.0-cospix*cospix);
179 return((1.0-x)*cospix+(1.0/MagickPIL)*sinpix);
182 static MagickRealType Box(const MagickRealType magick_unused(x),
183 const ResizeFilter *magick_unused(resize_filter))
186 A Box filter is a equal weighting function (all weights equal).
187 DO NOT LIMIT results by support or resize point sampling will work
188 as it requests points beyond its normal 0.0 support size.
193 static MagickRealType CubicBC(const MagickRealType x,
194 const ResizeFilter *resize_filter)
197 Cubic Filters using B,C determined values:
198 Mitchell-Netravali B=1/3 C=1/3 Qualitively ideal Cubic Filter
199 Catmull-Rom B= 0 C=1/2 Cublic Interpolation Function
200 Cubic B-Spline B= 1 C= 0 Spline Approximation of Gaussian
201 Hermite B= 0 C= 0 Quadratic Spline (support = 1)
203 See paper by Mitchell and Netravali, Reconstruction Filters in Computer
204 Graphics Computer Graphics, Volume 22, Number 4, August 1988
205 http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/
208 Coefficents are determined from B,C values:
211 P2 = (-18 +12*B + 6*C )/6
212 P3 = ( 12 - 9*B - 6*C )/6
214 Q1 = ( -12*B -48*C )/6
216 Q3 = ( - 1*B - 6*C )/6
218 which are used to define the filter:
220 P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
221 Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x <= 2
223 which ensures function is continuous in value and derivative (slope).
226 return(resize_filter->coeff[0]+x*(resize_filter->coeff[1]+x*
227 (resize_filter->coeff[2]+x*resize_filter->coeff[3])));
229 return(resize_filter->coeff[4]+x*(resize_filter->coeff[5]+x*
230 (resize_filter->coeff[6]+x*resize_filter->coeff[7])));
234 static MagickRealType Gaussian(const MagickRealType x,
235 const ResizeFilter *resize_filter)
238 Gaussian with a fixed sigma = 1/2
241 exp( -(x^2)/((2.0*sigma^2) ) / sqrt(2*PI*sigma^2)))
242 The constants are pre-calculated...
243 exp( -coeff[0]*(x^2)) ) * coeff[1]
244 However the multiplier coefficent is not needed and not used.
246 This separates the gaussian 'sigma' value from the 'blur/support' settings
247 allows for its use in special 'small sigma' gaussians, without the filter
248 'missing' pixels when blur and thus support becomes too small.
250 return(exp((double)(-resize_filter->coeff[0]*x*x))); }
252 static MagickRealType Hanning(const MagickRealType x,
253 const ResizeFilter *magick_unused(resize_filter))
256 Cosine window function:
259 const MagickRealType cospix = cos((double) (MagickPIL*x));
260 return(0.5+0.5*cospix);
263 static MagickRealType Hamming(const MagickRealType x,
264 const ResizeFilter *magick_unused(resize_filter))
267 Offset cosine window function:
270 const MagickRealType cospix = cos((double) (MagickPIL*x));
271 return(0.54+0.46*cospix);
274 static MagickRealType Kaiser(const MagickRealType x,
275 const ResizeFilter *magick_unused(resize_filter))
278 #define I0A (1.0/I0(Alpha))
281 Kaiser Windowing Function (bessel windowing): Alpha is a free
282 value from 5 to 8 (currently hardcoded to 6.5).
283 Future: make alpha the IOA pre-calculation, an 'expert' setting.
285 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
288 static MagickRealType Lagrange(const MagickRealType x,
289 const ResizeFilter *resize_filter)
302 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the
303 lagrange function and depends on the overall support window size
304 of the filter. That is: for a support of 2, it gives a lagrange-4
305 (piecewise cubic function).
307 "n" identifies the piece of the piecewise polynomial.
309 See Survey: Interpolation Methods, IEEE Transactions on Medical
310 Imaging, Vol 18, No 11, November 1999, p1049-1075, -- Equation 27
313 if (x > resize_filter->support)
315 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
316 /*n=(ssize_t)((1.0*order)/2.0+x); -- which piece does x belong to */
317 n = (ssize_t)(resize_filter->window_support + x);
319 for (i=0; i < order; i++)
321 value*=(n-i-x)/(n-i);
325 static MagickRealType Quadratic(const MagickRealType x,
326 const ResizeFilter *magick_unused(resize_filter))
329 2rd order (quadratic) B-Spline approximation of Gaussian.
334 return(0.5*(x-1.5)*(x-1.5));
338 static MagickRealType Sinc(const MagickRealType x,
339 const ResizeFilter *magick_unused(resize_filter))
342 Scaled sinc(x) function using a trig call:
343 sinc(x) == sin(pi x)/(pi x).
347 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
348 return(sin((double) pix)/pix);
350 return((MagickRealType) 1.0);
353 static MagickRealType SincFast(const MagickRealType x,
354 const ResizeFilter *magick_unused(resize_filter))
357 Approximations of the sinc function sin(pi x)/(pi x) over the
358 interval [-4,4] constructed by Nicolas Robidoux and Chantal
359 Racette with funding from the Natural Sciences and Engineering
360 Research Council of Canada.
362 Although the approximations are polynomials (for low order of
363 approximation) and quotients of polynomials (for higher order of
364 approximation) and consequently are similar in form to Taylor
365 polynomials/Pade approximants, the approximations are computed
366 with a completely different technique.
368 Summary: These approximations are "the best" in terms of bang
369 (accuracy) for the buck (flops). More specifically: Among the
370 polynomial quotients that can be computed using a fixed number of
371 flops (with a given "+ - * / budget"), the chosen polynomial
372 quotient is the one closest to the approximated function with
373 respect to maximum absolute relative error over the given
376 The Remez algorithm, as implemented in the boost library's minimax
377 package, is the key to the construction:
378 http://www.boost.org/doc/libs/1_36_0/libs/math/doc/...
379 ...sf_and_dist/html/math_toolkit/backgrounders/remez.html
382 If outside of the interval of approximation, use the standard trig
387 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
388 return(sin((double) pix)/pix);
392 The approximations only depend on x^2 (sinc is an even
395 const MagickRealType xx = x*x;
396 #if MAGICKCORE_QUANTUM_DEPTH <= 8
398 Maximum absolute relative error 6.3e-6 < 1/2^17.
400 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
401 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
402 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
403 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
404 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
405 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
406 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
407 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
408 const MagickRealType p =
409 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
410 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
411 #elif MAGICKCORE_QUANTUM_DEPTH <= 16
413 Max. abs. rel. error 2.2e-8 < 1/2^25.
415 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
416 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
417 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
418 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
419 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
420 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
421 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
422 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
423 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
424 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
425 const MagickRealType p =
426 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
427 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
430 Max. abs. rel. error 1.2e-12 < 1/2^39.
432 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
433 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
434 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
435 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
436 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
437 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
438 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
439 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
440 const MagickRealType p =
441 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
442 const MagickRealType d0 = 1.0L;
443 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
444 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
445 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
446 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
447 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
448 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
453 static MagickRealType Triangle(const MagickRealType x,
454 const ResizeFilter *magick_unused(resize_filter))
457 1st order (linear) B-Spline, bilinear interpolation, Tent 1D
458 filter, or a Bartlett 2D Cone filter.
465 static MagickRealType Welsh(const MagickRealType x,
466 const ResizeFilter *magick_unused(resize_filter))
469 Welsh parabolic windowing filter.
477 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
481 + A c q u i r e R e s i z e F i l t e r %
485 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
487 % AcquireResizeFilter() allocates the ResizeFilter structure. Choose
488 % from these filters:
490 % FIR (Finite impulse Response) Filters
491 % Box Triangle Quadratic
492 % Cubic Hermite Catrom
495 % IIR (Infinite impulse Response) Filters
496 % Gaussian Sinc Jinc (Bessel)
498 % Windowed Sinc/Jinc Filters
499 % Blackman Hanning Hamming
502 % Special purpose Filters
503 % SincFast Lanczos2D Robidoux
505 % The users "-filter" selection is used to lookup the default 'expert'
506 % settings for that filter from a internal table. However any provided
507 % 'expert' settings (see below) may override this selection.
509 % FIR filters are used as is, and are limited to that filters support
510 % window (unless over-ridden). 'Gaussian' while classed as an IIR
511 % filter, is also simply clipped by its support size (currently 1.5
512 % or approximatally 3*sigma as recommended by many references)
514 % The selection is typically either a windowed Sinc, or interpolated
515 % filter, for use by functions such as ResizeImage(). However if a
516 % 'cylindrical' filter flag is requested, any default Sinc weighting
517 % and windowing functions will be promoted to cylindrical Jinc form of
520 % Directly requesting 'Sinc' or 'Jinc' will force the use of that
521 % filter function without any windowing. This is not recommended,
522 % except by image processing experts or in expert options. Selecting a
523 % window filtering version of these functions is better.
525 % Lanczos is a special case of a Sinc-windowed Sinc, (or Jinc-Jinc for
526 % the cylindrical case) but defaulting to 3-lobe support, rather that
527 % the default 4 lobe support of the other windowed sinc/jinc filters.
529 % Two forms of the 'Sinc' function are available: Sinc and SincFast.
530 % Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
531 % selected if the user specifically specifies the use of a Sinc
532 % filter. SincFast uses highly accurate (and fast) polynomial (low Q)
533 % and rational (high Q) approximations, and will be used by default in
536 % The Lanczos2D and Robidoux filters are tuned for cylindrical
537 % (radial) EWA (Elliptical Weighted Average) distortion. Lanczos2D
538 % is a 2 lobe Lanczos-like filter using Jinc (for EWA) or Sinc.
539 % Robidoux used to be a sharpened version of Lanczos2D (with
540 % blur=0.958033808). Now, it is the unique Cubic 'Keys' filter that
541 % exactly preserves images with only vertical or horizontal features
542 % when performing 'no-op" with EWA distortion. It turns out to be
543 % close to both plain Mitchell and "sharpened" Lanczos2D.
545 % Special 'expert' options can be used to override any and all filter
546 % settings. This is not advised unless you have expert knowledge of
547 % the use of resampling filtered techniques. Check on the results of
548 % your selections using the "filter:verbose" setting to make sure you
549 % get the exact filter that you are tring to achieve.
551 % "filter:filter" Select the main function associated with
552 % this filter name, as the weighting function of the filter.
553 % This can be used to set a windowing function as a weighting
554 % function, for special purposes, such as graphing.
556 % If a "filter:window" operation has not been provided, then a 'Box'
557 % windowing function will be set to denote that no windowing function
560 % "filter:window" Select this windowing function for the filter.
561 % While any filter could be used as a windowing function, using the
562 % 'first lobe' of that filter over the whole support window, using a
563 % non-windowing function is not advisible. If no weighting filter
564 % function is specifed a 'SincFast' filter will be used.
566 % "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
567 % This a simpler method of setting filter support size that will
568 % correctly handle the Sinc/Jinc switch for an operators filtering
569 % requirements. Only integers should be given.
571 % "filter:support" Set the support size for filtering to the size given
572 % This not recommended for Sinc/Jinc windowed filters (lobes should
573 % be used instead). This will override any 'filter:lobes' option.
575 % "filter:win-support" Scale windowing function to this size instead.
576 % This causes the windowing (or self-windowing Lagrange filter) to act
577 % is if the support window it much much larger than what is actually
578 % supplied to the calling operator. The filter however is still
579 % clipped to the real support size given, by the support range suppiled
580 % to the caller. If unset this will equal the normal filter support
583 % "filter:blur" Scale the filter and support window by this amount.
584 % A value >1 will generally result in a more burred image with
585 % more ringing effects, while a value <1 will sharpen the
586 % resulting image with more aliasing and Morie effects.
588 % "filter:sigma" The sigma value to use for the Gaussian filter only.
589 % Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for cylindrical
590 % usage. It effectially provides a alturnative to 'blur' for Gaussians
591 % without it also effecting the final 'practical support' size.
594 % "filter:c" Override the preset B,C values for a Cubic type of filter
595 % If only one of these are given it is assumes to be a 'Keys'
596 % type of filter such that B+2C=1, where Keys 'alpha' value = C
598 % "filter:verbose" Output the exact results of the filter selections
599 % made, as well as plotting data for graphing the resulting filter
600 % over support range (blur adjusted).
602 % Set a true un-windowed Sinc filter with 10 lobes (very slow)
603 % -define filter:filter=Sinc
604 % -define filter:lobes=8
606 % For example force an 8 lobe Lanczos (Sinc or Jinc) filter...
608 % -define filter:lobes=8
610 % The format of the AcquireResizeFilter method is:
612 % ResizeFilter *AcquireResizeFilter(const Image *image,
613 % const FilterTypes filter_type, const MagickBooleanType radial,
614 % ExceptionInfo *exception)
616 % A description of each parameter follows:
618 % o image: the image.
620 % o filter: the filter type, defining a preset filter, window and
621 % support. The artifact settings listed above will override
624 % o blur: blur the filter by this amount, use 1.0 if unknown. Image
625 % artifact "filter:blur" will override this API call usage, including
626 % any internal change (such as for cylindrical usage).
628 % o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
629 % (radial) filter (Jinc)
631 % o exception: return any errors or warnings in this structure.
634 MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
635 const FilterTypes filter,const MagickRealType blur,
636 const MagickBooleanType cylindrical,ExceptionInfo *exception)
650 register ResizeFilter
657 Table Mapping given Filter, into Weighting and Windowing functions.
658 A 'Box' windowing function means its a simble non-windowed filter.
659 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
660 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
661 specifically requested.
663 WARNING: The order of this tabel must match the order of the
664 FilterTypes enumeration specified in "resample.h", or the filter
665 names will not match the filter being setup.
667 You can check filter setups with the "filter:verbose" setting.
674 } const mapping[SentinelFilter] =
676 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
677 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
678 { BoxFilter, BoxFilter }, /* Box averaging filter */
679 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
680 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
681 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
682 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
683 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
684 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
685 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
686 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
687 { CatromFilter, BoxFilter }, /* Cubic interpolator */
688 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
689 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
690 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
691 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
692 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
693 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
694 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
695 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
696 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
697 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
698 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
699 { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
700 { Lanczos2DSharpFilter, JincFilter },/* SPECIAL: ditto sharpened */
701 { RobidouxFilter, BoxFilter }, /* SPECIAL: Keys cubic tuned for EWA */
704 Table mapping the filter/window from the above table to an actual
705 function. The default support size for that filter as a weighting
706 function, the range to scale with to use that function as a sinc
707 windowing function, (typ 1.0).
709 Note that the filter_type -> function is 1 to 1 except for Sinc(),
710 SincFast(), and CubicBC() functions, which may have multiple
711 filter to function associations.
713 See "filter:verbose" handling below for the function -> filter
719 (*function)(const MagickRealType, const ResizeFilter*),
720 lobes, /* default lobes/support size of the weighting filter */
721 scale, /* windowing function range, for scaling windowing function */
722 B,C; /* Cubic Filter factors for a CubicBC function, else ignored */
723 } const filters[SentinelFilter] =
725 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
726 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
727 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
728 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
729 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
730 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
731 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
732 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
733 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
734 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
735 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
736 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
737 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
738 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
739 { Jinc, 3.0, 1.21967, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
740 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
741 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
742 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
743 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
744 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
745 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
746 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
747 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
748 { Jinc, 2.0, 1.21966989, 0.0, 0.0 }, /* Lanczos2D (Jinc-Jinc) */
749 { Jinc, 2.0, 1.16848499, 0.0, 0.0 }, /* Lanczos2D Sharpened */
750 { CubicBC, 2.0, 1.16848499, 0.37821575509399862, 0.31089212245300069 }
751 /* Robidoux: Keys cubic close to Lanczos2D with blur=0.958033808 */
754 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
755 function being used as a filter. It is used by the "filter:lobes" and for
756 the 'lobes' number in the above, the for support selection, so users do
757 not have to deal with the highly irrational sizes of the 'lobes' of the
761 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
762 using Jv-function with v=1, then dividing by PI.
764 static MagickRealType
786 Allocate resize filter.
788 assert(image != (const Image *) NULL);
789 assert(image->signature == MagickSignature);
790 if (image->debug != MagickFalse)
791 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
792 assert(UndefinedFilter < filter && filter < SentinelFilter);
793 assert(exception != (ExceptionInfo *) NULL);
794 assert(exception->signature == MagickSignature);
795 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
796 if (resize_filter == (ResizeFilter *) NULL)
797 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
799 Defaults for the requested filter.
801 filter_type=mapping[filter].filter;
802 window_type=mapping[filter].window;
803 resize_filter->blur = blur;
805 /* Cylindrical Filters should use Jinc instead of Sinc */
806 if (cylindrical != MagickFalse)
810 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
811 if ( filter != SincFilter )
812 filter_type=JincFilter;
815 /* Ditto for SincFast variant */
816 if ( filter != SincFastFilter )
817 filter_type=JincFilter;
820 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
821 filter_type=JincFilter;
822 window_type=JincFilter;
824 case Lanczos2DSharpFilter:
825 /* Sharpened by Nicholas Robidoux so as to optimize for
826 * minimal blurring of orthogonal lines
828 resize_filter->blur *= 0.958033808;
831 sigma = (MagickRealType) (MagickSQ2/2.0); /* Cylindrical Gaussian sigma is sqrt(2)/2 */
839 case Lanczos2DFilter:
840 case Lanczos2DSharpFilter:
841 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
842 window_type=SincFastFilter;
848 artifact=GetImageArtifact(image,"filter:filter");
849 if (artifact != (const char *) NULL)
851 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
852 if ((UndefinedFilter < option) && (option < SentinelFilter))
853 { /* Raw filter request - no window function. */
854 filter_type=(FilterTypes) option;
855 window_type=BoxFilter;
857 if (option == LanczosFilter)
858 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
859 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
860 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
862 /* Filter override with a specific window function. */
863 artifact=GetImageArtifact(image,"filter:window");
864 if (artifact != (const char *) NULL)
866 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
867 if ((UndefinedFilter < option) && (option < SentinelFilter))
869 if (option != LanczosFilter)
870 window_type=(FilterTypes) option;
872 window_type=cylindrical != MagickFalse ? JincFilter :
879 /* Window specified, but no filter function? Assume Sinc/Jinc. */
880 artifact=GetImageArtifact(image,"filter:window");
881 if (artifact != (const char *) NULL)
883 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
885 if ((UndefinedFilter < option) && (option < SentinelFilter))
887 filter_type=cylindrical != MagickFalse ?
888 JincFilter : SincFastFilter;
889 window_type=(FilterTypes) option;
893 /* Assign the real functions to use for the filters selected. */
894 resize_filter->filter=filters[filter_type].function;
895 resize_filter->support=filters[filter_type].lobes;
896 resize_filter->window=filters[window_type].function;
897 resize_filter->scale=filters[window_type].scale;
898 resize_filter->signature=MagickSignature;
900 /* Filter Modifications for orthogonal/cylindrical usage */
901 if (cylindrical != MagickFalse)
906 /* Support for Cylindrical Box should be sqrt(2)/2 */
907 resize_filter->support=(MagickRealType) MagickSQ1_2;
915 case Lanczos2DFilter:
916 case Lanczos2DSharpFilter:
917 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
918 resize_filter->filter=SincFast;
925 ** More Expert Option Modifications
928 /* User Sigma Override - no support change */
929 artifact=GetImageArtifact(image,"filter:sigma");
930 if (artifact != (const char *) NULL)
931 sigma=StringToDouble(artifact);
932 /* Define coefficents for Gaussian (assumes no cubic window) */
933 if ( GaussianFilter ) {
934 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
935 resize_filter->coeff[1] = (MagickRealType) (1.0/(Magick2PI*sigma*sigma)); /* unused */
939 artifact=GetImageArtifact(image,"filter:blur");
940 if (artifact != (const char *) NULL)
941 resize_filter->blur=StringToDouble(artifact);
942 if (resize_filter->blur < MagickEpsilon)
943 resize_filter->blur=(MagickRealType) MagickEpsilon;
945 /* Support Overrides */
946 artifact=GetImageArtifact(image,"filter:lobes");
947 if (artifact != (const char *) NULL)
952 lobes=(ssize_t) StringToLong(artifact);
955 resize_filter->support=(MagickRealType) lobes;
957 /* convert Jinc lobes to a real support value */
958 if (resize_filter->filter == Jinc)
960 if (resize_filter->support > 16)
961 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
963 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
965 /* expert override of the support setting */
966 artifact=GetImageArtifact(image,"filter:support");
967 if (artifact != (const char *) NULL)
968 resize_filter->support=fabs(StringToDouble(artifact));
970 Scale windowing function separatally to the support 'clipping'
971 window that calling operator is planning to actually use. (Expert
974 resize_filter->window_support=resize_filter->support; /* default */
975 artifact=GetImageArtifact(image,"filter:win-support");
976 if (artifact != (const char *) NULL)
977 resize_filter->window_support=fabs(StringToDouble(artifact));
979 Adjust window function scaling to the windowing support for
980 weighting function. This avoids a division on every filter call.
982 resize_filter->scale /= resize_filter->window_support;
985 * Set Cubic Spline B,C values, calculate Cubic coefficients.
989 if ((filters[filter_type].function == CubicBC) ||
990 (filters[window_type].function == CubicBC))
992 B=filters[filter_type].B;
993 C=filters[filter_type].C;
994 if (filters[window_type].function == CubicBC)
996 B=filters[window_type].B;
997 C=filters[window_type].C;
999 artifact=GetImageArtifact(image,"filter:b");
1000 if (artifact != (const char *) NULL)
1002 B=StringToDouble(artifact);
1003 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
1004 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
1005 if (artifact != (const char *) NULL)
1006 C=StringToDouble(artifact);
1010 artifact=GetImageArtifact(image,"filter:c");
1011 if (artifact != (const char *) NULL)
1013 C=StringToDouble(artifact);
1014 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
1017 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
1018 resize_filter->coeff[0]=(6.0-2.0*B)/6.0;
1019 resize_filter->coeff[1]=0.0;
1020 resize_filter->coeff[2]=(-18.0+12.0*B+6.0*C)/6.0;
1021 resize_filter->coeff[3]=(12.0-9.0*B-6.0*C)/6.0;
1022 resize_filter->coeff[4]=(8.0*B+24.0*C)/6.0;
1023 resize_filter->coeff[5]=(-12.0*B-48.0*C)/6.0;
1024 resize_filter->coeff[6]=(6.0*B+30.0*C)/6.0;
1025 resize_filter->coeff[7]=(-B-6.0*C)/6.0;
1029 Expert Option Request for verbose details of the resulting filter.
1031 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1035 artifact=GetImageArtifact(image,"filter:verbose");
1036 if (artifact != (const char *) NULL)
1043 Set the weighting function properly when the weighting
1044 function may not exactly match the filter of the same name.
1045 EG: a Point filter really uses a Box weighting function
1046 with a different support than is typically used.
1049 if (resize_filter->filter == Box) filter_type=BoxFilter;
1050 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1051 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1052 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1053 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1055 Report Filter Details.
1057 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1058 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
1059 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1060 MagickFilterOptions,filter_type));
1061 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
1062 MagickFilterOptions, window_type));
1063 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
1064 (double) resize_filter->support);
1065 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
1066 (double) resize_filter->window_support);
1067 (void) fprintf(stdout,"# scale_blur = %.*g\n",GetMagickPrecision(),
1068 (double) resize_filter->blur);
1069 if ( filter_type == GaussianFilter )
1070 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",GetMagickPrecision(),
1072 (void) fprintf(stdout,"# practical_support = %.*g\n",GetMagickPrecision(),
1074 if ( filter_type == CubicFilter || window_type == CubicFilter )
1075 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1076 (double) B,GetMagickPrecision(),(double) C);
1077 (void) fprintf(stdout,"\n");
1079 Output values of resulting filter graph -- for graphing
1082 for (x=0.0; x <= support; x+=0.01f)
1083 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1084 (double) GetResizeFilterWeight(resize_filter,x));
1085 /* A final value so gnuplot can graph the 'stop' properly. */
1086 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1089 /* Output the above once only for each image - remove setting */
1090 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1091 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1095 return(resize_filter);
1099 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1103 % A d a p t i v e R e s i z e I m a g e %
1107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1109 % AdaptiveResizeImage() adaptively resize image with pixel resampling.
1111 % The format of the AdaptiveResizeImage method is:
1113 % Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1114 % const size_t rows,ExceptionInfo *exception)
1116 % A description of each parameter follows:
1118 % o image: the image.
1120 % o columns: the number of columns in the resized image.
1122 % o rows: the number of rows in the resized image.
1124 % o exception: return any errors or warnings in this structure.
1127 MagickExport Image *AdaptiveResizeImage(const Image *image,
1128 const size_t columns,const size_t rows,ExceptionInfo *exception)
1130 #define AdaptiveResizeImageTag "Resize/Image"
1154 Adaptively resize image.
1156 assert(image != (const Image *) NULL);
1157 assert(image->signature == MagickSignature);
1158 if (image->debug != MagickFalse)
1159 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1160 assert(exception != (ExceptionInfo *) NULL);
1161 assert(exception->signature == MagickSignature);
1162 if ((columns == 0) || (rows == 0))
1163 return((Image *) NULL);
1164 if ((columns == image->columns) && (rows == image->rows))
1165 return(CloneImage(image,0,0,MagickTrue,exception));
1166 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1167 if (resize_image == (Image *) NULL)
1168 return((Image *) NULL);
1169 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1171 InheritException(exception,&resize_image->exception);
1172 resize_image=DestroyImage(resize_image);
1173 return((Image *) NULL);
1175 GetMagickPixelPacket(image,&pixel);
1176 resample_filter=AcquireResampleFilter(image,exception);
1177 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
1178 (void) SetResampleFilterInterpolateMethod(resample_filter,
1179 MeshInterpolatePixel);
1180 resize_view=AcquireCacheView(resize_image);
1181 for (y=0; y < (ssize_t) resize_image->rows; y++)
1183 register IndexPacket
1184 *restrict resize_indexes;
1189 register PixelPacket
1192 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1194 if (q == (PixelPacket *) NULL)
1196 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1197 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
1198 for (x=0; x < (ssize_t) resize_image->columns; x++)
1200 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1201 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1203 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1206 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1208 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1210 if (proceed == MagickFalse)
1213 resample_filter=DestroyResampleFilter(resample_filter);
1214 resize_view=DestroyCacheView(resize_view);
1215 return(resize_image);
1219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1223 + B e s s e l O r d e r O n e %
1227 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1229 % BesselOrderOne() computes the Bessel function of x of the first kind of
1230 % order 0. This is used to create the Jinc() filter function below.
1232 % Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1238 % j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1240 % where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1242 % cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1243 % = 1/sqrt(2) * (sin(x) - cos(x))
1244 % sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1245 % = -1/sqrt(2) * (sin(x) + cos(x))
1247 % The format of the BesselOrderOne method is:
1249 % MagickRealType BesselOrderOne(MagickRealType x)
1251 % A description of each parameter follows:
1253 % o x: MagickRealType value.
1258 static MagickRealType I0(MagickRealType x)
1269 Zeroth order Bessel function of the first kind.
1274 for (i=2; t > MagickEpsilon; i++)
1277 t*=y/((MagickRealType) i*i);
1283 static MagickRealType J1(MagickRealType x)
1295 0.581199354001606143928050809e+21,
1296 -0.6672106568924916298020941484e+20,
1297 0.2316433580634002297931815435e+19,
1298 -0.3588817569910106050743641413e+17,
1299 0.2908795263834775409737601689e+15,
1300 -0.1322983480332126453125473247e+13,
1301 0.3413234182301700539091292655e+10,
1302 -0.4695753530642995859767162166e+7,
1303 0.270112271089232341485679099e+4
1307 0.11623987080032122878585294e+22,
1308 0.1185770712190320999837113348e+20,
1309 0.6092061398917521746105196863e+17,
1310 0.2081661221307607351240184229e+15,
1311 0.5243710262167649715406728642e+12,
1312 0.1013863514358673989967045588e+10,
1313 0.1501793594998585505921097578e+7,
1314 0.1606931573481487801970916749e+4,
1320 for (i=7; i >= 0; i--)
1329 static MagickRealType P1(MagickRealType x)
1341 0.352246649133679798341724373e+5,
1342 0.62758845247161281269005675e+5,
1343 0.313539631109159574238669888e+5,
1344 0.49854832060594338434500455e+4,
1345 0.2111529182853962382105718e+3,
1346 0.12571716929145341558495e+1
1350 0.352246649133679798068390431e+5,
1351 0.626943469593560511888833731e+5,
1352 0.312404063819041039923015703e+5,
1353 0.4930396490181088979386097e+4,
1354 0.2030775189134759322293574e+3,
1360 for (i=4; i >= 0; i--)
1362 p=p*(8.0/x)*(8.0/x)+Pone[i];
1363 q=q*(8.0/x)*(8.0/x)+Qone[i];
1369 static MagickRealType Q1(MagickRealType x)
1381 0.3511751914303552822533318e+3,
1382 0.7210391804904475039280863e+3,
1383 0.4259873011654442389886993e+3,
1384 0.831898957673850827325226e+2,
1385 0.45681716295512267064405e+1,
1386 0.3532840052740123642735e-1
1390 0.74917374171809127714519505e+4,
1391 0.154141773392650970499848051e+5,
1392 0.91522317015169922705904727e+4,
1393 0.18111867005523513506724158e+4,
1394 0.1038187585462133728776636e+3,
1400 for (i=4; i >= 0; i--)
1402 p=p*(8.0/x)*(8.0/x)+Pone[i];
1403 q=q*(8.0/x)*(8.0/x)+Qone[i];
1408 static MagickRealType BesselOrderOne(MagickRealType x)
1421 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1422 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1430 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1434 + D e s t r o y R e s i z e F i l t e r %
1438 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1440 % DestroyResizeFilter() destroy the resize filter.
1442 % The format of the DestroyResizeFilter method is:
1444 % ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1446 % A description of each parameter follows:
1448 % o resize_filter: the resize filter.
1451 MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1453 assert(resize_filter != (ResizeFilter *) NULL);
1454 assert(resize_filter->signature == MagickSignature);
1455 resize_filter->signature=(~MagickSignature);
1456 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1457 return(resize_filter);
1461 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1465 + G e t R e s i z e F i l t e r S u p p o r t %
1469 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1471 % GetResizeFilterSupport() return the current support window size for this
1472 % filter. Note that this may have been enlarged by filter:blur factor.
1474 % The format of the GetResizeFilterSupport method is:
1476 % MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1478 % A description of each parameter follows:
1480 % o filter: Image filter to use.
1483 MagickExport MagickRealType GetResizeFilterSupport(
1484 const ResizeFilter *resize_filter)
1486 assert(resize_filter != (ResizeFilter *) NULL);
1487 assert(resize_filter->signature == MagickSignature);
1488 return(resize_filter->support*resize_filter->blur);
1492 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1496 + G e t R e s i z e F i l t e r W e i g h t %
1500 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1502 % GetResizeFilterWeight evaluates the specified resize filter at the point x
1503 % which usally lies between zero and the filters current 'support' and
1504 % returns the weight of the filter function at that point.
1506 % The format of the GetResizeFilterWeight method is:
1508 % MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1509 % const MagickRealType x)
1511 % A description of each parameter follows:
1513 % o filter: the filter type.
1518 MagickExport MagickRealType GetResizeFilterWeight(
1519 const ResizeFilter *resize_filter,const MagickRealType x)
1526 Windowing function - scale the weighting filter by this amount.
1528 assert(resize_filter != (ResizeFilter *) NULL);
1529 assert(resize_filter->signature == MagickSignature);
1530 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
1531 if ((resize_filter->window_support < MagickEpsilon) ||
1532 (resize_filter->window == Box))
1533 scale=1.0; /* Point or Box Filter -- avoid division by zero */
1536 scale=resize_filter->scale;
1537 scale=resize_filter->window(x_blur*scale,resize_filter);
1539 return(scale*resize_filter->filter(x_blur,resize_filter));
1543 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1547 % M a g n i f y I m a g e %
1551 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1553 % MagnifyImage() is a convenience method that scales an image proportionally
1554 % to twice its size.
1556 % The format of the MagnifyImage method is:
1558 % Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1560 % A description of each parameter follows:
1562 % o image: the image.
1564 % o exception: return any errors or warnings in this structure.
1567 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1572 assert(image != (Image *) NULL);
1573 assert(image->signature == MagickSignature);
1574 if (image->debug != MagickFalse)
1575 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1576 assert(exception != (ExceptionInfo *) NULL);
1577 assert(exception->signature == MagickSignature);
1578 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1580 return(magnify_image);
1584 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1588 % M i n i f y I m a g e %
1592 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1594 % MinifyImage() is a convenience method that scales an image proportionally
1597 % The format of the MinifyImage method is:
1599 % Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1601 % A description of each parameter follows:
1603 % o image: the image.
1605 % o exception: return any errors or warnings in this structure.
1608 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1613 assert(image != (Image *) NULL);
1614 assert(image->signature == MagickSignature);
1615 if (image->debug != MagickFalse)
1616 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1617 assert(exception != (ExceptionInfo *) NULL);
1618 assert(exception->signature == MagickSignature);
1619 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1621 return(minify_image);
1625 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1629 % R e s a m p l e I m a g e %
1633 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1635 % ResampleImage() resize image in terms of its pixel size, so that when
1636 % displayed at the given resolution it will be the same size in terms of
1637 % real world units as the original image at the original resolution.
1639 % The format of the ResampleImage method is:
1641 % Image *ResampleImage(Image *image,const double x_resolution,
1642 % const double y_resolution,const FilterTypes filter,const double blur,
1643 % ExceptionInfo *exception)
1645 % A description of each parameter follows:
1647 % o image: the image to be resized to fit the given resolution.
1649 % o x_resolution: the new image x resolution.
1651 % o y_resolution: the new image y resolution.
1653 % o filter: Image filter to use.
1655 % o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1658 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1659 const double y_resolution,const FilterTypes filter,const double blur,
1660 ExceptionInfo *exception)
1662 #define ResampleImageTag "Resample/Image"
1672 Initialize sampled image attributes.
1674 assert(image != (const Image *) NULL);
1675 assert(image->signature == MagickSignature);
1676 if (image->debug != MagickFalse)
1677 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1678 assert(exception != (ExceptionInfo *) NULL);
1679 assert(exception->signature == MagickSignature);
1680 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1681 72.0 : image->x_resolution)+0.5);
1682 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1683 72.0 : image->y_resolution)+0.5);
1684 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1685 if (resample_image != (Image *) NULL)
1687 resample_image->x_resolution=x_resolution;
1688 resample_image->y_resolution=y_resolution;
1690 return(resample_image);
1692 #if defined(MAGICKCORE_LQR_DELEGATE)
1695 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1699 % L i q u i d R e s c a l e I m a g e %
1703 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1705 % LiquidRescaleImage() rescales image with seam carving.
1707 % The format of the LiquidRescaleImage method is:
1709 % Image *LiquidRescaleImage(const Image *image,
1710 % const size_t columns,const size_t rows,
1711 % const double delta_x,const double rigidity,ExceptionInfo *exception)
1713 % A description of each parameter follows:
1715 % o image: the image.
1717 % o columns: the number of columns in the rescaled image.
1719 % o rows: the number of rows in the rescaled image.
1721 % o delta_x: maximum seam transversal step (0 means straight seams).
1723 % o rigidity: introduce a bias for non-straight seams (typically 0).
1725 % o exception: return any errors or warnings in this structure.
1728 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1729 const size_t rows,const double delta_x,const double rigidity,
1730 ExceptionInfo *exception)
1732 #define LiquidRescaleImageTag "Rescale/Image"
1766 Liquid rescale image.
1768 assert(image != (const Image *) NULL);
1769 assert(image->signature == MagickSignature);
1770 if (image->debug != MagickFalse)
1771 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1772 assert(exception != (ExceptionInfo *) NULL);
1773 assert(exception->signature == MagickSignature);
1774 if ((columns == 0) || (rows == 0))
1775 return((Image *) NULL);
1776 if ((columns == image->columns) && (rows == image->rows))
1777 return(CloneImage(image,0,0,MagickTrue,exception));
1778 if ((columns <= 2) || (rows <= 2))
1779 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
1780 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1790 Honor liquid resize size limitations.
1792 for (width=image->columns; columns >= (2*width-1); width*=2);
1793 for (height=image->rows; rows >= (2*height-1); height*=2);
1794 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1796 if (resize_image == (Image *) NULL)
1797 return((Image *) NULL);
1798 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1799 rigidity,exception);
1800 resize_image=DestroyImage(resize_image);
1801 return(rescale_image);
1804 if (image->matte == MagickFalse)
1806 if (image->colorspace == CMYKColorspace)
1809 if (image->matte == MagickFalse)
1812 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1813 strlen(map)*sizeof(*pixels));
1814 if (pixels == (unsigned char *) NULL)
1815 return((Image *) NULL);
1816 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1818 if (status == MagickFalse)
1820 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1821 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1823 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1824 if (carver == (LqrCarver *) NULL)
1826 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1827 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1829 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1830 lqr_status=lqr_carver_resize(carver,columns,rows);
1831 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1832 lqr_carver_get_height(carver),MagickTrue,exception);
1833 if (rescale_image == (Image *) NULL)
1835 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1836 return((Image *) NULL);
1838 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1840 InheritException(exception,&rescale_image->exception);
1841 rescale_image=DestroyImage(rescale_image);
1842 return((Image *) NULL);
1844 GetMagickPixelPacket(rescale_image,&pixel);
1845 (void) lqr_carver_scan_reset(carver);
1846 rescale_view=AcquireCacheView(rescale_image);
1847 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1849 register IndexPacket
1850 *restrict rescale_indexes;
1852 register PixelPacket
1855 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
1856 if (q == (PixelPacket *) NULL)
1858 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
1859 pixel.red=QuantumRange*(packet[0]/255.0);
1860 pixel.green=QuantumRange*(packet[1]/255.0);
1861 pixel.blue=QuantumRange*(packet[2]/255.0);
1862 if (image->colorspace != CMYKColorspace)
1864 if (image->matte == MagickFalse)
1865 pixel.opacity=QuantumRange*(packet[3]/255.0);
1869 pixel.index=QuantumRange*(packet[3]/255.0);
1870 if (image->matte == MagickFalse)
1871 pixel.opacity=QuantumRange*(packet[4]/255.0);
1873 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
1874 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
1877 rescale_view=DestroyCacheView(rescale_view);
1879 Relinquish resources.
1881 lqr_carver_destroy(carver);
1882 return(rescale_image);
1885 MagickExport Image *LiquidRescaleImage(const Image *image,
1886 const size_t magick_unused(columns),const size_t magick_unused(rows),
1887 const double magick_unused(delta_x),const double magick_unused(rigidity),
1888 ExceptionInfo *exception)
1890 assert(image != (const Image *) NULL);
1891 assert(image->signature == MagickSignature);
1892 if (image->debug != MagickFalse)
1893 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1894 assert(exception != (ExceptionInfo *) NULL);
1895 assert(exception->signature == MagickSignature);
1896 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1897 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1898 return((Image *) NULL);
1903 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1907 % R e s i z e I m a g e %
1911 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1913 % ResizeImage() scales an image to the desired dimensions, using the given
1914 % filter (see AcquireFilterInfo()).
1916 % If an undefined filter is given the filter defaults to Mitchell for a
1917 % colormapped image, a image with a matte channel, or if the image is
1918 % enlarged. Otherwise the filter defaults to a Lanczos.
1920 % ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1922 % The format of the ResizeImage method is:
1924 % Image *ResizeImage(Image *image,const size_t columns,
1925 % const size_t rows,const FilterTypes filter,const double blur,
1926 % ExceptionInfo *exception)
1928 % A description of each parameter follows:
1930 % o image: the image.
1932 % o columns: the number of columns in the scaled image.
1934 % o rows: the number of rows in the scaled image.
1936 % o filter: Image filter to use.
1938 % o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1941 % o exception: return any errors or warnings in this structure.
1945 typedef struct _ContributionInfo
1954 static ContributionInfo **DestroyContributionThreadSet(
1955 ContributionInfo **contribution)
1960 assert(contribution != (ContributionInfo **) NULL);
1961 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
1962 if (contribution[i] != (ContributionInfo *) NULL)
1963 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1965 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
1966 return(contribution);
1969 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1980 number_threads=GetOpenMPMaximumThreads();
1981 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
1982 sizeof(*contribution));
1983 if (contribution == (ContributionInfo **) NULL)
1984 return((ContributionInfo **) NULL);
1985 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
1986 for (i=0; i < (ssize_t) number_threads; i++)
1988 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1989 sizeof(**contribution));
1990 if (contribution[i] == (ContributionInfo *) NULL)
1991 return(DestroyContributionThreadSet(contribution));
1993 return(contribution);
1996 static inline double MagickMax(const double x,const double y)
2003 static inline double MagickMin(const double x,const double y)
2010 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2011 const Image *image,Image *resize_image,const MagickRealType x_factor,
2012 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2014 #define ResizeImageTag "Resize/Image"
2024 **restrict contributions;
2040 Apply filter to resize horizontally from image to resize image.
2042 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
2043 support=scale*GetResizeFilterSupport(resize_filter);
2044 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2045 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2047 InheritException(exception,&resize_image->exception);
2048 return(MagickFalse);
2053 Support too small even for nearest neighbour: Reduce to point
2056 support=(MagickRealType) 0.5;
2059 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2060 if (contributions == (ContributionInfo **) NULL)
2062 (void) ThrowMagickException(exception,GetMagickModule(),
2063 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2064 return(MagickFalse);
2068 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2069 image_view=AcquireCacheView(image);
2070 resize_view=AcquireCacheView(resize_image);
2071 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2072 #pragma omp parallel for shared(status)
2074 for (x=0; x < (ssize_t) resize_image->columns; x++)
2080 register const IndexPacket
2083 register const PixelPacket
2086 register ContributionInfo
2087 *restrict contribution;
2089 register IndexPacket
2090 *restrict resize_indexes;
2092 register PixelPacket
2103 if (status == MagickFalse)
2105 center=(MagickRealType) (x+0.5)/x_factor;
2106 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2107 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
2109 contribution=contributions[GetOpenMPThreadId()];
2110 for (n=0; n < (stop-start); n++)
2112 contribution[n].pixel=start+n;
2113 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2114 ((MagickRealType) (start+n)-center+0.5));
2115 density+=contribution[n].weight;
2117 if ((density != 0.0) && (density != 1.0))
2125 density=1.0/density;
2126 for (i=0; i < n; i++)
2127 contribution[i].weight*=density;
2129 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2130 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
2131 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2133 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2138 indexes=GetCacheViewVirtualIndexQueue(image_view);
2139 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2140 for (y=0; y < (ssize_t) resize_image->rows; y++)
2155 if (image->matte == MagickFalse)
2157 for (i=0; i < n; i++)
2159 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2160 (contribution[i].pixel-contribution[0].pixel);
2161 alpha=contribution[i].weight;
2162 pixel.red+=alpha*(p+j)->red;
2163 pixel.green+=alpha*(p+j)->green;
2164 pixel.blue+=alpha*(p+j)->blue;
2165 pixel.opacity+=alpha*(p+j)->opacity;
2167 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2168 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2169 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2170 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2171 if ((image->colorspace == CMYKColorspace) &&
2172 (resize_image->colorspace == CMYKColorspace))
2174 for (i=0; i < n; i++)
2176 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2177 (contribution[i].pixel-contribution[0].pixel);
2178 alpha=contribution[i].weight;
2179 pixel.index+=alpha*indexes[j];
2181 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
2190 for (i=0; i < n; i++)
2192 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2193 (contribution[i].pixel-contribution[0].pixel);
2194 alpha=contribution[i].weight*QuantumScale*
2195 GetAlphaPixelComponent(p+j);
2196 pixel.red+=alpha*(p+j)->red;
2197 pixel.green+=alpha*(p+j)->green;
2198 pixel.blue+=alpha*(p+j)->blue;
2199 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2202 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2203 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2204 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2205 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2206 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2207 if ((image->colorspace == CMYKColorspace) &&
2208 (resize_image->colorspace == CMYKColorspace))
2210 for (i=0; i < n; i++)
2212 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2213 (contribution[i].pixel-contribution[0].pixel);
2214 alpha=contribution[i].weight*QuantumScale*
2215 GetAlphaPixelComponent(p+j);
2216 pixel.index+=alpha*indexes[j];
2218 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2219 GetIndexPixelComponent(&pixel));
2222 if ((resize_image->storage_class == PseudoClass) &&
2223 (image->storage_class == PseudoClass))
2225 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
2227 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2228 (contribution[i-start].pixel-contribution[0].pixel);
2229 resize_indexes[y]=indexes[j];
2233 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2235 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2240 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2241 #pragma omp critical (MagickCore_HorizontalFilter)
2243 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2244 if (proceed == MagickFalse)
2248 resize_view=DestroyCacheView(resize_view);
2249 image_view=DestroyCacheView(image_view);
2250 contributions=DestroyContributionThreadSet(contributions);
2254 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2255 const Image *image,Image *resize_image,const MagickRealType y_factor,
2256 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2266 **restrict contributions;
2282 Apply filter to resize vertically from image to resize image.
2284 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
2285 support=scale*GetResizeFilterSupport(resize_filter);
2286 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2287 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2289 InheritException(exception,&resize_image->exception);
2290 return(MagickFalse);
2295 Support too small even for nearest neighbour: Reduce to point
2298 support=(MagickRealType) 0.5;
2301 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2302 if (contributions == (ContributionInfo **) NULL)
2304 (void) ThrowMagickException(exception,GetMagickModule(),
2305 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2306 return(MagickFalse);
2310 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2311 image_view=AcquireCacheView(image);
2312 resize_view=AcquireCacheView(resize_image);
2313 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2314 #pragma omp parallel for shared(status)
2316 for (y=0; y < (ssize_t) resize_image->rows; y++)
2322 register const IndexPacket
2325 register const PixelPacket
2328 register ContributionInfo
2329 *restrict contribution;
2331 register IndexPacket
2332 *restrict resize_indexes;
2334 register PixelPacket
2345 if (status == MagickFalse)
2347 center=(MagickRealType) (y+0.5)/y_factor;
2348 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2349 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
2351 contribution=contributions[GetOpenMPThreadId()];
2352 for (n=0; n < (stop-start); n++)
2354 contribution[n].pixel=start+n;
2355 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2356 ((MagickRealType) (start+n)-center+0.5));
2357 density+=contribution[n].weight;
2359 if ((density != 0.0) && (density != 1.0))
2367 density=1.0/density;
2368 for (i=0; i < n; i++)
2369 contribution[i].weight*=density;
2371 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
2372 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2374 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2376 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2381 indexes=GetCacheViewVirtualIndexQueue(image_view);
2382 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2383 for (x=0; x < (ssize_t) resize_image->columns; x++)
2398 if (image->matte == MagickFalse)
2400 for (i=0; i < n; i++)
2402 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2404 alpha=contribution[i].weight;
2405 pixel.red+=alpha*(p+j)->red;
2406 pixel.green+=alpha*(p+j)->green;
2407 pixel.blue+=alpha*(p+j)->blue;
2408 pixel.opacity+=alpha*(p+j)->opacity;
2410 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2411 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2412 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2413 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2414 if ((image->colorspace == CMYKColorspace) &&
2415 (resize_image->colorspace == CMYKColorspace))
2417 for (i=0; i < n; i++)
2419 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2421 alpha=contribution[i].weight;
2422 pixel.index+=alpha*indexes[j];
2424 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
2433 for (i=0; i < n; i++)
2435 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2437 alpha=contribution[i].weight*QuantumScale*
2438 GetAlphaPixelComponent(p+j);
2439 pixel.red+=alpha*(p+j)->red;
2440 pixel.green+=alpha*(p+j)->green;
2441 pixel.blue+=alpha*(p+j)->blue;
2442 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2445 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2446 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2447 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2448 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2449 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2450 if ((image->colorspace == CMYKColorspace) &&
2451 (resize_image->colorspace == CMYKColorspace))
2453 for (i=0; i < n; i++)
2455 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2457 alpha=contribution[i].weight*QuantumScale*
2458 GetAlphaPixelComponent(p+j);
2459 pixel.index+=alpha*indexes[j];
2461 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2462 GetIndexPixelComponent(&pixel));
2465 if ((resize_image->storage_class == PseudoClass) &&
2466 (image->storage_class == PseudoClass))
2468 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
2470 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
2472 resize_indexes[x]=indexes[j];
2476 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2478 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2483 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2484 #pragma omp critical (MagickCore_VerticalFilter)
2486 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2487 if (proceed == MagickFalse)
2491 resize_view=DestroyCacheView(resize_view);
2492 image_view=DestroyCacheView(image_view);
2493 contributions=DestroyContributionThreadSet(contributions);
2497 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2498 const size_t rows,const FilterTypes filter,const double blur,
2499 ExceptionInfo *exception)
2501 #define WorkLoadFactor 0.265
2527 Acquire resize image.
2529 assert(image != (Image *) NULL);
2530 assert(image->signature == MagickSignature);
2531 if (image->debug != MagickFalse)
2532 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2533 assert(exception != (ExceptionInfo *) NULL);
2534 assert(exception->signature == MagickSignature);
2535 if ((columns == 0) || (rows == 0))
2536 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2537 if ((columns == image->columns) && (rows == image->rows) &&
2538 (filter == UndefinedFilter) && (blur == 1.0))
2539 return(CloneImage(image,0,0,MagickTrue,exception));
2540 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2541 if (resize_image == (Image *) NULL)
2542 return(resize_image);
2544 Acquire resize filter.
2546 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2547 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2548 if ((x_factor*y_factor) > WorkLoadFactor)
2549 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2551 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2552 if (filter_image == (Image *) NULL)
2553 return(DestroyImage(resize_image));
2554 filter_type=LanczosFilter;
2555 if (filter != UndefinedFilter)
2558 if ((x_factor == 1.0) && (y_factor == 1.0))
2559 filter_type=PointFilter;
2561 if ((image->storage_class == PseudoClass) ||
2562 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2563 filter_type=MitchellFilter;
2564 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2570 if ((x_factor*y_factor) > WorkLoadFactor)
2572 span=(MagickSizeType) (filter_image->columns+rows);
2573 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
2575 resize_filter=DestroyResizeFilter(resize_filter);
2576 resize_filter=AcquireResizeFilter(filter_image,filter_type,blur,
2577 MagickFalse,exception);
2578 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
2579 span,&offset,exception);
2583 span=(MagickSizeType) (filter_image->rows+columns);
2584 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
2586 resize_filter=DestroyResizeFilter(resize_filter);
2587 resize_filter=AcquireResizeFilter(filter_image,filter_type,blur,
2588 MagickFalse,exception);
2589 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
2590 span,&offset,exception);
2595 filter_image=DestroyImage(filter_image);
2596 resize_filter=DestroyResizeFilter(resize_filter);
2597 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2598 return((Image *) NULL);
2599 resize_image->type=image->type;
2600 return(resize_image);
2604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2608 % S a m p l e I m a g e %
2612 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2614 % SampleImage() scales an image to the desired dimensions with pixel
2615 % sampling. Unlike other scaling methods, this method does not introduce
2616 % any additional color into the scaled image.
2618 % The format of the SampleImage method is:
2620 % Image *SampleImage(const Image *image,const size_t columns,
2621 % const size_t rows,ExceptionInfo *exception)
2623 % A description of each parameter follows:
2625 % o image: the image.
2627 % o columns: the number of columns in the sampled image.
2629 % o rows: the number of rows in the sampled image.
2631 % o exception: return any errors or warnings in this structure.
2634 MagickExport Image *SampleImage(const Image *image,const size_t columns,
2635 const size_t rows,ExceptionInfo *exception)
2637 #define SampleImageTag "Sample/Image"
2660 Initialize sampled image attributes.
2662 assert(image != (const Image *) NULL);
2663 assert(image->signature == MagickSignature);
2664 if (image->debug != MagickFalse)
2665 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2666 assert(exception != (ExceptionInfo *) NULL);
2667 assert(exception->signature == MagickSignature);
2668 if ((columns == 0) || (rows == 0))
2669 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2670 if ((columns == image->columns) && (rows == image->rows))
2671 return(CloneImage(image,0,0,MagickTrue,exception));
2672 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2673 if (sample_image == (Image *) NULL)
2674 return((Image *) NULL);
2676 Allocate scan line buffer and column offset buffers.
2678 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
2680 if (x_offset == (ssize_t *) NULL)
2682 sample_image=DestroyImage(sample_image);
2683 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2685 for (x=0; x < (ssize_t) sample_image->columns; x++)
2686 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
2687 sample_image->columns);
2693 image_view=AcquireCacheView(image);
2694 sample_view=AcquireCacheView(sample_image);
2695 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2696 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2698 for (y=0; y < (ssize_t) sample_image->rows; y++)
2700 register const IndexPacket
2703 register const PixelPacket
2706 register IndexPacket
2707 *restrict sample_indexes;
2709 register PixelPacket
2718 if (status == MagickFalse)
2720 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2721 sample_image->rows);
2722 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2724 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2726 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2731 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2732 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2736 for (x=0; x < (ssize_t) sample_image->columns; x++)
2737 *q++=p[x_offset[x]];
2738 if ((image->storage_class == PseudoClass) ||
2739 (image->colorspace == CMYKColorspace))
2740 for (x=0; x < (ssize_t) sample_image->columns; x++)
2741 sample_indexes[x]=indexes[x_offset[x]];
2742 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2744 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2749 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2750 #pragma omp critical (MagickCore_SampleImage)
2752 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2753 if (proceed == MagickFalse)
2757 image_view=DestroyCacheView(image_view);
2758 sample_view=DestroyCacheView(sample_view);
2759 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
2760 sample_image->type=image->type;
2761 return(sample_image);
2765 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2769 % S c a l e I m a g e %
2773 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2775 % ScaleImage() changes the size of an image to the given dimensions.
2777 % The format of the ScaleImage method is:
2779 % Image *ScaleImage(const Image *image,const size_t columns,
2780 % const size_t rows,ExceptionInfo *exception)
2782 % A description of each parameter follows:
2784 % o image: the image.
2786 % o columns: the number of columns in the scaled image.
2788 % o rows: the number of rows in the scaled image.
2790 % o exception: return any errors or warnings in this structure.
2793 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2794 const size_t rows,ExceptionInfo *exception)
2796 #define ScaleImageTag "Scale/Image"
2830 Initialize scaled image attributes.
2832 assert(image != (const Image *) NULL);
2833 assert(image->signature == MagickSignature);
2834 if (image->debug != MagickFalse)
2835 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2836 assert(exception != (ExceptionInfo *) NULL);
2837 assert(exception->signature == MagickSignature);
2838 if ((columns == 0) || (rows == 0))
2839 return((Image *) NULL);
2840 if ((columns == image->columns) && (rows == image->rows))
2841 return(CloneImage(image,0,0,MagickTrue,exception));
2842 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2843 if (scale_image == (Image *) NULL)
2844 return((Image *) NULL);
2845 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2847 InheritException(exception,&scale_image->exception);
2848 scale_image=DestroyImage(scale_image);
2849 return((Image *) NULL);
2854 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2857 if (image->rows != scale_image->rows)
2858 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2860 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2861 scale_image->columns,sizeof(*scale_scanline));
2862 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2864 if ((scanline == (MagickPixelPacket *) NULL) ||
2865 (scale_scanline == (MagickPixelPacket *) NULL) ||
2866 (x_vector == (MagickPixelPacket *) NULL) ||
2867 (y_vector == (MagickPixelPacket *) NULL))
2869 scale_image=DestroyImage(scale_image);
2870 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2876 next_row=MagickTrue;
2878 scale.y=(double) scale_image->rows/(double) image->rows;
2879 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2881 GetMagickPixelPacket(image,&pixel);
2882 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2884 image_view=AcquireCacheView(image);
2885 scale_view=AcquireCacheView(scale_image);
2886 for (y=0; y < (ssize_t) scale_image->rows; y++)
2888 register const IndexPacket
2891 register const PixelPacket
2894 register IndexPacket
2895 *restrict scale_indexes;
2897 register MagickPixelPacket
2901 register PixelPacket
2907 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2909 if (q == (PixelPacket *) NULL)
2911 scale_indexes=GetAuthenticIndexQueue(scale_image);
2912 if (scale_image->rows == image->rows)
2915 Read a new scanline.
2917 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2919 if (p == (const PixelPacket *) NULL)
2921 indexes=GetCacheViewVirtualIndexQueue(image_view);
2922 for (x=0; x < (ssize_t) image->columns; x++)
2924 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2925 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2926 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
2927 if (image->matte != MagickFalse)
2928 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
2929 if (indexes != (IndexPacket *) NULL)
2930 x_vector[x].index=(MagickRealType) indexes[x];
2939 while (scale.y < span.y)
2941 if ((next_row != MagickFalse) &&
2942 (number_rows < (ssize_t) image->rows))
2945 Read a new scanline.
2947 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2949 if (p == (const PixelPacket *) NULL)
2951 indexes=GetCacheViewVirtualIndexQueue(image_view);
2952 for (x=0; x < (ssize_t) image->columns; x++)
2954 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2955 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2956 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
2957 if (image->matte != MagickFalse)
2958 x_vector[x].opacity=(MagickRealType)
2959 GetOpacityPixelComponent(p);
2960 if (indexes != (IndexPacket *) NULL)
2961 x_vector[x].index=(MagickRealType) indexes[x];
2966 for (x=0; x < (ssize_t) image->columns; x++)
2968 y_vector[x].red+=scale.y*x_vector[x].red;
2969 y_vector[x].green+=scale.y*x_vector[x].green;
2970 y_vector[x].blue+=scale.y*x_vector[x].blue;
2971 if (scale_image->matte != MagickFalse)
2972 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2973 if (scale_indexes != (IndexPacket *) NULL)
2974 y_vector[x].index+=scale.y*x_vector[x].index;
2977 scale.y=(double) scale_image->rows/(double) image->rows;
2978 next_row=MagickTrue;
2980 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
2983 Read a new scanline.
2985 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2987 if (p == (const PixelPacket *) NULL)
2989 indexes=GetCacheViewVirtualIndexQueue(image_view);
2990 for (x=0; x < (ssize_t) image->columns; x++)
2992 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2993 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2994 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
2995 if (image->matte != MagickFalse)
2996 x_vector[x].opacity=(MagickRealType)
2997 GetOpacityPixelComponent(p);
2998 if (indexes != (IndexPacket *) NULL)
2999 x_vector[x].index=(MagickRealType) indexes[x];
3003 next_row=MagickFalse;
3006 for (x=0; x < (ssize_t) image->columns; x++)
3008 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3009 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3010 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3011 if (image->matte != MagickFalse)
3012 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3013 if (scale_indexes != (IndexPacket *) NULL)
3014 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3016 s->green=pixel.green;
3018 if (scale_image->matte != MagickFalse)
3019 s->opacity=pixel.opacity;
3020 if (scale_indexes != (IndexPacket *) NULL)
3021 s->index=pixel.index;
3028 scale.y=(double) scale_image->rows/(double) image->rows;
3029 next_row=MagickTrue;
3033 if (scale_image->columns == image->columns)
3036 Transfer scanline to scaled image.
3039 for (x=0; x < (ssize_t) scale_image->columns; x++)
3041 q->red=ClampToQuantum(s->red);
3042 q->green=ClampToQuantum(s->green);
3043 q->blue=ClampToQuantum(s->blue);
3044 if (scale_image->matte != MagickFalse)
3045 q->opacity=ClampToQuantum(s->opacity);
3046 if (scale_indexes != (IndexPacket *) NULL)
3047 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
3058 next_column=MagickFalse;
3062 for (x=0; x < (ssize_t) image->columns; x++)
3064 scale.x=(double) scale_image->columns/(double) image->columns;
3065 while (scale.x >= span.x)
3067 if (next_column != MagickFalse)
3072 pixel.red+=span.x*s->red;
3073 pixel.green+=span.x*s->green;
3074 pixel.blue+=span.x*s->blue;
3075 if (image->matte != MagickFalse)
3076 pixel.opacity+=span.x*s->opacity;
3077 if (scale_indexes != (IndexPacket *) NULL)
3078 pixel.index+=span.x*s->index;
3080 t->green=pixel.green;
3082 if (scale_image->matte != MagickFalse)
3083 t->opacity=pixel.opacity;
3084 if (scale_indexes != (IndexPacket *) NULL)
3085 t->index=pixel.index;
3088 next_column=MagickTrue;
3092 if (next_column != MagickFalse)
3095 next_column=MagickFalse;
3098 pixel.red+=scale.x*s->red;
3099 pixel.green+=scale.x*s->green;
3100 pixel.blue+=scale.x*s->blue;
3101 if (scale_image->matte != MagickFalse)
3102 pixel.opacity+=scale.x*s->opacity;
3103 if (scale_indexes != (IndexPacket *) NULL)
3104 pixel.index+=scale.x*s->index;
3112 pixel.red+=span.x*s->red;
3113 pixel.green+=span.x*s->green;
3114 pixel.blue+=span.x*s->blue;
3115 if (scale_image->matte != MagickFalse)
3116 pixel.opacity+=span.x*s->opacity;
3117 if (scale_indexes != (IndexPacket *) NULL)
3118 pixel.index+=span.x*s->index;
3120 if ((next_column == MagickFalse) &&
3121 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
3124 t->green=pixel.green;
3126 if (scale_image->matte != MagickFalse)
3127 t->opacity=pixel.opacity;
3128 if (scale_indexes != (IndexPacket *) NULL)
3129 t->index=pixel.index;
3132 Transfer scanline to scaled image.
3135 for (x=0; x < (ssize_t) scale_image->columns; x++)
3137 q->red=ClampToQuantum(t->red);
3138 q->green=ClampToQuantum(t->green);
3139 q->blue=ClampToQuantum(t->blue);
3140 if (scale_image->matte != MagickFalse)
3141 q->opacity=ClampToQuantum(t->opacity);
3142 if (scale_indexes != (IndexPacket *) NULL)
3143 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
3148 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
3150 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3152 if (proceed == MagickFalse)
3155 scale_view=DestroyCacheView(scale_view);
3156 image_view=DestroyCacheView(image_view);
3158 Free allocated memory.
3160 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3161 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3162 if (scale_image->rows != image->rows)
3163 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3164 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3165 scale_image->type=image->type;
3166 return(scale_image);
3170 THIS IS NOT USED -- to be removed
3172 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3176 + S e t R e s i z e F i l t e r S u p p o r t %
3180 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3182 % SetResizeFilterSupport() specifies which IR filter to use to window
3184 % The format of the SetResizeFilterSupport method is:
3186 % void SetResizeFilterSupport(ResizeFilter *resize_filter,
3187 % const MagickRealType support)
3189 % A description of each parameter follows:
3191 % o resize_filter: the resize filter.
3193 % o support: the filter spport radius.
3196 MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3197 const MagickRealType support)
3199 assert(resize_filter != (ResizeFilter *) NULL);
3200 assert(resize_filter->signature == MagickSignature);
3201 resize_filter->support=support;
3206 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3210 % T h u m b n a i l I m a g e %
3214 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3216 % ThumbnailImage() changes the size of an image to the given dimensions and
3217 % removes any associated profiles. The goal is to produce small low cost
3218 % thumbnail images suited for display on the Web.
3220 % The format of the ThumbnailImage method is:
3222 % Image *ThumbnailImage(const Image *image,const size_t columns,
3223 % const size_t rows,ExceptionInfo *exception)
3225 % A description of each parameter follows:
3227 % o image: the image.
3229 % o columns: the number of columns in the scaled image.
3231 % o rows: the number of rows in the scaled image.
3233 % o exception: return any errors or warnings in this structure.
3236 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3237 const size_t rows,ExceptionInfo *exception)
3239 #define SampleFactor 5
3242 value[MaxTextExtent];
3260 assert(image != (Image *) NULL);
3261 assert(image->signature == MagickSignature);
3262 if (image->debug != MagickFalse)
3263 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3264 assert(exception != (ExceptionInfo *) NULL);
3265 assert(exception->signature == MagickSignature);
3266 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3267 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3268 if ((x_factor*y_factor) > 0.1)
3269 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3272 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
3273 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3274 image->blur,exception);
3280 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3282 if (sample_image == (Image *) NULL)
3283 return((Image *) NULL);
3284 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3285 image->blur,exception);
3286 sample_image=DestroyImage(sample_image);
3288 if (thumbnail_image == (Image *) NULL)
3289 return(thumbnail_image);
3290 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3291 if (thumbnail_image->matte == MagickFalse)
3292 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3293 thumbnail_image->depth=8;
3294 thumbnail_image->interlace=NoInterlace;
3296 Strip all profiles except color profiles.
3298 ResetImageProfileIterator(thumbnail_image);
3299 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3301 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3303 (void) DeleteImageProfile(thumbnail_image,name);
3304 ResetImageProfileIterator(thumbnail_image);
3306 name=GetNextImageProfile(thumbnail_image);
3308 (void) DeleteImageProperty(thumbnail_image,"comment");
3309 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3310 if (strstr(image->magick_filename,"//") == (char *) NULL)
3311 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
3312 image->magick_filename);
3313 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3314 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3315 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3317 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3318 attributes.st_mtime);
3319 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3321 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3322 attributes.st_mtime);
3323 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
3324 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
3325 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3326 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3328 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3329 (void) SetImageProperty(thumbnail_image,"software",
3330 GetMagickVersion(&version));
3331 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3332 image->magick_columns);
3333 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
3334 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3335 image->magick_rows);
3336 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
3337 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3338 GetImageListLength(image));
3339 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3340 return(thumbnail_image);