blob: f533edec88fc42e946506ed5ef6048309e4edad1 [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%
anthony152700d2010-10-28 02:43:18 +0000517% The special a 'cylindrical' filter flag will promote the default
518% 4-lobed Windowed Sinc filter to a 3-lobed Windowed Jinc equivelent,
519% which is better suited to this style of image resampling. This
520% typically happens when using such a filter for images distortions.
cristy3ed852e2009-09-05 21:47:34 +0000521%
anthony152700d2010-10-28 02:43:18 +0000522% Directly requesting 'Sinc', 'Jinc' function as a filter will force
523% the use of function without any windowing, or promotion for
524% cylindrical usage. This is not recommended, except by image
525% processing experts, especially as part of expert option filter
526% function selection.
anthony06b1edf2010-10-25 01:19:50 +0000527%
anthony48f77622010-10-03 14:32:31 +0000528% Two forms of the 'Sinc' function are available: Sinc and SincFast.
nicolas07bac812010-09-19 18:47:02 +0000529% Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
530% selected if the user specifically specifies the use of a Sinc
531% filter. SincFast uses highly accurate (and fast) polynomial (low Q)
anthony48f77622010-10-03 14:32:31 +0000532% and rational (high Q) approximations, and will be used by default in
533% most cases.
anthonyba5a7c32010-09-15 02:42:25 +0000534%
anthony152700d2010-10-28 02:43:18 +0000535% Lanczos filter is a special 3-lobed Sinc windowed Sinc filter,
536% (or cylindrical promoted Jinc-Jinc filter). This filter is
537% probably the most popular windowed filter.
538%
539% LanczosSharp is a slightly sharpened (blur=0.9830391168464) form of
540% the Lanczos filter. It was designed specifically for cylindrical
541% EWA (Elliptical Weighted Average) distortion (as a Jinc-Jinc
542% filter), but can used as a slightly sharper orthogonal Lanczos
543% (Sinc-Sinc) filter. The blur value, the corresponding EWA filter
544% comes as close as possible to satisfying the following condition:
545%
546% 'No-Op' Vertical and Horizontal Line Preservation Condition:
547% Images with only vertical or horizontal features are preserved
548% when performing 'no-op" with EWA distortion.
549%
550% The Lanczos2 and Lanczos2Sharp filters are simply 2-lobe versions
551% of the Lanczos filters. The 'sharp' version uses a blur factor of
nicolas56d88732010-10-28 13:20:37 +0000552% 0.958027803631219, again chosen because the resulting EWA filter
553% comes as close as possible to satisfing the "'No-Op' Vertical and
554% Horizontal Line Preservation Condition".
anthony06b1edf2010-10-25 01:19:50 +0000555%
nicolasb7dff642010-10-25 02:04:14 +0000556% Robidoux is another filter tuned for EWA. It is the Keys cubic
anthony152700d2010-10-28 02:43:18 +0000557% filter defined by B=(228 - 108 sqrt(2))/199. Robidoux satisfies the
nicolasb7dff642010-10-25 02:04:14 +0000558% "'No-Op' Vertical and Horizontal Line Preservation Condition"
anthony152700d2010-10-28 02:43:18 +0000559% exactly. It also seems to provide only minimal bluring of a low
560% level 'pixel-hash' pattern in the 'No-Op Distort' case. It turns
561% out to be close to both plain Mitchell and Lanczos2Sharp filters.
562% For example, its first crossing is at (36 sqrt(2) + 123)/(72
563% sqrt(2) + 47) which is almost the same as the first crossing
564% of the other two.
565%
anthony61b5ddd2010-10-05 02:33:31 +0000566%
nicolas3061b8a2010-10-22 16:34:52 +0000567% 'EXPERT' OPTIONS:
nicolasce6dc292010-10-22 16:23:07 +0000568%
anthony152700d2010-10-28 02:43:18 +0000569% These artifact "defines" are not recommended for production use
570% without expert knowledge of resampling, filtering, and the effects
571% they have on the resulting resampled (resize ro distorted) image.
nicolas3061b8a2010-10-22 16:34:52 +0000572%
anthony152700d2010-10-28 02:43:18 +0000573% They can be used to override any and all filter default, and it is
574% recommended you make good use of "filter:verbose" to make sure that
575% the overall effect of your selection (before and after) is as
576% expected.
nicolas3061b8a2010-10-22 16:34:52 +0000577%
anthony28ad1d72010-10-26 06:30:24 +0000578% "filter:verbose" controls whether to output the exact results of
579% the filter selections made, as well as plotting data for
580% graphing the resulting filter over the filters support range.
cristy3ed852e2009-09-05 21:47:34 +0000581%
anthony48f77622010-10-03 14:32:31 +0000582% "filter:filter" Select the main function associated with
583% this filter name, as the weighting function of the filter.
584% This can be used to set a windowing function as a weighting
585% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000586%
nicolas3061b8a2010-10-22 16:34:52 +0000587% If a "filter:window" operation has not been provided, then a
588% 'Box' windowing function will be set to denote that no
589% windowing function is being used.
cristy3ed852e2009-09-05 21:47:34 +0000590%
nicolas3061b8a2010-10-22 16:34:52 +0000591% "filter:window" Select this windowing function for the filter.
592% While any filter could be used as a windowing function, using
593% the 'first lobe' of that filter over the whole support
594% window, using a non-windowing function is not advisible. If
595% no weighting filter function is specifed a 'SincFast' filter
596% will be used.
cristy3ed852e2009-09-05 21:47:34 +0000597%
nicolas3061b8a2010-10-22 16:34:52 +0000598% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
599% This a simpler method of setting filter support size that
600% will correctly handle the Sinc/Jinc switch for an operators
601% filtering requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000602%
nicolas3061b8a2010-10-22 16:34:52 +0000603% "filter:support" Set the support size for filtering to the size
604% given This not recommended for Sinc/Jinc windowed filters
605% (lobes should be used instead). This will override any
606% 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000607%
nicolas3061b8a2010-10-22 16:34:52 +0000608% "filter:win-support" Scale windowing function to this size
609% instead. This causes the windowing (or self-windowing
610% Lagrange filter) to act is if the support window it much much
611% larger than what is actually supplied to the calling
612% operator. The filter however is still clipped to the real
613% support size given, by the support range suppiled to the
614% caller. If unset this will equal the normal filter support
anthonyb6d08c52010-09-13 01:17:04 +0000615% size.
616%
nicolas3061b8a2010-10-22 16:34:52 +0000617% "filter:blur" Scale the filter and support window by this amount.
cristy3ed852e2009-09-05 21:47:34 +0000618% A value >1 will generally result in a more burred image with
619% more ringing effects, while a value <1 will sharpen the
anthony152700d2010-10-28 02:43:18 +0000620% resulting image with more aliasing effects.
cristy3ed852e2009-09-05 21:47:34 +0000621%
nicolas3061b8a2010-10-22 16:34:52 +0000622% "filter:sigma" The sigma value to use for the Gaussian filter
623% only. Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for
624% cylindrical usage. It effectially provides a alturnative to
625% 'blur' for Gaussians without it also effecting the final
626% 'practical support' size.
anthonyf5e76ef2010-10-12 01:22:01 +0000627%
cristy3ed852e2009-09-05 21:47:34 +0000628% "filter:b"
nicolas3061b8a2010-10-22 16:34:52 +0000629% "filter:c" Override the preset B,C values for a Cubic type of
630% filter If only one of these are given it is assumes to be a
631% 'Keys' type of filter such that B+2C=1, where Keys 'alpha'
632% value = C
cristy3ed852e2009-09-05 21:47:34 +0000633%
anthony06b1edf2010-10-25 01:19:50 +0000634% Examples:
cristy3ed852e2009-09-05 21:47:34 +0000635%
nicolas6e1267a2010-10-22 16:35:52 +0000636% Set a true un-windowed Sinc filter with 10 lobes (very slow):
anthony48f77622010-10-03 14:32:31 +0000637% -define filter:filter=Sinc
638% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000639%
nicolas6e1267a2010-10-22 16:35:52 +0000640% Set an 8 lobe Lanczos (Sinc or Jinc) filter:
cristy3ed852e2009-09-05 21:47:34 +0000641% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000642% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000643%
anthony06b1edf2010-10-25 01:19:50 +0000644%
cristy3ed852e2009-09-05 21:47:34 +0000645% The format of the AcquireResizeFilter method is:
646%
647% ResizeFilter *AcquireResizeFilter(const Image *image,
648% const FilterTypes filter_type, const MagickBooleanType radial,
649% ExceptionInfo *exception)
650%
cristy33b1c162010-01-23 22:51:51 +0000651% A description of each parameter follows:
652%
cristy3ed852e2009-09-05 21:47:34 +0000653% o image: the image.
654%
nicolas07bac812010-09-19 18:47:02 +0000655% o filter: the filter type, defining a preset filter, window and
656% support. The artifact settings listed above will override
657% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000658%
anthony48f77622010-10-03 14:32:31 +0000659% o blur: blur the filter by this amount, use 1.0 if unknown. Image
660% artifact "filter:blur" will override this API call usage, including
661% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000662%
anthony48f77622010-10-03 14:32:31 +0000663% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
664% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000665%
666% o exception: return any errors or warnings in this structure.
667%
668*/
669MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000670 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000671 const MagickBooleanType cylindrical,ExceptionInfo *exception)
672{
673 const char
674 *artifact;
675
676 FilterTypes
677 filter_type,
678 window_type;
679
cristy3ed852e2009-09-05 21:47:34 +0000680 MagickRealType
681 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000682 C,
683 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000684
685 register ResizeFilter
686 *resize_filter;
687
cristy9af9b5d2010-08-15 17:04:28 +0000688
cristy3ed852e2009-09-05 21:47:34 +0000689 /*
anthony48f77622010-10-03 14:32:31 +0000690 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000691 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000692 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
693 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
694 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000695
nicolas07bac812010-09-19 18:47:02 +0000696 WARNING: The order of this tabel must match the order of the
697 FilterTypes enumeration specified in "resample.h", or the filter
698 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000699
700 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000701 */
702 static struct
703 {
704 FilterTypes
705 filter,
706 window;
707 } const mapping[SentinelFilter] =
708 {
nicolasb7dff642010-10-25 02:04:14 +0000709 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
710 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
711 { BoxFilter, BoxFilter }, /* Box averaging filter */
712 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
713 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
714 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
715 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
anthony06b1edf2010-10-25 01:19:50 +0000716 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
nicolasb7dff642010-10-25 02:04:14 +0000717 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
718 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approx */
719 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
720 { CatromFilter, BoxFilter }, /* Cubic-Keys interpolator */
721 { MitchellFilter, BoxFilter }, /* 'Ideal' Cubic-Keys filter */
nicolasb7dff642010-10-25 02:04:14 +0000722 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
723 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony152700d2010-10-28 02:43:18 +0000724 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolasb7dff642010-10-25 02:04:14 +0000725 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
726 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
727 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
nicolasb7dff642010-10-25 02:04:14 +0000728 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
anthony06b1edf2010-10-25 01:19:50 +0000729 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
anthony152700d2010-10-28 02:43:18 +0000730 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing */
731 { LanczosFilter, LanczosFilter }, /* Lanczos Sinc-Sinc filters */
732 { LanczosSharpFilter, LanczosSharpFilter }, /* | these require */
733 { Lanczos2Filter, Lanczos2Filter }, /* | special handling */
734 { Lanczos2SharpFilter,Lanczos2SharpFilter },
735 { RobidouxFilter, BoxFilter }, /* Cubic Keys tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000736 };
737 /*
nicolas32f44eb2010-09-20 01:23:12 +0000738 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000739 function. The default support size for that filter as a weighting
740 function, the range to scale with to use that function as a sinc
741 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000742
anthony07a3f7f2010-09-16 03:03:11 +0000743 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000744 SincFast(), and CubicBC() functions, which may have multiple
745 filter to function associations.
746
747 See "filter:verbose" handling below for the function -> filter
748 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000749 */
750 static struct
751 {
752 MagickRealType
753 (*function)(const MagickRealType, const ResizeFilter*),
nicolas8eccc162010-10-16 19:48:13 +0000754 lobes, /* Default lobes/support size of the weighting filter. */
anthony450db502010-10-19 04:03:03 +0000755 scale, /* Support when function used as a windowing function
756 Typically equal to the location of the first zero crossing. */
757 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
cristy3ed852e2009-09-05 21:47:34 +0000758 } const filters[SentinelFilter] =
759 {
anthony61b5ddd2010-10-05 02:33:31 +0000760 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
761 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
762 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
763 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
764 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
765 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
766 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
767 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000768 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000769 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
770 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
771 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
anthony853d6972010-10-08 06:01:31 +0000772 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
nicolasf8689f42010-10-18 16:14:08 +0000773 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000774 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
anthony06b1edf2010-10-25 01:19:50 +0000775 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
anthony61b5ddd2010-10-05 02:33:31 +0000776 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
777 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
778 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
anthony61b5ddd2010-10-05 02:33:31 +0000779 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
780 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
anthony152700d2010-10-28 02:43:18 +0000781 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
782 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
783 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* lanczos, Sharpened */
784 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos, 2-lobed */
785 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2, sharpened */
anthony450db502010-10-19 04:03:03 +0000786 { CubicBC, 2.0, 1.1685777620836932,
nicolas0d5e5322010-10-22 15:29:30 +0000787 0.37821575509399867, 0.31089212245300067 }
788 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
cristy3ed852e2009-09-05 21:47:34 +0000789 };
790 /*
anthony9a98fc62010-10-11 02:47:19 +0000791 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
anthony450db502010-10-19 04:03:03 +0000792 function being used as a filter. It is used by the "filter:lobes" expert
793 setting and for 'lobes' for Jinc functions in the previous table. This
794 way users do not have to deal with the highly irrational lobe sizes of the
anthony9a98fc62010-10-11 02:47:19 +0000795 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000796
nicolase473f722010-10-07 00:05:13 +0000797 Values taken from
anthony48f77622010-10-03 14:32:31 +0000798 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000799 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000800 */
801 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000802 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000803 {
nicolas8eccc162010-10-16 19:48:13 +0000804 1.2196698912665045,
805 2.2331305943815286,
806 3.2383154841662362,
807 4.2410628637960699,
808 5.2427643768701817,
809 6.2439216898644877,
810 7.244759868719957,
811 8.2453949139520427,
812 9.2458926849494673,
813 10.246293348754916,
814 11.246622794877883,
815 12.246898461138105,
816 13.247132522181061,
817 14.247333735806849,
anthonyc2d07db2010-09-15 23:47:40 +0000818 15.2475085630373,
nicolas8eccc162010-10-16 19:48:13 +0000819 16.247661874700962
cristy3ed852e2009-09-05 21:47:34 +0000820 };
821
cristy33b1c162010-01-23 22:51:51 +0000822 /*
823 Allocate resize filter.
824 */
cristy3ed852e2009-09-05 21:47:34 +0000825 assert(image != (const Image *) NULL);
826 assert(image->signature == MagickSignature);
827 if (image->debug != MagickFalse)
828 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
829 assert(UndefinedFilter < filter && filter < SentinelFilter);
830 assert(exception != (ExceptionInfo *) NULL);
831 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000832 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000833 if (resize_filter == (ResizeFilter *) NULL)
834 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000835 /*
836 Defaults for the requested filter.
837 */
838 filter_type=mapping[filter].filter;
839 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000840 resize_filter->blur = blur;
841 sigma = 0.5;
anthony152700d2010-10-28 02:43:18 +0000842 /* Promote 1D Windowed Sinc Filters to a 2D Windowed Jinc filters */
843 if (cylindrical != MagickFalse && filter_type == SincFastFilter
844 && filter != SincFastFilter )
845 filter_type=JincFilter;
anthony61b5ddd2010-10-05 02:33:31 +0000846
anthony152700d2010-10-28 02:43:18 +0000847 /* Expert filter setting override */
cristy3ed852e2009-09-05 21:47:34 +0000848 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000849 if (artifact != (const char *) NULL)
850 {
anthony152700d2010-10-28 02:43:18 +0000851 ssize_t
852 option;
cristy9af9b5d2010-08-15 17:04:28 +0000853 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000854 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000855 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000856 filter_type=(FilterTypes) option;
857 window_type=BoxFilter;
858 }
nicolas07bac812010-09-19 18:47:02 +0000859 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000860 artifact=GetImageArtifact(image,"filter:window");
861 if (artifact != (const char *) NULL)
862 {
cristy9af9b5d2010-08-15 17:04:28 +0000863 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000864 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony152700d2010-10-28 02:43:18 +0000865 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000866 }
cristy3ed852e2009-09-05 21:47:34 +0000867 }
cristy33b1c162010-01-23 22:51:51 +0000868 else
869 {
anthony48f77622010-10-03 14:32:31 +0000870 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000871 artifact=GetImageArtifact(image,"filter:window");
872 if (artifact != (const char *) NULL)
873 {
anthony152700d2010-10-28 02:43:18 +0000874 ssize_t
875 option;
cristy33b1c162010-01-23 22:51:51 +0000876 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
877 artifact);
878 if ((UndefinedFilter < option) && (option < SentinelFilter))
879 {
anthony61b5ddd2010-10-05 02:33:31 +0000880 filter_type=cylindrical != MagickFalse ?
881 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000882 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000883 }
884 }
885 }
anthony152700d2010-10-28 02:43:18 +0000886
nicolas07bac812010-09-19 18:47:02 +0000887 /* Assign the real functions to use for the filters selected. */
anthony152700d2010-10-28 02:43:18 +0000888 resize_filter->signature=MagickSignature;
cristy33b1c162010-01-23 22:51:51 +0000889 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000890 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000891 resize_filter->window=filters[window_type].function;
892 resize_filter->scale=filters[window_type].scale;
anthony61b5ddd2010-10-05 02:33:31 +0000893
anthonyf5e76ef2010-10-12 01:22:01 +0000894 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000895 if (cylindrical != MagickFalse)
896 switch (filter_type)
897 {
anthony10b8bc82010-10-02 12:48:46 +0000898 case BoxFilter:
899 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000900 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000901 break;
anthony152700d2010-10-28 02:43:18 +0000902 case LanczosFilter:
903 case LanczosSharpFilter:
904 case Lanczos2Filter:
905 case Lanczos2SharpFilter:
906 resize_filter->filter=filters[JincFilter].function;
907 resize_filter->window=filters[JincFilter].function;
908 /* lobes and window scale remain as declared */
909 break;
910 case GaussianFilter:
911 /* Cylindrical Gaussian sigma is sqrt(2)/2. */
912 sigma = (MagickRealType) (MagickSQ2/2.0);
913 break;
anthony81b8bf92010-10-02 13:54:34 +0000914 default:
915 break;
anthony10b8bc82010-10-02 12:48:46 +0000916 }
anthony152700d2010-10-28 02:43:18 +0000917 /* Global Sharpening (regardless of orthoginal/cylindrical) */
918 switch (filter_type)
919 {
920 case LanczosSharpFilter:
921 resize_filter->blur *= 0.9830391168464;
922 break;
923 case Lanczos2SharpFilter:
nicolas56d88732010-10-28 13:20:37 +0000924 resize_filter->blur *= 0.958027803631219;
anthony152700d2010-10-28 02:43:18 +0000925 break;
926 default:
927 break;
928 }
anthony61b5ddd2010-10-05 02:33:31 +0000929
anthonyf5e76ef2010-10-12 01:22:01 +0000930 /*
anthony06b1edf2010-10-25 01:19:50 +0000931 ** Other Expert Option Modifications
anthonyf5e76ef2010-10-12 01:22:01 +0000932 */
933
934 /* User Sigma Override - no support change */
935 artifact=GetImageArtifact(image,"filter:sigma");
936 if (artifact != (const char *) NULL)
937 sigma=StringToDouble(artifact);
938 /* Define coefficents for Gaussian (assumes no cubic window) */
939 if ( GaussianFilter ) {
940 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
cristy68f103e2010-10-14 01:19:07 +0000941 resize_filter->coeff[1] = (MagickRealType) (1.0/(Magick2PI*sigma*sigma)); /* unused */
anthonyf5e76ef2010-10-12 01:22:01 +0000942 }
943
944 /* Blur Override */
945 artifact=GetImageArtifact(image,"filter:blur");
946 if (artifact != (const char *) NULL)
947 resize_filter->blur=StringToDouble(artifact);
948 if (resize_filter->blur < MagickEpsilon)
949 resize_filter->blur=(MagickRealType) MagickEpsilon;
950
951 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000952 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000953 if (artifact != (const char *) NULL)
954 {
cristybb503372010-05-27 20:51:26 +0000955 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000956 lobes;
957
cristy96b16132010-08-29 17:19:52 +0000958 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000959 if (lobes < 1)
960 lobes=1;
961 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000962 }
anthony152700d2010-10-28 02:43:18 +0000963 /* Convert a Jinc function lobes value to a real support value */
anthony61b5ddd2010-10-05 02:33:31 +0000964 if (resize_filter->filter == Jinc)
965 {
966 if (resize_filter->support > 16)
967 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
968 else
969 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
970 }
971 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000972 artifact=GetImageArtifact(image,"filter:support");
973 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000974 resize_filter->support=fabs(StringToDouble(artifact));
975 /*
nicolas07bac812010-09-19 18:47:02 +0000976 Scale windowing function separatally to the support 'clipping'
977 window that calling operator is planning to actually use. (Expert
978 override)
cristy3ed852e2009-09-05 21:47:34 +0000979 */
anthony55f12332010-09-10 01:13:02 +0000980 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000981 artifact=GetImageArtifact(image,"filter:win-support");
982 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000983 resize_filter->window_support=fabs(StringToDouble(artifact));
984 /*
anthony1f90a6b2010-09-14 08:56:31 +0000985 Adjust window function scaling to the windowing support for
986 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000987 */
988 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +0000989
anthony55f12332010-09-10 01:13:02 +0000990 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000991 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000992 */
cristy3ed852e2009-09-05 21:47:34 +0000993 B=0.0;
994 C=0.0;
cristy33b1c162010-01-23 22:51:51 +0000995 if ((filters[filter_type].function == CubicBC) ||
996 (filters[window_type].function == CubicBC))
997 {
anthony2d9b8b52010-09-14 08:31:07 +0000998 B=filters[filter_type].B;
999 C=filters[filter_type].C;
1000 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +00001001 {
anthony2d9b8b52010-09-14 08:31:07 +00001002 B=filters[window_type].B;
1003 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +00001004 }
cristy33b1c162010-01-23 22:51:51 +00001005 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001006 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001007 {
1008 B=StringToDouble(artifact);
nicolasd15ee9a2010-10-24 18:39:45 +00001009 C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001010 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001011 if (artifact != (const char *) NULL)
1012 C=StringToDouble(artifact);
1013 }
1014 else
1015 {
1016 artifact=GetImageArtifact(image,"filter:c");
1017 if (artifact != (const char *) NULL)
1018 {
1019 C=StringToDouble(artifact);
nicolasb7dff642010-10-25 02:04:14 +00001020 B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001021 }
1022 }
nicolasc6bac3b2010-10-24 18:10:45 +00001023 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
nicolasd15ee9a2010-10-24 18:39:45 +00001024 {
anthony06b1edf2010-10-25 01:19:50 +00001025 const double twoB = B+B;
1026 resize_filter->coeff[0]=1.0-(1.0/3.0)*B;
1027 resize_filter->coeff[1]=-3.0+twoB+C;
1028 resize_filter->coeff[2]=2.0-1.5*B-C;
1029 resize_filter->coeff[3]=(4.0/3.0)*B+4.0*C;
1030 resize_filter->coeff[4]=-8.0*C-twoB;
1031 resize_filter->coeff[5]=B+5.0*C;
1032 resize_filter->coeff[6]=(-1.0/6.0)*B-C;
nicolasd15ee9a2010-10-24 18:39:45 +00001033 }
nicolasc6bac3b2010-10-24 18:10:45 +00001034 }
anthonyf5e76ef2010-10-12 01:22:01 +00001035
anthony55f12332010-09-10 01:13:02 +00001036 /*
nicolas07bac812010-09-19 18:47:02 +00001037 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001038 */
cristyf5b49372010-10-16 01:06:47 +00001039#if defined(MAGICKCORE_OPENMP_SUPPORT)
1040 #pragma omp master
1041 {
1042#endif
1043 artifact=GetImageArtifact(image,"filter:verbose");
anthony28ad1d72010-10-26 06:30:24 +00001044 if (IsMagickTrue(artifact))
cristyf5b49372010-10-16 01:06:47 +00001045 {
1046 double
anthony06b1edf2010-10-25 01:19:50 +00001047 support,
cristyf5b49372010-10-16 01:06:47 +00001048 x;
cristy3ed852e2009-09-05 21:47:34 +00001049
cristyf5b49372010-10-16 01:06:47 +00001050 /*
1051 Set the weighting function properly when the weighting
1052 function may not exactly match the filter of the same name.
anthony06b1edf2010-10-25 01:19:50 +00001053 EG: a Point filter is really uses a Box weighting function
cristyf5b49372010-10-16 01:06:47 +00001054 with a different support than is typically used.
cristyf5b49372010-10-16 01:06:47 +00001055 */
1056 if (resize_filter->filter == Box) filter_type=BoxFilter;
1057 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1058 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1059 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1060 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
anthony152700d2010-10-28 02:43:18 +00001061 if (resize_filter->filter == Box) window_type=BoxFilter;
1062 if (resize_filter->window == Sinc) window_type=SincFilter;
1063 if (resize_filter->window == SincFast) window_type=SincFastFilter;
1064 if (resize_filter->filter == Jinc) window_type=JincFilter;
1065 if (resize_filter->window == CubicBC) window_type=CubicFilter;
cristyf5b49372010-10-16 01:06:47 +00001066 /*
1067 Report Filter Details.
1068 */
1069 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1070 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
anthony06b1edf2010-10-25 01:19:50 +00001071 (void) fprintf(stdout,"# filter = %s\n",
1072 MagickOptionToMnemonic(MagickFilterOptions,filter_type));
1073 (void) fprintf(stdout,"# window = %s\n",
1074 MagickOptionToMnemonic(MagickFilterOptions, window_type));
1075 (void) fprintf(stdout,"# support = %.*g\n",
1076 GetMagickPrecision(),(double) resize_filter->support);
1077 (void) fprintf(stdout,"# win-support = %.*g\n",
1078 GetMagickPrecision(),(double) resize_filter->window_support);
1079 (void) fprintf(stdout,"# scale_blur = %.*g\n",
1080 GetMagickPrecision(), (double)resize_filter->blur);
cristyf5b49372010-10-16 01:06:47 +00001081 if ( filter_type == GaussianFilter )
anthony06b1edf2010-10-25 01:19:50 +00001082 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",
1083 GetMagickPrecision(), (double)sigma);
1084 (void) fprintf(stdout,"# practical_support = %.*g\n",
1085 GetMagickPrecision(), (double)support);
cristyf5b49372010-10-16 01:06:47 +00001086 if ( filter_type == CubicFilter || window_type == CubicFilter )
anthony06b1edf2010-10-25 01:19:50 +00001087 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",
1088 GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
cristyf5b49372010-10-16 01:06:47 +00001089 (void) fprintf(stdout,"\n");
1090 /*
1091 Output values of resulting filter graph -- for graphing
1092 filter result.
1093 */
1094 for (x=0.0; x <= support; x+=0.01f)
1095 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1096 (double) GetResizeFilterWeight(resize_filter,x));
1097 /* A final value so gnuplot can graph the 'stop' properly. */
1098 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1099 0.0);
1100 }
1101 /* Output the above once only for each image - remove setting */
1102 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1103#if defined(MAGICKCORE_OPENMP_SUPPORT)
1104 }
1105#endif
cristy3ed852e2009-09-05 21:47:34 +00001106 return(resize_filter);
1107}
1108
1109/*
1110%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1111% %
1112% %
1113% %
1114% A d a p t i v e R e s i z e I m a g e %
1115% %
1116% %
1117% %
1118%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1119%
1120% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1121%
1122% The format of the AdaptiveResizeImage method is:
1123%
cristy9af9b5d2010-08-15 17:04:28 +00001124% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1125% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001126%
1127% A description of each parameter follows:
1128%
1129% o image: the image.
1130%
1131% o columns: the number of columns in the resized image.
1132%
1133% o rows: the number of rows in the resized image.
1134%
1135% o exception: return any errors or warnings in this structure.
1136%
1137*/
1138MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001139 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001140{
1141#define AdaptiveResizeImageTag "Resize/Image"
1142
cristyc4c8d132010-01-07 01:58:38 +00001143 CacheView
1144 *resize_view;
1145
cristy3ed852e2009-09-05 21:47:34 +00001146 Image
1147 *resize_image;
1148
cristy3ed852e2009-09-05 21:47:34 +00001149 MagickBooleanType
1150 proceed;
1151
1152 MagickPixelPacket
1153 pixel;
1154
1155 PointInfo
1156 offset;
1157
1158 ResampleFilter
1159 *resample_filter;
1160
cristy9af9b5d2010-08-15 17:04:28 +00001161 ssize_t
1162 y;
1163
cristy3ed852e2009-09-05 21:47:34 +00001164 /*
1165 Adaptively resize image.
1166 */
1167 assert(image != (const Image *) NULL);
1168 assert(image->signature == MagickSignature);
1169 if (image->debug != MagickFalse)
1170 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1171 assert(exception != (ExceptionInfo *) NULL);
1172 assert(exception->signature == MagickSignature);
1173 if ((columns == 0) || (rows == 0))
1174 return((Image *) NULL);
1175 if ((columns == image->columns) && (rows == image->rows))
1176 return(CloneImage(image,0,0,MagickTrue,exception));
1177 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1178 if (resize_image == (Image *) NULL)
1179 return((Image *) NULL);
1180 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1181 {
1182 InheritException(exception,&resize_image->exception);
1183 resize_image=DestroyImage(resize_image);
1184 return((Image *) NULL);
1185 }
1186 GetMagickPixelPacket(image,&pixel);
1187 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001188 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001189 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001190 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001191 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001192 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001193 {
1194 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001195 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001196
cristybb503372010-05-27 20:51:26 +00001197 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001198 x;
1199
1200 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001201 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001202
1203 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1204 exception);
1205 if (q == (PixelPacket *) NULL)
1206 break;
1207 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1208 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001209 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001210 {
1211 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1212 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1213 &pixel);
1214 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1215 q++;
1216 }
1217 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1218 break;
cristy96b16132010-08-29 17:19:52 +00001219 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1220 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001221 if (proceed == MagickFalse)
1222 break;
1223 }
1224 resample_filter=DestroyResampleFilter(resample_filter);
1225 resize_view=DestroyCacheView(resize_view);
1226 return(resize_image);
1227}
1228
1229/*
1230%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1231% %
1232% %
1233% %
1234+ B e s s e l O r d e r O n e %
1235% %
1236% %
1237% %
1238%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1239%
1240% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001241% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001242%
1243% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1244%
1245% j1(x) = x*j1(x);
1246%
1247% For x in (8,inf)
1248%
1249% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1250%
1251% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1252%
1253% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1254% = 1/sqrt(2) * (sin(x) - cos(x))
1255% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1256% = -1/sqrt(2) * (sin(x) + cos(x))
1257%
1258% The format of the BesselOrderOne method is:
1259%
1260% MagickRealType BesselOrderOne(MagickRealType x)
1261%
1262% A description of each parameter follows:
1263%
1264% o x: MagickRealType value.
1265%
1266*/
1267
1268#undef I0
1269static MagickRealType I0(MagickRealType x)
1270{
1271 MagickRealType
1272 sum,
1273 t,
1274 y;
1275
cristybb503372010-05-27 20:51:26 +00001276 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001277 i;
1278
1279 /*
1280 Zeroth order Bessel function of the first kind.
1281 */
1282 sum=1.0;
1283 y=x*x/4.0;
1284 t=y;
1285 for (i=2; t > MagickEpsilon; i++)
1286 {
1287 sum+=t;
1288 t*=y/((MagickRealType) i*i);
1289 }
1290 return(sum);
1291}
1292
1293#undef J1
1294static MagickRealType J1(MagickRealType x)
1295{
1296 MagickRealType
1297 p,
1298 q;
1299
cristybb503372010-05-27 20:51:26 +00001300 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001301 i;
1302
1303 static const double
1304 Pone[] =
1305 {
1306 0.581199354001606143928050809e+21,
1307 -0.6672106568924916298020941484e+20,
1308 0.2316433580634002297931815435e+19,
1309 -0.3588817569910106050743641413e+17,
1310 0.2908795263834775409737601689e+15,
1311 -0.1322983480332126453125473247e+13,
1312 0.3413234182301700539091292655e+10,
1313 -0.4695753530642995859767162166e+7,
1314 0.270112271089232341485679099e+4
1315 },
1316 Qone[] =
1317 {
1318 0.11623987080032122878585294e+22,
1319 0.1185770712190320999837113348e+20,
1320 0.6092061398917521746105196863e+17,
1321 0.2081661221307607351240184229e+15,
1322 0.5243710262167649715406728642e+12,
1323 0.1013863514358673989967045588e+10,
1324 0.1501793594998585505921097578e+7,
1325 0.1606931573481487801970916749e+4,
1326 0.1e+1
1327 };
1328
1329 p=Pone[8];
1330 q=Qone[8];
1331 for (i=7; i >= 0; i--)
1332 {
1333 p=p*x*x+Pone[i];
1334 q=q*x*x+Qone[i];
1335 }
1336 return(p/q);
1337}
1338
1339#undef P1
1340static MagickRealType P1(MagickRealType x)
1341{
1342 MagickRealType
1343 p,
1344 q;
1345
cristybb503372010-05-27 20:51:26 +00001346 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001347 i;
1348
1349 static const double
1350 Pone[] =
1351 {
1352 0.352246649133679798341724373e+5,
1353 0.62758845247161281269005675e+5,
1354 0.313539631109159574238669888e+5,
1355 0.49854832060594338434500455e+4,
1356 0.2111529182853962382105718e+3,
1357 0.12571716929145341558495e+1
1358 },
1359 Qone[] =
1360 {
1361 0.352246649133679798068390431e+5,
1362 0.626943469593560511888833731e+5,
1363 0.312404063819041039923015703e+5,
1364 0.4930396490181088979386097e+4,
1365 0.2030775189134759322293574e+3,
1366 0.1e+1
1367 };
1368
1369 p=Pone[5];
1370 q=Qone[5];
1371 for (i=4; i >= 0; i--)
1372 {
1373 p=p*(8.0/x)*(8.0/x)+Pone[i];
1374 q=q*(8.0/x)*(8.0/x)+Qone[i];
1375 }
1376 return(p/q);
1377}
1378
1379#undef Q1
1380static MagickRealType Q1(MagickRealType x)
1381{
1382 MagickRealType
1383 p,
1384 q;
1385
cristybb503372010-05-27 20:51:26 +00001386 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001387 i;
1388
1389 static const double
1390 Pone[] =
1391 {
1392 0.3511751914303552822533318e+3,
1393 0.7210391804904475039280863e+3,
1394 0.4259873011654442389886993e+3,
1395 0.831898957673850827325226e+2,
1396 0.45681716295512267064405e+1,
1397 0.3532840052740123642735e-1
1398 },
1399 Qone[] =
1400 {
1401 0.74917374171809127714519505e+4,
1402 0.154141773392650970499848051e+5,
1403 0.91522317015169922705904727e+4,
1404 0.18111867005523513506724158e+4,
1405 0.1038187585462133728776636e+3,
1406 0.1e+1
1407 };
1408
1409 p=Pone[5];
1410 q=Qone[5];
1411 for (i=4; i >= 0; i--)
1412 {
1413 p=p*(8.0/x)*(8.0/x)+Pone[i];
1414 q=q*(8.0/x)*(8.0/x)+Qone[i];
1415 }
1416 return(p/q);
1417}
1418
1419static MagickRealType BesselOrderOne(MagickRealType x)
1420{
1421 MagickRealType
1422 p,
1423 q;
1424
1425 if (x == 0.0)
1426 return(0.0);
1427 p=x;
1428 if (x < 0.0)
1429 x=(-x);
1430 if (x < 8.0)
1431 return(p*J1(x));
1432 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1433 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1434 cos((double) x))));
1435 if (p < 0.0)
1436 q=(-q);
1437 return(q);
1438}
1439
1440/*
1441%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1442% %
1443% %
1444% %
1445+ D e s t r o y R e s i z e F i l t e r %
1446% %
1447% %
1448% %
1449%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1450%
1451% DestroyResizeFilter() destroy the resize filter.
1452%
cristya2ffd7e2010-03-10 20:50:30 +00001453% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001454%
1455% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1456%
1457% A description of each parameter follows:
1458%
1459% o resize_filter: the resize filter.
1460%
1461*/
1462MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1463{
1464 assert(resize_filter != (ResizeFilter *) NULL);
1465 assert(resize_filter->signature == MagickSignature);
1466 resize_filter->signature=(~MagickSignature);
1467 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1468 return(resize_filter);
1469}
1470
1471/*
1472%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1473% %
1474% %
1475% %
1476+ G e t R e s i z e F i l t e r S u p p o r t %
1477% %
1478% %
1479% %
1480%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1481%
1482% GetResizeFilterSupport() return the current support window size for this
1483% filter. Note that this may have been enlarged by filter:blur factor.
1484%
1485% The format of the GetResizeFilterSupport method is:
1486%
1487% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1488%
1489% A description of each parameter follows:
1490%
1491% o filter: Image filter to use.
1492%
1493*/
1494MagickExport MagickRealType GetResizeFilterSupport(
1495 const ResizeFilter *resize_filter)
1496{
1497 assert(resize_filter != (ResizeFilter *) NULL);
1498 assert(resize_filter->signature == MagickSignature);
1499 return(resize_filter->support*resize_filter->blur);
1500}
1501
1502/*
1503%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1504% %
1505% %
1506% %
1507+ G e t R e s i z e F i l t e r W e i g h t %
1508% %
1509% %
1510% %
1511%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1512%
1513% GetResizeFilterWeight evaluates the specified resize filter at the point x
1514% which usally lies between zero and the filters current 'support' and
1515% returns the weight of the filter function at that point.
1516%
1517% The format of the GetResizeFilterWeight method is:
1518%
1519% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1520% const MagickRealType x)
1521%
1522% A description of each parameter follows:
1523%
1524% o filter: the filter type.
1525%
1526% o x: the point.
1527%
1528*/
1529MagickExport MagickRealType GetResizeFilterWeight(
1530 const ResizeFilter *resize_filter,const MagickRealType x)
1531{
1532 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001533 scale,
1534 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001535
1536 /*
1537 Windowing function - scale the weighting filter by this amount.
1538 */
1539 assert(resize_filter != (ResizeFilter *) NULL);
1540 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001541 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001542 if ((resize_filter->window_support < MagickEpsilon) ||
1543 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001544 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001545 else
1546 {
anthony55f12332010-09-10 01:13:02 +00001547 scale=resize_filter->scale;
1548 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001549 }
anthony55f12332010-09-10 01:13:02 +00001550 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001551}
1552
1553/*
1554%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1555% %
1556% %
1557% %
1558% M a g n i f y I m a g e %
1559% %
1560% %
1561% %
1562%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1563%
1564% MagnifyImage() is a convenience method that scales an image proportionally
1565% to twice its size.
1566%
1567% The format of the MagnifyImage method is:
1568%
1569% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1570%
1571% A description of each parameter follows:
1572%
1573% o image: the image.
1574%
1575% o exception: return any errors or warnings in this structure.
1576%
1577*/
1578MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1579{
1580 Image
1581 *magnify_image;
1582
1583 assert(image != (Image *) NULL);
1584 assert(image->signature == MagickSignature);
1585 if (image->debug != MagickFalse)
1586 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1587 assert(exception != (ExceptionInfo *) NULL);
1588 assert(exception->signature == MagickSignature);
1589 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1590 1.0,exception);
1591 return(magnify_image);
1592}
1593
1594/*
1595%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1596% %
1597% %
1598% %
1599% M i n i f y I m a g e %
1600% %
1601% %
1602% %
1603%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1604%
1605% MinifyImage() is a convenience method that scales an image proportionally
1606% to half its size.
1607%
1608% The format of the MinifyImage method is:
1609%
1610% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1611%
1612% A description of each parameter follows:
1613%
1614% o image: the image.
1615%
1616% o exception: return any errors or warnings in this structure.
1617%
1618*/
1619MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1620{
1621 Image
1622 *minify_image;
1623
1624 assert(image != (Image *) NULL);
1625 assert(image->signature == MagickSignature);
1626 if (image->debug != MagickFalse)
1627 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1628 assert(exception != (ExceptionInfo *) NULL);
1629 assert(exception->signature == MagickSignature);
1630 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1631 1.0,exception);
1632 return(minify_image);
1633}
1634
1635/*
1636%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1637% %
1638% %
1639% %
1640% R e s a m p l e I m a g e %
1641% %
1642% %
1643% %
1644%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1645%
1646% ResampleImage() resize image in terms of its pixel size, so that when
1647% displayed at the given resolution it will be the same size in terms of
1648% real world units as the original image at the original resolution.
1649%
1650% The format of the ResampleImage method is:
1651%
1652% Image *ResampleImage(Image *image,const double x_resolution,
1653% const double y_resolution,const FilterTypes filter,const double blur,
1654% ExceptionInfo *exception)
1655%
1656% A description of each parameter follows:
1657%
1658% o image: the image to be resized to fit the given resolution.
1659%
1660% o x_resolution: the new image x resolution.
1661%
1662% o y_resolution: the new image y resolution.
1663%
1664% o filter: Image filter to use.
1665%
1666% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1667%
1668*/
1669MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1670 const double y_resolution,const FilterTypes filter,const double blur,
1671 ExceptionInfo *exception)
1672{
1673#define ResampleImageTag "Resample/Image"
1674
1675 Image
1676 *resample_image;
1677
cristybb503372010-05-27 20:51:26 +00001678 size_t
cristy3ed852e2009-09-05 21:47:34 +00001679 height,
1680 width;
1681
1682 /*
1683 Initialize sampled image attributes.
1684 */
1685 assert(image != (const Image *) NULL);
1686 assert(image->signature == MagickSignature);
1687 if (image->debug != MagickFalse)
1688 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1689 assert(exception != (ExceptionInfo *) NULL);
1690 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001691 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1692 72.0 : image->x_resolution)+0.5);
1693 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1694 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001695 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1696 if (resample_image != (Image *) NULL)
1697 {
1698 resample_image->x_resolution=x_resolution;
1699 resample_image->y_resolution=y_resolution;
1700 }
1701 return(resample_image);
1702}
1703#if defined(MAGICKCORE_LQR_DELEGATE)
1704
1705/*
1706%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1707% %
1708% %
1709% %
1710% L i q u i d R e s c a l e I m a g e %
1711% %
1712% %
1713% %
1714%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1715%
1716% LiquidRescaleImage() rescales image with seam carving.
1717%
1718% The format of the LiquidRescaleImage method is:
1719%
1720% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001721% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001722% const double delta_x,const double rigidity,ExceptionInfo *exception)
1723%
1724% A description of each parameter follows:
1725%
1726% o image: the image.
1727%
1728% o columns: the number of columns in the rescaled image.
1729%
1730% o rows: the number of rows in the rescaled image.
1731%
1732% o delta_x: maximum seam transversal step (0 means straight seams).
1733%
1734% o rigidity: introduce a bias for non-straight seams (typically 0).
1735%
1736% o exception: return any errors or warnings in this structure.
1737%
1738*/
cristy9af9b5d2010-08-15 17:04:28 +00001739MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1740 const size_t rows,const double delta_x,const double rigidity,
1741 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001742{
1743#define LiquidRescaleImageTag "Rescale/Image"
1744
cristyc5c6f662010-09-22 14:23:02 +00001745 CacheView
1746 *rescale_view;
1747
cristy3ed852e2009-09-05 21:47:34 +00001748 const char
1749 *map;
1750
1751 guchar
1752 *packet;
1753
1754 Image
1755 *rescale_image;
1756
1757 int
1758 x,
1759 y;
1760
1761 LqrCarver
1762 *carver;
1763
1764 LqrRetVal
1765 lqr_status;
1766
1767 MagickBooleanType
1768 status;
1769
1770 MagickPixelPacket
1771 pixel;
1772
1773 unsigned char
1774 *pixels;
1775
1776 /*
1777 Liquid rescale image.
1778 */
1779 assert(image != (const Image *) NULL);
1780 assert(image->signature == MagickSignature);
1781 if (image->debug != MagickFalse)
1782 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1783 assert(exception != (ExceptionInfo *) NULL);
1784 assert(exception->signature == MagickSignature);
1785 if ((columns == 0) || (rows == 0))
1786 return((Image *) NULL);
1787 if ((columns == image->columns) && (rows == image->rows))
1788 return(CloneImage(image,0,0,MagickTrue,exception));
1789 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001790 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001791 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1792 {
1793 Image
1794 *resize_image;
1795
cristybb503372010-05-27 20:51:26 +00001796 size_t
cristy3ed852e2009-09-05 21:47:34 +00001797 height,
1798 width;
1799
1800 /*
1801 Honor liquid resize size limitations.
1802 */
1803 for (width=image->columns; columns >= (2*width-1); width*=2);
1804 for (height=image->rows; rows >= (2*height-1); height*=2);
1805 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1806 exception);
1807 if (resize_image == (Image *) NULL)
1808 return((Image *) NULL);
1809 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1810 rigidity,exception);
1811 resize_image=DestroyImage(resize_image);
1812 return(rescale_image);
1813 }
1814 map="RGB";
1815 if (image->matte == MagickFalse)
1816 map="RGBA";
1817 if (image->colorspace == CMYKColorspace)
1818 {
1819 map="CMYK";
1820 if (image->matte == MagickFalse)
1821 map="CMYKA";
1822 }
1823 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1824 strlen(map)*sizeof(*pixels));
1825 if (pixels == (unsigned char *) NULL)
1826 return((Image *) NULL);
1827 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1828 pixels,exception);
1829 if (status == MagickFalse)
1830 {
1831 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1832 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1833 }
1834 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1835 if (carver == (LqrCarver *) NULL)
1836 {
1837 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1838 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1839 }
1840 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1841 lqr_status=lqr_carver_resize(carver,columns,rows);
1842 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1843 lqr_carver_get_height(carver),MagickTrue,exception);
1844 if (rescale_image == (Image *) NULL)
1845 {
1846 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1847 return((Image *) NULL);
1848 }
1849 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1850 {
1851 InheritException(exception,&rescale_image->exception);
1852 rescale_image=DestroyImage(rescale_image);
1853 return((Image *) NULL);
1854 }
1855 GetMagickPixelPacket(rescale_image,&pixel);
1856 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001857 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001858 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1859 {
1860 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001861 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001862
1863 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001864 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001865
anthony22aad252010-09-23 06:59:07 +00001866 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001867 if (q == (PixelPacket *) NULL)
1868 break;
cristyc5c6f662010-09-22 14:23:02 +00001869 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001870 pixel.red=QuantumRange*(packet[0]/255.0);
1871 pixel.green=QuantumRange*(packet[1]/255.0);
1872 pixel.blue=QuantumRange*(packet[2]/255.0);
1873 if (image->colorspace != CMYKColorspace)
1874 {
1875 if (image->matte == MagickFalse)
1876 pixel.opacity=QuantumRange*(packet[3]/255.0);
1877 }
1878 else
1879 {
1880 pixel.index=QuantumRange*(packet[3]/255.0);
1881 if (image->matte == MagickFalse)
1882 pixel.opacity=QuantumRange*(packet[4]/255.0);
1883 }
1884 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001885 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001886 break;
1887 }
cristyc5c6f662010-09-22 14:23:02 +00001888 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001889 /*
1890 Relinquish resources.
1891 */
1892 lqr_carver_destroy(carver);
1893 return(rescale_image);
1894}
1895#else
1896MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001897 const size_t magick_unused(columns),const size_t magick_unused(rows),
1898 const double magick_unused(delta_x),const double magick_unused(rigidity),
1899 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001900{
1901 assert(image != (const Image *) NULL);
1902 assert(image->signature == MagickSignature);
1903 if (image->debug != MagickFalse)
1904 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1905 assert(exception != (ExceptionInfo *) NULL);
1906 assert(exception->signature == MagickSignature);
1907 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1908 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1909 return((Image *) NULL);
1910}
1911#endif
1912
1913/*
1914%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1915% %
1916% %
1917% %
1918% R e s i z e I m a g e %
1919% %
1920% %
1921% %
1922%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1923%
1924% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001925% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001926%
1927% If an undefined filter is given the filter defaults to Mitchell for a
1928% colormapped image, a image with a matte channel, or if the image is
1929% enlarged. Otherwise the filter defaults to a Lanczos.
1930%
1931% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1932%
1933% The format of the ResizeImage method is:
1934%
cristybb503372010-05-27 20:51:26 +00001935% Image *ResizeImage(Image *image,const size_t columns,
1936% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001937% ExceptionInfo *exception)
1938%
1939% A description of each parameter follows:
1940%
1941% o image: the image.
1942%
1943% o columns: the number of columns in the scaled image.
1944%
1945% o rows: the number of rows in the scaled image.
1946%
1947% o filter: Image filter to use.
1948%
cristy9af9b5d2010-08-15 17:04:28 +00001949% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1950% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001951%
1952% o exception: return any errors or warnings in this structure.
1953%
1954*/
1955
1956typedef struct _ContributionInfo
1957{
1958 MagickRealType
1959 weight;
1960
cristybb503372010-05-27 20:51:26 +00001961 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001962 pixel;
1963} ContributionInfo;
1964
1965static ContributionInfo **DestroyContributionThreadSet(
1966 ContributionInfo **contribution)
1967{
cristybb503372010-05-27 20:51:26 +00001968 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001969 i;
1970
1971 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001972 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001973 if (contribution[i] != (ContributionInfo *) NULL)
1974 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1975 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001976 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001977 return(contribution);
1978}
1979
1980static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1981{
cristybb503372010-05-27 20:51:26 +00001982 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001983 i;
1984
1985 ContributionInfo
1986 **contribution;
1987
cristybb503372010-05-27 20:51:26 +00001988 size_t
cristy3ed852e2009-09-05 21:47:34 +00001989 number_threads;
1990
1991 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001992 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001993 sizeof(*contribution));
1994 if (contribution == (ContributionInfo **) NULL)
1995 return((ContributionInfo **) NULL);
1996 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00001997 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00001998 {
1999 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2000 sizeof(**contribution));
2001 if (contribution[i] == (ContributionInfo *) NULL)
2002 return(DestroyContributionThreadSet(contribution));
2003 }
2004 return(contribution);
2005}
2006
2007static inline double MagickMax(const double x,const double y)
2008{
2009 if (x > y)
2010 return(x);
2011 return(y);
2012}
2013
2014static inline double MagickMin(const double x,const double y)
2015{
2016 if (x < y)
2017 return(x);
2018 return(y);
2019}
2020
2021static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2022 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002023 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002024{
2025#define ResizeImageTag "Resize/Image"
2026
cristyfa112112010-01-04 17:48:07 +00002027 CacheView
2028 *image_view,
2029 *resize_view;
2030
cristy3ed852e2009-09-05 21:47:34 +00002031 ClassType
2032 storage_class;
2033
2034 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002035 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002036
cristy3ed852e2009-09-05 21:47:34 +00002037 MagickBooleanType
2038 status;
2039
2040 MagickPixelPacket
2041 zero;
2042
2043 MagickRealType
2044 scale,
2045 support;
2046
cristy9af9b5d2010-08-15 17:04:28 +00002047 ssize_t
2048 x;
2049
cristy3ed852e2009-09-05 21:47:34 +00002050 /*
2051 Apply filter to resize horizontally from image to resize image.
2052 */
cristy5d824382010-09-06 14:00:17 +00002053 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002054 support=scale*GetResizeFilterSupport(resize_filter);
2055 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2056 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2057 {
2058 InheritException(exception,&resize_image->exception);
2059 return(MagickFalse);
2060 }
2061 if (support < 0.5)
2062 {
2063 /*
nicolas07bac812010-09-19 18:47:02 +00002064 Support too small even for nearest neighbour: Reduce to point
2065 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002066 */
2067 support=(MagickRealType) 0.5;
2068 scale=1.0;
2069 }
2070 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2071 if (contributions == (ContributionInfo **) NULL)
2072 {
2073 (void) ThrowMagickException(exception,GetMagickModule(),
2074 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2075 return(MagickFalse);
2076 }
2077 status=MagickTrue;
2078 scale=1.0/scale;
2079 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2080 image_view=AcquireCacheView(image);
2081 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002082#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002083 #pragma omp parallel for shared(status)
2084#endif
cristybb503372010-05-27 20:51:26 +00002085 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002086 {
cristy3ed852e2009-09-05 21:47:34 +00002087 MagickRealType
2088 center,
2089 density;
2090
2091 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002092 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002093
2094 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002095 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002096
cristy03dbbd22010-09-19 23:04:47 +00002097 register ContributionInfo
2098 *restrict contribution;
2099
cristy3ed852e2009-09-05 21:47:34 +00002100 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002101 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002102
cristy3ed852e2009-09-05 21:47:34 +00002103 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002104 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002105
cristy03dbbd22010-09-19 23:04:47 +00002106 register ssize_t
2107 y;
2108
cristy9af9b5d2010-08-15 17:04:28 +00002109 ssize_t
2110 n,
2111 start,
2112 stop;
2113
cristy3ed852e2009-09-05 21:47:34 +00002114 if (status == MagickFalse)
2115 continue;
2116 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002117 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2118 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002119 density=0.0;
2120 contribution=contributions[GetOpenMPThreadId()];
2121 for (n=0; n < (stop-start); n++)
2122 {
2123 contribution[n].pixel=start+n;
2124 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2125 ((MagickRealType) (start+n)-center+0.5));
2126 density+=contribution[n].weight;
2127 }
2128 if ((density != 0.0) && (density != 1.0))
2129 {
cristybb503372010-05-27 20:51:26 +00002130 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002131 i;
2132
2133 /*
2134 Normalize.
2135 */
2136 density=1.0/density;
2137 for (i=0; i < n; i++)
2138 contribution[i].weight*=density;
2139 }
cristy9af9b5d2010-08-15 17:04:28 +00002140 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2141 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002142 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2143 exception);
2144 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2145 {
2146 status=MagickFalse;
2147 continue;
2148 }
2149 indexes=GetCacheViewVirtualIndexQueue(image_view);
2150 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002151 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002152 {
cristy3ed852e2009-09-05 21:47:34 +00002153 MagickPixelPacket
2154 pixel;
2155
2156 MagickRealType
2157 alpha;
2158
cristybb503372010-05-27 20:51:26 +00002159 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002160 i;
2161
cristy9af9b5d2010-08-15 17:04:28 +00002162 ssize_t
2163 j;
2164
cristy3ed852e2009-09-05 21:47:34 +00002165 pixel=zero;
2166 if (image->matte == MagickFalse)
2167 {
2168 for (i=0; i < n; i++)
2169 {
2170 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2171 (contribution[i].pixel-contribution[0].pixel);
2172 alpha=contribution[i].weight;
2173 pixel.red+=alpha*(p+j)->red;
2174 pixel.green+=alpha*(p+j)->green;
2175 pixel.blue+=alpha*(p+j)->blue;
2176 pixel.opacity+=alpha*(p+j)->opacity;
2177 }
cristyce70c172010-01-07 17:15:30 +00002178 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2179 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2180 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2181 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002182 if ((image->colorspace == CMYKColorspace) &&
2183 (resize_image->colorspace == CMYKColorspace))
2184 {
2185 for (i=0; i < n; i++)
2186 {
2187 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2188 (contribution[i].pixel-contribution[0].pixel);
2189 alpha=contribution[i].weight;
2190 pixel.index+=alpha*indexes[j];
2191 }
cristyce70c172010-01-07 17:15:30 +00002192 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002193 }
2194 }
2195 else
2196 {
2197 MagickRealType
2198 gamma;
2199
2200 gamma=0.0;
2201 for (i=0; i < n; i++)
2202 {
2203 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2204 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002205 alpha=contribution[i].weight*QuantumScale*
2206 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002207 pixel.red+=alpha*(p+j)->red;
2208 pixel.green+=alpha*(p+j)->green;
2209 pixel.blue+=alpha*(p+j)->blue;
2210 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2211 gamma+=alpha;
2212 }
2213 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002214 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2215 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2216 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2217 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002218 if ((image->colorspace == CMYKColorspace) &&
2219 (resize_image->colorspace == CMYKColorspace))
2220 {
2221 for (i=0; i < n; i++)
2222 {
2223 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2224 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002225 alpha=contribution[i].weight*QuantumScale*
2226 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002227 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002228 }
cristyce70c172010-01-07 17:15:30 +00002229 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2230 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002231 }
2232 }
2233 if ((resize_image->storage_class == PseudoClass) &&
2234 (image->storage_class == PseudoClass))
2235 {
cristybb503372010-05-27 20:51:26 +00002236 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002237 1.0)+0.5);
2238 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2239 (contribution[i-start].pixel-contribution[0].pixel);
2240 resize_indexes[y]=indexes[j];
2241 }
2242 q++;
2243 }
2244 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2245 status=MagickFalse;
2246 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2247 {
2248 MagickBooleanType
2249 proceed;
2250
cristyb5d5f722009-11-04 03:03:49 +00002251#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002252 #pragma omp critical (MagickCore_HorizontalFilter)
2253#endif
cristy9af9b5d2010-08-15 17:04:28 +00002254 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002255 if (proceed == MagickFalse)
2256 status=MagickFalse;
2257 }
2258 }
2259 resize_view=DestroyCacheView(resize_view);
2260 image_view=DestroyCacheView(image_view);
2261 contributions=DestroyContributionThreadSet(contributions);
2262 return(status);
2263}
2264
2265static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2266 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002267 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002268{
cristyfa112112010-01-04 17:48:07 +00002269 CacheView
2270 *image_view,
2271 *resize_view;
2272
cristy3ed852e2009-09-05 21:47:34 +00002273 ClassType
2274 storage_class;
2275
2276 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002277 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002278
cristy3ed852e2009-09-05 21:47:34 +00002279 MagickBooleanType
2280 status;
2281
2282 MagickPixelPacket
2283 zero;
2284
2285 MagickRealType
2286 scale,
2287 support;
2288
cristy9af9b5d2010-08-15 17:04:28 +00002289 ssize_t
2290 y;
2291
cristy3ed852e2009-09-05 21:47:34 +00002292 /*
cristy9af9b5d2010-08-15 17:04:28 +00002293 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002294 */
cristy5d824382010-09-06 14:00:17 +00002295 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002296 support=scale*GetResizeFilterSupport(resize_filter);
2297 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2298 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2299 {
2300 InheritException(exception,&resize_image->exception);
2301 return(MagickFalse);
2302 }
2303 if (support < 0.5)
2304 {
2305 /*
nicolas07bac812010-09-19 18:47:02 +00002306 Support too small even for nearest neighbour: Reduce to point
2307 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002308 */
2309 support=(MagickRealType) 0.5;
2310 scale=1.0;
2311 }
2312 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2313 if (contributions == (ContributionInfo **) NULL)
2314 {
2315 (void) ThrowMagickException(exception,GetMagickModule(),
2316 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2317 return(MagickFalse);
2318 }
2319 status=MagickTrue;
2320 scale=1.0/scale;
2321 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2322 image_view=AcquireCacheView(image);
2323 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002324#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002325 #pragma omp parallel for shared(status)
2326#endif
cristybb503372010-05-27 20:51:26 +00002327 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002328 {
cristy3ed852e2009-09-05 21:47:34 +00002329 MagickRealType
2330 center,
2331 density;
2332
2333 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002334 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002335
2336 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002337 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002338
cristy03dbbd22010-09-19 23:04:47 +00002339 register ContributionInfo
2340 *restrict contribution;
2341
cristy3ed852e2009-09-05 21:47:34 +00002342 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002343 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002344
cristy9af9b5d2010-08-15 17:04:28 +00002345 register PixelPacket
2346 *restrict q;
2347
cristybb503372010-05-27 20:51:26 +00002348 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002349 x;
2350
cristy9af9b5d2010-08-15 17:04:28 +00002351 ssize_t
2352 n,
2353 start,
2354 stop;
cristy3ed852e2009-09-05 21:47:34 +00002355
2356 if (status == MagickFalse)
2357 continue;
cristy679e6962010-03-18 00:42:45 +00002358 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002359 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2360 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002361 density=0.0;
2362 contribution=contributions[GetOpenMPThreadId()];
2363 for (n=0; n < (stop-start); n++)
2364 {
2365 contribution[n].pixel=start+n;
2366 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2367 ((MagickRealType) (start+n)-center+0.5));
2368 density+=contribution[n].weight;
2369 }
2370 if ((density != 0.0) && (density != 1.0))
2371 {
cristybb503372010-05-27 20:51:26 +00002372 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002373 i;
2374
2375 /*
2376 Normalize.
2377 */
2378 density=1.0/density;
2379 for (i=0; i < n; i++)
2380 contribution[i].weight*=density;
2381 }
2382 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002383 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2384 exception);
cristy3ed852e2009-09-05 21:47:34 +00002385 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2386 exception);
2387 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2388 {
2389 status=MagickFalse;
2390 continue;
2391 }
2392 indexes=GetCacheViewVirtualIndexQueue(image_view);
2393 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002394 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002395 {
cristy3ed852e2009-09-05 21:47:34 +00002396 MagickPixelPacket
2397 pixel;
2398
2399 MagickRealType
2400 alpha;
2401
cristybb503372010-05-27 20:51:26 +00002402 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002403 i;
2404
cristy9af9b5d2010-08-15 17:04:28 +00002405 ssize_t
2406 j;
2407
cristy3ed852e2009-09-05 21:47:34 +00002408 pixel=zero;
2409 if (image->matte == MagickFalse)
2410 {
2411 for (i=0; i < n; i++)
2412 {
cristybb503372010-05-27 20:51:26 +00002413 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002414 image->columns+x);
2415 alpha=contribution[i].weight;
2416 pixel.red+=alpha*(p+j)->red;
2417 pixel.green+=alpha*(p+j)->green;
2418 pixel.blue+=alpha*(p+j)->blue;
2419 pixel.opacity+=alpha*(p+j)->opacity;
2420 }
cristyce70c172010-01-07 17:15:30 +00002421 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2422 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2423 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2424 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002425 if ((image->colorspace == CMYKColorspace) &&
2426 (resize_image->colorspace == CMYKColorspace))
2427 {
2428 for (i=0; i < n; i++)
2429 {
cristybb503372010-05-27 20:51:26 +00002430 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002431 image->columns+x);
2432 alpha=contribution[i].weight;
2433 pixel.index+=alpha*indexes[j];
2434 }
cristyce70c172010-01-07 17:15:30 +00002435 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002436 }
2437 }
2438 else
2439 {
2440 MagickRealType
2441 gamma;
2442
2443 gamma=0.0;
2444 for (i=0; i < n; i++)
2445 {
cristybb503372010-05-27 20:51:26 +00002446 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002447 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002448 alpha=contribution[i].weight*QuantumScale*
2449 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002450 pixel.red+=alpha*(p+j)->red;
2451 pixel.green+=alpha*(p+j)->green;
2452 pixel.blue+=alpha*(p+j)->blue;
2453 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2454 gamma+=alpha;
2455 }
2456 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002457 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2458 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2459 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2460 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002461 if ((image->colorspace == CMYKColorspace) &&
2462 (resize_image->colorspace == CMYKColorspace))
2463 {
2464 for (i=0; i < n; i++)
2465 {
cristybb503372010-05-27 20:51:26 +00002466 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002467 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002468 alpha=contribution[i].weight*QuantumScale*
2469 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002470 pixel.index+=alpha*indexes[j];
2471 }
cristyce70c172010-01-07 17:15:30 +00002472 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2473 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002474 }
2475 }
2476 if ((resize_image->storage_class == PseudoClass) &&
2477 (image->storage_class == PseudoClass))
2478 {
cristybb503372010-05-27 20:51:26 +00002479 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002480 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002481 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002482 image->columns+x);
2483 resize_indexes[x]=indexes[j];
2484 }
2485 q++;
2486 }
2487 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2488 status=MagickFalse;
2489 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2490 {
2491 MagickBooleanType
2492 proceed;
2493
cristyb5d5f722009-11-04 03:03:49 +00002494#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002495 #pragma omp critical (MagickCore_VerticalFilter)
2496#endif
cristy9af9b5d2010-08-15 17:04:28 +00002497 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002498 if (proceed == MagickFalse)
2499 status=MagickFalse;
2500 }
2501 }
2502 resize_view=DestroyCacheView(resize_view);
2503 image_view=DestroyCacheView(image_view);
2504 contributions=DestroyContributionThreadSet(contributions);
2505 return(status);
2506}
2507
cristybb503372010-05-27 20:51:26 +00002508MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2509 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002510 ExceptionInfo *exception)
2511{
2512#define WorkLoadFactor 0.265
2513
2514 FilterTypes
2515 filter_type;
2516
2517 Image
2518 *filter_image,
2519 *resize_image;
2520
cristy9af9b5d2010-08-15 17:04:28 +00002521 MagickOffsetType
2522 offset;
2523
cristy3ed852e2009-09-05 21:47:34 +00002524 MagickRealType
2525 x_factor,
2526 y_factor;
2527
2528 MagickSizeType
2529 span;
2530
2531 MagickStatusType
2532 status;
2533
2534 ResizeFilter
2535 *resize_filter;
2536
cristy3ed852e2009-09-05 21:47:34 +00002537 /*
2538 Acquire resize image.
2539 */
2540 assert(image != (Image *) NULL);
2541 assert(image->signature == MagickSignature);
2542 if (image->debug != MagickFalse)
2543 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2544 assert(exception != (ExceptionInfo *) NULL);
2545 assert(exception->signature == MagickSignature);
2546 if ((columns == 0) || (rows == 0))
2547 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2548 if ((columns == image->columns) && (rows == image->rows) &&
2549 (filter == UndefinedFilter) && (blur == 1.0))
2550 return(CloneImage(image,0,0,MagickTrue,exception));
2551 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2552 if (resize_image == (Image *) NULL)
2553 return(resize_image);
2554 /*
2555 Acquire resize filter.
2556 */
2557 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2558 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2559 if ((x_factor*y_factor) > WorkLoadFactor)
2560 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2561 else
2562 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2563 if (filter_image == (Image *) NULL)
2564 return(DestroyImage(resize_image));
2565 filter_type=LanczosFilter;
2566 if (filter != UndefinedFilter)
2567 filter_type=filter;
2568 else
2569 if ((x_factor == 1.0) && (y_factor == 1.0))
2570 filter_type=PointFilter;
2571 else
2572 if ((image->storage_class == PseudoClass) ||
2573 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2574 filter_type=MitchellFilter;
2575 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2576 exception);
2577 /*
2578 Resize image.
2579 */
cristy9af9b5d2010-08-15 17:04:28 +00002580 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002581 if ((x_factor*y_factor) > WorkLoadFactor)
2582 {
2583 span=(MagickSizeType) (filter_image->columns+rows);
2584 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002585 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002586 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002587 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002588 }
2589 else
2590 {
2591 span=(MagickSizeType) (filter_image->rows+columns);
2592 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002593 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002594 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002595 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002596 }
2597 /*
2598 Free resources.
2599 */
2600 filter_image=DestroyImage(filter_image);
2601 resize_filter=DestroyResizeFilter(resize_filter);
2602 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2603 return((Image *) NULL);
2604 resize_image->type=image->type;
2605 return(resize_image);
2606}
2607
2608/*
2609%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2610% %
2611% %
2612% %
2613% S a m p l e I m a g e %
2614% %
2615% %
2616% %
2617%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2618%
2619% SampleImage() scales an image to the desired dimensions with pixel
2620% sampling. Unlike other scaling methods, this method does not introduce
2621% any additional color into the scaled image.
2622%
2623% The format of the SampleImage method is:
2624%
cristybb503372010-05-27 20:51:26 +00002625% Image *SampleImage(const Image *image,const size_t columns,
2626% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002627%
2628% A description of each parameter follows:
2629%
2630% o image: the image.
2631%
2632% o columns: the number of columns in the sampled image.
2633%
2634% o rows: the number of rows in the sampled image.
2635%
2636% o exception: return any errors or warnings in this structure.
2637%
2638*/
cristybb503372010-05-27 20:51:26 +00002639MagickExport Image *SampleImage(const Image *image,const size_t columns,
2640 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002641{
2642#define SampleImageTag "Sample/Image"
2643
cristyc4c8d132010-01-07 01:58:38 +00002644 CacheView
2645 *image_view,
2646 *sample_view;
2647
cristy3ed852e2009-09-05 21:47:34 +00002648 Image
2649 *sample_image;
2650
cristy3ed852e2009-09-05 21:47:34 +00002651 MagickBooleanType
2652 status;
2653
cristy5f959472010-05-27 22:19:46 +00002654 MagickOffsetType
2655 progress;
2656
cristybb503372010-05-27 20:51:26 +00002657 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002658 x;
2659
cristy5f959472010-05-27 22:19:46 +00002660 ssize_t
2661 *x_offset,
2662 y;
2663
cristy3ed852e2009-09-05 21:47:34 +00002664 /*
2665 Initialize sampled image attributes.
2666 */
2667 assert(image != (const Image *) NULL);
2668 assert(image->signature == MagickSignature);
2669 if (image->debug != MagickFalse)
2670 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2671 assert(exception != (ExceptionInfo *) NULL);
2672 assert(exception->signature == MagickSignature);
2673 if ((columns == 0) || (rows == 0))
2674 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2675 if ((columns == image->columns) && (rows == image->rows))
2676 return(CloneImage(image,0,0,MagickTrue,exception));
2677 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2678 if (sample_image == (Image *) NULL)
2679 return((Image *) NULL);
2680 /*
2681 Allocate scan line buffer and column offset buffers.
2682 */
cristybb503372010-05-27 20:51:26 +00002683 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002684 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002685 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002686 {
2687 sample_image=DestroyImage(sample_image);
2688 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2689 }
cristybb503372010-05-27 20:51:26 +00002690 for (x=0; x < (ssize_t) sample_image->columns; x++)
2691 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002692 sample_image->columns);
2693 /*
2694 Sample each row.
2695 */
2696 status=MagickTrue;
2697 progress=0;
2698 image_view=AcquireCacheView(image);
2699 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002700#if defined(MAGICKCORE_OPENMP_SUPPORT)
2701 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002702#endif
cristybb503372010-05-27 20:51:26 +00002703 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002704 {
cristy3ed852e2009-09-05 21:47:34 +00002705 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002706 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002707
2708 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002709 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002710
2711 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002712 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002713
cristy3ed852e2009-09-05 21:47:34 +00002714 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002715 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002716
cristy03dbbd22010-09-19 23:04:47 +00002717 register ssize_t
2718 x;
2719
cristy9af9b5d2010-08-15 17:04:28 +00002720 ssize_t
2721 y_offset;
2722
cristy3ed852e2009-09-05 21:47:34 +00002723 if (status == MagickFalse)
2724 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002725 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2726 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002727 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2728 exception);
2729 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2730 exception);
2731 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2732 {
2733 status=MagickFalse;
2734 continue;
2735 }
2736 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2737 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2738 /*
2739 Sample each column.
2740 */
cristybb503372010-05-27 20:51:26 +00002741 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002742 *q++=p[x_offset[x]];
2743 if ((image->storage_class == PseudoClass) ||
2744 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002745 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002746 sample_indexes[x]=indexes[x_offset[x]];
2747 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2748 status=MagickFalse;
2749 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2750 {
2751 MagickBooleanType
2752 proceed;
2753
cristyb5d5f722009-11-04 03:03:49 +00002754#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002755 #pragma omp critical (MagickCore_SampleImage)
2756#endif
2757 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2758 if (proceed == MagickFalse)
2759 status=MagickFalse;
2760 }
2761 }
2762 image_view=DestroyCacheView(image_view);
2763 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002764 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002765 sample_image->type=image->type;
2766 return(sample_image);
2767}
2768
2769/*
2770%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2771% %
2772% %
2773% %
2774% S c a l e I m a g e %
2775% %
2776% %
2777% %
2778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2779%
2780% ScaleImage() changes the size of an image to the given dimensions.
2781%
2782% The format of the ScaleImage method is:
2783%
cristybb503372010-05-27 20:51:26 +00002784% Image *ScaleImage(const Image *image,const size_t columns,
2785% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002786%
2787% A description of each parameter follows:
2788%
2789% o image: the image.
2790%
2791% o columns: the number of columns in the scaled image.
2792%
2793% o rows: the number of rows in the scaled image.
2794%
2795% o exception: return any errors or warnings in this structure.
2796%
2797*/
cristybb503372010-05-27 20:51:26 +00002798MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2799 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002800{
2801#define ScaleImageTag "Scale/Image"
2802
cristyed6cb232010-01-20 03:07:53 +00002803 CacheView
2804 *image_view,
2805 *scale_view;
2806
cristy3ed852e2009-09-05 21:47:34 +00002807 Image
2808 *scale_image;
2809
cristy3ed852e2009-09-05 21:47:34 +00002810 MagickBooleanType
2811 next_column,
2812 next_row,
2813 proceed;
2814
2815 MagickPixelPacket
2816 pixel,
2817 *scale_scanline,
2818 *scanline,
2819 *x_vector,
2820 *y_vector,
2821 zero;
2822
cristy3ed852e2009-09-05 21:47:34 +00002823 PointInfo
2824 scale,
2825 span;
2826
cristybb503372010-05-27 20:51:26 +00002827 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002828 i;
2829
cristy9af9b5d2010-08-15 17:04:28 +00002830 ssize_t
2831 number_rows,
2832 y;
2833
cristy3ed852e2009-09-05 21:47:34 +00002834 /*
2835 Initialize scaled image attributes.
2836 */
2837 assert(image != (const Image *) NULL);
2838 assert(image->signature == MagickSignature);
2839 if (image->debug != MagickFalse)
2840 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2841 assert(exception != (ExceptionInfo *) NULL);
2842 assert(exception->signature == MagickSignature);
2843 if ((columns == 0) || (rows == 0))
2844 return((Image *) NULL);
2845 if ((columns == image->columns) && (rows == image->rows))
2846 return(CloneImage(image,0,0,MagickTrue,exception));
2847 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2848 if (scale_image == (Image *) NULL)
2849 return((Image *) NULL);
2850 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2851 {
2852 InheritException(exception,&scale_image->exception);
2853 scale_image=DestroyImage(scale_image);
2854 return((Image *) NULL);
2855 }
2856 /*
2857 Allocate memory.
2858 */
2859 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2860 sizeof(*x_vector));
2861 scanline=x_vector;
2862 if (image->rows != scale_image->rows)
2863 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2864 sizeof(*scanline));
2865 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2866 scale_image->columns,sizeof(*scale_scanline));
2867 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2868 sizeof(*y_vector));
2869 if ((scanline == (MagickPixelPacket *) NULL) ||
2870 (scale_scanline == (MagickPixelPacket *) NULL) ||
2871 (x_vector == (MagickPixelPacket *) NULL) ||
2872 (y_vector == (MagickPixelPacket *) NULL))
2873 {
2874 scale_image=DestroyImage(scale_image);
2875 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2876 }
2877 /*
2878 Scale image.
2879 */
2880 number_rows=0;
2881 next_row=MagickTrue;
2882 span.y=1.0;
2883 scale.y=(double) scale_image->rows/(double) image->rows;
2884 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2885 sizeof(*y_vector));
2886 GetMagickPixelPacket(image,&pixel);
2887 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2888 i=0;
cristyed6cb232010-01-20 03:07:53 +00002889 image_view=AcquireCacheView(image);
2890 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002891 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002892 {
2893 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002894 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002895
2896 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002897 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002898
2899 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002900 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002901
cristy3ed852e2009-09-05 21:47:34 +00002902 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002903 *restrict s,
2904 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002905
2906 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002907 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002908
cristy9af9b5d2010-08-15 17:04:28 +00002909 register ssize_t
2910 x;
2911
cristyed6cb232010-01-20 03:07:53 +00002912 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2913 exception);
cristy3ed852e2009-09-05 21:47:34 +00002914 if (q == (PixelPacket *) NULL)
2915 break;
2916 scale_indexes=GetAuthenticIndexQueue(scale_image);
2917 if (scale_image->rows == image->rows)
2918 {
2919 /*
2920 Read a new scanline.
2921 */
cristyed6cb232010-01-20 03:07:53 +00002922 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2923 exception);
cristy3ed852e2009-09-05 21:47:34 +00002924 if (p == (const PixelPacket *) NULL)
2925 break;
cristyed6cb232010-01-20 03:07:53 +00002926 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002927 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002928 {
cristyce70c172010-01-07 17:15:30 +00002929 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2930 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2931 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002932 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002933 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002934 if (indexes != (IndexPacket *) NULL)
2935 x_vector[x].index=(MagickRealType) indexes[x];
2936 p++;
2937 }
2938 }
2939 else
2940 {
2941 /*
2942 Scale Y direction.
2943 */
2944 while (scale.y < span.y)
2945 {
cristy9af9b5d2010-08-15 17:04:28 +00002946 if ((next_row != MagickFalse) &&
2947 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002948 {
2949 /*
2950 Read a new scanline.
2951 */
cristyed6cb232010-01-20 03:07:53 +00002952 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2953 exception);
cristy3ed852e2009-09-05 21:47:34 +00002954 if (p == (const PixelPacket *) NULL)
2955 break;
cristyed6cb232010-01-20 03:07:53 +00002956 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002957 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002958 {
cristyce70c172010-01-07 17:15:30 +00002959 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2960 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2961 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002962 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002963 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002964 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002965 if (indexes != (IndexPacket *) NULL)
2966 x_vector[x].index=(MagickRealType) indexes[x];
2967 p++;
2968 }
2969 number_rows++;
2970 }
cristybb503372010-05-27 20:51:26 +00002971 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002972 {
2973 y_vector[x].red+=scale.y*x_vector[x].red;
2974 y_vector[x].green+=scale.y*x_vector[x].green;
2975 y_vector[x].blue+=scale.y*x_vector[x].blue;
2976 if (scale_image->matte != MagickFalse)
2977 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2978 if (scale_indexes != (IndexPacket *) NULL)
2979 y_vector[x].index+=scale.y*x_vector[x].index;
2980 }
2981 span.y-=scale.y;
2982 scale.y=(double) scale_image->rows/(double) image->rows;
2983 next_row=MagickTrue;
2984 }
cristybb503372010-05-27 20:51:26 +00002985 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002986 {
2987 /*
2988 Read a new scanline.
2989 */
cristyed6cb232010-01-20 03:07:53 +00002990 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2991 exception);
cristy3ed852e2009-09-05 21:47:34 +00002992 if (p == (const PixelPacket *) NULL)
2993 break;
cristyed6cb232010-01-20 03:07:53 +00002994 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002995 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002996 {
cristyce70c172010-01-07 17:15:30 +00002997 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2998 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2999 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003000 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00003001 x_vector[x].opacity=(MagickRealType)
3002 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003003 if (indexes != (IndexPacket *) NULL)
3004 x_vector[x].index=(MagickRealType) indexes[x];
3005 p++;
3006 }
3007 number_rows++;
3008 next_row=MagickFalse;
3009 }
3010 s=scanline;
cristybb503372010-05-27 20:51:26 +00003011 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003012 {
3013 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3014 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3015 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3016 if (image->matte != MagickFalse)
3017 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3018 if (scale_indexes != (IndexPacket *) NULL)
3019 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3020 s->red=pixel.red;
3021 s->green=pixel.green;
3022 s->blue=pixel.blue;
3023 if (scale_image->matte != MagickFalse)
3024 s->opacity=pixel.opacity;
3025 if (scale_indexes != (IndexPacket *) NULL)
3026 s->index=pixel.index;
3027 s++;
3028 y_vector[x]=zero;
3029 }
3030 scale.y-=span.y;
3031 if (scale.y <= 0)
3032 {
3033 scale.y=(double) scale_image->rows/(double) image->rows;
3034 next_row=MagickTrue;
3035 }
3036 span.y=1.0;
3037 }
3038 if (scale_image->columns == image->columns)
3039 {
3040 /*
3041 Transfer scanline to scaled image.
3042 */
3043 s=scanline;
cristybb503372010-05-27 20:51:26 +00003044 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003045 {
cristyce70c172010-01-07 17:15:30 +00003046 q->red=ClampToQuantum(s->red);
3047 q->green=ClampToQuantum(s->green);
3048 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003049 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003050 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003051 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003052 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003053 q++;
3054 s++;
3055 }
3056 }
3057 else
3058 {
3059 /*
3060 Scale X direction.
3061 */
3062 pixel=zero;
3063 next_column=MagickFalse;
3064 span.x=1.0;
3065 s=scanline;
3066 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003067 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003068 {
3069 scale.x=(double) scale_image->columns/(double) image->columns;
3070 while (scale.x >= span.x)
3071 {
3072 if (next_column != MagickFalse)
3073 {
3074 pixel=zero;
3075 t++;
3076 }
3077 pixel.red+=span.x*s->red;
3078 pixel.green+=span.x*s->green;
3079 pixel.blue+=span.x*s->blue;
3080 if (image->matte != MagickFalse)
3081 pixel.opacity+=span.x*s->opacity;
3082 if (scale_indexes != (IndexPacket *) NULL)
3083 pixel.index+=span.x*s->index;
3084 t->red=pixel.red;
3085 t->green=pixel.green;
3086 t->blue=pixel.blue;
3087 if (scale_image->matte != MagickFalse)
3088 t->opacity=pixel.opacity;
3089 if (scale_indexes != (IndexPacket *) NULL)
3090 t->index=pixel.index;
3091 scale.x-=span.x;
3092 span.x=1.0;
3093 next_column=MagickTrue;
3094 }
3095 if (scale.x > 0)
3096 {
3097 if (next_column != MagickFalse)
3098 {
3099 pixel=zero;
3100 next_column=MagickFalse;
3101 t++;
3102 }
3103 pixel.red+=scale.x*s->red;
3104 pixel.green+=scale.x*s->green;
3105 pixel.blue+=scale.x*s->blue;
3106 if (scale_image->matte != MagickFalse)
3107 pixel.opacity+=scale.x*s->opacity;
3108 if (scale_indexes != (IndexPacket *) NULL)
3109 pixel.index+=scale.x*s->index;
3110 span.x-=scale.x;
3111 }
3112 s++;
3113 }
3114 if (span.x > 0)
3115 {
3116 s--;
3117 pixel.red+=span.x*s->red;
3118 pixel.green+=span.x*s->green;
3119 pixel.blue+=span.x*s->blue;
3120 if (scale_image->matte != MagickFalse)
3121 pixel.opacity+=span.x*s->opacity;
3122 if (scale_indexes != (IndexPacket *) NULL)
3123 pixel.index+=span.x*s->index;
3124 }
3125 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003126 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003127 {
3128 t->red=pixel.red;
3129 t->green=pixel.green;
3130 t->blue=pixel.blue;
3131 if (scale_image->matte != MagickFalse)
3132 t->opacity=pixel.opacity;
3133 if (scale_indexes != (IndexPacket *) NULL)
3134 t->index=pixel.index;
3135 }
3136 /*
3137 Transfer scanline to scaled image.
3138 */
3139 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003140 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003141 {
cristyce70c172010-01-07 17:15:30 +00003142 q->red=ClampToQuantum(t->red);
3143 q->green=ClampToQuantum(t->green);
3144 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003145 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003146 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003147 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003148 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003149 t++;
3150 q++;
3151 }
3152 }
cristyed6cb232010-01-20 03:07:53 +00003153 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003154 break;
cristy96b16132010-08-29 17:19:52 +00003155 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3156 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003157 if (proceed == MagickFalse)
3158 break;
3159 }
cristyed6cb232010-01-20 03:07:53 +00003160 scale_view=DestroyCacheView(scale_view);
3161 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003162 /*
3163 Free allocated memory.
3164 */
3165 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3166 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3167 if (scale_image->rows != image->rows)
3168 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3169 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3170 scale_image->type=image->type;
3171 return(scale_image);
3172}
3173
anthony02b4cb42010-10-10 04:54:35 +00003174#if 0
3175 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003176/*
3177%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3178% %
3179% %
3180% %
3181+ S e t R e s i z e F i l t e r S u p p o r t %
3182% %
3183% %
3184% %
3185%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3186%
3187% SetResizeFilterSupport() specifies which IR filter to use to window
3188%
3189% The format of the SetResizeFilterSupport method is:
3190%
3191% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3192% const MagickRealType support)
3193%
3194% A description of each parameter follows:
3195%
3196% o resize_filter: the resize filter.
3197%
3198% o support: the filter spport radius.
3199%
3200*/
3201MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3202 const MagickRealType support)
3203{
3204 assert(resize_filter != (ResizeFilter *) NULL);
3205 assert(resize_filter->signature == MagickSignature);
3206 resize_filter->support=support;
3207}
anthony02b4cb42010-10-10 04:54:35 +00003208#endif
cristy3ed852e2009-09-05 21:47:34 +00003209
3210/*
3211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3212% %
3213% %
3214% %
3215% T h u m b n a i l I m a g e %
3216% %
3217% %
3218% %
3219%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3220%
3221% ThumbnailImage() changes the size of an image to the given dimensions and
3222% removes any associated profiles. The goal is to produce small low cost
3223% thumbnail images suited for display on the Web.
3224%
3225% The format of the ThumbnailImage method is:
3226%
cristybb503372010-05-27 20:51:26 +00003227% Image *ThumbnailImage(const Image *image,const size_t columns,
3228% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003229%
3230% A description of each parameter follows:
3231%
3232% o image: the image.
3233%
3234% o columns: the number of columns in the scaled image.
3235%
3236% o rows: the number of rows in the scaled image.
3237%
3238% o exception: return any errors or warnings in this structure.
3239%
3240*/
cristy9af9b5d2010-08-15 17:04:28 +00003241MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3242 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003243{
3244#define SampleFactor 5
3245
3246 char
3247 value[MaxTextExtent];
3248
3249 const char
3250 *name;
3251
3252 Image
3253 *thumbnail_image;
3254
3255 MagickRealType
3256 x_factor,
3257 y_factor;
3258
cristybb503372010-05-27 20:51:26 +00003259 size_t
cristy3ed852e2009-09-05 21:47:34 +00003260 version;
3261
cristy9af9b5d2010-08-15 17:04:28 +00003262 struct stat
3263 attributes;
3264
cristy3ed852e2009-09-05 21:47:34 +00003265 assert(image != (Image *) NULL);
3266 assert(image->signature == MagickSignature);
3267 if (image->debug != MagickFalse)
3268 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3269 assert(exception != (ExceptionInfo *) NULL);
3270 assert(exception->signature == MagickSignature);
3271 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3272 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3273 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003274 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3275 exception);
cristy3ed852e2009-09-05 21:47:34 +00003276 else
3277 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003278 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3279 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003280 else
3281 {
3282 Image
3283 *sample_image;
3284
3285 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3286 exception);
3287 if (sample_image == (Image *) NULL)
3288 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003289 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3290 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003291 sample_image=DestroyImage(sample_image);
3292 }
3293 if (thumbnail_image == (Image *) NULL)
3294 return(thumbnail_image);
3295 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3296 if (thumbnail_image->matte == MagickFalse)
3297 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3298 thumbnail_image->depth=8;
3299 thumbnail_image->interlace=NoInterlace;
3300 /*
3301 Strip all profiles except color profiles.
3302 */
3303 ResetImageProfileIterator(thumbnail_image);
3304 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3305 {
3306 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3307 {
cristy2b726bd2010-01-11 01:05:39 +00003308 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003309 ResetImageProfileIterator(thumbnail_image);
3310 }
3311 name=GetNextImageProfile(thumbnail_image);
3312 }
3313 (void) DeleteImageProperty(thumbnail_image,"comment");
3314 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003315 if (strstr(image->magick_filename,"//") == (char *) NULL)
3316 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003317 image->magick_filename);
3318 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3319 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3320 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3321 {
cristye8c25f92010-06-03 00:53:06 +00003322 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003323 attributes.st_mtime);
3324 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3325 }
cristye8c25f92010-06-03 00:53:06 +00003326 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003327 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003328 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003329 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003330 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3331 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3332 LocaleLower(value);
3333 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3334 (void) SetImageProperty(thumbnail_image,"software",
3335 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003336 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3337 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003338 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003339 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003340 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003341 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003342 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3343 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003344 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3345 return(thumbnail_image);
3346}