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 A Box filter is a equal weighting function (all weights tha same).
185 DO NOT LIMIT results by support or resize point sampling will work
186 as it requests points beyond its normal 0.0 support size.
191 static MagickRealType CubicBC(const MagickRealType x,
192 const ResizeFilter *resize_filter)
195 Cubic Filters using B,C determined values:
196 Mitchell-Netravali B=1/3 C=1/3 Qualitively ideal Cubic Filter
197 Catmull-Rom B= 0 C=1/2 Cublic Interpolation Function
198 Cubic B-Spline B= 1 C= 0 Spline Approximation of Gaussian
199 Hermite B= 0 C= 0 Quadratic Spline (support = 1)
201 See paper by Mitchell and Netravali, Reconstruction Filters in Computer
202 Graphics Computer Graphics, Volume 22, Number 4, August 1988
203 http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/
206 Coefficents are determined from B,C values:
209 P2 = (-18 +12*B + 6*C )/6
210 P3 = ( 12 - 9*B - 6*C )/6
212 Q1 = ( -12*B -48*C )/6
214 Q3 = ( - 1*B - 6*C )/6
216 which are used to define the filter:
218 P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
219 Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x <= 2
221 which ensures function is continuous in value and derivative (slope).
224 return(resize_filter->cubic[0]+x*(resize_filter->cubic[1]+x*
225 (resize_filter->cubic[2]+x*resize_filter->cubic[3])));
227 return(resize_filter->cubic[4]+x*(resize_filter->cubic[5]+x*
228 (resize_filter->cubic[6]+x*resize_filter->cubic[7])));
232 static MagickRealType Gaussian(const MagickRealType x,
233 const ResizeFilter *magick_unused(resize_filter))
236 1D Gaussian with sigma=1/2
237 exp(-2 x^2)/sqrt(pi/2))
239 /*const MagickRealType alpha = (MagickRealType) (2.0/MagickSQ2PI);*/
240 return(exp((double) (-2.0*x*x)));
243 static MagickRealType Hanning(const MagickRealType x,
244 const ResizeFilter *magick_unused(resize_filter))
247 Cosine window function: .5 + .5 cos(pi x).
249 const MagickRealType cospix = cos((double) (MagickPIL*x));
250 return(0.5+0.5*cospix);
253 static MagickRealType Hamming(const MagickRealType x,
254 const ResizeFilter *magick_unused(resize_filter))
257 Offset cosine window function: .54 + .46 cos(pi x).
259 const MagickRealType cospix = cos((double) (MagickPIL*x));
260 return(0.54+0.46*cospix);
263 static MagickRealType Kaiser(const MagickRealType x,
264 const ResizeFilter *magick_unused(resize_filter))
267 #define I0A (1.0/I0(Alpha))
270 Kaiser Windowing Function (bessel windowing): Alpha is a free
271 value from 5 to 8 (currently hardcoded to 6.5).
272 Future: make alpha the IOA pre-calculation, an 'expert' setting.
274 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
277 static MagickRealType Lagrange(const MagickRealType x,
278 const ResizeFilter *resize_filter)
291 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the
292 lagrange function and depends on the overall support window size
293 of the filter. That is: for a support of 2, it gives a lagrange-4
294 (piecewise cubic function).
296 "n" identifies the piece of the piecewise polynomial.
298 See Survey: Interpolation Methods, IEEE Transactions on Medical
299 Imaging, Vol 18, No 11, November 1999, p1049-1075, -- Equation 27
302 if (x > resize_filter->support)
304 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
305 /*n=(ssize_t)((1.0*order)/2.0+x); -- which piece does x belong to */
306 n = (ssize_t)(resize_filter->window_support + x);
308 for (i=0; i < order; i++)
310 value*=(n-i-x)/(n-i);
314 static MagickRealType Quadratic(const MagickRealType x,
315 const ResizeFilter *magick_unused(resize_filter))
318 2rd order (quadratic) B-Spline approximation of Gaussian.
323 return(0.5*(x-1.5)*(x-1.5));
327 static MagickRealType Sinc(const MagickRealType x,
328 const ResizeFilter *magick_unused(resize_filter))
331 Scaled sinc(x) function using a trig call
332 sinc(x) == sin(pi x)/(pi x).
336 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
337 return(sin((double) pix)/pix);
339 return((MagickRealType) 1.0);
342 static MagickRealType SincFast(const MagickRealType x,
343 const ResizeFilter *magick_unused(resize_filter))
346 Approximations of the sinc function sin(pi x)/(pi x) over the
347 interval [-4,4] constructed by Nicolas Robidoux and Chantal
348 Racette with funding from the Natural Sciences and Engineering
349 Research Council of Canada.
351 Although the approximations are polynomials (for low order of
352 approximation) and quotients of polynomials (for higher order of
353 approximation) and consequently are similar in form to Taylor
354 polynomials/Pade approximants, the approximations are computed
355 with a completely different technique.
357 Summary: These approximations are "the best" in terms of bang
358 (accuracy) for the buck (flops). More specifically: Among the
359 polynomial quotients that can be computed using a fixed number of
360 flops (with a given "+ - * / budget"), the chosen polynomial
361 quotient is the one closest to the approximated function with
362 respect to maximum absolute relative error over the given
365 The Remez algorithm, as implemented in the boost library's minimax
366 package, is the key to the construction:
367 http://www.boost.org/doc/libs/1_36_0/libs/math/doc/...
368 ...sf_and_dist/html/math_toolkit/backgrounders/remez.html
371 If outside of the interval of approximation, use the standard trig
376 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
377 return(sin((double) pix)/pix);
381 The approximations only depend on x^2 (sinc is an even
384 const MagickRealType xx = x*x;
385 #if MAGICKCORE_QUANTUM_DEPTH <= 8
387 Maximum absolute relative error 6.3e-6 < 1/2^17.
389 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
390 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
391 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
392 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
393 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
394 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
395 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
396 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
397 const MagickRealType p =
398 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
399 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
400 #elif MAGICKCORE_QUANTUM_DEPTH <= 16
402 Max. abs. rel. error 2.2e-8 < 1/2^25.
404 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
405 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
406 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
407 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
408 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
409 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
410 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
411 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
412 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
413 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
414 const MagickRealType p =
415 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
416 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
417 #elif MAGICKCORE_QUANTUM_DEPTH <= 32
419 Max. abs. rel. error 1.2e-12 < 1/2^39.
421 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
422 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
423 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
424 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
425 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
426 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
427 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
428 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
429 const MagickRealType p =
430 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
431 const MagickRealType d0 = 1.0L;
432 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
433 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
434 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
435 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
436 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
437 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
440 Max. abs. rel. error 2.5e-17 < 1/2^55 if computed with "true"
441 long doubles, 2.6e-14 < 1/2^45 if long doubles are IEEE doubles.
443 const MagickRealType c0 = 0.173611111111111113332932116053816904714e-2L;
444 const MagickRealType c1 = -0.303723497515718809687399886229022703169e-3L;
445 const MagickRealType c2 = 0.233100817439489606061795544561807507471e-4L;
446 const MagickRealType c3 = -0.103906554814465396861269897523992002705e-5L;
447 const MagickRealType c4 = 0.299175768961095380104447394070231517712e-7L;
448 const MagickRealType c5 = -0.582555275175235235925786868156315453570e-9L;
449 const MagickRealType c6 = 0.774885118857072154223233850399192557934e-11L;
450 const MagickRealType c7 = -0.686148809066333092764596425714057372964e-13L;
451 const MagickRealType c8 = 0.371085247462909594457662716426170281684e-15L;
452 const MagickRealType c9 = -0.944551950759515118228796037513456335763e-18L;
453 const MagickRealType p =
454 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
455 const MagickRealType d0 = 1.0L;
456 const MagickRealType d1 = 0.463782211680615975951490586564903936283e-1L;
457 const MagickRealType d2 = 0.984899056548092584226994406603163505777e-3L;
458 const MagickRealType d3 = 0.121314604267142674069019025409802158375e-4L;
459 const MagickRealType d4 = 0.881998514405598677970025517260813044225e-7L;
460 const MagickRealType d5 = 0.310377434094436341006055666680844291856e-9L;
461 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*(d4+xx*d5))));
462 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
467 static MagickRealType Triangle(const MagickRealType x,
468 const ResizeFilter *magick_unused(resize_filter))
471 1st order (linear) B-Spline, bilinear interpolation, Tent 1D
472 filter, or a Bartlett 2D Cone filter.
479 static MagickRealType Welsh(const MagickRealType x,
480 const ResizeFilter *magick_unused(resize_filter))
483 Welsh parabolic windowing filter.
491 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
495 + A c q u i r e R e s i z e F i l t e r %
499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
501 % AcquireResizeFilter() allocates the ResizeFilter structure. Choose
502 % from these filters:
504 % FIR (Finite impulse Response) Filters
505 % Box Triangle Quadratic
506 % Cubic Hermite Catrom
509 % IIR (Infinite impulse Response) Filters
510 % Gaussian Sinc Bessel
512 % Windowed Sinc/Bessel Method
513 % Blackman Hanning Hamming
514 % Kaiser Lanczos (Sinc)
516 % FIR filters are used as is, and are limited by that filters support
517 % window (unless over-ridden). 'Gaussian' while classed as an IIR
518 % filter, is also simply clipped by its support size (currently 1.5
519 % but probably should be 2.0 for better use from EWA resampling)
521 % The users "-filter" selection ise used to lookup the default
522 % 'expert' settings for that filter from a internal table. However
523 % any provided 'expert' settings (see below) may override this
526 % The selection is typically either a windowed sinc, or interpolated
527 % filter, for use by functions such as ResizeImage(). However if a
528 % 'cylindrical' filter flag is requested, the default sinc weighting
529 % and windowing functions will be promoted to cylindrical Bessel
532 % Directly requesting 'Sinc' or 'Bessel' will force the use of that
533 % filter function, with a default 'Blackman' windowing method. This
534 % not however recommended as it removes the correct filter selection
535 % for different filtering image operations. Selecting a window
536 % filtering method is better.
538 % Lanczos is a special case of a sinc-windowed sinc, but defaulting
539 % to 3-lobe support, rather that the default 4 lobe support of the
540 % windowed sinc filters.
542 % Two forms of the sinc function are available: Sinc and SincFast.
543 % Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
544 % selected if the user specifically specifies the use of a Sinc
545 % filter. SincFast uses highly accurate (and fast) polynomial (low Q)
546 % and rational (high Q) approximations and is used by default in most
549 % Special 'expert' options can be used to override any and all filter
550 % settings. This is not advised unless you have expert knowledge of
551 % the use of resampling filtered techniques. Check on the results of
552 % your selections using the "filter:verbose" setting to make sure you
553 % got what you requested.
555 % "filter:filter" Select the function associated with this filter
556 % as the weighting function of the filter. This can be used to set a
557 % a windowing function as a weighting function.
559 % If a "filter:window" operation has not been provided, then a 'Box'
560 % windowing function will be set to denote that no windowing function
563 % "filter:window" Select this windowing function for the filter.
564 % While any filter could be used as a windowing function, using the
565 % 'first lobe' of that filter over the whole support window, using a
566 % non-windowing function is not advisible. If no weighting function
567 % is specifed a 'SincFast' filter will be used.
569 % "filter:lobes" Number of lobes to use for the Sinc/Bessel filter.
570 % This a simpler method of setting filter support size that will
571 % correctly handle the Sinc/Bessel switch for an operators filtering
572 % requirements. Only integers should be given.
574 % "filter:support" Set the support size for filtering to the size given
575 % This not recommended for Sinc/Bessel windowed filters (lobes should
576 % be used instead). This will override any 'filter:lobes' option.
578 % "filter:win-support" Scale windowing function to this size instead.
579 % This causes the windowing (or self-windowing Lagrange filter) to act
580 % is if the support window it much much larger than what is actually
581 % supplied to the calling operator. The filter however is still
582 % clipped to the real support size given, by the support range suppiled
583 % to the caller. If unset this will equal the normal filter support
586 % "filter:blur" Scale the filter and support window by this amount.
587 % A value >1 will generally result in a more burred image with
588 % more ringing effects, while a value <1 will sharpen the
589 % resulting image with more aliasing and Morie effects.
592 % "filter:c" Override the preset B,C values for a Cubic type of filter
593 % If only one of these are given it is assumes to be a 'Keys'
594 % type of filter such that B+2C=1, where Keys 'alpha' value = C
596 % "filter:verbose" Output the exact results of the filter selections
597 % made, as well as plotting data for graphing the resulting filter
598 % over support range (blur adjusted).
600 % Set a true un-windowed Sinc filter with 10 lobes (very slow)
601 % -set option:filter:filter Sinc
602 % -set option:filter:lobes 8
604 % For example force an 8 lobe Lanczos (Sinc or Bessel) filter...
606 % -set option:filter:lobes 8
609 % The format of the AcquireResizeFilter method is:
611 % ResizeFilter *AcquireResizeFilter(const Image *image,
612 % const FilterTypes filter_type, const MagickBooleanType radial,
613 % ExceptionInfo *exception)
615 % A description of each parameter follows:
617 % o image: the image.
619 % o filter: the filter type, defining a preset filter, window and
620 % support. The artifact settings listed above will override
623 % o blur: blur the filter by this amount, use 1.0 if unknown.
624 % Image artifact "filter:blur" will override this internal usage.
626 % o radial: 1D orthogonal filter (Sinc) or 2D radial filter (Bessel)
628 % o exception: return any errors or warnings in this structure.
631 MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
632 const FilterTypes filter,const MagickRealType blur,
633 const MagickBooleanType cylindrical,ExceptionInfo *exception)
646 register ResizeFilter
653 Table Mapping given Filter, into Weighting and Windowing
655 A 'Box' windowing function means its a simble non-windowed filter.
656 A 'Sinc' filter function (must be windowed) could be upgraded to a
657 'Bessel' filter if a "cylindrical" filter is requested, unless a
658 "Sinc" filter specifically request.
660 WARNING: The order of this tabel must match the order of the
661 FilterTypes enumeration specified in "resample.h", or the filter
662 names will not match the filter being setup.
664 You can check filter setups with the "filter:verbose" setting.
671 } const mapping[SentinelFilter] =
673 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
674 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
675 { BoxFilter, BoxFilter }, /* Box averaging filter */
676 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
677 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
678 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
679 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
680 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
681 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
682 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
683 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
684 { CatromFilter, BoxFilter }, /* Cubic interpolator */
685 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
686 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
687 { BesselFilter, BoxFilter }, /* Raw 3-lobed Bessel */
688 { SincFilter, BoxFilter }, /* Raw 4-lobed sinc */
689 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
690 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
691 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
692 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
693 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
694 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
695 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
698 Table mapping the filter/window from the above table to an actual
699 function. The default support size for that filter as a weighting
700 function, the range to scale with to use that function as a sinc
701 windowing function, (typ 1.0).
703 Note that the filter_type -> function is 1 to 1 except for Sinc(),
704 SincFast(), and CubicBC() functions, which may have multiple
705 filter to function associations.
707 See "filter:verbose" handling below for the function -> filter
713 (*function)(const MagickRealType, const ResizeFilter*),
714 support, /* default support size for function as a weighting filter */
715 scale, /* windowing function range, for scaling windowing function */
716 B,C; /* Cubic Filter factors for a CubicBC function, else ignored */
717 } const filters[SentinelFilter] =
719 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
720 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
721 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
722 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
723 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
724 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
725 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
726 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
727 { Gaussian, 1.5, 1.5, 0.0, 0.0 }, /* Gaussian */
728 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
729 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
730 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
731 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
732 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed sinc-sinc */
733 { Bessel, 3.2383, 1.2197, 0.0, 0.0 }, /* Raw 3-lobed Bessel */
734 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed sinc */
735 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
736 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
737 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
738 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
739 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
740 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
741 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
744 The known zero crossings of the Bessel() or the Jinc(x*PI)
745 function found by using
746 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp.
747 For Jv-function with v=1, divide X-roots by PI (tabled below).
749 static MagickRealType
771 Allocate resize filter.
773 assert(image != (const Image *) NULL);
774 assert(image->signature == MagickSignature);
775 if (image->debug != MagickFalse)
776 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
777 assert(UndefinedFilter < filter && filter < SentinelFilter);
778 assert(exception != (ExceptionInfo *) NULL);
779 assert(exception->signature == MagickSignature);
780 resize_filter=(ResizeFilter *) AcquireAlignedMemory(1,sizeof(*resize_filter));
781 if (resize_filter == (ResizeFilter *) NULL)
782 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
784 Defaults for the requested filter.
786 filter_type=mapping[filter].filter;
787 window_type=mapping[filter].window;
789 Filter blur -- scaling both filter and support window.
791 resize_filter->blur=blur;
792 artifact=GetImageArtifact(image,"filter:blur");
793 if (artifact != (const char *) NULL)
794 resize_filter->blur=StringToDouble(artifact);
795 if (resize_filter->blur < MagickEpsilon)
796 resize_filter->blur=(MagickRealType) MagickEpsilon;
798 Cylindrical Filters should use Bessel instead of Sinc, unless a
799 Sinc filter was specifically requested.
800 Result may be overridden by expert settings later.
802 if (cylindrical != MagickFalse)
808 Promote 1D Sinc Filter to a 2D Bessel filter, as long as the
809 user did not directly request a 'Sinc' filter.
811 if ( filter != SincFilter )
812 filter_type=BesselFilter;
817 /* Ditto for SincFast variant */
818 if ( filter != SincFastFilter )
819 filter_type=BesselFilter;
824 /* Promote Lanczos (Sinc-Sinc) to Lanczos (Bessel-Bessel). */
825 filter_type=BesselFilter;
826 window_type=BesselFilter;
831 What about other filters to make them 'cylindrical
832 friendly'? For example Mitchell is actually quite close to
833 a cylindrical Lanczos (Bessel-Bessel) with support 2. Are
834 there other well known 'cylindrical' specific filters?
838 artifact=GetImageArtifact(image,"filter:filter");
839 if (artifact != (const char *) NULL)
841 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
842 if ((UndefinedFilter < option) && (option < SentinelFilter))
844 /* Raw filter request - no window function. */
845 filter_type=(FilterTypes) option;
846 window_type=BoxFilter;
848 if (option == LanczosFilter)
850 /* Lanczos is not a real filter but a self windowing Sinc/Bessel. */
851 filter_type=cylindrical != MagickFalse ? BesselFilter : LanczosFilter;
852 window_type=cylindrical != MagickFalse ? BesselFilter :
855 /* Filter override with a specific window function. */
856 artifact=GetImageArtifact(image,"filter:window");
857 if (artifact != (const char *) NULL)
859 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
860 if ((UndefinedFilter < option) && (option < SentinelFilter))
862 if (option != LanczosFilter)
863 window_type=(FilterTypes) option;
865 window_type=cylindrical != MagickFalse ? BesselFilter :
872 /* Window specified, but no filter function? Assume Sinc/Bessel. */
873 artifact=GetImageArtifact(image,"filter:window");
874 if (artifact != (const char *) NULL)
876 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
878 if ((UndefinedFilter < option) && (option < SentinelFilter))
880 filter_type=cylindrical != MagickFalse ? BesselFilter :
882 window_type=(FilterTypes) option;
886 /* Assign the real functions to use for the filters selected. */
887 resize_filter->filter=filters[filter_type].function;
888 resize_filter->support=filters[filter_type].support;
889 resize_filter->window=filters[window_type].function;
890 resize_filter->scale=filters[window_type].scale;
891 resize_filter->signature=MagickSignature;
892 /* Filter support overrides. */
893 artifact=GetImageArtifact(image,"filter:lobes");
894 if (artifact != (const char *) NULL)
899 lobes=(ssize_t) StringToLong(artifact);
902 resize_filter->support=(MagickRealType) lobes;
903 if (filter_type == BesselFilter)
907 resize_filter->support=bessel_zeros[lobes-1];
910 artifact=GetImageArtifact(image,"filter:support");
911 if (artifact != (const char *) NULL)
912 resize_filter->support=fabs(StringToDouble(artifact));
914 Scale windowing function separatally to the support 'clipping'
915 window that calling operator is planning to actually use. (Expert
918 resize_filter->window_support=resize_filter->support; /* default */
919 artifact=GetImageArtifact(image,"filter:win-support");
920 if (artifact != (const char *) NULL)
921 resize_filter->window_support=fabs(StringToDouble(artifact));
923 Adjust window function scaling to the windowing support for
924 weighting function. This avoids a division on every filter call.
926 resize_filter->scale /= resize_filter->window_support;
928 Set Cubic Spline B,C values, calculate Cubic coefficients.
932 if ((filters[filter_type].function == CubicBC) ||
933 (filters[window_type].function == CubicBC))
935 B=filters[filter_type].B;
936 C=filters[filter_type].C;
937 if (filters[window_type].function == CubicBC)
939 B=filters[window_type].B;
940 C=filters[window_type].C;
942 artifact=GetImageArtifact(image,"filter:b");
943 if (artifact != (const char *) NULL)
945 B=StringToDouble(artifact);
946 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
947 artifact=GetImageArtifact(image,"filter:c");
948 if (artifact != (const char *) NULL)
949 C=StringToDouble(artifact);
953 artifact=GetImageArtifact(image,"filter:c");
954 if (artifact != (const char *) NULL)
956 C=StringToDouble(artifact);
957 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
961 Convert B,C values into Cubic Coefficents. See CubicBC().
963 resize_filter->cubic[0]=(6.0-2.0*B)/6.0;
964 resize_filter->cubic[1]=0.0;
965 resize_filter->cubic[2]=(-18.0+12.0*B+6.0*C)/6.0;
966 resize_filter->cubic[3]=(12.0-9.0*B-6.0*C)/6.0;
967 resize_filter->cubic[4]=(8.0*B+24.0*C)/6.0;
968 resize_filter->cubic[5]=(-12.0*B-48.0*C)/6.0;
969 resize_filter->cubic[6]=(6.0*B+30.0*C)/6.0;
970 resize_filter->cubic[7]=(-B-6.0*C)/6.0;
973 Expert Option Request for verbose details of the resulting filter.
975 #if defined(MAGICKCORE_OPENMP_SUPPORT)
976 /* if( GetOpenMPThreadId() == 0 ) { */
978 artifact=GetImageArtifact(image,"filter:verbose");
979 if (artifact != (const char *) NULL)
986 Set the weighting function properly when the weighting
987 function may not exactly match the filter of the same name.
988 EG: a Point filter really uses a Box weighting function
989 with a different support than is typically used.
992 if (resize_filter->filter == Box) filter_type=BoxFilter;
993 if (resize_filter->filter == Sinc) filter_type=SincFilter;
994 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
995 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
997 Report Filter Details.
999 support=GetResizeFilterSupport(resize_filter); /* support range */
1000 (void) fprintf(stdout,"#\n# Resize Filter (for graphing)\n#\n");
1001 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1002 MagickFilterOptions,filter_type));
1003 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
1004 MagickFilterOptions, window_type));
1005 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
1006 (double) resize_filter->support);
1007 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
1008 (double) resize_filter->window_support);
1009 (void) fprintf(stdout,"# blur = %.*g\n",GetMagickPrecision(),
1010 (double) resize_filter->blur);
1011 (void) fprintf(stdout,"# blurred_support = %.*g\n",GetMagickPrecision(),
1013 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1014 (double) B,GetMagickPrecision(),(double) C);
1015 (void) fprintf(stdout,"#\n");
1017 Output values of resulting filter graph -- for graphing
1020 for (x=0.0; x <= support; x+=0.01f)
1021 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1022 (double) GetResizeFilterWeight(resize_filter,x));
1023 /* A final value so gnuplot can graph the 'stop' properly. */
1024 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1027 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1030 return(resize_filter);
1034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1038 % A d a p t i v e R e s i z e I m a g e %
1042 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1044 % AdaptiveResizeImage() adaptively resize image with pixel resampling.
1046 % The format of the AdaptiveResizeImage method is:
1048 % Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1049 % const size_t rows,ExceptionInfo *exception)
1051 % A description of each parameter follows:
1053 % o image: the image.
1055 % o columns: the number of columns in the resized image.
1057 % o rows: the number of rows in the resized image.
1059 % o exception: return any errors or warnings in this structure.
1062 MagickExport Image *AdaptiveResizeImage(const Image *image,
1063 const size_t columns,const size_t rows,ExceptionInfo *exception)
1065 #define AdaptiveResizeImageTag "Resize/Image"
1089 Adaptively resize image.
1091 assert(image != (const Image *) NULL);
1092 assert(image->signature == MagickSignature);
1093 if (image->debug != MagickFalse)
1094 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1095 assert(exception != (ExceptionInfo *) NULL);
1096 assert(exception->signature == MagickSignature);
1097 if ((columns == 0) || (rows == 0))
1098 return((Image *) NULL);
1099 if ((columns == image->columns) && (rows == image->rows))
1100 return(CloneImage(image,0,0,MagickTrue,exception));
1101 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1102 if (resize_image == (Image *) NULL)
1103 return((Image *) NULL);
1104 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1106 InheritException(exception,&resize_image->exception);
1107 resize_image=DestroyImage(resize_image);
1108 return((Image *) NULL);
1110 GetMagickPixelPacket(image,&pixel);
1111 resample_filter=AcquireResampleFilter(image,exception);
1112 if (image->interpolate == UndefinedInterpolatePixel)
1113 (void) SetResampleFilterInterpolateMethod(resample_filter,
1114 MeshInterpolatePixel);
1115 resize_view=AcquireCacheView(resize_image);
1116 for (y=0; y < (ssize_t) resize_image->rows; y++)
1118 register IndexPacket
1119 *restrict resize_indexes;
1124 register PixelPacket
1127 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1129 if (q == (PixelPacket *) NULL)
1131 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1132 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
1133 for (x=0; x < (ssize_t) resize_image->columns; x++)
1135 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1136 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1138 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1141 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1143 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1145 if (proceed == MagickFalse)
1148 resample_filter=DestroyResampleFilter(resample_filter);
1149 resize_view=DestroyCacheView(resize_view);
1150 return(resize_image);
1154 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1158 + B e s s e l O r d e r O n e %
1162 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1164 % BesselOrderOne() computes the Bessel function of x of the first kind of
1167 % Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1173 % j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1175 % where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1177 % cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1178 % = 1/sqrt(2) * (sin(x) - cos(x))
1179 % sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1180 % = -1/sqrt(2) * (sin(x) + cos(x))
1182 % The format of the BesselOrderOne method is:
1184 % MagickRealType BesselOrderOne(MagickRealType x)
1186 % A description of each parameter follows:
1188 % o x: MagickRealType value.
1193 static MagickRealType I0(MagickRealType x)
1204 Zeroth order Bessel function of the first kind.
1209 for (i=2; t > MagickEpsilon; i++)
1212 t*=y/((MagickRealType) i*i);
1218 static MagickRealType J1(MagickRealType x)
1230 0.581199354001606143928050809e+21,
1231 -0.6672106568924916298020941484e+20,
1232 0.2316433580634002297931815435e+19,
1233 -0.3588817569910106050743641413e+17,
1234 0.2908795263834775409737601689e+15,
1235 -0.1322983480332126453125473247e+13,
1236 0.3413234182301700539091292655e+10,
1237 -0.4695753530642995859767162166e+7,
1238 0.270112271089232341485679099e+4
1242 0.11623987080032122878585294e+22,
1243 0.1185770712190320999837113348e+20,
1244 0.6092061398917521746105196863e+17,
1245 0.2081661221307607351240184229e+15,
1246 0.5243710262167649715406728642e+12,
1247 0.1013863514358673989967045588e+10,
1248 0.1501793594998585505921097578e+7,
1249 0.1606931573481487801970916749e+4,
1255 for (i=7; i >= 0; i--)
1264 static MagickRealType P1(MagickRealType x)
1276 0.352246649133679798341724373e+5,
1277 0.62758845247161281269005675e+5,
1278 0.313539631109159574238669888e+5,
1279 0.49854832060594338434500455e+4,
1280 0.2111529182853962382105718e+3,
1281 0.12571716929145341558495e+1
1285 0.352246649133679798068390431e+5,
1286 0.626943469593560511888833731e+5,
1287 0.312404063819041039923015703e+5,
1288 0.4930396490181088979386097e+4,
1289 0.2030775189134759322293574e+3,
1295 for (i=4; i >= 0; i--)
1297 p=p*(8.0/x)*(8.0/x)+Pone[i];
1298 q=q*(8.0/x)*(8.0/x)+Qone[i];
1304 static MagickRealType Q1(MagickRealType x)
1316 0.3511751914303552822533318e+3,
1317 0.7210391804904475039280863e+3,
1318 0.4259873011654442389886993e+3,
1319 0.831898957673850827325226e+2,
1320 0.45681716295512267064405e+1,
1321 0.3532840052740123642735e-1
1325 0.74917374171809127714519505e+4,
1326 0.154141773392650970499848051e+5,
1327 0.91522317015169922705904727e+4,
1328 0.18111867005523513506724158e+4,
1329 0.1038187585462133728776636e+3,
1335 for (i=4; i >= 0; i--)
1337 p=p*(8.0/x)*(8.0/x)+Pone[i];
1338 q=q*(8.0/x)*(8.0/x)+Qone[i];
1343 static MagickRealType BesselOrderOne(MagickRealType x)
1356 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1357 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1365 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1369 + D e s t r o y R e s i z e F i l t e r %
1373 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1375 % DestroyResizeFilter() destroy the resize filter.
1377 % The format of the DestroyResizeFilter method is:
1379 % ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1381 % A description of each parameter follows:
1383 % o resize_filter: the resize filter.
1386 MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1388 assert(resize_filter != (ResizeFilter *) NULL);
1389 assert(resize_filter->signature == MagickSignature);
1390 resize_filter->signature=(~MagickSignature);
1391 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1392 return(resize_filter);
1396 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1400 + G e t R e s i z e F i l t e r S u p p o r t %
1404 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1406 % GetResizeFilterSupport() return the current support window size for this
1407 % filter. Note that this may have been enlarged by filter:blur factor.
1409 % The format of the GetResizeFilterSupport method is:
1411 % MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1413 % A description of each parameter follows:
1415 % o filter: Image filter to use.
1418 MagickExport MagickRealType GetResizeFilterSupport(
1419 const ResizeFilter *resize_filter)
1421 assert(resize_filter != (ResizeFilter *) NULL);
1422 assert(resize_filter->signature == MagickSignature);
1423 return(resize_filter->support*resize_filter->blur);
1427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1431 + G e t R e s i z e F i l t e r W e i g h t %
1435 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1437 % GetResizeFilterWeight evaluates the specified resize filter at the point x
1438 % which usally lies between zero and the filters current 'support' and
1439 % returns the weight of the filter function at that point.
1441 % The format of the GetResizeFilterWeight method is:
1443 % MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1444 % const MagickRealType x)
1446 % A description of each parameter follows:
1448 % o filter: the filter type.
1453 MagickExport MagickRealType GetResizeFilterWeight(
1454 const ResizeFilter *resize_filter,const MagickRealType x)
1461 Windowing function - scale the weighting filter by this amount.
1463 assert(resize_filter != (ResizeFilter *) NULL);
1464 assert(resize_filter->signature == MagickSignature);
1465 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
1466 if ((resize_filter->window_support < MagickEpsilon) ||
1467 (resize_filter->window == Box))
1468 scale=1.0; /* Point or Box Filter -- avoid division by zero */
1471 scale=resize_filter->scale;
1472 scale=resize_filter->window(x_blur*scale,resize_filter);
1474 return(scale*resize_filter->filter(x_blur,resize_filter));
1478 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1482 % M a g n i f y I m a g e %
1486 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1488 % MagnifyImage() is a convenience method that scales an image proportionally
1489 % to twice its size.
1491 % The format of the MagnifyImage method is:
1493 % Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1495 % A description of each parameter follows:
1497 % o image: the image.
1499 % o exception: return any errors or warnings in this structure.
1502 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1507 assert(image != (Image *) NULL);
1508 assert(image->signature == MagickSignature);
1509 if (image->debug != MagickFalse)
1510 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1511 assert(exception != (ExceptionInfo *) NULL);
1512 assert(exception->signature == MagickSignature);
1513 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1515 return(magnify_image);
1519 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1523 % M i n i f y I m a g e %
1527 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1529 % MinifyImage() is a convenience method that scales an image proportionally
1532 % The format of the MinifyImage method is:
1534 % Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1536 % A description of each parameter follows:
1538 % o image: the image.
1540 % o exception: return any errors or warnings in this structure.
1543 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1548 assert(image != (Image *) NULL);
1549 assert(image->signature == MagickSignature);
1550 if (image->debug != MagickFalse)
1551 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1552 assert(exception != (ExceptionInfo *) NULL);
1553 assert(exception->signature == MagickSignature);
1554 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1556 return(minify_image);
1560 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1564 % R e s a m p l e I m a g e %
1568 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1570 % ResampleImage() resize image in terms of its pixel size, so that when
1571 % displayed at the given resolution it will be the same size in terms of
1572 % real world units as the original image at the original resolution.
1574 % The format of the ResampleImage method is:
1576 % Image *ResampleImage(Image *image,const double x_resolution,
1577 % const double y_resolution,const FilterTypes filter,const double blur,
1578 % ExceptionInfo *exception)
1580 % A description of each parameter follows:
1582 % o image: the image to be resized to fit the given resolution.
1584 % o x_resolution: the new image x resolution.
1586 % o y_resolution: the new image y resolution.
1588 % o filter: Image filter to use.
1590 % o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1593 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1594 const double y_resolution,const FilterTypes filter,const double blur,
1595 ExceptionInfo *exception)
1597 #define ResampleImageTag "Resample/Image"
1607 Initialize sampled image attributes.
1609 assert(image != (const Image *) NULL);
1610 assert(image->signature == MagickSignature);
1611 if (image->debug != MagickFalse)
1612 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1613 assert(exception != (ExceptionInfo *) NULL);
1614 assert(exception->signature == MagickSignature);
1615 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1616 72.0 : image->x_resolution)+0.5);
1617 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1618 72.0 : image->y_resolution)+0.5);
1619 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1620 if (resample_image != (Image *) NULL)
1622 resample_image->x_resolution=x_resolution;
1623 resample_image->y_resolution=y_resolution;
1625 return(resample_image);
1627 #if defined(MAGICKCORE_LQR_DELEGATE)
1630 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1634 % L i q u i d R e s c a l e I m a g e %
1638 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1640 % LiquidRescaleImage() rescales image with seam carving.
1642 % The format of the LiquidRescaleImage method is:
1644 % Image *LiquidRescaleImage(const Image *image,
1645 % const size_t columns,const size_t rows,
1646 % const double delta_x,const double rigidity,ExceptionInfo *exception)
1648 % A description of each parameter follows:
1650 % o image: the image.
1652 % o columns: the number of columns in the rescaled image.
1654 % o rows: the number of rows in the rescaled image.
1656 % o delta_x: maximum seam transversal step (0 means straight seams).
1658 % o rigidity: introduce a bias for non-straight seams (typically 0).
1660 % o exception: return any errors or warnings in this structure.
1663 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1664 const size_t rows,const double delta_x,const double rigidity,
1665 ExceptionInfo *exception)
1667 #define LiquidRescaleImageTag "Rescale/Image"
1701 Liquid rescale image.
1703 assert(image != (const Image *) NULL);
1704 assert(image->signature == MagickSignature);
1705 if (image->debug != MagickFalse)
1706 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1707 assert(exception != (ExceptionInfo *) NULL);
1708 assert(exception->signature == MagickSignature);
1709 if ((columns == 0) || (rows == 0))
1710 return((Image *) NULL);
1711 if ((columns == image->columns) && (rows == image->rows))
1712 return(CloneImage(image,0,0,MagickTrue,exception));
1713 if ((columns <= 2) || (rows <= 2))
1714 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
1715 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1725 Honor liquid resize size limitations.
1727 for (width=image->columns; columns >= (2*width-1); width*=2);
1728 for (height=image->rows; rows >= (2*height-1); height*=2);
1729 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1731 if (resize_image == (Image *) NULL)
1732 return((Image *) NULL);
1733 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1734 rigidity,exception);
1735 resize_image=DestroyImage(resize_image);
1736 return(rescale_image);
1739 if (image->matte == MagickFalse)
1741 if (image->colorspace == CMYKColorspace)
1744 if (image->matte == MagickFalse)
1747 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1748 strlen(map)*sizeof(*pixels));
1749 if (pixels == (unsigned char *) NULL)
1750 return((Image *) NULL);
1751 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1753 if (status == MagickFalse)
1755 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1756 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1758 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1759 if (carver == (LqrCarver *) NULL)
1761 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1762 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1764 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1765 lqr_status=lqr_carver_resize(carver,columns,rows);
1766 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1767 lqr_carver_get_height(carver),MagickTrue,exception);
1768 if (rescale_image == (Image *) NULL)
1770 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1771 return((Image *) NULL);
1773 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1775 InheritException(exception,&rescale_image->exception);
1776 rescale_image=DestroyImage(rescale_image);
1777 return((Image *) NULL);
1779 GetMagickPixelPacket(rescale_image,&pixel);
1780 (void) lqr_carver_scan_reset(carver);
1781 rescale_view=AcquireCacheView(rescale_image);
1782 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1784 register IndexPacket
1785 *restrict rescale_indexes;
1787 register PixelPacket
1790 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
1791 if (q == (PixelPacket *) NULL)
1793 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
1794 pixel.red=QuantumRange*(packet[0]/255.0);
1795 pixel.green=QuantumRange*(packet[1]/255.0);
1796 pixel.blue=QuantumRange*(packet[2]/255.0);
1797 if (image->colorspace != CMYKColorspace)
1799 if (image->matte == MagickFalse)
1800 pixel.opacity=QuantumRange*(packet[3]/255.0);
1804 pixel.index=QuantumRange*(packet[3]/255.0);
1805 if (image->matte == MagickFalse)
1806 pixel.opacity=QuantumRange*(packet[4]/255.0);
1808 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
1809 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
1812 rescale_view=DestroyCacheView(rescale_view);
1814 Relinquish resources.
1816 lqr_carver_destroy(carver);
1817 return(rescale_image);
1820 MagickExport Image *LiquidRescaleImage(const Image *image,
1821 const size_t magick_unused(columns),const size_t magick_unused(rows),
1822 const double magick_unused(delta_x),const double magick_unused(rigidity),
1823 ExceptionInfo *exception)
1825 assert(image != (const Image *) NULL);
1826 assert(image->signature == MagickSignature);
1827 if (image->debug != MagickFalse)
1828 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1829 assert(exception != (ExceptionInfo *) NULL);
1830 assert(exception->signature == MagickSignature);
1831 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1832 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1833 return((Image *) NULL);
1838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1842 % R e s i z e I m a g e %
1846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1848 % ResizeImage() scales an image to the desired dimensions, using the given
1849 % filter (see AcquireFilterInfo()).
1851 % If an undefined filter is given the filter defaults to Mitchell for a
1852 % colormapped image, a image with a matte channel, or if the image is
1853 % enlarged. Otherwise the filter defaults to a Lanczos.
1855 % ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1857 % The format of the ResizeImage method is:
1859 % Image *ResizeImage(Image *image,const size_t columns,
1860 % const size_t rows,const FilterTypes filter,const double blur,
1861 % ExceptionInfo *exception)
1863 % A description of each parameter follows:
1865 % o image: the image.
1867 % o columns: the number of columns in the scaled image.
1869 % o rows: the number of rows in the scaled image.
1871 % o filter: Image filter to use.
1873 % o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1876 % o exception: return any errors or warnings in this structure.
1880 typedef struct _ContributionInfo
1889 static ContributionInfo **DestroyContributionThreadSet(
1890 ContributionInfo **contribution)
1895 assert(contribution != (ContributionInfo **) NULL);
1896 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
1897 if (contribution[i] != (ContributionInfo *) NULL)
1898 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1900 contribution=(ContributionInfo **) RelinquishAlignedMemory(contribution);
1901 return(contribution);
1904 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1915 number_threads=GetOpenMPMaximumThreads();
1916 contribution=(ContributionInfo **) AcquireAlignedMemory(number_threads,
1917 sizeof(*contribution));
1918 if (contribution == (ContributionInfo **) NULL)
1919 return((ContributionInfo **) NULL);
1920 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
1921 for (i=0; i < (ssize_t) number_threads; i++)
1923 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1924 sizeof(**contribution));
1925 if (contribution[i] == (ContributionInfo *) NULL)
1926 return(DestroyContributionThreadSet(contribution));
1928 return(contribution);
1931 static inline double MagickMax(const double x,const double y)
1938 static inline double MagickMin(const double x,const double y)
1945 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
1946 const Image *image,Image *resize_image,const MagickRealType x_factor,
1947 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
1949 #define ResizeImageTag "Resize/Image"
1959 **restrict contributions;
1975 Apply filter to resize horizontally from image to resize image.
1977 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
1978 support=scale*GetResizeFilterSupport(resize_filter);
1979 storage_class=support > 0.5 ? DirectClass : image->storage_class;
1980 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
1982 InheritException(exception,&resize_image->exception);
1983 return(MagickFalse);
1988 Support too small even for nearest neighbour: Reduce to point
1991 support=(MagickRealType) 0.5;
1994 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
1995 if (contributions == (ContributionInfo **) NULL)
1997 (void) ThrowMagickException(exception,GetMagickModule(),
1998 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
1999 return(MagickFalse);
2003 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2004 image_view=AcquireCacheView(image);
2005 resize_view=AcquireCacheView(resize_image);
2006 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2007 #pragma omp parallel for shared(status)
2009 for (x=0; x < (ssize_t) resize_image->columns; x++)
2015 register const IndexPacket
2018 register const PixelPacket
2021 register ContributionInfo
2022 *restrict contribution;
2024 register IndexPacket
2025 *restrict resize_indexes;
2027 register PixelPacket
2038 if (status == MagickFalse)
2040 center=(MagickRealType) (x+0.5)/x_factor;
2041 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2042 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
2044 contribution=contributions[GetOpenMPThreadId()];
2045 for (n=0; n < (stop-start); n++)
2047 contribution[n].pixel=start+n;
2048 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2049 ((MagickRealType) (start+n)-center+0.5));
2050 density+=contribution[n].weight;
2052 if ((density != 0.0) && (density != 1.0))
2060 density=1.0/density;
2061 for (i=0; i < n; i++)
2062 contribution[i].weight*=density;
2064 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2065 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
2066 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2068 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2073 indexes=GetCacheViewVirtualIndexQueue(image_view);
2074 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2075 for (y=0; y < (ssize_t) resize_image->rows; y++)
2090 if (image->matte == MagickFalse)
2092 for (i=0; i < n; i++)
2094 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2095 (contribution[i].pixel-contribution[0].pixel);
2096 alpha=contribution[i].weight;
2097 pixel.red+=alpha*(p+j)->red;
2098 pixel.green+=alpha*(p+j)->green;
2099 pixel.blue+=alpha*(p+j)->blue;
2100 pixel.opacity+=alpha*(p+j)->opacity;
2102 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2103 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2104 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2105 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2106 if ((image->colorspace == CMYKColorspace) &&
2107 (resize_image->colorspace == CMYKColorspace))
2109 for (i=0; i < n; i++)
2111 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2112 (contribution[i].pixel-contribution[0].pixel);
2113 alpha=contribution[i].weight;
2114 pixel.index+=alpha*indexes[j];
2116 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
2125 for (i=0; i < n; i++)
2127 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2128 (contribution[i].pixel-contribution[0].pixel);
2129 alpha=contribution[i].weight*QuantumScale*
2130 GetAlphaPixelComponent(p+j);
2131 pixel.red+=alpha*(p+j)->red;
2132 pixel.green+=alpha*(p+j)->green;
2133 pixel.blue+=alpha*(p+j)->blue;
2134 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2137 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2138 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2139 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2140 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2141 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2142 if ((image->colorspace == CMYKColorspace) &&
2143 (resize_image->colorspace == CMYKColorspace))
2145 for (i=0; i < n; i++)
2147 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2148 (contribution[i].pixel-contribution[0].pixel);
2149 alpha=contribution[i].weight*QuantumScale*
2150 GetAlphaPixelComponent(p+j);
2151 pixel.index+=alpha*indexes[j];
2153 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2154 GetIndexPixelComponent(&pixel));
2157 if ((resize_image->storage_class == PseudoClass) &&
2158 (image->storage_class == PseudoClass))
2160 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
2162 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2163 (contribution[i-start].pixel-contribution[0].pixel);
2164 resize_indexes[y]=indexes[j];
2168 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2170 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2175 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2176 #pragma omp critical (MagickCore_HorizontalFilter)
2178 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2179 if (proceed == MagickFalse)
2183 resize_view=DestroyCacheView(resize_view);
2184 image_view=DestroyCacheView(image_view);
2185 contributions=DestroyContributionThreadSet(contributions);
2189 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2190 const Image *image,Image *resize_image,const MagickRealType y_factor,
2191 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2201 **restrict contributions;
2217 Apply filter to resize vertically from image to resize image.
2219 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
2220 support=scale*GetResizeFilterSupport(resize_filter);
2221 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2222 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2224 InheritException(exception,&resize_image->exception);
2225 return(MagickFalse);
2230 Support too small even for nearest neighbour: Reduce to point
2233 support=(MagickRealType) 0.5;
2236 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2237 if (contributions == (ContributionInfo **) NULL)
2239 (void) ThrowMagickException(exception,GetMagickModule(),
2240 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2241 return(MagickFalse);
2245 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2246 image_view=AcquireCacheView(image);
2247 resize_view=AcquireCacheView(resize_image);
2248 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2249 #pragma omp parallel for shared(status)
2251 for (y=0; y < (ssize_t) resize_image->rows; y++)
2257 register const IndexPacket
2260 register const PixelPacket
2263 register ContributionInfo
2264 *restrict contribution;
2266 register IndexPacket
2267 *restrict resize_indexes;
2269 register PixelPacket
2280 if (status == MagickFalse)
2282 center=(MagickRealType) (y+0.5)/y_factor;
2283 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2284 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
2286 contribution=contributions[GetOpenMPThreadId()];
2287 for (n=0; n < (stop-start); n++)
2289 contribution[n].pixel=start+n;
2290 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2291 ((MagickRealType) (start+n)-center+0.5));
2292 density+=contribution[n].weight;
2294 if ((density != 0.0) && (density != 1.0))
2302 density=1.0/density;
2303 for (i=0; i < n; i++)
2304 contribution[i].weight*=density;
2306 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
2307 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2309 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2311 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2316 indexes=GetCacheViewVirtualIndexQueue(image_view);
2317 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2318 for (x=0; x < (ssize_t) resize_image->columns; x++)
2333 if (image->matte == MagickFalse)
2335 for (i=0; i < n; i++)
2337 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2339 alpha=contribution[i].weight;
2340 pixel.red+=alpha*(p+j)->red;
2341 pixel.green+=alpha*(p+j)->green;
2342 pixel.blue+=alpha*(p+j)->blue;
2343 pixel.opacity+=alpha*(p+j)->opacity;
2345 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2346 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2347 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2348 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2349 if ((image->colorspace == CMYKColorspace) &&
2350 (resize_image->colorspace == CMYKColorspace))
2352 for (i=0; i < n; i++)
2354 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2356 alpha=contribution[i].weight;
2357 pixel.index+=alpha*indexes[j];
2359 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
2368 for (i=0; i < n; i++)
2370 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2372 alpha=contribution[i].weight*QuantumScale*
2373 GetAlphaPixelComponent(p+j);
2374 pixel.red+=alpha*(p+j)->red;
2375 pixel.green+=alpha*(p+j)->green;
2376 pixel.blue+=alpha*(p+j)->blue;
2377 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2380 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2381 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2382 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2383 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2384 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2385 if ((image->colorspace == CMYKColorspace) &&
2386 (resize_image->colorspace == CMYKColorspace))
2388 for (i=0; i < n; i++)
2390 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2392 alpha=contribution[i].weight*QuantumScale*
2393 GetAlphaPixelComponent(p+j);
2394 pixel.index+=alpha*indexes[j];
2396 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2397 GetIndexPixelComponent(&pixel));
2400 if ((resize_image->storage_class == PseudoClass) &&
2401 (image->storage_class == PseudoClass))
2403 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
2405 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
2407 resize_indexes[x]=indexes[j];
2411 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2413 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2418 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2419 #pragma omp critical (MagickCore_VerticalFilter)
2421 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2422 if (proceed == MagickFalse)
2426 resize_view=DestroyCacheView(resize_view);
2427 image_view=DestroyCacheView(image_view);
2428 contributions=DestroyContributionThreadSet(contributions);
2432 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2433 const size_t rows,const FilterTypes filter,const double blur,
2434 ExceptionInfo *exception)
2436 #define WorkLoadFactor 0.265
2462 Acquire resize image.
2464 assert(image != (Image *) NULL);
2465 assert(image->signature == MagickSignature);
2466 if (image->debug != MagickFalse)
2467 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2468 assert(exception != (ExceptionInfo *) NULL);
2469 assert(exception->signature == MagickSignature);
2470 if ((columns == 0) || (rows == 0))
2471 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2472 if ((columns == image->columns) && (rows == image->rows) &&
2473 (filter == UndefinedFilter) && (blur == 1.0))
2474 return(CloneImage(image,0,0,MagickTrue,exception));
2475 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2476 if (resize_image == (Image *) NULL)
2477 return(resize_image);
2479 Acquire resize filter.
2481 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2482 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2483 if ((x_factor*y_factor) > WorkLoadFactor)
2484 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2486 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2487 if (filter_image == (Image *) NULL)
2488 return(DestroyImage(resize_image));
2489 filter_type=LanczosFilter;
2490 if (filter != UndefinedFilter)
2493 if ((x_factor == 1.0) && (y_factor == 1.0))
2494 filter_type=PointFilter;
2496 if ((image->storage_class == PseudoClass) ||
2497 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2498 filter_type=MitchellFilter;
2499 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2505 if ((x_factor*y_factor) > WorkLoadFactor)
2507 span=(MagickSizeType) (filter_image->columns+rows);
2508 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
2510 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
2511 span,&offset,exception);
2515 span=(MagickSizeType) (filter_image->rows+columns);
2516 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
2518 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
2519 span,&offset,exception);
2524 filter_image=DestroyImage(filter_image);
2525 resize_filter=DestroyResizeFilter(resize_filter);
2526 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2527 return((Image *) NULL);
2528 resize_image->type=image->type;
2529 return(resize_image);
2533 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2537 % S a m p l e I m a g e %
2541 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2543 % SampleImage() scales an image to the desired dimensions with pixel
2544 % sampling. Unlike other scaling methods, this method does not introduce
2545 % any additional color into the scaled image.
2547 % The format of the SampleImage method is:
2549 % Image *SampleImage(const Image *image,const size_t columns,
2550 % const size_t rows,ExceptionInfo *exception)
2552 % A description of each parameter follows:
2554 % o image: the image.
2556 % o columns: the number of columns in the sampled image.
2558 % o rows: the number of rows in the sampled image.
2560 % o exception: return any errors or warnings in this structure.
2563 MagickExport Image *SampleImage(const Image *image,const size_t columns,
2564 const size_t rows,ExceptionInfo *exception)
2566 #define SampleImageTag "Sample/Image"
2589 Initialize sampled image attributes.
2591 assert(image != (const Image *) NULL);
2592 assert(image->signature == MagickSignature);
2593 if (image->debug != MagickFalse)
2594 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2595 assert(exception != (ExceptionInfo *) NULL);
2596 assert(exception->signature == MagickSignature);
2597 if ((columns == 0) || (rows == 0))
2598 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2599 if ((columns == image->columns) && (rows == image->rows))
2600 return(CloneImage(image,0,0,MagickTrue,exception));
2601 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2602 if (sample_image == (Image *) NULL)
2603 return((Image *) NULL);
2605 Allocate scan line buffer and column offset buffers.
2607 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
2609 if (x_offset == (ssize_t *) NULL)
2611 sample_image=DestroyImage(sample_image);
2612 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2614 for (x=0; x < (ssize_t) sample_image->columns; x++)
2615 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
2616 sample_image->columns);
2622 image_view=AcquireCacheView(image);
2623 sample_view=AcquireCacheView(sample_image);
2624 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2625 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2627 for (y=0; y < (ssize_t) sample_image->rows; y++)
2629 register const IndexPacket
2632 register const PixelPacket
2635 register IndexPacket
2636 *restrict sample_indexes;
2638 register PixelPacket
2647 if (status == MagickFalse)
2649 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2650 sample_image->rows);
2651 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2653 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2655 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2660 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2661 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2665 for (x=0; x < (ssize_t) sample_image->columns; x++)
2666 *q++=p[x_offset[x]];
2667 if ((image->storage_class == PseudoClass) ||
2668 (image->colorspace == CMYKColorspace))
2669 for (x=0; x < (ssize_t) sample_image->columns; x++)
2670 sample_indexes[x]=indexes[x_offset[x]];
2671 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2673 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2678 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2679 #pragma omp critical (MagickCore_SampleImage)
2681 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2682 if (proceed == MagickFalse)
2686 image_view=DestroyCacheView(image_view);
2687 sample_view=DestroyCacheView(sample_view);
2688 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
2689 sample_image->type=image->type;
2690 return(sample_image);
2694 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2698 % S c a l e I m a g e %
2702 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2704 % ScaleImage() changes the size of an image to the given dimensions.
2706 % The format of the ScaleImage method is:
2708 % Image *ScaleImage(const Image *image,const size_t columns,
2709 % const size_t rows,ExceptionInfo *exception)
2711 % A description of each parameter follows:
2713 % o image: the image.
2715 % o columns: the number of columns in the scaled image.
2717 % o rows: the number of rows in the scaled image.
2719 % o exception: return any errors or warnings in this structure.
2722 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2723 const size_t rows,ExceptionInfo *exception)
2725 #define ScaleImageTag "Scale/Image"
2759 Initialize scaled image attributes.
2761 assert(image != (const Image *) NULL);
2762 assert(image->signature == MagickSignature);
2763 if (image->debug != MagickFalse)
2764 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2765 assert(exception != (ExceptionInfo *) NULL);
2766 assert(exception->signature == MagickSignature);
2767 if ((columns == 0) || (rows == 0))
2768 return((Image *) NULL);
2769 if ((columns == image->columns) && (rows == image->rows))
2770 return(CloneImage(image,0,0,MagickTrue,exception));
2771 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2772 if (scale_image == (Image *) NULL)
2773 return((Image *) NULL);
2774 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2776 InheritException(exception,&scale_image->exception);
2777 scale_image=DestroyImage(scale_image);
2778 return((Image *) NULL);
2783 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2786 if (image->rows != scale_image->rows)
2787 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2789 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2790 scale_image->columns,sizeof(*scale_scanline));
2791 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2793 if ((scanline == (MagickPixelPacket *) NULL) ||
2794 (scale_scanline == (MagickPixelPacket *) NULL) ||
2795 (x_vector == (MagickPixelPacket *) NULL) ||
2796 (y_vector == (MagickPixelPacket *) NULL))
2798 scale_image=DestroyImage(scale_image);
2799 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2805 next_row=MagickTrue;
2807 scale.y=(double) scale_image->rows/(double) image->rows;
2808 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2810 GetMagickPixelPacket(image,&pixel);
2811 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2813 image_view=AcquireCacheView(image);
2814 scale_view=AcquireCacheView(scale_image);
2815 for (y=0; y < (ssize_t) scale_image->rows; y++)
2817 register const IndexPacket
2820 register const PixelPacket
2823 register IndexPacket
2824 *restrict scale_indexes;
2826 register MagickPixelPacket
2830 register PixelPacket
2836 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2838 if (q == (PixelPacket *) NULL)
2840 scale_indexes=GetAuthenticIndexQueue(scale_image);
2841 if (scale_image->rows == image->rows)
2844 Read a new scanline.
2846 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2848 if (p == (const PixelPacket *) NULL)
2850 indexes=GetCacheViewVirtualIndexQueue(image_view);
2851 for (x=0; x < (ssize_t) image->columns; x++)
2853 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2854 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2855 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
2856 if (image->matte != MagickFalse)
2857 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
2858 if (indexes != (IndexPacket *) NULL)
2859 x_vector[x].index=(MagickRealType) indexes[x];
2868 while (scale.y < span.y)
2870 if ((next_row != MagickFalse) &&
2871 (number_rows < (ssize_t) image->rows))
2874 Read a new scanline.
2876 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2878 if (p == (const PixelPacket *) NULL)
2880 indexes=GetCacheViewVirtualIndexQueue(image_view);
2881 for (x=0; x < (ssize_t) image->columns; x++)
2883 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2884 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2885 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
2886 if (image->matte != MagickFalse)
2887 x_vector[x].opacity=(MagickRealType)
2888 GetOpacityPixelComponent(p);
2889 if (indexes != (IndexPacket *) NULL)
2890 x_vector[x].index=(MagickRealType) indexes[x];
2895 for (x=0; x < (ssize_t) image->columns; x++)
2897 y_vector[x].red+=scale.y*x_vector[x].red;
2898 y_vector[x].green+=scale.y*x_vector[x].green;
2899 y_vector[x].blue+=scale.y*x_vector[x].blue;
2900 if (scale_image->matte != MagickFalse)
2901 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2902 if (scale_indexes != (IndexPacket *) NULL)
2903 y_vector[x].index+=scale.y*x_vector[x].index;
2906 scale.y=(double) scale_image->rows/(double) image->rows;
2907 next_row=MagickTrue;
2909 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
2912 Read a new scanline.
2914 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2916 if (p == (const PixelPacket *) NULL)
2918 indexes=GetCacheViewVirtualIndexQueue(image_view);
2919 for (x=0; x < (ssize_t) image->columns; x++)
2921 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2922 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2923 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
2924 if (image->matte != MagickFalse)
2925 x_vector[x].opacity=(MagickRealType)
2926 GetOpacityPixelComponent(p);
2927 if (indexes != (IndexPacket *) NULL)
2928 x_vector[x].index=(MagickRealType) indexes[x];
2932 next_row=MagickFalse;
2935 for (x=0; x < (ssize_t) image->columns; x++)
2937 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
2938 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
2939 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
2940 if (image->matte != MagickFalse)
2941 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
2942 if (scale_indexes != (IndexPacket *) NULL)
2943 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
2945 s->green=pixel.green;
2947 if (scale_image->matte != MagickFalse)
2948 s->opacity=pixel.opacity;
2949 if (scale_indexes != (IndexPacket *) NULL)
2950 s->index=pixel.index;
2957 scale.y=(double) scale_image->rows/(double) image->rows;
2958 next_row=MagickTrue;
2962 if (scale_image->columns == image->columns)
2965 Transfer scanline to scaled image.
2968 for (x=0; x < (ssize_t) scale_image->columns; x++)
2970 q->red=ClampToQuantum(s->red);
2971 q->green=ClampToQuantum(s->green);
2972 q->blue=ClampToQuantum(s->blue);
2973 if (scale_image->matte != MagickFalse)
2974 q->opacity=ClampToQuantum(s->opacity);
2975 if (scale_indexes != (IndexPacket *) NULL)
2976 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
2987 next_column=MagickFalse;
2991 for (x=0; x < (ssize_t) image->columns; x++)
2993 scale.x=(double) scale_image->columns/(double) image->columns;
2994 while (scale.x >= span.x)
2996 if (next_column != MagickFalse)
3001 pixel.red+=span.x*s->red;
3002 pixel.green+=span.x*s->green;
3003 pixel.blue+=span.x*s->blue;
3004 if (image->matte != MagickFalse)
3005 pixel.opacity+=span.x*s->opacity;
3006 if (scale_indexes != (IndexPacket *) NULL)
3007 pixel.index+=span.x*s->index;
3009 t->green=pixel.green;
3011 if (scale_image->matte != MagickFalse)
3012 t->opacity=pixel.opacity;
3013 if (scale_indexes != (IndexPacket *) NULL)
3014 t->index=pixel.index;
3017 next_column=MagickTrue;
3021 if (next_column != MagickFalse)
3024 next_column=MagickFalse;
3027 pixel.red+=scale.x*s->red;
3028 pixel.green+=scale.x*s->green;
3029 pixel.blue+=scale.x*s->blue;
3030 if (scale_image->matte != MagickFalse)
3031 pixel.opacity+=scale.x*s->opacity;
3032 if (scale_indexes != (IndexPacket *) NULL)
3033 pixel.index+=scale.x*s->index;
3041 pixel.red+=span.x*s->red;
3042 pixel.green+=span.x*s->green;
3043 pixel.blue+=span.x*s->blue;
3044 if (scale_image->matte != MagickFalse)
3045 pixel.opacity+=span.x*s->opacity;
3046 if (scale_indexes != (IndexPacket *) NULL)
3047 pixel.index+=span.x*s->index;
3049 if ((next_column == MagickFalse) &&
3050 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
3053 t->green=pixel.green;
3055 if (scale_image->matte != MagickFalse)
3056 t->opacity=pixel.opacity;
3057 if (scale_indexes != (IndexPacket *) NULL)
3058 t->index=pixel.index;
3061 Transfer scanline to scaled image.
3064 for (x=0; x < (ssize_t) scale_image->columns; x++)
3066 q->red=ClampToQuantum(t->red);
3067 q->green=ClampToQuantum(t->green);
3068 q->blue=ClampToQuantum(t->blue);
3069 if (scale_image->matte != MagickFalse)
3070 q->opacity=ClampToQuantum(t->opacity);
3071 if (scale_indexes != (IndexPacket *) NULL)
3072 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
3077 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
3079 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3081 if (proceed == MagickFalse)
3084 scale_view=DestroyCacheView(scale_view);
3085 image_view=DestroyCacheView(image_view);
3087 Free allocated memory.
3089 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3090 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3091 if (scale_image->rows != image->rows)
3092 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3093 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3094 scale_image->type=image->type;
3095 return(scale_image);
3099 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3103 + S e t R e s i z e F i l t e r S u p p o r t %
3107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3109 % SetResizeFilterSupport() specifies which IR filter to use to window
3111 % The format of the SetResizeFilterSupport method is:
3113 % void SetResizeFilterSupport(ResizeFilter *resize_filter,
3114 % const MagickRealType support)
3116 % A description of each parameter follows:
3118 % o resize_filter: the resize filter.
3120 % o support: the filter spport radius.
3123 MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3124 const MagickRealType support)
3126 assert(resize_filter != (ResizeFilter *) NULL);
3127 assert(resize_filter->signature == MagickSignature);
3128 resize_filter->support=support;
3132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3136 % T h u m b n a i l I m a g e %
3140 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3142 % ThumbnailImage() changes the size of an image to the given dimensions and
3143 % removes any associated profiles. The goal is to produce small low cost
3144 % thumbnail images suited for display on the Web.
3146 % The format of the ThumbnailImage method is:
3148 % Image *ThumbnailImage(const Image *image,const size_t columns,
3149 % const size_t rows,ExceptionInfo *exception)
3151 % A description of each parameter follows:
3153 % o image: the image.
3155 % o columns: the number of columns in the scaled image.
3157 % o rows: the number of rows in the scaled image.
3159 % o exception: return any errors or warnings in this structure.
3162 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3163 const size_t rows,ExceptionInfo *exception)
3165 #define SampleFactor 5
3168 value[MaxTextExtent];
3186 assert(image != (Image *) NULL);
3187 assert(image->signature == MagickSignature);
3188 if (image->debug != MagickFalse)
3189 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3190 assert(exception != (ExceptionInfo *) NULL);
3191 assert(exception->signature == MagickSignature);
3192 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3193 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3194 if ((x_factor*y_factor) > 0.1)
3195 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3198 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
3199 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3200 image->blur,exception);
3206 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3208 if (sample_image == (Image *) NULL)
3209 return((Image *) NULL);
3210 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3211 image->blur,exception);
3212 sample_image=DestroyImage(sample_image);
3214 if (thumbnail_image == (Image *) NULL)
3215 return(thumbnail_image);
3216 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3217 if (thumbnail_image->matte == MagickFalse)
3218 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3219 thumbnail_image->depth=8;
3220 thumbnail_image->interlace=NoInterlace;
3222 Strip all profiles except color profiles.
3224 ResetImageProfileIterator(thumbnail_image);
3225 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3227 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3229 (void) DeleteImageProfile(thumbnail_image,name);
3230 ResetImageProfileIterator(thumbnail_image);
3232 name=GetNextImageProfile(thumbnail_image);
3234 (void) DeleteImageProperty(thumbnail_image,"comment");
3235 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3236 if (strstr(image->magick_filename,"//") == (char *) NULL)
3237 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
3238 image->magick_filename);
3239 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3240 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3241 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3243 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3244 attributes.st_mtime);
3245 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3247 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3248 attributes.st_mtime);
3249 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
3250 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
3251 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3252 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3254 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3255 (void) SetImageProperty(thumbnail_image,"software",
3256 GetMagickVersion(&version));
3257 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3258 image->magick_columns);
3259 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
3260 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3261 image->magick_rows);
3262 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
3263 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3264 GetImageListLength(image));
3265 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3266 return(thumbnail_image);