blob: 953bf6d75b4f5910a571e46cf14431073ceb082d [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 */
anthonye06e4c12010-09-15 04:03:52 +00001031#if defined(MAGICKCORE_OPENMP_SUPPORT)
anthony72949792010-10-08 04:44:56 +00001032 #pragma omp single
1033 {
anthonye06e4c12010-09-15 04:03:52 +00001034#endif
1035 artifact=GetImageArtifact(image,"filter:verbose");
1036 if (artifact != (const char *) NULL)
1037 {
1038 double
1039 support,
1040 x;
cristy3ed852e2009-09-05 21:47:34 +00001041
nicolas07bac812010-09-19 18:47:02 +00001042 /*
anthony463be1d2010-09-26 01:07:36 +00001043 Set the weighting function properly when the weighting
1044 function may not exactly match the filter of the same name.
1045 EG: a Point filter really uses a Box weighting function
1046 with a different support than is typically used.
1047
anthonye06e4c12010-09-15 04:03:52 +00001048 */
anthony463be1d2010-09-26 01:07:36 +00001049 if (resize_filter->filter == Box) filter_type=BoxFilter;
1050 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1051 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
anthony61b5ddd2010-10-05 02:33:31 +00001052 if (resize_filter->filter == Jinc) filter_type=JincFilter;
anthony463be1d2010-09-26 01:07:36 +00001053 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
anthonye06e4c12010-09-15 04:03:52 +00001054 /*
nicolas07bac812010-09-19 18:47:02 +00001055 Report Filter Details.
anthonye06e4c12010-09-15 04:03:52 +00001056 */
anthonyf5e76ef2010-10-12 01:22:01 +00001057 support=GetResizeFilterSupport(resize_filter); /* practical_support */
anthony61b5ddd2010-10-05 02:33:31 +00001058 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
cristy03dbbd22010-09-19 23:04:47 +00001059 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1060 MagickFilterOptions,filter_type));
1061 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
anthony463be1d2010-09-26 01:07:36 +00001062 MagickFilterOptions, window_type));
cristy03dbbd22010-09-19 23:04:47 +00001063 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
anthony463be1d2010-09-26 01:07:36 +00001064 (double) resize_filter->support);
cristy03dbbd22010-09-19 23:04:47 +00001065 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
anthony463be1d2010-09-26 01:07:36 +00001066 (double) resize_filter->window_support);
anthonyf5e76ef2010-10-12 01:22:01 +00001067 (void) fprintf(stdout,"# scale_blur = %.*g\n",GetMagickPrecision(),
cristy03dbbd22010-09-19 23:04:47 +00001068 (double) resize_filter->blur);
anthonyf5e76ef2010-10-12 01:22:01 +00001069 if ( filter_type == GaussianFilter )
1070 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",GetMagickPrecision(),
1071 (double) sigma);
1072 (void) fprintf(stdout,"# practical_support = %.*g\n",GetMagickPrecision(),
cristy03dbbd22010-09-19 23:04:47 +00001073 (double) support);
anthonydc592432010-10-12 02:59:44 +00001074 if ( filter_type == CubicFilter || window_type == CubicFilter )
1075 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1076 (double) B,GetMagickPrecision(),(double) C);
anthony61b5ddd2010-10-05 02:33:31 +00001077 (void) fprintf(stdout,"\n");
anthonye06e4c12010-09-15 04:03:52 +00001078 /*
nicolas07bac812010-09-19 18:47:02 +00001079 Output values of resulting filter graph -- for graphing
1080 filter result.
anthonye06e4c12010-09-15 04:03:52 +00001081 */
1082 for (x=0.0; x <= support; x+=0.01f)
cristy03dbbd22010-09-19 23:04:47 +00001083 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1084 (double) GetResizeFilterWeight(resize_filter,x));
nicolas07bac812010-09-19 18:47:02 +00001085 /* A final value so gnuplot can graph the 'stop' properly. */
cristy03dbbd22010-09-19 23:04:47 +00001086 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1087 0.0);
anthonye06e4c12010-09-15 04:03:52 +00001088 }
anthonyf5e76ef2010-10-12 01:22:01 +00001089 /* Output the above once only for each image - remove setting */
cristybb66d9c2010-10-09 01:40:31 +00001090 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
anthonye06e4c12010-09-15 04:03:52 +00001091#if defined(MAGICKCORE_OPENMP_SUPPORT)
anthony61b5ddd2010-10-05 02:33:31 +00001092 }
anthonye06e4c12010-09-15 04:03:52 +00001093#endif
anthonyf5e76ef2010-10-12 01:22:01 +00001094
cristy3ed852e2009-09-05 21:47:34 +00001095 return(resize_filter);
1096}
1097
1098/*
1099%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1100% %
1101% %
1102% %
1103% A d a p t i v e R e s i z e I m a g e %
1104% %
1105% %
1106% %
1107%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1108%
1109% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1110%
1111% The format of the AdaptiveResizeImage method is:
1112%
cristy9af9b5d2010-08-15 17:04:28 +00001113% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1114% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001115%
1116% A description of each parameter follows:
1117%
1118% o image: the image.
1119%
1120% o columns: the number of columns in the resized image.
1121%
1122% o rows: the number of rows in the resized image.
1123%
1124% o exception: return any errors or warnings in this structure.
1125%
1126*/
1127MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001128 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001129{
1130#define AdaptiveResizeImageTag "Resize/Image"
1131
cristyc4c8d132010-01-07 01:58:38 +00001132 CacheView
1133 *resize_view;
1134
cristy3ed852e2009-09-05 21:47:34 +00001135 Image
1136 *resize_image;
1137
cristy3ed852e2009-09-05 21:47:34 +00001138 MagickBooleanType
1139 proceed;
1140
1141 MagickPixelPacket
1142 pixel;
1143
1144 PointInfo
1145 offset;
1146
1147 ResampleFilter
1148 *resample_filter;
1149
cristy9af9b5d2010-08-15 17:04:28 +00001150 ssize_t
1151 y;
1152
cristy3ed852e2009-09-05 21:47:34 +00001153 /*
1154 Adaptively resize image.
1155 */
1156 assert(image != (const Image *) NULL);
1157 assert(image->signature == MagickSignature);
1158 if (image->debug != MagickFalse)
1159 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1160 assert(exception != (ExceptionInfo *) NULL);
1161 assert(exception->signature == MagickSignature);
1162 if ((columns == 0) || (rows == 0))
1163 return((Image *) NULL);
1164 if ((columns == image->columns) && (rows == image->rows))
1165 return(CloneImage(image,0,0,MagickTrue,exception));
1166 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1167 if (resize_image == (Image *) NULL)
1168 return((Image *) NULL);
1169 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1170 {
1171 InheritException(exception,&resize_image->exception);
1172 resize_image=DestroyImage(resize_image);
1173 return((Image *) NULL);
1174 }
1175 GetMagickPixelPacket(image,&pixel);
1176 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001177 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001178 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001179 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001180 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001181 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001182 {
1183 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001184 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001185
cristybb503372010-05-27 20:51:26 +00001186 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001187 x;
1188
1189 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001190 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001191
1192 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1193 exception);
1194 if (q == (PixelPacket *) NULL)
1195 break;
1196 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1197 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001198 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001199 {
1200 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1201 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1202 &pixel);
1203 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1204 q++;
1205 }
1206 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1207 break;
cristy96b16132010-08-29 17:19:52 +00001208 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1209 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001210 if (proceed == MagickFalse)
1211 break;
1212 }
1213 resample_filter=DestroyResampleFilter(resample_filter);
1214 resize_view=DestroyCacheView(resize_view);
1215 return(resize_image);
1216}
1217
1218/*
1219%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1220% %
1221% %
1222% %
1223+ B e s s e l O r d e r O n e %
1224% %
1225% %
1226% %
1227%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1228%
1229% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001230% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001231%
1232% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1233%
1234% j1(x) = x*j1(x);
1235%
1236% For x in (8,inf)
1237%
1238% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1239%
1240% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1241%
1242% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1243% = 1/sqrt(2) * (sin(x) - cos(x))
1244% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1245% = -1/sqrt(2) * (sin(x) + cos(x))
1246%
1247% The format of the BesselOrderOne method is:
1248%
1249% MagickRealType BesselOrderOne(MagickRealType x)
1250%
1251% A description of each parameter follows:
1252%
1253% o x: MagickRealType value.
1254%
1255*/
1256
1257#undef I0
1258static MagickRealType I0(MagickRealType x)
1259{
1260 MagickRealType
1261 sum,
1262 t,
1263 y;
1264
cristybb503372010-05-27 20:51:26 +00001265 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001266 i;
1267
1268 /*
1269 Zeroth order Bessel function of the first kind.
1270 */
1271 sum=1.0;
1272 y=x*x/4.0;
1273 t=y;
1274 for (i=2; t > MagickEpsilon; i++)
1275 {
1276 sum+=t;
1277 t*=y/((MagickRealType) i*i);
1278 }
1279 return(sum);
1280}
1281
1282#undef J1
1283static MagickRealType J1(MagickRealType x)
1284{
1285 MagickRealType
1286 p,
1287 q;
1288
cristybb503372010-05-27 20:51:26 +00001289 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001290 i;
1291
1292 static const double
1293 Pone[] =
1294 {
1295 0.581199354001606143928050809e+21,
1296 -0.6672106568924916298020941484e+20,
1297 0.2316433580634002297931815435e+19,
1298 -0.3588817569910106050743641413e+17,
1299 0.2908795263834775409737601689e+15,
1300 -0.1322983480332126453125473247e+13,
1301 0.3413234182301700539091292655e+10,
1302 -0.4695753530642995859767162166e+7,
1303 0.270112271089232341485679099e+4
1304 },
1305 Qone[] =
1306 {
1307 0.11623987080032122878585294e+22,
1308 0.1185770712190320999837113348e+20,
1309 0.6092061398917521746105196863e+17,
1310 0.2081661221307607351240184229e+15,
1311 0.5243710262167649715406728642e+12,
1312 0.1013863514358673989967045588e+10,
1313 0.1501793594998585505921097578e+7,
1314 0.1606931573481487801970916749e+4,
1315 0.1e+1
1316 };
1317
1318 p=Pone[8];
1319 q=Qone[8];
1320 for (i=7; i >= 0; i--)
1321 {
1322 p=p*x*x+Pone[i];
1323 q=q*x*x+Qone[i];
1324 }
1325 return(p/q);
1326}
1327
1328#undef P1
1329static MagickRealType P1(MagickRealType x)
1330{
1331 MagickRealType
1332 p,
1333 q;
1334
cristybb503372010-05-27 20:51:26 +00001335 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001336 i;
1337
1338 static const double
1339 Pone[] =
1340 {
1341 0.352246649133679798341724373e+5,
1342 0.62758845247161281269005675e+5,
1343 0.313539631109159574238669888e+5,
1344 0.49854832060594338434500455e+4,
1345 0.2111529182853962382105718e+3,
1346 0.12571716929145341558495e+1
1347 },
1348 Qone[] =
1349 {
1350 0.352246649133679798068390431e+5,
1351 0.626943469593560511888833731e+5,
1352 0.312404063819041039923015703e+5,
1353 0.4930396490181088979386097e+4,
1354 0.2030775189134759322293574e+3,
1355 0.1e+1
1356 };
1357
1358 p=Pone[5];
1359 q=Qone[5];
1360 for (i=4; i >= 0; i--)
1361 {
1362 p=p*(8.0/x)*(8.0/x)+Pone[i];
1363 q=q*(8.0/x)*(8.0/x)+Qone[i];
1364 }
1365 return(p/q);
1366}
1367
1368#undef Q1
1369static MagickRealType Q1(MagickRealType x)
1370{
1371 MagickRealType
1372 p,
1373 q;
1374
cristybb503372010-05-27 20:51:26 +00001375 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001376 i;
1377
1378 static const double
1379 Pone[] =
1380 {
1381 0.3511751914303552822533318e+3,
1382 0.7210391804904475039280863e+3,
1383 0.4259873011654442389886993e+3,
1384 0.831898957673850827325226e+2,
1385 0.45681716295512267064405e+1,
1386 0.3532840052740123642735e-1
1387 },
1388 Qone[] =
1389 {
1390 0.74917374171809127714519505e+4,
1391 0.154141773392650970499848051e+5,
1392 0.91522317015169922705904727e+4,
1393 0.18111867005523513506724158e+4,
1394 0.1038187585462133728776636e+3,
1395 0.1e+1
1396 };
1397
1398 p=Pone[5];
1399 q=Qone[5];
1400 for (i=4; i >= 0; i--)
1401 {
1402 p=p*(8.0/x)*(8.0/x)+Pone[i];
1403 q=q*(8.0/x)*(8.0/x)+Qone[i];
1404 }
1405 return(p/q);
1406}
1407
1408static MagickRealType BesselOrderOne(MagickRealType x)
1409{
1410 MagickRealType
1411 p,
1412 q;
1413
1414 if (x == 0.0)
1415 return(0.0);
1416 p=x;
1417 if (x < 0.0)
1418 x=(-x);
1419 if (x < 8.0)
1420 return(p*J1(x));
1421 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1422 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1423 cos((double) x))));
1424 if (p < 0.0)
1425 q=(-q);
1426 return(q);
1427}
1428
1429/*
1430%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1431% %
1432% %
1433% %
1434+ D e s t r o y R e s i z e F i l t e r %
1435% %
1436% %
1437% %
1438%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1439%
1440% DestroyResizeFilter() destroy the resize filter.
1441%
cristya2ffd7e2010-03-10 20:50:30 +00001442% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001443%
1444% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1445%
1446% A description of each parameter follows:
1447%
1448% o resize_filter: the resize filter.
1449%
1450*/
1451MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1452{
1453 assert(resize_filter != (ResizeFilter *) NULL);
1454 assert(resize_filter->signature == MagickSignature);
1455 resize_filter->signature=(~MagickSignature);
1456 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1457 return(resize_filter);
1458}
1459
1460/*
1461%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1462% %
1463% %
1464% %
1465+ G e t R e s i z e F i l t e r S u p p o r t %
1466% %
1467% %
1468% %
1469%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1470%
1471% GetResizeFilterSupport() return the current support window size for this
1472% filter. Note that this may have been enlarged by filter:blur factor.
1473%
1474% The format of the GetResizeFilterSupport method is:
1475%
1476% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1477%
1478% A description of each parameter follows:
1479%
1480% o filter: Image filter to use.
1481%
1482*/
1483MagickExport MagickRealType GetResizeFilterSupport(
1484 const ResizeFilter *resize_filter)
1485{
1486 assert(resize_filter != (ResizeFilter *) NULL);
1487 assert(resize_filter->signature == MagickSignature);
1488 return(resize_filter->support*resize_filter->blur);
1489}
1490
1491/*
1492%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1493% %
1494% %
1495% %
1496+ G e t R e s i z e F i l t e r W e i g h t %
1497% %
1498% %
1499% %
1500%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1501%
1502% GetResizeFilterWeight evaluates the specified resize filter at the point x
1503% which usally lies between zero and the filters current 'support' and
1504% returns the weight of the filter function at that point.
1505%
1506% The format of the GetResizeFilterWeight method is:
1507%
1508% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1509% const MagickRealType x)
1510%
1511% A description of each parameter follows:
1512%
1513% o filter: the filter type.
1514%
1515% o x: the point.
1516%
1517*/
1518MagickExport MagickRealType GetResizeFilterWeight(
1519 const ResizeFilter *resize_filter,const MagickRealType x)
1520{
1521 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001522 scale,
1523 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001524
1525 /*
1526 Windowing function - scale the weighting filter by this amount.
1527 */
1528 assert(resize_filter != (ResizeFilter *) NULL);
1529 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001530 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001531 if ((resize_filter->window_support < MagickEpsilon) ||
1532 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001533 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001534 else
1535 {
anthony55f12332010-09-10 01:13:02 +00001536 scale=resize_filter->scale;
1537 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001538 }
anthony55f12332010-09-10 01:13:02 +00001539 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001540}
1541
1542/*
1543%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1544% %
1545% %
1546% %
1547% M a g n i f y I m a g e %
1548% %
1549% %
1550% %
1551%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1552%
1553% MagnifyImage() is a convenience method that scales an image proportionally
1554% to twice its size.
1555%
1556% The format of the MagnifyImage method is:
1557%
1558% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1559%
1560% A description of each parameter follows:
1561%
1562% o image: the image.
1563%
1564% o exception: return any errors or warnings in this structure.
1565%
1566*/
1567MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1568{
1569 Image
1570 *magnify_image;
1571
1572 assert(image != (Image *) NULL);
1573 assert(image->signature == MagickSignature);
1574 if (image->debug != MagickFalse)
1575 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1576 assert(exception != (ExceptionInfo *) NULL);
1577 assert(exception->signature == MagickSignature);
1578 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1579 1.0,exception);
1580 return(magnify_image);
1581}
1582
1583/*
1584%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1585% %
1586% %
1587% %
1588% M i n i f y I m a g e %
1589% %
1590% %
1591% %
1592%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1593%
1594% MinifyImage() is a convenience method that scales an image proportionally
1595% to half its size.
1596%
1597% The format of the MinifyImage method is:
1598%
1599% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1600%
1601% A description of each parameter follows:
1602%
1603% o image: the image.
1604%
1605% o exception: return any errors or warnings in this structure.
1606%
1607*/
1608MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1609{
1610 Image
1611 *minify_image;
1612
1613 assert(image != (Image *) NULL);
1614 assert(image->signature == MagickSignature);
1615 if (image->debug != MagickFalse)
1616 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1617 assert(exception != (ExceptionInfo *) NULL);
1618 assert(exception->signature == MagickSignature);
1619 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1620 1.0,exception);
1621 return(minify_image);
1622}
1623
1624/*
1625%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1626% %
1627% %
1628% %
1629% R e s a m p l e I m a g e %
1630% %
1631% %
1632% %
1633%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1634%
1635% ResampleImage() resize image in terms of its pixel size, so that when
1636% displayed at the given resolution it will be the same size in terms of
1637% real world units as the original image at the original resolution.
1638%
1639% The format of the ResampleImage method is:
1640%
1641% Image *ResampleImage(Image *image,const double x_resolution,
1642% const double y_resolution,const FilterTypes filter,const double blur,
1643% ExceptionInfo *exception)
1644%
1645% A description of each parameter follows:
1646%
1647% o image: the image to be resized to fit the given resolution.
1648%
1649% o x_resolution: the new image x resolution.
1650%
1651% o y_resolution: the new image y resolution.
1652%
1653% o filter: Image filter to use.
1654%
1655% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1656%
1657*/
1658MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1659 const double y_resolution,const FilterTypes filter,const double blur,
1660 ExceptionInfo *exception)
1661{
1662#define ResampleImageTag "Resample/Image"
1663
1664 Image
1665 *resample_image;
1666
cristybb503372010-05-27 20:51:26 +00001667 size_t
cristy3ed852e2009-09-05 21:47:34 +00001668 height,
1669 width;
1670
1671 /*
1672 Initialize sampled image attributes.
1673 */
1674 assert(image != (const Image *) NULL);
1675 assert(image->signature == MagickSignature);
1676 if (image->debug != MagickFalse)
1677 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1678 assert(exception != (ExceptionInfo *) NULL);
1679 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001680 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1681 72.0 : image->x_resolution)+0.5);
1682 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1683 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001684 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1685 if (resample_image != (Image *) NULL)
1686 {
1687 resample_image->x_resolution=x_resolution;
1688 resample_image->y_resolution=y_resolution;
1689 }
1690 return(resample_image);
1691}
1692#if defined(MAGICKCORE_LQR_DELEGATE)
1693
1694/*
1695%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1696% %
1697% %
1698% %
1699% L i q u i d R e s c a l e I m a g e %
1700% %
1701% %
1702% %
1703%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1704%
1705% LiquidRescaleImage() rescales image with seam carving.
1706%
1707% The format of the LiquidRescaleImage method is:
1708%
1709% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001710% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001711% const double delta_x,const double rigidity,ExceptionInfo *exception)
1712%
1713% A description of each parameter follows:
1714%
1715% o image: the image.
1716%
1717% o columns: the number of columns in the rescaled image.
1718%
1719% o rows: the number of rows in the rescaled image.
1720%
1721% o delta_x: maximum seam transversal step (0 means straight seams).
1722%
1723% o rigidity: introduce a bias for non-straight seams (typically 0).
1724%
1725% o exception: return any errors or warnings in this structure.
1726%
1727*/
cristy9af9b5d2010-08-15 17:04:28 +00001728MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1729 const size_t rows,const double delta_x,const double rigidity,
1730 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001731{
1732#define LiquidRescaleImageTag "Rescale/Image"
1733
cristyc5c6f662010-09-22 14:23:02 +00001734 CacheView
1735 *rescale_view;
1736
cristy3ed852e2009-09-05 21:47:34 +00001737 const char
1738 *map;
1739
1740 guchar
1741 *packet;
1742
1743 Image
1744 *rescale_image;
1745
1746 int
1747 x,
1748 y;
1749
1750 LqrCarver
1751 *carver;
1752
1753 LqrRetVal
1754 lqr_status;
1755
1756 MagickBooleanType
1757 status;
1758
1759 MagickPixelPacket
1760 pixel;
1761
1762 unsigned char
1763 *pixels;
1764
1765 /*
1766 Liquid rescale image.
1767 */
1768 assert(image != (const Image *) NULL);
1769 assert(image->signature == MagickSignature);
1770 if (image->debug != MagickFalse)
1771 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1772 assert(exception != (ExceptionInfo *) NULL);
1773 assert(exception->signature == MagickSignature);
1774 if ((columns == 0) || (rows == 0))
1775 return((Image *) NULL);
1776 if ((columns == image->columns) && (rows == image->rows))
1777 return(CloneImage(image,0,0,MagickTrue,exception));
1778 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001779 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001780 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1781 {
1782 Image
1783 *resize_image;
1784
cristybb503372010-05-27 20:51:26 +00001785 size_t
cristy3ed852e2009-09-05 21:47:34 +00001786 height,
1787 width;
1788
1789 /*
1790 Honor liquid resize size limitations.
1791 */
1792 for (width=image->columns; columns >= (2*width-1); width*=2);
1793 for (height=image->rows; rows >= (2*height-1); height*=2);
1794 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1795 exception);
1796 if (resize_image == (Image *) NULL)
1797 return((Image *) NULL);
1798 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1799 rigidity,exception);
1800 resize_image=DestroyImage(resize_image);
1801 return(rescale_image);
1802 }
1803 map="RGB";
1804 if (image->matte == MagickFalse)
1805 map="RGBA";
1806 if (image->colorspace == CMYKColorspace)
1807 {
1808 map="CMYK";
1809 if (image->matte == MagickFalse)
1810 map="CMYKA";
1811 }
1812 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1813 strlen(map)*sizeof(*pixels));
1814 if (pixels == (unsigned char *) NULL)
1815 return((Image *) NULL);
1816 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1817 pixels,exception);
1818 if (status == MagickFalse)
1819 {
1820 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1821 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1822 }
1823 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1824 if (carver == (LqrCarver *) NULL)
1825 {
1826 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1827 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1828 }
1829 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1830 lqr_status=lqr_carver_resize(carver,columns,rows);
1831 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1832 lqr_carver_get_height(carver),MagickTrue,exception);
1833 if (rescale_image == (Image *) NULL)
1834 {
1835 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1836 return((Image *) NULL);
1837 }
1838 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1839 {
1840 InheritException(exception,&rescale_image->exception);
1841 rescale_image=DestroyImage(rescale_image);
1842 return((Image *) NULL);
1843 }
1844 GetMagickPixelPacket(rescale_image,&pixel);
1845 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001846 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001847 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1848 {
1849 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001850 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001851
1852 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001853 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001854
anthony22aad252010-09-23 06:59:07 +00001855 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001856 if (q == (PixelPacket *) NULL)
1857 break;
cristyc5c6f662010-09-22 14:23:02 +00001858 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001859 pixel.red=QuantumRange*(packet[0]/255.0);
1860 pixel.green=QuantumRange*(packet[1]/255.0);
1861 pixel.blue=QuantumRange*(packet[2]/255.0);
1862 if (image->colorspace != CMYKColorspace)
1863 {
1864 if (image->matte == MagickFalse)
1865 pixel.opacity=QuantumRange*(packet[3]/255.0);
1866 }
1867 else
1868 {
1869 pixel.index=QuantumRange*(packet[3]/255.0);
1870 if (image->matte == MagickFalse)
1871 pixel.opacity=QuantumRange*(packet[4]/255.0);
1872 }
1873 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001874 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001875 break;
1876 }
cristyc5c6f662010-09-22 14:23:02 +00001877 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001878 /*
1879 Relinquish resources.
1880 */
1881 lqr_carver_destroy(carver);
1882 return(rescale_image);
1883}
1884#else
1885MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001886 const size_t magick_unused(columns),const size_t magick_unused(rows),
1887 const double magick_unused(delta_x),const double magick_unused(rigidity),
1888 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001889{
1890 assert(image != (const Image *) NULL);
1891 assert(image->signature == MagickSignature);
1892 if (image->debug != MagickFalse)
1893 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1894 assert(exception != (ExceptionInfo *) NULL);
1895 assert(exception->signature == MagickSignature);
1896 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1897 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1898 return((Image *) NULL);
1899}
1900#endif
1901
1902/*
1903%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1904% %
1905% %
1906% %
1907% R e s i z e I m a g e %
1908% %
1909% %
1910% %
1911%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1912%
1913% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001914% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001915%
1916% If an undefined filter is given the filter defaults to Mitchell for a
1917% colormapped image, a image with a matte channel, or if the image is
1918% enlarged. Otherwise the filter defaults to a Lanczos.
1919%
1920% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1921%
1922% The format of the ResizeImage method is:
1923%
cristybb503372010-05-27 20:51:26 +00001924% Image *ResizeImage(Image *image,const size_t columns,
1925% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001926% ExceptionInfo *exception)
1927%
1928% A description of each parameter follows:
1929%
1930% o image: the image.
1931%
1932% o columns: the number of columns in the scaled image.
1933%
1934% o rows: the number of rows in the scaled image.
1935%
1936% o filter: Image filter to use.
1937%
cristy9af9b5d2010-08-15 17:04:28 +00001938% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1939% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001940%
1941% o exception: return any errors or warnings in this structure.
1942%
1943*/
1944
1945typedef struct _ContributionInfo
1946{
1947 MagickRealType
1948 weight;
1949
cristybb503372010-05-27 20:51:26 +00001950 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001951 pixel;
1952} ContributionInfo;
1953
1954static ContributionInfo **DestroyContributionThreadSet(
1955 ContributionInfo **contribution)
1956{
cristybb503372010-05-27 20:51:26 +00001957 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001958 i;
1959
1960 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001961 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001962 if (contribution[i] != (ContributionInfo *) NULL)
1963 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1964 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001965 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001966 return(contribution);
1967}
1968
1969static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1970{
cristybb503372010-05-27 20:51:26 +00001971 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001972 i;
1973
1974 ContributionInfo
1975 **contribution;
1976
cristybb503372010-05-27 20:51:26 +00001977 size_t
cristy3ed852e2009-09-05 21:47:34 +00001978 number_threads;
1979
1980 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001981 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001982 sizeof(*contribution));
1983 if (contribution == (ContributionInfo **) NULL)
1984 return((ContributionInfo **) NULL);
1985 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00001986 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00001987 {
1988 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1989 sizeof(**contribution));
1990 if (contribution[i] == (ContributionInfo *) NULL)
1991 return(DestroyContributionThreadSet(contribution));
1992 }
1993 return(contribution);
1994}
1995
1996static inline double MagickMax(const double x,const double y)
1997{
1998 if (x > y)
1999 return(x);
2000 return(y);
2001}
2002
2003static inline double MagickMin(const double x,const double y)
2004{
2005 if (x < y)
2006 return(x);
2007 return(y);
2008}
2009
2010static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2011 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002012 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002013{
2014#define ResizeImageTag "Resize/Image"
2015
cristyfa112112010-01-04 17:48:07 +00002016 CacheView
2017 *image_view,
2018 *resize_view;
2019
cristy3ed852e2009-09-05 21:47:34 +00002020 ClassType
2021 storage_class;
2022
2023 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002024 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002025
cristy3ed852e2009-09-05 21:47:34 +00002026 MagickBooleanType
2027 status;
2028
2029 MagickPixelPacket
2030 zero;
2031
2032 MagickRealType
2033 scale,
2034 support;
2035
cristy9af9b5d2010-08-15 17:04:28 +00002036 ssize_t
2037 x;
2038
cristy3ed852e2009-09-05 21:47:34 +00002039 /*
2040 Apply filter to resize horizontally from image to resize image.
2041 */
cristy5d824382010-09-06 14:00:17 +00002042 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002043 support=scale*GetResizeFilterSupport(resize_filter);
2044 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2045 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2046 {
2047 InheritException(exception,&resize_image->exception);
2048 return(MagickFalse);
2049 }
2050 if (support < 0.5)
2051 {
2052 /*
nicolas07bac812010-09-19 18:47:02 +00002053 Support too small even for nearest neighbour: Reduce to point
2054 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002055 */
2056 support=(MagickRealType) 0.5;
2057 scale=1.0;
2058 }
2059 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2060 if (contributions == (ContributionInfo **) NULL)
2061 {
2062 (void) ThrowMagickException(exception,GetMagickModule(),
2063 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2064 return(MagickFalse);
2065 }
2066 status=MagickTrue;
2067 scale=1.0/scale;
2068 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2069 image_view=AcquireCacheView(image);
2070 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002071#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002072 #pragma omp parallel for shared(status)
2073#endif
cristybb503372010-05-27 20:51:26 +00002074 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002075 {
cristy3ed852e2009-09-05 21:47:34 +00002076 MagickRealType
2077 center,
2078 density;
2079
2080 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002081 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002082
2083 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002084 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002085
cristy03dbbd22010-09-19 23:04:47 +00002086 register ContributionInfo
2087 *restrict contribution;
2088
cristy3ed852e2009-09-05 21:47:34 +00002089 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002090 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002091
cristy3ed852e2009-09-05 21:47:34 +00002092 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002093 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002094
cristy03dbbd22010-09-19 23:04:47 +00002095 register ssize_t
2096 y;
2097
cristy9af9b5d2010-08-15 17:04:28 +00002098 ssize_t
2099 n,
2100 start,
2101 stop;
2102
cristy3ed852e2009-09-05 21:47:34 +00002103 if (status == MagickFalse)
2104 continue;
2105 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002106 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2107 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002108 density=0.0;
2109 contribution=contributions[GetOpenMPThreadId()];
2110 for (n=0; n < (stop-start); n++)
2111 {
2112 contribution[n].pixel=start+n;
2113 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2114 ((MagickRealType) (start+n)-center+0.5));
2115 density+=contribution[n].weight;
2116 }
2117 if ((density != 0.0) && (density != 1.0))
2118 {
cristybb503372010-05-27 20:51:26 +00002119 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002120 i;
2121
2122 /*
2123 Normalize.
2124 */
2125 density=1.0/density;
2126 for (i=0; i < n; i++)
2127 contribution[i].weight*=density;
2128 }
cristy9af9b5d2010-08-15 17:04:28 +00002129 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2130 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002131 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2132 exception);
2133 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2134 {
2135 status=MagickFalse;
2136 continue;
2137 }
2138 indexes=GetCacheViewVirtualIndexQueue(image_view);
2139 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002140 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002141 {
cristy3ed852e2009-09-05 21:47:34 +00002142 MagickPixelPacket
2143 pixel;
2144
2145 MagickRealType
2146 alpha;
2147
cristybb503372010-05-27 20:51:26 +00002148 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002149 i;
2150
cristy9af9b5d2010-08-15 17:04:28 +00002151 ssize_t
2152 j;
2153
cristy3ed852e2009-09-05 21:47:34 +00002154 pixel=zero;
2155 if (image->matte == MagickFalse)
2156 {
2157 for (i=0; i < n; i++)
2158 {
2159 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2160 (contribution[i].pixel-contribution[0].pixel);
2161 alpha=contribution[i].weight;
2162 pixel.red+=alpha*(p+j)->red;
2163 pixel.green+=alpha*(p+j)->green;
2164 pixel.blue+=alpha*(p+j)->blue;
2165 pixel.opacity+=alpha*(p+j)->opacity;
2166 }
cristyce70c172010-01-07 17:15:30 +00002167 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2168 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2169 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2170 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002171 if ((image->colorspace == CMYKColorspace) &&
2172 (resize_image->colorspace == CMYKColorspace))
2173 {
2174 for (i=0; i < n; i++)
2175 {
2176 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2177 (contribution[i].pixel-contribution[0].pixel);
2178 alpha=contribution[i].weight;
2179 pixel.index+=alpha*indexes[j];
2180 }
cristyce70c172010-01-07 17:15:30 +00002181 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002182 }
2183 }
2184 else
2185 {
2186 MagickRealType
2187 gamma;
2188
2189 gamma=0.0;
2190 for (i=0; i < n; i++)
2191 {
2192 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2193 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002194 alpha=contribution[i].weight*QuantumScale*
2195 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002196 pixel.red+=alpha*(p+j)->red;
2197 pixel.green+=alpha*(p+j)->green;
2198 pixel.blue+=alpha*(p+j)->blue;
2199 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2200 gamma+=alpha;
2201 }
2202 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002203 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2204 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2205 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2206 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002207 if ((image->colorspace == CMYKColorspace) &&
2208 (resize_image->colorspace == CMYKColorspace))
2209 {
2210 for (i=0; i < n; i++)
2211 {
2212 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2213 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002214 alpha=contribution[i].weight*QuantumScale*
2215 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002216 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002217 }
cristyce70c172010-01-07 17:15:30 +00002218 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2219 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002220 }
2221 }
2222 if ((resize_image->storage_class == PseudoClass) &&
2223 (image->storage_class == PseudoClass))
2224 {
cristybb503372010-05-27 20:51:26 +00002225 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002226 1.0)+0.5);
2227 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2228 (contribution[i-start].pixel-contribution[0].pixel);
2229 resize_indexes[y]=indexes[j];
2230 }
2231 q++;
2232 }
2233 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2234 status=MagickFalse;
2235 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2236 {
2237 MagickBooleanType
2238 proceed;
2239
cristyb5d5f722009-11-04 03:03:49 +00002240#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002241 #pragma omp critical (MagickCore_HorizontalFilter)
2242#endif
cristy9af9b5d2010-08-15 17:04:28 +00002243 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002244 if (proceed == MagickFalse)
2245 status=MagickFalse;
2246 }
2247 }
2248 resize_view=DestroyCacheView(resize_view);
2249 image_view=DestroyCacheView(image_view);
2250 contributions=DestroyContributionThreadSet(contributions);
2251 return(status);
2252}
2253
2254static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2255 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002256 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002257{
cristyfa112112010-01-04 17:48:07 +00002258 CacheView
2259 *image_view,
2260 *resize_view;
2261
cristy3ed852e2009-09-05 21:47:34 +00002262 ClassType
2263 storage_class;
2264
2265 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002266 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002267
cristy3ed852e2009-09-05 21:47:34 +00002268 MagickBooleanType
2269 status;
2270
2271 MagickPixelPacket
2272 zero;
2273
2274 MagickRealType
2275 scale,
2276 support;
2277
cristy9af9b5d2010-08-15 17:04:28 +00002278 ssize_t
2279 y;
2280
cristy3ed852e2009-09-05 21:47:34 +00002281 /*
cristy9af9b5d2010-08-15 17:04:28 +00002282 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002283 */
cristy5d824382010-09-06 14:00:17 +00002284 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002285 support=scale*GetResizeFilterSupport(resize_filter);
2286 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2287 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2288 {
2289 InheritException(exception,&resize_image->exception);
2290 return(MagickFalse);
2291 }
2292 if (support < 0.5)
2293 {
2294 /*
nicolas07bac812010-09-19 18:47:02 +00002295 Support too small even for nearest neighbour: Reduce to point
2296 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002297 */
2298 support=(MagickRealType) 0.5;
2299 scale=1.0;
2300 }
2301 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2302 if (contributions == (ContributionInfo **) NULL)
2303 {
2304 (void) ThrowMagickException(exception,GetMagickModule(),
2305 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2306 return(MagickFalse);
2307 }
2308 status=MagickTrue;
2309 scale=1.0/scale;
2310 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2311 image_view=AcquireCacheView(image);
2312 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002313#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002314 #pragma omp parallel for shared(status)
2315#endif
cristybb503372010-05-27 20:51:26 +00002316 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002317 {
cristy3ed852e2009-09-05 21:47:34 +00002318 MagickRealType
2319 center,
2320 density;
2321
2322 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002323 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002324
2325 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002326 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002327
cristy03dbbd22010-09-19 23:04:47 +00002328 register ContributionInfo
2329 *restrict contribution;
2330
cristy3ed852e2009-09-05 21:47:34 +00002331 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002332 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002333
cristy9af9b5d2010-08-15 17:04:28 +00002334 register PixelPacket
2335 *restrict q;
2336
cristybb503372010-05-27 20:51:26 +00002337 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002338 x;
2339
cristy9af9b5d2010-08-15 17:04:28 +00002340 ssize_t
2341 n,
2342 start,
2343 stop;
cristy3ed852e2009-09-05 21:47:34 +00002344
2345 if (status == MagickFalse)
2346 continue;
cristy679e6962010-03-18 00:42:45 +00002347 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002348 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2349 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002350 density=0.0;
2351 contribution=contributions[GetOpenMPThreadId()];
2352 for (n=0; n < (stop-start); n++)
2353 {
2354 contribution[n].pixel=start+n;
2355 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2356 ((MagickRealType) (start+n)-center+0.5));
2357 density+=contribution[n].weight;
2358 }
2359 if ((density != 0.0) && (density != 1.0))
2360 {
cristybb503372010-05-27 20:51:26 +00002361 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002362 i;
2363
2364 /*
2365 Normalize.
2366 */
2367 density=1.0/density;
2368 for (i=0; i < n; i++)
2369 contribution[i].weight*=density;
2370 }
2371 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002372 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2373 exception);
cristy3ed852e2009-09-05 21:47:34 +00002374 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2375 exception);
2376 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2377 {
2378 status=MagickFalse;
2379 continue;
2380 }
2381 indexes=GetCacheViewVirtualIndexQueue(image_view);
2382 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002383 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002384 {
cristy3ed852e2009-09-05 21:47:34 +00002385 MagickPixelPacket
2386 pixel;
2387
2388 MagickRealType
2389 alpha;
2390
cristybb503372010-05-27 20:51:26 +00002391 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002392 i;
2393
cristy9af9b5d2010-08-15 17:04:28 +00002394 ssize_t
2395 j;
2396
cristy3ed852e2009-09-05 21:47:34 +00002397 pixel=zero;
2398 if (image->matte == MagickFalse)
2399 {
2400 for (i=0; i < n; i++)
2401 {
cristybb503372010-05-27 20:51:26 +00002402 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002403 image->columns+x);
2404 alpha=contribution[i].weight;
2405 pixel.red+=alpha*(p+j)->red;
2406 pixel.green+=alpha*(p+j)->green;
2407 pixel.blue+=alpha*(p+j)->blue;
2408 pixel.opacity+=alpha*(p+j)->opacity;
2409 }
cristyce70c172010-01-07 17:15:30 +00002410 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2411 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2412 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2413 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002414 if ((image->colorspace == CMYKColorspace) &&
2415 (resize_image->colorspace == CMYKColorspace))
2416 {
2417 for (i=0; i < n; i++)
2418 {
cristybb503372010-05-27 20:51:26 +00002419 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002420 image->columns+x);
2421 alpha=contribution[i].weight;
2422 pixel.index+=alpha*indexes[j];
2423 }
cristyce70c172010-01-07 17:15:30 +00002424 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002425 }
2426 }
2427 else
2428 {
2429 MagickRealType
2430 gamma;
2431
2432 gamma=0.0;
2433 for (i=0; i < n; i++)
2434 {
cristybb503372010-05-27 20:51:26 +00002435 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002436 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002437 alpha=contribution[i].weight*QuantumScale*
2438 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002439 pixel.red+=alpha*(p+j)->red;
2440 pixel.green+=alpha*(p+j)->green;
2441 pixel.blue+=alpha*(p+j)->blue;
2442 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2443 gamma+=alpha;
2444 }
2445 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002446 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2447 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2448 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2449 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002450 if ((image->colorspace == CMYKColorspace) &&
2451 (resize_image->colorspace == CMYKColorspace))
2452 {
2453 for (i=0; i < n; i++)
2454 {
cristybb503372010-05-27 20:51:26 +00002455 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002456 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002457 alpha=contribution[i].weight*QuantumScale*
2458 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002459 pixel.index+=alpha*indexes[j];
2460 }
cristyce70c172010-01-07 17:15:30 +00002461 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2462 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002463 }
2464 }
2465 if ((resize_image->storage_class == PseudoClass) &&
2466 (image->storage_class == PseudoClass))
2467 {
cristybb503372010-05-27 20:51:26 +00002468 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002469 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002470 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002471 image->columns+x);
2472 resize_indexes[x]=indexes[j];
2473 }
2474 q++;
2475 }
2476 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2477 status=MagickFalse;
2478 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2479 {
2480 MagickBooleanType
2481 proceed;
2482
cristyb5d5f722009-11-04 03:03:49 +00002483#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002484 #pragma omp critical (MagickCore_VerticalFilter)
2485#endif
cristy9af9b5d2010-08-15 17:04:28 +00002486 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002487 if (proceed == MagickFalse)
2488 status=MagickFalse;
2489 }
2490 }
2491 resize_view=DestroyCacheView(resize_view);
2492 image_view=DestroyCacheView(image_view);
2493 contributions=DestroyContributionThreadSet(contributions);
2494 return(status);
2495}
2496
cristybb503372010-05-27 20:51:26 +00002497MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2498 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002499 ExceptionInfo *exception)
2500{
2501#define WorkLoadFactor 0.265
2502
2503 FilterTypes
2504 filter_type;
2505
2506 Image
2507 *filter_image,
2508 *resize_image;
2509
cristy9af9b5d2010-08-15 17:04:28 +00002510 MagickOffsetType
2511 offset;
2512
cristy3ed852e2009-09-05 21:47:34 +00002513 MagickRealType
2514 x_factor,
2515 y_factor;
2516
2517 MagickSizeType
2518 span;
2519
2520 MagickStatusType
2521 status;
2522
2523 ResizeFilter
2524 *resize_filter;
2525
cristy3ed852e2009-09-05 21:47:34 +00002526 /*
2527 Acquire resize image.
2528 */
2529 assert(image != (Image *) NULL);
2530 assert(image->signature == MagickSignature);
2531 if (image->debug != MagickFalse)
2532 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2533 assert(exception != (ExceptionInfo *) NULL);
2534 assert(exception->signature == MagickSignature);
2535 if ((columns == 0) || (rows == 0))
2536 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2537 if ((columns == image->columns) && (rows == image->rows) &&
2538 (filter == UndefinedFilter) && (blur == 1.0))
2539 return(CloneImage(image,0,0,MagickTrue,exception));
2540 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2541 if (resize_image == (Image *) NULL)
2542 return(resize_image);
2543 /*
2544 Acquire resize filter.
2545 */
2546 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2547 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2548 if ((x_factor*y_factor) > WorkLoadFactor)
2549 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2550 else
2551 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2552 if (filter_image == (Image *) NULL)
2553 return(DestroyImage(resize_image));
2554 filter_type=LanczosFilter;
2555 if (filter != UndefinedFilter)
2556 filter_type=filter;
2557 else
2558 if ((x_factor == 1.0) && (y_factor == 1.0))
2559 filter_type=PointFilter;
2560 else
2561 if ((image->storage_class == PseudoClass) ||
2562 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2563 filter_type=MitchellFilter;
2564 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2565 exception);
2566 /*
2567 Resize image.
2568 */
cristy9af9b5d2010-08-15 17:04:28 +00002569 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002570 if ((x_factor*y_factor) > WorkLoadFactor)
2571 {
2572 span=(MagickSizeType) (filter_image->columns+rows);
2573 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002574 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002575 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002576 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002577 }
2578 else
2579 {
2580 span=(MagickSizeType) (filter_image->rows+columns);
2581 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002582 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002583 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002584 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002585 }
2586 /*
2587 Free resources.
2588 */
2589 filter_image=DestroyImage(filter_image);
2590 resize_filter=DestroyResizeFilter(resize_filter);
2591 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2592 return((Image *) NULL);
2593 resize_image->type=image->type;
2594 return(resize_image);
2595}
2596
2597/*
2598%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2599% %
2600% %
2601% %
2602% S a m p l e I m a g e %
2603% %
2604% %
2605% %
2606%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2607%
2608% SampleImage() scales an image to the desired dimensions with pixel
2609% sampling. Unlike other scaling methods, this method does not introduce
2610% any additional color into the scaled image.
2611%
2612% The format of the SampleImage method is:
2613%
cristybb503372010-05-27 20:51:26 +00002614% Image *SampleImage(const Image *image,const size_t columns,
2615% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002616%
2617% A description of each parameter follows:
2618%
2619% o image: the image.
2620%
2621% o columns: the number of columns in the sampled image.
2622%
2623% o rows: the number of rows in the sampled image.
2624%
2625% o exception: return any errors or warnings in this structure.
2626%
2627*/
cristybb503372010-05-27 20:51:26 +00002628MagickExport Image *SampleImage(const Image *image,const size_t columns,
2629 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002630{
2631#define SampleImageTag "Sample/Image"
2632
cristyc4c8d132010-01-07 01:58:38 +00002633 CacheView
2634 *image_view,
2635 *sample_view;
2636
cristy3ed852e2009-09-05 21:47:34 +00002637 Image
2638 *sample_image;
2639
cristy3ed852e2009-09-05 21:47:34 +00002640 MagickBooleanType
2641 status;
2642
cristy5f959472010-05-27 22:19:46 +00002643 MagickOffsetType
2644 progress;
2645
cristybb503372010-05-27 20:51:26 +00002646 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002647 x;
2648
cristy5f959472010-05-27 22:19:46 +00002649 ssize_t
2650 *x_offset,
2651 y;
2652
cristy3ed852e2009-09-05 21:47:34 +00002653 /*
2654 Initialize sampled image attributes.
2655 */
2656 assert(image != (const Image *) NULL);
2657 assert(image->signature == MagickSignature);
2658 if (image->debug != MagickFalse)
2659 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2660 assert(exception != (ExceptionInfo *) NULL);
2661 assert(exception->signature == MagickSignature);
2662 if ((columns == 0) || (rows == 0))
2663 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2664 if ((columns == image->columns) && (rows == image->rows))
2665 return(CloneImage(image,0,0,MagickTrue,exception));
2666 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2667 if (sample_image == (Image *) NULL)
2668 return((Image *) NULL);
2669 /*
2670 Allocate scan line buffer and column offset buffers.
2671 */
cristybb503372010-05-27 20:51:26 +00002672 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002673 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002674 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002675 {
2676 sample_image=DestroyImage(sample_image);
2677 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2678 }
cristybb503372010-05-27 20:51:26 +00002679 for (x=0; x < (ssize_t) sample_image->columns; x++)
2680 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002681 sample_image->columns);
2682 /*
2683 Sample each row.
2684 */
2685 status=MagickTrue;
2686 progress=0;
2687 image_view=AcquireCacheView(image);
2688 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002689#if defined(MAGICKCORE_OPENMP_SUPPORT)
2690 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002691#endif
cristybb503372010-05-27 20:51:26 +00002692 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002693 {
cristy3ed852e2009-09-05 21:47:34 +00002694 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002695 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002696
2697 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002698 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002699
2700 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002701 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002702
cristy3ed852e2009-09-05 21:47:34 +00002703 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002704 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002705
cristy03dbbd22010-09-19 23:04:47 +00002706 register ssize_t
2707 x;
2708
cristy9af9b5d2010-08-15 17:04:28 +00002709 ssize_t
2710 y_offset;
2711
cristy3ed852e2009-09-05 21:47:34 +00002712 if (status == MagickFalse)
2713 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002714 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2715 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002716 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2717 exception);
2718 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2719 exception);
2720 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2721 {
2722 status=MagickFalse;
2723 continue;
2724 }
2725 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2726 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2727 /*
2728 Sample each column.
2729 */
cristybb503372010-05-27 20:51:26 +00002730 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002731 *q++=p[x_offset[x]];
2732 if ((image->storage_class == PseudoClass) ||
2733 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002734 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002735 sample_indexes[x]=indexes[x_offset[x]];
2736 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2737 status=MagickFalse;
2738 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2739 {
2740 MagickBooleanType
2741 proceed;
2742
cristyb5d5f722009-11-04 03:03:49 +00002743#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002744 #pragma omp critical (MagickCore_SampleImage)
2745#endif
2746 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2747 if (proceed == MagickFalse)
2748 status=MagickFalse;
2749 }
2750 }
2751 image_view=DestroyCacheView(image_view);
2752 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002753 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002754 sample_image->type=image->type;
2755 return(sample_image);
2756}
2757
2758/*
2759%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2760% %
2761% %
2762% %
2763% S c a l e I m a g e %
2764% %
2765% %
2766% %
2767%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2768%
2769% ScaleImage() changes the size of an image to the given dimensions.
2770%
2771% The format of the ScaleImage method is:
2772%
cristybb503372010-05-27 20:51:26 +00002773% Image *ScaleImage(const Image *image,const size_t columns,
2774% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002775%
2776% A description of each parameter follows:
2777%
2778% o image: the image.
2779%
2780% o columns: the number of columns in the scaled image.
2781%
2782% o rows: the number of rows in the scaled image.
2783%
2784% o exception: return any errors or warnings in this structure.
2785%
2786*/
cristybb503372010-05-27 20:51:26 +00002787MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2788 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002789{
2790#define ScaleImageTag "Scale/Image"
2791
cristyed6cb232010-01-20 03:07:53 +00002792 CacheView
2793 *image_view,
2794 *scale_view;
2795
cristy3ed852e2009-09-05 21:47:34 +00002796 Image
2797 *scale_image;
2798
cristy3ed852e2009-09-05 21:47:34 +00002799 MagickBooleanType
2800 next_column,
2801 next_row,
2802 proceed;
2803
2804 MagickPixelPacket
2805 pixel,
2806 *scale_scanline,
2807 *scanline,
2808 *x_vector,
2809 *y_vector,
2810 zero;
2811
cristy3ed852e2009-09-05 21:47:34 +00002812 PointInfo
2813 scale,
2814 span;
2815
cristybb503372010-05-27 20:51:26 +00002816 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002817 i;
2818
cristy9af9b5d2010-08-15 17:04:28 +00002819 ssize_t
2820 number_rows,
2821 y;
2822
cristy3ed852e2009-09-05 21:47:34 +00002823 /*
2824 Initialize scaled image attributes.
2825 */
2826 assert(image != (const Image *) NULL);
2827 assert(image->signature == MagickSignature);
2828 if (image->debug != MagickFalse)
2829 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2830 assert(exception != (ExceptionInfo *) NULL);
2831 assert(exception->signature == MagickSignature);
2832 if ((columns == 0) || (rows == 0))
2833 return((Image *) NULL);
2834 if ((columns == image->columns) && (rows == image->rows))
2835 return(CloneImage(image,0,0,MagickTrue,exception));
2836 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2837 if (scale_image == (Image *) NULL)
2838 return((Image *) NULL);
2839 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2840 {
2841 InheritException(exception,&scale_image->exception);
2842 scale_image=DestroyImage(scale_image);
2843 return((Image *) NULL);
2844 }
2845 /*
2846 Allocate memory.
2847 */
2848 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2849 sizeof(*x_vector));
2850 scanline=x_vector;
2851 if (image->rows != scale_image->rows)
2852 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2853 sizeof(*scanline));
2854 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2855 scale_image->columns,sizeof(*scale_scanline));
2856 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2857 sizeof(*y_vector));
2858 if ((scanline == (MagickPixelPacket *) NULL) ||
2859 (scale_scanline == (MagickPixelPacket *) NULL) ||
2860 (x_vector == (MagickPixelPacket *) NULL) ||
2861 (y_vector == (MagickPixelPacket *) NULL))
2862 {
2863 scale_image=DestroyImage(scale_image);
2864 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2865 }
2866 /*
2867 Scale image.
2868 */
2869 number_rows=0;
2870 next_row=MagickTrue;
2871 span.y=1.0;
2872 scale.y=(double) scale_image->rows/(double) image->rows;
2873 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2874 sizeof(*y_vector));
2875 GetMagickPixelPacket(image,&pixel);
2876 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2877 i=0;
cristyed6cb232010-01-20 03:07:53 +00002878 image_view=AcquireCacheView(image);
2879 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002880 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002881 {
2882 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002883 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002884
2885 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002886 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002887
2888 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002889 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002890
cristy3ed852e2009-09-05 21:47:34 +00002891 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002892 *restrict s,
2893 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002894
2895 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002896 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002897
cristy9af9b5d2010-08-15 17:04:28 +00002898 register ssize_t
2899 x;
2900
cristyed6cb232010-01-20 03:07:53 +00002901 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2902 exception);
cristy3ed852e2009-09-05 21:47:34 +00002903 if (q == (PixelPacket *) NULL)
2904 break;
2905 scale_indexes=GetAuthenticIndexQueue(scale_image);
2906 if (scale_image->rows == image->rows)
2907 {
2908 /*
2909 Read a new scanline.
2910 */
cristyed6cb232010-01-20 03:07:53 +00002911 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2912 exception);
cristy3ed852e2009-09-05 21:47:34 +00002913 if (p == (const PixelPacket *) NULL)
2914 break;
cristyed6cb232010-01-20 03:07:53 +00002915 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002916 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002917 {
cristyce70c172010-01-07 17:15:30 +00002918 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2919 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2920 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002921 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002922 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002923 if (indexes != (IndexPacket *) NULL)
2924 x_vector[x].index=(MagickRealType) indexes[x];
2925 p++;
2926 }
2927 }
2928 else
2929 {
2930 /*
2931 Scale Y direction.
2932 */
2933 while (scale.y < span.y)
2934 {
cristy9af9b5d2010-08-15 17:04:28 +00002935 if ((next_row != MagickFalse) &&
2936 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002937 {
2938 /*
2939 Read a new scanline.
2940 */
cristyed6cb232010-01-20 03:07:53 +00002941 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2942 exception);
cristy3ed852e2009-09-05 21:47:34 +00002943 if (p == (const PixelPacket *) NULL)
2944 break;
cristyed6cb232010-01-20 03:07:53 +00002945 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002946 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002947 {
cristyce70c172010-01-07 17:15:30 +00002948 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2949 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2950 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002951 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002952 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002953 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002954 if (indexes != (IndexPacket *) NULL)
2955 x_vector[x].index=(MagickRealType) indexes[x];
2956 p++;
2957 }
2958 number_rows++;
2959 }
cristybb503372010-05-27 20:51:26 +00002960 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002961 {
2962 y_vector[x].red+=scale.y*x_vector[x].red;
2963 y_vector[x].green+=scale.y*x_vector[x].green;
2964 y_vector[x].blue+=scale.y*x_vector[x].blue;
2965 if (scale_image->matte != MagickFalse)
2966 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2967 if (scale_indexes != (IndexPacket *) NULL)
2968 y_vector[x].index+=scale.y*x_vector[x].index;
2969 }
2970 span.y-=scale.y;
2971 scale.y=(double) scale_image->rows/(double) image->rows;
2972 next_row=MagickTrue;
2973 }
cristybb503372010-05-27 20:51:26 +00002974 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002975 {
2976 /*
2977 Read a new scanline.
2978 */
cristyed6cb232010-01-20 03:07:53 +00002979 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2980 exception);
cristy3ed852e2009-09-05 21:47:34 +00002981 if (p == (const PixelPacket *) NULL)
2982 break;
cristyed6cb232010-01-20 03:07:53 +00002983 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002984 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002985 {
cristyce70c172010-01-07 17:15:30 +00002986 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2987 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2988 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002989 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00002990 x_vector[x].opacity=(MagickRealType)
2991 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002992 if (indexes != (IndexPacket *) NULL)
2993 x_vector[x].index=(MagickRealType) indexes[x];
2994 p++;
2995 }
2996 number_rows++;
2997 next_row=MagickFalse;
2998 }
2999 s=scanline;
cristybb503372010-05-27 20:51:26 +00003000 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003001 {
3002 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3003 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3004 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3005 if (image->matte != MagickFalse)
3006 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3007 if (scale_indexes != (IndexPacket *) NULL)
3008 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3009 s->red=pixel.red;
3010 s->green=pixel.green;
3011 s->blue=pixel.blue;
3012 if (scale_image->matte != MagickFalse)
3013 s->opacity=pixel.opacity;
3014 if (scale_indexes != (IndexPacket *) NULL)
3015 s->index=pixel.index;
3016 s++;
3017 y_vector[x]=zero;
3018 }
3019 scale.y-=span.y;
3020 if (scale.y <= 0)
3021 {
3022 scale.y=(double) scale_image->rows/(double) image->rows;
3023 next_row=MagickTrue;
3024 }
3025 span.y=1.0;
3026 }
3027 if (scale_image->columns == image->columns)
3028 {
3029 /*
3030 Transfer scanline to scaled image.
3031 */
3032 s=scanline;
cristybb503372010-05-27 20:51:26 +00003033 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003034 {
cristyce70c172010-01-07 17:15:30 +00003035 q->red=ClampToQuantum(s->red);
3036 q->green=ClampToQuantum(s->green);
3037 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003038 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003039 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003040 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003041 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003042 q++;
3043 s++;
3044 }
3045 }
3046 else
3047 {
3048 /*
3049 Scale X direction.
3050 */
3051 pixel=zero;
3052 next_column=MagickFalse;
3053 span.x=1.0;
3054 s=scanline;
3055 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003056 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003057 {
3058 scale.x=(double) scale_image->columns/(double) image->columns;
3059 while (scale.x >= span.x)
3060 {
3061 if (next_column != MagickFalse)
3062 {
3063 pixel=zero;
3064 t++;
3065 }
3066 pixel.red+=span.x*s->red;
3067 pixel.green+=span.x*s->green;
3068 pixel.blue+=span.x*s->blue;
3069 if (image->matte != MagickFalse)
3070 pixel.opacity+=span.x*s->opacity;
3071 if (scale_indexes != (IndexPacket *) NULL)
3072 pixel.index+=span.x*s->index;
3073 t->red=pixel.red;
3074 t->green=pixel.green;
3075 t->blue=pixel.blue;
3076 if (scale_image->matte != MagickFalse)
3077 t->opacity=pixel.opacity;
3078 if (scale_indexes != (IndexPacket *) NULL)
3079 t->index=pixel.index;
3080 scale.x-=span.x;
3081 span.x=1.0;
3082 next_column=MagickTrue;
3083 }
3084 if (scale.x > 0)
3085 {
3086 if (next_column != MagickFalse)
3087 {
3088 pixel=zero;
3089 next_column=MagickFalse;
3090 t++;
3091 }
3092 pixel.red+=scale.x*s->red;
3093 pixel.green+=scale.x*s->green;
3094 pixel.blue+=scale.x*s->blue;
3095 if (scale_image->matte != MagickFalse)
3096 pixel.opacity+=scale.x*s->opacity;
3097 if (scale_indexes != (IndexPacket *) NULL)
3098 pixel.index+=scale.x*s->index;
3099 span.x-=scale.x;
3100 }
3101 s++;
3102 }
3103 if (span.x > 0)
3104 {
3105 s--;
3106 pixel.red+=span.x*s->red;
3107 pixel.green+=span.x*s->green;
3108 pixel.blue+=span.x*s->blue;
3109 if (scale_image->matte != MagickFalse)
3110 pixel.opacity+=span.x*s->opacity;
3111 if (scale_indexes != (IndexPacket *) NULL)
3112 pixel.index+=span.x*s->index;
3113 }
3114 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003115 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003116 {
3117 t->red=pixel.red;
3118 t->green=pixel.green;
3119 t->blue=pixel.blue;
3120 if (scale_image->matte != MagickFalse)
3121 t->opacity=pixel.opacity;
3122 if (scale_indexes != (IndexPacket *) NULL)
3123 t->index=pixel.index;
3124 }
3125 /*
3126 Transfer scanline to scaled image.
3127 */
3128 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003129 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003130 {
cristyce70c172010-01-07 17:15:30 +00003131 q->red=ClampToQuantum(t->red);
3132 q->green=ClampToQuantum(t->green);
3133 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003134 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003135 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003136 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003137 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003138 t++;
3139 q++;
3140 }
3141 }
cristyed6cb232010-01-20 03:07:53 +00003142 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003143 break;
cristy96b16132010-08-29 17:19:52 +00003144 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3145 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003146 if (proceed == MagickFalse)
3147 break;
3148 }
cristyed6cb232010-01-20 03:07:53 +00003149 scale_view=DestroyCacheView(scale_view);
3150 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003151 /*
3152 Free allocated memory.
3153 */
3154 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3155 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3156 if (scale_image->rows != image->rows)
3157 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3158 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3159 scale_image->type=image->type;
3160 return(scale_image);
3161}
3162
anthony02b4cb42010-10-10 04:54:35 +00003163#if 0
3164 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003165/*
3166%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3167% %
3168% %
3169% %
3170+ S e t R e s i z e F i l t e r S u p p o r t %
3171% %
3172% %
3173% %
3174%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3175%
3176% SetResizeFilterSupport() specifies which IR filter to use to window
3177%
3178% The format of the SetResizeFilterSupport method is:
3179%
3180% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3181% const MagickRealType support)
3182%
3183% A description of each parameter follows:
3184%
3185% o resize_filter: the resize filter.
3186%
3187% o support: the filter spport radius.
3188%
3189*/
3190MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3191 const MagickRealType support)
3192{
3193 assert(resize_filter != (ResizeFilter *) NULL);
3194 assert(resize_filter->signature == MagickSignature);
3195 resize_filter->support=support;
3196}
anthony02b4cb42010-10-10 04:54:35 +00003197#endif
cristy3ed852e2009-09-05 21:47:34 +00003198
3199/*
3200%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3201% %
3202% %
3203% %
3204% T h u m b n a i l I m a g e %
3205% %
3206% %
3207% %
3208%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3209%
3210% ThumbnailImage() changes the size of an image to the given dimensions and
3211% removes any associated profiles. The goal is to produce small low cost
3212% thumbnail images suited for display on the Web.
3213%
3214% The format of the ThumbnailImage method is:
3215%
cristybb503372010-05-27 20:51:26 +00003216% Image *ThumbnailImage(const Image *image,const size_t columns,
3217% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003218%
3219% A description of each parameter follows:
3220%
3221% o image: the image.
3222%
3223% o columns: the number of columns in the scaled image.
3224%
3225% o rows: the number of rows in the scaled image.
3226%
3227% o exception: return any errors or warnings in this structure.
3228%
3229*/
cristy9af9b5d2010-08-15 17:04:28 +00003230MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3231 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003232{
3233#define SampleFactor 5
3234
3235 char
3236 value[MaxTextExtent];
3237
3238 const char
3239 *name;
3240
3241 Image
3242 *thumbnail_image;
3243
3244 MagickRealType
3245 x_factor,
3246 y_factor;
3247
cristybb503372010-05-27 20:51:26 +00003248 size_t
cristy3ed852e2009-09-05 21:47:34 +00003249 version;
3250
cristy9af9b5d2010-08-15 17:04:28 +00003251 struct stat
3252 attributes;
3253
cristy3ed852e2009-09-05 21:47:34 +00003254 assert(image != (Image *) NULL);
3255 assert(image->signature == MagickSignature);
3256 if (image->debug != MagickFalse)
3257 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3258 assert(exception != (ExceptionInfo *) NULL);
3259 assert(exception->signature == MagickSignature);
3260 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3261 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3262 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003263 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3264 exception);
cristy3ed852e2009-09-05 21:47:34 +00003265 else
3266 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003267 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3268 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003269 else
3270 {
3271 Image
3272 *sample_image;
3273
3274 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3275 exception);
3276 if (sample_image == (Image *) NULL)
3277 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003278 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3279 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003280 sample_image=DestroyImage(sample_image);
3281 }
3282 if (thumbnail_image == (Image *) NULL)
3283 return(thumbnail_image);
3284 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3285 if (thumbnail_image->matte == MagickFalse)
3286 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3287 thumbnail_image->depth=8;
3288 thumbnail_image->interlace=NoInterlace;
3289 /*
3290 Strip all profiles except color profiles.
3291 */
3292 ResetImageProfileIterator(thumbnail_image);
3293 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3294 {
3295 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3296 {
cristy2b726bd2010-01-11 01:05:39 +00003297 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003298 ResetImageProfileIterator(thumbnail_image);
3299 }
3300 name=GetNextImageProfile(thumbnail_image);
3301 }
3302 (void) DeleteImageProperty(thumbnail_image,"comment");
3303 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003304 if (strstr(image->magick_filename,"//") == (char *) NULL)
3305 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003306 image->magick_filename);
3307 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3308 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3309 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3310 {
cristye8c25f92010-06-03 00:53:06 +00003311 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003312 attributes.st_mtime);
3313 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3314 }
cristye8c25f92010-06-03 00:53:06 +00003315 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003316 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003317 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003318 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003319 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3320 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3321 LocaleLower(value);
3322 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3323 (void) SetImageProperty(thumbnail_image,"software",
3324 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003325 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3326 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003327 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003328 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003329 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003330 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003331 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3332 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003333 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3334 return(thumbnail_image);
3335}