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 Bessel(const MagickRealType x,
139 const ResizeFilter *magick_unused(resize_filter))
142 See Pratt "Digital Image Processing" p.97 for Bessel functions.
143 This function is actually a X-scaled Jinc(x) function. See
144 http://mathworld.wolfram.com/JincFunction.html and page 11 of
145 http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf.
148 return(0.25*MagickPIL);
149 return(BesselOrderOne(MagickPIL*x)/(x+x));
152 static MagickRealType Blackman(const MagickRealType x,
153 const ResizeFilter *magick_unused(resize_filter))
156 Blackman: 2nd order cosine windowing function:
157 0.42 + 0.5 cos(pi x) + 0.08 cos(2pi x)
158 Refactored by Chantal Racette and Nicolas Robidoux to one trig
161 const MagickRealType cospix = cos((double) (MagickPIL*x));
162 return(0.34+cospix*(0.5+cospix*0.16));
165 static MagickRealType Bohman(const MagickRealType x,
166 const ResizeFilter *magick_unused(resize_filter))
169 Bohman: 2rd Order cosine windowing function:
170 (1-x) cos(pi x) + sin(pi x) / pi.
171 Refactored by Nicolas Robidoux to one trig call, one sqrt call,
172 and 7 flops, taking advantage of the fact that the support of
173 Bohman is 1 (so that we know that sin(pi x) >= 0).
175 const double cospix = cos((double) (MagickPIL*x));
176 const double sinpix = sqrt(1.0-cospix*cospix);
177 return((1.0-x)*cospix+(1.0/MagickPIL)*sinpix);
180 static MagickRealType Box(const MagickRealType magick_unused(x),
181 const ResizeFilter *magick_unused(resize_filter))
184 Return a Box filter up to its support size.
185 DO NOT LIMIT results by support or resize point sampling will fil
190 static MagickRealType CubicBC(const MagickRealType x,
191 const ResizeFilter *resize_filter)
194 Cubic Filters using B,C determined values:
195 Mitchell-Netravali B=1/3 C=1/3 Qualitively ideal Cubic Filter
196 Catmull-Rom B= 0 C=1/2 Cublic Interpolation Function
197 Cubic B-Spline B= 1 C= 0 Spline Approximation of Gaussian
198 Hermite B= 0 C= 0 Quadratic Spline (support = 1)
200 See paper by Mitchell and Netravali, Reconstruction Filters in Computer
201 Graphics Computer Graphics, Volume 22, Number 4, August 1988
202 http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/
205 Coefficents are determined from B,C values:
208 P2 = (-18 +12*B + 6*C )/6
209 P3 = ( 12 - 9*B - 6*C )/6
211 Q1 = ( -12*B -48*C )/6
213 Q3 = ( - 1*B - 6*C )/6
215 which are used to define the filter:
217 P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
218 Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x <= 2
220 which ensures function is continuous in value and derivative (slope).
223 return(resize_filter->cubic[0]+x*(resize_filter->cubic[1]+x*
224 (resize_filter->cubic[2]+x*resize_filter->cubic[3])));
226 return(resize_filter->cubic[4]+x*(resize_filter->cubic[5]+x*
227 (resize_filter->cubic[6]+x*resize_filter->cubic[7])));
231 static MagickRealType Gaussian(const MagickRealType x,
232 const ResizeFilter *magick_unused(resize_filter))
235 1D Gaussian with sigma=1/2
236 exp(-2 x^2)/sqrt(pi/2))
238 /*const MagickRealType alpha = (MagickRealType) (2.0/MagickSQ2PI);*/
239 return(exp((double) (-2.0*x*x)));
242 static MagickRealType Hanning(const MagickRealType x,
243 const ResizeFilter *magick_unused(resize_filter))
246 Cosine window function: .5 + .5 cos(pi x).
248 const MagickRealType cospix = cos((double) (MagickPIL*x));
249 return(0.5+0.5*cospix);
252 static MagickRealType Hamming(const MagickRealType x,
253 const ResizeFilter *magick_unused(resize_filter))
256 Offset cosine window function: .54 + .46 cos(pi x).
258 const MagickRealType cospix = cos((double) (MagickPIL*x));
259 return(0.54+0.46*cospix);
262 static MagickRealType Kaiser(const MagickRealType x,
263 const ResizeFilter *magick_unused(resize_filter))
266 #define I0A (1.0/I0(Alpha))
269 Kaiser Windowing Function (bessel windowing): Alpha is a free
270 value from 5 to 8 (currently hardcoded to 6.5).
271 Future: make alpha the IOA pre-calculation, an 'expert' setting.
273 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
276 static MagickRealType Lagrange(const MagickRealType x,
277 const ResizeFilter *resize_filter)
290 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the
291 lagrange function and depends on the overall support window size
292 of the filter. That is: for a support of 2, it gives a lagrange-4
293 (piecewise cubic function).
295 "n" identifies the piece of the piecewise polynomial.
297 See Survey: Interpolation Methods, IEEE Transactions on Medical
298 Imaging, Vol 18, No 11, November 1999, p1049-1075, -- Equation 27
301 if (x > resize_filter->support)
303 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
304 /*n=(ssize_t)((1.0*order)/2.0+x); -- which piece does x belong to */
305 n = (ssize_t)(resize_filter->window_support + x);
307 for (i=0; i < order; i++)
309 value*=(n-i-x)/(n-i);
313 static MagickRealType Quadratic(const MagickRealType x,
314 const ResizeFilter *magick_unused(resize_filter))
317 2rd order (quadratic) B-Spline approximation of Gaussian.
322 return(0.5*(x-1.5)*(x-1.5));
326 static MagickRealType Sinc(const MagickRealType x,
327 const ResizeFilter *magick_unused(resize_filter))
330 Scaled sinc(x) function using a trig call
331 sinc(x) == sin(pi x)/(pi x).
335 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
336 return(sin((double) pix)/pix);
338 return((MagickRealType) 1.0);
341 static MagickRealType SincFast(const MagickRealType x,
342 const ResizeFilter *magick_unused(resize_filter))
345 Approximations of the sinc function sin(pi x)/(pi x) over the
346 interval [-4,4] constructed by Nicolas Robidoux and Chantal
347 Racette with funding from the Natural Sciences and Engineering
348 Research Council of Canada.
350 Although the approximations are polynomials (for low order of
351 approximation) and quotients of polynomials (for higher order of
352 approximation) and consequently are similar in form to Taylor
353 polynomials/Pade approximants, the approximations are computed
354 with a completely different technique.
356 Summary: These approximations are "the best" in terms of bang
357 (accuracy) for the buck (flops). More specifically: Among the
358 polynomial quotients that can be computed using a fixed number of
359 flops (with a given "+ - * / budget"), the chosen polynomial
360 quotient is the one closest to the approximated function with
361 respect to maximum absolute relative error over the given
364 The Remez algorithm, as implemented in the boost library's minimax
365 package, is the key to the construction:
366 http://www.boost.org/doc/libs/1_36_0/libs/math/doc/...
367 ...sf_and_dist/html/math_toolkit/backgrounders/remez.html
370 If outside of the interval of approximation, use the standard trig
375 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
376 return(sin((double) pix)/pix);
380 The approximations only depend on x^2 (sinc is an even
383 const MagickRealType xx = x*x;
384 #if MAGICKCORE_QUANTUM_DEPTH <= 8
386 Maximum absolute relative error 6.3e-6 < 1/2^17.
388 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
389 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
390 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
391 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
392 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
393 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
394 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
395 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
396 const MagickRealType p =
397 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
398 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
399 #elif MAGICKCORE_QUANTUM_DEPTH <= 16
401 Max. abs. rel. error 2.2e-8 < 1/2^25.
403 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
404 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
405 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
406 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
407 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
408 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
409 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
410 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
411 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
412 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
413 const MagickRealType p =
414 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
415 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
416 #elif MAGICKCORE_QUANTUM_DEPTH <= 32
418 Max. abs. rel. error 1.2e-12 < 1/2^39.
420 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
421 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
422 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
423 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
424 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
425 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
426 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
427 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
428 const MagickRealType p =
429 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
430 const MagickRealType d0 = 1.0L;
431 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
432 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
433 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
434 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
435 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
436 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
439 Max. abs. rel. error 2.5e-17 < 1/2^55 if computed with "true"
440 long doubles, 2.6e-14 < 1/2^45 if long doubles are IEEE doubles.
442 const MagickRealType c0 = 0.173611111111111113332932116053816904714e-2L;
443 const MagickRealType c1 = -0.303723497515718809687399886229022703169e-3L;
444 const MagickRealType c2 = 0.233100817439489606061795544561807507471e-4L;
445 const MagickRealType c3 = -0.103906554814465396861269897523992002705e-5L;
446 const MagickRealType c4 = 0.299175768961095380104447394070231517712e-7L;
447 const MagickRealType c5 = -0.582555275175235235925786868156315453570e-9L;
448 const MagickRealType c6 = 0.774885118857072154223233850399192557934e-11L;
449 const MagickRealType c7 = -0.686148809066333092764596425714057372964e-13L;
450 const MagickRealType c8 = 0.371085247462909594457662716426170281684e-15L;
451 const MagickRealType c9 = -0.944551950759515118228796037513456335763e-18L;
452 const MagickRealType p =
453 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
454 const MagickRealType d0 = 1.0L;
455 const MagickRealType d1 = 0.463782211680615975951490586564903936283e-1L;
456 const MagickRealType d2 = 0.984899056548092584226994406603163505777e-3L;
457 const MagickRealType d3 = 0.121314604267142674069019025409802158375e-4L;
458 const MagickRealType d4 = 0.881998514405598677970025517260813044225e-7L;
459 const MagickRealType d5 = 0.310377434094436341006055666680844291856e-9L;
460 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*(d4+xx*d5))));
461 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
466 static MagickRealType Triangle(const MagickRealType x,
467 const ResizeFilter *magick_unused(resize_filter))
470 1st order (linear) B-Spline, bilinear interpolation, Tent 1D
471 filter, or a Bartlett 2D Cone filter.
478 static MagickRealType Welsh(const MagickRealType x,
479 const ResizeFilter *magick_unused(resize_filter))
482 Welsh parabolic windowing filter.
490 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
494 + A c q u i r e R e s i z e F i l t e r %
498 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
500 % AcquireResizeFilter() allocates the ResizeFilter structure. Choose
501 % from these filters:
503 % FIR (Finite impulse Response) Filters
504 % Box Triangle Quadratic
505 % Cubic Hermite Catrom
508 % IIR (Infinite impulse Response) Filters
509 % Gaussian Sinc Bessel
511 % Windowed Sinc/Bessel Method
512 % Blackman Hanning Hamming
513 % Kaiser Lanczos (Sinc)
515 % FIR filters are used as is, and are limited by that filters support
516 % window (unless over-ridden). 'Gaussian' while classed as an IIR
517 % filter, is also simply clipped by its support size (1.5).
519 % The users "-filter" selection ise used to lookup the default
520 % 'expert' settings for that filter from a internal table. However
521 % any provided 'expert' settings (see below) may override this
524 % The selection is typically either a windowed sinc, or interpolated
525 % filter, for use by functions such as ResizeImage(). However if a
526 % 'cylindrical' filter flag is requested, the default sinc weighting
527 % and windowing functions will be promoted to cylindrical Bessel
530 % Directly requesting 'Sinc' or 'Bessel' will force the use of that
531 % filter function, with a default 'Blackman' windowing method. This
532 % not however recommended as it removes the correct filter selection
533 % for different filtering image operations. Selecting a window
534 % filtering method is better.
536 % Lanczos is a special case of a sinc-windowed sinc, but defaulting
537 % to 3-lobe support, rather that the default 4 lobe support of the
538 % windowed sinc filters.
540 % Two forms of the sinc function are available: Sinc and SincFast.
541 % Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
542 % selected if the user specifically specifies the use of a Sinc
543 % filter. SincFast uses highly accurate (and fast) polynomial (low Q)
544 % and rational (high Q) approximations and is used by default in most
547 % Special 'expert' options can be used to override any and all filter
548 % settings. This is not advised unless you have expert knowledge of
549 % the use of resampling filtered techniques. Check on the results of
550 % your selections using the "filter:verbose" setting to make sure you
551 % got what you requested.
553 % "filter:filter" Select the function associated with this filter
554 % as the weighting function of the filter. This can be used to set a
555 % a windowing function as a weighting function.
557 % If a "filter:window" operation has not been provided, then a 'Box'
558 % windowing function will be set to denote that no windowing function
561 % "filter:window" Select this windowing function for the filter.
562 % While any filter could be used as a windowing function, using the
563 % 'first lobe' of that filter over the whole support window, using a
564 % non-windowing function is not advisible. If no weighting function
565 % is specifed a 'SincFast' filter will be used.
567 % "filter:lobes" Number of lobes to use for the Sinc/Bessel filter.
568 % This a simpler method of setting filter support size that will
569 % correctly handle the Sinc/Bessel switch for an operators filtering
570 % requirements. Only integers should be given.
572 % "filter:support" Set the support size for filtering to the size given
573 % This not recommended for Sinc/Bessel windowed filters (lobes should
574 % be used instead). This will override any 'filter:lobes' option.
576 % "filter:win-support" Scale windowing function to this size instead.
577 % This causes the windowing (or self-windowing Lagrange filter) to act
578 % is if the support window it much much larger than what is actually
579 % supplied to the calling operator. The filter however is still
580 % clipped to the real support size given, by the support range suppiled
581 % to the caller. If unset this will equal the normal filter support
584 % "filter:blur" Scale the filter and support window by this amount.
585 % A value >1 will generally result in a more burred image with
586 % more ringing effects, while a value <1 will sharpen the
587 % resulting image with more aliasing and Morie effects.
590 % "filter:c" Override the preset B,C values for a Cubic type of filter
591 % If only one of these are given it is assumes to be a 'Keys'
592 % type of filter such that B+2C=1, where Keys 'alpha' value = C
594 % "filter:verbose" Output the exact results of the filter selections
595 % made, as well as plotting data for graphing the resulting filter
596 % over support range (blur adjusted).
598 % Set a true un-windowed Sinc filter with 10 lobes (very slow)
599 % -set option:filter:filter Sinc
600 % -set option:filter:lobes 8
602 % For example force an 8 lobe Lanczos (Sinc or Bessel) filter...
604 % -set option:filter:lobes 8
607 % The format of the AcquireResizeFilter method is:
609 % ResizeFilter *AcquireResizeFilter(const Image *image,
610 % const FilterTypes filter_type, const MagickBooleanType radial,
611 % ExceptionInfo *exception)
613 % A description of each parameter follows:
615 % o image: the image.
617 % o filter: the filter type, defining a preset filter, window and
618 % support. The artifact settings listed above will override
621 % o blur: blur the filter by this amount, use 1.0 if unknown.
622 % Image artifact "filter:blur" will override this internal usage.
624 % o radial: 1D orthogonal filter (Sinc) or 2D radial filter (Bessel)
626 % o exception: return any errors or warnings in this structure.
629 MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
630 const FilterTypes filter,const MagickRealType blur,
631 const MagickBooleanType cylindrical,ExceptionInfo *exception)
644 register ResizeFilter
651 Table Mapping given Filter, into Weighting and Windowing
653 A 'Box' windowing function means its a simble non-windowed filter.
654 A 'Sinc' filter function (must be windowed) could be upgraded to a
655 'Bessel' filter if a "cylindrical" filter is requested, unless a
656 "Sinc" filter specifically request.
658 WARNING: The order of this tabel must match the order of the
659 FilterTypes enumeration specified in "resample.h", or the filter
660 names will not match the filter being setup.
662 You can check filter setups with the "filter:verbose" setting.
669 } const mapping[SentinelFilter] =
671 { UndefinedFilter, BoxFilter }, /* Undefined */
672 { PointFilter, BoxFilter }, /* SPECIAL: nearest neighbour filter */
673 { BoxFilter, BoxFilter }, /* Box averaging filter */
674 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
675 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
676 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
677 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
678 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
679 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
680 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
681 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
682 { CatromFilter, BoxFilter }, /* Cubic interpolator */
683 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
684 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
685 { BesselFilter, BoxFilter }, /* Raw 3-lobed Bessel */
686 { SincFilter, BoxFilter }, /* Raw 4-lobed sinc */
687 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
688 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
689 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
690 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
691 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
692 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
693 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
696 Table mapping the filter/window from the above table to an actual
697 function. The default support size for that filter as a weighting
698 function, the range to scale with to use that function as a sinc
699 windowing function, (typ 1.0).
701 Note that the filter_type -> function is 1 to 1 except for Sinc(),
702 SincFast(), and CubicBC() functions, which may have multiple
703 filter to function associations.
705 See "filter:verbose" handling below for the function -> filter
711 (*function)(const MagickRealType, const ResizeFilter*),
712 support, /* default support size for function as a weighting filter */
713 scale, /* windowing function range, for scaling windowing function */
714 B,C; /* Cubic Filter factors for a CubicBC function, else ignored */
715 } const filters[SentinelFilter] =
717 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Undefined */
718 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point */
719 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
720 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
721 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
722 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
723 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
724 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
725 { Gaussian, 1.5, 1.5, 0.0, 0.0 }, /* Gaussian */
726 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
727 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
728 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
729 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
730 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed sinc-sinc */
731 { Bessel, 3.2383, 1.2197, 0.0, 0.0 }, /* Raw 3-lobed Bessel */
732 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed sinc */
733 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
734 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
735 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
736 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
737 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
738 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
739 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
742 The known zero crossings of the Bessel() or the Jinc(x*PI)
743 function found by using
744 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp.
745 For Jv-function with v=1, divide X-roots by PI (tabled below).
747 static MagickRealType
769 Allocate resize filter.
771 assert(image != (const Image *) NULL);
772 assert(image->signature == MagickSignature);
773 if (image->debug != MagickFalse)
774 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
775 assert(UndefinedFilter < filter && filter < SentinelFilter);
776 assert(exception != (ExceptionInfo *) NULL);
777 assert(exception->signature == MagickSignature);
778 resize_filter=(ResizeFilter *) AcquireAlignedMemory(1,sizeof(*resize_filter));
779 if (resize_filter == (ResizeFilter *) NULL)
780 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
782 Defaults for the requested filter.
784 filter_type=mapping[filter].filter;
785 window_type=mapping[filter].window;
787 Filter blur -- scaling both filter and support window.
789 resize_filter->blur=blur;
790 artifact=GetImageArtifact(image,"filter:blur");
791 if (artifact != (const char *) NULL)
792 resize_filter->blur=StringToDouble(artifact);
793 if (resize_filter->blur < MagickEpsilon)
794 resize_filter->blur=(MagickRealType) MagickEpsilon;
796 Cylindrical Filters should use Bessel instead of Sinc, unless a
797 Sinc filter was specifically requested.
798 Result may be overridden by expert settings later.
800 if (cylindrical != MagickFalse)
806 Promote 1D Sinc Filter to a 2D Bessel filter, as long as the
807 user did not directly request a 'Sinc' filter.
809 if ( filter != SincFilter )
810 filter_type=BesselFilter;
815 /* Ditto for SincFast variant */
816 if ( filter != SincFastFilter )
817 filter_type=BesselFilter;
822 /* Promote Lanczos (Sinc-Sinc) to Lanczos (Bessel-Bessel). */
823 filter_type=BesselFilter;
824 window_type=BesselFilter;
829 What about other filters to make them 'cylindrical
830 friendly'? For example Mitchell is actually quite close to
831 a cylindrical Lanczos (Bessel-Bessel) with support 2. Are
832 there other well known 'cylindrical' specific filters?
836 artifact=GetImageArtifact(image,"filter:filter");
837 if (artifact != (const char *) NULL)
839 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
840 if ((UndefinedFilter < option) && (option < SentinelFilter))
842 /* Raw filter request - no window function. */
843 filter_type=(FilterTypes) option;
844 window_type=BoxFilter;
846 if (option == LanczosFilter)
848 /* Lanczos is not a real filter but a self windowing Sinc/Bessel. */
849 filter_type=cylindrical != MagickFalse ? BesselFilter : LanczosFilter;
850 window_type=cylindrical != MagickFalse ? BesselFilter :
853 /* Filter override with a specific window function. */
854 artifact=GetImageArtifact(image,"filter:window");
855 if (artifact != (const char *) NULL)
857 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
858 if ((UndefinedFilter < option) && (option < SentinelFilter))
860 if (option != LanczosFilter)
861 window_type=(FilterTypes) option;
863 window_type=cylindrical != MagickFalse ? BesselFilter :
870 /* Window specified, but no filter function? Assume Sinc/Bessel. */
871 artifact=GetImageArtifact(image,"filter:window");
872 if (artifact != (const char *) NULL)
874 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
876 if ((UndefinedFilter < option) && (option < SentinelFilter))
878 filter_type=cylindrical != MagickFalse ? BesselFilter :
880 window_type=(FilterTypes) option;
884 /* Assign the real functions to use for the filters selected. */
885 resize_filter->filter=filters[filter_type].function;
886 resize_filter->support=filters[filter_type].support;
887 resize_filter->window=filters[window_type].function;
888 resize_filter->scale=filters[window_type].scale;
889 resize_filter->signature=MagickSignature;
890 /* Filter support overrides. */
891 artifact=GetImageArtifact(image,"filter:lobes");
892 if (artifact != (const char *) NULL)
897 lobes=(ssize_t) StringToLong(artifact);
900 resize_filter->support=(MagickRealType) lobes;
901 if (filter_type == BesselFilter)
905 resize_filter->support=bessel_zeros[lobes-1];
908 artifact=GetImageArtifact(image,"filter:support");
909 if (artifact != (const char *) NULL)
910 resize_filter->support=fabs(StringToDouble(artifact));
912 Scale windowing function separatally to the support 'clipping'
913 window that calling operator is planning to actually use. (Expert
916 resize_filter->window_support=resize_filter->support; /* default */
917 artifact=GetImageArtifact(image,"filter:win-support");
918 if (artifact != (const char *) NULL)
919 resize_filter->window_support=fabs(StringToDouble(artifact));
921 Adjust window function scaling to the windowing support for
922 weighting function. This avoids a division on every filter call.
924 resize_filter->scale /= resize_filter->window_support;
926 Set Cubic Spline B,C values, calculate Cubic coefficients.
930 if ((filters[filter_type].function == CubicBC) ||
931 (filters[window_type].function == CubicBC))
933 B=filters[filter_type].B;
934 C=filters[filter_type].C;
935 if (filters[window_type].function == CubicBC)
937 B=filters[window_type].B;
938 C=filters[window_type].C;
940 artifact=GetImageArtifact(image,"filter:b");
941 if (artifact != (const char *) NULL)
943 B=StringToDouble(artifact);
944 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
945 artifact=GetImageArtifact(image,"filter:c");
946 if (artifact != (const char *) NULL)
947 C=StringToDouble(artifact);
951 artifact=GetImageArtifact(image,"filter:c");
952 if (artifact != (const char *) NULL)
954 C=StringToDouble(artifact);
955 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
959 Convert B,C values into Cubic Coefficents. See CubicBC().
961 resize_filter->cubic[0]=(6.0-2.0*B)/6.0;
962 resize_filter->cubic[1]=0.0;
963 resize_filter->cubic[2]=(-18.0+12.0*B+6.0*C)/6.0;
964 resize_filter->cubic[3]=(12.0-9.0*B-6.0*C)/6.0;
965 resize_filter->cubic[4]=(8.0*B+24.0*C)/6.0;
966 resize_filter->cubic[5]=(-12.0*B-48.0*C)/6.0;
967 resize_filter->cubic[6]=(6.0*B+30.0*C)/6.0;
968 resize_filter->cubic[7]=(-B-6.0*C)/6.0;
971 Expert Option Request for verbose details of the resulting filter.
973 #if defined(MAGICKCORE_OPENMP_SUPPORT)
974 /* if( GetOpenMPThreadId() == 0 ) { */
976 artifact=GetImageArtifact(image,"filter:verbose");
977 if (artifact != (const char *) NULL)
984 Set the weighting function properly when the weighting
985 function may not exactly match the filter of the same name.
986 EG: a Point filter really uses a Box weighting function
987 with a different support than is typically used.
990 if (resize_filter->filter == Box) filter_type=BoxFilter;
991 if (resize_filter->filter == Sinc) filter_type=SincFilter;
992 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
993 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
995 Report Filter Details.
997 support=GetResizeFilterSupport(resize_filter); /* support range */
998 (void) fprintf(stdout,"#\n# Resize Filter (for graphing)\n#\n");
999 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1000 MagickFilterOptions,filter_type));
1001 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
1002 MagickFilterOptions, window_type));
1003 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
1004 (double) resize_filter->support);
1005 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
1006 (double) resize_filter->window_support);
1007 (void) fprintf(stdout,"# blur = %.*g\n",GetMagickPrecision(),
1008 (double) resize_filter->blur);
1009 (void) fprintf(stdout,"# blurred_support = %.*g\n",GetMagickPrecision(),
1011 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1012 (double) B,GetMagickPrecision(),(double) C);
1013 (void) fprintf(stdout,"#\n");
1015 Output values of resulting filter graph -- for graphing
1018 for (x=0.0; x <= support; x+=0.01f)
1019 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1020 (double) GetResizeFilterWeight(resize_filter,x));
1021 /* A final value so gnuplot can graph the 'stop' properly. */
1022 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1025 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1028 return(resize_filter);
1032 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1036 % A d a p t i v e R e s i z e I m a g e %
1040 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1042 % AdaptiveResizeImage() adaptively resize image with pixel resampling.
1044 % The format of the AdaptiveResizeImage method is:
1046 % Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1047 % const size_t rows,ExceptionInfo *exception)
1049 % A description of each parameter follows:
1051 % o image: the image.
1053 % o columns: the number of columns in the resized image.
1055 % o rows: the number of rows in the resized image.
1057 % o exception: return any errors or warnings in this structure.
1060 MagickExport Image *AdaptiveResizeImage(const Image *image,
1061 const size_t columns,const size_t rows,ExceptionInfo *exception)
1063 #define AdaptiveResizeImageTag "Resize/Image"
1087 Adaptively resize image.
1089 assert(image != (const Image *) NULL);
1090 assert(image->signature == MagickSignature);
1091 if (image->debug != MagickFalse)
1092 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1093 assert(exception != (ExceptionInfo *) NULL);
1094 assert(exception->signature == MagickSignature);
1095 if ((columns == 0) || (rows == 0))
1096 return((Image *) NULL);
1097 if ((columns == image->columns) && (rows == image->rows))
1098 return(CloneImage(image,0,0,MagickTrue,exception));
1099 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1100 if (resize_image == (Image *) NULL)
1101 return((Image *) NULL);
1102 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1104 InheritException(exception,&resize_image->exception);
1105 resize_image=DestroyImage(resize_image);
1106 return((Image *) NULL);
1108 GetMagickPixelPacket(image,&pixel);
1109 resample_filter=AcquireResampleFilter(image,exception);
1110 if (image->interpolate == UndefinedInterpolatePixel)
1111 (void) SetResampleFilterInterpolateMethod(resample_filter,
1112 MeshInterpolatePixel);
1113 resize_view=AcquireCacheView(resize_image);
1114 for (y=0; y < (ssize_t) resize_image->rows; y++)
1116 register IndexPacket
1117 *restrict resize_indexes;
1122 register PixelPacket
1125 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1127 if (q == (PixelPacket *) NULL)
1129 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1130 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
1131 for (x=0; x < (ssize_t) resize_image->columns; x++)
1133 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1134 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1136 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1139 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1141 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1143 if (proceed == MagickFalse)
1146 resample_filter=DestroyResampleFilter(resample_filter);
1147 resize_view=DestroyCacheView(resize_view);
1148 return(resize_image);
1152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1156 + B e s s e l O r d e r O n e %
1160 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1162 % BesselOrderOne() computes the Bessel function of x of the first kind of
1165 % Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1171 % j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1173 % where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1175 % cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1176 % = 1/sqrt(2) * (sin(x) - cos(x))
1177 % sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1178 % = -1/sqrt(2) * (sin(x) + cos(x))
1180 % The format of the BesselOrderOne method is:
1182 % MagickRealType BesselOrderOne(MagickRealType x)
1184 % A description of each parameter follows:
1186 % o x: MagickRealType value.
1191 static MagickRealType I0(MagickRealType x)
1202 Zeroth order Bessel function of the first kind.
1207 for (i=2; t > MagickEpsilon; i++)
1210 t*=y/((MagickRealType) i*i);
1216 static MagickRealType J1(MagickRealType x)
1228 0.581199354001606143928050809e+21,
1229 -0.6672106568924916298020941484e+20,
1230 0.2316433580634002297931815435e+19,
1231 -0.3588817569910106050743641413e+17,
1232 0.2908795263834775409737601689e+15,
1233 -0.1322983480332126453125473247e+13,
1234 0.3413234182301700539091292655e+10,
1235 -0.4695753530642995859767162166e+7,
1236 0.270112271089232341485679099e+4
1240 0.11623987080032122878585294e+22,
1241 0.1185770712190320999837113348e+20,
1242 0.6092061398917521746105196863e+17,
1243 0.2081661221307607351240184229e+15,
1244 0.5243710262167649715406728642e+12,
1245 0.1013863514358673989967045588e+10,
1246 0.1501793594998585505921097578e+7,
1247 0.1606931573481487801970916749e+4,
1253 for (i=7; i >= 0; i--)
1262 static MagickRealType P1(MagickRealType x)
1274 0.352246649133679798341724373e+5,
1275 0.62758845247161281269005675e+5,
1276 0.313539631109159574238669888e+5,
1277 0.49854832060594338434500455e+4,
1278 0.2111529182853962382105718e+3,
1279 0.12571716929145341558495e+1
1283 0.352246649133679798068390431e+5,
1284 0.626943469593560511888833731e+5,
1285 0.312404063819041039923015703e+5,
1286 0.4930396490181088979386097e+4,
1287 0.2030775189134759322293574e+3,
1293 for (i=4; i >= 0; i--)
1295 p=p*(8.0/x)*(8.0/x)+Pone[i];
1296 q=q*(8.0/x)*(8.0/x)+Qone[i];
1302 static MagickRealType Q1(MagickRealType x)
1314 0.3511751914303552822533318e+3,
1315 0.7210391804904475039280863e+3,
1316 0.4259873011654442389886993e+3,
1317 0.831898957673850827325226e+2,
1318 0.45681716295512267064405e+1,
1319 0.3532840052740123642735e-1
1323 0.74917374171809127714519505e+4,
1324 0.154141773392650970499848051e+5,
1325 0.91522317015169922705904727e+4,
1326 0.18111867005523513506724158e+4,
1327 0.1038187585462133728776636e+3,
1333 for (i=4; i >= 0; i--)
1335 p=p*(8.0/x)*(8.0/x)+Pone[i];
1336 q=q*(8.0/x)*(8.0/x)+Qone[i];
1341 static MagickRealType BesselOrderOne(MagickRealType x)
1354 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1355 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1363 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1367 + D e s t r o y R e s i z e F i l t e r %
1371 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1373 % DestroyResizeFilter() destroy the resize filter.
1375 % The format of the DestroyResizeFilter method is:
1377 % ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1379 % A description of each parameter follows:
1381 % o resize_filter: the resize filter.
1384 MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1386 assert(resize_filter != (ResizeFilter *) NULL);
1387 assert(resize_filter->signature == MagickSignature);
1388 resize_filter->signature=(~MagickSignature);
1389 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1390 return(resize_filter);
1394 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1398 + G e t R e s i z e F i l t e r S u p p o r t %
1402 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1404 % GetResizeFilterSupport() return the current support window size for this
1405 % filter. Note that this may have been enlarged by filter:blur factor.
1407 % The format of the GetResizeFilterSupport method is:
1409 % MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1411 % A description of each parameter follows:
1413 % o filter: Image filter to use.
1416 MagickExport MagickRealType GetResizeFilterSupport(
1417 const ResizeFilter *resize_filter)
1419 assert(resize_filter != (ResizeFilter *) NULL);
1420 assert(resize_filter->signature == MagickSignature);
1421 return(resize_filter->support*resize_filter->blur);
1425 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1429 + G e t R e s i z e F i l t e r W e i g h t %
1433 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1435 % GetResizeFilterWeight evaluates the specified resize filter at the point x
1436 % which usally lies between zero and the filters current 'support' and
1437 % returns the weight of the filter function at that point.
1439 % The format of the GetResizeFilterWeight method is:
1441 % MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1442 % const MagickRealType x)
1444 % A description of each parameter follows:
1446 % o filter: the filter type.
1451 MagickExport MagickRealType GetResizeFilterWeight(
1452 const ResizeFilter *resize_filter,const MagickRealType x)
1459 Windowing function - scale the weighting filter by this amount.
1461 assert(resize_filter != (ResizeFilter *) NULL);
1462 assert(resize_filter->signature == MagickSignature);
1463 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
1464 if ((resize_filter->window_support < MagickEpsilon) ||
1465 (resize_filter->window == Box))
1466 scale=1.0; /* Point or Box Filter -- avoid division by zero */
1469 scale=resize_filter->scale;
1470 scale=resize_filter->window(x_blur*scale,resize_filter);
1472 return(scale*resize_filter->filter(x_blur,resize_filter));
1476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1480 % M a g n i f y I m a g e %
1484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1486 % MagnifyImage() is a convenience method that scales an image proportionally
1487 % to twice its size.
1489 % The format of the MagnifyImage method is:
1491 % Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1493 % A description of each parameter follows:
1495 % o image: the image.
1497 % o exception: return any errors or warnings in this structure.
1500 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1505 assert(image != (Image *) NULL);
1506 assert(image->signature == MagickSignature);
1507 if (image->debug != MagickFalse)
1508 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1509 assert(exception != (ExceptionInfo *) NULL);
1510 assert(exception->signature == MagickSignature);
1511 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1513 return(magnify_image);
1517 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1521 % M i n i f y I m a g e %
1525 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1527 % MinifyImage() is a convenience method that scales an image proportionally
1530 % The format of the MinifyImage method is:
1532 % Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1534 % A description of each parameter follows:
1536 % o image: the image.
1538 % o exception: return any errors or warnings in this structure.
1541 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1546 assert(image != (Image *) NULL);
1547 assert(image->signature == MagickSignature);
1548 if (image->debug != MagickFalse)
1549 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1550 assert(exception != (ExceptionInfo *) NULL);
1551 assert(exception->signature == MagickSignature);
1552 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1554 return(minify_image);
1558 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1562 % R e s a m p l e I m a g e %
1566 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1568 % ResampleImage() resize image in terms of its pixel size, so that when
1569 % displayed at the given resolution it will be the same size in terms of
1570 % real world units as the original image at the original resolution.
1572 % The format of the ResampleImage method is:
1574 % Image *ResampleImage(Image *image,const double x_resolution,
1575 % const double y_resolution,const FilterTypes filter,const double blur,
1576 % ExceptionInfo *exception)
1578 % A description of each parameter follows:
1580 % o image: the image to be resized to fit the given resolution.
1582 % o x_resolution: the new image x resolution.
1584 % o y_resolution: the new image y resolution.
1586 % o filter: Image filter to use.
1588 % o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1591 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1592 const double y_resolution,const FilterTypes filter,const double blur,
1593 ExceptionInfo *exception)
1595 #define ResampleImageTag "Resample/Image"
1605 Initialize sampled image attributes.
1607 assert(image != (const Image *) NULL);
1608 assert(image->signature == MagickSignature);
1609 if (image->debug != MagickFalse)
1610 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1611 assert(exception != (ExceptionInfo *) NULL);
1612 assert(exception->signature == MagickSignature);
1613 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1614 72.0 : image->x_resolution)+0.5);
1615 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1616 72.0 : image->y_resolution)+0.5);
1617 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1618 if (resample_image != (Image *) NULL)
1620 resample_image->x_resolution=x_resolution;
1621 resample_image->y_resolution=y_resolution;
1623 return(resample_image);
1625 #if defined(MAGICKCORE_LQR_DELEGATE)
1628 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1632 % L i q u i d R e s c a l e I m a g e %
1636 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1638 % LiquidRescaleImage() rescales image with seam carving.
1640 % The format of the LiquidRescaleImage method is:
1642 % Image *LiquidRescaleImage(const Image *image,
1643 % const size_t columns,const size_t rows,
1644 % const double delta_x,const double rigidity,ExceptionInfo *exception)
1646 % A description of each parameter follows:
1648 % o image: the image.
1650 % o columns: the number of columns in the rescaled image.
1652 % o rows: the number of rows in the rescaled image.
1654 % o delta_x: maximum seam transversal step (0 means straight seams).
1656 % o rigidity: introduce a bias for non-straight seams (typically 0).
1658 % o exception: return any errors or warnings in this structure.
1661 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1662 const size_t rows,const double delta_x,const double rigidity,
1663 ExceptionInfo *exception)
1665 #define LiquidRescaleImageTag "Rescale/Image"
1699 Liquid rescale image.
1701 assert(image != (const Image *) NULL);
1702 assert(image->signature == MagickSignature);
1703 if (image->debug != MagickFalse)
1704 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1705 assert(exception != (ExceptionInfo *) NULL);
1706 assert(exception->signature == MagickSignature);
1707 if ((columns == 0) || (rows == 0))
1708 return((Image *) NULL);
1709 if ((columns == image->columns) && (rows == image->rows))
1710 return(CloneImage(image,0,0,MagickTrue,exception));
1711 if ((columns <= 2) || (rows <= 2))
1712 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
1713 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1723 Honor liquid resize size limitations.
1725 for (width=image->columns; columns >= (2*width-1); width*=2);
1726 for (height=image->rows; rows >= (2*height-1); height*=2);
1727 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1729 if (resize_image == (Image *) NULL)
1730 return((Image *) NULL);
1731 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1732 rigidity,exception);
1733 resize_image=DestroyImage(resize_image);
1734 return(rescale_image);
1737 if (image->matte == MagickFalse)
1739 if (image->colorspace == CMYKColorspace)
1742 if (image->matte == MagickFalse)
1745 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1746 strlen(map)*sizeof(*pixels));
1747 if (pixels == (unsigned char *) NULL)
1748 return((Image *) NULL);
1749 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1751 if (status == MagickFalse)
1753 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1754 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1756 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1757 if (carver == (LqrCarver *) NULL)
1759 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1760 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1762 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1763 lqr_status=lqr_carver_resize(carver,columns,rows);
1764 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1765 lqr_carver_get_height(carver),MagickTrue,exception);
1766 if (rescale_image == (Image *) NULL)
1768 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1769 return((Image *) NULL);
1771 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1773 InheritException(exception,&rescale_image->exception);
1774 rescale_image=DestroyImage(rescale_image);
1775 return((Image *) NULL);
1777 GetMagickPixelPacket(rescale_image,&pixel);
1778 (void) lqr_carver_scan_reset(carver);
1779 rescale_view=AcquireCacheView(rescale_image);
1780 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1782 register IndexPacket
1783 *restrict rescale_indexes;
1785 register PixelPacket
1788 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
1789 if (q == (PixelPacket *) NULL)
1791 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
1792 pixel.red=QuantumRange*(packet[0]/255.0);
1793 pixel.green=QuantumRange*(packet[1]/255.0);
1794 pixel.blue=QuantumRange*(packet[2]/255.0);
1795 if (image->colorspace != CMYKColorspace)
1797 if (image->matte == MagickFalse)
1798 pixel.opacity=QuantumRange*(packet[3]/255.0);
1802 pixel.index=QuantumRange*(packet[3]/255.0);
1803 if (image->matte == MagickFalse)
1804 pixel.opacity=QuantumRange*(packet[4]/255.0);
1806 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
1807 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
1810 rescale_view=DestroyCacheView(rescale_view);
1812 Relinquish resources.
1814 lqr_carver_destroy(carver);
1815 return(rescale_image);
1818 MagickExport Image *LiquidRescaleImage(const Image *image,
1819 const size_t magick_unused(columns),const size_t magick_unused(rows),
1820 const double magick_unused(delta_x),const double magick_unused(rigidity),
1821 ExceptionInfo *exception)
1823 assert(image != (const Image *) NULL);
1824 assert(image->signature == MagickSignature);
1825 if (image->debug != MagickFalse)
1826 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1827 assert(exception != (ExceptionInfo *) NULL);
1828 assert(exception->signature == MagickSignature);
1829 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1830 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1831 return((Image *) NULL);
1836 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1840 % R e s i z e I m a g e %
1844 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1846 % ResizeImage() scales an image to the desired dimensions, using the given
1847 % filter (see AcquireFilterInfo()).
1849 % If an undefined filter is given the filter defaults to Mitchell for a
1850 % colormapped image, a image with a matte channel, or if the image is
1851 % enlarged. Otherwise the filter defaults to a Lanczos.
1853 % ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1855 % The format of the ResizeImage method is:
1857 % Image *ResizeImage(Image *image,const size_t columns,
1858 % const size_t rows,const FilterTypes filter,const double blur,
1859 % ExceptionInfo *exception)
1861 % A description of each parameter follows:
1863 % o image: the image.
1865 % o columns: the number of columns in the scaled image.
1867 % o rows: the number of rows in the scaled image.
1869 % o filter: Image filter to use.
1871 % o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1874 % o exception: return any errors or warnings in this structure.
1878 typedef struct _ContributionInfo
1887 static ContributionInfo **DestroyContributionThreadSet(
1888 ContributionInfo **contribution)
1893 assert(contribution != (ContributionInfo **) NULL);
1894 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
1895 if (contribution[i] != (ContributionInfo *) NULL)
1896 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1898 contribution=(ContributionInfo **) RelinquishAlignedMemory(contribution);
1899 return(contribution);
1902 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1913 number_threads=GetOpenMPMaximumThreads();
1914 contribution=(ContributionInfo **) AcquireAlignedMemory(number_threads,
1915 sizeof(*contribution));
1916 if (contribution == (ContributionInfo **) NULL)
1917 return((ContributionInfo **) NULL);
1918 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
1919 for (i=0; i < (ssize_t) number_threads; i++)
1921 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1922 sizeof(**contribution));
1923 if (contribution[i] == (ContributionInfo *) NULL)
1924 return(DestroyContributionThreadSet(contribution));
1926 return(contribution);
1929 static inline double MagickMax(const double x,const double y)
1936 static inline double MagickMin(const double x,const double y)
1943 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
1944 const Image *image,Image *resize_image,const MagickRealType x_factor,
1945 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
1947 #define ResizeImageTag "Resize/Image"
1957 **restrict contributions;
1973 Apply filter to resize horizontally from image to resize image.
1975 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
1976 support=scale*GetResizeFilterSupport(resize_filter);
1977 storage_class=support > 0.5 ? DirectClass : image->storage_class;
1978 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
1980 InheritException(exception,&resize_image->exception);
1981 return(MagickFalse);
1986 Support too small even for nearest neighbour: Reduce to point
1989 support=(MagickRealType) 0.5;
1992 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
1993 if (contributions == (ContributionInfo **) NULL)
1995 (void) ThrowMagickException(exception,GetMagickModule(),
1996 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
1997 return(MagickFalse);
2001 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2002 image_view=AcquireCacheView(image);
2003 resize_view=AcquireCacheView(resize_image);
2004 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2005 #pragma omp parallel for shared(status)
2007 for (x=0; x < (ssize_t) resize_image->columns; x++)
2013 register const IndexPacket
2016 register const PixelPacket
2019 register ContributionInfo
2020 *restrict contribution;
2022 register IndexPacket
2023 *restrict resize_indexes;
2025 register PixelPacket
2036 if (status == MagickFalse)
2038 center=(MagickRealType) (x+0.5)/x_factor;
2039 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2040 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
2042 contribution=contributions[GetOpenMPThreadId()];
2043 for (n=0; n < (stop-start); n++)
2045 contribution[n].pixel=start+n;
2046 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2047 ((MagickRealType) (start+n)-center+0.5));
2048 density+=contribution[n].weight;
2050 if ((density != 0.0) && (density != 1.0))
2058 density=1.0/density;
2059 for (i=0; i < n; i++)
2060 contribution[i].weight*=density;
2062 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2063 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
2064 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2066 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2071 indexes=GetCacheViewVirtualIndexQueue(image_view);
2072 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2073 for (y=0; y < (ssize_t) resize_image->rows; y++)
2088 if (image->matte == MagickFalse)
2090 for (i=0; i < n; i++)
2092 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2093 (contribution[i].pixel-contribution[0].pixel);
2094 alpha=contribution[i].weight;
2095 pixel.red+=alpha*(p+j)->red;
2096 pixel.green+=alpha*(p+j)->green;
2097 pixel.blue+=alpha*(p+j)->blue;
2098 pixel.opacity+=alpha*(p+j)->opacity;
2100 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2101 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2102 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2103 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2104 if ((image->colorspace == CMYKColorspace) &&
2105 (resize_image->colorspace == CMYKColorspace))
2107 for (i=0; i < n; i++)
2109 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2110 (contribution[i].pixel-contribution[0].pixel);
2111 alpha=contribution[i].weight;
2112 pixel.index+=alpha*indexes[j];
2114 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
2123 for (i=0; i < n; i++)
2125 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2126 (contribution[i].pixel-contribution[0].pixel);
2127 alpha=contribution[i].weight*QuantumScale*
2128 GetAlphaPixelComponent(p+j);
2129 pixel.red+=alpha*(p+j)->red;
2130 pixel.green+=alpha*(p+j)->green;
2131 pixel.blue+=alpha*(p+j)->blue;
2132 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2135 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2136 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2137 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2138 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2139 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2140 if ((image->colorspace == CMYKColorspace) &&
2141 (resize_image->colorspace == CMYKColorspace))
2143 for (i=0; i < n; i++)
2145 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2146 (contribution[i].pixel-contribution[0].pixel);
2147 alpha=contribution[i].weight*QuantumScale*
2148 GetAlphaPixelComponent(p+j);
2149 pixel.index+=alpha*indexes[j];
2151 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2152 GetIndexPixelComponent(&pixel));
2155 if ((resize_image->storage_class == PseudoClass) &&
2156 (image->storage_class == PseudoClass))
2158 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
2160 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2161 (contribution[i-start].pixel-contribution[0].pixel);
2162 resize_indexes[y]=indexes[j];
2166 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2168 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2173 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2174 #pragma omp critical (MagickCore_HorizontalFilter)
2176 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2177 if (proceed == MagickFalse)
2181 resize_view=DestroyCacheView(resize_view);
2182 image_view=DestroyCacheView(image_view);
2183 contributions=DestroyContributionThreadSet(contributions);
2187 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2188 const Image *image,Image *resize_image,const MagickRealType y_factor,
2189 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2199 **restrict contributions;
2215 Apply filter to resize vertically from image to resize image.
2217 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
2218 support=scale*GetResizeFilterSupport(resize_filter);
2219 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2220 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2222 InheritException(exception,&resize_image->exception);
2223 return(MagickFalse);
2228 Support too small even for nearest neighbour: Reduce to point
2231 support=(MagickRealType) 0.5;
2234 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2235 if (contributions == (ContributionInfo **) NULL)
2237 (void) ThrowMagickException(exception,GetMagickModule(),
2238 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2239 return(MagickFalse);
2243 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2244 image_view=AcquireCacheView(image);
2245 resize_view=AcquireCacheView(resize_image);
2246 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2247 #pragma omp parallel for shared(status)
2249 for (y=0; y < (ssize_t) resize_image->rows; y++)
2255 register const IndexPacket
2258 register const PixelPacket
2261 register ContributionInfo
2262 *restrict contribution;
2264 register IndexPacket
2265 *restrict resize_indexes;
2267 register PixelPacket
2278 if (status == MagickFalse)
2280 center=(MagickRealType) (y+0.5)/y_factor;
2281 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2282 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
2284 contribution=contributions[GetOpenMPThreadId()];
2285 for (n=0; n < (stop-start); n++)
2287 contribution[n].pixel=start+n;
2288 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2289 ((MagickRealType) (start+n)-center+0.5));
2290 density+=contribution[n].weight;
2292 if ((density != 0.0) && (density != 1.0))
2300 density=1.0/density;
2301 for (i=0; i < n; i++)
2302 contribution[i].weight*=density;
2304 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
2305 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2307 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2309 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2314 indexes=GetCacheViewVirtualIndexQueue(image_view);
2315 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2316 for (x=0; x < (ssize_t) resize_image->columns; x++)
2331 if (image->matte == MagickFalse)
2333 for (i=0; i < n; i++)
2335 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2337 alpha=contribution[i].weight;
2338 pixel.red+=alpha*(p+j)->red;
2339 pixel.green+=alpha*(p+j)->green;
2340 pixel.blue+=alpha*(p+j)->blue;
2341 pixel.opacity+=alpha*(p+j)->opacity;
2343 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2344 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2345 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2346 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2347 if ((image->colorspace == CMYKColorspace) &&
2348 (resize_image->colorspace == CMYKColorspace))
2350 for (i=0; i < n; i++)
2352 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2354 alpha=contribution[i].weight;
2355 pixel.index+=alpha*indexes[j];
2357 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
2366 for (i=0; i < n; i++)
2368 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2370 alpha=contribution[i].weight*QuantumScale*
2371 GetAlphaPixelComponent(p+j);
2372 pixel.red+=alpha*(p+j)->red;
2373 pixel.green+=alpha*(p+j)->green;
2374 pixel.blue+=alpha*(p+j)->blue;
2375 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2378 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2379 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2380 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2381 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2382 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2383 if ((image->colorspace == CMYKColorspace) &&
2384 (resize_image->colorspace == CMYKColorspace))
2386 for (i=0; i < n; i++)
2388 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2390 alpha=contribution[i].weight*QuantumScale*
2391 GetAlphaPixelComponent(p+j);
2392 pixel.index+=alpha*indexes[j];
2394 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2395 GetIndexPixelComponent(&pixel));
2398 if ((resize_image->storage_class == PseudoClass) &&
2399 (image->storage_class == PseudoClass))
2401 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
2403 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
2405 resize_indexes[x]=indexes[j];
2409 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2411 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2416 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2417 #pragma omp critical (MagickCore_VerticalFilter)
2419 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2420 if (proceed == MagickFalse)
2424 resize_view=DestroyCacheView(resize_view);
2425 image_view=DestroyCacheView(image_view);
2426 contributions=DestroyContributionThreadSet(contributions);
2430 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2431 const size_t rows,const FilterTypes filter,const double blur,
2432 ExceptionInfo *exception)
2434 #define WorkLoadFactor 0.265
2460 Acquire resize image.
2462 assert(image != (Image *) NULL);
2463 assert(image->signature == MagickSignature);
2464 if (image->debug != MagickFalse)
2465 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2466 assert(exception != (ExceptionInfo *) NULL);
2467 assert(exception->signature == MagickSignature);
2468 if ((columns == 0) || (rows == 0))
2469 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2470 if ((columns == image->columns) && (rows == image->rows) &&
2471 (filter == UndefinedFilter) && (blur == 1.0))
2472 return(CloneImage(image,0,0,MagickTrue,exception));
2473 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2474 if (resize_image == (Image *) NULL)
2475 return(resize_image);
2477 Acquire resize filter.
2479 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2480 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2481 if ((x_factor*y_factor) > WorkLoadFactor)
2482 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2484 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2485 if (filter_image == (Image *) NULL)
2486 return(DestroyImage(resize_image));
2487 filter_type=LanczosFilter;
2488 if (filter != UndefinedFilter)
2491 if ((x_factor == 1.0) && (y_factor == 1.0))
2492 filter_type=PointFilter;
2494 if ((image->storage_class == PseudoClass) ||
2495 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2496 filter_type=MitchellFilter;
2497 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2503 if ((x_factor*y_factor) > WorkLoadFactor)
2505 span=(MagickSizeType) (filter_image->columns+rows);
2506 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
2508 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
2509 span,&offset,exception);
2513 span=(MagickSizeType) (filter_image->rows+columns);
2514 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
2516 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
2517 span,&offset,exception);
2522 filter_image=DestroyImage(filter_image);
2523 resize_filter=DestroyResizeFilter(resize_filter);
2524 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2525 return((Image *) NULL);
2526 resize_image->type=image->type;
2527 return(resize_image);
2531 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2535 % S a m p l e I m a g e %
2539 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2541 % SampleImage() scales an image to the desired dimensions with pixel
2542 % sampling. Unlike other scaling methods, this method does not introduce
2543 % any additional color into the scaled image.
2545 % The format of the SampleImage method is:
2547 % Image *SampleImage(const Image *image,const size_t columns,
2548 % const size_t rows,ExceptionInfo *exception)
2550 % A description of each parameter follows:
2552 % o image: the image.
2554 % o columns: the number of columns in the sampled image.
2556 % o rows: the number of rows in the sampled image.
2558 % o exception: return any errors or warnings in this structure.
2561 MagickExport Image *SampleImage(const Image *image,const size_t columns,
2562 const size_t rows,ExceptionInfo *exception)
2564 #define SampleImageTag "Sample/Image"
2587 Initialize sampled image attributes.
2589 assert(image != (const Image *) NULL);
2590 assert(image->signature == MagickSignature);
2591 if (image->debug != MagickFalse)
2592 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2593 assert(exception != (ExceptionInfo *) NULL);
2594 assert(exception->signature == MagickSignature);
2595 if ((columns == 0) || (rows == 0))
2596 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2597 if ((columns == image->columns) && (rows == image->rows))
2598 return(CloneImage(image,0,0,MagickTrue,exception));
2599 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2600 if (sample_image == (Image *) NULL)
2601 return((Image *) NULL);
2603 Allocate scan line buffer and column offset buffers.
2605 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
2607 if (x_offset == (ssize_t *) NULL)
2609 sample_image=DestroyImage(sample_image);
2610 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2612 for (x=0; x < (ssize_t) sample_image->columns; x++)
2613 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
2614 sample_image->columns);
2620 image_view=AcquireCacheView(image);
2621 sample_view=AcquireCacheView(sample_image);
2622 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2623 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2625 for (y=0; y < (ssize_t) sample_image->rows; y++)
2627 register const IndexPacket
2630 register const PixelPacket
2633 register IndexPacket
2634 *restrict sample_indexes;
2636 register PixelPacket
2645 if (status == MagickFalse)
2647 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2648 sample_image->rows);
2649 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2651 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2653 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2658 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2659 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2663 for (x=0; x < (ssize_t) sample_image->columns; x++)
2664 *q++=p[x_offset[x]];
2665 if ((image->storage_class == PseudoClass) ||
2666 (image->colorspace == CMYKColorspace))
2667 for (x=0; x < (ssize_t) sample_image->columns; x++)
2668 sample_indexes[x]=indexes[x_offset[x]];
2669 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2671 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2676 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2677 #pragma omp critical (MagickCore_SampleImage)
2679 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2680 if (proceed == MagickFalse)
2684 image_view=DestroyCacheView(image_view);
2685 sample_view=DestroyCacheView(sample_view);
2686 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
2687 sample_image->type=image->type;
2688 return(sample_image);
2692 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2696 % S c a l e I m a g e %
2700 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2702 % ScaleImage() changes the size of an image to the given dimensions.
2704 % The format of the ScaleImage method is:
2706 % Image *ScaleImage(const Image *image,const size_t columns,
2707 % const size_t rows,ExceptionInfo *exception)
2709 % A description of each parameter follows:
2711 % o image: the image.
2713 % o columns: the number of columns in the scaled image.
2715 % o rows: the number of rows in the scaled image.
2717 % o exception: return any errors or warnings in this structure.
2720 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2721 const size_t rows,ExceptionInfo *exception)
2723 #define ScaleImageTag "Scale/Image"
2757 Initialize scaled image attributes.
2759 assert(image != (const Image *) NULL);
2760 assert(image->signature == MagickSignature);
2761 if (image->debug != MagickFalse)
2762 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2763 assert(exception != (ExceptionInfo *) NULL);
2764 assert(exception->signature == MagickSignature);
2765 if ((columns == 0) || (rows == 0))
2766 return((Image *) NULL);
2767 if ((columns == image->columns) && (rows == image->rows))
2768 return(CloneImage(image,0,0,MagickTrue,exception));
2769 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2770 if (scale_image == (Image *) NULL)
2771 return((Image *) NULL);
2772 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2774 InheritException(exception,&scale_image->exception);
2775 scale_image=DestroyImage(scale_image);
2776 return((Image *) NULL);
2781 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2784 if (image->rows != scale_image->rows)
2785 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2787 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2788 scale_image->columns,sizeof(*scale_scanline));
2789 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2791 if ((scanline == (MagickPixelPacket *) NULL) ||
2792 (scale_scanline == (MagickPixelPacket *) NULL) ||
2793 (x_vector == (MagickPixelPacket *) NULL) ||
2794 (y_vector == (MagickPixelPacket *) NULL))
2796 scale_image=DestroyImage(scale_image);
2797 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2803 next_row=MagickTrue;
2805 scale.y=(double) scale_image->rows/(double) image->rows;
2806 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2808 GetMagickPixelPacket(image,&pixel);
2809 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2811 image_view=AcquireCacheView(image);
2812 scale_view=AcquireCacheView(scale_image);
2813 for (y=0; y < (ssize_t) scale_image->rows; y++)
2815 register const IndexPacket
2818 register const PixelPacket
2821 register IndexPacket
2822 *restrict scale_indexes;
2824 register MagickPixelPacket
2828 register PixelPacket
2834 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2836 if (q == (PixelPacket *) NULL)
2838 scale_indexes=GetAuthenticIndexQueue(scale_image);
2839 if (scale_image->rows == image->rows)
2842 Read a new scanline.
2844 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2846 if (p == (const PixelPacket *) NULL)
2848 indexes=GetCacheViewVirtualIndexQueue(image_view);
2849 for (x=0; x < (ssize_t) image->columns; x++)
2851 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2852 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2853 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
2854 if (image->matte != MagickFalse)
2855 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
2856 if (indexes != (IndexPacket *) NULL)
2857 x_vector[x].index=(MagickRealType) indexes[x];
2866 while (scale.y < span.y)
2868 if ((next_row != MagickFalse) &&
2869 (number_rows < (ssize_t) image->rows))
2872 Read a new scanline.
2874 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2876 if (p == (const PixelPacket *) NULL)
2878 indexes=GetCacheViewVirtualIndexQueue(image_view);
2879 for (x=0; x < (ssize_t) image->columns; x++)
2881 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2882 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2883 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
2884 if (image->matte != MagickFalse)
2885 x_vector[x].opacity=(MagickRealType)
2886 GetOpacityPixelComponent(p);
2887 if (indexes != (IndexPacket *) NULL)
2888 x_vector[x].index=(MagickRealType) indexes[x];
2893 for (x=0; x < (ssize_t) image->columns; x++)
2895 y_vector[x].red+=scale.y*x_vector[x].red;
2896 y_vector[x].green+=scale.y*x_vector[x].green;
2897 y_vector[x].blue+=scale.y*x_vector[x].blue;
2898 if (scale_image->matte != MagickFalse)
2899 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2900 if (scale_indexes != (IndexPacket *) NULL)
2901 y_vector[x].index+=scale.y*x_vector[x].index;
2904 scale.y=(double) scale_image->rows/(double) image->rows;
2905 next_row=MagickTrue;
2907 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
2910 Read a new scanline.
2912 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2914 if (p == (const PixelPacket *) NULL)
2916 indexes=GetCacheViewVirtualIndexQueue(image_view);
2917 for (x=0; x < (ssize_t) image->columns; x++)
2919 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2920 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2921 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
2922 if (image->matte != MagickFalse)
2923 x_vector[x].opacity=(MagickRealType)
2924 GetOpacityPixelComponent(p);
2925 if (indexes != (IndexPacket *) NULL)
2926 x_vector[x].index=(MagickRealType) indexes[x];
2930 next_row=MagickFalse;
2933 for (x=0; x < (ssize_t) image->columns; x++)
2935 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
2936 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
2937 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
2938 if (image->matte != MagickFalse)
2939 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
2940 if (scale_indexes != (IndexPacket *) NULL)
2941 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
2943 s->green=pixel.green;
2945 if (scale_image->matte != MagickFalse)
2946 s->opacity=pixel.opacity;
2947 if (scale_indexes != (IndexPacket *) NULL)
2948 s->index=pixel.index;
2955 scale.y=(double) scale_image->rows/(double) image->rows;
2956 next_row=MagickTrue;
2960 if (scale_image->columns == image->columns)
2963 Transfer scanline to scaled image.
2966 for (x=0; x < (ssize_t) scale_image->columns; x++)
2968 q->red=ClampToQuantum(s->red);
2969 q->green=ClampToQuantum(s->green);
2970 q->blue=ClampToQuantum(s->blue);
2971 if (scale_image->matte != MagickFalse)
2972 q->opacity=ClampToQuantum(s->opacity);
2973 if (scale_indexes != (IndexPacket *) NULL)
2974 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
2985 next_column=MagickFalse;
2989 for (x=0; x < (ssize_t) image->columns; x++)
2991 scale.x=(double) scale_image->columns/(double) image->columns;
2992 while (scale.x >= span.x)
2994 if (next_column != MagickFalse)
2999 pixel.red+=span.x*s->red;
3000 pixel.green+=span.x*s->green;
3001 pixel.blue+=span.x*s->blue;
3002 if (image->matte != MagickFalse)
3003 pixel.opacity+=span.x*s->opacity;
3004 if (scale_indexes != (IndexPacket *) NULL)
3005 pixel.index+=span.x*s->index;
3007 t->green=pixel.green;
3009 if (scale_image->matte != MagickFalse)
3010 t->opacity=pixel.opacity;
3011 if (scale_indexes != (IndexPacket *) NULL)
3012 t->index=pixel.index;
3015 next_column=MagickTrue;
3019 if (next_column != MagickFalse)
3022 next_column=MagickFalse;
3025 pixel.red+=scale.x*s->red;
3026 pixel.green+=scale.x*s->green;
3027 pixel.blue+=scale.x*s->blue;
3028 if (scale_image->matte != MagickFalse)
3029 pixel.opacity+=scale.x*s->opacity;
3030 if (scale_indexes != (IndexPacket *) NULL)
3031 pixel.index+=scale.x*s->index;
3039 pixel.red+=span.x*s->red;
3040 pixel.green+=span.x*s->green;
3041 pixel.blue+=span.x*s->blue;
3042 if (scale_image->matte != MagickFalse)
3043 pixel.opacity+=span.x*s->opacity;
3044 if (scale_indexes != (IndexPacket *) NULL)
3045 pixel.index+=span.x*s->index;
3047 if ((next_column == MagickFalse) &&
3048 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
3051 t->green=pixel.green;
3053 if (scale_image->matte != MagickFalse)
3054 t->opacity=pixel.opacity;
3055 if (scale_indexes != (IndexPacket *) NULL)
3056 t->index=pixel.index;
3059 Transfer scanline to scaled image.
3062 for (x=0; x < (ssize_t) scale_image->columns; x++)
3064 q->red=ClampToQuantum(t->red);
3065 q->green=ClampToQuantum(t->green);
3066 q->blue=ClampToQuantum(t->blue);
3067 if (scale_image->matte != MagickFalse)
3068 q->opacity=ClampToQuantum(t->opacity);
3069 if (scale_indexes != (IndexPacket *) NULL)
3070 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
3075 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
3077 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3079 if (proceed == MagickFalse)
3082 scale_view=DestroyCacheView(scale_view);
3083 image_view=DestroyCacheView(image_view);
3085 Free allocated memory.
3087 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3088 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3089 if (scale_image->rows != image->rows)
3090 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3091 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3092 scale_image->type=image->type;
3093 return(scale_image);
3097 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3101 + S e t R e s i z e F i l t e r S u p p o r t %
3105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3107 % SetResizeFilterSupport() specifies which IR filter to use to window
3109 % The format of the SetResizeFilterSupport method is:
3111 % void SetResizeFilterSupport(ResizeFilter *resize_filter,
3112 % const MagickRealType support)
3114 % A description of each parameter follows:
3116 % o resize_filter: the resize filter.
3118 % o support: the filter spport radius.
3121 MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3122 const MagickRealType support)
3124 assert(resize_filter != (ResizeFilter *) NULL);
3125 assert(resize_filter->signature == MagickSignature);
3126 resize_filter->support=support;
3130 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3134 % T h u m b n a i l I m a g e %
3138 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3140 % ThumbnailImage() changes the size of an image to the given dimensions and
3141 % removes any associated profiles. The goal is to produce small low cost
3142 % thumbnail images suited for display on the Web.
3144 % The format of the ThumbnailImage method is:
3146 % Image *ThumbnailImage(const Image *image,const size_t columns,
3147 % const size_t rows,ExceptionInfo *exception)
3149 % A description of each parameter follows:
3151 % o image: the image.
3153 % o columns: the number of columns in the scaled image.
3155 % o rows: the number of rows in the scaled image.
3157 % o exception: return any errors or warnings in this structure.
3160 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3161 const size_t rows,ExceptionInfo *exception)
3163 #define SampleFactor 5
3166 value[MaxTextExtent];
3184 assert(image != (Image *) NULL);
3185 assert(image->signature == MagickSignature);
3186 if (image->debug != MagickFalse)
3187 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3188 assert(exception != (ExceptionInfo *) NULL);
3189 assert(exception->signature == MagickSignature);
3190 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3191 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3192 if ((x_factor*y_factor) > 0.1)
3193 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3196 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
3197 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3198 image->blur,exception);
3204 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3206 if (sample_image == (Image *) NULL)
3207 return((Image *) NULL);
3208 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3209 image->blur,exception);
3210 sample_image=DestroyImage(sample_image);
3212 if (thumbnail_image == (Image *) NULL)
3213 return(thumbnail_image);
3214 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3215 if (thumbnail_image->matte == MagickFalse)
3216 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3217 thumbnail_image->depth=8;
3218 thumbnail_image->interlace=NoInterlace;
3220 Strip all profiles except color profiles.
3222 ResetImageProfileIterator(thumbnail_image);
3223 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3225 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3227 (void) DeleteImageProfile(thumbnail_image,name);
3228 ResetImageProfileIterator(thumbnail_image);
3230 name=GetNextImageProfile(thumbnail_image);
3232 (void) DeleteImageProperty(thumbnail_image,"comment");
3233 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3234 if (strstr(image->magick_filename,"//") == (char *) NULL)
3235 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
3236 image->magick_filename);
3237 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3238 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3239 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3241 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3242 attributes.st_mtime);
3243 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3245 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3246 attributes.st_mtime);
3247 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
3248 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
3249 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3250 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3252 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3253 (void) SetImageProperty(thumbnail_image,"software",
3254 GetMagickVersion(&version));
3255 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3256 image->magick_columns);
3257 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
3258 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3259 image->magick_rows);
3260 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
3261 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3262 GetImageListLength(image));
3263 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3264 return(thumbnail_image);