blob: 0e6e949970f2c7812746b60a34036c8ae525bfe0 [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%
nicolas02a90d62010-10-28 18:43:40 +0000539% LanczosSharp is a slightly sharpened (blur=0.98303932214489908)
540% form of the Lanczos filter. It was designed specifically for
541% cylindrical EWA (Elliptical Weighted Average) distortion (as a
542% Jinc-Jinc filter), but can used as a slightly sharper orthogonal
543% Lanczos (Sinc-Sinc) filter. The blur value, the corresponding EWA
544% filter comes as close as possible to satisfying the following
545% condition:
anthony152700d2010-10-28 02:43:18 +0000546%
547% 'No-Op' Vertical and Horizontal Line Preservation Condition:
548% Images with only vertical or horizontal features are preserved
549% when performing 'no-op" with EWA distortion.
550%
551% The Lanczos2 and Lanczos2Sharp filters are simply 2-lobe versions
552% of the Lanczos filters. The 'sharp' version uses a blur factor of
nicolas56d88732010-10-28 13:20:37 +0000553% 0.958027803631219, again chosen because the resulting EWA filter
554% comes as close as possible to satisfing the "'No-Op' Vertical and
555% Horizontal Line Preservation Condition".
anthony06b1edf2010-10-25 01:19:50 +0000556%
nicolasb7dff642010-10-25 02:04:14 +0000557% Robidoux is another filter tuned for EWA. It is the Keys cubic
anthony152700d2010-10-28 02:43:18 +0000558% filter defined by B=(228 - 108 sqrt(2))/199. Robidoux satisfies the
nicolasb7dff642010-10-25 02:04:14 +0000559% "'No-Op' Vertical and Horizontal Line Preservation Condition"
anthony152700d2010-10-28 02:43:18 +0000560% exactly. It also seems to provide only minimal bluring of a low
561% level 'pixel-hash' pattern in the 'No-Op Distort' case. It turns
562% out to be close to both plain Mitchell and Lanczos2Sharp filters.
563% For example, its first crossing is at (36 sqrt(2) + 123)/(72
564% sqrt(2) + 47) which is almost the same as the first crossing
565% of the other two.
566%
anthony61b5ddd2010-10-05 02:33:31 +0000567%
nicolas3061b8a2010-10-22 16:34:52 +0000568% 'EXPERT' OPTIONS:
nicolasce6dc292010-10-22 16:23:07 +0000569%
anthony152700d2010-10-28 02:43:18 +0000570% These artifact "defines" are not recommended for production use
571% without expert knowledge of resampling, filtering, and the effects
572% they have on the resulting resampled (resize ro distorted) image.
nicolas3061b8a2010-10-22 16:34:52 +0000573%
anthony152700d2010-10-28 02:43:18 +0000574% They can be used to override any and all filter default, and it is
575% recommended you make good use of "filter:verbose" to make sure that
576% the overall effect of your selection (before and after) is as
577% expected.
nicolas3061b8a2010-10-22 16:34:52 +0000578%
anthony28ad1d72010-10-26 06:30:24 +0000579% "filter:verbose" controls whether to output the exact results of
580% the filter selections made, as well as plotting data for
581% graphing the resulting filter over the filters support range.
cristy3ed852e2009-09-05 21:47:34 +0000582%
anthony48f77622010-10-03 14:32:31 +0000583% "filter:filter" Select the main function associated with
584% this filter name, as the weighting function of the filter.
585% This can be used to set a windowing function as a weighting
586% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000587%
nicolas3061b8a2010-10-22 16:34:52 +0000588% If a "filter:window" operation has not been provided, then a
589% 'Box' windowing function will be set to denote that no
590% windowing function is being used.
cristy3ed852e2009-09-05 21:47:34 +0000591%
nicolas3061b8a2010-10-22 16:34:52 +0000592% "filter:window" Select this windowing function for the filter.
593% While any filter could be used as a windowing function, using
594% the 'first lobe' of that filter over the whole support
595% window, using a non-windowing function is not advisible. If
596% no weighting filter function is specifed a 'SincFast' filter
597% will be used.
cristy3ed852e2009-09-05 21:47:34 +0000598%
nicolas3061b8a2010-10-22 16:34:52 +0000599% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
600% This a simpler method of setting filter support size that
601% will correctly handle the Sinc/Jinc switch for an operators
602% filtering requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000603%
nicolas3061b8a2010-10-22 16:34:52 +0000604% "filter:support" Set the support size for filtering to the size
605% given This not recommended for Sinc/Jinc windowed filters
606% (lobes should be used instead). This will override any
607% 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000608%
nicolas3061b8a2010-10-22 16:34:52 +0000609% "filter:win-support" Scale windowing function to this size
610% instead. This causes the windowing (or self-windowing
611% Lagrange filter) to act is if the support window it much much
612% larger than what is actually supplied to the calling
613% operator. The filter however is still clipped to the real
614% support size given, by the support range suppiled to the
615% caller. If unset this will equal the normal filter support
anthonyb6d08c52010-09-13 01:17:04 +0000616% size.
617%
nicolas3061b8a2010-10-22 16:34:52 +0000618% "filter:blur" Scale the filter and support window by this amount.
cristy3ed852e2009-09-05 21:47:34 +0000619% A value >1 will generally result in a more burred image with
620% more ringing effects, while a value <1 will sharpen the
anthony152700d2010-10-28 02:43:18 +0000621% resulting image with more aliasing effects.
cristy3ed852e2009-09-05 21:47:34 +0000622%
nicolas3061b8a2010-10-22 16:34:52 +0000623% "filter:sigma" The sigma value to use for the Gaussian filter
624% only. Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for
625% cylindrical usage. It effectially provides a alturnative to
626% 'blur' for Gaussians without it also effecting the final
627% 'practical support' size.
anthonyf5e76ef2010-10-12 01:22:01 +0000628%
cristy3ed852e2009-09-05 21:47:34 +0000629% "filter:b"
nicolas3061b8a2010-10-22 16:34:52 +0000630% "filter:c" Override the preset B,C values for a Cubic type of
631% filter If only one of these are given it is assumes to be a
632% 'Keys' type of filter such that B+2C=1, where Keys 'alpha'
633% value = C
cristy3ed852e2009-09-05 21:47:34 +0000634%
anthony06b1edf2010-10-25 01:19:50 +0000635% Examples:
cristy3ed852e2009-09-05 21:47:34 +0000636%
nicolas6e1267a2010-10-22 16:35:52 +0000637% Set a true un-windowed Sinc filter with 10 lobes (very slow):
anthony48f77622010-10-03 14:32:31 +0000638% -define filter:filter=Sinc
639% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000640%
nicolas6e1267a2010-10-22 16:35:52 +0000641% Set an 8 lobe Lanczos (Sinc or Jinc) filter:
cristy3ed852e2009-09-05 21:47:34 +0000642% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000643% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000644%
anthony06b1edf2010-10-25 01:19:50 +0000645%
cristy3ed852e2009-09-05 21:47:34 +0000646% The format of the AcquireResizeFilter method is:
647%
648% ResizeFilter *AcquireResizeFilter(const Image *image,
649% const FilterTypes filter_type, const MagickBooleanType radial,
650% ExceptionInfo *exception)
651%
cristy33b1c162010-01-23 22:51:51 +0000652% A description of each parameter follows:
653%
cristy3ed852e2009-09-05 21:47:34 +0000654% o image: the image.
655%
nicolas07bac812010-09-19 18:47:02 +0000656% o filter: the filter type, defining a preset filter, window and
657% support. The artifact settings listed above will override
658% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000659%
anthony48f77622010-10-03 14:32:31 +0000660% o blur: blur the filter by this amount, use 1.0 if unknown. Image
661% artifact "filter:blur" will override this API call usage, including
662% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000663%
anthony48f77622010-10-03 14:32:31 +0000664% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
665% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000666%
667% o exception: return any errors or warnings in this structure.
668%
669*/
670MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000671 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000672 const MagickBooleanType cylindrical,ExceptionInfo *exception)
673{
674 const char
675 *artifact;
676
677 FilterTypes
678 filter_type,
679 window_type;
680
cristy3ed852e2009-09-05 21:47:34 +0000681 MagickRealType
682 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000683 C,
684 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000685
686 register ResizeFilter
687 *resize_filter;
688
cristy9af9b5d2010-08-15 17:04:28 +0000689
cristy3ed852e2009-09-05 21:47:34 +0000690 /*
anthony48f77622010-10-03 14:32:31 +0000691 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000692 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000693 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
694 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
695 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000696
nicolas07bac812010-09-19 18:47:02 +0000697 WARNING: The order of this tabel must match the order of the
698 FilterTypes enumeration specified in "resample.h", or the filter
699 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000700
701 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000702 */
703 static struct
704 {
705 FilterTypes
706 filter,
707 window;
708 } const mapping[SentinelFilter] =
709 {
nicolasb7dff642010-10-25 02:04:14 +0000710 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
711 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
712 { BoxFilter, BoxFilter }, /* Box averaging filter */
713 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
714 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
715 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
716 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
anthony06b1edf2010-10-25 01:19:50 +0000717 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
nicolasb7dff642010-10-25 02:04:14 +0000718 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
719 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approx */
720 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
721 { CatromFilter, BoxFilter }, /* Cubic-Keys interpolator */
722 { MitchellFilter, BoxFilter }, /* 'Ideal' Cubic-Keys filter */
nicolasb7dff642010-10-25 02:04:14 +0000723 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
724 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony152700d2010-10-28 02:43:18 +0000725 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolasb7dff642010-10-25 02:04:14 +0000726 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
727 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
728 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
nicolasb7dff642010-10-25 02:04:14 +0000729 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
anthony06b1edf2010-10-25 01:19:50 +0000730 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
anthony152700d2010-10-28 02:43:18 +0000731 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing */
732 { LanczosFilter, LanczosFilter }, /* Lanczos Sinc-Sinc filters */
733 { LanczosSharpFilter, LanczosSharpFilter }, /* | these require */
734 { Lanczos2Filter, Lanczos2Filter }, /* | special handling */
735 { Lanczos2SharpFilter,Lanczos2SharpFilter },
736 { RobidouxFilter, BoxFilter }, /* Cubic Keys tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000737 };
738 /*
nicolas32f44eb2010-09-20 01:23:12 +0000739 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000740 function. The default support size for that filter as a weighting
741 function, the range to scale with to use that function as a sinc
742 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000743
anthony07a3f7f2010-09-16 03:03:11 +0000744 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000745 SincFast(), and CubicBC() functions, which may have multiple
746 filter to function associations.
747
748 See "filter:verbose" handling below for the function -> filter
749 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000750 */
751 static struct
752 {
753 MagickRealType
754 (*function)(const MagickRealType, const ResizeFilter*),
nicolas8eccc162010-10-16 19:48:13 +0000755 lobes, /* Default lobes/support size of the weighting filter. */
anthony450db502010-10-19 04:03:03 +0000756 scale, /* Support when function used as a windowing function
757 Typically equal to the location of the first zero crossing. */
758 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
cristy3ed852e2009-09-05 21:47:34 +0000759 } const filters[SentinelFilter] =
760 {
anthony61b5ddd2010-10-05 02:33:31 +0000761 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
762 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
763 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
764 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
765 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
766 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
767 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
768 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000769 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000770 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
771 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
772 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
nicolasd349be62010-10-28 18:57:38 +0000773 { CubicBC, 2.0, 8.0/7.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
nicolasf8689f42010-10-18 16:14:08 +0000774 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000775 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
anthony06b1edf2010-10-25 01:19:50 +0000776 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
anthony61b5ddd2010-10-05 02:33:31 +0000777 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
778 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
779 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
anthony61b5ddd2010-10-05 02:33:31 +0000780 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
781 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
anthony152700d2010-10-28 02:43:18 +0000782 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
783 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
784 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* lanczos, Sharpened */
785 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos, 2-lobed */
786 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2, sharpened */
anthony450db502010-10-19 04:03:03 +0000787 { CubicBC, 2.0, 1.1685777620836932,
nicolas0d5e5322010-10-22 15:29:30 +0000788 0.37821575509399867, 0.31089212245300067 }
789 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
cristy3ed852e2009-09-05 21:47:34 +0000790 };
791 /*
anthony9a98fc62010-10-11 02:47:19 +0000792 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
anthony450db502010-10-19 04:03:03 +0000793 function being used as a filter. It is used by the "filter:lobes" expert
794 setting and for 'lobes' for Jinc functions in the previous table. This
795 way users do not have to deal with the highly irrational lobe sizes of the
anthony9a98fc62010-10-11 02:47:19 +0000796 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000797
nicolase473f722010-10-07 00:05:13 +0000798 Values taken from
anthony48f77622010-10-03 14:32:31 +0000799 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000800 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000801 */
802 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000803 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000804 {
nicolas8eccc162010-10-16 19:48:13 +0000805 1.2196698912665045,
806 2.2331305943815286,
807 3.2383154841662362,
808 4.2410628637960699,
809 5.2427643768701817,
810 6.2439216898644877,
811 7.244759868719957,
812 8.2453949139520427,
813 9.2458926849494673,
814 10.246293348754916,
815 11.246622794877883,
816 12.246898461138105,
817 13.247132522181061,
818 14.247333735806849,
anthonyc2d07db2010-09-15 23:47:40 +0000819 15.2475085630373,
nicolas8eccc162010-10-16 19:48:13 +0000820 16.247661874700962
cristy3ed852e2009-09-05 21:47:34 +0000821 };
822
cristy33b1c162010-01-23 22:51:51 +0000823 /*
824 Allocate resize filter.
825 */
cristy3ed852e2009-09-05 21:47:34 +0000826 assert(image != (const Image *) NULL);
827 assert(image->signature == MagickSignature);
828 if (image->debug != MagickFalse)
829 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
830 assert(UndefinedFilter < filter && filter < SentinelFilter);
831 assert(exception != (ExceptionInfo *) NULL);
832 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000833 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000834 if (resize_filter == (ResizeFilter *) NULL)
835 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000836 /*
837 Defaults for the requested filter.
838 */
839 filter_type=mapping[filter].filter;
840 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000841 resize_filter->blur = blur;
842 sigma = 0.5;
anthony152700d2010-10-28 02:43:18 +0000843 /* Promote 1D Windowed Sinc Filters to a 2D Windowed Jinc filters */
844 if (cylindrical != MagickFalse && filter_type == SincFastFilter
845 && filter != SincFastFilter )
846 filter_type=JincFilter;
anthony61b5ddd2010-10-05 02:33:31 +0000847
anthony152700d2010-10-28 02:43:18 +0000848 /* Expert filter setting override */
cristy3ed852e2009-09-05 21:47:34 +0000849 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000850 if (artifact != (const char *) NULL)
851 {
anthony152700d2010-10-28 02:43:18 +0000852 ssize_t
853 option;
cristy9af9b5d2010-08-15 17:04:28 +0000854 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000855 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000856 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000857 filter_type=(FilterTypes) option;
858 window_type=BoxFilter;
859 }
nicolas07bac812010-09-19 18:47:02 +0000860 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000861 artifact=GetImageArtifact(image,"filter:window");
862 if (artifact != (const char *) NULL)
863 {
cristy9af9b5d2010-08-15 17:04:28 +0000864 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000865 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony152700d2010-10-28 02:43:18 +0000866 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000867 }
cristy3ed852e2009-09-05 21:47:34 +0000868 }
cristy33b1c162010-01-23 22:51:51 +0000869 else
870 {
anthony48f77622010-10-03 14:32:31 +0000871 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000872 artifact=GetImageArtifact(image,"filter:window");
873 if (artifact != (const char *) NULL)
874 {
anthony152700d2010-10-28 02:43:18 +0000875 ssize_t
876 option;
cristy33b1c162010-01-23 22:51:51 +0000877 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
878 artifact);
879 if ((UndefinedFilter < option) && (option < SentinelFilter))
880 {
anthony61b5ddd2010-10-05 02:33:31 +0000881 filter_type=cylindrical != MagickFalse ?
882 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000883 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000884 }
885 }
886 }
anthony152700d2010-10-28 02:43:18 +0000887
nicolas07bac812010-09-19 18:47:02 +0000888 /* Assign the real functions to use for the filters selected. */
anthony152700d2010-10-28 02:43:18 +0000889 resize_filter->signature=MagickSignature;
cristy33b1c162010-01-23 22:51:51 +0000890 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000891 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000892 resize_filter->window=filters[window_type].function;
893 resize_filter->scale=filters[window_type].scale;
anthony61b5ddd2010-10-05 02:33:31 +0000894
anthonyf5e76ef2010-10-12 01:22:01 +0000895 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000896 if (cylindrical != MagickFalse)
897 switch (filter_type)
898 {
anthony10b8bc82010-10-02 12:48:46 +0000899 case BoxFilter:
900 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000901 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000902 break;
anthony152700d2010-10-28 02:43:18 +0000903 case LanczosFilter:
904 case LanczosSharpFilter:
905 case Lanczos2Filter:
906 case Lanczos2SharpFilter:
907 resize_filter->filter=filters[JincFilter].function;
908 resize_filter->window=filters[JincFilter].function;
909 /* lobes and window scale remain as declared */
910 break;
911 case GaussianFilter:
912 /* Cylindrical Gaussian sigma is sqrt(2)/2. */
913 sigma = (MagickRealType) (MagickSQ2/2.0);
914 break;
anthony81b8bf92010-10-02 13:54:34 +0000915 default:
916 break;
anthony10b8bc82010-10-02 12:48:46 +0000917 }
anthony152700d2010-10-28 02:43:18 +0000918 /* Global Sharpening (regardless of orthoginal/cylindrical) */
919 switch (filter_type)
920 {
921 case LanczosSharpFilter:
nicolas02a90d62010-10-28 18:43:40 +0000922 resize_filter->blur *= 0.98303932214489908;
anthony152700d2010-10-28 02:43:18 +0000923 break;
924 case Lanczos2SharpFilter:
nicolas56d88732010-10-28 13:20:37 +0000925 resize_filter->blur *= 0.958027803631219;
anthony152700d2010-10-28 02:43:18 +0000926 break;
927 default:
928 break;
929 }
anthony61b5ddd2010-10-05 02:33:31 +0000930
anthonyf5e76ef2010-10-12 01:22:01 +0000931 /*
anthony06b1edf2010-10-25 01:19:50 +0000932 ** Other Expert Option Modifications
anthonyf5e76ef2010-10-12 01:22:01 +0000933 */
934
935 /* User Sigma Override - no support change */
936 artifact=GetImageArtifact(image,"filter:sigma");
937 if (artifact != (const char *) NULL)
938 sigma=StringToDouble(artifact);
939 /* Define coefficents for Gaussian (assumes no cubic window) */
940 if ( GaussianFilter ) {
941 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
cristy68f103e2010-10-14 01:19:07 +0000942 resize_filter->coeff[1] = (MagickRealType) (1.0/(Magick2PI*sigma*sigma)); /* unused */
anthonyf5e76ef2010-10-12 01:22:01 +0000943 }
944
945 /* Blur Override */
946 artifact=GetImageArtifact(image,"filter:blur");
947 if (artifact != (const char *) NULL)
948 resize_filter->blur=StringToDouble(artifact);
949 if (resize_filter->blur < MagickEpsilon)
950 resize_filter->blur=(MagickRealType) MagickEpsilon;
951
952 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000953 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000954 if (artifact != (const char *) NULL)
955 {
cristybb503372010-05-27 20:51:26 +0000956 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000957 lobes;
958
cristy96b16132010-08-29 17:19:52 +0000959 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000960 if (lobes < 1)
961 lobes=1;
962 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000963 }
anthony152700d2010-10-28 02:43:18 +0000964 /* Convert a Jinc function lobes value to a real support value */
anthony61b5ddd2010-10-05 02:33:31 +0000965 if (resize_filter->filter == Jinc)
966 {
967 if (resize_filter->support > 16)
968 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
969 else
970 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
971 }
972 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000973 artifact=GetImageArtifact(image,"filter:support");
974 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000975 resize_filter->support=fabs(StringToDouble(artifact));
976 /*
nicolas07bac812010-09-19 18:47:02 +0000977 Scale windowing function separatally to the support 'clipping'
978 window that calling operator is planning to actually use. (Expert
979 override)
cristy3ed852e2009-09-05 21:47:34 +0000980 */
anthony55f12332010-09-10 01:13:02 +0000981 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000982 artifact=GetImageArtifact(image,"filter:win-support");
983 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000984 resize_filter->window_support=fabs(StringToDouble(artifact));
985 /*
anthony1f90a6b2010-09-14 08:56:31 +0000986 Adjust window function scaling to the windowing support for
987 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000988 */
989 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +0000990
anthony55f12332010-09-10 01:13:02 +0000991 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000992 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000993 */
cristy3ed852e2009-09-05 21:47:34 +0000994 B=0.0;
995 C=0.0;
cristy33b1c162010-01-23 22:51:51 +0000996 if ((filters[filter_type].function == CubicBC) ||
997 (filters[window_type].function == CubicBC))
998 {
anthony2d9b8b52010-09-14 08:31:07 +0000999 B=filters[filter_type].B;
1000 C=filters[filter_type].C;
1001 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +00001002 {
anthony2d9b8b52010-09-14 08:31:07 +00001003 B=filters[window_type].B;
1004 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +00001005 }
cristy33b1c162010-01-23 22:51:51 +00001006 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001007 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001008 {
1009 B=StringToDouble(artifact);
nicolasd15ee9a2010-10-24 18:39:45 +00001010 C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001011 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001012 if (artifact != (const char *) NULL)
1013 C=StringToDouble(artifact);
1014 }
1015 else
1016 {
1017 artifact=GetImageArtifact(image,"filter:c");
1018 if (artifact != (const char *) NULL)
1019 {
1020 C=StringToDouble(artifact);
nicolasb7dff642010-10-25 02:04:14 +00001021 B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001022 }
1023 }
nicolasc6bac3b2010-10-24 18:10:45 +00001024 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
nicolasd15ee9a2010-10-24 18:39:45 +00001025 {
anthony06b1edf2010-10-25 01:19:50 +00001026 const double twoB = B+B;
1027 resize_filter->coeff[0]=1.0-(1.0/3.0)*B;
1028 resize_filter->coeff[1]=-3.0+twoB+C;
1029 resize_filter->coeff[2]=2.0-1.5*B-C;
1030 resize_filter->coeff[3]=(4.0/3.0)*B+4.0*C;
1031 resize_filter->coeff[4]=-8.0*C-twoB;
1032 resize_filter->coeff[5]=B+5.0*C;
1033 resize_filter->coeff[6]=(-1.0/6.0)*B-C;
nicolasd15ee9a2010-10-24 18:39:45 +00001034 }
nicolasc6bac3b2010-10-24 18:10:45 +00001035 }
anthonyf5e76ef2010-10-12 01:22:01 +00001036
anthony55f12332010-09-10 01:13:02 +00001037 /*
nicolas07bac812010-09-19 18:47:02 +00001038 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001039 */
cristyf5b49372010-10-16 01:06:47 +00001040#if defined(MAGICKCORE_OPENMP_SUPPORT)
1041 #pragma omp master
1042 {
1043#endif
1044 artifact=GetImageArtifact(image,"filter:verbose");
anthony28ad1d72010-10-26 06:30:24 +00001045 if (IsMagickTrue(artifact))
cristyf5b49372010-10-16 01:06:47 +00001046 {
1047 double
anthony06b1edf2010-10-25 01:19:50 +00001048 support,
cristyf5b49372010-10-16 01:06:47 +00001049 x;
cristy3ed852e2009-09-05 21:47:34 +00001050
cristyf5b49372010-10-16 01:06:47 +00001051 /*
1052 Set the weighting function properly when the weighting
1053 function may not exactly match the filter of the same name.
anthony06b1edf2010-10-25 01:19:50 +00001054 EG: a Point filter is really uses a Box weighting function
cristyf5b49372010-10-16 01:06:47 +00001055 with a different support than is typically used.
cristyf5b49372010-10-16 01:06:47 +00001056 */
1057 if (resize_filter->filter == Box) filter_type=BoxFilter;
1058 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1059 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1060 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1061 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
anthony152700d2010-10-28 02:43:18 +00001062 if (resize_filter->filter == Box) window_type=BoxFilter;
1063 if (resize_filter->window == Sinc) window_type=SincFilter;
1064 if (resize_filter->window == SincFast) window_type=SincFastFilter;
1065 if (resize_filter->filter == Jinc) window_type=JincFilter;
1066 if (resize_filter->window == CubicBC) window_type=CubicFilter;
cristyf5b49372010-10-16 01:06:47 +00001067 /*
1068 Report Filter Details.
1069 */
1070 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1071 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
anthony06b1edf2010-10-25 01:19:50 +00001072 (void) fprintf(stdout,"# filter = %s\n",
1073 MagickOptionToMnemonic(MagickFilterOptions,filter_type));
1074 (void) fprintf(stdout,"# window = %s\n",
1075 MagickOptionToMnemonic(MagickFilterOptions, window_type));
1076 (void) fprintf(stdout,"# support = %.*g\n",
1077 GetMagickPrecision(),(double) resize_filter->support);
1078 (void) fprintf(stdout,"# win-support = %.*g\n",
1079 GetMagickPrecision(),(double) resize_filter->window_support);
1080 (void) fprintf(stdout,"# scale_blur = %.*g\n",
1081 GetMagickPrecision(), (double)resize_filter->blur);
cristyf5b49372010-10-16 01:06:47 +00001082 if ( filter_type == GaussianFilter )
anthony06b1edf2010-10-25 01:19:50 +00001083 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",
1084 GetMagickPrecision(), (double)sigma);
1085 (void) fprintf(stdout,"# practical_support = %.*g\n",
1086 GetMagickPrecision(), (double)support);
cristyf5b49372010-10-16 01:06:47 +00001087 if ( filter_type == CubicFilter || window_type == CubicFilter )
anthony06b1edf2010-10-25 01:19:50 +00001088 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",
1089 GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
cristyf5b49372010-10-16 01:06:47 +00001090 (void) fprintf(stdout,"\n");
1091 /*
1092 Output values of resulting filter graph -- for graphing
1093 filter result.
1094 */
1095 for (x=0.0; x <= support; x+=0.01f)
1096 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1097 (double) GetResizeFilterWeight(resize_filter,x));
1098 /* A final value so gnuplot can graph the 'stop' properly. */
1099 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1100 0.0);
1101 }
1102 /* Output the above once only for each image - remove setting */
1103 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1104#if defined(MAGICKCORE_OPENMP_SUPPORT)
1105 }
1106#endif
cristy3ed852e2009-09-05 21:47:34 +00001107 return(resize_filter);
1108}
1109
1110/*
1111%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1112% %
1113% %
1114% %
1115% A d a p t i v e R e s i z e I m a g e %
1116% %
1117% %
1118% %
1119%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1120%
1121% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1122%
1123% The format of the AdaptiveResizeImage method is:
1124%
cristy9af9b5d2010-08-15 17:04:28 +00001125% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1126% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001127%
1128% A description of each parameter follows:
1129%
1130% o image: the image.
1131%
1132% o columns: the number of columns in the resized image.
1133%
1134% o rows: the number of rows in the resized image.
1135%
1136% o exception: return any errors or warnings in this structure.
1137%
1138*/
1139MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001140 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001141{
1142#define AdaptiveResizeImageTag "Resize/Image"
1143
cristyc4c8d132010-01-07 01:58:38 +00001144 CacheView
1145 *resize_view;
1146
cristy3ed852e2009-09-05 21:47:34 +00001147 Image
1148 *resize_image;
1149
cristy3ed852e2009-09-05 21:47:34 +00001150 MagickBooleanType
1151 proceed;
1152
1153 MagickPixelPacket
1154 pixel;
1155
1156 PointInfo
1157 offset;
1158
1159 ResampleFilter
1160 *resample_filter;
1161
cristy9af9b5d2010-08-15 17:04:28 +00001162 ssize_t
1163 y;
1164
cristy3ed852e2009-09-05 21:47:34 +00001165 /*
1166 Adaptively resize image.
1167 */
1168 assert(image != (const Image *) NULL);
1169 assert(image->signature == MagickSignature);
1170 if (image->debug != MagickFalse)
1171 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1172 assert(exception != (ExceptionInfo *) NULL);
1173 assert(exception->signature == MagickSignature);
1174 if ((columns == 0) || (rows == 0))
1175 return((Image *) NULL);
1176 if ((columns == image->columns) && (rows == image->rows))
1177 return(CloneImage(image,0,0,MagickTrue,exception));
1178 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1179 if (resize_image == (Image *) NULL)
1180 return((Image *) NULL);
1181 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1182 {
1183 InheritException(exception,&resize_image->exception);
1184 resize_image=DestroyImage(resize_image);
1185 return((Image *) NULL);
1186 }
1187 GetMagickPixelPacket(image,&pixel);
1188 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001189 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001190 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001191 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001192 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001193 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001194 {
1195 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001196 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001197
cristybb503372010-05-27 20:51:26 +00001198 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001199 x;
1200
1201 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001202 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001203
1204 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1205 exception);
1206 if (q == (PixelPacket *) NULL)
1207 break;
1208 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1209 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001210 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001211 {
1212 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1213 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1214 &pixel);
1215 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1216 q++;
1217 }
1218 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1219 break;
cristy96b16132010-08-29 17:19:52 +00001220 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1221 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001222 if (proceed == MagickFalse)
1223 break;
1224 }
1225 resample_filter=DestroyResampleFilter(resample_filter);
1226 resize_view=DestroyCacheView(resize_view);
1227 return(resize_image);
1228}
1229
1230/*
1231%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1232% %
1233% %
1234% %
1235+ B e s s e l O r d e r O n e %
1236% %
1237% %
1238% %
1239%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1240%
1241% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001242% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001243%
1244% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1245%
1246% j1(x) = x*j1(x);
1247%
1248% For x in (8,inf)
1249%
1250% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1251%
1252% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1253%
1254% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1255% = 1/sqrt(2) * (sin(x) - cos(x))
1256% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1257% = -1/sqrt(2) * (sin(x) + cos(x))
1258%
1259% The format of the BesselOrderOne method is:
1260%
1261% MagickRealType BesselOrderOne(MagickRealType x)
1262%
1263% A description of each parameter follows:
1264%
1265% o x: MagickRealType value.
1266%
1267*/
1268
1269#undef I0
1270static MagickRealType I0(MagickRealType x)
1271{
1272 MagickRealType
1273 sum,
1274 t,
1275 y;
1276
cristybb503372010-05-27 20:51:26 +00001277 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001278 i;
1279
1280 /*
1281 Zeroth order Bessel function of the first kind.
1282 */
1283 sum=1.0;
1284 y=x*x/4.0;
1285 t=y;
1286 for (i=2; t > MagickEpsilon; i++)
1287 {
1288 sum+=t;
1289 t*=y/((MagickRealType) i*i);
1290 }
1291 return(sum);
1292}
1293
1294#undef J1
1295static MagickRealType J1(MagickRealType x)
1296{
1297 MagickRealType
1298 p,
1299 q;
1300
cristybb503372010-05-27 20:51:26 +00001301 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001302 i;
1303
1304 static const double
1305 Pone[] =
1306 {
1307 0.581199354001606143928050809e+21,
1308 -0.6672106568924916298020941484e+20,
1309 0.2316433580634002297931815435e+19,
1310 -0.3588817569910106050743641413e+17,
1311 0.2908795263834775409737601689e+15,
1312 -0.1322983480332126453125473247e+13,
1313 0.3413234182301700539091292655e+10,
1314 -0.4695753530642995859767162166e+7,
1315 0.270112271089232341485679099e+4
1316 },
1317 Qone[] =
1318 {
1319 0.11623987080032122878585294e+22,
1320 0.1185770712190320999837113348e+20,
1321 0.6092061398917521746105196863e+17,
1322 0.2081661221307607351240184229e+15,
1323 0.5243710262167649715406728642e+12,
1324 0.1013863514358673989967045588e+10,
1325 0.1501793594998585505921097578e+7,
1326 0.1606931573481487801970916749e+4,
1327 0.1e+1
1328 };
1329
1330 p=Pone[8];
1331 q=Qone[8];
1332 for (i=7; i >= 0; i--)
1333 {
1334 p=p*x*x+Pone[i];
1335 q=q*x*x+Qone[i];
1336 }
1337 return(p/q);
1338}
1339
1340#undef P1
1341static MagickRealType P1(MagickRealType x)
1342{
1343 MagickRealType
1344 p,
1345 q;
1346
cristybb503372010-05-27 20:51:26 +00001347 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001348 i;
1349
1350 static const double
1351 Pone[] =
1352 {
1353 0.352246649133679798341724373e+5,
1354 0.62758845247161281269005675e+5,
1355 0.313539631109159574238669888e+5,
1356 0.49854832060594338434500455e+4,
1357 0.2111529182853962382105718e+3,
1358 0.12571716929145341558495e+1
1359 },
1360 Qone[] =
1361 {
1362 0.352246649133679798068390431e+5,
1363 0.626943469593560511888833731e+5,
1364 0.312404063819041039923015703e+5,
1365 0.4930396490181088979386097e+4,
1366 0.2030775189134759322293574e+3,
1367 0.1e+1
1368 };
1369
1370 p=Pone[5];
1371 q=Qone[5];
1372 for (i=4; i >= 0; i--)
1373 {
1374 p=p*(8.0/x)*(8.0/x)+Pone[i];
1375 q=q*(8.0/x)*(8.0/x)+Qone[i];
1376 }
1377 return(p/q);
1378}
1379
1380#undef Q1
1381static MagickRealType Q1(MagickRealType x)
1382{
1383 MagickRealType
1384 p,
1385 q;
1386
cristybb503372010-05-27 20:51:26 +00001387 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001388 i;
1389
1390 static const double
1391 Pone[] =
1392 {
1393 0.3511751914303552822533318e+3,
1394 0.7210391804904475039280863e+3,
1395 0.4259873011654442389886993e+3,
1396 0.831898957673850827325226e+2,
1397 0.45681716295512267064405e+1,
1398 0.3532840052740123642735e-1
1399 },
1400 Qone[] =
1401 {
1402 0.74917374171809127714519505e+4,
1403 0.154141773392650970499848051e+5,
1404 0.91522317015169922705904727e+4,
1405 0.18111867005523513506724158e+4,
1406 0.1038187585462133728776636e+3,
1407 0.1e+1
1408 };
1409
1410 p=Pone[5];
1411 q=Qone[5];
1412 for (i=4; i >= 0; i--)
1413 {
1414 p=p*(8.0/x)*(8.0/x)+Pone[i];
1415 q=q*(8.0/x)*(8.0/x)+Qone[i];
1416 }
1417 return(p/q);
1418}
1419
1420static MagickRealType BesselOrderOne(MagickRealType x)
1421{
1422 MagickRealType
1423 p,
1424 q;
1425
1426 if (x == 0.0)
1427 return(0.0);
1428 p=x;
1429 if (x < 0.0)
1430 x=(-x);
1431 if (x < 8.0)
1432 return(p*J1(x));
1433 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1434 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1435 cos((double) x))));
1436 if (p < 0.0)
1437 q=(-q);
1438 return(q);
1439}
1440
1441/*
1442%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1443% %
1444% %
1445% %
1446+ D e s t r o y R e s i z e F i l t e r %
1447% %
1448% %
1449% %
1450%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1451%
1452% DestroyResizeFilter() destroy the resize filter.
1453%
cristya2ffd7e2010-03-10 20:50:30 +00001454% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001455%
1456% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1457%
1458% A description of each parameter follows:
1459%
1460% o resize_filter: the resize filter.
1461%
1462*/
1463MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1464{
1465 assert(resize_filter != (ResizeFilter *) NULL);
1466 assert(resize_filter->signature == MagickSignature);
1467 resize_filter->signature=(~MagickSignature);
1468 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1469 return(resize_filter);
1470}
1471
1472/*
1473%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1474% %
1475% %
1476% %
1477+ G e t R e s i z e F i l t e r S u p p o r t %
1478% %
1479% %
1480% %
1481%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1482%
1483% GetResizeFilterSupport() return the current support window size for this
1484% filter. Note that this may have been enlarged by filter:blur factor.
1485%
1486% The format of the GetResizeFilterSupport method is:
1487%
1488% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1489%
1490% A description of each parameter follows:
1491%
1492% o filter: Image filter to use.
1493%
1494*/
1495MagickExport MagickRealType GetResizeFilterSupport(
1496 const ResizeFilter *resize_filter)
1497{
1498 assert(resize_filter != (ResizeFilter *) NULL);
1499 assert(resize_filter->signature == MagickSignature);
1500 return(resize_filter->support*resize_filter->blur);
1501}
1502
1503/*
1504%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1505% %
1506% %
1507% %
1508+ G e t R e s i z e F i l t e r W e i g h t %
1509% %
1510% %
1511% %
1512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1513%
1514% GetResizeFilterWeight evaluates the specified resize filter at the point x
1515% which usally lies between zero and the filters current 'support' and
1516% returns the weight of the filter function at that point.
1517%
1518% The format of the GetResizeFilterWeight method is:
1519%
1520% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1521% const MagickRealType x)
1522%
1523% A description of each parameter follows:
1524%
1525% o filter: the filter type.
1526%
1527% o x: the point.
1528%
1529*/
1530MagickExport MagickRealType GetResizeFilterWeight(
1531 const ResizeFilter *resize_filter,const MagickRealType x)
1532{
1533 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001534 scale,
1535 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001536
1537 /*
1538 Windowing function - scale the weighting filter by this amount.
1539 */
1540 assert(resize_filter != (ResizeFilter *) NULL);
1541 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001542 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001543 if ((resize_filter->window_support < MagickEpsilon) ||
1544 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001545 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001546 else
1547 {
anthony55f12332010-09-10 01:13:02 +00001548 scale=resize_filter->scale;
1549 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001550 }
anthony55f12332010-09-10 01:13:02 +00001551 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001552}
1553
1554/*
1555%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1556% %
1557% %
1558% %
1559% M a g n i f y I m a g e %
1560% %
1561% %
1562% %
1563%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1564%
1565% MagnifyImage() is a convenience method that scales an image proportionally
1566% to twice its size.
1567%
1568% The format of the MagnifyImage method is:
1569%
1570% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1571%
1572% A description of each parameter follows:
1573%
1574% o image: the image.
1575%
1576% o exception: return any errors or warnings in this structure.
1577%
1578*/
1579MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1580{
1581 Image
1582 *magnify_image;
1583
1584 assert(image != (Image *) NULL);
1585 assert(image->signature == MagickSignature);
1586 if (image->debug != MagickFalse)
1587 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1588 assert(exception != (ExceptionInfo *) NULL);
1589 assert(exception->signature == MagickSignature);
1590 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1591 1.0,exception);
1592 return(magnify_image);
1593}
1594
1595/*
1596%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1597% %
1598% %
1599% %
1600% M i n i f y I m a g e %
1601% %
1602% %
1603% %
1604%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1605%
1606% MinifyImage() is a convenience method that scales an image proportionally
1607% to half its size.
1608%
1609% The format of the MinifyImage method is:
1610%
1611% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1612%
1613% A description of each parameter follows:
1614%
1615% o image: the image.
1616%
1617% o exception: return any errors or warnings in this structure.
1618%
1619*/
1620MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1621{
1622 Image
1623 *minify_image;
1624
1625 assert(image != (Image *) NULL);
1626 assert(image->signature == MagickSignature);
1627 if (image->debug != MagickFalse)
1628 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1629 assert(exception != (ExceptionInfo *) NULL);
1630 assert(exception->signature == MagickSignature);
1631 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1632 1.0,exception);
1633 return(minify_image);
1634}
1635
1636/*
1637%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1638% %
1639% %
1640% %
1641% R e s a m p l e I m a g e %
1642% %
1643% %
1644% %
1645%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1646%
1647% ResampleImage() resize image in terms of its pixel size, so that when
1648% displayed at the given resolution it will be the same size in terms of
1649% real world units as the original image at the original resolution.
1650%
1651% The format of the ResampleImage method is:
1652%
1653% Image *ResampleImage(Image *image,const double x_resolution,
1654% const double y_resolution,const FilterTypes filter,const double blur,
1655% ExceptionInfo *exception)
1656%
1657% A description of each parameter follows:
1658%
1659% o image: the image to be resized to fit the given resolution.
1660%
1661% o x_resolution: the new image x resolution.
1662%
1663% o y_resolution: the new image y resolution.
1664%
1665% o filter: Image filter to use.
1666%
1667% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1668%
1669*/
1670MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1671 const double y_resolution,const FilterTypes filter,const double blur,
1672 ExceptionInfo *exception)
1673{
1674#define ResampleImageTag "Resample/Image"
1675
1676 Image
1677 *resample_image;
1678
cristybb503372010-05-27 20:51:26 +00001679 size_t
cristy3ed852e2009-09-05 21:47:34 +00001680 height,
1681 width;
1682
1683 /*
1684 Initialize sampled image attributes.
1685 */
1686 assert(image != (const Image *) NULL);
1687 assert(image->signature == MagickSignature);
1688 if (image->debug != MagickFalse)
1689 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1690 assert(exception != (ExceptionInfo *) NULL);
1691 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001692 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1693 72.0 : image->x_resolution)+0.5);
1694 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1695 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001696 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1697 if (resample_image != (Image *) NULL)
1698 {
1699 resample_image->x_resolution=x_resolution;
1700 resample_image->y_resolution=y_resolution;
1701 }
1702 return(resample_image);
1703}
1704#if defined(MAGICKCORE_LQR_DELEGATE)
1705
1706/*
1707%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1708% %
1709% %
1710% %
1711% L i q u i d R e s c a l e I m a g e %
1712% %
1713% %
1714% %
1715%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1716%
1717% LiquidRescaleImage() rescales image with seam carving.
1718%
1719% The format of the LiquidRescaleImage method is:
1720%
1721% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001722% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001723% const double delta_x,const double rigidity,ExceptionInfo *exception)
1724%
1725% A description of each parameter follows:
1726%
1727% o image: the image.
1728%
1729% o columns: the number of columns in the rescaled image.
1730%
1731% o rows: the number of rows in the rescaled image.
1732%
1733% o delta_x: maximum seam transversal step (0 means straight seams).
1734%
1735% o rigidity: introduce a bias for non-straight seams (typically 0).
1736%
1737% o exception: return any errors or warnings in this structure.
1738%
1739*/
cristy9af9b5d2010-08-15 17:04:28 +00001740MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1741 const size_t rows,const double delta_x,const double rigidity,
1742 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001743{
1744#define LiquidRescaleImageTag "Rescale/Image"
1745
cristyc5c6f662010-09-22 14:23:02 +00001746 CacheView
1747 *rescale_view;
1748
cristy3ed852e2009-09-05 21:47:34 +00001749 const char
1750 *map;
1751
1752 guchar
1753 *packet;
1754
1755 Image
1756 *rescale_image;
1757
1758 int
1759 x,
1760 y;
1761
1762 LqrCarver
1763 *carver;
1764
1765 LqrRetVal
1766 lqr_status;
1767
1768 MagickBooleanType
1769 status;
1770
1771 MagickPixelPacket
1772 pixel;
1773
1774 unsigned char
1775 *pixels;
1776
1777 /*
1778 Liquid rescale image.
1779 */
1780 assert(image != (const Image *) NULL);
1781 assert(image->signature == MagickSignature);
1782 if (image->debug != MagickFalse)
1783 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1784 assert(exception != (ExceptionInfo *) NULL);
1785 assert(exception->signature == MagickSignature);
1786 if ((columns == 0) || (rows == 0))
1787 return((Image *) NULL);
1788 if ((columns == image->columns) && (rows == image->rows))
1789 return(CloneImage(image,0,0,MagickTrue,exception));
1790 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001791 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001792 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1793 {
1794 Image
1795 *resize_image;
1796
cristybb503372010-05-27 20:51:26 +00001797 size_t
cristy3ed852e2009-09-05 21:47:34 +00001798 height,
1799 width;
1800
1801 /*
1802 Honor liquid resize size limitations.
1803 */
1804 for (width=image->columns; columns >= (2*width-1); width*=2);
1805 for (height=image->rows; rows >= (2*height-1); height*=2);
1806 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1807 exception);
1808 if (resize_image == (Image *) NULL)
1809 return((Image *) NULL);
1810 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1811 rigidity,exception);
1812 resize_image=DestroyImage(resize_image);
1813 return(rescale_image);
1814 }
1815 map="RGB";
1816 if (image->matte == MagickFalse)
1817 map="RGBA";
1818 if (image->colorspace == CMYKColorspace)
1819 {
1820 map="CMYK";
1821 if (image->matte == MagickFalse)
1822 map="CMYKA";
1823 }
1824 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1825 strlen(map)*sizeof(*pixels));
1826 if (pixels == (unsigned char *) NULL)
1827 return((Image *) NULL);
1828 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1829 pixels,exception);
1830 if (status == MagickFalse)
1831 {
1832 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1833 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1834 }
1835 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1836 if (carver == (LqrCarver *) NULL)
1837 {
1838 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1839 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1840 }
1841 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1842 lqr_status=lqr_carver_resize(carver,columns,rows);
1843 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1844 lqr_carver_get_height(carver),MagickTrue,exception);
1845 if (rescale_image == (Image *) NULL)
1846 {
1847 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1848 return((Image *) NULL);
1849 }
1850 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1851 {
1852 InheritException(exception,&rescale_image->exception);
1853 rescale_image=DestroyImage(rescale_image);
1854 return((Image *) NULL);
1855 }
1856 GetMagickPixelPacket(rescale_image,&pixel);
1857 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001858 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001859 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1860 {
1861 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001862 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001863
1864 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001865 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001866
anthony22aad252010-09-23 06:59:07 +00001867 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001868 if (q == (PixelPacket *) NULL)
1869 break;
cristyc5c6f662010-09-22 14:23:02 +00001870 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001871 pixel.red=QuantumRange*(packet[0]/255.0);
1872 pixel.green=QuantumRange*(packet[1]/255.0);
1873 pixel.blue=QuantumRange*(packet[2]/255.0);
1874 if (image->colorspace != CMYKColorspace)
1875 {
1876 if (image->matte == MagickFalse)
1877 pixel.opacity=QuantumRange*(packet[3]/255.0);
1878 }
1879 else
1880 {
1881 pixel.index=QuantumRange*(packet[3]/255.0);
1882 if (image->matte == MagickFalse)
1883 pixel.opacity=QuantumRange*(packet[4]/255.0);
1884 }
1885 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001886 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001887 break;
1888 }
cristyc5c6f662010-09-22 14:23:02 +00001889 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001890 /*
1891 Relinquish resources.
1892 */
1893 lqr_carver_destroy(carver);
1894 return(rescale_image);
1895}
1896#else
1897MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001898 const size_t magick_unused(columns),const size_t magick_unused(rows),
1899 const double magick_unused(delta_x),const double magick_unused(rigidity),
1900 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001901{
1902 assert(image != (const Image *) NULL);
1903 assert(image->signature == MagickSignature);
1904 if (image->debug != MagickFalse)
1905 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1906 assert(exception != (ExceptionInfo *) NULL);
1907 assert(exception->signature == MagickSignature);
1908 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1909 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1910 return((Image *) NULL);
1911}
1912#endif
1913
1914/*
1915%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1916% %
1917% %
1918% %
1919% R e s i z e I m a g e %
1920% %
1921% %
1922% %
1923%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1924%
1925% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001926% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001927%
1928% If an undefined filter is given the filter defaults to Mitchell for a
1929% colormapped image, a image with a matte channel, or if the image is
1930% enlarged. Otherwise the filter defaults to a Lanczos.
1931%
1932% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1933%
1934% The format of the ResizeImage method is:
1935%
cristybb503372010-05-27 20:51:26 +00001936% Image *ResizeImage(Image *image,const size_t columns,
1937% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001938% ExceptionInfo *exception)
1939%
1940% A description of each parameter follows:
1941%
1942% o image: the image.
1943%
1944% o columns: the number of columns in the scaled image.
1945%
1946% o rows: the number of rows in the scaled image.
1947%
1948% o filter: Image filter to use.
1949%
cristy9af9b5d2010-08-15 17:04:28 +00001950% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1951% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001952%
1953% o exception: return any errors or warnings in this structure.
1954%
1955*/
1956
1957typedef struct _ContributionInfo
1958{
1959 MagickRealType
1960 weight;
1961
cristybb503372010-05-27 20:51:26 +00001962 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001963 pixel;
1964} ContributionInfo;
1965
1966static ContributionInfo **DestroyContributionThreadSet(
1967 ContributionInfo **contribution)
1968{
cristybb503372010-05-27 20:51:26 +00001969 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001970 i;
1971
1972 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001973 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001974 if (contribution[i] != (ContributionInfo *) NULL)
1975 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1976 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001977 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001978 return(contribution);
1979}
1980
1981static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1982{
cristybb503372010-05-27 20:51:26 +00001983 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001984 i;
1985
1986 ContributionInfo
1987 **contribution;
1988
cristybb503372010-05-27 20:51:26 +00001989 size_t
cristy3ed852e2009-09-05 21:47:34 +00001990 number_threads;
1991
1992 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001993 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001994 sizeof(*contribution));
1995 if (contribution == (ContributionInfo **) NULL)
1996 return((ContributionInfo **) NULL);
1997 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00001998 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00001999 {
2000 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2001 sizeof(**contribution));
2002 if (contribution[i] == (ContributionInfo *) NULL)
2003 return(DestroyContributionThreadSet(contribution));
2004 }
2005 return(contribution);
2006}
2007
2008static inline double MagickMax(const double x,const double y)
2009{
2010 if (x > y)
2011 return(x);
2012 return(y);
2013}
2014
2015static inline double MagickMin(const double x,const double y)
2016{
2017 if (x < y)
2018 return(x);
2019 return(y);
2020}
2021
2022static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2023 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002024 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002025{
2026#define ResizeImageTag "Resize/Image"
2027
cristyfa112112010-01-04 17:48:07 +00002028 CacheView
2029 *image_view,
2030 *resize_view;
2031
cristy3ed852e2009-09-05 21:47:34 +00002032 ClassType
2033 storage_class;
2034
2035 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002036 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002037
cristy3ed852e2009-09-05 21:47:34 +00002038 MagickBooleanType
2039 status;
2040
2041 MagickPixelPacket
2042 zero;
2043
2044 MagickRealType
2045 scale,
2046 support;
2047
cristy9af9b5d2010-08-15 17:04:28 +00002048 ssize_t
2049 x;
2050
cristy3ed852e2009-09-05 21:47:34 +00002051 /*
2052 Apply filter to resize horizontally from image to resize image.
2053 */
cristy5d824382010-09-06 14:00:17 +00002054 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002055 support=scale*GetResizeFilterSupport(resize_filter);
2056 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2057 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2058 {
2059 InheritException(exception,&resize_image->exception);
2060 return(MagickFalse);
2061 }
2062 if (support < 0.5)
2063 {
2064 /*
nicolas07bac812010-09-19 18:47:02 +00002065 Support too small even for nearest neighbour: Reduce to point
2066 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002067 */
2068 support=(MagickRealType) 0.5;
2069 scale=1.0;
2070 }
2071 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2072 if (contributions == (ContributionInfo **) NULL)
2073 {
2074 (void) ThrowMagickException(exception,GetMagickModule(),
2075 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2076 return(MagickFalse);
2077 }
2078 status=MagickTrue;
2079 scale=1.0/scale;
2080 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2081 image_view=AcquireCacheView(image);
2082 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002083#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002084 #pragma omp parallel for shared(status)
2085#endif
cristybb503372010-05-27 20:51:26 +00002086 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002087 {
cristy3ed852e2009-09-05 21:47:34 +00002088 MagickRealType
2089 center,
2090 density;
2091
2092 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002093 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002094
2095 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002096 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002097
cristy03dbbd22010-09-19 23:04:47 +00002098 register ContributionInfo
2099 *restrict contribution;
2100
cristy3ed852e2009-09-05 21:47:34 +00002101 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002102 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002103
cristy3ed852e2009-09-05 21:47:34 +00002104 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002105 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002106
cristy03dbbd22010-09-19 23:04:47 +00002107 register ssize_t
2108 y;
2109
cristy9af9b5d2010-08-15 17:04:28 +00002110 ssize_t
2111 n,
2112 start,
2113 stop;
2114
cristy3ed852e2009-09-05 21:47:34 +00002115 if (status == MagickFalse)
2116 continue;
2117 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002118 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2119 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002120 density=0.0;
2121 contribution=contributions[GetOpenMPThreadId()];
2122 for (n=0; n < (stop-start); n++)
2123 {
2124 contribution[n].pixel=start+n;
2125 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2126 ((MagickRealType) (start+n)-center+0.5));
2127 density+=contribution[n].weight;
2128 }
2129 if ((density != 0.0) && (density != 1.0))
2130 {
cristybb503372010-05-27 20:51:26 +00002131 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002132 i;
2133
2134 /*
2135 Normalize.
2136 */
2137 density=1.0/density;
2138 for (i=0; i < n; i++)
2139 contribution[i].weight*=density;
2140 }
cristy9af9b5d2010-08-15 17:04:28 +00002141 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2142 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002143 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2144 exception);
2145 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2146 {
2147 status=MagickFalse;
2148 continue;
2149 }
2150 indexes=GetCacheViewVirtualIndexQueue(image_view);
2151 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002152 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002153 {
cristy3ed852e2009-09-05 21:47:34 +00002154 MagickPixelPacket
2155 pixel;
2156
2157 MagickRealType
2158 alpha;
2159
cristybb503372010-05-27 20:51:26 +00002160 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002161 i;
2162
cristy9af9b5d2010-08-15 17:04:28 +00002163 ssize_t
2164 j;
2165
cristy3ed852e2009-09-05 21:47:34 +00002166 pixel=zero;
2167 if (image->matte == MagickFalse)
2168 {
2169 for (i=0; i < n; i++)
2170 {
2171 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2172 (contribution[i].pixel-contribution[0].pixel);
2173 alpha=contribution[i].weight;
2174 pixel.red+=alpha*(p+j)->red;
2175 pixel.green+=alpha*(p+j)->green;
2176 pixel.blue+=alpha*(p+j)->blue;
2177 pixel.opacity+=alpha*(p+j)->opacity;
2178 }
cristyce70c172010-01-07 17:15:30 +00002179 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2180 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2181 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2182 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002183 if ((image->colorspace == CMYKColorspace) &&
2184 (resize_image->colorspace == CMYKColorspace))
2185 {
2186 for (i=0; i < n; i++)
2187 {
2188 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2189 (contribution[i].pixel-contribution[0].pixel);
2190 alpha=contribution[i].weight;
2191 pixel.index+=alpha*indexes[j];
2192 }
cristyce70c172010-01-07 17:15:30 +00002193 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002194 }
2195 }
2196 else
2197 {
2198 MagickRealType
2199 gamma;
2200
2201 gamma=0.0;
2202 for (i=0; i < n; i++)
2203 {
2204 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2205 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002206 alpha=contribution[i].weight*QuantumScale*
2207 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002208 pixel.red+=alpha*(p+j)->red;
2209 pixel.green+=alpha*(p+j)->green;
2210 pixel.blue+=alpha*(p+j)->blue;
2211 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2212 gamma+=alpha;
2213 }
2214 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002215 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2216 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2217 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2218 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002219 if ((image->colorspace == CMYKColorspace) &&
2220 (resize_image->colorspace == CMYKColorspace))
2221 {
2222 for (i=0; i < n; i++)
2223 {
2224 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2225 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002226 alpha=contribution[i].weight*QuantumScale*
2227 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002228 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002229 }
cristyce70c172010-01-07 17:15:30 +00002230 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2231 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002232 }
2233 }
2234 if ((resize_image->storage_class == PseudoClass) &&
2235 (image->storage_class == PseudoClass))
2236 {
cristybb503372010-05-27 20:51:26 +00002237 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002238 1.0)+0.5);
2239 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2240 (contribution[i-start].pixel-contribution[0].pixel);
2241 resize_indexes[y]=indexes[j];
2242 }
2243 q++;
2244 }
2245 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2246 status=MagickFalse;
2247 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2248 {
2249 MagickBooleanType
2250 proceed;
2251
cristyb5d5f722009-11-04 03:03:49 +00002252#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002253 #pragma omp critical (MagickCore_HorizontalFilter)
2254#endif
cristy9af9b5d2010-08-15 17:04:28 +00002255 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002256 if (proceed == MagickFalse)
2257 status=MagickFalse;
2258 }
2259 }
2260 resize_view=DestroyCacheView(resize_view);
2261 image_view=DestroyCacheView(image_view);
2262 contributions=DestroyContributionThreadSet(contributions);
2263 return(status);
2264}
2265
2266static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2267 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002268 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002269{
cristyfa112112010-01-04 17:48:07 +00002270 CacheView
2271 *image_view,
2272 *resize_view;
2273
cristy3ed852e2009-09-05 21:47:34 +00002274 ClassType
2275 storage_class;
2276
2277 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002278 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002279
cristy3ed852e2009-09-05 21:47:34 +00002280 MagickBooleanType
2281 status;
2282
2283 MagickPixelPacket
2284 zero;
2285
2286 MagickRealType
2287 scale,
2288 support;
2289
cristy9af9b5d2010-08-15 17:04:28 +00002290 ssize_t
2291 y;
2292
cristy3ed852e2009-09-05 21:47:34 +00002293 /*
cristy9af9b5d2010-08-15 17:04:28 +00002294 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002295 */
cristy5d824382010-09-06 14:00:17 +00002296 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002297 support=scale*GetResizeFilterSupport(resize_filter);
2298 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2299 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2300 {
2301 InheritException(exception,&resize_image->exception);
2302 return(MagickFalse);
2303 }
2304 if (support < 0.5)
2305 {
2306 /*
nicolas07bac812010-09-19 18:47:02 +00002307 Support too small even for nearest neighbour: Reduce to point
2308 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002309 */
2310 support=(MagickRealType) 0.5;
2311 scale=1.0;
2312 }
2313 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2314 if (contributions == (ContributionInfo **) NULL)
2315 {
2316 (void) ThrowMagickException(exception,GetMagickModule(),
2317 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2318 return(MagickFalse);
2319 }
2320 status=MagickTrue;
2321 scale=1.0/scale;
2322 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2323 image_view=AcquireCacheView(image);
2324 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002325#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002326 #pragma omp parallel for shared(status)
2327#endif
cristybb503372010-05-27 20:51:26 +00002328 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002329 {
cristy3ed852e2009-09-05 21:47:34 +00002330 MagickRealType
2331 center,
2332 density;
2333
2334 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002335 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002336
2337 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002338 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002339
cristy03dbbd22010-09-19 23:04:47 +00002340 register ContributionInfo
2341 *restrict contribution;
2342
cristy3ed852e2009-09-05 21:47:34 +00002343 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002344 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002345
cristy9af9b5d2010-08-15 17:04:28 +00002346 register PixelPacket
2347 *restrict q;
2348
cristybb503372010-05-27 20:51:26 +00002349 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002350 x;
2351
cristy9af9b5d2010-08-15 17:04:28 +00002352 ssize_t
2353 n,
2354 start,
2355 stop;
cristy3ed852e2009-09-05 21:47:34 +00002356
2357 if (status == MagickFalse)
2358 continue;
cristy679e6962010-03-18 00:42:45 +00002359 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002360 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2361 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002362 density=0.0;
2363 contribution=contributions[GetOpenMPThreadId()];
2364 for (n=0; n < (stop-start); n++)
2365 {
2366 contribution[n].pixel=start+n;
2367 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2368 ((MagickRealType) (start+n)-center+0.5));
2369 density+=contribution[n].weight;
2370 }
2371 if ((density != 0.0) && (density != 1.0))
2372 {
cristybb503372010-05-27 20:51:26 +00002373 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002374 i;
2375
2376 /*
2377 Normalize.
2378 */
2379 density=1.0/density;
2380 for (i=0; i < n; i++)
2381 contribution[i].weight*=density;
2382 }
2383 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002384 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2385 exception);
cristy3ed852e2009-09-05 21:47:34 +00002386 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2387 exception);
2388 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2389 {
2390 status=MagickFalse;
2391 continue;
2392 }
2393 indexes=GetCacheViewVirtualIndexQueue(image_view);
2394 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002395 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002396 {
cristy3ed852e2009-09-05 21:47:34 +00002397 MagickPixelPacket
2398 pixel;
2399
2400 MagickRealType
2401 alpha;
2402
cristybb503372010-05-27 20:51:26 +00002403 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002404 i;
2405
cristy9af9b5d2010-08-15 17:04:28 +00002406 ssize_t
2407 j;
2408
cristy3ed852e2009-09-05 21:47:34 +00002409 pixel=zero;
2410 if (image->matte == MagickFalse)
2411 {
2412 for (i=0; i < n; i++)
2413 {
cristybb503372010-05-27 20:51:26 +00002414 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002415 image->columns+x);
2416 alpha=contribution[i].weight;
2417 pixel.red+=alpha*(p+j)->red;
2418 pixel.green+=alpha*(p+j)->green;
2419 pixel.blue+=alpha*(p+j)->blue;
2420 pixel.opacity+=alpha*(p+j)->opacity;
2421 }
cristyce70c172010-01-07 17:15:30 +00002422 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2423 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2424 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2425 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002426 if ((image->colorspace == CMYKColorspace) &&
2427 (resize_image->colorspace == CMYKColorspace))
2428 {
2429 for (i=0; i < n; i++)
2430 {
cristybb503372010-05-27 20:51:26 +00002431 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002432 image->columns+x);
2433 alpha=contribution[i].weight;
2434 pixel.index+=alpha*indexes[j];
2435 }
cristyce70c172010-01-07 17:15:30 +00002436 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002437 }
2438 }
2439 else
2440 {
2441 MagickRealType
2442 gamma;
2443
2444 gamma=0.0;
2445 for (i=0; i < n; i++)
2446 {
cristybb503372010-05-27 20:51:26 +00002447 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002448 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002449 alpha=contribution[i].weight*QuantumScale*
2450 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002451 pixel.red+=alpha*(p+j)->red;
2452 pixel.green+=alpha*(p+j)->green;
2453 pixel.blue+=alpha*(p+j)->blue;
2454 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2455 gamma+=alpha;
2456 }
2457 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002458 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2459 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2460 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2461 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002462 if ((image->colorspace == CMYKColorspace) &&
2463 (resize_image->colorspace == CMYKColorspace))
2464 {
2465 for (i=0; i < n; i++)
2466 {
cristybb503372010-05-27 20:51:26 +00002467 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002468 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002469 alpha=contribution[i].weight*QuantumScale*
2470 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002471 pixel.index+=alpha*indexes[j];
2472 }
cristyce70c172010-01-07 17:15:30 +00002473 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2474 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002475 }
2476 }
2477 if ((resize_image->storage_class == PseudoClass) &&
2478 (image->storage_class == PseudoClass))
2479 {
cristybb503372010-05-27 20:51:26 +00002480 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002481 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002482 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002483 image->columns+x);
2484 resize_indexes[x]=indexes[j];
2485 }
2486 q++;
2487 }
2488 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2489 status=MagickFalse;
2490 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2491 {
2492 MagickBooleanType
2493 proceed;
2494
cristyb5d5f722009-11-04 03:03:49 +00002495#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002496 #pragma omp critical (MagickCore_VerticalFilter)
2497#endif
cristy9af9b5d2010-08-15 17:04:28 +00002498 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002499 if (proceed == MagickFalse)
2500 status=MagickFalse;
2501 }
2502 }
2503 resize_view=DestroyCacheView(resize_view);
2504 image_view=DestroyCacheView(image_view);
2505 contributions=DestroyContributionThreadSet(contributions);
2506 return(status);
2507}
2508
cristybb503372010-05-27 20:51:26 +00002509MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2510 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002511 ExceptionInfo *exception)
2512{
2513#define WorkLoadFactor 0.265
2514
2515 FilterTypes
2516 filter_type;
2517
2518 Image
2519 *filter_image,
2520 *resize_image;
2521
cristy9af9b5d2010-08-15 17:04:28 +00002522 MagickOffsetType
2523 offset;
2524
cristy3ed852e2009-09-05 21:47:34 +00002525 MagickRealType
2526 x_factor,
2527 y_factor;
2528
2529 MagickSizeType
2530 span;
2531
2532 MagickStatusType
2533 status;
2534
2535 ResizeFilter
2536 *resize_filter;
2537
cristy3ed852e2009-09-05 21:47:34 +00002538 /*
2539 Acquire resize image.
2540 */
2541 assert(image != (Image *) NULL);
2542 assert(image->signature == MagickSignature);
2543 if (image->debug != MagickFalse)
2544 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2545 assert(exception != (ExceptionInfo *) NULL);
2546 assert(exception->signature == MagickSignature);
2547 if ((columns == 0) || (rows == 0))
2548 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2549 if ((columns == image->columns) && (rows == image->rows) &&
2550 (filter == UndefinedFilter) && (blur == 1.0))
2551 return(CloneImage(image,0,0,MagickTrue,exception));
2552 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2553 if (resize_image == (Image *) NULL)
2554 return(resize_image);
2555 /*
2556 Acquire resize filter.
2557 */
2558 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2559 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2560 if ((x_factor*y_factor) > WorkLoadFactor)
2561 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2562 else
2563 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2564 if (filter_image == (Image *) NULL)
2565 return(DestroyImage(resize_image));
2566 filter_type=LanczosFilter;
2567 if (filter != UndefinedFilter)
2568 filter_type=filter;
2569 else
2570 if ((x_factor == 1.0) && (y_factor == 1.0))
2571 filter_type=PointFilter;
2572 else
2573 if ((image->storage_class == PseudoClass) ||
2574 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2575 filter_type=MitchellFilter;
2576 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2577 exception);
2578 /*
2579 Resize image.
2580 */
cristy9af9b5d2010-08-15 17:04:28 +00002581 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002582 if ((x_factor*y_factor) > WorkLoadFactor)
2583 {
2584 span=(MagickSizeType) (filter_image->columns+rows);
2585 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002586 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002587 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002588 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002589 }
2590 else
2591 {
2592 span=(MagickSizeType) (filter_image->rows+columns);
2593 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002594 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002595 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002596 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002597 }
2598 /*
2599 Free resources.
2600 */
2601 filter_image=DestroyImage(filter_image);
2602 resize_filter=DestroyResizeFilter(resize_filter);
2603 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2604 return((Image *) NULL);
2605 resize_image->type=image->type;
2606 return(resize_image);
2607}
2608
2609/*
2610%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2611% %
2612% %
2613% %
2614% S a m p l e I m a g e %
2615% %
2616% %
2617% %
2618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2619%
2620% SampleImage() scales an image to the desired dimensions with pixel
2621% sampling. Unlike other scaling methods, this method does not introduce
2622% any additional color into the scaled image.
2623%
2624% The format of the SampleImage method is:
2625%
cristybb503372010-05-27 20:51:26 +00002626% Image *SampleImage(const Image *image,const size_t columns,
2627% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002628%
2629% A description of each parameter follows:
2630%
2631% o image: the image.
2632%
2633% o columns: the number of columns in the sampled image.
2634%
2635% o rows: the number of rows in the sampled image.
2636%
2637% o exception: return any errors or warnings in this structure.
2638%
2639*/
cristybb503372010-05-27 20:51:26 +00002640MagickExport Image *SampleImage(const Image *image,const size_t columns,
2641 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002642{
2643#define SampleImageTag "Sample/Image"
2644
cristyc4c8d132010-01-07 01:58:38 +00002645 CacheView
2646 *image_view,
2647 *sample_view;
2648
cristy3ed852e2009-09-05 21:47:34 +00002649 Image
2650 *sample_image;
2651
cristy3ed852e2009-09-05 21:47:34 +00002652 MagickBooleanType
2653 status;
2654
cristy5f959472010-05-27 22:19:46 +00002655 MagickOffsetType
2656 progress;
2657
cristybb503372010-05-27 20:51:26 +00002658 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002659 x;
2660
cristy5f959472010-05-27 22:19:46 +00002661 ssize_t
2662 *x_offset,
2663 y;
2664
cristy3ed852e2009-09-05 21:47:34 +00002665 /*
2666 Initialize sampled image attributes.
2667 */
2668 assert(image != (const Image *) NULL);
2669 assert(image->signature == MagickSignature);
2670 if (image->debug != MagickFalse)
2671 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2672 assert(exception != (ExceptionInfo *) NULL);
2673 assert(exception->signature == MagickSignature);
2674 if ((columns == 0) || (rows == 0))
2675 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2676 if ((columns == image->columns) && (rows == image->rows))
2677 return(CloneImage(image,0,0,MagickTrue,exception));
2678 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2679 if (sample_image == (Image *) NULL)
2680 return((Image *) NULL);
2681 /*
2682 Allocate scan line buffer and column offset buffers.
2683 */
cristybb503372010-05-27 20:51:26 +00002684 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002685 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002686 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002687 {
2688 sample_image=DestroyImage(sample_image);
2689 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2690 }
cristybb503372010-05-27 20:51:26 +00002691 for (x=0; x < (ssize_t) sample_image->columns; x++)
2692 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002693 sample_image->columns);
2694 /*
2695 Sample each row.
2696 */
2697 status=MagickTrue;
2698 progress=0;
2699 image_view=AcquireCacheView(image);
2700 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002701#if defined(MAGICKCORE_OPENMP_SUPPORT)
2702 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002703#endif
cristybb503372010-05-27 20:51:26 +00002704 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002705 {
cristy3ed852e2009-09-05 21:47:34 +00002706 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002707 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002708
2709 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002710 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002711
2712 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002713 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002714
cristy3ed852e2009-09-05 21:47:34 +00002715 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002716 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002717
cristy03dbbd22010-09-19 23:04:47 +00002718 register ssize_t
2719 x;
2720
cristy9af9b5d2010-08-15 17:04:28 +00002721 ssize_t
2722 y_offset;
2723
cristy3ed852e2009-09-05 21:47:34 +00002724 if (status == MagickFalse)
2725 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002726 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2727 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002728 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2729 exception);
2730 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2731 exception);
2732 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2733 {
2734 status=MagickFalse;
2735 continue;
2736 }
2737 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2738 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2739 /*
2740 Sample each column.
2741 */
cristybb503372010-05-27 20:51:26 +00002742 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002743 *q++=p[x_offset[x]];
2744 if ((image->storage_class == PseudoClass) ||
2745 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002746 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002747 sample_indexes[x]=indexes[x_offset[x]];
2748 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2749 status=MagickFalse;
2750 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2751 {
2752 MagickBooleanType
2753 proceed;
2754
cristyb5d5f722009-11-04 03:03:49 +00002755#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002756 #pragma omp critical (MagickCore_SampleImage)
2757#endif
2758 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2759 if (proceed == MagickFalse)
2760 status=MagickFalse;
2761 }
2762 }
2763 image_view=DestroyCacheView(image_view);
2764 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002765 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002766 sample_image->type=image->type;
2767 return(sample_image);
2768}
2769
2770/*
2771%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2772% %
2773% %
2774% %
2775% S c a l e I m a g e %
2776% %
2777% %
2778% %
2779%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2780%
2781% ScaleImage() changes the size of an image to the given dimensions.
2782%
2783% The format of the ScaleImage method is:
2784%
cristybb503372010-05-27 20:51:26 +00002785% Image *ScaleImage(const Image *image,const size_t columns,
2786% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002787%
2788% A description of each parameter follows:
2789%
2790% o image: the image.
2791%
2792% o columns: the number of columns in the scaled image.
2793%
2794% o rows: the number of rows in the scaled image.
2795%
2796% o exception: return any errors or warnings in this structure.
2797%
2798*/
cristybb503372010-05-27 20:51:26 +00002799MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2800 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002801{
2802#define ScaleImageTag "Scale/Image"
2803
cristyed6cb232010-01-20 03:07:53 +00002804 CacheView
2805 *image_view,
2806 *scale_view;
2807
cristy3ed852e2009-09-05 21:47:34 +00002808 Image
2809 *scale_image;
2810
cristy3ed852e2009-09-05 21:47:34 +00002811 MagickBooleanType
2812 next_column,
2813 next_row,
2814 proceed;
2815
2816 MagickPixelPacket
2817 pixel,
2818 *scale_scanline,
2819 *scanline,
2820 *x_vector,
2821 *y_vector,
2822 zero;
2823
cristy3ed852e2009-09-05 21:47:34 +00002824 PointInfo
2825 scale,
2826 span;
2827
cristybb503372010-05-27 20:51:26 +00002828 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002829 i;
2830
cristy9af9b5d2010-08-15 17:04:28 +00002831 ssize_t
2832 number_rows,
2833 y;
2834
cristy3ed852e2009-09-05 21:47:34 +00002835 /*
2836 Initialize scaled image attributes.
2837 */
2838 assert(image != (const Image *) NULL);
2839 assert(image->signature == MagickSignature);
2840 if (image->debug != MagickFalse)
2841 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2842 assert(exception != (ExceptionInfo *) NULL);
2843 assert(exception->signature == MagickSignature);
2844 if ((columns == 0) || (rows == 0))
2845 return((Image *) NULL);
2846 if ((columns == image->columns) && (rows == image->rows))
2847 return(CloneImage(image,0,0,MagickTrue,exception));
2848 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2849 if (scale_image == (Image *) NULL)
2850 return((Image *) NULL);
2851 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2852 {
2853 InheritException(exception,&scale_image->exception);
2854 scale_image=DestroyImage(scale_image);
2855 return((Image *) NULL);
2856 }
2857 /*
2858 Allocate memory.
2859 */
2860 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2861 sizeof(*x_vector));
2862 scanline=x_vector;
2863 if (image->rows != scale_image->rows)
2864 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2865 sizeof(*scanline));
2866 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2867 scale_image->columns,sizeof(*scale_scanline));
2868 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2869 sizeof(*y_vector));
2870 if ((scanline == (MagickPixelPacket *) NULL) ||
2871 (scale_scanline == (MagickPixelPacket *) NULL) ||
2872 (x_vector == (MagickPixelPacket *) NULL) ||
2873 (y_vector == (MagickPixelPacket *) NULL))
2874 {
2875 scale_image=DestroyImage(scale_image);
2876 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2877 }
2878 /*
2879 Scale image.
2880 */
2881 number_rows=0;
2882 next_row=MagickTrue;
2883 span.y=1.0;
2884 scale.y=(double) scale_image->rows/(double) image->rows;
2885 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2886 sizeof(*y_vector));
2887 GetMagickPixelPacket(image,&pixel);
2888 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2889 i=0;
cristyed6cb232010-01-20 03:07:53 +00002890 image_view=AcquireCacheView(image);
2891 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002892 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002893 {
2894 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002895 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002896
2897 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002898 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002899
2900 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002901 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002902
cristy3ed852e2009-09-05 21:47:34 +00002903 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002904 *restrict s,
2905 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002906
2907 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002908 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002909
cristy9af9b5d2010-08-15 17:04:28 +00002910 register ssize_t
2911 x;
2912
cristyed6cb232010-01-20 03:07:53 +00002913 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2914 exception);
cristy3ed852e2009-09-05 21:47:34 +00002915 if (q == (PixelPacket *) NULL)
2916 break;
2917 scale_indexes=GetAuthenticIndexQueue(scale_image);
2918 if (scale_image->rows == image->rows)
2919 {
2920 /*
2921 Read a new scanline.
2922 */
cristyed6cb232010-01-20 03:07:53 +00002923 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2924 exception);
cristy3ed852e2009-09-05 21:47:34 +00002925 if (p == (const PixelPacket *) NULL)
2926 break;
cristyed6cb232010-01-20 03:07:53 +00002927 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002928 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002929 {
cristyce70c172010-01-07 17:15:30 +00002930 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2931 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2932 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002933 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002934 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002935 if (indexes != (IndexPacket *) NULL)
2936 x_vector[x].index=(MagickRealType) indexes[x];
2937 p++;
2938 }
2939 }
2940 else
2941 {
2942 /*
2943 Scale Y direction.
2944 */
2945 while (scale.y < span.y)
2946 {
cristy9af9b5d2010-08-15 17:04:28 +00002947 if ((next_row != MagickFalse) &&
2948 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002949 {
2950 /*
2951 Read a new scanline.
2952 */
cristyed6cb232010-01-20 03:07:53 +00002953 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2954 exception);
cristy3ed852e2009-09-05 21:47:34 +00002955 if (p == (const PixelPacket *) NULL)
2956 break;
cristyed6cb232010-01-20 03:07:53 +00002957 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002958 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002959 {
cristyce70c172010-01-07 17:15:30 +00002960 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2961 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2962 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002963 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002964 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002965 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002966 if (indexes != (IndexPacket *) NULL)
2967 x_vector[x].index=(MagickRealType) indexes[x];
2968 p++;
2969 }
2970 number_rows++;
2971 }
cristybb503372010-05-27 20:51:26 +00002972 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002973 {
2974 y_vector[x].red+=scale.y*x_vector[x].red;
2975 y_vector[x].green+=scale.y*x_vector[x].green;
2976 y_vector[x].blue+=scale.y*x_vector[x].blue;
2977 if (scale_image->matte != MagickFalse)
2978 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2979 if (scale_indexes != (IndexPacket *) NULL)
2980 y_vector[x].index+=scale.y*x_vector[x].index;
2981 }
2982 span.y-=scale.y;
2983 scale.y=(double) scale_image->rows/(double) image->rows;
2984 next_row=MagickTrue;
2985 }
cristybb503372010-05-27 20:51:26 +00002986 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002987 {
2988 /*
2989 Read a new scanline.
2990 */
cristyed6cb232010-01-20 03:07:53 +00002991 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2992 exception);
cristy3ed852e2009-09-05 21:47:34 +00002993 if (p == (const PixelPacket *) NULL)
2994 break;
cristyed6cb232010-01-20 03:07:53 +00002995 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002996 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002997 {
cristyce70c172010-01-07 17:15:30 +00002998 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2999 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
3000 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003001 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00003002 x_vector[x].opacity=(MagickRealType)
3003 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003004 if (indexes != (IndexPacket *) NULL)
3005 x_vector[x].index=(MagickRealType) indexes[x];
3006 p++;
3007 }
3008 number_rows++;
3009 next_row=MagickFalse;
3010 }
3011 s=scanline;
cristybb503372010-05-27 20:51:26 +00003012 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003013 {
3014 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3015 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3016 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3017 if (image->matte != MagickFalse)
3018 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3019 if (scale_indexes != (IndexPacket *) NULL)
3020 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3021 s->red=pixel.red;
3022 s->green=pixel.green;
3023 s->blue=pixel.blue;
3024 if (scale_image->matte != MagickFalse)
3025 s->opacity=pixel.opacity;
3026 if (scale_indexes != (IndexPacket *) NULL)
3027 s->index=pixel.index;
3028 s++;
3029 y_vector[x]=zero;
3030 }
3031 scale.y-=span.y;
3032 if (scale.y <= 0)
3033 {
3034 scale.y=(double) scale_image->rows/(double) image->rows;
3035 next_row=MagickTrue;
3036 }
3037 span.y=1.0;
3038 }
3039 if (scale_image->columns == image->columns)
3040 {
3041 /*
3042 Transfer scanline to scaled image.
3043 */
3044 s=scanline;
cristybb503372010-05-27 20:51:26 +00003045 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003046 {
cristyce70c172010-01-07 17:15:30 +00003047 q->red=ClampToQuantum(s->red);
3048 q->green=ClampToQuantum(s->green);
3049 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003050 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003051 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003052 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003053 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003054 q++;
3055 s++;
3056 }
3057 }
3058 else
3059 {
3060 /*
3061 Scale X direction.
3062 */
3063 pixel=zero;
3064 next_column=MagickFalse;
3065 span.x=1.0;
3066 s=scanline;
3067 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003068 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003069 {
3070 scale.x=(double) scale_image->columns/(double) image->columns;
3071 while (scale.x >= span.x)
3072 {
3073 if (next_column != MagickFalse)
3074 {
3075 pixel=zero;
3076 t++;
3077 }
3078 pixel.red+=span.x*s->red;
3079 pixel.green+=span.x*s->green;
3080 pixel.blue+=span.x*s->blue;
3081 if (image->matte != MagickFalse)
3082 pixel.opacity+=span.x*s->opacity;
3083 if (scale_indexes != (IndexPacket *) NULL)
3084 pixel.index+=span.x*s->index;
3085 t->red=pixel.red;
3086 t->green=pixel.green;
3087 t->blue=pixel.blue;
3088 if (scale_image->matte != MagickFalse)
3089 t->opacity=pixel.opacity;
3090 if (scale_indexes != (IndexPacket *) NULL)
3091 t->index=pixel.index;
3092 scale.x-=span.x;
3093 span.x=1.0;
3094 next_column=MagickTrue;
3095 }
3096 if (scale.x > 0)
3097 {
3098 if (next_column != MagickFalse)
3099 {
3100 pixel=zero;
3101 next_column=MagickFalse;
3102 t++;
3103 }
3104 pixel.red+=scale.x*s->red;
3105 pixel.green+=scale.x*s->green;
3106 pixel.blue+=scale.x*s->blue;
3107 if (scale_image->matte != MagickFalse)
3108 pixel.opacity+=scale.x*s->opacity;
3109 if (scale_indexes != (IndexPacket *) NULL)
3110 pixel.index+=scale.x*s->index;
3111 span.x-=scale.x;
3112 }
3113 s++;
3114 }
3115 if (span.x > 0)
3116 {
3117 s--;
3118 pixel.red+=span.x*s->red;
3119 pixel.green+=span.x*s->green;
3120 pixel.blue+=span.x*s->blue;
3121 if (scale_image->matte != MagickFalse)
3122 pixel.opacity+=span.x*s->opacity;
3123 if (scale_indexes != (IndexPacket *) NULL)
3124 pixel.index+=span.x*s->index;
3125 }
3126 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003127 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003128 {
3129 t->red=pixel.red;
3130 t->green=pixel.green;
3131 t->blue=pixel.blue;
3132 if (scale_image->matte != MagickFalse)
3133 t->opacity=pixel.opacity;
3134 if (scale_indexes != (IndexPacket *) NULL)
3135 t->index=pixel.index;
3136 }
3137 /*
3138 Transfer scanline to scaled image.
3139 */
3140 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003141 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003142 {
cristyce70c172010-01-07 17:15:30 +00003143 q->red=ClampToQuantum(t->red);
3144 q->green=ClampToQuantum(t->green);
3145 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003146 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003147 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003148 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003149 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003150 t++;
3151 q++;
3152 }
3153 }
cristyed6cb232010-01-20 03:07:53 +00003154 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003155 break;
cristy96b16132010-08-29 17:19:52 +00003156 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3157 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003158 if (proceed == MagickFalse)
3159 break;
3160 }
cristyed6cb232010-01-20 03:07:53 +00003161 scale_view=DestroyCacheView(scale_view);
3162 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003163 /*
3164 Free allocated memory.
3165 */
3166 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3167 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3168 if (scale_image->rows != image->rows)
3169 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3170 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3171 scale_image->type=image->type;
3172 return(scale_image);
3173}
3174
anthony02b4cb42010-10-10 04:54:35 +00003175#if 0
3176 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003177/*
3178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3179% %
3180% %
3181% %
3182+ S e t R e s i z e F i l t e r S u p p o r t %
3183% %
3184% %
3185% %
3186%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3187%
3188% SetResizeFilterSupport() specifies which IR filter to use to window
3189%
3190% The format of the SetResizeFilterSupport method is:
3191%
3192% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3193% const MagickRealType support)
3194%
3195% A description of each parameter follows:
3196%
3197% o resize_filter: the resize filter.
3198%
3199% o support: the filter spport radius.
3200%
3201*/
3202MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3203 const MagickRealType support)
3204{
3205 assert(resize_filter != (ResizeFilter *) NULL);
3206 assert(resize_filter->signature == MagickSignature);
3207 resize_filter->support=support;
3208}
anthony02b4cb42010-10-10 04:54:35 +00003209#endif
cristy3ed852e2009-09-05 21:47:34 +00003210
3211/*
3212%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3213% %
3214% %
3215% %
3216% T h u m b n a i l I m a g e %
3217% %
3218% %
3219% %
3220%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3221%
3222% ThumbnailImage() changes the size of an image to the given dimensions and
3223% removes any associated profiles. The goal is to produce small low cost
3224% thumbnail images suited for display on the Web.
3225%
3226% The format of the ThumbnailImage method is:
3227%
cristybb503372010-05-27 20:51:26 +00003228% Image *ThumbnailImage(const Image *image,const size_t columns,
3229% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003230%
3231% A description of each parameter follows:
3232%
3233% o image: the image.
3234%
3235% o columns: the number of columns in the scaled image.
3236%
3237% o rows: the number of rows in the scaled image.
3238%
3239% o exception: return any errors or warnings in this structure.
3240%
3241*/
cristy9af9b5d2010-08-15 17:04:28 +00003242MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3243 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003244{
3245#define SampleFactor 5
3246
3247 char
3248 value[MaxTextExtent];
3249
3250 const char
3251 *name;
3252
3253 Image
3254 *thumbnail_image;
3255
3256 MagickRealType
3257 x_factor,
3258 y_factor;
3259
cristybb503372010-05-27 20:51:26 +00003260 size_t
cristy3ed852e2009-09-05 21:47:34 +00003261 version;
3262
cristy9af9b5d2010-08-15 17:04:28 +00003263 struct stat
3264 attributes;
3265
cristy3ed852e2009-09-05 21:47:34 +00003266 assert(image != (Image *) NULL);
3267 assert(image->signature == MagickSignature);
3268 if (image->debug != MagickFalse)
3269 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3270 assert(exception != (ExceptionInfo *) NULL);
3271 assert(exception->signature == MagickSignature);
3272 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3273 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3274 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003275 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3276 exception);
cristy3ed852e2009-09-05 21:47:34 +00003277 else
3278 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003279 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3280 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003281 else
3282 {
3283 Image
3284 *sample_image;
3285
3286 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3287 exception);
3288 if (sample_image == (Image *) NULL)
3289 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003290 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3291 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003292 sample_image=DestroyImage(sample_image);
3293 }
3294 if (thumbnail_image == (Image *) NULL)
3295 return(thumbnail_image);
3296 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3297 if (thumbnail_image->matte == MagickFalse)
3298 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3299 thumbnail_image->depth=8;
3300 thumbnail_image->interlace=NoInterlace;
3301 /*
3302 Strip all profiles except color profiles.
3303 */
3304 ResetImageProfileIterator(thumbnail_image);
3305 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3306 {
3307 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3308 {
cristy2b726bd2010-01-11 01:05:39 +00003309 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003310 ResetImageProfileIterator(thumbnail_image);
3311 }
3312 name=GetNextImageProfile(thumbnail_image);
3313 }
3314 (void) DeleteImageProperty(thumbnail_image,"comment");
3315 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003316 if (strstr(image->magick_filename,"//") == (char *) NULL)
3317 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003318 image->magick_filename);
3319 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3320 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3321 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3322 {
cristye8c25f92010-06-03 00:53:06 +00003323 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003324 attributes.st_mtime);
3325 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3326 }
cristye8c25f92010-06-03 00:53:06 +00003327 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003328 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003329 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003330 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003331 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3332 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3333 LocaleLower(value);
3334 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3335 (void) SetImageProperty(thumbnail_image,"software",
3336 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003337 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3338 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003339 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003340 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003341 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003342 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003343 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3344 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003345 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3346 return(thumbnail_image);
3347}