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 cubic[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 its is more accuritally named "Jinc".
150 return(0.25*MagickPIL);
151 return(BesselOrderOne(MagickPIL*x)/(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->cubic[0]+x*(resize_filter->cubic[1]+x*
227 (resize_filter->cubic[2]+x*resize_filter->cubic[3])));
229 return(resize_filter->cubic[4]+x*(resize_filter->cubic[5]+x*
230 (resize_filter->cubic[6]+x*resize_filter->cubic[7])));
234 static MagickRealType Gaussian(const MagickRealType x,
235 const ResizeFilter *magick_unused(resize_filter))
238 1D Gaussian with sigma=1/2:
239 exp(-2 x^2)/sqrt(pi/2))
241 /*const MagickRealType alpha = (MagickRealType) (2.0/MagickSQ2PI);*/
242 return(exp((double) (-2.0*x*x)));
245 static MagickRealType Hanning(const MagickRealType x,
246 const ResizeFilter *magick_unused(resize_filter))
249 Cosine window function:
252 const MagickRealType cospix = cos((double) (MagickPIL*x));
253 return(0.5+0.5*cospix);
256 static MagickRealType Hamming(const MagickRealType x,
257 const ResizeFilter *magick_unused(resize_filter))
260 Offset cosine window function:
263 const MagickRealType cospix = cos((double) (MagickPIL*x));
264 return(0.54+0.46*cospix);
267 static MagickRealType Kaiser(const MagickRealType x,
268 const ResizeFilter *magick_unused(resize_filter))
271 #define I0A (1.0/I0(Alpha))
274 Kaiser Windowing Function (bessel windowing): Alpha is a free
275 value from 5 to 8 (currently hardcoded to 6.5).
276 Future: make alpha the IOA pre-calculation, an 'expert' setting.
278 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
281 static MagickRealType Lagrange(const MagickRealType x,
282 const ResizeFilter *resize_filter)
295 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the
296 lagrange function and depends on the overall support window size
297 of the filter. That is: for a support of 2, it gives a lagrange-4
298 (piecewise cubic function).
300 "n" identifies the piece of the piecewise polynomial.
302 See Survey: Interpolation Methods, IEEE Transactions on Medical
303 Imaging, Vol 18, No 11, November 1999, p1049-1075, -- Equation 27
306 if (x > resize_filter->support)
308 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
309 /*n=(ssize_t)((1.0*order)/2.0+x); -- which piece does x belong to */
310 n = (ssize_t)(resize_filter->window_support + x);
312 for (i=0; i < order; i++)
314 value*=(n-i-x)/(n-i);
318 static MagickRealType Quadratic(const MagickRealType x,
319 const ResizeFilter *magick_unused(resize_filter))
322 2rd order (quadratic) B-Spline approximation of Gaussian.
327 return(0.5*(x-1.5)*(x-1.5));
331 static MagickRealType Sinc(const MagickRealType x,
332 const ResizeFilter *magick_unused(resize_filter))
335 Scaled sinc(x) function using a trig call:
336 sinc(x) == sin(pi x)/(pi x).
340 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
341 return(sin((double) pix)/pix);
343 return((MagickRealType) 1.0);
346 static MagickRealType SincFast(const MagickRealType x,
347 const ResizeFilter *magick_unused(resize_filter))
350 Approximations of the sinc function sin(pi x)/(pi x) over the
351 interval [-4,4] constructed by Nicolas Robidoux and Chantal
352 Racette with funding from the Natural Sciences and Engineering
353 Research Council of Canada.
355 Although the approximations are polynomials (for low order of
356 approximation) and quotients of polynomials (for higher order of
357 approximation) and consequently are similar in form to Taylor
358 polynomials/Pade approximants, the approximations are computed
359 with a completely different technique.
361 Summary: These approximations are "the best" in terms of bang
362 (accuracy) for the buck (flops). More specifically: Among the
363 polynomial quotients that can be computed using a fixed number of
364 flops (with a given "+ - * / budget"), the chosen polynomial
365 quotient is the one closest to the approximated function with
366 respect to maximum absolute relative error over the given
369 The Remez algorithm, as implemented in the boost library's minimax
370 package, is the key to the construction:
371 http://www.boost.org/doc/libs/1_36_0/libs/math/doc/...
372 ...sf_and_dist/html/math_toolkit/backgrounders/remez.html
375 If outside of the interval of approximation, use the standard trig
380 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
381 return(sin((double) pix)/pix);
385 The approximations only depend on x^2 (sinc is an even
388 const MagickRealType xx = x*x;
389 #if MAGICKCORE_QUANTUM_DEPTH <= 8
391 Maximum absolute relative error 6.3e-6 < 1/2^17.
393 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
394 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
395 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
396 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
397 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
398 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
399 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
400 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
401 const MagickRealType p =
402 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
403 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
404 #elif MAGICKCORE_QUANTUM_DEPTH <= 16
406 Max. abs. rel. error 2.2e-8 < 1/2^25.
408 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
409 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
410 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
411 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
412 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
413 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
414 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
415 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
416 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
417 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
418 const MagickRealType p =
419 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
420 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
423 Max. abs. rel. error 1.2e-12 < 1/2^39.
425 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
426 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
427 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
428 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
429 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
430 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
431 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
432 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
433 const MagickRealType p =
434 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
435 const MagickRealType d0 = 1.0L;
436 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
437 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
438 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
439 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
440 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
441 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
446 static MagickRealType Triangle(const MagickRealType x,
447 const ResizeFilter *magick_unused(resize_filter))
450 1st order (linear) B-Spline, bilinear interpolation, Tent 1D
451 filter, or a Bartlett 2D Cone filter.
458 static MagickRealType Welsh(const MagickRealType x,
459 const ResizeFilter *magick_unused(resize_filter))
462 Welsh parabolic windowing filter.
470 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
474 + A c q u i r e R e s i z e F i l t e r %
478 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
480 % AcquireResizeFilter() allocates the ResizeFilter structure. Choose
481 % from these filters:
483 % FIR (Finite impulse Response) Filters
484 % Box Triangle Quadratic
485 % Cubic Hermite Catrom
488 % IIR (Infinite impulse Response) Filters
489 % Gaussian Sinc Jinc (Bessel)
491 % Windowed Sinc/Jinc Filters
492 % Blackman Hanning Hamming
495 % Special purpose Filters
498 % The users "-filter" selection is used to lookup the default 'expert'
499 % settings for that filter from a internal table. However any provided
500 % 'expert' settings (see below) may override this selection.
502 % FIR filters are used as is, and are limited to that filters support
503 % window (unless over-ridden). 'Gaussian' while classed as an IIR
504 % filter, is also simply clipped by its support size (currently 1.5
505 % ro approximatally 3*sigma as recommended by many references)
507 % The selection is typically either a windowed Sinc, or interpolated
508 % filter, for use by functions such as ResizeImage(). However if a
509 % 'cylindrical' filter flag is requested, any default Sinc weighting
510 % and windowing functions will be promoted to cylindrical Jinc form of
513 % Directly requesting 'Sinc' or 'Jinc' will force the use of that
514 % filter function without any windowing. This is not recommended,
515 % except by image processing experts or in expert options. Selecting a
516 % window filtering version of these functions is better.
518 % Lanczos is a special case of a Sinc-windowed Sinc, (or Jinc-Jinc for
519 % the cylindrical case) but defaulting to 3-lobe support, rather that
520 % the default 4 lobe support of the other windowed sinc/jinc filters.
522 % Two forms of the 'Sinc' function are available: Sinc and SincFast.
523 % Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
524 % selected if the user specifically specifies the use of a Sinc
525 % filter. SincFast uses highly accurate (and fast) polynomial (low Q)
526 % and rational (high Q) approximations, and will be used by default in
529 % The Lanczos2D filter is just a normal 2-lobed Lanczos filter. But if
530 % selected as a cylindrical (radial) filter, the 2-lobed Jinc windowed
531 % Jinc will be modified by a blur factor to negate the effect of windowing
532 % the Jinc function, and greatly reduce resulting blur.
534 % Special 'expert' options can be used to override any and all filter
535 % settings. This is not advised unless you have expert knowledge of
536 % the use of resampling filtered techniques. Check on the results of
537 % your selections using the "filter:verbose" setting to make sure you
538 % get the exact filter that you are tring to achieve.
540 % "filter:filter" Select the main function associated with
541 % this filter name, as the weighting function of the filter.
542 % This can be used to set a windowing function as a weighting
543 % function, for special purposes, such as graphing.
545 % If a "filter:window" operation has not been provided, then a 'Box'
546 % windowing function will be set to denote that no windowing function
549 % "filter:window" Select this windowing function for the filter.
550 % While any filter could be used as a windowing function, using the
551 % 'first lobe' of that filter over the whole support window, using a
552 % non-windowing function is not advisible. If no weighting filter
553 % function is specifed a 'SincFast' filter will be used.
555 % "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
556 % This a simpler method of setting filter support size that will
557 % correctly handle the Sinc/Jinc switch for an operators filtering
558 % requirements. Only integers should be given.
560 % "filter:support" Set the support size for filtering to the size given
561 % This not recommended for Sinc/Jinc windowed filters (lobes should
562 % be used instead). This will override any 'filter:lobes' option.
564 % "filter:win-support" Scale windowing function to this size instead.
565 % This causes the windowing (or self-windowing Lagrange filter) to act
566 % is if the support window it much much larger than what is actually
567 % supplied to the calling operator. The filter however is still
568 % clipped to the real support size given, by the support range suppiled
569 % to the caller. If unset this will equal the normal filter support
572 % "filter:blur" Scale the filter and support window by this amount.
573 % A value >1 will generally result in a more burred image with
574 % more ringing effects, while a value <1 will sharpen the
575 % resulting image with more aliasing and Morie effects.
578 % "filter:c" Override the preset B,C values for a Cubic type of filter
579 % If only one of these are given it is assumes to be a 'Keys'
580 % type of filter such that B+2C=1, where Keys 'alpha' value = C
582 % "filter:verbose" Output the exact results of the filter selections
583 % made, as well as plotting data for graphing the resulting filter
584 % over support range (blur adjusted).
586 % Set a true un-windowed Sinc filter with 10 lobes (very slow)
587 % -define filter:filter=Sinc
588 % -define filter:lobes=8
590 % For example force an 8 lobe Lanczos (Sinc or Jinc) filter...
592 % -define filter:lobes=8
594 % The format of the AcquireResizeFilter method is:
596 % ResizeFilter *AcquireResizeFilter(const Image *image,
597 % const FilterTypes filter_type, const MagickBooleanType radial,
598 % ExceptionInfo *exception)
600 % A description of each parameter follows:
602 % o image: the image.
604 % o filter: the filter type, defining a preset filter, window and
605 % support. The artifact settings listed above will override
608 % o blur: blur the filter by this amount, use 1.0 if unknown. Image
609 % artifact "filter:blur" will override this API call usage, including
610 % any internal change (such as for cylindrical usage).
612 % o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
613 % (radial) filter (Jinc)
615 % o exception: return any errors or warnings in this structure.
618 MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
619 const FilterTypes filter,const MagickRealType blur,
620 const MagickBooleanType cylindrical,ExceptionInfo *exception)
633 register ResizeFilter
640 Table Mapping given Filter, into Weighting and Windowing functions.
641 A 'Box' windowing function means its a simble non-windowed filter.
642 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
643 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
644 specifically requested.
646 WARNING: The order of this tabel must match the order of the
647 FilterTypes enumeration specified in "resample.h", or the filter
648 names will not match the filter being setup.
650 You can check filter setups with the "filter:verbose" setting.
657 } const mapping[SentinelFilter] =
659 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
660 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
661 { BoxFilter, BoxFilter }, /* Box averaging filter */
662 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
663 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
664 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
665 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
666 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
667 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
668 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
669 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
670 { CatromFilter, BoxFilter }, /* Cubic interpolator */
671 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
672 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
673 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
674 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
675 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
676 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
677 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
678 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
679 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
680 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
681 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
682 { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
685 Table mapping the filter/window from the above table to an actual
686 function. The default support size for that filter as a weighting
687 function, the range to scale with to use that function as a sinc
688 windowing function, (typ 1.0).
690 Note that the filter_type -> function is 1 to 1 except for Sinc(),
691 SincFast(), and CubicBC() functions, which may have multiple
692 filter to function associations.
694 See "filter:verbose" handling below for the function -> filter
700 (*function)(const MagickRealType, const ResizeFilter*),
701 lobes, /* default lobes/support size of the weighting filter */
702 scale, /* windowing function range, for scaling windowing function */
703 B,C; /* Cubic Filter factors for a CubicBC function, else ignored */
704 } const filters[SentinelFilter] =
706 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
707 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
708 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
709 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
710 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
711 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
712 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
713 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
714 { Gaussian, 1.5, 1.5, 0.0, 0.0 }, /* Gaussian */
715 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
716 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
717 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
718 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
719 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed sinc-sinc */
720 { Jinc, 3.0, 1.21967, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
721 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
722 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
723 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
724 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
725 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
726 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
727 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
728 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
729 { Jinc, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2D, adjusted below */
732 The known zero crossings of the Jinc() or more accuritally the Jinc(x*PI)
733 function being used as a filter. It is used by the "filter:lobes" for of
734 support selection, so users do not have to deal with the highly irrational
735 sizes of the 'lobes' of the Jinc filter.
737 Values were sourced from
738 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
739 Using Jv-function with v=1, then divided by PI.
741 static MagickRealType
763 Allocate resize filter.
765 assert(image != (const Image *) NULL);
766 assert(image->signature == MagickSignature);
767 if (image->debug != MagickFalse)
768 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
769 assert(UndefinedFilter < filter && filter < SentinelFilter);
770 assert(exception != (ExceptionInfo *) NULL);
771 assert(exception->signature == MagickSignature);
772 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
773 if (resize_filter == (ResizeFilter *) NULL)
774 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
776 Defaults for the requested filter.
778 filter_type=mapping[filter].filter;
779 window_type=mapping[filter].window;
780 /* Cylindrical Filters should use Jinc instead of Sinc */
781 if (cylindrical != MagickFalse)
785 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
786 if ( filter != SincFilter )
787 filter_type=JincFilter;
790 /* Ditto for SincFast variant */
791 if ( filter != SincFastFilter )
792 filter_type=JincFilter;
796 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc */
797 filter_type=JincFilter;
798 window_type=JincFilter;
806 case Lanczos2DFilter:
807 /* depromote to a 2-lobe Sinc-Sinc for orthoginal use */
808 window_type=SincFastFilter;
814 artifact=GetImageArtifact(image,"filter:filter");
815 if (artifact != (const char *) NULL)
817 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
818 if ((UndefinedFilter < option) && (option < SentinelFilter))
819 { /* Raw filter request - no window function. */
820 filter_type=(FilterTypes) option;
821 window_type=BoxFilter;
823 if (option == LanczosFilter)
824 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
825 filter_type=cylindrical != MagickFalse ? JincFilter : Lanczos2DFilter;
826 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
828 /* Filter override with a specific window function. */
829 artifact=GetImageArtifact(image,"filter:window");
830 if (artifact != (const char *) NULL)
832 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
833 if ((UndefinedFilter < option) && (option < SentinelFilter))
835 if (option != LanczosFilter)
836 window_type=(FilterTypes) option;
838 window_type=cylindrical != MagickFalse ? JincFilter :
845 /* Window specified, but no filter function? Assume Sinc/Jinc. */
846 artifact=GetImageArtifact(image,"filter:window");
847 if (artifact != (const char *) NULL)
849 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
851 if ((UndefinedFilter < option) && (option < SentinelFilter))
853 filter_type=cylindrical != MagickFalse ?
854 JincFilter : SincFastFilter;
855 window_type=(FilterTypes) option;
859 /* Assign the real functions to use for the filters selected. */
860 resize_filter->filter=filters[filter_type].function;
861 resize_filter->support=filters[filter_type].lobes;
862 resize_filter->window=filters[window_type].function;
863 resize_filter->scale=filters[window_type].scale;
864 resize_filter->signature=MagickSignature;
866 /* Filter blur -- scaling both filter and support window. */
867 resize_filter->blur=blur;
868 artifact=GetImageArtifact(image,"filter:blur");
869 if (artifact != (const char *) NULL)
870 resize_filter->blur=StringToDouble(artifact);
871 if (resize_filter->blur < MagickEpsilon)
872 resize_filter->blur=(MagickRealType) MagickEpsilon;
874 if (cylindrical != MagickFalse)
879 /* Support for Cylindrical Box should be sqrt(2)/2 */
880 resize_filter->support=(MagickRealType) MagickSQ1_2;
883 /* Cylindrical Gaussian should have a sigma of sqrt(2)/2
884 * and not the default sigma of 1/2 - so use blur to enlarge
885 * and adjust support so actual practical support = 2.0 by default
887 resize_filter->blur *= MagickSQ2;
888 resize_filter->support = (MagickRealType) MagickSQ2; /* which times blur => 2.0 */
890 case Lanczos2DFilter:
891 /* Special 2 lobed cylindrical Jinc-Jinc filter,
892 * with a special blur adjustment to remove the blurring
893 * effect of the windowing of the Jinc function (in the 2
894 * lobed case only). To be used as the default filter for EWA
895 * Resampling and Distorts.
897 * Derivation: Set the scaling s=1/blur of the Lanczos2D
898 * filter function so that
899 * Lanczos2D(s)=-2*Lanczos2D(s*sqrt(2))-Lanczos2D(s*2)
900 * which implies that with a no-op a single vertical line is
901 * not amplified (although it has negative ripples), and also
902 * so that the high frequency checkerboard mode, as well as
903 * the high frequency vertical, and horizontal, stripe modes
904 * are slightly damped.
906 * (The value which preserves the high frequency vertical, and
907 * horizontal, stripe modes (and slightly dampens the
908 * checkerboard mode) satisfies
909 * Lanczos2D(s)=-2*Lanczos2D(s*sqrt(2))
910 * which gives 0.9549921738 instead of 0.958033808.)
912 * Note from Nicolas: It is still not totally clear what
913 * scaling of the Lanczos2D kernel is optimal for Clamped-EWA
916 resize_filter->blur *= (MagickRealType) 0.958033808;
923 case Lanczos2DFilter:
924 /* depromote to a 2-lobe Sinc-Sinc for orthoginal use */
925 resize_filter->filter=SincFast;
931 /* Filter support overrides. */
932 artifact=GetImageArtifact(image,"filter:lobes");
933 if (artifact != (const char *) NULL)
938 lobes=(ssize_t) StringToLong(artifact);
941 resize_filter->support=(MagickRealType) lobes;
943 /* convert Jinc lobes to a real support value */
944 if (resize_filter->filter == Jinc)
946 if (resize_filter->support > 16)
947 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
949 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
951 /* expert override of the support setting */
952 artifact=GetImageArtifact(image,"filter:support");
953 if (artifact != (const char *) NULL)
954 resize_filter->support=fabs(StringToDouble(artifact));
956 Scale windowing function separatally to the support 'clipping'
957 window that calling operator is planning to actually use. (Expert
960 resize_filter->window_support=resize_filter->support; /* default */
961 artifact=GetImageArtifact(image,"filter:win-support");
962 if (artifact != (const char *) NULL)
963 resize_filter->window_support=fabs(StringToDouble(artifact));
965 Adjust window function scaling to the windowing support for
966 weighting function. This avoids a division on every filter call.
968 resize_filter->scale /= resize_filter->window_support;
970 Set Cubic Spline B,C values, calculate Cubic coefficients.
974 if ((filters[filter_type].function == CubicBC) ||
975 (filters[window_type].function == CubicBC))
977 B=filters[filter_type].B;
978 C=filters[filter_type].C;
979 if (filters[window_type].function == CubicBC)
981 B=filters[window_type].B;
982 C=filters[window_type].C;
984 artifact=GetImageArtifact(image,"filter:b");
985 if (artifact != (const char *) NULL)
987 B=StringToDouble(artifact);
988 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
989 artifact=GetImageArtifact(image,"filter:c");
990 if (artifact != (const char *) NULL)
991 C=StringToDouble(artifact);
995 artifact=GetImageArtifact(image,"filter:c");
996 if (artifact != (const char *) NULL)
998 C=StringToDouble(artifact);
999 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
1003 Convert B,C values into Cubic Coefficents. See CubicBC().
1005 resize_filter->cubic[0]=(6.0-2.0*B)/6.0;
1006 resize_filter->cubic[1]=0.0;
1007 resize_filter->cubic[2]=(-18.0+12.0*B+6.0*C)/6.0;
1008 resize_filter->cubic[3]=(12.0-9.0*B-6.0*C)/6.0;
1009 resize_filter->cubic[4]=(8.0*B+24.0*C)/6.0;
1010 resize_filter->cubic[5]=(-12.0*B-48.0*C)/6.0;
1011 resize_filter->cubic[6]=(6.0*B+30.0*C)/6.0;
1012 resize_filter->cubic[7]=(-B-6.0*C)/6.0;
1015 Expert Option Request for verbose details of the resulting filter.
1017 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1018 if( GetOpenMPThreadId() == 0 ) {
1020 artifact=GetImageArtifact(image,"filter:verbose");
1021 if (artifact != (const char *) NULL)
1028 Set the weighting function properly when the weighting
1029 function may not exactly match the filter of the same name.
1030 EG: a Point filter really uses a Box weighting function
1031 with a different support than is typically used.
1034 if (resize_filter->filter == Box) filter_type=BoxFilter;
1035 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1036 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1037 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1038 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1040 Report Filter Details.
1042 support=GetResizeFilterSupport(resize_filter); /* support range */
1043 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
1044 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1045 MagickFilterOptions,filter_type));
1046 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
1047 MagickFilterOptions, window_type));
1048 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
1049 (double) resize_filter->support);
1050 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
1051 (double) resize_filter->window_support);
1052 (void) fprintf(stdout,"# blur = %.*g\n",GetMagickPrecision(),
1053 (double) resize_filter->blur);
1054 (void) fprintf(stdout,"# blurred_support = %.*g\n",GetMagickPrecision(),
1056 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1057 (double) B,GetMagickPrecision(),(double) C);
1058 (void) fprintf(stdout,"\n");
1060 Output values of resulting filter graph -- for graphing
1063 for (x=0.0; x <= support; x+=0.01f)
1064 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1065 (double) GetResizeFilterWeight(resize_filter,x));
1066 /* A final value so gnuplot can graph the 'stop' properly. */
1067 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1070 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1073 return(resize_filter);
1077 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1081 % A d a p t i v e R e s i z e I m a g e %
1085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1087 % AdaptiveResizeImage() adaptively resize image with pixel resampling.
1089 % The format of the AdaptiveResizeImage method is:
1091 % Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1092 % const size_t rows,ExceptionInfo *exception)
1094 % A description of each parameter follows:
1096 % o image: the image.
1098 % o columns: the number of columns in the resized image.
1100 % o rows: the number of rows in the resized image.
1102 % o exception: return any errors or warnings in this structure.
1105 MagickExport Image *AdaptiveResizeImage(const Image *image,
1106 const size_t columns,const size_t rows,ExceptionInfo *exception)
1108 #define AdaptiveResizeImageTag "Resize/Image"
1132 Adaptively resize image.
1134 assert(image != (const Image *) NULL);
1135 assert(image->signature == MagickSignature);
1136 if (image->debug != MagickFalse)
1137 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1138 assert(exception != (ExceptionInfo *) NULL);
1139 assert(exception->signature == MagickSignature);
1140 if ((columns == 0) || (rows == 0))
1141 return((Image *) NULL);
1142 if ((columns == image->columns) && (rows == image->rows))
1143 return(CloneImage(image,0,0,MagickTrue,exception));
1144 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1145 if (resize_image == (Image *) NULL)
1146 return((Image *) NULL);
1147 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1149 InheritException(exception,&resize_image->exception);
1150 resize_image=DestroyImage(resize_image);
1151 return((Image *) NULL);
1153 GetMagickPixelPacket(image,&pixel);
1154 resample_filter=AcquireResampleFilter(image,exception);
1155 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
1156 (void) SetResampleFilterInterpolateMethod(resample_filter,
1157 MeshInterpolatePixel);
1158 resize_view=AcquireCacheView(resize_image);
1159 for (y=0; y < (ssize_t) resize_image->rows; y++)
1161 register IndexPacket
1162 *restrict resize_indexes;
1167 register PixelPacket
1170 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1172 if (q == (PixelPacket *) NULL)
1174 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1175 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
1176 for (x=0; x < (ssize_t) resize_image->columns; x++)
1178 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1179 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1181 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1184 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1186 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1188 if (proceed == MagickFalse)
1191 resample_filter=DestroyResampleFilter(resample_filter);
1192 resize_view=DestroyCacheView(resize_view);
1193 return(resize_image);
1197 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1201 + B e s s e l O r d e r O n e %
1205 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1207 % BesselOrderOne() computes the Bessel function of x of the first kind of
1208 % order 0. This is used to create the Jinc() filter function below.
1210 % Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1216 % j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1218 % where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1220 % cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1221 % = 1/sqrt(2) * (sin(x) - cos(x))
1222 % sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1223 % = -1/sqrt(2) * (sin(x) + cos(x))
1225 % The format of the BesselOrderOne method is:
1227 % MagickRealType BesselOrderOne(MagickRealType x)
1229 % A description of each parameter follows:
1231 % o x: MagickRealType value.
1236 static MagickRealType I0(MagickRealType x)
1247 Zeroth order Bessel function of the first kind.
1252 for (i=2; t > MagickEpsilon; i++)
1255 t*=y/((MagickRealType) i*i);
1261 static MagickRealType J1(MagickRealType x)
1273 0.581199354001606143928050809e+21,
1274 -0.6672106568924916298020941484e+20,
1275 0.2316433580634002297931815435e+19,
1276 -0.3588817569910106050743641413e+17,
1277 0.2908795263834775409737601689e+15,
1278 -0.1322983480332126453125473247e+13,
1279 0.3413234182301700539091292655e+10,
1280 -0.4695753530642995859767162166e+7,
1281 0.270112271089232341485679099e+4
1285 0.11623987080032122878585294e+22,
1286 0.1185770712190320999837113348e+20,
1287 0.6092061398917521746105196863e+17,
1288 0.2081661221307607351240184229e+15,
1289 0.5243710262167649715406728642e+12,
1290 0.1013863514358673989967045588e+10,
1291 0.1501793594998585505921097578e+7,
1292 0.1606931573481487801970916749e+4,
1298 for (i=7; i >= 0; i--)
1307 static MagickRealType P1(MagickRealType x)
1319 0.352246649133679798341724373e+5,
1320 0.62758845247161281269005675e+5,
1321 0.313539631109159574238669888e+5,
1322 0.49854832060594338434500455e+4,
1323 0.2111529182853962382105718e+3,
1324 0.12571716929145341558495e+1
1328 0.352246649133679798068390431e+5,
1329 0.626943469593560511888833731e+5,
1330 0.312404063819041039923015703e+5,
1331 0.4930396490181088979386097e+4,
1332 0.2030775189134759322293574e+3,
1338 for (i=4; i >= 0; i--)
1340 p=p*(8.0/x)*(8.0/x)+Pone[i];
1341 q=q*(8.0/x)*(8.0/x)+Qone[i];
1347 static MagickRealType Q1(MagickRealType x)
1359 0.3511751914303552822533318e+3,
1360 0.7210391804904475039280863e+3,
1361 0.4259873011654442389886993e+3,
1362 0.831898957673850827325226e+2,
1363 0.45681716295512267064405e+1,
1364 0.3532840052740123642735e-1
1368 0.74917374171809127714519505e+4,
1369 0.154141773392650970499848051e+5,
1370 0.91522317015169922705904727e+4,
1371 0.18111867005523513506724158e+4,
1372 0.1038187585462133728776636e+3,
1378 for (i=4; i >= 0; i--)
1380 p=p*(8.0/x)*(8.0/x)+Pone[i];
1381 q=q*(8.0/x)*(8.0/x)+Qone[i];
1386 static MagickRealType BesselOrderOne(MagickRealType x)
1399 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1400 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1408 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1412 + D e s t r o y R e s i z e F i l t e r %
1416 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1418 % DestroyResizeFilter() destroy the resize filter.
1420 % The format of the DestroyResizeFilter method is:
1422 % ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1424 % A description of each parameter follows:
1426 % o resize_filter: the resize filter.
1429 MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1431 assert(resize_filter != (ResizeFilter *) NULL);
1432 assert(resize_filter->signature == MagickSignature);
1433 resize_filter->signature=(~MagickSignature);
1434 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1435 return(resize_filter);
1439 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1443 + G e t R e s i z e F i l t e r S u p p o r t %
1447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1449 % GetResizeFilterSupport() return the current support window size for this
1450 % filter. Note that this may have been enlarged by filter:blur factor.
1452 % The format of the GetResizeFilterSupport method is:
1454 % MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1456 % A description of each parameter follows:
1458 % o filter: Image filter to use.
1461 MagickExport MagickRealType GetResizeFilterSupport(
1462 const ResizeFilter *resize_filter)
1464 assert(resize_filter != (ResizeFilter *) NULL);
1465 assert(resize_filter->signature == MagickSignature);
1466 return(resize_filter->support*resize_filter->blur);
1470 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1474 + G e t R e s i z e F i l t e r W e i g h t %
1478 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1480 % GetResizeFilterWeight evaluates the specified resize filter at the point x
1481 % which usally lies between zero and the filters current 'support' and
1482 % returns the weight of the filter function at that point.
1484 % The format of the GetResizeFilterWeight method is:
1486 % MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1487 % const MagickRealType x)
1489 % A description of each parameter follows:
1491 % o filter: the filter type.
1496 MagickExport MagickRealType GetResizeFilterWeight(
1497 const ResizeFilter *resize_filter,const MagickRealType x)
1504 Windowing function - scale the weighting filter by this amount.
1506 assert(resize_filter != (ResizeFilter *) NULL);
1507 assert(resize_filter->signature == MagickSignature);
1508 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
1509 if ((resize_filter->window_support < MagickEpsilon) ||
1510 (resize_filter->window == Box))
1511 scale=1.0; /* Point or Box Filter -- avoid division by zero */
1514 scale=resize_filter->scale;
1515 scale=resize_filter->window(x_blur*scale,resize_filter);
1517 return(scale*resize_filter->filter(x_blur,resize_filter));
1521 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1525 % M a g n i f y I m a g e %
1529 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1531 % MagnifyImage() is a convenience method that scales an image proportionally
1532 % to twice its size.
1534 % The format of the MagnifyImage method is:
1536 % Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1538 % A description of each parameter follows:
1540 % o image: the image.
1542 % o exception: return any errors or warnings in this structure.
1545 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1550 assert(image != (Image *) NULL);
1551 assert(image->signature == MagickSignature);
1552 if (image->debug != MagickFalse)
1553 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1554 assert(exception != (ExceptionInfo *) NULL);
1555 assert(exception->signature == MagickSignature);
1556 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1558 return(magnify_image);
1562 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1566 % M i n i f y I m a g e %
1570 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1572 % MinifyImage() is a convenience method that scales an image proportionally
1575 % The format of the MinifyImage method is:
1577 % Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1579 % A description of each parameter follows:
1581 % o image: the image.
1583 % o exception: return any errors or warnings in this structure.
1586 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1591 assert(image != (Image *) NULL);
1592 assert(image->signature == MagickSignature);
1593 if (image->debug != MagickFalse)
1594 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1595 assert(exception != (ExceptionInfo *) NULL);
1596 assert(exception->signature == MagickSignature);
1597 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1599 return(minify_image);
1603 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1607 % R e s a m p l e I m a g e %
1611 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1613 % ResampleImage() resize image in terms of its pixel size, so that when
1614 % displayed at the given resolution it will be the same size in terms of
1615 % real world units as the original image at the original resolution.
1617 % The format of the ResampleImage method is:
1619 % Image *ResampleImage(Image *image,const double x_resolution,
1620 % const double y_resolution,const FilterTypes filter,const double blur,
1621 % ExceptionInfo *exception)
1623 % A description of each parameter follows:
1625 % o image: the image to be resized to fit the given resolution.
1627 % o x_resolution: the new image x resolution.
1629 % o y_resolution: the new image y resolution.
1631 % o filter: Image filter to use.
1633 % o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1636 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1637 const double y_resolution,const FilterTypes filter,const double blur,
1638 ExceptionInfo *exception)
1640 #define ResampleImageTag "Resample/Image"
1650 Initialize sampled image attributes.
1652 assert(image != (const Image *) NULL);
1653 assert(image->signature == MagickSignature);
1654 if (image->debug != MagickFalse)
1655 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1656 assert(exception != (ExceptionInfo *) NULL);
1657 assert(exception->signature == MagickSignature);
1658 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1659 72.0 : image->x_resolution)+0.5);
1660 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1661 72.0 : image->y_resolution)+0.5);
1662 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1663 if (resample_image != (Image *) NULL)
1665 resample_image->x_resolution=x_resolution;
1666 resample_image->y_resolution=y_resolution;
1668 return(resample_image);
1670 #if defined(MAGICKCORE_LQR_DELEGATE)
1673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1677 % L i q u i d R e s c a l e I m a g e %
1681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1683 % LiquidRescaleImage() rescales image with seam carving.
1685 % The format of the LiquidRescaleImage method is:
1687 % Image *LiquidRescaleImage(const Image *image,
1688 % const size_t columns,const size_t rows,
1689 % const double delta_x,const double rigidity,ExceptionInfo *exception)
1691 % A description of each parameter follows:
1693 % o image: the image.
1695 % o columns: the number of columns in the rescaled image.
1697 % o rows: the number of rows in the rescaled image.
1699 % o delta_x: maximum seam transversal step (0 means straight seams).
1701 % o rigidity: introduce a bias for non-straight seams (typically 0).
1703 % o exception: return any errors or warnings in this structure.
1706 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1707 const size_t rows,const double delta_x,const double rigidity,
1708 ExceptionInfo *exception)
1710 #define LiquidRescaleImageTag "Rescale/Image"
1744 Liquid rescale image.
1746 assert(image != (const Image *) NULL);
1747 assert(image->signature == MagickSignature);
1748 if (image->debug != MagickFalse)
1749 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1750 assert(exception != (ExceptionInfo *) NULL);
1751 assert(exception->signature == MagickSignature);
1752 if ((columns == 0) || (rows == 0))
1753 return((Image *) NULL);
1754 if ((columns == image->columns) && (rows == image->rows))
1755 return(CloneImage(image,0,0,MagickTrue,exception));
1756 if ((columns <= 2) || (rows <= 2))
1757 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
1758 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1768 Honor liquid resize size limitations.
1770 for (width=image->columns; columns >= (2*width-1); width*=2);
1771 for (height=image->rows; rows >= (2*height-1); height*=2);
1772 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1774 if (resize_image == (Image *) NULL)
1775 return((Image *) NULL);
1776 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1777 rigidity,exception);
1778 resize_image=DestroyImage(resize_image);
1779 return(rescale_image);
1782 if (image->matte == MagickFalse)
1784 if (image->colorspace == CMYKColorspace)
1787 if (image->matte == MagickFalse)
1790 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1791 strlen(map)*sizeof(*pixels));
1792 if (pixels == (unsigned char *) NULL)
1793 return((Image *) NULL);
1794 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1796 if (status == MagickFalse)
1798 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1799 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1801 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1802 if (carver == (LqrCarver *) NULL)
1804 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1805 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1807 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1808 lqr_status=lqr_carver_resize(carver,columns,rows);
1809 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1810 lqr_carver_get_height(carver),MagickTrue,exception);
1811 if (rescale_image == (Image *) NULL)
1813 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1814 return((Image *) NULL);
1816 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1818 InheritException(exception,&rescale_image->exception);
1819 rescale_image=DestroyImage(rescale_image);
1820 return((Image *) NULL);
1822 GetMagickPixelPacket(rescale_image,&pixel);
1823 (void) lqr_carver_scan_reset(carver);
1824 rescale_view=AcquireCacheView(rescale_image);
1825 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1827 register IndexPacket
1828 *restrict rescale_indexes;
1830 register PixelPacket
1833 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
1834 if (q == (PixelPacket *) NULL)
1836 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
1837 pixel.red=QuantumRange*(packet[0]/255.0);
1838 pixel.green=QuantumRange*(packet[1]/255.0);
1839 pixel.blue=QuantumRange*(packet[2]/255.0);
1840 if (image->colorspace != CMYKColorspace)
1842 if (image->matte == MagickFalse)
1843 pixel.opacity=QuantumRange*(packet[3]/255.0);
1847 pixel.index=QuantumRange*(packet[3]/255.0);
1848 if (image->matte == MagickFalse)
1849 pixel.opacity=QuantumRange*(packet[4]/255.0);
1851 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
1852 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
1855 rescale_view=DestroyCacheView(rescale_view);
1857 Relinquish resources.
1859 lqr_carver_destroy(carver);
1860 return(rescale_image);
1863 MagickExport Image *LiquidRescaleImage(const Image *image,
1864 const size_t magick_unused(columns),const size_t magick_unused(rows),
1865 const double magick_unused(delta_x),const double magick_unused(rigidity),
1866 ExceptionInfo *exception)
1868 assert(image != (const Image *) NULL);
1869 assert(image->signature == MagickSignature);
1870 if (image->debug != MagickFalse)
1871 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1872 assert(exception != (ExceptionInfo *) NULL);
1873 assert(exception->signature == MagickSignature);
1874 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1875 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1876 return((Image *) NULL);
1881 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1885 % R e s i z e I m a g e %
1889 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1891 % ResizeImage() scales an image to the desired dimensions, using the given
1892 % filter (see AcquireFilterInfo()).
1894 % If an undefined filter is given the filter defaults to Mitchell for a
1895 % colormapped image, a image with a matte channel, or if the image is
1896 % enlarged. Otherwise the filter defaults to a Lanczos.
1898 % ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1900 % The format of the ResizeImage method is:
1902 % Image *ResizeImage(Image *image,const size_t columns,
1903 % const size_t rows,const FilterTypes filter,const double blur,
1904 % ExceptionInfo *exception)
1906 % A description of each parameter follows:
1908 % o image: the image.
1910 % o columns: the number of columns in the scaled image.
1912 % o rows: the number of rows in the scaled image.
1914 % o filter: Image filter to use.
1916 % o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1919 % o exception: return any errors or warnings in this structure.
1923 typedef struct _ContributionInfo
1932 static ContributionInfo **DestroyContributionThreadSet(
1933 ContributionInfo **contribution)
1938 assert(contribution != (ContributionInfo **) NULL);
1939 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
1940 if (contribution[i] != (ContributionInfo *) NULL)
1941 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1943 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
1944 return(contribution);
1947 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1958 number_threads=GetOpenMPMaximumThreads();
1959 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
1960 sizeof(*contribution));
1961 if (contribution == (ContributionInfo **) NULL)
1962 return((ContributionInfo **) NULL);
1963 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
1964 for (i=0; i < (ssize_t) number_threads; i++)
1966 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1967 sizeof(**contribution));
1968 if (contribution[i] == (ContributionInfo *) NULL)
1969 return(DestroyContributionThreadSet(contribution));
1971 return(contribution);
1974 static inline double MagickMax(const double x,const double y)
1981 static inline double MagickMin(const double x,const double y)
1988 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
1989 const Image *image,Image *resize_image,const MagickRealType x_factor,
1990 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
1992 #define ResizeImageTag "Resize/Image"
2002 **restrict contributions;
2018 Apply filter to resize horizontally from image to resize image.
2020 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
2021 support=scale*GetResizeFilterSupport(resize_filter);
2022 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2023 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2025 InheritException(exception,&resize_image->exception);
2026 return(MagickFalse);
2031 Support too small even for nearest neighbour: Reduce to point
2034 support=(MagickRealType) 0.5;
2037 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2038 if (contributions == (ContributionInfo **) NULL)
2040 (void) ThrowMagickException(exception,GetMagickModule(),
2041 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2042 return(MagickFalse);
2046 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2047 image_view=AcquireCacheView(image);
2048 resize_view=AcquireCacheView(resize_image);
2049 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2050 #pragma omp parallel for shared(status)
2052 for (x=0; x < (ssize_t) resize_image->columns; x++)
2058 register const IndexPacket
2061 register const PixelPacket
2064 register ContributionInfo
2065 *restrict contribution;
2067 register IndexPacket
2068 *restrict resize_indexes;
2070 register PixelPacket
2081 if (status == MagickFalse)
2083 center=(MagickRealType) (x+0.5)/x_factor;
2084 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2085 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
2087 contribution=contributions[GetOpenMPThreadId()];
2088 for (n=0; n < (stop-start); n++)
2090 contribution[n].pixel=start+n;
2091 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2092 ((MagickRealType) (start+n)-center+0.5));
2093 density+=contribution[n].weight;
2095 if ((density != 0.0) && (density != 1.0))
2103 density=1.0/density;
2104 for (i=0; i < n; i++)
2105 contribution[i].weight*=density;
2107 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2108 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
2109 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2111 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2116 indexes=GetCacheViewVirtualIndexQueue(image_view);
2117 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2118 for (y=0; y < (ssize_t) resize_image->rows; y++)
2133 if (image->matte == MagickFalse)
2135 for (i=0; i < n; i++)
2137 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2138 (contribution[i].pixel-contribution[0].pixel);
2139 alpha=contribution[i].weight;
2140 pixel.red+=alpha*(p+j)->red;
2141 pixel.green+=alpha*(p+j)->green;
2142 pixel.blue+=alpha*(p+j)->blue;
2143 pixel.opacity+=alpha*(p+j)->opacity;
2145 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2146 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2147 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2148 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2149 if ((image->colorspace == CMYKColorspace) &&
2150 (resize_image->colorspace == CMYKColorspace))
2152 for (i=0; i < n; i++)
2154 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2155 (contribution[i].pixel-contribution[0].pixel);
2156 alpha=contribution[i].weight;
2157 pixel.index+=alpha*indexes[j];
2159 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
2168 for (i=0; i < n; i++)
2170 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2171 (contribution[i].pixel-contribution[0].pixel);
2172 alpha=contribution[i].weight*QuantumScale*
2173 GetAlphaPixelComponent(p+j);
2174 pixel.red+=alpha*(p+j)->red;
2175 pixel.green+=alpha*(p+j)->green;
2176 pixel.blue+=alpha*(p+j)->blue;
2177 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2180 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2181 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2182 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2183 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2184 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2185 if ((image->colorspace == CMYKColorspace) &&
2186 (resize_image->colorspace == CMYKColorspace))
2188 for (i=0; i < n; i++)
2190 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2191 (contribution[i].pixel-contribution[0].pixel);
2192 alpha=contribution[i].weight*QuantumScale*
2193 GetAlphaPixelComponent(p+j);
2194 pixel.index+=alpha*indexes[j];
2196 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2197 GetIndexPixelComponent(&pixel));
2200 if ((resize_image->storage_class == PseudoClass) &&
2201 (image->storage_class == PseudoClass))
2203 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
2205 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2206 (contribution[i-start].pixel-contribution[0].pixel);
2207 resize_indexes[y]=indexes[j];
2211 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2213 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2218 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2219 #pragma omp critical (MagickCore_HorizontalFilter)
2221 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2222 if (proceed == MagickFalse)
2226 resize_view=DestroyCacheView(resize_view);
2227 image_view=DestroyCacheView(image_view);
2228 contributions=DestroyContributionThreadSet(contributions);
2232 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2233 const Image *image,Image *resize_image,const MagickRealType y_factor,
2234 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2244 **restrict contributions;
2260 Apply filter to resize vertically from image to resize image.
2262 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
2263 support=scale*GetResizeFilterSupport(resize_filter);
2264 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2265 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2267 InheritException(exception,&resize_image->exception);
2268 return(MagickFalse);
2273 Support too small even for nearest neighbour: Reduce to point
2276 support=(MagickRealType) 0.5;
2279 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2280 if (contributions == (ContributionInfo **) NULL)
2282 (void) ThrowMagickException(exception,GetMagickModule(),
2283 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2284 return(MagickFalse);
2288 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2289 image_view=AcquireCacheView(image);
2290 resize_view=AcquireCacheView(resize_image);
2291 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2292 #pragma omp parallel for shared(status)
2294 for (y=0; y < (ssize_t) resize_image->rows; y++)
2300 register const IndexPacket
2303 register const PixelPacket
2306 register ContributionInfo
2307 *restrict contribution;
2309 register IndexPacket
2310 *restrict resize_indexes;
2312 register PixelPacket
2323 if (status == MagickFalse)
2325 center=(MagickRealType) (y+0.5)/y_factor;
2326 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2327 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
2329 contribution=contributions[GetOpenMPThreadId()];
2330 for (n=0; n < (stop-start); n++)
2332 contribution[n].pixel=start+n;
2333 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2334 ((MagickRealType) (start+n)-center+0.5));
2335 density+=contribution[n].weight;
2337 if ((density != 0.0) && (density != 1.0))
2345 density=1.0/density;
2346 for (i=0; i < n; i++)
2347 contribution[i].weight*=density;
2349 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
2350 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2352 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2354 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2359 indexes=GetCacheViewVirtualIndexQueue(image_view);
2360 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2361 for (x=0; x < (ssize_t) resize_image->columns; x++)
2376 if (image->matte == MagickFalse)
2378 for (i=0; i < n; i++)
2380 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2382 alpha=contribution[i].weight;
2383 pixel.red+=alpha*(p+j)->red;
2384 pixel.green+=alpha*(p+j)->green;
2385 pixel.blue+=alpha*(p+j)->blue;
2386 pixel.opacity+=alpha*(p+j)->opacity;
2388 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2389 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2390 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2391 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2392 if ((image->colorspace == CMYKColorspace) &&
2393 (resize_image->colorspace == CMYKColorspace))
2395 for (i=0; i < n; i++)
2397 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2399 alpha=contribution[i].weight;
2400 pixel.index+=alpha*indexes[j];
2402 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
2411 for (i=0; i < n; i++)
2413 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2415 alpha=contribution[i].weight*QuantumScale*
2416 GetAlphaPixelComponent(p+j);
2417 pixel.red+=alpha*(p+j)->red;
2418 pixel.green+=alpha*(p+j)->green;
2419 pixel.blue+=alpha*(p+j)->blue;
2420 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2423 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2424 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2425 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2426 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2427 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2428 if ((image->colorspace == CMYKColorspace) &&
2429 (resize_image->colorspace == CMYKColorspace))
2431 for (i=0; i < n; i++)
2433 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2435 alpha=contribution[i].weight*QuantumScale*
2436 GetAlphaPixelComponent(p+j);
2437 pixel.index+=alpha*indexes[j];
2439 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2440 GetIndexPixelComponent(&pixel));
2443 if ((resize_image->storage_class == PseudoClass) &&
2444 (image->storage_class == PseudoClass))
2446 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
2448 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
2450 resize_indexes[x]=indexes[j];
2454 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2456 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2461 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2462 #pragma omp critical (MagickCore_VerticalFilter)
2464 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2465 if (proceed == MagickFalse)
2469 resize_view=DestroyCacheView(resize_view);
2470 image_view=DestroyCacheView(image_view);
2471 contributions=DestroyContributionThreadSet(contributions);
2475 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2476 const size_t rows,const FilterTypes filter,const double blur,
2477 ExceptionInfo *exception)
2479 #define WorkLoadFactor 0.265
2505 Acquire resize image.
2507 assert(image != (Image *) NULL);
2508 assert(image->signature == MagickSignature);
2509 if (image->debug != MagickFalse)
2510 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2511 assert(exception != (ExceptionInfo *) NULL);
2512 assert(exception->signature == MagickSignature);
2513 if ((columns == 0) || (rows == 0))
2514 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2515 if ((columns == image->columns) && (rows == image->rows) &&
2516 (filter == UndefinedFilter) && (blur == 1.0))
2517 return(CloneImage(image,0,0,MagickTrue,exception));
2518 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2519 if (resize_image == (Image *) NULL)
2520 return(resize_image);
2522 Acquire resize filter.
2524 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2525 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2526 if ((x_factor*y_factor) > WorkLoadFactor)
2527 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2529 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2530 if (filter_image == (Image *) NULL)
2531 return(DestroyImage(resize_image));
2532 filter_type=LanczosFilter;
2533 if (filter != UndefinedFilter)
2536 if ((x_factor == 1.0) && (y_factor == 1.0))
2537 filter_type=PointFilter;
2539 if ((image->storage_class == PseudoClass) ||
2540 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2541 filter_type=MitchellFilter;
2542 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2548 if ((x_factor*y_factor) > WorkLoadFactor)
2550 span=(MagickSizeType) (filter_image->columns+rows);
2551 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
2553 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
2554 span,&offset,exception);
2558 span=(MagickSizeType) (filter_image->rows+columns);
2559 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
2561 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
2562 span,&offset,exception);
2567 filter_image=DestroyImage(filter_image);
2568 resize_filter=DestroyResizeFilter(resize_filter);
2569 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2570 return((Image *) NULL);
2571 resize_image->type=image->type;
2572 return(resize_image);
2576 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2580 % S a m p l e I m a g e %
2584 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2586 % SampleImage() scales an image to the desired dimensions with pixel
2587 % sampling. Unlike other scaling methods, this method does not introduce
2588 % any additional color into the scaled image.
2590 % The format of the SampleImage method is:
2592 % Image *SampleImage(const Image *image,const size_t columns,
2593 % const size_t rows,ExceptionInfo *exception)
2595 % A description of each parameter follows:
2597 % o image: the image.
2599 % o columns: the number of columns in the sampled image.
2601 % o rows: the number of rows in the sampled image.
2603 % o exception: return any errors or warnings in this structure.
2606 MagickExport Image *SampleImage(const Image *image,const size_t columns,
2607 const size_t rows,ExceptionInfo *exception)
2609 #define SampleImageTag "Sample/Image"
2632 Initialize sampled image attributes.
2634 assert(image != (const Image *) NULL);
2635 assert(image->signature == MagickSignature);
2636 if (image->debug != MagickFalse)
2637 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2638 assert(exception != (ExceptionInfo *) NULL);
2639 assert(exception->signature == MagickSignature);
2640 if ((columns == 0) || (rows == 0))
2641 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2642 if ((columns == image->columns) && (rows == image->rows))
2643 return(CloneImage(image,0,0,MagickTrue,exception));
2644 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2645 if (sample_image == (Image *) NULL)
2646 return((Image *) NULL);
2648 Allocate scan line buffer and column offset buffers.
2650 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
2652 if (x_offset == (ssize_t *) NULL)
2654 sample_image=DestroyImage(sample_image);
2655 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2657 for (x=0; x < (ssize_t) sample_image->columns; x++)
2658 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
2659 sample_image->columns);
2665 image_view=AcquireCacheView(image);
2666 sample_view=AcquireCacheView(sample_image);
2667 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2668 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2670 for (y=0; y < (ssize_t) sample_image->rows; y++)
2672 register const IndexPacket
2675 register const PixelPacket
2678 register IndexPacket
2679 *restrict sample_indexes;
2681 register PixelPacket
2690 if (status == MagickFalse)
2692 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2693 sample_image->rows);
2694 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2696 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2698 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2703 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2704 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2708 for (x=0; x < (ssize_t) sample_image->columns; x++)
2709 *q++=p[x_offset[x]];
2710 if ((image->storage_class == PseudoClass) ||
2711 (image->colorspace == CMYKColorspace))
2712 for (x=0; x < (ssize_t) sample_image->columns; x++)
2713 sample_indexes[x]=indexes[x_offset[x]];
2714 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2716 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2721 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2722 #pragma omp critical (MagickCore_SampleImage)
2724 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2725 if (proceed == MagickFalse)
2729 image_view=DestroyCacheView(image_view);
2730 sample_view=DestroyCacheView(sample_view);
2731 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
2732 sample_image->type=image->type;
2733 return(sample_image);
2737 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2741 % S c a l e I m a g e %
2745 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2747 % ScaleImage() changes the size of an image to the given dimensions.
2749 % The format of the ScaleImage method is:
2751 % Image *ScaleImage(const Image *image,const size_t columns,
2752 % const size_t rows,ExceptionInfo *exception)
2754 % A description of each parameter follows:
2756 % o image: the image.
2758 % o columns: the number of columns in the scaled image.
2760 % o rows: the number of rows in the scaled image.
2762 % o exception: return any errors or warnings in this structure.
2765 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2766 const size_t rows,ExceptionInfo *exception)
2768 #define ScaleImageTag "Scale/Image"
2802 Initialize scaled image attributes.
2804 assert(image != (const Image *) NULL);
2805 assert(image->signature == MagickSignature);
2806 if (image->debug != MagickFalse)
2807 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2808 assert(exception != (ExceptionInfo *) NULL);
2809 assert(exception->signature == MagickSignature);
2810 if ((columns == 0) || (rows == 0))
2811 return((Image *) NULL);
2812 if ((columns == image->columns) && (rows == image->rows))
2813 return(CloneImage(image,0,0,MagickTrue,exception));
2814 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2815 if (scale_image == (Image *) NULL)
2816 return((Image *) NULL);
2817 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2819 InheritException(exception,&scale_image->exception);
2820 scale_image=DestroyImage(scale_image);
2821 return((Image *) NULL);
2826 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2829 if (image->rows != scale_image->rows)
2830 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2832 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2833 scale_image->columns,sizeof(*scale_scanline));
2834 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2836 if ((scanline == (MagickPixelPacket *) NULL) ||
2837 (scale_scanline == (MagickPixelPacket *) NULL) ||
2838 (x_vector == (MagickPixelPacket *) NULL) ||
2839 (y_vector == (MagickPixelPacket *) NULL))
2841 scale_image=DestroyImage(scale_image);
2842 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2848 next_row=MagickTrue;
2850 scale.y=(double) scale_image->rows/(double) image->rows;
2851 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2853 GetMagickPixelPacket(image,&pixel);
2854 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2856 image_view=AcquireCacheView(image);
2857 scale_view=AcquireCacheView(scale_image);
2858 for (y=0; y < (ssize_t) scale_image->rows; y++)
2860 register const IndexPacket
2863 register const PixelPacket
2866 register IndexPacket
2867 *restrict scale_indexes;
2869 register MagickPixelPacket
2873 register PixelPacket
2879 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2881 if (q == (PixelPacket *) NULL)
2883 scale_indexes=GetAuthenticIndexQueue(scale_image);
2884 if (scale_image->rows == image->rows)
2887 Read a new scanline.
2889 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2891 if (p == (const PixelPacket *) NULL)
2893 indexes=GetCacheViewVirtualIndexQueue(image_view);
2894 for (x=0; x < (ssize_t) image->columns; x++)
2896 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2897 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2898 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
2899 if (image->matte != MagickFalse)
2900 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
2901 if (indexes != (IndexPacket *) NULL)
2902 x_vector[x].index=(MagickRealType) indexes[x];
2911 while (scale.y < span.y)
2913 if ((next_row != MagickFalse) &&
2914 (number_rows < (ssize_t) image->rows))
2917 Read a new scanline.
2919 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2921 if (p == (const PixelPacket *) NULL)
2923 indexes=GetCacheViewVirtualIndexQueue(image_view);
2924 for (x=0; x < (ssize_t) image->columns; x++)
2926 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2927 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2928 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
2929 if (image->matte != MagickFalse)
2930 x_vector[x].opacity=(MagickRealType)
2931 GetOpacityPixelComponent(p);
2932 if (indexes != (IndexPacket *) NULL)
2933 x_vector[x].index=(MagickRealType) indexes[x];
2938 for (x=0; x < (ssize_t) image->columns; x++)
2940 y_vector[x].red+=scale.y*x_vector[x].red;
2941 y_vector[x].green+=scale.y*x_vector[x].green;
2942 y_vector[x].blue+=scale.y*x_vector[x].blue;
2943 if (scale_image->matte != MagickFalse)
2944 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2945 if (scale_indexes != (IndexPacket *) NULL)
2946 y_vector[x].index+=scale.y*x_vector[x].index;
2949 scale.y=(double) scale_image->rows/(double) image->rows;
2950 next_row=MagickTrue;
2952 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
2955 Read a new scanline.
2957 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2959 if (p == (const PixelPacket *) NULL)
2961 indexes=GetCacheViewVirtualIndexQueue(image_view);
2962 for (x=0; x < (ssize_t) image->columns; x++)
2964 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2965 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2966 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
2967 if (image->matte != MagickFalse)
2968 x_vector[x].opacity=(MagickRealType)
2969 GetOpacityPixelComponent(p);
2970 if (indexes != (IndexPacket *) NULL)
2971 x_vector[x].index=(MagickRealType) indexes[x];
2975 next_row=MagickFalse;
2978 for (x=0; x < (ssize_t) image->columns; x++)
2980 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
2981 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
2982 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
2983 if (image->matte != MagickFalse)
2984 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
2985 if (scale_indexes != (IndexPacket *) NULL)
2986 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
2988 s->green=pixel.green;
2990 if (scale_image->matte != MagickFalse)
2991 s->opacity=pixel.opacity;
2992 if (scale_indexes != (IndexPacket *) NULL)
2993 s->index=pixel.index;
3000 scale.y=(double) scale_image->rows/(double) image->rows;
3001 next_row=MagickTrue;
3005 if (scale_image->columns == image->columns)
3008 Transfer scanline to scaled image.
3011 for (x=0; x < (ssize_t) scale_image->columns; x++)
3013 q->red=ClampToQuantum(s->red);
3014 q->green=ClampToQuantum(s->green);
3015 q->blue=ClampToQuantum(s->blue);
3016 if (scale_image->matte != MagickFalse)
3017 q->opacity=ClampToQuantum(s->opacity);
3018 if (scale_indexes != (IndexPacket *) NULL)
3019 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
3030 next_column=MagickFalse;
3034 for (x=0; x < (ssize_t) image->columns; x++)
3036 scale.x=(double) scale_image->columns/(double) image->columns;
3037 while (scale.x >= span.x)
3039 if (next_column != MagickFalse)
3044 pixel.red+=span.x*s->red;
3045 pixel.green+=span.x*s->green;
3046 pixel.blue+=span.x*s->blue;
3047 if (image->matte != MagickFalse)
3048 pixel.opacity+=span.x*s->opacity;
3049 if (scale_indexes != (IndexPacket *) NULL)
3050 pixel.index+=span.x*s->index;
3052 t->green=pixel.green;
3054 if (scale_image->matte != MagickFalse)
3055 t->opacity=pixel.opacity;
3056 if (scale_indexes != (IndexPacket *) NULL)
3057 t->index=pixel.index;
3060 next_column=MagickTrue;
3064 if (next_column != MagickFalse)
3067 next_column=MagickFalse;
3070 pixel.red+=scale.x*s->red;
3071 pixel.green+=scale.x*s->green;
3072 pixel.blue+=scale.x*s->blue;
3073 if (scale_image->matte != MagickFalse)
3074 pixel.opacity+=scale.x*s->opacity;
3075 if (scale_indexes != (IndexPacket *) NULL)
3076 pixel.index+=scale.x*s->index;
3084 pixel.red+=span.x*s->red;
3085 pixel.green+=span.x*s->green;
3086 pixel.blue+=span.x*s->blue;
3087 if (scale_image->matte != MagickFalse)
3088 pixel.opacity+=span.x*s->opacity;
3089 if (scale_indexes != (IndexPacket *) NULL)
3090 pixel.index+=span.x*s->index;
3092 if ((next_column == MagickFalse) &&
3093 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
3096 t->green=pixel.green;
3098 if (scale_image->matte != MagickFalse)
3099 t->opacity=pixel.opacity;
3100 if (scale_indexes != (IndexPacket *) NULL)
3101 t->index=pixel.index;
3104 Transfer scanline to scaled image.
3107 for (x=0; x < (ssize_t) scale_image->columns; x++)
3109 q->red=ClampToQuantum(t->red);
3110 q->green=ClampToQuantum(t->green);
3111 q->blue=ClampToQuantum(t->blue);
3112 if (scale_image->matte != MagickFalse)
3113 q->opacity=ClampToQuantum(t->opacity);
3114 if (scale_indexes != (IndexPacket *) NULL)
3115 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
3120 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
3122 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3124 if (proceed == MagickFalse)
3127 scale_view=DestroyCacheView(scale_view);
3128 image_view=DestroyCacheView(image_view);
3130 Free allocated memory.
3132 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3133 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3134 if (scale_image->rows != image->rows)
3135 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3136 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3137 scale_image->type=image->type;
3138 return(scale_image);
3142 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3146 + S e t R e s i z e F i l t e r S u p p o r t %
3150 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3152 % SetResizeFilterSupport() specifies which IR filter to use to window
3154 % The format of the SetResizeFilterSupport method is:
3156 % void SetResizeFilterSupport(ResizeFilter *resize_filter,
3157 % const MagickRealType support)
3159 % A description of each parameter follows:
3161 % o resize_filter: the resize filter.
3163 % o support: the filter spport radius.
3166 MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3167 const MagickRealType support)
3169 assert(resize_filter != (ResizeFilter *) NULL);
3170 assert(resize_filter->signature == MagickSignature);
3171 resize_filter->support=support;
3175 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3179 % T h u m b n a i l I m a g e %
3183 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3185 % ThumbnailImage() changes the size of an image to the given dimensions and
3186 % removes any associated profiles. The goal is to produce small low cost
3187 % thumbnail images suited for display on the Web.
3189 % The format of the ThumbnailImage method is:
3191 % Image *ThumbnailImage(const Image *image,const size_t columns,
3192 % const size_t rows,ExceptionInfo *exception)
3194 % A description of each parameter follows:
3196 % o image: the image.
3198 % o columns: the number of columns in the scaled image.
3200 % o rows: the number of rows in the scaled image.
3202 % o exception: return any errors or warnings in this structure.
3205 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3206 const size_t rows,ExceptionInfo *exception)
3208 #define SampleFactor 5
3211 value[MaxTextExtent];
3229 assert(image != (Image *) NULL);
3230 assert(image->signature == MagickSignature);
3231 if (image->debug != MagickFalse)
3232 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3233 assert(exception != (ExceptionInfo *) NULL);
3234 assert(exception->signature == MagickSignature);
3235 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3236 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3237 if ((x_factor*y_factor) > 0.1)
3238 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3241 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
3242 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3243 image->blur,exception);
3249 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3251 if (sample_image == (Image *) NULL)
3252 return((Image *) NULL);
3253 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3254 image->blur,exception);
3255 sample_image=DestroyImage(sample_image);
3257 if (thumbnail_image == (Image *) NULL)
3258 return(thumbnail_image);
3259 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3260 if (thumbnail_image->matte == MagickFalse)
3261 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3262 thumbnail_image->depth=8;
3263 thumbnail_image->interlace=NoInterlace;
3265 Strip all profiles except color profiles.
3267 ResetImageProfileIterator(thumbnail_image);
3268 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3270 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3272 (void) DeleteImageProfile(thumbnail_image,name);
3273 ResetImageProfileIterator(thumbnail_image);
3275 name=GetNextImageProfile(thumbnail_image);
3277 (void) DeleteImageProperty(thumbnail_image,"comment");
3278 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3279 if (strstr(image->magick_filename,"//") == (char *) NULL)
3280 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
3281 image->magick_filename);
3282 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3283 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3284 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3286 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3287 attributes.st_mtime);
3288 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3290 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3291 attributes.st_mtime);
3292 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
3293 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
3294 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3295 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3297 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3298 (void) SetImageProperty(thumbnail_image,"software",
3299 GetMagickVersion(&version));
3300 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3301 image->magick_columns);
3302 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
3303 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3304 image->magick_rows);
3305 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
3306 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3307 GetImageListLength(image));
3308 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3309 return(thumbnail_image);