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 equal).
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:
250 const MagickRealType cospix = cos((double) (MagickPIL*x));
251 return(0.5+0.5*cospix);
254 static MagickRealType Hamming(const MagickRealType x,
255 const ResizeFilter *magick_unused(resize_filter))
258 Offset cosine window function:
261 const MagickRealType cospix = cos((double) (MagickPIL*x));
262 return(0.54+0.46*cospix);
265 static MagickRealType Kaiser(const MagickRealType x,
266 const ResizeFilter *magick_unused(resize_filter))
269 #define I0A (1.0/I0(Alpha))
272 Kaiser Windowing Function (bessel windowing): Alpha is a free
273 value from 5 to 8 (currently hardcoded to 6.5).
274 Future: make alpha the IOA pre-calculation, an 'expert' setting.
276 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
279 static MagickRealType Lagrange(const MagickRealType x,
280 const ResizeFilter *resize_filter)
293 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the
294 lagrange function and depends on the overall support window size
295 of the filter. That is: for a support of 2, it gives a lagrange-4
296 (piecewise cubic function).
298 "n" identifies the piece of the piecewise polynomial.
300 See Survey: Interpolation Methods, IEEE Transactions on Medical
301 Imaging, Vol 18, No 11, November 1999, p1049-1075, -- Equation 27
304 if (x > resize_filter->support)
306 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
307 /*n=(ssize_t)((1.0*order)/2.0+x); -- which piece does x belong to */
308 n = (ssize_t)(resize_filter->window_support + x);
310 for (i=0; i < order; i++)
312 value*=(n-i-x)/(n-i);
316 static MagickRealType Quadratic(const MagickRealType x,
317 const ResizeFilter *magick_unused(resize_filter))
320 2rd order (quadratic) B-Spline approximation of Gaussian.
325 return(0.5*(x-1.5)*(x-1.5));
329 static MagickRealType Sinc(const MagickRealType x,
330 const ResizeFilter *magick_unused(resize_filter))
333 Scaled sinc(x) function using a trig call:
334 sinc(x) == sin(pi x)/(pi x).
338 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
339 return(sin((double) pix)/pix);
341 return((MagickRealType) 1.0);
344 static MagickRealType SincFast(const MagickRealType x,
345 const ResizeFilter *magick_unused(resize_filter))
348 Approximations of the sinc function sin(pi x)/(pi x) over the
349 interval [-4,4] constructed by Nicolas Robidoux and Chantal
350 Racette with funding from the Natural Sciences and Engineering
351 Research Council of Canada.
353 Although the approximations are polynomials (for low order of
354 approximation) and quotients of polynomials (for higher order of
355 approximation) and consequently are similar in form to Taylor
356 polynomials/Pade approximants, the approximations are computed
357 with a completely different technique.
359 Summary: These approximations are "the best" in terms of bang
360 (accuracy) for the buck (flops). More specifically: Among the
361 polynomial quotients that can be computed using a fixed number of
362 flops (with a given "+ - * / budget"), the chosen polynomial
363 quotient is the one closest to the approximated function with
364 respect to maximum absolute relative error over the given
367 The Remez algorithm, as implemented in the boost library's minimax
368 package, is the key to the construction:
369 http://www.boost.org/doc/libs/1_36_0/libs/math/doc/...
370 ...sf_and_dist/html/math_toolkit/backgrounders/remez.html
373 If outside of the interval of approximation, use the standard trig
378 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
379 return(sin((double) pix)/pix);
383 The approximations only depend on x^2 (sinc is an even
386 const MagickRealType xx = x*x;
387 #if MAGICKCORE_QUANTUM_DEPTH <= 8
389 Maximum absolute relative error 6.3e-6 < 1/2^17.
391 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
392 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
393 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
394 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
395 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
396 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
397 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
398 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
399 const MagickRealType p =
400 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
401 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
402 #elif MAGICKCORE_QUANTUM_DEPTH <= 16
404 Max. abs. rel. error 2.2e-8 < 1/2^25.
406 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
407 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
408 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
409 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
410 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
411 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
412 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
413 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
414 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
415 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
416 const MagickRealType p =
417 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
418 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
421 Max. abs. rel. error 1.2e-12 < 1/2^39.
423 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
424 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
425 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
426 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
427 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
428 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
429 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
430 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
431 const MagickRealType p =
432 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
433 const MagickRealType d0 = 1.0L;
434 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
435 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
436 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
437 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
438 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
439 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
444 static MagickRealType Triangle(const MagickRealType x,
445 const ResizeFilter *magick_unused(resize_filter))
448 1st order (linear) B-Spline, bilinear interpolation, Tent 1D
449 filter, or a Bartlett 2D Cone filter.
456 static MagickRealType Welsh(const MagickRealType x,
457 const ResizeFilter *magick_unused(resize_filter))
460 Welsh parabolic windowing filter.
468 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
472 + A c q u i r e R e s i z e F i l t e r %
476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
478 % AcquireResizeFilter() allocates the ResizeFilter structure. Choose
479 % from these filters:
481 % FIR (Finite impulse Response) Filters
482 % Box Triangle Quadratic
483 % Cubic Hermite Catrom
486 % IIR (Infinite impulse Response) Filters
487 % Gaussian Sinc Bessel
489 % Windowed Sinc/Bessel Method
490 % Blackman Hanning Hamming
491 % Kaiser Lanczos (Sinc)
493 % FIR filters are used as is, and are limited by that filters support
494 % window (unless over-ridden). 'Gaussian' while classed as an IIR
495 % filter, is also simply clipped by its support size (currently 1.5
496 % but probably should be 2.0 for better use from EWA resampling)
498 % The users "-filter" selection ise used to lookup the default
499 % 'expert' settings for that filter from a internal table. However
500 % any provided 'expert' settings (see below) may override this
503 % The selection is typically either a windowed sinc, or interpolated
504 % filter, for use by functions such as ResizeImage(). However if a
505 % 'cylindrical' filter flag is requested, the default sinc weighting
506 % and windowing functions will be promoted to cylindrical Bessel
509 % Directly requesting 'Sinc' or 'Bessel' will force the use of that
510 % filter function, with a default 'Blackman' windowing method. This
511 % not however recommended as it removes the correct filter selection
512 % for different filtering image operations. Selecting a window
513 % filtering method is better.
515 % Lanczos is a special case of a sinc-windowed sinc, but defaulting
516 % to 3-lobe support, rather that the default 4 lobe support of the
517 % windowed sinc filters.
519 % Two forms of the sinc function are available: Sinc and SincFast.
520 % Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
521 % selected if the user specifically specifies the use of a Sinc
522 % filter. SincFast uses highly accurate (and fast) polynomial (low Q)
523 % and rational (high Q) approximations and is used by default in most
526 % Special 'expert' options can be used to override any and all filter
527 % settings. This is not advised unless you have expert knowledge of
528 % the use of resampling filtered techniques. Check on the results of
529 % your selections using the "filter:verbose" setting to make sure you
530 % got what you requested.
532 % "filter:filter" Select the function associated with this filter
533 % as the weighting function of the filter. This can be used to set a
534 % a windowing function as a weighting function.
536 % If a "filter:window" operation has not been provided, then a 'Box'
537 % windowing function will be set to denote that no windowing function
540 % "filter:window" Select this windowing function for the filter.
541 % While any filter could be used as a windowing function, using the
542 % 'first lobe' of that filter over the whole support window, using a
543 % non-windowing function is not advisible. If no weighting function
544 % is specifed a 'SincFast' filter will be used.
546 % "filter:lobes" Number of lobes to use for the Sinc/Bessel filter.
547 % This a simpler method of setting filter support size that will
548 % correctly handle the Sinc/Bessel switch for an operators filtering
549 % requirements. Only integers should be given.
551 % "filter:support" Set the support size for filtering to the size given
552 % This not recommended for Sinc/Bessel windowed filters (lobes should
553 % be used instead). This will override any 'filter:lobes' option.
555 % "filter:win-support" Scale windowing function to this size instead.
556 % This causes the windowing (or self-windowing Lagrange filter) to act
557 % is if the support window it much much larger than what is actually
558 % supplied to the calling operator. The filter however is still
559 % clipped to the real support size given, by the support range suppiled
560 % to the caller. If unset this will equal the normal filter support
563 % "filter:blur" Scale the filter and support window by this amount.
564 % A value >1 will generally result in a more burred image with
565 % more ringing effects, while a value <1 will sharpen the
566 % resulting image with more aliasing and Morie effects.
569 % "filter:c" Override the preset B,C values for a Cubic type of filter
570 % If only one of these are given it is assumes to be a 'Keys'
571 % type of filter such that B+2C=1, where Keys 'alpha' value = C
573 % "filter:verbose" Output the exact results of the filter selections
574 % made, as well as plotting data for graphing the resulting filter
575 % over support range (blur adjusted).
577 % Set a true un-windowed Sinc filter with 10 lobes (very slow)
578 % -set option:filter:filter Sinc
579 % -set option:filter:lobes 8
581 % For example force an 8 lobe Lanczos (Sinc or Bessel) filter...
583 % -set option:filter:lobes 8
586 % The format of the AcquireResizeFilter method is:
588 % ResizeFilter *AcquireResizeFilter(const Image *image,
589 % const FilterTypes filter_type, const MagickBooleanType radial,
590 % ExceptionInfo *exception)
592 % A description of each parameter follows:
594 % o image: the image.
596 % o filter: the filter type, defining a preset filter, window and
597 % support. The artifact settings listed above will override
600 % o blur: blur the filter by this amount, use 1.0 if unknown.
601 % Image artifact "filter:blur" will override this internal usage.
603 % o radial: 1D orthogonal filter (Sinc) or 2D radial filter (Bessel)
605 % o exception: return any errors or warnings in this structure.
608 MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
609 const FilterTypes filter,const MagickRealType blur,
610 const MagickBooleanType cylindrical,ExceptionInfo *exception)
623 register ResizeFilter
630 Table Mapping given Filter, into Weighting and Windowing
632 A 'Box' windowing function means its a simble non-windowed filter.
633 A 'Sinc' filter function (must be windowed) could be upgraded to a
634 'Bessel' filter if a "cylindrical" filter is requested, unless a
635 "Sinc" filter specifically request.
637 WARNING: The order of this tabel must match the order of the
638 FilterTypes enumeration specified in "resample.h", or the filter
639 names will not match the filter being setup.
641 You can check filter setups with the "filter:verbose" setting.
648 } const mapping[SentinelFilter] =
650 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
651 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
652 { BoxFilter, BoxFilter }, /* Box averaging filter */
653 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
654 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
655 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
656 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
657 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
658 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
659 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
660 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
661 { CatromFilter, BoxFilter }, /* Cubic interpolator */
662 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
663 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
664 { BesselFilter, BoxFilter }, /* Raw 3-lobed Bessel */
665 { SincFilter, BoxFilter }, /* Raw 4-lobed sinc */
666 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
667 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
668 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
669 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
670 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
671 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
672 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
675 Table mapping the filter/window from the above table to an actual
676 function. The default support size for that filter as a weighting
677 function, the range to scale with to use that function as a sinc
678 windowing function, (typ 1.0).
680 Note that the filter_type -> function is 1 to 1 except for Sinc(),
681 SincFast(), and CubicBC() functions, which may have multiple
682 filter to function associations.
684 See "filter:verbose" handling below for the function -> filter
690 (*function)(const MagickRealType, const ResizeFilter*),
691 support, /* default support size for function as a weighting filter */
692 scale, /* windowing function range, for scaling windowing function */
693 B,C; /* Cubic Filter factors for a CubicBC function, else ignored */
694 } const filters[SentinelFilter] =
696 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
697 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
698 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
699 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
700 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
701 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
702 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
703 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
704 { Gaussian, 1.5, 1.5, 0.0, 0.0 }, /* Gaussian */
705 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
706 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
707 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
708 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
709 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed sinc-sinc */
710 { Bessel, 3.2383, 1.2197, 0.0, 0.0 }, /* Raw 3-lobed Bessel */
711 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed sinc */
712 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
713 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
714 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
715 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
716 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
717 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
718 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
721 The known zero crossings of the Bessel() or the Jinc(x*PI)
722 function found by using
723 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp.
724 For Jv-function with v=1, divide X-roots by PI (tabled below).
726 static MagickRealType
748 Allocate resize filter.
750 assert(image != (const Image *) NULL);
751 assert(image->signature == MagickSignature);
752 if (image->debug != MagickFalse)
753 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
754 assert(UndefinedFilter < filter && filter < SentinelFilter);
755 assert(exception != (ExceptionInfo *) NULL);
756 assert(exception->signature == MagickSignature);
757 resize_filter=(ResizeFilter *) AcquireAlignedMemory(1,sizeof(*resize_filter));
758 if (resize_filter == (ResizeFilter *) NULL)
759 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
761 Defaults for the requested filter.
763 filter_type=mapping[filter].filter;
764 window_type=mapping[filter].window;
765 /* Cylindrical Filters should use Bessel instead of Sinc */
766 if (cylindrical != MagickFalse)
770 /* Promote 1D Sinc Filter to a 2D Bessel filter. */
771 if ( filter != SincFilter )
772 filter_type=BesselFilter;
775 /* Ditto for SincFast variant */
776 if ( filter != SincFastFilter )
777 filter_type=BesselFilter;
780 /* Promote Lanczos from a Sinc-Sinc to a Bessel-Bessel */
781 filter_type=BesselFilter;
782 window_type=BesselFilter;
786 What about other filters to make them 'cylindrical
787 friendly'? For example Mitchell is actually quite close to
788 a cylindrical Lanczos (Bessel-Bessel) with support 2. Are
789 there other well known 'cylindrical' specific filters?
793 artifact=GetImageArtifact(image,"filter:filter");
794 if (artifact != (const char *) NULL)
796 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
797 if ((UndefinedFilter < option) && (option < SentinelFilter))
799 /* Raw filter request - no window function. */
800 filter_type=(FilterTypes) option;
801 window_type=BoxFilter;
803 if (option == LanczosFilter)
805 /* Lanczos is not a real filter but a self windowing Sinc/Bessel. */
806 filter_type=cylindrical != MagickFalse ? BesselFilter : LanczosFilter;
807 window_type=cylindrical != MagickFalse ? BesselFilter :
810 /* Filter override with a specific window function. */
811 artifact=GetImageArtifact(image,"filter:window");
812 if (artifact != (const char *) NULL)
814 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
815 if ((UndefinedFilter < option) && (option < SentinelFilter))
817 if (option != LanczosFilter)
818 window_type=(FilterTypes) option;
820 window_type=cylindrical != MagickFalse ? BesselFilter :
827 /* Window specified, but no filter function? Assume Sinc/Bessel. */
828 artifact=GetImageArtifact(image,"filter:window");
829 if (artifact != (const char *) NULL)
831 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
833 if ((UndefinedFilter < option) && (option < SentinelFilter))
835 filter_type=cylindrical != MagickFalse ? BesselFilter :
837 window_type=(FilterTypes) option;
841 /* Assign the real functions to use for the filters selected. */
842 resize_filter->filter=filters[filter_type].function;
843 resize_filter->support=filters[filter_type].support;
844 resize_filter->window=filters[window_type].function;
845 resize_filter->scale=filters[window_type].scale;
846 resize_filter->signature=MagickSignature;
847 /* Filter blur -- scaling both filter and support window. */
848 resize_filter->blur=blur;
849 artifact=GetImageArtifact(image,"filter:blur");
850 if (artifact != (const char *) NULL)
851 resize_filter->blur=StringToDouble(artifact);
852 if (resize_filter->blur < MagickEpsilon)
853 resize_filter->blur=(MagickRealType) MagickEpsilon;
855 if (cylindrical != MagickFalse)
860 /* Support for Cylindrical Box should be sqrt(2)/2 */
861 resize_filter->support=MagickSQ1_2;
864 /* Cylindrical Gaussian should have a sigma of sqrt(2)/2
865 * and not the default sigma of 1/2 - so use blur to enlarge
866 * and adjust support so actual practical support = 2.0 by default
868 resize_filter->blur *= MagickSQ2;
869 resize_filter->support = MagickSQ2; /* which times blur => 2.0 */
874 /* Filter support overrides. */
875 artifact=GetImageArtifact(image,"filter:lobes");
876 if (artifact != (const char *) NULL)
881 lobes=(ssize_t) StringToLong(artifact);
884 resize_filter->support=(MagickRealType) lobes;
885 if (filter_type == BesselFilter)
889 resize_filter->support=bessel_zeros[lobes-1];
892 artifact=GetImageArtifact(image,"filter:support");
893 if (artifact != (const char *) NULL)
894 resize_filter->support=fabs(StringToDouble(artifact));
896 Scale windowing function separatally to the support 'clipping'
897 window that calling operator is planning to actually use. (Expert
900 resize_filter->window_support=resize_filter->support; /* default */
901 artifact=GetImageArtifact(image,"filter:win-support");
902 if (artifact != (const char *) NULL)
903 resize_filter->window_support=fabs(StringToDouble(artifact));
905 Adjust window function scaling to the windowing support for
906 weighting function. This avoids a division on every filter call.
908 resize_filter->scale /= resize_filter->window_support;
910 Set Cubic Spline B,C values, calculate Cubic coefficients.
914 if ((filters[filter_type].function == CubicBC) ||
915 (filters[window_type].function == CubicBC))
917 B=filters[filter_type].B;
918 C=filters[filter_type].C;
919 if (filters[window_type].function == CubicBC)
921 B=filters[window_type].B;
922 C=filters[window_type].C;
924 artifact=GetImageArtifact(image,"filter:b");
925 if (artifact != (const char *) NULL)
927 B=StringToDouble(artifact);
928 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
929 artifact=GetImageArtifact(image,"filter:c");
930 if (artifact != (const char *) NULL)
931 C=StringToDouble(artifact);
935 artifact=GetImageArtifact(image,"filter:c");
936 if (artifact != (const char *) NULL)
938 C=StringToDouble(artifact);
939 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
943 Convert B,C values into Cubic Coefficents. See CubicBC().
945 resize_filter->cubic[0]=(6.0-2.0*B)/6.0;
946 resize_filter->cubic[1]=0.0;
947 resize_filter->cubic[2]=(-18.0+12.0*B+6.0*C)/6.0;
948 resize_filter->cubic[3]=(12.0-9.0*B-6.0*C)/6.0;
949 resize_filter->cubic[4]=(8.0*B+24.0*C)/6.0;
950 resize_filter->cubic[5]=(-12.0*B-48.0*C)/6.0;
951 resize_filter->cubic[6]=(6.0*B+30.0*C)/6.0;
952 resize_filter->cubic[7]=(-B-6.0*C)/6.0;
955 Expert Option Request for verbose details of the resulting filter.
957 #if defined(MAGICKCORE_OPENMP_SUPPORT)
958 /* if( GetOpenMPThreadId() == 0 ) { */
960 artifact=GetImageArtifact(image,"filter:verbose");
961 if (artifact != (const char *) NULL)
968 Set the weighting function properly when the weighting
969 function may not exactly match the filter of the same name.
970 EG: a Point filter really uses a Box weighting function
971 with a different support than is typically used.
974 if (resize_filter->filter == Box) filter_type=BoxFilter;
975 if (resize_filter->filter == Sinc) filter_type=SincFilter;
976 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
977 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
979 Report Filter Details.
981 support=GetResizeFilterSupport(resize_filter); /* support range */
982 (void) fprintf(stdout,"#\n# Resize Filter (for graphing)\n#\n");
983 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
984 MagickFilterOptions,filter_type));
985 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
986 MagickFilterOptions, window_type));
987 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
988 (double) resize_filter->support);
989 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
990 (double) resize_filter->window_support);
991 (void) fprintf(stdout,"# blur = %.*g\n",GetMagickPrecision(),
992 (double) resize_filter->blur);
993 (void) fprintf(stdout,"# blurred_support = %.*g\n",GetMagickPrecision(),
995 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
996 (double) B,GetMagickPrecision(),(double) C);
997 (void) fprintf(stdout,"#\n");
999 Output values of resulting filter graph -- for graphing
1002 for (x=0.0; x <= support; x+=0.01f)
1003 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1004 (double) GetResizeFilterWeight(resize_filter,x));
1005 /* A final value so gnuplot can graph the 'stop' properly. */
1006 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1009 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1012 return(resize_filter);
1016 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1020 % A d a p t i v e R e s i z e I m a g e %
1024 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1026 % AdaptiveResizeImage() adaptively resize image with pixel resampling.
1028 % The format of the AdaptiveResizeImage method is:
1030 % Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1031 % const size_t rows,ExceptionInfo *exception)
1033 % A description of each parameter follows:
1035 % o image: the image.
1037 % o columns: the number of columns in the resized image.
1039 % o rows: the number of rows in the resized image.
1041 % o exception: return any errors or warnings in this structure.
1044 MagickExport Image *AdaptiveResizeImage(const Image *image,
1045 const size_t columns,const size_t rows,ExceptionInfo *exception)
1047 #define AdaptiveResizeImageTag "Resize/Image"
1071 Adaptively resize image.
1073 assert(image != (const Image *) NULL);
1074 assert(image->signature == MagickSignature);
1075 if (image->debug != MagickFalse)
1076 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1077 assert(exception != (ExceptionInfo *) NULL);
1078 assert(exception->signature == MagickSignature);
1079 if ((columns == 0) || (rows == 0))
1080 return((Image *) NULL);
1081 if ((columns == image->columns) && (rows == image->rows))
1082 return(CloneImage(image,0,0,MagickTrue,exception));
1083 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1084 if (resize_image == (Image *) NULL)
1085 return((Image *) NULL);
1086 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1088 InheritException(exception,&resize_image->exception);
1089 resize_image=DestroyImage(resize_image);
1090 return((Image *) NULL);
1092 GetMagickPixelPacket(image,&pixel);
1093 resample_filter=AcquireResampleFilter(image,exception);
1094 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
1095 (void) SetResampleFilterInterpolateMethod(resample_filter,
1096 MeshInterpolatePixel);
1097 resize_view=AcquireCacheView(resize_image);
1098 for (y=0; y < (ssize_t) resize_image->rows; y++)
1100 register IndexPacket
1101 *restrict resize_indexes;
1106 register PixelPacket
1109 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1111 if (q == (PixelPacket *) NULL)
1113 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1114 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
1115 for (x=0; x < (ssize_t) resize_image->columns; x++)
1117 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1118 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1120 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1123 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1125 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1127 if (proceed == MagickFalse)
1130 resample_filter=DestroyResampleFilter(resample_filter);
1131 resize_view=DestroyCacheView(resize_view);
1132 return(resize_image);
1136 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1140 + B e s s e l O r d e r O n e %
1144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1146 % BesselOrderOne() computes the Bessel function of x of the first kind of
1149 % Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1155 % j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1157 % where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1159 % cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1160 % = 1/sqrt(2) * (sin(x) - cos(x))
1161 % sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1162 % = -1/sqrt(2) * (sin(x) + cos(x))
1164 % The format of the BesselOrderOne method is:
1166 % MagickRealType BesselOrderOne(MagickRealType x)
1168 % A description of each parameter follows:
1170 % o x: MagickRealType value.
1175 static MagickRealType I0(MagickRealType x)
1186 Zeroth order Bessel function of the first kind.
1191 for (i=2; t > MagickEpsilon; i++)
1194 t*=y/((MagickRealType) i*i);
1200 static MagickRealType J1(MagickRealType x)
1212 0.581199354001606143928050809e+21,
1213 -0.6672106568924916298020941484e+20,
1214 0.2316433580634002297931815435e+19,
1215 -0.3588817569910106050743641413e+17,
1216 0.2908795263834775409737601689e+15,
1217 -0.1322983480332126453125473247e+13,
1218 0.3413234182301700539091292655e+10,
1219 -0.4695753530642995859767162166e+7,
1220 0.270112271089232341485679099e+4
1224 0.11623987080032122878585294e+22,
1225 0.1185770712190320999837113348e+20,
1226 0.6092061398917521746105196863e+17,
1227 0.2081661221307607351240184229e+15,
1228 0.5243710262167649715406728642e+12,
1229 0.1013863514358673989967045588e+10,
1230 0.1501793594998585505921097578e+7,
1231 0.1606931573481487801970916749e+4,
1237 for (i=7; i >= 0; i--)
1246 static MagickRealType P1(MagickRealType x)
1258 0.352246649133679798341724373e+5,
1259 0.62758845247161281269005675e+5,
1260 0.313539631109159574238669888e+5,
1261 0.49854832060594338434500455e+4,
1262 0.2111529182853962382105718e+3,
1263 0.12571716929145341558495e+1
1267 0.352246649133679798068390431e+5,
1268 0.626943469593560511888833731e+5,
1269 0.312404063819041039923015703e+5,
1270 0.4930396490181088979386097e+4,
1271 0.2030775189134759322293574e+3,
1277 for (i=4; i >= 0; i--)
1279 p=p*(8.0/x)*(8.0/x)+Pone[i];
1280 q=q*(8.0/x)*(8.0/x)+Qone[i];
1286 static MagickRealType Q1(MagickRealType x)
1298 0.3511751914303552822533318e+3,
1299 0.7210391804904475039280863e+3,
1300 0.4259873011654442389886993e+3,
1301 0.831898957673850827325226e+2,
1302 0.45681716295512267064405e+1,
1303 0.3532840052740123642735e-1
1307 0.74917374171809127714519505e+4,
1308 0.154141773392650970499848051e+5,
1309 0.91522317015169922705904727e+4,
1310 0.18111867005523513506724158e+4,
1311 0.1038187585462133728776636e+3,
1317 for (i=4; i >= 0; i--)
1319 p=p*(8.0/x)*(8.0/x)+Pone[i];
1320 q=q*(8.0/x)*(8.0/x)+Qone[i];
1325 static MagickRealType BesselOrderOne(MagickRealType x)
1338 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1339 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1347 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1351 + D e s t r o y R e s i z e F i l t e r %
1355 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1357 % DestroyResizeFilter() destroy the resize filter.
1359 % The format of the DestroyResizeFilter method is:
1361 % ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1363 % A description of each parameter follows:
1365 % o resize_filter: the resize filter.
1368 MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1370 assert(resize_filter != (ResizeFilter *) NULL);
1371 assert(resize_filter->signature == MagickSignature);
1372 resize_filter->signature=(~MagickSignature);
1373 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1374 return(resize_filter);
1378 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1382 + G e t R e s i z e F i l t e r S u p p o r t %
1386 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1388 % GetResizeFilterSupport() return the current support window size for this
1389 % filter. Note that this may have been enlarged by filter:blur factor.
1391 % The format of the GetResizeFilterSupport method is:
1393 % MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1395 % A description of each parameter follows:
1397 % o filter: Image filter to use.
1400 MagickExport MagickRealType GetResizeFilterSupport(
1401 const ResizeFilter *resize_filter)
1403 assert(resize_filter != (ResizeFilter *) NULL);
1404 assert(resize_filter->signature == MagickSignature);
1405 return(resize_filter->support*resize_filter->blur);
1409 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1413 + G e t R e s i z e F i l t e r W e i g h t %
1417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1419 % GetResizeFilterWeight evaluates the specified resize filter at the point x
1420 % which usally lies between zero and the filters current 'support' and
1421 % returns the weight of the filter function at that point.
1423 % The format of the GetResizeFilterWeight method is:
1425 % MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1426 % const MagickRealType x)
1428 % A description of each parameter follows:
1430 % o filter: the filter type.
1435 MagickExport MagickRealType GetResizeFilterWeight(
1436 const ResizeFilter *resize_filter,const MagickRealType x)
1443 Windowing function - scale the weighting filter by this amount.
1445 assert(resize_filter != (ResizeFilter *) NULL);
1446 assert(resize_filter->signature == MagickSignature);
1447 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
1448 if ((resize_filter->window_support < MagickEpsilon) ||
1449 (resize_filter->window == Box))
1450 scale=1.0; /* Point or Box Filter -- avoid division by zero */
1453 scale=resize_filter->scale;
1454 scale=resize_filter->window(x_blur*scale,resize_filter);
1456 return(scale*resize_filter->filter(x_blur,resize_filter));
1460 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1464 % M a g n i f y I m a g e %
1468 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1470 % MagnifyImage() is a convenience method that scales an image proportionally
1471 % to twice its size.
1473 % The format of the MagnifyImage method is:
1475 % Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1477 % A description of each parameter follows:
1479 % o image: the image.
1481 % o exception: return any errors or warnings in this structure.
1484 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1489 assert(image != (Image *) NULL);
1490 assert(image->signature == MagickSignature);
1491 if (image->debug != MagickFalse)
1492 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1493 assert(exception != (ExceptionInfo *) NULL);
1494 assert(exception->signature == MagickSignature);
1495 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1497 return(magnify_image);
1501 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1505 % M i n i f y I m a g e %
1509 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1511 % MinifyImage() is a convenience method that scales an image proportionally
1514 % The format of the MinifyImage method is:
1516 % Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1518 % A description of each parameter follows:
1520 % o image: the image.
1522 % o exception: return any errors or warnings in this structure.
1525 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1530 assert(image != (Image *) NULL);
1531 assert(image->signature == MagickSignature);
1532 if (image->debug != MagickFalse)
1533 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1534 assert(exception != (ExceptionInfo *) NULL);
1535 assert(exception->signature == MagickSignature);
1536 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1538 return(minify_image);
1542 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1546 % R e s a m p l e I m a g e %
1550 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1552 % ResampleImage() resize image in terms of its pixel size, so that when
1553 % displayed at the given resolution it will be the same size in terms of
1554 % real world units as the original image at the original resolution.
1556 % The format of the ResampleImage method is:
1558 % Image *ResampleImage(Image *image,const double x_resolution,
1559 % const double y_resolution,const FilterTypes filter,const double blur,
1560 % ExceptionInfo *exception)
1562 % A description of each parameter follows:
1564 % o image: the image to be resized to fit the given resolution.
1566 % o x_resolution: the new image x resolution.
1568 % o y_resolution: the new image y resolution.
1570 % o filter: Image filter to use.
1572 % o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1575 MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1576 const double y_resolution,const FilterTypes filter,const double blur,
1577 ExceptionInfo *exception)
1579 #define ResampleImageTag "Resample/Image"
1589 Initialize sampled image attributes.
1591 assert(image != (const Image *) NULL);
1592 assert(image->signature == MagickSignature);
1593 if (image->debug != MagickFalse)
1594 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1595 assert(exception != (ExceptionInfo *) NULL);
1596 assert(exception->signature == MagickSignature);
1597 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1598 72.0 : image->x_resolution)+0.5);
1599 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1600 72.0 : image->y_resolution)+0.5);
1601 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1602 if (resample_image != (Image *) NULL)
1604 resample_image->x_resolution=x_resolution;
1605 resample_image->y_resolution=y_resolution;
1607 return(resample_image);
1609 #if defined(MAGICKCORE_LQR_DELEGATE)
1612 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1616 % L i q u i d R e s c a l e I m a g e %
1620 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1622 % LiquidRescaleImage() rescales image with seam carving.
1624 % The format of the LiquidRescaleImage method is:
1626 % Image *LiquidRescaleImage(const Image *image,
1627 % const size_t columns,const size_t rows,
1628 % const double delta_x,const double rigidity,ExceptionInfo *exception)
1630 % A description of each parameter follows:
1632 % o image: the image.
1634 % o columns: the number of columns in the rescaled image.
1636 % o rows: the number of rows in the rescaled image.
1638 % o delta_x: maximum seam transversal step (0 means straight seams).
1640 % o rigidity: introduce a bias for non-straight seams (typically 0).
1642 % o exception: return any errors or warnings in this structure.
1645 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1646 const size_t rows,const double delta_x,const double rigidity,
1647 ExceptionInfo *exception)
1649 #define LiquidRescaleImageTag "Rescale/Image"
1683 Liquid rescale image.
1685 assert(image != (const Image *) NULL);
1686 assert(image->signature == MagickSignature);
1687 if (image->debug != MagickFalse)
1688 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1689 assert(exception != (ExceptionInfo *) NULL);
1690 assert(exception->signature == MagickSignature);
1691 if ((columns == 0) || (rows == 0))
1692 return((Image *) NULL);
1693 if ((columns == image->columns) && (rows == image->rows))
1694 return(CloneImage(image,0,0,MagickTrue,exception));
1695 if ((columns <= 2) || (rows <= 2))
1696 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
1697 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1707 Honor liquid resize size limitations.
1709 for (width=image->columns; columns >= (2*width-1); width*=2);
1710 for (height=image->rows; rows >= (2*height-1); height*=2);
1711 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1713 if (resize_image == (Image *) NULL)
1714 return((Image *) NULL);
1715 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1716 rigidity,exception);
1717 resize_image=DestroyImage(resize_image);
1718 return(rescale_image);
1721 if (image->matte == MagickFalse)
1723 if (image->colorspace == CMYKColorspace)
1726 if (image->matte == MagickFalse)
1729 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1730 strlen(map)*sizeof(*pixels));
1731 if (pixels == (unsigned char *) NULL)
1732 return((Image *) NULL);
1733 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1735 if (status == MagickFalse)
1737 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1738 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1740 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1741 if (carver == (LqrCarver *) NULL)
1743 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1744 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1746 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1747 lqr_status=lqr_carver_resize(carver,columns,rows);
1748 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1749 lqr_carver_get_height(carver),MagickTrue,exception);
1750 if (rescale_image == (Image *) NULL)
1752 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1753 return((Image *) NULL);
1755 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1757 InheritException(exception,&rescale_image->exception);
1758 rescale_image=DestroyImage(rescale_image);
1759 return((Image *) NULL);
1761 GetMagickPixelPacket(rescale_image,&pixel);
1762 (void) lqr_carver_scan_reset(carver);
1763 rescale_view=AcquireCacheView(rescale_image);
1764 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1766 register IndexPacket
1767 *restrict rescale_indexes;
1769 register PixelPacket
1772 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
1773 if (q == (PixelPacket *) NULL)
1775 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
1776 pixel.red=QuantumRange*(packet[0]/255.0);
1777 pixel.green=QuantumRange*(packet[1]/255.0);
1778 pixel.blue=QuantumRange*(packet[2]/255.0);
1779 if (image->colorspace != CMYKColorspace)
1781 if (image->matte == MagickFalse)
1782 pixel.opacity=QuantumRange*(packet[3]/255.0);
1786 pixel.index=QuantumRange*(packet[3]/255.0);
1787 if (image->matte == MagickFalse)
1788 pixel.opacity=QuantumRange*(packet[4]/255.0);
1790 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
1791 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
1794 rescale_view=DestroyCacheView(rescale_view);
1796 Relinquish resources.
1798 lqr_carver_destroy(carver);
1799 return(rescale_image);
1802 MagickExport Image *LiquidRescaleImage(const Image *image,
1803 const size_t magick_unused(columns),const size_t magick_unused(rows),
1804 const double magick_unused(delta_x),const double magick_unused(rigidity),
1805 ExceptionInfo *exception)
1807 assert(image != (const Image *) NULL);
1808 assert(image->signature == MagickSignature);
1809 if (image->debug != MagickFalse)
1810 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1811 assert(exception != (ExceptionInfo *) NULL);
1812 assert(exception->signature == MagickSignature);
1813 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1814 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1815 return((Image *) NULL);
1820 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1824 % R e s i z e I m a g e %
1828 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1830 % ResizeImage() scales an image to the desired dimensions, using the given
1831 % filter (see AcquireFilterInfo()).
1833 % If an undefined filter is given the filter defaults to Mitchell for a
1834 % colormapped image, a image with a matte channel, or if the image is
1835 % enlarged. Otherwise the filter defaults to a Lanczos.
1837 % ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1839 % The format of the ResizeImage method is:
1841 % Image *ResizeImage(Image *image,const size_t columns,
1842 % const size_t rows,const FilterTypes filter,const double blur,
1843 % ExceptionInfo *exception)
1845 % A description of each parameter follows:
1847 % o image: the image.
1849 % o columns: the number of columns in the scaled image.
1851 % o rows: the number of rows in the scaled image.
1853 % o filter: Image filter to use.
1855 % o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1858 % o exception: return any errors or warnings in this structure.
1862 typedef struct _ContributionInfo
1871 static ContributionInfo **DestroyContributionThreadSet(
1872 ContributionInfo **contribution)
1877 assert(contribution != (ContributionInfo **) NULL);
1878 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
1879 if (contribution[i] != (ContributionInfo *) NULL)
1880 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1882 contribution=(ContributionInfo **) RelinquishAlignedMemory(contribution);
1883 return(contribution);
1886 static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1897 number_threads=GetOpenMPMaximumThreads();
1898 contribution=(ContributionInfo **) AcquireAlignedMemory(number_threads,
1899 sizeof(*contribution));
1900 if (contribution == (ContributionInfo **) NULL)
1901 return((ContributionInfo **) NULL);
1902 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
1903 for (i=0; i < (ssize_t) number_threads; i++)
1905 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1906 sizeof(**contribution));
1907 if (contribution[i] == (ContributionInfo *) NULL)
1908 return(DestroyContributionThreadSet(contribution));
1910 return(contribution);
1913 static inline double MagickMax(const double x,const double y)
1920 static inline double MagickMin(const double x,const double y)
1927 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
1928 const Image *image,Image *resize_image,const MagickRealType x_factor,
1929 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
1931 #define ResizeImageTag "Resize/Image"
1941 **restrict contributions;
1957 Apply filter to resize horizontally from image to resize image.
1959 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
1960 support=scale*GetResizeFilterSupport(resize_filter);
1961 storage_class=support > 0.5 ? DirectClass : image->storage_class;
1962 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
1964 InheritException(exception,&resize_image->exception);
1965 return(MagickFalse);
1970 Support too small even for nearest neighbour: Reduce to point
1973 support=(MagickRealType) 0.5;
1976 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
1977 if (contributions == (ContributionInfo **) NULL)
1979 (void) ThrowMagickException(exception,GetMagickModule(),
1980 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
1981 return(MagickFalse);
1985 (void) ResetMagickMemory(&zero,0,sizeof(zero));
1986 image_view=AcquireCacheView(image);
1987 resize_view=AcquireCacheView(resize_image);
1988 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1989 #pragma omp parallel for shared(status)
1991 for (x=0; x < (ssize_t) resize_image->columns; x++)
1997 register const IndexPacket
2000 register const PixelPacket
2003 register ContributionInfo
2004 *restrict contribution;
2006 register IndexPacket
2007 *restrict resize_indexes;
2009 register PixelPacket
2020 if (status == MagickFalse)
2022 center=(MagickRealType) (x+0.5)/x_factor;
2023 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2024 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
2026 contribution=contributions[GetOpenMPThreadId()];
2027 for (n=0; n < (stop-start); n++)
2029 contribution[n].pixel=start+n;
2030 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2031 ((MagickRealType) (start+n)-center+0.5));
2032 density+=contribution[n].weight;
2034 if ((density != 0.0) && (density != 1.0))
2042 density=1.0/density;
2043 for (i=0; i < n; i++)
2044 contribution[i].weight*=density;
2046 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2047 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
2048 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2050 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2055 indexes=GetCacheViewVirtualIndexQueue(image_view);
2056 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2057 for (y=0; y < (ssize_t) resize_image->rows; y++)
2072 if (image->matte == MagickFalse)
2074 for (i=0; i < n; i++)
2076 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2077 (contribution[i].pixel-contribution[0].pixel);
2078 alpha=contribution[i].weight;
2079 pixel.red+=alpha*(p+j)->red;
2080 pixel.green+=alpha*(p+j)->green;
2081 pixel.blue+=alpha*(p+j)->blue;
2082 pixel.opacity+=alpha*(p+j)->opacity;
2084 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2085 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2086 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2087 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2088 if ((image->colorspace == CMYKColorspace) &&
2089 (resize_image->colorspace == CMYKColorspace))
2091 for (i=0; i < n; i++)
2093 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2094 (contribution[i].pixel-contribution[0].pixel);
2095 alpha=contribution[i].weight;
2096 pixel.index+=alpha*indexes[j];
2098 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
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*QuantumScale*
2112 GetAlphaPixelComponent(p+j);
2113 pixel.red+=alpha*(p+j)->red;
2114 pixel.green+=alpha*(p+j)->green;
2115 pixel.blue+=alpha*(p+j)->blue;
2116 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2119 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2120 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2121 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2122 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2123 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2124 if ((image->colorspace == CMYKColorspace) &&
2125 (resize_image->colorspace == CMYKColorspace))
2127 for (i=0; i < n; i++)
2129 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2130 (contribution[i].pixel-contribution[0].pixel);
2131 alpha=contribution[i].weight*QuantumScale*
2132 GetAlphaPixelComponent(p+j);
2133 pixel.index+=alpha*indexes[j];
2135 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2136 GetIndexPixelComponent(&pixel));
2139 if ((resize_image->storage_class == PseudoClass) &&
2140 (image->storage_class == PseudoClass))
2142 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
2144 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2145 (contribution[i-start].pixel-contribution[0].pixel);
2146 resize_indexes[y]=indexes[j];
2150 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2152 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2157 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2158 #pragma omp critical (MagickCore_HorizontalFilter)
2160 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2161 if (proceed == MagickFalse)
2165 resize_view=DestroyCacheView(resize_view);
2166 image_view=DestroyCacheView(image_view);
2167 contributions=DestroyContributionThreadSet(contributions);
2171 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2172 const Image *image,Image *resize_image,const MagickRealType y_factor,
2173 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
2183 **restrict contributions;
2199 Apply filter to resize vertically from image to resize image.
2201 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
2202 support=scale*GetResizeFilterSupport(resize_filter);
2203 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2204 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2206 InheritException(exception,&resize_image->exception);
2207 return(MagickFalse);
2212 Support too small even for nearest neighbour: Reduce to point
2215 support=(MagickRealType) 0.5;
2218 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2219 if (contributions == (ContributionInfo **) NULL)
2221 (void) ThrowMagickException(exception,GetMagickModule(),
2222 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2223 return(MagickFalse);
2227 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2228 image_view=AcquireCacheView(image);
2229 resize_view=AcquireCacheView(resize_image);
2230 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2231 #pragma omp parallel for shared(status)
2233 for (y=0; y < (ssize_t) resize_image->rows; y++)
2239 register const IndexPacket
2242 register const PixelPacket
2245 register ContributionInfo
2246 *restrict contribution;
2248 register IndexPacket
2249 *restrict resize_indexes;
2251 register PixelPacket
2262 if (status == MagickFalse)
2264 center=(MagickRealType) (y+0.5)/y_factor;
2265 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2266 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
2268 contribution=contributions[GetOpenMPThreadId()];
2269 for (n=0; n < (stop-start); n++)
2271 contribution[n].pixel=start+n;
2272 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2273 ((MagickRealType) (start+n)-center+0.5));
2274 density+=contribution[n].weight;
2276 if ((density != 0.0) && (density != 1.0))
2284 density=1.0/density;
2285 for (i=0; i < n; i++)
2286 contribution[i].weight*=density;
2288 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
2289 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2291 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2293 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2298 indexes=GetCacheViewVirtualIndexQueue(image_view);
2299 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2300 for (x=0; x < (ssize_t) resize_image->columns; x++)
2315 if (image->matte == MagickFalse)
2317 for (i=0; i < n; i++)
2319 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2321 alpha=contribution[i].weight;
2322 pixel.red+=alpha*(p+j)->red;
2323 pixel.green+=alpha*(p+j)->green;
2324 pixel.blue+=alpha*(p+j)->blue;
2325 pixel.opacity+=alpha*(p+j)->opacity;
2327 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2328 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2329 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2330 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2331 if ((image->colorspace == CMYKColorspace) &&
2332 (resize_image->colorspace == CMYKColorspace))
2334 for (i=0; i < n; i++)
2336 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2338 alpha=contribution[i].weight;
2339 pixel.index+=alpha*indexes[j];
2341 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
2350 for (i=0; i < n; i++)
2352 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2354 alpha=contribution[i].weight*QuantumScale*
2355 GetAlphaPixelComponent(p+j);
2356 pixel.red+=alpha*(p+j)->red;
2357 pixel.green+=alpha*(p+j)->green;
2358 pixel.blue+=alpha*(p+j)->blue;
2359 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2362 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2363 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2364 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2365 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2366 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
2367 if ((image->colorspace == CMYKColorspace) &&
2368 (resize_image->colorspace == CMYKColorspace))
2370 for (i=0; i < n; i++)
2372 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
2374 alpha=contribution[i].weight*QuantumScale*
2375 GetAlphaPixelComponent(p+j);
2376 pixel.index+=alpha*indexes[j];
2378 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2379 GetIndexPixelComponent(&pixel));
2382 if ((resize_image->storage_class == PseudoClass) &&
2383 (image->storage_class == PseudoClass))
2385 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
2387 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
2389 resize_indexes[x]=indexes[j];
2393 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2395 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2400 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2401 #pragma omp critical (MagickCore_VerticalFilter)
2403 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
2404 if (proceed == MagickFalse)
2408 resize_view=DestroyCacheView(resize_view);
2409 image_view=DestroyCacheView(image_view);
2410 contributions=DestroyContributionThreadSet(contributions);
2414 MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2415 const size_t rows,const FilterTypes filter,const double blur,
2416 ExceptionInfo *exception)
2418 #define WorkLoadFactor 0.265
2444 Acquire resize image.
2446 assert(image != (Image *) NULL);
2447 assert(image->signature == MagickSignature);
2448 if (image->debug != MagickFalse)
2449 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2450 assert(exception != (ExceptionInfo *) NULL);
2451 assert(exception->signature == MagickSignature);
2452 if ((columns == 0) || (rows == 0))
2453 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2454 if ((columns == image->columns) && (rows == image->rows) &&
2455 (filter == UndefinedFilter) && (blur == 1.0))
2456 return(CloneImage(image,0,0,MagickTrue,exception));
2457 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2458 if (resize_image == (Image *) NULL)
2459 return(resize_image);
2461 Acquire resize filter.
2463 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2464 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2465 if ((x_factor*y_factor) > WorkLoadFactor)
2466 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2468 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2469 if (filter_image == (Image *) NULL)
2470 return(DestroyImage(resize_image));
2471 filter_type=LanczosFilter;
2472 if (filter != UndefinedFilter)
2475 if ((x_factor == 1.0) && (y_factor == 1.0))
2476 filter_type=PointFilter;
2478 if ((image->storage_class == PseudoClass) ||
2479 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2480 filter_type=MitchellFilter;
2481 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2487 if ((x_factor*y_factor) > WorkLoadFactor)
2489 span=(MagickSizeType) (filter_image->columns+rows);
2490 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
2492 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
2493 span,&offset,exception);
2497 span=(MagickSizeType) (filter_image->rows+columns);
2498 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
2500 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
2501 span,&offset,exception);
2506 filter_image=DestroyImage(filter_image);
2507 resize_filter=DestroyResizeFilter(resize_filter);
2508 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2509 return((Image *) NULL);
2510 resize_image->type=image->type;
2511 return(resize_image);
2515 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2519 % S a m p l e I m a g e %
2523 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2525 % SampleImage() scales an image to the desired dimensions with pixel
2526 % sampling. Unlike other scaling methods, this method does not introduce
2527 % any additional color into the scaled image.
2529 % The format of the SampleImage method is:
2531 % Image *SampleImage(const Image *image,const size_t columns,
2532 % const size_t rows,ExceptionInfo *exception)
2534 % A description of each parameter follows:
2536 % o image: the image.
2538 % o columns: the number of columns in the sampled image.
2540 % o rows: the number of rows in the sampled image.
2542 % o exception: return any errors or warnings in this structure.
2545 MagickExport Image *SampleImage(const Image *image,const size_t columns,
2546 const size_t rows,ExceptionInfo *exception)
2548 #define SampleImageTag "Sample/Image"
2571 Initialize sampled image attributes.
2573 assert(image != (const Image *) NULL);
2574 assert(image->signature == MagickSignature);
2575 if (image->debug != MagickFalse)
2576 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2577 assert(exception != (ExceptionInfo *) NULL);
2578 assert(exception->signature == MagickSignature);
2579 if ((columns == 0) || (rows == 0))
2580 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2581 if ((columns == image->columns) && (rows == image->rows))
2582 return(CloneImage(image,0,0,MagickTrue,exception));
2583 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2584 if (sample_image == (Image *) NULL)
2585 return((Image *) NULL);
2587 Allocate scan line buffer and column offset buffers.
2589 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
2591 if (x_offset == (ssize_t *) NULL)
2593 sample_image=DestroyImage(sample_image);
2594 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2596 for (x=0; x < (ssize_t) sample_image->columns; x++)
2597 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
2598 sample_image->columns);
2604 image_view=AcquireCacheView(image);
2605 sample_view=AcquireCacheView(sample_image);
2606 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2607 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2609 for (y=0; y < (ssize_t) sample_image->rows; y++)
2611 register const IndexPacket
2614 register const PixelPacket
2617 register IndexPacket
2618 *restrict sample_indexes;
2620 register PixelPacket
2629 if (status == MagickFalse)
2631 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2632 sample_image->rows);
2633 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2635 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2637 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2642 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2643 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2647 for (x=0; x < (ssize_t) sample_image->columns; x++)
2648 *q++=p[x_offset[x]];
2649 if ((image->storage_class == PseudoClass) ||
2650 (image->colorspace == CMYKColorspace))
2651 for (x=0; x < (ssize_t) sample_image->columns; x++)
2652 sample_indexes[x]=indexes[x_offset[x]];
2653 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2655 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2660 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2661 #pragma omp critical (MagickCore_SampleImage)
2663 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2664 if (proceed == MagickFalse)
2668 image_view=DestroyCacheView(image_view);
2669 sample_view=DestroyCacheView(sample_view);
2670 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
2671 sample_image->type=image->type;
2672 return(sample_image);
2676 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2680 % S c a l e I m a g e %
2684 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2686 % ScaleImage() changes the size of an image to the given dimensions.
2688 % The format of the ScaleImage method is:
2690 % Image *ScaleImage(const Image *image,const size_t columns,
2691 % const size_t rows,ExceptionInfo *exception)
2693 % A description of each parameter follows:
2695 % o image: the image.
2697 % o columns: the number of columns in the scaled image.
2699 % o rows: the number of rows in the scaled image.
2701 % o exception: return any errors or warnings in this structure.
2704 MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2705 const size_t rows,ExceptionInfo *exception)
2707 #define ScaleImageTag "Scale/Image"
2741 Initialize scaled image attributes.
2743 assert(image != (const Image *) NULL);
2744 assert(image->signature == MagickSignature);
2745 if (image->debug != MagickFalse)
2746 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2747 assert(exception != (ExceptionInfo *) NULL);
2748 assert(exception->signature == MagickSignature);
2749 if ((columns == 0) || (rows == 0))
2750 return((Image *) NULL);
2751 if ((columns == image->columns) && (rows == image->rows))
2752 return(CloneImage(image,0,0,MagickTrue,exception));
2753 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2754 if (scale_image == (Image *) NULL)
2755 return((Image *) NULL);
2756 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2758 InheritException(exception,&scale_image->exception);
2759 scale_image=DestroyImage(scale_image);
2760 return((Image *) NULL);
2765 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2768 if (image->rows != scale_image->rows)
2769 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2771 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2772 scale_image->columns,sizeof(*scale_scanline));
2773 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2775 if ((scanline == (MagickPixelPacket *) NULL) ||
2776 (scale_scanline == (MagickPixelPacket *) NULL) ||
2777 (x_vector == (MagickPixelPacket *) NULL) ||
2778 (y_vector == (MagickPixelPacket *) NULL))
2780 scale_image=DestroyImage(scale_image);
2781 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2787 next_row=MagickTrue;
2789 scale.y=(double) scale_image->rows/(double) image->rows;
2790 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2792 GetMagickPixelPacket(image,&pixel);
2793 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2795 image_view=AcquireCacheView(image);
2796 scale_view=AcquireCacheView(scale_image);
2797 for (y=0; y < (ssize_t) scale_image->rows; y++)
2799 register const IndexPacket
2802 register const PixelPacket
2805 register IndexPacket
2806 *restrict scale_indexes;
2808 register MagickPixelPacket
2812 register PixelPacket
2818 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2820 if (q == (PixelPacket *) NULL)
2822 scale_indexes=GetAuthenticIndexQueue(scale_image);
2823 if (scale_image->rows == image->rows)
2826 Read a new scanline.
2828 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2830 if (p == (const PixelPacket *) NULL)
2832 indexes=GetCacheViewVirtualIndexQueue(image_view);
2833 for (x=0; x < (ssize_t) image->columns; x++)
2835 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2836 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2837 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
2838 if (image->matte != MagickFalse)
2839 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
2840 if (indexes != (IndexPacket *) NULL)
2841 x_vector[x].index=(MagickRealType) indexes[x];
2850 while (scale.y < span.y)
2852 if ((next_row != MagickFalse) &&
2853 (number_rows < (ssize_t) image->rows))
2856 Read a new scanline.
2858 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2860 if (p == (const PixelPacket *) NULL)
2862 indexes=GetCacheViewVirtualIndexQueue(image_view);
2863 for (x=0; x < (ssize_t) image->columns; x++)
2865 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2866 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2867 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
2868 if (image->matte != MagickFalse)
2869 x_vector[x].opacity=(MagickRealType)
2870 GetOpacityPixelComponent(p);
2871 if (indexes != (IndexPacket *) NULL)
2872 x_vector[x].index=(MagickRealType) indexes[x];
2877 for (x=0; x < (ssize_t) image->columns; x++)
2879 y_vector[x].red+=scale.y*x_vector[x].red;
2880 y_vector[x].green+=scale.y*x_vector[x].green;
2881 y_vector[x].blue+=scale.y*x_vector[x].blue;
2882 if (scale_image->matte != MagickFalse)
2883 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2884 if (scale_indexes != (IndexPacket *) NULL)
2885 y_vector[x].index+=scale.y*x_vector[x].index;
2888 scale.y=(double) scale_image->rows/(double) image->rows;
2889 next_row=MagickTrue;
2891 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
2894 Read a new scanline.
2896 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2898 if (p == (const PixelPacket *) NULL)
2900 indexes=GetCacheViewVirtualIndexQueue(image_view);
2901 for (x=0; x < (ssize_t) image->columns; x++)
2903 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2904 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2905 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
2906 if (image->matte != MagickFalse)
2907 x_vector[x].opacity=(MagickRealType)
2908 GetOpacityPixelComponent(p);
2909 if (indexes != (IndexPacket *) NULL)
2910 x_vector[x].index=(MagickRealType) indexes[x];
2914 next_row=MagickFalse;
2917 for (x=0; x < (ssize_t) image->columns; x++)
2919 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
2920 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
2921 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
2922 if (image->matte != MagickFalse)
2923 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
2924 if (scale_indexes != (IndexPacket *) NULL)
2925 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
2927 s->green=pixel.green;
2929 if (scale_image->matte != MagickFalse)
2930 s->opacity=pixel.opacity;
2931 if (scale_indexes != (IndexPacket *) NULL)
2932 s->index=pixel.index;
2939 scale.y=(double) scale_image->rows/(double) image->rows;
2940 next_row=MagickTrue;
2944 if (scale_image->columns == image->columns)
2947 Transfer scanline to scaled image.
2950 for (x=0; x < (ssize_t) scale_image->columns; x++)
2952 q->red=ClampToQuantum(s->red);
2953 q->green=ClampToQuantum(s->green);
2954 q->blue=ClampToQuantum(s->blue);
2955 if (scale_image->matte != MagickFalse)
2956 q->opacity=ClampToQuantum(s->opacity);
2957 if (scale_indexes != (IndexPacket *) NULL)
2958 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
2969 next_column=MagickFalse;
2973 for (x=0; x < (ssize_t) image->columns; x++)
2975 scale.x=(double) scale_image->columns/(double) image->columns;
2976 while (scale.x >= span.x)
2978 if (next_column != MagickFalse)
2983 pixel.red+=span.x*s->red;
2984 pixel.green+=span.x*s->green;
2985 pixel.blue+=span.x*s->blue;
2986 if (image->matte != MagickFalse)
2987 pixel.opacity+=span.x*s->opacity;
2988 if (scale_indexes != (IndexPacket *) NULL)
2989 pixel.index+=span.x*s->index;
2991 t->green=pixel.green;
2993 if (scale_image->matte != MagickFalse)
2994 t->opacity=pixel.opacity;
2995 if (scale_indexes != (IndexPacket *) NULL)
2996 t->index=pixel.index;
2999 next_column=MagickTrue;
3003 if (next_column != MagickFalse)
3006 next_column=MagickFalse;
3009 pixel.red+=scale.x*s->red;
3010 pixel.green+=scale.x*s->green;
3011 pixel.blue+=scale.x*s->blue;
3012 if (scale_image->matte != MagickFalse)
3013 pixel.opacity+=scale.x*s->opacity;
3014 if (scale_indexes != (IndexPacket *) NULL)
3015 pixel.index+=scale.x*s->index;
3023 pixel.red+=span.x*s->red;
3024 pixel.green+=span.x*s->green;
3025 pixel.blue+=span.x*s->blue;
3026 if (scale_image->matte != MagickFalse)
3027 pixel.opacity+=span.x*s->opacity;
3028 if (scale_indexes != (IndexPacket *) NULL)
3029 pixel.index+=span.x*s->index;
3031 if ((next_column == MagickFalse) &&
3032 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
3035 t->green=pixel.green;
3037 if (scale_image->matte != MagickFalse)
3038 t->opacity=pixel.opacity;
3039 if (scale_indexes != (IndexPacket *) NULL)
3040 t->index=pixel.index;
3043 Transfer scanline to scaled image.
3046 for (x=0; x < (ssize_t) scale_image->columns; x++)
3048 q->red=ClampToQuantum(t->red);
3049 q->green=ClampToQuantum(t->green);
3050 q->blue=ClampToQuantum(t->blue);
3051 if (scale_image->matte != MagickFalse)
3052 q->opacity=ClampToQuantum(t->opacity);
3053 if (scale_indexes != (IndexPacket *) NULL)
3054 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
3059 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
3061 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3063 if (proceed == MagickFalse)
3066 scale_view=DestroyCacheView(scale_view);
3067 image_view=DestroyCacheView(image_view);
3069 Free allocated memory.
3071 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3072 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3073 if (scale_image->rows != image->rows)
3074 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3075 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3076 scale_image->type=image->type;
3077 return(scale_image);
3081 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3085 + S e t R e s i z e F i l t e r S u p p o r t %
3089 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3091 % SetResizeFilterSupport() specifies which IR filter to use to window
3093 % The format of the SetResizeFilterSupport method is:
3095 % void SetResizeFilterSupport(ResizeFilter *resize_filter,
3096 % const MagickRealType support)
3098 % A description of each parameter follows:
3100 % o resize_filter: the resize filter.
3102 % o support: the filter spport radius.
3105 MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3106 const MagickRealType support)
3108 assert(resize_filter != (ResizeFilter *) NULL);
3109 assert(resize_filter->signature == MagickSignature);
3110 resize_filter->support=support;
3114 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3118 % T h u m b n a i l I m a g e %
3122 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3124 % ThumbnailImage() changes the size of an image to the given dimensions and
3125 % removes any associated profiles. The goal is to produce small low cost
3126 % thumbnail images suited for display on the Web.
3128 % The format of the ThumbnailImage method is:
3130 % Image *ThumbnailImage(const Image *image,const size_t columns,
3131 % const size_t rows,ExceptionInfo *exception)
3133 % A description of each parameter follows:
3135 % o image: the image.
3137 % o columns: the number of columns in the scaled image.
3139 % o rows: the number of rows in the scaled image.
3141 % o exception: return any errors or warnings in this structure.
3144 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3145 const size_t rows,ExceptionInfo *exception)
3147 #define SampleFactor 5
3150 value[MaxTextExtent];
3168 assert(image != (Image *) NULL);
3169 assert(image->signature == MagickSignature);
3170 if (image->debug != MagickFalse)
3171 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3172 assert(exception != (ExceptionInfo *) NULL);
3173 assert(exception->signature == MagickSignature);
3174 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3175 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3176 if ((x_factor*y_factor) > 0.1)
3177 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3180 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
3181 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3182 image->blur,exception);
3188 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3190 if (sample_image == (Image *) NULL)
3191 return((Image *) NULL);
3192 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3193 image->blur,exception);
3194 sample_image=DestroyImage(sample_image);
3196 if (thumbnail_image == (Image *) NULL)
3197 return(thumbnail_image);
3198 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3199 if (thumbnail_image->matte == MagickFalse)
3200 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3201 thumbnail_image->depth=8;
3202 thumbnail_image->interlace=NoInterlace;
3204 Strip all profiles except color profiles.
3206 ResetImageProfileIterator(thumbnail_image);
3207 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3209 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3211 (void) DeleteImageProfile(thumbnail_image,name);
3212 ResetImageProfileIterator(thumbnail_image);
3214 name=GetNextImageProfile(thumbnail_image);
3216 (void) DeleteImageProperty(thumbnail_image,"comment");
3217 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3218 if (strstr(image->magick_filename,"//") == (char *) NULL)
3219 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
3220 image->magick_filename);
3221 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3222 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3223 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3225 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3226 attributes.st_mtime);
3227 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3229 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3230 attributes.st_mtime);
3231 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
3232 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
3233 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3234 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3236 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3237 (void) SetImageProperty(thumbnail_image,"software",
3238 GetMagickVersion(&version));
3239 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3240 image->magick_columns);
3241 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
3242 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3243 image->magick_rows);
3244 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
3245 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3246 GetImageListLength(image));
3247 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3248 return(thumbnail_image);