blob: 16c7b976ca43eea3a783cfa01c41910847511ccc [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:
cristy3ed852e2009-09-05 21:47:34 +0000209 P0 = ( 6 - 2*B )/6
210 P1 = 0
211 P2 = (-18 +12*B + 6*C )/6
212 P3 = ( 12 - 9*B - 6*C )/6
213 Q0 = ( 8*B +24*C )/6
214 Q1 = ( -12*B -48*C )/6
215 Q2 = ( 6*B +30*C )/6
216 Q3 = ( - 1*B - 6*C )/6
217
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
224 (slope). This implies that P1 is always zero; for this reason, it
225 is skipped.
cristy3ed852e2009-09-05 21:47:34 +0000226 */
227 if (x < 1.0)
nicolasc6bac3b2010-10-24 18:10:45 +0000228 return(resize_filter->coeff[0]+x*(x*
nicolase3b9eca2010-10-24 19:48:22 +0000229 (resize_filter->coeff[1]+x*resize_filter->coeff[2])));
cristy3ed852e2009-09-05 21:47:34 +0000230 if (x < 2.0)
nicolase3b9eca2010-10-24 19:48:22 +0000231 return(resize_filter->coeff[3]+x*(resize_filter->coeff[4]+x*
232 (resize_filter->coeff[5]+x*resize_filter->coeff[6])));
cristy3ed852e2009-09-05 21:47:34 +0000233 return(0.0);
234}
235
236static MagickRealType Gaussian(const MagickRealType x,
anthonyf5e76ef2010-10-12 01:22:01 +0000237 const ResizeFilter *resize_filter)
cristy3ed852e2009-09-05 21:47:34 +0000238{
cristy560d8182010-09-08 22:36:25 +0000239 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000240 Gaussian with a fixed sigma = 1/2
241
242 Gaussian Formula...
243 exp( -(x^2)/((2.0*sigma^2) ) / sqrt(2*PI*sigma^2)))
244 The constants are pre-calculated...
245 exp( -coeff[0]*(x^2)) ) * coeff[1]
246 However the multiplier coefficent is not needed and not used.
247
248 This separates the gaussian 'sigma' value from the 'blur/support' settings
249 allows for its use in special 'small sigma' gaussians, without the filter
250 'missing' pixels when blur and thus support becomes too small.
cristy560d8182010-09-08 22:36:25 +0000251 */
anthonyf5e76ef2010-10-12 01:22:01 +0000252 return(exp((double)(-resize_filter->coeff[0]*x*x))); }
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
460 filter, or a Bartlett 2D Cone filter.
cristy3ed852e2009-09-05 21:47:34 +0000461 */
462 if (x < 1.0)
463 return(1.0-x);
464 return(0.0);
465}
466
467static MagickRealType Welsh(const MagickRealType x,
468 const ResizeFilter *magick_unused(resize_filter))
469{
470 /*
471 Welsh parabolic windowing filter.
472 */
cristy560d8182010-09-08 22:36:25 +0000473 if (x < 1.0)
cristy3ed852e2009-09-05 21:47:34 +0000474 return(1.0-x*x);
475 return(0.0);
476}
477
478/*
479%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
480% %
481% %
482% %
483+ A c q u i r e R e s i z e F i l t e r %
484% %
485% %
486% %
487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
488%
nicolas07bac812010-09-19 18:47:02 +0000489% AcquireResizeFilter() allocates the ResizeFilter structure. Choose
490% from these filters:
cristy3ed852e2009-09-05 21:47:34 +0000491%
492% FIR (Finite impulse Response) Filters
493% Box Triangle Quadratic
494% Cubic Hermite Catrom
495% Mitchell
496%
497% IIR (Infinite impulse Response) Filters
anthony48f77622010-10-03 14:32:31 +0000498% Gaussian Sinc Jinc (Bessel)
cristy3ed852e2009-09-05 21:47:34 +0000499%
anthony48f77622010-10-03 14:32:31 +0000500% Windowed Sinc/Jinc Filters
cristy3ed852e2009-09-05 21:47:34 +0000501% Blackman Hanning Hamming
anthony48f77622010-10-03 14:32:31 +0000502% Kaiser Lanczos
cristy3ed852e2009-09-05 21:47:34 +0000503%
anthony61b5ddd2010-10-05 02:33:31 +0000504% Special purpose Filters
anthony853d6972010-10-08 06:01:31 +0000505% SincFast Lanczos2D Robidoux
anthony61b5ddd2010-10-05 02:33:31 +0000506%
anthony48f77622010-10-03 14:32:31 +0000507% The users "-filter" selection is used to lookup the default 'expert'
508% settings for that filter from a internal table. However any provided
509% 'expert' settings (see below) may override this selection.
510%
511% FIR filters are used as is, and are limited to that filters support
nicolas07bac812010-09-19 18:47:02 +0000512% window (unless over-ridden). 'Gaussian' while classed as an IIR
anthonyc331dec2010-09-26 01:30:14 +0000513% filter, is also simply clipped by its support size (currently 1.5
anthonyf5e76ef2010-10-12 01:22:01 +0000514% or approximatally 3*sigma as recommended by many references)
cristy3ed852e2009-09-05 21:47:34 +0000515%
anthony48f77622010-10-03 14:32:31 +0000516% The selection is typically either a windowed Sinc, or interpolated
nicolas07bac812010-09-19 18:47:02 +0000517% filter, for use by functions such as ResizeImage(). However if a
anthony48f77622010-10-03 14:32:31 +0000518% 'cylindrical' filter flag is requested, any default Sinc weighting
519% and windowing functions will be promoted to cylindrical Jinc form of
520% function.
cristy3ed852e2009-09-05 21:47:34 +0000521%
anthony48f77622010-10-03 14:32:31 +0000522% Directly requesting 'Sinc' or 'Jinc' will force the use of that
523% filter function without any windowing. This is not recommended,
524% except by image processing experts or in expert options. Selecting a
525% window filtering version of these functions is better.
cristy3ed852e2009-09-05 21:47:34 +0000526%
anthony48f77622010-10-03 14:32:31 +0000527% Lanczos is a special case of a Sinc-windowed Sinc, (or Jinc-Jinc for
528% the cylindrical case) but defaulting to 3-lobe support, rather that
529% the default 4 lobe support of the other windowed sinc/jinc filters.
cristy3ed852e2009-09-05 21:47:34 +0000530%
anthony48f77622010-10-03 14:32:31 +0000531% Two forms of the 'Sinc' function are available: Sinc and SincFast.
nicolas07bac812010-09-19 18:47:02 +0000532% Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
533% selected if the user specifically specifies the use of a Sinc
534% filter. SincFast uses highly accurate (and fast) polynomial (low Q)
anthony48f77622010-10-03 14:32:31 +0000535% and rational (high Q) approximations, and will be used by default in
536% most cases.
anthonyba5a7c32010-09-15 02:42:25 +0000537%
nicolas1eb2fcf2010-10-10 20:45:17 +0000538% The Lanczos2D and Robidoux filters are tuned for cylindrical
539% (radial) EWA (Elliptical Weighted Average) distortion. Lanczos2D
nicolasdff19b42010-10-10 20:53:13 +0000540% is a 2 lobe Lanczos-like filter using Jinc (for EWA) or Sinc.
nicolas6ca6e962010-10-10 20:49:01 +0000541% Robidoux used to be a sharpened version of Lanczos2D (with
nicolas0d5e5322010-10-22 15:29:30 +0000542% blur=0.958033808). Now, Robidoux is the unique Keys cubic spline
543% filter satisfying the following condition:
544%
545% Robidoux exactly preserves images with only vertical or
546% horizontal features when performing 'no-op" with EWA distortion.
547%
nicolas618a9a72010-10-22 19:23:43 +0000548% That is, Robidoux is the BC-Spline with B=(228 - 108 sqrt(2))/199
549% and C=(108 sqrt(2) - 29)/398. Robidoux turns out to be close to
550% both plain Mitchell and "sharpened" Lanczos2D. For example, it's
551% first crossing is (36 sqrt(2) + 123)/(72 sqrt(2) + 47) which is
552% almost identical to the first crossing of the other two.
anthony61b5ddd2010-10-05 02:33:31 +0000553%
nicolas3061b8a2010-10-22 16:34:52 +0000554% 'EXPERT' OPTIONS:
nicolasce6dc292010-10-22 16:23:07 +0000555%
nicolas3061b8a2010-10-22 16:34:52 +0000556% (Not recommended without expert knowledge of resampling and
557% filtering.)
558%
559% You can override any and all filter settings. Use "filter:verbose"
560% to make sure that the overall effect of your selections is as
561% expected.
562%
563% "filter:verbose" Output the exact results of the filter
564% selections made, as well as plotting data for graphing the
565% resulting filter over support range (blur adjusted).
cristy3ed852e2009-09-05 21:47:34 +0000566%
anthony48f77622010-10-03 14:32:31 +0000567% "filter:filter" Select the main function associated with
568% this filter name, as the weighting function of the filter.
569% This can be used to set a windowing function as a weighting
570% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000571%
nicolas3061b8a2010-10-22 16:34:52 +0000572% If a "filter:window" operation has not been provided, then a
573% 'Box' windowing function will be set to denote that no
574% windowing function is being used.
cristy3ed852e2009-09-05 21:47:34 +0000575%
nicolas3061b8a2010-10-22 16:34:52 +0000576% "filter:window" Select this windowing function for the filter.
577% While any filter could be used as a windowing function, using
578% the 'first lobe' of that filter over the whole support
579% window, using a non-windowing function is not advisible. If
580% no weighting filter function is specifed a 'SincFast' filter
581% will be used.
cristy3ed852e2009-09-05 21:47:34 +0000582%
nicolas3061b8a2010-10-22 16:34:52 +0000583% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
584% This a simpler method of setting filter support size that
585% will correctly handle the Sinc/Jinc switch for an operators
586% filtering requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000587%
nicolas3061b8a2010-10-22 16:34:52 +0000588% "filter:support" Set the support size for filtering to the size
589% given This not recommended for Sinc/Jinc windowed filters
590% (lobes should be used instead). This will override any
591% 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000592%
nicolas3061b8a2010-10-22 16:34:52 +0000593% "filter:win-support" Scale windowing function to this size
594% instead. This causes the windowing (or self-windowing
595% Lagrange filter) to act is if the support window it much much
596% larger than what is actually supplied to the calling
597% operator. The filter however is still clipped to the real
598% support size given, by the support range suppiled to the
599% caller. If unset this will equal the normal filter support
anthonyb6d08c52010-09-13 01:17:04 +0000600% size.
601%
nicolas3061b8a2010-10-22 16:34:52 +0000602% "filter:blur" Scale the filter and support window by this amount.
cristy3ed852e2009-09-05 21:47:34 +0000603% A value >1 will generally result in a more burred image with
604% more ringing effects, while a value <1 will sharpen the
605% resulting image with more aliasing and Morie effects.
606%
nicolas3061b8a2010-10-22 16:34:52 +0000607% "filter:sigma" The sigma value to use for the Gaussian filter
608% only. Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for
609% cylindrical usage. It effectially provides a alturnative to
610% 'blur' for Gaussians without it also effecting the final
611% 'practical support' size.
anthonyf5e76ef2010-10-12 01:22:01 +0000612%
cristy3ed852e2009-09-05 21:47:34 +0000613% "filter:b"
nicolas3061b8a2010-10-22 16:34:52 +0000614% "filter:c" Override the preset B,C values for a Cubic type of
615% filter If only one of these are given it is assumes to be a
616% 'Keys' type of filter such that B+2C=1, where Keys 'alpha'
617% value = C
cristy3ed852e2009-09-05 21:47:34 +0000618%
nicolas3061b8a2010-10-22 16:34:52 +0000619% Examples:
cristy3ed852e2009-09-05 21:47:34 +0000620%
nicolas6e1267a2010-10-22 16:35:52 +0000621% Set a true un-windowed Sinc filter with 10 lobes (very slow):
anthony48f77622010-10-03 14:32:31 +0000622% -define filter:filter=Sinc
623% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000624%
nicolas6e1267a2010-10-22 16:35:52 +0000625% Set an 8 lobe Lanczos (Sinc or Jinc) filter:
cristy3ed852e2009-09-05 21:47:34 +0000626% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000627% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000628%
cristy3ed852e2009-09-05 21:47:34 +0000629% The format of the AcquireResizeFilter method is:
630%
631% ResizeFilter *AcquireResizeFilter(const Image *image,
632% const FilterTypes filter_type, const MagickBooleanType radial,
633% ExceptionInfo *exception)
634%
cristy33b1c162010-01-23 22:51:51 +0000635% A description of each parameter follows:
636%
cristy3ed852e2009-09-05 21:47:34 +0000637% o image: the image.
638%
nicolas07bac812010-09-19 18:47:02 +0000639% o filter: the filter type, defining a preset filter, window and
640% support. The artifact settings listed above will override
641% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000642%
anthony48f77622010-10-03 14:32:31 +0000643% o blur: blur the filter by this amount, use 1.0 if unknown. Image
644% artifact "filter:blur" will override this API call usage, including
645% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000646%
anthony48f77622010-10-03 14:32:31 +0000647% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
648% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000649%
650% o exception: return any errors or warnings in this structure.
651%
652*/
653MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000654 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000655 const MagickBooleanType cylindrical,ExceptionInfo *exception)
656{
657 const char
658 *artifact;
659
660 FilterTypes
661 filter_type,
662 window_type;
663
cristy3ed852e2009-09-05 21:47:34 +0000664 MagickRealType
665 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000666 C,
667 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000668
669 register ResizeFilter
670 *resize_filter;
671
cristy9af9b5d2010-08-15 17:04:28 +0000672 ssize_t
673 option;
674
cristy3ed852e2009-09-05 21:47:34 +0000675 /*
anthony48f77622010-10-03 14:32:31 +0000676 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000677 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000678 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
679 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
680 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000681
nicolas07bac812010-09-19 18:47:02 +0000682 WARNING: The order of this tabel must match the order of the
683 FilterTypes enumeration specified in "resample.h", or the filter
684 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000685
686 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000687 */
688 static struct
689 {
690 FilterTypes
691 filter,
692 window;
693 } const mapping[SentinelFilter] =
694 {
anthony462ee072010-09-27 12:34:02 +0000695 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
696 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
697 { BoxFilter, BoxFilter }, /* Box averaging filter */
698 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
699 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
700 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
701 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
702 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
703 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
704 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
705 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
706 { CatromFilter, BoxFilter }, /* Cubic interpolator */
707 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
708 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
anthony48f77622010-10-03 14:32:31 +0000709 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
710 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony462ee072010-09-27 12:34:02 +0000711 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
712 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
713 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
714 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
715 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
716 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
717 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolas5835ee02010-10-10 20:40:15 +0000718 { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
anthonye5f06452010-10-12 05:48:17 +0000719 { Lanczos2DSharpFilter, JincFilter },/* SPECIAL: ditto sharpened */
720 { RobidouxFilter, BoxFilter }, /* SPECIAL: Keys cubic tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000721 };
722 /*
nicolas32f44eb2010-09-20 01:23:12 +0000723 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000724 function. The default support size for that filter as a weighting
725 function, the range to scale with to use that function as a sinc
726 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000727
anthony07a3f7f2010-09-16 03:03:11 +0000728 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000729 SincFast(), and CubicBC() functions, which may have multiple
730 filter to function associations.
731
732 See "filter:verbose" handling below for the function -> filter
733 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000734 */
735 static struct
736 {
737 MagickRealType
738 (*function)(const MagickRealType, const ResizeFilter*),
nicolas8eccc162010-10-16 19:48:13 +0000739 lobes, /* Default lobes/support size of the weighting filter. */
anthony450db502010-10-19 04:03:03 +0000740 scale, /* Support when function used as a windowing function
741 Typically equal to the location of the first zero crossing. */
742 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
cristy3ed852e2009-09-05 21:47:34 +0000743 } const filters[SentinelFilter] =
744 {
anthony61b5ddd2010-10-05 02:33:31 +0000745 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
746 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
747 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
748 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
749 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
750 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
751 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
752 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000753 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000754 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
755 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
756 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
anthony853d6972010-10-08 06:01:31 +0000757 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
758 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
nicolasf8689f42010-10-18 16:14:08 +0000759 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000760 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
761 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
762 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
763 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
764 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
765 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
766 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
767 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
nicolas8eccc162010-10-16 19:48:13 +0000768 { Jinc, 2.0, 1.2196698912665045, 0.0, 0.0 },
769 /* Lanczos2D (Jinc-Jinc) */
770 { Jinc, 2.0, 1.1684849904329952, 0.0, 0.0 },
771 /* Lanczos2D sharpened with blur=0.958033808 */
anthony450db502010-10-19 04:03:03 +0000772 { CubicBC, 2.0, 1.1685777620836932,
nicolas0d5e5322010-10-22 15:29:30 +0000773 0.37821575509399867, 0.31089212245300067 }
774 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
cristy3ed852e2009-09-05 21:47:34 +0000775 };
776 /*
anthony9a98fc62010-10-11 02:47:19 +0000777 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
anthony450db502010-10-19 04:03:03 +0000778 function being used as a filter. It is used by the "filter:lobes" expert
779 setting and for 'lobes' for Jinc functions in the previous table. This
780 way users do not have to deal with the highly irrational lobe sizes of the
anthony9a98fc62010-10-11 02:47:19 +0000781 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000782
nicolase473f722010-10-07 00:05:13 +0000783 Values taken from
anthony48f77622010-10-03 14:32:31 +0000784 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000785 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000786 */
787 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000788 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000789 {
nicolas8eccc162010-10-16 19:48:13 +0000790 1.2196698912665045,
791 2.2331305943815286,
792 3.2383154841662362,
793 4.2410628637960699,
794 5.2427643768701817,
795 6.2439216898644877,
796 7.244759868719957,
797 8.2453949139520427,
798 9.2458926849494673,
799 10.246293348754916,
800 11.246622794877883,
801 12.246898461138105,
802 13.247132522181061,
803 14.247333735806849,
anthonyc2d07db2010-09-15 23:47:40 +0000804 15.2475085630373,
nicolas8eccc162010-10-16 19:48:13 +0000805 16.247661874700962
cristy3ed852e2009-09-05 21:47:34 +0000806 };
807
cristy33b1c162010-01-23 22:51:51 +0000808 /*
809 Allocate resize filter.
810 */
cristy3ed852e2009-09-05 21:47:34 +0000811 assert(image != (const Image *) NULL);
812 assert(image->signature == MagickSignature);
813 if (image->debug != MagickFalse)
814 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
815 assert(UndefinedFilter < filter && filter < SentinelFilter);
816 assert(exception != (ExceptionInfo *) NULL);
817 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000818 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000819 if (resize_filter == (ResizeFilter *) NULL)
820 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000821 /*
822 Defaults for the requested filter.
823 */
824 filter_type=mapping[filter].filter;
825 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000826 resize_filter->blur = blur;
827 sigma = 0.5;
anthony48f77622010-10-03 14:32:31 +0000828 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000829 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000830 switch (filter_type)
831 {
832 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000833 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000834 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000835 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000836 break;
anthonyba5a7c32010-09-15 02:42:25 +0000837 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000838 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000839 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000840 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000841 break;
cristy33b1c162010-01-23 22:51:51 +0000842 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000843 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000844 filter_type=JincFilter;
845 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000846 break;
anthony08958462010-10-12 06:48:35 +0000847 case Lanczos2DSharpFilter:
nicolas1f4c0512010-10-20 20:19:30 +0000848 /* Sharpened by Nicolas Robidoux so as to optimize for minimal
849 * blurring of orthogonal lines
anthony08958462010-10-12 06:48:35 +0000850 */
851 resize_filter->blur *= 0.958033808;
852 break;
anthonyf5e76ef2010-10-12 01:22:01 +0000853 case GaussianFilter:
cristy68f103e2010-10-14 01:19:07 +0000854 sigma = (MagickRealType) (MagickSQ2/2.0); /* Cylindrical Gaussian sigma is sqrt(2)/2 */
anthonyf5e76ef2010-10-12 01:22:01 +0000855 break;
cristya782ecf2010-01-25 02:59:14 +0000856 default:
857 break;
cristy3ed852e2009-09-05 21:47:34 +0000858 }
anthony61b5ddd2010-10-05 02:33:31 +0000859 else
860 switch (filter_type)
861 {
862 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000863 case Lanczos2DSharpFilter:
nicolas45b58a92010-10-07 15:46:39 +0000864 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000865 window_type=SincFastFilter;
866 break;
867 default:
868 break;
869 }
870
cristy3ed852e2009-09-05 21:47:34 +0000871 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000872 if (artifact != (const char *) NULL)
873 {
cristy9af9b5d2010-08-15 17:04:28 +0000874 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000875 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000876 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000877 filter_type=(FilterTypes) option;
878 window_type=BoxFilter;
879 }
880 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000881 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000882 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000883 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000884 }
nicolas07bac812010-09-19 18:47:02 +0000885 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000886 artifact=GetImageArtifact(image,"filter:window");
887 if (artifact != (const char *) NULL)
888 {
cristy9af9b5d2010-08-15 17:04:28 +0000889 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000890 if ((UndefinedFilter < option) && (option < SentinelFilter))
891 {
892 if (option != LanczosFilter)
893 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000894 else
anthony48f77622010-10-03 14:32:31 +0000895 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000896 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000897 }
cristy33b1c162010-01-23 22:51:51 +0000898 }
cristy3ed852e2009-09-05 21:47:34 +0000899 }
cristy33b1c162010-01-23 22:51:51 +0000900 else
901 {
anthony48f77622010-10-03 14:32:31 +0000902 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000903 artifact=GetImageArtifact(image,"filter:window");
904 if (artifact != (const char *) NULL)
905 {
906 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
907 artifact);
908 if ((UndefinedFilter < option) && (option < SentinelFilter))
909 {
anthony61b5ddd2010-10-05 02:33:31 +0000910 filter_type=cylindrical != MagickFalse ?
911 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000912 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000913 }
914 }
915 }
nicolas07bac812010-09-19 18:47:02 +0000916 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000917 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000918 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000919 resize_filter->window=filters[window_type].function;
920 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000921 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000922
anthonyf5e76ef2010-10-12 01:22:01 +0000923 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000924 if (cylindrical != MagickFalse)
925 switch (filter_type)
926 {
927 case PointFilter:
928 case BoxFilter:
929 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000930 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000931 break;
anthony81b8bf92010-10-02 13:54:34 +0000932 default:
933 break;
anthony10b8bc82010-10-02 12:48:46 +0000934 }
anthony61b5ddd2010-10-05 02:33:31 +0000935 else
936 switch (filter_type)
937 {
938 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000939 case Lanczos2DSharpFilter:
anthony853d6972010-10-08 06:01:31 +0000940 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000941 resize_filter->filter=SincFast;
942 break;
943 default:
944 break;
945 }
946
anthonyf5e76ef2010-10-12 01:22:01 +0000947 /*
948 ** More Expert Option Modifications
949 */
950
951 /* User Sigma Override - no support change */
952 artifact=GetImageArtifact(image,"filter:sigma");
953 if (artifact != (const char *) NULL)
954 sigma=StringToDouble(artifact);
955 /* Define coefficents for Gaussian (assumes no cubic window) */
956 if ( GaussianFilter ) {
957 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
cristy68f103e2010-10-14 01:19:07 +0000958 resize_filter->coeff[1] = (MagickRealType) (1.0/(Magick2PI*sigma*sigma)); /* unused */
anthonyf5e76ef2010-10-12 01:22:01 +0000959 }
960
961 /* Blur Override */
962 artifact=GetImageArtifact(image,"filter:blur");
963 if (artifact != (const char *) NULL)
964 resize_filter->blur=StringToDouble(artifact);
965 if (resize_filter->blur < MagickEpsilon)
966 resize_filter->blur=(MagickRealType) MagickEpsilon;
967
968 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000969 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000970 if (artifact != (const char *) NULL)
971 {
cristybb503372010-05-27 20:51:26 +0000972 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000973 lobes;
974
cristy96b16132010-08-29 17:19:52 +0000975 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000976 if (lobes < 1)
977 lobes=1;
978 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000979 }
anthony61b5ddd2010-10-05 02:33:31 +0000980 /* convert Jinc lobes to a real support value */
981 if (resize_filter->filter == Jinc)
982 {
983 if (resize_filter->support > 16)
984 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
985 else
986 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
987 }
988 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000989 artifact=GetImageArtifact(image,"filter:support");
990 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000991 resize_filter->support=fabs(StringToDouble(artifact));
992 /*
nicolas07bac812010-09-19 18:47:02 +0000993 Scale windowing function separatally to the support 'clipping'
994 window that calling operator is planning to actually use. (Expert
995 override)
cristy3ed852e2009-09-05 21:47:34 +0000996 */
anthony55f12332010-09-10 01:13:02 +0000997 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000998 artifact=GetImageArtifact(image,"filter:win-support");
999 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001000 resize_filter->window_support=fabs(StringToDouble(artifact));
1001 /*
anthony1f90a6b2010-09-14 08:56:31 +00001002 Adjust window function scaling to the windowing support for
1003 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +00001004 */
1005 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +00001006
anthony55f12332010-09-10 01:13:02 +00001007 /*
anthonyf5e76ef2010-10-12 01:22:01 +00001008 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +00001009 */
cristy3ed852e2009-09-05 21:47:34 +00001010 B=0.0;
1011 C=0.0;
cristy33b1c162010-01-23 22:51:51 +00001012 if ((filters[filter_type].function == CubicBC) ||
1013 (filters[window_type].function == CubicBC))
1014 {
anthony2d9b8b52010-09-14 08:31:07 +00001015 B=filters[filter_type].B;
1016 C=filters[filter_type].C;
1017 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +00001018 {
anthony2d9b8b52010-09-14 08:31:07 +00001019 B=filters[window_type].B;
1020 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +00001021 }
cristy33b1c162010-01-23 22:51:51 +00001022 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001023 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001024 {
1025 B=StringToDouble(artifact);
nicolasd15ee9a2010-10-24 18:39:45 +00001026 C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001027 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001028 if (artifact != (const char *) NULL)
1029 C=StringToDouble(artifact);
1030 }
1031 else
1032 {
1033 artifact=GetImageArtifact(image,"filter:c");
1034 if (artifact != (const char *) NULL)
1035 {
1036 C=StringToDouble(artifact);
nicolasd15ee9a2010-10-24 18:39:45 +00001037 B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001038 }
1039 }
nicolasc6bac3b2010-10-24 18:10:45 +00001040 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
nicolasd15ee9a2010-10-24 18:39:45 +00001041 {
1042 const double twoB = B+B;
1043 resize_filter->coeff[0]=1.0-(1.0/3.0)*B;
nicolase3b9eca2010-10-24 19:48:22 +00001044 resize_filter->coeff[1]=-3.0+twoB+C;
1045 resize_filter->coeff[2]=2.0-1.5*B-C;
1046 resize_filter->coeff[3]=(4.0/3.0)*B+4.0*C;
1047 resize_filter->coeff[4]=-8.0*C-twoB;
1048 resize_filter->coeff[5]=B+5.0*C;
1049 resize_filter->coeff[6]=(-1.0/6.0)*B-C;
nicolasd15ee9a2010-10-24 18:39:45 +00001050 }
nicolasc6bac3b2010-10-24 18:10:45 +00001051 }
anthonyf5e76ef2010-10-12 01:22:01 +00001052
anthony55f12332010-09-10 01:13:02 +00001053 /*
nicolas07bac812010-09-19 18:47:02 +00001054 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001055 */
cristyf5b49372010-10-16 01:06:47 +00001056#if defined(MAGICKCORE_OPENMP_SUPPORT)
1057 #pragma omp master
1058 {
1059#endif
1060 artifact=GetImageArtifact(image,"filter:verbose");
1061 if (artifact != (const char *) NULL)
1062 {
1063 double
anthony450db502010-10-19 04:03:03 +00001064 support,
cristyf5b49372010-10-16 01:06:47 +00001065 x;
cristy3ed852e2009-09-05 21:47:34 +00001066
cristyf5b49372010-10-16 01:06:47 +00001067 /*
1068 Set the weighting function properly when the weighting
1069 function may not exactly match the filter of the same name.
1070 EG: a Point filter really uses a Box weighting function
1071 with a different support than is typically used.
anthony463be1d2010-09-26 01:07:36 +00001072
cristyf5b49372010-10-16 01:06:47 +00001073 */
1074 if (resize_filter->filter == Box) filter_type=BoxFilter;
1075 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1076 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1077 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1078 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1079 /*
1080 Report Filter Details.
1081 */
1082 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1083 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
1084 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1085 MagickFilterOptions,filter_type));
1086 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
1087 MagickFilterOptions, window_type));
1088 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
1089 (double) resize_filter->support);
1090 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
1091 (double) resize_filter->window_support);
1092 (void) fprintf(stdout,"# scale_blur = %.*g\n",GetMagickPrecision(),
1093 (double) resize_filter->blur);
1094 if ( filter_type == GaussianFilter )
1095 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",GetMagickPrecision(),
1096 (double) sigma);
1097 (void) fprintf(stdout,"# practical_support = %.*g\n",GetMagickPrecision(),
1098 (double) support);
1099 if ( filter_type == CubicFilter || window_type == CubicFilter )
1100 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1101 (double) B,GetMagickPrecision(),(double) C);
1102 (void) fprintf(stdout,"\n");
1103 /*
1104 Output values of resulting filter graph -- for graphing
1105 filter result.
1106 */
1107 for (x=0.0; x <= support; x+=0.01f)
1108 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1109 (double) GetResizeFilterWeight(resize_filter,x));
1110 /* A final value so gnuplot can graph the 'stop' properly. */
1111 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1112 0.0);
1113 }
1114 /* Output the above once only for each image - remove setting */
1115 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1116#if defined(MAGICKCORE_OPENMP_SUPPORT)
1117 }
1118#endif
cristy3ed852e2009-09-05 21:47:34 +00001119 return(resize_filter);
1120}
1121
1122/*
1123%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1124% %
1125% %
1126% %
1127% A d a p t i v e R e s i z e I m a g e %
1128% %
1129% %
1130% %
1131%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1132%
1133% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1134%
1135% The format of the AdaptiveResizeImage method is:
1136%
cristy9af9b5d2010-08-15 17:04:28 +00001137% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1138% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001139%
1140% A description of each parameter follows:
1141%
1142% o image: the image.
1143%
1144% o columns: the number of columns in the resized image.
1145%
1146% o rows: the number of rows in the resized image.
1147%
1148% o exception: return any errors or warnings in this structure.
1149%
1150*/
1151MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001152 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001153{
1154#define AdaptiveResizeImageTag "Resize/Image"
1155
cristyc4c8d132010-01-07 01:58:38 +00001156 CacheView
1157 *resize_view;
1158
cristy3ed852e2009-09-05 21:47:34 +00001159 Image
1160 *resize_image;
1161
cristy3ed852e2009-09-05 21:47:34 +00001162 MagickBooleanType
1163 proceed;
1164
1165 MagickPixelPacket
1166 pixel;
1167
1168 PointInfo
1169 offset;
1170
1171 ResampleFilter
1172 *resample_filter;
1173
cristy9af9b5d2010-08-15 17:04:28 +00001174 ssize_t
1175 y;
1176
cristy3ed852e2009-09-05 21:47:34 +00001177 /*
1178 Adaptively resize image.
1179 */
1180 assert(image != (const Image *) NULL);
1181 assert(image->signature == MagickSignature);
1182 if (image->debug != MagickFalse)
1183 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1184 assert(exception != (ExceptionInfo *) NULL);
1185 assert(exception->signature == MagickSignature);
1186 if ((columns == 0) || (rows == 0))
1187 return((Image *) NULL);
1188 if ((columns == image->columns) && (rows == image->rows))
1189 return(CloneImage(image,0,0,MagickTrue,exception));
1190 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1191 if (resize_image == (Image *) NULL)
1192 return((Image *) NULL);
1193 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1194 {
1195 InheritException(exception,&resize_image->exception);
1196 resize_image=DestroyImage(resize_image);
1197 return((Image *) NULL);
1198 }
1199 GetMagickPixelPacket(image,&pixel);
1200 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001201 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001202 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001203 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001204 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001205 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001206 {
1207 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001208 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001209
cristybb503372010-05-27 20:51:26 +00001210 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001211 x;
1212
1213 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001214 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001215
1216 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1217 exception);
1218 if (q == (PixelPacket *) NULL)
1219 break;
1220 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1221 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001222 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001223 {
1224 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1225 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1226 &pixel);
1227 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1228 q++;
1229 }
1230 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1231 break;
cristy96b16132010-08-29 17:19:52 +00001232 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1233 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001234 if (proceed == MagickFalse)
1235 break;
1236 }
1237 resample_filter=DestroyResampleFilter(resample_filter);
1238 resize_view=DestroyCacheView(resize_view);
1239 return(resize_image);
1240}
1241
1242/*
1243%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1244% %
1245% %
1246% %
1247+ B e s s e l O r d e r O n e %
1248% %
1249% %
1250% %
1251%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1252%
1253% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001254% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001255%
1256% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1257%
1258% j1(x) = x*j1(x);
1259%
1260% For x in (8,inf)
1261%
1262% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1263%
1264% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1265%
1266% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1267% = 1/sqrt(2) * (sin(x) - cos(x))
1268% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1269% = -1/sqrt(2) * (sin(x) + cos(x))
1270%
1271% The format of the BesselOrderOne method is:
1272%
1273% MagickRealType BesselOrderOne(MagickRealType x)
1274%
1275% A description of each parameter follows:
1276%
1277% o x: MagickRealType value.
1278%
1279*/
1280
1281#undef I0
1282static MagickRealType I0(MagickRealType x)
1283{
1284 MagickRealType
1285 sum,
1286 t,
1287 y;
1288
cristybb503372010-05-27 20:51:26 +00001289 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001290 i;
1291
1292 /*
1293 Zeroth order Bessel function of the first kind.
1294 */
1295 sum=1.0;
1296 y=x*x/4.0;
1297 t=y;
1298 for (i=2; t > MagickEpsilon; i++)
1299 {
1300 sum+=t;
1301 t*=y/((MagickRealType) i*i);
1302 }
1303 return(sum);
1304}
1305
1306#undef J1
1307static MagickRealType J1(MagickRealType x)
1308{
1309 MagickRealType
1310 p,
1311 q;
1312
cristybb503372010-05-27 20:51:26 +00001313 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001314 i;
1315
1316 static const double
1317 Pone[] =
1318 {
1319 0.581199354001606143928050809e+21,
1320 -0.6672106568924916298020941484e+20,
1321 0.2316433580634002297931815435e+19,
1322 -0.3588817569910106050743641413e+17,
1323 0.2908795263834775409737601689e+15,
1324 -0.1322983480332126453125473247e+13,
1325 0.3413234182301700539091292655e+10,
1326 -0.4695753530642995859767162166e+7,
1327 0.270112271089232341485679099e+4
1328 },
1329 Qone[] =
1330 {
1331 0.11623987080032122878585294e+22,
1332 0.1185770712190320999837113348e+20,
1333 0.6092061398917521746105196863e+17,
1334 0.2081661221307607351240184229e+15,
1335 0.5243710262167649715406728642e+12,
1336 0.1013863514358673989967045588e+10,
1337 0.1501793594998585505921097578e+7,
1338 0.1606931573481487801970916749e+4,
1339 0.1e+1
1340 };
1341
1342 p=Pone[8];
1343 q=Qone[8];
1344 for (i=7; i >= 0; i--)
1345 {
1346 p=p*x*x+Pone[i];
1347 q=q*x*x+Qone[i];
1348 }
1349 return(p/q);
1350}
1351
1352#undef P1
1353static MagickRealType P1(MagickRealType x)
1354{
1355 MagickRealType
1356 p,
1357 q;
1358
cristybb503372010-05-27 20:51:26 +00001359 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001360 i;
1361
1362 static const double
1363 Pone[] =
1364 {
1365 0.352246649133679798341724373e+5,
1366 0.62758845247161281269005675e+5,
1367 0.313539631109159574238669888e+5,
1368 0.49854832060594338434500455e+4,
1369 0.2111529182853962382105718e+3,
1370 0.12571716929145341558495e+1
1371 },
1372 Qone[] =
1373 {
1374 0.352246649133679798068390431e+5,
1375 0.626943469593560511888833731e+5,
1376 0.312404063819041039923015703e+5,
1377 0.4930396490181088979386097e+4,
1378 0.2030775189134759322293574e+3,
1379 0.1e+1
1380 };
1381
1382 p=Pone[5];
1383 q=Qone[5];
1384 for (i=4; i >= 0; i--)
1385 {
1386 p=p*(8.0/x)*(8.0/x)+Pone[i];
1387 q=q*(8.0/x)*(8.0/x)+Qone[i];
1388 }
1389 return(p/q);
1390}
1391
1392#undef Q1
1393static MagickRealType Q1(MagickRealType x)
1394{
1395 MagickRealType
1396 p,
1397 q;
1398
cristybb503372010-05-27 20:51:26 +00001399 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001400 i;
1401
1402 static const double
1403 Pone[] =
1404 {
1405 0.3511751914303552822533318e+3,
1406 0.7210391804904475039280863e+3,
1407 0.4259873011654442389886993e+3,
1408 0.831898957673850827325226e+2,
1409 0.45681716295512267064405e+1,
1410 0.3532840052740123642735e-1
1411 },
1412 Qone[] =
1413 {
1414 0.74917374171809127714519505e+4,
1415 0.154141773392650970499848051e+5,
1416 0.91522317015169922705904727e+4,
1417 0.18111867005523513506724158e+4,
1418 0.1038187585462133728776636e+3,
1419 0.1e+1
1420 };
1421
1422 p=Pone[5];
1423 q=Qone[5];
1424 for (i=4; i >= 0; i--)
1425 {
1426 p=p*(8.0/x)*(8.0/x)+Pone[i];
1427 q=q*(8.0/x)*(8.0/x)+Qone[i];
1428 }
1429 return(p/q);
1430}
1431
1432static MagickRealType BesselOrderOne(MagickRealType x)
1433{
1434 MagickRealType
1435 p,
1436 q;
1437
1438 if (x == 0.0)
1439 return(0.0);
1440 p=x;
1441 if (x < 0.0)
1442 x=(-x);
1443 if (x < 8.0)
1444 return(p*J1(x));
1445 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1446 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1447 cos((double) x))));
1448 if (p < 0.0)
1449 q=(-q);
1450 return(q);
1451}
1452
1453/*
1454%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1455% %
1456% %
1457% %
1458+ D e s t r o y R e s i z e F i l t e r %
1459% %
1460% %
1461% %
1462%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1463%
1464% DestroyResizeFilter() destroy the resize filter.
1465%
cristya2ffd7e2010-03-10 20:50:30 +00001466% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001467%
1468% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1469%
1470% A description of each parameter follows:
1471%
1472% o resize_filter: the resize filter.
1473%
1474*/
1475MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1476{
1477 assert(resize_filter != (ResizeFilter *) NULL);
1478 assert(resize_filter->signature == MagickSignature);
1479 resize_filter->signature=(~MagickSignature);
1480 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1481 return(resize_filter);
1482}
1483
1484/*
1485%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1486% %
1487% %
1488% %
1489+ G e t R e s i z e F i l t e r S u p p o r t %
1490% %
1491% %
1492% %
1493%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1494%
1495% GetResizeFilterSupport() return the current support window size for this
1496% filter. Note that this may have been enlarged by filter:blur factor.
1497%
1498% The format of the GetResizeFilterSupport method is:
1499%
1500% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1501%
1502% A description of each parameter follows:
1503%
1504% o filter: Image filter to use.
1505%
1506*/
1507MagickExport MagickRealType GetResizeFilterSupport(
1508 const ResizeFilter *resize_filter)
1509{
1510 assert(resize_filter != (ResizeFilter *) NULL);
1511 assert(resize_filter->signature == MagickSignature);
1512 return(resize_filter->support*resize_filter->blur);
1513}
1514
1515/*
1516%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1517% %
1518% %
1519% %
1520+ G e t R e s i z e F i l t e r W e i g h t %
1521% %
1522% %
1523% %
1524%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1525%
1526% GetResizeFilterWeight evaluates the specified resize filter at the point x
1527% which usally lies between zero and the filters current 'support' and
1528% returns the weight of the filter function at that point.
1529%
1530% The format of the GetResizeFilterWeight method is:
1531%
1532% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1533% const MagickRealType x)
1534%
1535% A description of each parameter follows:
1536%
1537% o filter: the filter type.
1538%
1539% o x: the point.
1540%
1541*/
1542MagickExport MagickRealType GetResizeFilterWeight(
1543 const ResizeFilter *resize_filter,const MagickRealType x)
1544{
1545 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001546 scale,
1547 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001548
1549 /*
1550 Windowing function - scale the weighting filter by this amount.
1551 */
1552 assert(resize_filter != (ResizeFilter *) NULL);
1553 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001554 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001555 if ((resize_filter->window_support < MagickEpsilon) ||
1556 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001557 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001558 else
1559 {
anthony55f12332010-09-10 01:13:02 +00001560 scale=resize_filter->scale;
1561 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001562 }
anthony55f12332010-09-10 01:13:02 +00001563 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001564}
1565
1566/*
1567%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1568% %
1569% %
1570% %
1571% M a g n i f y I m a g e %
1572% %
1573% %
1574% %
1575%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1576%
1577% MagnifyImage() is a convenience method that scales an image proportionally
1578% to twice its size.
1579%
1580% The format of the MagnifyImage method is:
1581%
1582% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1583%
1584% A description of each parameter follows:
1585%
1586% o image: the image.
1587%
1588% o exception: return any errors or warnings in this structure.
1589%
1590*/
1591MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1592{
1593 Image
1594 *magnify_image;
1595
1596 assert(image != (Image *) NULL);
1597 assert(image->signature == MagickSignature);
1598 if (image->debug != MagickFalse)
1599 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1600 assert(exception != (ExceptionInfo *) NULL);
1601 assert(exception->signature == MagickSignature);
1602 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1603 1.0,exception);
1604 return(magnify_image);
1605}
1606
1607/*
1608%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1609% %
1610% %
1611% %
1612% M i n i f y I m a g e %
1613% %
1614% %
1615% %
1616%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1617%
1618% MinifyImage() is a convenience method that scales an image proportionally
1619% to half its size.
1620%
1621% The format of the MinifyImage method is:
1622%
1623% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1624%
1625% A description of each parameter follows:
1626%
1627% o image: the image.
1628%
1629% o exception: return any errors or warnings in this structure.
1630%
1631*/
1632MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1633{
1634 Image
1635 *minify_image;
1636
1637 assert(image != (Image *) NULL);
1638 assert(image->signature == MagickSignature);
1639 if (image->debug != MagickFalse)
1640 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1641 assert(exception != (ExceptionInfo *) NULL);
1642 assert(exception->signature == MagickSignature);
1643 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1644 1.0,exception);
1645 return(minify_image);
1646}
1647
1648/*
1649%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1650% %
1651% %
1652% %
1653% R e s a m p l e I m a g e %
1654% %
1655% %
1656% %
1657%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1658%
1659% ResampleImage() resize image in terms of its pixel size, so that when
1660% displayed at the given resolution it will be the same size in terms of
1661% real world units as the original image at the original resolution.
1662%
1663% The format of the ResampleImage method is:
1664%
1665% Image *ResampleImage(Image *image,const double x_resolution,
1666% const double y_resolution,const FilterTypes filter,const double blur,
1667% ExceptionInfo *exception)
1668%
1669% A description of each parameter follows:
1670%
1671% o image: the image to be resized to fit the given resolution.
1672%
1673% o x_resolution: the new image x resolution.
1674%
1675% o y_resolution: the new image y resolution.
1676%
1677% o filter: Image filter to use.
1678%
1679% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1680%
1681*/
1682MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1683 const double y_resolution,const FilterTypes filter,const double blur,
1684 ExceptionInfo *exception)
1685{
1686#define ResampleImageTag "Resample/Image"
1687
1688 Image
1689 *resample_image;
1690
cristybb503372010-05-27 20:51:26 +00001691 size_t
cristy3ed852e2009-09-05 21:47:34 +00001692 height,
1693 width;
1694
1695 /*
1696 Initialize sampled image attributes.
1697 */
1698 assert(image != (const Image *) NULL);
1699 assert(image->signature == MagickSignature);
1700 if (image->debug != MagickFalse)
1701 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1702 assert(exception != (ExceptionInfo *) NULL);
1703 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001704 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1705 72.0 : image->x_resolution)+0.5);
1706 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1707 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001708 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1709 if (resample_image != (Image *) NULL)
1710 {
1711 resample_image->x_resolution=x_resolution;
1712 resample_image->y_resolution=y_resolution;
1713 }
1714 return(resample_image);
1715}
1716#if defined(MAGICKCORE_LQR_DELEGATE)
1717
1718/*
1719%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1720% %
1721% %
1722% %
1723% L i q u i d R e s c a l e I m a g e %
1724% %
1725% %
1726% %
1727%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1728%
1729% LiquidRescaleImage() rescales image with seam carving.
1730%
1731% The format of the LiquidRescaleImage method is:
1732%
1733% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001734% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001735% const double delta_x,const double rigidity,ExceptionInfo *exception)
1736%
1737% A description of each parameter follows:
1738%
1739% o image: the image.
1740%
1741% o columns: the number of columns in the rescaled image.
1742%
1743% o rows: the number of rows in the rescaled image.
1744%
1745% o delta_x: maximum seam transversal step (0 means straight seams).
1746%
1747% o rigidity: introduce a bias for non-straight seams (typically 0).
1748%
1749% o exception: return any errors or warnings in this structure.
1750%
1751*/
cristy9af9b5d2010-08-15 17:04:28 +00001752MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1753 const size_t rows,const double delta_x,const double rigidity,
1754 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001755{
1756#define LiquidRescaleImageTag "Rescale/Image"
1757
cristyc5c6f662010-09-22 14:23:02 +00001758 CacheView
1759 *rescale_view;
1760
cristy3ed852e2009-09-05 21:47:34 +00001761 const char
1762 *map;
1763
1764 guchar
1765 *packet;
1766
1767 Image
1768 *rescale_image;
1769
1770 int
1771 x,
1772 y;
1773
1774 LqrCarver
1775 *carver;
1776
1777 LqrRetVal
1778 lqr_status;
1779
1780 MagickBooleanType
1781 status;
1782
1783 MagickPixelPacket
1784 pixel;
1785
1786 unsigned char
1787 *pixels;
1788
1789 /*
1790 Liquid rescale image.
1791 */
1792 assert(image != (const Image *) NULL);
1793 assert(image->signature == MagickSignature);
1794 if (image->debug != MagickFalse)
1795 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1796 assert(exception != (ExceptionInfo *) NULL);
1797 assert(exception->signature == MagickSignature);
1798 if ((columns == 0) || (rows == 0))
1799 return((Image *) NULL);
1800 if ((columns == image->columns) && (rows == image->rows))
1801 return(CloneImage(image,0,0,MagickTrue,exception));
1802 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001803 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001804 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1805 {
1806 Image
1807 *resize_image;
1808
cristybb503372010-05-27 20:51:26 +00001809 size_t
cristy3ed852e2009-09-05 21:47:34 +00001810 height,
1811 width;
1812
1813 /*
1814 Honor liquid resize size limitations.
1815 */
1816 for (width=image->columns; columns >= (2*width-1); width*=2);
1817 for (height=image->rows; rows >= (2*height-1); height*=2);
1818 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1819 exception);
1820 if (resize_image == (Image *) NULL)
1821 return((Image *) NULL);
1822 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1823 rigidity,exception);
1824 resize_image=DestroyImage(resize_image);
1825 return(rescale_image);
1826 }
1827 map="RGB";
1828 if (image->matte == MagickFalse)
1829 map="RGBA";
1830 if (image->colorspace == CMYKColorspace)
1831 {
1832 map="CMYK";
1833 if (image->matte == MagickFalse)
1834 map="CMYKA";
1835 }
1836 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1837 strlen(map)*sizeof(*pixels));
1838 if (pixels == (unsigned char *) NULL)
1839 return((Image *) NULL);
1840 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1841 pixels,exception);
1842 if (status == MagickFalse)
1843 {
1844 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1845 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1846 }
1847 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1848 if (carver == (LqrCarver *) NULL)
1849 {
1850 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1851 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1852 }
1853 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1854 lqr_status=lqr_carver_resize(carver,columns,rows);
1855 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1856 lqr_carver_get_height(carver),MagickTrue,exception);
1857 if (rescale_image == (Image *) NULL)
1858 {
1859 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1860 return((Image *) NULL);
1861 }
1862 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1863 {
1864 InheritException(exception,&rescale_image->exception);
1865 rescale_image=DestroyImage(rescale_image);
1866 return((Image *) NULL);
1867 }
1868 GetMagickPixelPacket(rescale_image,&pixel);
1869 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001870 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001871 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1872 {
1873 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001874 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001875
1876 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001877 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001878
anthony22aad252010-09-23 06:59:07 +00001879 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001880 if (q == (PixelPacket *) NULL)
1881 break;
cristyc5c6f662010-09-22 14:23:02 +00001882 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001883 pixel.red=QuantumRange*(packet[0]/255.0);
1884 pixel.green=QuantumRange*(packet[1]/255.0);
1885 pixel.blue=QuantumRange*(packet[2]/255.0);
1886 if (image->colorspace != CMYKColorspace)
1887 {
1888 if (image->matte == MagickFalse)
1889 pixel.opacity=QuantumRange*(packet[3]/255.0);
1890 }
1891 else
1892 {
1893 pixel.index=QuantumRange*(packet[3]/255.0);
1894 if (image->matte == MagickFalse)
1895 pixel.opacity=QuantumRange*(packet[4]/255.0);
1896 }
1897 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001898 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001899 break;
1900 }
cristyc5c6f662010-09-22 14:23:02 +00001901 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001902 /*
1903 Relinquish resources.
1904 */
1905 lqr_carver_destroy(carver);
1906 return(rescale_image);
1907}
1908#else
1909MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001910 const size_t magick_unused(columns),const size_t magick_unused(rows),
1911 const double magick_unused(delta_x),const double magick_unused(rigidity),
1912 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001913{
1914 assert(image != (const Image *) NULL);
1915 assert(image->signature == MagickSignature);
1916 if (image->debug != MagickFalse)
1917 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1918 assert(exception != (ExceptionInfo *) NULL);
1919 assert(exception->signature == MagickSignature);
1920 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1921 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1922 return((Image *) NULL);
1923}
1924#endif
1925
1926/*
1927%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1928% %
1929% %
1930% %
1931% R e s i z e I m a g e %
1932% %
1933% %
1934% %
1935%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1936%
1937% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001938% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001939%
1940% If an undefined filter is given the filter defaults to Mitchell for a
1941% colormapped image, a image with a matte channel, or if the image is
1942% enlarged. Otherwise the filter defaults to a Lanczos.
1943%
1944% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1945%
1946% The format of the ResizeImage method is:
1947%
cristybb503372010-05-27 20:51:26 +00001948% Image *ResizeImage(Image *image,const size_t columns,
1949% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001950% ExceptionInfo *exception)
1951%
1952% A description of each parameter follows:
1953%
1954% o image: the image.
1955%
1956% o columns: the number of columns in the scaled image.
1957%
1958% o rows: the number of rows in the scaled image.
1959%
1960% o filter: Image filter to use.
1961%
cristy9af9b5d2010-08-15 17:04:28 +00001962% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1963% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001964%
1965% o exception: return any errors or warnings in this structure.
1966%
1967*/
1968
1969typedef struct _ContributionInfo
1970{
1971 MagickRealType
1972 weight;
1973
cristybb503372010-05-27 20:51:26 +00001974 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001975 pixel;
1976} ContributionInfo;
1977
1978static ContributionInfo **DestroyContributionThreadSet(
1979 ContributionInfo **contribution)
1980{
cristybb503372010-05-27 20:51:26 +00001981 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001982 i;
1983
1984 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001985 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001986 if (contribution[i] != (ContributionInfo *) NULL)
1987 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1988 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001989 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001990 return(contribution);
1991}
1992
1993static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1994{
cristybb503372010-05-27 20:51:26 +00001995 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001996 i;
1997
1998 ContributionInfo
1999 **contribution;
2000
cristybb503372010-05-27 20:51:26 +00002001 size_t
cristy3ed852e2009-09-05 21:47:34 +00002002 number_threads;
2003
2004 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002005 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00002006 sizeof(*contribution));
2007 if (contribution == (ContributionInfo **) NULL)
2008 return((ContributionInfo **) NULL);
2009 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00002010 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002011 {
2012 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2013 sizeof(**contribution));
2014 if (contribution[i] == (ContributionInfo *) NULL)
2015 return(DestroyContributionThreadSet(contribution));
2016 }
2017 return(contribution);
2018}
2019
2020static inline double MagickMax(const double x,const double y)
2021{
2022 if (x > y)
2023 return(x);
2024 return(y);
2025}
2026
2027static inline double MagickMin(const double x,const double y)
2028{
2029 if (x < y)
2030 return(x);
2031 return(y);
2032}
2033
2034static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2035 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002036 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002037{
2038#define ResizeImageTag "Resize/Image"
2039
cristyfa112112010-01-04 17:48:07 +00002040 CacheView
2041 *image_view,
2042 *resize_view;
2043
cristy3ed852e2009-09-05 21:47:34 +00002044 ClassType
2045 storage_class;
2046
2047 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002048 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002049
cristy3ed852e2009-09-05 21:47:34 +00002050 MagickBooleanType
2051 status;
2052
2053 MagickPixelPacket
2054 zero;
2055
2056 MagickRealType
2057 scale,
2058 support;
2059
cristy9af9b5d2010-08-15 17:04:28 +00002060 ssize_t
2061 x;
2062
cristy3ed852e2009-09-05 21:47:34 +00002063 /*
2064 Apply filter to resize horizontally from image to resize image.
2065 */
cristy5d824382010-09-06 14:00:17 +00002066 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002067 support=scale*GetResizeFilterSupport(resize_filter);
2068 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2069 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2070 {
2071 InheritException(exception,&resize_image->exception);
2072 return(MagickFalse);
2073 }
2074 if (support < 0.5)
2075 {
2076 /*
nicolas07bac812010-09-19 18:47:02 +00002077 Support too small even for nearest neighbour: Reduce to point
2078 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002079 */
2080 support=(MagickRealType) 0.5;
2081 scale=1.0;
2082 }
2083 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2084 if (contributions == (ContributionInfo **) NULL)
2085 {
2086 (void) ThrowMagickException(exception,GetMagickModule(),
2087 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2088 return(MagickFalse);
2089 }
2090 status=MagickTrue;
2091 scale=1.0/scale;
2092 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2093 image_view=AcquireCacheView(image);
2094 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002095#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002096 #pragma omp parallel for shared(status)
2097#endif
cristybb503372010-05-27 20:51:26 +00002098 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002099 {
cristy3ed852e2009-09-05 21:47:34 +00002100 MagickRealType
2101 center,
2102 density;
2103
2104 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002105 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002106
2107 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002108 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002109
cristy03dbbd22010-09-19 23:04:47 +00002110 register ContributionInfo
2111 *restrict contribution;
2112
cristy3ed852e2009-09-05 21:47:34 +00002113 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002114 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002115
cristy3ed852e2009-09-05 21:47:34 +00002116 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002117 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002118
cristy03dbbd22010-09-19 23:04:47 +00002119 register ssize_t
2120 y;
2121
cristy9af9b5d2010-08-15 17:04:28 +00002122 ssize_t
2123 n,
2124 start,
2125 stop;
2126
cristy3ed852e2009-09-05 21:47:34 +00002127 if (status == MagickFalse)
2128 continue;
2129 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002130 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2131 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002132 density=0.0;
2133 contribution=contributions[GetOpenMPThreadId()];
2134 for (n=0; n < (stop-start); n++)
2135 {
2136 contribution[n].pixel=start+n;
2137 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2138 ((MagickRealType) (start+n)-center+0.5));
2139 density+=contribution[n].weight;
2140 }
2141 if ((density != 0.0) && (density != 1.0))
2142 {
cristybb503372010-05-27 20:51:26 +00002143 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002144 i;
2145
2146 /*
2147 Normalize.
2148 */
2149 density=1.0/density;
2150 for (i=0; i < n; i++)
2151 contribution[i].weight*=density;
2152 }
cristy9af9b5d2010-08-15 17:04:28 +00002153 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2154 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002155 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2156 exception);
2157 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2158 {
2159 status=MagickFalse;
2160 continue;
2161 }
2162 indexes=GetCacheViewVirtualIndexQueue(image_view);
2163 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002164 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002165 {
cristy3ed852e2009-09-05 21:47:34 +00002166 MagickPixelPacket
2167 pixel;
2168
2169 MagickRealType
2170 alpha;
2171
cristybb503372010-05-27 20:51:26 +00002172 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002173 i;
2174
cristy9af9b5d2010-08-15 17:04:28 +00002175 ssize_t
2176 j;
2177
cristy3ed852e2009-09-05 21:47:34 +00002178 pixel=zero;
2179 if (image->matte == MagickFalse)
2180 {
2181 for (i=0; i < n; i++)
2182 {
2183 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2184 (contribution[i].pixel-contribution[0].pixel);
2185 alpha=contribution[i].weight;
2186 pixel.red+=alpha*(p+j)->red;
2187 pixel.green+=alpha*(p+j)->green;
2188 pixel.blue+=alpha*(p+j)->blue;
2189 pixel.opacity+=alpha*(p+j)->opacity;
2190 }
cristyce70c172010-01-07 17:15:30 +00002191 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2192 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2193 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2194 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002195 if ((image->colorspace == CMYKColorspace) &&
2196 (resize_image->colorspace == CMYKColorspace))
2197 {
2198 for (i=0; i < n; i++)
2199 {
2200 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2201 (contribution[i].pixel-contribution[0].pixel);
2202 alpha=contribution[i].weight;
2203 pixel.index+=alpha*indexes[j];
2204 }
cristyce70c172010-01-07 17:15:30 +00002205 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002206 }
2207 }
2208 else
2209 {
2210 MagickRealType
2211 gamma;
2212
2213 gamma=0.0;
2214 for (i=0; i < n; i++)
2215 {
2216 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2217 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002218 alpha=contribution[i].weight*QuantumScale*
2219 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002220 pixel.red+=alpha*(p+j)->red;
2221 pixel.green+=alpha*(p+j)->green;
2222 pixel.blue+=alpha*(p+j)->blue;
2223 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2224 gamma+=alpha;
2225 }
2226 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002227 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2228 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2229 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2230 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002231 if ((image->colorspace == CMYKColorspace) &&
2232 (resize_image->colorspace == CMYKColorspace))
2233 {
2234 for (i=0; i < n; i++)
2235 {
2236 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2237 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002238 alpha=contribution[i].weight*QuantumScale*
2239 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002240 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002241 }
cristyce70c172010-01-07 17:15:30 +00002242 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2243 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002244 }
2245 }
2246 if ((resize_image->storage_class == PseudoClass) &&
2247 (image->storage_class == PseudoClass))
2248 {
cristybb503372010-05-27 20:51:26 +00002249 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002250 1.0)+0.5);
2251 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2252 (contribution[i-start].pixel-contribution[0].pixel);
2253 resize_indexes[y]=indexes[j];
2254 }
2255 q++;
2256 }
2257 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2258 status=MagickFalse;
2259 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2260 {
2261 MagickBooleanType
2262 proceed;
2263
cristyb5d5f722009-11-04 03:03:49 +00002264#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002265 #pragma omp critical (MagickCore_HorizontalFilter)
2266#endif
cristy9af9b5d2010-08-15 17:04:28 +00002267 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002268 if (proceed == MagickFalse)
2269 status=MagickFalse;
2270 }
2271 }
2272 resize_view=DestroyCacheView(resize_view);
2273 image_view=DestroyCacheView(image_view);
2274 contributions=DestroyContributionThreadSet(contributions);
2275 return(status);
2276}
2277
2278static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2279 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002280 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002281{
cristyfa112112010-01-04 17:48:07 +00002282 CacheView
2283 *image_view,
2284 *resize_view;
2285
cristy3ed852e2009-09-05 21:47:34 +00002286 ClassType
2287 storage_class;
2288
2289 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002290 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002291
cristy3ed852e2009-09-05 21:47:34 +00002292 MagickBooleanType
2293 status;
2294
2295 MagickPixelPacket
2296 zero;
2297
2298 MagickRealType
2299 scale,
2300 support;
2301
cristy9af9b5d2010-08-15 17:04:28 +00002302 ssize_t
2303 y;
2304
cristy3ed852e2009-09-05 21:47:34 +00002305 /*
cristy9af9b5d2010-08-15 17:04:28 +00002306 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002307 */
cristy5d824382010-09-06 14:00:17 +00002308 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002309 support=scale*GetResizeFilterSupport(resize_filter);
2310 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2311 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2312 {
2313 InheritException(exception,&resize_image->exception);
2314 return(MagickFalse);
2315 }
2316 if (support < 0.5)
2317 {
2318 /*
nicolas07bac812010-09-19 18:47:02 +00002319 Support too small even for nearest neighbour: Reduce to point
2320 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002321 */
2322 support=(MagickRealType) 0.5;
2323 scale=1.0;
2324 }
2325 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2326 if (contributions == (ContributionInfo **) NULL)
2327 {
2328 (void) ThrowMagickException(exception,GetMagickModule(),
2329 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2330 return(MagickFalse);
2331 }
2332 status=MagickTrue;
2333 scale=1.0/scale;
2334 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2335 image_view=AcquireCacheView(image);
2336 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002337#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002338 #pragma omp parallel for shared(status)
2339#endif
cristybb503372010-05-27 20:51:26 +00002340 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002341 {
cristy3ed852e2009-09-05 21:47:34 +00002342 MagickRealType
2343 center,
2344 density;
2345
2346 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002347 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002348
2349 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002350 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002351
cristy03dbbd22010-09-19 23:04:47 +00002352 register ContributionInfo
2353 *restrict contribution;
2354
cristy3ed852e2009-09-05 21:47:34 +00002355 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002356 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002357
cristy9af9b5d2010-08-15 17:04:28 +00002358 register PixelPacket
2359 *restrict q;
2360
cristybb503372010-05-27 20:51:26 +00002361 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002362 x;
2363
cristy9af9b5d2010-08-15 17:04:28 +00002364 ssize_t
2365 n,
2366 start,
2367 stop;
cristy3ed852e2009-09-05 21:47:34 +00002368
2369 if (status == MagickFalse)
2370 continue;
cristy679e6962010-03-18 00:42:45 +00002371 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002372 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2373 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002374 density=0.0;
2375 contribution=contributions[GetOpenMPThreadId()];
2376 for (n=0; n < (stop-start); n++)
2377 {
2378 contribution[n].pixel=start+n;
2379 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2380 ((MagickRealType) (start+n)-center+0.5));
2381 density+=contribution[n].weight;
2382 }
2383 if ((density != 0.0) && (density != 1.0))
2384 {
cristybb503372010-05-27 20:51:26 +00002385 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002386 i;
2387
2388 /*
2389 Normalize.
2390 */
2391 density=1.0/density;
2392 for (i=0; i < n; i++)
2393 contribution[i].weight*=density;
2394 }
2395 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002396 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2397 exception);
cristy3ed852e2009-09-05 21:47:34 +00002398 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2399 exception);
2400 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2401 {
2402 status=MagickFalse;
2403 continue;
2404 }
2405 indexes=GetCacheViewVirtualIndexQueue(image_view);
2406 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002407 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002408 {
cristy3ed852e2009-09-05 21:47:34 +00002409 MagickPixelPacket
2410 pixel;
2411
2412 MagickRealType
2413 alpha;
2414
cristybb503372010-05-27 20:51:26 +00002415 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002416 i;
2417
cristy9af9b5d2010-08-15 17:04:28 +00002418 ssize_t
2419 j;
2420
cristy3ed852e2009-09-05 21:47:34 +00002421 pixel=zero;
2422 if (image->matte == MagickFalse)
2423 {
2424 for (i=0; i < n; i++)
2425 {
cristybb503372010-05-27 20:51:26 +00002426 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002427 image->columns+x);
2428 alpha=contribution[i].weight;
2429 pixel.red+=alpha*(p+j)->red;
2430 pixel.green+=alpha*(p+j)->green;
2431 pixel.blue+=alpha*(p+j)->blue;
2432 pixel.opacity+=alpha*(p+j)->opacity;
2433 }
cristyce70c172010-01-07 17:15:30 +00002434 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2435 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2436 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2437 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002438 if ((image->colorspace == CMYKColorspace) &&
2439 (resize_image->colorspace == CMYKColorspace))
2440 {
2441 for (i=0; i < n; i++)
2442 {
cristybb503372010-05-27 20:51:26 +00002443 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002444 image->columns+x);
2445 alpha=contribution[i].weight;
2446 pixel.index+=alpha*indexes[j];
2447 }
cristyce70c172010-01-07 17:15:30 +00002448 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002449 }
2450 }
2451 else
2452 {
2453 MagickRealType
2454 gamma;
2455
2456 gamma=0.0;
2457 for (i=0; i < n; i++)
2458 {
cristybb503372010-05-27 20:51:26 +00002459 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002460 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002461 alpha=contribution[i].weight*QuantumScale*
2462 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002463 pixel.red+=alpha*(p+j)->red;
2464 pixel.green+=alpha*(p+j)->green;
2465 pixel.blue+=alpha*(p+j)->blue;
2466 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2467 gamma+=alpha;
2468 }
2469 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002470 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2471 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2472 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2473 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002474 if ((image->colorspace == CMYKColorspace) &&
2475 (resize_image->colorspace == CMYKColorspace))
2476 {
2477 for (i=0; i < n; i++)
2478 {
cristybb503372010-05-27 20:51:26 +00002479 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002480 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002481 alpha=contribution[i].weight*QuantumScale*
2482 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002483 pixel.index+=alpha*indexes[j];
2484 }
cristyce70c172010-01-07 17:15:30 +00002485 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2486 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002487 }
2488 }
2489 if ((resize_image->storage_class == PseudoClass) &&
2490 (image->storage_class == PseudoClass))
2491 {
cristybb503372010-05-27 20:51:26 +00002492 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002493 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002494 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002495 image->columns+x);
2496 resize_indexes[x]=indexes[j];
2497 }
2498 q++;
2499 }
2500 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2501 status=MagickFalse;
2502 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2503 {
2504 MagickBooleanType
2505 proceed;
2506
cristyb5d5f722009-11-04 03:03:49 +00002507#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002508 #pragma omp critical (MagickCore_VerticalFilter)
2509#endif
cristy9af9b5d2010-08-15 17:04:28 +00002510 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002511 if (proceed == MagickFalse)
2512 status=MagickFalse;
2513 }
2514 }
2515 resize_view=DestroyCacheView(resize_view);
2516 image_view=DestroyCacheView(image_view);
2517 contributions=DestroyContributionThreadSet(contributions);
2518 return(status);
2519}
2520
cristybb503372010-05-27 20:51:26 +00002521MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2522 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002523 ExceptionInfo *exception)
2524{
2525#define WorkLoadFactor 0.265
2526
2527 FilterTypes
2528 filter_type;
2529
2530 Image
2531 *filter_image,
2532 *resize_image;
2533
cristy9af9b5d2010-08-15 17:04:28 +00002534 MagickOffsetType
2535 offset;
2536
cristy3ed852e2009-09-05 21:47:34 +00002537 MagickRealType
2538 x_factor,
2539 y_factor;
2540
2541 MagickSizeType
2542 span;
2543
2544 MagickStatusType
2545 status;
2546
2547 ResizeFilter
2548 *resize_filter;
2549
cristy3ed852e2009-09-05 21:47:34 +00002550 /*
2551 Acquire resize image.
2552 */
2553 assert(image != (Image *) NULL);
2554 assert(image->signature == MagickSignature);
2555 if (image->debug != MagickFalse)
2556 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2557 assert(exception != (ExceptionInfo *) NULL);
2558 assert(exception->signature == MagickSignature);
2559 if ((columns == 0) || (rows == 0))
2560 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2561 if ((columns == image->columns) && (rows == image->rows) &&
2562 (filter == UndefinedFilter) && (blur == 1.0))
2563 return(CloneImage(image,0,0,MagickTrue,exception));
2564 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2565 if (resize_image == (Image *) NULL)
2566 return(resize_image);
2567 /*
2568 Acquire resize filter.
2569 */
2570 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2571 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2572 if ((x_factor*y_factor) > WorkLoadFactor)
2573 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2574 else
2575 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2576 if (filter_image == (Image *) NULL)
2577 return(DestroyImage(resize_image));
2578 filter_type=LanczosFilter;
2579 if (filter != UndefinedFilter)
2580 filter_type=filter;
2581 else
2582 if ((x_factor == 1.0) && (y_factor == 1.0))
2583 filter_type=PointFilter;
2584 else
2585 if ((image->storage_class == PseudoClass) ||
2586 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2587 filter_type=MitchellFilter;
2588 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2589 exception);
2590 /*
2591 Resize image.
2592 */
cristy9af9b5d2010-08-15 17:04:28 +00002593 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002594 if ((x_factor*y_factor) > WorkLoadFactor)
2595 {
2596 span=(MagickSizeType) (filter_image->columns+rows);
2597 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002598 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002599 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002600 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002601 }
2602 else
2603 {
2604 span=(MagickSizeType) (filter_image->rows+columns);
2605 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002606 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002607 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002608 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002609 }
2610 /*
2611 Free resources.
2612 */
2613 filter_image=DestroyImage(filter_image);
2614 resize_filter=DestroyResizeFilter(resize_filter);
2615 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2616 return((Image *) NULL);
2617 resize_image->type=image->type;
2618 return(resize_image);
2619}
2620
2621/*
2622%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2623% %
2624% %
2625% %
2626% S a m p l e I m a g e %
2627% %
2628% %
2629% %
2630%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2631%
2632% SampleImage() scales an image to the desired dimensions with pixel
2633% sampling. Unlike other scaling methods, this method does not introduce
2634% any additional color into the scaled image.
2635%
2636% The format of the SampleImage method is:
2637%
cristybb503372010-05-27 20:51:26 +00002638% Image *SampleImage(const Image *image,const size_t columns,
2639% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002640%
2641% A description of each parameter follows:
2642%
2643% o image: the image.
2644%
2645% o columns: the number of columns in the sampled image.
2646%
2647% o rows: the number of rows in the sampled image.
2648%
2649% o exception: return any errors or warnings in this structure.
2650%
2651*/
cristybb503372010-05-27 20:51:26 +00002652MagickExport Image *SampleImage(const Image *image,const size_t columns,
2653 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002654{
2655#define SampleImageTag "Sample/Image"
2656
cristyc4c8d132010-01-07 01:58:38 +00002657 CacheView
2658 *image_view,
2659 *sample_view;
2660
cristy3ed852e2009-09-05 21:47:34 +00002661 Image
2662 *sample_image;
2663
cristy3ed852e2009-09-05 21:47:34 +00002664 MagickBooleanType
2665 status;
2666
cristy5f959472010-05-27 22:19:46 +00002667 MagickOffsetType
2668 progress;
2669
cristybb503372010-05-27 20:51:26 +00002670 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002671 x;
2672
cristy5f959472010-05-27 22:19:46 +00002673 ssize_t
2674 *x_offset,
2675 y;
2676
cristy3ed852e2009-09-05 21:47:34 +00002677 /*
2678 Initialize sampled image attributes.
2679 */
2680 assert(image != (const Image *) NULL);
2681 assert(image->signature == MagickSignature);
2682 if (image->debug != MagickFalse)
2683 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2684 assert(exception != (ExceptionInfo *) NULL);
2685 assert(exception->signature == MagickSignature);
2686 if ((columns == 0) || (rows == 0))
2687 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2688 if ((columns == image->columns) && (rows == image->rows))
2689 return(CloneImage(image,0,0,MagickTrue,exception));
2690 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2691 if (sample_image == (Image *) NULL)
2692 return((Image *) NULL);
2693 /*
2694 Allocate scan line buffer and column offset buffers.
2695 */
cristybb503372010-05-27 20:51:26 +00002696 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002697 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002698 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002699 {
2700 sample_image=DestroyImage(sample_image);
2701 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2702 }
cristybb503372010-05-27 20:51:26 +00002703 for (x=0; x < (ssize_t) sample_image->columns; x++)
2704 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002705 sample_image->columns);
2706 /*
2707 Sample each row.
2708 */
2709 status=MagickTrue;
2710 progress=0;
2711 image_view=AcquireCacheView(image);
2712 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002713#if defined(MAGICKCORE_OPENMP_SUPPORT)
2714 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002715#endif
cristybb503372010-05-27 20:51:26 +00002716 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002717 {
cristy3ed852e2009-09-05 21:47:34 +00002718 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002719 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002720
2721 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002722 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002723
2724 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002725 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002726
cristy3ed852e2009-09-05 21:47:34 +00002727 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002728 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002729
cristy03dbbd22010-09-19 23:04:47 +00002730 register ssize_t
2731 x;
2732
cristy9af9b5d2010-08-15 17:04:28 +00002733 ssize_t
2734 y_offset;
2735
cristy3ed852e2009-09-05 21:47:34 +00002736 if (status == MagickFalse)
2737 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002738 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2739 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002740 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2741 exception);
2742 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2743 exception);
2744 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2745 {
2746 status=MagickFalse;
2747 continue;
2748 }
2749 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2750 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2751 /*
2752 Sample each column.
2753 */
cristybb503372010-05-27 20:51:26 +00002754 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002755 *q++=p[x_offset[x]];
2756 if ((image->storage_class == PseudoClass) ||
2757 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002758 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002759 sample_indexes[x]=indexes[x_offset[x]];
2760 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2761 status=MagickFalse;
2762 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2763 {
2764 MagickBooleanType
2765 proceed;
2766
cristyb5d5f722009-11-04 03:03:49 +00002767#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002768 #pragma omp critical (MagickCore_SampleImage)
2769#endif
2770 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2771 if (proceed == MagickFalse)
2772 status=MagickFalse;
2773 }
2774 }
2775 image_view=DestroyCacheView(image_view);
2776 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002777 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002778 sample_image->type=image->type;
2779 return(sample_image);
2780}
2781
2782/*
2783%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2784% %
2785% %
2786% %
2787% S c a l e I m a g e %
2788% %
2789% %
2790% %
2791%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2792%
2793% ScaleImage() changes the size of an image to the given dimensions.
2794%
2795% The format of the ScaleImage method is:
2796%
cristybb503372010-05-27 20:51:26 +00002797% Image *ScaleImage(const Image *image,const size_t columns,
2798% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002799%
2800% A description of each parameter follows:
2801%
2802% o image: the image.
2803%
2804% o columns: the number of columns in the scaled image.
2805%
2806% o rows: the number of rows in the scaled image.
2807%
2808% o exception: return any errors or warnings in this structure.
2809%
2810*/
cristybb503372010-05-27 20:51:26 +00002811MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2812 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002813{
2814#define ScaleImageTag "Scale/Image"
2815
cristyed6cb232010-01-20 03:07:53 +00002816 CacheView
2817 *image_view,
2818 *scale_view;
2819
cristy3ed852e2009-09-05 21:47:34 +00002820 Image
2821 *scale_image;
2822
cristy3ed852e2009-09-05 21:47:34 +00002823 MagickBooleanType
2824 next_column,
2825 next_row,
2826 proceed;
2827
2828 MagickPixelPacket
2829 pixel,
2830 *scale_scanline,
2831 *scanline,
2832 *x_vector,
2833 *y_vector,
2834 zero;
2835
cristy3ed852e2009-09-05 21:47:34 +00002836 PointInfo
2837 scale,
2838 span;
2839
cristybb503372010-05-27 20:51:26 +00002840 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002841 i;
2842
cristy9af9b5d2010-08-15 17:04:28 +00002843 ssize_t
2844 number_rows,
2845 y;
2846
cristy3ed852e2009-09-05 21:47:34 +00002847 /*
2848 Initialize scaled image attributes.
2849 */
2850 assert(image != (const Image *) NULL);
2851 assert(image->signature == MagickSignature);
2852 if (image->debug != MagickFalse)
2853 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2854 assert(exception != (ExceptionInfo *) NULL);
2855 assert(exception->signature == MagickSignature);
2856 if ((columns == 0) || (rows == 0))
2857 return((Image *) NULL);
2858 if ((columns == image->columns) && (rows == image->rows))
2859 return(CloneImage(image,0,0,MagickTrue,exception));
2860 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2861 if (scale_image == (Image *) NULL)
2862 return((Image *) NULL);
2863 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2864 {
2865 InheritException(exception,&scale_image->exception);
2866 scale_image=DestroyImage(scale_image);
2867 return((Image *) NULL);
2868 }
2869 /*
2870 Allocate memory.
2871 */
2872 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2873 sizeof(*x_vector));
2874 scanline=x_vector;
2875 if (image->rows != scale_image->rows)
2876 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2877 sizeof(*scanline));
2878 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2879 scale_image->columns,sizeof(*scale_scanline));
2880 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2881 sizeof(*y_vector));
2882 if ((scanline == (MagickPixelPacket *) NULL) ||
2883 (scale_scanline == (MagickPixelPacket *) NULL) ||
2884 (x_vector == (MagickPixelPacket *) NULL) ||
2885 (y_vector == (MagickPixelPacket *) NULL))
2886 {
2887 scale_image=DestroyImage(scale_image);
2888 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2889 }
2890 /*
2891 Scale image.
2892 */
2893 number_rows=0;
2894 next_row=MagickTrue;
2895 span.y=1.0;
2896 scale.y=(double) scale_image->rows/(double) image->rows;
2897 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2898 sizeof(*y_vector));
2899 GetMagickPixelPacket(image,&pixel);
2900 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2901 i=0;
cristyed6cb232010-01-20 03:07:53 +00002902 image_view=AcquireCacheView(image);
2903 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002904 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002905 {
2906 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002907 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002908
2909 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002910 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002911
2912 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002913 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002914
cristy3ed852e2009-09-05 21:47:34 +00002915 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002916 *restrict s,
2917 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002918
2919 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002920 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002921
cristy9af9b5d2010-08-15 17:04:28 +00002922 register ssize_t
2923 x;
2924
cristyed6cb232010-01-20 03:07:53 +00002925 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2926 exception);
cristy3ed852e2009-09-05 21:47:34 +00002927 if (q == (PixelPacket *) NULL)
2928 break;
2929 scale_indexes=GetAuthenticIndexQueue(scale_image);
2930 if (scale_image->rows == image->rows)
2931 {
2932 /*
2933 Read a new scanline.
2934 */
cristyed6cb232010-01-20 03:07:53 +00002935 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2936 exception);
cristy3ed852e2009-09-05 21:47:34 +00002937 if (p == (const PixelPacket *) NULL)
2938 break;
cristyed6cb232010-01-20 03:07:53 +00002939 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002940 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002941 {
cristyce70c172010-01-07 17:15:30 +00002942 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2943 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2944 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002945 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002946 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002947 if (indexes != (IndexPacket *) NULL)
2948 x_vector[x].index=(MagickRealType) indexes[x];
2949 p++;
2950 }
2951 }
2952 else
2953 {
2954 /*
2955 Scale Y direction.
2956 */
2957 while (scale.y < span.y)
2958 {
cristy9af9b5d2010-08-15 17:04:28 +00002959 if ((next_row != MagickFalse) &&
2960 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002961 {
2962 /*
2963 Read a new scanline.
2964 */
cristyed6cb232010-01-20 03:07:53 +00002965 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2966 exception);
cristy3ed852e2009-09-05 21:47:34 +00002967 if (p == (const PixelPacket *) NULL)
2968 break;
cristyed6cb232010-01-20 03:07:53 +00002969 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002970 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002971 {
cristyce70c172010-01-07 17:15:30 +00002972 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2973 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2974 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002975 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002976 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002977 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002978 if (indexes != (IndexPacket *) NULL)
2979 x_vector[x].index=(MagickRealType) indexes[x];
2980 p++;
2981 }
2982 number_rows++;
2983 }
cristybb503372010-05-27 20:51:26 +00002984 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002985 {
2986 y_vector[x].red+=scale.y*x_vector[x].red;
2987 y_vector[x].green+=scale.y*x_vector[x].green;
2988 y_vector[x].blue+=scale.y*x_vector[x].blue;
2989 if (scale_image->matte != MagickFalse)
2990 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2991 if (scale_indexes != (IndexPacket *) NULL)
2992 y_vector[x].index+=scale.y*x_vector[x].index;
2993 }
2994 span.y-=scale.y;
2995 scale.y=(double) scale_image->rows/(double) image->rows;
2996 next_row=MagickTrue;
2997 }
cristybb503372010-05-27 20:51:26 +00002998 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002999 {
3000 /*
3001 Read a new scanline.
3002 */
cristyed6cb232010-01-20 03:07:53 +00003003 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
3004 exception);
cristy3ed852e2009-09-05 21:47:34 +00003005 if (p == (const PixelPacket *) NULL)
3006 break;
cristyed6cb232010-01-20 03:07:53 +00003007 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00003008 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003009 {
cristyce70c172010-01-07 17:15:30 +00003010 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
3011 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
3012 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003013 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00003014 x_vector[x].opacity=(MagickRealType)
3015 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003016 if (indexes != (IndexPacket *) NULL)
3017 x_vector[x].index=(MagickRealType) indexes[x];
3018 p++;
3019 }
3020 number_rows++;
3021 next_row=MagickFalse;
3022 }
3023 s=scanline;
cristybb503372010-05-27 20:51:26 +00003024 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003025 {
3026 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3027 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3028 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3029 if (image->matte != MagickFalse)
3030 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3031 if (scale_indexes != (IndexPacket *) NULL)
3032 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3033 s->red=pixel.red;
3034 s->green=pixel.green;
3035 s->blue=pixel.blue;
3036 if (scale_image->matte != MagickFalse)
3037 s->opacity=pixel.opacity;
3038 if (scale_indexes != (IndexPacket *) NULL)
3039 s->index=pixel.index;
3040 s++;
3041 y_vector[x]=zero;
3042 }
3043 scale.y-=span.y;
3044 if (scale.y <= 0)
3045 {
3046 scale.y=(double) scale_image->rows/(double) image->rows;
3047 next_row=MagickTrue;
3048 }
3049 span.y=1.0;
3050 }
3051 if (scale_image->columns == image->columns)
3052 {
3053 /*
3054 Transfer scanline to scaled image.
3055 */
3056 s=scanline;
cristybb503372010-05-27 20:51:26 +00003057 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003058 {
cristyce70c172010-01-07 17:15:30 +00003059 q->red=ClampToQuantum(s->red);
3060 q->green=ClampToQuantum(s->green);
3061 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003062 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003063 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003064 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003065 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003066 q++;
3067 s++;
3068 }
3069 }
3070 else
3071 {
3072 /*
3073 Scale X direction.
3074 */
3075 pixel=zero;
3076 next_column=MagickFalse;
3077 span.x=1.0;
3078 s=scanline;
3079 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003080 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003081 {
3082 scale.x=(double) scale_image->columns/(double) image->columns;
3083 while (scale.x >= span.x)
3084 {
3085 if (next_column != MagickFalse)
3086 {
3087 pixel=zero;
3088 t++;
3089 }
3090 pixel.red+=span.x*s->red;
3091 pixel.green+=span.x*s->green;
3092 pixel.blue+=span.x*s->blue;
3093 if (image->matte != MagickFalse)
3094 pixel.opacity+=span.x*s->opacity;
3095 if (scale_indexes != (IndexPacket *) NULL)
3096 pixel.index+=span.x*s->index;
3097 t->red=pixel.red;
3098 t->green=pixel.green;
3099 t->blue=pixel.blue;
3100 if (scale_image->matte != MagickFalse)
3101 t->opacity=pixel.opacity;
3102 if (scale_indexes != (IndexPacket *) NULL)
3103 t->index=pixel.index;
3104 scale.x-=span.x;
3105 span.x=1.0;
3106 next_column=MagickTrue;
3107 }
3108 if (scale.x > 0)
3109 {
3110 if (next_column != MagickFalse)
3111 {
3112 pixel=zero;
3113 next_column=MagickFalse;
3114 t++;
3115 }
3116 pixel.red+=scale.x*s->red;
3117 pixel.green+=scale.x*s->green;
3118 pixel.blue+=scale.x*s->blue;
3119 if (scale_image->matte != MagickFalse)
3120 pixel.opacity+=scale.x*s->opacity;
3121 if (scale_indexes != (IndexPacket *) NULL)
3122 pixel.index+=scale.x*s->index;
3123 span.x-=scale.x;
3124 }
3125 s++;
3126 }
3127 if (span.x > 0)
3128 {
3129 s--;
3130 pixel.red+=span.x*s->red;
3131 pixel.green+=span.x*s->green;
3132 pixel.blue+=span.x*s->blue;
3133 if (scale_image->matte != MagickFalse)
3134 pixel.opacity+=span.x*s->opacity;
3135 if (scale_indexes != (IndexPacket *) NULL)
3136 pixel.index+=span.x*s->index;
3137 }
3138 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003139 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003140 {
3141 t->red=pixel.red;
3142 t->green=pixel.green;
3143 t->blue=pixel.blue;
3144 if (scale_image->matte != MagickFalse)
3145 t->opacity=pixel.opacity;
3146 if (scale_indexes != (IndexPacket *) NULL)
3147 t->index=pixel.index;
3148 }
3149 /*
3150 Transfer scanline to scaled image.
3151 */
3152 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003153 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003154 {
cristyce70c172010-01-07 17:15:30 +00003155 q->red=ClampToQuantum(t->red);
3156 q->green=ClampToQuantum(t->green);
3157 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003158 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003159 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003160 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003161 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003162 t++;
3163 q++;
3164 }
3165 }
cristyed6cb232010-01-20 03:07:53 +00003166 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003167 break;
cristy96b16132010-08-29 17:19:52 +00003168 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3169 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003170 if (proceed == MagickFalse)
3171 break;
3172 }
cristyed6cb232010-01-20 03:07:53 +00003173 scale_view=DestroyCacheView(scale_view);
3174 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003175 /*
3176 Free allocated memory.
3177 */
3178 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3179 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3180 if (scale_image->rows != image->rows)
3181 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3182 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3183 scale_image->type=image->type;
3184 return(scale_image);
3185}
3186
anthony02b4cb42010-10-10 04:54:35 +00003187#if 0
3188 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003189/*
3190%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3191% %
3192% %
3193% %
3194+ S e t R e s i z e F i l t e r S u p p o r t %
3195% %
3196% %
3197% %
3198%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3199%
3200% SetResizeFilterSupport() specifies which IR filter to use to window
3201%
3202% The format of the SetResizeFilterSupport method is:
3203%
3204% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3205% const MagickRealType support)
3206%
3207% A description of each parameter follows:
3208%
3209% o resize_filter: the resize filter.
3210%
3211% o support: the filter spport radius.
3212%
3213*/
3214MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3215 const MagickRealType support)
3216{
3217 assert(resize_filter != (ResizeFilter *) NULL);
3218 assert(resize_filter->signature == MagickSignature);
3219 resize_filter->support=support;
3220}
anthony02b4cb42010-10-10 04:54:35 +00003221#endif
cristy3ed852e2009-09-05 21:47:34 +00003222
3223/*
3224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3225% %
3226% %
3227% %
3228% T h u m b n a i l I m a g e %
3229% %
3230% %
3231% %
3232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3233%
3234% ThumbnailImage() changes the size of an image to the given dimensions and
3235% removes any associated profiles. The goal is to produce small low cost
3236% thumbnail images suited for display on the Web.
3237%
3238% The format of the ThumbnailImage method is:
3239%
cristybb503372010-05-27 20:51:26 +00003240% Image *ThumbnailImage(const Image *image,const size_t columns,
3241% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003242%
3243% A description of each parameter follows:
3244%
3245% o image: the image.
3246%
3247% o columns: the number of columns in the scaled image.
3248%
3249% o rows: the number of rows in the scaled image.
3250%
3251% o exception: return any errors or warnings in this structure.
3252%
3253*/
cristy9af9b5d2010-08-15 17:04:28 +00003254MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3255 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003256{
3257#define SampleFactor 5
3258
3259 char
3260 value[MaxTextExtent];
3261
3262 const char
3263 *name;
3264
3265 Image
3266 *thumbnail_image;
3267
3268 MagickRealType
3269 x_factor,
3270 y_factor;
3271
cristybb503372010-05-27 20:51:26 +00003272 size_t
cristy3ed852e2009-09-05 21:47:34 +00003273 version;
3274
cristy9af9b5d2010-08-15 17:04:28 +00003275 struct stat
3276 attributes;
3277
cristy3ed852e2009-09-05 21:47:34 +00003278 assert(image != (Image *) NULL);
3279 assert(image->signature == MagickSignature);
3280 if (image->debug != MagickFalse)
3281 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3282 assert(exception != (ExceptionInfo *) NULL);
3283 assert(exception->signature == MagickSignature);
3284 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3285 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3286 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003287 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3288 exception);
cristy3ed852e2009-09-05 21:47:34 +00003289 else
3290 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003291 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3292 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003293 else
3294 {
3295 Image
3296 *sample_image;
3297
3298 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3299 exception);
3300 if (sample_image == (Image *) NULL)
3301 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003302 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3303 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003304 sample_image=DestroyImage(sample_image);
3305 }
3306 if (thumbnail_image == (Image *) NULL)
3307 return(thumbnail_image);
3308 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3309 if (thumbnail_image->matte == MagickFalse)
3310 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3311 thumbnail_image->depth=8;
3312 thumbnail_image->interlace=NoInterlace;
3313 /*
3314 Strip all profiles except color profiles.
3315 */
3316 ResetImageProfileIterator(thumbnail_image);
3317 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3318 {
3319 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3320 {
cristy2b726bd2010-01-11 01:05:39 +00003321 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003322 ResetImageProfileIterator(thumbnail_image);
3323 }
3324 name=GetNextImageProfile(thumbnail_image);
3325 }
3326 (void) DeleteImageProperty(thumbnail_image,"comment");
3327 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003328 if (strstr(image->magick_filename,"//") == (char *) NULL)
3329 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003330 image->magick_filename);
3331 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3332 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3333 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3334 {
cristye8c25f92010-06-03 00:53:06 +00003335 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003336 attributes.st_mtime);
3337 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3338 }
cristye8c25f92010-06-03 00:53:06 +00003339 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003340 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003341 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003342 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003343 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3344 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3345 LocaleLower(value);
3346 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3347 (void) SetImageProperty(thumbnail_image,"software",
3348 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003349 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3350 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003351 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003352 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003353 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003354 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003355 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3356 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003357 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3358 return(thumbnail_image);
3359}