blob: 898fe7dc2c2e64c53aeaee6086c41b829584fdbd [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% RRRR EEEEE SSSSS IIIII ZZZZZ EEEEE %
7% R R E SS I ZZ E %
8% RRRR EEE SSS I ZZZ EEE %
9% R R E SS I ZZ E %
10% R R EEEEE SSSSS IIIII ZZZZZ EEEEE %
11% %
12% %
13% MagickCore Image Resize Methods %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
42#include "magick/studio.h"
43#include "magick/artifact.h"
44#include "magick/blob.h"
45#include "magick/cache.h"
46#include "magick/cache-view.h"
47#include "magick/color.h"
48#include "magick/color-private.h"
49#include "magick/draw.h"
50#include "magick/exception.h"
51#include "magick/exception-private.h"
52#include "magick/gem.h"
53#include "magick/image.h"
54#include "magick/image-private.h"
55#include "magick/list.h"
56#include "magick/memory_.h"
anthony55f12332010-09-10 01:13:02 +000057#include "magick/magick.h"
cristy3ed852e2009-09-05 21:47:34 +000058#include "magick/pixel-private.h"
59#include "magick/property.h"
60#include "magick/monitor.h"
61#include "magick/monitor-private.h"
62#include "magick/pixel.h"
63#include "magick/option.h"
64#include "magick/resample.h"
65#include "magick/resize.h"
66#include "magick/resize-private.h"
67#include "magick/string_.h"
cristyf2f27272009-12-17 14:48:46 +000068#include "magick/string-private.h"
cristy3ed852e2009-09-05 21:47:34 +000069#include "magick/thread-private.h"
70#include "magick/utility.h"
71#include "magick/version.h"
72#if defined(MAGICKCORE_LQR_DELEGATE)
73#include <lqr.h>
74#endif
75
76/*
77 Typedef declarations.
78*/
79struct _ResizeFilter
80{
81 MagickRealType
82 (*filter)(const MagickRealType,const ResizeFilter *),
83 (*window)(const MagickRealType,const ResizeFilter *),
cristy33b1c162010-01-23 22:51:51 +000084 support, /* filter region of support - the filter support limit */
85 window_support, /* window support, usally equal to support (expert only) */
anthony55f12332010-09-10 01:13:02 +000086 scale, /* dimension scaling to fit window support (usally 1.0) */
cristy33b1c162010-01-23 22:51:51 +000087 blur, /* x-scale (blur-sharpen) */
anthonyf5e76ef2010-10-12 01:22:01 +000088 coeff[8]; /* cubic coefficents for smooth Cubic filters */
cristy3ed852e2009-09-05 21:47:34 +000089
cristybb503372010-05-27 20:51:26 +000090 size_t
cristy3ed852e2009-09-05 21:47:34 +000091 signature;
92};
93
94/*
95 Forward declaractions.
96*/
97static MagickRealType
98 I0(MagickRealType x),
anthonyb6d08c52010-09-13 01:17:04 +000099 BesselOrderOne(MagickRealType),
anthony07a3f7f2010-09-16 03:03:11 +0000100 Sinc(const MagickRealType, const ResizeFilter *),
anthonyba5a7c32010-09-15 02:42:25 +0000101 SincFast(const MagickRealType, const ResizeFilter *);
cristy3ed852e2009-09-05 21:47:34 +0000102
103/*
104%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
105% %
106% %
107% %
108+ F i l t e r F u n c t i o n s %
109% %
110% %
111% %
112%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
113%
cristy33b1c162010-01-23 22:51:51 +0000114% These are the various filter and windowing functions that are provided.
cristy3ed852e2009-09-05 21:47:34 +0000115%
cristy33b1c162010-01-23 22:51:51 +0000116% They are internal to this module only. See AcquireResizeFilterInfo() for
117% details of the access to these functions, via the GetResizeFilterSupport()
118% and GetResizeFilterWeight() API interface.
cristy3ed852e2009-09-05 21:47:34 +0000119%
120% The individual filter functions have this format...
121%
122% static MagickRealtype *FilterName(const MagickRealType x,
123% const MagickRealType support)
124%
cristy33b1c162010-01-23 22:51:51 +0000125% A description of each parameter follows:
cristy3ed852e2009-09-05 21:47:34 +0000126%
cristy33b1c162010-01-23 22:51:51 +0000127% o x: the distance from the sampling point generally in the range of 0 to
128% support. The GetResizeFilterWeight() ensures this a positive value.
129%
130% o resize_filter: current filter information. This allows function to
131% access support, and possibly other pre-calculated information defining
132% the functions.
cristy3ed852e2009-09-05 21:47:34 +0000133%
134*/
135
cristyc5c6f662010-09-22 14:23:02 +0000136#define MagickPIL ((MagickRealType) 3.14159265358979323846264338327950288420L)
cristy560d8182010-09-08 22:36:25 +0000137
anthony48f77622010-10-03 14:32:31 +0000138static MagickRealType Jinc(const MagickRealType x,
cristy3ed852e2009-09-05 21:47:34 +0000139 const ResizeFilter *magick_unused(resize_filter))
140{
141 /*
anthony48f77622010-10-03 14:32:31 +0000142 See Pratt "Digital Image Processing" p.97 for Jinc/Bessel functions.
cristy33b1c162010-01-23 22:51:51 +0000143 http://mathworld.wolfram.com/JincFunction.html and page 11 of
anthony48f77622010-10-03 14:32:31 +0000144 http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
145
nicolase473f722010-10-07 00:05:13 +0000146 The original "zoom" program by Paul Heckbert called this "Bessel".
147 But really it is more accurately named "Jinc".
cristy3ed852e2009-09-05 21:47:34 +0000148 */
149 if (x == 0.0)
nicolas5a36f342010-10-07 00:11:32 +0000150 return(0.5*MagickPIL);
151 return(BesselOrderOne(MagickPIL*x)/x);
cristy3ed852e2009-09-05 21:47:34 +0000152}
153
154static MagickRealType Blackman(const MagickRealType x,
cristyce70c172010-01-07 17:15:30 +0000155 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000156{
157 /*
cristy83017922010-09-05 20:45:15 +0000158 Blackman: 2nd order cosine windowing function:
cristy21ce88a2010-09-05 01:37:25 +0000159 0.42 + 0.5 cos(pi x) + 0.08 cos(2pi x)
cristy560d8182010-09-08 22:36:25 +0000160 Refactored by Chantal Racette and Nicolas Robidoux to one trig
161 call and five flops.
cristy3ed852e2009-09-05 21:47:34 +0000162 */
cristyc5c6f662010-09-22 14:23:02 +0000163 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000164 return(0.34+cospix*(0.5+cospix*0.16));
cristy3ed852e2009-09-05 21:47:34 +0000165}
166
167static MagickRealType Bohman(const MagickRealType x,
cristyce70c172010-01-07 17:15:30 +0000168 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000169{
170 /*
cristy560d8182010-09-08 22:36:25 +0000171 Bohman: 2rd Order cosine windowing function:
172 (1-x) cos(pi x) + sin(pi x) / pi.
nicolasa4f7b4a2010-09-20 20:28:38 +0000173 Refactored by Nicolas Robidoux to one trig call, one sqrt call,
174 and 7 flops, taking advantage of the fact that the support of
175 Bohman is 1 (so that we know that sin(pi x) >= 0).
cristy3ed852e2009-09-05 21:47:34 +0000176 */
cristyc5c6f662010-09-22 14:23:02 +0000177 const double cospix = cos((double) (MagickPIL*x));
nicolasa4f7b4a2010-09-20 20:28:38 +0000178 const double sinpix = sqrt(1.0-cospix*cospix);
nicolas2ffd3b22010-09-24 20:27:31 +0000179 return((1.0-x)*cospix+(1.0/MagickPIL)*sinpix);
cristy3ed852e2009-09-05 21:47:34 +0000180}
181
anthony463be1d2010-09-26 01:07:36 +0000182static MagickRealType Box(const MagickRealType magick_unused(x),
183 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000184{
185 /*
nicolas40477452010-09-27 23:42:08 +0000186 A Box filter is a equal weighting function (all weights equal).
anthonyc331dec2010-09-26 01:30:14 +0000187 DO NOT LIMIT results by support or resize point sampling will work
188 as it requests points beyond its normal 0.0 support size.
cristy3ed852e2009-09-05 21:47:34 +0000189 */
anthony463be1d2010-09-26 01:07:36 +0000190 return(1.0);
cristy3ed852e2009-09-05 21:47:34 +0000191}
192
193static MagickRealType CubicBC(const MagickRealType x,
194 const ResizeFilter *resize_filter)
195{
196 /*
197 Cubic Filters using B,C determined values:
cristyf59a8922010-02-28 19:51:23 +0000198 Mitchell-Netravali B=1/3 C=1/3 Qualitively ideal Cubic Filter
199 Catmull-Rom B= 0 C=1/2 Cublic Interpolation Function
200 Cubic B-Spline B= 1 C= 0 Spline Approximation of Gaussian
201 Hermite B= 0 C= 0 Quadratic Spline (support = 1)
cristy3ed852e2009-09-05 21:47:34 +0000202
cristy33b1c162010-01-23 22:51:51 +0000203 See paper by Mitchell and Netravali, Reconstruction Filters in Computer
204 Graphics Computer Graphics, Volume 22, Number 4, August 1988
205 http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/
cristyf59a8922010-02-28 19:51:23 +0000206 Mitchell.pdf.
cristy3ed852e2009-09-05 21:47:34 +0000207
cristy33b1c162010-01-23 22:51:51 +0000208 Coefficents are determined from B,C values:
cristy3ed852e2009-09-05 21:47:34 +0000209 P0 = ( 6 - 2*B )/6
210 P1 = 0
211 P2 = (-18 +12*B + 6*C )/6
212 P3 = ( 12 - 9*B - 6*C )/6
213 Q0 = ( 8*B +24*C )/6
214 Q1 = ( -12*B -48*C )/6
215 Q2 = ( 6*B +30*C )/6
216 Q3 = ( - 1*B - 6*C )/6
217
cristy33b1c162010-01-23 22:51:51 +0000218 which are used to define the filter:
cristyf59a8922010-02-28 19:51:23 +0000219
cristy3ed852e2009-09-05 21:47:34 +0000220 P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
221 Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x <= 2
222
cristy33b1c162010-01-23 22:51:51 +0000223 which ensures function is continuous in value and derivative (slope).
cristy3ed852e2009-09-05 21:47:34 +0000224 */
225 if (x < 1.0)
anthonyf5e76ef2010-10-12 01:22:01 +0000226 return(resize_filter->coeff[0]+x*(resize_filter->coeff[1]+x*
227 (resize_filter->coeff[2]+x*resize_filter->coeff[3])));
cristy3ed852e2009-09-05 21:47:34 +0000228 if (x < 2.0)
anthonyf5e76ef2010-10-12 01:22:01 +0000229 return(resize_filter->coeff[4]+x*(resize_filter->coeff[5]+x*
230 (resize_filter->coeff[6]+x*resize_filter->coeff[7])));
cristy3ed852e2009-09-05 21:47:34 +0000231 return(0.0);
232}
233
234static MagickRealType Gaussian(const MagickRealType x,
anthonyf5e76ef2010-10-12 01:22:01 +0000235 const ResizeFilter *resize_filter)
cristy3ed852e2009-09-05 21:47:34 +0000236{
cristy560d8182010-09-08 22:36:25 +0000237 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000238 Gaussian with a fixed sigma = 1/2
239
240 Gaussian Formula...
241 exp( -(x^2)/((2.0*sigma^2) ) / sqrt(2*PI*sigma^2)))
242 The constants are pre-calculated...
243 exp( -coeff[0]*(x^2)) ) * coeff[1]
244 However the multiplier coefficent is not needed and not used.
245
246 This separates the gaussian 'sigma' value from the 'blur/support' settings
247 allows for its use in special 'small sigma' gaussians, without the filter
248 'missing' pixels when blur and thus support becomes too small.
cristy560d8182010-09-08 22:36:25 +0000249 */
anthonyf5e76ef2010-10-12 01:22:01 +0000250 return(exp((double)(-resize_filter->coeff[0]*x*x))); }
cristy3ed852e2009-09-05 21:47:34 +0000251
252static MagickRealType Hanning(const MagickRealType x,
253 const ResizeFilter *magick_unused(resize_filter))
254{
255 /*
nicolas40477452010-09-27 23:42:08 +0000256 Cosine window function:
257 .5+.5cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000258 */
cristyc5c6f662010-09-22 14:23:02 +0000259 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000260 return(0.5+0.5*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000261}
262
263static MagickRealType Hamming(const MagickRealType x,
264 const ResizeFilter *magick_unused(resize_filter))
265{
266 /*
nicolas40477452010-09-27 23:42:08 +0000267 Offset cosine window function:
268 .54 + .46 cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000269 */
cristyc5c6f662010-09-22 14:23:02 +0000270 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000271 return(0.54+0.46*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000272}
273
274static MagickRealType Kaiser(const MagickRealType x,
275 const ResizeFilter *magick_unused(resize_filter))
276{
277#define Alpha 6.5
278#define I0A (1.0/I0(Alpha))
279
280 /*
nicolas07bac812010-09-19 18:47:02 +0000281 Kaiser Windowing Function (bessel windowing): Alpha is a free
282 value from 5 to 8 (currently hardcoded to 6.5).
283 Future: make alpha the IOA pre-calculation, an 'expert' setting.
cristy3ed852e2009-09-05 21:47:34 +0000284 */
285 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
286}
287
288static MagickRealType Lagrange(const MagickRealType x,
289 const ResizeFilter *resize_filter)
290{
cristy3ed852e2009-09-05 21:47:34 +0000291 MagickRealType
292 value;
293
cristybb503372010-05-27 20:51:26 +0000294 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000295 i;
296
cristy9af9b5d2010-08-15 17:04:28 +0000297 ssize_t
298 n,
299 order;
300
cristy3ed852e2009-09-05 21:47:34 +0000301 /*
nicolas07bac812010-09-19 18:47:02 +0000302 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the
303 lagrange function and depends on the overall support window size
304 of the filter. That is: for a support of 2, it gives a lagrange-4
305 (piecewise cubic function).
cristy3ed852e2009-09-05 21:47:34 +0000306
nicolas07bac812010-09-19 18:47:02 +0000307 "n" identifies the piece of the piecewise polynomial.
cristy3ed852e2009-09-05 21:47:34 +0000308
nicolas07bac812010-09-19 18:47:02 +0000309 See Survey: Interpolation Methods, IEEE Transactions on Medical
310 Imaging, Vol 18, No 11, November 1999, p1049-1075, -- Equation 27
311 on p1064.
cristy3ed852e2009-09-05 21:47:34 +0000312 */
313 if (x > resize_filter->support)
314 return(0.0);
cristybb503372010-05-27 20:51:26 +0000315 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
anthonyc2d07db2010-09-15 23:47:40 +0000316 /*n=(ssize_t)((1.0*order)/2.0+x); -- which piece does x belong to */
317 n = (ssize_t)(resize_filter->window_support + x);
cristy3ed852e2009-09-05 21:47:34 +0000318 value=1.0f;
319 for (i=0; i < order; i++)
320 if (i != n)
321 value*=(n-i-x)/(n-i);
322 return(value);
323}
324
325static MagickRealType Quadratic(const MagickRealType x,
326 const ResizeFilter *magick_unused(resize_filter))
327{
328 /*
329 2rd order (quadratic) B-Spline approximation of Gaussian.
330 */
331 if (x < 0.5)
332 return(0.75-x*x);
333 if (x < 1.5)
334 return(0.5*(x-1.5)*(x-1.5));
335 return(0.0);
336}
337
anthony07a3f7f2010-09-16 03:03:11 +0000338static MagickRealType Sinc(const MagickRealType x,
cristy3ed852e2009-09-05 21:47:34 +0000339 const ResizeFilter *magick_unused(resize_filter))
340{
anthony720660f2010-09-07 10:05:14 +0000341 /*
nicolas40477452010-09-27 23:42:08 +0000342 Scaled sinc(x) function using a trig call:
nicolas07bac812010-09-19 18:47:02 +0000343 sinc(x) == sin(pi x)/(pi x).
anthony720660f2010-09-07 10:05:14 +0000344 */
anthony2d9b8b52010-09-14 08:31:07 +0000345 if (x != 0.0)
cristy560d8182010-09-08 22:36:25 +0000346 {
cristyc5c6f662010-09-22 14:23:02 +0000347 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
nicolas07bac812010-09-19 18:47:02 +0000348 return(sin((double) pix)/pix);
cristy560d8182010-09-08 22:36:25 +0000349 }
nicolas2ffd3b22010-09-24 20:27:31 +0000350 return((MagickRealType) 1.0);
anthony720660f2010-09-07 10:05:14 +0000351}
352
anthonyba5a7c32010-09-15 02:42:25 +0000353static MagickRealType SincFast(const MagickRealType x,
anthony720660f2010-09-07 10:05:14 +0000354 const ResizeFilter *magick_unused(resize_filter))
355{
cristy560d8182010-09-08 22:36:25 +0000356 /*
357 Approximations of the sinc function sin(pi x)/(pi x) over the
358 interval [-4,4] constructed by Nicolas Robidoux and Chantal
359 Racette with funding from the Natural Sciences and Engineering
360 Research Council of Canada.
nicolas07bac812010-09-19 18:47:02 +0000361
362 Although the approximations are polynomials (for low order of
363 approximation) and quotients of polynomials (for higher order of
364 approximation) and consequently are similar in form to Taylor
365 polynomials/Pade approximants, the approximations are computed
366 with a completely different technique.
367
368 Summary: These approximations are "the best" in terms of bang
369 (accuracy) for the buck (flops). More specifically: Among the
370 polynomial quotients that can be computed using a fixed number of
371 flops (with a given "+ - * / budget"), the chosen polynomial
372 quotient is the one closest to the approximated function with
373 respect to maximum absolute relative error over the given
374 interval.
375
376 The Remez algorithm, as implemented in the boost library's minimax
nicolas3aab40c2010-09-19 21:14:15 +0000377 package, is the key to the construction:
nicolas07bac812010-09-19 18:47:02 +0000378 http://www.boost.org/doc/libs/1_36_0/libs/math/doc/...
379 ...sf_and_dist/html/math_toolkit/backgrounders/remez.html
cristy560d8182010-09-08 22:36:25 +0000380 */
nicolas3aab40c2010-09-19 21:14:15 +0000381 /*
382 If outside of the interval of approximation, use the standard trig
383 formula.
384 */
anthony2d9b8b52010-09-14 08:31:07 +0000385 if (x > 4.0)
cristy03dbbd22010-09-19 23:04:47 +0000386 {
cristyc5c6f662010-09-22 14:23:02 +0000387 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
cristy03dbbd22010-09-19 23:04:47 +0000388 return(sin((double) pix)/pix);
389 }
anthony2d9b8b52010-09-14 08:31:07 +0000390 {
nicolas07bac812010-09-19 18:47:02 +0000391 /*
392 The approximations only depend on x^2 (sinc is an even
393 function).
394 */
395 const MagickRealType xx = x*x;
cristy83017922010-09-05 20:45:15 +0000396#if MAGICKCORE_QUANTUM_DEPTH <= 8
397 /*
anthony2d9b8b52010-09-14 08:31:07 +0000398 Maximum absolute relative error 6.3e-6 < 1/2^17.
cristy738e7562010-09-01 12:48:07 +0000399 */
400 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
401 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
402 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
403 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
404 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
405 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
406 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
407 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
nicolas3aab40c2010-09-19 21:14:15 +0000408 const MagickRealType p =
409 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
nicolas07bac812010-09-19 18:47:02 +0000410 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
anthony2d9b8b52010-09-14 08:31:07 +0000411#elif MAGICKCORE_QUANTUM_DEPTH <= 16
cristydbeb3eb2010-09-09 13:41:36 +0000412 /*
anthony2d9b8b52010-09-14 08:31:07 +0000413 Max. abs. rel. error 2.2e-8 < 1/2^25.
cristydbeb3eb2010-09-09 13:41:36 +0000414 */
415 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
416 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
nicolas3aab40c2010-09-19 21:14:15 +0000417 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
418 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
419 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
420 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
421 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
cristydbeb3eb2010-09-09 13:41:36 +0000422 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
423 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
424 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
anthony853d6972010-10-08 06:01:31 +0000425 const MagickRealType p =
nicolas3aab40c2010-09-19 21:14:15 +0000426 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 +0000427 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
nicolasc2d28f02010-09-27 18:56:15 +0000428#else
nicolas3aab40c2010-09-19 21:14:15 +0000429 /*
430 Max. abs. rel. error 1.2e-12 < 1/2^39.
431 */
432 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
433 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
434 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
435 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
436 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
437 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
438 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
439 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
440 const MagickRealType p =
441 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
442 const MagickRealType d0 = 1.0L;
443 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
444 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
445 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
446 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
447 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
448 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
cristy657c6352010-08-29 14:05:08 +0000449#endif
cristy83017922010-09-05 20:45:15 +0000450 }
cristy3ed852e2009-09-05 21:47:34 +0000451}
452
453static MagickRealType Triangle(const MagickRealType x,
454 const ResizeFilter *magick_unused(resize_filter))
455{
456 /*
nicolas0edb0862010-09-19 18:56:19 +0000457 1st order (linear) B-Spline, bilinear interpolation, Tent 1D
458 filter, or a Bartlett 2D Cone filter.
cristy3ed852e2009-09-05 21:47:34 +0000459 */
460 if (x < 1.0)
461 return(1.0-x);
462 return(0.0);
463}
464
465static MagickRealType Welsh(const MagickRealType x,
466 const ResizeFilter *magick_unused(resize_filter))
467{
468 /*
469 Welsh parabolic windowing filter.
470 */
cristy560d8182010-09-08 22:36:25 +0000471 if (x < 1.0)
cristy3ed852e2009-09-05 21:47:34 +0000472 return(1.0-x*x);
473 return(0.0);
474}
475
476/*
477%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
478% %
479% %
480% %
481+ A c q u i r e R e s i z e F i l t e r %
482% %
483% %
484% %
485%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
486%
nicolas07bac812010-09-19 18:47:02 +0000487% AcquireResizeFilter() allocates the ResizeFilter structure. Choose
488% from these filters:
cristy3ed852e2009-09-05 21:47:34 +0000489%
490% FIR (Finite impulse Response) Filters
491% Box Triangle Quadratic
492% Cubic Hermite Catrom
493% Mitchell
494%
495% IIR (Infinite impulse Response) Filters
anthony48f77622010-10-03 14:32:31 +0000496% Gaussian Sinc Jinc (Bessel)
cristy3ed852e2009-09-05 21:47:34 +0000497%
anthony48f77622010-10-03 14:32:31 +0000498% Windowed Sinc/Jinc Filters
cristy3ed852e2009-09-05 21:47:34 +0000499% Blackman Hanning Hamming
anthony48f77622010-10-03 14:32:31 +0000500% Kaiser Lanczos
cristy3ed852e2009-09-05 21:47:34 +0000501%
anthony61b5ddd2010-10-05 02:33:31 +0000502% Special purpose Filters
anthony853d6972010-10-08 06:01:31 +0000503% SincFast Lanczos2D Robidoux
anthony61b5ddd2010-10-05 02:33:31 +0000504%
anthony48f77622010-10-03 14:32:31 +0000505% The users "-filter" selection is used to lookup the default 'expert'
506% settings for that filter from a internal table. However any provided
507% 'expert' settings (see below) may override this selection.
508%
509% FIR filters are used as is, and are limited to that filters support
nicolas07bac812010-09-19 18:47:02 +0000510% window (unless over-ridden). 'Gaussian' while classed as an IIR
anthonyc331dec2010-09-26 01:30:14 +0000511% filter, is also simply clipped by its support size (currently 1.5
anthonyf5e76ef2010-10-12 01:22:01 +0000512% or approximatally 3*sigma as recommended by many references)
cristy3ed852e2009-09-05 21:47:34 +0000513%
anthony48f77622010-10-03 14:32:31 +0000514% The selection is typically either a windowed Sinc, or interpolated
nicolas07bac812010-09-19 18:47:02 +0000515% filter, for use by functions such as ResizeImage(). However if a
anthony48f77622010-10-03 14:32:31 +0000516% 'cylindrical' filter flag is requested, any default Sinc weighting
517% and windowing functions will be promoted to cylindrical Jinc form of
518% function.
cristy3ed852e2009-09-05 21:47:34 +0000519%
anthony48f77622010-10-03 14:32:31 +0000520% Directly requesting 'Sinc' or 'Jinc' will force the use of that
521% filter function without any windowing. This is not recommended,
522% except by image processing experts or in expert options. Selecting a
523% window filtering version of these functions is better.
cristy3ed852e2009-09-05 21:47:34 +0000524%
anthony48f77622010-10-03 14:32:31 +0000525% Lanczos is a special case of a Sinc-windowed Sinc, (or Jinc-Jinc for
526% the cylindrical case) but defaulting to 3-lobe support, rather that
527% the default 4 lobe support of the other windowed sinc/jinc filters.
cristy3ed852e2009-09-05 21:47:34 +0000528%
anthony48f77622010-10-03 14:32:31 +0000529% Two forms of the 'Sinc' function are available: Sinc and SincFast.
nicolas07bac812010-09-19 18:47:02 +0000530% Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
531% selected if the user specifically specifies the use of a Sinc
532% filter. SincFast uses highly accurate (and fast) polynomial (low Q)
anthony48f77622010-10-03 14:32:31 +0000533% and rational (high Q) approximations, and will be used by default in
534% most cases.
anthonyba5a7c32010-09-15 02:42:25 +0000535%
nicolas1eb2fcf2010-10-10 20:45:17 +0000536% The Lanczos2D and Robidoux filters are tuned for cylindrical
537% (radial) EWA (Elliptical Weighted Average) distortion. Lanczos2D
nicolasdff19b42010-10-10 20:53:13 +0000538% is a 2 lobe Lanczos-like filter using Jinc (for EWA) or Sinc.
nicolas6ca6e962010-10-10 20:49:01 +0000539% Robidoux used to be a sharpened version of Lanczos2D (with
nicolasdff19b42010-10-10 20:53:13 +0000540% blur=0.958033808). Now, it is the unique Cubic 'Keys' filter that
541% exactly preserves images with only vertical or horizontal features
542% when performing 'no-op" with EWA distortion. It turns out to be
nicolas219dd842010-10-10 21:02:40 +0000543% close to both plain Mitchell and "sharpened" Lanczos2D.
anthony61b5ddd2010-10-05 02:33:31 +0000544%
nicolas07bac812010-09-19 18:47:02 +0000545% Special 'expert' options can be used to override any and all filter
546% settings. This is not advised unless you have expert knowledge of
547% the use of resampling filtered techniques. Check on the results of
548% your selections using the "filter:verbose" setting to make sure you
anthony61b5ddd2010-10-05 02:33:31 +0000549% get the exact filter that you are tring to achieve.
cristy3ed852e2009-09-05 21:47:34 +0000550%
anthony48f77622010-10-03 14:32:31 +0000551% "filter:filter" Select the main function associated with
552% this filter name, as the weighting function of the filter.
553% This can be used to set a windowing function as a weighting
554% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000555%
anthony7bdc0ed2010-09-15 01:52:32 +0000556% If a "filter:window" operation has not been provided, then a 'Box'
557% windowing function will be set to denote that no windowing function
558% is being used.
cristy3ed852e2009-09-05 21:47:34 +0000559%
560% "filter:window" Select this windowing function for the filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000561% While any filter could be used as a windowing function, using the
562% 'first lobe' of that filter over the whole support window, using a
anthony48f77622010-10-03 14:32:31 +0000563% non-windowing function is not advisible. If no weighting filter
564% function is specifed a 'SincFast' filter will be used.
cristy3ed852e2009-09-05 21:47:34 +0000565%
anthony48f77622010-10-03 14:32:31 +0000566% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000567% This a simpler method of setting filter support size that will
anthony48f77622010-10-03 14:32:31 +0000568% correctly handle the Sinc/Jinc switch for an operators filtering
anthony7bdc0ed2010-09-15 01:52:32 +0000569% requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000570%
571% "filter:support" Set the support size for filtering to the size given
anthony48f77622010-10-03 14:32:31 +0000572% This not recommended for Sinc/Jinc windowed filters (lobes should
anthony7bdc0ed2010-09-15 01:52:32 +0000573% be used instead). This will override any 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000574%
anthonyb6d08c52010-09-13 01:17:04 +0000575% "filter:win-support" Scale windowing function to this size instead.
576% This causes the windowing (or self-windowing Lagrange filter) to act
577% is if the support window it much much larger than what is actually
578% supplied to the calling operator. The filter however is still
579% clipped to the real support size given, by the support range suppiled
580% to the caller. If unset this will equal the normal filter support
581% size.
582%
cristy3ed852e2009-09-05 21:47:34 +0000583% "filter:blur" Scale the filter and support window by this amount.
584% A value >1 will generally result in a more burred image with
585% more ringing effects, while a value <1 will sharpen the
586% resulting image with more aliasing and Morie effects.
587%
anthonyf5e76ef2010-10-12 01:22:01 +0000588% "filter:sigma" The sigma value to use for the Gaussian filter only.
589% Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for cylindrical
590% usage. It effectially provides a alturnative to 'blur' for Gaussians
591% without it also effecting the final 'practical support' size.
592%
cristy3ed852e2009-09-05 21:47:34 +0000593% "filter:b"
594% "filter:c" Override the preset B,C values for a Cubic type of filter
595% If only one of these are given it is assumes to be a 'Keys'
596% type of filter such that B+2C=1, where Keys 'alpha' value = C
597%
anthonyb6d08c52010-09-13 01:17:04 +0000598% "filter:verbose" Output the exact results of the filter selections
599% made, as well as plotting data for graphing the resulting filter
600% over support range (blur adjusted).
cristy3ed852e2009-09-05 21:47:34 +0000601%
602% Set a true un-windowed Sinc filter with 10 lobes (very slow)
anthony48f77622010-10-03 14:32:31 +0000603% -define filter:filter=Sinc
604% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000605%
anthony48f77622010-10-03 14:32:31 +0000606% For example force an 8 lobe Lanczos (Sinc or Jinc) filter...
cristy3ed852e2009-09-05 21:47:34 +0000607% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000608% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000609%
cristy3ed852e2009-09-05 21:47:34 +0000610% The format of the AcquireResizeFilter method is:
611%
612% ResizeFilter *AcquireResizeFilter(const Image *image,
613% const FilterTypes filter_type, const MagickBooleanType radial,
614% ExceptionInfo *exception)
615%
cristy33b1c162010-01-23 22:51:51 +0000616% A description of each parameter follows:
617%
cristy3ed852e2009-09-05 21:47:34 +0000618% o image: the image.
619%
nicolas07bac812010-09-19 18:47:02 +0000620% o filter: the filter type, defining a preset filter, window and
621% support. The artifact settings listed above will override
622% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000623%
anthony48f77622010-10-03 14:32:31 +0000624% o blur: blur the filter by this amount, use 1.0 if unknown. Image
625% artifact "filter:blur" will override this API call usage, including
626% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000627%
anthony48f77622010-10-03 14:32:31 +0000628% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
629% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000630%
631% o exception: return any errors or warnings in this structure.
632%
633*/
634MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000635 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000636 const MagickBooleanType cylindrical,ExceptionInfo *exception)
637{
638 const char
639 *artifact;
640
641 FilterTypes
642 filter_type,
643 window_type;
644
cristy3ed852e2009-09-05 21:47:34 +0000645 MagickRealType
646 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000647 C,
648 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000649
650 register ResizeFilter
651 *resize_filter;
652
cristy9af9b5d2010-08-15 17:04:28 +0000653 ssize_t
654 option;
655
cristy3ed852e2009-09-05 21:47:34 +0000656 /*
anthony48f77622010-10-03 14:32:31 +0000657 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000658 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000659 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
660 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
661 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000662
nicolas07bac812010-09-19 18:47:02 +0000663 WARNING: The order of this tabel must match the order of the
664 FilterTypes enumeration specified in "resample.h", or the filter
665 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000666
667 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000668 */
669 static struct
670 {
671 FilterTypes
672 filter,
673 window;
674 } const mapping[SentinelFilter] =
675 {
anthony462ee072010-09-27 12:34:02 +0000676 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
677 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
678 { BoxFilter, BoxFilter }, /* Box averaging filter */
679 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
680 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
681 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
682 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
683 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
684 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
685 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
686 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
687 { CatromFilter, BoxFilter }, /* Cubic interpolator */
688 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
689 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
anthony48f77622010-10-03 14:32:31 +0000690 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
691 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony462ee072010-09-27 12:34:02 +0000692 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
693 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
694 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
695 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
696 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
697 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
698 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolas5835ee02010-10-10 20:40:15 +0000699 { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
anthonye5f06452010-10-12 05:48:17 +0000700 { Lanczos2DSharpFilter, JincFilter },/* SPECIAL: ditto sharpened */
701 { RobidouxFilter, BoxFilter }, /* SPECIAL: Keys cubic tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000702 };
703 /*
nicolas32f44eb2010-09-20 01:23:12 +0000704 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000705 function. The default support size for that filter as a weighting
706 function, the range to scale with to use that function as a sinc
707 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000708
anthony07a3f7f2010-09-16 03:03:11 +0000709 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000710 SincFast(), and CubicBC() functions, which may have multiple
711 filter to function associations.
712
713 See "filter:verbose" handling below for the function -> filter
714 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000715 */
716 static struct
717 {
718 MagickRealType
719 (*function)(const MagickRealType, const ResizeFilter*),
anthony61b5ddd2010-10-05 02:33:31 +0000720 lobes, /* default lobes/support size of the weighting filter */
anthony2d9b8b52010-09-14 08:31:07 +0000721 scale, /* windowing function range, for scaling windowing function */
anthony933b4bf2010-09-10 02:31:37 +0000722 B,C; /* Cubic Filter factors for a CubicBC function, else ignored */
cristy3ed852e2009-09-05 21:47:34 +0000723 } const filters[SentinelFilter] =
724 {
anthony61b5ddd2010-10-05 02:33:31 +0000725 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
726 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
727 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
728 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
729 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
730 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
731 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
732 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000733 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000734 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
735 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
736 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
anthony853d6972010-10-08 06:01:31 +0000737 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
738 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
739 { Jinc, 3.0, 1.21967, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000740 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
741 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
742 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
743 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
744 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
745 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
746 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
747 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
anthony08958462010-10-12 06:48:35 +0000748 { Jinc, 2.0, 1.21966989, 0.0, 0.0 }, /* Lanczos2D (Jinc-Jinc) */
749 { Jinc, 2.0, 1.16848499, 0.0, 0.0 }, /* Lanczos2D Sharpened */
750 { CubicBC, 2.0, 1.16848499, 0.37821575509399862, 0.31089212245300069 }
nicolas0134bbe2010-10-10 15:55:42 +0000751 /* Robidoux: Keys cubic close to Lanczos2D with blur=0.958033808 */
cristy3ed852e2009-09-05 21:47:34 +0000752 };
753 /*
anthony9a98fc62010-10-11 02:47:19 +0000754 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
755 function being used as a filter. It is used by the "filter:lobes" and for
756 the 'lobes' number in the above, the for support selection, so users do
757 not have to deal with the highly irrational sizes of the 'lobes' of the
758 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000759
nicolase473f722010-10-07 00:05:13 +0000760 Values taken from
anthony48f77622010-10-03 14:32:31 +0000761 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000762 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000763 */
764 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000765 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000766 {
anthonyc2d07db2010-09-15 23:47:40 +0000767 1.21966989126651,
768 2.23313059438153,
769 3.23831548416624,
770 4.24106286379607,
771 5.24276437687019,
772 6.24392168986449,
773 7.24475986871996,
774 8.24539491395205,
775 9.24589268494948,
776 10.2462933487549,
777 11.2466227948779,
778 12.2468984611381,
779 13.2471325221811,
780 14.2473337358069,
781 15.2475085630373,
782 16.247661874701
cristy3ed852e2009-09-05 21:47:34 +0000783 };
784
cristy33b1c162010-01-23 22:51:51 +0000785 /*
786 Allocate resize filter.
787 */
cristy3ed852e2009-09-05 21:47:34 +0000788 assert(image != (const Image *) NULL);
789 assert(image->signature == MagickSignature);
790 if (image->debug != MagickFalse)
791 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
792 assert(UndefinedFilter < filter && filter < SentinelFilter);
793 assert(exception != (ExceptionInfo *) NULL);
794 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000795 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000796 if (resize_filter == (ResizeFilter *) NULL)
797 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000798 /*
799 Defaults for the requested filter.
800 */
801 filter_type=mapping[filter].filter;
802 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000803 resize_filter->blur = blur;
804 sigma = 0.5;
anthony48f77622010-10-03 14:32:31 +0000805 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000806 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000807 switch (filter_type)
808 {
809 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000810 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000811 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000812 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000813 break;
anthonyba5a7c32010-09-15 02:42:25 +0000814 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000815 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000816 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000817 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000818 break;
cristy33b1c162010-01-23 22:51:51 +0000819 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000820 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000821 filter_type=JincFilter;
822 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000823 break;
anthony08958462010-10-12 06:48:35 +0000824 case Lanczos2DSharpFilter:
825 /* Sharpened by Nicholas Robidoux so as to optimize for
826 * minimal blurring of orthogonal lines
827 */
828 resize_filter->blur *= 0.958033808;
829 break;
anthonyf5e76ef2010-10-12 01:22:01 +0000830 case GaussianFilter:
cristy68f103e2010-10-14 01:19:07 +0000831 sigma = (MagickRealType) (MagickSQ2/2.0); /* Cylindrical Gaussian sigma is sqrt(2)/2 */
anthonyf5e76ef2010-10-12 01:22:01 +0000832 break;
cristya782ecf2010-01-25 02:59:14 +0000833 default:
834 break;
cristy3ed852e2009-09-05 21:47:34 +0000835 }
anthony61b5ddd2010-10-05 02:33:31 +0000836 else
837 switch (filter_type)
838 {
839 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000840 case Lanczos2DSharpFilter:
nicolas45b58a92010-10-07 15:46:39 +0000841 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000842 window_type=SincFastFilter;
843 break;
844 default:
845 break;
846 }
847
cristy3ed852e2009-09-05 21:47:34 +0000848 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000849 if (artifact != (const char *) NULL)
850 {
cristy9af9b5d2010-08-15 17:04:28 +0000851 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000852 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000853 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000854 filter_type=(FilterTypes) option;
855 window_type=BoxFilter;
856 }
857 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000858 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000859 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000860 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000861 }
nicolas07bac812010-09-19 18:47:02 +0000862 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000863 artifact=GetImageArtifact(image,"filter:window");
864 if (artifact != (const char *) NULL)
865 {
cristy9af9b5d2010-08-15 17:04:28 +0000866 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000867 if ((UndefinedFilter < option) && (option < SentinelFilter))
868 {
869 if (option != LanczosFilter)
870 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000871 else
anthony48f77622010-10-03 14:32:31 +0000872 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000873 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000874 }
cristy33b1c162010-01-23 22:51:51 +0000875 }
cristy3ed852e2009-09-05 21:47:34 +0000876 }
cristy33b1c162010-01-23 22:51:51 +0000877 else
878 {
anthony48f77622010-10-03 14:32:31 +0000879 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000880 artifact=GetImageArtifact(image,"filter:window");
881 if (artifact != (const char *) NULL)
882 {
883 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
884 artifact);
885 if ((UndefinedFilter < option) && (option < SentinelFilter))
886 {
anthony61b5ddd2010-10-05 02:33:31 +0000887 filter_type=cylindrical != MagickFalse ?
888 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000889 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000890 }
891 }
892 }
nicolas07bac812010-09-19 18:47:02 +0000893 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000894 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000895 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000896 resize_filter->window=filters[window_type].function;
897 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000898 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000899
anthonyf5e76ef2010-10-12 01:22:01 +0000900 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000901 if (cylindrical != MagickFalse)
902 switch (filter_type)
903 {
904 case PointFilter:
905 case BoxFilter:
906 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000907 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000908 break;
anthony81b8bf92010-10-02 13:54:34 +0000909 default:
910 break;
anthony10b8bc82010-10-02 12:48:46 +0000911 }
anthony61b5ddd2010-10-05 02:33:31 +0000912 else
913 switch (filter_type)
914 {
915 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000916 case Lanczos2DSharpFilter:
anthony853d6972010-10-08 06:01:31 +0000917 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000918 resize_filter->filter=SincFast;
919 break;
920 default:
921 break;
922 }
923
anthonyf5e76ef2010-10-12 01:22:01 +0000924 /*
925 ** More Expert Option Modifications
926 */
927
928 /* User Sigma Override - no support change */
929 artifact=GetImageArtifact(image,"filter:sigma");
930 if (artifact != (const char *) NULL)
931 sigma=StringToDouble(artifact);
932 /* Define coefficents for Gaussian (assumes no cubic window) */
933 if ( GaussianFilter ) {
934 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
cristy68f103e2010-10-14 01:19:07 +0000935 resize_filter->coeff[1] = (MagickRealType) (1.0/(Magick2PI*sigma*sigma)); /* unused */
anthonyf5e76ef2010-10-12 01:22:01 +0000936 }
937
938 /* Blur Override */
939 artifact=GetImageArtifact(image,"filter:blur");
940 if (artifact != (const char *) NULL)
941 resize_filter->blur=StringToDouble(artifact);
942 if (resize_filter->blur < MagickEpsilon)
943 resize_filter->blur=(MagickRealType) MagickEpsilon;
944
945 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000946 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000947 if (artifact != (const char *) NULL)
948 {
cristybb503372010-05-27 20:51:26 +0000949 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000950 lobes;
951
cristy96b16132010-08-29 17:19:52 +0000952 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000953 if (lobes < 1)
954 lobes=1;
955 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000956 }
anthony61b5ddd2010-10-05 02:33:31 +0000957 /* convert Jinc lobes to a real support value */
958 if (resize_filter->filter == Jinc)
959 {
960 if (resize_filter->support > 16)
961 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
962 else
963 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
964 }
965 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000966 artifact=GetImageArtifact(image,"filter:support");
967 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000968 resize_filter->support=fabs(StringToDouble(artifact));
969 /*
nicolas07bac812010-09-19 18:47:02 +0000970 Scale windowing function separatally to the support 'clipping'
971 window that calling operator is planning to actually use. (Expert
972 override)
cristy3ed852e2009-09-05 21:47:34 +0000973 */
anthony55f12332010-09-10 01:13:02 +0000974 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000975 artifact=GetImageArtifact(image,"filter:win-support");
976 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000977 resize_filter->window_support=fabs(StringToDouble(artifact));
978 /*
anthony1f90a6b2010-09-14 08:56:31 +0000979 Adjust window function scaling to the windowing support for
980 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000981 */
982 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +0000983
anthony55f12332010-09-10 01:13:02 +0000984 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000985 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000986 */
cristy3ed852e2009-09-05 21:47:34 +0000987 B=0.0;
988 C=0.0;
cristy33b1c162010-01-23 22:51:51 +0000989 if ((filters[filter_type].function == CubicBC) ||
990 (filters[window_type].function == CubicBC))
991 {
anthony2d9b8b52010-09-14 08:31:07 +0000992 B=filters[filter_type].B;
993 C=filters[filter_type].C;
994 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +0000995 {
anthony2d9b8b52010-09-14 08:31:07 +0000996 B=filters[window_type].B;
997 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +0000998 }
cristy33b1c162010-01-23 22:51:51 +0000999 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001000 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001001 {
1002 B=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001003 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001004 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001005 if (artifact != (const char *) NULL)
1006 C=StringToDouble(artifact);
1007 }
1008 else
1009 {
1010 artifact=GetImageArtifact(image,"filter:c");
1011 if (artifact != (const char *) NULL)
1012 {
1013 C=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001014 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001015 }
1016 }
anthonyf5e76ef2010-10-12 01:22:01 +00001017 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
1018 resize_filter->coeff[0]=(6.0-2.0*B)/6.0;
1019 resize_filter->coeff[1]=0.0;
1020 resize_filter->coeff[2]=(-18.0+12.0*B+6.0*C)/6.0;
1021 resize_filter->coeff[3]=(12.0-9.0*B-6.0*C)/6.0;
1022 resize_filter->coeff[4]=(8.0*B+24.0*C)/6.0;
1023 resize_filter->coeff[5]=(-12.0*B-48.0*C)/6.0;
1024 resize_filter->coeff[6]=(6.0*B+30.0*C)/6.0;
1025 resize_filter->coeff[7]=(-B-6.0*C)/6.0;
cristy3ed852e2009-09-05 21:47:34 +00001026 }
anthonyf5e76ef2010-10-12 01:22:01 +00001027
anthony55f12332010-09-10 01:13:02 +00001028 /*
nicolas07bac812010-09-19 18:47:02 +00001029 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001030 */
cristyf1f1f252010-10-16 00:55:07 +00001031 artifact=GetImageArtifact(image,"filter:verbose");
1032 if ((artifact != (const char *) NULL) && (GetOpenMPThreadId() == 0))
1033 {
1034 double
1035 support,
1036 x;
cristy3ed852e2009-09-05 21:47:34 +00001037
cristyf1f1f252010-10-16 00:55:07 +00001038 /*
1039 Set the weighting function properly when the weighting
1040 function may not exactly match the filter of the same name.
1041 EG: a Point filter really uses a Box weighting function
1042 with a different support than is typically used.
anthony463be1d2010-09-26 01:07:36 +00001043
cristyf1f1f252010-10-16 00:55:07 +00001044 */
1045 if (resize_filter->filter == Box) filter_type=BoxFilter;
1046 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1047 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1048 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1049 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1050 /*
1051 Report Filter Details.
1052 */
1053 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1054 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
1055 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1056 MagickFilterOptions,filter_type));
1057 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
1058 MagickFilterOptions, window_type));
1059 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
1060 (double) resize_filter->support);
1061 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
1062 (double) resize_filter->window_support);
1063 (void) fprintf(stdout,"# scale_blur = %.*g\n",GetMagickPrecision(),
1064 (double) resize_filter->blur);
1065 if ( filter_type == GaussianFilter )
1066 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",GetMagickPrecision(),
1067 (double) sigma);
1068 (void) fprintf(stdout,"# practical_support = %.*g\n",GetMagickPrecision(),
1069 (double) support);
1070 if ( filter_type == CubicFilter || window_type == CubicFilter )
1071 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1072 (double) B,GetMagickPrecision(),(double) C);
1073 (void) fprintf(stdout,"\n");
1074 /*
1075 Output values of resulting filter graph -- for graphing
1076 filter result.
1077 */
1078 for (x=0.0; x <= support; x+=0.01f)
1079 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1080 (double) GetResizeFilterWeight(resize_filter,x));
1081 /* A final value so gnuplot can graph the 'stop' properly. */
1082 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1083 0.0);
anthony61b5ddd2010-10-05 02:33:31 +00001084 }
cristyf1f1f252010-10-16 00:55:07 +00001085 /* Output the above once only for each image - remove setting */
1086 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
cristy3ed852e2009-09-05 21:47:34 +00001087 return(resize_filter);
1088}
1089
1090/*
1091%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1092% %
1093% %
1094% %
1095% A d a p t i v e R e s i z e I m a g e %
1096% %
1097% %
1098% %
1099%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1100%
1101% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1102%
1103% The format of the AdaptiveResizeImage method is:
1104%
cristy9af9b5d2010-08-15 17:04:28 +00001105% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1106% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001107%
1108% A description of each parameter follows:
1109%
1110% o image: the image.
1111%
1112% o columns: the number of columns in the resized image.
1113%
1114% o rows: the number of rows in the resized image.
1115%
1116% o exception: return any errors or warnings in this structure.
1117%
1118*/
1119MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001120 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001121{
1122#define AdaptiveResizeImageTag "Resize/Image"
1123
cristyc4c8d132010-01-07 01:58:38 +00001124 CacheView
1125 *resize_view;
1126
cristy3ed852e2009-09-05 21:47:34 +00001127 Image
1128 *resize_image;
1129
cristy3ed852e2009-09-05 21:47:34 +00001130 MagickBooleanType
1131 proceed;
1132
1133 MagickPixelPacket
1134 pixel;
1135
1136 PointInfo
1137 offset;
1138
1139 ResampleFilter
1140 *resample_filter;
1141
cristy9af9b5d2010-08-15 17:04:28 +00001142 ssize_t
1143 y;
1144
cristy3ed852e2009-09-05 21:47:34 +00001145 /*
1146 Adaptively resize image.
1147 */
1148 assert(image != (const Image *) NULL);
1149 assert(image->signature == MagickSignature);
1150 if (image->debug != MagickFalse)
1151 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1152 assert(exception != (ExceptionInfo *) NULL);
1153 assert(exception->signature == MagickSignature);
1154 if ((columns == 0) || (rows == 0))
1155 return((Image *) NULL);
1156 if ((columns == image->columns) && (rows == image->rows))
1157 return(CloneImage(image,0,0,MagickTrue,exception));
1158 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1159 if (resize_image == (Image *) NULL)
1160 return((Image *) NULL);
1161 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1162 {
1163 InheritException(exception,&resize_image->exception);
1164 resize_image=DestroyImage(resize_image);
1165 return((Image *) NULL);
1166 }
1167 GetMagickPixelPacket(image,&pixel);
1168 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001169 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001170 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001171 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001172 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001173 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001174 {
1175 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001176 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001177
cristybb503372010-05-27 20:51:26 +00001178 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001179 x;
1180
1181 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001182 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001183
1184 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1185 exception);
1186 if (q == (PixelPacket *) NULL)
1187 break;
1188 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1189 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001190 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001191 {
1192 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1193 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1194 &pixel);
1195 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1196 q++;
1197 }
1198 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1199 break;
cristy96b16132010-08-29 17:19:52 +00001200 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1201 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001202 if (proceed == MagickFalse)
1203 break;
1204 }
1205 resample_filter=DestroyResampleFilter(resample_filter);
1206 resize_view=DestroyCacheView(resize_view);
1207 return(resize_image);
1208}
1209
1210/*
1211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1212% %
1213% %
1214% %
1215+ B e s s e l O r d e r O n e %
1216% %
1217% %
1218% %
1219%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1220%
1221% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001222% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001223%
1224% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1225%
1226% j1(x) = x*j1(x);
1227%
1228% For x in (8,inf)
1229%
1230% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1231%
1232% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1233%
1234% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1235% = 1/sqrt(2) * (sin(x) - cos(x))
1236% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1237% = -1/sqrt(2) * (sin(x) + cos(x))
1238%
1239% The format of the BesselOrderOne method is:
1240%
1241% MagickRealType BesselOrderOne(MagickRealType x)
1242%
1243% A description of each parameter follows:
1244%
1245% o x: MagickRealType value.
1246%
1247*/
1248
1249#undef I0
1250static MagickRealType I0(MagickRealType x)
1251{
1252 MagickRealType
1253 sum,
1254 t,
1255 y;
1256
cristybb503372010-05-27 20:51:26 +00001257 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001258 i;
1259
1260 /*
1261 Zeroth order Bessel function of the first kind.
1262 */
1263 sum=1.0;
1264 y=x*x/4.0;
1265 t=y;
1266 for (i=2; t > MagickEpsilon; i++)
1267 {
1268 sum+=t;
1269 t*=y/((MagickRealType) i*i);
1270 }
1271 return(sum);
1272}
1273
1274#undef J1
1275static MagickRealType J1(MagickRealType x)
1276{
1277 MagickRealType
1278 p,
1279 q;
1280
cristybb503372010-05-27 20:51:26 +00001281 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001282 i;
1283
1284 static const double
1285 Pone[] =
1286 {
1287 0.581199354001606143928050809e+21,
1288 -0.6672106568924916298020941484e+20,
1289 0.2316433580634002297931815435e+19,
1290 -0.3588817569910106050743641413e+17,
1291 0.2908795263834775409737601689e+15,
1292 -0.1322983480332126453125473247e+13,
1293 0.3413234182301700539091292655e+10,
1294 -0.4695753530642995859767162166e+7,
1295 0.270112271089232341485679099e+4
1296 },
1297 Qone[] =
1298 {
1299 0.11623987080032122878585294e+22,
1300 0.1185770712190320999837113348e+20,
1301 0.6092061398917521746105196863e+17,
1302 0.2081661221307607351240184229e+15,
1303 0.5243710262167649715406728642e+12,
1304 0.1013863514358673989967045588e+10,
1305 0.1501793594998585505921097578e+7,
1306 0.1606931573481487801970916749e+4,
1307 0.1e+1
1308 };
1309
1310 p=Pone[8];
1311 q=Qone[8];
1312 for (i=7; i >= 0; i--)
1313 {
1314 p=p*x*x+Pone[i];
1315 q=q*x*x+Qone[i];
1316 }
1317 return(p/q);
1318}
1319
1320#undef P1
1321static MagickRealType P1(MagickRealType x)
1322{
1323 MagickRealType
1324 p,
1325 q;
1326
cristybb503372010-05-27 20:51:26 +00001327 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001328 i;
1329
1330 static const double
1331 Pone[] =
1332 {
1333 0.352246649133679798341724373e+5,
1334 0.62758845247161281269005675e+5,
1335 0.313539631109159574238669888e+5,
1336 0.49854832060594338434500455e+4,
1337 0.2111529182853962382105718e+3,
1338 0.12571716929145341558495e+1
1339 },
1340 Qone[] =
1341 {
1342 0.352246649133679798068390431e+5,
1343 0.626943469593560511888833731e+5,
1344 0.312404063819041039923015703e+5,
1345 0.4930396490181088979386097e+4,
1346 0.2030775189134759322293574e+3,
1347 0.1e+1
1348 };
1349
1350 p=Pone[5];
1351 q=Qone[5];
1352 for (i=4; i >= 0; i--)
1353 {
1354 p=p*(8.0/x)*(8.0/x)+Pone[i];
1355 q=q*(8.0/x)*(8.0/x)+Qone[i];
1356 }
1357 return(p/q);
1358}
1359
1360#undef Q1
1361static MagickRealType Q1(MagickRealType x)
1362{
1363 MagickRealType
1364 p,
1365 q;
1366
cristybb503372010-05-27 20:51:26 +00001367 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001368 i;
1369
1370 static const double
1371 Pone[] =
1372 {
1373 0.3511751914303552822533318e+3,
1374 0.7210391804904475039280863e+3,
1375 0.4259873011654442389886993e+3,
1376 0.831898957673850827325226e+2,
1377 0.45681716295512267064405e+1,
1378 0.3532840052740123642735e-1
1379 },
1380 Qone[] =
1381 {
1382 0.74917374171809127714519505e+4,
1383 0.154141773392650970499848051e+5,
1384 0.91522317015169922705904727e+4,
1385 0.18111867005523513506724158e+4,
1386 0.1038187585462133728776636e+3,
1387 0.1e+1
1388 };
1389
1390 p=Pone[5];
1391 q=Qone[5];
1392 for (i=4; i >= 0; i--)
1393 {
1394 p=p*(8.0/x)*(8.0/x)+Pone[i];
1395 q=q*(8.0/x)*(8.0/x)+Qone[i];
1396 }
1397 return(p/q);
1398}
1399
1400static MagickRealType BesselOrderOne(MagickRealType x)
1401{
1402 MagickRealType
1403 p,
1404 q;
1405
1406 if (x == 0.0)
1407 return(0.0);
1408 p=x;
1409 if (x < 0.0)
1410 x=(-x);
1411 if (x < 8.0)
1412 return(p*J1(x));
1413 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1414 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1415 cos((double) x))));
1416 if (p < 0.0)
1417 q=(-q);
1418 return(q);
1419}
1420
1421/*
1422%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1423% %
1424% %
1425% %
1426+ D e s t r o y R e s i z e F i l t e r %
1427% %
1428% %
1429% %
1430%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1431%
1432% DestroyResizeFilter() destroy the resize filter.
1433%
cristya2ffd7e2010-03-10 20:50:30 +00001434% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001435%
1436% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1437%
1438% A description of each parameter follows:
1439%
1440% o resize_filter: the resize filter.
1441%
1442*/
1443MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1444{
1445 assert(resize_filter != (ResizeFilter *) NULL);
1446 assert(resize_filter->signature == MagickSignature);
1447 resize_filter->signature=(~MagickSignature);
1448 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1449 return(resize_filter);
1450}
1451
1452/*
1453%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1454% %
1455% %
1456% %
1457+ G e t R e s i z e F i l t e r S u p p o r t %
1458% %
1459% %
1460% %
1461%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1462%
1463% GetResizeFilterSupport() return the current support window size for this
1464% filter. Note that this may have been enlarged by filter:blur factor.
1465%
1466% The format of the GetResizeFilterSupport method is:
1467%
1468% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1469%
1470% A description of each parameter follows:
1471%
1472% o filter: Image filter to use.
1473%
1474*/
1475MagickExport MagickRealType GetResizeFilterSupport(
1476 const ResizeFilter *resize_filter)
1477{
1478 assert(resize_filter != (ResizeFilter *) NULL);
1479 assert(resize_filter->signature == MagickSignature);
1480 return(resize_filter->support*resize_filter->blur);
1481}
1482
1483/*
1484%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1485% %
1486% %
1487% %
1488+ G e t R e s i z e F i l t e r W e i g h t %
1489% %
1490% %
1491% %
1492%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1493%
1494% GetResizeFilterWeight evaluates the specified resize filter at the point x
1495% which usally lies between zero and the filters current 'support' and
1496% returns the weight of the filter function at that point.
1497%
1498% The format of the GetResizeFilterWeight method is:
1499%
1500% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1501% const MagickRealType x)
1502%
1503% A description of each parameter follows:
1504%
1505% o filter: the filter type.
1506%
1507% o x: the point.
1508%
1509*/
1510MagickExport MagickRealType GetResizeFilterWeight(
1511 const ResizeFilter *resize_filter,const MagickRealType x)
1512{
1513 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001514 scale,
1515 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001516
1517 /*
1518 Windowing function - scale the weighting filter by this amount.
1519 */
1520 assert(resize_filter != (ResizeFilter *) NULL);
1521 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001522 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001523 if ((resize_filter->window_support < MagickEpsilon) ||
1524 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001525 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001526 else
1527 {
anthony55f12332010-09-10 01:13:02 +00001528 scale=resize_filter->scale;
1529 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001530 }
anthony55f12332010-09-10 01:13:02 +00001531 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001532}
1533
1534/*
1535%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1536% %
1537% %
1538% %
1539% M a g n i f y I m a g e %
1540% %
1541% %
1542% %
1543%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1544%
1545% MagnifyImage() is a convenience method that scales an image proportionally
1546% to twice its size.
1547%
1548% The format of the MagnifyImage method is:
1549%
1550% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1551%
1552% A description of each parameter follows:
1553%
1554% o image: the image.
1555%
1556% o exception: return any errors or warnings in this structure.
1557%
1558*/
1559MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1560{
1561 Image
1562 *magnify_image;
1563
1564 assert(image != (Image *) NULL);
1565 assert(image->signature == MagickSignature);
1566 if (image->debug != MagickFalse)
1567 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1568 assert(exception != (ExceptionInfo *) NULL);
1569 assert(exception->signature == MagickSignature);
1570 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1571 1.0,exception);
1572 return(magnify_image);
1573}
1574
1575/*
1576%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1577% %
1578% %
1579% %
1580% M i n i f y I m a g e %
1581% %
1582% %
1583% %
1584%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1585%
1586% MinifyImage() is a convenience method that scales an image proportionally
1587% to half its size.
1588%
1589% The format of the MinifyImage method is:
1590%
1591% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1592%
1593% A description of each parameter follows:
1594%
1595% o image: the image.
1596%
1597% o exception: return any errors or warnings in this structure.
1598%
1599*/
1600MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1601{
1602 Image
1603 *minify_image;
1604
1605 assert(image != (Image *) NULL);
1606 assert(image->signature == MagickSignature);
1607 if (image->debug != MagickFalse)
1608 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1609 assert(exception != (ExceptionInfo *) NULL);
1610 assert(exception->signature == MagickSignature);
1611 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1612 1.0,exception);
1613 return(minify_image);
1614}
1615
1616/*
1617%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1618% %
1619% %
1620% %
1621% R e s a m p l e I m a g e %
1622% %
1623% %
1624% %
1625%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1626%
1627% ResampleImage() resize image in terms of its pixel size, so that when
1628% displayed at the given resolution it will be the same size in terms of
1629% real world units as the original image at the original resolution.
1630%
1631% The format of the ResampleImage method is:
1632%
1633% Image *ResampleImage(Image *image,const double x_resolution,
1634% const double y_resolution,const FilterTypes filter,const double blur,
1635% ExceptionInfo *exception)
1636%
1637% A description of each parameter follows:
1638%
1639% o image: the image to be resized to fit the given resolution.
1640%
1641% o x_resolution: the new image x resolution.
1642%
1643% o y_resolution: the new image y resolution.
1644%
1645% o filter: Image filter to use.
1646%
1647% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1648%
1649*/
1650MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1651 const double y_resolution,const FilterTypes filter,const double blur,
1652 ExceptionInfo *exception)
1653{
1654#define ResampleImageTag "Resample/Image"
1655
1656 Image
1657 *resample_image;
1658
cristybb503372010-05-27 20:51:26 +00001659 size_t
cristy3ed852e2009-09-05 21:47:34 +00001660 height,
1661 width;
1662
1663 /*
1664 Initialize sampled image attributes.
1665 */
1666 assert(image != (const Image *) NULL);
1667 assert(image->signature == MagickSignature);
1668 if (image->debug != MagickFalse)
1669 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1670 assert(exception != (ExceptionInfo *) NULL);
1671 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001672 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1673 72.0 : image->x_resolution)+0.5);
1674 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1675 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001676 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1677 if (resample_image != (Image *) NULL)
1678 {
1679 resample_image->x_resolution=x_resolution;
1680 resample_image->y_resolution=y_resolution;
1681 }
1682 return(resample_image);
1683}
1684#if defined(MAGICKCORE_LQR_DELEGATE)
1685
1686/*
1687%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1688% %
1689% %
1690% %
1691% L i q u i d R e s c a l e I m a g e %
1692% %
1693% %
1694% %
1695%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1696%
1697% LiquidRescaleImage() rescales image with seam carving.
1698%
1699% The format of the LiquidRescaleImage method is:
1700%
1701% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001702% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001703% const double delta_x,const double rigidity,ExceptionInfo *exception)
1704%
1705% A description of each parameter follows:
1706%
1707% o image: the image.
1708%
1709% o columns: the number of columns in the rescaled image.
1710%
1711% o rows: the number of rows in the rescaled image.
1712%
1713% o delta_x: maximum seam transversal step (0 means straight seams).
1714%
1715% o rigidity: introduce a bias for non-straight seams (typically 0).
1716%
1717% o exception: return any errors or warnings in this structure.
1718%
1719*/
cristy9af9b5d2010-08-15 17:04:28 +00001720MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1721 const size_t rows,const double delta_x,const double rigidity,
1722 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001723{
1724#define LiquidRescaleImageTag "Rescale/Image"
1725
cristyc5c6f662010-09-22 14:23:02 +00001726 CacheView
1727 *rescale_view;
1728
cristy3ed852e2009-09-05 21:47:34 +00001729 const char
1730 *map;
1731
1732 guchar
1733 *packet;
1734
1735 Image
1736 *rescale_image;
1737
1738 int
1739 x,
1740 y;
1741
1742 LqrCarver
1743 *carver;
1744
1745 LqrRetVal
1746 lqr_status;
1747
1748 MagickBooleanType
1749 status;
1750
1751 MagickPixelPacket
1752 pixel;
1753
1754 unsigned char
1755 *pixels;
1756
1757 /*
1758 Liquid rescale image.
1759 */
1760 assert(image != (const Image *) NULL);
1761 assert(image->signature == MagickSignature);
1762 if (image->debug != MagickFalse)
1763 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1764 assert(exception != (ExceptionInfo *) NULL);
1765 assert(exception->signature == MagickSignature);
1766 if ((columns == 0) || (rows == 0))
1767 return((Image *) NULL);
1768 if ((columns == image->columns) && (rows == image->rows))
1769 return(CloneImage(image,0,0,MagickTrue,exception));
1770 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001771 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001772 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1773 {
1774 Image
1775 *resize_image;
1776
cristybb503372010-05-27 20:51:26 +00001777 size_t
cristy3ed852e2009-09-05 21:47:34 +00001778 height,
1779 width;
1780
1781 /*
1782 Honor liquid resize size limitations.
1783 */
1784 for (width=image->columns; columns >= (2*width-1); width*=2);
1785 for (height=image->rows; rows >= (2*height-1); height*=2);
1786 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1787 exception);
1788 if (resize_image == (Image *) NULL)
1789 return((Image *) NULL);
1790 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1791 rigidity,exception);
1792 resize_image=DestroyImage(resize_image);
1793 return(rescale_image);
1794 }
1795 map="RGB";
1796 if (image->matte == MagickFalse)
1797 map="RGBA";
1798 if (image->colorspace == CMYKColorspace)
1799 {
1800 map="CMYK";
1801 if (image->matte == MagickFalse)
1802 map="CMYKA";
1803 }
1804 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1805 strlen(map)*sizeof(*pixels));
1806 if (pixels == (unsigned char *) NULL)
1807 return((Image *) NULL);
1808 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1809 pixels,exception);
1810 if (status == MagickFalse)
1811 {
1812 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1813 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1814 }
1815 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1816 if (carver == (LqrCarver *) NULL)
1817 {
1818 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1819 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1820 }
1821 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1822 lqr_status=lqr_carver_resize(carver,columns,rows);
1823 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1824 lqr_carver_get_height(carver),MagickTrue,exception);
1825 if (rescale_image == (Image *) NULL)
1826 {
1827 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1828 return((Image *) NULL);
1829 }
1830 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1831 {
1832 InheritException(exception,&rescale_image->exception);
1833 rescale_image=DestroyImage(rescale_image);
1834 return((Image *) NULL);
1835 }
1836 GetMagickPixelPacket(rescale_image,&pixel);
1837 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001838 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001839 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1840 {
1841 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001842 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001843
1844 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001845 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001846
anthony22aad252010-09-23 06:59:07 +00001847 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001848 if (q == (PixelPacket *) NULL)
1849 break;
cristyc5c6f662010-09-22 14:23:02 +00001850 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001851 pixel.red=QuantumRange*(packet[0]/255.0);
1852 pixel.green=QuantumRange*(packet[1]/255.0);
1853 pixel.blue=QuantumRange*(packet[2]/255.0);
1854 if (image->colorspace != CMYKColorspace)
1855 {
1856 if (image->matte == MagickFalse)
1857 pixel.opacity=QuantumRange*(packet[3]/255.0);
1858 }
1859 else
1860 {
1861 pixel.index=QuantumRange*(packet[3]/255.0);
1862 if (image->matte == MagickFalse)
1863 pixel.opacity=QuantumRange*(packet[4]/255.0);
1864 }
1865 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001866 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001867 break;
1868 }
cristyc5c6f662010-09-22 14:23:02 +00001869 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001870 /*
1871 Relinquish resources.
1872 */
1873 lqr_carver_destroy(carver);
1874 return(rescale_image);
1875}
1876#else
1877MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001878 const size_t magick_unused(columns),const size_t magick_unused(rows),
1879 const double magick_unused(delta_x),const double magick_unused(rigidity),
1880 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001881{
1882 assert(image != (const Image *) NULL);
1883 assert(image->signature == MagickSignature);
1884 if (image->debug != MagickFalse)
1885 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1886 assert(exception != (ExceptionInfo *) NULL);
1887 assert(exception->signature == MagickSignature);
1888 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1889 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1890 return((Image *) NULL);
1891}
1892#endif
1893
1894/*
1895%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1896% %
1897% %
1898% %
1899% R e s i z e I m a g e %
1900% %
1901% %
1902% %
1903%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1904%
1905% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001906% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001907%
1908% If an undefined filter is given the filter defaults to Mitchell for a
1909% colormapped image, a image with a matte channel, or if the image is
1910% enlarged. Otherwise the filter defaults to a Lanczos.
1911%
1912% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1913%
1914% The format of the ResizeImage method is:
1915%
cristybb503372010-05-27 20:51:26 +00001916% Image *ResizeImage(Image *image,const size_t columns,
1917% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001918% ExceptionInfo *exception)
1919%
1920% A description of each parameter follows:
1921%
1922% o image: the image.
1923%
1924% o columns: the number of columns in the scaled image.
1925%
1926% o rows: the number of rows in the scaled image.
1927%
1928% o filter: Image filter to use.
1929%
cristy9af9b5d2010-08-15 17:04:28 +00001930% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1931% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001932%
1933% o exception: return any errors or warnings in this structure.
1934%
1935*/
1936
1937typedef struct _ContributionInfo
1938{
1939 MagickRealType
1940 weight;
1941
cristybb503372010-05-27 20:51:26 +00001942 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001943 pixel;
1944} ContributionInfo;
1945
1946static ContributionInfo **DestroyContributionThreadSet(
1947 ContributionInfo **contribution)
1948{
cristybb503372010-05-27 20:51:26 +00001949 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001950 i;
1951
1952 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001953 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001954 if (contribution[i] != (ContributionInfo *) NULL)
1955 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1956 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001957 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001958 return(contribution);
1959}
1960
1961static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1962{
cristybb503372010-05-27 20:51:26 +00001963 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001964 i;
1965
1966 ContributionInfo
1967 **contribution;
1968
cristybb503372010-05-27 20:51:26 +00001969 size_t
cristy3ed852e2009-09-05 21:47:34 +00001970 number_threads;
1971
1972 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001973 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001974 sizeof(*contribution));
1975 if (contribution == (ContributionInfo **) NULL)
1976 return((ContributionInfo **) NULL);
1977 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00001978 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00001979 {
1980 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1981 sizeof(**contribution));
1982 if (contribution[i] == (ContributionInfo *) NULL)
1983 return(DestroyContributionThreadSet(contribution));
1984 }
1985 return(contribution);
1986}
1987
1988static inline double MagickMax(const double x,const double y)
1989{
1990 if (x > y)
1991 return(x);
1992 return(y);
1993}
1994
1995static inline double MagickMin(const double x,const double y)
1996{
1997 if (x < y)
1998 return(x);
1999 return(y);
2000}
2001
2002static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2003 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002004 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002005{
2006#define ResizeImageTag "Resize/Image"
2007
cristyfa112112010-01-04 17:48:07 +00002008 CacheView
2009 *image_view,
2010 *resize_view;
2011
cristy3ed852e2009-09-05 21:47:34 +00002012 ClassType
2013 storage_class;
2014
2015 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002016 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002017
cristy3ed852e2009-09-05 21:47:34 +00002018 MagickBooleanType
2019 status;
2020
2021 MagickPixelPacket
2022 zero;
2023
2024 MagickRealType
2025 scale,
2026 support;
2027
cristy9af9b5d2010-08-15 17:04:28 +00002028 ssize_t
2029 x;
2030
cristy3ed852e2009-09-05 21:47:34 +00002031 /*
2032 Apply filter to resize horizontally from image to resize image.
2033 */
cristy5d824382010-09-06 14:00:17 +00002034 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002035 support=scale*GetResizeFilterSupport(resize_filter);
2036 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2037 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2038 {
2039 InheritException(exception,&resize_image->exception);
2040 return(MagickFalse);
2041 }
2042 if (support < 0.5)
2043 {
2044 /*
nicolas07bac812010-09-19 18:47:02 +00002045 Support too small even for nearest neighbour: Reduce to point
2046 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002047 */
2048 support=(MagickRealType) 0.5;
2049 scale=1.0;
2050 }
2051 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2052 if (contributions == (ContributionInfo **) NULL)
2053 {
2054 (void) ThrowMagickException(exception,GetMagickModule(),
2055 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2056 return(MagickFalse);
2057 }
2058 status=MagickTrue;
2059 scale=1.0/scale;
2060 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2061 image_view=AcquireCacheView(image);
2062 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002063#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002064 #pragma omp parallel for shared(status)
2065#endif
cristybb503372010-05-27 20:51:26 +00002066 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002067 {
cristy3ed852e2009-09-05 21:47:34 +00002068 MagickRealType
2069 center,
2070 density;
2071
2072 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002073 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002074
2075 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002076 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002077
cristy03dbbd22010-09-19 23:04:47 +00002078 register ContributionInfo
2079 *restrict contribution;
2080
cristy3ed852e2009-09-05 21:47:34 +00002081 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002082 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002083
cristy3ed852e2009-09-05 21:47:34 +00002084 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002085 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002086
cristy03dbbd22010-09-19 23:04:47 +00002087 register ssize_t
2088 y;
2089
cristy9af9b5d2010-08-15 17:04:28 +00002090 ssize_t
2091 n,
2092 start,
2093 stop;
2094
cristy3ed852e2009-09-05 21:47:34 +00002095 if (status == MagickFalse)
2096 continue;
2097 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002098 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2099 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002100 density=0.0;
2101 contribution=contributions[GetOpenMPThreadId()];
2102 for (n=0; n < (stop-start); n++)
2103 {
2104 contribution[n].pixel=start+n;
2105 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2106 ((MagickRealType) (start+n)-center+0.5));
2107 density+=contribution[n].weight;
2108 }
2109 if ((density != 0.0) && (density != 1.0))
2110 {
cristybb503372010-05-27 20:51:26 +00002111 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002112 i;
2113
2114 /*
2115 Normalize.
2116 */
2117 density=1.0/density;
2118 for (i=0; i < n; i++)
2119 contribution[i].weight*=density;
2120 }
cristy9af9b5d2010-08-15 17:04:28 +00002121 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2122 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002123 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2124 exception);
2125 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2126 {
2127 status=MagickFalse;
2128 continue;
2129 }
2130 indexes=GetCacheViewVirtualIndexQueue(image_view);
2131 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002132 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002133 {
cristy3ed852e2009-09-05 21:47:34 +00002134 MagickPixelPacket
2135 pixel;
2136
2137 MagickRealType
2138 alpha;
2139
cristybb503372010-05-27 20:51:26 +00002140 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002141 i;
2142
cristy9af9b5d2010-08-15 17:04:28 +00002143 ssize_t
2144 j;
2145
cristy3ed852e2009-09-05 21:47:34 +00002146 pixel=zero;
2147 if (image->matte == MagickFalse)
2148 {
2149 for (i=0; i < n; i++)
2150 {
2151 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2152 (contribution[i].pixel-contribution[0].pixel);
2153 alpha=contribution[i].weight;
2154 pixel.red+=alpha*(p+j)->red;
2155 pixel.green+=alpha*(p+j)->green;
2156 pixel.blue+=alpha*(p+j)->blue;
2157 pixel.opacity+=alpha*(p+j)->opacity;
2158 }
cristyce70c172010-01-07 17:15:30 +00002159 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2160 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2161 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2162 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002163 if ((image->colorspace == CMYKColorspace) &&
2164 (resize_image->colorspace == CMYKColorspace))
2165 {
2166 for (i=0; i < n; i++)
2167 {
2168 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2169 (contribution[i].pixel-contribution[0].pixel);
2170 alpha=contribution[i].weight;
2171 pixel.index+=alpha*indexes[j];
2172 }
cristyce70c172010-01-07 17:15:30 +00002173 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002174 }
2175 }
2176 else
2177 {
2178 MagickRealType
2179 gamma;
2180
2181 gamma=0.0;
2182 for (i=0; i < n; i++)
2183 {
2184 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2185 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002186 alpha=contribution[i].weight*QuantumScale*
2187 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002188 pixel.red+=alpha*(p+j)->red;
2189 pixel.green+=alpha*(p+j)->green;
2190 pixel.blue+=alpha*(p+j)->blue;
2191 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2192 gamma+=alpha;
2193 }
2194 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002195 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2196 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2197 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2198 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002199 if ((image->colorspace == CMYKColorspace) &&
2200 (resize_image->colorspace == CMYKColorspace))
2201 {
2202 for (i=0; i < n; i++)
2203 {
2204 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2205 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002206 alpha=contribution[i].weight*QuantumScale*
2207 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002208 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002209 }
cristyce70c172010-01-07 17:15:30 +00002210 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2211 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002212 }
2213 }
2214 if ((resize_image->storage_class == PseudoClass) &&
2215 (image->storage_class == PseudoClass))
2216 {
cristybb503372010-05-27 20:51:26 +00002217 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002218 1.0)+0.5);
2219 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2220 (contribution[i-start].pixel-contribution[0].pixel);
2221 resize_indexes[y]=indexes[j];
2222 }
2223 q++;
2224 }
2225 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2226 status=MagickFalse;
2227 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2228 {
2229 MagickBooleanType
2230 proceed;
2231
cristyb5d5f722009-11-04 03:03:49 +00002232#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002233 #pragma omp critical (MagickCore_HorizontalFilter)
2234#endif
cristy9af9b5d2010-08-15 17:04:28 +00002235 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002236 if (proceed == MagickFalse)
2237 status=MagickFalse;
2238 }
2239 }
2240 resize_view=DestroyCacheView(resize_view);
2241 image_view=DestroyCacheView(image_view);
2242 contributions=DestroyContributionThreadSet(contributions);
2243 return(status);
2244}
2245
2246static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2247 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002248 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002249{
cristyfa112112010-01-04 17:48:07 +00002250 CacheView
2251 *image_view,
2252 *resize_view;
2253
cristy3ed852e2009-09-05 21:47:34 +00002254 ClassType
2255 storage_class;
2256
2257 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002258 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002259
cristy3ed852e2009-09-05 21:47:34 +00002260 MagickBooleanType
2261 status;
2262
2263 MagickPixelPacket
2264 zero;
2265
2266 MagickRealType
2267 scale,
2268 support;
2269
cristy9af9b5d2010-08-15 17:04:28 +00002270 ssize_t
2271 y;
2272
cristy3ed852e2009-09-05 21:47:34 +00002273 /*
cristy9af9b5d2010-08-15 17:04:28 +00002274 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002275 */
cristy5d824382010-09-06 14:00:17 +00002276 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002277 support=scale*GetResizeFilterSupport(resize_filter);
2278 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2279 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2280 {
2281 InheritException(exception,&resize_image->exception);
2282 return(MagickFalse);
2283 }
2284 if (support < 0.5)
2285 {
2286 /*
nicolas07bac812010-09-19 18:47:02 +00002287 Support too small even for nearest neighbour: Reduce to point
2288 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002289 */
2290 support=(MagickRealType) 0.5;
2291 scale=1.0;
2292 }
2293 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2294 if (contributions == (ContributionInfo **) NULL)
2295 {
2296 (void) ThrowMagickException(exception,GetMagickModule(),
2297 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2298 return(MagickFalse);
2299 }
2300 status=MagickTrue;
2301 scale=1.0/scale;
2302 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2303 image_view=AcquireCacheView(image);
2304 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002305#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002306 #pragma omp parallel for shared(status)
2307#endif
cristybb503372010-05-27 20:51:26 +00002308 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002309 {
cristy3ed852e2009-09-05 21:47:34 +00002310 MagickRealType
2311 center,
2312 density;
2313
2314 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002315 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002316
2317 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002318 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002319
cristy03dbbd22010-09-19 23:04:47 +00002320 register ContributionInfo
2321 *restrict contribution;
2322
cristy3ed852e2009-09-05 21:47:34 +00002323 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002324 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002325
cristy9af9b5d2010-08-15 17:04:28 +00002326 register PixelPacket
2327 *restrict q;
2328
cristybb503372010-05-27 20:51:26 +00002329 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002330 x;
2331
cristy9af9b5d2010-08-15 17:04:28 +00002332 ssize_t
2333 n,
2334 start,
2335 stop;
cristy3ed852e2009-09-05 21:47:34 +00002336
2337 if (status == MagickFalse)
2338 continue;
cristy679e6962010-03-18 00:42:45 +00002339 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002340 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2341 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002342 density=0.0;
2343 contribution=contributions[GetOpenMPThreadId()];
2344 for (n=0; n < (stop-start); n++)
2345 {
2346 contribution[n].pixel=start+n;
2347 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2348 ((MagickRealType) (start+n)-center+0.5));
2349 density+=contribution[n].weight;
2350 }
2351 if ((density != 0.0) && (density != 1.0))
2352 {
cristybb503372010-05-27 20:51:26 +00002353 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002354 i;
2355
2356 /*
2357 Normalize.
2358 */
2359 density=1.0/density;
2360 for (i=0; i < n; i++)
2361 contribution[i].weight*=density;
2362 }
2363 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002364 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2365 exception);
cristy3ed852e2009-09-05 21:47:34 +00002366 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2367 exception);
2368 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2369 {
2370 status=MagickFalse;
2371 continue;
2372 }
2373 indexes=GetCacheViewVirtualIndexQueue(image_view);
2374 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002375 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002376 {
cristy3ed852e2009-09-05 21:47:34 +00002377 MagickPixelPacket
2378 pixel;
2379
2380 MagickRealType
2381 alpha;
2382
cristybb503372010-05-27 20:51:26 +00002383 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002384 i;
2385
cristy9af9b5d2010-08-15 17:04:28 +00002386 ssize_t
2387 j;
2388
cristy3ed852e2009-09-05 21:47:34 +00002389 pixel=zero;
2390 if (image->matte == MagickFalse)
2391 {
2392 for (i=0; i < n; i++)
2393 {
cristybb503372010-05-27 20:51:26 +00002394 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002395 image->columns+x);
2396 alpha=contribution[i].weight;
2397 pixel.red+=alpha*(p+j)->red;
2398 pixel.green+=alpha*(p+j)->green;
2399 pixel.blue+=alpha*(p+j)->blue;
2400 pixel.opacity+=alpha*(p+j)->opacity;
2401 }
cristyce70c172010-01-07 17:15:30 +00002402 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2403 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2404 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2405 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002406 if ((image->colorspace == CMYKColorspace) &&
2407 (resize_image->colorspace == CMYKColorspace))
2408 {
2409 for (i=0; i < n; i++)
2410 {
cristybb503372010-05-27 20:51:26 +00002411 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002412 image->columns+x);
2413 alpha=contribution[i].weight;
2414 pixel.index+=alpha*indexes[j];
2415 }
cristyce70c172010-01-07 17:15:30 +00002416 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002417 }
2418 }
2419 else
2420 {
2421 MagickRealType
2422 gamma;
2423
2424 gamma=0.0;
2425 for (i=0; i < n; i++)
2426 {
cristybb503372010-05-27 20:51:26 +00002427 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002428 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002429 alpha=contribution[i].weight*QuantumScale*
2430 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002431 pixel.red+=alpha*(p+j)->red;
2432 pixel.green+=alpha*(p+j)->green;
2433 pixel.blue+=alpha*(p+j)->blue;
2434 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2435 gamma+=alpha;
2436 }
2437 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002438 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2439 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2440 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2441 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002442 if ((image->colorspace == CMYKColorspace) &&
2443 (resize_image->colorspace == CMYKColorspace))
2444 {
2445 for (i=0; i < n; i++)
2446 {
cristybb503372010-05-27 20:51:26 +00002447 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002448 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002449 alpha=contribution[i].weight*QuantumScale*
2450 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002451 pixel.index+=alpha*indexes[j];
2452 }
cristyce70c172010-01-07 17:15:30 +00002453 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2454 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002455 }
2456 }
2457 if ((resize_image->storage_class == PseudoClass) &&
2458 (image->storage_class == PseudoClass))
2459 {
cristybb503372010-05-27 20:51:26 +00002460 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002461 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002462 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002463 image->columns+x);
2464 resize_indexes[x]=indexes[j];
2465 }
2466 q++;
2467 }
2468 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2469 status=MagickFalse;
2470 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2471 {
2472 MagickBooleanType
2473 proceed;
2474
cristyb5d5f722009-11-04 03:03:49 +00002475#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002476 #pragma omp critical (MagickCore_VerticalFilter)
2477#endif
cristy9af9b5d2010-08-15 17:04:28 +00002478 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002479 if (proceed == MagickFalse)
2480 status=MagickFalse;
2481 }
2482 }
2483 resize_view=DestroyCacheView(resize_view);
2484 image_view=DestroyCacheView(image_view);
2485 contributions=DestroyContributionThreadSet(contributions);
2486 return(status);
2487}
2488
cristybb503372010-05-27 20:51:26 +00002489MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2490 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002491 ExceptionInfo *exception)
2492{
2493#define WorkLoadFactor 0.265
2494
2495 FilterTypes
2496 filter_type;
2497
2498 Image
2499 *filter_image,
2500 *resize_image;
2501
cristy9af9b5d2010-08-15 17:04:28 +00002502 MagickOffsetType
2503 offset;
2504
cristy3ed852e2009-09-05 21:47:34 +00002505 MagickRealType
2506 x_factor,
2507 y_factor;
2508
2509 MagickSizeType
2510 span;
2511
2512 MagickStatusType
2513 status;
2514
2515 ResizeFilter
2516 *resize_filter;
2517
cristy3ed852e2009-09-05 21:47:34 +00002518 /*
2519 Acquire resize image.
2520 */
2521 assert(image != (Image *) NULL);
2522 assert(image->signature == MagickSignature);
2523 if (image->debug != MagickFalse)
2524 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2525 assert(exception != (ExceptionInfo *) NULL);
2526 assert(exception->signature == MagickSignature);
2527 if ((columns == 0) || (rows == 0))
2528 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2529 if ((columns == image->columns) && (rows == image->rows) &&
2530 (filter == UndefinedFilter) && (blur == 1.0))
2531 return(CloneImage(image,0,0,MagickTrue,exception));
2532 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2533 if (resize_image == (Image *) NULL)
2534 return(resize_image);
2535 /*
2536 Acquire resize filter.
2537 */
2538 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2539 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2540 if ((x_factor*y_factor) > WorkLoadFactor)
2541 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2542 else
2543 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2544 if (filter_image == (Image *) NULL)
2545 return(DestroyImage(resize_image));
2546 filter_type=LanczosFilter;
2547 if (filter != UndefinedFilter)
2548 filter_type=filter;
2549 else
2550 if ((x_factor == 1.0) && (y_factor == 1.0))
2551 filter_type=PointFilter;
2552 else
2553 if ((image->storage_class == PseudoClass) ||
2554 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2555 filter_type=MitchellFilter;
2556 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2557 exception);
2558 /*
2559 Resize image.
2560 */
cristy9af9b5d2010-08-15 17:04:28 +00002561 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002562 if ((x_factor*y_factor) > WorkLoadFactor)
2563 {
2564 span=(MagickSizeType) (filter_image->columns+rows);
2565 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002566 &offset,exception);
cristy065f8be2010-10-16 00:21:58 +00002567 resize_filter=DestroyResizeFilter(resize_filter);
2568 resize_filter=AcquireResizeFilter(filter_image,filter_type,blur,
2569 MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00002570 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002571 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002572 }
2573 else
2574 {
2575 span=(MagickSizeType) (filter_image->rows+columns);
2576 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002577 &offset,exception);
cristy065f8be2010-10-16 00:21:58 +00002578 resize_filter=DestroyResizeFilter(resize_filter);
2579 resize_filter=AcquireResizeFilter(filter_image,filter_type,blur,
2580 MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00002581 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002582 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002583 }
2584 /*
2585 Free resources.
2586 */
2587 filter_image=DestroyImage(filter_image);
2588 resize_filter=DestroyResizeFilter(resize_filter);
2589 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2590 return((Image *) NULL);
2591 resize_image->type=image->type;
2592 return(resize_image);
2593}
2594
2595/*
2596%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2597% %
2598% %
2599% %
2600% S a m p l e I m a g e %
2601% %
2602% %
2603% %
2604%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2605%
2606% SampleImage() scales an image to the desired dimensions with pixel
2607% sampling. Unlike other scaling methods, this method does not introduce
2608% any additional color into the scaled image.
2609%
2610% The format of the SampleImage method is:
2611%
cristybb503372010-05-27 20:51:26 +00002612% Image *SampleImage(const Image *image,const size_t columns,
2613% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002614%
2615% A description of each parameter follows:
2616%
2617% o image: the image.
2618%
2619% o columns: the number of columns in the sampled image.
2620%
2621% o rows: the number of rows in the sampled image.
2622%
2623% o exception: return any errors or warnings in this structure.
2624%
2625*/
cristybb503372010-05-27 20:51:26 +00002626MagickExport Image *SampleImage(const Image *image,const size_t columns,
2627 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002628{
2629#define SampleImageTag "Sample/Image"
2630
cristyc4c8d132010-01-07 01:58:38 +00002631 CacheView
2632 *image_view,
2633 *sample_view;
2634
cristy3ed852e2009-09-05 21:47:34 +00002635 Image
2636 *sample_image;
2637
cristy3ed852e2009-09-05 21:47:34 +00002638 MagickBooleanType
2639 status;
2640
cristy5f959472010-05-27 22:19:46 +00002641 MagickOffsetType
2642 progress;
2643
cristybb503372010-05-27 20:51:26 +00002644 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002645 x;
2646
cristy5f959472010-05-27 22:19:46 +00002647 ssize_t
2648 *x_offset,
2649 y;
2650
cristy3ed852e2009-09-05 21:47:34 +00002651 /*
2652 Initialize sampled image attributes.
2653 */
2654 assert(image != (const Image *) NULL);
2655 assert(image->signature == MagickSignature);
2656 if (image->debug != MagickFalse)
2657 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2658 assert(exception != (ExceptionInfo *) NULL);
2659 assert(exception->signature == MagickSignature);
2660 if ((columns == 0) || (rows == 0))
2661 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2662 if ((columns == image->columns) && (rows == image->rows))
2663 return(CloneImage(image,0,0,MagickTrue,exception));
2664 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2665 if (sample_image == (Image *) NULL)
2666 return((Image *) NULL);
2667 /*
2668 Allocate scan line buffer and column offset buffers.
2669 */
cristybb503372010-05-27 20:51:26 +00002670 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002671 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002672 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002673 {
2674 sample_image=DestroyImage(sample_image);
2675 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2676 }
cristybb503372010-05-27 20:51:26 +00002677 for (x=0; x < (ssize_t) sample_image->columns; x++)
2678 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002679 sample_image->columns);
2680 /*
2681 Sample each row.
2682 */
2683 status=MagickTrue;
2684 progress=0;
2685 image_view=AcquireCacheView(image);
2686 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002687#if defined(MAGICKCORE_OPENMP_SUPPORT)
2688 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002689#endif
cristybb503372010-05-27 20:51:26 +00002690 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002691 {
cristy3ed852e2009-09-05 21:47:34 +00002692 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002693 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002694
2695 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002696 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002697
2698 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002699 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002700
cristy3ed852e2009-09-05 21:47:34 +00002701 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002702 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002703
cristy03dbbd22010-09-19 23:04:47 +00002704 register ssize_t
2705 x;
2706
cristy9af9b5d2010-08-15 17:04:28 +00002707 ssize_t
2708 y_offset;
2709
cristy3ed852e2009-09-05 21:47:34 +00002710 if (status == MagickFalse)
2711 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002712 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2713 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002714 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2715 exception);
2716 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2717 exception);
2718 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2719 {
2720 status=MagickFalse;
2721 continue;
2722 }
2723 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2724 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2725 /*
2726 Sample each column.
2727 */
cristybb503372010-05-27 20:51:26 +00002728 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002729 *q++=p[x_offset[x]];
2730 if ((image->storage_class == PseudoClass) ||
2731 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002732 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002733 sample_indexes[x]=indexes[x_offset[x]];
2734 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2735 status=MagickFalse;
2736 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2737 {
2738 MagickBooleanType
2739 proceed;
2740
cristyb5d5f722009-11-04 03:03:49 +00002741#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002742 #pragma omp critical (MagickCore_SampleImage)
2743#endif
2744 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2745 if (proceed == MagickFalse)
2746 status=MagickFalse;
2747 }
2748 }
2749 image_view=DestroyCacheView(image_view);
2750 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002751 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002752 sample_image->type=image->type;
2753 return(sample_image);
2754}
2755
2756/*
2757%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2758% %
2759% %
2760% %
2761% S c a l e I m a g e %
2762% %
2763% %
2764% %
2765%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2766%
2767% ScaleImage() changes the size of an image to the given dimensions.
2768%
2769% The format of the ScaleImage method is:
2770%
cristybb503372010-05-27 20:51:26 +00002771% Image *ScaleImage(const Image *image,const size_t columns,
2772% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002773%
2774% A description of each parameter follows:
2775%
2776% o image: the image.
2777%
2778% o columns: the number of columns in the scaled image.
2779%
2780% o rows: the number of rows in the scaled image.
2781%
2782% o exception: return any errors or warnings in this structure.
2783%
2784*/
cristybb503372010-05-27 20:51:26 +00002785MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2786 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002787{
2788#define ScaleImageTag "Scale/Image"
2789
cristyed6cb232010-01-20 03:07:53 +00002790 CacheView
2791 *image_view,
2792 *scale_view;
2793
cristy3ed852e2009-09-05 21:47:34 +00002794 Image
2795 *scale_image;
2796
cristy3ed852e2009-09-05 21:47:34 +00002797 MagickBooleanType
2798 next_column,
2799 next_row,
2800 proceed;
2801
2802 MagickPixelPacket
2803 pixel,
2804 *scale_scanline,
2805 *scanline,
2806 *x_vector,
2807 *y_vector,
2808 zero;
2809
cristy3ed852e2009-09-05 21:47:34 +00002810 PointInfo
2811 scale,
2812 span;
2813
cristybb503372010-05-27 20:51:26 +00002814 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002815 i;
2816
cristy9af9b5d2010-08-15 17:04:28 +00002817 ssize_t
2818 number_rows,
2819 y;
2820
cristy3ed852e2009-09-05 21:47:34 +00002821 /*
2822 Initialize scaled image attributes.
2823 */
2824 assert(image != (const Image *) NULL);
2825 assert(image->signature == MagickSignature);
2826 if (image->debug != MagickFalse)
2827 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2828 assert(exception != (ExceptionInfo *) NULL);
2829 assert(exception->signature == MagickSignature);
2830 if ((columns == 0) || (rows == 0))
2831 return((Image *) NULL);
2832 if ((columns == image->columns) && (rows == image->rows))
2833 return(CloneImage(image,0,0,MagickTrue,exception));
2834 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2835 if (scale_image == (Image *) NULL)
2836 return((Image *) NULL);
2837 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2838 {
2839 InheritException(exception,&scale_image->exception);
2840 scale_image=DestroyImage(scale_image);
2841 return((Image *) NULL);
2842 }
2843 /*
2844 Allocate memory.
2845 */
2846 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2847 sizeof(*x_vector));
2848 scanline=x_vector;
2849 if (image->rows != scale_image->rows)
2850 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2851 sizeof(*scanline));
2852 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2853 scale_image->columns,sizeof(*scale_scanline));
2854 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2855 sizeof(*y_vector));
2856 if ((scanline == (MagickPixelPacket *) NULL) ||
2857 (scale_scanline == (MagickPixelPacket *) NULL) ||
2858 (x_vector == (MagickPixelPacket *) NULL) ||
2859 (y_vector == (MagickPixelPacket *) NULL))
2860 {
2861 scale_image=DestroyImage(scale_image);
2862 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2863 }
2864 /*
2865 Scale image.
2866 */
2867 number_rows=0;
2868 next_row=MagickTrue;
2869 span.y=1.0;
2870 scale.y=(double) scale_image->rows/(double) image->rows;
2871 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2872 sizeof(*y_vector));
2873 GetMagickPixelPacket(image,&pixel);
2874 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2875 i=0;
cristyed6cb232010-01-20 03:07:53 +00002876 image_view=AcquireCacheView(image);
2877 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002878 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002879 {
2880 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002881 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002882
2883 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002884 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002885
2886 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002887 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002888
cristy3ed852e2009-09-05 21:47:34 +00002889 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002890 *restrict s,
2891 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002892
2893 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002894 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002895
cristy9af9b5d2010-08-15 17:04:28 +00002896 register ssize_t
2897 x;
2898
cristyed6cb232010-01-20 03:07:53 +00002899 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2900 exception);
cristy3ed852e2009-09-05 21:47:34 +00002901 if (q == (PixelPacket *) NULL)
2902 break;
2903 scale_indexes=GetAuthenticIndexQueue(scale_image);
2904 if (scale_image->rows == image->rows)
2905 {
2906 /*
2907 Read a new scanline.
2908 */
cristyed6cb232010-01-20 03:07:53 +00002909 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2910 exception);
cristy3ed852e2009-09-05 21:47:34 +00002911 if (p == (const PixelPacket *) NULL)
2912 break;
cristyed6cb232010-01-20 03:07:53 +00002913 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002914 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002915 {
cristyce70c172010-01-07 17:15:30 +00002916 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2917 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2918 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002919 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002920 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002921 if (indexes != (IndexPacket *) NULL)
2922 x_vector[x].index=(MagickRealType) indexes[x];
2923 p++;
2924 }
2925 }
2926 else
2927 {
2928 /*
2929 Scale Y direction.
2930 */
2931 while (scale.y < span.y)
2932 {
cristy9af9b5d2010-08-15 17:04:28 +00002933 if ((next_row != MagickFalse) &&
2934 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002935 {
2936 /*
2937 Read a new scanline.
2938 */
cristyed6cb232010-01-20 03:07:53 +00002939 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2940 exception);
cristy3ed852e2009-09-05 21:47:34 +00002941 if (p == (const PixelPacket *) NULL)
2942 break;
cristyed6cb232010-01-20 03:07:53 +00002943 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002944 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002945 {
cristyce70c172010-01-07 17:15:30 +00002946 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2947 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2948 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002949 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002950 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002951 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002952 if (indexes != (IndexPacket *) NULL)
2953 x_vector[x].index=(MagickRealType) indexes[x];
2954 p++;
2955 }
2956 number_rows++;
2957 }
cristybb503372010-05-27 20:51:26 +00002958 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002959 {
2960 y_vector[x].red+=scale.y*x_vector[x].red;
2961 y_vector[x].green+=scale.y*x_vector[x].green;
2962 y_vector[x].blue+=scale.y*x_vector[x].blue;
2963 if (scale_image->matte != MagickFalse)
2964 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2965 if (scale_indexes != (IndexPacket *) NULL)
2966 y_vector[x].index+=scale.y*x_vector[x].index;
2967 }
2968 span.y-=scale.y;
2969 scale.y=(double) scale_image->rows/(double) image->rows;
2970 next_row=MagickTrue;
2971 }
cristybb503372010-05-27 20:51:26 +00002972 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002973 {
2974 /*
2975 Read a new scanline.
2976 */
cristyed6cb232010-01-20 03:07:53 +00002977 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2978 exception);
cristy3ed852e2009-09-05 21:47:34 +00002979 if (p == (const PixelPacket *) NULL)
2980 break;
cristyed6cb232010-01-20 03:07:53 +00002981 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002982 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002983 {
cristyce70c172010-01-07 17:15:30 +00002984 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2985 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2986 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002987 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00002988 x_vector[x].opacity=(MagickRealType)
2989 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002990 if (indexes != (IndexPacket *) NULL)
2991 x_vector[x].index=(MagickRealType) indexes[x];
2992 p++;
2993 }
2994 number_rows++;
2995 next_row=MagickFalse;
2996 }
2997 s=scanline;
cristybb503372010-05-27 20:51:26 +00002998 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002999 {
3000 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3001 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3002 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3003 if (image->matte != MagickFalse)
3004 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3005 if (scale_indexes != (IndexPacket *) NULL)
3006 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3007 s->red=pixel.red;
3008 s->green=pixel.green;
3009 s->blue=pixel.blue;
3010 if (scale_image->matte != MagickFalse)
3011 s->opacity=pixel.opacity;
3012 if (scale_indexes != (IndexPacket *) NULL)
3013 s->index=pixel.index;
3014 s++;
3015 y_vector[x]=zero;
3016 }
3017 scale.y-=span.y;
3018 if (scale.y <= 0)
3019 {
3020 scale.y=(double) scale_image->rows/(double) image->rows;
3021 next_row=MagickTrue;
3022 }
3023 span.y=1.0;
3024 }
3025 if (scale_image->columns == image->columns)
3026 {
3027 /*
3028 Transfer scanline to scaled image.
3029 */
3030 s=scanline;
cristybb503372010-05-27 20:51:26 +00003031 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003032 {
cristyce70c172010-01-07 17:15:30 +00003033 q->red=ClampToQuantum(s->red);
3034 q->green=ClampToQuantum(s->green);
3035 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003036 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003037 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003038 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003039 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003040 q++;
3041 s++;
3042 }
3043 }
3044 else
3045 {
3046 /*
3047 Scale X direction.
3048 */
3049 pixel=zero;
3050 next_column=MagickFalse;
3051 span.x=1.0;
3052 s=scanline;
3053 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003054 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003055 {
3056 scale.x=(double) scale_image->columns/(double) image->columns;
3057 while (scale.x >= span.x)
3058 {
3059 if (next_column != MagickFalse)
3060 {
3061 pixel=zero;
3062 t++;
3063 }
3064 pixel.red+=span.x*s->red;
3065 pixel.green+=span.x*s->green;
3066 pixel.blue+=span.x*s->blue;
3067 if (image->matte != MagickFalse)
3068 pixel.opacity+=span.x*s->opacity;
3069 if (scale_indexes != (IndexPacket *) NULL)
3070 pixel.index+=span.x*s->index;
3071 t->red=pixel.red;
3072 t->green=pixel.green;
3073 t->blue=pixel.blue;
3074 if (scale_image->matte != MagickFalse)
3075 t->opacity=pixel.opacity;
3076 if (scale_indexes != (IndexPacket *) NULL)
3077 t->index=pixel.index;
3078 scale.x-=span.x;
3079 span.x=1.0;
3080 next_column=MagickTrue;
3081 }
3082 if (scale.x > 0)
3083 {
3084 if (next_column != MagickFalse)
3085 {
3086 pixel=zero;
3087 next_column=MagickFalse;
3088 t++;
3089 }
3090 pixel.red+=scale.x*s->red;
3091 pixel.green+=scale.x*s->green;
3092 pixel.blue+=scale.x*s->blue;
3093 if (scale_image->matte != MagickFalse)
3094 pixel.opacity+=scale.x*s->opacity;
3095 if (scale_indexes != (IndexPacket *) NULL)
3096 pixel.index+=scale.x*s->index;
3097 span.x-=scale.x;
3098 }
3099 s++;
3100 }
3101 if (span.x > 0)
3102 {
3103 s--;
3104 pixel.red+=span.x*s->red;
3105 pixel.green+=span.x*s->green;
3106 pixel.blue+=span.x*s->blue;
3107 if (scale_image->matte != MagickFalse)
3108 pixel.opacity+=span.x*s->opacity;
3109 if (scale_indexes != (IndexPacket *) NULL)
3110 pixel.index+=span.x*s->index;
3111 }
3112 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003113 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003114 {
3115 t->red=pixel.red;
3116 t->green=pixel.green;
3117 t->blue=pixel.blue;
3118 if (scale_image->matte != MagickFalse)
3119 t->opacity=pixel.opacity;
3120 if (scale_indexes != (IndexPacket *) NULL)
3121 t->index=pixel.index;
3122 }
3123 /*
3124 Transfer scanline to scaled image.
3125 */
3126 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003127 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003128 {
cristyce70c172010-01-07 17:15:30 +00003129 q->red=ClampToQuantum(t->red);
3130 q->green=ClampToQuantum(t->green);
3131 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003132 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003133 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003134 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003135 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003136 t++;
3137 q++;
3138 }
3139 }
cristyed6cb232010-01-20 03:07:53 +00003140 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003141 break;
cristy96b16132010-08-29 17:19:52 +00003142 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3143 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003144 if (proceed == MagickFalse)
3145 break;
3146 }
cristyed6cb232010-01-20 03:07:53 +00003147 scale_view=DestroyCacheView(scale_view);
3148 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003149 /*
3150 Free allocated memory.
3151 */
3152 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3153 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3154 if (scale_image->rows != image->rows)
3155 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3156 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3157 scale_image->type=image->type;
3158 return(scale_image);
3159}
3160
anthony02b4cb42010-10-10 04:54:35 +00003161#if 0
3162 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003163/*
3164%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3165% %
3166% %
3167% %
3168+ S e t R e s i z e F i l t e r S u p p o r t %
3169% %
3170% %
3171% %
3172%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3173%
3174% SetResizeFilterSupport() specifies which IR filter to use to window
3175%
3176% The format of the SetResizeFilterSupport method is:
3177%
3178% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3179% const MagickRealType support)
3180%
3181% A description of each parameter follows:
3182%
3183% o resize_filter: the resize filter.
3184%
3185% o support: the filter spport radius.
3186%
3187*/
3188MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3189 const MagickRealType support)
3190{
3191 assert(resize_filter != (ResizeFilter *) NULL);
3192 assert(resize_filter->signature == MagickSignature);
3193 resize_filter->support=support;
3194}
anthony02b4cb42010-10-10 04:54:35 +00003195#endif
cristy3ed852e2009-09-05 21:47:34 +00003196
3197/*
3198%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3199% %
3200% %
3201% %
3202% T h u m b n a i l I m a g e %
3203% %
3204% %
3205% %
3206%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3207%
3208% ThumbnailImage() changes the size of an image to the given dimensions and
3209% removes any associated profiles. The goal is to produce small low cost
3210% thumbnail images suited for display on the Web.
3211%
3212% The format of the ThumbnailImage method is:
3213%
cristybb503372010-05-27 20:51:26 +00003214% Image *ThumbnailImage(const Image *image,const size_t columns,
3215% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003216%
3217% A description of each parameter follows:
3218%
3219% o image: the image.
3220%
3221% o columns: the number of columns in the scaled image.
3222%
3223% o rows: the number of rows in the scaled image.
3224%
3225% o exception: return any errors or warnings in this structure.
3226%
3227*/
cristy9af9b5d2010-08-15 17:04:28 +00003228MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3229 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003230{
3231#define SampleFactor 5
3232
3233 char
3234 value[MaxTextExtent];
3235
3236 const char
3237 *name;
3238
3239 Image
3240 *thumbnail_image;
3241
3242 MagickRealType
3243 x_factor,
3244 y_factor;
3245
cristybb503372010-05-27 20:51:26 +00003246 size_t
cristy3ed852e2009-09-05 21:47:34 +00003247 version;
3248
cristy9af9b5d2010-08-15 17:04:28 +00003249 struct stat
3250 attributes;
3251
cristy3ed852e2009-09-05 21:47:34 +00003252 assert(image != (Image *) NULL);
3253 assert(image->signature == MagickSignature);
3254 if (image->debug != MagickFalse)
3255 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3256 assert(exception != (ExceptionInfo *) NULL);
3257 assert(exception->signature == MagickSignature);
3258 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3259 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3260 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003261 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3262 exception);
cristy3ed852e2009-09-05 21:47:34 +00003263 else
3264 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003265 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3266 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003267 else
3268 {
3269 Image
3270 *sample_image;
3271
3272 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3273 exception);
3274 if (sample_image == (Image *) NULL)
3275 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003276 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3277 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003278 sample_image=DestroyImage(sample_image);
3279 }
3280 if (thumbnail_image == (Image *) NULL)
3281 return(thumbnail_image);
3282 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3283 if (thumbnail_image->matte == MagickFalse)
3284 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3285 thumbnail_image->depth=8;
3286 thumbnail_image->interlace=NoInterlace;
3287 /*
3288 Strip all profiles except color profiles.
3289 */
3290 ResetImageProfileIterator(thumbnail_image);
3291 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3292 {
3293 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3294 {
cristy2b726bd2010-01-11 01:05:39 +00003295 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003296 ResetImageProfileIterator(thumbnail_image);
3297 }
3298 name=GetNextImageProfile(thumbnail_image);
3299 }
3300 (void) DeleteImageProperty(thumbnail_image,"comment");
3301 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003302 if (strstr(image->magick_filename,"//") == (char *) NULL)
3303 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003304 image->magick_filename);
3305 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3306 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3307 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3308 {
cristye8c25f92010-06-03 00:53:06 +00003309 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003310 attributes.st_mtime);
3311 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3312 }
cristye8c25f92010-06-03 00:53:06 +00003313 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003314 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003315 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003316 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003317 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3318 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3319 LocaleLower(value);
3320 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3321 (void) SetImageProperty(thumbnail_image,"software",
3322 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003323 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3324 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003325 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003326 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003327 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003328 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003329 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3330 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003331 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3332 return(thumbnail_image);
3333}