blob: 62461387f61be4245264947b30f1cf2379d6f7be [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) */
nicolas0134bbe2010-10-10 15:55:42 +0000748 { Jinc, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2D (Jinc-Jinc) */
anthonye5f06452010-10-12 05:48:17 +0000749 { Jinc, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2D Sharpened */
anthony02b4cb42010-10-10 04:54:35 +0000750 { CubicBC, 2.0, 1.0, 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;
anthony61b5ddd2010-10-05 02:33:31 +0000819
cristy33b1c162010-01-23 22:51:51 +0000820 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000821 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000822 filter_type=JincFilter;
823 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000824 break;
anthonyf5e76ef2010-10-12 01:22:01 +0000825 case GaussianFilter:
826 sigma = MagickSQ2/2; /* Cylindrical Gaussian sigma is sqrt(2)/2 */
827 break;
cristya782ecf2010-01-25 02:59:14 +0000828 default:
829 break;
cristy3ed852e2009-09-05 21:47:34 +0000830 }
anthony61b5ddd2010-10-05 02:33:31 +0000831 else
832 switch (filter_type)
833 {
834 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000835 case Lanczos2DSharpFilter:
nicolas45b58a92010-10-07 15:46:39 +0000836 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000837 window_type=SincFastFilter;
838 break;
839 default:
840 break;
841 }
842
cristy3ed852e2009-09-05 21:47:34 +0000843 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000844 if (artifact != (const char *) NULL)
845 {
cristy9af9b5d2010-08-15 17:04:28 +0000846 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000847 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000848 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000849 filter_type=(FilterTypes) option;
850 window_type=BoxFilter;
851 }
852 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000853 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000854 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000855 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000856 }
nicolas07bac812010-09-19 18:47:02 +0000857 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000858 artifact=GetImageArtifact(image,"filter:window");
859 if (artifact != (const char *) NULL)
860 {
cristy9af9b5d2010-08-15 17:04:28 +0000861 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000862 if ((UndefinedFilter < option) && (option < SentinelFilter))
863 {
864 if (option != LanczosFilter)
865 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000866 else
anthony48f77622010-10-03 14:32:31 +0000867 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000868 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000869 }
cristy33b1c162010-01-23 22:51:51 +0000870 }
cristy3ed852e2009-09-05 21:47:34 +0000871 }
cristy33b1c162010-01-23 22:51:51 +0000872 else
873 {
anthony48f77622010-10-03 14:32:31 +0000874 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000875 artifact=GetImageArtifact(image,"filter:window");
876 if (artifact != (const char *) NULL)
877 {
878 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
879 artifact);
880 if ((UndefinedFilter < option) && (option < SentinelFilter))
881 {
anthony61b5ddd2010-10-05 02:33:31 +0000882 filter_type=cylindrical != MagickFalse ?
883 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000884 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000885 }
886 }
887 }
nicolas07bac812010-09-19 18:47:02 +0000888 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000889 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000890 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000891 resize_filter->window=filters[window_type].function;
892 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000893 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000894
anthonyf5e76ef2010-10-12 01:22:01 +0000895 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000896 if (cylindrical != MagickFalse)
897 switch (filter_type)
898 {
899 case PointFilter:
900 case BoxFilter:
901 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000902 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000903 break;
anthonye5f06452010-10-12 05:48:17 +0000904 case Lanczos2DSharpFilter:
905 /* Sharpened by Nicholas Robidoux so as to optimize for
906 * minimal blurring of orthogonal lines
907 */
908 resize_filter->blur *= 0.958033808;
909 break;
anthony81b8bf92010-10-02 13:54:34 +0000910 default:
911 break;
anthony10b8bc82010-10-02 12:48:46 +0000912 }
anthony61b5ddd2010-10-05 02:33:31 +0000913 else
914 switch (filter_type)
915 {
916 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000917 case Lanczos2DSharpFilter:
anthony853d6972010-10-08 06:01:31 +0000918 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000919 resize_filter->filter=SincFast;
920 break;
921 default:
922 break;
923 }
924
anthonyf5e76ef2010-10-12 01:22:01 +0000925 /*
926 ** More Expert Option Modifications
927 */
928
929 /* User Sigma Override - no support change */
930 artifact=GetImageArtifact(image,"filter:sigma");
931 if (artifact != (const char *) NULL)
932 sigma=StringToDouble(artifact);
933 /* Define coefficents for Gaussian (assumes no cubic window) */
934 if ( GaussianFilter ) {
935 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
936 resize_filter->coeff[1] = 1.0/(Magick2PI*sigma*sigma); /* unused */
937 }
938
939 /* Blur Override */
940 artifact=GetImageArtifact(image,"filter:blur");
941 if (artifact != (const char *) NULL)
942 resize_filter->blur=StringToDouble(artifact);
943 if (resize_filter->blur < MagickEpsilon)
944 resize_filter->blur=(MagickRealType) MagickEpsilon;
945
946 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000947 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000948 if (artifact != (const char *) NULL)
949 {
cristybb503372010-05-27 20:51:26 +0000950 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000951 lobes;
952
cristy96b16132010-08-29 17:19:52 +0000953 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000954 if (lobes < 1)
955 lobes=1;
956 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000957 }
anthony61b5ddd2010-10-05 02:33:31 +0000958 /* convert Jinc lobes to a real support value */
959 if (resize_filter->filter == Jinc)
960 {
961 if (resize_filter->support > 16)
962 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
963 else
964 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
965 }
966 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000967 artifact=GetImageArtifact(image,"filter:support");
968 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000969 resize_filter->support=fabs(StringToDouble(artifact));
970 /*
nicolas07bac812010-09-19 18:47:02 +0000971 Scale windowing function separatally to the support 'clipping'
972 window that calling operator is planning to actually use. (Expert
973 override)
cristy3ed852e2009-09-05 21:47:34 +0000974 */
anthony55f12332010-09-10 01:13:02 +0000975 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000976 artifact=GetImageArtifact(image,"filter:win-support");
977 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000978 resize_filter->window_support=fabs(StringToDouble(artifact));
979 /*
anthony1f90a6b2010-09-14 08:56:31 +0000980 Adjust window function scaling to the windowing support for
981 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000982 */
983 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +0000984
anthony55f12332010-09-10 01:13:02 +0000985 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000986 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000987 */
cristy3ed852e2009-09-05 21:47:34 +0000988 B=0.0;
989 C=0.0;
cristy33b1c162010-01-23 22:51:51 +0000990 if ((filters[filter_type].function == CubicBC) ||
991 (filters[window_type].function == CubicBC))
992 {
anthony2d9b8b52010-09-14 08:31:07 +0000993 B=filters[filter_type].B;
994 C=filters[filter_type].C;
995 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +0000996 {
anthony2d9b8b52010-09-14 08:31:07 +0000997 B=filters[window_type].B;
998 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +0000999 }
cristy33b1c162010-01-23 22:51:51 +00001000 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001001 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001002 {
1003 B=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001004 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001005 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001006 if (artifact != (const char *) NULL)
1007 C=StringToDouble(artifact);
1008 }
1009 else
1010 {
1011 artifact=GetImageArtifact(image,"filter:c");
1012 if (artifact != (const char *) NULL)
1013 {
1014 C=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001015 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001016 }
1017 }
anthonyf5e76ef2010-10-12 01:22:01 +00001018 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
1019 resize_filter->coeff[0]=(6.0-2.0*B)/6.0;
1020 resize_filter->coeff[1]=0.0;
1021 resize_filter->coeff[2]=(-18.0+12.0*B+6.0*C)/6.0;
1022 resize_filter->coeff[3]=(12.0-9.0*B-6.0*C)/6.0;
1023 resize_filter->coeff[4]=(8.0*B+24.0*C)/6.0;
1024 resize_filter->coeff[5]=(-12.0*B-48.0*C)/6.0;
1025 resize_filter->coeff[6]=(6.0*B+30.0*C)/6.0;
1026 resize_filter->coeff[7]=(-B-6.0*C)/6.0;
cristy3ed852e2009-09-05 21:47:34 +00001027 }
anthonyf5e76ef2010-10-12 01:22:01 +00001028
anthony55f12332010-09-10 01:13:02 +00001029 /*
nicolas07bac812010-09-19 18:47:02 +00001030 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001031 */
anthonye06e4c12010-09-15 04:03:52 +00001032#if defined(MAGICKCORE_OPENMP_SUPPORT)
anthony72949792010-10-08 04:44:56 +00001033 #pragma omp single
1034 {
anthonye06e4c12010-09-15 04:03:52 +00001035#endif
1036 artifact=GetImageArtifact(image,"filter:verbose");
1037 if (artifact != (const char *) NULL)
1038 {
1039 double
1040 support,
1041 x;
cristy3ed852e2009-09-05 21:47:34 +00001042
nicolas07bac812010-09-19 18:47:02 +00001043 /*
anthony463be1d2010-09-26 01:07:36 +00001044 Set the weighting function properly when the weighting
1045 function may not exactly match the filter of the same name.
1046 EG: a Point filter really uses a Box weighting function
1047 with a different support than is typically used.
1048
anthonye06e4c12010-09-15 04:03:52 +00001049 */
anthony463be1d2010-09-26 01:07:36 +00001050 if (resize_filter->filter == Box) filter_type=BoxFilter;
1051 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1052 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
anthony61b5ddd2010-10-05 02:33:31 +00001053 if (resize_filter->filter == Jinc) filter_type=JincFilter;
anthony463be1d2010-09-26 01:07:36 +00001054 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
anthonye06e4c12010-09-15 04:03:52 +00001055 /*
nicolas07bac812010-09-19 18:47:02 +00001056 Report Filter Details.
anthonye06e4c12010-09-15 04:03:52 +00001057 */
anthonyf5e76ef2010-10-12 01:22:01 +00001058 support=GetResizeFilterSupport(resize_filter); /* practical_support */
anthony61b5ddd2010-10-05 02:33:31 +00001059 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
cristy03dbbd22010-09-19 23:04:47 +00001060 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1061 MagickFilterOptions,filter_type));
1062 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
anthony463be1d2010-09-26 01:07:36 +00001063 MagickFilterOptions, window_type));
cristy03dbbd22010-09-19 23:04:47 +00001064 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
anthony463be1d2010-09-26 01:07:36 +00001065 (double) resize_filter->support);
cristy03dbbd22010-09-19 23:04:47 +00001066 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
anthony463be1d2010-09-26 01:07:36 +00001067 (double) resize_filter->window_support);
anthonyf5e76ef2010-10-12 01:22:01 +00001068 (void) fprintf(stdout,"# scale_blur = %.*g\n",GetMagickPrecision(),
cristy03dbbd22010-09-19 23:04:47 +00001069 (double) resize_filter->blur);
anthonyf5e76ef2010-10-12 01:22:01 +00001070 if ( filter_type == GaussianFilter )
1071 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",GetMagickPrecision(),
1072 (double) sigma);
1073 (void) fprintf(stdout,"# practical_support = %.*g\n",GetMagickPrecision(),
cristy03dbbd22010-09-19 23:04:47 +00001074 (double) support);
anthonydc592432010-10-12 02:59:44 +00001075 if ( filter_type == CubicFilter || window_type == CubicFilter )
1076 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1077 (double) B,GetMagickPrecision(),(double) C);
anthony61b5ddd2010-10-05 02:33:31 +00001078 (void) fprintf(stdout,"\n");
anthonye06e4c12010-09-15 04:03:52 +00001079 /*
nicolas07bac812010-09-19 18:47:02 +00001080 Output values of resulting filter graph -- for graphing
1081 filter result.
anthonye06e4c12010-09-15 04:03:52 +00001082 */
1083 for (x=0.0; x <= support; x+=0.01f)
cristy03dbbd22010-09-19 23:04:47 +00001084 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1085 (double) GetResizeFilterWeight(resize_filter,x));
nicolas07bac812010-09-19 18:47:02 +00001086 /* A final value so gnuplot can graph the 'stop' properly. */
cristy03dbbd22010-09-19 23:04:47 +00001087 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1088 0.0);
anthonye06e4c12010-09-15 04:03:52 +00001089 }
anthonyf5e76ef2010-10-12 01:22:01 +00001090 /* Output the above once only for each image - remove setting */
cristybb66d9c2010-10-09 01:40:31 +00001091 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
anthonye06e4c12010-09-15 04:03:52 +00001092#if defined(MAGICKCORE_OPENMP_SUPPORT)
anthony61b5ddd2010-10-05 02:33:31 +00001093 }
anthonye06e4c12010-09-15 04:03:52 +00001094#endif
anthonyf5e76ef2010-10-12 01:22:01 +00001095
cristy3ed852e2009-09-05 21:47:34 +00001096 return(resize_filter);
1097}
1098
1099/*
1100%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1101% %
1102% %
1103% %
1104% A d a p t i v e R e s i z e I m a g e %
1105% %
1106% %
1107% %
1108%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1109%
1110% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1111%
1112% The format of the AdaptiveResizeImage method is:
1113%
cristy9af9b5d2010-08-15 17:04:28 +00001114% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1115% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001116%
1117% A description of each parameter follows:
1118%
1119% o image: the image.
1120%
1121% o columns: the number of columns in the resized image.
1122%
1123% o rows: the number of rows in the resized image.
1124%
1125% o exception: return any errors or warnings in this structure.
1126%
1127*/
1128MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001129 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001130{
1131#define AdaptiveResizeImageTag "Resize/Image"
1132
cristyc4c8d132010-01-07 01:58:38 +00001133 CacheView
1134 *resize_view;
1135
cristy3ed852e2009-09-05 21:47:34 +00001136 Image
1137 *resize_image;
1138
cristy3ed852e2009-09-05 21:47:34 +00001139 MagickBooleanType
1140 proceed;
1141
1142 MagickPixelPacket
1143 pixel;
1144
1145 PointInfo
1146 offset;
1147
1148 ResampleFilter
1149 *resample_filter;
1150
cristy9af9b5d2010-08-15 17:04:28 +00001151 ssize_t
1152 y;
1153
cristy3ed852e2009-09-05 21:47:34 +00001154 /*
1155 Adaptively resize image.
1156 */
1157 assert(image != (const Image *) NULL);
1158 assert(image->signature == MagickSignature);
1159 if (image->debug != MagickFalse)
1160 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1161 assert(exception != (ExceptionInfo *) NULL);
1162 assert(exception->signature == MagickSignature);
1163 if ((columns == 0) || (rows == 0))
1164 return((Image *) NULL);
1165 if ((columns == image->columns) && (rows == image->rows))
1166 return(CloneImage(image,0,0,MagickTrue,exception));
1167 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1168 if (resize_image == (Image *) NULL)
1169 return((Image *) NULL);
1170 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1171 {
1172 InheritException(exception,&resize_image->exception);
1173 resize_image=DestroyImage(resize_image);
1174 return((Image *) NULL);
1175 }
1176 GetMagickPixelPacket(image,&pixel);
1177 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001178 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001179 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001180 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001181 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001182 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001183 {
1184 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001185 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001186
cristybb503372010-05-27 20:51:26 +00001187 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001188 x;
1189
1190 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001191 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001192
1193 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1194 exception);
1195 if (q == (PixelPacket *) NULL)
1196 break;
1197 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1198 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001199 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001200 {
1201 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1202 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1203 &pixel);
1204 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1205 q++;
1206 }
1207 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1208 break;
cristy96b16132010-08-29 17:19:52 +00001209 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1210 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001211 if (proceed == MagickFalse)
1212 break;
1213 }
1214 resample_filter=DestroyResampleFilter(resample_filter);
1215 resize_view=DestroyCacheView(resize_view);
1216 return(resize_image);
1217}
1218
1219/*
1220%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1221% %
1222% %
1223% %
1224+ B e s s e l O r d e r O n e %
1225% %
1226% %
1227% %
1228%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1229%
1230% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001231% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001232%
1233% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1234%
1235% j1(x) = x*j1(x);
1236%
1237% For x in (8,inf)
1238%
1239% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1240%
1241% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1242%
1243% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1244% = 1/sqrt(2) * (sin(x) - cos(x))
1245% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1246% = -1/sqrt(2) * (sin(x) + cos(x))
1247%
1248% The format of the BesselOrderOne method is:
1249%
1250% MagickRealType BesselOrderOne(MagickRealType x)
1251%
1252% A description of each parameter follows:
1253%
1254% o x: MagickRealType value.
1255%
1256*/
1257
1258#undef I0
1259static MagickRealType I0(MagickRealType x)
1260{
1261 MagickRealType
1262 sum,
1263 t,
1264 y;
1265
cristybb503372010-05-27 20:51:26 +00001266 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001267 i;
1268
1269 /*
1270 Zeroth order Bessel function of the first kind.
1271 */
1272 sum=1.0;
1273 y=x*x/4.0;
1274 t=y;
1275 for (i=2; t > MagickEpsilon; i++)
1276 {
1277 sum+=t;
1278 t*=y/((MagickRealType) i*i);
1279 }
1280 return(sum);
1281}
1282
1283#undef J1
1284static MagickRealType J1(MagickRealType x)
1285{
1286 MagickRealType
1287 p,
1288 q;
1289
cristybb503372010-05-27 20:51:26 +00001290 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001291 i;
1292
1293 static const double
1294 Pone[] =
1295 {
1296 0.581199354001606143928050809e+21,
1297 -0.6672106568924916298020941484e+20,
1298 0.2316433580634002297931815435e+19,
1299 -0.3588817569910106050743641413e+17,
1300 0.2908795263834775409737601689e+15,
1301 -0.1322983480332126453125473247e+13,
1302 0.3413234182301700539091292655e+10,
1303 -0.4695753530642995859767162166e+7,
1304 0.270112271089232341485679099e+4
1305 },
1306 Qone[] =
1307 {
1308 0.11623987080032122878585294e+22,
1309 0.1185770712190320999837113348e+20,
1310 0.6092061398917521746105196863e+17,
1311 0.2081661221307607351240184229e+15,
1312 0.5243710262167649715406728642e+12,
1313 0.1013863514358673989967045588e+10,
1314 0.1501793594998585505921097578e+7,
1315 0.1606931573481487801970916749e+4,
1316 0.1e+1
1317 };
1318
1319 p=Pone[8];
1320 q=Qone[8];
1321 for (i=7; i >= 0; i--)
1322 {
1323 p=p*x*x+Pone[i];
1324 q=q*x*x+Qone[i];
1325 }
1326 return(p/q);
1327}
1328
1329#undef P1
1330static MagickRealType P1(MagickRealType x)
1331{
1332 MagickRealType
1333 p,
1334 q;
1335
cristybb503372010-05-27 20:51:26 +00001336 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001337 i;
1338
1339 static const double
1340 Pone[] =
1341 {
1342 0.352246649133679798341724373e+5,
1343 0.62758845247161281269005675e+5,
1344 0.313539631109159574238669888e+5,
1345 0.49854832060594338434500455e+4,
1346 0.2111529182853962382105718e+3,
1347 0.12571716929145341558495e+1
1348 },
1349 Qone[] =
1350 {
1351 0.352246649133679798068390431e+5,
1352 0.626943469593560511888833731e+5,
1353 0.312404063819041039923015703e+5,
1354 0.4930396490181088979386097e+4,
1355 0.2030775189134759322293574e+3,
1356 0.1e+1
1357 };
1358
1359 p=Pone[5];
1360 q=Qone[5];
1361 for (i=4; i >= 0; i--)
1362 {
1363 p=p*(8.0/x)*(8.0/x)+Pone[i];
1364 q=q*(8.0/x)*(8.0/x)+Qone[i];
1365 }
1366 return(p/q);
1367}
1368
1369#undef Q1
1370static MagickRealType Q1(MagickRealType x)
1371{
1372 MagickRealType
1373 p,
1374 q;
1375
cristybb503372010-05-27 20:51:26 +00001376 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001377 i;
1378
1379 static const double
1380 Pone[] =
1381 {
1382 0.3511751914303552822533318e+3,
1383 0.7210391804904475039280863e+3,
1384 0.4259873011654442389886993e+3,
1385 0.831898957673850827325226e+2,
1386 0.45681716295512267064405e+1,
1387 0.3532840052740123642735e-1
1388 },
1389 Qone[] =
1390 {
1391 0.74917374171809127714519505e+4,
1392 0.154141773392650970499848051e+5,
1393 0.91522317015169922705904727e+4,
1394 0.18111867005523513506724158e+4,
1395 0.1038187585462133728776636e+3,
1396 0.1e+1
1397 };
1398
1399 p=Pone[5];
1400 q=Qone[5];
1401 for (i=4; i >= 0; i--)
1402 {
1403 p=p*(8.0/x)*(8.0/x)+Pone[i];
1404 q=q*(8.0/x)*(8.0/x)+Qone[i];
1405 }
1406 return(p/q);
1407}
1408
1409static MagickRealType BesselOrderOne(MagickRealType x)
1410{
1411 MagickRealType
1412 p,
1413 q;
1414
1415 if (x == 0.0)
1416 return(0.0);
1417 p=x;
1418 if (x < 0.0)
1419 x=(-x);
1420 if (x < 8.0)
1421 return(p*J1(x));
1422 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1423 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1424 cos((double) x))));
1425 if (p < 0.0)
1426 q=(-q);
1427 return(q);
1428}
1429
1430/*
1431%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1432% %
1433% %
1434% %
1435+ D e s t r o y R e s i z e F i l t e r %
1436% %
1437% %
1438% %
1439%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1440%
1441% DestroyResizeFilter() destroy the resize filter.
1442%
cristya2ffd7e2010-03-10 20:50:30 +00001443% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001444%
1445% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1446%
1447% A description of each parameter follows:
1448%
1449% o resize_filter: the resize filter.
1450%
1451*/
1452MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1453{
1454 assert(resize_filter != (ResizeFilter *) NULL);
1455 assert(resize_filter->signature == MagickSignature);
1456 resize_filter->signature=(~MagickSignature);
1457 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1458 return(resize_filter);
1459}
1460
1461/*
1462%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1463% %
1464% %
1465% %
1466+ G e t R e s i z e F i l t e r S u p p o r t %
1467% %
1468% %
1469% %
1470%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1471%
1472% GetResizeFilterSupport() return the current support window size for this
1473% filter. Note that this may have been enlarged by filter:blur factor.
1474%
1475% The format of the GetResizeFilterSupport method is:
1476%
1477% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1478%
1479% A description of each parameter follows:
1480%
1481% o filter: Image filter to use.
1482%
1483*/
1484MagickExport MagickRealType GetResizeFilterSupport(
1485 const ResizeFilter *resize_filter)
1486{
1487 assert(resize_filter != (ResizeFilter *) NULL);
1488 assert(resize_filter->signature == MagickSignature);
1489 return(resize_filter->support*resize_filter->blur);
1490}
1491
1492/*
1493%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1494% %
1495% %
1496% %
1497+ G e t R e s i z e F i l t e r W e i g h t %
1498% %
1499% %
1500% %
1501%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1502%
1503% GetResizeFilterWeight evaluates the specified resize filter at the point x
1504% which usally lies between zero and the filters current 'support' and
1505% returns the weight of the filter function at that point.
1506%
1507% The format of the GetResizeFilterWeight method is:
1508%
1509% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1510% const MagickRealType x)
1511%
1512% A description of each parameter follows:
1513%
1514% o filter: the filter type.
1515%
1516% o x: the point.
1517%
1518*/
1519MagickExport MagickRealType GetResizeFilterWeight(
1520 const ResizeFilter *resize_filter,const MagickRealType x)
1521{
1522 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001523 scale,
1524 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001525
1526 /*
1527 Windowing function - scale the weighting filter by this amount.
1528 */
1529 assert(resize_filter != (ResizeFilter *) NULL);
1530 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001531 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001532 if ((resize_filter->window_support < MagickEpsilon) ||
1533 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001534 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001535 else
1536 {
anthony55f12332010-09-10 01:13:02 +00001537 scale=resize_filter->scale;
1538 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001539 }
anthony55f12332010-09-10 01:13:02 +00001540 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001541}
1542
1543/*
1544%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1545% %
1546% %
1547% %
1548% M a g n i f y I m a g e %
1549% %
1550% %
1551% %
1552%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1553%
1554% MagnifyImage() is a convenience method that scales an image proportionally
1555% to twice its size.
1556%
1557% The format of the MagnifyImage method is:
1558%
1559% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1560%
1561% A description of each parameter follows:
1562%
1563% o image: the image.
1564%
1565% o exception: return any errors or warnings in this structure.
1566%
1567*/
1568MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1569{
1570 Image
1571 *magnify_image;
1572
1573 assert(image != (Image *) NULL);
1574 assert(image->signature == MagickSignature);
1575 if (image->debug != MagickFalse)
1576 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1577 assert(exception != (ExceptionInfo *) NULL);
1578 assert(exception->signature == MagickSignature);
1579 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1580 1.0,exception);
1581 return(magnify_image);
1582}
1583
1584/*
1585%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1586% %
1587% %
1588% %
1589% M i n i f y I m a g e %
1590% %
1591% %
1592% %
1593%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1594%
1595% MinifyImage() is a convenience method that scales an image proportionally
1596% to half its size.
1597%
1598% The format of the MinifyImage method is:
1599%
1600% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1601%
1602% A description of each parameter follows:
1603%
1604% o image: the image.
1605%
1606% o exception: return any errors or warnings in this structure.
1607%
1608*/
1609MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1610{
1611 Image
1612 *minify_image;
1613
1614 assert(image != (Image *) NULL);
1615 assert(image->signature == MagickSignature);
1616 if (image->debug != MagickFalse)
1617 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1618 assert(exception != (ExceptionInfo *) NULL);
1619 assert(exception->signature == MagickSignature);
1620 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1621 1.0,exception);
1622 return(minify_image);
1623}
1624
1625/*
1626%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1627% %
1628% %
1629% %
1630% R e s a m p l e I m a g e %
1631% %
1632% %
1633% %
1634%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1635%
1636% ResampleImage() resize image in terms of its pixel size, so that when
1637% displayed at the given resolution it will be the same size in terms of
1638% real world units as the original image at the original resolution.
1639%
1640% The format of the ResampleImage method is:
1641%
1642% Image *ResampleImage(Image *image,const double x_resolution,
1643% const double y_resolution,const FilterTypes filter,const double blur,
1644% ExceptionInfo *exception)
1645%
1646% A description of each parameter follows:
1647%
1648% o image: the image to be resized to fit the given resolution.
1649%
1650% o x_resolution: the new image x resolution.
1651%
1652% o y_resolution: the new image y resolution.
1653%
1654% o filter: Image filter to use.
1655%
1656% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1657%
1658*/
1659MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1660 const double y_resolution,const FilterTypes filter,const double blur,
1661 ExceptionInfo *exception)
1662{
1663#define ResampleImageTag "Resample/Image"
1664
1665 Image
1666 *resample_image;
1667
cristybb503372010-05-27 20:51:26 +00001668 size_t
cristy3ed852e2009-09-05 21:47:34 +00001669 height,
1670 width;
1671
1672 /*
1673 Initialize sampled image attributes.
1674 */
1675 assert(image != (const Image *) NULL);
1676 assert(image->signature == MagickSignature);
1677 if (image->debug != MagickFalse)
1678 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1679 assert(exception != (ExceptionInfo *) NULL);
1680 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001681 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1682 72.0 : image->x_resolution)+0.5);
1683 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1684 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001685 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1686 if (resample_image != (Image *) NULL)
1687 {
1688 resample_image->x_resolution=x_resolution;
1689 resample_image->y_resolution=y_resolution;
1690 }
1691 return(resample_image);
1692}
1693#if defined(MAGICKCORE_LQR_DELEGATE)
1694
1695/*
1696%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1697% %
1698% %
1699% %
1700% L i q u i d R e s c a l e I m a g e %
1701% %
1702% %
1703% %
1704%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1705%
1706% LiquidRescaleImage() rescales image with seam carving.
1707%
1708% The format of the LiquidRescaleImage method is:
1709%
1710% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001711% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001712% const double delta_x,const double rigidity,ExceptionInfo *exception)
1713%
1714% A description of each parameter follows:
1715%
1716% o image: the image.
1717%
1718% o columns: the number of columns in the rescaled image.
1719%
1720% o rows: the number of rows in the rescaled image.
1721%
1722% o delta_x: maximum seam transversal step (0 means straight seams).
1723%
1724% o rigidity: introduce a bias for non-straight seams (typically 0).
1725%
1726% o exception: return any errors or warnings in this structure.
1727%
1728*/
cristy9af9b5d2010-08-15 17:04:28 +00001729MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1730 const size_t rows,const double delta_x,const double rigidity,
1731 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001732{
1733#define LiquidRescaleImageTag "Rescale/Image"
1734
cristyc5c6f662010-09-22 14:23:02 +00001735 CacheView
1736 *rescale_view;
1737
cristy3ed852e2009-09-05 21:47:34 +00001738 const char
1739 *map;
1740
1741 guchar
1742 *packet;
1743
1744 Image
1745 *rescale_image;
1746
1747 int
1748 x,
1749 y;
1750
1751 LqrCarver
1752 *carver;
1753
1754 LqrRetVal
1755 lqr_status;
1756
1757 MagickBooleanType
1758 status;
1759
1760 MagickPixelPacket
1761 pixel;
1762
1763 unsigned char
1764 *pixels;
1765
1766 /*
1767 Liquid rescale image.
1768 */
1769 assert(image != (const Image *) NULL);
1770 assert(image->signature == MagickSignature);
1771 if (image->debug != MagickFalse)
1772 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1773 assert(exception != (ExceptionInfo *) NULL);
1774 assert(exception->signature == MagickSignature);
1775 if ((columns == 0) || (rows == 0))
1776 return((Image *) NULL);
1777 if ((columns == image->columns) && (rows == image->rows))
1778 return(CloneImage(image,0,0,MagickTrue,exception));
1779 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001780 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001781 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1782 {
1783 Image
1784 *resize_image;
1785
cristybb503372010-05-27 20:51:26 +00001786 size_t
cristy3ed852e2009-09-05 21:47:34 +00001787 height,
1788 width;
1789
1790 /*
1791 Honor liquid resize size limitations.
1792 */
1793 for (width=image->columns; columns >= (2*width-1); width*=2);
1794 for (height=image->rows; rows >= (2*height-1); height*=2);
1795 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1796 exception);
1797 if (resize_image == (Image *) NULL)
1798 return((Image *) NULL);
1799 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1800 rigidity,exception);
1801 resize_image=DestroyImage(resize_image);
1802 return(rescale_image);
1803 }
1804 map="RGB";
1805 if (image->matte == MagickFalse)
1806 map="RGBA";
1807 if (image->colorspace == CMYKColorspace)
1808 {
1809 map="CMYK";
1810 if (image->matte == MagickFalse)
1811 map="CMYKA";
1812 }
1813 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1814 strlen(map)*sizeof(*pixels));
1815 if (pixels == (unsigned char *) NULL)
1816 return((Image *) NULL);
1817 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1818 pixels,exception);
1819 if (status == MagickFalse)
1820 {
1821 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1822 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1823 }
1824 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1825 if (carver == (LqrCarver *) NULL)
1826 {
1827 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1828 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1829 }
1830 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1831 lqr_status=lqr_carver_resize(carver,columns,rows);
1832 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1833 lqr_carver_get_height(carver),MagickTrue,exception);
1834 if (rescale_image == (Image *) NULL)
1835 {
1836 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1837 return((Image *) NULL);
1838 }
1839 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1840 {
1841 InheritException(exception,&rescale_image->exception);
1842 rescale_image=DestroyImage(rescale_image);
1843 return((Image *) NULL);
1844 }
1845 GetMagickPixelPacket(rescale_image,&pixel);
1846 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001847 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001848 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1849 {
1850 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001851 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001852
1853 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001854 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001855
anthony22aad252010-09-23 06:59:07 +00001856 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001857 if (q == (PixelPacket *) NULL)
1858 break;
cristyc5c6f662010-09-22 14:23:02 +00001859 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001860 pixel.red=QuantumRange*(packet[0]/255.0);
1861 pixel.green=QuantumRange*(packet[1]/255.0);
1862 pixel.blue=QuantumRange*(packet[2]/255.0);
1863 if (image->colorspace != CMYKColorspace)
1864 {
1865 if (image->matte == MagickFalse)
1866 pixel.opacity=QuantumRange*(packet[3]/255.0);
1867 }
1868 else
1869 {
1870 pixel.index=QuantumRange*(packet[3]/255.0);
1871 if (image->matte == MagickFalse)
1872 pixel.opacity=QuantumRange*(packet[4]/255.0);
1873 }
1874 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001875 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001876 break;
1877 }
cristyc5c6f662010-09-22 14:23:02 +00001878 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001879 /*
1880 Relinquish resources.
1881 */
1882 lqr_carver_destroy(carver);
1883 return(rescale_image);
1884}
1885#else
1886MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001887 const size_t magick_unused(columns),const size_t magick_unused(rows),
1888 const double magick_unused(delta_x),const double magick_unused(rigidity),
1889 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001890{
1891 assert(image != (const Image *) NULL);
1892 assert(image->signature == MagickSignature);
1893 if (image->debug != MagickFalse)
1894 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1895 assert(exception != (ExceptionInfo *) NULL);
1896 assert(exception->signature == MagickSignature);
1897 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1898 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1899 return((Image *) NULL);
1900}
1901#endif
1902
1903/*
1904%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1905% %
1906% %
1907% %
1908% R e s i z e I m a g e %
1909% %
1910% %
1911% %
1912%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1913%
1914% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001915% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001916%
1917% If an undefined filter is given the filter defaults to Mitchell for a
1918% colormapped image, a image with a matte channel, or if the image is
1919% enlarged. Otherwise the filter defaults to a Lanczos.
1920%
1921% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1922%
1923% The format of the ResizeImage method is:
1924%
cristybb503372010-05-27 20:51:26 +00001925% Image *ResizeImage(Image *image,const size_t columns,
1926% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001927% ExceptionInfo *exception)
1928%
1929% A description of each parameter follows:
1930%
1931% o image: the image.
1932%
1933% o columns: the number of columns in the scaled image.
1934%
1935% o rows: the number of rows in the scaled image.
1936%
1937% o filter: Image filter to use.
1938%
cristy9af9b5d2010-08-15 17:04:28 +00001939% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1940% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001941%
1942% o exception: return any errors or warnings in this structure.
1943%
1944*/
1945
1946typedef struct _ContributionInfo
1947{
1948 MagickRealType
1949 weight;
1950
cristybb503372010-05-27 20:51:26 +00001951 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001952 pixel;
1953} ContributionInfo;
1954
1955static ContributionInfo **DestroyContributionThreadSet(
1956 ContributionInfo **contribution)
1957{
cristybb503372010-05-27 20:51:26 +00001958 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001959 i;
1960
1961 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001962 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001963 if (contribution[i] != (ContributionInfo *) NULL)
1964 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1965 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001966 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001967 return(contribution);
1968}
1969
1970static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1971{
cristybb503372010-05-27 20:51:26 +00001972 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001973 i;
1974
1975 ContributionInfo
1976 **contribution;
1977
cristybb503372010-05-27 20:51:26 +00001978 size_t
cristy3ed852e2009-09-05 21:47:34 +00001979 number_threads;
1980
1981 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001982 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001983 sizeof(*contribution));
1984 if (contribution == (ContributionInfo **) NULL)
1985 return((ContributionInfo **) NULL);
1986 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00001987 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00001988 {
1989 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1990 sizeof(**contribution));
1991 if (contribution[i] == (ContributionInfo *) NULL)
1992 return(DestroyContributionThreadSet(contribution));
1993 }
1994 return(contribution);
1995}
1996
1997static inline double MagickMax(const double x,const double y)
1998{
1999 if (x > y)
2000 return(x);
2001 return(y);
2002}
2003
2004static inline double MagickMin(const double x,const double y)
2005{
2006 if (x < y)
2007 return(x);
2008 return(y);
2009}
2010
2011static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2012 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002013 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002014{
2015#define ResizeImageTag "Resize/Image"
2016
cristyfa112112010-01-04 17:48:07 +00002017 CacheView
2018 *image_view,
2019 *resize_view;
2020
cristy3ed852e2009-09-05 21:47:34 +00002021 ClassType
2022 storage_class;
2023
2024 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002025 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002026
cristy3ed852e2009-09-05 21:47:34 +00002027 MagickBooleanType
2028 status;
2029
2030 MagickPixelPacket
2031 zero;
2032
2033 MagickRealType
2034 scale,
2035 support;
2036
cristy9af9b5d2010-08-15 17:04:28 +00002037 ssize_t
2038 x;
2039
cristy3ed852e2009-09-05 21:47:34 +00002040 /*
2041 Apply filter to resize horizontally from image to resize image.
2042 */
cristy5d824382010-09-06 14:00:17 +00002043 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002044 support=scale*GetResizeFilterSupport(resize_filter);
2045 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2046 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2047 {
2048 InheritException(exception,&resize_image->exception);
2049 return(MagickFalse);
2050 }
2051 if (support < 0.5)
2052 {
2053 /*
nicolas07bac812010-09-19 18:47:02 +00002054 Support too small even for nearest neighbour: Reduce to point
2055 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002056 */
2057 support=(MagickRealType) 0.5;
2058 scale=1.0;
2059 }
2060 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2061 if (contributions == (ContributionInfo **) NULL)
2062 {
2063 (void) ThrowMagickException(exception,GetMagickModule(),
2064 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2065 return(MagickFalse);
2066 }
2067 status=MagickTrue;
2068 scale=1.0/scale;
2069 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2070 image_view=AcquireCacheView(image);
2071 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002072#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002073 #pragma omp parallel for shared(status)
2074#endif
cristybb503372010-05-27 20:51:26 +00002075 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002076 {
cristy3ed852e2009-09-05 21:47:34 +00002077 MagickRealType
2078 center,
2079 density;
2080
2081 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002082 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002083
2084 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002085 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002086
cristy03dbbd22010-09-19 23:04:47 +00002087 register ContributionInfo
2088 *restrict contribution;
2089
cristy3ed852e2009-09-05 21:47:34 +00002090 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002091 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002092
cristy3ed852e2009-09-05 21:47:34 +00002093 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002094 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002095
cristy03dbbd22010-09-19 23:04:47 +00002096 register ssize_t
2097 y;
2098
cristy9af9b5d2010-08-15 17:04:28 +00002099 ssize_t
2100 n,
2101 start,
2102 stop;
2103
cristy3ed852e2009-09-05 21:47:34 +00002104 if (status == MagickFalse)
2105 continue;
2106 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002107 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2108 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002109 density=0.0;
2110 contribution=contributions[GetOpenMPThreadId()];
2111 for (n=0; n < (stop-start); n++)
2112 {
2113 contribution[n].pixel=start+n;
2114 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2115 ((MagickRealType) (start+n)-center+0.5));
2116 density+=contribution[n].weight;
2117 }
2118 if ((density != 0.0) && (density != 1.0))
2119 {
cristybb503372010-05-27 20:51:26 +00002120 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002121 i;
2122
2123 /*
2124 Normalize.
2125 */
2126 density=1.0/density;
2127 for (i=0; i < n; i++)
2128 contribution[i].weight*=density;
2129 }
cristy9af9b5d2010-08-15 17:04:28 +00002130 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2131 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002132 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2133 exception);
2134 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2135 {
2136 status=MagickFalse;
2137 continue;
2138 }
2139 indexes=GetCacheViewVirtualIndexQueue(image_view);
2140 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002141 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002142 {
cristy3ed852e2009-09-05 21:47:34 +00002143 MagickPixelPacket
2144 pixel;
2145
2146 MagickRealType
2147 alpha;
2148
cristybb503372010-05-27 20:51:26 +00002149 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002150 i;
2151
cristy9af9b5d2010-08-15 17:04:28 +00002152 ssize_t
2153 j;
2154
cristy3ed852e2009-09-05 21:47:34 +00002155 pixel=zero;
2156 if (image->matte == MagickFalse)
2157 {
2158 for (i=0; i < n; i++)
2159 {
2160 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2161 (contribution[i].pixel-contribution[0].pixel);
2162 alpha=contribution[i].weight;
2163 pixel.red+=alpha*(p+j)->red;
2164 pixel.green+=alpha*(p+j)->green;
2165 pixel.blue+=alpha*(p+j)->blue;
2166 pixel.opacity+=alpha*(p+j)->opacity;
2167 }
cristyce70c172010-01-07 17:15:30 +00002168 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2169 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2170 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2171 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002172 if ((image->colorspace == CMYKColorspace) &&
2173 (resize_image->colorspace == CMYKColorspace))
2174 {
2175 for (i=0; i < n; i++)
2176 {
2177 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2178 (contribution[i].pixel-contribution[0].pixel);
2179 alpha=contribution[i].weight;
2180 pixel.index+=alpha*indexes[j];
2181 }
cristyce70c172010-01-07 17:15:30 +00002182 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002183 }
2184 }
2185 else
2186 {
2187 MagickRealType
2188 gamma;
2189
2190 gamma=0.0;
2191 for (i=0; i < n; i++)
2192 {
2193 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2194 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002195 alpha=contribution[i].weight*QuantumScale*
2196 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002197 pixel.red+=alpha*(p+j)->red;
2198 pixel.green+=alpha*(p+j)->green;
2199 pixel.blue+=alpha*(p+j)->blue;
2200 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2201 gamma+=alpha;
2202 }
2203 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002204 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2205 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2206 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2207 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002208 if ((image->colorspace == CMYKColorspace) &&
2209 (resize_image->colorspace == CMYKColorspace))
2210 {
2211 for (i=0; i < n; i++)
2212 {
2213 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2214 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002215 alpha=contribution[i].weight*QuantumScale*
2216 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002217 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002218 }
cristyce70c172010-01-07 17:15:30 +00002219 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2220 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002221 }
2222 }
2223 if ((resize_image->storage_class == PseudoClass) &&
2224 (image->storage_class == PseudoClass))
2225 {
cristybb503372010-05-27 20:51:26 +00002226 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002227 1.0)+0.5);
2228 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2229 (contribution[i-start].pixel-contribution[0].pixel);
2230 resize_indexes[y]=indexes[j];
2231 }
2232 q++;
2233 }
2234 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2235 status=MagickFalse;
2236 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2237 {
2238 MagickBooleanType
2239 proceed;
2240
cristyb5d5f722009-11-04 03:03:49 +00002241#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002242 #pragma omp critical (MagickCore_HorizontalFilter)
2243#endif
cristy9af9b5d2010-08-15 17:04:28 +00002244 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002245 if (proceed == MagickFalse)
2246 status=MagickFalse;
2247 }
2248 }
2249 resize_view=DestroyCacheView(resize_view);
2250 image_view=DestroyCacheView(image_view);
2251 contributions=DestroyContributionThreadSet(contributions);
2252 return(status);
2253}
2254
2255static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2256 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002257 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002258{
cristyfa112112010-01-04 17:48:07 +00002259 CacheView
2260 *image_view,
2261 *resize_view;
2262
cristy3ed852e2009-09-05 21:47:34 +00002263 ClassType
2264 storage_class;
2265
2266 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002267 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002268
cristy3ed852e2009-09-05 21:47:34 +00002269 MagickBooleanType
2270 status;
2271
2272 MagickPixelPacket
2273 zero;
2274
2275 MagickRealType
2276 scale,
2277 support;
2278
cristy9af9b5d2010-08-15 17:04:28 +00002279 ssize_t
2280 y;
2281
cristy3ed852e2009-09-05 21:47:34 +00002282 /*
cristy9af9b5d2010-08-15 17:04:28 +00002283 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002284 */
cristy5d824382010-09-06 14:00:17 +00002285 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002286 support=scale*GetResizeFilterSupport(resize_filter);
2287 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2288 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2289 {
2290 InheritException(exception,&resize_image->exception);
2291 return(MagickFalse);
2292 }
2293 if (support < 0.5)
2294 {
2295 /*
nicolas07bac812010-09-19 18:47:02 +00002296 Support too small even for nearest neighbour: Reduce to point
2297 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002298 */
2299 support=(MagickRealType) 0.5;
2300 scale=1.0;
2301 }
2302 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2303 if (contributions == (ContributionInfo **) NULL)
2304 {
2305 (void) ThrowMagickException(exception,GetMagickModule(),
2306 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2307 return(MagickFalse);
2308 }
2309 status=MagickTrue;
2310 scale=1.0/scale;
2311 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2312 image_view=AcquireCacheView(image);
2313 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002314#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002315 #pragma omp parallel for shared(status)
2316#endif
cristybb503372010-05-27 20:51:26 +00002317 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002318 {
cristy3ed852e2009-09-05 21:47:34 +00002319 MagickRealType
2320 center,
2321 density;
2322
2323 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002324 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002325
2326 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002327 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002328
cristy03dbbd22010-09-19 23:04:47 +00002329 register ContributionInfo
2330 *restrict contribution;
2331
cristy3ed852e2009-09-05 21:47:34 +00002332 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002333 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002334
cristy9af9b5d2010-08-15 17:04:28 +00002335 register PixelPacket
2336 *restrict q;
2337
cristybb503372010-05-27 20:51:26 +00002338 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002339 x;
2340
cristy9af9b5d2010-08-15 17:04:28 +00002341 ssize_t
2342 n,
2343 start,
2344 stop;
cristy3ed852e2009-09-05 21:47:34 +00002345
2346 if (status == MagickFalse)
2347 continue;
cristy679e6962010-03-18 00:42:45 +00002348 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002349 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2350 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002351 density=0.0;
2352 contribution=contributions[GetOpenMPThreadId()];
2353 for (n=0; n < (stop-start); n++)
2354 {
2355 contribution[n].pixel=start+n;
2356 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2357 ((MagickRealType) (start+n)-center+0.5));
2358 density+=contribution[n].weight;
2359 }
2360 if ((density != 0.0) && (density != 1.0))
2361 {
cristybb503372010-05-27 20:51:26 +00002362 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002363 i;
2364
2365 /*
2366 Normalize.
2367 */
2368 density=1.0/density;
2369 for (i=0; i < n; i++)
2370 contribution[i].weight*=density;
2371 }
2372 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002373 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2374 exception);
cristy3ed852e2009-09-05 21:47:34 +00002375 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2376 exception);
2377 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2378 {
2379 status=MagickFalse;
2380 continue;
2381 }
2382 indexes=GetCacheViewVirtualIndexQueue(image_view);
2383 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002384 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002385 {
cristy3ed852e2009-09-05 21:47:34 +00002386 MagickPixelPacket
2387 pixel;
2388
2389 MagickRealType
2390 alpha;
2391
cristybb503372010-05-27 20:51:26 +00002392 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002393 i;
2394
cristy9af9b5d2010-08-15 17:04:28 +00002395 ssize_t
2396 j;
2397
cristy3ed852e2009-09-05 21:47:34 +00002398 pixel=zero;
2399 if (image->matte == MagickFalse)
2400 {
2401 for (i=0; i < n; i++)
2402 {
cristybb503372010-05-27 20:51:26 +00002403 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002404 image->columns+x);
2405 alpha=contribution[i].weight;
2406 pixel.red+=alpha*(p+j)->red;
2407 pixel.green+=alpha*(p+j)->green;
2408 pixel.blue+=alpha*(p+j)->blue;
2409 pixel.opacity+=alpha*(p+j)->opacity;
2410 }
cristyce70c172010-01-07 17:15:30 +00002411 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2412 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2413 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2414 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002415 if ((image->colorspace == CMYKColorspace) &&
2416 (resize_image->colorspace == CMYKColorspace))
2417 {
2418 for (i=0; i < n; i++)
2419 {
cristybb503372010-05-27 20:51:26 +00002420 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002421 image->columns+x);
2422 alpha=contribution[i].weight;
2423 pixel.index+=alpha*indexes[j];
2424 }
cristyce70c172010-01-07 17:15:30 +00002425 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002426 }
2427 }
2428 else
2429 {
2430 MagickRealType
2431 gamma;
2432
2433 gamma=0.0;
2434 for (i=0; i < n; i++)
2435 {
cristybb503372010-05-27 20:51:26 +00002436 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002437 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002438 alpha=contribution[i].weight*QuantumScale*
2439 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002440 pixel.red+=alpha*(p+j)->red;
2441 pixel.green+=alpha*(p+j)->green;
2442 pixel.blue+=alpha*(p+j)->blue;
2443 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2444 gamma+=alpha;
2445 }
2446 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002447 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2448 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2449 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2450 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002451 if ((image->colorspace == CMYKColorspace) &&
2452 (resize_image->colorspace == CMYKColorspace))
2453 {
2454 for (i=0; i < n; i++)
2455 {
cristybb503372010-05-27 20:51:26 +00002456 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002457 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002458 alpha=contribution[i].weight*QuantumScale*
2459 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002460 pixel.index+=alpha*indexes[j];
2461 }
cristyce70c172010-01-07 17:15:30 +00002462 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2463 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002464 }
2465 }
2466 if ((resize_image->storage_class == PseudoClass) &&
2467 (image->storage_class == PseudoClass))
2468 {
cristybb503372010-05-27 20:51:26 +00002469 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002470 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002471 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002472 image->columns+x);
2473 resize_indexes[x]=indexes[j];
2474 }
2475 q++;
2476 }
2477 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2478 status=MagickFalse;
2479 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2480 {
2481 MagickBooleanType
2482 proceed;
2483
cristyb5d5f722009-11-04 03:03:49 +00002484#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002485 #pragma omp critical (MagickCore_VerticalFilter)
2486#endif
cristy9af9b5d2010-08-15 17:04:28 +00002487 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002488 if (proceed == MagickFalse)
2489 status=MagickFalse;
2490 }
2491 }
2492 resize_view=DestroyCacheView(resize_view);
2493 image_view=DestroyCacheView(image_view);
2494 contributions=DestroyContributionThreadSet(contributions);
2495 return(status);
2496}
2497
cristybb503372010-05-27 20:51:26 +00002498MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2499 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002500 ExceptionInfo *exception)
2501{
2502#define WorkLoadFactor 0.265
2503
2504 FilterTypes
2505 filter_type;
2506
2507 Image
2508 *filter_image,
2509 *resize_image;
2510
cristy9af9b5d2010-08-15 17:04:28 +00002511 MagickOffsetType
2512 offset;
2513
cristy3ed852e2009-09-05 21:47:34 +00002514 MagickRealType
2515 x_factor,
2516 y_factor;
2517
2518 MagickSizeType
2519 span;
2520
2521 MagickStatusType
2522 status;
2523
2524 ResizeFilter
2525 *resize_filter;
2526
cristy3ed852e2009-09-05 21:47:34 +00002527 /*
2528 Acquire resize image.
2529 */
2530 assert(image != (Image *) NULL);
2531 assert(image->signature == MagickSignature);
2532 if (image->debug != MagickFalse)
2533 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2534 assert(exception != (ExceptionInfo *) NULL);
2535 assert(exception->signature == MagickSignature);
2536 if ((columns == 0) || (rows == 0))
2537 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2538 if ((columns == image->columns) && (rows == image->rows) &&
2539 (filter == UndefinedFilter) && (blur == 1.0))
2540 return(CloneImage(image,0,0,MagickTrue,exception));
2541 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2542 if (resize_image == (Image *) NULL)
2543 return(resize_image);
2544 /*
2545 Acquire resize filter.
2546 */
2547 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2548 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2549 if ((x_factor*y_factor) > WorkLoadFactor)
2550 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2551 else
2552 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2553 if (filter_image == (Image *) NULL)
2554 return(DestroyImage(resize_image));
2555 filter_type=LanczosFilter;
2556 if (filter != UndefinedFilter)
2557 filter_type=filter;
2558 else
2559 if ((x_factor == 1.0) && (y_factor == 1.0))
2560 filter_type=PointFilter;
2561 else
2562 if ((image->storage_class == PseudoClass) ||
2563 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2564 filter_type=MitchellFilter;
2565 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2566 exception);
2567 /*
2568 Resize image.
2569 */
cristy9af9b5d2010-08-15 17:04:28 +00002570 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002571 if ((x_factor*y_factor) > WorkLoadFactor)
2572 {
2573 span=(MagickSizeType) (filter_image->columns+rows);
2574 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002575 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002576 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002577 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002578 }
2579 else
2580 {
2581 span=(MagickSizeType) (filter_image->rows+columns);
2582 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002583 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002584 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002585 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002586 }
2587 /*
2588 Free resources.
2589 */
2590 filter_image=DestroyImage(filter_image);
2591 resize_filter=DestroyResizeFilter(resize_filter);
2592 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2593 return((Image *) NULL);
2594 resize_image->type=image->type;
2595 return(resize_image);
2596}
2597
2598/*
2599%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2600% %
2601% %
2602% %
2603% S a m p l e I m a g e %
2604% %
2605% %
2606% %
2607%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2608%
2609% SampleImage() scales an image to the desired dimensions with pixel
2610% sampling. Unlike other scaling methods, this method does not introduce
2611% any additional color into the scaled image.
2612%
2613% The format of the SampleImage method is:
2614%
cristybb503372010-05-27 20:51:26 +00002615% Image *SampleImage(const Image *image,const size_t columns,
2616% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002617%
2618% A description of each parameter follows:
2619%
2620% o image: the image.
2621%
2622% o columns: the number of columns in the sampled image.
2623%
2624% o rows: the number of rows in the sampled image.
2625%
2626% o exception: return any errors or warnings in this structure.
2627%
2628*/
cristybb503372010-05-27 20:51:26 +00002629MagickExport Image *SampleImage(const Image *image,const size_t columns,
2630 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002631{
2632#define SampleImageTag "Sample/Image"
2633
cristyc4c8d132010-01-07 01:58:38 +00002634 CacheView
2635 *image_view,
2636 *sample_view;
2637
cristy3ed852e2009-09-05 21:47:34 +00002638 Image
2639 *sample_image;
2640
cristy3ed852e2009-09-05 21:47:34 +00002641 MagickBooleanType
2642 status;
2643
cristy5f959472010-05-27 22:19:46 +00002644 MagickOffsetType
2645 progress;
2646
cristybb503372010-05-27 20:51:26 +00002647 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002648 x;
2649
cristy5f959472010-05-27 22:19:46 +00002650 ssize_t
2651 *x_offset,
2652 y;
2653
cristy3ed852e2009-09-05 21:47:34 +00002654 /*
2655 Initialize sampled image attributes.
2656 */
2657 assert(image != (const Image *) NULL);
2658 assert(image->signature == MagickSignature);
2659 if (image->debug != MagickFalse)
2660 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2661 assert(exception != (ExceptionInfo *) NULL);
2662 assert(exception->signature == MagickSignature);
2663 if ((columns == 0) || (rows == 0))
2664 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2665 if ((columns == image->columns) && (rows == image->rows))
2666 return(CloneImage(image,0,0,MagickTrue,exception));
2667 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2668 if (sample_image == (Image *) NULL)
2669 return((Image *) NULL);
2670 /*
2671 Allocate scan line buffer and column offset buffers.
2672 */
cristybb503372010-05-27 20:51:26 +00002673 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002674 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002675 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002676 {
2677 sample_image=DestroyImage(sample_image);
2678 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2679 }
cristybb503372010-05-27 20:51:26 +00002680 for (x=0; x < (ssize_t) sample_image->columns; x++)
2681 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002682 sample_image->columns);
2683 /*
2684 Sample each row.
2685 */
2686 status=MagickTrue;
2687 progress=0;
2688 image_view=AcquireCacheView(image);
2689 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002690#if defined(MAGICKCORE_OPENMP_SUPPORT)
2691 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002692#endif
cristybb503372010-05-27 20:51:26 +00002693 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002694 {
cristy3ed852e2009-09-05 21:47:34 +00002695 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002696 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002697
2698 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002699 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002700
2701 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002702 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002703
cristy3ed852e2009-09-05 21:47:34 +00002704 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002705 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002706
cristy03dbbd22010-09-19 23:04:47 +00002707 register ssize_t
2708 x;
2709
cristy9af9b5d2010-08-15 17:04:28 +00002710 ssize_t
2711 y_offset;
2712
cristy3ed852e2009-09-05 21:47:34 +00002713 if (status == MagickFalse)
2714 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002715 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2716 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002717 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2718 exception);
2719 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2720 exception);
2721 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2722 {
2723 status=MagickFalse;
2724 continue;
2725 }
2726 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2727 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2728 /*
2729 Sample each column.
2730 */
cristybb503372010-05-27 20:51:26 +00002731 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002732 *q++=p[x_offset[x]];
2733 if ((image->storage_class == PseudoClass) ||
2734 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002735 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002736 sample_indexes[x]=indexes[x_offset[x]];
2737 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2738 status=MagickFalse;
2739 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2740 {
2741 MagickBooleanType
2742 proceed;
2743
cristyb5d5f722009-11-04 03:03:49 +00002744#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002745 #pragma omp critical (MagickCore_SampleImage)
2746#endif
2747 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2748 if (proceed == MagickFalse)
2749 status=MagickFalse;
2750 }
2751 }
2752 image_view=DestroyCacheView(image_view);
2753 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002754 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002755 sample_image->type=image->type;
2756 return(sample_image);
2757}
2758
2759/*
2760%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2761% %
2762% %
2763% %
2764% S c a l e I m a g e %
2765% %
2766% %
2767% %
2768%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2769%
2770% ScaleImage() changes the size of an image to the given dimensions.
2771%
2772% The format of the ScaleImage method is:
2773%
cristybb503372010-05-27 20:51:26 +00002774% Image *ScaleImage(const Image *image,const size_t columns,
2775% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002776%
2777% A description of each parameter follows:
2778%
2779% o image: the image.
2780%
2781% o columns: the number of columns in the scaled image.
2782%
2783% o rows: the number of rows in the scaled image.
2784%
2785% o exception: return any errors or warnings in this structure.
2786%
2787*/
cristybb503372010-05-27 20:51:26 +00002788MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2789 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002790{
2791#define ScaleImageTag "Scale/Image"
2792
cristyed6cb232010-01-20 03:07:53 +00002793 CacheView
2794 *image_view,
2795 *scale_view;
2796
cristy3ed852e2009-09-05 21:47:34 +00002797 Image
2798 *scale_image;
2799
cristy3ed852e2009-09-05 21:47:34 +00002800 MagickBooleanType
2801 next_column,
2802 next_row,
2803 proceed;
2804
2805 MagickPixelPacket
2806 pixel,
2807 *scale_scanline,
2808 *scanline,
2809 *x_vector,
2810 *y_vector,
2811 zero;
2812
cristy3ed852e2009-09-05 21:47:34 +00002813 PointInfo
2814 scale,
2815 span;
2816
cristybb503372010-05-27 20:51:26 +00002817 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002818 i;
2819
cristy9af9b5d2010-08-15 17:04:28 +00002820 ssize_t
2821 number_rows,
2822 y;
2823
cristy3ed852e2009-09-05 21:47:34 +00002824 /*
2825 Initialize scaled image attributes.
2826 */
2827 assert(image != (const Image *) NULL);
2828 assert(image->signature == MagickSignature);
2829 if (image->debug != MagickFalse)
2830 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2831 assert(exception != (ExceptionInfo *) NULL);
2832 assert(exception->signature == MagickSignature);
2833 if ((columns == 0) || (rows == 0))
2834 return((Image *) NULL);
2835 if ((columns == image->columns) && (rows == image->rows))
2836 return(CloneImage(image,0,0,MagickTrue,exception));
2837 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2838 if (scale_image == (Image *) NULL)
2839 return((Image *) NULL);
2840 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2841 {
2842 InheritException(exception,&scale_image->exception);
2843 scale_image=DestroyImage(scale_image);
2844 return((Image *) NULL);
2845 }
2846 /*
2847 Allocate memory.
2848 */
2849 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2850 sizeof(*x_vector));
2851 scanline=x_vector;
2852 if (image->rows != scale_image->rows)
2853 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2854 sizeof(*scanline));
2855 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2856 scale_image->columns,sizeof(*scale_scanline));
2857 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2858 sizeof(*y_vector));
2859 if ((scanline == (MagickPixelPacket *) NULL) ||
2860 (scale_scanline == (MagickPixelPacket *) NULL) ||
2861 (x_vector == (MagickPixelPacket *) NULL) ||
2862 (y_vector == (MagickPixelPacket *) NULL))
2863 {
2864 scale_image=DestroyImage(scale_image);
2865 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2866 }
2867 /*
2868 Scale image.
2869 */
2870 number_rows=0;
2871 next_row=MagickTrue;
2872 span.y=1.0;
2873 scale.y=(double) scale_image->rows/(double) image->rows;
2874 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2875 sizeof(*y_vector));
2876 GetMagickPixelPacket(image,&pixel);
2877 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2878 i=0;
cristyed6cb232010-01-20 03:07:53 +00002879 image_view=AcquireCacheView(image);
2880 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002881 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002882 {
2883 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002884 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002885
2886 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002887 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002888
2889 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002890 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002891
cristy3ed852e2009-09-05 21:47:34 +00002892 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002893 *restrict s,
2894 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002895
2896 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002897 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002898
cristy9af9b5d2010-08-15 17:04:28 +00002899 register ssize_t
2900 x;
2901
cristyed6cb232010-01-20 03:07:53 +00002902 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2903 exception);
cristy3ed852e2009-09-05 21:47:34 +00002904 if (q == (PixelPacket *) NULL)
2905 break;
2906 scale_indexes=GetAuthenticIndexQueue(scale_image);
2907 if (scale_image->rows == image->rows)
2908 {
2909 /*
2910 Read a new scanline.
2911 */
cristyed6cb232010-01-20 03:07:53 +00002912 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2913 exception);
cristy3ed852e2009-09-05 21:47:34 +00002914 if (p == (const PixelPacket *) NULL)
2915 break;
cristyed6cb232010-01-20 03:07:53 +00002916 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002917 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002918 {
cristyce70c172010-01-07 17:15:30 +00002919 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2920 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2921 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002922 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002923 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002924 if (indexes != (IndexPacket *) NULL)
2925 x_vector[x].index=(MagickRealType) indexes[x];
2926 p++;
2927 }
2928 }
2929 else
2930 {
2931 /*
2932 Scale Y direction.
2933 */
2934 while (scale.y < span.y)
2935 {
cristy9af9b5d2010-08-15 17:04:28 +00002936 if ((next_row != MagickFalse) &&
2937 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002938 {
2939 /*
2940 Read a new scanline.
2941 */
cristyed6cb232010-01-20 03:07:53 +00002942 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2943 exception);
cristy3ed852e2009-09-05 21:47:34 +00002944 if (p == (const PixelPacket *) NULL)
2945 break;
cristyed6cb232010-01-20 03:07:53 +00002946 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002947 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002948 {
cristyce70c172010-01-07 17:15:30 +00002949 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2950 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2951 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002952 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002953 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002954 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002955 if (indexes != (IndexPacket *) NULL)
2956 x_vector[x].index=(MagickRealType) indexes[x];
2957 p++;
2958 }
2959 number_rows++;
2960 }
cristybb503372010-05-27 20:51:26 +00002961 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002962 {
2963 y_vector[x].red+=scale.y*x_vector[x].red;
2964 y_vector[x].green+=scale.y*x_vector[x].green;
2965 y_vector[x].blue+=scale.y*x_vector[x].blue;
2966 if (scale_image->matte != MagickFalse)
2967 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2968 if (scale_indexes != (IndexPacket *) NULL)
2969 y_vector[x].index+=scale.y*x_vector[x].index;
2970 }
2971 span.y-=scale.y;
2972 scale.y=(double) scale_image->rows/(double) image->rows;
2973 next_row=MagickTrue;
2974 }
cristybb503372010-05-27 20:51:26 +00002975 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002976 {
2977 /*
2978 Read a new scanline.
2979 */
cristyed6cb232010-01-20 03:07:53 +00002980 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2981 exception);
cristy3ed852e2009-09-05 21:47:34 +00002982 if (p == (const PixelPacket *) NULL)
2983 break;
cristyed6cb232010-01-20 03:07:53 +00002984 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002985 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002986 {
cristyce70c172010-01-07 17:15:30 +00002987 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2988 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2989 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002990 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00002991 x_vector[x].opacity=(MagickRealType)
2992 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002993 if (indexes != (IndexPacket *) NULL)
2994 x_vector[x].index=(MagickRealType) indexes[x];
2995 p++;
2996 }
2997 number_rows++;
2998 next_row=MagickFalse;
2999 }
3000 s=scanline;
cristybb503372010-05-27 20:51:26 +00003001 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003002 {
3003 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3004 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3005 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3006 if (image->matte != MagickFalse)
3007 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3008 if (scale_indexes != (IndexPacket *) NULL)
3009 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3010 s->red=pixel.red;
3011 s->green=pixel.green;
3012 s->blue=pixel.blue;
3013 if (scale_image->matte != MagickFalse)
3014 s->opacity=pixel.opacity;
3015 if (scale_indexes != (IndexPacket *) NULL)
3016 s->index=pixel.index;
3017 s++;
3018 y_vector[x]=zero;
3019 }
3020 scale.y-=span.y;
3021 if (scale.y <= 0)
3022 {
3023 scale.y=(double) scale_image->rows/(double) image->rows;
3024 next_row=MagickTrue;
3025 }
3026 span.y=1.0;
3027 }
3028 if (scale_image->columns == image->columns)
3029 {
3030 /*
3031 Transfer scanline to scaled image.
3032 */
3033 s=scanline;
cristybb503372010-05-27 20:51:26 +00003034 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003035 {
cristyce70c172010-01-07 17:15:30 +00003036 q->red=ClampToQuantum(s->red);
3037 q->green=ClampToQuantum(s->green);
3038 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003039 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003040 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003041 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003042 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003043 q++;
3044 s++;
3045 }
3046 }
3047 else
3048 {
3049 /*
3050 Scale X direction.
3051 */
3052 pixel=zero;
3053 next_column=MagickFalse;
3054 span.x=1.0;
3055 s=scanline;
3056 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003057 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003058 {
3059 scale.x=(double) scale_image->columns/(double) image->columns;
3060 while (scale.x >= span.x)
3061 {
3062 if (next_column != MagickFalse)
3063 {
3064 pixel=zero;
3065 t++;
3066 }
3067 pixel.red+=span.x*s->red;
3068 pixel.green+=span.x*s->green;
3069 pixel.blue+=span.x*s->blue;
3070 if (image->matte != MagickFalse)
3071 pixel.opacity+=span.x*s->opacity;
3072 if (scale_indexes != (IndexPacket *) NULL)
3073 pixel.index+=span.x*s->index;
3074 t->red=pixel.red;
3075 t->green=pixel.green;
3076 t->blue=pixel.blue;
3077 if (scale_image->matte != MagickFalse)
3078 t->opacity=pixel.opacity;
3079 if (scale_indexes != (IndexPacket *) NULL)
3080 t->index=pixel.index;
3081 scale.x-=span.x;
3082 span.x=1.0;
3083 next_column=MagickTrue;
3084 }
3085 if (scale.x > 0)
3086 {
3087 if (next_column != MagickFalse)
3088 {
3089 pixel=zero;
3090 next_column=MagickFalse;
3091 t++;
3092 }
3093 pixel.red+=scale.x*s->red;
3094 pixel.green+=scale.x*s->green;
3095 pixel.blue+=scale.x*s->blue;
3096 if (scale_image->matte != MagickFalse)
3097 pixel.opacity+=scale.x*s->opacity;
3098 if (scale_indexes != (IndexPacket *) NULL)
3099 pixel.index+=scale.x*s->index;
3100 span.x-=scale.x;
3101 }
3102 s++;
3103 }
3104 if (span.x > 0)
3105 {
3106 s--;
3107 pixel.red+=span.x*s->red;
3108 pixel.green+=span.x*s->green;
3109 pixel.blue+=span.x*s->blue;
3110 if (scale_image->matte != MagickFalse)
3111 pixel.opacity+=span.x*s->opacity;
3112 if (scale_indexes != (IndexPacket *) NULL)
3113 pixel.index+=span.x*s->index;
3114 }
3115 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003116 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003117 {
3118 t->red=pixel.red;
3119 t->green=pixel.green;
3120 t->blue=pixel.blue;
3121 if (scale_image->matte != MagickFalse)
3122 t->opacity=pixel.opacity;
3123 if (scale_indexes != (IndexPacket *) NULL)
3124 t->index=pixel.index;
3125 }
3126 /*
3127 Transfer scanline to scaled image.
3128 */
3129 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003130 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003131 {
cristyce70c172010-01-07 17:15:30 +00003132 q->red=ClampToQuantum(t->red);
3133 q->green=ClampToQuantum(t->green);
3134 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003135 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003136 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003137 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003138 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003139 t++;
3140 q++;
3141 }
3142 }
cristyed6cb232010-01-20 03:07:53 +00003143 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003144 break;
cristy96b16132010-08-29 17:19:52 +00003145 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3146 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003147 if (proceed == MagickFalse)
3148 break;
3149 }
cristyed6cb232010-01-20 03:07:53 +00003150 scale_view=DestroyCacheView(scale_view);
3151 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003152 /*
3153 Free allocated memory.
3154 */
3155 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3156 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3157 if (scale_image->rows != image->rows)
3158 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3159 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3160 scale_image->type=image->type;
3161 return(scale_image);
3162}
3163
anthony02b4cb42010-10-10 04:54:35 +00003164#if 0
3165 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003166/*
3167%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3168% %
3169% %
3170% %
3171+ S e t R e s i z e F i l t e r S u p p o r t %
3172% %
3173% %
3174% %
3175%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3176%
3177% SetResizeFilterSupport() specifies which IR filter to use to window
3178%
3179% The format of the SetResizeFilterSupport method is:
3180%
3181% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3182% const MagickRealType support)
3183%
3184% A description of each parameter follows:
3185%
3186% o resize_filter: the resize filter.
3187%
3188% o support: the filter spport radius.
3189%
3190*/
3191MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3192 const MagickRealType support)
3193{
3194 assert(resize_filter != (ResizeFilter *) NULL);
3195 assert(resize_filter->signature == MagickSignature);
3196 resize_filter->support=support;
3197}
anthony02b4cb42010-10-10 04:54:35 +00003198#endif
cristy3ed852e2009-09-05 21:47:34 +00003199
3200/*
3201%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3202% %
3203% %
3204% %
3205% T h u m b n a i l I m a g e %
3206% %
3207% %
3208% %
3209%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3210%
3211% ThumbnailImage() changes the size of an image to the given dimensions and
3212% removes any associated profiles. The goal is to produce small low cost
3213% thumbnail images suited for display on the Web.
3214%
3215% The format of the ThumbnailImage method is:
3216%
cristybb503372010-05-27 20:51:26 +00003217% Image *ThumbnailImage(const Image *image,const size_t columns,
3218% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003219%
3220% A description of each parameter follows:
3221%
3222% o image: the image.
3223%
3224% o columns: the number of columns in the scaled image.
3225%
3226% o rows: the number of rows in the scaled image.
3227%
3228% o exception: return any errors or warnings in this structure.
3229%
3230*/
cristy9af9b5d2010-08-15 17:04:28 +00003231MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3232 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003233{
3234#define SampleFactor 5
3235
3236 char
3237 value[MaxTextExtent];
3238
3239 const char
3240 *name;
3241
3242 Image
3243 *thumbnail_image;
3244
3245 MagickRealType
3246 x_factor,
3247 y_factor;
3248
cristybb503372010-05-27 20:51:26 +00003249 size_t
cristy3ed852e2009-09-05 21:47:34 +00003250 version;
3251
cristy9af9b5d2010-08-15 17:04:28 +00003252 struct stat
3253 attributes;
3254
cristy3ed852e2009-09-05 21:47:34 +00003255 assert(image != (Image *) NULL);
3256 assert(image->signature == MagickSignature);
3257 if (image->debug != MagickFalse)
3258 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3259 assert(exception != (ExceptionInfo *) NULL);
3260 assert(exception->signature == MagickSignature);
3261 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3262 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3263 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003264 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3265 exception);
cristy3ed852e2009-09-05 21:47:34 +00003266 else
3267 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003268 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3269 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003270 else
3271 {
3272 Image
3273 *sample_image;
3274
3275 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3276 exception);
3277 if (sample_image == (Image *) NULL)
3278 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003279 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3280 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003281 sample_image=DestroyImage(sample_image);
3282 }
3283 if (thumbnail_image == (Image *) NULL)
3284 return(thumbnail_image);
3285 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3286 if (thumbnail_image->matte == MagickFalse)
3287 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3288 thumbnail_image->depth=8;
3289 thumbnail_image->interlace=NoInterlace;
3290 /*
3291 Strip all profiles except color profiles.
3292 */
3293 ResetImageProfileIterator(thumbnail_image);
3294 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3295 {
3296 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3297 {
cristy2b726bd2010-01-11 01:05:39 +00003298 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003299 ResetImageProfileIterator(thumbnail_image);
3300 }
3301 name=GetNextImageProfile(thumbnail_image);
3302 }
3303 (void) DeleteImageProperty(thumbnail_image,"comment");
3304 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003305 if (strstr(image->magick_filename,"//") == (char *) NULL)
3306 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003307 image->magick_filename);
3308 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3309 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3310 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3311 {
cristye8c25f92010-06-03 00:53:06 +00003312 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003313 attributes.st_mtime);
3314 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3315 }
cristye8c25f92010-06-03 00:53:06 +00003316 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003317 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003318 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003319 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003320 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3321 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3322 LocaleLower(value);
3323 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3324 (void) SetImageProperty(thumbnail_image,"software",
3325 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003326 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3327 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003328 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003329 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003330 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003331 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003332 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3333 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003334 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3335 return(thumbnail_image);
3336}