blob: 09d0cbada1f19de8a031eb6fcb5e5f12ca535d00 [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%
nicolas1eb2fcf2010-10-10 20:45:17 +0000529% The Lanczos2D and Robidoux filters are tuned for cylindrical
530% (radial) EWA (Elliptical Weighted Average) distortion. Lanczos2D
531% is a 2-lobed Lanczos filter using Sinc or Jinc (as appropriate).
532% Robidoux used to be a slightly sharpened version of Lanczos2D (with
533% blur=0.958033808); Now, it is a Cubic 'Keys' filter tuned so that
534% images with only vertical (or horizontal) features are exactly
535% preserved when performing 'no-op" with EWA distortion, which turns
536% out to be close to both plain Mitchell and "sharpened" Lanczos2D.
anthony61b5ddd2010-10-05 02:33:31 +0000537%
nicolas07bac812010-09-19 18:47:02 +0000538% Special 'expert' options can be used to override any and all filter
539% settings. This is not advised unless you have expert knowledge of
540% the use of resampling filtered techniques. Check on the results of
541% your selections using the "filter:verbose" setting to make sure you
anthony61b5ddd2010-10-05 02:33:31 +0000542% get the exact filter that you are tring to achieve.
cristy3ed852e2009-09-05 21:47:34 +0000543%
anthony48f77622010-10-03 14:32:31 +0000544% "filter:filter" Select the main function associated with
545% this filter name, as the weighting function of the filter.
546% This can be used to set a windowing function as a weighting
547% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000548%
anthony7bdc0ed2010-09-15 01:52:32 +0000549% If a "filter:window" operation has not been provided, then a 'Box'
550% windowing function will be set to denote that no windowing function
551% is being used.
cristy3ed852e2009-09-05 21:47:34 +0000552%
553% "filter:window" Select this windowing function for the filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000554% While any filter could be used as a windowing function, using the
555% 'first lobe' of that filter over the whole support window, using a
anthony48f77622010-10-03 14:32:31 +0000556% non-windowing function is not advisible. If no weighting filter
557% function is specifed a 'SincFast' filter will be used.
cristy3ed852e2009-09-05 21:47:34 +0000558%
anthony48f77622010-10-03 14:32:31 +0000559% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000560% This a simpler method of setting filter support size that will
anthony48f77622010-10-03 14:32:31 +0000561% correctly handle the Sinc/Jinc switch for an operators filtering
anthony7bdc0ed2010-09-15 01:52:32 +0000562% requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000563%
564% "filter:support" Set the support size for filtering to the size given
anthony48f77622010-10-03 14:32:31 +0000565% This not recommended for Sinc/Jinc windowed filters (lobes should
anthony7bdc0ed2010-09-15 01:52:32 +0000566% be used instead). This will override any 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000567%
anthonyb6d08c52010-09-13 01:17:04 +0000568% "filter:win-support" Scale windowing function to this size instead.
569% This causes the windowing (or self-windowing Lagrange filter) to act
570% is if the support window it much much larger than what is actually
571% supplied to the calling operator. The filter however is still
572% clipped to the real support size given, by the support range suppiled
573% to the caller. If unset this will equal the normal filter support
574% size.
575%
cristy3ed852e2009-09-05 21:47:34 +0000576% "filter:blur" Scale the filter and support window by this amount.
577% A value >1 will generally result in a more burred image with
578% more ringing effects, while a value <1 will sharpen the
579% resulting image with more aliasing and Morie effects.
580%
cristy3ed852e2009-09-05 21:47:34 +0000581% "filter:b"
582% "filter:c" Override the preset B,C values for a Cubic type of filter
583% If only one of these are given it is assumes to be a 'Keys'
584% type of filter such that B+2C=1, where Keys 'alpha' value = C
585%
anthonyb6d08c52010-09-13 01:17:04 +0000586% "filter:verbose" Output the exact results of the filter selections
587% made, as well as plotting data for graphing the resulting filter
588% over support range (blur adjusted).
cristy3ed852e2009-09-05 21:47:34 +0000589%
590% Set a true un-windowed Sinc filter with 10 lobes (very slow)
anthony48f77622010-10-03 14:32:31 +0000591% -define filter:filter=Sinc
592% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000593%
anthony48f77622010-10-03 14:32:31 +0000594% For example force an 8 lobe Lanczos (Sinc or Jinc) filter...
cristy3ed852e2009-09-05 21:47:34 +0000595% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000596% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000597%
cristy3ed852e2009-09-05 21:47:34 +0000598% The format of the AcquireResizeFilter method is:
599%
600% ResizeFilter *AcquireResizeFilter(const Image *image,
601% const FilterTypes filter_type, const MagickBooleanType radial,
602% ExceptionInfo *exception)
603%
cristy33b1c162010-01-23 22:51:51 +0000604% A description of each parameter follows:
605%
cristy3ed852e2009-09-05 21:47:34 +0000606% o image: the image.
607%
nicolas07bac812010-09-19 18:47:02 +0000608% o filter: the filter type, defining a preset filter, window and
609% support. The artifact settings listed above will override
610% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000611%
anthony48f77622010-10-03 14:32:31 +0000612% o blur: blur the filter by this amount, use 1.0 if unknown. Image
613% artifact "filter:blur" will override this API call usage, including
614% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000615%
anthony48f77622010-10-03 14:32:31 +0000616% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
617% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000618%
619% o exception: return any errors or warnings in this structure.
620%
621*/
622MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000623 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000624 const MagickBooleanType cylindrical,ExceptionInfo *exception)
625{
626 const char
627 *artifact;
628
629 FilterTypes
630 filter_type,
631 window_type;
632
cristy3ed852e2009-09-05 21:47:34 +0000633 MagickRealType
634 B,
635 C;
636
637 register ResizeFilter
638 *resize_filter;
639
cristy9af9b5d2010-08-15 17:04:28 +0000640 ssize_t
641 option;
642
cristy3ed852e2009-09-05 21:47:34 +0000643 /*
anthony48f77622010-10-03 14:32:31 +0000644 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000645 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000646 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
647 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
648 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000649
nicolas07bac812010-09-19 18:47:02 +0000650 WARNING: The order of this tabel must match the order of the
651 FilterTypes enumeration specified in "resample.h", or the filter
652 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000653
654 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000655 */
656 static struct
657 {
658 FilterTypes
659 filter,
660 window;
661 } const mapping[SentinelFilter] =
662 {
anthony462ee072010-09-27 12:34:02 +0000663 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
664 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
665 { BoxFilter, BoxFilter }, /* Box averaging filter */
666 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
667 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
668 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
669 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
670 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
671 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
672 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
673 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
674 { CatromFilter, BoxFilter }, /* Cubic interpolator */
675 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
676 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
anthony48f77622010-10-03 14:32:31 +0000677 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
678 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony462ee072010-09-27 12:34:02 +0000679 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
680 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
681 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
682 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
683 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
684 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
685 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolas5835ee02010-10-10 20:40:15 +0000686 { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
nicolas1eb2fcf2010-10-10 20:45:17 +0000687 { RobidouxFilter, BoxFilter }, /* SPECIAL: Keys cubic tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000688 };
689 /*
nicolas32f44eb2010-09-20 01:23:12 +0000690 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000691 function. The default support size for that filter as a weighting
692 function, the range to scale with to use that function as a sinc
693 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000694
anthony07a3f7f2010-09-16 03:03:11 +0000695 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000696 SincFast(), and CubicBC() functions, which may have multiple
697 filter to function associations.
698
699 See "filter:verbose" handling below for the function -> filter
700 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000701 */
702 static struct
703 {
704 MagickRealType
705 (*function)(const MagickRealType, const ResizeFilter*),
anthony61b5ddd2010-10-05 02:33:31 +0000706 lobes, /* default lobes/support size of the weighting filter */
anthony2d9b8b52010-09-14 08:31:07 +0000707 scale, /* windowing function range, for scaling windowing function */
anthony933b4bf2010-09-10 02:31:37 +0000708 B,C; /* Cubic Filter factors for a CubicBC function, else ignored */
cristy3ed852e2009-09-05 21:47:34 +0000709 } const filters[SentinelFilter] =
710 {
anthony61b5ddd2010-10-05 02:33:31 +0000711 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
712 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
713 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
714 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
715 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
716 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
717 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
718 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
719 { Gaussian, 1.5, 1.5, 0.0, 0.0 }, /* Gaussian */
720 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
721 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
722 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
anthony853d6972010-10-08 06:01:31 +0000723 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
724 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
725 { Jinc, 3.0, 1.21967, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000726 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
727 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
728 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
729 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
730 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
731 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
732 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
733 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
nicolas0134bbe2010-10-10 15:55:42 +0000734 { Jinc, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2D (Jinc-Jinc) */
anthony02b4cb42010-10-10 04:54:35 +0000735 { CubicBC, 2.0, 1.0, 0.37821575509399862, 0.31089212245300069 }
nicolas0134bbe2010-10-10 15:55:42 +0000736 /* Robidoux: Keys cubic close to Lanczos2D with blur=0.958033808 */
cristy3ed852e2009-09-05 21:47:34 +0000737 };
738 /*
nicolase473f722010-10-07 00:05:13 +0000739 The known zero crossings of the Jinc() or more accurately the
740 Jinc(x*PI) function being used as a filter. It is used by the
741 "filter:lobes" for support selection, so users do not have to deal
742 with the highly irrational sizes of the 'lobes' of the Jinc
743 filter.
anthony48f77622010-10-03 14:32:31 +0000744
nicolase473f722010-10-07 00:05:13 +0000745 Values taken from
anthony48f77622010-10-03 14:32:31 +0000746 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000747 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000748 */
749 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000750 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000751 {
anthonyc2d07db2010-09-15 23:47:40 +0000752 1.21966989126651,
753 2.23313059438153,
754 3.23831548416624,
755 4.24106286379607,
756 5.24276437687019,
757 6.24392168986449,
758 7.24475986871996,
759 8.24539491395205,
760 9.24589268494948,
761 10.2462933487549,
762 11.2466227948779,
763 12.2468984611381,
764 13.2471325221811,
765 14.2473337358069,
766 15.2475085630373,
767 16.247661874701
cristy3ed852e2009-09-05 21:47:34 +0000768 };
769
cristy33b1c162010-01-23 22:51:51 +0000770 /*
771 Allocate resize filter.
772 */
cristy3ed852e2009-09-05 21:47:34 +0000773 assert(image != (const Image *) NULL);
774 assert(image->signature == MagickSignature);
775 if (image->debug != MagickFalse)
776 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
777 assert(UndefinedFilter < filter && filter < SentinelFilter);
778 assert(exception != (ExceptionInfo *) NULL);
779 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000780 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000781 if (resize_filter == (ResizeFilter *) NULL)
782 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000783 /*
784 Defaults for the requested filter.
785 */
786 filter_type=mapping[filter].filter;
787 window_type=mapping[filter].window;
anthony48f77622010-10-03 14:32:31 +0000788 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000789 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000790 switch (filter_type)
791 {
792 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000793 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000794 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000795 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000796 break;
anthonyba5a7c32010-09-15 02:42:25 +0000797 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000798 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000799 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000800 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000801 break;
anthony61b5ddd2010-10-05 02:33:31 +0000802
cristy33b1c162010-01-23 22:51:51 +0000803 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000804 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000805 filter_type=JincFilter;
806 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000807 break;
cristya782ecf2010-01-25 02:59:14 +0000808 default:
809 break;
cristy3ed852e2009-09-05 21:47:34 +0000810 }
anthony61b5ddd2010-10-05 02:33:31 +0000811 else
812 switch (filter_type)
813 {
814 case Lanczos2DFilter:
nicolas45b58a92010-10-07 15:46:39 +0000815 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000816 window_type=SincFastFilter;
817 break;
818 default:
819 break;
820 }
821
cristy3ed852e2009-09-05 21:47:34 +0000822 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000823 if (artifact != (const char *) NULL)
824 {
cristy9af9b5d2010-08-15 17:04:28 +0000825 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000826 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000827 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000828 filter_type=(FilterTypes) option;
829 window_type=BoxFilter;
830 }
831 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000832 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000833 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000834 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000835 }
nicolas07bac812010-09-19 18:47:02 +0000836 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000837 artifact=GetImageArtifact(image,"filter:window");
838 if (artifact != (const char *) NULL)
839 {
cristy9af9b5d2010-08-15 17:04:28 +0000840 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000841 if ((UndefinedFilter < option) && (option < SentinelFilter))
842 {
843 if (option != LanczosFilter)
844 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000845 else
anthony48f77622010-10-03 14:32:31 +0000846 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000847 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000848 }
cristy33b1c162010-01-23 22:51:51 +0000849 }
cristy3ed852e2009-09-05 21:47:34 +0000850 }
cristy33b1c162010-01-23 22:51:51 +0000851 else
852 {
anthony48f77622010-10-03 14:32:31 +0000853 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000854 artifact=GetImageArtifact(image,"filter:window");
855 if (artifact != (const char *) NULL)
856 {
857 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
858 artifact);
859 if ((UndefinedFilter < option) && (option < SentinelFilter))
860 {
anthony61b5ddd2010-10-05 02:33:31 +0000861 filter_type=cylindrical != MagickFalse ?
862 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000863 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000864 }
865 }
866 }
nicolas07bac812010-09-19 18:47:02 +0000867 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000868 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000869 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000870 resize_filter->window=filters[window_type].function;
871 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000872 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000873
anthony10b8bc82010-10-02 12:48:46 +0000874 /* Filter blur -- scaling both filter and support window. */
875 resize_filter->blur=blur;
876 artifact=GetImageArtifact(image,"filter:blur");
877 if (artifact != (const char *) NULL)
878 resize_filter->blur=StringToDouble(artifact);
879 if (resize_filter->blur < MagickEpsilon)
880 resize_filter->blur=(MagickRealType) MagickEpsilon;
881
882 if (cylindrical != MagickFalse)
883 switch (filter_type)
884 {
885 case PointFilter:
886 case BoxFilter:
887 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000888 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000889 break;
890 case GaussianFilter:
891 /* Cylindrical Gaussian should have a sigma of sqrt(2)/2
892 * and not the default sigma of 1/2 - so use blur to enlarge
893 * and adjust support so actual practical support = 2.0 by default
894 */
895 resize_filter->blur *= MagickSQ2;
cristy1c9bb452010-10-03 16:48:33 +0000896 resize_filter->support = (MagickRealType) MagickSQ2; /* which times blur => 2.0 */
anthony10b8bc82010-10-02 12:48:46 +0000897 break;
anthony81b8bf92010-10-02 13:54:34 +0000898 default:
899 break;
anthony10b8bc82010-10-02 12:48:46 +0000900 }
anthony61b5ddd2010-10-05 02:33:31 +0000901 else
902 switch (filter_type)
903 {
904 case Lanczos2DFilter:
anthony853d6972010-10-08 06:01:31 +0000905 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000906 resize_filter->filter=SincFast;
907 break;
908 default:
909 break;
910 }
911
anthony2d9b8b52010-09-14 08:31:07 +0000912 /* Filter support overrides. */
cristy3ed852e2009-09-05 21:47:34 +0000913 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000914 if (artifact != (const char *) NULL)
915 {
cristybb503372010-05-27 20:51:26 +0000916 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000917 lobes;
918
cristy96b16132010-08-29 17:19:52 +0000919 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000920 if (lobes < 1)
921 lobes=1;
922 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000923 }
anthony61b5ddd2010-10-05 02:33:31 +0000924 /* convert Jinc lobes to a real support value */
925 if (resize_filter->filter == Jinc)
926 {
927 if (resize_filter->support > 16)
928 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
929 else
930 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
931 }
932 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000933 artifact=GetImageArtifact(image,"filter:support");
934 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000935 resize_filter->support=fabs(StringToDouble(artifact));
936 /*
nicolas07bac812010-09-19 18:47:02 +0000937 Scale windowing function separatally to the support 'clipping'
938 window that calling operator is planning to actually use. (Expert
939 override)
cristy3ed852e2009-09-05 21:47:34 +0000940 */
anthony55f12332010-09-10 01:13:02 +0000941 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000942 artifact=GetImageArtifact(image,"filter:win-support");
943 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000944 resize_filter->window_support=fabs(StringToDouble(artifact));
945 /*
anthony1f90a6b2010-09-14 08:56:31 +0000946 Adjust window function scaling to the windowing support for
947 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000948 */
949 resize_filter->scale /= resize_filter->window_support;
950 /*
nicolas07bac812010-09-19 18:47:02 +0000951 Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000952 */
cristy3ed852e2009-09-05 21:47:34 +0000953 B=0.0;
954 C=0.0;
cristy33b1c162010-01-23 22:51:51 +0000955 if ((filters[filter_type].function == CubicBC) ||
956 (filters[window_type].function == CubicBC))
957 {
anthony2d9b8b52010-09-14 08:31:07 +0000958 B=filters[filter_type].B;
959 C=filters[filter_type].C;
960 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +0000961 {
anthony2d9b8b52010-09-14 08:31:07 +0000962 B=filters[window_type].B;
963 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +0000964 }
cristy33b1c162010-01-23 22:51:51 +0000965 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +0000966 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000967 {
968 B=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +0000969 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +0000970 artifact=GetImageArtifact(image,"filter:c");
971 if (artifact != (const char *) NULL)
972 C=StringToDouble(artifact);
973 }
974 else
975 {
976 artifact=GetImageArtifact(image,"filter:c");
977 if (artifact != (const char *) NULL)
978 {
979 C=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +0000980 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +0000981 }
982 }
983 /*
nicolas07bac812010-09-19 18:47:02 +0000984 Convert B,C values into Cubic Coefficents. See CubicBC().
cristy33b1c162010-01-23 22:51:51 +0000985 */
986 resize_filter->cubic[0]=(6.0-2.0*B)/6.0;
cristy3ed852e2009-09-05 21:47:34 +0000987 resize_filter->cubic[1]=0.0;
cristy33b1c162010-01-23 22:51:51 +0000988 resize_filter->cubic[2]=(-18.0+12.0*B+6.0*C)/6.0;
989 resize_filter->cubic[3]=(12.0-9.0*B-6.0*C)/6.0;
990 resize_filter->cubic[4]=(8.0*B+24.0*C)/6.0;
991 resize_filter->cubic[5]=(-12.0*B-48.0*C)/6.0;
992 resize_filter->cubic[6]=(6.0*B+30.0*C)/6.0;
nicolas58c77cd2010-09-20 15:51:39 +0000993 resize_filter->cubic[7]=(-B-6.0*C)/6.0;
cristy3ed852e2009-09-05 21:47:34 +0000994 }
anthony55f12332010-09-10 01:13:02 +0000995 /*
nicolas07bac812010-09-19 18:47:02 +0000996 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +0000997 */
anthonye06e4c12010-09-15 04:03:52 +0000998#if defined(MAGICKCORE_OPENMP_SUPPORT)
anthony72949792010-10-08 04:44:56 +0000999 #pragma omp single
1000 {
anthonye06e4c12010-09-15 04:03:52 +00001001#endif
1002 artifact=GetImageArtifact(image,"filter:verbose");
1003 if (artifact != (const char *) NULL)
1004 {
1005 double
1006 support,
1007 x;
cristy3ed852e2009-09-05 21:47:34 +00001008
nicolas07bac812010-09-19 18:47:02 +00001009 /*
anthony463be1d2010-09-26 01:07:36 +00001010 Set the weighting function properly when the weighting
1011 function may not exactly match the filter of the same name.
1012 EG: a Point filter really uses a Box weighting function
1013 with a different support than is typically used.
1014
anthonye06e4c12010-09-15 04:03:52 +00001015 */
anthony463be1d2010-09-26 01:07:36 +00001016 if (resize_filter->filter == Box) filter_type=BoxFilter;
1017 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1018 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
anthony61b5ddd2010-10-05 02:33:31 +00001019 if (resize_filter->filter == Jinc) filter_type=JincFilter;
anthony463be1d2010-09-26 01:07:36 +00001020 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
anthonye06e4c12010-09-15 04:03:52 +00001021 /*
nicolas07bac812010-09-19 18:47:02 +00001022 Report Filter Details.
anthonye06e4c12010-09-15 04:03:52 +00001023 */
cristy03dbbd22010-09-19 23:04:47 +00001024 support=GetResizeFilterSupport(resize_filter); /* support range */
anthony61b5ddd2010-10-05 02:33:31 +00001025 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
cristy03dbbd22010-09-19 23:04:47 +00001026 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1027 MagickFilterOptions,filter_type));
1028 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
anthony463be1d2010-09-26 01:07:36 +00001029 MagickFilterOptions, window_type));
cristy03dbbd22010-09-19 23:04:47 +00001030 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
anthony463be1d2010-09-26 01:07:36 +00001031 (double) resize_filter->support);
cristy03dbbd22010-09-19 23:04:47 +00001032 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
anthony463be1d2010-09-26 01:07:36 +00001033 (double) resize_filter->window_support);
cristy03dbbd22010-09-19 23:04:47 +00001034 (void) fprintf(stdout,"# blur = %.*g\n",GetMagickPrecision(),
1035 (double) resize_filter->blur);
anthony463be1d2010-09-26 01:07:36 +00001036 (void) fprintf(stdout,"# blurred_support = %.*g\n",GetMagickPrecision(),
cristy03dbbd22010-09-19 23:04:47 +00001037 (double) support);
1038 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1039 (double) B,GetMagickPrecision(),(double) C);
anthony61b5ddd2010-10-05 02:33:31 +00001040 (void) fprintf(stdout,"\n");
anthonye06e4c12010-09-15 04:03:52 +00001041 /*
nicolas07bac812010-09-19 18:47:02 +00001042 Output values of resulting filter graph -- for graphing
1043 filter result.
anthonye06e4c12010-09-15 04:03:52 +00001044 */
1045 for (x=0.0; x <= support; x+=0.01f)
cristy03dbbd22010-09-19 23:04:47 +00001046 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1047 (double) GetResizeFilterWeight(resize_filter,x));
nicolas07bac812010-09-19 18:47:02 +00001048 /* A final value so gnuplot can graph the 'stop' properly. */
cristy03dbbd22010-09-19 23:04:47 +00001049 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1050 0.0);
anthonye06e4c12010-09-15 04:03:52 +00001051 }
anthony72949792010-10-08 04:44:56 +00001052 /* output the above once only for each image, and each setting */
cristybb66d9c2010-10-09 01:40:31 +00001053 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
anthonye06e4c12010-09-15 04:03:52 +00001054#if defined(MAGICKCORE_OPENMP_SUPPORT)
anthony61b5ddd2010-10-05 02:33:31 +00001055 }
anthonye06e4c12010-09-15 04:03:52 +00001056#endif
cristy3ed852e2009-09-05 21:47:34 +00001057 return(resize_filter);
1058}
1059
1060/*
1061%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1062% %
1063% %
1064% %
1065% A d a p t i v e R e s i z e I m a g e %
1066% %
1067% %
1068% %
1069%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1070%
1071% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1072%
1073% The format of the AdaptiveResizeImage method is:
1074%
cristy9af9b5d2010-08-15 17:04:28 +00001075% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1076% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001077%
1078% A description of each parameter follows:
1079%
1080% o image: the image.
1081%
1082% o columns: the number of columns in the resized image.
1083%
1084% o rows: the number of rows in the resized image.
1085%
1086% o exception: return any errors or warnings in this structure.
1087%
1088*/
1089MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001090 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001091{
1092#define AdaptiveResizeImageTag "Resize/Image"
1093
cristyc4c8d132010-01-07 01:58:38 +00001094 CacheView
1095 *resize_view;
1096
cristy3ed852e2009-09-05 21:47:34 +00001097 Image
1098 *resize_image;
1099
cristy3ed852e2009-09-05 21:47:34 +00001100 MagickBooleanType
1101 proceed;
1102
1103 MagickPixelPacket
1104 pixel;
1105
1106 PointInfo
1107 offset;
1108
1109 ResampleFilter
1110 *resample_filter;
1111
cristy9af9b5d2010-08-15 17:04:28 +00001112 ssize_t
1113 y;
1114
cristy3ed852e2009-09-05 21:47:34 +00001115 /*
1116 Adaptively resize image.
1117 */
1118 assert(image != (const Image *) NULL);
1119 assert(image->signature == MagickSignature);
1120 if (image->debug != MagickFalse)
1121 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1122 assert(exception != (ExceptionInfo *) NULL);
1123 assert(exception->signature == MagickSignature);
1124 if ((columns == 0) || (rows == 0))
1125 return((Image *) NULL);
1126 if ((columns == image->columns) && (rows == image->rows))
1127 return(CloneImage(image,0,0,MagickTrue,exception));
1128 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1129 if (resize_image == (Image *) NULL)
1130 return((Image *) NULL);
1131 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1132 {
1133 InheritException(exception,&resize_image->exception);
1134 resize_image=DestroyImage(resize_image);
1135 return((Image *) NULL);
1136 }
1137 GetMagickPixelPacket(image,&pixel);
1138 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001139 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001140 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001141 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001142 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001143 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001144 {
1145 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001146 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001147
cristybb503372010-05-27 20:51:26 +00001148 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001149 x;
1150
1151 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001152 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001153
1154 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1155 exception);
1156 if (q == (PixelPacket *) NULL)
1157 break;
1158 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1159 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001160 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001161 {
1162 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1163 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1164 &pixel);
1165 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1166 q++;
1167 }
1168 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1169 break;
cristy96b16132010-08-29 17:19:52 +00001170 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1171 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001172 if (proceed == MagickFalse)
1173 break;
1174 }
1175 resample_filter=DestroyResampleFilter(resample_filter);
1176 resize_view=DestroyCacheView(resize_view);
1177 return(resize_image);
1178}
1179
1180/*
1181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1182% %
1183% %
1184% %
1185+ B e s s e l O r d e r O n e %
1186% %
1187% %
1188% %
1189%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1190%
1191% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001192% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001193%
1194% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1195%
1196% j1(x) = x*j1(x);
1197%
1198% For x in (8,inf)
1199%
1200% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1201%
1202% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1203%
1204% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1205% = 1/sqrt(2) * (sin(x) - cos(x))
1206% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1207% = -1/sqrt(2) * (sin(x) + cos(x))
1208%
1209% The format of the BesselOrderOne method is:
1210%
1211% MagickRealType BesselOrderOne(MagickRealType x)
1212%
1213% A description of each parameter follows:
1214%
1215% o x: MagickRealType value.
1216%
1217*/
1218
1219#undef I0
1220static MagickRealType I0(MagickRealType x)
1221{
1222 MagickRealType
1223 sum,
1224 t,
1225 y;
1226
cristybb503372010-05-27 20:51:26 +00001227 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001228 i;
1229
1230 /*
1231 Zeroth order Bessel function of the first kind.
1232 */
1233 sum=1.0;
1234 y=x*x/4.0;
1235 t=y;
1236 for (i=2; t > MagickEpsilon; i++)
1237 {
1238 sum+=t;
1239 t*=y/((MagickRealType) i*i);
1240 }
1241 return(sum);
1242}
1243
1244#undef J1
1245static MagickRealType J1(MagickRealType x)
1246{
1247 MagickRealType
1248 p,
1249 q;
1250
cristybb503372010-05-27 20:51:26 +00001251 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001252 i;
1253
1254 static const double
1255 Pone[] =
1256 {
1257 0.581199354001606143928050809e+21,
1258 -0.6672106568924916298020941484e+20,
1259 0.2316433580634002297931815435e+19,
1260 -0.3588817569910106050743641413e+17,
1261 0.2908795263834775409737601689e+15,
1262 -0.1322983480332126453125473247e+13,
1263 0.3413234182301700539091292655e+10,
1264 -0.4695753530642995859767162166e+7,
1265 0.270112271089232341485679099e+4
1266 },
1267 Qone[] =
1268 {
1269 0.11623987080032122878585294e+22,
1270 0.1185770712190320999837113348e+20,
1271 0.6092061398917521746105196863e+17,
1272 0.2081661221307607351240184229e+15,
1273 0.5243710262167649715406728642e+12,
1274 0.1013863514358673989967045588e+10,
1275 0.1501793594998585505921097578e+7,
1276 0.1606931573481487801970916749e+4,
1277 0.1e+1
1278 };
1279
1280 p=Pone[8];
1281 q=Qone[8];
1282 for (i=7; i >= 0; i--)
1283 {
1284 p=p*x*x+Pone[i];
1285 q=q*x*x+Qone[i];
1286 }
1287 return(p/q);
1288}
1289
1290#undef P1
1291static MagickRealType P1(MagickRealType x)
1292{
1293 MagickRealType
1294 p,
1295 q;
1296
cristybb503372010-05-27 20:51:26 +00001297 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001298 i;
1299
1300 static const double
1301 Pone[] =
1302 {
1303 0.352246649133679798341724373e+5,
1304 0.62758845247161281269005675e+5,
1305 0.313539631109159574238669888e+5,
1306 0.49854832060594338434500455e+4,
1307 0.2111529182853962382105718e+3,
1308 0.12571716929145341558495e+1
1309 },
1310 Qone[] =
1311 {
1312 0.352246649133679798068390431e+5,
1313 0.626943469593560511888833731e+5,
1314 0.312404063819041039923015703e+5,
1315 0.4930396490181088979386097e+4,
1316 0.2030775189134759322293574e+3,
1317 0.1e+1
1318 };
1319
1320 p=Pone[5];
1321 q=Qone[5];
1322 for (i=4; i >= 0; i--)
1323 {
1324 p=p*(8.0/x)*(8.0/x)+Pone[i];
1325 q=q*(8.0/x)*(8.0/x)+Qone[i];
1326 }
1327 return(p/q);
1328}
1329
1330#undef Q1
1331static MagickRealType Q1(MagickRealType x)
1332{
1333 MagickRealType
1334 p,
1335 q;
1336
cristybb503372010-05-27 20:51:26 +00001337 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001338 i;
1339
1340 static const double
1341 Pone[] =
1342 {
1343 0.3511751914303552822533318e+3,
1344 0.7210391804904475039280863e+3,
1345 0.4259873011654442389886993e+3,
1346 0.831898957673850827325226e+2,
1347 0.45681716295512267064405e+1,
1348 0.3532840052740123642735e-1
1349 },
1350 Qone[] =
1351 {
1352 0.74917374171809127714519505e+4,
1353 0.154141773392650970499848051e+5,
1354 0.91522317015169922705904727e+4,
1355 0.18111867005523513506724158e+4,
1356 0.1038187585462133728776636e+3,
1357 0.1e+1
1358 };
1359
1360 p=Pone[5];
1361 q=Qone[5];
1362 for (i=4; i >= 0; i--)
1363 {
1364 p=p*(8.0/x)*(8.0/x)+Pone[i];
1365 q=q*(8.0/x)*(8.0/x)+Qone[i];
1366 }
1367 return(p/q);
1368}
1369
1370static MagickRealType BesselOrderOne(MagickRealType x)
1371{
1372 MagickRealType
1373 p,
1374 q;
1375
1376 if (x == 0.0)
1377 return(0.0);
1378 p=x;
1379 if (x < 0.0)
1380 x=(-x);
1381 if (x < 8.0)
1382 return(p*J1(x));
1383 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1384 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1385 cos((double) x))));
1386 if (p < 0.0)
1387 q=(-q);
1388 return(q);
1389}
1390
1391/*
1392%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1393% %
1394% %
1395% %
1396+ D e s t r o y R e s i z e F i l t e r %
1397% %
1398% %
1399% %
1400%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1401%
1402% DestroyResizeFilter() destroy the resize filter.
1403%
cristya2ffd7e2010-03-10 20:50:30 +00001404% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001405%
1406% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1407%
1408% A description of each parameter follows:
1409%
1410% o resize_filter: the resize filter.
1411%
1412*/
1413MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1414{
1415 assert(resize_filter != (ResizeFilter *) NULL);
1416 assert(resize_filter->signature == MagickSignature);
1417 resize_filter->signature=(~MagickSignature);
1418 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1419 return(resize_filter);
1420}
1421
1422/*
1423%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1424% %
1425% %
1426% %
1427+ G e t R e s i z e F i l t e r S u p p o r t %
1428% %
1429% %
1430% %
1431%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1432%
1433% GetResizeFilterSupport() return the current support window size for this
1434% filter. Note that this may have been enlarged by filter:blur factor.
1435%
1436% The format of the GetResizeFilterSupport method is:
1437%
1438% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1439%
1440% A description of each parameter follows:
1441%
1442% o filter: Image filter to use.
1443%
1444*/
1445MagickExport MagickRealType GetResizeFilterSupport(
1446 const ResizeFilter *resize_filter)
1447{
1448 assert(resize_filter != (ResizeFilter *) NULL);
1449 assert(resize_filter->signature == MagickSignature);
1450 return(resize_filter->support*resize_filter->blur);
1451}
1452
1453/*
1454%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1455% %
1456% %
1457% %
1458+ G e t R e s i z e F i l t e r W e i g h t %
1459% %
1460% %
1461% %
1462%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1463%
1464% GetResizeFilterWeight evaluates the specified resize filter at the point x
1465% which usally lies between zero and the filters current 'support' and
1466% returns the weight of the filter function at that point.
1467%
1468% The format of the GetResizeFilterWeight method is:
1469%
1470% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1471% const MagickRealType x)
1472%
1473% A description of each parameter follows:
1474%
1475% o filter: the filter type.
1476%
1477% o x: the point.
1478%
1479*/
1480MagickExport MagickRealType GetResizeFilterWeight(
1481 const ResizeFilter *resize_filter,const MagickRealType x)
1482{
1483 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001484 scale,
1485 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001486
1487 /*
1488 Windowing function - scale the weighting filter by this amount.
1489 */
1490 assert(resize_filter != (ResizeFilter *) NULL);
1491 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001492 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001493 if ((resize_filter->window_support < MagickEpsilon) ||
1494 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001495 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001496 else
1497 {
anthony55f12332010-09-10 01:13:02 +00001498 scale=resize_filter->scale;
1499 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001500 }
anthony55f12332010-09-10 01:13:02 +00001501 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001502}
1503
1504/*
1505%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1506% %
1507% %
1508% %
1509% M a g n i f y I m a g e %
1510% %
1511% %
1512% %
1513%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1514%
1515% MagnifyImage() is a convenience method that scales an image proportionally
1516% to twice its size.
1517%
1518% The format of the MagnifyImage method is:
1519%
1520% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1521%
1522% A description of each parameter follows:
1523%
1524% o image: the image.
1525%
1526% o exception: return any errors or warnings in this structure.
1527%
1528*/
1529MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1530{
1531 Image
1532 *magnify_image;
1533
1534 assert(image != (Image *) NULL);
1535 assert(image->signature == MagickSignature);
1536 if (image->debug != MagickFalse)
1537 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1538 assert(exception != (ExceptionInfo *) NULL);
1539 assert(exception->signature == MagickSignature);
1540 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1541 1.0,exception);
1542 return(magnify_image);
1543}
1544
1545/*
1546%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1547% %
1548% %
1549% %
1550% M i n i f y I m a g e %
1551% %
1552% %
1553% %
1554%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1555%
1556% MinifyImage() is a convenience method that scales an image proportionally
1557% to half its size.
1558%
1559% The format of the MinifyImage method is:
1560%
1561% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1562%
1563% A description of each parameter follows:
1564%
1565% o image: the image.
1566%
1567% o exception: return any errors or warnings in this structure.
1568%
1569*/
1570MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1571{
1572 Image
1573 *minify_image;
1574
1575 assert(image != (Image *) NULL);
1576 assert(image->signature == MagickSignature);
1577 if (image->debug != MagickFalse)
1578 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1579 assert(exception != (ExceptionInfo *) NULL);
1580 assert(exception->signature == MagickSignature);
1581 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1582 1.0,exception);
1583 return(minify_image);
1584}
1585
1586/*
1587%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1588% %
1589% %
1590% %
1591% R e s a m p l e I m a g e %
1592% %
1593% %
1594% %
1595%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1596%
1597% ResampleImage() resize image in terms of its pixel size, so that when
1598% displayed at the given resolution it will be the same size in terms of
1599% real world units as the original image at the original resolution.
1600%
1601% The format of the ResampleImage method is:
1602%
1603% Image *ResampleImage(Image *image,const double x_resolution,
1604% const double y_resolution,const FilterTypes filter,const double blur,
1605% ExceptionInfo *exception)
1606%
1607% A description of each parameter follows:
1608%
1609% o image: the image to be resized to fit the given resolution.
1610%
1611% o x_resolution: the new image x resolution.
1612%
1613% o y_resolution: the new image y resolution.
1614%
1615% o filter: Image filter to use.
1616%
1617% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1618%
1619*/
1620MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1621 const double y_resolution,const FilterTypes filter,const double blur,
1622 ExceptionInfo *exception)
1623{
1624#define ResampleImageTag "Resample/Image"
1625
1626 Image
1627 *resample_image;
1628
cristybb503372010-05-27 20:51:26 +00001629 size_t
cristy3ed852e2009-09-05 21:47:34 +00001630 height,
1631 width;
1632
1633 /*
1634 Initialize sampled image attributes.
1635 */
1636 assert(image != (const Image *) NULL);
1637 assert(image->signature == MagickSignature);
1638 if (image->debug != MagickFalse)
1639 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1640 assert(exception != (ExceptionInfo *) NULL);
1641 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001642 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1643 72.0 : image->x_resolution)+0.5);
1644 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1645 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001646 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1647 if (resample_image != (Image *) NULL)
1648 {
1649 resample_image->x_resolution=x_resolution;
1650 resample_image->y_resolution=y_resolution;
1651 }
1652 return(resample_image);
1653}
1654#if defined(MAGICKCORE_LQR_DELEGATE)
1655
1656/*
1657%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1658% %
1659% %
1660% %
1661% L i q u i d R e s c a l e I m a g e %
1662% %
1663% %
1664% %
1665%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1666%
1667% LiquidRescaleImage() rescales image with seam carving.
1668%
1669% The format of the LiquidRescaleImage method is:
1670%
1671% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001672% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001673% const double delta_x,const double rigidity,ExceptionInfo *exception)
1674%
1675% A description of each parameter follows:
1676%
1677% o image: the image.
1678%
1679% o columns: the number of columns in the rescaled image.
1680%
1681% o rows: the number of rows in the rescaled image.
1682%
1683% o delta_x: maximum seam transversal step (0 means straight seams).
1684%
1685% o rigidity: introduce a bias for non-straight seams (typically 0).
1686%
1687% o exception: return any errors or warnings in this structure.
1688%
1689*/
cristy9af9b5d2010-08-15 17:04:28 +00001690MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1691 const size_t rows,const double delta_x,const double rigidity,
1692 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001693{
1694#define LiquidRescaleImageTag "Rescale/Image"
1695
cristyc5c6f662010-09-22 14:23:02 +00001696 CacheView
1697 *rescale_view;
1698
cristy3ed852e2009-09-05 21:47:34 +00001699 const char
1700 *map;
1701
1702 guchar
1703 *packet;
1704
1705 Image
1706 *rescale_image;
1707
1708 int
1709 x,
1710 y;
1711
1712 LqrCarver
1713 *carver;
1714
1715 LqrRetVal
1716 lqr_status;
1717
1718 MagickBooleanType
1719 status;
1720
1721 MagickPixelPacket
1722 pixel;
1723
1724 unsigned char
1725 *pixels;
1726
1727 /*
1728 Liquid rescale image.
1729 */
1730 assert(image != (const Image *) NULL);
1731 assert(image->signature == MagickSignature);
1732 if (image->debug != MagickFalse)
1733 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1734 assert(exception != (ExceptionInfo *) NULL);
1735 assert(exception->signature == MagickSignature);
1736 if ((columns == 0) || (rows == 0))
1737 return((Image *) NULL);
1738 if ((columns == image->columns) && (rows == image->rows))
1739 return(CloneImage(image,0,0,MagickTrue,exception));
1740 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001741 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001742 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1743 {
1744 Image
1745 *resize_image;
1746
cristybb503372010-05-27 20:51:26 +00001747 size_t
cristy3ed852e2009-09-05 21:47:34 +00001748 height,
1749 width;
1750
1751 /*
1752 Honor liquid resize size limitations.
1753 */
1754 for (width=image->columns; columns >= (2*width-1); width*=2);
1755 for (height=image->rows; rows >= (2*height-1); height*=2);
1756 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1757 exception);
1758 if (resize_image == (Image *) NULL)
1759 return((Image *) NULL);
1760 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1761 rigidity,exception);
1762 resize_image=DestroyImage(resize_image);
1763 return(rescale_image);
1764 }
1765 map="RGB";
1766 if (image->matte == MagickFalse)
1767 map="RGBA";
1768 if (image->colorspace == CMYKColorspace)
1769 {
1770 map="CMYK";
1771 if (image->matte == MagickFalse)
1772 map="CMYKA";
1773 }
1774 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1775 strlen(map)*sizeof(*pixels));
1776 if (pixels == (unsigned char *) NULL)
1777 return((Image *) NULL);
1778 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1779 pixels,exception);
1780 if (status == MagickFalse)
1781 {
1782 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1783 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1784 }
1785 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1786 if (carver == (LqrCarver *) NULL)
1787 {
1788 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1789 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1790 }
1791 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1792 lqr_status=lqr_carver_resize(carver,columns,rows);
1793 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1794 lqr_carver_get_height(carver),MagickTrue,exception);
1795 if (rescale_image == (Image *) NULL)
1796 {
1797 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1798 return((Image *) NULL);
1799 }
1800 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1801 {
1802 InheritException(exception,&rescale_image->exception);
1803 rescale_image=DestroyImage(rescale_image);
1804 return((Image *) NULL);
1805 }
1806 GetMagickPixelPacket(rescale_image,&pixel);
1807 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001808 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001809 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1810 {
1811 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001812 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001813
1814 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001815 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001816
anthony22aad252010-09-23 06:59:07 +00001817 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001818 if (q == (PixelPacket *) NULL)
1819 break;
cristyc5c6f662010-09-22 14:23:02 +00001820 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001821 pixel.red=QuantumRange*(packet[0]/255.0);
1822 pixel.green=QuantumRange*(packet[1]/255.0);
1823 pixel.blue=QuantumRange*(packet[2]/255.0);
1824 if (image->colorspace != CMYKColorspace)
1825 {
1826 if (image->matte == MagickFalse)
1827 pixel.opacity=QuantumRange*(packet[3]/255.0);
1828 }
1829 else
1830 {
1831 pixel.index=QuantumRange*(packet[3]/255.0);
1832 if (image->matte == MagickFalse)
1833 pixel.opacity=QuantumRange*(packet[4]/255.0);
1834 }
1835 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001836 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001837 break;
1838 }
cristyc5c6f662010-09-22 14:23:02 +00001839 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001840 /*
1841 Relinquish resources.
1842 */
1843 lqr_carver_destroy(carver);
1844 return(rescale_image);
1845}
1846#else
1847MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001848 const size_t magick_unused(columns),const size_t magick_unused(rows),
1849 const double magick_unused(delta_x),const double magick_unused(rigidity),
1850 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001851{
1852 assert(image != (const Image *) NULL);
1853 assert(image->signature == MagickSignature);
1854 if (image->debug != MagickFalse)
1855 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1856 assert(exception != (ExceptionInfo *) NULL);
1857 assert(exception->signature == MagickSignature);
1858 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1859 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1860 return((Image *) NULL);
1861}
1862#endif
1863
1864/*
1865%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1866% %
1867% %
1868% %
1869% R e s i z e I m a g e %
1870% %
1871% %
1872% %
1873%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1874%
1875% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001876% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001877%
1878% If an undefined filter is given the filter defaults to Mitchell for a
1879% colormapped image, a image with a matte channel, or if the image is
1880% enlarged. Otherwise the filter defaults to a Lanczos.
1881%
1882% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1883%
1884% The format of the ResizeImage method is:
1885%
cristybb503372010-05-27 20:51:26 +00001886% Image *ResizeImage(Image *image,const size_t columns,
1887% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001888% ExceptionInfo *exception)
1889%
1890% A description of each parameter follows:
1891%
1892% o image: the image.
1893%
1894% o columns: the number of columns in the scaled image.
1895%
1896% o rows: the number of rows in the scaled image.
1897%
1898% o filter: Image filter to use.
1899%
cristy9af9b5d2010-08-15 17:04:28 +00001900% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1901% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001902%
1903% o exception: return any errors or warnings in this structure.
1904%
1905*/
1906
1907typedef struct _ContributionInfo
1908{
1909 MagickRealType
1910 weight;
1911
cristybb503372010-05-27 20:51:26 +00001912 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001913 pixel;
1914} ContributionInfo;
1915
1916static ContributionInfo **DestroyContributionThreadSet(
1917 ContributionInfo **contribution)
1918{
cristybb503372010-05-27 20:51:26 +00001919 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001920 i;
1921
1922 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001923 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001924 if (contribution[i] != (ContributionInfo *) NULL)
1925 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1926 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001927 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001928 return(contribution);
1929}
1930
1931static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1932{
cristybb503372010-05-27 20:51:26 +00001933 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001934 i;
1935
1936 ContributionInfo
1937 **contribution;
1938
cristybb503372010-05-27 20:51:26 +00001939 size_t
cristy3ed852e2009-09-05 21:47:34 +00001940 number_threads;
1941
1942 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001943 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001944 sizeof(*contribution));
1945 if (contribution == (ContributionInfo **) NULL)
1946 return((ContributionInfo **) NULL);
1947 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00001948 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00001949 {
1950 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1951 sizeof(**contribution));
1952 if (contribution[i] == (ContributionInfo *) NULL)
1953 return(DestroyContributionThreadSet(contribution));
1954 }
1955 return(contribution);
1956}
1957
1958static inline double MagickMax(const double x,const double y)
1959{
1960 if (x > y)
1961 return(x);
1962 return(y);
1963}
1964
1965static inline double MagickMin(const double x,const double y)
1966{
1967 if (x < y)
1968 return(x);
1969 return(y);
1970}
1971
1972static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
1973 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00001974 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001975{
1976#define ResizeImageTag "Resize/Image"
1977
cristyfa112112010-01-04 17:48:07 +00001978 CacheView
1979 *image_view,
1980 *resize_view;
1981
cristy3ed852e2009-09-05 21:47:34 +00001982 ClassType
1983 storage_class;
1984
1985 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00001986 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00001987
cristy3ed852e2009-09-05 21:47:34 +00001988 MagickBooleanType
1989 status;
1990
1991 MagickPixelPacket
1992 zero;
1993
1994 MagickRealType
1995 scale,
1996 support;
1997
cristy9af9b5d2010-08-15 17:04:28 +00001998 ssize_t
1999 x;
2000
cristy3ed852e2009-09-05 21:47:34 +00002001 /*
2002 Apply filter to resize horizontally from image to resize image.
2003 */
cristy5d824382010-09-06 14:00:17 +00002004 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002005 support=scale*GetResizeFilterSupport(resize_filter);
2006 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2007 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2008 {
2009 InheritException(exception,&resize_image->exception);
2010 return(MagickFalse);
2011 }
2012 if (support < 0.5)
2013 {
2014 /*
nicolas07bac812010-09-19 18:47:02 +00002015 Support too small even for nearest neighbour: Reduce to point
2016 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002017 */
2018 support=(MagickRealType) 0.5;
2019 scale=1.0;
2020 }
2021 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2022 if (contributions == (ContributionInfo **) NULL)
2023 {
2024 (void) ThrowMagickException(exception,GetMagickModule(),
2025 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2026 return(MagickFalse);
2027 }
2028 status=MagickTrue;
2029 scale=1.0/scale;
2030 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2031 image_view=AcquireCacheView(image);
2032 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002033#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002034 #pragma omp parallel for shared(status)
2035#endif
cristybb503372010-05-27 20:51:26 +00002036 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002037 {
cristy3ed852e2009-09-05 21:47:34 +00002038 MagickRealType
2039 center,
2040 density;
2041
2042 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002043 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002044
2045 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002046 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002047
cristy03dbbd22010-09-19 23:04:47 +00002048 register ContributionInfo
2049 *restrict contribution;
2050
cristy3ed852e2009-09-05 21:47:34 +00002051 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002052 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002053
cristy3ed852e2009-09-05 21:47:34 +00002054 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002055 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002056
cristy03dbbd22010-09-19 23:04:47 +00002057 register ssize_t
2058 y;
2059
cristy9af9b5d2010-08-15 17:04:28 +00002060 ssize_t
2061 n,
2062 start,
2063 stop;
2064
cristy3ed852e2009-09-05 21:47:34 +00002065 if (status == MagickFalse)
2066 continue;
2067 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002068 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2069 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002070 density=0.0;
2071 contribution=contributions[GetOpenMPThreadId()];
2072 for (n=0; n < (stop-start); n++)
2073 {
2074 contribution[n].pixel=start+n;
2075 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2076 ((MagickRealType) (start+n)-center+0.5));
2077 density+=contribution[n].weight;
2078 }
2079 if ((density != 0.0) && (density != 1.0))
2080 {
cristybb503372010-05-27 20:51:26 +00002081 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002082 i;
2083
2084 /*
2085 Normalize.
2086 */
2087 density=1.0/density;
2088 for (i=0; i < n; i++)
2089 contribution[i].weight*=density;
2090 }
cristy9af9b5d2010-08-15 17:04:28 +00002091 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2092 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002093 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2094 exception);
2095 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2096 {
2097 status=MagickFalse;
2098 continue;
2099 }
2100 indexes=GetCacheViewVirtualIndexQueue(image_view);
2101 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002102 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002103 {
cristy3ed852e2009-09-05 21:47:34 +00002104 MagickPixelPacket
2105 pixel;
2106
2107 MagickRealType
2108 alpha;
2109
cristybb503372010-05-27 20:51:26 +00002110 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002111 i;
2112
cristy9af9b5d2010-08-15 17:04:28 +00002113 ssize_t
2114 j;
2115
cristy3ed852e2009-09-05 21:47:34 +00002116 pixel=zero;
2117 if (image->matte == MagickFalse)
2118 {
2119 for (i=0; i < n; i++)
2120 {
2121 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2122 (contribution[i].pixel-contribution[0].pixel);
2123 alpha=contribution[i].weight;
2124 pixel.red+=alpha*(p+j)->red;
2125 pixel.green+=alpha*(p+j)->green;
2126 pixel.blue+=alpha*(p+j)->blue;
2127 pixel.opacity+=alpha*(p+j)->opacity;
2128 }
cristyce70c172010-01-07 17:15:30 +00002129 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2130 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2131 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2132 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002133 if ((image->colorspace == CMYKColorspace) &&
2134 (resize_image->colorspace == CMYKColorspace))
2135 {
2136 for (i=0; i < n; i++)
2137 {
2138 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2139 (contribution[i].pixel-contribution[0].pixel);
2140 alpha=contribution[i].weight;
2141 pixel.index+=alpha*indexes[j];
2142 }
cristyce70c172010-01-07 17:15:30 +00002143 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002144 }
2145 }
2146 else
2147 {
2148 MagickRealType
2149 gamma;
2150
2151 gamma=0.0;
2152 for (i=0; i < n; i++)
2153 {
2154 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2155 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002156 alpha=contribution[i].weight*QuantumScale*
2157 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002158 pixel.red+=alpha*(p+j)->red;
2159 pixel.green+=alpha*(p+j)->green;
2160 pixel.blue+=alpha*(p+j)->blue;
2161 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2162 gamma+=alpha;
2163 }
2164 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002165 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2166 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2167 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2168 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002169 if ((image->colorspace == CMYKColorspace) &&
2170 (resize_image->colorspace == CMYKColorspace))
2171 {
2172 for (i=0; i < n; i++)
2173 {
2174 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2175 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002176 alpha=contribution[i].weight*QuantumScale*
2177 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002178 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002179 }
cristyce70c172010-01-07 17:15:30 +00002180 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2181 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002182 }
2183 }
2184 if ((resize_image->storage_class == PseudoClass) &&
2185 (image->storage_class == PseudoClass))
2186 {
cristybb503372010-05-27 20:51:26 +00002187 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002188 1.0)+0.5);
2189 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2190 (contribution[i-start].pixel-contribution[0].pixel);
2191 resize_indexes[y]=indexes[j];
2192 }
2193 q++;
2194 }
2195 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2196 status=MagickFalse;
2197 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2198 {
2199 MagickBooleanType
2200 proceed;
2201
cristyb5d5f722009-11-04 03:03:49 +00002202#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002203 #pragma omp critical (MagickCore_HorizontalFilter)
2204#endif
cristy9af9b5d2010-08-15 17:04:28 +00002205 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002206 if (proceed == MagickFalse)
2207 status=MagickFalse;
2208 }
2209 }
2210 resize_view=DestroyCacheView(resize_view);
2211 image_view=DestroyCacheView(image_view);
2212 contributions=DestroyContributionThreadSet(contributions);
2213 return(status);
2214}
2215
2216static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2217 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002218 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002219{
cristyfa112112010-01-04 17:48:07 +00002220 CacheView
2221 *image_view,
2222 *resize_view;
2223
cristy3ed852e2009-09-05 21:47:34 +00002224 ClassType
2225 storage_class;
2226
2227 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002228 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002229
cristy3ed852e2009-09-05 21:47:34 +00002230 MagickBooleanType
2231 status;
2232
2233 MagickPixelPacket
2234 zero;
2235
2236 MagickRealType
2237 scale,
2238 support;
2239
cristy9af9b5d2010-08-15 17:04:28 +00002240 ssize_t
2241 y;
2242
cristy3ed852e2009-09-05 21:47:34 +00002243 /*
cristy9af9b5d2010-08-15 17:04:28 +00002244 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002245 */
cristy5d824382010-09-06 14:00:17 +00002246 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002247 support=scale*GetResizeFilterSupport(resize_filter);
2248 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2249 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2250 {
2251 InheritException(exception,&resize_image->exception);
2252 return(MagickFalse);
2253 }
2254 if (support < 0.5)
2255 {
2256 /*
nicolas07bac812010-09-19 18:47:02 +00002257 Support too small even for nearest neighbour: Reduce to point
2258 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002259 */
2260 support=(MagickRealType) 0.5;
2261 scale=1.0;
2262 }
2263 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2264 if (contributions == (ContributionInfo **) NULL)
2265 {
2266 (void) ThrowMagickException(exception,GetMagickModule(),
2267 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2268 return(MagickFalse);
2269 }
2270 status=MagickTrue;
2271 scale=1.0/scale;
2272 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2273 image_view=AcquireCacheView(image);
2274 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002275#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002276 #pragma omp parallel for shared(status)
2277#endif
cristybb503372010-05-27 20:51:26 +00002278 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002279 {
cristy3ed852e2009-09-05 21:47:34 +00002280 MagickRealType
2281 center,
2282 density;
2283
2284 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002285 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002286
2287 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002288 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002289
cristy03dbbd22010-09-19 23:04:47 +00002290 register ContributionInfo
2291 *restrict contribution;
2292
cristy3ed852e2009-09-05 21:47:34 +00002293 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002294 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002295
cristy9af9b5d2010-08-15 17:04:28 +00002296 register PixelPacket
2297 *restrict q;
2298
cristybb503372010-05-27 20:51:26 +00002299 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002300 x;
2301
cristy9af9b5d2010-08-15 17:04:28 +00002302 ssize_t
2303 n,
2304 start,
2305 stop;
cristy3ed852e2009-09-05 21:47:34 +00002306
2307 if (status == MagickFalse)
2308 continue;
cristy679e6962010-03-18 00:42:45 +00002309 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002310 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2311 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002312 density=0.0;
2313 contribution=contributions[GetOpenMPThreadId()];
2314 for (n=0; n < (stop-start); n++)
2315 {
2316 contribution[n].pixel=start+n;
2317 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2318 ((MagickRealType) (start+n)-center+0.5));
2319 density+=contribution[n].weight;
2320 }
2321 if ((density != 0.0) && (density != 1.0))
2322 {
cristybb503372010-05-27 20:51:26 +00002323 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002324 i;
2325
2326 /*
2327 Normalize.
2328 */
2329 density=1.0/density;
2330 for (i=0; i < n; i++)
2331 contribution[i].weight*=density;
2332 }
2333 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002334 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2335 exception);
cristy3ed852e2009-09-05 21:47:34 +00002336 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2337 exception);
2338 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2339 {
2340 status=MagickFalse;
2341 continue;
2342 }
2343 indexes=GetCacheViewVirtualIndexQueue(image_view);
2344 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002345 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002346 {
cristy3ed852e2009-09-05 21:47:34 +00002347 MagickPixelPacket
2348 pixel;
2349
2350 MagickRealType
2351 alpha;
2352
cristybb503372010-05-27 20:51:26 +00002353 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002354 i;
2355
cristy9af9b5d2010-08-15 17:04:28 +00002356 ssize_t
2357 j;
2358
cristy3ed852e2009-09-05 21:47:34 +00002359 pixel=zero;
2360 if (image->matte == MagickFalse)
2361 {
2362 for (i=0; i < n; i++)
2363 {
cristybb503372010-05-27 20:51:26 +00002364 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002365 image->columns+x);
2366 alpha=contribution[i].weight;
2367 pixel.red+=alpha*(p+j)->red;
2368 pixel.green+=alpha*(p+j)->green;
2369 pixel.blue+=alpha*(p+j)->blue;
2370 pixel.opacity+=alpha*(p+j)->opacity;
2371 }
cristyce70c172010-01-07 17:15:30 +00002372 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2373 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2374 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2375 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002376 if ((image->colorspace == CMYKColorspace) &&
2377 (resize_image->colorspace == CMYKColorspace))
2378 {
2379 for (i=0; i < n; i++)
2380 {
cristybb503372010-05-27 20:51:26 +00002381 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002382 image->columns+x);
2383 alpha=contribution[i].weight;
2384 pixel.index+=alpha*indexes[j];
2385 }
cristyce70c172010-01-07 17:15:30 +00002386 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002387 }
2388 }
2389 else
2390 {
2391 MagickRealType
2392 gamma;
2393
2394 gamma=0.0;
2395 for (i=0; i < n; i++)
2396 {
cristybb503372010-05-27 20:51:26 +00002397 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002398 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002399 alpha=contribution[i].weight*QuantumScale*
2400 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002401 pixel.red+=alpha*(p+j)->red;
2402 pixel.green+=alpha*(p+j)->green;
2403 pixel.blue+=alpha*(p+j)->blue;
2404 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2405 gamma+=alpha;
2406 }
2407 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002408 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2409 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2410 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2411 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002412 if ((image->colorspace == CMYKColorspace) &&
2413 (resize_image->colorspace == CMYKColorspace))
2414 {
2415 for (i=0; i < n; i++)
2416 {
cristybb503372010-05-27 20:51:26 +00002417 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002418 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002419 alpha=contribution[i].weight*QuantumScale*
2420 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002421 pixel.index+=alpha*indexes[j];
2422 }
cristyce70c172010-01-07 17:15:30 +00002423 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2424 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002425 }
2426 }
2427 if ((resize_image->storage_class == PseudoClass) &&
2428 (image->storage_class == PseudoClass))
2429 {
cristybb503372010-05-27 20:51:26 +00002430 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002431 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002432 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002433 image->columns+x);
2434 resize_indexes[x]=indexes[j];
2435 }
2436 q++;
2437 }
2438 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2439 status=MagickFalse;
2440 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2441 {
2442 MagickBooleanType
2443 proceed;
2444
cristyb5d5f722009-11-04 03:03:49 +00002445#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002446 #pragma omp critical (MagickCore_VerticalFilter)
2447#endif
cristy9af9b5d2010-08-15 17:04:28 +00002448 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002449 if (proceed == MagickFalse)
2450 status=MagickFalse;
2451 }
2452 }
2453 resize_view=DestroyCacheView(resize_view);
2454 image_view=DestroyCacheView(image_view);
2455 contributions=DestroyContributionThreadSet(contributions);
2456 return(status);
2457}
2458
cristybb503372010-05-27 20:51:26 +00002459MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2460 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002461 ExceptionInfo *exception)
2462{
2463#define WorkLoadFactor 0.265
2464
2465 FilterTypes
2466 filter_type;
2467
2468 Image
2469 *filter_image,
2470 *resize_image;
2471
cristy9af9b5d2010-08-15 17:04:28 +00002472 MagickOffsetType
2473 offset;
2474
cristy3ed852e2009-09-05 21:47:34 +00002475 MagickRealType
2476 x_factor,
2477 y_factor;
2478
2479 MagickSizeType
2480 span;
2481
2482 MagickStatusType
2483 status;
2484
2485 ResizeFilter
2486 *resize_filter;
2487
cristy3ed852e2009-09-05 21:47:34 +00002488 /*
2489 Acquire resize image.
2490 */
2491 assert(image != (Image *) NULL);
2492 assert(image->signature == MagickSignature);
2493 if (image->debug != MagickFalse)
2494 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2495 assert(exception != (ExceptionInfo *) NULL);
2496 assert(exception->signature == MagickSignature);
2497 if ((columns == 0) || (rows == 0))
2498 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2499 if ((columns == image->columns) && (rows == image->rows) &&
2500 (filter == UndefinedFilter) && (blur == 1.0))
2501 return(CloneImage(image,0,0,MagickTrue,exception));
2502 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2503 if (resize_image == (Image *) NULL)
2504 return(resize_image);
2505 /*
2506 Acquire resize filter.
2507 */
2508 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2509 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2510 if ((x_factor*y_factor) > WorkLoadFactor)
2511 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2512 else
2513 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2514 if (filter_image == (Image *) NULL)
2515 return(DestroyImage(resize_image));
2516 filter_type=LanczosFilter;
2517 if (filter != UndefinedFilter)
2518 filter_type=filter;
2519 else
2520 if ((x_factor == 1.0) && (y_factor == 1.0))
2521 filter_type=PointFilter;
2522 else
2523 if ((image->storage_class == PseudoClass) ||
2524 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2525 filter_type=MitchellFilter;
2526 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2527 exception);
2528 /*
2529 Resize image.
2530 */
cristy9af9b5d2010-08-15 17:04:28 +00002531 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002532 if ((x_factor*y_factor) > WorkLoadFactor)
2533 {
2534 span=(MagickSizeType) (filter_image->columns+rows);
2535 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002536 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002537 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002538 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002539 }
2540 else
2541 {
2542 span=(MagickSizeType) (filter_image->rows+columns);
2543 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002544 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002545 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002546 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002547 }
2548 /*
2549 Free resources.
2550 */
2551 filter_image=DestroyImage(filter_image);
2552 resize_filter=DestroyResizeFilter(resize_filter);
2553 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2554 return((Image *) NULL);
2555 resize_image->type=image->type;
2556 return(resize_image);
2557}
2558
2559/*
2560%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2561% %
2562% %
2563% %
2564% S a m p l e I m a g e %
2565% %
2566% %
2567% %
2568%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2569%
2570% SampleImage() scales an image to the desired dimensions with pixel
2571% sampling. Unlike other scaling methods, this method does not introduce
2572% any additional color into the scaled image.
2573%
2574% The format of the SampleImage method is:
2575%
cristybb503372010-05-27 20:51:26 +00002576% Image *SampleImage(const Image *image,const size_t columns,
2577% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002578%
2579% A description of each parameter follows:
2580%
2581% o image: the image.
2582%
2583% o columns: the number of columns in the sampled image.
2584%
2585% o rows: the number of rows in the sampled image.
2586%
2587% o exception: return any errors or warnings in this structure.
2588%
2589*/
cristybb503372010-05-27 20:51:26 +00002590MagickExport Image *SampleImage(const Image *image,const size_t columns,
2591 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002592{
2593#define SampleImageTag "Sample/Image"
2594
cristyc4c8d132010-01-07 01:58:38 +00002595 CacheView
2596 *image_view,
2597 *sample_view;
2598
cristy3ed852e2009-09-05 21:47:34 +00002599 Image
2600 *sample_image;
2601
cristy3ed852e2009-09-05 21:47:34 +00002602 MagickBooleanType
2603 status;
2604
cristy5f959472010-05-27 22:19:46 +00002605 MagickOffsetType
2606 progress;
2607
cristybb503372010-05-27 20:51:26 +00002608 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002609 x;
2610
cristy5f959472010-05-27 22:19:46 +00002611 ssize_t
2612 *x_offset,
2613 y;
2614
cristy3ed852e2009-09-05 21:47:34 +00002615 /*
2616 Initialize sampled image attributes.
2617 */
2618 assert(image != (const Image *) NULL);
2619 assert(image->signature == MagickSignature);
2620 if (image->debug != MagickFalse)
2621 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2622 assert(exception != (ExceptionInfo *) NULL);
2623 assert(exception->signature == MagickSignature);
2624 if ((columns == 0) || (rows == 0))
2625 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2626 if ((columns == image->columns) && (rows == image->rows))
2627 return(CloneImage(image,0,0,MagickTrue,exception));
2628 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2629 if (sample_image == (Image *) NULL)
2630 return((Image *) NULL);
2631 /*
2632 Allocate scan line buffer and column offset buffers.
2633 */
cristybb503372010-05-27 20:51:26 +00002634 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002635 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002636 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002637 {
2638 sample_image=DestroyImage(sample_image);
2639 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2640 }
cristybb503372010-05-27 20:51:26 +00002641 for (x=0; x < (ssize_t) sample_image->columns; x++)
2642 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002643 sample_image->columns);
2644 /*
2645 Sample each row.
2646 */
2647 status=MagickTrue;
2648 progress=0;
2649 image_view=AcquireCacheView(image);
2650 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002651#if defined(MAGICKCORE_OPENMP_SUPPORT)
2652 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002653#endif
cristybb503372010-05-27 20:51:26 +00002654 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002655 {
cristy3ed852e2009-09-05 21:47:34 +00002656 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002657 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002658
2659 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002660 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002661
2662 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002663 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002664
cristy3ed852e2009-09-05 21:47:34 +00002665 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002666 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002667
cristy03dbbd22010-09-19 23:04:47 +00002668 register ssize_t
2669 x;
2670
cristy9af9b5d2010-08-15 17:04:28 +00002671 ssize_t
2672 y_offset;
2673
cristy3ed852e2009-09-05 21:47:34 +00002674 if (status == MagickFalse)
2675 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002676 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2677 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002678 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2679 exception);
2680 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2681 exception);
2682 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2683 {
2684 status=MagickFalse;
2685 continue;
2686 }
2687 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2688 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2689 /*
2690 Sample each column.
2691 */
cristybb503372010-05-27 20:51:26 +00002692 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002693 *q++=p[x_offset[x]];
2694 if ((image->storage_class == PseudoClass) ||
2695 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002696 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002697 sample_indexes[x]=indexes[x_offset[x]];
2698 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2699 status=MagickFalse;
2700 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2701 {
2702 MagickBooleanType
2703 proceed;
2704
cristyb5d5f722009-11-04 03:03:49 +00002705#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002706 #pragma omp critical (MagickCore_SampleImage)
2707#endif
2708 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2709 if (proceed == MagickFalse)
2710 status=MagickFalse;
2711 }
2712 }
2713 image_view=DestroyCacheView(image_view);
2714 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002715 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002716 sample_image->type=image->type;
2717 return(sample_image);
2718}
2719
2720/*
2721%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2722% %
2723% %
2724% %
2725% S c a l e I m a g e %
2726% %
2727% %
2728% %
2729%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2730%
2731% ScaleImage() changes the size of an image to the given dimensions.
2732%
2733% The format of the ScaleImage method is:
2734%
cristybb503372010-05-27 20:51:26 +00002735% Image *ScaleImage(const Image *image,const size_t columns,
2736% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002737%
2738% A description of each parameter follows:
2739%
2740% o image: the image.
2741%
2742% o columns: the number of columns in the scaled image.
2743%
2744% o rows: the number of rows in the scaled image.
2745%
2746% o exception: return any errors or warnings in this structure.
2747%
2748*/
cristybb503372010-05-27 20:51:26 +00002749MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2750 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002751{
2752#define ScaleImageTag "Scale/Image"
2753
cristyed6cb232010-01-20 03:07:53 +00002754 CacheView
2755 *image_view,
2756 *scale_view;
2757
cristy3ed852e2009-09-05 21:47:34 +00002758 Image
2759 *scale_image;
2760
cristy3ed852e2009-09-05 21:47:34 +00002761 MagickBooleanType
2762 next_column,
2763 next_row,
2764 proceed;
2765
2766 MagickPixelPacket
2767 pixel,
2768 *scale_scanline,
2769 *scanline,
2770 *x_vector,
2771 *y_vector,
2772 zero;
2773
cristy3ed852e2009-09-05 21:47:34 +00002774 PointInfo
2775 scale,
2776 span;
2777
cristybb503372010-05-27 20:51:26 +00002778 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002779 i;
2780
cristy9af9b5d2010-08-15 17:04:28 +00002781 ssize_t
2782 number_rows,
2783 y;
2784
cristy3ed852e2009-09-05 21:47:34 +00002785 /*
2786 Initialize scaled image attributes.
2787 */
2788 assert(image != (const Image *) NULL);
2789 assert(image->signature == MagickSignature);
2790 if (image->debug != MagickFalse)
2791 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2792 assert(exception != (ExceptionInfo *) NULL);
2793 assert(exception->signature == MagickSignature);
2794 if ((columns == 0) || (rows == 0))
2795 return((Image *) NULL);
2796 if ((columns == image->columns) && (rows == image->rows))
2797 return(CloneImage(image,0,0,MagickTrue,exception));
2798 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2799 if (scale_image == (Image *) NULL)
2800 return((Image *) NULL);
2801 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2802 {
2803 InheritException(exception,&scale_image->exception);
2804 scale_image=DestroyImage(scale_image);
2805 return((Image *) NULL);
2806 }
2807 /*
2808 Allocate memory.
2809 */
2810 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2811 sizeof(*x_vector));
2812 scanline=x_vector;
2813 if (image->rows != scale_image->rows)
2814 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2815 sizeof(*scanline));
2816 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2817 scale_image->columns,sizeof(*scale_scanline));
2818 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2819 sizeof(*y_vector));
2820 if ((scanline == (MagickPixelPacket *) NULL) ||
2821 (scale_scanline == (MagickPixelPacket *) NULL) ||
2822 (x_vector == (MagickPixelPacket *) NULL) ||
2823 (y_vector == (MagickPixelPacket *) NULL))
2824 {
2825 scale_image=DestroyImage(scale_image);
2826 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2827 }
2828 /*
2829 Scale image.
2830 */
2831 number_rows=0;
2832 next_row=MagickTrue;
2833 span.y=1.0;
2834 scale.y=(double) scale_image->rows/(double) image->rows;
2835 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2836 sizeof(*y_vector));
2837 GetMagickPixelPacket(image,&pixel);
2838 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2839 i=0;
cristyed6cb232010-01-20 03:07:53 +00002840 image_view=AcquireCacheView(image);
2841 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002842 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002843 {
2844 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002845 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002846
2847 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002848 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002849
2850 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002851 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002852
cristy3ed852e2009-09-05 21:47:34 +00002853 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002854 *restrict s,
2855 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002856
2857 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002858 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002859
cristy9af9b5d2010-08-15 17:04:28 +00002860 register ssize_t
2861 x;
2862
cristyed6cb232010-01-20 03:07:53 +00002863 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2864 exception);
cristy3ed852e2009-09-05 21:47:34 +00002865 if (q == (PixelPacket *) NULL)
2866 break;
2867 scale_indexes=GetAuthenticIndexQueue(scale_image);
2868 if (scale_image->rows == image->rows)
2869 {
2870 /*
2871 Read a new scanline.
2872 */
cristyed6cb232010-01-20 03:07:53 +00002873 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2874 exception);
cristy3ed852e2009-09-05 21:47:34 +00002875 if (p == (const PixelPacket *) NULL)
2876 break;
cristyed6cb232010-01-20 03:07:53 +00002877 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002878 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002879 {
cristyce70c172010-01-07 17:15:30 +00002880 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2881 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2882 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002883 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002884 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002885 if (indexes != (IndexPacket *) NULL)
2886 x_vector[x].index=(MagickRealType) indexes[x];
2887 p++;
2888 }
2889 }
2890 else
2891 {
2892 /*
2893 Scale Y direction.
2894 */
2895 while (scale.y < span.y)
2896 {
cristy9af9b5d2010-08-15 17:04:28 +00002897 if ((next_row != MagickFalse) &&
2898 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002899 {
2900 /*
2901 Read a new scanline.
2902 */
cristyed6cb232010-01-20 03:07:53 +00002903 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2904 exception);
cristy3ed852e2009-09-05 21:47:34 +00002905 if (p == (const PixelPacket *) NULL)
2906 break;
cristyed6cb232010-01-20 03:07:53 +00002907 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002908 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002909 {
cristyce70c172010-01-07 17:15:30 +00002910 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2911 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2912 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002913 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002914 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002915 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002916 if (indexes != (IndexPacket *) NULL)
2917 x_vector[x].index=(MagickRealType) indexes[x];
2918 p++;
2919 }
2920 number_rows++;
2921 }
cristybb503372010-05-27 20:51:26 +00002922 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002923 {
2924 y_vector[x].red+=scale.y*x_vector[x].red;
2925 y_vector[x].green+=scale.y*x_vector[x].green;
2926 y_vector[x].blue+=scale.y*x_vector[x].blue;
2927 if (scale_image->matte != MagickFalse)
2928 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2929 if (scale_indexes != (IndexPacket *) NULL)
2930 y_vector[x].index+=scale.y*x_vector[x].index;
2931 }
2932 span.y-=scale.y;
2933 scale.y=(double) scale_image->rows/(double) image->rows;
2934 next_row=MagickTrue;
2935 }
cristybb503372010-05-27 20:51:26 +00002936 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002937 {
2938 /*
2939 Read a new scanline.
2940 */
cristyed6cb232010-01-20 03:07:53 +00002941 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2942 exception);
cristy3ed852e2009-09-05 21:47:34 +00002943 if (p == (const PixelPacket *) NULL)
2944 break;
cristyed6cb232010-01-20 03:07:53 +00002945 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002946 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002947 {
cristyce70c172010-01-07 17:15:30 +00002948 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2949 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2950 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002951 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00002952 x_vector[x].opacity=(MagickRealType)
2953 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002954 if (indexes != (IndexPacket *) NULL)
2955 x_vector[x].index=(MagickRealType) indexes[x];
2956 p++;
2957 }
2958 number_rows++;
2959 next_row=MagickFalse;
2960 }
2961 s=scanline;
cristybb503372010-05-27 20:51:26 +00002962 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002963 {
2964 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
2965 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
2966 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
2967 if (image->matte != MagickFalse)
2968 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
2969 if (scale_indexes != (IndexPacket *) NULL)
2970 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
2971 s->red=pixel.red;
2972 s->green=pixel.green;
2973 s->blue=pixel.blue;
2974 if (scale_image->matte != MagickFalse)
2975 s->opacity=pixel.opacity;
2976 if (scale_indexes != (IndexPacket *) NULL)
2977 s->index=pixel.index;
2978 s++;
2979 y_vector[x]=zero;
2980 }
2981 scale.y-=span.y;
2982 if (scale.y <= 0)
2983 {
2984 scale.y=(double) scale_image->rows/(double) image->rows;
2985 next_row=MagickTrue;
2986 }
2987 span.y=1.0;
2988 }
2989 if (scale_image->columns == image->columns)
2990 {
2991 /*
2992 Transfer scanline to scaled image.
2993 */
2994 s=scanline;
cristybb503372010-05-27 20:51:26 +00002995 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002996 {
cristyce70c172010-01-07 17:15:30 +00002997 q->red=ClampToQuantum(s->red);
2998 q->green=ClampToQuantum(s->green);
2999 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003000 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003001 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003002 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003003 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003004 q++;
3005 s++;
3006 }
3007 }
3008 else
3009 {
3010 /*
3011 Scale X direction.
3012 */
3013 pixel=zero;
3014 next_column=MagickFalse;
3015 span.x=1.0;
3016 s=scanline;
3017 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003018 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003019 {
3020 scale.x=(double) scale_image->columns/(double) image->columns;
3021 while (scale.x >= span.x)
3022 {
3023 if (next_column != MagickFalse)
3024 {
3025 pixel=zero;
3026 t++;
3027 }
3028 pixel.red+=span.x*s->red;
3029 pixel.green+=span.x*s->green;
3030 pixel.blue+=span.x*s->blue;
3031 if (image->matte != MagickFalse)
3032 pixel.opacity+=span.x*s->opacity;
3033 if (scale_indexes != (IndexPacket *) NULL)
3034 pixel.index+=span.x*s->index;
3035 t->red=pixel.red;
3036 t->green=pixel.green;
3037 t->blue=pixel.blue;
3038 if (scale_image->matte != MagickFalse)
3039 t->opacity=pixel.opacity;
3040 if (scale_indexes != (IndexPacket *) NULL)
3041 t->index=pixel.index;
3042 scale.x-=span.x;
3043 span.x=1.0;
3044 next_column=MagickTrue;
3045 }
3046 if (scale.x > 0)
3047 {
3048 if (next_column != MagickFalse)
3049 {
3050 pixel=zero;
3051 next_column=MagickFalse;
3052 t++;
3053 }
3054 pixel.red+=scale.x*s->red;
3055 pixel.green+=scale.x*s->green;
3056 pixel.blue+=scale.x*s->blue;
3057 if (scale_image->matte != MagickFalse)
3058 pixel.opacity+=scale.x*s->opacity;
3059 if (scale_indexes != (IndexPacket *) NULL)
3060 pixel.index+=scale.x*s->index;
3061 span.x-=scale.x;
3062 }
3063 s++;
3064 }
3065 if (span.x > 0)
3066 {
3067 s--;
3068 pixel.red+=span.x*s->red;
3069 pixel.green+=span.x*s->green;
3070 pixel.blue+=span.x*s->blue;
3071 if (scale_image->matte != MagickFalse)
3072 pixel.opacity+=span.x*s->opacity;
3073 if (scale_indexes != (IndexPacket *) NULL)
3074 pixel.index+=span.x*s->index;
3075 }
3076 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003077 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003078 {
3079 t->red=pixel.red;
3080 t->green=pixel.green;
3081 t->blue=pixel.blue;
3082 if (scale_image->matte != MagickFalse)
3083 t->opacity=pixel.opacity;
3084 if (scale_indexes != (IndexPacket *) NULL)
3085 t->index=pixel.index;
3086 }
3087 /*
3088 Transfer scanline to scaled image.
3089 */
3090 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003091 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003092 {
cristyce70c172010-01-07 17:15:30 +00003093 q->red=ClampToQuantum(t->red);
3094 q->green=ClampToQuantum(t->green);
3095 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003096 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003097 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003098 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003099 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003100 t++;
3101 q++;
3102 }
3103 }
cristyed6cb232010-01-20 03:07:53 +00003104 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003105 break;
cristy96b16132010-08-29 17:19:52 +00003106 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3107 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003108 if (proceed == MagickFalse)
3109 break;
3110 }
cristyed6cb232010-01-20 03:07:53 +00003111 scale_view=DestroyCacheView(scale_view);
3112 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003113 /*
3114 Free allocated memory.
3115 */
3116 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3117 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3118 if (scale_image->rows != image->rows)
3119 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3120 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3121 scale_image->type=image->type;
3122 return(scale_image);
3123}
3124
anthony02b4cb42010-10-10 04:54:35 +00003125#if 0
3126 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003127/*
3128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3129% %
3130% %
3131% %
3132+ S e t R e s i z e F i l t e r S u p p o r t %
3133% %
3134% %
3135% %
3136%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3137%
3138% SetResizeFilterSupport() specifies which IR filter to use to window
3139%
3140% The format of the SetResizeFilterSupport method is:
3141%
3142% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3143% const MagickRealType support)
3144%
3145% A description of each parameter follows:
3146%
3147% o resize_filter: the resize filter.
3148%
3149% o support: the filter spport radius.
3150%
3151*/
3152MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3153 const MagickRealType support)
3154{
3155 assert(resize_filter != (ResizeFilter *) NULL);
3156 assert(resize_filter->signature == MagickSignature);
3157 resize_filter->support=support;
3158}
anthony02b4cb42010-10-10 04:54:35 +00003159#endif
cristy3ed852e2009-09-05 21:47:34 +00003160
3161/*
3162%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3163% %
3164% %
3165% %
3166% T h u m b n a i l I m a g e %
3167% %
3168% %
3169% %
3170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3171%
3172% ThumbnailImage() changes the size of an image to the given dimensions and
3173% removes any associated profiles. The goal is to produce small low cost
3174% thumbnail images suited for display on the Web.
3175%
3176% The format of the ThumbnailImage method is:
3177%
cristybb503372010-05-27 20:51:26 +00003178% Image *ThumbnailImage(const Image *image,const size_t columns,
3179% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003180%
3181% A description of each parameter follows:
3182%
3183% o image: the image.
3184%
3185% o columns: the number of columns in the scaled image.
3186%
3187% o rows: the number of rows in the scaled image.
3188%
3189% o exception: return any errors or warnings in this structure.
3190%
3191*/
cristy9af9b5d2010-08-15 17:04:28 +00003192MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3193 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003194{
3195#define SampleFactor 5
3196
3197 char
3198 value[MaxTextExtent];
3199
3200 const char
3201 *name;
3202
3203 Image
3204 *thumbnail_image;
3205
3206 MagickRealType
3207 x_factor,
3208 y_factor;
3209
cristybb503372010-05-27 20:51:26 +00003210 size_t
cristy3ed852e2009-09-05 21:47:34 +00003211 version;
3212
cristy9af9b5d2010-08-15 17:04:28 +00003213 struct stat
3214 attributes;
3215
cristy3ed852e2009-09-05 21:47:34 +00003216 assert(image != (Image *) NULL);
3217 assert(image->signature == MagickSignature);
3218 if (image->debug != MagickFalse)
3219 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3220 assert(exception != (ExceptionInfo *) NULL);
3221 assert(exception->signature == MagickSignature);
3222 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3223 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3224 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003225 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3226 exception);
cristy3ed852e2009-09-05 21:47:34 +00003227 else
3228 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003229 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3230 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003231 else
3232 {
3233 Image
3234 *sample_image;
3235
3236 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3237 exception);
3238 if (sample_image == (Image *) NULL)
3239 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003240 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3241 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003242 sample_image=DestroyImage(sample_image);
3243 }
3244 if (thumbnail_image == (Image *) NULL)
3245 return(thumbnail_image);
3246 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3247 if (thumbnail_image->matte == MagickFalse)
3248 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3249 thumbnail_image->depth=8;
3250 thumbnail_image->interlace=NoInterlace;
3251 /*
3252 Strip all profiles except color profiles.
3253 */
3254 ResetImageProfileIterator(thumbnail_image);
3255 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3256 {
3257 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3258 {
cristy2b726bd2010-01-11 01:05:39 +00003259 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003260 ResetImageProfileIterator(thumbnail_image);
3261 }
3262 name=GetNextImageProfile(thumbnail_image);
3263 }
3264 (void) DeleteImageProperty(thumbnail_image,"comment");
3265 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003266 if (strstr(image->magick_filename,"//") == (char *) NULL)
3267 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003268 image->magick_filename);
3269 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3270 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3271 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3272 {
cristye8c25f92010-06-03 00:53:06 +00003273 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003274 attributes.st_mtime);
3275 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3276 }
cristye8c25f92010-06-03 00:53:06 +00003277 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003278 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003279 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003280 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003281 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3282 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3283 LocaleLower(value);
3284 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3285 (void) SetImageProperty(thumbnail_image,"software",
3286 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003287 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3288 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003289 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003290 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003291 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003292 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003293 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3294 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003295 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3296 return(thumbnail_image);
3297}