blob: af1540cae4d8cc127113e45a7ac942a99272ecc3 [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"
65#include "magick/resize.h"
66#include "magick/resize-private.h"
67#include "magick/string_.h"
cristyf2f27272009-12-17 14:48:46 +000068#include "magick/string-private.h"
cristy3ed852e2009-09-05 21:47:34 +000069#include "magick/thread-private.h"
70#include "magick/utility.h"
71#include "magick/version.h"
72#if defined(MAGICKCORE_LQR_DELEGATE)
73#include <lqr.h>
74#endif
75
76/*
77 Typedef declarations.
78*/
79struct _ResizeFilter
80{
81 MagickRealType
82 (*filter)(const MagickRealType,const ResizeFilter *),
83 (*window)(const MagickRealType,const ResizeFilter *),
cristy33b1c162010-01-23 22:51:51 +000084 support, /* filter region of support - the filter support limit */
85 window_support, /* window support, usally equal to support (expert only) */
anthony55f12332010-09-10 01:13:02 +000086 scale, /* dimension scaling to fit window support (usally 1.0) */
cristy33b1c162010-01-23 22:51:51 +000087 blur, /* x-scale (blur-sharpen) */
nicolase3b9eca2010-10-24 19:48:22 +000088 coeff[7]; /* cubic coefficents for BC-cubic spline filters */
cristy3ed852e2009-09-05 21:47:34 +000089
cristybb503372010-05-27 20:51:26 +000090 size_t
cristy3ed852e2009-09-05 21:47:34 +000091 signature;
92};
93
94/*
95 Forward declaractions.
96*/
97static MagickRealType
98 I0(MagickRealType x),
anthonyb6d08c52010-09-13 01:17:04 +000099 BesselOrderOne(MagickRealType),
anthony07a3f7f2010-09-16 03:03:11 +0000100 Sinc(const MagickRealType, const ResizeFilter *),
anthonyba5a7c32010-09-15 02:42:25 +0000101 SincFast(const MagickRealType, const ResizeFilter *);
cristy3ed852e2009-09-05 21:47:34 +0000102
103/*
104%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
105% %
106% %
107% %
108+ F i l t e r F u n c t i o n s %
109% %
110% %
111% %
112%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
113%
cristy33b1c162010-01-23 22:51:51 +0000114% These are the various filter and windowing functions that are provided.
cristy3ed852e2009-09-05 21:47:34 +0000115%
cristy33b1c162010-01-23 22:51:51 +0000116% They are internal to this module only. See AcquireResizeFilterInfo() for
117% details of the access to these functions, via the GetResizeFilterSupport()
118% and GetResizeFilterWeight() API interface.
cristy3ed852e2009-09-05 21:47:34 +0000119%
120% The individual filter functions have this format...
121%
122% static MagickRealtype *FilterName(const MagickRealType x,
123% const MagickRealType support)
124%
cristy33b1c162010-01-23 22:51:51 +0000125% A description of each parameter follows:
cristy3ed852e2009-09-05 21:47:34 +0000126%
cristy33b1c162010-01-23 22:51:51 +0000127% o x: the distance from the sampling point generally in the range of 0 to
128% support. The GetResizeFilterWeight() ensures this a positive value.
129%
130% o resize_filter: current filter information. This allows function to
131% access support, and possibly other pre-calculated information defining
132% the functions.
cristy3ed852e2009-09-05 21:47:34 +0000133%
134*/
135
cristyc5c6f662010-09-22 14:23:02 +0000136#define MagickPIL ((MagickRealType) 3.14159265358979323846264338327950288420L)
cristy560d8182010-09-08 22:36:25 +0000137
anthony48f77622010-10-03 14:32:31 +0000138static MagickRealType Jinc(const MagickRealType x,
cristy3ed852e2009-09-05 21:47:34 +0000139 const ResizeFilter *magick_unused(resize_filter))
140{
141 /*
anthony48f77622010-10-03 14:32:31 +0000142 See Pratt "Digital Image Processing" p.97 for Jinc/Bessel functions.
cristy33b1c162010-01-23 22:51:51 +0000143 http://mathworld.wolfram.com/JincFunction.html and page 11 of
anthony48f77622010-10-03 14:32:31 +0000144 http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
145
nicolase473f722010-10-07 00:05:13 +0000146 The original "zoom" program by Paul Heckbert called this "Bessel".
147 But really it is more accurately named "Jinc".
cristy3ed852e2009-09-05 21:47:34 +0000148 */
149 if (x == 0.0)
nicolas5a36f342010-10-07 00:11:32 +0000150 return(0.5*MagickPIL);
151 return(BesselOrderOne(MagickPIL*x)/x);
cristy3ed852e2009-09-05 21:47:34 +0000152}
153
154static MagickRealType Blackman(const MagickRealType x,
cristyce70c172010-01-07 17:15:30 +0000155 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000156{
157 /*
cristy83017922010-09-05 20:45:15 +0000158 Blackman: 2nd order cosine windowing function:
cristy21ce88a2010-09-05 01:37:25 +0000159 0.42 + 0.5 cos(pi x) + 0.08 cos(2pi x)
cristy560d8182010-09-08 22:36:25 +0000160 Refactored by Chantal Racette and Nicolas Robidoux to one trig
161 call and five flops.
cristy3ed852e2009-09-05 21:47:34 +0000162 */
cristyc5c6f662010-09-22 14:23:02 +0000163 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000164 return(0.34+cospix*(0.5+cospix*0.16));
cristy3ed852e2009-09-05 21:47:34 +0000165}
166
167static MagickRealType Bohman(const MagickRealType x,
cristyce70c172010-01-07 17:15:30 +0000168 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000169{
170 /*
cristy560d8182010-09-08 22:36:25 +0000171 Bohman: 2rd Order cosine windowing function:
172 (1-x) cos(pi x) + sin(pi x) / pi.
nicolasa4f7b4a2010-09-20 20:28:38 +0000173 Refactored by Nicolas Robidoux to one trig call, one sqrt call,
174 and 7 flops, taking advantage of the fact that the support of
175 Bohman is 1 (so that we know that sin(pi x) >= 0).
cristy3ed852e2009-09-05 21:47:34 +0000176 */
cristyc5c6f662010-09-22 14:23:02 +0000177 const double cospix = cos((double) (MagickPIL*x));
nicolasa4f7b4a2010-09-20 20:28:38 +0000178 const double sinpix = sqrt(1.0-cospix*cospix);
nicolas2ffd3b22010-09-24 20:27:31 +0000179 return((1.0-x)*cospix+(1.0/MagickPIL)*sinpix);
cristy3ed852e2009-09-05 21:47:34 +0000180}
181
anthony463be1d2010-09-26 01:07:36 +0000182static MagickRealType Box(const MagickRealType magick_unused(x),
183 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000184{
185 /*
nicolas40477452010-09-27 23:42:08 +0000186 A Box filter is a equal weighting function (all weights equal).
anthonyc331dec2010-09-26 01:30:14 +0000187 DO NOT LIMIT results by support or resize point sampling will work
188 as it requests points beyond its normal 0.0 support size.
cristy3ed852e2009-09-05 21:47:34 +0000189 */
anthony463be1d2010-09-26 01:07:36 +0000190 return(1.0);
cristy3ed852e2009-09-05 21:47:34 +0000191}
192
193static MagickRealType CubicBC(const MagickRealType x,
194 const ResizeFilter *resize_filter)
195{
196 /*
197 Cubic Filters using B,C determined values:
nicolase3b9eca2010-10-24 19:48:22 +0000198 Mitchell-Netravali B= 1/3 C= 1/3 "Balanced" cubic spline filter
199 Catmull-Rom B= 0 C= 1/2 Interpolatory and exact on linears
200 Cubic B-Spline B= 1 C= 0 Spline approximation of Gaussian
201 Hermite B= 0 C= 0 Spline with small support (= 1)
cristy3ed852e2009-09-05 21:47:34 +0000202
cristy33b1c162010-01-23 22:51:51 +0000203 See paper by Mitchell and Netravali, Reconstruction Filters in Computer
204 Graphics Computer Graphics, Volume 22, Number 4, August 1988
205 http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/
cristyf59a8922010-02-28 19:51:23 +0000206 Mitchell.pdf.
cristy3ed852e2009-09-05 21:47:34 +0000207
cristy33b1c162010-01-23 22:51:51 +0000208 Coefficents are determined from B,C values:
anthony06b1edf2010-10-25 01:19:50 +0000209 P0 = ( 6 - 2*B )/6 = coeff[0]
cristy3ed852e2009-09-05 21:47:34 +0000210 P1 = 0
anthony06b1edf2010-10-25 01:19:50 +0000211 P2 = (-18 +12*B + 6*C )/6 = coeff[1]
212 P3 = ( 12 - 9*B - 6*C )/6 = coeff[2]
213 Q0 = ( 8*B +24*C )/6 = coeff[3]
214 Q1 = ( -12*B -48*C )/6 = coeff[4]
215 Q2 = ( 6*B +30*C )/6 = coeff[5]
216 Q3 = ( - 1*B - 6*C )/6 = coeff[6]
cristy3ed852e2009-09-05 21:47:34 +0000217
cristy33b1c162010-01-23 22:51:51 +0000218 which are used to define the filter:
cristyf59a8922010-02-28 19:51:23 +0000219
cristy3ed852e2009-09-05 21:47:34 +0000220 P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
nicolase3b9eca2010-10-24 19:48:22 +0000221 Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x < 2
cristy3ed852e2009-09-05 21:47:34 +0000222
nicolase3b9eca2010-10-24 19:48:22 +0000223 which ensures function is continuous in value and derivative
nicolas89d73f92010-10-24 20:11:54 +0000224 (slope).
cristy3ed852e2009-09-05 21:47:34 +0000225 */
226 if (x < 1.0)
nicolasc6bac3b2010-10-24 18:10:45 +0000227 return(resize_filter->coeff[0]+x*(x*
nicolase3b9eca2010-10-24 19:48:22 +0000228 (resize_filter->coeff[1]+x*resize_filter->coeff[2])));
cristy3ed852e2009-09-05 21:47:34 +0000229 if (x < 2.0)
nicolase3b9eca2010-10-24 19:48:22 +0000230 return(resize_filter->coeff[3]+x*(resize_filter->coeff[4]+x*
231 (resize_filter->coeff[5]+x*resize_filter->coeff[6])));
cristy3ed852e2009-09-05 21:47:34 +0000232 return(0.0);
233}
234
235static MagickRealType Gaussian(const MagickRealType x,
anthonyf5e76ef2010-10-12 01:22:01 +0000236 const ResizeFilter *resize_filter)
cristy3ed852e2009-09-05 21:47:34 +0000237{
cristy560d8182010-09-08 22:36:25 +0000238 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000239 Gaussian with a fixed sigma = 1/2
240
241 Gaussian Formula...
242 exp( -(x^2)/((2.0*sigma^2) ) / sqrt(2*PI*sigma^2)))
243 The constants are pre-calculated...
244 exp( -coeff[0]*(x^2)) ) * coeff[1]
anthony06b1edf2010-10-25 01:19:50 +0000245 However the multiplier coefficent (1) is not needed and not used.
anthonyf5e76ef2010-10-12 01:22:01 +0000246
247 This separates the gaussian 'sigma' value from the 'blur/support' settings
anthony06b1edf2010-10-25 01:19:50 +0000248 allowing for its use in special 'small sigma' gaussians, without the filter
249 'missing' pixels when blurring because the support is too small.
cristy560d8182010-09-08 22:36:25 +0000250 */
anthony06b1edf2010-10-25 01:19:50 +0000251 return(exp((double)(-resize_filter->coeff[0]*x*x)));
252}
cristy3ed852e2009-09-05 21:47:34 +0000253
254static MagickRealType Hanning(const MagickRealType x,
255 const ResizeFilter *magick_unused(resize_filter))
256{
257 /*
nicolas40477452010-09-27 23:42:08 +0000258 Cosine window function:
259 .5+.5cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000260 */
cristyc5c6f662010-09-22 14:23:02 +0000261 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000262 return(0.5+0.5*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000263}
264
265static MagickRealType Hamming(const MagickRealType x,
266 const ResizeFilter *magick_unused(resize_filter))
267{
268 /*
nicolas40477452010-09-27 23:42:08 +0000269 Offset cosine window function:
270 .54 + .46 cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000271 */
cristyc5c6f662010-09-22 14:23:02 +0000272 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000273 return(0.54+0.46*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000274}
275
276static MagickRealType Kaiser(const MagickRealType x,
277 const ResizeFilter *magick_unused(resize_filter))
278{
279#define Alpha 6.5
280#define I0A (1.0/I0(Alpha))
281
282 /*
nicolas07bac812010-09-19 18:47:02 +0000283 Kaiser Windowing Function (bessel windowing): Alpha is a free
284 value from 5 to 8 (currently hardcoded to 6.5).
285 Future: make alpha the IOA pre-calculation, an 'expert' setting.
cristy3ed852e2009-09-05 21:47:34 +0000286 */
287 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
288}
289
290static MagickRealType Lagrange(const MagickRealType x,
291 const ResizeFilter *resize_filter)
292{
cristy3ed852e2009-09-05 21:47:34 +0000293 MagickRealType
294 value;
295
cristybb503372010-05-27 20:51:26 +0000296 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000297 i;
298
cristy9af9b5d2010-08-15 17:04:28 +0000299 ssize_t
300 n,
301 order;
302
cristy3ed852e2009-09-05 21:47:34 +0000303 /*
nicolas07bac812010-09-19 18:47:02 +0000304 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the
305 lagrange function and depends on the overall support window size
306 of the filter. That is: for a support of 2, it gives a lagrange-4
307 (piecewise cubic function).
cristy3ed852e2009-09-05 21:47:34 +0000308
nicolas07bac812010-09-19 18:47:02 +0000309 "n" identifies the piece of the piecewise polynomial.
cristy3ed852e2009-09-05 21:47:34 +0000310
nicolas07bac812010-09-19 18:47:02 +0000311 See Survey: Interpolation Methods, IEEE Transactions on Medical
312 Imaging, Vol 18, No 11, November 1999, p1049-1075, -- Equation 27
313 on p1064.
cristy3ed852e2009-09-05 21:47:34 +0000314 */
315 if (x > resize_filter->support)
316 return(0.0);
cristybb503372010-05-27 20:51:26 +0000317 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
anthonyc2d07db2010-09-15 23:47:40 +0000318 /*n=(ssize_t)((1.0*order)/2.0+x); -- which piece does x belong to */
319 n = (ssize_t)(resize_filter->window_support + x);
cristy3ed852e2009-09-05 21:47:34 +0000320 value=1.0f;
321 for (i=0; i < order; i++)
322 if (i != n)
323 value*=(n-i-x)/(n-i);
324 return(value);
325}
326
327static MagickRealType Quadratic(const MagickRealType x,
328 const ResizeFilter *magick_unused(resize_filter))
329{
330 /*
331 2rd order (quadratic) B-Spline approximation of Gaussian.
332 */
333 if (x < 0.5)
334 return(0.75-x*x);
335 if (x < 1.5)
336 return(0.5*(x-1.5)*(x-1.5));
337 return(0.0);
338}
339
anthony07a3f7f2010-09-16 03:03:11 +0000340static MagickRealType Sinc(const MagickRealType x,
cristy3ed852e2009-09-05 21:47:34 +0000341 const ResizeFilter *magick_unused(resize_filter))
342{
anthony720660f2010-09-07 10:05:14 +0000343 /*
nicolas40477452010-09-27 23:42:08 +0000344 Scaled sinc(x) function using a trig call:
nicolas07bac812010-09-19 18:47:02 +0000345 sinc(x) == sin(pi x)/(pi x).
anthony720660f2010-09-07 10:05:14 +0000346 */
anthony2d9b8b52010-09-14 08:31:07 +0000347 if (x != 0.0)
cristy560d8182010-09-08 22:36:25 +0000348 {
cristyc5c6f662010-09-22 14:23:02 +0000349 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
nicolas07bac812010-09-19 18:47:02 +0000350 return(sin((double) pix)/pix);
cristy560d8182010-09-08 22:36:25 +0000351 }
nicolas2ffd3b22010-09-24 20:27:31 +0000352 return((MagickRealType) 1.0);
anthony720660f2010-09-07 10:05:14 +0000353}
354
anthonyba5a7c32010-09-15 02:42:25 +0000355static MagickRealType SincFast(const MagickRealType x,
anthony720660f2010-09-07 10:05:14 +0000356 const ResizeFilter *magick_unused(resize_filter))
357{
cristy560d8182010-09-08 22:36:25 +0000358 /*
359 Approximations of the sinc function sin(pi x)/(pi x) over the
360 interval [-4,4] constructed by Nicolas Robidoux and Chantal
361 Racette with funding from the Natural Sciences and Engineering
362 Research Council of Canada.
nicolas07bac812010-09-19 18:47:02 +0000363
364 Although the approximations are polynomials (for low order of
365 approximation) and quotients of polynomials (for higher order of
366 approximation) and consequently are similar in form to Taylor
367 polynomials/Pade approximants, the approximations are computed
368 with a completely different technique.
369
370 Summary: These approximations are "the best" in terms of bang
371 (accuracy) for the buck (flops). More specifically: Among the
372 polynomial quotients that can be computed using a fixed number of
373 flops (with a given "+ - * / budget"), the chosen polynomial
374 quotient is the one closest to the approximated function with
375 respect to maximum absolute relative error over the given
376 interval.
377
378 The Remez algorithm, as implemented in the boost library's minimax
nicolas3aab40c2010-09-19 21:14:15 +0000379 package, is the key to the construction:
nicolas07bac812010-09-19 18:47:02 +0000380 http://www.boost.org/doc/libs/1_36_0/libs/math/doc/...
381 ...sf_and_dist/html/math_toolkit/backgrounders/remez.html
cristy560d8182010-09-08 22:36:25 +0000382 */
nicolas3aab40c2010-09-19 21:14:15 +0000383 /*
384 If outside of the interval of approximation, use the standard trig
385 formula.
386 */
anthony2d9b8b52010-09-14 08:31:07 +0000387 if (x > 4.0)
cristy03dbbd22010-09-19 23:04:47 +0000388 {
cristyc5c6f662010-09-22 14:23:02 +0000389 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
cristy03dbbd22010-09-19 23:04:47 +0000390 return(sin((double) pix)/pix);
391 }
anthony2d9b8b52010-09-14 08:31:07 +0000392 {
nicolas07bac812010-09-19 18:47:02 +0000393 /*
394 The approximations only depend on x^2 (sinc is an even
395 function).
396 */
397 const MagickRealType xx = x*x;
cristy83017922010-09-05 20:45:15 +0000398#if MAGICKCORE_QUANTUM_DEPTH <= 8
399 /*
anthony2d9b8b52010-09-14 08:31:07 +0000400 Maximum absolute relative error 6.3e-6 < 1/2^17.
cristy738e7562010-09-01 12:48:07 +0000401 */
402 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
403 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
404 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
405 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
406 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
407 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
408 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
409 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
nicolas3aab40c2010-09-19 21:14:15 +0000410 const MagickRealType p =
411 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
nicolas07bac812010-09-19 18:47:02 +0000412 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
anthony2d9b8b52010-09-14 08:31:07 +0000413#elif MAGICKCORE_QUANTUM_DEPTH <= 16
cristydbeb3eb2010-09-09 13:41:36 +0000414 /*
anthony2d9b8b52010-09-14 08:31:07 +0000415 Max. abs. rel. error 2.2e-8 < 1/2^25.
cristydbeb3eb2010-09-09 13:41:36 +0000416 */
417 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
418 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
nicolas3aab40c2010-09-19 21:14:15 +0000419 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
420 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
421 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
422 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
423 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
cristydbeb3eb2010-09-09 13:41:36 +0000424 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
425 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
426 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
anthony853d6972010-10-08 06:01:31 +0000427 const MagickRealType p =
nicolas3aab40c2010-09-19 21:14:15 +0000428 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 +0000429 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
nicolasc2d28f02010-09-27 18:56:15 +0000430#else
nicolas3aab40c2010-09-19 21:14:15 +0000431 /*
432 Max. abs. rel. error 1.2e-12 < 1/2^39.
433 */
434 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
435 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
436 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
437 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
438 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
439 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
440 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
441 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
442 const MagickRealType p =
443 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
444 const MagickRealType d0 = 1.0L;
445 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
446 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
447 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
448 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
449 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
450 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
cristy657c6352010-08-29 14:05:08 +0000451#endif
cristy83017922010-09-05 20:45:15 +0000452 }
cristy3ed852e2009-09-05 21:47:34 +0000453}
454
455static MagickRealType Triangle(const MagickRealType x,
456 const ResizeFilter *magick_unused(resize_filter))
457{
458 /*
nicolas0edb0862010-09-19 18:56:19 +0000459 1st order (linear) B-Spline, bilinear interpolation, Tent 1D
anthony06b1edf2010-10-25 01:19:50 +0000460 filter, or a Bartlett 2D Cone filter. Also used as a
461 Bartlett Windowing function for Sinc().
cristy3ed852e2009-09-05 21:47:34 +0000462 */
463 if (x < 1.0)
464 return(1.0-x);
465 return(0.0);
466}
467
468static MagickRealType Welsh(const MagickRealType x,
469 const ResizeFilter *magick_unused(resize_filter))
470{
471 /*
472 Welsh parabolic windowing filter.
473 */
cristy560d8182010-09-08 22:36:25 +0000474 if (x < 1.0)
cristy3ed852e2009-09-05 21:47:34 +0000475 return(1.0-x*x);
476 return(0.0);
477}
478
479/*
480%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
481% %
482% %
483% %
484+ A c q u i r e R e s i z e F i l t e r %
485% %
486% %
487% %
488%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
489%
nicolas07bac812010-09-19 18:47:02 +0000490% AcquireResizeFilter() allocates the ResizeFilter structure. Choose
491% from these filters:
cristy3ed852e2009-09-05 21:47:34 +0000492%
493% FIR (Finite impulse Response) Filters
494% Box Triangle Quadratic
495% Cubic Hermite Catrom
496% Mitchell
497%
498% IIR (Infinite impulse Response) Filters
anthony48f77622010-10-03 14:32:31 +0000499% Gaussian Sinc Jinc (Bessel)
cristy3ed852e2009-09-05 21:47:34 +0000500%
anthony48f77622010-10-03 14:32:31 +0000501% Windowed Sinc/Jinc Filters
cristy3ed852e2009-09-05 21:47:34 +0000502% Blackman Hanning Hamming
anthony48f77622010-10-03 14:32:31 +0000503% Kaiser Lanczos
cristy3ed852e2009-09-05 21:47:34 +0000504%
anthony61b5ddd2010-10-05 02:33:31 +0000505% Special purpose Filters
anthony06b1edf2010-10-25 01:19:50 +0000506% SincFast LanczosSharp Lanczos2D Lanczos2DSharp Robidoux
anthony61b5ddd2010-10-05 02:33:31 +0000507%
anthony48f77622010-10-03 14:32:31 +0000508% The users "-filter" selection is used to lookup the default 'expert'
509% settings for that filter from a internal table. However any provided
510% 'expert' settings (see below) may override this selection.
511%
512% FIR filters are used as is, and are limited to that filters support
nicolas07bac812010-09-19 18:47:02 +0000513% window (unless over-ridden). 'Gaussian' while classed as an IIR
anthonyc331dec2010-09-26 01:30:14 +0000514% filter, is also simply clipped by its support size (currently 1.5
anthonyf5e76ef2010-10-12 01:22:01 +0000515% or approximatally 3*sigma as recommended by many references)
cristy3ed852e2009-09-05 21:47:34 +0000516%
anthony48f77622010-10-03 14:32:31 +0000517% The selection is typically either a windowed Sinc, or interpolated
nicolas07bac812010-09-19 18:47:02 +0000518% filter, for use by functions such as ResizeImage(). However if a
anthony48f77622010-10-03 14:32:31 +0000519% 'cylindrical' filter flag is requested, any default Sinc weighting
520% and windowing functions will be promoted to cylindrical Jinc form of
521% function.
cristy3ed852e2009-09-05 21:47:34 +0000522%
anthony48f77622010-10-03 14:32:31 +0000523% Directly requesting 'Sinc' or 'Jinc' will force the use of that
524% filter function without any windowing. This is not recommended,
525% except by image processing experts or in expert options. Selecting a
526% window filtering version of these functions is better.
cristy3ed852e2009-09-05 21:47:34 +0000527%
anthony48f77622010-10-03 14:32:31 +0000528% Lanczos is a special case of a Sinc-windowed Sinc, (or Jinc-Jinc for
529% the cylindrical case) but defaulting to 3-lobe support, rather that
530% the default 4 lobe support of the other windowed sinc/jinc filters.
cristy3ed852e2009-09-05 21:47:34 +0000531%
anthony06b1edf2010-10-25 01:19:50 +0000532% LanczosSharp is a slightly sharpened (blur=0.9830391168464) form of
533% the Lanczos filter. It was designed specifically for cylindrical
nicolasb7dff642010-10-25 02:04:14 +0000534% (radial) EWA (Elliptical Weighted Average) distortion (using a Jinc
535% windowed Jinc); it can also be used as a slightly sharper
536% orthogonal Lanczos (Sinc-Sinc) filter. With the chosen blur value,
537% the corresponding EWA filter comes as close as possible to
538% satisfying the following condition:
539%
540% 'No-Op' Vertical and Horizontal Line Preservation Condition:
541% Images with only vertical or horizontal features are preserved
542% when performing 'no-op" with EWA distortion.
anthony06b1edf2010-10-25 01:19:50 +0000543%
anthony48f77622010-10-03 14:32:31 +0000544% Two forms of the 'Sinc' function are available: Sinc and SincFast.
nicolas07bac812010-09-19 18:47:02 +0000545% Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
546% selected if the user specifically specifies the use of a Sinc
547% filter. SincFast uses highly accurate (and fast) polynomial (low Q)
anthony48f77622010-10-03 14:32:31 +0000548% and rational (high Q) approximations, and will be used by default in
549% most cases.
anthonyba5a7c32010-09-15 02:42:25 +0000550%
nicolasb7dff642010-10-25 02:04:14 +0000551% The Lanczos2D and Lanczos2DSharp filters are for EWA distorts.
552% They are both 2-lobe Lanczos-like filters but always use a
553% Jinc-Jinc filter function regardless of whether or not they are
554% used as orthogonal filters. The 'sharp' version uses
555% blur=0.958033808, chosen because the resulting EWA filter comes as
556% close as possible to satisfing the "'No-Op' Vertical and Horizontal
557% Line Preservation 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
560% spline defined by B=(228 - 108 sqrt(2))/199. Robidoux satisfies the
561% "'No-Op' Vertical and Horizontal Line Preservation Condition"
562% exactly. It turns out to be close to both plain Mitchell and
563% "sharpened" Lanczos2D; for example, its first crossing is (36
564% sqrt(2) + 123)/(72 sqrt(2) + 47) which is almost identical to the
565% first crossing of the other two.
anthony61b5ddd2010-10-05 02:33:31 +0000566%
nicolas3061b8a2010-10-22 16:34:52 +0000567% 'EXPERT' OPTIONS:
nicolasce6dc292010-10-22 16:23:07 +0000568%
anthony06b1edf2010-10-25 01:19:50 +0000569% These settings are not recommended for production use without expert
570% knowledge of resampling, filtering, and the effects they have on the
571% resulting resampled (resize ro distorted) image.
nicolas3061b8a2010-10-22 16:34:52 +0000572%
anthony06b1edf2010-10-25 01:19:50 +0000573% You can override any and all filter settings, and it is recommended
574% you make good use of "filter:verbose" to make sure that the overall
575% effect of your selection (before and after) is as expected.
nicolas3061b8a2010-10-22 16:34:52 +0000576%
anthony28ad1d72010-10-26 06:30:24 +0000577% "filter:verbose" controls whether to output the exact results of
578% the filter selections made, as well as plotting data for
579% graphing the resulting filter over the filters support range.
cristy3ed852e2009-09-05 21:47:34 +0000580%
anthony48f77622010-10-03 14:32:31 +0000581% "filter:filter" Select the main function associated with
582% this filter name, as the weighting function of the filter.
583% This can be used to set a windowing function as a weighting
584% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000585%
nicolas3061b8a2010-10-22 16:34:52 +0000586% If a "filter:window" operation has not been provided, then a
587% 'Box' windowing function will be set to denote that no
588% windowing function is being used.
cristy3ed852e2009-09-05 21:47:34 +0000589%
nicolas3061b8a2010-10-22 16:34:52 +0000590% "filter:window" Select this windowing function for the filter.
591% While any filter could be used as a windowing function, using
592% the 'first lobe' of that filter over the whole support
593% window, using a non-windowing function is not advisible. If
594% no weighting filter function is specifed a 'SincFast' filter
595% will be used.
cristy3ed852e2009-09-05 21:47:34 +0000596%
nicolas3061b8a2010-10-22 16:34:52 +0000597% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
598% This a simpler method of setting filter support size that
599% will correctly handle the Sinc/Jinc switch for an operators
600% filtering requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000601%
nicolas3061b8a2010-10-22 16:34:52 +0000602% "filter:support" Set the support size for filtering to the size
603% given This not recommended for Sinc/Jinc windowed filters
604% (lobes should be used instead). This will override any
605% 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000606%
nicolas3061b8a2010-10-22 16:34:52 +0000607% "filter:win-support" Scale windowing function to this size
608% instead. This causes the windowing (or self-windowing
609% Lagrange filter) to act is if the support window it much much
610% larger than what is actually supplied to the calling
611% operator. The filter however is still clipped to the real
612% support size given, by the support range suppiled to the
613% caller. If unset this will equal the normal filter support
anthonyb6d08c52010-09-13 01:17:04 +0000614% size.
615%
nicolas3061b8a2010-10-22 16:34:52 +0000616% "filter:blur" Scale the filter and support window by this amount.
cristy3ed852e2009-09-05 21:47:34 +0000617% A value >1 will generally result in a more burred image with
618% more ringing effects, while a value <1 will sharpen the
619% resulting image with more aliasing and Morie effects.
620%
nicolas3061b8a2010-10-22 16:34:52 +0000621% "filter:sigma" The sigma value to use for the Gaussian filter
622% only. Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for
623% cylindrical usage. It effectially provides a alturnative to
624% 'blur' for Gaussians without it also effecting the final
625% 'practical support' size.
anthonyf5e76ef2010-10-12 01:22:01 +0000626%
cristy3ed852e2009-09-05 21:47:34 +0000627% "filter:b"
nicolas3061b8a2010-10-22 16:34:52 +0000628% "filter:c" Override the preset B,C values for a Cubic type of
629% filter If only one of these are given it is assumes to be a
630% 'Keys' type of filter such that B+2C=1, where Keys 'alpha'
631% value = C
cristy3ed852e2009-09-05 21:47:34 +0000632%
anthony06b1edf2010-10-25 01:19:50 +0000633% Examples:
cristy3ed852e2009-09-05 21:47:34 +0000634%
nicolas6e1267a2010-10-22 16:35:52 +0000635% Set a true un-windowed Sinc filter with 10 lobes (very slow):
anthony48f77622010-10-03 14:32:31 +0000636% -define filter:filter=Sinc
637% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000638%
nicolas6e1267a2010-10-22 16:35:52 +0000639% Set an 8 lobe Lanczos (Sinc or Jinc) filter:
cristy3ed852e2009-09-05 21:47:34 +0000640% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000641% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000642%
anthony06b1edf2010-10-25 01:19:50 +0000643%
cristy3ed852e2009-09-05 21:47:34 +0000644% The format of the AcquireResizeFilter method is:
645%
646% ResizeFilter *AcquireResizeFilter(const Image *image,
647% const FilterTypes filter_type, const MagickBooleanType radial,
648% ExceptionInfo *exception)
649%
cristy33b1c162010-01-23 22:51:51 +0000650% A description of each parameter follows:
651%
cristy3ed852e2009-09-05 21:47:34 +0000652% o image: the image.
653%
nicolas07bac812010-09-19 18:47:02 +0000654% o filter: the filter type, defining a preset filter, window and
655% support. The artifact settings listed above will override
656% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000657%
anthony48f77622010-10-03 14:32:31 +0000658% o blur: blur the filter by this amount, use 1.0 if unknown. Image
659% artifact "filter:blur" will override this API call usage, including
660% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000661%
anthony48f77622010-10-03 14:32:31 +0000662% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
663% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000664%
665% o exception: return any errors or warnings in this structure.
666%
667*/
668MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000669 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000670 const MagickBooleanType cylindrical,ExceptionInfo *exception)
671{
672 const char
673 *artifact;
674
675 FilterTypes
676 filter_type,
677 window_type;
678
cristy3ed852e2009-09-05 21:47:34 +0000679 MagickRealType
680 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000681 C,
682 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000683
684 register ResizeFilter
685 *resize_filter;
686
cristy9af9b5d2010-08-15 17:04:28 +0000687 ssize_t
688 option;
689
cristy3ed852e2009-09-05 21:47:34 +0000690 /*
anthony48f77622010-10-03 14:32:31 +0000691 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000692 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000693 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
694 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
695 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000696
nicolas07bac812010-09-19 18:47:02 +0000697 WARNING: The order of this tabel must match the order of the
698 FilterTypes enumeration specified in "resample.h", or the filter
699 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000700
701 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000702 */
703 static struct
704 {
705 FilterTypes
706 filter,
707 window;
708 } const mapping[SentinelFilter] =
709 {
nicolasb7dff642010-10-25 02:04:14 +0000710 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
711 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
712 { BoxFilter, BoxFilter }, /* Box averaging filter */
713 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
714 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
715 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
716 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
anthony06b1edf2010-10-25 01:19:50 +0000717 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
nicolasb7dff642010-10-25 02:04:14 +0000718 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
719 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approx */
720 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
721 { CatromFilter, BoxFilter }, /* Cubic-Keys interpolator */
722 { MitchellFilter, BoxFilter }, /* 'Ideal' Cubic-Keys filter */
anthony06b1edf2010-10-25 01:19:50 +0000723 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
nicolasb7dff642010-10-25 02:04:14 +0000724 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
725 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
726 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
727 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
728 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
729 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing */
730 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
anthony06b1edf2010-10-25 01:19:50 +0000731 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
nicolasb7dff642010-10-25 02:04:14 +0000732 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
anthony06b1edf2010-10-25 01:19:50 +0000733 { LanczosSharpFilter, SincFastFilter }, /* SPECIAL: Sharpened Lanczos */
nicolasb7dff642010-10-25 02:04:14 +0000734 { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
735 { Lanczos2DSharpFilter, JincFilter }, /* SPECIAL: ditto sharpened */
736 { RobidouxFilter, BoxFilter }, /* SPECIAL: Keys tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000737 };
738 /*
nicolas32f44eb2010-09-20 01:23:12 +0000739 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000740 function. The default support size for that filter as a weighting
741 function, the range to scale with to use that function as a sinc
742 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000743
anthony07a3f7f2010-09-16 03:03:11 +0000744 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000745 SincFast(), and CubicBC() functions, which may have multiple
746 filter to function associations.
747
748 See "filter:verbose" handling below for the function -> filter
749 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000750 */
751 static struct
752 {
753 MagickRealType
754 (*function)(const MagickRealType, const ResizeFilter*),
nicolas8eccc162010-10-16 19:48:13 +0000755 lobes, /* Default lobes/support size of the weighting filter. */
anthony450db502010-10-19 04:03:03 +0000756 scale, /* Support when function used as a windowing function
757 Typically equal to the location of the first zero crossing. */
758 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
cristy3ed852e2009-09-05 21:47:34 +0000759 } const filters[SentinelFilter] =
760 {
anthony61b5ddd2010-10-05 02:33:31 +0000761 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
762 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
763 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
764 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
765 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
766 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
767 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
768 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000769 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000770 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
771 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
772 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
anthony853d6972010-10-08 06:01:31 +0000773 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
774 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
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) */
781 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
782 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
783 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
anthony06b1edf2010-10-25 01:19:50 +0000784 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Sharpened 3-lobe Lanczos */
nicolas8eccc162010-10-16 19:48:13 +0000785 { Jinc, 2.0, 1.2196698912665045, 0.0, 0.0 },
anthony06b1edf2010-10-25 01:19:50 +0000786 /* 2-lobe Cylindrical Lanczos (Jinc-Jinc) */
nicolas8eccc162010-10-16 19:48:13 +0000787 { Jinc, 2.0, 1.1684849904329952, 0.0, 0.0 },
788 /* Lanczos2D sharpened with blur=0.958033808 */
anthony450db502010-10-19 04:03:03 +0000789 { CubicBC, 2.0, 1.1685777620836932,
nicolas0d5e5322010-10-22 15:29:30 +0000790 0.37821575509399867, 0.31089212245300067 }
791 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
cristy3ed852e2009-09-05 21:47:34 +0000792 };
793 /*
anthony9a98fc62010-10-11 02:47:19 +0000794 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
anthony450db502010-10-19 04:03:03 +0000795 function being used as a filter. It is used by the "filter:lobes" expert
796 setting and for 'lobes' for Jinc functions in the previous table. This
797 way users do not have to deal with the highly irrational lobe sizes of the
anthony9a98fc62010-10-11 02:47:19 +0000798 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000799
nicolase473f722010-10-07 00:05:13 +0000800 Values taken from
anthony48f77622010-10-03 14:32:31 +0000801 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000802 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000803 */
804 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000805 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000806 {
nicolas8eccc162010-10-16 19:48:13 +0000807 1.2196698912665045,
808 2.2331305943815286,
809 3.2383154841662362,
810 4.2410628637960699,
811 5.2427643768701817,
812 6.2439216898644877,
813 7.244759868719957,
814 8.2453949139520427,
815 9.2458926849494673,
816 10.246293348754916,
817 11.246622794877883,
818 12.246898461138105,
819 13.247132522181061,
820 14.247333735806849,
anthonyc2d07db2010-09-15 23:47:40 +0000821 15.2475085630373,
nicolas8eccc162010-10-16 19:48:13 +0000822 16.247661874700962
cristy3ed852e2009-09-05 21:47:34 +0000823 };
824
cristy33b1c162010-01-23 22:51:51 +0000825 /*
826 Allocate resize filter.
827 */
cristy3ed852e2009-09-05 21:47:34 +0000828 assert(image != (const Image *) NULL);
829 assert(image->signature == MagickSignature);
830 if (image->debug != MagickFalse)
831 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
832 assert(UndefinedFilter < filter && filter < SentinelFilter);
833 assert(exception != (ExceptionInfo *) NULL);
834 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000835 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000836 if (resize_filter == (ResizeFilter *) NULL)
837 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000838 /*
839 Defaults for the requested filter.
840 */
841 filter_type=mapping[filter].filter;
842 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000843 resize_filter->blur = blur;
844 sigma = 0.5;
anthony48f77622010-10-03 14:32:31 +0000845 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000846 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000847 switch (filter_type)
848 {
849 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000850 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000851 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000852 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000853 break;
anthonyba5a7c32010-09-15 02:42:25 +0000854 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000855 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000856 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000857 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000858 break;
anthony06b1edf2010-10-25 01:19:50 +0000859 case LanczosSharpFilter:
nicolasb7dff642010-10-25 02:04:14 +0000860 /* Sharpen Lanczos so as to optimize for minimal blurring of
861 * orthogonal lines in EWA cylindrical usage. Value
862 * determined by Nicolas Robidoux.
863 */
anthony06b1edf2010-10-25 01:19:50 +0000864 resize_filter->blur *= 0.9830391168464;
865 /* fall-thru to promote it to Jinc-Jinc */
cristy33b1c162010-01-23 22:51:51 +0000866 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000867 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000868 filter_type=JincFilter;
869 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000870 break;
anthony08958462010-10-12 06:48:35 +0000871 case Lanczos2DSharpFilter:
anthony06b1edf2010-10-25 01:19:50 +0000872 /* Sharpen Lanczos2d so as to optimize for minimal blurring of
873 * orthogonal lines in EWA cylindrical usage. Value determined by
874 * Nicolas Robidoux. This filter is always a Jinc-Jinc.
anthony08958462010-10-12 06:48:35 +0000875 */
876 resize_filter->blur *= 0.958033808;
877 break;
anthonyf5e76ef2010-10-12 01:22:01 +0000878 case GaussianFilter:
nicolasb7dff642010-10-25 02:04:14 +0000879 /* Cylindrical Gaussian sigma is sqrt(2)/2. */
880 sigma = (MagickRealType) (MagickSQ2/2.0);
anthonyf5e76ef2010-10-12 01:22:01 +0000881 break;
cristya782ecf2010-01-25 02:59:14 +0000882 default:
883 break;
cristy3ed852e2009-09-05 21:47:34 +0000884 }
anthony61b5ddd2010-10-05 02:33:31 +0000885 else
886 switch (filter_type)
887 {
anthony06b1edf2010-10-25 01:19:50 +0000888 case LanczosSharpFilter:
nicolasb7dff642010-10-25 02:04:14 +0000889 resize_filter->blur *= 0.9830391168464; /* Orthogonal sharpen too. */
anthony06b1edf2010-10-25 01:19:50 +0000890 break;
anthonye5f06452010-10-12 05:48:17 +0000891 case Lanczos2DSharpFilter:
nicolasb7dff642010-10-25 02:04:14 +0000892 resize_filter->blur *= 0.958033808; /* Orthogonal sharpen too. */
anthony61b5ddd2010-10-05 02:33:31 +0000893 break;
894 default:
895 break;
896 }
897
cristy3ed852e2009-09-05 21:47:34 +0000898 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000899 if (artifact != (const char *) NULL)
900 {
cristy9af9b5d2010-08-15 17:04:28 +0000901 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000902 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000903 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000904 filter_type=(FilterTypes) option;
905 window_type=BoxFilter;
906 }
907 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000908 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000909 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000910 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000911 }
nicolas07bac812010-09-19 18:47:02 +0000912 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000913 artifact=GetImageArtifact(image,"filter:window");
914 if (artifact != (const char *) NULL)
915 {
cristy9af9b5d2010-08-15 17:04:28 +0000916 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000917 if ((UndefinedFilter < option) && (option < SentinelFilter))
918 {
919 if (option != LanczosFilter)
920 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000921 else
anthony48f77622010-10-03 14:32:31 +0000922 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000923 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000924 }
cristy33b1c162010-01-23 22:51:51 +0000925 }
cristy3ed852e2009-09-05 21:47:34 +0000926 }
cristy33b1c162010-01-23 22:51:51 +0000927 else
928 {
anthony48f77622010-10-03 14:32:31 +0000929 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000930 artifact=GetImageArtifact(image,"filter:window");
931 if (artifact != (const char *) NULL)
932 {
933 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
934 artifact);
935 if ((UndefinedFilter < option) && (option < SentinelFilter))
936 {
anthony61b5ddd2010-10-05 02:33:31 +0000937 filter_type=cylindrical != MagickFalse ?
938 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000939 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000940 }
941 }
942 }
nicolas07bac812010-09-19 18:47:02 +0000943 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000944 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000945 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000946 resize_filter->window=filters[window_type].function;
947 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000948 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000949
anthonyf5e76ef2010-10-12 01:22:01 +0000950 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000951 if (cylindrical != MagickFalse)
952 switch (filter_type)
953 {
954 case PointFilter:
955 case BoxFilter:
956 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000957 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000958 break;
anthony81b8bf92010-10-02 13:54:34 +0000959 default:
960 break;
anthony10b8bc82010-10-02 12:48:46 +0000961 }
anthony61b5ddd2010-10-05 02:33:31 +0000962
anthonyf5e76ef2010-10-12 01:22:01 +0000963 /*
anthony06b1edf2010-10-25 01:19:50 +0000964 ** Other Expert Option Modifications
anthonyf5e76ef2010-10-12 01:22:01 +0000965 */
966
967 /* User Sigma Override - no support change */
968 artifact=GetImageArtifact(image,"filter:sigma");
969 if (artifact != (const char *) NULL)
970 sigma=StringToDouble(artifact);
971 /* Define coefficents for Gaussian (assumes no cubic window) */
972 if ( GaussianFilter ) {
973 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
cristy68f103e2010-10-14 01:19:07 +0000974 resize_filter->coeff[1] = (MagickRealType) (1.0/(Magick2PI*sigma*sigma)); /* unused */
anthonyf5e76ef2010-10-12 01:22:01 +0000975 }
976
977 /* Blur Override */
978 artifact=GetImageArtifact(image,"filter:blur");
979 if (artifact != (const char *) NULL)
980 resize_filter->blur=StringToDouble(artifact);
981 if (resize_filter->blur < MagickEpsilon)
982 resize_filter->blur=(MagickRealType) MagickEpsilon;
983
984 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000985 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000986 if (artifact != (const char *) NULL)
987 {
cristybb503372010-05-27 20:51:26 +0000988 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000989 lobes;
990
cristy96b16132010-08-29 17:19:52 +0000991 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000992 if (lobes < 1)
993 lobes=1;
994 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000995 }
anthony06b1edf2010-10-25 01:19:50 +0000996 /* convert a Jinc function lobes value to a real support value */
anthony61b5ddd2010-10-05 02:33:31 +0000997 if (resize_filter->filter == Jinc)
998 {
999 if (resize_filter->support > 16)
1000 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
1001 else
1002 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
1003 }
1004 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +00001005 artifact=GetImageArtifact(image,"filter:support");
1006 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001007 resize_filter->support=fabs(StringToDouble(artifact));
1008 /*
nicolas07bac812010-09-19 18:47:02 +00001009 Scale windowing function separatally to the support 'clipping'
1010 window that calling operator is planning to actually use. (Expert
1011 override)
cristy3ed852e2009-09-05 21:47:34 +00001012 */
anthony55f12332010-09-10 01:13:02 +00001013 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +00001014 artifact=GetImageArtifact(image,"filter:win-support");
1015 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001016 resize_filter->window_support=fabs(StringToDouble(artifact));
1017 /*
anthony1f90a6b2010-09-14 08:56:31 +00001018 Adjust window function scaling to the windowing support for
1019 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +00001020 */
1021 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +00001022
anthony55f12332010-09-10 01:13:02 +00001023 /*
anthonyf5e76ef2010-10-12 01:22:01 +00001024 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +00001025 */
cristy3ed852e2009-09-05 21:47:34 +00001026 B=0.0;
1027 C=0.0;
cristy33b1c162010-01-23 22:51:51 +00001028 if ((filters[filter_type].function == CubicBC) ||
1029 (filters[window_type].function == CubicBC))
1030 {
anthony2d9b8b52010-09-14 08:31:07 +00001031 B=filters[filter_type].B;
1032 C=filters[filter_type].C;
1033 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +00001034 {
anthony2d9b8b52010-09-14 08:31:07 +00001035 B=filters[window_type].B;
1036 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +00001037 }
cristy33b1c162010-01-23 22:51:51 +00001038 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001039 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001040 {
1041 B=StringToDouble(artifact);
nicolasd15ee9a2010-10-24 18:39:45 +00001042 C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001043 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001044 if (artifact != (const char *) NULL)
1045 C=StringToDouble(artifact);
1046 }
1047 else
1048 {
1049 artifact=GetImageArtifact(image,"filter:c");
1050 if (artifact != (const char *) NULL)
1051 {
1052 C=StringToDouble(artifact);
nicolasb7dff642010-10-25 02:04:14 +00001053 B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001054 }
1055 }
nicolasc6bac3b2010-10-24 18:10:45 +00001056 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
nicolasd15ee9a2010-10-24 18:39:45 +00001057 {
anthony06b1edf2010-10-25 01:19:50 +00001058 const double twoB = B+B;
1059 resize_filter->coeff[0]=1.0-(1.0/3.0)*B;
1060 resize_filter->coeff[1]=-3.0+twoB+C;
1061 resize_filter->coeff[2]=2.0-1.5*B-C;
1062 resize_filter->coeff[3]=(4.0/3.0)*B+4.0*C;
1063 resize_filter->coeff[4]=-8.0*C-twoB;
1064 resize_filter->coeff[5]=B+5.0*C;
1065 resize_filter->coeff[6]=(-1.0/6.0)*B-C;
nicolasd15ee9a2010-10-24 18:39:45 +00001066 }
nicolasc6bac3b2010-10-24 18:10:45 +00001067 }
anthonyf5e76ef2010-10-12 01:22:01 +00001068
anthony55f12332010-09-10 01:13:02 +00001069 /*
nicolas07bac812010-09-19 18:47:02 +00001070 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001071 */
cristyf5b49372010-10-16 01:06:47 +00001072#if defined(MAGICKCORE_OPENMP_SUPPORT)
1073 #pragma omp master
1074 {
1075#endif
1076 artifact=GetImageArtifact(image,"filter:verbose");
anthony28ad1d72010-10-26 06:30:24 +00001077 if (IsMagickTrue(artifact))
cristyf5b49372010-10-16 01:06:47 +00001078 {
1079 double
anthony06b1edf2010-10-25 01:19:50 +00001080 support,
cristyf5b49372010-10-16 01:06:47 +00001081 x;
cristy3ed852e2009-09-05 21:47:34 +00001082
cristyf5b49372010-10-16 01:06:47 +00001083 /*
1084 Set the weighting function properly when the weighting
1085 function may not exactly match the filter of the same name.
anthony06b1edf2010-10-25 01:19:50 +00001086 EG: a Point filter is really uses a Box weighting function
cristyf5b49372010-10-16 01:06:47 +00001087 with a different support than is typically used.
cristyf5b49372010-10-16 01:06:47 +00001088 */
1089 if (resize_filter->filter == Box) filter_type=BoxFilter;
1090 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1091 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1092 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1093 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1094 /*
1095 Report Filter Details.
1096 */
1097 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1098 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
anthony06b1edf2010-10-25 01:19:50 +00001099 (void) fprintf(stdout,"# filter = %s\n",
1100 MagickOptionToMnemonic(MagickFilterOptions,filter_type));
1101 (void) fprintf(stdout,"# window = %s\n",
1102 MagickOptionToMnemonic(MagickFilterOptions, window_type));
1103 (void) fprintf(stdout,"# support = %.*g\n",
1104 GetMagickPrecision(),(double) resize_filter->support);
1105 (void) fprintf(stdout,"# win-support = %.*g\n",
1106 GetMagickPrecision(),(double) resize_filter->window_support);
1107 (void) fprintf(stdout,"# scale_blur = %.*g\n",
1108 GetMagickPrecision(), (double)resize_filter->blur);
cristyf5b49372010-10-16 01:06:47 +00001109 if ( filter_type == GaussianFilter )
anthony06b1edf2010-10-25 01:19:50 +00001110 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",
1111 GetMagickPrecision(), (double)sigma);
1112 (void) fprintf(stdout,"# practical_support = %.*g\n",
1113 GetMagickPrecision(), (double)support);
cristyf5b49372010-10-16 01:06:47 +00001114 if ( filter_type == CubicFilter || window_type == CubicFilter )
anthony06b1edf2010-10-25 01:19:50 +00001115 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",
1116 GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
cristyf5b49372010-10-16 01:06:47 +00001117 (void) fprintf(stdout,"\n");
1118 /*
1119 Output values of resulting filter graph -- for graphing
1120 filter result.
1121 */
1122 for (x=0.0; x <= support; x+=0.01f)
1123 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1124 (double) GetResizeFilterWeight(resize_filter,x));
1125 /* A final value so gnuplot can graph the 'stop' properly. */
1126 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1127 0.0);
1128 }
1129 /* Output the above once only for each image - remove setting */
1130 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1131#if defined(MAGICKCORE_OPENMP_SUPPORT)
1132 }
1133#endif
cristy3ed852e2009-09-05 21:47:34 +00001134 return(resize_filter);
1135}
1136
1137/*
1138%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1139% %
1140% %
1141% %
1142% A d a p t i v e R e s i z e I m a g e %
1143% %
1144% %
1145% %
1146%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1147%
1148% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1149%
1150% The format of the AdaptiveResizeImage method is:
1151%
cristy9af9b5d2010-08-15 17:04:28 +00001152% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1153% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001154%
1155% A description of each parameter follows:
1156%
1157% o image: the image.
1158%
1159% o columns: the number of columns in the resized image.
1160%
1161% o rows: the number of rows in the resized image.
1162%
1163% o exception: return any errors or warnings in this structure.
1164%
1165*/
1166MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001167 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001168{
1169#define AdaptiveResizeImageTag "Resize/Image"
1170
cristyc4c8d132010-01-07 01:58:38 +00001171 CacheView
1172 *resize_view;
1173
cristy3ed852e2009-09-05 21:47:34 +00001174 Image
1175 *resize_image;
1176
cristy3ed852e2009-09-05 21:47:34 +00001177 MagickBooleanType
1178 proceed;
1179
1180 MagickPixelPacket
1181 pixel;
1182
1183 PointInfo
1184 offset;
1185
1186 ResampleFilter
1187 *resample_filter;
1188
cristy9af9b5d2010-08-15 17:04:28 +00001189 ssize_t
1190 y;
1191
cristy3ed852e2009-09-05 21:47:34 +00001192 /*
1193 Adaptively resize image.
1194 */
1195 assert(image != (const Image *) NULL);
1196 assert(image->signature == MagickSignature);
1197 if (image->debug != MagickFalse)
1198 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1199 assert(exception != (ExceptionInfo *) NULL);
1200 assert(exception->signature == MagickSignature);
1201 if ((columns == 0) || (rows == 0))
1202 return((Image *) NULL);
1203 if ((columns == image->columns) && (rows == image->rows))
1204 return(CloneImage(image,0,0,MagickTrue,exception));
1205 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1206 if (resize_image == (Image *) NULL)
1207 return((Image *) NULL);
1208 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1209 {
1210 InheritException(exception,&resize_image->exception);
1211 resize_image=DestroyImage(resize_image);
1212 return((Image *) NULL);
1213 }
1214 GetMagickPixelPacket(image,&pixel);
1215 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001216 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001217 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001218 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001219 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001220 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001221 {
1222 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001223 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001224
cristybb503372010-05-27 20:51:26 +00001225 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001226 x;
1227
1228 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001229 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001230
1231 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1232 exception);
1233 if (q == (PixelPacket *) NULL)
1234 break;
1235 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1236 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001237 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001238 {
1239 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1240 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1241 &pixel);
1242 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1243 q++;
1244 }
1245 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1246 break;
cristy96b16132010-08-29 17:19:52 +00001247 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1248 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001249 if (proceed == MagickFalse)
1250 break;
1251 }
1252 resample_filter=DestroyResampleFilter(resample_filter);
1253 resize_view=DestroyCacheView(resize_view);
1254 return(resize_image);
1255}
1256
1257/*
1258%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1259% %
1260% %
1261% %
1262+ B e s s e l O r d e r O n e %
1263% %
1264% %
1265% %
1266%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1267%
1268% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001269% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001270%
1271% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1272%
1273% j1(x) = x*j1(x);
1274%
1275% For x in (8,inf)
1276%
1277% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1278%
1279% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1280%
1281% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1282% = 1/sqrt(2) * (sin(x) - cos(x))
1283% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1284% = -1/sqrt(2) * (sin(x) + cos(x))
1285%
1286% The format of the BesselOrderOne method is:
1287%
1288% MagickRealType BesselOrderOne(MagickRealType x)
1289%
1290% A description of each parameter follows:
1291%
1292% o x: MagickRealType value.
1293%
1294*/
1295
1296#undef I0
1297static MagickRealType I0(MagickRealType x)
1298{
1299 MagickRealType
1300 sum,
1301 t,
1302 y;
1303
cristybb503372010-05-27 20:51:26 +00001304 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001305 i;
1306
1307 /*
1308 Zeroth order Bessel function of the first kind.
1309 */
1310 sum=1.0;
1311 y=x*x/4.0;
1312 t=y;
1313 for (i=2; t > MagickEpsilon; i++)
1314 {
1315 sum+=t;
1316 t*=y/((MagickRealType) i*i);
1317 }
1318 return(sum);
1319}
1320
1321#undef J1
1322static MagickRealType J1(MagickRealType x)
1323{
1324 MagickRealType
1325 p,
1326 q;
1327
cristybb503372010-05-27 20:51:26 +00001328 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001329 i;
1330
1331 static const double
1332 Pone[] =
1333 {
1334 0.581199354001606143928050809e+21,
1335 -0.6672106568924916298020941484e+20,
1336 0.2316433580634002297931815435e+19,
1337 -0.3588817569910106050743641413e+17,
1338 0.2908795263834775409737601689e+15,
1339 -0.1322983480332126453125473247e+13,
1340 0.3413234182301700539091292655e+10,
1341 -0.4695753530642995859767162166e+7,
1342 0.270112271089232341485679099e+4
1343 },
1344 Qone[] =
1345 {
1346 0.11623987080032122878585294e+22,
1347 0.1185770712190320999837113348e+20,
1348 0.6092061398917521746105196863e+17,
1349 0.2081661221307607351240184229e+15,
1350 0.5243710262167649715406728642e+12,
1351 0.1013863514358673989967045588e+10,
1352 0.1501793594998585505921097578e+7,
1353 0.1606931573481487801970916749e+4,
1354 0.1e+1
1355 };
1356
1357 p=Pone[8];
1358 q=Qone[8];
1359 for (i=7; i >= 0; i--)
1360 {
1361 p=p*x*x+Pone[i];
1362 q=q*x*x+Qone[i];
1363 }
1364 return(p/q);
1365}
1366
1367#undef P1
1368static MagickRealType P1(MagickRealType x)
1369{
1370 MagickRealType
1371 p,
1372 q;
1373
cristybb503372010-05-27 20:51:26 +00001374 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001375 i;
1376
1377 static const double
1378 Pone[] =
1379 {
1380 0.352246649133679798341724373e+5,
1381 0.62758845247161281269005675e+5,
1382 0.313539631109159574238669888e+5,
1383 0.49854832060594338434500455e+4,
1384 0.2111529182853962382105718e+3,
1385 0.12571716929145341558495e+1
1386 },
1387 Qone[] =
1388 {
1389 0.352246649133679798068390431e+5,
1390 0.626943469593560511888833731e+5,
1391 0.312404063819041039923015703e+5,
1392 0.4930396490181088979386097e+4,
1393 0.2030775189134759322293574e+3,
1394 0.1e+1
1395 };
1396
1397 p=Pone[5];
1398 q=Qone[5];
1399 for (i=4; i >= 0; i--)
1400 {
1401 p=p*(8.0/x)*(8.0/x)+Pone[i];
1402 q=q*(8.0/x)*(8.0/x)+Qone[i];
1403 }
1404 return(p/q);
1405}
1406
1407#undef Q1
1408static MagickRealType Q1(MagickRealType x)
1409{
1410 MagickRealType
1411 p,
1412 q;
1413
cristybb503372010-05-27 20:51:26 +00001414 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001415 i;
1416
1417 static const double
1418 Pone[] =
1419 {
1420 0.3511751914303552822533318e+3,
1421 0.7210391804904475039280863e+3,
1422 0.4259873011654442389886993e+3,
1423 0.831898957673850827325226e+2,
1424 0.45681716295512267064405e+1,
1425 0.3532840052740123642735e-1
1426 },
1427 Qone[] =
1428 {
1429 0.74917374171809127714519505e+4,
1430 0.154141773392650970499848051e+5,
1431 0.91522317015169922705904727e+4,
1432 0.18111867005523513506724158e+4,
1433 0.1038187585462133728776636e+3,
1434 0.1e+1
1435 };
1436
1437 p=Pone[5];
1438 q=Qone[5];
1439 for (i=4; i >= 0; i--)
1440 {
1441 p=p*(8.0/x)*(8.0/x)+Pone[i];
1442 q=q*(8.0/x)*(8.0/x)+Qone[i];
1443 }
1444 return(p/q);
1445}
1446
1447static MagickRealType BesselOrderOne(MagickRealType x)
1448{
1449 MagickRealType
1450 p,
1451 q;
1452
1453 if (x == 0.0)
1454 return(0.0);
1455 p=x;
1456 if (x < 0.0)
1457 x=(-x);
1458 if (x < 8.0)
1459 return(p*J1(x));
1460 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1461 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1462 cos((double) x))));
1463 if (p < 0.0)
1464 q=(-q);
1465 return(q);
1466}
1467
1468/*
1469%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1470% %
1471% %
1472% %
1473+ D e s t r o y R e s i z e F i l t e r %
1474% %
1475% %
1476% %
1477%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1478%
1479% DestroyResizeFilter() destroy the resize filter.
1480%
cristya2ffd7e2010-03-10 20:50:30 +00001481% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001482%
1483% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1484%
1485% A description of each parameter follows:
1486%
1487% o resize_filter: the resize filter.
1488%
1489*/
1490MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1491{
1492 assert(resize_filter != (ResizeFilter *) NULL);
1493 assert(resize_filter->signature == MagickSignature);
1494 resize_filter->signature=(~MagickSignature);
1495 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1496 return(resize_filter);
1497}
1498
1499/*
1500%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1501% %
1502% %
1503% %
1504+ G e t R e s i z e F i l t e r S u p p o r t %
1505% %
1506% %
1507% %
1508%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1509%
1510% GetResizeFilterSupport() return the current support window size for this
1511% filter. Note that this may have been enlarged by filter:blur factor.
1512%
1513% The format of the GetResizeFilterSupport method is:
1514%
1515% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1516%
1517% A description of each parameter follows:
1518%
1519% o filter: Image filter to use.
1520%
1521*/
1522MagickExport MagickRealType GetResizeFilterSupport(
1523 const ResizeFilter *resize_filter)
1524{
1525 assert(resize_filter != (ResizeFilter *) NULL);
1526 assert(resize_filter->signature == MagickSignature);
1527 return(resize_filter->support*resize_filter->blur);
1528}
1529
1530/*
1531%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1532% %
1533% %
1534% %
1535+ G e t R e s i z e F i l t e r W e i g h t %
1536% %
1537% %
1538% %
1539%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1540%
1541% GetResizeFilterWeight evaluates the specified resize filter at the point x
1542% which usally lies between zero and the filters current 'support' and
1543% returns the weight of the filter function at that point.
1544%
1545% The format of the GetResizeFilterWeight method is:
1546%
1547% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1548% const MagickRealType x)
1549%
1550% A description of each parameter follows:
1551%
1552% o filter: the filter type.
1553%
1554% o x: the point.
1555%
1556*/
1557MagickExport MagickRealType GetResizeFilterWeight(
1558 const ResizeFilter *resize_filter,const MagickRealType x)
1559{
1560 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001561 scale,
1562 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001563
1564 /*
1565 Windowing function - scale the weighting filter by this amount.
1566 */
1567 assert(resize_filter != (ResizeFilter *) NULL);
1568 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001569 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001570 if ((resize_filter->window_support < MagickEpsilon) ||
1571 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001572 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001573 else
1574 {
anthony55f12332010-09-10 01:13:02 +00001575 scale=resize_filter->scale;
1576 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001577 }
anthony55f12332010-09-10 01:13:02 +00001578 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001579}
1580
1581/*
1582%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1583% %
1584% %
1585% %
1586% M a g n i f y I m a g e %
1587% %
1588% %
1589% %
1590%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1591%
1592% MagnifyImage() is a convenience method that scales an image proportionally
1593% to twice its size.
1594%
1595% The format of the MagnifyImage method is:
1596%
1597% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1598%
1599% A description of each parameter follows:
1600%
1601% o image: the image.
1602%
1603% o exception: return any errors or warnings in this structure.
1604%
1605*/
1606MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1607{
1608 Image
1609 *magnify_image;
1610
1611 assert(image != (Image *) NULL);
1612 assert(image->signature == MagickSignature);
1613 if (image->debug != MagickFalse)
1614 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1615 assert(exception != (ExceptionInfo *) NULL);
1616 assert(exception->signature == MagickSignature);
1617 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1618 1.0,exception);
1619 return(magnify_image);
1620}
1621
1622/*
1623%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1624% %
1625% %
1626% %
1627% M i n i f y I m a g e %
1628% %
1629% %
1630% %
1631%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1632%
1633% MinifyImage() is a convenience method that scales an image proportionally
1634% to half its size.
1635%
1636% The format of the MinifyImage method is:
1637%
1638% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1639%
1640% A description of each parameter follows:
1641%
1642% o image: the image.
1643%
1644% o exception: return any errors or warnings in this structure.
1645%
1646*/
1647MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1648{
1649 Image
1650 *minify_image;
1651
1652 assert(image != (Image *) NULL);
1653 assert(image->signature == MagickSignature);
1654 if (image->debug != MagickFalse)
1655 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1656 assert(exception != (ExceptionInfo *) NULL);
1657 assert(exception->signature == MagickSignature);
1658 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1659 1.0,exception);
1660 return(minify_image);
1661}
1662
1663/*
1664%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1665% %
1666% %
1667% %
1668% R e s a m p l e I m a g e %
1669% %
1670% %
1671% %
1672%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1673%
1674% ResampleImage() resize image in terms of its pixel size, so that when
1675% displayed at the given resolution it will be the same size in terms of
1676% real world units as the original image at the original resolution.
1677%
1678% The format of the ResampleImage method is:
1679%
1680% Image *ResampleImage(Image *image,const double x_resolution,
1681% const double y_resolution,const FilterTypes filter,const double blur,
1682% ExceptionInfo *exception)
1683%
1684% A description of each parameter follows:
1685%
1686% o image: the image to be resized to fit the given resolution.
1687%
1688% o x_resolution: the new image x resolution.
1689%
1690% o y_resolution: the new image y resolution.
1691%
1692% o filter: Image filter to use.
1693%
1694% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1695%
1696*/
1697MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1698 const double y_resolution,const FilterTypes filter,const double blur,
1699 ExceptionInfo *exception)
1700{
1701#define ResampleImageTag "Resample/Image"
1702
1703 Image
1704 *resample_image;
1705
cristybb503372010-05-27 20:51:26 +00001706 size_t
cristy3ed852e2009-09-05 21:47:34 +00001707 height,
1708 width;
1709
1710 /*
1711 Initialize sampled image attributes.
1712 */
1713 assert(image != (const Image *) NULL);
1714 assert(image->signature == MagickSignature);
1715 if (image->debug != MagickFalse)
1716 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1717 assert(exception != (ExceptionInfo *) NULL);
1718 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001719 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1720 72.0 : image->x_resolution)+0.5);
1721 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1722 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001723 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1724 if (resample_image != (Image *) NULL)
1725 {
1726 resample_image->x_resolution=x_resolution;
1727 resample_image->y_resolution=y_resolution;
1728 }
1729 return(resample_image);
1730}
1731#if defined(MAGICKCORE_LQR_DELEGATE)
1732
1733/*
1734%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1735% %
1736% %
1737% %
1738% L i q u i d R e s c a l e I m a g e %
1739% %
1740% %
1741% %
1742%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1743%
1744% LiquidRescaleImage() rescales image with seam carving.
1745%
1746% The format of the LiquidRescaleImage method is:
1747%
1748% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001749% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001750% const double delta_x,const double rigidity,ExceptionInfo *exception)
1751%
1752% A description of each parameter follows:
1753%
1754% o image: the image.
1755%
1756% o columns: the number of columns in the rescaled image.
1757%
1758% o rows: the number of rows in the rescaled image.
1759%
1760% o delta_x: maximum seam transversal step (0 means straight seams).
1761%
1762% o rigidity: introduce a bias for non-straight seams (typically 0).
1763%
1764% o exception: return any errors or warnings in this structure.
1765%
1766*/
cristy9af9b5d2010-08-15 17:04:28 +00001767MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1768 const size_t rows,const double delta_x,const double rigidity,
1769 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001770{
1771#define LiquidRescaleImageTag "Rescale/Image"
1772
cristyc5c6f662010-09-22 14:23:02 +00001773 CacheView
1774 *rescale_view;
1775
cristy3ed852e2009-09-05 21:47:34 +00001776 const char
1777 *map;
1778
1779 guchar
1780 *packet;
1781
1782 Image
1783 *rescale_image;
1784
1785 int
1786 x,
1787 y;
1788
1789 LqrCarver
1790 *carver;
1791
1792 LqrRetVal
1793 lqr_status;
1794
1795 MagickBooleanType
1796 status;
1797
1798 MagickPixelPacket
1799 pixel;
1800
1801 unsigned char
1802 *pixels;
1803
1804 /*
1805 Liquid rescale image.
1806 */
1807 assert(image != (const Image *) NULL);
1808 assert(image->signature == MagickSignature);
1809 if (image->debug != MagickFalse)
1810 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1811 assert(exception != (ExceptionInfo *) NULL);
1812 assert(exception->signature == MagickSignature);
1813 if ((columns == 0) || (rows == 0))
1814 return((Image *) NULL);
1815 if ((columns == image->columns) && (rows == image->rows))
1816 return(CloneImage(image,0,0,MagickTrue,exception));
1817 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001818 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001819 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1820 {
1821 Image
1822 *resize_image;
1823
cristybb503372010-05-27 20:51:26 +00001824 size_t
cristy3ed852e2009-09-05 21:47:34 +00001825 height,
1826 width;
1827
1828 /*
1829 Honor liquid resize size limitations.
1830 */
1831 for (width=image->columns; columns >= (2*width-1); width*=2);
1832 for (height=image->rows; rows >= (2*height-1); height*=2);
1833 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1834 exception);
1835 if (resize_image == (Image *) NULL)
1836 return((Image *) NULL);
1837 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1838 rigidity,exception);
1839 resize_image=DestroyImage(resize_image);
1840 return(rescale_image);
1841 }
1842 map="RGB";
1843 if (image->matte == MagickFalse)
1844 map="RGBA";
1845 if (image->colorspace == CMYKColorspace)
1846 {
1847 map="CMYK";
1848 if (image->matte == MagickFalse)
1849 map="CMYKA";
1850 }
1851 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1852 strlen(map)*sizeof(*pixels));
1853 if (pixels == (unsigned char *) NULL)
1854 return((Image *) NULL);
1855 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1856 pixels,exception);
1857 if (status == MagickFalse)
1858 {
1859 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1860 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1861 }
1862 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1863 if (carver == (LqrCarver *) NULL)
1864 {
1865 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1866 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1867 }
1868 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1869 lqr_status=lqr_carver_resize(carver,columns,rows);
1870 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1871 lqr_carver_get_height(carver),MagickTrue,exception);
1872 if (rescale_image == (Image *) NULL)
1873 {
1874 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1875 return((Image *) NULL);
1876 }
1877 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1878 {
1879 InheritException(exception,&rescale_image->exception);
1880 rescale_image=DestroyImage(rescale_image);
1881 return((Image *) NULL);
1882 }
1883 GetMagickPixelPacket(rescale_image,&pixel);
1884 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001885 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001886 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1887 {
1888 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001889 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001890
1891 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001892 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001893
anthony22aad252010-09-23 06:59:07 +00001894 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001895 if (q == (PixelPacket *) NULL)
1896 break;
cristyc5c6f662010-09-22 14:23:02 +00001897 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001898 pixel.red=QuantumRange*(packet[0]/255.0);
1899 pixel.green=QuantumRange*(packet[1]/255.0);
1900 pixel.blue=QuantumRange*(packet[2]/255.0);
1901 if (image->colorspace != CMYKColorspace)
1902 {
1903 if (image->matte == MagickFalse)
1904 pixel.opacity=QuantumRange*(packet[3]/255.0);
1905 }
1906 else
1907 {
1908 pixel.index=QuantumRange*(packet[3]/255.0);
1909 if (image->matte == MagickFalse)
1910 pixel.opacity=QuantumRange*(packet[4]/255.0);
1911 }
1912 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001913 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001914 break;
1915 }
cristyc5c6f662010-09-22 14:23:02 +00001916 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001917 /*
1918 Relinquish resources.
1919 */
1920 lqr_carver_destroy(carver);
1921 return(rescale_image);
1922}
1923#else
1924MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001925 const size_t magick_unused(columns),const size_t magick_unused(rows),
1926 const double magick_unused(delta_x),const double magick_unused(rigidity),
1927 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001928{
1929 assert(image != (const Image *) NULL);
1930 assert(image->signature == MagickSignature);
1931 if (image->debug != MagickFalse)
1932 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1933 assert(exception != (ExceptionInfo *) NULL);
1934 assert(exception->signature == MagickSignature);
1935 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1936 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1937 return((Image *) NULL);
1938}
1939#endif
1940
1941/*
1942%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1943% %
1944% %
1945% %
1946% R e s i z e I m a g e %
1947% %
1948% %
1949% %
1950%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1951%
1952% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001953% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001954%
1955% If an undefined filter is given the filter defaults to Mitchell for a
1956% colormapped image, a image with a matte channel, or if the image is
1957% enlarged. Otherwise the filter defaults to a Lanczos.
1958%
1959% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1960%
1961% The format of the ResizeImage method is:
1962%
cristybb503372010-05-27 20:51:26 +00001963% Image *ResizeImage(Image *image,const size_t columns,
1964% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001965% ExceptionInfo *exception)
1966%
1967% A description of each parameter follows:
1968%
1969% o image: the image.
1970%
1971% o columns: the number of columns in the scaled image.
1972%
1973% o rows: the number of rows in the scaled image.
1974%
1975% o filter: Image filter to use.
1976%
cristy9af9b5d2010-08-15 17:04:28 +00001977% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1978% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001979%
1980% o exception: return any errors or warnings in this structure.
1981%
1982*/
1983
1984typedef struct _ContributionInfo
1985{
1986 MagickRealType
1987 weight;
1988
cristybb503372010-05-27 20:51:26 +00001989 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001990 pixel;
1991} ContributionInfo;
1992
1993static ContributionInfo **DestroyContributionThreadSet(
1994 ContributionInfo **contribution)
1995{
cristybb503372010-05-27 20:51:26 +00001996 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001997 i;
1998
1999 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00002000 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002001 if (contribution[i] != (ContributionInfo *) NULL)
2002 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
2003 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00002004 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00002005 return(contribution);
2006}
2007
2008static ContributionInfo **AcquireContributionThreadSet(const size_t count)
2009{
cristybb503372010-05-27 20:51:26 +00002010 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002011 i;
2012
2013 ContributionInfo
2014 **contribution;
2015
cristybb503372010-05-27 20:51:26 +00002016 size_t
cristy3ed852e2009-09-05 21:47:34 +00002017 number_threads;
2018
2019 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002020 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00002021 sizeof(*contribution));
2022 if (contribution == (ContributionInfo **) NULL)
2023 return((ContributionInfo **) NULL);
2024 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00002025 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002026 {
2027 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2028 sizeof(**contribution));
2029 if (contribution[i] == (ContributionInfo *) NULL)
2030 return(DestroyContributionThreadSet(contribution));
2031 }
2032 return(contribution);
2033}
2034
2035static inline double MagickMax(const double x,const double y)
2036{
2037 if (x > y)
2038 return(x);
2039 return(y);
2040}
2041
2042static inline double MagickMin(const double x,const double y)
2043{
2044 if (x < y)
2045 return(x);
2046 return(y);
2047}
2048
2049static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2050 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002051 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002052{
2053#define ResizeImageTag "Resize/Image"
2054
cristyfa112112010-01-04 17:48:07 +00002055 CacheView
2056 *image_view,
2057 *resize_view;
2058
cristy3ed852e2009-09-05 21:47:34 +00002059 ClassType
2060 storage_class;
2061
2062 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002063 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002064
cristy3ed852e2009-09-05 21:47:34 +00002065 MagickBooleanType
2066 status;
2067
2068 MagickPixelPacket
2069 zero;
2070
2071 MagickRealType
2072 scale,
2073 support;
2074
cristy9af9b5d2010-08-15 17:04:28 +00002075 ssize_t
2076 x;
2077
cristy3ed852e2009-09-05 21:47:34 +00002078 /*
2079 Apply filter to resize horizontally from image to resize image.
2080 */
cristy5d824382010-09-06 14:00:17 +00002081 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002082 support=scale*GetResizeFilterSupport(resize_filter);
2083 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2084 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2085 {
2086 InheritException(exception,&resize_image->exception);
2087 return(MagickFalse);
2088 }
2089 if (support < 0.5)
2090 {
2091 /*
nicolas07bac812010-09-19 18:47:02 +00002092 Support too small even for nearest neighbour: Reduce to point
2093 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002094 */
2095 support=(MagickRealType) 0.5;
2096 scale=1.0;
2097 }
2098 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2099 if (contributions == (ContributionInfo **) NULL)
2100 {
2101 (void) ThrowMagickException(exception,GetMagickModule(),
2102 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2103 return(MagickFalse);
2104 }
2105 status=MagickTrue;
2106 scale=1.0/scale;
2107 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2108 image_view=AcquireCacheView(image);
2109 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002110#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002111 #pragma omp parallel for shared(status)
2112#endif
cristybb503372010-05-27 20:51:26 +00002113 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002114 {
cristy3ed852e2009-09-05 21:47:34 +00002115 MagickRealType
2116 center,
2117 density;
2118
2119 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002120 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002121
2122 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002123 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002124
cristy03dbbd22010-09-19 23:04:47 +00002125 register ContributionInfo
2126 *restrict contribution;
2127
cristy3ed852e2009-09-05 21:47:34 +00002128 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002129 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002130
cristy3ed852e2009-09-05 21:47:34 +00002131 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002132 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002133
cristy03dbbd22010-09-19 23:04:47 +00002134 register ssize_t
2135 y;
2136
cristy9af9b5d2010-08-15 17:04:28 +00002137 ssize_t
2138 n,
2139 start,
2140 stop;
2141
cristy3ed852e2009-09-05 21:47:34 +00002142 if (status == MagickFalse)
2143 continue;
2144 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002145 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2146 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002147 density=0.0;
2148 contribution=contributions[GetOpenMPThreadId()];
2149 for (n=0; n < (stop-start); n++)
2150 {
2151 contribution[n].pixel=start+n;
2152 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2153 ((MagickRealType) (start+n)-center+0.5));
2154 density+=contribution[n].weight;
2155 }
2156 if ((density != 0.0) && (density != 1.0))
2157 {
cristybb503372010-05-27 20:51:26 +00002158 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002159 i;
2160
2161 /*
2162 Normalize.
2163 */
2164 density=1.0/density;
2165 for (i=0; i < n; i++)
2166 contribution[i].weight*=density;
2167 }
cristy9af9b5d2010-08-15 17:04:28 +00002168 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2169 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002170 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2171 exception);
2172 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2173 {
2174 status=MagickFalse;
2175 continue;
2176 }
2177 indexes=GetCacheViewVirtualIndexQueue(image_view);
2178 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002179 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002180 {
cristy3ed852e2009-09-05 21:47:34 +00002181 MagickPixelPacket
2182 pixel;
2183
2184 MagickRealType
2185 alpha;
2186
cristybb503372010-05-27 20:51:26 +00002187 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002188 i;
2189
cristy9af9b5d2010-08-15 17:04:28 +00002190 ssize_t
2191 j;
2192
cristy3ed852e2009-09-05 21:47:34 +00002193 pixel=zero;
2194 if (image->matte == MagickFalse)
2195 {
2196 for (i=0; i < n; i++)
2197 {
2198 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2199 (contribution[i].pixel-contribution[0].pixel);
2200 alpha=contribution[i].weight;
2201 pixel.red+=alpha*(p+j)->red;
2202 pixel.green+=alpha*(p+j)->green;
2203 pixel.blue+=alpha*(p+j)->blue;
2204 pixel.opacity+=alpha*(p+j)->opacity;
2205 }
cristyce70c172010-01-07 17:15:30 +00002206 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2207 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2208 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2209 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002210 if ((image->colorspace == CMYKColorspace) &&
2211 (resize_image->colorspace == CMYKColorspace))
2212 {
2213 for (i=0; i < n; i++)
2214 {
2215 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2216 (contribution[i].pixel-contribution[0].pixel);
2217 alpha=contribution[i].weight;
2218 pixel.index+=alpha*indexes[j];
2219 }
cristyce70c172010-01-07 17:15:30 +00002220 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002221 }
2222 }
2223 else
2224 {
2225 MagickRealType
2226 gamma;
2227
2228 gamma=0.0;
2229 for (i=0; i < n; i++)
2230 {
2231 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2232 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002233 alpha=contribution[i].weight*QuantumScale*
2234 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002235 pixel.red+=alpha*(p+j)->red;
2236 pixel.green+=alpha*(p+j)->green;
2237 pixel.blue+=alpha*(p+j)->blue;
2238 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2239 gamma+=alpha;
2240 }
2241 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002242 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2243 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2244 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2245 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002246 if ((image->colorspace == CMYKColorspace) &&
2247 (resize_image->colorspace == CMYKColorspace))
2248 {
2249 for (i=0; i < n; i++)
2250 {
2251 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2252 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002253 alpha=contribution[i].weight*QuantumScale*
2254 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002255 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002256 }
cristyce70c172010-01-07 17:15:30 +00002257 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2258 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002259 }
2260 }
2261 if ((resize_image->storage_class == PseudoClass) &&
2262 (image->storage_class == PseudoClass))
2263 {
cristybb503372010-05-27 20:51:26 +00002264 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002265 1.0)+0.5);
2266 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2267 (contribution[i-start].pixel-contribution[0].pixel);
2268 resize_indexes[y]=indexes[j];
2269 }
2270 q++;
2271 }
2272 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2273 status=MagickFalse;
2274 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2275 {
2276 MagickBooleanType
2277 proceed;
2278
cristyb5d5f722009-11-04 03:03:49 +00002279#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002280 #pragma omp critical (MagickCore_HorizontalFilter)
2281#endif
cristy9af9b5d2010-08-15 17:04:28 +00002282 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002283 if (proceed == MagickFalse)
2284 status=MagickFalse;
2285 }
2286 }
2287 resize_view=DestroyCacheView(resize_view);
2288 image_view=DestroyCacheView(image_view);
2289 contributions=DestroyContributionThreadSet(contributions);
2290 return(status);
2291}
2292
2293static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2294 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002295 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002296{
cristyfa112112010-01-04 17:48:07 +00002297 CacheView
2298 *image_view,
2299 *resize_view;
2300
cristy3ed852e2009-09-05 21:47:34 +00002301 ClassType
2302 storage_class;
2303
2304 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002305 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002306
cristy3ed852e2009-09-05 21:47:34 +00002307 MagickBooleanType
2308 status;
2309
2310 MagickPixelPacket
2311 zero;
2312
2313 MagickRealType
2314 scale,
2315 support;
2316
cristy9af9b5d2010-08-15 17:04:28 +00002317 ssize_t
2318 y;
2319
cristy3ed852e2009-09-05 21:47:34 +00002320 /*
cristy9af9b5d2010-08-15 17:04:28 +00002321 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002322 */
cristy5d824382010-09-06 14:00:17 +00002323 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002324 support=scale*GetResizeFilterSupport(resize_filter);
2325 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2326 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2327 {
2328 InheritException(exception,&resize_image->exception);
2329 return(MagickFalse);
2330 }
2331 if (support < 0.5)
2332 {
2333 /*
nicolas07bac812010-09-19 18:47:02 +00002334 Support too small even for nearest neighbour: Reduce to point
2335 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002336 */
2337 support=(MagickRealType) 0.5;
2338 scale=1.0;
2339 }
2340 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2341 if (contributions == (ContributionInfo **) NULL)
2342 {
2343 (void) ThrowMagickException(exception,GetMagickModule(),
2344 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2345 return(MagickFalse);
2346 }
2347 status=MagickTrue;
2348 scale=1.0/scale;
2349 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2350 image_view=AcquireCacheView(image);
2351 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002352#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002353 #pragma omp parallel for shared(status)
2354#endif
cristybb503372010-05-27 20:51:26 +00002355 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002356 {
cristy3ed852e2009-09-05 21:47:34 +00002357 MagickRealType
2358 center,
2359 density;
2360
2361 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002362 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002363
2364 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002365 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002366
cristy03dbbd22010-09-19 23:04:47 +00002367 register ContributionInfo
2368 *restrict contribution;
2369
cristy3ed852e2009-09-05 21:47:34 +00002370 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002371 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002372
cristy9af9b5d2010-08-15 17:04:28 +00002373 register PixelPacket
2374 *restrict q;
2375
cristybb503372010-05-27 20:51:26 +00002376 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002377 x;
2378
cristy9af9b5d2010-08-15 17:04:28 +00002379 ssize_t
2380 n,
2381 start,
2382 stop;
cristy3ed852e2009-09-05 21:47:34 +00002383
2384 if (status == MagickFalse)
2385 continue;
cristy679e6962010-03-18 00:42:45 +00002386 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002387 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2388 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002389 density=0.0;
2390 contribution=contributions[GetOpenMPThreadId()];
2391 for (n=0; n < (stop-start); n++)
2392 {
2393 contribution[n].pixel=start+n;
2394 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2395 ((MagickRealType) (start+n)-center+0.5));
2396 density+=contribution[n].weight;
2397 }
2398 if ((density != 0.0) && (density != 1.0))
2399 {
cristybb503372010-05-27 20:51:26 +00002400 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002401 i;
2402
2403 /*
2404 Normalize.
2405 */
2406 density=1.0/density;
2407 for (i=0; i < n; i++)
2408 contribution[i].weight*=density;
2409 }
2410 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002411 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2412 exception);
cristy3ed852e2009-09-05 21:47:34 +00002413 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2414 exception);
2415 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2416 {
2417 status=MagickFalse;
2418 continue;
2419 }
2420 indexes=GetCacheViewVirtualIndexQueue(image_view);
2421 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002422 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002423 {
cristy3ed852e2009-09-05 21:47:34 +00002424 MagickPixelPacket
2425 pixel;
2426
2427 MagickRealType
2428 alpha;
2429
cristybb503372010-05-27 20:51:26 +00002430 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002431 i;
2432
cristy9af9b5d2010-08-15 17:04:28 +00002433 ssize_t
2434 j;
2435
cristy3ed852e2009-09-05 21:47:34 +00002436 pixel=zero;
2437 if (image->matte == MagickFalse)
2438 {
2439 for (i=0; i < n; i++)
2440 {
cristybb503372010-05-27 20:51:26 +00002441 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002442 image->columns+x);
2443 alpha=contribution[i].weight;
2444 pixel.red+=alpha*(p+j)->red;
2445 pixel.green+=alpha*(p+j)->green;
2446 pixel.blue+=alpha*(p+j)->blue;
2447 pixel.opacity+=alpha*(p+j)->opacity;
2448 }
cristyce70c172010-01-07 17:15:30 +00002449 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2450 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2451 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2452 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002453 if ((image->colorspace == CMYKColorspace) &&
2454 (resize_image->colorspace == CMYKColorspace))
2455 {
2456 for (i=0; i < n; i++)
2457 {
cristybb503372010-05-27 20:51:26 +00002458 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002459 image->columns+x);
2460 alpha=contribution[i].weight;
2461 pixel.index+=alpha*indexes[j];
2462 }
cristyce70c172010-01-07 17:15:30 +00002463 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002464 }
2465 }
2466 else
2467 {
2468 MagickRealType
2469 gamma;
2470
2471 gamma=0.0;
2472 for (i=0; i < n; i++)
2473 {
cristybb503372010-05-27 20:51:26 +00002474 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002475 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002476 alpha=contribution[i].weight*QuantumScale*
2477 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002478 pixel.red+=alpha*(p+j)->red;
2479 pixel.green+=alpha*(p+j)->green;
2480 pixel.blue+=alpha*(p+j)->blue;
2481 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2482 gamma+=alpha;
2483 }
2484 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002485 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2486 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2487 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2488 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002489 if ((image->colorspace == CMYKColorspace) &&
2490 (resize_image->colorspace == CMYKColorspace))
2491 {
2492 for (i=0; i < n; i++)
2493 {
cristybb503372010-05-27 20:51:26 +00002494 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002495 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002496 alpha=contribution[i].weight*QuantumScale*
2497 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002498 pixel.index+=alpha*indexes[j];
2499 }
cristyce70c172010-01-07 17:15:30 +00002500 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2501 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002502 }
2503 }
2504 if ((resize_image->storage_class == PseudoClass) &&
2505 (image->storage_class == PseudoClass))
2506 {
cristybb503372010-05-27 20:51:26 +00002507 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002508 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002509 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002510 image->columns+x);
2511 resize_indexes[x]=indexes[j];
2512 }
2513 q++;
2514 }
2515 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2516 status=MagickFalse;
2517 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2518 {
2519 MagickBooleanType
2520 proceed;
2521
cristyb5d5f722009-11-04 03:03:49 +00002522#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002523 #pragma omp critical (MagickCore_VerticalFilter)
2524#endif
cristy9af9b5d2010-08-15 17:04:28 +00002525 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002526 if (proceed == MagickFalse)
2527 status=MagickFalse;
2528 }
2529 }
2530 resize_view=DestroyCacheView(resize_view);
2531 image_view=DestroyCacheView(image_view);
2532 contributions=DestroyContributionThreadSet(contributions);
2533 return(status);
2534}
2535
cristybb503372010-05-27 20:51:26 +00002536MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2537 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002538 ExceptionInfo *exception)
2539{
2540#define WorkLoadFactor 0.265
2541
2542 FilterTypes
2543 filter_type;
2544
2545 Image
2546 *filter_image,
2547 *resize_image;
2548
cristy9af9b5d2010-08-15 17:04:28 +00002549 MagickOffsetType
2550 offset;
2551
cristy3ed852e2009-09-05 21:47:34 +00002552 MagickRealType
2553 x_factor,
2554 y_factor;
2555
2556 MagickSizeType
2557 span;
2558
2559 MagickStatusType
2560 status;
2561
2562 ResizeFilter
2563 *resize_filter;
2564
cristy3ed852e2009-09-05 21:47:34 +00002565 /*
2566 Acquire resize image.
2567 */
2568 assert(image != (Image *) NULL);
2569 assert(image->signature == MagickSignature);
2570 if (image->debug != MagickFalse)
2571 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2572 assert(exception != (ExceptionInfo *) NULL);
2573 assert(exception->signature == MagickSignature);
2574 if ((columns == 0) || (rows == 0))
2575 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2576 if ((columns == image->columns) && (rows == image->rows) &&
2577 (filter == UndefinedFilter) && (blur == 1.0))
2578 return(CloneImage(image,0,0,MagickTrue,exception));
2579 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2580 if (resize_image == (Image *) NULL)
2581 return(resize_image);
2582 /*
2583 Acquire resize filter.
2584 */
2585 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2586 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2587 if ((x_factor*y_factor) > WorkLoadFactor)
2588 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2589 else
2590 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2591 if (filter_image == (Image *) NULL)
2592 return(DestroyImage(resize_image));
2593 filter_type=LanczosFilter;
2594 if (filter != UndefinedFilter)
2595 filter_type=filter;
2596 else
2597 if ((x_factor == 1.0) && (y_factor == 1.0))
2598 filter_type=PointFilter;
2599 else
2600 if ((image->storage_class == PseudoClass) ||
2601 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2602 filter_type=MitchellFilter;
2603 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2604 exception);
2605 /*
2606 Resize image.
2607 */
cristy9af9b5d2010-08-15 17:04:28 +00002608 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002609 if ((x_factor*y_factor) > WorkLoadFactor)
2610 {
2611 span=(MagickSizeType) (filter_image->columns+rows);
2612 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002613 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002614 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002615 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002616 }
2617 else
2618 {
2619 span=(MagickSizeType) (filter_image->rows+columns);
2620 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002621 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002622 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002623 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002624 }
2625 /*
2626 Free resources.
2627 */
2628 filter_image=DestroyImage(filter_image);
2629 resize_filter=DestroyResizeFilter(resize_filter);
2630 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2631 return((Image *) NULL);
2632 resize_image->type=image->type;
2633 return(resize_image);
2634}
2635
2636/*
2637%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2638% %
2639% %
2640% %
2641% S a m p l e I m a g e %
2642% %
2643% %
2644% %
2645%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2646%
2647% SampleImage() scales an image to the desired dimensions with pixel
2648% sampling. Unlike other scaling methods, this method does not introduce
2649% any additional color into the scaled image.
2650%
2651% The format of the SampleImage method is:
2652%
cristybb503372010-05-27 20:51:26 +00002653% Image *SampleImage(const Image *image,const size_t columns,
2654% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002655%
2656% A description of each parameter follows:
2657%
2658% o image: the image.
2659%
2660% o columns: the number of columns in the sampled image.
2661%
2662% o rows: the number of rows in the sampled image.
2663%
2664% o exception: return any errors or warnings in this structure.
2665%
2666*/
cristybb503372010-05-27 20:51:26 +00002667MagickExport Image *SampleImage(const Image *image,const size_t columns,
2668 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002669{
2670#define SampleImageTag "Sample/Image"
2671
cristyc4c8d132010-01-07 01:58:38 +00002672 CacheView
2673 *image_view,
2674 *sample_view;
2675
cristy3ed852e2009-09-05 21:47:34 +00002676 Image
2677 *sample_image;
2678
cristy3ed852e2009-09-05 21:47:34 +00002679 MagickBooleanType
2680 status;
2681
cristy5f959472010-05-27 22:19:46 +00002682 MagickOffsetType
2683 progress;
2684
cristybb503372010-05-27 20:51:26 +00002685 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002686 x;
2687
cristy5f959472010-05-27 22:19:46 +00002688 ssize_t
2689 *x_offset,
2690 y;
2691
cristy3ed852e2009-09-05 21:47:34 +00002692 /*
2693 Initialize sampled image attributes.
2694 */
2695 assert(image != (const Image *) NULL);
2696 assert(image->signature == MagickSignature);
2697 if (image->debug != MagickFalse)
2698 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2699 assert(exception != (ExceptionInfo *) NULL);
2700 assert(exception->signature == MagickSignature);
2701 if ((columns == 0) || (rows == 0))
2702 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2703 if ((columns == image->columns) && (rows == image->rows))
2704 return(CloneImage(image,0,0,MagickTrue,exception));
2705 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2706 if (sample_image == (Image *) NULL)
2707 return((Image *) NULL);
2708 /*
2709 Allocate scan line buffer and column offset buffers.
2710 */
cristybb503372010-05-27 20:51:26 +00002711 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002712 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002713 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002714 {
2715 sample_image=DestroyImage(sample_image);
2716 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2717 }
cristybb503372010-05-27 20:51:26 +00002718 for (x=0; x < (ssize_t) sample_image->columns; x++)
2719 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002720 sample_image->columns);
2721 /*
2722 Sample each row.
2723 */
2724 status=MagickTrue;
2725 progress=0;
2726 image_view=AcquireCacheView(image);
2727 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002728#if defined(MAGICKCORE_OPENMP_SUPPORT)
2729 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002730#endif
cristybb503372010-05-27 20:51:26 +00002731 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002732 {
cristy3ed852e2009-09-05 21:47:34 +00002733 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002734 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002735
2736 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002737 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002738
2739 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002740 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002741
cristy3ed852e2009-09-05 21:47:34 +00002742 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002743 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002744
cristy03dbbd22010-09-19 23:04:47 +00002745 register ssize_t
2746 x;
2747
cristy9af9b5d2010-08-15 17:04:28 +00002748 ssize_t
2749 y_offset;
2750
cristy3ed852e2009-09-05 21:47:34 +00002751 if (status == MagickFalse)
2752 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002753 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2754 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002755 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2756 exception);
2757 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2758 exception);
2759 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2760 {
2761 status=MagickFalse;
2762 continue;
2763 }
2764 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2765 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2766 /*
2767 Sample each column.
2768 */
cristybb503372010-05-27 20:51:26 +00002769 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002770 *q++=p[x_offset[x]];
2771 if ((image->storage_class == PseudoClass) ||
2772 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002773 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002774 sample_indexes[x]=indexes[x_offset[x]];
2775 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2776 status=MagickFalse;
2777 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2778 {
2779 MagickBooleanType
2780 proceed;
2781
cristyb5d5f722009-11-04 03:03:49 +00002782#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002783 #pragma omp critical (MagickCore_SampleImage)
2784#endif
2785 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2786 if (proceed == MagickFalse)
2787 status=MagickFalse;
2788 }
2789 }
2790 image_view=DestroyCacheView(image_view);
2791 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002792 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002793 sample_image->type=image->type;
2794 return(sample_image);
2795}
2796
2797/*
2798%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2799% %
2800% %
2801% %
2802% S c a l e I m a g e %
2803% %
2804% %
2805% %
2806%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2807%
2808% ScaleImage() changes the size of an image to the given dimensions.
2809%
2810% The format of the ScaleImage method is:
2811%
cristybb503372010-05-27 20:51:26 +00002812% Image *ScaleImage(const Image *image,const size_t columns,
2813% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002814%
2815% A description of each parameter follows:
2816%
2817% o image: the image.
2818%
2819% o columns: the number of columns in the scaled image.
2820%
2821% o rows: the number of rows in the scaled image.
2822%
2823% o exception: return any errors or warnings in this structure.
2824%
2825*/
cristybb503372010-05-27 20:51:26 +00002826MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2827 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002828{
2829#define ScaleImageTag "Scale/Image"
2830
cristyed6cb232010-01-20 03:07:53 +00002831 CacheView
2832 *image_view,
2833 *scale_view;
2834
cristy3ed852e2009-09-05 21:47:34 +00002835 Image
2836 *scale_image;
2837
cristy3ed852e2009-09-05 21:47:34 +00002838 MagickBooleanType
2839 next_column,
2840 next_row,
2841 proceed;
2842
2843 MagickPixelPacket
2844 pixel,
2845 *scale_scanline,
2846 *scanline,
2847 *x_vector,
2848 *y_vector,
2849 zero;
2850
cristy3ed852e2009-09-05 21:47:34 +00002851 PointInfo
2852 scale,
2853 span;
2854
cristybb503372010-05-27 20:51:26 +00002855 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002856 i;
2857
cristy9af9b5d2010-08-15 17:04:28 +00002858 ssize_t
2859 number_rows,
2860 y;
2861
cristy3ed852e2009-09-05 21:47:34 +00002862 /*
2863 Initialize scaled image attributes.
2864 */
2865 assert(image != (const Image *) NULL);
2866 assert(image->signature == MagickSignature);
2867 if (image->debug != MagickFalse)
2868 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2869 assert(exception != (ExceptionInfo *) NULL);
2870 assert(exception->signature == MagickSignature);
2871 if ((columns == 0) || (rows == 0))
2872 return((Image *) NULL);
2873 if ((columns == image->columns) && (rows == image->rows))
2874 return(CloneImage(image,0,0,MagickTrue,exception));
2875 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2876 if (scale_image == (Image *) NULL)
2877 return((Image *) NULL);
2878 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2879 {
2880 InheritException(exception,&scale_image->exception);
2881 scale_image=DestroyImage(scale_image);
2882 return((Image *) NULL);
2883 }
2884 /*
2885 Allocate memory.
2886 */
2887 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2888 sizeof(*x_vector));
2889 scanline=x_vector;
2890 if (image->rows != scale_image->rows)
2891 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2892 sizeof(*scanline));
2893 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2894 scale_image->columns,sizeof(*scale_scanline));
2895 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2896 sizeof(*y_vector));
2897 if ((scanline == (MagickPixelPacket *) NULL) ||
2898 (scale_scanline == (MagickPixelPacket *) NULL) ||
2899 (x_vector == (MagickPixelPacket *) NULL) ||
2900 (y_vector == (MagickPixelPacket *) NULL))
2901 {
2902 scale_image=DestroyImage(scale_image);
2903 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2904 }
2905 /*
2906 Scale image.
2907 */
2908 number_rows=0;
2909 next_row=MagickTrue;
2910 span.y=1.0;
2911 scale.y=(double) scale_image->rows/(double) image->rows;
2912 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2913 sizeof(*y_vector));
2914 GetMagickPixelPacket(image,&pixel);
2915 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2916 i=0;
cristyed6cb232010-01-20 03:07:53 +00002917 image_view=AcquireCacheView(image);
2918 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002919 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002920 {
2921 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002922 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002923
2924 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002925 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002926
2927 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002928 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002929
cristy3ed852e2009-09-05 21:47:34 +00002930 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002931 *restrict s,
2932 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002933
2934 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002935 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002936
cristy9af9b5d2010-08-15 17:04:28 +00002937 register ssize_t
2938 x;
2939
cristyed6cb232010-01-20 03:07:53 +00002940 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2941 exception);
cristy3ed852e2009-09-05 21:47:34 +00002942 if (q == (PixelPacket *) NULL)
2943 break;
2944 scale_indexes=GetAuthenticIndexQueue(scale_image);
2945 if (scale_image->rows == image->rows)
2946 {
2947 /*
2948 Read a new scanline.
2949 */
cristyed6cb232010-01-20 03:07:53 +00002950 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2951 exception);
cristy3ed852e2009-09-05 21:47:34 +00002952 if (p == (const PixelPacket *) NULL)
2953 break;
cristyed6cb232010-01-20 03:07:53 +00002954 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002955 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002956 {
cristyce70c172010-01-07 17:15:30 +00002957 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2958 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2959 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002960 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002961 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002962 if (indexes != (IndexPacket *) NULL)
2963 x_vector[x].index=(MagickRealType) indexes[x];
2964 p++;
2965 }
2966 }
2967 else
2968 {
2969 /*
2970 Scale Y direction.
2971 */
2972 while (scale.y < span.y)
2973 {
cristy9af9b5d2010-08-15 17:04:28 +00002974 if ((next_row != MagickFalse) &&
2975 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002976 {
2977 /*
2978 Read a new scanline.
2979 */
cristyed6cb232010-01-20 03:07:53 +00002980 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2981 exception);
cristy3ed852e2009-09-05 21:47:34 +00002982 if (p == (const PixelPacket *) NULL)
2983 break;
cristyed6cb232010-01-20 03:07:53 +00002984 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002985 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002986 {
cristyce70c172010-01-07 17:15:30 +00002987 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2988 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2989 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002990 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002991 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002992 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002993 if (indexes != (IndexPacket *) NULL)
2994 x_vector[x].index=(MagickRealType) indexes[x];
2995 p++;
2996 }
2997 number_rows++;
2998 }
cristybb503372010-05-27 20:51:26 +00002999 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003000 {
3001 y_vector[x].red+=scale.y*x_vector[x].red;
3002 y_vector[x].green+=scale.y*x_vector[x].green;
3003 y_vector[x].blue+=scale.y*x_vector[x].blue;
3004 if (scale_image->matte != MagickFalse)
3005 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
3006 if (scale_indexes != (IndexPacket *) NULL)
3007 y_vector[x].index+=scale.y*x_vector[x].index;
3008 }
3009 span.y-=scale.y;
3010 scale.y=(double) scale_image->rows/(double) image->rows;
3011 next_row=MagickTrue;
3012 }
cristybb503372010-05-27 20:51:26 +00003013 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00003014 {
3015 /*
3016 Read a new scanline.
3017 */
cristyed6cb232010-01-20 03:07:53 +00003018 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
3019 exception);
cristy3ed852e2009-09-05 21:47:34 +00003020 if (p == (const PixelPacket *) NULL)
3021 break;
cristyed6cb232010-01-20 03:07:53 +00003022 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00003023 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003024 {
cristyce70c172010-01-07 17:15:30 +00003025 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
3026 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
3027 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003028 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00003029 x_vector[x].opacity=(MagickRealType)
3030 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003031 if (indexes != (IndexPacket *) NULL)
3032 x_vector[x].index=(MagickRealType) indexes[x];
3033 p++;
3034 }
3035 number_rows++;
3036 next_row=MagickFalse;
3037 }
3038 s=scanline;
cristybb503372010-05-27 20:51:26 +00003039 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003040 {
3041 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3042 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3043 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3044 if (image->matte != MagickFalse)
3045 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3046 if (scale_indexes != (IndexPacket *) NULL)
3047 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3048 s->red=pixel.red;
3049 s->green=pixel.green;
3050 s->blue=pixel.blue;
3051 if (scale_image->matte != MagickFalse)
3052 s->opacity=pixel.opacity;
3053 if (scale_indexes != (IndexPacket *) NULL)
3054 s->index=pixel.index;
3055 s++;
3056 y_vector[x]=zero;
3057 }
3058 scale.y-=span.y;
3059 if (scale.y <= 0)
3060 {
3061 scale.y=(double) scale_image->rows/(double) image->rows;
3062 next_row=MagickTrue;
3063 }
3064 span.y=1.0;
3065 }
3066 if (scale_image->columns == image->columns)
3067 {
3068 /*
3069 Transfer scanline to scaled image.
3070 */
3071 s=scanline;
cristybb503372010-05-27 20:51:26 +00003072 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003073 {
cristyce70c172010-01-07 17:15:30 +00003074 q->red=ClampToQuantum(s->red);
3075 q->green=ClampToQuantum(s->green);
3076 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003077 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003078 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003079 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003080 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003081 q++;
3082 s++;
3083 }
3084 }
3085 else
3086 {
3087 /*
3088 Scale X direction.
3089 */
3090 pixel=zero;
3091 next_column=MagickFalse;
3092 span.x=1.0;
3093 s=scanline;
3094 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003095 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003096 {
3097 scale.x=(double) scale_image->columns/(double) image->columns;
3098 while (scale.x >= span.x)
3099 {
3100 if (next_column != MagickFalse)
3101 {
3102 pixel=zero;
3103 t++;
3104 }
3105 pixel.red+=span.x*s->red;
3106 pixel.green+=span.x*s->green;
3107 pixel.blue+=span.x*s->blue;
3108 if (image->matte != MagickFalse)
3109 pixel.opacity+=span.x*s->opacity;
3110 if (scale_indexes != (IndexPacket *) NULL)
3111 pixel.index+=span.x*s->index;
3112 t->red=pixel.red;
3113 t->green=pixel.green;
3114 t->blue=pixel.blue;
3115 if (scale_image->matte != MagickFalse)
3116 t->opacity=pixel.opacity;
3117 if (scale_indexes != (IndexPacket *) NULL)
3118 t->index=pixel.index;
3119 scale.x-=span.x;
3120 span.x=1.0;
3121 next_column=MagickTrue;
3122 }
3123 if (scale.x > 0)
3124 {
3125 if (next_column != MagickFalse)
3126 {
3127 pixel=zero;
3128 next_column=MagickFalse;
3129 t++;
3130 }
3131 pixel.red+=scale.x*s->red;
3132 pixel.green+=scale.x*s->green;
3133 pixel.blue+=scale.x*s->blue;
3134 if (scale_image->matte != MagickFalse)
3135 pixel.opacity+=scale.x*s->opacity;
3136 if (scale_indexes != (IndexPacket *) NULL)
3137 pixel.index+=scale.x*s->index;
3138 span.x-=scale.x;
3139 }
3140 s++;
3141 }
3142 if (span.x > 0)
3143 {
3144 s--;
3145 pixel.red+=span.x*s->red;
3146 pixel.green+=span.x*s->green;
3147 pixel.blue+=span.x*s->blue;
3148 if (scale_image->matte != MagickFalse)
3149 pixel.opacity+=span.x*s->opacity;
3150 if (scale_indexes != (IndexPacket *) NULL)
3151 pixel.index+=span.x*s->index;
3152 }
3153 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003154 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003155 {
3156 t->red=pixel.red;
3157 t->green=pixel.green;
3158 t->blue=pixel.blue;
3159 if (scale_image->matte != MagickFalse)
3160 t->opacity=pixel.opacity;
3161 if (scale_indexes != (IndexPacket *) NULL)
3162 t->index=pixel.index;
3163 }
3164 /*
3165 Transfer scanline to scaled image.
3166 */
3167 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003168 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003169 {
cristyce70c172010-01-07 17:15:30 +00003170 q->red=ClampToQuantum(t->red);
3171 q->green=ClampToQuantum(t->green);
3172 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003173 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003174 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003175 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003176 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003177 t++;
3178 q++;
3179 }
3180 }
cristyed6cb232010-01-20 03:07:53 +00003181 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003182 break;
cristy96b16132010-08-29 17:19:52 +00003183 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3184 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003185 if (proceed == MagickFalse)
3186 break;
3187 }
cristyed6cb232010-01-20 03:07:53 +00003188 scale_view=DestroyCacheView(scale_view);
3189 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003190 /*
3191 Free allocated memory.
3192 */
3193 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3194 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3195 if (scale_image->rows != image->rows)
3196 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3197 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3198 scale_image->type=image->type;
3199 return(scale_image);
3200}
3201
anthony02b4cb42010-10-10 04:54:35 +00003202#if 0
3203 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003204/*
3205%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3206% %
3207% %
3208% %
3209+ S e t R e s i z e F i l t e r S u p p o r t %
3210% %
3211% %
3212% %
3213%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3214%
3215% SetResizeFilterSupport() specifies which IR filter to use to window
3216%
3217% The format of the SetResizeFilterSupport method is:
3218%
3219% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3220% const MagickRealType support)
3221%
3222% A description of each parameter follows:
3223%
3224% o resize_filter: the resize filter.
3225%
3226% o support: the filter spport radius.
3227%
3228*/
3229MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3230 const MagickRealType support)
3231{
3232 assert(resize_filter != (ResizeFilter *) NULL);
3233 assert(resize_filter->signature == MagickSignature);
3234 resize_filter->support=support;
3235}
anthony02b4cb42010-10-10 04:54:35 +00003236#endif
cristy3ed852e2009-09-05 21:47:34 +00003237
3238/*
3239%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3240% %
3241% %
3242% %
3243% T h u m b n a i l I m a g e %
3244% %
3245% %
3246% %
3247%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3248%
3249% ThumbnailImage() changes the size of an image to the given dimensions and
3250% removes any associated profiles. The goal is to produce small low cost
3251% thumbnail images suited for display on the Web.
3252%
3253% The format of the ThumbnailImage method is:
3254%
cristybb503372010-05-27 20:51:26 +00003255% Image *ThumbnailImage(const Image *image,const size_t columns,
3256% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003257%
3258% A description of each parameter follows:
3259%
3260% o image: the image.
3261%
3262% o columns: the number of columns in the scaled image.
3263%
3264% o rows: the number of rows in the scaled image.
3265%
3266% o exception: return any errors or warnings in this structure.
3267%
3268*/
cristy9af9b5d2010-08-15 17:04:28 +00003269MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3270 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003271{
3272#define SampleFactor 5
3273
3274 char
3275 value[MaxTextExtent];
3276
3277 const char
3278 *name;
3279
3280 Image
3281 *thumbnail_image;
3282
3283 MagickRealType
3284 x_factor,
3285 y_factor;
3286
cristybb503372010-05-27 20:51:26 +00003287 size_t
cristy3ed852e2009-09-05 21:47:34 +00003288 version;
3289
cristy9af9b5d2010-08-15 17:04:28 +00003290 struct stat
3291 attributes;
3292
cristy3ed852e2009-09-05 21:47:34 +00003293 assert(image != (Image *) NULL);
3294 assert(image->signature == MagickSignature);
3295 if (image->debug != MagickFalse)
3296 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3297 assert(exception != (ExceptionInfo *) NULL);
3298 assert(exception->signature == MagickSignature);
3299 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3300 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3301 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003302 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3303 exception);
cristy3ed852e2009-09-05 21:47:34 +00003304 else
3305 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003306 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3307 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003308 else
3309 {
3310 Image
3311 *sample_image;
3312
3313 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3314 exception);
3315 if (sample_image == (Image *) NULL)
3316 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003317 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3318 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003319 sample_image=DestroyImage(sample_image);
3320 }
3321 if (thumbnail_image == (Image *) NULL)
3322 return(thumbnail_image);
3323 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3324 if (thumbnail_image->matte == MagickFalse)
3325 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3326 thumbnail_image->depth=8;
3327 thumbnail_image->interlace=NoInterlace;
3328 /*
3329 Strip all profiles except color profiles.
3330 */
3331 ResetImageProfileIterator(thumbnail_image);
3332 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3333 {
3334 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3335 {
cristy2b726bd2010-01-11 01:05:39 +00003336 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003337 ResetImageProfileIterator(thumbnail_image);
3338 }
3339 name=GetNextImageProfile(thumbnail_image);
3340 }
3341 (void) DeleteImageProperty(thumbnail_image,"comment");
3342 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003343 if (strstr(image->magick_filename,"//") == (char *) NULL)
3344 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003345 image->magick_filename);
3346 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3347 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3348 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3349 {
cristye8c25f92010-06-03 00:53:06 +00003350 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003351 attributes.st_mtime);
3352 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3353 }
cristye8c25f92010-06-03 00:53:06 +00003354 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003355 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003356 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003357 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003358 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3359 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3360 LocaleLower(value);
3361 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3362 (void) SetImageProperty(thumbnail_image,"software",
3363 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003364 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3365 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003366 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003367 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003368 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003369 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003370 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3371 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003372 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3373 return(thumbnail_image);
3374}