blob: 42b3699f95bc1f1578e40dcb6242116be8b056c8 [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) */
anthonyf5e76ef2010-10-12 01:22:01 +000088 coeff[8]; /* cubic coefficents for smooth Cubic 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:
cristyf59a8922010-02-28 19:51:23 +0000198 Mitchell-Netravali B=1/3 C=1/3 Qualitively ideal Cubic Filter
199 Catmull-Rom B= 0 C=1/2 Cublic Interpolation Function
200 Cubic B-Spline B= 1 C= 0 Spline Approximation of Gaussian
201 Hermite B= 0 C= 0 Quadratic Spline (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
221 Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x <= 2
222
cristy33b1c162010-01-23 22:51:51 +0000223 which ensures function is continuous in value and derivative (slope).
nicolasc6bac3b2010-10-24 18:10:45 +0000224
225 (This implies that coeff[1] is always zero. It is omitted in the
226 computation below.)
cristy3ed852e2009-09-05 21:47:34 +0000227 */
228 if (x < 1.0)
nicolasc6bac3b2010-10-24 18:10:45 +0000229 return(resize_filter->coeff[0]+x*(x*
anthonyf5e76ef2010-10-12 01:22:01 +0000230 (resize_filter->coeff[2]+x*resize_filter->coeff[3])));
cristy3ed852e2009-09-05 21:47:34 +0000231 if (x < 2.0)
anthonyf5e76ef2010-10-12 01:22:01 +0000232 return(resize_filter->coeff[4]+x*(resize_filter->coeff[5]+x*
233 (resize_filter->coeff[6]+x*resize_filter->coeff[7])));
cristy3ed852e2009-09-05 21:47:34 +0000234 return(0.0);
235}
236
237static MagickRealType Gaussian(const MagickRealType x,
anthonyf5e76ef2010-10-12 01:22:01 +0000238 const ResizeFilter *resize_filter)
cristy3ed852e2009-09-05 21:47:34 +0000239{
cristy560d8182010-09-08 22:36:25 +0000240 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000241 Gaussian with a fixed sigma = 1/2
242
243 Gaussian Formula...
244 exp( -(x^2)/((2.0*sigma^2) ) / sqrt(2*PI*sigma^2)))
245 The constants are pre-calculated...
246 exp( -coeff[0]*(x^2)) ) * coeff[1]
247 However the multiplier coefficent is not needed and not used.
248
249 This separates the gaussian 'sigma' value from the 'blur/support' settings
250 allows for its use in special 'small sigma' gaussians, without the filter
251 'missing' pixels when blur and thus support becomes too small.
cristy560d8182010-09-08 22:36:25 +0000252 */
anthonyf5e76ef2010-10-12 01:22:01 +0000253 return(exp((double)(-resize_filter->coeff[0]*x*x))); }
cristy3ed852e2009-09-05 21:47:34 +0000254
255static MagickRealType Hanning(const MagickRealType x,
256 const ResizeFilter *magick_unused(resize_filter))
257{
258 /*
nicolas40477452010-09-27 23:42:08 +0000259 Cosine window function:
260 .5+.5cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000261 */
cristyc5c6f662010-09-22 14:23:02 +0000262 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000263 return(0.5+0.5*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000264}
265
266static MagickRealType Hamming(const MagickRealType x,
267 const ResizeFilter *magick_unused(resize_filter))
268{
269 /*
nicolas40477452010-09-27 23:42:08 +0000270 Offset cosine window function:
271 .54 + .46 cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000272 */
cristyc5c6f662010-09-22 14:23:02 +0000273 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000274 return(0.54+0.46*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000275}
276
277static MagickRealType Kaiser(const MagickRealType x,
278 const ResizeFilter *magick_unused(resize_filter))
279{
280#define Alpha 6.5
281#define I0A (1.0/I0(Alpha))
282
283 /*
nicolas07bac812010-09-19 18:47:02 +0000284 Kaiser Windowing Function (bessel windowing): Alpha is a free
285 value from 5 to 8 (currently hardcoded to 6.5).
286 Future: make alpha the IOA pre-calculation, an 'expert' setting.
cristy3ed852e2009-09-05 21:47:34 +0000287 */
288 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
289}
290
291static MagickRealType Lagrange(const MagickRealType x,
292 const ResizeFilter *resize_filter)
293{
cristy3ed852e2009-09-05 21:47:34 +0000294 MagickRealType
295 value;
296
cristybb503372010-05-27 20:51:26 +0000297 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000298 i;
299
cristy9af9b5d2010-08-15 17:04:28 +0000300 ssize_t
301 n,
302 order;
303
cristy3ed852e2009-09-05 21:47:34 +0000304 /*
nicolas07bac812010-09-19 18:47:02 +0000305 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the
306 lagrange function and depends on the overall support window size
307 of the filter. That is: for a support of 2, it gives a lagrange-4
308 (piecewise cubic function).
cristy3ed852e2009-09-05 21:47:34 +0000309
nicolas07bac812010-09-19 18:47:02 +0000310 "n" identifies the piece of the piecewise polynomial.
cristy3ed852e2009-09-05 21:47:34 +0000311
nicolas07bac812010-09-19 18:47:02 +0000312 See Survey: Interpolation Methods, IEEE Transactions on Medical
313 Imaging, Vol 18, No 11, November 1999, p1049-1075, -- Equation 27
314 on p1064.
cristy3ed852e2009-09-05 21:47:34 +0000315 */
316 if (x > resize_filter->support)
317 return(0.0);
cristybb503372010-05-27 20:51:26 +0000318 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
anthonyc2d07db2010-09-15 23:47:40 +0000319 /*n=(ssize_t)((1.0*order)/2.0+x); -- which piece does x belong to */
320 n = (ssize_t)(resize_filter->window_support + x);
cristy3ed852e2009-09-05 21:47:34 +0000321 value=1.0f;
322 for (i=0; i < order; i++)
323 if (i != n)
324 value*=(n-i-x)/(n-i);
325 return(value);
326}
327
328static MagickRealType Quadratic(const MagickRealType x,
329 const ResizeFilter *magick_unused(resize_filter))
330{
331 /*
332 2rd order (quadratic) B-Spline approximation of Gaussian.
333 */
334 if (x < 0.5)
335 return(0.75-x*x);
336 if (x < 1.5)
337 return(0.5*(x-1.5)*(x-1.5));
338 return(0.0);
339}
340
anthony07a3f7f2010-09-16 03:03:11 +0000341static MagickRealType Sinc(const MagickRealType x,
cristy3ed852e2009-09-05 21:47:34 +0000342 const ResizeFilter *magick_unused(resize_filter))
343{
anthony720660f2010-09-07 10:05:14 +0000344 /*
nicolas40477452010-09-27 23:42:08 +0000345 Scaled sinc(x) function using a trig call:
nicolas07bac812010-09-19 18:47:02 +0000346 sinc(x) == sin(pi x)/(pi x).
anthony720660f2010-09-07 10:05:14 +0000347 */
anthony2d9b8b52010-09-14 08:31:07 +0000348 if (x != 0.0)
cristy560d8182010-09-08 22:36:25 +0000349 {
cristyc5c6f662010-09-22 14:23:02 +0000350 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
nicolas07bac812010-09-19 18:47:02 +0000351 return(sin((double) pix)/pix);
cristy560d8182010-09-08 22:36:25 +0000352 }
nicolas2ffd3b22010-09-24 20:27:31 +0000353 return((MagickRealType) 1.0);
anthony720660f2010-09-07 10:05:14 +0000354}
355
anthonyba5a7c32010-09-15 02:42:25 +0000356static MagickRealType SincFast(const MagickRealType x,
anthony720660f2010-09-07 10:05:14 +0000357 const ResizeFilter *magick_unused(resize_filter))
358{
cristy560d8182010-09-08 22:36:25 +0000359 /*
360 Approximations of the sinc function sin(pi x)/(pi x) over the
361 interval [-4,4] constructed by Nicolas Robidoux and Chantal
362 Racette with funding from the Natural Sciences and Engineering
363 Research Council of Canada.
nicolas07bac812010-09-19 18:47:02 +0000364
365 Although the approximations are polynomials (for low order of
366 approximation) and quotients of polynomials (for higher order of
367 approximation) and consequently are similar in form to Taylor
368 polynomials/Pade approximants, the approximations are computed
369 with a completely different technique.
370
371 Summary: These approximations are "the best" in terms of bang
372 (accuracy) for the buck (flops). More specifically: Among the
373 polynomial quotients that can be computed using a fixed number of
374 flops (with a given "+ - * / budget"), the chosen polynomial
375 quotient is the one closest to the approximated function with
376 respect to maximum absolute relative error over the given
377 interval.
378
379 The Remez algorithm, as implemented in the boost library's minimax
nicolas3aab40c2010-09-19 21:14:15 +0000380 package, is the key to the construction:
nicolas07bac812010-09-19 18:47:02 +0000381 http://www.boost.org/doc/libs/1_36_0/libs/math/doc/...
382 ...sf_and_dist/html/math_toolkit/backgrounders/remez.html
cristy560d8182010-09-08 22:36:25 +0000383 */
nicolas3aab40c2010-09-19 21:14:15 +0000384 /*
385 If outside of the interval of approximation, use the standard trig
386 formula.
387 */
anthony2d9b8b52010-09-14 08:31:07 +0000388 if (x > 4.0)
cristy03dbbd22010-09-19 23:04:47 +0000389 {
cristyc5c6f662010-09-22 14:23:02 +0000390 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
cristy03dbbd22010-09-19 23:04:47 +0000391 return(sin((double) pix)/pix);
392 }
anthony2d9b8b52010-09-14 08:31:07 +0000393 {
nicolas07bac812010-09-19 18:47:02 +0000394 /*
395 The approximations only depend on x^2 (sinc is an even
396 function).
397 */
398 const MagickRealType xx = x*x;
cristy83017922010-09-05 20:45:15 +0000399#if MAGICKCORE_QUANTUM_DEPTH <= 8
400 /*
anthony2d9b8b52010-09-14 08:31:07 +0000401 Maximum absolute relative error 6.3e-6 < 1/2^17.
cristy738e7562010-09-01 12:48:07 +0000402 */
403 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
404 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
405 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
406 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
407 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
408 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
409 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
410 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
nicolas3aab40c2010-09-19 21:14:15 +0000411 const MagickRealType p =
412 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
nicolas07bac812010-09-19 18:47:02 +0000413 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
anthony2d9b8b52010-09-14 08:31:07 +0000414#elif MAGICKCORE_QUANTUM_DEPTH <= 16
cristydbeb3eb2010-09-09 13:41:36 +0000415 /*
anthony2d9b8b52010-09-14 08:31:07 +0000416 Max. abs. rel. error 2.2e-8 < 1/2^25.
cristydbeb3eb2010-09-09 13:41:36 +0000417 */
418 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
419 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
nicolas3aab40c2010-09-19 21:14:15 +0000420 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
421 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
422 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
423 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
424 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
cristydbeb3eb2010-09-09 13:41:36 +0000425 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
426 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
427 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
anthony853d6972010-10-08 06:01:31 +0000428 const MagickRealType p =
nicolas3aab40c2010-09-19 21:14:15 +0000429 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
nicolas07bac812010-09-19 18:47:02 +0000430 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
nicolasc2d28f02010-09-27 18:56:15 +0000431#else
nicolas3aab40c2010-09-19 21:14:15 +0000432 /*
433 Max. abs. rel. error 1.2e-12 < 1/2^39.
434 */
435 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
436 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
437 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
438 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
439 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
440 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
441 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
442 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
443 const MagickRealType p =
444 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
445 const MagickRealType d0 = 1.0L;
446 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
447 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
448 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
449 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
450 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
451 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
cristy657c6352010-08-29 14:05:08 +0000452#endif
cristy83017922010-09-05 20:45:15 +0000453 }
cristy3ed852e2009-09-05 21:47:34 +0000454}
455
456static MagickRealType Triangle(const MagickRealType x,
457 const ResizeFilter *magick_unused(resize_filter))
458{
459 /*
nicolas0edb0862010-09-19 18:56:19 +0000460 1st order (linear) B-Spline, bilinear interpolation, Tent 1D
461 filter, or a Bartlett 2D Cone filter.
cristy3ed852e2009-09-05 21:47:34 +0000462 */
463 if (x < 1.0)
464 return(1.0-x);
465 return(0.0);
466}
467
468static MagickRealType Welsh(const MagickRealType x,
469 const ResizeFilter *magick_unused(resize_filter))
470{
471 /*
472 Welsh parabolic windowing filter.
473 */
cristy560d8182010-09-08 22:36:25 +0000474 if (x < 1.0)
cristy3ed852e2009-09-05 21:47:34 +0000475 return(1.0-x*x);
476 return(0.0);
477}
478
479/*
480%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
481% %
482% %
483% %
484+ A c q u i r e R e s i z e F i l t e r %
485% %
486% %
487% %
488%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
489%
nicolas07bac812010-09-19 18:47:02 +0000490% AcquireResizeFilter() allocates the ResizeFilter structure. Choose
491% from these filters:
cristy3ed852e2009-09-05 21:47:34 +0000492%
493% FIR (Finite impulse Response) Filters
494% Box Triangle Quadratic
495% Cubic Hermite Catrom
496% Mitchell
497%
498% IIR (Infinite impulse Response) Filters
anthony48f77622010-10-03 14:32:31 +0000499% Gaussian Sinc Jinc (Bessel)
cristy3ed852e2009-09-05 21:47:34 +0000500%
anthony48f77622010-10-03 14:32:31 +0000501% Windowed Sinc/Jinc Filters
cristy3ed852e2009-09-05 21:47:34 +0000502% Blackman Hanning Hamming
anthony48f77622010-10-03 14:32:31 +0000503% Kaiser Lanczos
cristy3ed852e2009-09-05 21:47:34 +0000504%
anthony61b5ddd2010-10-05 02:33:31 +0000505% Special purpose Filters
anthony853d6972010-10-08 06:01:31 +0000506% SincFast Lanczos2D Robidoux
anthony61b5ddd2010-10-05 02:33:31 +0000507%
anthony48f77622010-10-03 14:32:31 +0000508% The users "-filter" selection is used to lookup the default 'expert'
509% settings for that filter from a internal table. However any provided
510% 'expert' settings (see below) may override this selection.
511%
512% FIR filters are used as is, and are limited to that filters support
nicolas07bac812010-09-19 18:47:02 +0000513% window (unless over-ridden). 'Gaussian' while classed as an IIR
anthonyc331dec2010-09-26 01:30:14 +0000514% filter, is also simply clipped by its support size (currently 1.5
anthonyf5e76ef2010-10-12 01:22:01 +0000515% or approximatally 3*sigma as recommended by many references)
cristy3ed852e2009-09-05 21:47:34 +0000516%
anthony48f77622010-10-03 14:32:31 +0000517% The selection is typically either a windowed Sinc, or interpolated
nicolas07bac812010-09-19 18:47:02 +0000518% filter, for use by functions such as ResizeImage(). However if a
anthony48f77622010-10-03 14:32:31 +0000519% 'cylindrical' filter flag is requested, any default Sinc weighting
520% and windowing functions will be promoted to cylindrical Jinc form of
521% function.
cristy3ed852e2009-09-05 21:47:34 +0000522%
anthony48f77622010-10-03 14:32:31 +0000523% Directly requesting 'Sinc' or 'Jinc' will force the use of that
524% filter function without any windowing. This is not recommended,
525% except by image processing experts or in expert options. Selecting a
526% window filtering version of these functions is better.
cristy3ed852e2009-09-05 21:47:34 +0000527%
anthony48f77622010-10-03 14:32:31 +0000528% Lanczos is a special case of a Sinc-windowed Sinc, (or Jinc-Jinc for
529% the cylindrical case) but defaulting to 3-lobe support, rather that
530% the default 4 lobe support of the other windowed sinc/jinc filters.
cristy3ed852e2009-09-05 21:47:34 +0000531%
anthony48f77622010-10-03 14:32:31 +0000532% Two forms of the 'Sinc' function are available: Sinc and SincFast.
nicolas07bac812010-09-19 18:47:02 +0000533% Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
534% selected if the user specifically specifies the use of a Sinc
535% filter. SincFast uses highly accurate (and fast) polynomial (low Q)
anthony48f77622010-10-03 14:32:31 +0000536% and rational (high Q) approximations, and will be used by default in
537% most cases.
anthonyba5a7c32010-09-15 02:42:25 +0000538%
nicolas1eb2fcf2010-10-10 20:45:17 +0000539% The Lanczos2D and Robidoux filters are tuned for cylindrical
540% (radial) EWA (Elliptical Weighted Average) distortion. Lanczos2D
nicolasdff19b42010-10-10 20:53:13 +0000541% is a 2 lobe Lanczos-like filter using Jinc (for EWA) or Sinc.
nicolas6ca6e962010-10-10 20:49:01 +0000542% Robidoux used to be a sharpened version of Lanczos2D (with
nicolas0d5e5322010-10-22 15:29:30 +0000543% blur=0.958033808). Now, Robidoux is the unique Keys cubic spline
544% filter satisfying the following condition:
545%
546% Robidoux exactly preserves images with only vertical or
547% horizontal features when performing 'no-op" with EWA distortion.
548%
nicolas618a9a72010-10-22 19:23:43 +0000549% That is, Robidoux is the BC-Spline with B=(228 - 108 sqrt(2))/199
550% and C=(108 sqrt(2) - 29)/398. Robidoux turns out to be close to
551% both plain Mitchell and "sharpened" Lanczos2D. For example, it's
552% first crossing is (36 sqrt(2) + 123)/(72 sqrt(2) + 47) which is
553% almost identical to the first crossing of the other two.
anthony61b5ddd2010-10-05 02:33:31 +0000554%
nicolas3061b8a2010-10-22 16:34:52 +0000555% 'EXPERT' OPTIONS:
nicolasce6dc292010-10-22 16:23:07 +0000556%
nicolas3061b8a2010-10-22 16:34:52 +0000557% (Not recommended without expert knowledge of resampling and
558% filtering.)
559%
560% You can override any and all filter settings. Use "filter:verbose"
561% to make sure that the overall effect of your selections is as
562% expected.
563%
564% "filter:verbose" Output the exact results of the filter
565% selections made, as well as plotting data for graphing the
566% resulting filter over support range (blur adjusted).
cristy3ed852e2009-09-05 21:47:34 +0000567%
anthony48f77622010-10-03 14:32:31 +0000568% "filter:filter" Select the main function associated with
569% this filter name, as the weighting function of the filter.
570% This can be used to set a windowing function as a weighting
571% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000572%
nicolas3061b8a2010-10-22 16:34:52 +0000573% If a "filter:window" operation has not been provided, then a
574% 'Box' windowing function will be set to denote that no
575% windowing function is being used.
cristy3ed852e2009-09-05 21:47:34 +0000576%
nicolas3061b8a2010-10-22 16:34:52 +0000577% "filter:window" Select this windowing function for the filter.
578% While any filter could be used as a windowing function, using
579% the 'first lobe' of that filter over the whole support
580% window, using a non-windowing function is not advisible. If
581% no weighting filter function is specifed a 'SincFast' filter
582% will be used.
cristy3ed852e2009-09-05 21:47:34 +0000583%
nicolas3061b8a2010-10-22 16:34:52 +0000584% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
585% This a simpler method of setting filter support size that
586% will correctly handle the Sinc/Jinc switch for an operators
587% filtering requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000588%
nicolas3061b8a2010-10-22 16:34:52 +0000589% "filter:support" Set the support size for filtering to the size
590% given This not recommended for Sinc/Jinc windowed filters
591% (lobes should be used instead). This will override any
592% 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000593%
nicolas3061b8a2010-10-22 16:34:52 +0000594% "filter:win-support" Scale windowing function to this size
595% instead. This causes the windowing (or self-windowing
596% Lagrange filter) to act is if the support window it much much
597% larger than what is actually supplied to the calling
598% operator. The filter however is still clipped to the real
599% support size given, by the support range suppiled to the
600% caller. If unset this will equal the normal filter support
anthonyb6d08c52010-09-13 01:17:04 +0000601% size.
602%
nicolas3061b8a2010-10-22 16:34:52 +0000603% "filter:blur" Scale the filter and support window by this amount.
cristy3ed852e2009-09-05 21:47:34 +0000604% A value >1 will generally result in a more burred image with
605% more ringing effects, while a value <1 will sharpen the
606% resulting image with more aliasing and Morie effects.
607%
nicolas3061b8a2010-10-22 16:34:52 +0000608% "filter:sigma" The sigma value to use for the Gaussian filter
609% only. Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for
610% cylindrical usage. It effectially provides a alturnative to
611% 'blur' for Gaussians without it also effecting the final
612% 'practical support' size.
anthonyf5e76ef2010-10-12 01:22:01 +0000613%
cristy3ed852e2009-09-05 21:47:34 +0000614% "filter:b"
nicolas3061b8a2010-10-22 16:34:52 +0000615% "filter:c" Override the preset B,C values for a Cubic type of
616% filter If only one of these are given it is assumes to be a
617% 'Keys' type of filter such that B+2C=1, where Keys 'alpha'
618% value = C
cristy3ed852e2009-09-05 21:47:34 +0000619%
nicolas3061b8a2010-10-22 16:34:52 +0000620% Examples:
cristy3ed852e2009-09-05 21:47:34 +0000621%
nicolas6e1267a2010-10-22 16:35:52 +0000622% Set a true un-windowed Sinc filter with 10 lobes (very slow):
anthony48f77622010-10-03 14:32:31 +0000623% -define filter:filter=Sinc
624% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000625%
nicolas6e1267a2010-10-22 16:35:52 +0000626% Set an 8 lobe Lanczos (Sinc or Jinc) filter:
cristy3ed852e2009-09-05 21:47:34 +0000627% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000628% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000629%
cristy3ed852e2009-09-05 21:47:34 +0000630% The format of the AcquireResizeFilter method is:
631%
632% ResizeFilter *AcquireResizeFilter(const Image *image,
633% const FilterTypes filter_type, const MagickBooleanType radial,
634% ExceptionInfo *exception)
635%
cristy33b1c162010-01-23 22:51:51 +0000636% A description of each parameter follows:
637%
cristy3ed852e2009-09-05 21:47:34 +0000638% o image: the image.
639%
nicolas07bac812010-09-19 18:47:02 +0000640% o filter: the filter type, defining a preset filter, window and
641% support. The artifact settings listed above will override
642% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000643%
anthony48f77622010-10-03 14:32:31 +0000644% o blur: blur the filter by this amount, use 1.0 if unknown. Image
645% artifact "filter:blur" will override this API call usage, including
646% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000647%
anthony48f77622010-10-03 14:32:31 +0000648% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
649% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000650%
651% o exception: return any errors or warnings in this structure.
652%
653*/
654MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000655 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000656 const MagickBooleanType cylindrical,ExceptionInfo *exception)
657{
658 const char
659 *artifact;
660
661 FilterTypes
662 filter_type,
663 window_type;
664
cristy3ed852e2009-09-05 21:47:34 +0000665 MagickRealType
666 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000667 C,
668 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000669
670 register ResizeFilter
671 *resize_filter;
672
cristy9af9b5d2010-08-15 17:04:28 +0000673 ssize_t
674 option;
675
cristy3ed852e2009-09-05 21:47:34 +0000676 /*
anthony48f77622010-10-03 14:32:31 +0000677 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000678 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000679 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
680 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
681 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000682
nicolas07bac812010-09-19 18:47:02 +0000683 WARNING: The order of this tabel must match the order of the
684 FilterTypes enumeration specified in "resample.h", or the filter
685 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000686
687 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000688 */
689 static struct
690 {
691 FilterTypes
692 filter,
693 window;
694 } const mapping[SentinelFilter] =
695 {
anthony462ee072010-09-27 12:34:02 +0000696 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
697 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
698 { BoxFilter, BoxFilter }, /* Box averaging filter */
699 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
700 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
701 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
702 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
703 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
704 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
705 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
706 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
707 { CatromFilter, BoxFilter }, /* Cubic interpolator */
708 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
709 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
anthony48f77622010-10-03 14:32:31 +0000710 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
711 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony462ee072010-09-27 12:34:02 +0000712 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
713 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
714 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
715 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
716 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
717 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
718 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolas5835ee02010-10-10 20:40:15 +0000719 { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
anthonye5f06452010-10-12 05:48:17 +0000720 { Lanczos2DSharpFilter, JincFilter },/* SPECIAL: ditto sharpened */
721 { RobidouxFilter, BoxFilter }, /* SPECIAL: Keys cubic tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000722 };
723 /*
nicolas32f44eb2010-09-20 01:23:12 +0000724 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000725 function. The default support size for that filter as a weighting
726 function, the range to scale with to use that function as a sinc
727 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000728
anthony07a3f7f2010-09-16 03:03:11 +0000729 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000730 SincFast(), and CubicBC() functions, which may have multiple
731 filter to function associations.
732
733 See "filter:verbose" handling below for the function -> filter
734 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000735 */
736 static struct
737 {
738 MagickRealType
739 (*function)(const MagickRealType, const ResizeFilter*),
nicolas8eccc162010-10-16 19:48:13 +0000740 lobes, /* Default lobes/support size of the weighting filter. */
anthony450db502010-10-19 04:03:03 +0000741 scale, /* Support when function used as a windowing function
742 Typically equal to the location of the first zero crossing. */
743 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
cristy3ed852e2009-09-05 21:47:34 +0000744 } const filters[SentinelFilter] =
745 {
anthony61b5ddd2010-10-05 02:33:31 +0000746 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
747 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
748 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
749 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
750 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
751 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
752 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
753 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000754 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000755 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
756 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
757 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
anthony853d6972010-10-08 06:01:31 +0000758 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
759 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
nicolasf8689f42010-10-18 16:14:08 +0000760 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000761 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
762 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
763 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
764 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
765 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
766 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
767 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
768 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
nicolas8eccc162010-10-16 19:48:13 +0000769 { Jinc, 2.0, 1.2196698912665045, 0.0, 0.0 },
770 /* Lanczos2D (Jinc-Jinc) */
771 { Jinc, 2.0, 1.1684849904329952, 0.0, 0.0 },
772 /* Lanczos2D sharpened with blur=0.958033808 */
anthony450db502010-10-19 04:03:03 +0000773 { CubicBC, 2.0, 1.1685777620836932,
nicolas0d5e5322010-10-22 15:29:30 +0000774 0.37821575509399867, 0.31089212245300067 }
775 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
cristy3ed852e2009-09-05 21:47:34 +0000776 };
777 /*
anthony9a98fc62010-10-11 02:47:19 +0000778 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
anthony450db502010-10-19 04:03:03 +0000779 function being used as a filter. It is used by the "filter:lobes" expert
780 setting and for 'lobes' for Jinc functions in the previous table. This
781 way users do not have to deal with the highly irrational lobe sizes of the
anthony9a98fc62010-10-11 02:47:19 +0000782 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000783
nicolase473f722010-10-07 00:05:13 +0000784 Values taken from
anthony48f77622010-10-03 14:32:31 +0000785 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000786 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000787 */
788 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000789 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000790 {
nicolas8eccc162010-10-16 19:48:13 +0000791 1.2196698912665045,
792 2.2331305943815286,
793 3.2383154841662362,
794 4.2410628637960699,
795 5.2427643768701817,
796 6.2439216898644877,
797 7.244759868719957,
798 8.2453949139520427,
799 9.2458926849494673,
800 10.246293348754916,
801 11.246622794877883,
802 12.246898461138105,
803 13.247132522181061,
804 14.247333735806849,
anthonyc2d07db2010-09-15 23:47:40 +0000805 15.2475085630373,
nicolas8eccc162010-10-16 19:48:13 +0000806 16.247661874700962
cristy3ed852e2009-09-05 21:47:34 +0000807 };
808
cristy33b1c162010-01-23 22:51:51 +0000809 /*
810 Allocate resize filter.
811 */
cristy3ed852e2009-09-05 21:47:34 +0000812 assert(image != (const Image *) NULL);
813 assert(image->signature == MagickSignature);
814 if (image->debug != MagickFalse)
815 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
816 assert(UndefinedFilter < filter && filter < SentinelFilter);
817 assert(exception != (ExceptionInfo *) NULL);
818 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000819 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000820 if (resize_filter == (ResizeFilter *) NULL)
821 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000822 /*
823 Defaults for the requested filter.
824 */
825 filter_type=mapping[filter].filter;
826 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000827 resize_filter->blur = blur;
828 sigma = 0.5;
anthony48f77622010-10-03 14:32:31 +0000829 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000830 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000831 switch (filter_type)
832 {
833 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000834 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000835 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000836 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000837 break;
anthonyba5a7c32010-09-15 02:42:25 +0000838 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000839 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000840 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000841 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000842 break;
cristy33b1c162010-01-23 22:51:51 +0000843 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000844 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000845 filter_type=JincFilter;
846 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000847 break;
anthony08958462010-10-12 06:48:35 +0000848 case Lanczos2DSharpFilter:
nicolas1f4c0512010-10-20 20:19:30 +0000849 /* Sharpened by Nicolas Robidoux so as to optimize for minimal
850 * blurring of orthogonal lines
anthony08958462010-10-12 06:48:35 +0000851 */
852 resize_filter->blur *= 0.958033808;
853 break;
anthonyf5e76ef2010-10-12 01:22:01 +0000854 case GaussianFilter:
cristy68f103e2010-10-14 01:19:07 +0000855 sigma = (MagickRealType) (MagickSQ2/2.0); /* Cylindrical Gaussian sigma is sqrt(2)/2 */
anthonyf5e76ef2010-10-12 01:22:01 +0000856 break;
cristya782ecf2010-01-25 02:59:14 +0000857 default:
858 break;
cristy3ed852e2009-09-05 21:47:34 +0000859 }
anthony61b5ddd2010-10-05 02:33:31 +0000860 else
861 switch (filter_type)
862 {
863 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000864 case Lanczos2DSharpFilter:
nicolas45b58a92010-10-07 15:46:39 +0000865 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000866 window_type=SincFastFilter;
867 break;
868 default:
869 break;
870 }
871
cristy3ed852e2009-09-05 21:47:34 +0000872 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000873 if (artifact != (const char *) NULL)
874 {
cristy9af9b5d2010-08-15 17:04:28 +0000875 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000876 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000877 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000878 filter_type=(FilterTypes) option;
879 window_type=BoxFilter;
880 }
881 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000882 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000883 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000884 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000885 }
nicolas07bac812010-09-19 18:47:02 +0000886 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000887 artifact=GetImageArtifact(image,"filter:window");
888 if (artifact != (const char *) NULL)
889 {
cristy9af9b5d2010-08-15 17:04:28 +0000890 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000891 if ((UndefinedFilter < option) && (option < SentinelFilter))
892 {
893 if (option != LanczosFilter)
894 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000895 else
anthony48f77622010-10-03 14:32:31 +0000896 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000897 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000898 }
cristy33b1c162010-01-23 22:51:51 +0000899 }
cristy3ed852e2009-09-05 21:47:34 +0000900 }
cristy33b1c162010-01-23 22:51:51 +0000901 else
902 {
anthony48f77622010-10-03 14:32:31 +0000903 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000904 artifact=GetImageArtifact(image,"filter:window");
905 if (artifact != (const char *) NULL)
906 {
907 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
908 artifact);
909 if ((UndefinedFilter < option) && (option < SentinelFilter))
910 {
anthony61b5ddd2010-10-05 02:33:31 +0000911 filter_type=cylindrical != MagickFalse ?
912 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000913 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000914 }
915 }
916 }
nicolas07bac812010-09-19 18:47:02 +0000917 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000918 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000919 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000920 resize_filter->window=filters[window_type].function;
921 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000922 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000923
anthonyf5e76ef2010-10-12 01:22:01 +0000924 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000925 if (cylindrical != MagickFalse)
926 switch (filter_type)
927 {
928 case PointFilter:
929 case BoxFilter:
930 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000931 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000932 break;
anthony81b8bf92010-10-02 13:54:34 +0000933 default:
934 break;
anthony10b8bc82010-10-02 12:48:46 +0000935 }
anthony61b5ddd2010-10-05 02:33:31 +0000936 else
937 switch (filter_type)
938 {
939 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000940 case Lanczos2DSharpFilter:
anthony853d6972010-10-08 06:01:31 +0000941 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000942 resize_filter->filter=SincFast;
943 break;
944 default:
945 break;
946 }
947
anthonyf5e76ef2010-10-12 01:22:01 +0000948 /*
949 ** More Expert Option Modifications
950 */
951
952 /* User Sigma Override - no support change */
953 artifact=GetImageArtifact(image,"filter:sigma");
954 if (artifact != (const char *) NULL)
955 sigma=StringToDouble(artifact);
956 /* Define coefficents for Gaussian (assumes no cubic window) */
957 if ( GaussianFilter ) {
958 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
cristy68f103e2010-10-14 01:19:07 +0000959 resize_filter->coeff[1] = (MagickRealType) (1.0/(Magick2PI*sigma*sigma)); /* unused */
anthonyf5e76ef2010-10-12 01:22:01 +0000960 }
961
962 /* Blur Override */
963 artifact=GetImageArtifact(image,"filter:blur");
964 if (artifact != (const char *) NULL)
965 resize_filter->blur=StringToDouble(artifact);
966 if (resize_filter->blur < MagickEpsilon)
967 resize_filter->blur=(MagickRealType) MagickEpsilon;
968
969 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000970 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000971 if (artifact != (const char *) NULL)
972 {
cristybb503372010-05-27 20:51:26 +0000973 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000974 lobes;
975
cristy96b16132010-08-29 17:19:52 +0000976 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000977 if (lobes < 1)
978 lobes=1;
979 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000980 }
anthony61b5ddd2010-10-05 02:33:31 +0000981 /* convert Jinc lobes to a real support value */
982 if (resize_filter->filter == Jinc)
983 {
984 if (resize_filter->support > 16)
985 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
986 else
987 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
988 }
989 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000990 artifact=GetImageArtifact(image,"filter:support");
991 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000992 resize_filter->support=fabs(StringToDouble(artifact));
993 /*
nicolas07bac812010-09-19 18:47:02 +0000994 Scale windowing function separatally to the support 'clipping'
995 window that calling operator is planning to actually use. (Expert
996 override)
cristy3ed852e2009-09-05 21:47:34 +0000997 */
anthony55f12332010-09-10 01:13:02 +0000998 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000999 artifact=GetImageArtifact(image,"filter:win-support");
1000 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001001 resize_filter->window_support=fabs(StringToDouble(artifact));
1002 /*
anthony1f90a6b2010-09-14 08:56:31 +00001003 Adjust window function scaling to the windowing support for
1004 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +00001005 */
1006 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +00001007
anthony55f12332010-09-10 01:13:02 +00001008 /*
anthonyf5e76ef2010-10-12 01:22:01 +00001009 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +00001010 */
cristy3ed852e2009-09-05 21:47:34 +00001011 B=0.0;
1012 C=0.0;
cristy33b1c162010-01-23 22:51:51 +00001013 if ((filters[filter_type].function == CubicBC) ||
1014 (filters[window_type].function == CubicBC))
1015 {
anthony2d9b8b52010-09-14 08:31:07 +00001016 B=filters[filter_type].B;
1017 C=filters[filter_type].C;
1018 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +00001019 {
anthony2d9b8b52010-09-14 08:31:07 +00001020 B=filters[window_type].B;
1021 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +00001022 }
cristy33b1c162010-01-23 22:51:51 +00001023 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001024 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001025 {
1026 B=StringToDouble(artifact);
nicolasd15ee9a2010-10-24 18:39:45 +00001027 C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001028 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001029 if (artifact != (const char *) NULL)
1030 C=StringToDouble(artifact);
1031 }
1032 else
1033 {
1034 artifact=GetImageArtifact(image,"filter:c");
1035 if (artifact != (const char *) NULL)
1036 {
1037 C=StringToDouble(artifact);
nicolasd15ee9a2010-10-24 18:39:45 +00001038 B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001039 }
1040 }
nicolasc6bac3b2010-10-24 18:10:45 +00001041 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
nicolasd15ee9a2010-10-24 18:39:45 +00001042 {
1043 const double twoB = B+B;
1044 resize_filter->coeff[0]=1.0-(1.0/3.0)*B;
1045 resize_filter->coeff[1]=0.0;
1046 resize_filter->coeff[2]=-3.0+twoB+C;
1047 resize_filter->coeff[3]=2.0-1.5*B-C;
1048 resize_filter->coeff[4]=(4.0/3.0)*B+4.0*C;
1049 resize_filter->coeff[5]=-8.0*C-twoB;
1050 resize_filter->coeff[6]=B+5.0*C;
1051 resize_filter->coeff[7]=(-1.0/6.0)*B-C;
1052 }
nicolasc6bac3b2010-10-24 18:10:45 +00001053 }
anthonyf5e76ef2010-10-12 01:22:01 +00001054
anthony55f12332010-09-10 01:13:02 +00001055 /*
nicolas07bac812010-09-19 18:47:02 +00001056 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001057 */
cristyf5b49372010-10-16 01:06:47 +00001058#if defined(MAGICKCORE_OPENMP_SUPPORT)
1059 #pragma omp master
1060 {
1061#endif
1062 artifact=GetImageArtifact(image,"filter:verbose");
1063 if (artifact != (const char *) NULL)
1064 {
1065 double
anthony450db502010-10-19 04:03:03 +00001066 support,
cristyf5b49372010-10-16 01:06:47 +00001067 x;
cristy3ed852e2009-09-05 21:47:34 +00001068
cristyf5b49372010-10-16 01:06:47 +00001069 /*
1070 Set the weighting function properly when the weighting
1071 function may not exactly match the filter of the same name.
1072 EG: a Point filter really uses a Box weighting function
1073 with a different support than is typically used.
anthony463be1d2010-09-26 01:07:36 +00001074
cristyf5b49372010-10-16 01:06:47 +00001075 */
1076 if (resize_filter->filter == Box) filter_type=BoxFilter;
1077 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1078 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1079 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1080 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1081 /*
1082 Report Filter Details.
1083 */
1084 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1085 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
1086 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1087 MagickFilterOptions,filter_type));
1088 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
1089 MagickFilterOptions, window_type));
1090 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
1091 (double) resize_filter->support);
1092 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
1093 (double) resize_filter->window_support);
1094 (void) fprintf(stdout,"# scale_blur = %.*g\n",GetMagickPrecision(),
1095 (double) resize_filter->blur);
1096 if ( filter_type == GaussianFilter )
1097 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",GetMagickPrecision(),
1098 (double) sigma);
1099 (void) fprintf(stdout,"# practical_support = %.*g\n",GetMagickPrecision(),
1100 (double) support);
1101 if ( filter_type == CubicFilter || window_type == CubicFilter )
1102 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1103 (double) B,GetMagickPrecision(),(double) C);
1104 (void) fprintf(stdout,"\n");
1105 /*
1106 Output values of resulting filter graph -- for graphing
1107 filter result.
1108 */
1109 for (x=0.0; x <= support; x+=0.01f)
1110 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1111 (double) GetResizeFilterWeight(resize_filter,x));
1112 /* A final value so gnuplot can graph the 'stop' properly. */
1113 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1114 0.0);
1115 }
1116 /* Output the above once only for each image - remove setting */
1117 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1118#if defined(MAGICKCORE_OPENMP_SUPPORT)
1119 }
1120#endif
cristy3ed852e2009-09-05 21:47:34 +00001121 return(resize_filter);
1122}
1123
1124/*
1125%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1126% %
1127% %
1128% %
1129% A d a p t i v e R e s i z e I m a g e %
1130% %
1131% %
1132% %
1133%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1134%
1135% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1136%
1137% The format of the AdaptiveResizeImage method is:
1138%
cristy9af9b5d2010-08-15 17:04:28 +00001139% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1140% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001141%
1142% A description of each parameter follows:
1143%
1144% o image: the image.
1145%
1146% o columns: the number of columns in the resized image.
1147%
1148% o rows: the number of rows in the resized image.
1149%
1150% o exception: return any errors or warnings in this structure.
1151%
1152*/
1153MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001154 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001155{
1156#define AdaptiveResizeImageTag "Resize/Image"
1157
cristyc4c8d132010-01-07 01:58:38 +00001158 CacheView
1159 *resize_view;
1160
cristy3ed852e2009-09-05 21:47:34 +00001161 Image
1162 *resize_image;
1163
cristy3ed852e2009-09-05 21:47:34 +00001164 MagickBooleanType
1165 proceed;
1166
1167 MagickPixelPacket
1168 pixel;
1169
1170 PointInfo
1171 offset;
1172
1173 ResampleFilter
1174 *resample_filter;
1175
cristy9af9b5d2010-08-15 17:04:28 +00001176 ssize_t
1177 y;
1178
cristy3ed852e2009-09-05 21:47:34 +00001179 /*
1180 Adaptively resize image.
1181 */
1182 assert(image != (const Image *) NULL);
1183 assert(image->signature == MagickSignature);
1184 if (image->debug != MagickFalse)
1185 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1186 assert(exception != (ExceptionInfo *) NULL);
1187 assert(exception->signature == MagickSignature);
1188 if ((columns == 0) || (rows == 0))
1189 return((Image *) NULL);
1190 if ((columns == image->columns) && (rows == image->rows))
1191 return(CloneImage(image,0,0,MagickTrue,exception));
1192 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1193 if (resize_image == (Image *) NULL)
1194 return((Image *) NULL);
1195 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1196 {
1197 InheritException(exception,&resize_image->exception);
1198 resize_image=DestroyImage(resize_image);
1199 return((Image *) NULL);
1200 }
1201 GetMagickPixelPacket(image,&pixel);
1202 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001203 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001204 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001205 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001206 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001207 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001208 {
1209 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001210 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001211
cristybb503372010-05-27 20:51:26 +00001212 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001213 x;
1214
1215 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001216 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001217
1218 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1219 exception);
1220 if (q == (PixelPacket *) NULL)
1221 break;
1222 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1223 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001224 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001225 {
1226 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1227 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1228 &pixel);
1229 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1230 q++;
1231 }
1232 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1233 break;
cristy96b16132010-08-29 17:19:52 +00001234 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1235 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001236 if (proceed == MagickFalse)
1237 break;
1238 }
1239 resample_filter=DestroyResampleFilter(resample_filter);
1240 resize_view=DestroyCacheView(resize_view);
1241 return(resize_image);
1242}
1243
1244/*
1245%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1246% %
1247% %
1248% %
1249+ B e s s e l O r d e r O n e %
1250% %
1251% %
1252% %
1253%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1254%
1255% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001256% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001257%
1258% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1259%
1260% j1(x) = x*j1(x);
1261%
1262% For x in (8,inf)
1263%
1264% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1265%
1266% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1267%
1268% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1269% = 1/sqrt(2) * (sin(x) - cos(x))
1270% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1271% = -1/sqrt(2) * (sin(x) + cos(x))
1272%
1273% The format of the BesselOrderOne method is:
1274%
1275% MagickRealType BesselOrderOne(MagickRealType x)
1276%
1277% A description of each parameter follows:
1278%
1279% o x: MagickRealType value.
1280%
1281*/
1282
1283#undef I0
1284static MagickRealType I0(MagickRealType x)
1285{
1286 MagickRealType
1287 sum,
1288 t,
1289 y;
1290
cristybb503372010-05-27 20:51:26 +00001291 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001292 i;
1293
1294 /*
1295 Zeroth order Bessel function of the first kind.
1296 */
1297 sum=1.0;
1298 y=x*x/4.0;
1299 t=y;
1300 for (i=2; t > MagickEpsilon; i++)
1301 {
1302 sum+=t;
1303 t*=y/((MagickRealType) i*i);
1304 }
1305 return(sum);
1306}
1307
1308#undef J1
1309static MagickRealType J1(MagickRealType x)
1310{
1311 MagickRealType
1312 p,
1313 q;
1314
cristybb503372010-05-27 20:51:26 +00001315 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001316 i;
1317
1318 static const double
1319 Pone[] =
1320 {
1321 0.581199354001606143928050809e+21,
1322 -0.6672106568924916298020941484e+20,
1323 0.2316433580634002297931815435e+19,
1324 -0.3588817569910106050743641413e+17,
1325 0.2908795263834775409737601689e+15,
1326 -0.1322983480332126453125473247e+13,
1327 0.3413234182301700539091292655e+10,
1328 -0.4695753530642995859767162166e+7,
1329 0.270112271089232341485679099e+4
1330 },
1331 Qone[] =
1332 {
1333 0.11623987080032122878585294e+22,
1334 0.1185770712190320999837113348e+20,
1335 0.6092061398917521746105196863e+17,
1336 0.2081661221307607351240184229e+15,
1337 0.5243710262167649715406728642e+12,
1338 0.1013863514358673989967045588e+10,
1339 0.1501793594998585505921097578e+7,
1340 0.1606931573481487801970916749e+4,
1341 0.1e+1
1342 };
1343
1344 p=Pone[8];
1345 q=Qone[8];
1346 for (i=7; i >= 0; i--)
1347 {
1348 p=p*x*x+Pone[i];
1349 q=q*x*x+Qone[i];
1350 }
1351 return(p/q);
1352}
1353
1354#undef P1
1355static MagickRealType P1(MagickRealType x)
1356{
1357 MagickRealType
1358 p,
1359 q;
1360
cristybb503372010-05-27 20:51:26 +00001361 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001362 i;
1363
1364 static const double
1365 Pone[] =
1366 {
1367 0.352246649133679798341724373e+5,
1368 0.62758845247161281269005675e+5,
1369 0.313539631109159574238669888e+5,
1370 0.49854832060594338434500455e+4,
1371 0.2111529182853962382105718e+3,
1372 0.12571716929145341558495e+1
1373 },
1374 Qone[] =
1375 {
1376 0.352246649133679798068390431e+5,
1377 0.626943469593560511888833731e+5,
1378 0.312404063819041039923015703e+5,
1379 0.4930396490181088979386097e+4,
1380 0.2030775189134759322293574e+3,
1381 0.1e+1
1382 };
1383
1384 p=Pone[5];
1385 q=Qone[5];
1386 for (i=4; i >= 0; i--)
1387 {
1388 p=p*(8.0/x)*(8.0/x)+Pone[i];
1389 q=q*(8.0/x)*(8.0/x)+Qone[i];
1390 }
1391 return(p/q);
1392}
1393
1394#undef Q1
1395static MagickRealType Q1(MagickRealType x)
1396{
1397 MagickRealType
1398 p,
1399 q;
1400
cristybb503372010-05-27 20:51:26 +00001401 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001402 i;
1403
1404 static const double
1405 Pone[] =
1406 {
1407 0.3511751914303552822533318e+3,
1408 0.7210391804904475039280863e+3,
1409 0.4259873011654442389886993e+3,
1410 0.831898957673850827325226e+2,
1411 0.45681716295512267064405e+1,
1412 0.3532840052740123642735e-1
1413 },
1414 Qone[] =
1415 {
1416 0.74917374171809127714519505e+4,
1417 0.154141773392650970499848051e+5,
1418 0.91522317015169922705904727e+4,
1419 0.18111867005523513506724158e+4,
1420 0.1038187585462133728776636e+3,
1421 0.1e+1
1422 };
1423
1424 p=Pone[5];
1425 q=Qone[5];
1426 for (i=4; i >= 0; i--)
1427 {
1428 p=p*(8.0/x)*(8.0/x)+Pone[i];
1429 q=q*(8.0/x)*(8.0/x)+Qone[i];
1430 }
1431 return(p/q);
1432}
1433
1434static MagickRealType BesselOrderOne(MagickRealType x)
1435{
1436 MagickRealType
1437 p,
1438 q;
1439
1440 if (x == 0.0)
1441 return(0.0);
1442 p=x;
1443 if (x < 0.0)
1444 x=(-x);
1445 if (x < 8.0)
1446 return(p*J1(x));
1447 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1448 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1449 cos((double) x))));
1450 if (p < 0.0)
1451 q=(-q);
1452 return(q);
1453}
1454
1455/*
1456%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1457% %
1458% %
1459% %
1460+ D e s t r o y R e s i z e F i l t e r %
1461% %
1462% %
1463% %
1464%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1465%
1466% DestroyResizeFilter() destroy the resize filter.
1467%
cristya2ffd7e2010-03-10 20:50:30 +00001468% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001469%
1470% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1471%
1472% A description of each parameter follows:
1473%
1474% o resize_filter: the resize filter.
1475%
1476*/
1477MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1478{
1479 assert(resize_filter != (ResizeFilter *) NULL);
1480 assert(resize_filter->signature == MagickSignature);
1481 resize_filter->signature=(~MagickSignature);
1482 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1483 return(resize_filter);
1484}
1485
1486/*
1487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1488% %
1489% %
1490% %
1491+ G e t R e s i z e F i l t e r S u p p o r t %
1492% %
1493% %
1494% %
1495%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1496%
1497% GetResizeFilterSupport() return the current support window size for this
1498% filter. Note that this may have been enlarged by filter:blur factor.
1499%
1500% The format of the GetResizeFilterSupport method is:
1501%
1502% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1503%
1504% A description of each parameter follows:
1505%
1506% o filter: Image filter to use.
1507%
1508*/
1509MagickExport MagickRealType GetResizeFilterSupport(
1510 const ResizeFilter *resize_filter)
1511{
1512 assert(resize_filter != (ResizeFilter *) NULL);
1513 assert(resize_filter->signature == MagickSignature);
1514 return(resize_filter->support*resize_filter->blur);
1515}
1516
1517/*
1518%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1519% %
1520% %
1521% %
1522+ G e t R e s i z e F i l t e r W e i g h t %
1523% %
1524% %
1525% %
1526%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1527%
1528% GetResizeFilterWeight evaluates the specified resize filter at the point x
1529% which usally lies between zero and the filters current 'support' and
1530% returns the weight of the filter function at that point.
1531%
1532% The format of the GetResizeFilterWeight method is:
1533%
1534% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1535% const MagickRealType x)
1536%
1537% A description of each parameter follows:
1538%
1539% o filter: the filter type.
1540%
1541% o x: the point.
1542%
1543*/
1544MagickExport MagickRealType GetResizeFilterWeight(
1545 const ResizeFilter *resize_filter,const MagickRealType x)
1546{
1547 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001548 scale,
1549 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001550
1551 /*
1552 Windowing function - scale the weighting filter by this amount.
1553 */
1554 assert(resize_filter != (ResizeFilter *) NULL);
1555 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001556 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001557 if ((resize_filter->window_support < MagickEpsilon) ||
1558 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001559 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001560 else
1561 {
anthony55f12332010-09-10 01:13:02 +00001562 scale=resize_filter->scale;
1563 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001564 }
anthony55f12332010-09-10 01:13:02 +00001565 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001566}
1567
1568/*
1569%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1570% %
1571% %
1572% %
1573% M a g n i f y I m a g e %
1574% %
1575% %
1576% %
1577%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1578%
1579% MagnifyImage() is a convenience method that scales an image proportionally
1580% to twice its size.
1581%
1582% The format of the MagnifyImage method is:
1583%
1584% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1585%
1586% A description of each parameter follows:
1587%
1588% o image: the image.
1589%
1590% o exception: return any errors or warnings in this structure.
1591%
1592*/
1593MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1594{
1595 Image
1596 *magnify_image;
1597
1598 assert(image != (Image *) NULL);
1599 assert(image->signature == MagickSignature);
1600 if (image->debug != MagickFalse)
1601 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1602 assert(exception != (ExceptionInfo *) NULL);
1603 assert(exception->signature == MagickSignature);
1604 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1605 1.0,exception);
1606 return(magnify_image);
1607}
1608
1609/*
1610%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1611% %
1612% %
1613% %
1614% M i n i f y I m a g e %
1615% %
1616% %
1617% %
1618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1619%
1620% MinifyImage() is a convenience method that scales an image proportionally
1621% to half its size.
1622%
1623% The format of the MinifyImage method is:
1624%
1625% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1626%
1627% A description of each parameter follows:
1628%
1629% o image: the image.
1630%
1631% o exception: return any errors or warnings in this structure.
1632%
1633*/
1634MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1635{
1636 Image
1637 *minify_image;
1638
1639 assert(image != (Image *) NULL);
1640 assert(image->signature == MagickSignature);
1641 if (image->debug != MagickFalse)
1642 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1643 assert(exception != (ExceptionInfo *) NULL);
1644 assert(exception->signature == MagickSignature);
1645 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1646 1.0,exception);
1647 return(minify_image);
1648}
1649
1650/*
1651%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1652% %
1653% %
1654% %
1655% R e s a m p l e I m a g e %
1656% %
1657% %
1658% %
1659%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1660%
1661% ResampleImage() resize image in terms of its pixel size, so that when
1662% displayed at the given resolution it will be the same size in terms of
1663% real world units as the original image at the original resolution.
1664%
1665% The format of the ResampleImage method is:
1666%
1667% Image *ResampleImage(Image *image,const double x_resolution,
1668% const double y_resolution,const FilterTypes filter,const double blur,
1669% ExceptionInfo *exception)
1670%
1671% A description of each parameter follows:
1672%
1673% o image: the image to be resized to fit the given resolution.
1674%
1675% o x_resolution: the new image x resolution.
1676%
1677% o y_resolution: the new image y resolution.
1678%
1679% o filter: Image filter to use.
1680%
1681% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1682%
1683*/
1684MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1685 const double y_resolution,const FilterTypes filter,const double blur,
1686 ExceptionInfo *exception)
1687{
1688#define ResampleImageTag "Resample/Image"
1689
1690 Image
1691 *resample_image;
1692
cristybb503372010-05-27 20:51:26 +00001693 size_t
cristy3ed852e2009-09-05 21:47:34 +00001694 height,
1695 width;
1696
1697 /*
1698 Initialize sampled image attributes.
1699 */
1700 assert(image != (const Image *) NULL);
1701 assert(image->signature == MagickSignature);
1702 if (image->debug != MagickFalse)
1703 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1704 assert(exception != (ExceptionInfo *) NULL);
1705 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001706 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1707 72.0 : image->x_resolution)+0.5);
1708 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1709 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001710 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1711 if (resample_image != (Image *) NULL)
1712 {
1713 resample_image->x_resolution=x_resolution;
1714 resample_image->y_resolution=y_resolution;
1715 }
1716 return(resample_image);
1717}
1718#if defined(MAGICKCORE_LQR_DELEGATE)
1719
1720/*
1721%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1722% %
1723% %
1724% %
1725% L i q u i d R e s c a l e I m a g e %
1726% %
1727% %
1728% %
1729%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1730%
1731% LiquidRescaleImage() rescales image with seam carving.
1732%
1733% The format of the LiquidRescaleImage method is:
1734%
1735% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001736% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001737% const double delta_x,const double rigidity,ExceptionInfo *exception)
1738%
1739% A description of each parameter follows:
1740%
1741% o image: the image.
1742%
1743% o columns: the number of columns in the rescaled image.
1744%
1745% o rows: the number of rows in the rescaled image.
1746%
1747% o delta_x: maximum seam transversal step (0 means straight seams).
1748%
1749% o rigidity: introduce a bias for non-straight seams (typically 0).
1750%
1751% o exception: return any errors or warnings in this structure.
1752%
1753*/
cristy9af9b5d2010-08-15 17:04:28 +00001754MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1755 const size_t rows,const double delta_x,const double rigidity,
1756 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001757{
1758#define LiquidRescaleImageTag "Rescale/Image"
1759
cristyc5c6f662010-09-22 14:23:02 +00001760 CacheView
1761 *rescale_view;
1762
cristy3ed852e2009-09-05 21:47:34 +00001763 const char
1764 *map;
1765
1766 guchar
1767 *packet;
1768
1769 Image
1770 *rescale_image;
1771
1772 int
1773 x,
1774 y;
1775
1776 LqrCarver
1777 *carver;
1778
1779 LqrRetVal
1780 lqr_status;
1781
1782 MagickBooleanType
1783 status;
1784
1785 MagickPixelPacket
1786 pixel;
1787
1788 unsigned char
1789 *pixels;
1790
1791 /*
1792 Liquid rescale image.
1793 */
1794 assert(image != (const Image *) NULL);
1795 assert(image->signature == MagickSignature);
1796 if (image->debug != MagickFalse)
1797 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1798 assert(exception != (ExceptionInfo *) NULL);
1799 assert(exception->signature == MagickSignature);
1800 if ((columns == 0) || (rows == 0))
1801 return((Image *) NULL);
1802 if ((columns == image->columns) && (rows == image->rows))
1803 return(CloneImage(image,0,0,MagickTrue,exception));
1804 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001805 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001806 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1807 {
1808 Image
1809 *resize_image;
1810
cristybb503372010-05-27 20:51:26 +00001811 size_t
cristy3ed852e2009-09-05 21:47:34 +00001812 height,
1813 width;
1814
1815 /*
1816 Honor liquid resize size limitations.
1817 */
1818 for (width=image->columns; columns >= (2*width-1); width*=2);
1819 for (height=image->rows; rows >= (2*height-1); height*=2);
1820 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1821 exception);
1822 if (resize_image == (Image *) NULL)
1823 return((Image *) NULL);
1824 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1825 rigidity,exception);
1826 resize_image=DestroyImage(resize_image);
1827 return(rescale_image);
1828 }
1829 map="RGB";
1830 if (image->matte == MagickFalse)
1831 map="RGBA";
1832 if (image->colorspace == CMYKColorspace)
1833 {
1834 map="CMYK";
1835 if (image->matte == MagickFalse)
1836 map="CMYKA";
1837 }
1838 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1839 strlen(map)*sizeof(*pixels));
1840 if (pixels == (unsigned char *) NULL)
1841 return((Image *) NULL);
1842 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1843 pixels,exception);
1844 if (status == MagickFalse)
1845 {
1846 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1847 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1848 }
1849 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1850 if (carver == (LqrCarver *) NULL)
1851 {
1852 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1853 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1854 }
1855 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1856 lqr_status=lqr_carver_resize(carver,columns,rows);
1857 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1858 lqr_carver_get_height(carver),MagickTrue,exception);
1859 if (rescale_image == (Image *) NULL)
1860 {
1861 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1862 return((Image *) NULL);
1863 }
1864 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1865 {
1866 InheritException(exception,&rescale_image->exception);
1867 rescale_image=DestroyImage(rescale_image);
1868 return((Image *) NULL);
1869 }
1870 GetMagickPixelPacket(rescale_image,&pixel);
1871 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001872 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001873 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1874 {
1875 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001876 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001877
1878 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001879 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001880
anthony22aad252010-09-23 06:59:07 +00001881 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001882 if (q == (PixelPacket *) NULL)
1883 break;
cristyc5c6f662010-09-22 14:23:02 +00001884 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001885 pixel.red=QuantumRange*(packet[0]/255.0);
1886 pixel.green=QuantumRange*(packet[1]/255.0);
1887 pixel.blue=QuantumRange*(packet[2]/255.0);
1888 if (image->colorspace != CMYKColorspace)
1889 {
1890 if (image->matte == MagickFalse)
1891 pixel.opacity=QuantumRange*(packet[3]/255.0);
1892 }
1893 else
1894 {
1895 pixel.index=QuantumRange*(packet[3]/255.0);
1896 if (image->matte == MagickFalse)
1897 pixel.opacity=QuantumRange*(packet[4]/255.0);
1898 }
1899 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001900 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001901 break;
1902 }
cristyc5c6f662010-09-22 14:23:02 +00001903 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001904 /*
1905 Relinquish resources.
1906 */
1907 lqr_carver_destroy(carver);
1908 return(rescale_image);
1909}
1910#else
1911MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001912 const size_t magick_unused(columns),const size_t magick_unused(rows),
1913 const double magick_unused(delta_x),const double magick_unused(rigidity),
1914 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001915{
1916 assert(image != (const Image *) NULL);
1917 assert(image->signature == MagickSignature);
1918 if (image->debug != MagickFalse)
1919 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1920 assert(exception != (ExceptionInfo *) NULL);
1921 assert(exception->signature == MagickSignature);
1922 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1923 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1924 return((Image *) NULL);
1925}
1926#endif
1927
1928/*
1929%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1930% %
1931% %
1932% %
1933% R e s i z e I m a g e %
1934% %
1935% %
1936% %
1937%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1938%
1939% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001940% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001941%
1942% If an undefined filter is given the filter defaults to Mitchell for a
1943% colormapped image, a image with a matte channel, or if the image is
1944% enlarged. Otherwise the filter defaults to a Lanczos.
1945%
1946% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1947%
1948% The format of the ResizeImage method is:
1949%
cristybb503372010-05-27 20:51:26 +00001950% Image *ResizeImage(Image *image,const size_t columns,
1951% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001952% ExceptionInfo *exception)
1953%
1954% A description of each parameter follows:
1955%
1956% o image: the image.
1957%
1958% o columns: the number of columns in the scaled image.
1959%
1960% o rows: the number of rows in the scaled image.
1961%
1962% o filter: Image filter to use.
1963%
cristy9af9b5d2010-08-15 17:04:28 +00001964% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1965% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001966%
1967% o exception: return any errors or warnings in this structure.
1968%
1969*/
1970
1971typedef struct _ContributionInfo
1972{
1973 MagickRealType
1974 weight;
1975
cristybb503372010-05-27 20:51:26 +00001976 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001977 pixel;
1978} ContributionInfo;
1979
1980static ContributionInfo **DestroyContributionThreadSet(
1981 ContributionInfo **contribution)
1982{
cristybb503372010-05-27 20:51:26 +00001983 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001984 i;
1985
1986 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001987 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001988 if (contribution[i] != (ContributionInfo *) NULL)
1989 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1990 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001991 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001992 return(contribution);
1993}
1994
1995static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1996{
cristybb503372010-05-27 20:51:26 +00001997 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001998 i;
1999
2000 ContributionInfo
2001 **contribution;
2002
cristybb503372010-05-27 20:51:26 +00002003 size_t
cristy3ed852e2009-09-05 21:47:34 +00002004 number_threads;
2005
2006 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002007 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00002008 sizeof(*contribution));
2009 if (contribution == (ContributionInfo **) NULL)
2010 return((ContributionInfo **) NULL);
2011 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00002012 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002013 {
2014 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2015 sizeof(**contribution));
2016 if (contribution[i] == (ContributionInfo *) NULL)
2017 return(DestroyContributionThreadSet(contribution));
2018 }
2019 return(contribution);
2020}
2021
2022static inline double MagickMax(const double x,const double y)
2023{
2024 if (x > y)
2025 return(x);
2026 return(y);
2027}
2028
2029static inline double MagickMin(const double x,const double y)
2030{
2031 if (x < y)
2032 return(x);
2033 return(y);
2034}
2035
2036static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2037 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002038 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002039{
2040#define ResizeImageTag "Resize/Image"
2041
cristyfa112112010-01-04 17:48:07 +00002042 CacheView
2043 *image_view,
2044 *resize_view;
2045
cristy3ed852e2009-09-05 21:47:34 +00002046 ClassType
2047 storage_class;
2048
2049 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002050 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002051
cristy3ed852e2009-09-05 21:47:34 +00002052 MagickBooleanType
2053 status;
2054
2055 MagickPixelPacket
2056 zero;
2057
2058 MagickRealType
2059 scale,
2060 support;
2061
cristy9af9b5d2010-08-15 17:04:28 +00002062 ssize_t
2063 x;
2064
cristy3ed852e2009-09-05 21:47:34 +00002065 /*
2066 Apply filter to resize horizontally from image to resize image.
2067 */
cristy5d824382010-09-06 14:00:17 +00002068 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002069 support=scale*GetResizeFilterSupport(resize_filter);
2070 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2071 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2072 {
2073 InheritException(exception,&resize_image->exception);
2074 return(MagickFalse);
2075 }
2076 if (support < 0.5)
2077 {
2078 /*
nicolas07bac812010-09-19 18:47:02 +00002079 Support too small even for nearest neighbour: Reduce to point
2080 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002081 */
2082 support=(MagickRealType) 0.5;
2083 scale=1.0;
2084 }
2085 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2086 if (contributions == (ContributionInfo **) NULL)
2087 {
2088 (void) ThrowMagickException(exception,GetMagickModule(),
2089 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2090 return(MagickFalse);
2091 }
2092 status=MagickTrue;
2093 scale=1.0/scale;
2094 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2095 image_view=AcquireCacheView(image);
2096 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002097#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002098 #pragma omp parallel for shared(status)
2099#endif
cristybb503372010-05-27 20:51:26 +00002100 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002101 {
cristy3ed852e2009-09-05 21:47:34 +00002102 MagickRealType
2103 center,
2104 density;
2105
2106 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002107 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002108
2109 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002110 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002111
cristy03dbbd22010-09-19 23:04:47 +00002112 register ContributionInfo
2113 *restrict contribution;
2114
cristy3ed852e2009-09-05 21:47:34 +00002115 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002116 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002117
cristy3ed852e2009-09-05 21:47:34 +00002118 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002119 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002120
cristy03dbbd22010-09-19 23:04:47 +00002121 register ssize_t
2122 y;
2123
cristy9af9b5d2010-08-15 17:04:28 +00002124 ssize_t
2125 n,
2126 start,
2127 stop;
2128
cristy3ed852e2009-09-05 21:47:34 +00002129 if (status == MagickFalse)
2130 continue;
2131 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002132 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2133 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002134 density=0.0;
2135 contribution=contributions[GetOpenMPThreadId()];
2136 for (n=0; n < (stop-start); n++)
2137 {
2138 contribution[n].pixel=start+n;
2139 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2140 ((MagickRealType) (start+n)-center+0.5));
2141 density+=contribution[n].weight;
2142 }
2143 if ((density != 0.0) && (density != 1.0))
2144 {
cristybb503372010-05-27 20:51:26 +00002145 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002146 i;
2147
2148 /*
2149 Normalize.
2150 */
2151 density=1.0/density;
2152 for (i=0; i < n; i++)
2153 contribution[i].weight*=density;
2154 }
cristy9af9b5d2010-08-15 17:04:28 +00002155 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2156 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002157 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2158 exception);
2159 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2160 {
2161 status=MagickFalse;
2162 continue;
2163 }
2164 indexes=GetCacheViewVirtualIndexQueue(image_view);
2165 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002166 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002167 {
cristy3ed852e2009-09-05 21:47:34 +00002168 MagickPixelPacket
2169 pixel;
2170
2171 MagickRealType
2172 alpha;
2173
cristybb503372010-05-27 20:51:26 +00002174 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002175 i;
2176
cristy9af9b5d2010-08-15 17:04:28 +00002177 ssize_t
2178 j;
2179
cristy3ed852e2009-09-05 21:47:34 +00002180 pixel=zero;
2181 if (image->matte == MagickFalse)
2182 {
2183 for (i=0; i < n; i++)
2184 {
2185 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2186 (contribution[i].pixel-contribution[0].pixel);
2187 alpha=contribution[i].weight;
2188 pixel.red+=alpha*(p+j)->red;
2189 pixel.green+=alpha*(p+j)->green;
2190 pixel.blue+=alpha*(p+j)->blue;
2191 pixel.opacity+=alpha*(p+j)->opacity;
2192 }
cristyce70c172010-01-07 17:15:30 +00002193 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2194 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2195 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2196 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002197 if ((image->colorspace == CMYKColorspace) &&
2198 (resize_image->colorspace == CMYKColorspace))
2199 {
2200 for (i=0; i < n; i++)
2201 {
2202 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2203 (contribution[i].pixel-contribution[0].pixel);
2204 alpha=contribution[i].weight;
2205 pixel.index+=alpha*indexes[j];
2206 }
cristyce70c172010-01-07 17:15:30 +00002207 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002208 }
2209 }
2210 else
2211 {
2212 MagickRealType
2213 gamma;
2214
2215 gamma=0.0;
2216 for (i=0; i < n; i++)
2217 {
2218 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2219 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002220 alpha=contribution[i].weight*QuantumScale*
2221 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002222 pixel.red+=alpha*(p+j)->red;
2223 pixel.green+=alpha*(p+j)->green;
2224 pixel.blue+=alpha*(p+j)->blue;
2225 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2226 gamma+=alpha;
2227 }
2228 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002229 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2230 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2231 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2232 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002233 if ((image->colorspace == CMYKColorspace) &&
2234 (resize_image->colorspace == CMYKColorspace))
2235 {
2236 for (i=0; i < n; i++)
2237 {
2238 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2239 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002240 alpha=contribution[i].weight*QuantumScale*
2241 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002242 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002243 }
cristyce70c172010-01-07 17:15:30 +00002244 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2245 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002246 }
2247 }
2248 if ((resize_image->storage_class == PseudoClass) &&
2249 (image->storage_class == PseudoClass))
2250 {
cristybb503372010-05-27 20:51:26 +00002251 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002252 1.0)+0.5);
2253 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2254 (contribution[i-start].pixel-contribution[0].pixel);
2255 resize_indexes[y]=indexes[j];
2256 }
2257 q++;
2258 }
2259 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2260 status=MagickFalse;
2261 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2262 {
2263 MagickBooleanType
2264 proceed;
2265
cristyb5d5f722009-11-04 03:03:49 +00002266#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002267 #pragma omp critical (MagickCore_HorizontalFilter)
2268#endif
cristy9af9b5d2010-08-15 17:04:28 +00002269 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002270 if (proceed == MagickFalse)
2271 status=MagickFalse;
2272 }
2273 }
2274 resize_view=DestroyCacheView(resize_view);
2275 image_view=DestroyCacheView(image_view);
2276 contributions=DestroyContributionThreadSet(contributions);
2277 return(status);
2278}
2279
2280static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2281 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002282 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002283{
cristyfa112112010-01-04 17:48:07 +00002284 CacheView
2285 *image_view,
2286 *resize_view;
2287
cristy3ed852e2009-09-05 21:47:34 +00002288 ClassType
2289 storage_class;
2290
2291 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002292 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002293
cristy3ed852e2009-09-05 21:47:34 +00002294 MagickBooleanType
2295 status;
2296
2297 MagickPixelPacket
2298 zero;
2299
2300 MagickRealType
2301 scale,
2302 support;
2303
cristy9af9b5d2010-08-15 17:04:28 +00002304 ssize_t
2305 y;
2306
cristy3ed852e2009-09-05 21:47:34 +00002307 /*
cristy9af9b5d2010-08-15 17:04:28 +00002308 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002309 */
cristy5d824382010-09-06 14:00:17 +00002310 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002311 support=scale*GetResizeFilterSupport(resize_filter);
2312 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2313 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2314 {
2315 InheritException(exception,&resize_image->exception);
2316 return(MagickFalse);
2317 }
2318 if (support < 0.5)
2319 {
2320 /*
nicolas07bac812010-09-19 18:47:02 +00002321 Support too small even for nearest neighbour: Reduce to point
2322 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002323 */
2324 support=(MagickRealType) 0.5;
2325 scale=1.0;
2326 }
2327 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2328 if (contributions == (ContributionInfo **) NULL)
2329 {
2330 (void) ThrowMagickException(exception,GetMagickModule(),
2331 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2332 return(MagickFalse);
2333 }
2334 status=MagickTrue;
2335 scale=1.0/scale;
2336 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2337 image_view=AcquireCacheView(image);
2338 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002339#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002340 #pragma omp parallel for shared(status)
2341#endif
cristybb503372010-05-27 20:51:26 +00002342 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002343 {
cristy3ed852e2009-09-05 21:47:34 +00002344 MagickRealType
2345 center,
2346 density;
2347
2348 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002349 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002350
2351 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002352 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002353
cristy03dbbd22010-09-19 23:04:47 +00002354 register ContributionInfo
2355 *restrict contribution;
2356
cristy3ed852e2009-09-05 21:47:34 +00002357 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002358 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002359
cristy9af9b5d2010-08-15 17:04:28 +00002360 register PixelPacket
2361 *restrict q;
2362
cristybb503372010-05-27 20:51:26 +00002363 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002364 x;
2365
cristy9af9b5d2010-08-15 17:04:28 +00002366 ssize_t
2367 n,
2368 start,
2369 stop;
cristy3ed852e2009-09-05 21:47:34 +00002370
2371 if (status == MagickFalse)
2372 continue;
cristy679e6962010-03-18 00:42:45 +00002373 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002374 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2375 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002376 density=0.0;
2377 contribution=contributions[GetOpenMPThreadId()];
2378 for (n=0; n < (stop-start); n++)
2379 {
2380 contribution[n].pixel=start+n;
2381 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2382 ((MagickRealType) (start+n)-center+0.5));
2383 density+=contribution[n].weight;
2384 }
2385 if ((density != 0.0) && (density != 1.0))
2386 {
cristybb503372010-05-27 20:51:26 +00002387 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002388 i;
2389
2390 /*
2391 Normalize.
2392 */
2393 density=1.0/density;
2394 for (i=0; i < n; i++)
2395 contribution[i].weight*=density;
2396 }
2397 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002398 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2399 exception);
cristy3ed852e2009-09-05 21:47:34 +00002400 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2401 exception);
2402 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2403 {
2404 status=MagickFalse;
2405 continue;
2406 }
2407 indexes=GetCacheViewVirtualIndexQueue(image_view);
2408 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002409 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002410 {
cristy3ed852e2009-09-05 21:47:34 +00002411 MagickPixelPacket
2412 pixel;
2413
2414 MagickRealType
2415 alpha;
2416
cristybb503372010-05-27 20:51:26 +00002417 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002418 i;
2419
cristy9af9b5d2010-08-15 17:04:28 +00002420 ssize_t
2421 j;
2422
cristy3ed852e2009-09-05 21:47:34 +00002423 pixel=zero;
2424 if (image->matte == MagickFalse)
2425 {
2426 for (i=0; i < n; i++)
2427 {
cristybb503372010-05-27 20:51:26 +00002428 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002429 image->columns+x);
2430 alpha=contribution[i].weight;
2431 pixel.red+=alpha*(p+j)->red;
2432 pixel.green+=alpha*(p+j)->green;
2433 pixel.blue+=alpha*(p+j)->blue;
2434 pixel.opacity+=alpha*(p+j)->opacity;
2435 }
cristyce70c172010-01-07 17:15:30 +00002436 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2437 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2438 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2439 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002440 if ((image->colorspace == CMYKColorspace) &&
2441 (resize_image->colorspace == CMYKColorspace))
2442 {
2443 for (i=0; i < n; i++)
2444 {
cristybb503372010-05-27 20:51:26 +00002445 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002446 image->columns+x);
2447 alpha=contribution[i].weight;
2448 pixel.index+=alpha*indexes[j];
2449 }
cristyce70c172010-01-07 17:15:30 +00002450 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002451 }
2452 }
2453 else
2454 {
2455 MagickRealType
2456 gamma;
2457
2458 gamma=0.0;
2459 for (i=0; i < n; i++)
2460 {
cristybb503372010-05-27 20:51:26 +00002461 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002462 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002463 alpha=contribution[i].weight*QuantumScale*
2464 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002465 pixel.red+=alpha*(p+j)->red;
2466 pixel.green+=alpha*(p+j)->green;
2467 pixel.blue+=alpha*(p+j)->blue;
2468 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2469 gamma+=alpha;
2470 }
2471 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002472 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2473 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2474 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2475 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002476 if ((image->colorspace == CMYKColorspace) &&
2477 (resize_image->colorspace == CMYKColorspace))
2478 {
2479 for (i=0; i < n; i++)
2480 {
cristybb503372010-05-27 20:51:26 +00002481 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002482 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002483 alpha=contribution[i].weight*QuantumScale*
2484 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002485 pixel.index+=alpha*indexes[j];
2486 }
cristyce70c172010-01-07 17:15:30 +00002487 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2488 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002489 }
2490 }
2491 if ((resize_image->storage_class == PseudoClass) &&
2492 (image->storage_class == PseudoClass))
2493 {
cristybb503372010-05-27 20:51:26 +00002494 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002495 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002496 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002497 image->columns+x);
2498 resize_indexes[x]=indexes[j];
2499 }
2500 q++;
2501 }
2502 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2503 status=MagickFalse;
2504 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2505 {
2506 MagickBooleanType
2507 proceed;
2508
cristyb5d5f722009-11-04 03:03:49 +00002509#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002510 #pragma omp critical (MagickCore_VerticalFilter)
2511#endif
cristy9af9b5d2010-08-15 17:04:28 +00002512 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002513 if (proceed == MagickFalse)
2514 status=MagickFalse;
2515 }
2516 }
2517 resize_view=DestroyCacheView(resize_view);
2518 image_view=DestroyCacheView(image_view);
2519 contributions=DestroyContributionThreadSet(contributions);
2520 return(status);
2521}
2522
cristybb503372010-05-27 20:51:26 +00002523MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2524 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002525 ExceptionInfo *exception)
2526{
2527#define WorkLoadFactor 0.265
2528
2529 FilterTypes
2530 filter_type;
2531
2532 Image
2533 *filter_image,
2534 *resize_image;
2535
cristy9af9b5d2010-08-15 17:04:28 +00002536 MagickOffsetType
2537 offset;
2538
cristy3ed852e2009-09-05 21:47:34 +00002539 MagickRealType
2540 x_factor,
2541 y_factor;
2542
2543 MagickSizeType
2544 span;
2545
2546 MagickStatusType
2547 status;
2548
2549 ResizeFilter
2550 *resize_filter;
2551
cristy3ed852e2009-09-05 21:47:34 +00002552 /*
2553 Acquire resize image.
2554 */
2555 assert(image != (Image *) NULL);
2556 assert(image->signature == MagickSignature);
2557 if (image->debug != MagickFalse)
2558 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2559 assert(exception != (ExceptionInfo *) NULL);
2560 assert(exception->signature == MagickSignature);
2561 if ((columns == 0) || (rows == 0))
2562 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2563 if ((columns == image->columns) && (rows == image->rows) &&
2564 (filter == UndefinedFilter) && (blur == 1.0))
2565 return(CloneImage(image,0,0,MagickTrue,exception));
2566 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2567 if (resize_image == (Image *) NULL)
2568 return(resize_image);
2569 /*
2570 Acquire resize filter.
2571 */
2572 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2573 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2574 if ((x_factor*y_factor) > WorkLoadFactor)
2575 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2576 else
2577 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2578 if (filter_image == (Image *) NULL)
2579 return(DestroyImage(resize_image));
2580 filter_type=LanczosFilter;
2581 if (filter != UndefinedFilter)
2582 filter_type=filter;
2583 else
2584 if ((x_factor == 1.0) && (y_factor == 1.0))
2585 filter_type=PointFilter;
2586 else
2587 if ((image->storage_class == PseudoClass) ||
2588 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2589 filter_type=MitchellFilter;
2590 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2591 exception);
2592 /*
2593 Resize image.
2594 */
cristy9af9b5d2010-08-15 17:04:28 +00002595 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002596 if ((x_factor*y_factor) > WorkLoadFactor)
2597 {
2598 span=(MagickSizeType) (filter_image->columns+rows);
2599 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002600 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002601 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002602 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002603 }
2604 else
2605 {
2606 span=(MagickSizeType) (filter_image->rows+columns);
2607 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002608 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002609 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002610 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002611 }
2612 /*
2613 Free resources.
2614 */
2615 filter_image=DestroyImage(filter_image);
2616 resize_filter=DestroyResizeFilter(resize_filter);
2617 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2618 return((Image *) NULL);
2619 resize_image->type=image->type;
2620 return(resize_image);
2621}
2622
2623/*
2624%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2625% %
2626% %
2627% %
2628% S a m p l e I m a g e %
2629% %
2630% %
2631% %
2632%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2633%
2634% SampleImage() scales an image to the desired dimensions with pixel
2635% sampling. Unlike other scaling methods, this method does not introduce
2636% any additional color into the scaled image.
2637%
2638% The format of the SampleImage method is:
2639%
cristybb503372010-05-27 20:51:26 +00002640% Image *SampleImage(const Image *image,const size_t columns,
2641% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002642%
2643% A description of each parameter follows:
2644%
2645% o image: the image.
2646%
2647% o columns: the number of columns in the sampled image.
2648%
2649% o rows: the number of rows in the sampled image.
2650%
2651% o exception: return any errors or warnings in this structure.
2652%
2653*/
cristybb503372010-05-27 20:51:26 +00002654MagickExport Image *SampleImage(const Image *image,const size_t columns,
2655 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002656{
2657#define SampleImageTag "Sample/Image"
2658
cristyc4c8d132010-01-07 01:58:38 +00002659 CacheView
2660 *image_view,
2661 *sample_view;
2662
cristy3ed852e2009-09-05 21:47:34 +00002663 Image
2664 *sample_image;
2665
cristy3ed852e2009-09-05 21:47:34 +00002666 MagickBooleanType
2667 status;
2668
cristy5f959472010-05-27 22:19:46 +00002669 MagickOffsetType
2670 progress;
2671
cristybb503372010-05-27 20:51:26 +00002672 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002673 x;
2674
cristy5f959472010-05-27 22:19:46 +00002675 ssize_t
2676 *x_offset,
2677 y;
2678
cristy3ed852e2009-09-05 21:47:34 +00002679 /*
2680 Initialize sampled image attributes.
2681 */
2682 assert(image != (const Image *) NULL);
2683 assert(image->signature == MagickSignature);
2684 if (image->debug != MagickFalse)
2685 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2686 assert(exception != (ExceptionInfo *) NULL);
2687 assert(exception->signature == MagickSignature);
2688 if ((columns == 0) || (rows == 0))
2689 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2690 if ((columns == image->columns) && (rows == image->rows))
2691 return(CloneImage(image,0,0,MagickTrue,exception));
2692 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2693 if (sample_image == (Image *) NULL)
2694 return((Image *) NULL);
2695 /*
2696 Allocate scan line buffer and column offset buffers.
2697 */
cristybb503372010-05-27 20:51:26 +00002698 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002699 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002700 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002701 {
2702 sample_image=DestroyImage(sample_image);
2703 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2704 }
cristybb503372010-05-27 20:51:26 +00002705 for (x=0; x < (ssize_t) sample_image->columns; x++)
2706 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002707 sample_image->columns);
2708 /*
2709 Sample each row.
2710 */
2711 status=MagickTrue;
2712 progress=0;
2713 image_view=AcquireCacheView(image);
2714 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002715#if defined(MAGICKCORE_OPENMP_SUPPORT)
2716 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002717#endif
cristybb503372010-05-27 20:51:26 +00002718 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002719 {
cristy3ed852e2009-09-05 21:47:34 +00002720 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002721 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002722
2723 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002724 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002725
2726 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002727 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002728
cristy3ed852e2009-09-05 21:47:34 +00002729 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002730 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002731
cristy03dbbd22010-09-19 23:04:47 +00002732 register ssize_t
2733 x;
2734
cristy9af9b5d2010-08-15 17:04:28 +00002735 ssize_t
2736 y_offset;
2737
cristy3ed852e2009-09-05 21:47:34 +00002738 if (status == MagickFalse)
2739 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002740 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2741 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002742 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2743 exception);
2744 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2745 exception);
2746 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2747 {
2748 status=MagickFalse;
2749 continue;
2750 }
2751 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2752 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2753 /*
2754 Sample each column.
2755 */
cristybb503372010-05-27 20:51:26 +00002756 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002757 *q++=p[x_offset[x]];
2758 if ((image->storage_class == PseudoClass) ||
2759 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002760 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002761 sample_indexes[x]=indexes[x_offset[x]];
2762 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2763 status=MagickFalse;
2764 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2765 {
2766 MagickBooleanType
2767 proceed;
2768
cristyb5d5f722009-11-04 03:03:49 +00002769#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002770 #pragma omp critical (MagickCore_SampleImage)
2771#endif
2772 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2773 if (proceed == MagickFalse)
2774 status=MagickFalse;
2775 }
2776 }
2777 image_view=DestroyCacheView(image_view);
2778 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002779 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002780 sample_image->type=image->type;
2781 return(sample_image);
2782}
2783
2784/*
2785%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2786% %
2787% %
2788% %
2789% S c a l e I m a g e %
2790% %
2791% %
2792% %
2793%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2794%
2795% ScaleImage() changes the size of an image to the given dimensions.
2796%
2797% The format of the ScaleImage method is:
2798%
cristybb503372010-05-27 20:51:26 +00002799% Image *ScaleImage(const Image *image,const size_t columns,
2800% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002801%
2802% A description of each parameter follows:
2803%
2804% o image: the image.
2805%
2806% o columns: the number of columns in the scaled image.
2807%
2808% o rows: the number of rows in the scaled image.
2809%
2810% o exception: return any errors or warnings in this structure.
2811%
2812*/
cristybb503372010-05-27 20:51:26 +00002813MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2814 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002815{
2816#define ScaleImageTag "Scale/Image"
2817
cristyed6cb232010-01-20 03:07:53 +00002818 CacheView
2819 *image_view,
2820 *scale_view;
2821
cristy3ed852e2009-09-05 21:47:34 +00002822 Image
2823 *scale_image;
2824
cristy3ed852e2009-09-05 21:47:34 +00002825 MagickBooleanType
2826 next_column,
2827 next_row,
2828 proceed;
2829
2830 MagickPixelPacket
2831 pixel,
2832 *scale_scanline,
2833 *scanline,
2834 *x_vector,
2835 *y_vector,
2836 zero;
2837
cristy3ed852e2009-09-05 21:47:34 +00002838 PointInfo
2839 scale,
2840 span;
2841
cristybb503372010-05-27 20:51:26 +00002842 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002843 i;
2844
cristy9af9b5d2010-08-15 17:04:28 +00002845 ssize_t
2846 number_rows,
2847 y;
2848
cristy3ed852e2009-09-05 21:47:34 +00002849 /*
2850 Initialize scaled image attributes.
2851 */
2852 assert(image != (const Image *) NULL);
2853 assert(image->signature == MagickSignature);
2854 if (image->debug != MagickFalse)
2855 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2856 assert(exception != (ExceptionInfo *) NULL);
2857 assert(exception->signature == MagickSignature);
2858 if ((columns == 0) || (rows == 0))
2859 return((Image *) NULL);
2860 if ((columns == image->columns) && (rows == image->rows))
2861 return(CloneImage(image,0,0,MagickTrue,exception));
2862 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2863 if (scale_image == (Image *) NULL)
2864 return((Image *) NULL);
2865 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2866 {
2867 InheritException(exception,&scale_image->exception);
2868 scale_image=DestroyImage(scale_image);
2869 return((Image *) NULL);
2870 }
2871 /*
2872 Allocate memory.
2873 */
2874 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2875 sizeof(*x_vector));
2876 scanline=x_vector;
2877 if (image->rows != scale_image->rows)
2878 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2879 sizeof(*scanline));
2880 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2881 scale_image->columns,sizeof(*scale_scanline));
2882 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2883 sizeof(*y_vector));
2884 if ((scanline == (MagickPixelPacket *) NULL) ||
2885 (scale_scanline == (MagickPixelPacket *) NULL) ||
2886 (x_vector == (MagickPixelPacket *) NULL) ||
2887 (y_vector == (MagickPixelPacket *) NULL))
2888 {
2889 scale_image=DestroyImage(scale_image);
2890 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2891 }
2892 /*
2893 Scale image.
2894 */
2895 number_rows=0;
2896 next_row=MagickTrue;
2897 span.y=1.0;
2898 scale.y=(double) scale_image->rows/(double) image->rows;
2899 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2900 sizeof(*y_vector));
2901 GetMagickPixelPacket(image,&pixel);
2902 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2903 i=0;
cristyed6cb232010-01-20 03:07:53 +00002904 image_view=AcquireCacheView(image);
2905 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002906 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002907 {
2908 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002909 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002910
2911 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002912 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002913
2914 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002915 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002916
cristy3ed852e2009-09-05 21:47:34 +00002917 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002918 *restrict s,
2919 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002920
2921 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002922 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002923
cristy9af9b5d2010-08-15 17:04:28 +00002924 register ssize_t
2925 x;
2926
cristyed6cb232010-01-20 03:07:53 +00002927 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2928 exception);
cristy3ed852e2009-09-05 21:47:34 +00002929 if (q == (PixelPacket *) NULL)
2930 break;
2931 scale_indexes=GetAuthenticIndexQueue(scale_image);
2932 if (scale_image->rows == image->rows)
2933 {
2934 /*
2935 Read a new scanline.
2936 */
cristyed6cb232010-01-20 03:07:53 +00002937 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2938 exception);
cristy3ed852e2009-09-05 21:47:34 +00002939 if (p == (const PixelPacket *) NULL)
2940 break;
cristyed6cb232010-01-20 03:07:53 +00002941 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002942 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002943 {
cristyce70c172010-01-07 17:15:30 +00002944 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2945 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2946 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002947 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002948 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002949 if (indexes != (IndexPacket *) NULL)
2950 x_vector[x].index=(MagickRealType) indexes[x];
2951 p++;
2952 }
2953 }
2954 else
2955 {
2956 /*
2957 Scale Y direction.
2958 */
2959 while (scale.y < span.y)
2960 {
cristy9af9b5d2010-08-15 17:04:28 +00002961 if ((next_row != MagickFalse) &&
2962 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002963 {
2964 /*
2965 Read a new scanline.
2966 */
cristyed6cb232010-01-20 03:07:53 +00002967 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2968 exception);
cristy3ed852e2009-09-05 21:47:34 +00002969 if (p == (const PixelPacket *) NULL)
2970 break;
cristyed6cb232010-01-20 03:07:53 +00002971 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002972 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002973 {
cristyce70c172010-01-07 17:15:30 +00002974 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2975 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2976 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002977 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002978 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002979 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002980 if (indexes != (IndexPacket *) NULL)
2981 x_vector[x].index=(MagickRealType) indexes[x];
2982 p++;
2983 }
2984 number_rows++;
2985 }
cristybb503372010-05-27 20:51:26 +00002986 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002987 {
2988 y_vector[x].red+=scale.y*x_vector[x].red;
2989 y_vector[x].green+=scale.y*x_vector[x].green;
2990 y_vector[x].blue+=scale.y*x_vector[x].blue;
2991 if (scale_image->matte != MagickFalse)
2992 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2993 if (scale_indexes != (IndexPacket *) NULL)
2994 y_vector[x].index+=scale.y*x_vector[x].index;
2995 }
2996 span.y-=scale.y;
2997 scale.y=(double) scale_image->rows/(double) image->rows;
2998 next_row=MagickTrue;
2999 }
cristybb503372010-05-27 20:51:26 +00003000 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00003001 {
3002 /*
3003 Read a new scanline.
3004 */
cristyed6cb232010-01-20 03:07:53 +00003005 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
3006 exception);
cristy3ed852e2009-09-05 21:47:34 +00003007 if (p == (const PixelPacket *) NULL)
3008 break;
cristyed6cb232010-01-20 03:07:53 +00003009 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00003010 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003011 {
cristyce70c172010-01-07 17:15:30 +00003012 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
3013 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
3014 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003015 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00003016 x_vector[x].opacity=(MagickRealType)
3017 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003018 if (indexes != (IndexPacket *) NULL)
3019 x_vector[x].index=(MagickRealType) indexes[x];
3020 p++;
3021 }
3022 number_rows++;
3023 next_row=MagickFalse;
3024 }
3025 s=scanline;
cristybb503372010-05-27 20:51:26 +00003026 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003027 {
3028 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3029 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3030 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3031 if (image->matte != MagickFalse)
3032 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3033 if (scale_indexes != (IndexPacket *) NULL)
3034 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3035 s->red=pixel.red;
3036 s->green=pixel.green;
3037 s->blue=pixel.blue;
3038 if (scale_image->matte != MagickFalse)
3039 s->opacity=pixel.opacity;
3040 if (scale_indexes != (IndexPacket *) NULL)
3041 s->index=pixel.index;
3042 s++;
3043 y_vector[x]=zero;
3044 }
3045 scale.y-=span.y;
3046 if (scale.y <= 0)
3047 {
3048 scale.y=(double) scale_image->rows/(double) image->rows;
3049 next_row=MagickTrue;
3050 }
3051 span.y=1.0;
3052 }
3053 if (scale_image->columns == image->columns)
3054 {
3055 /*
3056 Transfer scanline to scaled image.
3057 */
3058 s=scanline;
cristybb503372010-05-27 20:51:26 +00003059 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003060 {
cristyce70c172010-01-07 17:15:30 +00003061 q->red=ClampToQuantum(s->red);
3062 q->green=ClampToQuantum(s->green);
3063 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003064 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003065 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003066 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003067 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003068 q++;
3069 s++;
3070 }
3071 }
3072 else
3073 {
3074 /*
3075 Scale X direction.
3076 */
3077 pixel=zero;
3078 next_column=MagickFalse;
3079 span.x=1.0;
3080 s=scanline;
3081 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003082 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003083 {
3084 scale.x=(double) scale_image->columns/(double) image->columns;
3085 while (scale.x >= span.x)
3086 {
3087 if (next_column != MagickFalse)
3088 {
3089 pixel=zero;
3090 t++;
3091 }
3092 pixel.red+=span.x*s->red;
3093 pixel.green+=span.x*s->green;
3094 pixel.blue+=span.x*s->blue;
3095 if (image->matte != MagickFalse)
3096 pixel.opacity+=span.x*s->opacity;
3097 if (scale_indexes != (IndexPacket *) NULL)
3098 pixel.index+=span.x*s->index;
3099 t->red=pixel.red;
3100 t->green=pixel.green;
3101 t->blue=pixel.blue;
3102 if (scale_image->matte != MagickFalse)
3103 t->opacity=pixel.opacity;
3104 if (scale_indexes != (IndexPacket *) NULL)
3105 t->index=pixel.index;
3106 scale.x-=span.x;
3107 span.x=1.0;
3108 next_column=MagickTrue;
3109 }
3110 if (scale.x > 0)
3111 {
3112 if (next_column != MagickFalse)
3113 {
3114 pixel=zero;
3115 next_column=MagickFalse;
3116 t++;
3117 }
3118 pixel.red+=scale.x*s->red;
3119 pixel.green+=scale.x*s->green;
3120 pixel.blue+=scale.x*s->blue;
3121 if (scale_image->matte != MagickFalse)
3122 pixel.opacity+=scale.x*s->opacity;
3123 if (scale_indexes != (IndexPacket *) NULL)
3124 pixel.index+=scale.x*s->index;
3125 span.x-=scale.x;
3126 }
3127 s++;
3128 }
3129 if (span.x > 0)
3130 {
3131 s--;
3132 pixel.red+=span.x*s->red;
3133 pixel.green+=span.x*s->green;
3134 pixel.blue+=span.x*s->blue;
3135 if (scale_image->matte != MagickFalse)
3136 pixel.opacity+=span.x*s->opacity;
3137 if (scale_indexes != (IndexPacket *) NULL)
3138 pixel.index+=span.x*s->index;
3139 }
3140 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003141 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003142 {
3143 t->red=pixel.red;
3144 t->green=pixel.green;
3145 t->blue=pixel.blue;
3146 if (scale_image->matte != MagickFalse)
3147 t->opacity=pixel.opacity;
3148 if (scale_indexes != (IndexPacket *) NULL)
3149 t->index=pixel.index;
3150 }
3151 /*
3152 Transfer scanline to scaled image.
3153 */
3154 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003155 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003156 {
cristyce70c172010-01-07 17:15:30 +00003157 q->red=ClampToQuantum(t->red);
3158 q->green=ClampToQuantum(t->green);
3159 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003160 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003161 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003162 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003163 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003164 t++;
3165 q++;
3166 }
3167 }
cristyed6cb232010-01-20 03:07:53 +00003168 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003169 break;
cristy96b16132010-08-29 17:19:52 +00003170 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3171 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003172 if (proceed == MagickFalse)
3173 break;
3174 }
cristyed6cb232010-01-20 03:07:53 +00003175 scale_view=DestroyCacheView(scale_view);
3176 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003177 /*
3178 Free allocated memory.
3179 */
3180 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3181 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3182 if (scale_image->rows != image->rows)
3183 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3184 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3185 scale_image->type=image->type;
3186 return(scale_image);
3187}
3188
anthony02b4cb42010-10-10 04:54:35 +00003189#if 0
3190 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003191/*
3192%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3193% %
3194% %
3195% %
3196+ S e t R e s i z e F i l t e r S u p p o r t %
3197% %
3198% %
3199% %
3200%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3201%
3202% SetResizeFilterSupport() specifies which IR filter to use to window
3203%
3204% The format of the SetResizeFilterSupport method is:
3205%
3206% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3207% const MagickRealType support)
3208%
3209% A description of each parameter follows:
3210%
3211% o resize_filter: the resize filter.
3212%
3213% o support: the filter spport radius.
3214%
3215*/
3216MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3217 const MagickRealType support)
3218{
3219 assert(resize_filter != (ResizeFilter *) NULL);
3220 assert(resize_filter->signature == MagickSignature);
3221 resize_filter->support=support;
3222}
anthony02b4cb42010-10-10 04:54:35 +00003223#endif
cristy3ed852e2009-09-05 21:47:34 +00003224
3225/*
3226%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3227% %
3228% %
3229% %
3230% T h u m b n a i l I m a g e %
3231% %
3232% %
3233% %
3234%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3235%
3236% ThumbnailImage() changes the size of an image to the given dimensions and
3237% removes any associated profiles. The goal is to produce small low cost
3238% thumbnail images suited for display on the Web.
3239%
3240% The format of the ThumbnailImage method is:
3241%
cristybb503372010-05-27 20:51:26 +00003242% Image *ThumbnailImage(const Image *image,const size_t columns,
3243% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003244%
3245% A description of each parameter follows:
3246%
3247% o image: the image.
3248%
3249% o columns: the number of columns in the scaled image.
3250%
3251% o rows: the number of rows in the scaled image.
3252%
3253% o exception: return any errors or warnings in this structure.
3254%
3255*/
cristy9af9b5d2010-08-15 17:04:28 +00003256MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3257 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003258{
3259#define SampleFactor 5
3260
3261 char
3262 value[MaxTextExtent];
3263
3264 const char
3265 *name;
3266
3267 Image
3268 *thumbnail_image;
3269
3270 MagickRealType
3271 x_factor,
3272 y_factor;
3273
cristybb503372010-05-27 20:51:26 +00003274 size_t
cristy3ed852e2009-09-05 21:47:34 +00003275 version;
3276
cristy9af9b5d2010-08-15 17:04:28 +00003277 struct stat
3278 attributes;
3279
cristy3ed852e2009-09-05 21:47:34 +00003280 assert(image != (Image *) NULL);
3281 assert(image->signature == MagickSignature);
3282 if (image->debug != MagickFalse)
3283 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3284 assert(exception != (ExceptionInfo *) NULL);
3285 assert(exception->signature == MagickSignature);
3286 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3287 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3288 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003289 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3290 exception);
cristy3ed852e2009-09-05 21:47:34 +00003291 else
3292 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003293 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3294 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003295 else
3296 {
3297 Image
3298 *sample_image;
3299
3300 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3301 exception);
3302 if (sample_image == (Image *) NULL)
3303 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003304 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3305 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003306 sample_image=DestroyImage(sample_image);
3307 }
3308 if (thumbnail_image == (Image *) NULL)
3309 return(thumbnail_image);
3310 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3311 if (thumbnail_image->matte == MagickFalse)
3312 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3313 thumbnail_image->depth=8;
3314 thumbnail_image->interlace=NoInterlace;
3315 /*
3316 Strip all profiles except color profiles.
3317 */
3318 ResetImageProfileIterator(thumbnail_image);
3319 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3320 {
3321 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3322 {
cristy2b726bd2010-01-11 01:05:39 +00003323 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003324 ResetImageProfileIterator(thumbnail_image);
3325 }
3326 name=GetNextImageProfile(thumbnail_image);
3327 }
3328 (void) DeleteImageProperty(thumbnail_image,"comment");
3329 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003330 if (strstr(image->magick_filename,"//") == (char *) NULL)
3331 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003332 image->magick_filename);
3333 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3334 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3335 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3336 {
cristye8c25f92010-06-03 00:53:06 +00003337 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003338 attributes.st_mtime);
3339 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3340 }
cristye8c25f92010-06-03 00:53:06 +00003341 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003342 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003343 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003344 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003345 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3346 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3347 LocaleLower(value);
3348 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3349 (void) SetImageProperty(thumbnail_image,"software",
3350 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003351 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3352 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003353 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003354 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003355 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003356 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003357 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3358 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003359 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3360 return(thumbnail_image);
3361}