blob: b7d68223ad4071a726b68ce4ae8a14808dba2b01 [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)
nicolas2ab65102010-11-15 06:03:11 +0000542% form of the Lanczos filter, specifically designed for EWA
543% distortion (as a Jinc-Jinc); it can also be used as a slightly
544% sharper orthogonal Lanczos (Sinc-Sinc) filter. The chosen blur
545% value comes as close as possible to satisfying the following
546% condition without changing the character of the corresponding EWA
547% filter:
anthony152700d2010-10-28 02:43:18 +0000548%
549% 'No-Op' Vertical and Horizontal Line Preservation Condition:
550% Images with only vertical or horizontal features are preserved
551% when performing 'no-op" with EWA distortion.
552%
nicolas2d3579a2010-11-13 15:31:28 +0000553% The Lanczos2 and Lanczos2Sharp filters are 2-lobe versions of the
554% Lanczos filters. The 'sharp' version uses a blur factor of
nicolasca48bce2010-11-14 17:54:53 +0000555% 0.9549963639785485, again chosen because the resulting EWA filter
nicolasc22cc712010-11-13 16:39:10 +0000556% comes as close as possible to satisfying the above
nicolasca48bce2010-11-14 17:54:53 +0000557% condition.
anthony06b1edf2010-10-25 01:19:50 +0000558%
nicolasb7dff642010-10-25 02:04:14 +0000559% Robidoux is another filter tuned for EWA. It is the Keys cubic
anthony152700d2010-10-28 02:43:18 +0000560% filter defined by B=(228 - 108 sqrt(2))/199. Robidoux satisfies the
nicolasb7dff642010-10-25 02:04:14 +0000561% "'No-Op' Vertical and Horizontal Line Preservation Condition"
nicolasc22cc712010-11-13 16:39:10 +0000562% exactly, and it moderately blurs high frequency 'pixel-hash'
563% patterns under no-op. It turns out to be close to both Mitchell
564% and Lanczos2Sharp. For example, its first crossing is at (36
565% sqrt(2) + 123)/(72 sqrt(2) + 47), almost the same as the first
566% crossing of Mitchell and Lanczos2Sharp.
anthony152700d2010-10-28 02:43:18 +0000567%
anthony61b5ddd2010-10-05 02:33:31 +0000568%
nicolas3061b8a2010-10-22 16:34:52 +0000569% 'EXPERT' OPTIONS:
nicolasce6dc292010-10-22 16:23:07 +0000570%
anthony152700d2010-10-28 02:43:18 +0000571% These artifact "defines" are not recommended for production use
572% without expert knowledge of resampling, filtering, and the effects
573% they have on the resulting resampled (resize ro distorted) image.
nicolas3061b8a2010-10-22 16:34:52 +0000574%
anthony152700d2010-10-28 02:43:18 +0000575% They can be used to override any and all filter default, and it is
576% recommended you make good use of "filter:verbose" to make sure that
577% the overall effect of your selection (before and after) is as
578% expected.
nicolas3061b8a2010-10-22 16:34:52 +0000579%
anthony28ad1d72010-10-26 06:30:24 +0000580% "filter:verbose" controls whether to output the exact results of
581% the filter selections made, as well as plotting data for
582% graphing the resulting filter over the filters support range.
cristy3ed852e2009-09-05 21:47:34 +0000583%
anthony48f77622010-10-03 14:32:31 +0000584% "filter:filter" Select the main function associated with
585% this filter name, as the weighting function of the filter.
586% This can be used to set a windowing function as a weighting
587% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000588%
nicolas3061b8a2010-10-22 16:34:52 +0000589% If a "filter:window" operation has not been provided, then a
590% 'Box' windowing function will be set to denote that no
591% windowing function is being used.
cristy3ed852e2009-09-05 21:47:34 +0000592%
nicolas3061b8a2010-10-22 16:34:52 +0000593% "filter:window" Select this windowing function for the filter.
594% While any filter could be used as a windowing function, using
595% the 'first lobe' of that filter over the whole support
596% window, using a non-windowing function is not advisible. If
597% no weighting filter function is specifed a 'SincFast' filter
598% will be used.
cristy3ed852e2009-09-05 21:47:34 +0000599%
nicolas3061b8a2010-10-22 16:34:52 +0000600% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
601% This a simpler method of setting filter support size that
602% will correctly handle the Sinc/Jinc switch for an operators
603% filtering requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000604%
nicolas3061b8a2010-10-22 16:34:52 +0000605% "filter:support" Set the support size for filtering to the size
606% given This not recommended for Sinc/Jinc windowed filters
607% (lobes should be used instead). This will override any
608% 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000609%
nicolas3061b8a2010-10-22 16:34:52 +0000610% "filter:win-support" Scale windowing function to this size
611% instead. This causes the windowing (or self-windowing
612% Lagrange filter) to act is if the support window it much much
613% larger than what is actually supplied to the calling
614% operator. The filter however is still clipped to the real
615% support size given, by the support range suppiled to the
616% caller. If unset this will equal the normal filter support
anthonyb6d08c52010-09-13 01:17:04 +0000617% size.
618%
nicolas3061b8a2010-10-22 16:34:52 +0000619% "filter:blur" Scale the filter and support window by this amount.
cristy3ed852e2009-09-05 21:47:34 +0000620% A value >1 will generally result in a more burred image with
621% more ringing effects, while a value <1 will sharpen the
anthony152700d2010-10-28 02:43:18 +0000622% resulting image with more aliasing effects.
cristy3ed852e2009-09-05 21:47:34 +0000623%
nicolas3061b8a2010-10-22 16:34:52 +0000624% "filter:sigma" The sigma value to use for the Gaussian filter
625% only. Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for
626% cylindrical usage. It effectially provides a alturnative to
627% 'blur' for Gaussians without it also effecting the final
628% 'practical support' size.
anthonyf5e76ef2010-10-12 01:22:01 +0000629%
cristy3ed852e2009-09-05 21:47:34 +0000630% "filter:b"
nicolas3061b8a2010-10-22 16:34:52 +0000631% "filter:c" Override the preset B,C values for a Cubic type of
632% filter If only one of these are given it is assumes to be a
633% 'Keys' type of filter such that B+2C=1, where Keys 'alpha'
634% value = C
cristy3ed852e2009-09-05 21:47:34 +0000635%
anthony06b1edf2010-10-25 01:19:50 +0000636% Examples:
cristy3ed852e2009-09-05 21:47:34 +0000637%
nicolas6e1267a2010-10-22 16:35:52 +0000638% Set a true un-windowed Sinc filter with 10 lobes (very slow):
anthony48f77622010-10-03 14:32:31 +0000639% -define filter:filter=Sinc
640% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000641%
nicolas6e1267a2010-10-22 16:35:52 +0000642% Set an 8 lobe Lanczos (Sinc or Jinc) filter:
cristy3ed852e2009-09-05 21:47:34 +0000643% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000644% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000645%
anthony06b1edf2010-10-25 01:19:50 +0000646%
cristy3ed852e2009-09-05 21:47:34 +0000647% The format of the AcquireResizeFilter method is:
648%
649% ResizeFilter *AcquireResizeFilter(const Image *image,
650% const FilterTypes filter_type, const MagickBooleanType radial,
651% ExceptionInfo *exception)
652%
cristy33b1c162010-01-23 22:51:51 +0000653% A description of each parameter follows:
654%
cristy3ed852e2009-09-05 21:47:34 +0000655% o image: the image.
656%
nicolas07bac812010-09-19 18:47:02 +0000657% o filter: the filter type, defining a preset filter, window and
658% support. The artifact settings listed above will override
659% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000660%
anthony48f77622010-10-03 14:32:31 +0000661% o blur: blur the filter by this amount, use 1.0 if unknown. Image
662% artifact "filter:blur" will override this API call usage, including
663% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000664%
anthony48f77622010-10-03 14:32:31 +0000665% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
666% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000667%
668% o exception: return any errors or warnings in this structure.
669%
670*/
671MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000672 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000673 const MagickBooleanType cylindrical,ExceptionInfo *exception)
674{
675 const char
676 *artifact;
677
678 FilterTypes
679 filter_type,
680 window_type;
681
cristy3ed852e2009-09-05 21:47:34 +0000682 MagickRealType
683 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000684 C,
685 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000686
687 register ResizeFilter
688 *resize_filter;
689
cristy9af9b5d2010-08-15 17:04:28 +0000690
cristy3ed852e2009-09-05 21:47:34 +0000691 /*
anthony48f77622010-10-03 14:32:31 +0000692 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000693 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000694 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
695 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
696 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000697
nicolas07bac812010-09-19 18:47:02 +0000698 WARNING: The order of this tabel must match the order of the
699 FilterTypes enumeration specified in "resample.h", or the filter
700 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000701
702 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000703 */
704 static struct
705 {
706 FilterTypes
707 filter,
708 window;
709 } const mapping[SentinelFilter] =
710 {
nicolasb7dff642010-10-25 02:04:14 +0000711 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
712 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
713 { BoxFilter, BoxFilter }, /* Box averaging filter */
714 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
715 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
716 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
717 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
anthony06b1edf2010-10-25 01:19:50 +0000718 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
nicolasb7dff642010-10-25 02:04:14 +0000719 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
720 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approx */
721 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
722 { CatromFilter, BoxFilter }, /* Cubic-Keys interpolator */
723 { MitchellFilter, BoxFilter }, /* 'Ideal' Cubic-Keys filter */
nicolasb7dff642010-10-25 02:04:14 +0000724 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
725 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony152700d2010-10-28 02:43:18 +0000726 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolasb7dff642010-10-25 02:04:14 +0000727 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
728 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
729 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
nicolasb7dff642010-10-25 02:04:14 +0000730 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
anthony06b1edf2010-10-25 01:19:50 +0000731 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
anthony152700d2010-10-28 02:43:18 +0000732 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing */
733 { LanczosFilter, LanczosFilter }, /* Lanczos Sinc-Sinc filters */
734 { LanczosSharpFilter, LanczosSharpFilter }, /* | these require */
735 { Lanczos2Filter, Lanczos2Filter }, /* | special handling */
736 { Lanczos2SharpFilter,Lanczos2SharpFilter },
737 { RobidouxFilter, BoxFilter }, /* Cubic Keys tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000738 };
739 /*
nicolas32f44eb2010-09-20 01:23:12 +0000740 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000741 function. The default support size for that filter as a weighting
742 function, the range to scale with to use that function as a sinc
743 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000744
anthony07a3f7f2010-09-16 03:03:11 +0000745 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000746 SincFast(), and CubicBC() functions, which may have multiple
747 filter to function associations.
748
749 See "filter:verbose" handling below for the function -> filter
750 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000751 */
752 static struct
753 {
754 MagickRealType
755 (*function)(const MagickRealType, const ResizeFilter*),
nicolas8eccc162010-10-16 19:48:13 +0000756 lobes, /* Default lobes/support size of the weighting filter. */
anthony450db502010-10-19 04:03:03 +0000757 scale, /* Support when function used as a windowing function
758 Typically equal to the location of the first zero crossing. */
759 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
cristy3ed852e2009-09-05 21:47:34 +0000760 } const filters[SentinelFilter] =
761 {
anthony61b5ddd2010-10-05 02:33:31 +0000762 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
763 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
764 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
765 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
766 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
767 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
768 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
769 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000770 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000771 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
772 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
773 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
nicolasd349be62010-10-28 18:57:38 +0000774 { CubicBC, 2.0, 8.0/7.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
nicolasf8689f42010-10-18 16:14:08 +0000775 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000776 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
anthony06b1edf2010-10-25 01:19:50 +0000777 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
anthony61b5ddd2010-10-05 02:33:31 +0000778 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
779 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
780 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
anthony61b5ddd2010-10-05 02:33:31 +0000781 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
782 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
anthony152700d2010-10-28 02:43:18 +0000783 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
784 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
785 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* lanczos, Sharpened */
786 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos, 2-lobed */
787 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2, sharpened */
anthony450db502010-10-19 04:03:03 +0000788 { CubicBC, 2.0, 1.1685777620836932,
nicolas0d5e5322010-10-22 15:29:30 +0000789 0.37821575509399867, 0.31089212245300067 }
790 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
cristy3ed852e2009-09-05 21:47:34 +0000791 };
792 /*
anthony9a98fc62010-10-11 02:47:19 +0000793 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
anthony450db502010-10-19 04:03:03 +0000794 function being used as a filter. It is used by the "filter:lobes" expert
795 setting and for 'lobes' for Jinc functions in the previous table. This
796 way users do not have to deal with the highly irrational lobe sizes of the
anthony9a98fc62010-10-11 02:47:19 +0000797 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000798
nicolase473f722010-10-07 00:05:13 +0000799 Values taken from
anthony48f77622010-10-03 14:32:31 +0000800 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000801 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000802 */
803 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000804 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000805 {
nicolas8eccc162010-10-16 19:48:13 +0000806 1.2196698912665045,
807 2.2331305943815286,
808 3.2383154841662362,
809 4.2410628637960699,
810 5.2427643768701817,
811 6.2439216898644877,
812 7.244759868719957,
813 8.2453949139520427,
814 9.2458926849494673,
815 10.246293348754916,
816 11.246622794877883,
817 12.246898461138105,
818 13.247132522181061,
819 14.247333735806849,
anthonyc2d07db2010-09-15 23:47:40 +0000820 15.2475085630373,
nicolas8eccc162010-10-16 19:48:13 +0000821 16.247661874700962
cristy3ed852e2009-09-05 21:47:34 +0000822 };
823
cristy33b1c162010-01-23 22:51:51 +0000824 /*
825 Allocate resize filter.
826 */
cristy3ed852e2009-09-05 21:47:34 +0000827 assert(image != (const Image *) NULL);
828 assert(image->signature == MagickSignature);
829 if (image->debug != MagickFalse)
830 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
831 assert(UndefinedFilter < filter && filter < SentinelFilter);
832 assert(exception != (ExceptionInfo *) NULL);
833 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000834 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000835 if (resize_filter == (ResizeFilter *) NULL)
836 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000837 /*
838 Defaults for the requested filter.
839 */
840 filter_type=mapping[filter].filter;
841 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000842 resize_filter->blur = blur;
843 sigma = 0.5;
anthony152700d2010-10-28 02:43:18 +0000844 /* Promote 1D Windowed Sinc Filters to a 2D Windowed Jinc filters */
845 if (cylindrical != MagickFalse && filter_type == SincFastFilter
846 && filter != SincFastFilter )
847 filter_type=JincFilter;
anthony61b5ddd2010-10-05 02:33:31 +0000848
anthony152700d2010-10-28 02:43:18 +0000849 /* Expert filter setting override */
cristy3ed852e2009-09-05 21:47:34 +0000850 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000851 if (artifact != (const char *) NULL)
852 {
anthony152700d2010-10-28 02:43:18 +0000853 ssize_t
854 option;
cristy9af9b5d2010-08-15 17:04:28 +0000855 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000856 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000857 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000858 filter_type=(FilterTypes) option;
859 window_type=BoxFilter;
860 }
nicolas07bac812010-09-19 18:47:02 +0000861 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000862 artifact=GetImageArtifact(image,"filter:window");
863 if (artifact != (const char *) NULL)
864 {
cristy9af9b5d2010-08-15 17:04:28 +0000865 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000866 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony152700d2010-10-28 02:43:18 +0000867 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000868 }
cristy3ed852e2009-09-05 21:47:34 +0000869 }
cristy33b1c162010-01-23 22:51:51 +0000870 else
871 {
anthony48f77622010-10-03 14:32:31 +0000872 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000873 artifact=GetImageArtifact(image,"filter:window");
874 if (artifact != (const char *) NULL)
875 {
anthony152700d2010-10-28 02:43:18 +0000876 ssize_t
877 option;
cristy33b1c162010-01-23 22:51:51 +0000878 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
879 artifact);
880 if ((UndefinedFilter < option) && (option < SentinelFilter))
881 {
anthony61b5ddd2010-10-05 02:33:31 +0000882 filter_type=cylindrical != MagickFalse ?
883 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000884 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000885 }
886 }
887 }
anthony152700d2010-10-28 02:43:18 +0000888
nicolas07bac812010-09-19 18:47:02 +0000889 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000890 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000891 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000892 resize_filter->window=filters[window_type].function;
893 resize_filter->scale=filters[window_type].scale;
anthony029ba0e2010-10-29 00:54:24 +0000894 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000895
anthonyf5e76ef2010-10-12 01:22:01 +0000896 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000897 if (cylindrical != MagickFalse)
898 switch (filter_type)
899 {
anthony10b8bc82010-10-02 12:48:46 +0000900 case BoxFilter:
901 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000902 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000903 break;
anthony152700d2010-10-28 02:43:18 +0000904 case LanczosFilter:
905 case LanczosSharpFilter:
906 case Lanczos2Filter:
907 case Lanczos2SharpFilter:
908 resize_filter->filter=filters[JincFilter].function;
909 resize_filter->window=filters[JincFilter].function;
anthony029ba0e2010-10-29 00:54:24 +0000910 resize_filter->scale=filters[JincFilter].scale;
911 /* number of lobes (support window size) remain unchanged */
anthony152700d2010-10-28 02:43:18 +0000912 break;
913 case GaussianFilter:
914 /* Cylindrical Gaussian sigma is sqrt(2)/2. */
915 sigma = (MagickRealType) (MagickSQ2/2.0);
916 break;
anthony81b8bf92010-10-02 13:54:34 +0000917 default:
918 break;
anthony10b8bc82010-10-02 12:48:46 +0000919 }
anthony152700d2010-10-28 02:43:18 +0000920 /* Global Sharpening (regardless of orthoginal/cylindrical) */
921 switch (filter_type)
922 {
923 case LanczosSharpFilter:
nicolasaf6ee7c2010-11-13 03:34:22 +0000924 resize_filter->blur *= 0.9812505644269356;
anthony152700d2010-10-28 02:43:18 +0000925 break;
926 case Lanczos2SharpFilter:
nicolasca48bce2010-11-14 17:54:53 +0000927 resize_filter->blur *= 0.9549963639785485;
anthony152700d2010-10-28 02:43:18 +0000928 break;
929 default:
930 break;
931 }
anthony61b5ddd2010-10-05 02:33:31 +0000932
anthonyf5e76ef2010-10-12 01:22:01 +0000933 /*
anthony06b1edf2010-10-25 01:19:50 +0000934 ** Other Expert Option Modifications
anthonyf5e76ef2010-10-12 01:22:01 +0000935 */
936
937 /* User Sigma Override - no support change */
938 artifact=GetImageArtifact(image,"filter:sigma");
939 if (artifact != (const char *) NULL)
940 sigma=StringToDouble(artifact);
941 /* Define coefficents for Gaussian (assumes no cubic window) */
942 if ( GaussianFilter ) {
cristy5cce74b2010-11-15 03:24:28 +0000943 resize_filter->coefficient[0]=1.0/(2.0*sigma*sigma);
944 resize_filter->coefficient[1]=(MagickRealType) (1.0/(Magick2PI*sigma*
945 sigma)); /* unused */
anthonyf5e76ef2010-10-12 01:22:01 +0000946 }
947
948 /* Blur Override */
949 artifact=GetImageArtifact(image,"filter:blur");
950 if (artifact != (const char *) NULL)
951 resize_filter->blur=StringToDouble(artifact);
952 if (resize_filter->blur < MagickEpsilon)
953 resize_filter->blur=(MagickRealType) MagickEpsilon;
954
955 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000956 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000957 if (artifact != (const char *) NULL)
958 {
cristybb503372010-05-27 20:51:26 +0000959 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000960 lobes;
961
cristy96b16132010-08-29 17:19:52 +0000962 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000963 if (lobes < 1)
964 lobes=1;
965 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000966 }
anthony152700d2010-10-28 02:43:18 +0000967 /* Convert a Jinc function lobes value to a real support value */
anthony61b5ddd2010-10-05 02:33:31 +0000968 if (resize_filter->filter == Jinc)
969 {
970 if (resize_filter->support > 16)
971 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
972 else
973 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
974 }
975 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000976 artifact=GetImageArtifact(image,"filter:support");
977 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000978 resize_filter->support=fabs(StringToDouble(artifact));
979 /*
nicolas07bac812010-09-19 18:47:02 +0000980 Scale windowing function separatally to the support 'clipping'
981 window that calling operator is planning to actually use. (Expert
982 override)
cristy3ed852e2009-09-05 21:47:34 +0000983 */
anthony55f12332010-09-10 01:13:02 +0000984 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000985 artifact=GetImageArtifact(image,"filter:win-support");
986 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000987 resize_filter->window_support=fabs(StringToDouble(artifact));
988 /*
anthony029ba0e2010-10-29 00:54:24 +0000989 Adjust window function scaling to match windowing support for
anthony1f90a6b2010-09-14 08:56:31 +0000990 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000991 */
992 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +0000993
anthony55f12332010-09-10 01:13:02 +0000994 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000995 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000996 */
cristy3ed852e2009-09-05 21:47:34 +0000997 B=0.0;
998 C=0.0;
cristy33b1c162010-01-23 22:51:51 +0000999 if ((filters[filter_type].function == CubicBC) ||
1000 (filters[window_type].function == CubicBC))
1001 {
anthony2d9b8b52010-09-14 08:31:07 +00001002 B=filters[filter_type].B;
1003 C=filters[filter_type].C;
1004 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +00001005 {
anthony2d9b8b52010-09-14 08:31:07 +00001006 B=filters[window_type].B;
1007 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +00001008 }
cristy33b1c162010-01-23 22:51:51 +00001009 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001010 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001011 {
1012 B=StringToDouble(artifact);
nicolasd15ee9a2010-10-24 18:39:45 +00001013 C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001014 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001015 if (artifact != (const char *) NULL)
1016 C=StringToDouble(artifact);
1017 }
1018 else
1019 {
1020 artifact=GetImageArtifact(image,"filter:c");
1021 if (artifact != (const char *) NULL)
1022 {
1023 C=StringToDouble(artifact);
nicolasb7dff642010-10-25 02:04:14 +00001024 B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001025 }
1026 }
nicolasc6bac3b2010-10-24 18:10:45 +00001027 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
nicolasd15ee9a2010-10-24 18:39:45 +00001028 {
anthony06b1edf2010-10-25 01:19:50 +00001029 const double twoB = B+B;
cristy5cce74b2010-11-15 03:24:28 +00001030 resize_filter->coefficient[0]=1.0-(1.0/3.0)*B;
1031 resize_filter->coefficient[1]=-3.0+twoB+C;
1032 resize_filter->coefficient[2]=2.0-1.5*B-C;
1033 resize_filter->coefficient[3]=(4.0/3.0)*B+4.0*C;
1034 resize_filter->coefficient[4]=-8.0*C-twoB;
1035 resize_filter->coefficient[5]=B+5.0*C;
1036 resize_filter->coefficient[6]=(-1.0/6.0)*B-C;
nicolasd15ee9a2010-10-24 18:39:45 +00001037 }
nicolasc6bac3b2010-10-24 18:10:45 +00001038 }
anthonyf5e76ef2010-10-12 01:22:01 +00001039
anthony55f12332010-09-10 01:13:02 +00001040 /*
nicolas07bac812010-09-19 18:47:02 +00001041 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001042 */
cristyf5b49372010-10-16 01:06:47 +00001043#if defined(MAGICKCORE_OPENMP_SUPPORT)
1044 #pragma omp master
1045 {
1046#endif
1047 artifact=GetImageArtifact(image,"filter:verbose");
anthony28ad1d72010-10-26 06:30:24 +00001048 if (IsMagickTrue(artifact))
cristyf5b49372010-10-16 01:06:47 +00001049 {
1050 double
anthony06b1edf2010-10-25 01:19:50 +00001051 support,
cristyf5b49372010-10-16 01:06:47 +00001052 x;
cristy3ed852e2009-09-05 21:47:34 +00001053
cristyf5b49372010-10-16 01:06:47 +00001054 /*
1055 Set the weighting function properly when the weighting
1056 function may not exactly match the filter of the same name.
anthony06b1edf2010-10-25 01:19:50 +00001057 EG: a Point filter is really uses a Box weighting function
cristyf5b49372010-10-16 01:06:47 +00001058 with a different support than is typically used.
cristyf5b49372010-10-16 01:06:47 +00001059 */
1060 if (resize_filter->filter == Box) filter_type=BoxFilter;
1061 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1062 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1063 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1064 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
anthony1ad20052010-10-29 01:53:03 +00001065 if (resize_filter->window == Box) window_type=BoxFilter;
anthony152700d2010-10-28 02:43:18 +00001066 if (resize_filter->window == Sinc) window_type=SincFilter;
1067 if (resize_filter->window == SincFast) window_type=SincFastFilter;
anthony1ad20052010-10-29 01:53:03 +00001068 if (resize_filter->window == Jinc) window_type=JincFilter;
anthony152700d2010-10-28 02:43:18 +00001069 if (resize_filter->window == CubicBC) window_type=CubicFilter;
cristyf5b49372010-10-16 01:06:47 +00001070 /*
1071 Report Filter Details.
1072 */
1073 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1074 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
anthony06b1edf2010-10-25 01:19:50 +00001075 (void) fprintf(stdout,"# filter = %s\n",
1076 MagickOptionToMnemonic(MagickFilterOptions,filter_type));
1077 (void) fprintf(stdout,"# window = %s\n",
1078 MagickOptionToMnemonic(MagickFilterOptions, window_type));
1079 (void) fprintf(stdout,"# support = %.*g\n",
1080 GetMagickPrecision(),(double) resize_filter->support);
1081 (void) fprintf(stdout,"# win-support = %.*g\n",
1082 GetMagickPrecision(),(double) resize_filter->window_support);
1083 (void) fprintf(stdout,"# scale_blur = %.*g\n",
1084 GetMagickPrecision(), (double)resize_filter->blur);
cristyf5b49372010-10-16 01:06:47 +00001085 if ( filter_type == GaussianFilter )
anthony06b1edf2010-10-25 01:19:50 +00001086 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",
1087 GetMagickPrecision(), (double)sigma);
1088 (void) fprintf(stdout,"# practical_support = %.*g\n",
1089 GetMagickPrecision(), (double)support);
cristyf5b49372010-10-16 01:06:47 +00001090 if ( filter_type == CubicFilter || window_type == CubicFilter )
anthony06b1edf2010-10-25 01:19:50 +00001091 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",
1092 GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
cristyf5b49372010-10-16 01:06:47 +00001093 (void) fprintf(stdout,"\n");
1094 /*
1095 Output values of resulting filter graph -- for graphing
1096 filter result.
1097 */
1098 for (x=0.0; x <= support; x+=0.01f)
1099 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1100 (double) GetResizeFilterWeight(resize_filter,x));
1101 /* A final value so gnuplot can graph the 'stop' properly. */
1102 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1103 0.0);
1104 }
1105 /* Output the above once only for each image - remove setting */
1106 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1107#if defined(MAGICKCORE_OPENMP_SUPPORT)
1108 }
1109#endif
cristy3ed852e2009-09-05 21:47:34 +00001110 return(resize_filter);
1111}
1112
1113/*
1114%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1115% %
1116% %
1117% %
1118% A d a p t i v e R e s i z e I m a g e %
1119% %
1120% %
1121% %
1122%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1123%
1124% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1125%
1126% The format of the AdaptiveResizeImage method is:
1127%
cristy9af9b5d2010-08-15 17:04:28 +00001128% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1129% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001130%
1131% A description of each parameter follows:
1132%
1133% o image: the image.
1134%
1135% o columns: the number of columns in the resized image.
1136%
1137% o rows: the number of rows in the resized image.
1138%
1139% o exception: return any errors or warnings in this structure.
1140%
1141*/
1142MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001143 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001144{
1145#define AdaptiveResizeImageTag "Resize/Image"
1146
cristyc4c8d132010-01-07 01:58:38 +00001147 CacheView
1148 *resize_view;
1149
cristy3ed852e2009-09-05 21:47:34 +00001150 Image
1151 *resize_image;
1152
cristy3ed852e2009-09-05 21:47:34 +00001153 MagickBooleanType
cristy5cce74b2010-11-15 03:24:28 +00001154 status;
cristy3ed852e2009-09-05 21:47:34 +00001155
cristy5cce74b2010-11-15 03:24:28 +00001156 MagickOffsetType
1157 progress;
cristy3ed852e2009-09-05 21:47:34 +00001158
1159 ResampleFilter
cristya6a18782010-11-15 01:56:25 +00001160 **resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00001161
cristy9af9b5d2010-08-15 17:04:28 +00001162 ssize_t
1163 y;
1164
cristy3ed852e2009-09-05 21:47:34 +00001165 /*
1166 Adaptively resize image.
1167 */
1168 assert(image != (const Image *) NULL);
1169 assert(image->signature == MagickSignature);
1170 if (image->debug != MagickFalse)
1171 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1172 assert(exception != (ExceptionInfo *) NULL);
1173 assert(exception->signature == MagickSignature);
1174 if ((columns == 0) || (rows == 0))
1175 return((Image *) NULL);
1176 if ((columns == image->columns) && (rows == image->rows))
1177 return(CloneImage(image,0,0,MagickTrue,exception));
1178 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1179 if (resize_image == (Image *) NULL)
1180 return((Image *) NULL);
1181 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1182 {
1183 InheritException(exception,&resize_image->exception);
1184 resize_image=DestroyImage(resize_image);
1185 return((Image *) NULL);
1186 }
cristy5cce74b2010-11-15 03:24:28 +00001187 status=MagickTrue;
1188 progress=0;
cristya6a18782010-11-15 01:56:25 +00001189 resample_filter=AcquireResampleFilterThreadSet(image,
1190 UndefinedVirtualPixelMethod,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00001191 resize_view=AcquireCacheView(resize_image);
cristy5cce74b2010-11-15 03:24:28 +00001192#if defined(MAGICKCORE_OPENMP_SUPPORT)
1193 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
1194#endif
cristybb503372010-05-27 20:51:26 +00001195 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001196 {
cristya6a18782010-11-15 01:56:25 +00001197 const int
1198 id = GetOpenMPThreadId();
1199
cristy5cce74b2010-11-15 03:24:28 +00001200 MagickPixelPacket
1201 pixel;
1202
1203 PointInfo
1204 offset;
1205
cristy3ed852e2009-09-05 21:47:34 +00001206 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001207 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001208
cristy3ed852e2009-09-05 21:47:34 +00001209 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001210 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001211
cristyb2a9be02010-11-15 15:00:14 +00001212 register ssize_t
1213 x;
1214
cristy5cce74b2010-11-15 03:24:28 +00001215 if (status == MagickFalse)
1216 continue;
cristy3ed852e2009-09-05 21:47:34 +00001217 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1218 exception);
1219 if (q == (PixelPacket *) NULL)
cristy5cce74b2010-11-15 03:24:28 +00001220 continue;
cristy3ed852e2009-09-05 21:47:34 +00001221 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1222 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristy5cce74b2010-11-15 03:24:28 +00001223 GetMagickPixelPacket(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001224 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001225 {
1226 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
cristya6a18782010-11-15 01:56:25 +00001227 (void) ResamplePixelColor(resample_filter[id],offset.x-0.5,offset.y-0.5,
cristy3ed852e2009-09-05 21:47:34 +00001228 &pixel);
1229 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1230 q++;
1231 }
1232 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
cristy5cce74b2010-11-15 03:24:28 +00001233 continue;
1234 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1235 {
1236 MagickBooleanType
1237 proceed;
1238
1239#if defined(MAGICKCORE_OPENMP_SUPPORT)
1240 #pragma omp critical (MagickCore_AdaptiveResizeImage)
1241#endif
1242 proceed=SetImageProgress(image,AdaptiveResizeImageTag,progress++,
1243 image->rows);
1244 if (proceed == MagickFalse)
1245 status=MagickFalse;
1246 }
cristy3ed852e2009-09-05 21:47:34 +00001247 }
cristya6a18782010-11-15 01:56:25 +00001248 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
cristy3ed852e2009-09-05 21:47:34 +00001249 resize_view=DestroyCacheView(resize_view);
cristy5cce74b2010-11-15 03:24:28 +00001250 if (status == MagickFalse)
1251 resize_image=DestroyImage(resize_image);
cristy3ed852e2009-09-05 21:47:34 +00001252 return(resize_image);
1253}
1254
1255/*
1256%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1257% %
1258% %
1259% %
1260+ B e s s e l O r d e r O n e %
1261% %
1262% %
1263% %
1264%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1265%
1266% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001267% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001268%
1269% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1270%
1271% j1(x) = x*j1(x);
1272%
1273% For x in (8,inf)
1274%
1275% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1276%
1277% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1278%
1279% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1280% = 1/sqrt(2) * (sin(x) - cos(x))
1281% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1282% = -1/sqrt(2) * (sin(x) + cos(x))
1283%
1284% The format of the BesselOrderOne method is:
1285%
1286% MagickRealType BesselOrderOne(MagickRealType x)
1287%
1288% A description of each parameter follows:
1289%
1290% o x: MagickRealType value.
1291%
1292*/
1293
1294#undef I0
1295static MagickRealType I0(MagickRealType x)
1296{
1297 MagickRealType
1298 sum,
1299 t,
1300 y;
1301
cristybb503372010-05-27 20:51:26 +00001302 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001303 i;
1304
1305 /*
1306 Zeroth order Bessel function of the first kind.
1307 */
1308 sum=1.0;
1309 y=x*x/4.0;
1310 t=y;
1311 for (i=2; t > MagickEpsilon; i++)
1312 {
1313 sum+=t;
1314 t*=y/((MagickRealType) i*i);
1315 }
1316 return(sum);
1317}
1318
1319#undef J1
1320static MagickRealType J1(MagickRealType x)
1321{
1322 MagickRealType
1323 p,
1324 q;
1325
cristybb503372010-05-27 20:51:26 +00001326 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001327 i;
1328
1329 static const double
1330 Pone[] =
1331 {
1332 0.581199354001606143928050809e+21,
1333 -0.6672106568924916298020941484e+20,
1334 0.2316433580634002297931815435e+19,
1335 -0.3588817569910106050743641413e+17,
1336 0.2908795263834775409737601689e+15,
1337 -0.1322983480332126453125473247e+13,
1338 0.3413234182301700539091292655e+10,
1339 -0.4695753530642995859767162166e+7,
1340 0.270112271089232341485679099e+4
1341 },
1342 Qone[] =
1343 {
1344 0.11623987080032122878585294e+22,
1345 0.1185770712190320999837113348e+20,
1346 0.6092061398917521746105196863e+17,
1347 0.2081661221307607351240184229e+15,
1348 0.5243710262167649715406728642e+12,
1349 0.1013863514358673989967045588e+10,
1350 0.1501793594998585505921097578e+7,
1351 0.1606931573481487801970916749e+4,
1352 0.1e+1
1353 };
1354
1355 p=Pone[8];
1356 q=Qone[8];
1357 for (i=7; i >= 0; i--)
1358 {
1359 p=p*x*x+Pone[i];
1360 q=q*x*x+Qone[i];
1361 }
1362 return(p/q);
1363}
1364
1365#undef P1
1366static MagickRealType P1(MagickRealType x)
1367{
1368 MagickRealType
1369 p,
1370 q;
1371
cristybb503372010-05-27 20:51:26 +00001372 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001373 i;
1374
1375 static const double
1376 Pone[] =
1377 {
1378 0.352246649133679798341724373e+5,
1379 0.62758845247161281269005675e+5,
1380 0.313539631109159574238669888e+5,
1381 0.49854832060594338434500455e+4,
1382 0.2111529182853962382105718e+3,
1383 0.12571716929145341558495e+1
1384 },
1385 Qone[] =
1386 {
1387 0.352246649133679798068390431e+5,
1388 0.626943469593560511888833731e+5,
1389 0.312404063819041039923015703e+5,
1390 0.4930396490181088979386097e+4,
1391 0.2030775189134759322293574e+3,
1392 0.1e+1
1393 };
1394
1395 p=Pone[5];
1396 q=Qone[5];
1397 for (i=4; i >= 0; i--)
1398 {
1399 p=p*(8.0/x)*(8.0/x)+Pone[i];
1400 q=q*(8.0/x)*(8.0/x)+Qone[i];
1401 }
1402 return(p/q);
1403}
1404
1405#undef Q1
1406static MagickRealType Q1(MagickRealType x)
1407{
1408 MagickRealType
1409 p,
1410 q;
1411
cristybb503372010-05-27 20:51:26 +00001412 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001413 i;
1414
1415 static const double
1416 Pone[] =
1417 {
1418 0.3511751914303552822533318e+3,
1419 0.7210391804904475039280863e+3,
1420 0.4259873011654442389886993e+3,
1421 0.831898957673850827325226e+2,
1422 0.45681716295512267064405e+1,
1423 0.3532840052740123642735e-1
1424 },
1425 Qone[] =
1426 {
1427 0.74917374171809127714519505e+4,
1428 0.154141773392650970499848051e+5,
1429 0.91522317015169922705904727e+4,
1430 0.18111867005523513506724158e+4,
1431 0.1038187585462133728776636e+3,
1432 0.1e+1
1433 };
1434
1435 p=Pone[5];
1436 q=Qone[5];
1437 for (i=4; i >= 0; i--)
1438 {
1439 p=p*(8.0/x)*(8.0/x)+Pone[i];
1440 q=q*(8.0/x)*(8.0/x)+Qone[i];
1441 }
1442 return(p/q);
1443}
1444
1445static MagickRealType BesselOrderOne(MagickRealType x)
1446{
1447 MagickRealType
1448 p,
1449 q;
1450
1451 if (x == 0.0)
1452 return(0.0);
1453 p=x;
1454 if (x < 0.0)
1455 x=(-x);
1456 if (x < 8.0)
1457 return(p*J1(x));
1458 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1459 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1460 cos((double) x))));
1461 if (p < 0.0)
1462 q=(-q);
1463 return(q);
1464}
1465
1466/*
1467%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1468% %
1469% %
1470% %
1471+ D e s t r o y R e s i z e F i l t e r %
1472% %
1473% %
1474% %
1475%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1476%
1477% DestroyResizeFilter() destroy the resize filter.
1478%
cristya2ffd7e2010-03-10 20:50:30 +00001479% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001480%
1481% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1482%
1483% A description of each parameter follows:
1484%
1485% o resize_filter: the resize filter.
1486%
1487*/
1488MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1489{
1490 assert(resize_filter != (ResizeFilter *) NULL);
1491 assert(resize_filter->signature == MagickSignature);
1492 resize_filter->signature=(~MagickSignature);
1493 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1494 return(resize_filter);
1495}
1496
1497/*
1498%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1499% %
1500% %
1501% %
1502+ G e t R e s i z e F i l t e r S u p p o r t %
1503% %
1504% %
1505% %
1506%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1507%
1508% GetResizeFilterSupport() return the current support window size for this
1509% filter. Note that this may have been enlarged by filter:blur factor.
1510%
1511% The format of the GetResizeFilterSupport method is:
1512%
1513% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1514%
1515% A description of each parameter follows:
1516%
1517% o filter: Image filter to use.
1518%
1519*/
1520MagickExport MagickRealType GetResizeFilterSupport(
1521 const ResizeFilter *resize_filter)
1522{
1523 assert(resize_filter != (ResizeFilter *) NULL);
1524 assert(resize_filter->signature == MagickSignature);
1525 return(resize_filter->support*resize_filter->blur);
1526}
1527
1528/*
1529%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1530% %
1531% %
1532% %
1533+ G e t R e s i z e F i l t e r W e i g h t %
1534% %
1535% %
1536% %
1537%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1538%
1539% GetResizeFilterWeight evaluates the specified resize filter at the point x
1540% which usally lies between zero and the filters current 'support' and
1541% returns the weight of the filter function at that point.
1542%
1543% The format of the GetResizeFilterWeight method is:
1544%
1545% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1546% const MagickRealType x)
1547%
1548% A description of each parameter follows:
1549%
1550% o filter: the filter type.
1551%
1552% o x: the point.
1553%
1554*/
1555MagickExport MagickRealType GetResizeFilterWeight(
1556 const ResizeFilter *resize_filter,const MagickRealType x)
1557{
1558 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001559 scale,
1560 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001561
1562 /*
1563 Windowing function - scale the weighting filter by this amount.
1564 */
1565 assert(resize_filter != (ResizeFilter *) NULL);
1566 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001567 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001568 if ((resize_filter->window_support < MagickEpsilon) ||
1569 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001570 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001571 else
1572 {
anthony55f12332010-09-10 01:13:02 +00001573 scale=resize_filter->scale;
1574 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001575 }
anthony55f12332010-09-10 01:13:02 +00001576 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001577}
1578
1579/*
1580%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1581% %
1582% %
1583% %
1584% M a g n i f y I m a g e %
1585% %
1586% %
1587% %
1588%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1589%
1590% MagnifyImage() is a convenience method that scales an image proportionally
1591% to twice its size.
1592%
1593% The format of the MagnifyImage method is:
1594%
1595% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1596%
1597% A description of each parameter follows:
1598%
1599% o image: the image.
1600%
1601% o exception: return any errors or warnings in this structure.
1602%
1603*/
1604MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1605{
1606 Image
1607 *magnify_image;
1608
1609 assert(image != (Image *) NULL);
1610 assert(image->signature == MagickSignature);
1611 if (image->debug != MagickFalse)
1612 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1613 assert(exception != (ExceptionInfo *) NULL);
1614 assert(exception->signature == MagickSignature);
1615 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1616 1.0,exception);
1617 return(magnify_image);
1618}
1619
1620/*
1621%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1622% %
1623% %
1624% %
1625% M i n i f y I m a g e %
1626% %
1627% %
1628% %
1629%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1630%
1631% MinifyImage() is a convenience method that scales an image proportionally
1632% to half its size.
1633%
1634% The format of the MinifyImage method is:
1635%
1636% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1637%
1638% A description of each parameter follows:
1639%
1640% o image: the image.
1641%
1642% o exception: return any errors or warnings in this structure.
1643%
1644*/
1645MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1646{
1647 Image
1648 *minify_image;
1649
1650 assert(image != (Image *) NULL);
1651 assert(image->signature == MagickSignature);
1652 if (image->debug != MagickFalse)
1653 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1654 assert(exception != (ExceptionInfo *) NULL);
1655 assert(exception->signature == MagickSignature);
1656 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1657 1.0,exception);
1658 return(minify_image);
1659}
1660
1661/*
1662%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1663% %
1664% %
1665% %
1666% R e s a m p l e I m a g e %
1667% %
1668% %
1669% %
1670%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1671%
1672% ResampleImage() resize image in terms of its pixel size, so that when
1673% displayed at the given resolution it will be the same size in terms of
1674% real world units as the original image at the original resolution.
1675%
1676% The format of the ResampleImage method is:
1677%
1678% Image *ResampleImage(Image *image,const double x_resolution,
1679% const double y_resolution,const FilterTypes filter,const double blur,
1680% ExceptionInfo *exception)
1681%
1682% A description of each parameter follows:
1683%
1684% o image: the image to be resized to fit the given resolution.
1685%
1686% o x_resolution: the new image x resolution.
1687%
1688% o y_resolution: the new image y resolution.
1689%
1690% o filter: Image filter to use.
1691%
1692% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1693%
1694*/
1695MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1696 const double y_resolution,const FilterTypes filter,const double blur,
1697 ExceptionInfo *exception)
1698{
1699#define ResampleImageTag "Resample/Image"
1700
1701 Image
1702 *resample_image;
1703
cristybb503372010-05-27 20:51:26 +00001704 size_t
cristy3ed852e2009-09-05 21:47:34 +00001705 height,
1706 width;
1707
1708 /*
1709 Initialize sampled image attributes.
1710 */
1711 assert(image != (const Image *) NULL);
1712 assert(image->signature == MagickSignature);
1713 if (image->debug != MagickFalse)
1714 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1715 assert(exception != (ExceptionInfo *) NULL);
1716 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001717 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1718 72.0 : image->x_resolution)+0.5);
1719 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1720 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001721 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1722 if (resample_image != (Image *) NULL)
1723 {
1724 resample_image->x_resolution=x_resolution;
1725 resample_image->y_resolution=y_resolution;
1726 }
1727 return(resample_image);
1728}
1729#if defined(MAGICKCORE_LQR_DELEGATE)
1730
1731/*
1732%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1733% %
1734% %
1735% %
1736% L i q u i d R e s c a l e I m a g e %
1737% %
1738% %
1739% %
1740%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1741%
1742% LiquidRescaleImage() rescales image with seam carving.
1743%
1744% The format of the LiquidRescaleImage method is:
1745%
1746% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001747% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001748% const double delta_x,const double rigidity,ExceptionInfo *exception)
1749%
1750% A description of each parameter follows:
1751%
1752% o image: the image.
1753%
1754% o columns: the number of columns in the rescaled image.
1755%
1756% o rows: the number of rows in the rescaled image.
1757%
1758% o delta_x: maximum seam transversal step (0 means straight seams).
1759%
1760% o rigidity: introduce a bias for non-straight seams (typically 0).
1761%
1762% o exception: return any errors or warnings in this structure.
1763%
1764*/
cristy9af9b5d2010-08-15 17:04:28 +00001765MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1766 const size_t rows,const double delta_x,const double rigidity,
1767 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001768{
1769#define LiquidRescaleImageTag "Rescale/Image"
1770
cristyc5c6f662010-09-22 14:23:02 +00001771 CacheView
1772 *rescale_view;
1773
cristy3ed852e2009-09-05 21:47:34 +00001774 const char
1775 *map;
1776
1777 guchar
1778 *packet;
1779
1780 Image
1781 *rescale_image;
1782
1783 int
1784 x,
1785 y;
1786
1787 LqrCarver
1788 *carver;
1789
1790 LqrRetVal
1791 lqr_status;
1792
1793 MagickBooleanType
1794 status;
1795
1796 MagickPixelPacket
1797 pixel;
1798
1799 unsigned char
1800 *pixels;
1801
1802 /*
1803 Liquid rescale image.
1804 */
1805 assert(image != (const Image *) NULL);
1806 assert(image->signature == MagickSignature);
1807 if (image->debug != MagickFalse)
1808 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1809 assert(exception != (ExceptionInfo *) NULL);
1810 assert(exception->signature == MagickSignature);
1811 if ((columns == 0) || (rows == 0))
1812 return((Image *) NULL);
1813 if ((columns == image->columns) && (rows == image->rows))
1814 return(CloneImage(image,0,0,MagickTrue,exception));
1815 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001816 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001817 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1818 {
1819 Image
1820 *resize_image;
1821
cristybb503372010-05-27 20:51:26 +00001822 size_t
cristy3ed852e2009-09-05 21:47:34 +00001823 height,
1824 width;
1825
1826 /*
1827 Honor liquid resize size limitations.
1828 */
1829 for (width=image->columns; columns >= (2*width-1); width*=2);
1830 for (height=image->rows; rows >= (2*height-1); height*=2);
1831 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1832 exception);
1833 if (resize_image == (Image *) NULL)
1834 return((Image *) NULL);
1835 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1836 rigidity,exception);
1837 resize_image=DestroyImage(resize_image);
1838 return(rescale_image);
1839 }
1840 map="RGB";
1841 if (image->matte == MagickFalse)
1842 map="RGBA";
1843 if (image->colorspace == CMYKColorspace)
1844 {
1845 map="CMYK";
1846 if (image->matte == MagickFalse)
1847 map="CMYKA";
1848 }
1849 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1850 strlen(map)*sizeof(*pixels));
1851 if (pixels == (unsigned char *) NULL)
1852 return((Image *) NULL);
1853 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1854 pixels,exception);
1855 if (status == MagickFalse)
1856 {
1857 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1858 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1859 }
1860 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1861 if (carver == (LqrCarver *) NULL)
1862 {
1863 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1864 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1865 }
1866 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1867 lqr_status=lqr_carver_resize(carver,columns,rows);
1868 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1869 lqr_carver_get_height(carver),MagickTrue,exception);
1870 if (rescale_image == (Image *) NULL)
1871 {
1872 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1873 return((Image *) NULL);
1874 }
1875 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1876 {
1877 InheritException(exception,&rescale_image->exception);
1878 rescale_image=DestroyImage(rescale_image);
1879 return((Image *) NULL);
1880 }
1881 GetMagickPixelPacket(rescale_image,&pixel);
1882 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001883 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001884 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1885 {
1886 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001887 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001888
1889 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001890 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001891
anthony22aad252010-09-23 06:59:07 +00001892 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001893 if (q == (PixelPacket *) NULL)
1894 break;
cristyc5c6f662010-09-22 14:23:02 +00001895 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001896 pixel.red=QuantumRange*(packet[0]/255.0);
1897 pixel.green=QuantumRange*(packet[1]/255.0);
1898 pixel.blue=QuantumRange*(packet[2]/255.0);
1899 if (image->colorspace != CMYKColorspace)
1900 {
1901 if (image->matte == MagickFalse)
1902 pixel.opacity=QuantumRange*(packet[3]/255.0);
1903 }
1904 else
1905 {
1906 pixel.index=QuantumRange*(packet[3]/255.0);
1907 if (image->matte == MagickFalse)
1908 pixel.opacity=QuantumRange*(packet[4]/255.0);
1909 }
1910 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001911 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001912 break;
1913 }
cristyc5c6f662010-09-22 14:23:02 +00001914 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001915 /*
1916 Relinquish resources.
1917 */
1918 lqr_carver_destroy(carver);
1919 return(rescale_image);
1920}
1921#else
1922MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001923 const size_t magick_unused(columns),const size_t magick_unused(rows),
1924 const double magick_unused(delta_x),const double magick_unused(rigidity),
1925 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001926{
1927 assert(image != (const Image *) NULL);
1928 assert(image->signature == MagickSignature);
1929 if (image->debug != MagickFalse)
1930 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1931 assert(exception != (ExceptionInfo *) NULL);
1932 assert(exception->signature == MagickSignature);
1933 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1934 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1935 return((Image *) NULL);
1936}
1937#endif
1938
1939/*
1940%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1941% %
1942% %
1943% %
1944% R e s i z e I m a g e %
1945% %
1946% %
1947% %
1948%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1949%
1950% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001951% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001952%
1953% If an undefined filter is given the filter defaults to Mitchell for a
1954% colormapped image, a image with a matte channel, or if the image is
1955% enlarged. Otherwise the filter defaults to a Lanczos.
1956%
1957% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1958%
1959% The format of the ResizeImage method is:
1960%
cristybb503372010-05-27 20:51:26 +00001961% Image *ResizeImage(Image *image,const size_t columns,
1962% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001963% ExceptionInfo *exception)
1964%
1965% A description of each parameter follows:
1966%
1967% o image: the image.
1968%
1969% o columns: the number of columns in the scaled image.
1970%
1971% o rows: the number of rows in the scaled image.
1972%
1973% o filter: Image filter to use.
1974%
cristy9af9b5d2010-08-15 17:04:28 +00001975% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1976% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001977%
1978% o exception: return any errors or warnings in this structure.
1979%
1980*/
1981
1982typedef struct _ContributionInfo
1983{
1984 MagickRealType
1985 weight;
1986
cristybb503372010-05-27 20:51:26 +00001987 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001988 pixel;
1989} ContributionInfo;
1990
1991static ContributionInfo **DestroyContributionThreadSet(
1992 ContributionInfo **contribution)
1993{
cristybb503372010-05-27 20:51:26 +00001994 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001995 i;
1996
1997 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001998 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001999 if (contribution[i] != (ContributionInfo *) NULL)
2000 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
2001 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00002002 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00002003 return(contribution);
2004}
2005
2006static ContributionInfo **AcquireContributionThreadSet(const size_t count)
2007{
cristybb503372010-05-27 20:51:26 +00002008 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002009 i;
2010
2011 ContributionInfo
2012 **contribution;
2013
cristybb503372010-05-27 20:51:26 +00002014 size_t
cristy3ed852e2009-09-05 21:47:34 +00002015 number_threads;
2016
2017 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002018 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00002019 sizeof(*contribution));
2020 if (contribution == (ContributionInfo **) NULL)
2021 return((ContributionInfo **) NULL);
2022 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00002023 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002024 {
2025 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2026 sizeof(**contribution));
2027 if (contribution[i] == (ContributionInfo *) NULL)
2028 return(DestroyContributionThreadSet(contribution));
2029 }
2030 return(contribution);
2031}
2032
2033static inline double MagickMax(const double x,const double y)
2034{
2035 if (x > y)
2036 return(x);
2037 return(y);
2038}
2039
2040static inline double MagickMin(const double x,const double y)
2041{
2042 if (x < y)
2043 return(x);
2044 return(y);
2045}
2046
2047static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2048 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002049 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002050{
2051#define ResizeImageTag "Resize/Image"
2052
cristyfa112112010-01-04 17:48:07 +00002053 CacheView
2054 *image_view,
2055 *resize_view;
2056
cristy3ed852e2009-09-05 21:47:34 +00002057 ClassType
2058 storage_class;
2059
2060 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002061 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002062
cristy3ed852e2009-09-05 21:47:34 +00002063 MagickBooleanType
2064 status;
2065
2066 MagickPixelPacket
2067 zero;
2068
2069 MagickRealType
2070 scale,
2071 support;
2072
cristy9af9b5d2010-08-15 17:04:28 +00002073 ssize_t
2074 x;
2075
cristy3ed852e2009-09-05 21:47:34 +00002076 /*
2077 Apply filter to resize horizontally from image to resize image.
2078 */
cristy5d824382010-09-06 14:00:17 +00002079 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002080 support=scale*GetResizeFilterSupport(resize_filter);
2081 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2082 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2083 {
2084 InheritException(exception,&resize_image->exception);
2085 return(MagickFalse);
2086 }
2087 if (support < 0.5)
2088 {
2089 /*
nicolas07bac812010-09-19 18:47:02 +00002090 Support too small even for nearest neighbour: Reduce to point
2091 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002092 */
2093 support=(MagickRealType) 0.5;
2094 scale=1.0;
2095 }
2096 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2097 if (contributions == (ContributionInfo **) NULL)
2098 {
2099 (void) ThrowMagickException(exception,GetMagickModule(),
2100 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2101 return(MagickFalse);
2102 }
2103 status=MagickTrue;
2104 scale=1.0/scale;
2105 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2106 image_view=AcquireCacheView(image);
2107 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002108#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002109 #pragma omp parallel for shared(status)
2110#endif
cristybb503372010-05-27 20:51:26 +00002111 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002112 {
cristy3ed852e2009-09-05 21:47:34 +00002113 MagickRealType
2114 center,
2115 density;
2116
2117 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002118 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002119
2120 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002121 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002122
cristy03dbbd22010-09-19 23:04:47 +00002123 register ContributionInfo
2124 *restrict contribution;
2125
cristy3ed852e2009-09-05 21:47:34 +00002126 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002127 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002128
cristy3ed852e2009-09-05 21:47:34 +00002129 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002130 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002131
cristy03dbbd22010-09-19 23:04:47 +00002132 register ssize_t
2133 y;
2134
cristy9af9b5d2010-08-15 17:04:28 +00002135 ssize_t
2136 n,
2137 start,
2138 stop;
2139
cristy3ed852e2009-09-05 21:47:34 +00002140 if (status == MagickFalse)
2141 continue;
2142 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002143 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2144 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002145 density=0.0;
2146 contribution=contributions[GetOpenMPThreadId()];
2147 for (n=0; n < (stop-start); n++)
2148 {
2149 contribution[n].pixel=start+n;
2150 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2151 ((MagickRealType) (start+n)-center+0.5));
2152 density+=contribution[n].weight;
2153 }
2154 if ((density != 0.0) && (density != 1.0))
2155 {
cristybb503372010-05-27 20:51:26 +00002156 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002157 i;
2158
2159 /*
2160 Normalize.
2161 */
2162 density=1.0/density;
2163 for (i=0; i < n; i++)
2164 contribution[i].weight*=density;
2165 }
cristy9af9b5d2010-08-15 17:04:28 +00002166 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2167 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002168 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2169 exception);
2170 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2171 {
2172 status=MagickFalse;
2173 continue;
2174 }
2175 indexes=GetCacheViewVirtualIndexQueue(image_view);
2176 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002177 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002178 {
cristy3ed852e2009-09-05 21:47:34 +00002179 MagickPixelPacket
2180 pixel;
2181
2182 MagickRealType
2183 alpha;
2184
cristybb503372010-05-27 20:51:26 +00002185 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002186 i;
2187
cristy9af9b5d2010-08-15 17:04:28 +00002188 ssize_t
2189 j;
2190
cristy3ed852e2009-09-05 21:47:34 +00002191 pixel=zero;
2192 if (image->matte == MagickFalse)
2193 {
2194 for (i=0; i < n; i++)
2195 {
2196 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2197 (contribution[i].pixel-contribution[0].pixel);
2198 alpha=contribution[i].weight;
2199 pixel.red+=alpha*(p+j)->red;
2200 pixel.green+=alpha*(p+j)->green;
2201 pixel.blue+=alpha*(p+j)->blue;
2202 pixel.opacity+=alpha*(p+j)->opacity;
2203 }
cristyce70c172010-01-07 17:15:30 +00002204 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2205 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2206 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2207 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002208 if ((image->colorspace == CMYKColorspace) &&
2209 (resize_image->colorspace == CMYKColorspace))
2210 {
2211 for (i=0; i < n; i++)
2212 {
2213 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2214 (contribution[i].pixel-contribution[0].pixel);
2215 alpha=contribution[i].weight;
2216 pixel.index+=alpha*indexes[j];
2217 }
cristyce70c172010-01-07 17:15:30 +00002218 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002219 }
2220 }
2221 else
2222 {
2223 MagickRealType
2224 gamma;
2225
2226 gamma=0.0;
2227 for (i=0; i < n; i++)
2228 {
2229 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2230 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002231 alpha=contribution[i].weight*QuantumScale*
2232 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002233 pixel.red+=alpha*(p+j)->red;
2234 pixel.green+=alpha*(p+j)->green;
2235 pixel.blue+=alpha*(p+j)->blue;
2236 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2237 gamma+=alpha;
2238 }
2239 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002240 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2241 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2242 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2243 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002244 if ((image->colorspace == CMYKColorspace) &&
2245 (resize_image->colorspace == CMYKColorspace))
2246 {
2247 for (i=0; i < n; i++)
2248 {
2249 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2250 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002251 alpha=contribution[i].weight*QuantumScale*
2252 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002253 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002254 }
cristyce70c172010-01-07 17:15:30 +00002255 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2256 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002257 }
2258 }
2259 if ((resize_image->storage_class == PseudoClass) &&
2260 (image->storage_class == PseudoClass))
2261 {
cristybb503372010-05-27 20:51:26 +00002262 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002263 1.0)+0.5);
2264 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2265 (contribution[i-start].pixel-contribution[0].pixel);
2266 resize_indexes[y]=indexes[j];
2267 }
2268 q++;
2269 }
2270 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2271 status=MagickFalse;
2272 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2273 {
2274 MagickBooleanType
2275 proceed;
2276
cristyb5d5f722009-11-04 03:03:49 +00002277#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002278 #pragma omp critical (MagickCore_HorizontalFilter)
2279#endif
cristy9af9b5d2010-08-15 17:04:28 +00002280 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002281 if (proceed == MagickFalse)
2282 status=MagickFalse;
2283 }
2284 }
2285 resize_view=DestroyCacheView(resize_view);
2286 image_view=DestroyCacheView(image_view);
2287 contributions=DestroyContributionThreadSet(contributions);
2288 return(status);
2289}
2290
2291static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2292 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002293 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002294{
cristyfa112112010-01-04 17:48:07 +00002295 CacheView
2296 *image_view,
2297 *resize_view;
2298
cristy3ed852e2009-09-05 21:47:34 +00002299 ClassType
2300 storage_class;
2301
2302 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002303 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002304
cristy3ed852e2009-09-05 21:47:34 +00002305 MagickBooleanType
2306 status;
2307
2308 MagickPixelPacket
2309 zero;
2310
2311 MagickRealType
2312 scale,
2313 support;
2314
cristy9af9b5d2010-08-15 17:04:28 +00002315 ssize_t
2316 y;
2317
cristy3ed852e2009-09-05 21:47:34 +00002318 /*
cristy9af9b5d2010-08-15 17:04:28 +00002319 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002320 */
cristy5d824382010-09-06 14:00:17 +00002321 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002322 support=scale*GetResizeFilterSupport(resize_filter);
2323 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2324 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2325 {
2326 InheritException(exception,&resize_image->exception);
2327 return(MagickFalse);
2328 }
2329 if (support < 0.5)
2330 {
2331 /*
nicolas07bac812010-09-19 18:47:02 +00002332 Support too small even for nearest neighbour: Reduce to point
2333 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002334 */
2335 support=(MagickRealType) 0.5;
2336 scale=1.0;
2337 }
2338 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2339 if (contributions == (ContributionInfo **) NULL)
2340 {
2341 (void) ThrowMagickException(exception,GetMagickModule(),
2342 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2343 return(MagickFalse);
2344 }
2345 status=MagickTrue;
2346 scale=1.0/scale;
2347 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2348 image_view=AcquireCacheView(image);
2349 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002350#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002351 #pragma omp parallel for shared(status)
2352#endif
cristybb503372010-05-27 20:51:26 +00002353 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002354 {
cristy3ed852e2009-09-05 21:47:34 +00002355 MagickRealType
2356 center,
2357 density;
2358
2359 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002360 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002361
2362 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002363 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002364
cristy03dbbd22010-09-19 23:04:47 +00002365 register ContributionInfo
2366 *restrict contribution;
2367
cristy3ed852e2009-09-05 21:47:34 +00002368 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002369 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002370
cristy9af9b5d2010-08-15 17:04:28 +00002371 register PixelPacket
2372 *restrict q;
2373
cristybb503372010-05-27 20:51:26 +00002374 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002375 x;
2376
cristy9af9b5d2010-08-15 17:04:28 +00002377 ssize_t
2378 n,
2379 start,
2380 stop;
cristy3ed852e2009-09-05 21:47:34 +00002381
2382 if (status == MagickFalse)
2383 continue;
cristy679e6962010-03-18 00:42:45 +00002384 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002385 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2386 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002387 density=0.0;
2388 contribution=contributions[GetOpenMPThreadId()];
2389 for (n=0; n < (stop-start); n++)
2390 {
2391 contribution[n].pixel=start+n;
2392 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2393 ((MagickRealType) (start+n)-center+0.5));
2394 density+=contribution[n].weight;
2395 }
2396 if ((density != 0.0) && (density != 1.0))
2397 {
cristybb503372010-05-27 20:51:26 +00002398 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002399 i;
2400
2401 /*
2402 Normalize.
2403 */
2404 density=1.0/density;
2405 for (i=0; i < n; i++)
2406 contribution[i].weight*=density;
2407 }
2408 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002409 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2410 exception);
cristy3ed852e2009-09-05 21:47:34 +00002411 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2412 exception);
2413 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2414 {
2415 status=MagickFalse;
2416 continue;
2417 }
2418 indexes=GetCacheViewVirtualIndexQueue(image_view);
2419 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002420 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002421 {
cristy3ed852e2009-09-05 21:47:34 +00002422 MagickPixelPacket
2423 pixel;
2424
2425 MagickRealType
2426 alpha;
2427
cristybb503372010-05-27 20:51:26 +00002428 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002429 i;
2430
cristy9af9b5d2010-08-15 17:04:28 +00002431 ssize_t
2432 j;
2433
cristy3ed852e2009-09-05 21:47:34 +00002434 pixel=zero;
2435 if (image->matte == MagickFalse)
2436 {
2437 for (i=0; i < n; i++)
2438 {
cristybb503372010-05-27 20:51:26 +00002439 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002440 image->columns+x);
2441 alpha=contribution[i].weight;
2442 pixel.red+=alpha*(p+j)->red;
2443 pixel.green+=alpha*(p+j)->green;
2444 pixel.blue+=alpha*(p+j)->blue;
2445 pixel.opacity+=alpha*(p+j)->opacity;
2446 }
cristyce70c172010-01-07 17:15:30 +00002447 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2448 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2449 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2450 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002451 if ((image->colorspace == CMYKColorspace) &&
2452 (resize_image->colorspace == CMYKColorspace))
2453 {
2454 for (i=0; i < n; i++)
2455 {
cristybb503372010-05-27 20:51:26 +00002456 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002457 image->columns+x);
2458 alpha=contribution[i].weight;
2459 pixel.index+=alpha*indexes[j];
2460 }
cristyce70c172010-01-07 17:15:30 +00002461 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002462 }
2463 }
2464 else
2465 {
2466 MagickRealType
2467 gamma;
2468
2469 gamma=0.0;
2470 for (i=0; i < n; i++)
2471 {
cristybb503372010-05-27 20:51:26 +00002472 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002473 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002474 alpha=contribution[i].weight*QuantumScale*
2475 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002476 pixel.red+=alpha*(p+j)->red;
2477 pixel.green+=alpha*(p+j)->green;
2478 pixel.blue+=alpha*(p+j)->blue;
2479 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2480 gamma+=alpha;
2481 }
2482 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002483 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2484 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2485 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2486 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002487 if ((image->colorspace == CMYKColorspace) &&
2488 (resize_image->colorspace == CMYKColorspace))
2489 {
2490 for (i=0; i < n; i++)
2491 {
cristybb503372010-05-27 20:51:26 +00002492 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002493 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002494 alpha=contribution[i].weight*QuantumScale*
2495 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002496 pixel.index+=alpha*indexes[j];
2497 }
cristyce70c172010-01-07 17:15:30 +00002498 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2499 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002500 }
2501 }
2502 if ((resize_image->storage_class == PseudoClass) &&
2503 (image->storage_class == PseudoClass))
2504 {
cristybb503372010-05-27 20:51:26 +00002505 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002506 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002507 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002508 image->columns+x);
2509 resize_indexes[x]=indexes[j];
2510 }
2511 q++;
2512 }
2513 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2514 status=MagickFalse;
2515 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2516 {
2517 MagickBooleanType
2518 proceed;
2519
cristyb5d5f722009-11-04 03:03:49 +00002520#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002521 #pragma omp critical (MagickCore_VerticalFilter)
2522#endif
cristy9af9b5d2010-08-15 17:04:28 +00002523 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002524 if (proceed == MagickFalse)
2525 status=MagickFalse;
2526 }
2527 }
2528 resize_view=DestroyCacheView(resize_view);
2529 image_view=DestroyCacheView(image_view);
2530 contributions=DestroyContributionThreadSet(contributions);
2531 return(status);
2532}
2533
cristybb503372010-05-27 20:51:26 +00002534MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2535 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002536 ExceptionInfo *exception)
2537{
2538#define WorkLoadFactor 0.265
2539
2540 FilterTypes
2541 filter_type;
2542
2543 Image
2544 *filter_image,
2545 *resize_image;
2546
cristy9af9b5d2010-08-15 17:04:28 +00002547 MagickOffsetType
2548 offset;
2549
cristy3ed852e2009-09-05 21:47:34 +00002550 MagickRealType
2551 x_factor,
2552 y_factor;
2553
2554 MagickSizeType
2555 span;
2556
2557 MagickStatusType
2558 status;
2559
2560 ResizeFilter
2561 *resize_filter;
2562
cristy3ed852e2009-09-05 21:47:34 +00002563 /*
2564 Acquire resize image.
2565 */
2566 assert(image != (Image *) NULL);
2567 assert(image->signature == MagickSignature);
2568 if (image->debug != MagickFalse)
2569 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2570 assert(exception != (ExceptionInfo *) NULL);
2571 assert(exception->signature == MagickSignature);
2572 if ((columns == 0) || (rows == 0))
2573 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2574 if ((columns == image->columns) && (rows == image->rows) &&
2575 (filter == UndefinedFilter) && (blur == 1.0))
2576 return(CloneImage(image,0,0,MagickTrue,exception));
2577 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2578 if (resize_image == (Image *) NULL)
2579 return(resize_image);
2580 /*
2581 Acquire resize filter.
2582 */
2583 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2584 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2585 if ((x_factor*y_factor) > WorkLoadFactor)
2586 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2587 else
2588 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2589 if (filter_image == (Image *) NULL)
2590 return(DestroyImage(resize_image));
2591 filter_type=LanczosFilter;
2592 if (filter != UndefinedFilter)
2593 filter_type=filter;
2594 else
2595 if ((x_factor == 1.0) && (y_factor == 1.0))
2596 filter_type=PointFilter;
2597 else
2598 if ((image->storage_class == PseudoClass) ||
2599 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2600 filter_type=MitchellFilter;
2601 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2602 exception);
2603 /*
2604 Resize image.
2605 */
cristy9af9b5d2010-08-15 17:04:28 +00002606 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002607 if ((x_factor*y_factor) > WorkLoadFactor)
2608 {
2609 span=(MagickSizeType) (filter_image->columns+rows);
2610 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002611 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002612 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002613 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002614 }
2615 else
2616 {
2617 span=(MagickSizeType) (filter_image->rows+columns);
2618 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002619 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002620 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002621 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002622 }
2623 /*
2624 Free resources.
2625 */
2626 filter_image=DestroyImage(filter_image);
2627 resize_filter=DestroyResizeFilter(resize_filter);
2628 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2629 return((Image *) NULL);
2630 resize_image->type=image->type;
2631 return(resize_image);
2632}
2633
2634/*
2635%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2636% %
2637% %
2638% %
2639% S a m p l e I m a g e %
2640% %
2641% %
2642% %
2643%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2644%
2645% SampleImage() scales an image to the desired dimensions with pixel
2646% sampling. Unlike other scaling methods, this method does not introduce
2647% any additional color into the scaled image.
2648%
2649% The format of the SampleImage method is:
2650%
cristybb503372010-05-27 20:51:26 +00002651% Image *SampleImage(const Image *image,const size_t columns,
2652% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002653%
2654% A description of each parameter follows:
2655%
2656% o image: the image.
2657%
2658% o columns: the number of columns in the sampled image.
2659%
2660% o rows: the number of rows in the sampled image.
2661%
2662% o exception: return any errors or warnings in this structure.
2663%
2664*/
cristybb503372010-05-27 20:51:26 +00002665MagickExport Image *SampleImage(const Image *image,const size_t columns,
2666 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002667{
2668#define SampleImageTag "Sample/Image"
2669
cristyc4c8d132010-01-07 01:58:38 +00002670 CacheView
2671 *image_view,
2672 *sample_view;
2673
cristy3ed852e2009-09-05 21:47:34 +00002674 Image
2675 *sample_image;
2676
cristy3ed852e2009-09-05 21:47:34 +00002677 MagickBooleanType
2678 status;
2679
cristy5f959472010-05-27 22:19:46 +00002680 MagickOffsetType
2681 progress;
2682
cristybb503372010-05-27 20:51:26 +00002683 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002684 x;
2685
cristy5f959472010-05-27 22:19:46 +00002686 ssize_t
2687 *x_offset,
2688 y;
2689
cristy3ed852e2009-09-05 21:47:34 +00002690 /*
2691 Initialize sampled image attributes.
2692 */
2693 assert(image != (const Image *) NULL);
2694 assert(image->signature == MagickSignature);
2695 if (image->debug != MagickFalse)
2696 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2697 assert(exception != (ExceptionInfo *) NULL);
2698 assert(exception->signature == MagickSignature);
2699 if ((columns == 0) || (rows == 0))
2700 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2701 if ((columns == image->columns) && (rows == image->rows))
2702 return(CloneImage(image,0,0,MagickTrue,exception));
2703 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2704 if (sample_image == (Image *) NULL)
2705 return((Image *) NULL);
2706 /*
2707 Allocate scan line buffer and column offset buffers.
2708 */
cristybb503372010-05-27 20:51:26 +00002709 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002710 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002711 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002712 {
2713 sample_image=DestroyImage(sample_image);
2714 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2715 }
cristybb503372010-05-27 20:51:26 +00002716 for (x=0; x < (ssize_t) sample_image->columns; x++)
2717 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002718 sample_image->columns);
2719 /*
2720 Sample each row.
2721 */
2722 status=MagickTrue;
2723 progress=0;
2724 image_view=AcquireCacheView(image);
2725 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002726#if defined(MAGICKCORE_OPENMP_SUPPORT)
2727 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002728#endif
cristybb503372010-05-27 20:51:26 +00002729 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002730 {
cristy3ed852e2009-09-05 21:47:34 +00002731 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002732 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002733
2734 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002735 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002736
2737 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002738 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002739
cristy3ed852e2009-09-05 21:47:34 +00002740 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002741 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002742
cristy03dbbd22010-09-19 23:04:47 +00002743 register ssize_t
2744 x;
2745
cristy9af9b5d2010-08-15 17:04:28 +00002746 ssize_t
2747 y_offset;
2748
cristy3ed852e2009-09-05 21:47:34 +00002749 if (status == MagickFalse)
2750 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002751 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2752 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002753 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2754 exception);
2755 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2756 exception);
2757 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2758 {
2759 status=MagickFalse;
2760 continue;
2761 }
2762 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2763 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2764 /*
2765 Sample each column.
2766 */
cristybb503372010-05-27 20:51:26 +00002767 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002768 *q++=p[x_offset[x]];
2769 if ((image->storage_class == PseudoClass) ||
2770 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002771 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002772 sample_indexes[x]=indexes[x_offset[x]];
2773 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2774 status=MagickFalse;
2775 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2776 {
2777 MagickBooleanType
2778 proceed;
2779
cristyb5d5f722009-11-04 03:03:49 +00002780#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002781 #pragma omp critical (MagickCore_SampleImage)
2782#endif
2783 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2784 if (proceed == MagickFalse)
2785 status=MagickFalse;
2786 }
2787 }
2788 image_view=DestroyCacheView(image_view);
2789 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002790 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002791 sample_image->type=image->type;
2792 return(sample_image);
2793}
2794
2795/*
2796%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2797% %
2798% %
2799% %
2800% S c a l e I m a g e %
2801% %
2802% %
2803% %
2804%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2805%
2806% ScaleImage() changes the size of an image to the given dimensions.
2807%
2808% The format of the ScaleImage method is:
2809%
cristybb503372010-05-27 20:51:26 +00002810% Image *ScaleImage(const Image *image,const size_t columns,
2811% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002812%
2813% A description of each parameter follows:
2814%
2815% o image: the image.
2816%
2817% o columns: the number of columns in the scaled image.
2818%
2819% o rows: the number of rows in the scaled image.
2820%
2821% o exception: return any errors or warnings in this structure.
2822%
2823*/
cristybb503372010-05-27 20:51:26 +00002824MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2825 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002826{
2827#define ScaleImageTag "Scale/Image"
2828
cristyed6cb232010-01-20 03:07:53 +00002829 CacheView
2830 *image_view,
2831 *scale_view;
2832
cristy3ed852e2009-09-05 21:47:34 +00002833 Image
2834 *scale_image;
2835
cristy3ed852e2009-09-05 21:47:34 +00002836 MagickBooleanType
2837 next_column,
2838 next_row,
2839 proceed;
2840
2841 MagickPixelPacket
2842 pixel,
2843 *scale_scanline,
2844 *scanline,
2845 *x_vector,
2846 *y_vector,
2847 zero;
2848
cristy3ed852e2009-09-05 21:47:34 +00002849 PointInfo
2850 scale,
2851 span;
2852
cristybb503372010-05-27 20:51:26 +00002853 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002854 i;
2855
cristy9af9b5d2010-08-15 17:04:28 +00002856 ssize_t
2857 number_rows,
2858 y;
2859
cristy3ed852e2009-09-05 21:47:34 +00002860 /*
2861 Initialize scaled image attributes.
2862 */
2863 assert(image != (const Image *) NULL);
2864 assert(image->signature == MagickSignature);
2865 if (image->debug != MagickFalse)
2866 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2867 assert(exception != (ExceptionInfo *) NULL);
2868 assert(exception->signature == MagickSignature);
2869 if ((columns == 0) || (rows == 0))
2870 return((Image *) NULL);
2871 if ((columns == image->columns) && (rows == image->rows))
2872 return(CloneImage(image,0,0,MagickTrue,exception));
2873 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2874 if (scale_image == (Image *) NULL)
2875 return((Image *) NULL);
2876 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2877 {
2878 InheritException(exception,&scale_image->exception);
2879 scale_image=DestroyImage(scale_image);
2880 return((Image *) NULL);
2881 }
2882 /*
2883 Allocate memory.
2884 */
2885 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2886 sizeof(*x_vector));
2887 scanline=x_vector;
2888 if (image->rows != scale_image->rows)
2889 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2890 sizeof(*scanline));
2891 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2892 scale_image->columns,sizeof(*scale_scanline));
2893 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2894 sizeof(*y_vector));
2895 if ((scanline == (MagickPixelPacket *) NULL) ||
2896 (scale_scanline == (MagickPixelPacket *) NULL) ||
2897 (x_vector == (MagickPixelPacket *) NULL) ||
2898 (y_vector == (MagickPixelPacket *) NULL))
2899 {
2900 scale_image=DestroyImage(scale_image);
2901 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2902 }
2903 /*
2904 Scale image.
2905 */
2906 number_rows=0;
2907 next_row=MagickTrue;
2908 span.y=1.0;
2909 scale.y=(double) scale_image->rows/(double) image->rows;
2910 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2911 sizeof(*y_vector));
2912 GetMagickPixelPacket(image,&pixel);
2913 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2914 i=0;
cristyed6cb232010-01-20 03:07:53 +00002915 image_view=AcquireCacheView(image);
2916 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002917 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002918 {
2919 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002920 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002921
2922 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002923 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002924
2925 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002926 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002927
cristy3ed852e2009-09-05 21:47:34 +00002928 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002929 *restrict s,
2930 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002931
2932 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002933 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002934
cristy9af9b5d2010-08-15 17:04:28 +00002935 register ssize_t
2936 x;
2937
cristyed6cb232010-01-20 03:07:53 +00002938 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2939 exception);
cristy3ed852e2009-09-05 21:47:34 +00002940 if (q == (PixelPacket *) NULL)
2941 break;
cristyba9a1e22010-11-10 23:14:28 +00002942 scale_indexes=GetCacheViewAuthenticIndexQueue(scale_view);
cristy3ed852e2009-09-05 21:47:34 +00002943 if (scale_image->rows == image->rows)
2944 {
2945 /*
2946 Read a new scanline.
2947 */
cristyed6cb232010-01-20 03:07:53 +00002948 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2949 exception);
cristy3ed852e2009-09-05 21:47:34 +00002950 if (p == (const PixelPacket *) NULL)
2951 break;
cristyed6cb232010-01-20 03:07:53 +00002952 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002953 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002954 {
cristyce70c172010-01-07 17:15:30 +00002955 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2956 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2957 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002958 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002959 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002960 if (indexes != (IndexPacket *) NULL)
2961 x_vector[x].index=(MagickRealType) indexes[x];
2962 p++;
2963 }
2964 }
2965 else
2966 {
2967 /*
2968 Scale Y direction.
2969 */
2970 while (scale.y < span.y)
2971 {
cristy9af9b5d2010-08-15 17:04:28 +00002972 if ((next_row != MagickFalse) &&
2973 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002974 {
2975 /*
2976 Read a new scanline.
2977 */
cristyed6cb232010-01-20 03:07:53 +00002978 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2979 exception);
cristy3ed852e2009-09-05 21:47:34 +00002980 if (p == (const PixelPacket *) NULL)
2981 break;
cristyed6cb232010-01-20 03:07:53 +00002982 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002983 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002984 {
cristyce70c172010-01-07 17:15:30 +00002985 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2986 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2987 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002988 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002989 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002990 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002991 if (indexes != (IndexPacket *) NULL)
2992 x_vector[x].index=(MagickRealType) indexes[x];
2993 p++;
2994 }
2995 number_rows++;
2996 }
cristybb503372010-05-27 20:51:26 +00002997 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002998 {
2999 y_vector[x].red+=scale.y*x_vector[x].red;
3000 y_vector[x].green+=scale.y*x_vector[x].green;
3001 y_vector[x].blue+=scale.y*x_vector[x].blue;
3002 if (scale_image->matte != MagickFalse)
3003 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
3004 if (scale_indexes != (IndexPacket *) NULL)
3005 y_vector[x].index+=scale.y*x_vector[x].index;
3006 }
3007 span.y-=scale.y;
3008 scale.y=(double) scale_image->rows/(double) image->rows;
3009 next_row=MagickTrue;
3010 }
cristybb503372010-05-27 20:51:26 +00003011 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00003012 {
3013 /*
3014 Read a new scanline.
3015 */
cristyed6cb232010-01-20 03:07:53 +00003016 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
3017 exception);
cristy3ed852e2009-09-05 21:47:34 +00003018 if (p == (const PixelPacket *) NULL)
3019 break;
cristyed6cb232010-01-20 03:07:53 +00003020 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00003021 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003022 {
cristyce70c172010-01-07 17:15:30 +00003023 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
3024 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
3025 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003026 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00003027 x_vector[x].opacity=(MagickRealType)
3028 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003029 if (indexes != (IndexPacket *) NULL)
3030 x_vector[x].index=(MagickRealType) indexes[x];
3031 p++;
3032 }
3033 number_rows++;
3034 next_row=MagickFalse;
3035 }
3036 s=scanline;
cristybb503372010-05-27 20:51:26 +00003037 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003038 {
3039 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3040 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3041 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3042 if (image->matte != MagickFalse)
3043 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3044 if (scale_indexes != (IndexPacket *) NULL)
3045 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3046 s->red=pixel.red;
3047 s->green=pixel.green;
3048 s->blue=pixel.blue;
3049 if (scale_image->matte != MagickFalse)
3050 s->opacity=pixel.opacity;
3051 if (scale_indexes != (IndexPacket *) NULL)
3052 s->index=pixel.index;
3053 s++;
3054 y_vector[x]=zero;
3055 }
3056 scale.y-=span.y;
3057 if (scale.y <= 0)
3058 {
3059 scale.y=(double) scale_image->rows/(double) image->rows;
3060 next_row=MagickTrue;
3061 }
3062 span.y=1.0;
3063 }
3064 if (scale_image->columns == image->columns)
3065 {
3066 /*
3067 Transfer scanline to scaled image.
3068 */
3069 s=scanline;
cristybb503372010-05-27 20:51:26 +00003070 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003071 {
cristyce70c172010-01-07 17:15:30 +00003072 q->red=ClampToQuantum(s->red);
3073 q->green=ClampToQuantum(s->green);
3074 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003075 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003076 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003077 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003078 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003079 q++;
3080 s++;
3081 }
3082 }
3083 else
3084 {
3085 /*
3086 Scale X direction.
3087 */
3088 pixel=zero;
3089 next_column=MagickFalse;
3090 span.x=1.0;
3091 s=scanline;
3092 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003093 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003094 {
3095 scale.x=(double) scale_image->columns/(double) image->columns;
3096 while (scale.x >= span.x)
3097 {
3098 if (next_column != MagickFalse)
3099 {
3100 pixel=zero;
3101 t++;
3102 }
3103 pixel.red+=span.x*s->red;
3104 pixel.green+=span.x*s->green;
3105 pixel.blue+=span.x*s->blue;
3106 if (image->matte != MagickFalse)
3107 pixel.opacity+=span.x*s->opacity;
3108 if (scale_indexes != (IndexPacket *) NULL)
3109 pixel.index+=span.x*s->index;
3110 t->red=pixel.red;
3111 t->green=pixel.green;
3112 t->blue=pixel.blue;
3113 if (scale_image->matte != MagickFalse)
3114 t->opacity=pixel.opacity;
3115 if (scale_indexes != (IndexPacket *) NULL)
3116 t->index=pixel.index;
3117 scale.x-=span.x;
3118 span.x=1.0;
3119 next_column=MagickTrue;
3120 }
3121 if (scale.x > 0)
3122 {
3123 if (next_column != MagickFalse)
3124 {
3125 pixel=zero;
3126 next_column=MagickFalse;
3127 t++;
3128 }
3129 pixel.red+=scale.x*s->red;
3130 pixel.green+=scale.x*s->green;
3131 pixel.blue+=scale.x*s->blue;
3132 if (scale_image->matte != MagickFalse)
3133 pixel.opacity+=scale.x*s->opacity;
3134 if (scale_indexes != (IndexPacket *) NULL)
3135 pixel.index+=scale.x*s->index;
3136 span.x-=scale.x;
3137 }
3138 s++;
3139 }
3140 if (span.x > 0)
3141 {
3142 s--;
3143 pixel.red+=span.x*s->red;
3144 pixel.green+=span.x*s->green;
3145 pixel.blue+=span.x*s->blue;
3146 if (scale_image->matte != MagickFalse)
3147 pixel.opacity+=span.x*s->opacity;
3148 if (scale_indexes != (IndexPacket *) NULL)
3149 pixel.index+=span.x*s->index;
3150 }
3151 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003152 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003153 {
3154 t->red=pixel.red;
3155 t->green=pixel.green;
3156 t->blue=pixel.blue;
3157 if (scale_image->matte != MagickFalse)
3158 t->opacity=pixel.opacity;
3159 if (scale_indexes != (IndexPacket *) NULL)
3160 t->index=pixel.index;
3161 }
3162 /*
3163 Transfer scanline to scaled image.
3164 */
3165 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003166 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003167 {
cristyce70c172010-01-07 17:15:30 +00003168 q->red=ClampToQuantum(t->red);
3169 q->green=ClampToQuantum(t->green);
3170 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003171 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003172 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003173 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003174 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003175 t++;
3176 q++;
3177 }
3178 }
cristyed6cb232010-01-20 03:07:53 +00003179 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003180 break;
cristy96b16132010-08-29 17:19:52 +00003181 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3182 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003183 if (proceed == MagickFalse)
3184 break;
3185 }
cristyed6cb232010-01-20 03:07:53 +00003186 scale_view=DestroyCacheView(scale_view);
3187 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003188 /*
3189 Free allocated memory.
3190 */
3191 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3192 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3193 if (scale_image->rows != image->rows)
3194 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3195 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3196 scale_image->type=image->type;
3197 return(scale_image);
3198}
3199
anthony02b4cb42010-10-10 04:54:35 +00003200#if 0
3201 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003202/*
3203%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3204% %
3205% %
3206% %
3207+ S e t R e s i z e F i l t e r S u p p o r t %
3208% %
3209% %
3210% %
3211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3212%
3213% SetResizeFilterSupport() specifies which IR filter to use to window
3214%
3215% The format of the SetResizeFilterSupport method is:
3216%
3217% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3218% const MagickRealType support)
3219%
3220% A description of each parameter follows:
3221%
3222% o resize_filter: the resize filter.
3223%
3224% o support: the filter spport radius.
3225%
3226*/
3227MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3228 const MagickRealType support)
3229{
3230 assert(resize_filter != (ResizeFilter *) NULL);
3231 assert(resize_filter->signature == MagickSignature);
3232 resize_filter->support=support;
3233}
anthony02b4cb42010-10-10 04:54:35 +00003234#endif
cristy3ed852e2009-09-05 21:47:34 +00003235
3236/*
3237%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3238% %
3239% %
3240% %
3241% T h u m b n a i l I m a g e %
3242% %
3243% %
3244% %
3245%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3246%
3247% ThumbnailImage() changes the size of an image to the given dimensions and
3248% removes any associated profiles. The goal is to produce small low cost
3249% thumbnail images suited for display on the Web.
3250%
3251% The format of the ThumbnailImage method is:
3252%
cristybb503372010-05-27 20:51:26 +00003253% Image *ThumbnailImage(const Image *image,const size_t columns,
3254% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003255%
3256% A description of each parameter follows:
3257%
3258% o image: the image.
3259%
3260% o columns: the number of columns in the scaled image.
3261%
3262% o rows: the number of rows in the scaled image.
3263%
3264% o exception: return any errors or warnings in this structure.
3265%
3266*/
cristy9af9b5d2010-08-15 17:04:28 +00003267MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3268 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003269{
3270#define SampleFactor 5
3271
3272 char
3273 value[MaxTextExtent];
3274
3275 const char
3276 *name;
3277
3278 Image
3279 *thumbnail_image;
3280
3281 MagickRealType
3282 x_factor,
3283 y_factor;
3284
cristybb503372010-05-27 20:51:26 +00003285 size_t
cristy3ed852e2009-09-05 21:47:34 +00003286 version;
3287
cristy9af9b5d2010-08-15 17:04:28 +00003288 struct stat
3289 attributes;
3290
cristy3ed852e2009-09-05 21:47:34 +00003291 assert(image != (Image *) NULL);
3292 assert(image->signature == MagickSignature);
3293 if (image->debug != MagickFalse)
3294 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3295 assert(exception != (ExceptionInfo *) NULL);
3296 assert(exception->signature == MagickSignature);
3297 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3298 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3299 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003300 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3301 exception);
cristy3ed852e2009-09-05 21:47:34 +00003302 else
3303 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003304 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3305 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003306 else
3307 {
3308 Image
3309 *sample_image;
3310
3311 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3312 exception);
3313 if (sample_image == (Image *) NULL)
3314 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003315 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3316 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003317 sample_image=DestroyImage(sample_image);
3318 }
3319 if (thumbnail_image == (Image *) NULL)
3320 return(thumbnail_image);
3321 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3322 if (thumbnail_image->matte == MagickFalse)
3323 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3324 thumbnail_image->depth=8;
3325 thumbnail_image->interlace=NoInterlace;
3326 /*
3327 Strip all profiles except color profiles.
3328 */
3329 ResetImageProfileIterator(thumbnail_image);
3330 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3331 {
3332 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3333 {
cristy2b726bd2010-01-11 01:05:39 +00003334 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003335 ResetImageProfileIterator(thumbnail_image);
3336 }
3337 name=GetNextImageProfile(thumbnail_image);
3338 }
3339 (void) DeleteImageProperty(thumbnail_image,"comment");
3340 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003341 if (strstr(image->magick_filename,"//") == (char *) NULL)
3342 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003343 image->magick_filename);
3344 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3345 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3346 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3347 {
cristye8c25f92010-06-03 00:53:06 +00003348 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003349 attributes.st_mtime);
3350 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3351 }
cristye8c25f92010-06-03 00:53:06 +00003352 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003353 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003354 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003355 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003356 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3357 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3358 LocaleLower(value);
3359 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3360 (void) SetImageProperty(thumbnail_image,"software",
3361 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003362 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3363 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003364 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003365 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003366 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003367 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003368 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3369 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003370 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3371 return(thumbnail_image);
3372}