blob: 7e2b0c15bfa32010b934af51ffa2c551357dd0c3 [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) */
88 cubic[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).
cristy3ed852e2009-09-05 21:47:34 +0000224 */
225 if (x < 1.0)
226 return(resize_filter->cubic[0]+x*(resize_filter->cubic[1]+x*
227 (resize_filter->cubic[2]+x*resize_filter->cubic[3])));
228 if (x < 2.0)
cristy679e6962010-03-18 00:42:45 +0000229 return(resize_filter->cubic[4]+x*(resize_filter->cubic[5]+x*
cristy03dbbd22010-09-19 23:04:47 +0000230 (resize_filter->cubic[6]+x*resize_filter->cubic[7])));
cristy3ed852e2009-09-05 21:47:34 +0000231 return(0.0);
232}
233
234static MagickRealType Gaussian(const MagickRealType x,
235 const ResizeFilter *magick_unused(resize_filter))
236{
cristy560d8182010-09-08 22:36:25 +0000237 /*
nicolas40477452010-09-27 23:42:08 +0000238 1D Gaussian with sigma=1/2:
anthonyb6d08c52010-09-13 01:17:04 +0000239 exp(-2 x^2)/sqrt(pi/2))
cristy560d8182010-09-08 22:36:25 +0000240 */
anthonyf21ee692010-09-15 12:06:44 +0000241 /*const MagickRealType alpha = (MagickRealType) (2.0/MagickSQ2PI);*/
cristy03dbbd22010-09-19 23:04:47 +0000242 return(exp((double) (-2.0*x*x)));
cristy3ed852e2009-09-05 21:47:34 +0000243}
244
245static MagickRealType Hanning(const MagickRealType x,
246 const ResizeFilter *magick_unused(resize_filter))
247{
248 /*
nicolas40477452010-09-27 23:42:08 +0000249 Cosine window function:
250 .5+.5cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000251 */
cristyc5c6f662010-09-22 14:23:02 +0000252 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000253 return(0.5+0.5*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000254}
255
256static MagickRealType Hamming(const MagickRealType x,
257 const ResizeFilter *magick_unused(resize_filter))
258{
259 /*
nicolas40477452010-09-27 23:42:08 +0000260 Offset cosine window function:
261 .54 + .46 cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000262 */
cristyc5c6f662010-09-22 14:23:02 +0000263 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000264 return(0.54+0.46*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000265}
266
267static MagickRealType Kaiser(const MagickRealType x,
268 const ResizeFilter *magick_unused(resize_filter))
269{
270#define Alpha 6.5
271#define I0A (1.0/I0(Alpha))
272
273 /*
nicolas07bac812010-09-19 18:47:02 +0000274 Kaiser Windowing Function (bessel windowing): Alpha is a free
275 value from 5 to 8 (currently hardcoded to 6.5).
276 Future: make alpha the IOA pre-calculation, an 'expert' setting.
cristy3ed852e2009-09-05 21:47:34 +0000277 */
278 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
279}
280
281static MagickRealType Lagrange(const MagickRealType x,
282 const ResizeFilter *resize_filter)
283{
cristy3ed852e2009-09-05 21:47:34 +0000284 MagickRealType
285 value;
286
cristybb503372010-05-27 20:51:26 +0000287 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000288 i;
289
cristy9af9b5d2010-08-15 17:04:28 +0000290 ssize_t
291 n,
292 order;
293
cristy3ed852e2009-09-05 21:47:34 +0000294 /*
nicolas07bac812010-09-19 18:47:02 +0000295 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the
296 lagrange function and depends on the overall support window size
297 of the filter. That is: for a support of 2, it gives a lagrange-4
298 (piecewise cubic function).
cristy3ed852e2009-09-05 21:47:34 +0000299
nicolas07bac812010-09-19 18:47:02 +0000300 "n" identifies the piece of the piecewise polynomial.
cristy3ed852e2009-09-05 21:47:34 +0000301
nicolas07bac812010-09-19 18:47:02 +0000302 See Survey: Interpolation Methods, IEEE Transactions on Medical
303 Imaging, Vol 18, No 11, November 1999, p1049-1075, -- Equation 27
304 on p1064.
cristy3ed852e2009-09-05 21:47:34 +0000305 */
306 if (x > resize_filter->support)
307 return(0.0);
cristybb503372010-05-27 20:51:26 +0000308 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
anthonyc2d07db2010-09-15 23:47:40 +0000309 /*n=(ssize_t)((1.0*order)/2.0+x); -- which piece does x belong to */
310 n = (ssize_t)(resize_filter->window_support + x);
cristy3ed852e2009-09-05 21:47:34 +0000311 value=1.0f;
312 for (i=0; i < order; i++)
313 if (i != n)
314 value*=(n-i-x)/(n-i);
315 return(value);
316}
317
318static MagickRealType Quadratic(const MagickRealType x,
319 const ResizeFilter *magick_unused(resize_filter))
320{
321 /*
322 2rd order (quadratic) B-Spline approximation of Gaussian.
323 */
324 if (x < 0.5)
325 return(0.75-x*x);
326 if (x < 1.5)
327 return(0.5*(x-1.5)*(x-1.5));
328 return(0.0);
329}
330
anthony07a3f7f2010-09-16 03:03:11 +0000331static MagickRealType Sinc(const MagickRealType x,
cristy3ed852e2009-09-05 21:47:34 +0000332 const ResizeFilter *magick_unused(resize_filter))
333{
anthony720660f2010-09-07 10:05:14 +0000334 /*
nicolas40477452010-09-27 23:42:08 +0000335 Scaled sinc(x) function using a trig call:
nicolas07bac812010-09-19 18:47:02 +0000336 sinc(x) == sin(pi x)/(pi x).
anthony720660f2010-09-07 10:05:14 +0000337 */
anthony2d9b8b52010-09-14 08:31:07 +0000338 if (x != 0.0)
cristy560d8182010-09-08 22:36:25 +0000339 {
cristyc5c6f662010-09-22 14:23:02 +0000340 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
nicolas07bac812010-09-19 18:47:02 +0000341 return(sin((double) pix)/pix);
cristy560d8182010-09-08 22:36:25 +0000342 }
nicolas2ffd3b22010-09-24 20:27:31 +0000343 return((MagickRealType) 1.0);
anthony720660f2010-09-07 10:05:14 +0000344}
345
anthonyba5a7c32010-09-15 02:42:25 +0000346static MagickRealType SincFast(const MagickRealType x,
anthony720660f2010-09-07 10:05:14 +0000347 const ResizeFilter *magick_unused(resize_filter))
348{
cristy560d8182010-09-08 22:36:25 +0000349 /*
350 Approximations of the sinc function sin(pi x)/(pi x) over the
351 interval [-4,4] constructed by Nicolas Robidoux and Chantal
352 Racette with funding from the Natural Sciences and Engineering
353 Research Council of Canada.
nicolas07bac812010-09-19 18:47:02 +0000354
355 Although the approximations are polynomials (for low order of
356 approximation) and quotients of polynomials (for higher order of
357 approximation) and consequently are similar in form to Taylor
358 polynomials/Pade approximants, the approximations are computed
359 with a completely different technique.
360
361 Summary: These approximations are "the best" in terms of bang
362 (accuracy) for the buck (flops). More specifically: Among the
363 polynomial quotients that can be computed using a fixed number of
364 flops (with a given "+ - * / budget"), the chosen polynomial
365 quotient is the one closest to the approximated function with
366 respect to maximum absolute relative error over the given
367 interval.
368
369 The Remez algorithm, as implemented in the boost library's minimax
nicolas3aab40c2010-09-19 21:14:15 +0000370 package, is the key to the construction:
nicolas07bac812010-09-19 18:47:02 +0000371 http://www.boost.org/doc/libs/1_36_0/libs/math/doc/...
372 ...sf_and_dist/html/math_toolkit/backgrounders/remez.html
cristy560d8182010-09-08 22:36:25 +0000373 */
nicolas3aab40c2010-09-19 21:14:15 +0000374 /*
375 If outside of the interval of approximation, use the standard trig
376 formula.
377 */
anthony2d9b8b52010-09-14 08:31:07 +0000378 if (x > 4.0)
cristy03dbbd22010-09-19 23:04:47 +0000379 {
cristyc5c6f662010-09-22 14:23:02 +0000380 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
cristy03dbbd22010-09-19 23:04:47 +0000381 return(sin((double) pix)/pix);
382 }
anthony2d9b8b52010-09-14 08:31:07 +0000383 {
nicolas07bac812010-09-19 18:47:02 +0000384 /*
385 The approximations only depend on x^2 (sinc is an even
386 function).
387 */
388 const MagickRealType xx = x*x;
cristy83017922010-09-05 20:45:15 +0000389#if MAGICKCORE_QUANTUM_DEPTH <= 8
390 /*
anthony2d9b8b52010-09-14 08:31:07 +0000391 Maximum absolute relative error 6.3e-6 < 1/2^17.
cristy738e7562010-09-01 12:48:07 +0000392 */
393 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
394 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
395 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
396 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
397 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
398 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
399 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
400 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
nicolas3aab40c2010-09-19 21:14:15 +0000401 const MagickRealType p =
402 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
nicolas07bac812010-09-19 18:47:02 +0000403 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
anthony2d9b8b52010-09-14 08:31:07 +0000404#elif MAGICKCORE_QUANTUM_DEPTH <= 16
cristydbeb3eb2010-09-09 13:41:36 +0000405 /*
anthony2d9b8b52010-09-14 08:31:07 +0000406 Max. abs. rel. error 2.2e-8 < 1/2^25.
cristydbeb3eb2010-09-09 13:41:36 +0000407 */
408 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
409 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
nicolas3aab40c2010-09-19 21:14:15 +0000410 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
411 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
412 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
413 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
414 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
cristydbeb3eb2010-09-09 13:41:36 +0000415 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
416 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
417 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
anthony853d6972010-10-08 06:01:31 +0000418 const MagickRealType p =
nicolas3aab40c2010-09-19 21:14:15 +0000419 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 +0000420 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
nicolasc2d28f02010-09-27 18:56:15 +0000421#else
nicolas3aab40c2010-09-19 21:14:15 +0000422 /*
423 Max. abs. rel. error 1.2e-12 < 1/2^39.
424 */
425 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
426 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
427 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
428 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
429 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
430 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
431 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
432 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
433 const MagickRealType p =
434 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
435 const MagickRealType d0 = 1.0L;
436 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
437 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
438 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
439 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
440 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
441 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
cristy657c6352010-08-29 14:05:08 +0000442#endif
cristy83017922010-09-05 20:45:15 +0000443 }
cristy3ed852e2009-09-05 21:47:34 +0000444}
445
446static MagickRealType Triangle(const MagickRealType x,
447 const ResizeFilter *magick_unused(resize_filter))
448{
449 /*
nicolas0edb0862010-09-19 18:56:19 +0000450 1st order (linear) B-Spline, bilinear interpolation, Tent 1D
451 filter, or a Bartlett 2D Cone filter.
cristy3ed852e2009-09-05 21:47:34 +0000452 */
453 if (x < 1.0)
454 return(1.0-x);
455 return(0.0);
456}
457
458static MagickRealType Welsh(const MagickRealType x,
459 const ResizeFilter *magick_unused(resize_filter))
460{
461 /*
462 Welsh parabolic windowing filter.
463 */
cristy560d8182010-09-08 22:36:25 +0000464 if (x < 1.0)
cristy3ed852e2009-09-05 21:47:34 +0000465 return(1.0-x*x);
466 return(0.0);
467}
468
469/*
470%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
471% %
472% %
473% %
474+ A c q u i r e R e s i z e F i l t e r %
475% %
476% %
477% %
478%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
479%
nicolas07bac812010-09-19 18:47:02 +0000480% AcquireResizeFilter() allocates the ResizeFilter structure. Choose
481% from these filters:
cristy3ed852e2009-09-05 21:47:34 +0000482%
483% FIR (Finite impulse Response) Filters
484% Box Triangle Quadratic
485% Cubic Hermite Catrom
486% Mitchell
487%
488% IIR (Infinite impulse Response) Filters
anthony48f77622010-10-03 14:32:31 +0000489% Gaussian Sinc Jinc (Bessel)
cristy3ed852e2009-09-05 21:47:34 +0000490%
anthony48f77622010-10-03 14:32:31 +0000491% Windowed Sinc/Jinc Filters
cristy3ed852e2009-09-05 21:47:34 +0000492% Blackman Hanning Hamming
anthony48f77622010-10-03 14:32:31 +0000493% Kaiser Lanczos
cristy3ed852e2009-09-05 21:47:34 +0000494%
anthony61b5ddd2010-10-05 02:33:31 +0000495% Special purpose Filters
anthony853d6972010-10-08 06:01:31 +0000496% SincFast Lanczos2D Robidoux
anthony61b5ddd2010-10-05 02:33:31 +0000497%
anthony48f77622010-10-03 14:32:31 +0000498% The users "-filter" selection is used to lookup the default 'expert'
499% settings for that filter from a internal table. However any provided
500% 'expert' settings (see below) may override this selection.
501%
502% FIR filters are used as is, and are limited to that filters support
nicolas07bac812010-09-19 18:47:02 +0000503% window (unless over-ridden). 'Gaussian' while classed as an IIR
anthonyc331dec2010-09-26 01:30:14 +0000504% filter, is also simply clipped by its support size (currently 1.5
anthony48f77622010-10-03 14:32:31 +0000505% ro approximatally 3*sigma as recommended by many references)
cristy3ed852e2009-09-05 21:47:34 +0000506%
anthony48f77622010-10-03 14:32:31 +0000507% The selection is typically either a windowed Sinc, or interpolated
nicolas07bac812010-09-19 18:47:02 +0000508% filter, for use by functions such as ResizeImage(). However if a
anthony48f77622010-10-03 14:32:31 +0000509% 'cylindrical' filter flag is requested, any default Sinc weighting
510% and windowing functions will be promoted to cylindrical Jinc form of
511% function.
cristy3ed852e2009-09-05 21:47:34 +0000512%
anthony48f77622010-10-03 14:32:31 +0000513% Directly requesting 'Sinc' or 'Jinc' will force the use of that
514% filter function without any windowing. This is not recommended,
515% except by image processing experts or in expert options. Selecting a
516% window filtering version of these functions is better.
cristy3ed852e2009-09-05 21:47:34 +0000517%
anthony48f77622010-10-03 14:32:31 +0000518% Lanczos is a special case of a Sinc-windowed Sinc, (or Jinc-Jinc for
519% the cylindrical case) but defaulting to 3-lobe support, rather that
520% the default 4 lobe support of the other windowed sinc/jinc filters.
cristy3ed852e2009-09-05 21:47:34 +0000521%
anthony48f77622010-10-03 14:32:31 +0000522% Two forms of the 'Sinc' function are available: Sinc and SincFast.
nicolas07bac812010-09-19 18:47:02 +0000523% Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
524% selected if the user specifically specifies the use of a Sinc
525% filter. SincFast uses highly accurate (and fast) polynomial (low Q)
anthony48f77622010-10-03 14:32:31 +0000526% and rational (high Q) approximations, and will be used by default in
527% most cases.
anthonyba5a7c32010-09-15 02:42:25 +0000528%
anthony853d6972010-10-08 06:01:31 +0000529% The Lanczos2D filter is just 2-lobed Lanczos using Sinc/Jinc as
anthony02b4cb42010-10-10 04:54:35 +0000530% appropriate. The Robidoux used to be a slightly sharpened version
531% of this, but is now the equivelent Cubic 'Keys' filter very similar to
532% "Mitchell". Remember these are designed specifically for use as
533% cylindrical (radial) EWA Distortion filters, to be less blurry in the
534% 'no-op' situation.
anthony61b5ddd2010-10-05 02:33:31 +0000535%
nicolas07bac812010-09-19 18:47:02 +0000536% Special 'expert' options can be used to override any and all filter
537% settings. This is not advised unless you have expert knowledge of
538% the use of resampling filtered techniques. Check on the results of
539% your selections using the "filter:verbose" setting to make sure you
anthony61b5ddd2010-10-05 02:33:31 +0000540% get the exact filter that you are tring to achieve.
cristy3ed852e2009-09-05 21:47:34 +0000541%
anthony48f77622010-10-03 14:32:31 +0000542% "filter:filter" Select the main function associated with
543% this filter name, as the weighting function of the filter.
544% This can be used to set a windowing function as a weighting
545% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000546%
anthony7bdc0ed2010-09-15 01:52:32 +0000547% If a "filter:window" operation has not been provided, then a 'Box'
548% windowing function will be set to denote that no windowing function
549% is being used.
cristy3ed852e2009-09-05 21:47:34 +0000550%
551% "filter:window" Select this windowing function for the filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000552% While any filter could be used as a windowing function, using the
553% 'first lobe' of that filter over the whole support window, using a
anthony48f77622010-10-03 14:32:31 +0000554% non-windowing function is not advisible. If no weighting filter
555% function is specifed a 'SincFast' filter will be used.
cristy3ed852e2009-09-05 21:47:34 +0000556%
anthony48f77622010-10-03 14:32:31 +0000557% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000558% This a simpler method of setting filter support size that will
anthony48f77622010-10-03 14:32:31 +0000559% correctly handle the Sinc/Jinc switch for an operators filtering
anthony7bdc0ed2010-09-15 01:52:32 +0000560% requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000561%
562% "filter:support" Set the support size for filtering to the size given
anthony48f77622010-10-03 14:32:31 +0000563% This not recommended for Sinc/Jinc windowed filters (lobes should
anthony7bdc0ed2010-09-15 01:52:32 +0000564% be used instead). This will override any 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000565%
anthonyb6d08c52010-09-13 01:17:04 +0000566% "filter:win-support" Scale windowing function to this size instead.
567% This causes the windowing (or self-windowing Lagrange filter) to act
568% is if the support window it much much larger than what is actually
569% supplied to the calling operator. The filter however is still
570% clipped to the real support size given, by the support range suppiled
571% to the caller. If unset this will equal the normal filter support
572% size.
573%
cristy3ed852e2009-09-05 21:47:34 +0000574% "filter:blur" Scale the filter and support window by this amount.
575% A value >1 will generally result in a more burred image with
576% more ringing effects, while a value <1 will sharpen the
577% resulting image with more aliasing and Morie effects.
578%
cristy3ed852e2009-09-05 21:47:34 +0000579% "filter:b"
580% "filter:c" Override the preset B,C values for a Cubic type of filter
581% If only one of these are given it is assumes to be a 'Keys'
582% type of filter such that B+2C=1, where Keys 'alpha' value = C
583%
anthonyb6d08c52010-09-13 01:17:04 +0000584% "filter:verbose" Output the exact results of the filter selections
585% made, as well as plotting data for graphing the resulting filter
586% over support range (blur adjusted).
cristy3ed852e2009-09-05 21:47:34 +0000587%
588% Set a true un-windowed Sinc filter with 10 lobes (very slow)
anthony48f77622010-10-03 14:32:31 +0000589% -define filter:filter=Sinc
590% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000591%
anthony48f77622010-10-03 14:32:31 +0000592% For example force an 8 lobe Lanczos (Sinc or Jinc) filter...
cristy3ed852e2009-09-05 21:47:34 +0000593% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000594% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000595%
cristy3ed852e2009-09-05 21:47:34 +0000596% The format of the AcquireResizeFilter method is:
597%
598% ResizeFilter *AcquireResizeFilter(const Image *image,
599% const FilterTypes filter_type, const MagickBooleanType radial,
600% ExceptionInfo *exception)
601%
cristy33b1c162010-01-23 22:51:51 +0000602% A description of each parameter follows:
603%
cristy3ed852e2009-09-05 21:47:34 +0000604% o image: the image.
605%
nicolas07bac812010-09-19 18:47:02 +0000606% o filter: the filter type, defining a preset filter, window and
607% support. The artifact settings listed above will override
608% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000609%
anthony48f77622010-10-03 14:32:31 +0000610% o blur: blur the filter by this amount, use 1.0 if unknown. Image
611% artifact "filter:blur" will override this API call usage, including
612% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000613%
anthony48f77622010-10-03 14:32:31 +0000614% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
615% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000616%
617% o exception: return any errors or warnings in this structure.
618%
619*/
620MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000621 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000622 const MagickBooleanType cylindrical,ExceptionInfo *exception)
623{
624 const char
625 *artifact;
626
627 FilterTypes
628 filter_type,
629 window_type;
630
cristy3ed852e2009-09-05 21:47:34 +0000631 MagickRealType
632 B,
633 C;
634
635 register ResizeFilter
636 *resize_filter;
637
cristy9af9b5d2010-08-15 17:04:28 +0000638 ssize_t
639 option;
640
cristy3ed852e2009-09-05 21:47:34 +0000641 /*
anthony48f77622010-10-03 14:32:31 +0000642 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000643 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000644 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
645 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
646 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000647
nicolas07bac812010-09-19 18:47:02 +0000648 WARNING: The order of this tabel must match the order of the
649 FilterTypes enumeration specified in "resample.h", or the filter
650 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000651
652 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000653 */
654 static struct
655 {
656 FilterTypes
657 filter,
658 window;
659 } const mapping[SentinelFilter] =
660 {
anthony462ee072010-09-27 12:34:02 +0000661 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
662 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
663 { BoxFilter, BoxFilter }, /* Box averaging filter */
664 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
665 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
666 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
667 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
668 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
669 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
670 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
671 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
672 { CatromFilter, BoxFilter }, /* Cubic interpolator */
673 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
674 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
anthony48f77622010-10-03 14:32:31 +0000675 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
676 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony462ee072010-09-27 12:34:02 +0000677 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
678 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
679 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
680 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
681 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
682 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
683 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
anthony61b5ddd2010-10-05 02:33:31 +0000684 { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
anthony02b4cb42010-10-10 04:54:35 +0000685 { RobidouxFilter, BoxFilter }, /* SPECIAL: cubic jinc-jinc equiv. */
cristy3ed852e2009-09-05 21:47:34 +0000686 };
687 /*
nicolas32f44eb2010-09-20 01:23:12 +0000688 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000689 function. The default support size for that filter as a weighting
690 function, the range to scale with to use that function as a sinc
691 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000692
anthony07a3f7f2010-09-16 03:03:11 +0000693 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000694 SincFast(), and CubicBC() functions, which may have multiple
695 filter to function associations.
696
697 See "filter:verbose" handling below for the function -> filter
698 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000699 */
700 static struct
701 {
702 MagickRealType
703 (*function)(const MagickRealType, const ResizeFilter*),
anthony61b5ddd2010-10-05 02:33:31 +0000704 lobes, /* default lobes/support size of the weighting filter */
anthony2d9b8b52010-09-14 08:31:07 +0000705 scale, /* windowing function range, for scaling windowing function */
anthony933b4bf2010-09-10 02:31:37 +0000706 B,C; /* Cubic Filter factors for a CubicBC function, else ignored */
cristy3ed852e2009-09-05 21:47:34 +0000707 } const filters[SentinelFilter] =
708 {
anthony61b5ddd2010-10-05 02:33:31 +0000709 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
710 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
711 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
712 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
713 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
714 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
715 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
716 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
717 { Gaussian, 1.5, 1.5, 0.0, 0.0 }, /* Gaussian */
718 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
719 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
720 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
anthony853d6972010-10-08 06:01:31 +0000721 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
722 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
723 { Jinc, 3.0, 1.21967, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000724 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
725 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
726 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
727 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
728 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
729 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
730 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
731 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
anthony853d6972010-10-08 06:01:31 +0000732 { Jinc, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2D, Jinc-Jinc */
anthony02b4cb42010-10-10 04:54:35 +0000733 /* Robidoux, A Cubic 'Keys' equiv of a Lanczos2D with a blur=0.958033808 */
734 { CubicBC, 2.0, 1.0, 0.37821575509399862, 0.31089212245300069 }
cristy3ed852e2009-09-05 21:47:34 +0000735 };
736 /*
nicolase473f722010-10-07 00:05:13 +0000737 The known zero crossings of the Jinc() or more accurately the
738 Jinc(x*PI) function being used as a filter. It is used by the
739 "filter:lobes" for support selection, so users do not have to deal
740 with the highly irrational sizes of the 'lobes' of the Jinc
741 filter.
anthony48f77622010-10-03 14:32:31 +0000742
nicolase473f722010-10-07 00:05:13 +0000743 Values taken from
anthony48f77622010-10-03 14:32:31 +0000744 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000745 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000746 */
747 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000748 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000749 {
anthonyc2d07db2010-09-15 23:47:40 +0000750 1.21966989126651,
751 2.23313059438153,
752 3.23831548416624,
753 4.24106286379607,
754 5.24276437687019,
755 6.24392168986449,
756 7.24475986871996,
757 8.24539491395205,
758 9.24589268494948,
759 10.2462933487549,
760 11.2466227948779,
761 12.2468984611381,
762 13.2471325221811,
763 14.2473337358069,
764 15.2475085630373,
765 16.247661874701
cristy3ed852e2009-09-05 21:47:34 +0000766 };
767
cristy33b1c162010-01-23 22:51:51 +0000768 /*
769 Allocate resize filter.
770 */
cristy3ed852e2009-09-05 21:47:34 +0000771 assert(image != (const Image *) NULL);
772 assert(image->signature == MagickSignature);
773 if (image->debug != MagickFalse)
774 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
775 assert(UndefinedFilter < filter && filter < SentinelFilter);
776 assert(exception != (ExceptionInfo *) NULL);
777 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000778 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000779 if (resize_filter == (ResizeFilter *) NULL)
780 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000781 /*
782 Defaults for the requested filter.
783 */
784 filter_type=mapping[filter].filter;
785 window_type=mapping[filter].window;
anthony48f77622010-10-03 14:32:31 +0000786 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000787 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000788 switch (filter_type)
789 {
790 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000791 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000792 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000793 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000794 break;
anthonyba5a7c32010-09-15 02:42:25 +0000795 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000796 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000797 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000798 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000799 break;
anthony61b5ddd2010-10-05 02:33:31 +0000800
cristy33b1c162010-01-23 22:51:51 +0000801 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000802 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000803 filter_type=JincFilter;
804 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000805 break;
cristya782ecf2010-01-25 02:59:14 +0000806 default:
807 break;
cristy3ed852e2009-09-05 21:47:34 +0000808 }
anthony61b5ddd2010-10-05 02:33:31 +0000809 else
810 switch (filter_type)
811 {
812 case Lanczos2DFilter:
nicolas45b58a92010-10-07 15:46:39 +0000813 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000814 window_type=SincFastFilter;
815 break;
816 default:
817 break;
818 }
819
cristy3ed852e2009-09-05 21:47:34 +0000820 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000821 if (artifact != (const char *) NULL)
822 {
cristy9af9b5d2010-08-15 17:04:28 +0000823 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000824 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000825 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000826 filter_type=(FilterTypes) option;
827 window_type=BoxFilter;
828 }
829 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000830 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000831 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000832 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000833 }
nicolas07bac812010-09-19 18:47:02 +0000834 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000835 artifact=GetImageArtifact(image,"filter:window");
836 if (artifact != (const char *) NULL)
837 {
cristy9af9b5d2010-08-15 17:04:28 +0000838 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000839 if ((UndefinedFilter < option) && (option < SentinelFilter))
840 {
841 if (option != LanczosFilter)
842 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000843 else
anthony48f77622010-10-03 14:32:31 +0000844 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000845 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000846 }
cristy33b1c162010-01-23 22:51:51 +0000847 }
cristy3ed852e2009-09-05 21:47:34 +0000848 }
cristy33b1c162010-01-23 22:51:51 +0000849 else
850 {
anthony48f77622010-10-03 14:32:31 +0000851 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000852 artifact=GetImageArtifact(image,"filter:window");
853 if (artifact != (const char *) NULL)
854 {
855 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
856 artifact);
857 if ((UndefinedFilter < option) && (option < SentinelFilter))
858 {
anthony61b5ddd2010-10-05 02:33:31 +0000859 filter_type=cylindrical != MagickFalse ?
860 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000861 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000862 }
863 }
864 }
nicolas07bac812010-09-19 18:47:02 +0000865 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000866 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000867 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000868 resize_filter->window=filters[window_type].function;
869 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000870 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000871
anthony10b8bc82010-10-02 12:48:46 +0000872 /* Filter blur -- scaling both filter and support window. */
873 resize_filter->blur=blur;
874 artifact=GetImageArtifact(image,"filter:blur");
875 if (artifact != (const char *) NULL)
876 resize_filter->blur=StringToDouble(artifact);
877 if (resize_filter->blur < MagickEpsilon)
878 resize_filter->blur=(MagickRealType) MagickEpsilon;
879
880 if (cylindrical != MagickFalse)
881 switch (filter_type)
882 {
883 case PointFilter:
884 case BoxFilter:
885 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000886 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000887 break;
888 case GaussianFilter:
889 /* Cylindrical Gaussian should have a sigma of sqrt(2)/2
890 * and not the default sigma of 1/2 - so use blur to enlarge
891 * and adjust support so actual practical support = 2.0 by default
892 */
893 resize_filter->blur *= MagickSQ2;
cristy1c9bb452010-10-03 16:48:33 +0000894 resize_filter->support = (MagickRealType) MagickSQ2; /* which times blur => 2.0 */
anthony10b8bc82010-10-02 12:48:46 +0000895 break;
anthony81b8bf92010-10-02 13:54:34 +0000896 default:
897 break;
anthony10b8bc82010-10-02 12:48:46 +0000898 }
anthony61b5ddd2010-10-05 02:33:31 +0000899 else
900 switch (filter_type)
901 {
902 case Lanczos2DFilter:
anthony853d6972010-10-08 06:01:31 +0000903 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000904 resize_filter->filter=SincFast;
905 break;
906 default:
907 break;
908 }
909
anthony2d9b8b52010-09-14 08:31:07 +0000910 /* Filter support overrides. */
cristy3ed852e2009-09-05 21:47:34 +0000911 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000912 if (artifact != (const char *) NULL)
913 {
cristybb503372010-05-27 20:51:26 +0000914 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000915 lobes;
916
cristy96b16132010-08-29 17:19:52 +0000917 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000918 if (lobes < 1)
919 lobes=1;
920 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000921 }
anthony61b5ddd2010-10-05 02:33:31 +0000922 /* convert Jinc lobes to a real support value */
923 if (resize_filter->filter == Jinc)
924 {
925 if (resize_filter->support > 16)
926 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
927 else
928 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
929 }
930 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000931 artifact=GetImageArtifact(image,"filter:support");
932 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000933 resize_filter->support=fabs(StringToDouble(artifact));
934 /*
nicolas07bac812010-09-19 18:47:02 +0000935 Scale windowing function separatally to the support 'clipping'
936 window that calling operator is planning to actually use. (Expert
937 override)
cristy3ed852e2009-09-05 21:47:34 +0000938 */
anthony55f12332010-09-10 01:13:02 +0000939 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000940 artifact=GetImageArtifact(image,"filter:win-support");
941 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000942 resize_filter->window_support=fabs(StringToDouble(artifact));
943 /*
anthony1f90a6b2010-09-14 08:56:31 +0000944 Adjust window function scaling to the windowing support for
945 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000946 */
947 resize_filter->scale /= resize_filter->window_support;
948 /*
nicolas07bac812010-09-19 18:47:02 +0000949 Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000950 */
cristy3ed852e2009-09-05 21:47:34 +0000951 B=0.0;
952 C=0.0;
cristy33b1c162010-01-23 22:51:51 +0000953 if ((filters[filter_type].function == CubicBC) ||
954 (filters[window_type].function == CubicBC))
955 {
anthony2d9b8b52010-09-14 08:31:07 +0000956 B=filters[filter_type].B;
957 C=filters[filter_type].C;
958 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +0000959 {
anthony2d9b8b52010-09-14 08:31:07 +0000960 B=filters[window_type].B;
961 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +0000962 }
cristy33b1c162010-01-23 22:51:51 +0000963 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +0000964 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000965 {
966 B=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +0000967 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +0000968 artifact=GetImageArtifact(image,"filter:c");
969 if (artifact != (const char *) NULL)
970 C=StringToDouble(artifact);
971 }
972 else
973 {
974 artifact=GetImageArtifact(image,"filter:c");
975 if (artifact != (const char *) NULL)
976 {
977 C=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +0000978 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +0000979 }
980 }
981 /*
nicolas07bac812010-09-19 18:47:02 +0000982 Convert B,C values into Cubic Coefficents. See CubicBC().
cristy33b1c162010-01-23 22:51:51 +0000983 */
984 resize_filter->cubic[0]=(6.0-2.0*B)/6.0;
cristy3ed852e2009-09-05 21:47:34 +0000985 resize_filter->cubic[1]=0.0;
cristy33b1c162010-01-23 22:51:51 +0000986 resize_filter->cubic[2]=(-18.0+12.0*B+6.0*C)/6.0;
987 resize_filter->cubic[3]=(12.0-9.0*B-6.0*C)/6.0;
988 resize_filter->cubic[4]=(8.0*B+24.0*C)/6.0;
989 resize_filter->cubic[5]=(-12.0*B-48.0*C)/6.0;
990 resize_filter->cubic[6]=(6.0*B+30.0*C)/6.0;
nicolas58c77cd2010-09-20 15:51:39 +0000991 resize_filter->cubic[7]=(-B-6.0*C)/6.0;
cristy3ed852e2009-09-05 21:47:34 +0000992 }
anthony55f12332010-09-10 01:13:02 +0000993 /*
nicolas07bac812010-09-19 18:47:02 +0000994 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +0000995 */
anthonye06e4c12010-09-15 04:03:52 +0000996#if defined(MAGICKCORE_OPENMP_SUPPORT)
anthony72949792010-10-08 04:44:56 +0000997 #pragma omp single
998 {
anthonye06e4c12010-09-15 04:03:52 +0000999#endif
1000 artifact=GetImageArtifact(image,"filter:verbose");
1001 if (artifact != (const char *) NULL)
1002 {
1003 double
1004 support,
1005 x;
cristy3ed852e2009-09-05 21:47:34 +00001006
nicolas07bac812010-09-19 18:47:02 +00001007 /*
anthony463be1d2010-09-26 01:07:36 +00001008 Set the weighting function properly when the weighting
1009 function may not exactly match the filter of the same name.
1010 EG: a Point filter really uses a Box weighting function
1011 with a different support than is typically used.
1012
anthonye06e4c12010-09-15 04:03:52 +00001013 */
anthony463be1d2010-09-26 01:07:36 +00001014 if (resize_filter->filter == Box) filter_type=BoxFilter;
1015 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1016 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
anthony61b5ddd2010-10-05 02:33:31 +00001017 if (resize_filter->filter == Jinc) filter_type=JincFilter;
anthony463be1d2010-09-26 01:07:36 +00001018 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
anthonye06e4c12010-09-15 04:03:52 +00001019 /*
nicolas07bac812010-09-19 18:47:02 +00001020 Report Filter Details.
anthonye06e4c12010-09-15 04:03:52 +00001021 */
cristy03dbbd22010-09-19 23:04:47 +00001022 support=GetResizeFilterSupport(resize_filter); /* support range */
anthony61b5ddd2010-10-05 02:33:31 +00001023 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
cristy03dbbd22010-09-19 23:04:47 +00001024 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1025 MagickFilterOptions,filter_type));
1026 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
anthony463be1d2010-09-26 01:07:36 +00001027 MagickFilterOptions, window_type));
cristy03dbbd22010-09-19 23:04:47 +00001028 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
anthony463be1d2010-09-26 01:07:36 +00001029 (double) resize_filter->support);
cristy03dbbd22010-09-19 23:04:47 +00001030 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
anthony463be1d2010-09-26 01:07:36 +00001031 (double) resize_filter->window_support);
cristy03dbbd22010-09-19 23:04:47 +00001032 (void) fprintf(stdout,"# blur = %.*g\n",GetMagickPrecision(),
1033 (double) resize_filter->blur);
anthony463be1d2010-09-26 01:07:36 +00001034 (void) fprintf(stdout,"# blurred_support = %.*g\n",GetMagickPrecision(),
cristy03dbbd22010-09-19 23:04:47 +00001035 (double) support);
1036 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1037 (double) B,GetMagickPrecision(),(double) C);
anthony61b5ddd2010-10-05 02:33:31 +00001038 (void) fprintf(stdout,"\n");
anthonye06e4c12010-09-15 04:03:52 +00001039 /*
nicolas07bac812010-09-19 18:47:02 +00001040 Output values of resulting filter graph -- for graphing
1041 filter result.
anthonye06e4c12010-09-15 04:03:52 +00001042 */
1043 for (x=0.0; x <= support; x+=0.01f)
cristy03dbbd22010-09-19 23:04:47 +00001044 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1045 (double) GetResizeFilterWeight(resize_filter,x));
nicolas07bac812010-09-19 18:47:02 +00001046 /* A final value so gnuplot can graph the 'stop' properly. */
cristy03dbbd22010-09-19 23:04:47 +00001047 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1048 0.0);
anthonye06e4c12010-09-15 04:03:52 +00001049 }
anthony72949792010-10-08 04:44:56 +00001050 /* output the above once only for each image, and each setting */
cristybb66d9c2010-10-09 01:40:31 +00001051 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
anthonye06e4c12010-09-15 04:03:52 +00001052#if defined(MAGICKCORE_OPENMP_SUPPORT)
anthony61b5ddd2010-10-05 02:33:31 +00001053 }
anthonye06e4c12010-09-15 04:03:52 +00001054#endif
cristy3ed852e2009-09-05 21:47:34 +00001055 return(resize_filter);
1056}
1057
1058/*
1059%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1060% %
1061% %
1062% %
1063% A d a p t i v e R e s i z e I m a g e %
1064% %
1065% %
1066% %
1067%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1068%
1069% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1070%
1071% The format of the AdaptiveResizeImage method is:
1072%
cristy9af9b5d2010-08-15 17:04:28 +00001073% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1074% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001075%
1076% A description of each parameter follows:
1077%
1078% o image: the image.
1079%
1080% o columns: the number of columns in the resized image.
1081%
1082% o rows: the number of rows in the resized image.
1083%
1084% o exception: return any errors or warnings in this structure.
1085%
1086*/
1087MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001088 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001089{
1090#define AdaptiveResizeImageTag "Resize/Image"
1091
cristyc4c8d132010-01-07 01:58:38 +00001092 CacheView
1093 *resize_view;
1094
cristy3ed852e2009-09-05 21:47:34 +00001095 Image
1096 *resize_image;
1097
cristy3ed852e2009-09-05 21:47:34 +00001098 MagickBooleanType
1099 proceed;
1100
1101 MagickPixelPacket
1102 pixel;
1103
1104 PointInfo
1105 offset;
1106
1107 ResampleFilter
1108 *resample_filter;
1109
cristy9af9b5d2010-08-15 17:04:28 +00001110 ssize_t
1111 y;
1112
cristy3ed852e2009-09-05 21:47:34 +00001113 /*
1114 Adaptively resize image.
1115 */
1116 assert(image != (const Image *) NULL);
1117 assert(image->signature == MagickSignature);
1118 if (image->debug != MagickFalse)
1119 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1120 assert(exception != (ExceptionInfo *) NULL);
1121 assert(exception->signature == MagickSignature);
1122 if ((columns == 0) || (rows == 0))
1123 return((Image *) NULL);
1124 if ((columns == image->columns) && (rows == image->rows))
1125 return(CloneImage(image,0,0,MagickTrue,exception));
1126 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1127 if (resize_image == (Image *) NULL)
1128 return((Image *) NULL);
1129 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1130 {
1131 InheritException(exception,&resize_image->exception);
1132 resize_image=DestroyImage(resize_image);
1133 return((Image *) NULL);
1134 }
1135 GetMagickPixelPacket(image,&pixel);
1136 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001137 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001138 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001139 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001140 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001141 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001142 {
1143 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001144 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001145
cristybb503372010-05-27 20:51:26 +00001146 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001147 x;
1148
1149 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001150 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001151
1152 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1153 exception);
1154 if (q == (PixelPacket *) NULL)
1155 break;
1156 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1157 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001158 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001159 {
1160 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1161 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1162 &pixel);
1163 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1164 q++;
1165 }
1166 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1167 break;
cristy96b16132010-08-29 17:19:52 +00001168 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1169 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001170 if (proceed == MagickFalse)
1171 break;
1172 }
1173 resample_filter=DestroyResampleFilter(resample_filter);
1174 resize_view=DestroyCacheView(resize_view);
1175 return(resize_image);
1176}
1177
1178/*
1179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1180% %
1181% %
1182% %
1183+ B e s s e l O r d e r O n e %
1184% %
1185% %
1186% %
1187%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1188%
1189% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001190% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001191%
1192% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1193%
1194% j1(x) = x*j1(x);
1195%
1196% For x in (8,inf)
1197%
1198% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1199%
1200% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1201%
1202% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1203% = 1/sqrt(2) * (sin(x) - cos(x))
1204% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1205% = -1/sqrt(2) * (sin(x) + cos(x))
1206%
1207% The format of the BesselOrderOne method is:
1208%
1209% MagickRealType BesselOrderOne(MagickRealType x)
1210%
1211% A description of each parameter follows:
1212%
1213% o x: MagickRealType value.
1214%
1215*/
1216
1217#undef I0
1218static MagickRealType I0(MagickRealType x)
1219{
1220 MagickRealType
1221 sum,
1222 t,
1223 y;
1224
cristybb503372010-05-27 20:51:26 +00001225 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001226 i;
1227
1228 /*
1229 Zeroth order Bessel function of the first kind.
1230 */
1231 sum=1.0;
1232 y=x*x/4.0;
1233 t=y;
1234 for (i=2; t > MagickEpsilon; i++)
1235 {
1236 sum+=t;
1237 t*=y/((MagickRealType) i*i);
1238 }
1239 return(sum);
1240}
1241
1242#undef J1
1243static MagickRealType J1(MagickRealType x)
1244{
1245 MagickRealType
1246 p,
1247 q;
1248
cristybb503372010-05-27 20:51:26 +00001249 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001250 i;
1251
1252 static const double
1253 Pone[] =
1254 {
1255 0.581199354001606143928050809e+21,
1256 -0.6672106568924916298020941484e+20,
1257 0.2316433580634002297931815435e+19,
1258 -0.3588817569910106050743641413e+17,
1259 0.2908795263834775409737601689e+15,
1260 -0.1322983480332126453125473247e+13,
1261 0.3413234182301700539091292655e+10,
1262 -0.4695753530642995859767162166e+7,
1263 0.270112271089232341485679099e+4
1264 },
1265 Qone[] =
1266 {
1267 0.11623987080032122878585294e+22,
1268 0.1185770712190320999837113348e+20,
1269 0.6092061398917521746105196863e+17,
1270 0.2081661221307607351240184229e+15,
1271 0.5243710262167649715406728642e+12,
1272 0.1013863514358673989967045588e+10,
1273 0.1501793594998585505921097578e+7,
1274 0.1606931573481487801970916749e+4,
1275 0.1e+1
1276 };
1277
1278 p=Pone[8];
1279 q=Qone[8];
1280 for (i=7; i >= 0; i--)
1281 {
1282 p=p*x*x+Pone[i];
1283 q=q*x*x+Qone[i];
1284 }
1285 return(p/q);
1286}
1287
1288#undef P1
1289static MagickRealType P1(MagickRealType x)
1290{
1291 MagickRealType
1292 p,
1293 q;
1294
cristybb503372010-05-27 20:51:26 +00001295 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001296 i;
1297
1298 static const double
1299 Pone[] =
1300 {
1301 0.352246649133679798341724373e+5,
1302 0.62758845247161281269005675e+5,
1303 0.313539631109159574238669888e+5,
1304 0.49854832060594338434500455e+4,
1305 0.2111529182853962382105718e+3,
1306 0.12571716929145341558495e+1
1307 },
1308 Qone[] =
1309 {
1310 0.352246649133679798068390431e+5,
1311 0.626943469593560511888833731e+5,
1312 0.312404063819041039923015703e+5,
1313 0.4930396490181088979386097e+4,
1314 0.2030775189134759322293574e+3,
1315 0.1e+1
1316 };
1317
1318 p=Pone[5];
1319 q=Qone[5];
1320 for (i=4; i >= 0; i--)
1321 {
1322 p=p*(8.0/x)*(8.0/x)+Pone[i];
1323 q=q*(8.0/x)*(8.0/x)+Qone[i];
1324 }
1325 return(p/q);
1326}
1327
1328#undef Q1
1329static MagickRealType Q1(MagickRealType x)
1330{
1331 MagickRealType
1332 p,
1333 q;
1334
cristybb503372010-05-27 20:51:26 +00001335 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001336 i;
1337
1338 static const double
1339 Pone[] =
1340 {
1341 0.3511751914303552822533318e+3,
1342 0.7210391804904475039280863e+3,
1343 0.4259873011654442389886993e+3,
1344 0.831898957673850827325226e+2,
1345 0.45681716295512267064405e+1,
1346 0.3532840052740123642735e-1
1347 },
1348 Qone[] =
1349 {
1350 0.74917374171809127714519505e+4,
1351 0.154141773392650970499848051e+5,
1352 0.91522317015169922705904727e+4,
1353 0.18111867005523513506724158e+4,
1354 0.1038187585462133728776636e+3,
1355 0.1e+1
1356 };
1357
1358 p=Pone[5];
1359 q=Qone[5];
1360 for (i=4; i >= 0; i--)
1361 {
1362 p=p*(8.0/x)*(8.0/x)+Pone[i];
1363 q=q*(8.0/x)*(8.0/x)+Qone[i];
1364 }
1365 return(p/q);
1366}
1367
1368static MagickRealType BesselOrderOne(MagickRealType x)
1369{
1370 MagickRealType
1371 p,
1372 q;
1373
1374 if (x == 0.0)
1375 return(0.0);
1376 p=x;
1377 if (x < 0.0)
1378 x=(-x);
1379 if (x < 8.0)
1380 return(p*J1(x));
1381 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1382 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1383 cos((double) x))));
1384 if (p < 0.0)
1385 q=(-q);
1386 return(q);
1387}
1388
1389/*
1390%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1391% %
1392% %
1393% %
1394+ D e s t r o y R e s i z e F i l t e r %
1395% %
1396% %
1397% %
1398%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1399%
1400% DestroyResizeFilter() destroy the resize filter.
1401%
cristya2ffd7e2010-03-10 20:50:30 +00001402% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001403%
1404% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1405%
1406% A description of each parameter follows:
1407%
1408% o resize_filter: the resize filter.
1409%
1410*/
1411MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1412{
1413 assert(resize_filter != (ResizeFilter *) NULL);
1414 assert(resize_filter->signature == MagickSignature);
1415 resize_filter->signature=(~MagickSignature);
1416 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1417 return(resize_filter);
1418}
1419
1420/*
1421%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1422% %
1423% %
1424% %
1425+ G e t R e s i z e F i l t e r S u p p o r t %
1426% %
1427% %
1428% %
1429%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1430%
1431% GetResizeFilterSupport() return the current support window size for this
1432% filter. Note that this may have been enlarged by filter:blur factor.
1433%
1434% The format of the GetResizeFilterSupport method is:
1435%
1436% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1437%
1438% A description of each parameter follows:
1439%
1440% o filter: Image filter to use.
1441%
1442*/
1443MagickExport MagickRealType GetResizeFilterSupport(
1444 const ResizeFilter *resize_filter)
1445{
1446 assert(resize_filter != (ResizeFilter *) NULL);
1447 assert(resize_filter->signature == MagickSignature);
1448 return(resize_filter->support*resize_filter->blur);
1449}
1450
1451/*
1452%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1453% %
1454% %
1455% %
1456+ G e t R e s i z e F i l t e r W e i g h t %
1457% %
1458% %
1459% %
1460%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1461%
1462% GetResizeFilterWeight evaluates the specified resize filter at the point x
1463% which usally lies between zero and the filters current 'support' and
1464% returns the weight of the filter function at that point.
1465%
1466% The format of the GetResizeFilterWeight method is:
1467%
1468% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1469% const MagickRealType x)
1470%
1471% A description of each parameter follows:
1472%
1473% o filter: the filter type.
1474%
1475% o x: the point.
1476%
1477*/
1478MagickExport MagickRealType GetResizeFilterWeight(
1479 const ResizeFilter *resize_filter,const MagickRealType x)
1480{
1481 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001482 scale,
1483 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001484
1485 /*
1486 Windowing function - scale the weighting filter by this amount.
1487 */
1488 assert(resize_filter != (ResizeFilter *) NULL);
1489 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001490 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001491 if ((resize_filter->window_support < MagickEpsilon) ||
1492 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001493 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001494 else
1495 {
anthony55f12332010-09-10 01:13:02 +00001496 scale=resize_filter->scale;
1497 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001498 }
anthony55f12332010-09-10 01:13:02 +00001499 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001500}
1501
1502/*
1503%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1504% %
1505% %
1506% %
1507% M a g n i f y I m a g e %
1508% %
1509% %
1510% %
1511%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1512%
1513% MagnifyImage() is a convenience method that scales an image proportionally
1514% to twice its size.
1515%
1516% The format of the MagnifyImage method is:
1517%
1518% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1519%
1520% A description of each parameter follows:
1521%
1522% o image: the image.
1523%
1524% o exception: return any errors or warnings in this structure.
1525%
1526*/
1527MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1528{
1529 Image
1530 *magnify_image;
1531
1532 assert(image != (Image *) NULL);
1533 assert(image->signature == MagickSignature);
1534 if (image->debug != MagickFalse)
1535 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1536 assert(exception != (ExceptionInfo *) NULL);
1537 assert(exception->signature == MagickSignature);
1538 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1539 1.0,exception);
1540 return(magnify_image);
1541}
1542
1543/*
1544%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1545% %
1546% %
1547% %
1548% M i n i f y I m a g e %
1549% %
1550% %
1551% %
1552%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1553%
1554% MinifyImage() is a convenience method that scales an image proportionally
1555% to half its size.
1556%
1557% The format of the MinifyImage method is:
1558%
1559% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1560%
1561% A description of each parameter follows:
1562%
1563% o image: the image.
1564%
1565% o exception: return any errors or warnings in this structure.
1566%
1567*/
1568MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1569{
1570 Image
1571 *minify_image;
1572
1573 assert(image != (Image *) NULL);
1574 assert(image->signature == MagickSignature);
1575 if (image->debug != MagickFalse)
1576 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1577 assert(exception != (ExceptionInfo *) NULL);
1578 assert(exception->signature == MagickSignature);
1579 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1580 1.0,exception);
1581 return(minify_image);
1582}
1583
1584/*
1585%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1586% %
1587% %
1588% %
1589% R e s a m p l e I m a g e %
1590% %
1591% %
1592% %
1593%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1594%
1595% ResampleImage() resize image in terms of its pixel size, so that when
1596% displayed at the given resolution it will be the same size in terms of
1597% real world units as the original image at the original resolution.
1598%
1599% The format of the ResampleImage method is:
1600%
1601% Image *ResampleImage(Image *image,const double x_resolution,
1602% const double y_resolution,const FilterTypes filter,const double blur,
1603% ExceptionInfo *exception)
1604%
1605% A description of each parameter follows:
1606%
1607% o image: the image to be resized to fit the given resolution.
1608%
1609% o x_resolution: the new image x resolution.
1610%
1611% o y_resolution: the new image y resolution.
1612%
1613% o filter: Image filter to use.
1614%
1615% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1616%
1617*/
1618MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1619 const double y_resolution,const FilterTypes filter,const double blur,
1620 ExceptionInfo *exception)
1621{
1622#define ResampleImageTag "Resample/Image"
1623
1624 Image
1625 *resample_image;
1626
cristybb503372010-05-27 20:51:26 +00001627 size_t
cristy3ed852e2009-09-05 21:47:34 +00001628 height,
1629 width;
1630
1631 /*
1632 Initialize sampled image attributes.
1633 */
1634 assert(image != (const Image *) NULL);
1635 assert(image->signature == MagickSignature);
1636 if (image->debug != MagickFalse)
1637 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1638 assert(exception != (ExceptionInfo *) NULL);
1639 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001640 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1641 72.0 : image->x_resolution)+0.5);
1642 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1643 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001644 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1645 if (resample_image != (Image *) NULL)
1646 {
1647 resample_image->x_resolution=x_resolution;
1648 resample_image->y_resolution=y_resolution;
1649 }
1650 return(resample_image);
1651}
1652#if defined(MAGICKCORE_LQR_DELEGATE)
1653
1654/*
1655%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1656% %
1657% %
1658% %
1659% L i q u i d R e s c a l e I m a g e %
1660% %
1661% %
1662% %
1663%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1664%
1665% LiquidRescaleImage() rescales image with seam carving.
1666%
1667% The format of the LiquidRescaleImage method is:
1668%
1669% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001670% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001671% const double delta_x,const double rigidity,ExceptionInfo *exception)
1672%
1673% A description of each parameter follows:
1674%
1675% o image: the image.
1676%
1677% o columns: the number of columns in the rescaled image.
1678%
1679% o rows: the number of rows in the rescaled image.
1680%
1681% o delta_x: maximum seam transversal step (0 means straight seams).
1682%
1683% o rigidity: introduce a bias for non-straight seams (typically 0).
1684%
1685% o exception: return any errors or warnings in this structure.
1686%
1687*/
cristy9af9b5d2010-08-15 17:04:28 +00001688MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1689 const size_t rows,const double delta_x,const double rigidity,
1690 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001691{
1692#define LiquidRescaleImageTag "Rescale/Image"
1693
cristyc5c6f662010-09-22 14:23:02 +00001694 CacheView
1695 *rescale_view;
1696
cristy3ed852e2009-09-05 21:47:34 +00001697 const char
1698 *map;
1699
1700 guchar
1701 *packet;
1702
1703 Image
1704 *rescale_image;
1705
1706 int
1707 x,
1708 y;
1709
1710 LqrCarver
1711 *carver;
1712
1713 LqrRetVal
1714 lqr_status;
1715
1716 MagickBooleanType
1717 status;
1718
1719 MagickPixelPacket
1720 pixel;
1721
1722 unsigned char
1723 *pixels;
1724
1725 /*
1726 Liquid rescale image.
1727 */
1728 assert(image != (const Image *) NULL);
1729 assert(image->signature == MagickSignature);
1730 if (image->debug != MagickFalse)
1731 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1732 assert(exception != (ExceptionInfo *) NULL);
1733 assert(exception->signature == MagickSignature);
1734 if ((columns == 0) || (rows == 0))
1735 return((Image *) NULL);
1736 if ((columns == image->columns) && (rows == image->rows))
1737 return(CloneImage(image,0,0,MagickTrue,exception));
1738 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001739 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001740 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1741 {
1742 Image
1743 *resize_image;
1744
cristybb503372010-05-27 20:51:26 +00001745 size_t
cristy3ed852e2009-09-05 21:47:34 +00001746 height,
1747 width;
1748
1749 /*
1750 Honor liquid resize size limitations.
1751 */
1752 for (width=image->columns; columns >= (2*width-1); width*=2);
1753 for (height=image->rows; rows >= (2*height-1); height*=2);
1754 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1755 exception);
1756 if (resize_image == (Image *) NULL)
1757 return((Image *) NULL);
1758 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1759 rigidity,exception);
1760 resize_image=DestroyImage(resize_image);
1761 return(rescale_image);
1762 }
1763 map="RGB";
1764 if (image->matte == MagickFalse)
1765 map="RGBA";
1766 if (image->colorspace == CMYKColorspace)
1767 {
1768 map="CMYK";
1769 if (image->matte == MagickFalse)
1770 map="CMYKA";
1771 }
1772 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1773 strlen(map)*sizeof(*pixels));
1774 if (pixels == (unsigned char *) NULL)
1775 return((Image *) NULL);
1776 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1777 pixels,exception);
1778 if (status == MagickFalse)
1779 {
1780 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1781 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1782 }
1783 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1784 if (carver == (LqrCarver *) NULL)
1785 {
1786 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1787 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1788 }
1789 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1790 lqr_status=lqr_carver_resize(carver,columns,rows);
1791 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1792 lqr_carver_get_height(carver),MagickTrue,exception);
1793 if (rescale_image == (Image *) NULL)
1794 {
1795 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1796 return((Image *) NULL);
1797 }
1798 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1799 {
1800 InheritException(exception,&rescale_image->exception);
1801 rescale_image=DestroyImage(rescale_image);
1802 return((Image *) NULL);
1803 }
1804 GetMagickPixelPacket(rescale_image,&pixel);
1805 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001806 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001807 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1808 {
1809 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001810 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001811
1812 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001813 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001814
anthony22aad252010-09-23 06:59:07 +00001815 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001816 if (q == (PixelPacket *) NULL)
1817 break;
cristyc5c6f662010-09-22 14:23:02 +00001818 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001819 pixel.red=QuantumRange*(packet[0]/255.0);
1820 pixel.green=QuantumRange*(packet[1]/255.0);
1821 pixel.blue=QuantumRange*(packet[2]/255.0);
1822 if (image->colorspace != CMYKColorspace)
1823 {
1824 if (image->matte == MagickFalse)
1825 pixel.opacity=QuantumRange*(packet[3]/255.0);
1826 }
1827 else
1828 {
1829 pixel.index=QuantumRange*(packet[3]/255.0);
1830 if (image->matte == MagickFalse)
1831 pixel.opacity=QuantumRange*(packet[4]/255.0);
1832 }
1833 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001834 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001835 break;
1836 }
cristyc5c6f662010-09-22 14:23:02 +00001837 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001838 /*
1839 Relinquish resources.
1840 */
1841 lqr_carver_destroy(carver);
1842 return(rescale_image);
1843}
1844#else
1845MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001846 const size_t magick_unused(columns),const size_t magick_unused(rows),
1847 const double magick_unused(delta_x),const double magick_unused(rigidity),
1848 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001849{
1850 assert(image != (const Image *) NULL);
1851 assert(image->signature == MagickSignature);
1852 if (image->debug != MagickFalse)
1853 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1854 assert(exception != (ExceptionInfo *) NULL);
1855 assert(exception->signature == MagickSignature);
1856 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1857 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1858 return((Image *) NULL);
1859}
1860#endif
1861
1862/*
1863%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1864% %
1865% %
1866% %
1867% R e s i z e I m a g e %
1868% %
1869% %
1870% %
1871%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1872%
1873% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001874% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001875%
1876% If an undefined filter is given the filter defaults to Mitchell for a
1877% colormapped image, a image with a matte channel, or if the image is
1878% enlarged. Otherwise the filter defaults to a Lanczos.
1879%
1880% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1881%
1882% The format of the ResizeImage method is:
1883%
cristybb503372010-05-27 20:51:26 +00001884% Image *ResizeImage(Image *image,const size_t columns,
1885% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001886% ExceptionInfo *exception)
1887%
1888% A description of each parameter follows:
1889%
1890% o image: the image.
1891%
1892% o columns: the number of columns in the scaled image.
1893%
1894% o rows: the number of rows in the scaled image.
1895%
1896% o filter: Image filter to use.
1897%
cristy9af9b5d2010-08-15 17:04:28 +00001898% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1899% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001900%
1901% o exception: return any errors or warnings in this structure.
1902%
1903*/
1904
1905typedef struct _ContributionInfo
1906{
1907 MagickRealType
1908 weight;
1909
cristybb503372010-05-27 20:51:26 +00001910 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001911 pixel;
1912} ContributionInfo;
1913
1914static ContributionInfo **DestroyContributionThreadSet(
1915 ContributionInfo **contribution)
1916{
cristybb503372010-05-27 20:51:26 +00001917 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001918 i;
1919
1920 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001921 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001922 if (contribution[i] != (ContributionInfo *) NULL)
1923 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1924 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001925 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001926 return(contribution);
1927}
1928
1929static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1930{
cristybb503372010-05-27 20:51:26 +00001931 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001932 i;
1933
1934 ContributionInfo
1935 **contribution;
1936
cristybb503372010-05-27 20:51:26 +00001937 size_t
cristy3ed852e2009-09-05 21:47:34 +00001938 number_threads;
1939
1940 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001941 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001942 sizeof(*contribution));
1943 if (contribution == (ContributionInfo **) NULL)
1944 return((ContributionInfo **) NULL);
1945 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00001946 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00001947 {
1948 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1949 sizeof(**contribution));
1950 if (contribution[i] == (ContributionInfo *) NULL)
1951 return(DestroyContributionThreadSet(contribution));
1952 }
1953 return(contribution);
1954}
1955
1956static inline double MagickMax(const double x,const double y)
1957{
1958 if (x > y)
1959 return(x);
1960 return(y);
1961}
1962
1963static inline double MagickMin(const double x,const double y)
1964{
1965 if (x < y)
1966 return(x);
1967 return(y);
1968}
1969
1970static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
1971 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00001972 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001973{
1974#define ResizeImageTag "Resize/Image"
1975
cristyfa112112010-01-04 17:48:07 +00001976 CacheView
1977 *image_view,
1978 *resize_view;
1979
cristy3ed852e2009-09-05 21:47:34 +00001980 ClassType
1981 storage_class;
1982
1983 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00001984 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00001985
cristy3ed852e2009-09-05 21:47:34 +00001986 MagickBooleanType
1987 status;
1988
1989 MagickPixelPacket
1990 zero;
1991
1992 MagickRealType
1993 scale,
1994 support;
1995
cristy9af9b5d2010-08-15 17:04:28 +00001996 ssize_t
1997 x;
1998
cristy3ed852e2009-09-05 21:47:34 +00001999 /*
2000 Apply filter to resize horizontally from image to resize image.
2001 */
cristy5d824382010-09-06 14:00:17 +00002002 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002003 support=scale*GetResizeFilterSupport(resize_filter);
2004 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2005 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2006 {
2007 InheritException(exception,&resize_image->exception);
2008 return(MagickFalse);
2009 }
2010 if (support < 0.5)
2011 {
2012 /*
nicolas07bac812010-09-19 18:47:02 +00002013 Support too small even for nearest neighbour: Reduce to point
2014 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002015 */
2016 support=(MagickRealType) 0.5;
2017 scale=1.0;
2018 }
2019 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2020 if (contributions == (ContributionInfo **) NULL)
2021 {
2022 (void) ThrowMagickException(exception,GetMagickModule(),
2023 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2024 return(MagickFalse);
2025 }
2026 status=MagickTrue;
2027 scale=1.0/scale;
2028 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2029 image_view=AcquireCacheView(image);
2030 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002031#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002032 #pragma omp parallel for shared(status)
2033#endif
cristybb503372010-05-27 20:51:26 +00002034 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002035 {
cristy3ed852e2009-09-05 21:47:34 +00002036 MagickRealType
2037 center,
2038 density;
2039
2040 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002041 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002042
2043 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002044 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002045
cristy03dbbd22010-09-19 23:04:47 +00002046 register ContributionInfo
2047 *restrict contribution;
2048
cristy3ed852e2009-09-05 21:47:34 +00002049 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002050 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002051
cristy3ed852e2009-09-05 21:47:34 +00002052 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002053 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002054
cristy03dbbd22010-09-19 23:04:47 +00002055 register ssize_t
2056 y;
2057
cristy9af9b5d2010-08-15 17:04:28 +00002058 ssize_t
2059 n,
2060 start,
2061 stop;
2062
cristy3ed852e2009-09-05 21:47:34 +00002063 if (status == MagickFalse)
2064 continue;
2065 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002066 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2067 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002068 density=0.0;
2069 contribution=contributions[GetOpenMPThreadId()];
2070 for (n=0; n < (stop-start); n++)
2071 {
2072 contribution[n].pixel=start+n;
2073 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2074 ((MagickRealType) (start+n)-center+0.5));
2075 density+=contribution[n].weight;
2076 }
2077 if ((density != 0.0) && (density != 1.0))
2078 {
cristybb503372010-05-27 20:51:26 +00002079 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002080 i;
2081
2082 /*
2083 Normalize.
2084 */
2085 density=1.0/density;
2086 for (i=0; i < n; i++)
2087 contribution[i].weight*=density;
2088 }
cristy9af9b5d2010-08-15 17:04:28 +00002089 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2090 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002091 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2092 exception);
2093 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2094 {
2095 status=MagickFalse;
2096 continue;
2097 }
2098 indexes=GetCacheViewVirtualIndexQueue(image_view);
2099 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002100 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002101 {
cristy3ed852e2009-09-05 21:47:34 +00002102 MagickPixelPacket
2103 pixel;
2104
2105 MagickRealType
2106 alpha;
2107
cristybb503372010-05-27 20:51:26 +00002108 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002109 i;
2110
cristy9af9b5d2010-08-15 17:04:28 +00002111 ssize_t
2112 j;
2113
cristy3ed852e2009-09-05 21:47:34 +00002114 pixel=zero;
2115 if (image->matte == MagickFalse)
2116 {
2117 for (i=0; i < n; i++)
2118 {
2119 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2120 (contribution[i].pixel-contribution[0].pixel);
2121 alpha=contribution[i].weight;
2122 pixel.red+=alpha*(p+j)->red;
2123 pixel.green+=alpha*(p+j)->green;
2124 pixel.blue+=alpha*(p+j)->blue;
2125 pixel.opacity+=alpha*(p+j)->opacity;
2126 }
cristyce70c172010-01-07 17:15:30 +00002127 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2128 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2129 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2130 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002131 if ((image->colorspace == CMYKColorspace) &&
2132 (resize_image->colorspace == CMYKColorspace))
2133 {
2134 for (i=0; i < n; i++)
2135 {
2136 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2137 (contribution[i].pixel-contribution[0].pixel);
2138 alpha=contribution[i].weight;
2139 pixel.index+=alpha*indexes[j];
2140 }
cristyce70c172010-01-07 17:15:30 +00002141 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002142 }
2143 }
2144 else
2145 {
2146 MagickRealType
2147 gamma;
2148
2149 gamma=0.0;
2150 for (i=0; i < n; i++)
2151 {
2152 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2153 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002154 alpha=contribution[i].weight*QuantumScale*
2155 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002156 pixel.red+=alpha*(p+j)->red;
2157 pixel.green+=alpha*(p+j)->green;
2158 pixel.blue+=alpha*(p+j)->blue;
2159 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2160 gamma+=alpha;
2161 }
2162 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002163 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2164 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2165 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2166 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002167 if ((image->colorspace == CMYKColorspace) &&
2168 (resize_image->colorspace == CMYKColorspace))
2169 {
2170 for (i=0; i < n; i++)
2171 {
2172 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2173 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002174 alpha=contribution[i].weight*QuantumScale*
2175 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002176 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002177 }
cristyce70c172010-01-07 17:15:30 +00002178 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2179 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002180 }
2181 }
2182 if ((resize_image->storage_class == PseudoClass) &&
2183 (image->storage_class == PseudoClass))
2184 {
cristybb503372010-05-27 20:51:26 +00002185 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002186 1.0)+0.5);
2187 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2188 (contribution[i-start].pixel-contribution[0].pixel);
2189 resize_indexes[y]=indexes[j];
2190 }
2191 q++;
2192 }
2193 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2194 status=MagickFalse;
2195 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2196 {
2197 MagickBooleanType
2198 proceed;
2199
cristyb5d5f722009-11-04 03:03:49 +00002200#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002201 #pragma omp critical (MagickCore_HorizontalFilter)
2202#endif
cristy9af9b5d2010-08-15 17:04:28 +00002203 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002204 if (proceed == MagickFalse)
2205 status=MagickFalse;
2206 }
2207 }
2208 resize_view=DestroyCacheView(resize_view);
2209 image_view=DestroyCacheView(image_view);
2210 contributions=DestroyContributionThreadSet(contributions);
2211 return(status);
2212}
2213
2214static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2215 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002216 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002217{
cristyfa112112010-01-04 17:48:07 +00002218 CacheView
2219 *image_view,
2220 *resize_view;
2221
cristy3ed852e2009-09-05 21:47:34 +00002222 ClassType
2223 storage_class;
2224
2225 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002226 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002227
cristy3ed852e2009-09-05 21:47:34 +00002228 MagickBooleanType
2229 status;
2230
2231 MagickPixelPacket
2232 zero;
2233
2234 MagickRealType
2235 scale,
2236 support;
2237
cristy9af9b5d2010-08-15 17:04:28 +00002238 ssize_t
2239 y;
2240
cristy3ed852e2009-09-05 21:47:34 +00002241 /*
cristy9af9b5d2010-08-15 17:04:28 +00002242 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002243 */
cristy5d824382010-09-06 14:00:17 +00002244 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002245 support=scale*GetResizeFilterSupport(resize_filter);
2246 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2247 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2248 {
2249 InheritException(exception,&resize_image->exception);
2250 return(MagickFalse);
2251 }
2252 if (support < 0.5)
2253 {
2254 /*
nicolas07bac812010-09-19 18:47:02 +00002255 Support too small even for nearest neighbour: Reduce to point
2256 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002257 */
2258 support=(MagickRealType) 0.5;
2259 scale=1.0;
2260 }
2261 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2262 if (contributions == (ContributionInfo **) NULL)
2263 {
2264 (void) ThrowMagickException(exception,GetMagickModule(),
2265 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2266 return(MagickFalse);
2267 }
2268 status=MagickTrue;
2269 scale=1.0/scale;
2270 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2271 image_view=AcquireCacheView(image);
2272 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002273#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002274 #pragma omp parallel for shared(status)
2275#endif
cristybb503372010-05-27 20:51:26 +00002276 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002277 {
cristy3ed852e2009-09-05 21:47:34 +00002278 MagickRealType
2279 center,
2280 density;
2281
2282 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002283 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002284
2285 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002286 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002287
cristy03dbbd22010-09-19 23:04:47 +00002288 register ContributionInfo
2289 *restrict contribution;
2290
cristy3ed852e2009-09-05 21:47:34 +00002291 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002292 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002293
cristy9af9b5d2010-08-15 17:04:28 +00002294 register PixelPacket
2295 *restrict q;
2296
cristybb503372010-05-27 20:51:26 +00002297 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002298 x;
2299
cristy9af9b5d2010-08-15 17:04:28 +00002300 ssize_t
2301 n,
2302 start,
2303 stop;
cristy3ed852e2009-09-05 21:47:34 +00002304
2305 if (status == MagickFalse)
2306 continue;
cristy679e6962010-03-18 00:42:45 +00002307 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002308 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2309 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002310 density=0.0;
2311 contribution=contributions[GetOpenMPThreadId()];
2312 for (n=0; n < (stop-start); n++)
2313 {
2314 contribution[n].pixel=start+n;
2315 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2316 ((MagickRealType) (start+n)-center+0.5));
2317 density+=contribution[n].weight;
2318 }
2319 if ((density != 0.0) && (density != 1.0))
2320 {
cristybb503372010-05-27 20:51:26 +00002321 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002322 i;
2323
2324 /*
2325 Normalize.
2326 */
2327 density=1.0/density;
2328 for (i=0; i < n; i++)
2329 contribution[i].weight*=density;
2330 }
2331 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002332 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2333 exception);
cristy3ed852e2009-09-05 21:47:34 +00002334 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2335 exception);
2336 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2337 {
2338 status=MagickFalse;
2339 continue;
2340 }
2341 indexes=GetCacheViewVirtualIndexQueue(image_view);
2342 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002343 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002344 {
cristy3ed852e2009-09-05 21:47:34 +00002345 MagickPixelPacket
2346 pixel;
2347
2348 MagickRealType
2349 alpha;
2350
cristybb503372010-05-27 20:51:26 +00002351 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002352 i;
2353
cristy9af9b5d2010-08-15 17:04:28 +00002354 ssize_t
2355 j;
2356
cristy3ed852e2009-09-05 21:47:34 +00002357 pixel=zero;
2358 if (image->matte == MagickFalse)
2359 {
2360 for (i=0; i < n; i++)
2361 {
cristybb503372010-05-27 20:51:26 +00002362 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002363 image->columns+x);
2364 alpha=contribution[i].weight;
2365 pixel.red+=alpha*(p+j)->red;
2366 pixel.green+=alpha*(p+j)->green;
2367 pixel.blue+=alpha*(p+j)->blue;
2368 pixel.opacity+=alpha*(p+j)->opacity;
2369 }
cristyce70c172010-01-07 17:15:30 +00002370 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2371 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2372 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2373 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002374 if ((image->colorspace == CMYKColorspace) &&
2375 (resize_image->colorspace == CMYKColorspace))
2376 {
2377 for (i=0; i < n; i++)
2378 {
cristybb503372010-05-27 20:51:26 +00002379 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002380 image->columns+x);
2381 alpha=contribution[i].weight;
2382 pixel.index+=alpha*indexes[j];
2383 }
cristyce70c172010-01-07 17:15:30 +00002384 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002385 }
2386 }
2387 else
2388 {
2389 MagickRealType
2390 gamma;
2391
2392 gamma=0.0;
2393 for (i=0; i < n; i++)
2394 {
cristybb503372010-05-27 20:51:26 +00002395 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002396 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002397 alpha=contribution[i].weight*QuantumScale*
2398 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002399 pixel.red+=alpha*(p+j)->red;
2400 pixel.green+=alpha*(p+j)->green;
2401 pixel.blue+=alpha*(p+j)->blue;
2402 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2403 gamma+=alpha;
2404 }
2405 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002406 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2407 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2408 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2409 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002410 if ((image->colorspace == CMYKColorspace) &&
2411 (resize_image->colorspace == CMYKColorspace))
2412 {
2413 for (i=0; i < n; i++)
2414 {
cristybb503372010-05-27 20:51:26 +00002415 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002416 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002417 alpha=contribution[i].weight*QuantumScale*
2418 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002419 pixel.index+=alpha*indexes[j];
2420 }
cristyce70c172010-01-07 17:15:30 +00002421 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2422 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002423 }
2424 }
2425 if ((resize_image->storage_class == PseudoClass) &&
2426 (image->storage_class == PseudoClass))
2427 {
cristybb503372010-05-27 20:51:26 +00002428 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002429 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002430 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002431 image->columns+x);
2432 resize_indexes[x]=indexes[j];
2433 }
2434 q++;
2435 }
2436 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2437 status=MagickFalse;
2438 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2439 {
2440 MagickBooleanType
2441 proceed;
2442
cristyb5d5f722009-11-04 03:03:49 +00002443#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002444 #pragma omp critical (MagickCore_VerticalFilter)
2445#endif
cristy9af9b5d2010-08-15 17:04:28 +00002446 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002447 if (proceed == MagickFalse)
2448 status=MagickFalse;
2449 }
2450 }
2451 resize_view=DestroyCacheView(resize_view);
2452 image_view=DestroyCacheView(image_view);
2453 contributions=DestroyContributionThreadSet(contributions);
2454 return(status);
2455}
2456
cristybb503372010-05-27 20:51:26 +00002457MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2458 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002459 ExceptionInfo *exception)
2460{
2461#define WorkLoadFactor 0.265
2462
2463 FilterTypes
2464 filter_type;
2465
2466 Image
2467 *filter_image,
2468 *resize_image;
2469
cristy9af9b5d2010-08-15 17:04:28 +00002470 MagickOffsetType
2471 offset;
2472
cristy3ed852e2009-09-05 21:47:34 +00002473 MagickRealType
2474 x_factor,
2475 y_factor;
2476
2477 MagickSizeType
2478 span;
2479
2480 MagickStatusType
2481 status;
2482
2483 ResizeFilter
2484 *resize_filter;
2485
cristy3ed852e2009-09-05 21:47:34 +00002486 /*
2487 Acquire resize image.
2488 */
2489 assert(image != (Image *) NULL);
2490 assert(image->signature == MagickSignature);
2491 if (image->debug != MagickFalse)
2492 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2493 assert(exception != (ExceptionInfo *) NULL);
2494 assert(exception->signature == MagickSignature);
2495 if ((columns == 0) || (rows == 0))
2496 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2497 if ((columns == image->columns) && (rows == image->rows) &&
2498 (filter == UndefinedFilter) && (blur == 1.0))
2499 return(CloneImage(image,0,0,MagickTrue,exception));
2500 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2501 if (resize_image == (Image *) NULL)
2502 return(resize_image);
2503 /*
2504 Acquire resize filter.
2505 */
2506 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2507 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2508 if ((x_factor*y_factor) > WorkLoadFactor)
2509 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2510 else
2511 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2512 if (filter_image == (Image *) NULL)
2513 return(DestroyImage(resize_image));
2514 filter_type=LanczosFilter;
2515 if (filter != UndefinedFilter)
2516 filter_type=filter;
2517 else
2518 if ((x_factor == 1.0) && (y_factor == 1.0))
2519 filter_type=PointFilter;
2520 else
2521 if ((image->storage_class == PseudoClass) ||
2522 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2523 filter_type=MitchellFilter;
2524 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2525 exception);
2526 /*
2527 Resize image.
2528 */
cristy9af9b5d2010-08-15 17:04:28 +00002529 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002530 if ((x_factor*y_factor) > WorkLoadFactor)
2531 {
2532 span=(MagickSizeType) (filter_image->columns+rows);
2533 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002534 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002535 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002536 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002537 }
2538 else
2539 {
2540 span=(MagickSizeType) (filter_image->rows+columns);
2541 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002542 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002543 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002544 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002545 }
2546 /*
2547 Free resources.
2548 */
2549 filter_image=DestroyImage(filter_image);
2550 resize_filter=DestroyResizeFilter(resize_filter);
2551 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2552 return((Image *) NULL);
2553 resize_image->type=image->type;
2554 return(resize_image);
2555}
2556
2557/*
2558%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2559% %
2560% %
2561% %
2562% S a m p l e I m a g e %
2563% %
2564% %
2565% %
2566%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2567%
2568% SampleImage() scales an image to the desired dimensions with pixel
2569% sampling. Unlike other scaling methods, this method does not introduce
2570% any additional color into the scaled image.
2571%
2572% The format of the SampleImage method is:
2573%
cristybb503372010-05-27 20:51:26 +00002574% Image *SampleImage(const Image *image,const size_t columns,
2575% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002576%
2577% A description of each parameter follows:
2578%
2579% o image: the image.
2580%
2581% o columns: the number of columns in the sampled image.
2582%
2583% o rows: the number of rows in the sampled image.
2584%
2585% o exception: return any errors or warnings in this structure.
2586%
2587*/
cristybb503372010-05-27 20:51:26 +00002588MagickExport Image *SampleImage(const Image *image,const size_t columns,
2589 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002590{
2591#define SampleImageTag "Sample/Image"
2592
cristyc4c8d132010-01-07 01:58:38 +00002593 CacheView
2594 *image_view,
2595 *sample_view;
2596
cristy3ed852e2009-09-05 21:47:34 +00002597 Image
2598 *sample_image;
2599
cristy3ed852e2009-09-05 21:47:34 +00002600 MagickBooleanType
2601 status;
2602
cristy5f959472010-05-27 22:19:46 +00002603 MagickOffsetType
2604 progress;
2605
cristybb503372010-05-27 20:51:26 +00002606 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002607 x;
2608
cristy5f959472010-05-27 22:19:46 +00002609 ssize_t
2610 *x_offset,
2611 y;
2612
cristy3ed852e2009-09-05 21:47:34 +00002613 /*
2614 Initialize sampled image attributes.
2615 */
2616 assert(image != (const Image *) NULL);
2617 assert(image->signature == MagickSignature);
2618 if (image->debug != MagickFalse)
2619 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2620 assert(exception != (ExceptionInfo *) NULL);
2621 assert(exception->signature == MagickSignature);
2622 if ((columns == 0) || (rows == 0))
2623 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2624 if ((columns == image->columns) && (rows == image->rows))
2625 return(CloneImage(image,0,0,MagickTrue,exception));
2626 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2627 if (sample_image == (Image *) NULL)
2628 return((Image *) NULL);
2629 /*
2630 Allocate scan line buffer and column offset buffers.
2631 */
cristybb503372010-05-27 20:51:26 +00002632 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002633 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002634 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002635 {
2636 sample_image=DestroyImage(sample_image);
2637 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2638 }
cristybb503372010-05-27 20:51:26 +00002639 for (x=0; x < (ssize_t) sample_image->columns; x++)
2640 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002641 sample_image->columns);
2642 /*
2643 Sample each row.
2644 */
2645 status=MagickTrue;
2646 progress=0;
2647 image_view=AcquireCacheView(image);
2648 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002649#if defined(MAGICKCORE_OPENMP_SUPPORT)
2650 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002651#endif
cristybb503372010-05-27 20:51:26 +00002652 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002653 {
cristy3ed852e2009-09-05 21:47:34 +00002654 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002655 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002656
2657 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002658 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002659
2660 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002661 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002662
cristy3ed852e2009-09-05 21:47:34 +00002663 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002664 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002665
cristy03dbbd22010-09-19 23:04:47 +00002666 register ssize_t
2667 x;
2668
cristy9af9b5d2010-08-15 17:04:28 +00002669 ssize_t
2670 y_offset;
2671
cristy3ed852e2009-09-05 21:47:34 +00002672 if (status == MagickFalse)
2673 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002674 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2675 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002676 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2677 exception);
2678 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2679 exception);
2680 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2681 {
2682 status=MagickFalse;
2683 continue;
2684 }
2685 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2686 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2687 /*
2688 Sample each column.
2689 */
cristybb503372010-05-27 20:51:26 +00002690 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002691 *q++=p[x_offset[x]];
2692 if ((image->storage_class == PseudoClass) ||
2693 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002694 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002695 sample_indexes[x]=indexes[x_offset[x]];
2696 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2697 status=MagickFalse;
2698 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2699 {
2700 MagickBooleanType
2701 proceed;
2702
cristyb5d5f722009-11-04 03:03:49 +00002703#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002704 #pragma omp critical (MagickCore_SampleImage)
2705#endif
2706 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2707 if (proceed == MagickFalse)
2708 status=MagickFalse;
2709 }
2710 }
2711 image_view=DestroyCacheView(image_view);
2712 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002713 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002714 sample_image->type=image->type;
2715 return(sample_image);
2716}
2717
2718/*
2719%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2720% %
2721% %
2722% %
2723% S c a l e I m a g e %
2724% %
2725% %
2726% %
2727%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2728%
2729% ScaleImage() changes the size of an image to the given dimensions.
2730%
2731% The format of the ScaleImage method is:
2732%
cristybb503372010-05-27 20:51:26 +00002733% Image *ScaleImage(const Image *image,const size_t columns,
2734% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002735%
2736% A description of each parameter follows:
2737%
2738% o image: the image.
2739%
2740% o columns: the number of columns in the scaled image.
2741%
2742% o rows: the number of rows in the scaled image.
2743%
2744% o exception: return any errors or warnings in this structure.
2745%
2746*/
cristybb503372010-05-27 20:51:26 +00002747MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2748 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002749{
2750#define ScaleImageTag "Scale/Image"
2751
cristyed6cb232010-01-20 03:07:53 +00002752 CacheView
2753 *image_view,
2754 *scale_view;
2755
cristy3ed852e2009-09-05 21:47:34 +00002756 Image
2757 *scale_image;
2758
cristy3ed852e2009-09-05 21:47:34 +00002759 MagickBooleanType
2760 next_column,
2761 next_row,
2762 proceed;
2763
2764 MagickPixelPacket
2765 pixel,
2766 *scale_scanline,
2767 *scanline,
2768 *x_vector,
2769 *y_vector,
2770 zero;
2771
cristy3ed852e2009-09-05 21:47:34 +00002772 PointInfo
2773 scale,
2774 span;
2775
cristybb503372010-05-27 20:51:26 +00002776 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002777 i;
2778
cristy9af9b5d2010-08-15 17:04:28 +00002779 ssize_t
2780 number_rows,
2781 y;
2782
cristy3ed852e2009-09-05 21:47:34 +00002783 /*
2784 Initialize scaled image attributes.
2785 */
2786 assert(image != (const Image *) NULL);
2787 assert(image->signature == MagickSignature);
2788 if (image->debug != MagickFalse)
2789 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2790 assert(exception != (ExceptionInfo *) NULL);
2791 assert(exception->signature == MagickSignature);
2792 if ((columns == 0) || (rows == 0))
2793 return((Image *) NULL);
2794 if ((columns == image->columns) && (rows == image->rows))
2795 return(CloneImage(image,0,0,MagickTrue,exception));
2796 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2797 if (scale_image == (Image *) NULL)
2798 return((Image *) NULL);
2799 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2800 {
2801 InheritException(exception,&scale_image->exception);
2802 scale_image=DestroyImage(scale_image);
2803 return((Image *) NULL);
2804 }
2805 /*
2806 Allocate memory.
2807 */
2808 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2809 sizeof(*x_vector));
2810 scanline=x_vector;
2811 if (image->rows != scale_image->rows)
2812 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2813 sizeof(*scanline));
2814 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2815 scale_image->columns,sizeof(*scale_scanline));
2816 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2817 sizeof(*y_vector));
2818 if ((scanline == (MagickPixelPacket *) NULL) ||
2819 (scale_scanline == (MagickPixelPacket *) NULL) ||
2820 (x_vector == (MagickPixelPacket *) NULL) ||
2821 (y_vector == (MagickPixelPacket *) NULL))
2822 {
2823 scale_image=DestroyImage(scale_image);
2824 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2825 }
2826 /*
2827 Scale image.
2828 */
2829 number_rows=0;
2830 next_row=MagickTrue;
2831 span.y=1.0;
2832 scale.y=(double) scale_image->rows/(double) image->rows;
2833 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2834 sizeof(*y_vector));
2835 GetMagickPixelPacket(image,&pixel);
2836 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2837 i=0;
cristyed6cb232010-01-20 03:07:53 +00002838 image_view=AcquireCacheView(image);
2839 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002840 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002841 {
2842 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002843 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002844
2845 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002846 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002847
2848 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002849 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002850
cristy3ed852e2009-09-05 21:47:34 +00002851 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002852 *restrict s,
2853 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002854
2855 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002856 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002857
cristy9af9b5d2010-08-15 17:04:28 +00002858 register ssize_t
2859 x;
2860
cristyed6cb232010-01-20 03:07:53 +00002861 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2862 exception);
cristy3ed852e2009-09-05 21:47:34 +00002863 if (q == (PixelPacket *) NULL)
2864 break;
2865 scale_indexes=GetAuthenticIndexQueue(scale_image);
2866 if (scale_image->rows == image->rows)
2867 {
2868 /*
2869 Read a new scanline.
2870 */
cristyed6cb232010-01-20 03:07:53 +00002871 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2872 exception);
cristy3ed852e2009-09-05 21:47:34 +00002873 if (p == (const PixelPacket *) NULL)
2874 break;
cristyed6cb232010-01-20 03:07:53 +00002875 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002876 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002877 {
cristyce70c172010-01-07 17:15:30 +00002878 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2879 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2880 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002881 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002882 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002883 if (indexes != (IndexPacket *) NULL)
2884 x_vector[x].index=(MagickRealType) indexes[x];
2885 p++;
2886 }
2887 }
2888 else
2889 {
2890 /*
2891 Scale Y direction.
2892 */
2893 while (scale.y < span.y)
2894 {
cristy9af9b5d2010-08-15 17:04:28 +00002895 if ((next_row != MagickFalse) &&
2896 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002897 {
2898 /*
2899 Read a new scanline.
2900 */
cristyed6cb232010-01-20 03:07:53 +00002901 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2902 exception);
cristy3ed852e2009-09-05 21:47:34 +00002903 if (p == (const PixelPacket *) NULL)
2904 break;
cristyed6cb232010-01-20 03:07:53 +00002905 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002906 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002907 {
cristyce70c172010-01-07 17:15:30 +00002908 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2909 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2910 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002911 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002912 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002913 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002914 if (indexes != (IndexPacket *) NULL)
2915 x_vector[x].index=(MagickRealType) indexes[x];
2916 p++;
2917 }
2918 number_rows++;
2919 }
cristybb503372010-05-27 20:51:26 +00002920 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002921 {
2922 y_vector[x].red+=scale.y*x_vector[x].red;
2923 y_vector[x].green+=scale.y*x_vector[x].green;
2924 y_vector[x].blue+=scale.y*x_vector[x].blue;
2925 if (scale_image->matte != MagickFalse)
2926 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2927 if (scale_indexes != (IndexPacket *) NULL)
2928 y_vector[x].index+=scale.y*x_vector[x].index;
2929 }
2930 span.y-=scale.y;
2931 scale.y=(double) scale_image->rows/(double) image->rows;
2932 next_row=MagickTrue;
2933 }
cristybb503372010-05-27 20:51:26 +00002934 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002935 {
2936 /*
2937 Read a new scanline.
2938 */
cristyed6cb232010-01-20 03:07:53 +00002939 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2940 exception);
cristy3ed852e2009-09-05 21:47:34 +00002941 if (p == (const PixelPacket *) NULL)
2942 break;
cristyed6cb232010-01-20 03:07:53 +00002943 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002944 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002945 {
cristyce70c172010-01-07 17:15:30 +00002946 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2947 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2948 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002949 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00002950 x_vector[x].opacity=(MagickRealType)
2951 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002952 if (indexes != (IndexPacket *) NULL)
2953 x_vector[x].index=(MagickRealType) indexes[x];
2954 p++;
2955 }
2956 number_rows++;
2957 next_row=MagickFalse;
2958 }
2959 s=scanline;
cristybb503372010-05-27 20:51:26 +00002960 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002961 {
2962 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
2963 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
2964 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
2965 if (image->matte != MagickFalse)
2966 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
2967 if (scale_indexes != (IndexPacket *) NULL)
2968 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
2969 s->red=pixel.red;
2970 s->green=pixel.green;
2971 s->blue=pixel.blue;
2972 if (scale_image->matte != MagickFalse)
2973 s->opacity=pixel.opacity;
2974 if (scale_indexes != (IndexPacket *) NULL)
2975 s->index=pixel.index;
2976 s++;
2977 y_vector[x]=zero;
2978 }
2979 scale.y-=span.y;
2980 if (scale.y <= 0)
2981 {
2982 scale.y=(double) scale_image->rows/(double) image->rows;
2983 next_row=MagickTrue;
2984 }
2985 span.y=1.0;
2986 }
2987 if (scale_image->columns == image->columns)
2988 {
2989 /*
2990 Transfer scanline to scaled image.
2991 */
2992 s=scanline;
cristybb503372010-05-27 20:51:26 +00002993 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002994 {
cristyce70c172010-01-07 17:15:30 +00002995 q->red=ClampToQuantum(s->red);
2996 q->green=ClampToQuantum(s->green);
2997 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00002998 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002999 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003000 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003001 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003002 q++;
3003 s++;
3004 }
3005 }
3006 else
3007 {
3008 /*
3009 Scale X direction.
3010 */
3011 pixel=zero;
3012 next_column=MagickFalse;
3013 span.x=1.0;
3014 s=scanline;
3015 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003016 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003017 {
3018 scale.x=(double) scale_image->columns/(double) image->columns;
3019 while (scale.x >= span.x)
3020 {
3021 if (next_column != MagickFalse)
3022 {
3023 pixel=zero;
3024 t++;
3025 }
3026 pixel.red+=span.x*s->red;
3027 pixel.green+=span.x*s->green;
3028 pixel.blue+=span.x*s->blue;
3029 if (image->matte != MagickFalse)
3030 pixel.opacity+=span.x*s->opacity;
3031 if (scale_indexes != (IndexPacket *) NULL)
3032 pixel.index+=span.x*s->index;
3033 t->red=pixel.red;
3034 t->green=pixel.green;
3035 t->blue=pixel.blue;
3036 if (scale_image->matte != MagickFalse)
3037 t->opacity=pixel.opacity;
3038 if (scale_indexes != (IndexPacket *) NULL)
3039 t->index=pixel.index;
3040 scale.x-=span.x;
3041 span.x=1.0;
3042 next_column=MagickTrue;
3043 }
3044 if (scale.x > 0)
3045 {
3046 if (next_column != MagickFalse)
3047 {
3048 pixel=zero;
3049 next_column=MagickFalse;
3050 t++;
3051 }
3052 pixel.red+=scale.x*s->red;
3053 pixel.green+=scale.x*s->green;
3054 pixel.blue+=scale.x*s->blue;
3055 if (scale_image->matte != MagickFalse)
3056 pixel.opacity+=scale.x*s->opacity;
3057 if (scale_indexes != (IndexPacket *) NULL)
3058 pixel.index+=scale.x*s->index;
3059 span.x-=scale.x;
3060 }
3061 s++;
3062 }
3063 if (span.x > 0)
3064 {
3065 s--;
3066 pixel.red+=span.x*s->red;
3067 pixel.green+=span.x*s->green;
3068 pixel.blue+=span.x*s->blue;
3069 if (scale_image->matte != MagickFalse)
3070 pixel.opacity+=span.x*s->opacity;
3071 if (scale_indexes != (IndexPacket *) NULL)
3072 pixel.index+=span.x*s->index;
3073 }
3074 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003075 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003076 {
3077 t->red=pixel.red;
3078 t->green=pixel.green;
3079 t->blue=pixel.blue;
3080 if (scale_image->matte != MagickFalse)
3081 t->opacity=pixel.opacity;
3082 if (scale_indexes != (IndexPacket *) NULL)
3083 t->index=pixel.index;
3084 }
3085 /*
3086 Transfer scanline to scaled image.
3087 */
3088 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003089 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003090 {
cristyce70c172010-01-07 17:15:30 +00003091 q->red=ClampToQuantum(t->red);
3092 q->green=ClampToQuantum(t->green);
3093 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003094 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003095 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003096 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003097 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003098 t++;
3099 q++;
3100 }
3101 }
cristyed6cb232010-01-20 03:07:53 +00003102 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003103 break;
cristy96b16132010-08-29 17:19:52 +00003104 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3105 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003106 if (proceed == MagickFalse)
3107 break;
3108 }
cristyed6cb232010-01-20 03:07:53 +00003109 scale_view=DestroyCacheView(scale_view);
3110 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003111 /*
3112 Free allocated memory.
3113 */
3114 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3115 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3116 if (scale_image->rows != image->rows)
3117 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3118 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3119 scale_image->type=image->type;
3120 return(scale_image);
3121}
3122
anthony02b4cb42010-10-10 04:54:35 +00003123#if 0
3124 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003125/*
3126%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3127% %
3128% %
3129% %
3130+ S e t R e s i z e F i l t e r S u p p o r t %
3131% %
3132% %
3133% %
3134%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3135%
3136% SetResizeFilterSupport() specifies which IR filter to use to window
3137%
3138% The format of the SetResizeFilterSupport method is:
3139%
3140% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3141% const MagickRealType support)
3142%
3143% A description of each parameter follows:
3144%
3145% o resize_filter: the resize filter.
3146%
3147% o support: the filter spport radius.
3148%
3149*/
3150MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3151 const MagickRealType support)
3152{
3153 assert(resize_filter != (ResizeFilter *) NULL);
3154 assert(resize_filter->signature == MagickSignature);
3155 resize_filter->support=support;
3156}
anthony02b4cb42010-10-10 04:54:35 +00003157#endif
cristy3ed852e2009-09-05 21:47:34 +00003158
3159/*
3160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3161% %
3162% %
3163% %
3164% T h u m b n a i l I m a g e %
3165% %
3166% %
3167% %
3168%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3169%
3170% ThumbnailImage() changes the size of an image to the given dimensions and
3171% removes any associated profiles. The goal is to produce small low cost
3172% thumbnail images suited for display on the Web.
3173%
3174% The format of the ThumbnailImage method is:
3175%
cristybb503372010-05-27 20:51:26 +00003176% Image *ThumbnailImage(const Image *image,const size_t columns,
3177% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003178%
3179% A description of each parameter follows:
3180%
3181% o image: the image.
3182%
3183% o columns: the number of columns in the scaled image.
3184%
3185% o rows: the number of rows in the scaled image.
3186%
3187% o exception: return any errors or warnings in this structure.
3188%
3189*/
cristy9af9b5d2010-08-15 17:04:28 +00003190MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3191 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003192{
3193#define SampleFactor 5
3194
3195 char
3196 value[MaxTextExtent];
3197
3198 const char
3199 *name;
3200
3201 Image
3202 *thumbnail_image;
3203
3204 MagickRealType
3205 x_factor,
3206 y_factor;
3207
cristybb503372010-05-27 20:51:26 +00003208 size_t
cristy3ed852e2009-09-05 21:47:34 +00003209 version;
3210
cristy9af9b5d2010-08-15 17:04:28 +00003211 struct stat
3212 attributes;
3213
cristy3ed852e2009-09-05 21:47:34 +00003214 assert(image != (Image *) NULL);
3215 assert(image->signature == MagickSignature);
3216 if (image->debug != MagickFalse)
3217 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3218 assert(exception != (ExceptionInfo *) NULL);
3219 assert(exception->signature == MagickSignature);
3220 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3221 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3222 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003223 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3224 exception);
cristy3ed852e2009-09-05 21:47:34 +00003225 else
3226 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003227 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3228 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003229 else
3230 {
3231 Image
3232 *sample_image;
3233
3234 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3235 exception);
3236 if (sample_image == (Image *) NULL)
3237 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003238 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3239 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003240 sample_image=DestroyImage(sample_image);
3241 }
3242 if (thumbnail_image == (Image *) NULL)
3243 return(thumbnail_image);
3244 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3245 if (thumbnail_image->matte == MagickFalse)
3246 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3247 thumbnail_image->depth=8;
3248 thumbnail_image->interlace=NoInterlace;
3249 /*
3250 Strip all profiles except color profiles.
3251 */
3252 ResetImageProfileIterator(thumbnail_image);
3253 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3254 {
3255 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3256 {
cristy2b726bd2010-01-11 01:05:39 +00003257 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003258 ResetImageProfileIterator(thumbnail_image);
3259 }
3260 name=GetNextImageProfile(thumbnail_image);
3261 }
3262 (void) DeleteImageProperty(thumbnail_image,"comment");
3263 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003264 if (strstr(image->magick_filename,"//") == (char *) NULL)
3265 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003266 image->magick_filename);
3267 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3268 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3269 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3270 {
cristye8c25f92010-06-03 00:53:06 +00003271 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003272 attributes.st_mtime);
3273 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3274 }
cristye8c25f92010-06-03 00:53:06 +00003275 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003276 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003277 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003278 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003279 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3280 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3281 LocaleLower(value);
3282 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3283 (void) SetImageProperty(thumbnail_image,"software",
3284 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003285 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3286 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003287 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003288 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003289 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003290 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003291 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3292 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003293 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3294 return(thumbnail_image);
3295}