blob: 1261281dc9638acb26dfa7bf3cdf5ca2848c6e92 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% RRRR EEEEE SSSSS IIIII ZZZZZ EEEEE %
7% R R E SS I ZZ E %
8% RRRR EEE SSS I ZZZ EEE %
9% R R E SS I ZZ E %
10% R R EEEEE SSSSS IIIII ZZZZZ EEEEE %
11% %
12% %
13% MagickCore Image Resize Methods %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
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. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
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"
anthony55f12332010-09-10 01:13:02 +000057#include "magick/magick.h"
cristy3ed852e2009-09-05 21:47:34 +000058#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"
cristya6a18782010-11-15 01:56:25 +000065#include "magick/resample-private.h"
cristy3ed852e2009-09-05 21:47:34 +000066#include "magick/resize.h"
67#include "magick/resize-private.h"
68#include "magick/string_.h"
cristyf2f27272009-12-17 14:48:46 +000069#include "magick/string-private.h"
cristy3ed852e2009-09-05 21:47:34 +000070#include "magick/thread-private.h"
71#include "magick/utility.h"
72#include "magick/version.h"
73#if defined(MAGICKCORE_LQR_DELEGATE)
74#include <lqr.h>
75#endif
76
77/*
78 Typedef declarations.
79*/
80struct _ResizeFilter
81{
82 MagickRealType
83 (*filter)(const MagickRealType,const ResizeFilter *),
84 (*window)(const MagickRealType,const ResizeFilter *),
cristy33b1c162010-01-23 22:51:51 +000085 support, /* filter region of support - the filter support limit */
86 window_support, /* window support, usally equal to support (expert only) */
anthony55f12332010-09-10 01:13:02 +000087 scale, /* dimension scaling to fit window support (usally 1.0) */
cristy33b1c162010-01-23 22:51:51 +000088 blur, /* x-scale (blur-sharpen) */
cristy5cce74b2010-11-15 03:24:28 +000089 coefficient[7]; /* cubic coefficents for BC-cubic spline filters */
cristy3ed852e2009-09-05 21:47:34 +000090
cristybb503372010-05-27 20:51:26 +000091 size_t
cristy3ed852e2009-09-05 21:47:34 +000092 signature;
93};
94
95/*
96 Forward declaractions.
97*/
98static MagickRealType
99 I0(MagickRealType x),
anthonyb6d08c52010-09-13 01:17:04 +0000100 BesselOrderOne(MagickRealType),
anthony07a3f7f2010-09-16 03:03:11 +0000101 Sinc(const MagickRealType, const ResizeFilter *),
anthonyba5a7c32010-09-15 02:42:25 +0000102 SincFast(const MagickRealType, const ResizeFilter *);
cristy3ed852e2009-09-05 21:47:34 +0000103
104/*
105%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106% %
107% %
108% %
109+ F i l t e r F u n c t i o n s %
110% %
111% %
112% %
113%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114%
cristy33b1c162010-01-23 22:51:51 +0000115% These are the various filter and windowing functions that are provided.
cristy3ed852e2009-09-05 21:47:34 +0000116%
cristy33b1c162010-01-23 22:51:51 +0000117% They are internal to this module only. See AcquireResizeFilterInfo() for
118% details of the access to these functions, via the GetResizeFilterSupport()
119% and GetResizeFilterWeight() API interface.
cristy3ed852e2009-09-05 21:47:34 +0000120%
121% The individual filter functions have this format...
122%
123% static MagickRealtype *FilterName(const MagickRealType x,
124% const MagickRealType support)
125%
cristy33b1c162010-01-23 22:51:51 +0000126% A description of each parameter follows:
cristy3ed852e2009-09-05 21:47:34 +0000127%
cristy33b1c162010-01-23 22:51:51 +0000128% o x: the distance from the sampling point generally in the range of 0 to
129% support. The GetResizeFilterWeight() ensures this a positive value.
130%
131% o resize_filter: current filter information. This allows function to
132% access support, and possibly other pre-calculated information defining
133% the functions.
cristy3ed852e2009-09-05 21:47:34 +0000134%
135*/
136
cristyc5c6f662010-09-22 14:23:02 +0000137#define MagickPIL ((MagickRealType) 3.14159265358979323846264338327950288420L)
cristy560d8182010-09-08 22:36:25 +0000138
anthony48f77622010-10-03 14:32:31 +0000139static MagickRealType Jinc(const MagickRealType x,
cristy3ed852e2009-09-05 21:47:34 +0000140 const ResizeFilter *magick_unused(resize_filter))
141{
142 /*
anthony48f77622010-10-03 14:32:31 +0000143 See Pratt "Digital Image Processing" p.97 for Jinc/Bessel functions.
cristy33b1c162010-01-23 22:51:51 +0000144 http://mathworld.wolfram.com/JincFunction.html and page 11 of
anthony48f77622010-10-03 14:32:31 +0000145 http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
146
nicolase473f722010-10-07 00:05:13 +0000147 The original "zoom" program by Paul Heckbert called this "Bessel".
148 But really it is more accurately named "Jinc".
cristy3ed852e2009-09-05 21:47:34 +0000149 */
150 if (x == 0.0)
nicolas5a36f342010-10-07 00:11:32 +0000151 return(0.5*MagickPIL);
152 return(BesselOrderOne(MagickPIL*x)/x);
cristy3ed852e2009-09-05 21:47:34 +0000153}
154
155static MagickRealType Blackman(const MagickRealType x,
cristyce70c172010-01-07 17:15:30 +0000156 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000157{
158 /*
cristy83017922010-09-05 20:45:15 +0000159 Blackman: 2nd order cosine windowing function:
cristy21ce88a2010-09-05 01:37:25 +0000160 0.42 + 0.5 cos(pi x) + 0.08 cos(2pi x)
cristy560d8182010-09-08 22:36:25 +0000161 Refactored by Chantal Racette and Nicolas Robidoux to one trig
162 call and five flops.
cristy3ed852e2009-09-05 21:47:34 +0000163 */
cristyc5c6f662010-09-22 14:23:02 +0000164 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000165 return(0.34+cospix*(0.5+cospix*0.16));
cristy3ed852e2009-09-05 21:47:34 +0000166}
167
168static MagickRealType Bohman(const MagickRealType x,
cristyce70c172010-01-07 17:15:30 +0000169 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000170{
171 /*
cristy560d8182010-09-08 22:36:25 +0000172 Bohman: 2rd Order cosine windowing function:
173 (1-x) cos(pi x) + sin(pi x) / pi.
nicolasa4f7b4a2010-09-20 20:28:38 +0000174 Refactored by Nicolas Robidoux to one trig call, one sqrt call,
175 and 7 flops, taking advantage of the fact that the support of
176 Bohman is 1 (so that we know that sin(pi x) >= 0).
cristy3ed852e2009-09-05 21:47:34 +0000177 */
cristyc5c6f662010-09-22 14:23:02 +0000178 const double cospix = cos((double) (MagickPIL*x));
nicolasa4f7b4a2010-09-20 20:28:38 +0000179 const double sinpix = sqrt(1.0-cospix*cospix);
nicolas2ffd3b22010-09-24 20:27:31 +0000180 return((1.0-x)*cospix+(1.0/MagickPIL)*sinpix);
cristy3ed852e2009-09-05 21:47:34 +0000181}
182
anthony463be1d2010-09-26 01:07:36 +0000183static MagickRealType Box(const MagickRealType magick_unused(x),
184 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000185{
186 /*
nicolas40477452010-09-27 23:42:08 +0000187 A Box filter is a equal weighting function (all weights equal).
anthonyc331dec2010-09-26 01:30:14 +0000188 DO NOT LIMIT results by support or resize point sampling will work
189 as it requests points beyond its normal 0.0 support size.
cristy3ed852e2009-09-05 21:47:34 +0000190 */
anthony463be1d2010-09-26 01:07:36 +0000191 return(1.0);
cristy3ed852e2009-09-05 21:47:34 +0000192}
193
194static MagickRealType CubicBC(const MagickRealType x,
195 const ResizeFilter *resize_filter)
196{
197 /*
198 Cubic Filters using B,C determined values:
nicolase3b9eca2010-10-24 19:48:22 +0000199 Mitchell-Netravali B= 1/3 C= 1/3 "Balanced" cubic spline filter
200 Catmull-Rom B= 0 C= 1/2 Interpolatory and exact on linears
201 Cubic B-Spline B= 1 C= 0 Spline approximation of Gaussian
202 Hermite B= 0 C= 0 Spline with small support (= 1)
cristy3ed852e2009-09-05 21:47:34 +0000203
cristy33b1c162010-01-23 22:51:51 +0000204 See paper by Mitchell and Netravali, Reconstruction Filters in Computer
205 Graphics Computer Graphics, Volume 22, Number 4, August 1988
206 http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/
cristyf59a8922010-02-28 19:51:23 +0000207 Mitchell.pdf.
cristy3ed852e2009-09-05 21:47:34 +0000208
cristy33b1c162010-01-23 22:51:51 +0000209 Coefficents are determined from B,C values:
anthony06b1edf2010-10-25 01:19:50 +0000210 P0 = ( 6 - 2*B )/6 = coeff[0]
cristy3ed852e2009-09-05 21:47:34 +0000211 P1 = 0
anthony06b1edf2010-10-25 01:19:50 +0000212 P2 = (-18 +12*B + 6*C )/6 = coeff[1]
213 P3 = ( 12 - 9*B - 6*C )/6 = coeff[2]
214 Q0 = ( 8*B +24*C )/6 = coeff[3]
215 Q1 = ( -12*B -48*C )/6 = coeff[4]
216 Q2 = ( 6*B +30*C )/6 = coeff[5]
217 Q3 = ( - 1*B - 6*C )/6 = coeff[6]
cristy3ed852e2009-09-05 21:47:34 +0000218
cristy33b1c162010-01-23 22:51:51 +0000219 which are used to define the filter:
cristyf59a8922010-02-28 19:51:23 +0000220
cristy3ed852e2009-09-05 21:47:34 +0000221 P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
nicolase3b9eca2010-10-24 19:48:22 +0000222 Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x < 2
cristy3ed852e2009-09-05 21:47:34 +0000223
nicolase3b9eca2010-10-24 19:48:22 +0000224 which ensures function is continuous in value and derivative
nicolas89d73f92010-10-24 20:11:54 +0000225 (slope).
cristy3ed852e2009-09-05 21:47:34 +0000226 */
227 if (x < 1.0)
cristy5cce74b2010-11-15 03:24:28 +0000228 return(resize_filter->coefficient[0]+x*(x*
229 (resize_filter->coefficient[1]+x*resize_filter->coefficient[2])));
cristy3ed852e2009-09-05 21:47:34 +0000230 if (x < 2.0)
cristy5cce74b2010-11-15 03:24:28 +0000231 return(resize_filter->coefficient[3]+x*(resize_filter->coefficient[4]+x*
232 (resize_filter->coefficient[5]+x*resize_filter->coefficient[6])));
cristy3ed852e2009-09-05 21:47:34 +0000233 return(0.0);
234}
235
236static MagickRealType Gaussian(const MagickRealType x,
anthonyf5e76ef2010-10-12 01:22:01 +0000237 const ResizeFilter *resize_filter)
cristy3ed852e2009-09-05 21:47:34 +0000238{
cristy560d8182010-09-08 22:36:25 +0000239 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000240 Gaussian with a fixed sigma = 1/2
241
242 Gaussian Formula...
243 exp( -(x^2)/((2.0*sigma^2) ) / sqrt(2*PI*sigma^2)))
244 The constants are pre-calculated...
245 exp( -coeff[0]*(x^2)) ) * coeff[1]
anthony06b1edf2010-10-25 01:19:50 +0000246 However the multiplier coefficent (1) is not needed and not used.
anthonyf5e76ef2010-10-12 01:22:01 +0000247
248 This separates the gaussian 'sigma' value from the 'blur/support' settings
anthony06b1edf2010-10-25 01:19:50 +0000249 allowing for its use in special 'small sigma' gaussians, without the filter
250 'missing' pixels when blurring because the support is too small.
cristy560d8182010-09-08 22:36:25 +0000251 */
cristy5cce74b2010-11-15 03:24:28 +0000252 return(exp((double)(-resize_filter->coefficient[0]*x*x)));
anthony06b1edf2010-10-25 01:19:50 +0000253}
cristy3ed852e2009-09-05 21:47:34 +0000254
255static MagickRealType Hanning(const MagickRealType x,
256 const ResizeFilter *magick_unused(resize_filter))
257{
258 /*
nicolas40477452010-09-27 23:42:08 +0000259 Cosine window function:
260 .5+.5cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000261 */
cristyc5c6f662010-09-22 14:23:02 +0000262 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000263 return(0.5+0.5*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000264}
265
266static MagickRealType Hamming(const MagickRealType x,
267 const ResizeFilter *magick_unused(resize_filter))
268{
269 /*
nicolas40477452010-09-27 23:42:08 +0000270 Offset cosine window function:
271 .54 + .46 cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000272 */
cristyc5c6f662010-09-22 14:23:02 +0000273 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000274 return(0.54+0.46*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000275}
276
277static MagickRealType Kaiser(const MagickRealType x,
278 const ResizeFilter *magick_unused(resize_filter))
279{
280#define Alpha 6.5
281#define I0A (1.0/I0(Alpha))
282
283 /*
nicolas07bac812010-09-19 18:47:02 +0000284 Kaiser Windowing Function (bessel windowing): Alpha is a free
285 value from 5 to 8 (currently hardcoded to 6.5).
286 Future: make alpha the IOA pre-calculation, an 'expert' setting.
cristy3ed852e2009-09-05 21:47:34 +0000287 */
288 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
289}
290
291static MagickRealType Lagrange(const MagickRealType x,
292 const ResizeFilter *resize_filter)
293{
cristy3ed852e2009-09-05 21:47:34 +0000294 MagickRealType
295 value;
296
cristybb503372010-05-27 20:51:26 +0000297 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000298 i;
299
cristy9af9b5d2010-08-15 17:04:28 +0000300 ssize_t
301 n,
302 order;
303
cristy3ed852e2009-09-05 21:47:34 +0000304 /*
nicolas07bac812010-09-19 18:47:02 +0000305 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the
306 lagrange function and depends on the overall support window size
307 of the filter. That is: for a support of 2, it gives a lagrange-4
308 (piecewise cubic function).
cristy3ed852e2009-09-05 21:47:34 +0000309
nicolas07bac812010-09-19 18:47:02 +0000310 "n" identifies the piece of the piecewise polynomial.
cristy3ed852e2009-09-05 21:47:34 +0000311
nicolas07bac812010-09-19 18:47:02 +0000312 See Survey: Interpolation Methods, IEEE Transactions on Medical
313 Imaging, Vol 18, No 11, November 1999, p1049-1075, -- Equation 27
314 on p1064.
cristy3ed852e2009-09-05 21:47:34 +0000315 */
316 if (x > resize_filter->support)
317 return(0.0);
cristybb503372010-05-27 20:51:26 +0000318 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
anthonyc2d07db2010-09-15 23:47:40 +0000319 /*n=(ssize_t)((1.0*order)/2.0+x); -- which piece does x belong to */
320 n = (ssize_t)(resize_filter->window_support + x);
cristy3ed852e2009-09-05 21:47:34 +0000321 value=1.0f;
322 for (i=0; i < order; i++)
323 if (i != n)
324 value*=(n-i-x)/(n-i);
325 return(value);
326}
327
328static MagickRealType Quadratic(const MagickRealType x,
329 const ResizeFilter *magick_unused(resize_filter))
330{
331 /*
332 2rd order (quadratic) B-Spline approximation of Gaussian.
333 */
334 if (x < 0.5)
335 return(0.75-x*x);
336 if (x < 1.5)
337 return(0.5*(x-1.5)*(x-1.5));
338 return(0.0);
339}
340
anthony07a3f7f2010-09-16 03:03:11 +0000341static MagickRealType Sinc(const MagickRealType x,
cristy3ed852e2009-09-05 21:47:34 +0000342 const ResizeFilter *magick_unused(resize_filter))
343{
anthony720660f2010-09-07 10:05:14 +0000344 /*
nicolas40477452010-09-27 23:42:08 +0000345 Scaled sinc(x) function using a trig call:
nicolas07bac812010-09-19 18:47:02 +0000346 sinc(x) == sin(pi x)/(pi x).
anthony720660f2010-09-07 10:05:14 +0000347 */
anthony2d9b8b52010-09-14 08:31:07 +0000348 if (x != 0.0)
cristy560d8182010-09-08 22:36:25 +0000349 {
cristyc5c6f662010-09-22 14:23:02 +0000350 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
nicolas07bac812010-09-19 18:47:02 +0000351 return(sin((double) pix)/pix);
cristy560d8182010-09-08 22:36:25 +0000352 }
nicolas2ffd3b22010-09-24 20:27:31 +0000353 return((MagickRealType) 1.0);
anthony720660f2010-09-07 10:05:14 +0000354}
355
anthonyba5a7c32010-09-15 02:42:25 +0000356static MagickRealType SincFast(const MagickRealType x,
anthony720660f2010-09-07 10:05:14 +0000357 const ResizeFilter *magick_unused(resize_filter))
358{
cristy560d8182010-09-08 22:36:25 +0000359 /*
360 Approximations of the sinc function sin(pi x)/(pi x) over the
361 interval [-4,4] constructed by Nicolas Robidoux and Chantal
362 Racette with funding from the Natural Sciences and Engineering
363 Research Council of Canada.
nicolas07bac812010-09-19 18:47:02 +0000364
365 Although the approximations are polynomials (for low order of
366 approximation) and quotients of polynomials (for higher order of
367 approximation) and consequently are similar in form to Taylor
368 polynomials/Pade approximants, the approximations are computed
369 with a completely different technique.
370
371 Summary: These approximations are "the best" in terms of bang
372 (accuracy) for the buck (flops). More specifically: Among the
373 polynomial quotients that can be computed using a fixed number of
374 flops (with a given "+ - * / budget"), the chosen polynomial
375 quotient is the one closest to the approximated function with
376 respect to maximum absolute relative error over the given
377 interval.
378
379 The Remez algorithm, as implemented in the boost library's minimax
nicolas3aab40c2010-09-19 21:14:15 +0000380 package, is the key to the construction:
nicolas07bac812010-09-19 18:47:02 +0000381 http://www.boost.org/doc/libs/1_36_0/libs/math/doc/...
382 ...sf_and_dist/html/math_toolkit/backgrounders/remez.html
cristy560d8182010-09-08 22:36:25 +0000383 */
nicolas3aab40c2010-09-19 21:14:15 +0000384 /*
385 If outside of the interval of approximation, use the standard trig
386 formula.
387 */
anthony2d9b8b52010-09-14 08:31:07 +0000388 if (x > 4.0)
cristy03dbbd22010-09-19 23:04:47 +0000389 {
cristyc5c6f662010-09-22 14:23:02 +0000390 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
cristy03dbbd22010-09-19 23:04:47 +0000391 return(sin((double) pix)/pix);
392 }
anthony2d9b8b52010-09-14 08:31:07 +0000393 {
nicolas07bac812010-09-19 18:47:02 +0000394 /*
395 The approximations only depend on x^2 (sinc is an even
396 function).
397 */
398 const MagickRealType xx = x*x;
cristy83017922010-09-05 20:45:15 +0000399#if MAGICKCORE_QUANTUM_DEPTH <= 8
400 /*
anthony2d9b8b52010-09-14 08:31:07 +0000401 Maximum absolute relative error 6.3e-6 < 1/2^17.
cristy738e7562010-09-01 12:48:07 +0000402 */
403 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
404 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
405 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
406 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
407 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
408 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
409 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
410 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
nicolas3aab40c2010-09-19 21:14:15 +0000411 const MagickRealType p =
412 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
nicolas07bac812010-09-19 18:47:02 +0000413 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
anthony2d9b8b52010-09-14 08:31:07 +0000414#elif MAGICKCORE_QUANTUM_DEPTH <= 16
cristydbeb3eb2010-09-09 13:41:36 +0000415 /*
anthony2d9b8b52010-09-14 08:31:07 +0000416 Max. abs. rel. error 2.2e-8 < 1/2^25.
cristydbeb3eb2010-09-09 13:41:36 +0000417 */
418 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
419 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
nicolas3aab40c2010-09-19 21:14:15 +0000420 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
421 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
422 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
423 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
424 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
cristydbeb3eb2010-09-09 13:41:36 +0000425 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
426 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
427 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
anthony853d6972010-10-08 06:01:31 +0000428 const MagickRealType p =
nicolas3aab40c2010-09-19 21:14:15 +0000429 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
nicolas07bac812010-09-19 18:47:02 +0000430 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
nicolasc2d28f02010-09-27 18:56:15 +0000431#else
nicolas3aab40c2010-09-19 21:14:15 +0000432 /*
433 Max. abs. rel. error 1.2e-12 < 1/2^39.
434 */
435 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
436 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
437 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
438 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
439 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
440 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
441 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
442 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
443 const MagickRealType p =
444 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
445 const MagickRealType d0 = 1.0L;
446 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
447 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
448 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
449 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
450 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
451 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
cristy657c6352010-08-29 14:05:08 +0000452#endif
cristy83017922010-09-05 20:45:15 +0000453 }
cristy3ed852e2009-09-05 21:47:34 +0000454}
455
456static MagickRealType Triangle(const MagickRealType x,
457 const ResizeFilter *magick_unused(resize_filter))
458{
459 /*
nicolas0edb0862010-09-19 18:56:19 +0000460 1st order (linear) B-Spline, bilinear interpolation, Tent 1D
anthony06b1edf2010-10-25 01:19:50 +0000461 filter, or a Bartlett 2D Cone filter. Also used as a
462 Bartlett Windowing function for Sinc().
cristy3ed852e2009-09-05 21:47:34 +0000463 */
464 if (x < 1.0)
465 return(1.0-x);
466 return(0.0);
467}
468
469static MagickRealType Welsh(const MagickRealType x,
470 const ResizeFilter *magick_unused(resize_filter))
471{
472 /*
473 Welsh parabolic windowing filter.
474 */
cristy560d8182010-09-08 22:36:25 +0000475 if (x < 1.0)
cristy3ed852e2009-09-05 21:47:34 +0000476 return(1.0-x*x);
477 return(0.0);
478}
479
480/*
481%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
482% %
483% %
484% %
485+ A c q u i r e R e s i z e F i l t e r %
486% %
487% %
488% %
489%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
490%
nicolas07bac812010-09-19 18:47:02 +0000491% AcquireResizeFilter() allocates the ResizeFilter structure. Choose
492% from these filters:
cristy3ed852e2009-09-05 21:47:34 +0000493%
494% FIR (Finite impulse Response) Filters
495% Box Triangle Quadratic
496% Cubic Hermite Catrom
497% Mitchell
498%
499% IIR (Infinite impulse Response) Filters
anthony48f77622010-10-03 14:32:31 +0000500% Gaussian Sinc Jinc (Bessel)
cristy3ed852e2009-09-05 21:47:34 +0000501%
anthony48f77622010-10-03 14:32:31 +0000502% Windowed Sinc/Jinc Filters
cristy3ed852e2009-09-05 21:47:34 +0000503% Blackman Hanning Hamming
anthony48f77622010-10-03 14:32:31 +0000504% Kaiser Lanczos
cristy3ed852e2009-09-05 21:47:34 +0000505%
anthony61b5ddd2010-10-05 02:33:31 +0000506% Special purpose Filters
anthony06b1edf2010-10-25 01:19:50 +0000507% SincFast LanczosSharp Lanczos2D Lanczos2DSharp Robidoux
anthony61b5ddd2010-10-05 02:33:31 +0000508%
anthony48f77622010-10-03 14:32:31 +0000509% The users "-filter" selection is used to lookup the default 'expert'
510% settings for that filter from a internal table. However any provided
511% 'expert' settings (see below) may override this selection.
512%
513% FIR filters are used as is, and are limited to that filters support
nicolas07bac812010-09-19 18:47:02 +0000514% window (unless over-ridden). 'Gaussian' while classed as an IIR
anthonyc331dec2010-09-26 01:30:14 +0000515% filter, is also simply clipped by its support size (currently 1.5
anthonyf5e76ef2010-10-12 01:22:01 +0000516% or approximatally 3*sigma as recommended by many references)
cristy3ed852e2009-09-05 21:47:34 +0000517%
anthony152700d2010-10-28 02:43:18 +0000518% The special a 'cylindrical' filter flag will promote the default
519% 4-lobed Windowed Sinc filter to a 3-lobed Windowed Jinc equivelent,
520% which is better suited to this style of image resampling. This
521% typically happens when using such a filter for images distortions.
cristy3ed852e2009-09-05 21:47:34 +0000522%
anthony152700d2010-10-28 02:43:18 +0000523% Directly requesting 'Sinc', 'Jinc' function as a filter will force
524% the use of function without any windowing, or promotion for
525% cylindrical usage. This is not recommended, except by image
526% processing experts, especially as part of expert option filter
527% function selection.
anthony06b1edf2010-10-25 01:19:50 +0000528%
anthony48f77622010-10-03 14:32:31 +0000529% Two forms of the 'Sinc' function are available: Sinc and SincFast.
nicolas07bac812010-09-19 18:47:02 +0000530% Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
531% selected if the user specifically specifies the use of a Sinc
532% filter. SincFast uses highly accurate (and fast) polynomial (low Q)
anthony48f77622010-10-03 14:32:31 +0000533% and rational (high Q) approximations, and will be used by default in
534% most cases.
anthonyba5a7c32010-09-15 02:42:25 +0000535%
nicolasfc612942010-11-14 17:23:29 +0000536% The Lanczos filter is a special 3-lobed Sinc-windowed Sinc filter
537% (promoted to Jinc-windowed Jinc for cylindrical (Elliptical
538% Weighted Average) use). The Sinc version is the most popular
539% windowed filter.
anthony152700d2010-10-28 02:43:18 +0000540%
nicolasfc612942010-11-14 17:23:29 +0000541% LanczosSharp is a slightly sharpened (blur=0.9812505644269356 < 1)
542% form of the Lanczos filter, specifcally designed for EWA distortion
543% (as a Jinc-Jinc); it can also be used as a slightly sharper
nicolas2d3579a2010-11-13 15:31:28 +0000544% orthogonal Lanczos (Sinc-Sinc) filter. The chosen blur value comes
545% as close as possible to satisfying the following condition without
546% changing the character of the corresponding EWA filter:
anthony152700d2010-10-28 02:43:18 +0000547%
548% 'No-Op' Vertical and Horizontal Line Preservation Condition:
549% Images with only vertical or horizontal features are preserved
550% when performing 'no-op" with EWA distortion.
551%
nicolas2d3579a2010-11-13 15:31:28 +0000552% The Lanczos2 and Lanczos2Sharp filters are 2-lobe versions of the
553% Lanczos filters. The 'sharp' version uses a blur factor of
nicolasca48bce2010-11-14 17:54:53 +0000554% 0.9549963639785485, again chosen because the resulting EWA filter
nicolasc22cc712010-11-13 16:39:10 +0000555% comes as close as possible to satisfying the above
nicolasca48bce2010-11-14 17:54:53 +0000556% condition.
anthony06b1edf2010-10-25 01:19:50 +0000557%
nicolasb7dff642010-10-25 02:04:14 +0000558% Robidoux is another filter tuned for EWA. It is the Keys cubic
anthony152700d2010-10-28 02:43:18 +0000559% filter defined by B=(228 - 108 sqrt(2))/199. Robidoux satisfies the
nicolasb7dff642010-10-25 02:04:14 +0000560% "'No-Op' Vertical and Horizontal Line Preservation Condition"
nicolasc22cc712010-11-13 16:39:10 +0000561% exactly, and it moderately blurs high frequency 'pixel-hash'
562% patterns under no-op. It turns out to be close to both Mitchell
563% and Lanczos2Sharp. For example, its first crossing is at (36
564% sqrt(2) + 123)/(72 sqrt(2) + 47), almost the same as the first
565% crossing of Mitchell and Lanczos2Sharp.
anthony152700d2010-10-28 02:43:18 +0000566%
anthony61b5ddd2010-10-05 02:33:31 +0000567%
nicolas3061b8a2010-10-22 16:34:52 +0000568% 'EXPERT' OPTIONS:
nicolasce6dc292010-10-22 16:23:07 +0000569%
anthony152700d2010-10-28 02:43:18 +0000570% These artifact "defines" are not recommended for production use
571% without expert knowledge of resampling, filtering, and the effects
572% they have on the resulting resampled (resize ro distorted) image.
nicolas3061b8a2010-10-22 16:34:52 +0000573%
anthony152700d2010-10-28 02:43:18 +0000574% They can be used to override any and all filter default, and it is
575% recommended you make good use of "filter:verbose" to make sure that
576% the overall effect of your selection (before and after) is as
577% expected.
nicolas3061b8a2010-10-22 16:34:52 +0000578%
anthony28ad1d72010-10-26 06:30:24 +0000579% "filter:verbose" controls whether to output the exact results of
580% the filter selections made, as well as plotting data for
581% graphing the resulting filter over the filters support range.
cristy3ed852e2009-09-05 21:47:34 +0000582%
anthony48f77622010-10-03 14:32:31 +0000583% "filter:filter" Select the main function associated with
584% this filter name, as the weighting function of the filter.
585% This can be used to set a windowing function as a weighting
586% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000587%
nicolas3061b8a2010-10-22 16:34:52 +0000588% If a "filter:window" operation has not been provided, then a
589% 'Box' windowing function will be set to denote that no
590% windowing function is being used.
cristy3ed852e2009-09-05 21:47:34 +0000591%
nicolas3061b8a2010-10-22 16:34:52 +0000592% "filter:window" Select this windowing function for the filter.
593% While any filter could be used as a windowing function, using
594% the 'first lobe' of that filter over the whole support
595% window, using a non-windowing function is not advisible. If
596% no weighting filter function is specifed a 'SincFast' filter
597% will be used.
cristy3ed852e2009-09-05 21:47:34 +0000598%
nicolas3061b8a2010-10-22 16:34:52 +0000599% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
600% This a simpler method of setting filter support size that
601% will correctly handle the Sinc/Jinc switch for an operators
602% filtering requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000603%
nicolas3061b8a2010-10-22 16:34:52 +0000604% "filter:support" Set the support size for filtering to the size
605% given This not recommended for Sinc/Jinc windowed filters
606% (lobes should be used instead). This will override any
607% 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000608%
nicolas3061b8a2010-10-22 16:34:52 +0000609% "filter:win-support" Scale windowing function to this size
610% instead. This causes the windowing (or self-windowing
611% Lagrange filter) to act is if the support window it much much
612% larger than what is actually supplied to the calling
613% operator. The filter however is still clipped to the real
614% support size given, by the support range suppiled to the
615% caller. If unset this will equal the normal filter support
anthonyb6d08c52010-09-13 01:17:04 +0000616% size.
617%
nicolas3061b8a2010-10-22 16:34:52 +0000618% "filter:blur" Scale the filter and support window by this amount.
cristy3ed852e2009-09-05 21:47:34 +0000619% A value >1 will generally result in a more burred image with
620% more ringing effects, while a value <1 will sharpen the
anthony152700d2010-10-28 02:43:18 +0000621% resulting image with more aliasing effects.
cristy3ed852e2009-09-05 21:47:34 +0000622%
nicolas3061b8a2010-10-22 16:34:52 +0000623% "filter:sigma" The sigma value to use for the Gaussian filter
624% only. Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for
625% cylindrical usage. It effectially provides a alturnative to
626% 'blur' for Gaussians without it also effecting the final
627% 'practical support' size.
anthonyf5e76ef2010-10-12 01:22:01 +0000628%
cristy3ed852e2009-09-05 21:47:34 +0000629% "filter:b"
nicolas3061b8a2010-10-22 16:34:52 +0000630% "filter:c" Override the preset B,C values for a Cubic type of
631% filter If only one of these are given it is assumes to be a
632% 'Keys' type of filter such that B+2C=1, where Keys 'alpha'
633% value = C
cristy3ed852e2009-09-05 21:47:34 +0000634%
anthony06b1edf2010-10-25 01:19:50 +0000635% Examples:
cristy3ed852e2009-09-05 21:47:34 +0000636%
nicolas6e1267a2010-10-22 16:35:52 +0000637% Set a true un-windowed Sinc filter with 10 lobes (very slow):
anthony48f77622010-10-03 14:32:31 +0000638% -define filter:filter=Sinc
639% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000640%
nicolas6e1267a2010-10-22 16:35:52 +0000641% Set an 8 lobe Lanczos (Sinc or Jinc) filter:
cristy3ed852e2009-09-05 21:47:34 +0000642% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000643% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000644%
anthony06b1edf2010-10-25 01:19:50 +0000645%
cristy3ed852e2009-09-05 21:47:34 +0000646% The format of the AcquireResizeFilter method is:
647%
648% ResizeFilter *AcquireResizeFilter(const Image *image,
649% const FilterTypes filter_type, const MagickBooleanType radial,
650% ExceptionInfo *exception)
651%
cristy33b1c162010-01-23 22:51:51 +0000652% A description of each parameter follows:
653%
cristy3ed852e2009-09-05 21:47:34 +0000654% o image: the image.
655%
nicolas07bac812010-09-19 18:47:02 +0000656% o filter: the filter type, defining a preset filter, window and
657% support. The artifact settings listed above will override
658% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000659%
anthony48f77622010-10-03 14:32:31 +0000660% o blur: blur the filter by this amount, use 1.0 if unknown. Image
661% artifact "filter:blur" will override this API call usage, including
662% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000663%
anthony48f77622010-10-03 14:32:31 +0000664% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
665% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000666%
667% o exception: return any errors or warnings in this structure.
668%
669*/
670MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000671 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000672 const MagickBooleanType cylindrical,ExceptionInfo *exception)
673{
674 const char
675 *artifact;
676
677 FilterTypes
678 filter_type,
679 window_type;
680
cristy3ed852e2009-09-05 21:47:34 +0000681 MagickRealType
682 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000683 C,
684 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000685
686 register ResizeFilter
687 *resize_filter;
688
cristy9af9b5d2010-08-15 17:04:28 +0000689
cristy3ed852e2009-09-05 21:47:34 +0000690 /*
anthony48f77622010-10-03 14:32:31 +0000691 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000692 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000693 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
694 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
695 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000696
nicolas07bac812010-09-19 18:47:02 +0000697 WARNING: The order of this tabel must match the order of the
698 FilterTypes enumeration specified in "resample.h", or the filter
699 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000700
701 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000702 */
703 static struct
704 {
705 FilterTypes
706 filter,
707 window;
708 } const mapping[SentinelFilter] =
709 {
nicolasb7dff642010-10-25 02:04:14 +0000710 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
711 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
712 { BoxFilter, BoxFilter }, /* Box averaging filter */
713 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
714 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
715 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
716 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
anthony06b1edf2010-10-25 01:19:50 +0000717 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
nicolasb7dff642010-10-25 02:04:14 +0000718 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
719 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approx */
720 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
721 { CatromFilter, BoxFilter }, /* Cubic-Keys interpolator */
722 { MitchellFilter, BoxFilter }, /* 'Ideal' Cubic-Keys filter */
nicolasb7dff642010-10-25 02:04:14 +0000723 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
724 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony152700d2010-10-28 02:43:18 +0000725 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolasb7dff642010-10-25 02:04:14 +0000726 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
727 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
728 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
nicolasb7dff642010-10-25 02:04:14 +0000729 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
anthony06b1edf2010-10-25 01:19:50 +0000730 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
anthony152700d2010-10-28 02:43:18 +0000731 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing */
732 { LanczosFilter, LanczosFilter }, /* Lanczos Sinc-Sinc filters */
733 { LanczosSharpFilter, LanczosSharpFilter }, /* | these require */
734 { Lanczos2Filter, Lanczos2Filter }, /* | special handling */
735 { Lanczos2SharpFilter,Lanczos2SharpFilter },
736 { RobidouxFilter, BoxFilter }, /* Cubic Keys tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000737 };
738 /*
nicolas32f44eb2010-09-20 01:23:12 +0000739 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000740 function. The default support size for that filter as a weighting
741 function, the range to scale with to use that function as a sinc
742 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000743
anthony07a3f7f2010-09-16 03:03:11 +0000744 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000745 SincFast(), and CubicBC() functions, which may have multiple
746 filter to function associations.
747
748 See "filter:verbose" handling below for the function -> filter
749 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000750 */
751 static struct
752 {
753 MagickRealType
754 (*function)(const MagickRealType, const ResizeFilter*),
nicolas8eccc162010-10-16 19:48:13 +0000755 lobes, /* Default lobes/support size of the weighting filter. */
anthony450db502010-10-19 04:03:03 +0000756 scale, /* Support when function used as a windowing function
757 Typically equal to the location of the first zero crossing. */
758 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
cristy3ed852e2009-09-05 21:47:34 +0000759 } const filters[SentinelFilter] =
760 {
anthony61b5ddd2010-10-05 02:33:31 +0000761 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
762 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
763 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
764 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
765 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
766 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
767 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
768 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000769 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000770 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
771 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
772 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
nicolasd349be62010-10-28 18:57:38 +0000773 { CubicBC, 2.0, 8.0/7.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
nicolasf8689f42010-10-18 16:14:08 +0000774 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000775 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
anthony06b1edf2010-10-25 01:19:50 +0000776 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
anthony61b5ddd2010-10-05 02:33:31 +0000777 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
778 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
779 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
anthony61b5ddd2010-10-05 02:33:31 +0000780 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
781 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
anthony152700d2010-10-28 02:43:18 +0000782 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
783 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
784 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* lanczos, Sharpened */
785 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos, 2-lobed */
786 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2, sharpened */
anthony450db502010-10-19 04:03:03 +0000787 { CubicBC, 2.0, 1.1685777620836932,
nicolas0d5e5322010-10-22 15:29:30 +0000788 0.37821575509399867, 0.31089212245300067 }
789 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
cristy3ed852e2009-09-05 21:47:34 +0000790 };
791 /*
anthony9a98fc62010-10-11 02:47:19 +0000792 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
anthony450db502010-10-19 04:03:03 +0000793 function being used as a filter. It is used by the "filter:lobes" expert
794 setting and for 'lobes' for Jinc functions in the previous table. This
795 way users do not have to deal with the highly irrational lobe sizes of the
anthony9a98fc62010-10-11 02:47:19 +0000796 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000797
nicolase473f722010-10-07 00:05:13 +0000798 Values taken from
anthony48f77622010-10-03 14:32:31 +0000799 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000800 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000801 */
802 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000803 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000804 {
nicolas8eccc162010-10-16 19:48:13 +0000805 1.2196698912665045,
806 2.2331305943815286,
807 3.2383154841662362,
808 4.2410628637960699,
809 5.2427643768701817,
810 6.2439216898644877,
811 7.244759868719957,
812 8.2453949139520427,
813 9.2458926849494673,
814 10.246293348754916,
815 11.246622794877883,
816 12.246898461138105,
817 13.247132522181061,
818 14.247333735806849,
anthonyc2d07db2010-09-15 23:47:40 +0000819 15.2475085630373,
nicolas8eccc162010-10-16 19:48:13 +0000820 16.247661874700962
cristy3ed852e2009-09-05 21:47:34 +0000821 };
822
cristy33b1c162010-01-23 22:51:51 +0000823 /*
824 Allocate resize filter.
825 */
cristy3ed852e2009-09-05 21:47:34 +0000826 assert(image != (const Image *) NULL);
827 assert(image->signature == MagickSignature);
828 if (image->debug != MagickFalse)
829 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
830 assert(UndefinedFilter < filter && filter < SentinelFilter);
831 assert(exception != (ExceptionInfo *) NULL);
832 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000833 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000834 if (resize_filter == (ResizeFilter *) NULL)
835 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000836 /*
837 Defaults for the requested filter.
838 */
839 filter_type=mapping[filter].filter;
840 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000841 resize_filter->blur = blur;
842 sigma = 0.5;
anthony152700d2010-10-28 02:43:18 +0000843 /* Promote 1D Windowed Sinc Filters to a 2D Windowed Jinc filters */
844 if (cylindrical != MagickFalse && filter_type == SincFastFilter
845 && filter != SincFastFilter )
846 filter_type=JincFilter;
anthony61b5ddd2010-10-05 02:33:31 +0000847
anthony152700d2010-10-28 02:43:18 +0000848 /* Expert filter setting override */
cristy3ed852e2009-09-05 21:47:34 +0000849 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000850 if (artifact != (const char *) NULL)
851 {
anthony152700d2010-10-28 02:43:18 +0000852 ssize_t
853 option;
cristy9af9b5d2010-08-15 17:04:28 +0000854 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000855 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000856 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000857 filter_type=(FilterTypes) option;
858 window_type=BoxFilter;
859 }
nicolas07bac812010-09-19 18:47:02 +0000860 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000861 artifact=GetImageArtifact(image,"filter:window");
862 if (artifact != (const char *) NULL)
863 {
cristy9af9b5d2010-08-15 17:04:28 +0000864 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000865 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony152700d2010-10-28 02:43:18 +0000866 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000867 }
cristy3ed852e2009-09-05 21:47:34 +0000868 }
cristy33b1c162010-01-23 22:51:51 +0000869 else
870 {
anthony48f77622010-10-03 14:32:31 +0000871 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000872 artifact=GetImageArtifact(image,"filter:window");
873 if (artifact != (const char *) NULL)
874 {
anthony152700d2010-10-28 02:43:18 +0000875 ssize_t
876 option;
cristy33b1c162010-01-23 22:51:51 +0000877 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
878 artifact);
879 if ((UndefinedFilter < option) && (option < SentinelFilter))
880 {
anthony61b5ddd2010-10-05 02:33:31 +0000881 filter_type=cylindrical != MagickFalse ?
882 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000883 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000884 }
885 }
886 }
anthony152700d2010-10-28 02:43:18 +0000887
nicolas07bac812010-09-19 18:47:02 +0000888 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000889 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000890 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000891 resize_filter->window=filters[window_type].function;
892 resize_filter->scale=filters[window_type].scale;
anthony029ba0e2010-10-29 00:54:24 +0000893 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000894
anthonyf5e76ef2010-10-12 01:22:01 +0000895 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000896 if (cylindrical != MagickFalse)
897 switch (filter_type)
898 {
anthony10b8bc82010-10-02 12:48:46 +0000899 case BoxFilter:
900 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000901 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000902 break;
anthony152700d2010-10-28 02:43:18 +0000903 case LanczosFilter:
904 case LanczosSharpFilter:
905 case Lanczos2Filter:
906 case Lanczos2SharpFilter:
907 resize_filter->filter=filters[JincFilter].function;
908 resize_filter->window=filters[JincFilter].function;
anthony029ba0e2010-10-29 00:54:24 +0000909 resize_filter->scale=filters[JincFilter].scale;
910 /* number of lobes (support window size) remain unchanged */
anthony152700d2010-10-28 02:43:18 +0000911 break;
912 case GaussianFilter:
913 /* Cylindrical Gaussian sigma is sqrt(2)/2. */
914 sigma = (MagickRealType) (MagickSQ2/2.0);
915 break;
anthony81b8bf92010-10-02 13:54:34 +0000916 default:
917 break;
anthony10b8bc82010-10-02 12:48:46 +0000918 }
anthony152700d2010-10-28 02:43:18 +0000919 /* Global Sharpening (regardless of orthoginal/cylindrical) */
920 switch (filter_type)
921 {
922 case LanczosSharpFilter:
nicolasaf6ee7c2010-11-13 03:34:22 +0000923 resize_filter->blur *= 0.9812505644269356;
anthony152700d2010-10-28 02:43:18 +0000924 break;
925 case Lanczos2SharpFilter:
nicolasca48bce2010-11-14 17:54:53 +0000926 resize_filter->blur *= 0.9549963639785485;
anthony152700d2010-10-28 02:43:18 +0000927 break;
928 default:
929 break;
930 }
anthony61b5ddd2010-10-05 02:33:31 +0000931
anthonyf5e76ef2010-10-12 01:22:01 +0000932 /*
anthony06b1edf2010-10-25 01:19:50 +0000933 ** Other Expert Option Modifications
anthonyf5e76ef2010-10-12 01:22:01 +0000934 */
935
936 /* User Sigma Override - no support change */
937 artifact=GetImageArtifact(image,"filter:sigma");
938 if (artifact != (const char *) NULL)
939 sigma=StringToDouble(artifact);
940 /* Define coefficents for Gaussian (assumes no cubic window) */
941 if ( GaussianFilter ) {
cristy5cce74b2010-11-15 03:24:28 +0000942 resize_filter->coefficient[0]=1.0/(2.0*sigma*sigma);
943 resize_filter->coefficient[1]=(MagickRealType) (1.0/(Magick2PI*sigma*
944 sigma)); /* unused */
anthonyf5e76ef2010-10-12 01:22:01 +0000945 }
946
947 /* Blur Override */
948 artifact=GetImageArtifact(image,"filter:blur");
949 if (artifact != (const char *) NULL)
950 resize_filter->blur=StringToDouble(artifact);
951 if (resize_filter->blur < MagickEpsilon)
952 resize_filter->blur=(MagickRealType) MagickEpsilon;
953
954 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000955 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000956 if (artifact != (const char *) NULL)
957 {
cristybb503372010-05-27 20:51:26 +0000958 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000959 lobes;
960
cristy96b16132010-08-29 17:19:52 +0000961 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000962 if (lobes < 1)
963 lobes=1;
964 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000965 }
anthony152700d2010-10-28 02:43:18 +0000966 /* Convert a Jinc function lobes value to a real support value */
anthony61b5ddd2010-10-05 02:33:31 +0000967 if (resize_filter->filter == Jinc)
968 {
969 if (resize_filter->support > 16)
970 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
971 else
972 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
973 }
974 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000975 artifact=GetImageArtifact(image,"filter:support");
976 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000977 resize_filter->support=fabs(StringToDouble(artifact));
978 /*
nicolas07bac812010-09-19 18:47:02 +0000979 Scale windowing function separatally to the support 'clipping'
980 window that calling operator is planning to actually use. (Expert
981 override)
cristy3ed852e2009-09-05 21:47:34 +0000982 */
anthony55f12332010-09-10 01:13:02 +0000983 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000984 artifact=GetImageArtifact(image,"filter:win-support");
985 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000986 resize_filter->window_support=fabs(StringToDouble(artifact));
987 /*
anthony029ba0e2010-10-29 00:54:24 +0000988 Adjust window function scaling to match windowing support for
anthony1f90a6b2010-09-14 08:56:31 +0000989 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000990 */
991 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +0000992
anthony55f12332010-09-10 01:13:02 +0000993 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000994 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000995 */
cristy3ed852e2009-09-05 21:47:34 +0000996 B=0.0;
997 C=0.0;
cristy33b1c162010-01-23 22:51:51 +0000998 if ((filters[filter_type].function == CubicBC) ||
999 (filters[window_type].function == CubicBC))
1000 {
anthony2d9b8b52010-09-14 08:31:07 +00001001 B=filters[filter_type].B;
1002 C=filters[filter_type].C;
1003 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +00001004 {
anthony2d9b8b52010-09-14 08:31:07 +00001005 B=filters[window_type].B;
1006 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +00001007 }
cristy33b1c162010-01-23 22:51:51 +00001008 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001009 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001010 {
1011 B=StringToDouble(artifact);
nicolasd15ee9a2010-10-24 18:39:45 +00001012 C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001013 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001014 if (artifact != (const char *) NULL)
1015 C=StringToDouble(artifact);
1016 }
1017 else
1018 {
1019 artifact=GetImageArtifact(image,"filter:c");
1020 if (artifact != (const char *) NULL)
1021 {
1022 C=StringToDouble(artifact);
nicolasb7dff642010-10-25 02:04:14 +00001023 B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001024 }
1025 }
nicolasc6bac3b2010-10-24 18:10:45 +00001026 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
nicolasd15ee9a2010-10-24 18:39:45 +00001027 {
anthony06b1edf2010-10-25 01:19:50 +00001028 const double twoB = B+B;
cristy5cce74b2010-11-15 03:24:28 +00001029 resize_filter->coefficient[0]=1.0-(1.0/3.0)*B;
1030 resize_filter->coefficient[1]=-3.0+twoB+C;
1031 resize_filter->coefficient[2]=2.0-1.5*B-C;
1032 resize_filter->coefficient[3]=(4.0/3.0)*B+4.0*C;
1033 resize_filter->coefficient[4]=-8.0*C-twoB;
1034 resize_filter->coefficient[5]=B+5.0*C;
1035 resize_filter->coefficient[6]=(-1.0/6.0)*B-C;
nicolasd15ee9a2010-10-24 18:39:45 +00001036 }
nicolasc6bac3b2010-10-24 18:10:45 +00001037 }
anthonyf5e76ef2010-10-12 01:22:01 +00001038
anthony55f12332010-09-10 01:13:02 +00001039 /*
nicolas07bac812010-09-19 18:47:02 +00001040 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001041 */
cristyf5b49372010-10-16 01:06:47 +00001042#if defined(MAGICKCORE_OPENMP_SUPPORT)
1043 #pragma omp master
1044 {
1045#endif
1046 artifact=GetImageArtifact(image,"filter:verbose");
anthony28ad1d72010-10-26 06:30:24 +00001047 if (IsMagickTrue(artifact))
cristyf5b49372010-10-16 01:06:47 +00001048 {
1049 double
anthony06b1edf2010-10-25 01:19:50 +00001050 support,
cristyf5b49372010-10-16 01:06:47 +00001051 x;
cristy3ed852e2009-09-05 21:47:34 +00001052
cristyf5b49372010-10-16 01:06:47 +00001053 /*
1054 Set the weighting function properly when the weighting
1055 function may not exactly match the filter of the same name.
anthony06b1edf2010-10-25 01:19:50 +00001056 EG: a Point filter is really uses a Box weighting function
cristyf5b49372010-10-16 01:06:47 +00001057 with a different support than is typically used.
cristyf5b49372010-10-16 01:06:47 +00001058 */
1059 if (resize_filter->filter == Box) filter_type=BoxFilter;
1060 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1061 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1062 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1063 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
anthony1ad20052010-10-29 01:53:03 +00001064 if (resize_filter->window == Box) window_type=BoxFilter;
anthony152700d2010-10-28 02:43:18 +00001065 if (resize_filter->window == Sinc) window_type=SincFilter;
1066 if (resize_filter->window == SincFast) window_type=SincFastFilter;
anthony1ad20052010-10-29 01:53:03 +00001067 if (resize_filter->window == Jinc) window_type=JincFilter;
anthony152700d2010-10-28 02:43:18 +00001068 if (resize_filter->window == CubicBC) window_type=CubicFilter;
cristyf5b49372010-10-16 01:06:47 +00001069 /*
1070 Report Filter Details.
1071 */
1072 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1073 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
anthony06b1edf2010-10-25 01:19:50 +00001074 (void) fprintf(stdout,"# filter = %s\n",
1075 MagickOptionToMnemonic(MagickFilterOptions,filter_type));
1076 (void) fprintf(stdout,"# window = %s\n",
1077 MagickOptionToMnemonic(MagickFilterOptions, window_type));
1078 (void) fprintf(stdout,"# support = %.*g\n",
1079 GetMagickPrecision(),(double) resize_filter->support);
1080 (void) fprintf(stdout,"# win-support = %.*g\n",
1081 GetMagickPrecision(),(double) resize_filter->window_support);
1082 (void) fprintf(stdout,"# scale_blur = %.*g\n",
1083 GetMagickPrecision(), (double)resize_filter->blur);
cristyf5b49372010-10-16 01:06:47 +00001084 if ( filter_type == GaussianFilter )
anthony06b1edf2010-10-25 01:19:50 +00001085 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",
1086 GetMagickPrecision(), (double)sigma);
1087 (void) fprintf(stdout,"# practical_support = %.*g\n",
1088 GetMagickPrecision(), (double)support);
cristyf5b49372010-10-16 01:06:47 +00001089 if ( filter_type == CubicFilter || window_type == CubicFilter )
anthony06b1edf2010-10-25 01:19:50 +00001090 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",
1091 GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
cristyf5b49372010-10-16 01:06:47 +00001092 (void) fprintf(stdout,"\n");
1093 /*
1094 Output values of resulting filter graph -- for graphing
1095 filter result.
1096 */
1097 for (x=0.0; x <= support; x+=0.01f)
1098 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1099 (double) GetResizeFilterWeight(resize_filter,x));
1100 /* A final value so gnuplot can graph the 'stop' properly. */
1101 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1102 0.0);
1103 }
1104 /* Output the above once only for each image - remove setting */
1105 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1106#if defined(MAGICKCORE_OPENMP_SUPPORT)
1107 }
1108#endif
cristy3ed852e2009-09-05 21:47:34 +00001109 return(resize_filter);
1110}
1111
1112/*
1113%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1114% %
1115% %
1116% %
1117% A d a p t i v e R e s i z e I m a g e %
1118% %
1119% %
1120% %
1121%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1122%
1123% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1124%
1125% The format of the AdaptiveResizeImage method is:
1126%
cristy9af9b5d2010-08-15 17:04:28 +00001127% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1128% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001129%
1130% A description of each parameter follows:
1131%
1132% o image: the image.
1133%
1134% o columns: the number of columns in the resized image.
1135%
1136% o rows: the number of rows in the resized image.
1137%
1138% o exception: return any errors or warnings in this structure.
1139%
1140*/
1141MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001142 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001143{
1144#define AdaptiveResizeImageTag "Resize/Image"
1145
cristyc4c8d132010-01-07 01:58:38 +00001146 CacheView
1147 *resize_view;
1148
cristy3ed852e2009-09-05 21:47:34 +00001149 Image
1150 *resize_image;
1151
cristy3ed852e2009-09-05 21:47:34 +00001152 MagickBooleanType
cristy5cce74b2010-11-15 03:24:28 +00001153 status;
cristy3ed852e2009-09-05 21:47:34 +00001154
cristy5cce74b2010-11-15 03:24:28 +00001155 MagickOffsetType
1156 progress;
cristy3ed852e2009-09-05 21:47:34 +00001157
1158 ResampleFilter
cristya6a18782010-11-15 01:56:25 +00001159 **resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00001160
cristy9af9b5d2010-08-15 17:04:28 +00001161 ssize_t
1162 y;
1163
cristy3ed852e2009-09-05 21:47:34 +00001164 /*
1165 Adaptively resize image.
1166 */
1167 assert(image != (const Image *) NULL);
1168 assert(image->signature == MagickSignature);
1169 if (image->debug != MagickFalse)
1170 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1171 assert(exception != (ExceptionInfo *) NULL);
1172 assert(exception->signature == MagickSignature);
1173 if ((columns == 0) || (rows == 0))
1174 return((Image *) NULL);
1175 if ((columns == image->columns) && (rows == image->rows))
1176 return(CloneImage(image,0,0,MagickTrue,exception));
1177 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1178 if (resize_image == (Image *) NULL)
1179 return((Image *) NULL);
1180 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1181 {
1182 InheritException(exception,&resize_image->exception);
1183 resize_image=DestroyImage(resize_image);
1184 return((Image *) NULL);
1185 }
cristy5cce74b2010-11-15 03:24:28 +00001186 status=MagickTrue;
1187 progress=0;
cristya6a18782010-11-15 01:56:25 +00001188 resample_filter=AcquireResampleFilterThreadSet(image,
1189 UndefinedVirtualPixelMethod,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00001190 resize_view=AcquireCacheView(resize_image);
cristy5cce74b2010-11-15 03:24:28 +00001191#if defined(MAGICKCORE_OPENMP_SUPPORT)
1192 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
1193#endif
cristybb503372010-05-27 20:51:26 +00001194 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001195 {
cristya6a18782010-11-15 01:56:25 +00001196 const int
1197 id = GetOpenMPThreadId();
1198
cristy5cce74b2010-11-15 03:24:28 +00001199 MagickPixelPacket
1200 pixel;
1201
1202 PointInfo
1203 offset;
1204
cristy3ed852e2009-09-05 21:47:34 +00001205 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001206 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001207
cristybb503372010-05-27 20:51:26 +00001208 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001209 x;
1210
1211 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001212 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001213
cristy5cce74b2010-11-15 03:24:28 +00001214 if (status == MagickFalse)
1215 continue;
cristy3ed852e2009-09-05 21:47:34 +00001216 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1217 exception);
1218 if (q == (PixelPacket *) NULL)
cristy5cce74b2010-11-15 03:24:28 +00001219 continue;
cristy3ed852e2009-09-05 21:47:34 +00001220 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1221 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristy5cce74b2010-11-15 03:24:28 +00001222 GetMagickPixelPacket(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001223 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001224 {
1225 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
cristya6a18782010-11-15 01:56:25 +00001226 (void) ResamplePixelColor(resample_filter[id],offset.x-0.5,offset.y-0.5,
cristy3ed852e2009-09-05 21:47:34 +00001227 &pixel);
1228 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1229 q++;
1230 }
1231 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
cristy5cce74b2010-11-15 03:24:28 +00001232 continue;
1233 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1234 {
1235 MagickBooleanType
1236 proceed;
1237
1238#if defined(MAGICKCORE_OPENMP_SUPPORT)
1239 #pragma omp critical (MagickCore_AdaptiveResizeImage)
1240#endif
1241 proceed=SetImageProgress(image,AdaptiveResizeImageTag,progress++,
1242 image->rows);
1243 if (proceed == MagickFalse)
1244 status=MagickFalse;
1245 }
cristy3ed852e2009-09-05 21:47:34 +00001246 }
cristya6a18782010-11-15 01:56:25 +00001247 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
cristy3ed852e2009-09-05 21:47:34 +00001248 resize_view=DestroyCacheView(resize_view);
cristy5cce74b2010-11-15 03:24:28 +00001249 if (status == MagickFalse)
1250 resize_image=DestroyImage(resize_image);
cristy3ed852e2009-09-05 21:47:34 +00001251 return(resize_image);
1252}
1253
1254/*
1255%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1256% %
1257% %
1258% %
1259+ B e s s e l O r d e r O n e %
1260% %
1261% %
1262% %
1263%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1264%
1265% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001266% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001267%
1268% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1269%
1270% j1(x) = x*j1(x);
1271%
1272% For x in (8,inf)
1273%
1274% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1275%
1276% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1277%
1278% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1279% = 1/sqrt(2) * (sin(x) - cos(x))
1280% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1281% = -1/sqrt(2) * (sin(x) + cos(x))
1282%
1283% The format of the BesselOrderOne method is:
1284%
1285% MagickRealType BesselOrderOne(MagickRealType x)
1286%
1287% A description of each parameter follows:
1288%
1289% o x: MagickRealType value.
1290%
1291*/
1292
1293#undef I0
1294static MagickRealType I0(MagickRealType x)
1295{
1296 MagickRealType
1297 sum,
1298 t,
1299 y;
1300
cristybb503372010-05-27 20:51:26 +00001301 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001302 i;
1303
1304 /*
1305 Zeroth order Bessel function of the first kind.
1306 */
1307 sum=1.0;
1308 y=x*x/4.0;
1309 t=y;
1310 for (i=2; t > MagickEpsilon; i++)
1311 {
1312 sum+=t;
1313 t*=y/((MagickRealType) i*i);
1314 }
1315 return(sum);
1316}
1317
1318#undef J1
1319static MagickRealType J1(MagickRealType x)
1320{
1321 MagickRealType
1322 p,
1323 q;
1324
cristybb503372010-05-27 20:51:26 +00001325 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001326 i;
1327
1328 static const double
1329 Pone[] =
1330 {
1331 0.581199354001606143928050809e+21,
1332 -0.6672106568924916298020941484e+20,
1333 0.2316433580634002297931815435e+19,
1334 -0.3588817569910106050743641413e+17,
1335 0.2908795263834775409737601689e+15,
1336 -0.1322983480332126453125473247e+13,
1337 0.3413234182301700539091292655e+10,
1338 -0.4695753530642995859767162166e+7,
1339 0.270112271089232341485679099e+4
1340 },
1341 Qone[] =
1342 {
1343 0.11623987080032122878585294e+22,
1344 0.1185770712190320999837113348e+20,
1345 0.6092061398917521746105196863e+17,
1346 0.2081661221307607351240184229e+15,
1347 0.5243710262167649715406728642e+12,
1348 0.1013863514358673989967045588e+10,
1349 0.1501793594998585505921097578e+7,
1350 0.1606931573481487801970916749e+4,
1351 0.1e+1
1352 };
1353
1354 p=Pone[8];
1355 q=Qone[8];
1356 for (i=7; i >= 0; i--)
1357 {
1358 p=p*x*x+Pone[i];
1359 q=q*x*x+Qone[i];
1360 }
1361 return(p/q);
1362}
1363
1364#undef P1
1365static MagickRealType P1(MagickRealType x)
1366{
1367 MagickRealType
1368 p,
1369 q;
1370
cristybb503372010-05-27 20:51:26 +00001371 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001372 i;
1373
1374 static const double
1375 Pone[] =
1376 {
1377 0.352246649133679798341724373e+5,
1378 0.62758845247161281269005675e+5,
1379 0.313539631109159574238669888e+5,
1380 0.49854832060594338434500455e+4,
1381 0.2111529182853962382105718e+3,
1382 0.12571716929145341558495e+1
1383 },
1384 Qone[] =
1385 {
1386 0.352246649133679798068390431e+5,
1387 0.626943469593560511888833731e+5,
1388 0.312404063819041039923015703e+5,
1389 0.4930396490181088979386097e+4,
1390 0.2030775189134759322293574e+3,
1391 0.1e+1
1392 };
1393
1394 p=Pone[5];
1395 q=Qone[5];
1396 for (i=4; i >= 0; i--)
1397 {
1398 p=p*(8.0/x)*(8.0/x)+Pone[i];
1399 q=q*(8.0/x)*(8.0/x)+Qone[i];
1400 }
1401 return(p/q);
1402}
1403
1404#undef Q1
1405static MagickRealType Q1(MagickRealType x)
1406{
1407 MagickRealType
1408 p,
1409 q;
1410
cristybb503372010-05-27 20:51:26 +00001411 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001412 i;
1413
1414 static const double
1415 Pone[] =
1416 {
1417 0.3511751914303552822533318e+3,
1418 0.7210391804904475039280863e+3,
1419 0.4259873011654442389886993e+3,
1420 0.831898957673850827325226e+2,
1421 0.45681716295512267064405e+1,
1422 0.3532840052740123642735e-1
1423 },
1424 Qone[] =
1425 {
1426 0.74917374171809127714519505e+4,
1427 0.154141773392650970499848051e+5,
1428 0.91522317015169922705904727e+4,
1429 0.18111867005523513506724158e+4,
1430 0.1038187585462133728776636e+3,
1431 0.1e+1
1432 };
1433
1434 p=Pone[5];
1435 q=Qone[5];
1436 for (i=4; i >= 0; i--)
1437 {
1438 p=p*(8.0/x)*(8.0/x)+Pone[i];
1439 q=q*(8.0/x)*(8.0/x)+Qone[i];
1440 }
1441 return(p/q);
1442}
1443
1444static MagickRealType BesselOrderOne(MagickRealType x)
1445{
1446 MagickRealType
1447 p,
1448 q;
1449
1450 if (x == 0.0)
1451 return(0.0);
1452 p=x;
1453 if (x < 0.0)
1454 x=(-x);
1455 if (x < 8.0)
1456 return(p*J1(x));
1457 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1458 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1459 cos((double) x))));
1460 if (p < 0.0)
1461 q=(-q);
1462 return(q);
1463}
1464
1465/*
1466%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1467% %
1468% %
1469% %
1470+ D e s t r o y R e s i z e F i l t e r %
1471% %
1472% %
1473% %
1474%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1475%
1476% DestroyResizeFilter() destroy the resize filter.
1477%
cristya2ffd7e2010-03-10 20:50:30 +00001478% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001479%
1480% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1481%
1482% A description of each parameter follows:
1483%
1484% o resize_filter: the resize filter.
1485%
1486*/
1487MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1488{
1489 assert(resize_filter != (ResizeFilter *) NULL);
1490 assert(resize_filter->signature == MagickSignature);
1491 resize_filter->signature=(~MagickSignature);
1492 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1493 return(resize_filter);
1494}
1495
1496/*
1497%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1498% %
1499% %
1500% %
1501+ G e t R e s i z e F i l t e r S u p p o r t %
1502% %
1503% %
1504% %
1505%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1506%
1507% GetResizeFilterSupport() return the current support window size for this
1508% filter. Note that this may have been enlarged by filter:blur factor.
1509%
1510% The format of the GetResizeFilterSupport method is:
1511%
1512% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1513%
1514% A description of each parameter follows:
1515%
1516% o filter: Image filter to use.
1517%
1518*/
1519MagickExport MagickRealType GetResizeFilterSupport(
1520 const ResizeFilter *resize_filter)
1521{
1522 assert(resize_filter != (ResizeFilter *) NULL);
1523 assert(resize_filter->signature == MagickSignature);
1524 return(resize_filter->support*resize_filter->blur);
1525}
1526
1527/*
1528%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1529% %
1530% %
1531% %
1532+ G e t R e s i z e F i l t e r W e i g h t %
1533% %
1534% %
1535% %
1536%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1537%
1538% GetResizeFilterWeight evaluates the specified resize filter at the point x
1539% which usally lies between zero and the filters current 'support' and
1540% returns the weight of the filter function at that point.
1541%
1542% The format of the GetResizeFilterWeight method is:
1543%
1544% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1545% const MagickRealType x)
1546%
1547% A description of each parameter follows:
1548%
1549% o filter: the filter type.
1550%
1551% o x: the point.
1552%
1553*/
1554MagickExport MagickRealType GetResizeFilterWeight(
1555 const ResizeFilter *resize_filter,const MagickRealType x)
1556{
1557 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001558 scale,
1559 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001560
1561 /*
1562 Windowing function - scale the weighting filter by this amount.
1563 */
1564 assert(resize_filter != (ResizeFilter *) NULL);
1565 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001566 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001567 if ((resize_filter->window_support < MagickEpsilon) ||
1568 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001569 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001570 else
1571 {
anthony55f12332010-09-10 01:13:02 +00001572 scale=resize_filter->scale;
1573 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001574 }
anthony55f12332010-09-10 01:13:02 +00001575 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001576}
1577
1578/*
1579%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1580% %
1581% %
1582% %
1583% M a g n i f y I m a g e %
1584% %
1585% %
1586% %
1587%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1588%
1589% MagnifyImage() is a convenience method that scales an image proportionally
1590% to twice its size.
1591%
1592% The format of the MagnifyImage method is:
1593%
1594% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1595%
1596% A description of each parameter follows:
1597%
1598% o image: the image.
1599%
1600% o exception: return any errors or warnings in this structure.
1601%
1602*/
1603MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1604{
1605 Image
1606 *magnify_image;
1607
1608 assert(image != (Image *) NULL);
1609 assert(image->signature == MagickSignature);
1610 if (image->debug != MagickFalse)
1611 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1612 assert(exception != (ExceptionInfo *) NULL);
1613 assert(exception->signature == MagickSignature);
1614 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1615 1.0,exception);
1616 return(magnify_image);
1617}
1618
1619/*
1620%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1621% %
1622% %
1623% %
1624% M i n i f y I m a g e %
1625% %
1626% %
1627% %
1628%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1629%
1630% MinifyImage() is a convenience method that scales an image proportionally
1631% to half its size.
1632%
1633% The format of the MinifyImage method is:
1634%
1635% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1636%
1637% A description of each parameter follows:
1638%
1639% o image: the image.
1640%
1641% o exception: return any errors or warnings in this structure.
1642%
1643*/
1644MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1645{
1646 Image
1647 *minify_image;
1648
1649 assert(image != (Image *) NULL);
1650 assert(image->signature == MagickSignature);
1651 if (image->debug != MagickFalse)
1652 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1653 assert(exception != (ExceptionInfo *) NULL);
1654 assert(exception->signature == MagickSignature);
1655 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1656 1.0,exception);
1657 return(minify_image);
1658}
1659
1660/*
1661%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1662% %
1663% %
1664% %
1665% R e s a m p l e I m a g e %
1666% %
1667% %
1668% %
1669%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1670%
1671% ResampleImage() resize image in terms of its pixel size, so that when
1672% displayed at the given resolution it will be the same size in terms of
1673% real world units as the original image at the original resolution.
1674%
1675% The format of the ResampleImage method is:
1676%
1677% Image *ResampleImage(Image *image,const double x_resolution,
1678% const double y_resolution,const FilterTypes filter,const double blur,
1679% ExceptionInfo *exception)
1680%
1681% A description of each parameter follows:
1682%
1683% o image: the image to be resized to fit the given resolution.
1684%
1685% o x_resolution: the new image x resolution.
1686%
1687% o y_resolution: the new image y resolution.
1688%
1689% o filter: Image filter to use.
1690%
1691% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1692%
1693*/
1694MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1695 const double y_resolution,const FilterTypes filter,const double blur,
1696 ExceptionInfo *exception)
1697{
1698#define ResampleImageTag "Resample/Image"
1699
1700 Image
1701 *resample_image;
1702
cristybb503372010-05-27 20:51:26 +00001703 size_t
cristy3ed852e2009-09-05 21:47:34 +00001704 height,
1705 width;
1706
1707 /*
1708 Initialize sampled image attributes.
1709 */
1710 assert(image != (const Image *) NULL);
1711 assert(image->signature == MagickSignature);
1712 if (image->debug != MagickFalse)
1713 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1714 assert(exception != (ExceptionInfo *) NULL);
1715 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001716 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1717 72.0 : image->x_resolution)+0.5);
1718 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1719 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001720 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1721 if (resample_image != (Image *) NULL)
1722 {
1723 resample_image->x_resolution=x_resolution;
1724 resample_image->y_resolution=y_resolution;
1725 }
1726 return(resample_image);
1727}
1728#if defined(MAGICKCORE_LQR_DELEGATE)
1729
1730/*
1731%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1732% %
1733% %
1734% %
1735% L i q u i d R e s c a l e I m a g e %
1736% %
1737% %
1738% %
1739%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1740%
1741% LiquidRescaleImage() rescales image with seam carving.
1742%
1743% The format of the LiquidRescaleImage method is:
1744%
1745% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001746% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001747% const double delta_x,const double rigidity,ExceptionInfo *exception)
1748%
1749% A description of each parameter follows:
1750%
1751% o image: the image.
1752%
1753% o columns: the number of columns in the rescaled image.
1754%
1755% o rows: the number of rows in the rescaled image.
1756%
1757% o delta_x: maximum seam transversal step (0 means straight seams).
1758%
1759% o rigidity: introduce a bias for non-straight seams (typically 0).
1760%
1761% o exception: return any errors or warnings in this structure.
1762%
1763*/
cristy9af9b5d2010-08-15 17:04:28 +00001764MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1765 const size_t rows,const double delta_x,const double rigidity,
1766 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001767{
1768#define LiquidRescaleImageTag "Rescale/Image"
1769
cristyc5c6f662010-09-22 14:23:02 +00001770 CacheView
1771 *rescale_view;
1772
cristy3ed852e2009-09-05 21:47:34 +00001773 const char
1774 *map;
1775
1776 guchar
1777 *packet;
1778
1779 Image
1780 *rescale_image;
1781
1782 int
1783 x,
1784 y;
1785
1786 LqrCarver
1787 *carver;
1788
1789 LqrRetVal
1790 lqr_status;
1791
1792 MagickBooleanType
1793 status;
1794
1795 MagickPixelPacket
1796 pixel;
1797
1798 unsigned char
1799 *pixels;
1800
1801 /*
1802 Liquid rescale image.
1803 */
1804 assert(image != (const Image *) NULL);
1805 assert(image->signature == MagickSignature);
1806 if (image->debug != MagickFalse)
1807 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1808 assert(exception != (ExceptionInfo *) NULL);
1809 assert(exception->signature == MagickSignature);
1810 if ((columns == 0) || (rows == 0))
1811 return((Image *) NULL);
1812 if ((columns == image->columns) && (rows == image->rows))
1813 return(CloneImage(image,0,0,MagickTrue,exception));
1814 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001815 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001816 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1817 {
1818 Image
1819 *resize_image;
1820
cristybb503372010-05-27 20:51:26 +00001821 size_t
cristy3ed852e2009-09-05 21:47:34 +00001822 height,
1823 width;
1824
1825 /*
1826 Honor liquid resize size limitations.
1827 */
1828 for (width=image->columns; columns >= (2*width-1); width*=2);
1829 for (height=image->rows; rows >= (2*height-1); height*=2);
1830 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1831 exception);
1832 if (resize_image == (Image *) NULL)
1833 return((Image *) NULL);
1834 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1835 rigidity,exception);
1836 resize_image=DestroyImage(resize_image);
1837 return(rescale_image);
1838 }
1839 map="RGB";
1840 if (image->matte == MagickFalse)
1841 map="RGBA";
1842 if (image->colorspace == CMYKColorspace)
1843 {
1844 map="CMYK";
1845 if (image->matte == MagickFalse)
1846 map="CMYKA";
1847 }
1848 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1849 strlen(map)*sizeof(*pixels));
1850 if (pixels == (unsigned char *) NULL)
1851 return((Image *) NULL);
1852 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1853 pixels,exception);
1854 if (status == MagickFalse)
1855 {
1856 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1857 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1858 }
1859 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1860 if (carver == (LqrCarver *) NULL)
1861 {
1862 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1863 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1864 }
1865 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1866 lqr_status=lqr_carver_resize(carver,columns,rows);
1867 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1868 lqr_carver_get_height(carver),MagickTrue,exception);
1869 if (rescale_image == (Image *) NULL)
1870 {
1871 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1872 return((Image *) NULL);
1873 }
1874 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1875 {
1876 InheritException(exception,&rescale_image->exception);
1877 rescale_image=DestroyImage(rescale_image);
1878 return((Image *) NULL);
1879 }
1880 GetMagickPixelPacket(rescale_image,&pixel);
1881 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001882 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001883 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1884 {
1885 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001886 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001887
1888 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001889 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001890
anthony22aad252010-09-23 06:59:07 +00001891 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001892 if (q == (PixelPacket *) NULL)
1893 break;
cristyc5c6f662010-09-22 14:23:02 +00001894 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001895 pixel.red=QuantumRange*(packet[0]/255.0);
1896 pixel.green=QuantumRange*(packet[1]/255.0);
1897 pixel.blue=QuantumRange*(packet[2]/255.0);
1898 if (image->colorspace != CMYKColorspace)
1899 {
1900 if (image->matte == MagickFalse)
1901 pixel.opacity=QuantumRange*(packet[3]/255.0);
1902 }
1903 else
1904 {
1905 pixel.index=QuantumRange*(packet[3]/255.0);
1906 if (image->matte == MagickFalse)
1907 pixel.opacity=QuantumRange*(packet[4]/255.0);
1908 }
1909 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001910 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001911 break;
1912 }
cristyc5c6f662010-09-22 14:23:02 +00001913 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001914 /*
1915 Relinquish resources.
1916 */
1917 lqr_carver_destroy(carver);
1918 return(rescale_image);
1919}
1920#else
1921MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001922 const size_t magick_unused(columns),const size_t magick_unused(rows),
1923 const double magick_unused(delta_x),const double magick_unused(rigidity),
1924 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001925{
1926 assert(image != (const Image *) NULL);
1927 assert(image->signature == MagickSignature);
1928 if (image->debug != MagickFalse)
1929 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1930 assert(exception != (ExceptionInfo *) NULL);
1931 assert(exception->signature == MagickSignature);
1932 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1933 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1934 return((Image *) NULL);
1935}
1936#endif
1937
1938/*
1939%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1940% %
1941% %
1942% %
1943% R e s i z e I m a g e %
1944% %
1945% %
1946% %
1947%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1948%
1949% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001950% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001951%
1952% If an undefined filter is given the filter defaults to Mitchell for a
1953% colormapped image, a image with a matte channel, or if the image is
1954% enlarged. Otherwise the filter defaults to a Lanczos.
1955%
1956% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1957%
1958% The format of the ResizeImage method is:
1959%
cristybb503372010-05-27 20:51:26 +00001960% Image *ResizeImage(Image *image,const size_t columns,
1961% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001962% ExceptionInfo *exception)
1963%
1964% A description of each parameter follows:
1965%
1966% o image: the image.
1967%
1968% o columns: the number of columns in the scaled image.
1969%
1970% o rows: the number of rows in the scaled image.
1971%
1972% o filter: Image filter to use.
1973%
cristy9af9b5d2010-08-15 17:04:28 +00001974% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1975% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001976%
1977% o exception: return any errors or warnings in this structure.
1978%
1979*/
1980
1981typedef struct _ContributionInfo
1982{
1983 MagickRealType
1984 weight;
1985
cristybb503372010-05-27 20:51:26 +00001986 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001987 pixel;
1988} ContributionInfo;
1989
1990static ContributionInfo **DestroyContributionThreadSet(
1991 ContributionInfo **contribution)
1992{
cristybb503372010-05-27 20:51:26 +00001993 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001994 i;
1995
1996 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001997 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001998 if (contribution[i] != (ContributionInfo *) NULL)
1999 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
2000 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00002001 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00002002 return(contribution);
2003}
2004
2005static ContributionInfo **AcquireContributionThreadSet(const size_t count)
2006{
cristybb503372010-05-27 20:51:26 +00002007 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002008 i;
2009
2010 ContributionInfo
2011 **contribution;
2012
cristybb503372010-05-27 20:51:26 +00002013 size_t
cristy3ed852e2009-09-05 21:47:34 +00002014 number_threads;
2015
2016 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002017 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00002018 sizeof(*contribution));
2019 if (contribution == (ContributionInfo **) NULL)
2020 return((ContributionInfo **) NULL);
2021 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00002022 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002023 {
2024 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2025 sizeof(**contribution));
2026 if (contribution[i] == (ContributionInfo *) NULL)
2027 return(DestroyContributionThreadSet(contribution));
2028 }
2029 return(contribution);
2030}
2031
2032static inline double MagickMax(const double x,const double y)
2033{
2034 if (x > y)
2035 return(x);
2036 return(y);
2037}
2038
2039static inline double MagickMin(const double x,const double y)
2040{
2041 if (x < y)
2042 return(x);
2043 return(y);
2044}
2045
2046static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2047 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002048 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002049{
2050#define ResizeImageTag "Resize/Image"
2051
cristyfa112112010-01-04 17:48:07 +00002052 CacheView
2053 *image_view,
2054 *resize_view;
2055
cristy3ed852e2009-09-05 21:47:34 +00002056 ClassType
2057 storage_class;
2058
2059 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002060 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002061
cristy3ed852e2009-09-05 21:47:34 +00002062 MagickBooleanType
2063 status;
2064
2065 MagickPixelPacket
2066 zero;
2067
2068 MagickRealType
2069 scale,
2070 support;
2071
cristy9af9b5d2010-08-15 17:04:28 +00002072 ssize_t
2073 x;
2074
cristy3ed852e2009-09-05 21:47:34 +00002075 /*
2076 Apply filter to resize horizontally from image to resize image.
2077 */
cristy5d824382010-09-06 14:00:17 +00002078 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002079 support=scale*GetResizeFilterSupport(resize_filter);
2080 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2081 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2082 {
2083 InheritException(exception,&resize_image->exception);
2084 return(MagickFalse);
2085 }
2086 if (support < 0.5)
2087 {
2088 /*
nicolas07bac812010-09-19 18:47:02 +00002089 Support too small even for nearest neighbour: Reduce to point
2090 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002091 */
2092 support=(MagickRealType) 0.5;
2093 scale=1.0;
2094 }
2095 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2096 if (contributions == (ContributionInfo **) NULL)
2097 {
2098 (void) ThrowMagickException(exception,GetMagickModule(),
2099 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2100 return(MagickFalse);
2101 }
2102 status=MagickTrue;
2103 scale=1.0/scale;
2104 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2105 image_view=AcquireCacheView(image);
2106 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002107#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002108 #pragma omp parallel for shared(status)
2109#endif
cristybb503372010-05-27 20:51:26 +00002110 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002111 {
cristy3ed852e2009-09-05 21:47:34 +00002112 MagickRealType
2113 center,
2114 density;
2115
2116 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002117 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002118
2119 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002120 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002121
cristy03dbbd22010-09-19 23:04:47 +00002122 register ContributionInfo
2123 *restrict contribution;
2124
cristy3ed852e2009-09-05 21:47:34 +00002125 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002126 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002127
cristy3ed852e2009-09-05 21:47:34 +00002128 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002129 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002130
cristy03dbbd22010-09-19 23:04:47 +00002131 register ssize_t
2132 y;
2133
cristy9af9b5d2010-08-15 17:04:28 +00002134 ssize_t
2135 n,
2136 start,
2137 stop;
2138
cristy3ed852e2009-09-05 21:47:34 +00002139 if (status == MagickFalse)
2140 continue;
2141 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002142 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2143 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002144 density=0.0;
2145 contribution=contributions[GetOpenMPThreadId()];
2146 for (n=0; n < (stop-start); n++)
2147 {
2148 contribution[n].pixel=start+n;
2149 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2150 ((MagickRealType) (start+n)-center+0.5));
2151 density+=contribution[n].weight;
2152 }
2153 if ((density != 0.0) && (density != 1.0))
2154 {
cristybb503372010-05-27 20:51:26 +00002155 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002156 i;
2157
2158 /*
2159 Normalize.
2160 */
2161 density=1.0/density;
2162 for (i=0; i < n; i++)
2163 contribution[i].weight*=density;
2164 }
cristy9af9b5d2010-08-15 17:04:28 +00002165 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2166 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002167 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2168 exception);
2169 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2170 {
2171 status=MagickFalse;
2172 continue;
2173 }
2174 indexes=GetCacheViewVirtualIndexQueue(image_view);
2175 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002176 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002177 {
cristy3ed852e2009-09-05 21:47:34 +00002178 MagickPixelPacket
2179 pixel;
2180
2181 MagickRealType
2182 alpha;
2183
cristybb503372010-05-27 20:51:26 +00002184 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002185 i;
2186
cristy9af9b5d2010-08-15 17:04:28 +00002187 ssize_t
2188 j;
2189
cristy3ed852e2009-09-05 21:47:34 +00002190 pixel=zero;
2191 if (image->matte == MagickFalse)
2192 {
2193 for (i=0; i < n; i++)
2194 {
2195 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2196 (contribution[i].pixel-contribution[0].pixel);
2197 alpha=contribution[i].weight;
2198 pixel.red+=alpha*(p+j)->red;
2199 pixel.green+=alpha*(p+j)->green;
2200 pixel.blue+=alpha*(p+j)->blue;
2201 pixel.opacity+=alpha*(p+j)->opacity;
2202 }
cristyce70c172010-01-07 17:15:30 +00002203 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2204 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2205 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2206 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002207 if ((image->colorspace == CMYKColorspace) &&
2208 (resize_image->colorspace == CMYKColorspace))
2209 {
2210 for (i=0; i < n; i++)
2211 {
2212 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2213 (contribution[i].pixel-contribution[0].pixel);
2214 alpha=contribution[i].weight;
2215 pixel.index+=alpha*indexes[j];
2216 }
cristyce70c172010-01-07 17:15:30 +00002217 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002218 }
2219 }
2220 else
2221 {
2222 MagickRealType
2223 gamma;
2224
2225 gamma=0.0;
2226 for (i=0; i < n; i++)
2227 {
2228 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2229 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002230 alpha=contribution[i].weight*QuantumScale*
2231 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002232 pixel.red+=alpha*(p+j)->red;
2233 pixel.green+=alpha*(p+j)->green;
2234 pixel.blue+=alpha*(p+j)->blue;
2235 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2236 gamma+=alpha;
2237 }
2238 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002239 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2240 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2241 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2242 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002243 if ((image->colorspace == CMYKColorspace) &&
2244 (resize_image->colorspace == CMYKColorspace))
2245 {
2246 for (i=0; i < n; i++)
2247 {
2248 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2249 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002250 alpha=contribution[i].weight*QuantumScale*
2251 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002252 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002253 }
cristyce70c172010-01-07 17:15:30 +00002254 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2255 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002256 }
2257 }
2258 if ((resize_image->storage_class == PseudoClass) &&
2259 (image->storage_class == PseudoClass))
2260 {
cristybb503372010-05-27 20:51:26 +00002261 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002262 1.0)+0.5);
2263 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2264 (contribution[i-start].pixel-contribution[0].pixel);
2265 resize_indexes[y]=indexes[j];
2266 }
2267 q++;
2268 }
2269 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2270 status=MagickFalse;
2271 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2272 {
2273 MagickBooleanType
2274 proceed;
2275
cristyb5d5f722009-11-04 03:03:49 +00002276#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002277 #pragma omp critical (MagickCore_HorizontalFilter)
2278#endif
cristy9af9b5d2010-08-15 17:04:28 +00002279 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002280 if (proceed == MagickFalse)
2281 status=MagickFalse;
2282 }
2283 }
2284 resize_view=DestroyCacheView(resize_view);
2285 image_view=DestroyCacheView(image_view);
2286 contributions=DestroyContributionThreadSet(contributions);
2287 return(status);
2288}
2289
2290static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2291 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002292 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002293{
cristyfa112112010-01-04 17:48:07 +00002294 CacheView
2295 *image_view,
2296 *resize_view;
2297
cristy3ed852e2009-09-05 21:47:34 +00002298 ClassType
2299 storage_class;
2300
2301 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002302 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002303
cristy3ed852e2009-09-05 21:47:34 +00002304 MagickBooleanType
2305 status;
2306
2307 MagickPixelPacket
2308 zero;
2309
2310 MagickRealType
2311 scale,
2312 support;
2313
cristy9af9b5d2010-08-15 17:04:28 +00002314 ssize_t
2315 y;
2316
cristy3ed852e2009-09-05 21:47:34 +00002317 /*
cristy9af9b5d2010-08-15 17:04:28 +00002318 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002319 */
cristy5d824382010-09-06 14:00:17 +00002320 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002321 support=scale*GetResizeFilterSupport(resize_filter);
2322 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2323 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2324 {
2325 InheritException(exception,&resize_image->exception);
2326 return(MagickFalse);
2327 }
2328 if (support < 0.5)
2329 {
2330 /*
nicolas07bac812010-09-19 18:47:02 +00002331 Support too small even for nearest neighbour: Reduce to point
2332 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002333 */
2334 support=(MagickRealType) 0.5;
2335 scale=1.0;
2336 }
2337 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2338 if (contributions == (ContributionInfo **) NULL)
2339 {
2340 (void) ThrowMagickException(exception,GetMagickModule(),
2341 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2342 return(MagickFalse);
2343 }
2344 status=MagickTrue;
2345 scale=1.0/scale;
2346 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2347 image_view=AcquireCacheView(image);
2348 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002349#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002350 #pragma omp parallel for shared(status)
2351#endif
cristybb503372010-05-27 20:51:26 +00002352 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002353 {
cristy3ed852e2009-09-05 21:47:34 +00002354 MagickRealType
2355 center,
2356 density;
2357
2358 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002359 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002360
2361 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002362 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002363
cristy03dbbd22010-09-19 23:04:47 +00002364 register ContributionInfo
2365 *restrict contribution;
2366
cristy3ed852e2009-09-05 21:47:34 +00002367 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002368 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002369
cristy9af9b5d2010-08-15 17:04:28 +00002370 register PixelPacket
2371 *restrict q;
2372
cristybb503372010-05-27 20:51:26 +00002373 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002374 x;
2375
cristy9af9b5d2010-08-15 17:04:28 +00002376 ssize_t
2377 n,
2378 start,
2379 stop;
cristy3ed852e2009-09-05 21:47:34 +00002380
2381 if (status == MagickFalse)
2382 continue;
cristy679e6962010-03-18 00:42:45 +00002383 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002384 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2385 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002386 density=0.0;
2387 contribution=contributions[GetOpenMPThreadId()];
2388 for (n=0; n < (stop-start); n++)
2389 {
2390 contribution[n].pixel=start+n;
2391 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2392 ((MagickRealType) (start+n)-center+0.5));
2393 density+=contribution[n].weight;
2394 }
2395 if ((density != 0.0) && (density != 1.0))
2396 {
cristybb503372010-05-27 20:51:26 +00002397 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002398 i;
2399
2400 /*
2401 Normalize.
2402 */
2403 density=1.0/density;
2404 for (i=0; i < n; i++)
2405 contribution[i].weight*=density;
2406 }
2407 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002408 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2409 exception);
cristy3ed852e2009-09-05 21:47:34 +00002410 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2411 exception);
2412 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2413 {
2414 status=MagickFalse;
2415 continue;
2416 }
2417 indexes=GetCacheViewVirtualIndexQueue(image_view);
2418 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002419 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002420 {
cristy3ed852e2009-09-05 21:47:34 +00002421 MagickPixelPacket
2422 pixel;
2423
2424 MagickRealType
2425 alpha;
2426
cristybb503372010-05-27 20:51:26 +00002427 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002428 i;
2429
cristy9af9b5d2010-08-15 17:04:28 +00002430 ssize_t
2431 j;
2432
cristy3ed852e2009-09-05 21:47:34 +00002433 pixel=zero;
2434 if (image->matte == MagickFalse)
2435 {
2436 for (i=0; i < n; i++)
2437 {
cristybb503372010-05-27 20:51:26 +00002438 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002439 image->columns+x);
2440 alpha=contribution[i].weight;
2441 pixel.red+=alpha*(p+j)->red;
2442 pixel.green+=alpha*(p+j)->green;
2443 pixel.blue+=alpha*(p+j)->blue;
2444 pixel.opacity+=alpha*(p+j)->opacity;
2445 }
cristyce70c172010-01-07 17:15:30 +00002446 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2447 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2448 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2449 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002450 if ((image->colorspace == CMYKColorspace) &&
2451 (resize_image->colorspace == CMYKColorspace))
2452 {
2453 for (i=0; i < n; i++)
2454 {
cristybb503372010-05-27 20:51:26 +00002455 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002456 image->columns+x);
2457 alpha=contribution[i].weight;
2458 pixel.index+=alpha*indexes[j];
2459 }
cristyce70c172010-01-07 17:15:30 +00002460 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002461 }
2462 }
2463 else
2464 {
2465 MagickRealType
2466 gamma;
2467
2468 gamma=0.0;
2469 for (i=0; i < n; i++)
2470 {
cristybb503372010-05-27 20:51:26 +00002471 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002472 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002473 alpha=contribution[i].weight*QuantumScale*
2474 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002475 pixel.red+=alpha*(p+j)->red;
2476 pixel.green+=alpha*(p+j)->green;
2477 pixel.blue+=alpha*(p+j)->blue;
2478 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2479 gamma+=alpha;
2480 }
2481 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002482 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2483 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2484 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2485 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002486 if ((image->colorspace == CMYKColorspace) &&
2487 (resize_image->colorspace == CMYKColorspace))
2488 {
2489 for (i=0; i < n; i++)
2490 {
cristybb503372010-05-27 20:51:26 +00002491 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002492 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002493 alpha=contribution[i].weight*QuantumScale*
2494 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002495 pixel.index+=alpha*indexes[j];
2496 }
cristyce70c172010-01-07 17:15:30 +00002497 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2498 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002499 }
2500 }
2501 if ((resize_image->storage_class == PseudoClass) &&
2502 (image->storage_class == PseudoClass))
2503 {
cristybb503372010-05-27 20:51:26 +00002504 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002505 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002506 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002507 image->columns+x);
2508 resize_indexes[x]=indexes[j];
2509 }
2510 q++;
2511 }
2512 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2513 status=MagickFalse;
2514 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2515 {
2516 MagickBooleanType
2517 proceed;
2518
cristyb5d5f722009-11-04 03:03:49 +00002519#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002520 #pragma omp critical (MagickCore_VerticalFilter)
2521#endif
cristy9af9b5d2010-08-15 17:04:28 +00002522 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002523 if (proceed == MagickFalse)
2524 status=MagickFalse;
2525 }
2526 }
2527 resize_view=DestroyCacheView(resize_view);
2528 image_view=DestroyCacheView(image_view);
2529 contributions=DestroyContributionThreadSet(contributions);
2530 return(status);
2531}
2532
cristybb503372010-05-27 20:51:26 +00002533MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2534 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002535 ExceptionInfo *exception)
2536{
2537#define WorkLoadFactor 0.265
2538
2539 FilterTypes
2540 filter_type;
2541
2542 Image
2543 *filter_image,
2544 *resize_image;
2545
cristy9af9b5d2010-08-15 17:04:28 +00002546 MagickOffsetType
2547 offset;
2548
cristy3ed852e2009-09-05 21:47:34 +00002549 MagickRealType
2550 x_factor,
2551 y_factor;
2552
2553 MagickSizeType
2554 span;
2555
2556 MagickStatusType
2557 status;
2558
2559 ResizeFilter
2560 *resize_filter;
2561
cristy3ed852e2009-09-05 21:47:34 +00002562 /*
2563 Acquire resize image.
2564 */
2565 assert(image != (Image *) NULL);
2566 assert(image->signature == MagickSignature);
2567 if (image->debug != MagickFalse)
2568 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2569 assert(exception != (ExceptionInfo *) NULL);
2570 assert(exception->signature == MagickSignature);
2571 if ((columns == 0) || (rows == 0))
2572 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2573 if ((columns == image->columns) && (rows == image->rows) &&
2574 (filter == UndefinedFilter) && (blur == 1.0))
2575 return(CloneImage(image,0,0,MagickTrue,exception));
2576 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2577 if (resize_image == (Image *) NULL)
2578 return(resize_image);
2579 /*
2580 Acquire resize filter.
2581 */
2582 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2583 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2584 if ((x_factor*y_factor) > WorkLoadFactor)
2585 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2586 else
2587 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2588 if (filter_image == (Image *) NULL)
2589 return(DestroyImage(resize_image));
2590 filter_type=LanczosFilter;
2591 if (filter != UndefinedFilter)
2592 filter_type=filter;
2593 else
2594 if ((x_factor == 1.0) && (y_factor == 1.0))
2595 filter_type=PointFilter;
2596 else
2597 if ((image->storage_class == PseudoClass) ||
2598 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2599 filter_type=MitchellFilter;
2600 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2601 exception);
2602 /*
2603 Resize image.
2604 */
cristy9af9b5d2010-08-15 17:04:28 +00002605 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002606 if ((x_factor*y_factor) > WorkLoadFactor)
2607 {
2608 span=(MagickSizeType) (filter_image->columns+rows);
2609 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002610 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002611 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002612 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002613 }
2614 else
2615 {
2616 span=(MagickSizeType) (filter_image->rows+columns);
2617 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002618 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002619 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002620 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002621 }
2622 /*
2623 Free resources.
2624 */
2625 filter_image=DestroyImage(filter_image);
2626 resize_filter=DestroyResizeFilter(resize_filter);
2627 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2628 return((Image *) NULL);
2629 resize_image->type=image->type;
2630 return(resize_image);
2631}
2632
2633/*
2634%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2635% %
2636% %
2637% %
2638% S a m p l e I m a g e %
2639% %
2640% %
2641% %
2642%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2643%
2644% SampleImage() scales an image to the desired dimensions with pixel
2645% sampling. Unlike other scaling methods, this method does not introduce
2646% any additional color into the scaled image.
2647%
2648% The format of the SampleImage method is:
2649%
cristybb503372010-05-27 20:51:26 +00002650% Image *SampleImage(const Image *image,const size_t columns,
2651% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002652%
2653% A description of each parameter follows:
2654%
2655% o image: the image.
2656%
2657% o columns: the number of columns in the sampled image.
2658%
2659% o rows: the number of rows in the sampled image.
2660%
2661% o exception: return any errors or warnings in this structure.
2662%
2663*/
cristybb503372010-05-27 20:51:26 +00002664MagickExport Image *SampleImage(const Image *image,const size_t columns,
2665 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002666{
2667#define SampleImageTag "Sample/Image"
2668
cristyc4c8d132010-01-07 01:58:38 +00002669 CacheView
2670 *image_view,
2671 *sample_view;
2672
cristy3ed852e2009-09-05 21:47:34 +00002673 Image
2674 *sample_image;
2675
cristy3ed852e2009-09-05 21:47:34 +00002676 MagickBooleanType
2677 status;
2678
cristy5f959472010-05-27 22:19:46 +00002679 MagickOffsetType
2680 progress;
2681
cristybb503372010-05-27 20:51:26 +00002682 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002683 x;
2684
cristy5f959472010-05-27 22:19:46 +00002685 ssize_t
2686 *x_offset,
2687 y;
2688
cristy3ed852e2009-09-05 21:47:34 +00002689 /*
2690 Initialize sampled image attributes.
2691 */
2692 assert(image != (const Image *) NULL);
2693 assert(image->signature == MagickSignature);
2694 if (image->debug != MagickFalse)
2695 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2696 assert(exception != (ExceptionInfo *) NULL);
2697 assert(exception->signature == MagickSignature);
2698 if ((columns == 0) || (rows == 0))
2699 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2700 if ((columns == image->columns) && (rows == image->rows))
2701 return(CloneImage(image,0,0,MagickTrue,exception));
2702 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2703 if (sample_image == (Image *) NULL)
2704 return((Image *) NULL);
2705 /*
2706 Allocate scan line buffer and column offset buffers.
2707 */
cristybb503372010-05-27 20:51:26 +00002708 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002709 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002710 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002711 {
2712 sample_image=DestroyImage(sample_image);
2713 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2714 }
cristybb503372010-05-27 20:51:26 +00002715 for (x=0; x < (ssize_t) sample_image->columns; x++)
2716 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002717 sample_image->columns);
2718 /*
2719 Sample each row.
2720 */
2721 status=MagickTrue;
2722 progress=0;
2723 image_view=AcquireCacheView(image);
2724 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002725#if defined(MAGICKCORE_OPENMP_SUPPORT)
2726 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002727#endif
cristybb503372010-05-27 20:51:26 +00002728 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002729 {
cristy3ed852e2009-09-05 21:47:34 +00002730 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002731 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002732
2733 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002734 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002735
2736 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002737 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002738
cristy3ed852e2009-09-05 21:47:34 +00002739 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002740 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002741
cristy03dbbd22010-09-19 23:04:47 +00002742 register ssize_t
2743 x;
2744
cristy9af9b5d2010-08-15 17:04:28 +00002745 ssize_t
2746 y_offset;
2747
cristy3ed852e2009-09-05 21:47:34 +00002748 if (status == MagickFalse)
2749 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002750 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2751 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002752 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2753 exception);
2754 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2755 exception);
2756 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2757 {
2758 status=MagickFalse;
2759 continue;
2760 }
2761 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2762 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2763 /*
2764 Sample each column.
2765 */
cristybb503372010-05-27 20:51:26 +00002766 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002767 *q++=p[x_offset[x]];
2768 if ((image->storage_class == PseudoClass) ||
2769 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002770 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002771 sample_indexes[x]=indexes[x_offset[x]];
2772 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2773 status=MagickFalse;
2774 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2775 {
2776 MagickBooleanType
2777 proceed;
2778
cristyb5d5f722009-11-04 03:03:49 +00002779#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002780 #pragma omp critical (MagickCore_SampleImage)
2781#endif
2782 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2783 if (proceed == MagickFalse)
2784 status=MagickFalse;
2785 }
2786 }
2787 image_view=DestroyCacheView(image_view);
2788 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002789 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002790 sample_image->type=image->type;
2791 return(sample_image);
2792}
2793
2794/*
2795%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2796% %
2797% %
2798% %
2799% S c a l e I m a g e %
2800% %
2801% %
2802% %
2803%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2804%
2805% ScaleImage() changes the size of an image to the given dimensions.
2806%
2807% The format of the ScaleImage method is:
2808%
cristybb503372010-05-27 20:51:26 +00002809% Image *ScaleImage(const Image *image,const size_t columns,
2810% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002811%
2812% A description of each parameter follows:
2813%
2814% o image: the image.
2815%
2816% o columns: the number of columns in the scaled image.
2817%
2818% o rows: the number of rows in the scaled image.
2819%
2820% o exception: return any errors or warnings in this structure.
2821%
2822*/
cristybb503372010-05-27 20:51:26 +00002823MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2824 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002825{
2826#define ScaleImageTag "Scale/Image"
2827
cristyed6cb232010-01-20 03:07:53 +00002828 CacheView
2829 *image_view,
2830 *scale_view;
2831
cristy3ed852e2009-09-05 21:47:34 +00002832 Image
2833 *scale_image;
2834
cristy3ed852e2009-09-05 21:47:34 +00002835 MagickBooleanType
2836 next_column,
2837 next_row,
2838 proceed;
2839
2840 MagickPixelPacket
2841 pixel,
2842 *scale_scanline,
2843 *scanline,
2844 *x_vector,
2845 *y_vector,
2846 zero;
2847
cristy3ed852e2009-09-05 21:47:34 +00002848 PointInfo
2849 scale,
2850 span;
2851
cristybb503372010-05-27 20:51:26 +00002852 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002853 i;
2854
cristy9af9b5d2010-08-15 17:04:28 +00002855 ssize_t
2856 number_rows,
2857 y;
2858
cristy3ed852e2009-09-05 21:47:34 +00002859 /*
2860 Initialize scaled image attributes.
2861 */
2862 assert(image != (const Image *) NULL);
2863 assert(image->signature == MagickSignature);
2864 if (image->debug != MagickFalse)
2865 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2866 assert(exception != (ExceptionInfo *) NULL);
2867 assert(exception->signature == MagickSignature);
2868 if ((columns == 0) || (rows == 0))
2869 return((Image *) NULL);
2870 if ((columns == image->columns) && (rows == image->rows))
2871 return(CloneImage(image,0,0,MagickTrue,exception));
2872 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2873 if (scale_image == (Image *) NULL)
2874 return((Image *) NULL);
2875 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2876 {
2877 InheritException(exception,&scale_image->exception);
2878 scale_image=DestroyImage(scale_image);
2879 return((Image *) NULL);
2880 }
2881 /*
2882 Allocate memory.
2883 */
2884 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2885 sizeof(*x_vector));
2886 scanline=x_vector;
2887 if (image->rows != scale_image->rows)
2888 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2889 sizeof(*scanline));
2890 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2891 scale_image->columns,sizeof(*scale_scanline));
2892 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2893 sizeof(*y_vector));
2894 if ((scanline == (MagickPixelPacket *) NULL) ||
2895 (scale_scanline == (MagickPixelPacket *) NULL) ||
2896 (x_vector == (MagickPixelPacket *) NULL) ||
2897 (y_vector == (MagickPixelPacket *) NULL))
2898 {
2899 scale_image=DestroyImage(scale_image);
2900 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2901 }
2902 /*
2903 Scale image.
2904 */
2905 number_rows=0;
2906 next_row=MagickTrue;
2907 span.y=1.0;
2908 scale.y=(double) scale_image->rows/(double) image->rows;
2909 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2910 sizeof(*y_vector));
2911 GetMagickPixelPacket(image,&pixel);
2912 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2913 i=0;
cristyed6cb232010-01-20 03:07:53 +00002914 image_view=AcquireCacheView(image);
2915 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002916 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002917 {
2918 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002919 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002920
2921 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002922 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002923
2924 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002925 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002926
cristy3ed852e2009-09-05 21:47:34 +00002927 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002928 *restrict s,
2929 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002930
2931 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002932 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002933
cristy9af9b5d2010-08-15 17:04:28 +00002934 register ssize_t
2935 x;
2936
cristyed6cb232010-01-20 03:07:53 +00002937 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2938 exception);
cristy3ed852e2009-09-05 21:47:34 +00002939 if (q == (PixelPacket *) NULL)
2940 break;
cristyba9a1e22010-11-10 23:14:28 +00002941 scale_indexes=GetCacheViewAuthenticIndexQueue(scale_view);
cristy3ed852e2009-09-05 21:47:34 +00002942 if (scale_image->rows == image->rows)
2943 {
2944 /*
2945 Read a new scanline.
2946 */
cristyed6cb232010-01-20 03:07:53 +00002947 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2948 exception);
cristy3ed852e2009-09-05 21:47:34 +00002949 if (p == (const PixelPacket *) NULL)
2950 break;
cristyed6cb232010-01-20 03:07:53 +00002951 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002952 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002953 {
cristyce70c172010-01-07 17:15:30 +00002954 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2955 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2956 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002957 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002958 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002959 if (indexes != (IndexPacket *) NULL)
2960 x_vector[x].index=(MagickRealType) indexes[x];
2961 p++;
2962 }
2963 }
2964 else
2965 {
2966 /*
2967 Scale Y direction.
2968 */
2969 while (scale.y < span.y)
2970 {
cristy9af9b5d2010-08-15 17:04:28 +00002971 if ((next_row != MagickFalse) &&
2972 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002973 {
2974 /*
2975 Read a new scanline.
2976 */
cristyed6cb232010-01-20 03:07:53 +00002977 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2978 exception);
cristy3ed852e2009-09-05 21:47:34 +00002979 if (p == (const PixelPacket *) NULL)
2980 break;
cristyed6cb232010-01-20 03:07:53 +00002981 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002982 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002983 {
cristyce70c172010-01-07 17:15:30 +00002984 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2985 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2986 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002987 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002988 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002989 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002990 if (indexes != (IndexPacket *) NULL)
2991 x_vector[x].index=(MagickRealType) indexes[x];
2992 p++;
2993 }
2994 number_rows++;
2995 }
cristybb503372010-05-27 20:51:26 +00002996 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002997 {
2998 y_vector[x].red+=scale.y*x_vector[x].red;
2999 y_vector[x].green+=scale.y*x_vector[x].green;
3000 y_vector[x].blue+=scale.y*x_vector[x].blue;
3001 if (scale_image->matte != MagickFalse)
3002 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
3003 if (scale_indexes != (IndexPacket *) NULL)
3004 y_vector[x].index+=scale.y*x_vector[x].index;
3005 }
3006 span.y-=scale.y;
3007 scale.y=(double) scale_image->rows/(double) image->rows;
3008 next_row=MagickTrue;
3009 }
cristybb503372010-05-27 20:51:26 +00003010 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00003011 {
3012 /*
3013 Read a new scanline.
3014 */
cristyed6cb232010-01-20 03:07:53 +00003015 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
3016 exception);
cristy3ed852e2009-09-05 21:47:34 +00003017 if (p == (const PixelPacket *) NULL)
3018 break;
cristyed6cb232010-01-20 03:07:53 +00003019 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00003020 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003021 {
cristyce70c172010-01-07 17:15:30 +00003022 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
3023 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
3024 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003025 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00003026 x_vector[x].opacity=(MagickRealType)
3027 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003028 if (indexes != (IndexPacket *) NULL)
3029 x_vector[x].index=(MagickRealType) indexes[x];
3030 p++;
3031 }
3032 number_rows++;
3033 next_row=MagickFalse;
3034 }
3035 s=scanline;
cristybb503372010-05-27 20:51:26 +00003036 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003037 {
3038 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3039 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3040 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3041 if (image->matte != MagickFalse)
3042 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3043 if (scale_indexes != (IndexPacket *) NULL)
3044 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3045 s->red=pixel.red;
3046 s->green=pixel.green;
3047 s->blue=pixel.blue;
3048 if (scale_image->matte != MagickFalse)
3049 s->opacity=pixel.opacity;
3050 if (scale_indexes != (IndexPacket *) NULL)
3051 s->index=pixel.index;
3052 s++;
3053 y_vector[x]=zero;
3054 }
3055 scale.y-=span.y;
3056 if (scale.y <= 0)
3057 {
3058 scale.y=(double) scale_image->rows/(double) image->rows;
3059 next_row=MagickTrue;
3060 }
3061 span.y=1.0;
3062 }
3063 if (scale_image->columns == image->columns)
3064 {
3065 /*
3066 Transfer scanline to scaled image.
3067 */
3068 s=scanline;
cristybb503372010-05-27 20:51:26 +00003069 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003070 {
cristyce70c172010-01-07 17:15:30 +00003071 q->red=ClampToQuantum(s->red);
3072 q->green=ClampToQuantum(s->green);
3073 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003074 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003075 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003076 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003077 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003078 q++;
3079 s++;
3080 }
3081 }
3082 else
3083 {
3084 /*
3085 Scale X direction.
3086 */
3087 pixel=zero;
3088 next_column=MagickFalse;
3089 span.x=1.0;
3090 s=scanline;
3091 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003092 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003093 {
3094 scale.x=(double) scale_image->columns/(double) image->columns;
3095 while (scale.x >= span.x)
3096 {
3097 if (next_column != MagickFalse)
3098 {
3099 pixel=zero;
3100 t++;
3101 }
3102 pixel.red+=span.x*s->red;
3103 pixel.green+=span.x*s->green;
3104 pixel.blue+=span.x*s->blue;
3105 if (image->matte != MagickFalse)
3106 pixel.opacity+=span.x*s->opacity;
3107 if (scale_indexes != (IndexPacket *) NULL)
3108 pixel.index+=span.x*s->index;
3109 t->red=pixel.red;
3110 t->green=pixel.green;
3111 t->blue=pixel.blue;
3112 if (scale_image->matte != MagickFalse)
3113 t->opacity=pixel.opacity;
3114 if (scale_indexes != (IndexPacket *) NULL)
3115 t->index=pixel.index;
3116 scale.x-=span.x;
3117 span.x=1.0;
3118 next_column=MagickTrue;
3119 }
3120 if (scale.x > 0)
3121 {
3122 if (next_column != MagickFalse)
3123 {
3124 pixel=zero;
3125 next_column=MagickFalse;
3126 t++;
3127 }
3128 pixel.red+=scale.x*s->red;
3129 pixel.green+=scale.x*s->green;
3130 pixel.blue+=scale.x*s->blue;
3131 if (scale_image->matte != MagickFalse)
3132 pixel.opacity+=scale.x*s->opacity;
3133 if (scale_indexes != (IndexPacket *) NULL)
3134 pixel.index+=scale.x*s->index;
3135 span.x-=scale.x;
3136 }
3137 s++;
3138 }
3139 if (span.x > 0)
3140 {
3141 s--;
3142 pixel.red+=span.x*s->red;
3143 pixel.green+=span.x*s->green;
3144 pixel.blue+=span.x*s->blue;
3145 if (scale_image->matte != MagickFalse)
3146 pixel.opacity+=span.x*s->opacity;
3147 if (scale_indexes != (IndexPacket *) NULL)
3148 pixel.index+=span.x*s->index;
3149 }
3150 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003151 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003152 {
3153 t->red=pixel.red;
3154 t->green=pixel.green;
3155 t->blue=pixel.blue;
3156 if (scale_image->matte != MagickFalse)
3157 t->opacity=pixel.opacity;
3158 if (scale_indexes != (IndexPacket *) NULL)
3159 t->index=pixel.index;
3160 }
3161 /*
3162 Transfer scanline to scaled image.
3163 */
3164 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003165 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003166 {
cristyce70c172010-01-07 17:15:30 +00003167 q->red=ClampToQuantum(t->red);
3168 q->green=ClampToQuantum(t->green);
3169 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003170 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003171 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003172 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003173 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003174 t++;
3175 q++;
3176 }
3177 }
cristyed6cb232010-01-20 03:07:53 +00003178 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003179 break;
cristy96b16132010-08-29 17:19:52 +00003180 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3181 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003182 if (proceed == MagickFalse)
3183 break;
3184 }
cristyed6cb232010-01-20 03:07:53 +00003185 scale_view=DestroyCacheView(scale_view);
3186 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003187 /*
3188 Free allocated memory.
3189 */
3190 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3191 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3192 if (scale_image->rows != image->rows)
3193 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3194 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3195 scale_image->type=image->type;
3196 return(scale_image);
3197}
3198
anthony02b4cb42010-10-10 04:54:35 +00003199#if 0
3200 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003201/*
3202%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3203% %
3204% %
3205% %
3206+ S e t R e s i z e F i l t e r S u p p o r t %
3207% %
3208% %
3209% %
3210%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3211%
3212% SetResizeFilterSupport() specifies which IR filter to use to window
3213%
3214% The format of the SetResizeFilterSupport method is:
3215%
3216% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3217% const MagickRealType support)
3218%
3219% A description of each parameter follows:
3220%
3221% o resize_filter: the resize filter.
3222%
3223% o support: the filter spport radius.
3224%
3225*/
3226MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3227 const MagickRealType support)
3228{
3229 assert(resize_filter != (ResizeFilter *) NULL);
3230 assert(resize_filter->signature == MagickSignature);
3231 resize_filter->support=support;
3232}
anthony02b4cb42010-10-10 04:54:35 +00003233#endif
cristy3ed852e2009-09-05 21:47:34 +00003234
3235/*
3236%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3237% %
3238% %
3239% %
3240% T h u m b n a i l I m a g e %
3241% %
3242% %
3243% %
3244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3245%
3246% ThumbnailImage() changes the size of an image to the given dimensions and
3247% removes any associated profiles. The goal is to produce small low cost
3248% thumbnail images suited for display on the Web.
3249%
3250% The format of the ThumbnailImage method is:
3251%
cristybb503372010-05-27 20:51:26 +00003252% Image *ThumbnailImage(const Image *image,const size_t columns,
3253% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003254%
3255% A description of each parameter follows:
3256%
3257% o image: the image.
3258%
3259% o columns: the number of columns in the scaled image.
3260%
3261% o rows: the number of rows in the scaled image.
3262%
3263% o exception: return any errors or warnings in this structure.
3264%
3265*/
cristy9af9b5d2010-08-15 17:04:28 +00003266MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3267 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003268{
3269#define SampleFactor 5
3270
3271 char
3272 value[MaxTextExtent];
3273
3274 const char
3275 *name;
3276
3277 Image
3278 *thumbnail_image;
3279
3280 MagickRealType
3281 x_factor,
3282 y_factor;
3283
cristybb503372010-05-27 20:51:26 +00003284 size_t
cristy3ed852e2009-09-05 21:47:34 +00003285 version;
3286
cristy9af9b5d2010-08-15 17:04:28 +00003287 struct stat
3288 attributes;
3289
cristy3ed852e2009-09-05 21:47:34 +00003290 assert(image != (Image *) NULL);
3291 assert(image->signature == MagickSignature);
3292 if (image->debug != MagickFalse)
3293 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3294 assert(exception != (ExceptionInfo *) NULL);
3295 assert(exception->signature == MagickSignature);
3296 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3297 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3298 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003299 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3300 exception);
cristy3ed852e2009-09-05 21:47:34 +00003301 else
3302 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003303 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3304 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003305 else
3306 {
3307 Image
3308 *sample_image;
3309
3310 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3311 exception);
3312 if (sample_image == (Image *) NULL)
3313 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003314 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3315 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003316 sample_image=DestroyImage(sample_image);
3317 }
3318 if (thumbnail_image == (Image *) NULL)
3319 return(thumbnail_image);
3320 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3321 if (thumbnail_image->matte == MagickFalse)
3322 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3323 thumbnail_image->depth=8;
3324 thumbnail_image->interlace=NoInterlace;
3325 /*
3326 Strip all profiles except color profiles.
3327 */
3328 ResetImageProfileIterator(thumbnail_image);
3329 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3330 {
3331 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3332 {
cristy2b726bd2010-01-11 01:05:39 +00003333 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003334 ResetImageProfileIterator(thumbnail_image);
3335 }
3336 name=GetNextImageProfile(thumbnail_image);
3337 }
3338 (void) DeleteImageProperty(thumbnail_image,"comment");
3339 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003340 if (strstr(image->magick_filename,"//") == (char *) NULL)
3341 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003342 image->magick_filename);
3343 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3344 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3345 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3346 {
cristye8c25f92010-06-03 00:53:06 +00003347 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003348 attributes.st_mtime);
3349 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3350 }
cristye8c25f92010-06-03 00:53:06 +00003351 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003352 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003353 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003354 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003355 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3356 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3357 LocaleLower(value);
3358 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3359 (void) SetImageProperty(thumbnail_image,"software",
3360 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003361 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3362 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003363 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003364 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003365 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003366 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003367 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3368 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003369 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3370 return(thumbnail_image);
3371}