blob: 2032ee729032b8e39501aca73b58ff46f74f977a [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 */
nicolas1eb2fcf2010-10-10 20:45:17 +0000700 { RobidouxFilter, BoxFilter }, /* SPECIAL: Keys cubic tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000701 };
702 /*
nicolas32f44eb2010-09-20 01:23:12 +0000703 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000704 function. The default support size for that filter as a weighting
705 function, the range to scale with to use that function as a sinc
706 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000707
anthony07a3f7f2010-09-16 03:03:11 +0000708 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000709 SincFast(), and CubicBC() functions, which may have multiple
710 filter to function associations.
711
712 See "filter:verbose" handling below for the function -> filter
713 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000714 */
715 static struct
716 {
717 MagickRealType
718 (*function)(const MagickRealType, const ResizeFilter*),
anthony61b5ddd2010-10-05 02:33:31 +0000719 lobes, /* default lobes/support size of the weighting filter */
anthony2d9b8b52010-09-14 08:31:07 +0000720 scale, /* windowing function range, for scaling windowing function */
anthony933b4bf2010-09-10 02:31:37 +0000721 B,C; /* Cubic Filter factors for a CubicBC function, else ignored */
cristy3ed852e2009-09-05 21:47:34 +0000722 } const filters[SentinelFilter] =
723 {
anthony61b5ddd2010-10-05 02:33:31 +0000724 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
725 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
726 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
727 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
728 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
729 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
730 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
731 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000732 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000733 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
734 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
735 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
anthony853d6972010-10-08 06:01:31 +0000736 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
737 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
738 { Jinc, 3.0, 1.21967, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000739 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
740 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
741 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
742 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
743 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
744 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
745 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
746 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
nicolas0134bbe2010-10-10 15:55:42 +0000747 { Jinc, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2D (Jinc-Jinc) */
anthony02b4cb42010-10-10 04:54:35 +0000748 { CubicBC, 2.0, 1.0, 0.37821575509399862, 0.31089212245300069 }
nicolas0134bbe2010-10-10 15:55:42 +0000749 /* Robidoux: Keys cubic close to Lanczos2D with blur=0.958033808 */
cristy3ed852e2009-09-05 21:47:34 +0000750 };
751 /*
anthony9a98fc62010-10-11 02:47:19 +0000752 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
753 function being used as a filter. It is used by the "filter:lobes" and for
754 the 'lobes' number in the above, the for support selection, so users do
755 not have to deal with the highly irrational sizes of the 'lobes' of the
756 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000757
nicolase473f722010-10-07 00:05:13 +0000758 Values taken from
anthony48f77622010-10-03 14:32:31 +0000759 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000760 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000761 */
762 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000763 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000764 {
anthonyc2d07db2010-09-15 23:47:40 +0000765 1.21966989126651,
766 2.23313059438153,
767 3.23831548416624,
768 4.24106286379607,
769 5.24276437687019,
770 6.24392168986449,
771 7.24475986871996,
772 8.24539491395205,
773 9.24589268494948,
774 10.2462933487549,
775 11.2466227948779,
776 12.2468984611381,
777 13.2471325221811,
778 14.2473337358069,
779 15.2475085630373,
780 16.247661874701
cristy3ed852e2009-09-05 21:47:34 +0000781 };
782
cristy33b1c162010-01-23 22:51:51 +0000783 /*
784 Allocate resize filter.
785 */
cristy3ed852e2009-09-05 21:47:34 +0000786 assert(image != (const Image *) NULL);
787 assert(image->signature == MagickSignature);
788 if (image->debug != MagickFalse)
789 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
790 assert(UndefinedFilter < filter && filter < SentinelFilter);
791 assert(exception != (ExceptionInfo *) NULL);
792 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000793 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000794 if (resize_filter == (ResizeFilter *) NULL)
795 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000796 /*
797 Defaults for the requested filter.
798 */
799 filter_type=mapping[filter].filter;
800 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000801 resize_filter->blur = blur;
802 sigma = 0.5;
anthony48f77622010-10-03 14:32:31 +0000803 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000804 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000805 switch (filter_type)
806 {
807 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000808 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000809 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000810 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000811 break;
anthonyba5a7c32010-09-15 02:42:25 +0000812 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000813 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000814 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000815 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000816 break;
anthony61b5ddd2010-10-05 02:33:31 +0000817
cristy33b1c162010-01-23 22:51:51 +0000818 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000819 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000820 filter_type=JincFilter;
821 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000822 break;
anthonyf5e76ef2010-10-12 01:22:01 +0000823 case GaussianFilter:
824 sigma = MagickSQ2/2; /* Cylindrical Gaussian sigma is sqrt(2)/2 */
825 break;
cristya782ecf2010-01-25 02:59:14 +0000826 default:
827 break;
cristy3ed852e2009-09-05 21:47:34 +0000828 }
anthony61b5ddd2010-10-05 02:33:31 +0000829 else
830 switch (filter_type)
831 {
832 case Lanczos2DFilter:
nicolas45b58a92010-10-07 15:46:39 +0000833 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000834 window_type=SincFastFilter;
835 break;
836 default:
837 break;
838 }
839
cristy3ed852e2009-09-05 21:47:34 +0000840 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000841 if (artifact != (const char *) NULL)
842 {
cristy9af9b5d2010-08-15 17:04:28 +0000843 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000844 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000845 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000846 filter_type=(FilterTypes) option;
847 window_type=BoxFilter;
848 }
849 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000850 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000851 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000852 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000853 }
nicolas07bac812010-09-19 18:47:02 +0000854 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000855 artifact=GetImageArtifact(image,"filter:window");
856 if (artifact != (const char *) NULL)
857 {
cristy9af9b5d2010-08-15 17:04:28 +0000858 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000859 if ((UndefinedFilter < option) && (option < SentinelFilter))
860 {
861 if (option != LanczosFilter)
862 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000863 else
anthony48f77622010-10-03 14:32:31 +0000864 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000865 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000866 }
cristy33b1c162010-01-23 22:51:51 +0000867 }
cristy3ed852e2009-09-05 21:47:34 +0000868 }
cristy33b1c162010-01-23 22:51:51 +0000869 else
870 {
anthony48f77622010-10-03 14:32:31 +0000871 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000872 artifact=GetImageArtifact(image,"filter:window");
873 if (artifact != (const char *) NULL)
874 {
875 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
876 artifact);
877 if ((UndefinedFilter < option) && (option < SentinelFilter))
878 {
anthony61b5ddd2010-10-05 02:33:31 +0000879 filter_type=cylindrical != MagickFalse ?
880 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000881 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000882 }
883 }
884 }
nicolas07bac812010-09-19 18:47:02 +0000885 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000886 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000887 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000888 resize_filter->window=filters[window_type].function;
889 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000890 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000891
anthonyf5e76ef2010-10-12 01:22:01 +0000892 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000893 if (cylindrical != MagickFalse)
894 switch (filter_type)
895 {
896 case PointFilter:
897 case BoxFilter:
898 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000899 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000900 break;
anthony81b8bf92010-10-02 13:54:34 +0000901 default:
902 break;
anthony10b8bc82010-10-02 12:48:46 +0000903 }
anthony61b5ddd2010-10-05 02:33:31 +0000904 else
905 switch (filter_type)
906 {
907 case Lanczos2DFilter:
anthony853d6972010-10-08 06:01:31 +0000908 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000909 resize_filter->filter=SincFast;
910 break;
911 default:
912 break;
913 }
914
anthonyf5e76ef2010-10-12 01:22:01 +0000915 /*
916 ** More Expert Option Modifications
917 */
918
919 /* User Sigma Override - no support change */
920 artifact=GetImageArtifact(image,"filter:sigma");
921 if (artifact != (const char *) NULL)
922 sigma=StringToDouble(artifact);
923 /* Define coefficents for Gaussian (assumes no cubic window) */
924 if ( GaussianFilter ) {
925 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
926 resize_filter->coeff[1] = 1.0/(Magick2PI*sigma*sigma); /* unused */
927 }
928
929 /* Blur Override */
930 artifact=GetImageArtifact(image,"filter:blur");
931 if (artifact != (const char *) NULL)
932 resize_filter->blur=StringToDouble(artifact);
933 if (resize_filter->blur < MagickEpsilon)
934 resize_filter->blur=(MagickRealType) MagickEpsilon;
935
936 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000937 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000938 if (artifact != (const char *) NULL)
939 {
cristybb503372010-05-27 20:51:26 +0000940 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000941 lobes;
942
cristy96b16132010-08-29 17:19:52 +0000943 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000944 if (lobes < 1)
945 lobes=1;
946 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000947 }
anthony61b5ddd2010-10-05 02:33:31 +0000948 /* convert Jinc lobes to a real support value */
949 if (resize_filter->filter == Jinc)
950 {
951 if (resize_filter->support > 16)
952 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
953 else
954 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
955 }
956 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000957 artifact=GetImageArtifact(image,"filter:support");
958 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000959 resize_filter->support=fabs(StringToDouble(artifact));
960 /*
nicolas07bac812010-09-19 18:47:02 +0000961 Scale windowing function separatally to the support 'clipping'
962 window that calling operator is planning to actually use. (Expert
963 override)
cristy3ed852e2009-09-05 21:47:34 +0000964 */
anthony55f12332010-09-10 01:13:02 +0000965 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000966 artifact=GetImageArtifact(image,"filter:win-support");
967 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000968 resize_filter->window_support=fabs(StringToDouble(artifact));
969 /*
anthony1f90a6b2010-09-14 08:56:31 +0000970 Adjust window function scaling to the windowing support for
971 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000972 */
973 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +0000974
anthony55f12332010-09-10 01:13:02 +0000975 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000976 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000977 */
cristy3ed852e2009-09-05 21:47:34 +0000978 B=0.0;
979 C=0.0;
cristy33b1c162010-01-23 22:51:51 +0000980 if ((filters[filter_type].function == CubicBC) ||
981 (filters[window_type].function == CubicBC))
982 {
anthony2d9b8b52010-09-14 08:31:07 +0000983 B=filters[filter_type].B;
984 C=filters[filter_type].C;
985 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +0000986 {
anthony2d9b8b52010-09-14 08:31:07 +0000987 B=filters[window_type].B;
988 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +0000989 }
cristy33b1c162010-01-23 22:51:51 +0000990 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +0000991 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000992 {
993 B=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +0000994 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +0000995 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +0000996 if (artifact != (const char *) NULL)
997 C=StringToDouble(artifact);
998 }
999 else
1000 {
1001 artifact=GetImageArtifact(image,"filter:c");
1002 if (artifact != (const char *) NULL)
1003 {
1004 C=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001005 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001006 }
1007 }
anthonyf5e76ef2010-10-12 01:22:01 +00001008 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
1009 resize_filter->coeff[0]=(6.0-2.0*B)/6.0;
1010 resize_filter->coeff[1]=0.0;
1011 resize_filter->coeff[2]=(-18.0+12.0*B+6.0*C)/6.0;
1012 resize_filter->coeff[3]=(12.0-9.0*B-6.0*C)/6.0;
1013 resize_filter->coeff[4]=(8.0*B+24.0*C)/6.0;
1014 resize_filter->coeff[5]=(-12.0*B-48.0*C)/6.0;
1015 resize_filter->coeff[6]=(6.0*B+30.0*C)/6.0;
1016 resize_filter->coeff[7]=(-B-6.0*C)/6.0;
cristy3ed852e2009-09-05 21:47:34 +00001017 }
anthonyf5e76ef2010-10-12 01:22:01 +00001018
anthony55f12332010-09-10 01:13:02 +00001019 /*
nicolas07bac812010-09-19 18:47:02 +00001020 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001021 */
anthonye06e4c12010-09-15 04:03:52 +00001022#if defined(MAGICKCORE_OPENMP_SUPPORT)
anthony72949792010-10-08 04:44:56 +00001023 #pragma omp single
1024 {
anthonye06e4c12010-09-15 04:03:52 +00001025#endif
1026 artifact=GetImageArtifact(image,"filter:verbose");
1027 if (artifact != (const char *) NULL)
1028 {
1029 double
1030 support,
1031 x;
cristy3ed852e2009-09-05 21:47:34 +00001032
nicolas07bac812010-09-19 18:47:02 +00001033 /*
anthony463be1d2010-09-26 01:07:36 +00001034 Set the weighting function properly when the weighting
1035 function may not exactly match the filter of the same name.
1036 EG: a Point filter really uses a Box weighting function
1037 with a different support than is typically used.
1038
anthonye06e4c12010-09-15 04:03:52 +00001039 */
anthony463be1d2010-09-26 01:07:36 +00001040 if (resize_filter->filter == Box) filter_type=BoxFilter;
1041 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1042 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
anthony61b5ddd2010-10-05 02:33:31 +00001043 if (resize_filter->filter == Jinc) filter_type=JincFilter;
anthony463be1d2010-09-26 01:07:36 +00001044 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
anthonye06e4c12010-09-15 04:03:52 +00001045 /*
nicolas07bac812010-09-19 18:47:02 +00001046 Report Filter Details.
anthonye06e4c12010-09-15 04:03:52 +00001047 */
anthonyf5e76ef2010-10-12 01:22:01 +00001048 support=GetResizeFilterSupport(resize_filter); /* practical_support */
anthony61b5ddd2010-10-05 02:33:31 +00001049 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
cristy03dbbd22010-09-19 23:04:47 +00001050 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1051 MagickFilterOptions,filter_type));
1052 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
anthony463be1d2010-09-26 01:07:36 +00001053 MagickFilterOptions, window_type));
cristy03dbbd22010-09-19 23:04:47 +00001054 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
anthony463be1d2010-09-26 01:07:36 +00001055 (double) resize_filter->support);
cristy03dbbd22010-09-19 23:04:47 +00001056 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
anthony463be1d2010-09-26 01:07:36 +00001057 (double) resize_filter->window_support);
anthonyf5e76ef2010-10-12 01:22:01 +00001058 (void) fprintf(stdout,"# scale_blur = %.*g\n",GetMagickPrecision(),
cristy03dbbd22010-09-19 23:04:47 +00001059 (double) resize_filter->blur);
anthonyf5e76ef2010-10-12 01:22:01 +00001060 if ( filter_type == GaussianFilter )
1061 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",GetMagickPrecision(),
1062 (double) sigma);
1063 (void) fprintf(stdout,"# practical_support = %.*g\n",GetMagickPrecision(),
cristy03dbbd22010-09-19 23:04:47 +00001064 (double) support);
1065 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1066 (double) B,GetMagickPrecision(),(double) C);
anthony61b5ddd2010-10-05 02:33:31 +00001067 (void) fprintf(stdout,"\n");
anthonye06e4c12010-09-15 04:03:52 +00001068 /*
nicolas07bac812010-09-19 18:47:02 +00001069 Output values of resulting filter graph -- for graphing
1070 filter result.
anthonye06e4c12010-09-15 04:03:52 +00001071 */
1072 for (x=0.0; x <= support; x+=0.01f)
cristy03dbbd22010-09-19 23:04:47 +00001073 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1074 (double) GetResizeFilterWeight(resize_filter,x));
nicolas07bac812010-09-19 18:47:02 +00001075 /* A final value so gnuplot can graph the 'stop' properly. */
cristy03dbbd22010-09-19 23:04:47 +00001076 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1077 0.0);
anthonye06e4c12010-09-15 04:03:52 +00001078 }
anthonyf5e76ef2010-10-12 01:22:01 +00001079 /* Output the above once only for each image - remove setting */
cristybb66d9c2010-10-09 01:40:31 +00001080 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
anthonye06e4c12010-09-15 04:03:52 +00001081#if defined(MAGICKCORE_OPENMP_SUPPORT)
anthony61b5ddd2010-10-05 02:33:31 +00001082 }
anthonye06e4c12010-09-15 04:03:52 +00001083#endif
anthonyf5e76ef2010-10-12 01:22:01 +00001084
cristy3ed852e2009-09-05 21:47:34 +00001085 return(resize_filter);
1086}
1087
1088/*
1089%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1090% %
1091% %
1092% %
1093% A d a p t i v e R e s i z e I m a g e %
1094% %
1095% %
1096% %
1097%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1098%
1099% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1100%
1101% The format of the AdaptiveResizeImage method is:
1102%
cristy9af9b5d2010-08-15 17:04:28 +00001103% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1104% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001105%
1106% A description of each parameter follows:
1107%
1108% o image: the image.
1109%
1110% o columns: the number of columns in the resized image.
1111%
1112% o rows: the number of rows in the resized image.
1113%
1114% o exception: return any errors or warnings in this structure.
1115%
1116*/
1117MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001118 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001119{
1120#define AdaptiveResizeImageTag "Resize/Image"
1121
cristyc4c8d132010-01-07 01:58:38 +00001122 CacheView
1123 *resize_view;
1124
cristy3ed852e2009-09-05 21:47:34 +00001125 Image
1126 *resize_image;
1127
cristy3ed852e2009-09-05 21:47:34 +00001128 MagickBooleanType
1129 proceed;
1130
1131 MagickPixelPacket
1132 pixel;
1133
1134 PointInfo
1135 offset;
1136
1137 ResampleFilter
1138 *resample_filter;
1139
cristy9af9b5d2010-08-15 17:04:28 +00001140 ssize_t
1141 y;
1142
cristy3ed852e2009-09-05 21:47:34 +00001143 /*
1144 Adaptively resize image.
1145 */
1146 assert(image != (const Image *) NULL);
1147 assert(image->signature == MagickSignature);
1148 if (image->debug != MagickFalse)
1149 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1150 assert(exception != (ExceptionInfo *) NULL);
1151 assert(exception->signature == MagickSignature);
1152 if ((columns == 0) || (rows == 0))
1153 return((Image *) NULL);
1154 if ((columns == image->columns) && (rows == image->rows))
1155 return(CloneImage(image,0,0,MagickTrue,exception));
1156 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1157 if (resize_image == (Image *) NULL)
1158 return((Image *) NULL);
1159 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1160 {
1161 InheritException(exception,&resize_image->exception);
1162 resize_image=DestroyImage(resize_image);
1163 return((Image *) NULL);
1164 }
1165 GetMagickPixelPacket(image,&pixel);
1166 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001167 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001168 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001169 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001170 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001171 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001172 {
1173 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001174 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001175
cristybb503372010-05-27 20:51:26 +00001176 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001177 x;
1178
1179 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001180 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001181
1182 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1183 exception);
1184 if (q == (PixelPacket *) NULL)
1185 break;
1186 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1187 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001188 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001189 {
1190 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1191 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1192 &pixel);
1193 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1194 q++;
1195 }
1196 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1197 break;
cristy96b16132010-08-29 17:19:52 +00001198 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1199 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001200 if (proceed == MagickFalse)
1201 break;
1202 }
1203 resample_filter=DestroyResampleFilter(resample_filter);
1204 resize_view=DestroyCacheView(resize_view);
1205 return(resize_image);
1206}
1207
1208/*
1209%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1210% %
1211% %
1212% %
1213+ B e s s e l O r d e r O n e %
1214% %
1215% %
1216% %
1217%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1218%
1219% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001220% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001221%
1222% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1223%
1224% j1(x) = x*j1(x);
1225%
1226% For x in (8,inf)
1227%
1228% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1229%
1230% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1231%
1232% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1233% = 1/sqrt(2) * (sin(x) - cos(x))
1234% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1235% = -1/sqrt(2) * (sin(x) + cos(x))
1236%
1237% The format of the BesselOrderOne method is:
1238%
1239% MagickRealType BesselOrderOne(MagickRealType x)
1240%
1241% A description of each parameter follows:
1242%
1243% o x: MagickRealType value.
1244%
1245*/
1246
1247#undef I0
1248static MagickRealType I0(MagickRealType x)
1249{
1250 MagickRealType
1251 sum,
1252 t,
1253 y;
1254
cristybb503372010-05-27 20:51:26 +00001255 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001256 i;
1257
1258 /*
1259 Zeroth order Bessel function of the first kind.
1260 */
1261 sum=1.0;
1262 y=x*x/4.0;
1263 t=y;
1264 for (i=2; t > MagickEpsilon; i++)
1265 {
1266 sum+=t;
1267 t*=y/((MagickRealType) i*i);
1268 }
1269 return(sum);
1270}
1271
1272#undef J1
1273static MagickRealType J1(MagickRealType x)
1274{
1275 MagickRealType
1276 p,
1277 q;
1278
cristybb503372010-05-27 20:51:26 +00001279 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001280 i;
1281
1282 static const double
1283 Pone[] =
1284 {
1285 0.581199354001606143928050809e+21,
1286 -0.6672106568924916298020941484e+20,
1287 0.2316433580634002297931815435e+19,
1288 -0.3588817569910106050743641413e+17,
1289 0.2908795263834775409737601689e+15,
1290 -0.1322983480332126453125473247e+13,
1291 0.3413234182301700539091292655e+10,
1292 -0.4695753530642995859767162166e+7,
1293 0.270112271089232341485679099e+4
1294 },
1295 Qone[] =
1296 {
1297 0.11623987080032122878585294e+22,
1298 0.1185770712190320999837113348e+20,
1299 0.6092061398917521746105196863e+17,
1300 0.2081661221307607351240184229e+15,
1301 0.5243710262167649715406728642e+12,
1302 0.1013863514358673989967045588e+10,
1303 0.1501793594998585505921097578e+7,
1304 0.1606931573481487801970916749e+4,
1305 0.1e+1
1306 };
1307
1308 p=Pone[8];
1309 q=Qone[8];
1310 for (i=7; i >= 0; i--)
1311 {
1312 p=p*x*x+Pone[i];
1313 q=q*x*x+Qone[i];
1314 }
1315 return(p/q);
1316}
1317
1318#undef P1
1319static MagickRealType P1(MagickRealType x)
1320{
1321 MagickRealType
1322 p,
1323 q;
1324
cristybb503372010-05-27 20:51:26 +00001325 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001326 i;
1327
1328 static const double
1329 Pone[] =
1330 {
1331 0.352246649133679798341724373e+5,
1332 0.62758845247161281269005675e+5,
1333 0.313539631109159574238669888e+5,
1334 0.49854832060594338434500455e+4,
1335 0.2111529182853962382105718e+3,
1336 0.12571716929145341558495e+1
1337 },
1338 Qone[] =
1339 {
1340 0.352246649133679798068390431e+5,
1341 0.626943469593560511888833731e+5,
1342 0.312404063819041039923015703e+5,
1343 0.4930396490181088979386097e+4,
1344 0.2030775189134759322293574e+3,
1345 0.1e+1
1346 };
1347
1348 p=Pone[5];
1349 q=Qone[5];
1350 for (i=4; i >= 0; i--)
1351 {
1352 p=p*(8.0/x)*(8.0/x)+Pone[i];
1353 q=q*(8.0/x)*(8.0/x)+Qone[i];
1354 }
1355 return(p/q);
1356}
1357
1358#undef Q1
1359static MagickRealType Q1(MagickRealType x)
1360{
1361 MagickRealType
1362 p,
1363 q;
1364
cristybb503372010-05-27 20:51:26 +00001365 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001366 i;
1367
1368 static const double
1369 Pone[] =
1370 {
1371 0.3511751914303552822533318e+3,
1372 0.7210391804904475039280863e+3,
1373 0.4259873011654442389886993e+3,
1374 0.831898957673850827325226e+2,
1375 0.45681716295512267064405e+1,
1376 0.3532840052740123642735e-1
1377 },
1378 Qone[] =
1379 {
1380 0.74917374171809127714519505e+4,
1381 0.154141773392650970499848051e+5,
1382 0.91522317015169922705904727e+4,
1383 0.18111867005523513506724158e+4,
1384 0.1038187585462133728776636e+3,
1385 0.1e+1
1386 };
1387
1388 p=Pone[5];
1389 q=Qone[5];
1390 for (i=4; i >= 0; i--)
1391 {
1392 p=p*(8.0/x)*(8.0/x)+Pone[i];
1393 q=q*(8.0/x)*(8.0/x)+Qone[i];
1394 }
1395 return(p/q);
1396}
1397
1398static MagickRealType BesselOrderOne(MagickRealType x)
1399{
1400 MagickRealType
1401 p,
1402 q;
1403
1404 if (x == 0.0)
1405 return(0.0);
1406 p=x;
1407 if (x < 0.0)
1408 x=(-x);
1409 if (x < 8.0)
1410 return(p*J1(x));
1411 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1412 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1413 cos((double) x))));
1414 if (p < 0.0)
1415 q=(-q);
1416 return(q);
1417}
1418
1419/*
1420%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1421% %
1422% %
1423% %
1424+ D e s t r o y R e s i z e F i l t e r %
1425% %
1426% %
1427% %
1428%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1429%
1430% DestroyResizeFilter() destroy the resize filter.
1431%
cristya2ffd7e2010-03-10 20:50:30 +00001432% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001433%
1434% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1435%
1436% A description of each parameter follows:
1437%
1438% o resize_filter: the resize filter.
1439%
1440*/
1441MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1442{
1443 assert(resize_filter != (ResizeFilter *) NULL);
1444 assert(resize_filter->signature == MagickSignature);
1445 resize_filter->signature=(~MagickSignature);
1446 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1447 return(resize_filter);
1448}
1449
1450/*
1451%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1452% %
1453% %
1454% %
1455+ G e t R e s i z e F i l t e r S u p p o r t %
1456% %
1457% %
1458% %
1459%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1460%
1461% GetResizeFilterSupport() return the current support window size for this
1462% filter. Note that this may have been enlarged by filter:blur factor.
1463%
1464% The format of the GetResizeFilterSupport method is:
1465%
1466% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1467%
1468% A description of each parameter follows:
1469%
1470% o filter: Image filter to use.
1471%
1472*/
1473MagickExport MagickRealType GetResizeFilterSupport(
1474 const ResizeFilter *resize_filter)
1475{
1476 assert(resize_filter != (ResizeFilter *) NULL);
1477 assert(resize_filter->signature == MagickSignature);
1478 return(resize_filter->support*resize_filter->blur);
1479}
1480
1481/*
1482%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1483% %
1484% %
1485% %
1486+ G e t R e s i z e F i l t e r W e i g h t %
1487% %
1488% %
1489% %
1490%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1491%
1492% GetResizeFilterWeight evaluates the specified resize filter at the point x
1493% which usally lies between zero and the filters current 'support' and
1494% returns the weight of the filter function at that point.
1495%
1496% The format of the GetResizeFilterWeight method is:
1497%
1498% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1499% const MagickRealType x)
1500%
1501% A description of each parameter follows:
1502%
1503% o filter: the filter type.
1504%
1505% o x: the point.
1506%
1507*/
1508MagickExport MagickRealType GetResizeFilterWeight(
1509 const ResizeFilter *resize_filter,const MagickRealType x)
1510{
1511 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001512 scale,
1513 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001514
1515 /*
1516 Windowing function - scale the weighting filter by this amount.
1517 */
1518 assert(resize_filter != (ResizeFilter *) NULL);
1519 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001520 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001521 if ((resize_filter->window_support < MagickEpsilon) ||
1522 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001523 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001524 else
1525 {
anthony55f12332010-09-10 01:13:02 +00001526 scale=resize_filter->scale;
1527 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001528 }
anthony55f12332010-09-10 01:13:02 +00001529 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001530}
1531
1532/*
1533%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1534% %
1535% %
1536% %
1537% M a g n i f y I m a g e %
1538% %
1539% %
1540% %
1541%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1542%
1543% MagnifyImage() is a convenience method that scales an image proportionally
1544% to twice its size.
1545%
1546% The format of the MagnifyImage method is:
1547%
1548% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1549%
1550% A description of each parameter follows:
1551%
1552% o image: the image.
1553%
1554% o exception: return any errors or warnings in this structure.
1555%
1556*/
1557MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1558{
1559 Image
1560 *magnify_image;
1561
1562 assert(image != (Image *) NULL);
1563 assert(image->signature == MagickSignature);
1564 if (image->debug != MagickFalse)
1565 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1566 assert(exception != (ExceptionInfo *) NULL);
1567 assert(exception->signature == MagickSignature);
1568 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1569 1.0,exception);
1570 return(magnify_image);
1571}
1572
1573/*
1574%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1575% %
1576% %
1577% %
1578% M i n i f y I m a g e %
1579% %
1580% %
1581% %
1582%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1583%
1584% MinifyImage() is a convenience method that scales an image proportionally
1585% to half its size.
1586%
1587% The format of the MinifyImage method is:
1588%
1589% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1590%
1591% A description of each parameter follows:
1592%
1593% o image: the image.
1594%
1595% o exception: return any errors or warnings in this structure.
1596%
1597*/
1598MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1599{
1600 Image
1601 *minify_image;
1602
1603 assert(image != (Image *) NULL);
1604 assert(image->signature == MagickSignature);
1605 if (image->debug != MagickFalse)
1606 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1607 assert(exception != (ExceptionInfo *) NULL);
1608 assert(exception->signature == MagickSignature);
1609 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1610 1.0,exception);
1611 return(minify_image);
1612}
1613
1614/*
1615%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1616% %
1617% %
1618% %
1619% R e s a m p l e I m a g e %
1620% %
1621% %
1622% %
1623%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1624%
1625% ResampleImage() resize image in terms of its pixel size, so that when
1626% displayed at the given resolution it will be the same size in terms of
1627% real world units as the original image at the original resolution.
1628%
1629% The format of the ResampleImage method is:
1630%
1631% Image *ResampleImage(Image *image,const double x_resolution,
1632% const double y_resolution,const FilterTypes filter,const double blur,
1633% ExceptionInfo *exception)
1634%
1635% A description of each parameter follows:
1636%
1637% o image: the image to be resized to fit the given resolution.
1638%
1639% o x_resolution: the new image x resolution.
1640%
1641% o y_resolution: the new image y resolution.
1642%
1643% o filter: Image filter to use.
1644%
1645% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1646%
1647*/
1648MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1649 const double y_resolution,const FilterTypes filter,const double blur,
1650 ExceptionInfo *exception)
1651{
1652#define ResampleImageTag "Resample/Image"
1653
1654 Image
1655 *resample_image;
1656
cristybb503372010-05-27 20:51:26 +00001657 size_t
cristy3ed852e2009-09-05 21:47:34 +00001658 height,
1659 width;
1660
1661 /*
1662 Initialize sampled image attributes.
1663 */
1664 assert(image != (const Image *) NULL);
1665 assert(image->signature == MagickSignature);
1666 if (image->debug != MagickFalse)
1667 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1668 assert(exception != (ExceptionInfo *) NULL);
1669 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001670 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1671 72.0 : image->x_resolution)+0.5);
1672 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1673 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001674 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1675 if (resample_image != (Image *) NULL)
1676 {
1677 resample_image->x_resolution=x_resolution;
1678 resample_image->y_resolution=y_resolution;
1679 }
1680 return(resample_image);
1681}
1682#if defined(MAGICKCORE_LQR_DELEGATE)
1683
1684/*
1685%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1686% %
1687% %
1688% %
1689% L i q u i d R e s c a l e I m a g e %
1690% %
1691% %
1692% %
1693%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1694%
1695% LiquidRescaleImage() rescales image with seam carving.
1696%
1697% The format of the LiquidRescaleImage method is:
1698%
1699% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001700% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001701% const double delta_x,const double rigidity,ExceptionInfo *exception)
1702%
1703% A description of each parameter follows:
1704%
1705% o image: the image.
1706%
1707% o columns: the number of columns in the rescaled image.
1708%
1709% o rows: the number of rows in the rescaled image.
1710%
1711% o delta_x: maximum seam transversal step (0 means straight seams).
1712%
1713% o rigidity: introduce a bias for non-straight seams (typically 0).
1714%
1715% o exception: return any errors or warnings in this structure.
1716%
1717*/
cristy9af9b5d2010-08-15 17:04:28 +00001718MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1719 const size_t rows,const double delta_x,const double rigidity,
1720 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001721{
1722#define LiquidRescaleImageTag "Rescale/Image"
1723
cristyc5c6f662010-09-22 14:23:02 +00001724 CacheView
1725 *rescale_view;
1726
cristy3ed852e2009-09-05 21:47:34 +00001727 const char
1728 *map;
1729
1730 guchar
1731 *packet;
1732
1733 Image
1734 *rescale_image;
1735
1736 int
1737 x,
1738 y;
1739
1740 LqrCarver
1741 *carver;
1742
1743 LqrRetVal
1744 lqr_status;
1745
1746 MagickBooleanType
1747 status;
1748
1749 MagickPixelPacket
1750 pixel;
1751
1752 unsigned char
1753 *pixels;
1754
1755 /*
1756 Liquid rescale image.
1757 */
1758 assert(image != (const Image *) NULL);
1759 assert(image->signature == MagickSignature);
1760 if (image->debug != MagickFalse)
1761 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1762 assert(exception != (ExceptionInfo *) NULL);
1763 assert(exception->signature == MagickSignature);
1764 if ((columns == 0) || (rows == 0))
1765 return((Image *) NULL);
1766 if ((columns == image->columns) && (rows == image->rows))
1767 return(CloneImage(image,0,0,MagickTrue,exception));
1768 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001769 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001770 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1771 {
1772 Image
1773 *resize_image;
1774
cristybb503372010-05-27 20:51:26 +00001775 size_t
cristy3ed852e2009-09-05 21:47:34 +00001776 height,
1777 width;
1778
1779 /*
1780 Honor liquid resize size limitations.
1781 */
1782 for (width=image->columns; columns >= (2*width-1); width*=2);
1783 for (height=image->rows; rows >= (2*height-1); height*=2);
1784 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1785 exception);
1786 if (resize_image == (Image *) NULL)
1787 return((Image *) NULL);
1788 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1789 rigidity,exception);
1790 resize_image=DestroyImage(resize_image);
1791 return(rescale_image);
1792 }
1793 map="RGB";
1794 if (image->matte == MagickFalse)
1795 map="RGBA";
1796 if (image->colorspace == CMYKColorspace)
1797 {
1798 map="CMYK";
1799 if (image->matte == MagickFalse)
1800 map="CMYKA";
1801 }
1802 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1803 strlen(map)*sizeof(*pixels));
1804 if (pixels == (unsigned char *) NULL)
1805 return((Image *) NULL);
1806 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1807 pixels,exception);
1808 if (status == MagickFalse)
1809 {
1810 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1811 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1812 }
1813 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1814 if (carver == (LqrCarver *) NULL)
1815 {
1816 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1817 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1818 }
1819 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1820 lqr_status=lqr_carver_resize(carver,columns,rows);
1821 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1822 lqr_carver_get_height(carver),MagickTrue,exception);
1823 if (rescale_image == (Image *) NULL)
1824 {
1825 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1826 return((Image *) NULL);
1827 }
1828 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1829 {
1830 InheritException(exception,&rescale_image->exception);
1831 rescale_image=DestroyImage(rescale_image);
1832 return((Image *) NULL);
1833 }
1834 GetMagickPixelPacket(rescale_image,&pixel);
1835 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001836 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001837 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1838 {
1839 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001840 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001841
1842 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001843 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001844
anthony22aad252010-09-23 06:59:07 +00001845 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001846 if (q == (PixelPacket *) NULL)
1847 break;
cristyc5c6f662010-09-22 14:23:02 +00001848 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001849 pixel.red=QuantumRange*(packet[0]/255.0);
1850 pixel.green=QuantumRange*(packet[1]/255.0);
1851 pixel.blue=QuantumRange*(packet[2]/255.0);
1852 if (image->colorspace != CMYKColorspace)
1853 {
1854 if (image->matte == MagickFalse)
1855 pixel.opacity=QuantumRange*(packet[3]/255.0);
1856 }
1857 else
1858 {
1859 pixel.index=QuantumRange*(packet[3]/255.0);
1860 if (image->matte == MagickFalse)
1861 pixel.opacity=QuantumRange*(packet[4]/255.0);
1862 }
1863 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001864 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001865 break;
1866 }
cristyc5c6f662010-09-22 14:23:02 +00001867 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001868 /*
1869 Relinquish resources.
1870 */
1871 lqr_carver_destroy(carver);
1872 return(rescale_image);
1873}
1874#else
1875MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001876 const size_t magick_unused(columns),const size_t magick_unused(rows),
1877 const double magick_unused(delta_x),const double magick_unused(rigidity),
1878 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001879{
1880 assert(image != (const Image *) NULL);
1881 assert(image->signature == MagickSignature);
1882 if (image->debug != MagickFalse)
1883 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1884 assert(exception != (ExceptionInfo *) NULL);
1885 assert(exception->signature == MagickSignature);
1886 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1887 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1888 return((Image *) NULL);
1889}
1890#endif
1891
1892/*
1893%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1894% %
1895% %
1896% %
1897% R e s i z e I m a g e %
1898% %
1899% %
1900% %
1901%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1902%
1903% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001904% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001905%
1906% If an undefined filter is given the filter defaults to Mitchell for a
1907% colormapped image, a image with a matte channel, or if the image is
1908% enlarged. Otherwise the filter defaults to a Lanczos.
1909%
1910% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1911%
1912% The format of the ResizeImage method is:
1913%
cristybb503372010-05-27 20:51:26 +00001914% Image *ResizeImage(Image *image,const size_t columns,
1915% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001916% ExceptionInfo *exception)
1917%
1918% A description of each parameter follows:
1919%
1920% o image: the image.
1921%
1922% o columns: the number of columns in the scaled image.
1923%
1924% o rows: the number of rows in the scaled image.
1925%
1926% o filter: Image filter to use.
1927%
cristy9af9b5d2010-08-15 17:04:28 +00001928% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1929% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001930%
1931% o exception: return any errors or warnings in this structure.
1932%
1933*/
1934
1935typedef struct _ContributionInfo
1936{
1937 MagickRealType
1938 weight;
1939
cristybb503372010-05-27 20:51:26 +00001940 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001941 pixel;
1942} ContributionInfo;
1943
1944static ContributionInfo **DestroyContributionThreadSet(
1945 ContributionInfo **contribution)
1946{
cristybb503372010-05-27 20:51:26 +00001947 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001948 i;
1949
1950 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001951 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001952 if (contribution[i] != (ContributionInfo *) NULL)
1953 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1954 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001955 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001956 return(contribution);
1957}
1958
1959static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1960{
cristybb503372010-05-27 20:51:26 +00001961 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001962 i;
1963
1964 ContributionInfo
1965 **contribution;
1966
cristybb503372010-05-27 20:51:26 +00001967 size_t
cristy3ed852e2009-09-05 21:47:34 +00001968 number_threads;
1969
1970 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001971 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001972 sizeof(*contribution));
1973 if (contribution == (ContributionInfo **) NULL)
1974 return((ContributionInfo **) NULL);
1975 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00001976 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00001977 {
1978 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1979 sizeof(**contribution));
1980 if (contribution[i] == (ContributionInfo *) NULL)
1981 return(DestroyContributionThreadSet(contribution));
1982 }
1983 return(contribution);
1984}
1985
1986static inline double MagickMax(const double x,const double y)
1987{
1988 if (x > y)
1989 return(x);
1990 return(y);
1991}
1992
1993static inline double MagickMin(const double x,const double y)
1994{
1995 if (x < y)
1996 return(x);
1997 return(y);
1998}
1999
2000static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2001 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002002 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002003{
2004#define ResizeImageTag "Resize/Image"
2005
cristyfa112112010-01-04 17:48:07 +00002006 CacheView
2007 *image_view,
2008 *resize_view;
2009
cristy3ed852e2009-09-05 21:47:34 +00002010 ClassType
2011 storage_class;
2012
2013 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002014 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002015
cristy3ed852e2009-09-05 21:47:34 +00002016 MagickBooleanType
2017 status;
2018
2019 MagickPixelPacket
2020 zero;
2021
2022 MagickRealType
2023 scale,
2024 support;
2025
cristy9af9b5d2010-08-15 17:04:28 +00002026 ssize_t
2027 x;
2028
cristy3ed852e2009-09-05 21:47:34 +00002029 /*
2030 Apply filter to resize horizontally from image to resize image.
2031 */
cristy5d824382010-09-06 14:00:17 +00002032 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002033 support=scale*GetResizeFilterSupport(resize_filter);
2034 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2035 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2036 {
2037 InheritException(exception,&resize_image->exception);
2038 return(MagickFalse);
2039 }
2040 if (support < 0.5)
2041 {
2042 /*
nicolas07bac812010-09-19 18:47:02 +00002043 Support too small even for nearest neighbour: Reduce to point
2044 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002045 */
2046 support=(MagickRealType) 0.5;
2047 scale=1.0;
2048 }
2049 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2050 if (contributions == (ContributionInfo **) NULL)
2051 {
2052 (void) ThrowMagickException(exception,GetMagickModule(),
2053 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2054 return(MagickFalse);
2055 }
2056 status=MagickTrue;
2057 scale=1.0/scale;
2058 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2059 image_view=AcquireCacheView(image);
2060 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002061#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002062 #pragma omp parallel for shared(status)
2063#endif
cristybb503372010-05-27 20:51:26 +00002064 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002065 {
cristy3ed852e2009-09-05 21:47:34 +00002066 MagickRealType
2067 center,
2068 density;
2069
2070 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002071 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002072
2073 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002074 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002075
cristy03dbbd22010-09-19 23:04:47 +00002076 register ContributionInfo
2077 *restrict contribution;
2078
cristy3ed852e2009-09-05 21:47:34 +00002079 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002080 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002081
cristy3ed852e2009-09-05 21:47:34 +00002082 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002083 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002084
cristy03dbbd22010-09-19 23:04:47 +00002085 register ssize_t
2086 y;
2087
cristy9af9b5d2010-08-15 17:04:28 +00002088 ssize_t
2089 n,
2090 start,
2091 stop;
2092
cristy3ed852e2009-09-05 21:47:34 +00002093 if (status == MagickFalse)
2094 continue;
2095 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002096 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2097 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002098 density=0.0;
2099 contribution=contributions[GetOpenMPThreadId()];
2100 for (n=0; n < (stop-start); n++)
2101 {
2102 contribution[n].pixel=start+n;
2103 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2104 ((MagickRealType) (start+n)-center+0.5));
2105 density+=contribution[n].weight;
2106 }
2107 if ((density != 0.0) && (density != 1.0))
2108 {
cristybb503372010-05-27 20:51:26 +00002109 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002110 i;
2111
2112 /*
2113 Normalize.
2114 */
2115 density=1.0/density;
2116 for (i=0; i < n; i++)
2117 contribution[i].weight*=density;
2118 }
cristy9af9b5d2010-08-15 17:04:28 +00002119 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2120 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002121 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2122 exception);
2123 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2124 {
2125 status=MagickFalse;
2126 continue;
2127 }
2128 indexes=GetCacheViewVirtualIndexQueue(image_view);
2129 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002130 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002131 {
cristy3ed852e2009-09-05 21:47:34 +00002132 MagickPixelPacket
2133 pixel;
2134
2135 MagickRealType
2136 alpha;
2137
cristybb503372010-05-27 20:51:26 +00002138 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002139 i;
2140
cristy9af9b5d2010-08-15 17:04:28 +00002141 ssize_t
2142 j;
2143
cristy3ed852e2009-09-05 21:47:34 +00002144 pixel=zero;
2145 if (image->matte == MagickFalse)
2146 {
2147 for (i=0; i < n; i++)
2148 {
2149 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2150 (contribution[i].pixel-contribution[0].pixel);
2151 alpha=contribution[i].weight;
2152 pixel.red+=alpha*(p+j)->red;
2153 pixel.green+=alpha*(p+j)->green;
2154 pixel.blue+=alpha*(p+j)->blue;
2155 pixel.opacity+=alpha*(p+j)->opacity;
2156 }
cristyce70c172010-01-07 17:15:30 +00002157 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2158 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2159 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2160 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002161 if ((image->colorspace == CMYKColorspace) &&
2162 (resize_image->colorspace == CMYKColorspace))
2163 {
2164 for (i=0; i < n; i++)
2165 {
2166 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2167 (contribution[i].pixel-contribution[0].pixel);
2168 alpha=contribution[i].weight;
2169 pixel.index+=alpha*indexes[j];
2170 }
cristyce70c172010-01-07 17:15:30 +00002171 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002172 }
2173 }
2174 else
2175 {
2176 MagickRealType
2177 gamma;
2178
2179 gamma=0.0;
2180 for (i=0; i < n; i++)
2181 {
2182 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2183 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002184 alpha=contribution[i].weight*QuantumScale*
2185 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002186 pixel.red+=alpha*(p+j)->red;
2187 pixel.green+=alpha*(p+j)->green;
2188 pixel.blue+=alpha*(p+j)->blue;
2189 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2190 gamma+=alpha;
2191 }
2192 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002193 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2194 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2195 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2196 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002197 if ((image->colorspace == CMYKColorspace) &&
2198 (resize_image->colorspace == CMYKColorspace))
2199 {
2200 for (i=0; i < n; i++)
2201 {
2202 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2203 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002204 alpha=contribution[i].weight*QuantumScale*
2205 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002206 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002207 }
cristyce70c172010-01-07 17:15:30 +00002208 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2209 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002210 }
2211 }
2212 if ((resize_image->storage_class == PseudoClass) &&
2213 (image->storage_class == PseudoClass))
2214 {
cristybb503372010-05-27 20:51:26 +00002215 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002216 1.0)+0.5);
2217 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2218 (contribution[i-start].pixel-contribution[0].pixel);
2219 resize_indexes[y]=indexes[j];
2220 }
2221 q++;
2222 }
2223 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2224 status=MagickFalse;
2225 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2226 {
2227 MagickBooleanType
2228 proceed;
2229
cristyb5d5f722009-11-04 03:03:49 +00002230#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002231 #pragma omp critical (MagickCore_HorizontalFilter)
2232#endif
cristy9af9b5d2010-08-15 17:04:28 +00002233 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002234 if (proceed == MagickFalse)
2235 status=MagickFalse;
2236 }
2237 }
2238 resize_view=DestroyCacheView(resize_view);
2239 image_view=DestroyCacheView(image_view);
2240 contributions=DestroyContributionThreadSet(contributions);
2241 return(status);
2242}
2243
2244static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2245 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002246 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002247{
cristyfa112112010-01-04 17:48:07 +00002248 CacheView
2249 *image_view,
2250 *resize_view;
2251
cristy3ed852e2009-09-05 21:47:34 +00002252 ClassType
2253 storage_class;
2254
2255 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002256 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002257
cristy3ed852e2009-09-05 21:47:34 +00002258 MagickBooleanType
2259 status;
2260
2261 MagickPixelPacket
2262 zero;
2263
2264 MagickRealType
2265 scale,
2266 support;
2267
cristy9af9b5d2010-08-15 17:04:28 +00002268 ssize_t
2269 y;
2270
cristy3ed852e2009-09-05 21:47:34 +00002271 /*
cristy9af9b5d2010-08-15 17:04:28 +00002272 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002273 */
cristy5d824382010-09-06 14:00:17 +00002274 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002275 support=scale*GetResizeFilterSupport(resize_filter);
2276 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2277 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2278 {
2279 InheritException(exception,&resize_image->exception);
2280 return(MagickFalse);
2281 }
2282 if (support < 0.5)
2283 {
2284 /*
nicolas07bac812010-09-19 18:47:02 +00002285 Support too small even for nearest neighbour: Reduce to point
2286 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002287 */
2288 support=(MagickRealType) 0.5;
2289 scale=1.0;
2290 }
2291 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2292 if (contributions == (ContributionInfo **) NULL)
2293 {
2294 (void) ThrowMagickException(exception,GetMagickModule(),
2295 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2296 return(MagickFalse);
2297 }
2298 status=MagickTrue;
2299 scale=1.0/scale;
2300 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2301 image_view=AcquireCacheView(image);
2302 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002303#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002304 #pragma omp parallel for shared(status)
2305#endif
cristybb503372010-05-27 20:51:26 +00002306 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002307 {
cristy3ed852e2009-09-05 21:47:34 +00002308 MagickRealType
2309 center,
2310 density;
2311
2312 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002313 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002314
2315 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002316 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002317
cristy03dbbd22010-09-19 23:04:47 +00002318 register ContributionInfo
2319 *restrict contribution;
2320
cristy3ed852e2009-09-05 21:47:34 +00002321 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002322 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002323
cristy9af9b5d2010-08-15 17:04:28 +00002324 register PixelPacket
2325 *restrict q;
2326
cristybb503372010-05-27 20:51:26 +00002327 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002328 x;
2329
cristy9af9b5d2010-08-15 17:04:28 +00002330 ssize_t
2331 n,
2332 start,
2333 stop;
cristy3ed852e2009-09-05 21:47:34 +00002334
2335 if (status == MagickFalse)
2336 continue;
cristy679e6962010-03-18 00:42:45 +00002337 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002338 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2339 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002340 density=0.0;
2341 contribution=contributions[GetOpenMPThreadId()];
2342 for (n=0; n < (stop-start); n++)
2343 {
2344 contribution[n].pixel=start+n;
2345 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2346 ((MagickRealType) (start+n)-center+0.5));
2347 density+=contribution[n].weight;
2348 }
2349 if ((density != 0.0) && (density != 1.0))
2350 {
cristybb503372010-05-27 20:51:26 +00002351 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002352 i;
2353
2354 /*
2355 Normalize.
2356 */
2357 density=1.0/density;
2358 for (i=0; i < n; i++)
2359 contribution[i].weight*=density;
2360 }
2361 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002362 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2363 exception);
cristy3ed852e2009-09-05 21:47:34 +00002364 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2365 exception);
2366 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2367 {
2368 status=MagickFalse;
2369 continue;
2370 }
2371 indexes=GetCacheViewVirtualIndexQueue(image_view);
2372 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002373 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002374 {
cristy3ed852e2009-09-05 21:47:34 +00002375 MagickPixelPacket
2376 pixel;
2377
2378 MagickRealType
2379 alpha;
2380
cristybb503372010-05-27 20:51:26 +00002381 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002382 i;
2383
cristy9af9b5d2010-08-15 17:04:28 +00002384 ssize_t
2385 j;
2386
cristy3ed852e2009-09-05 21:47:34 +00002387 pixel=zero;
2388 if (image->matte == MagickFalse)
2389 {
2390 for (i=0; i < n; i++)
2391 {
cristybb503372010-05-27 20:51:26 +00002392 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002393 image->columns+x);
2394 alpha=contribution[i].weight;
2395 pixel.red+=alpha*(p+j)->red;
2396 pixel.green+=alpha*(p+j)->green;
2397 pixel.blue+=alpha*(p+j)->blue;
2398 pixel.opacity+=alpha*(p+j)->opacity;
2399 }
cristyce70c172010-01-07 17:15:30 +00002400 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2401 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2402 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2403 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002404 if ((image->colorspace == CMYKColorspace) &&
2405 (resize_image->colorspace == CMYKColorspace))
2406 {
2407 for (i=0; i < n; i++)
2408 {
cristybb503372010-05-27 20:51:26 +00002409 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002410 image->columns+x);
2411 alpha=contribution[i].weight;
2412 pixel.index+=alpha*indexes[j];
2413 }
cristyce70c172010-01-07 17:15:30 +00002414 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002415 }
2416 }
2417 else
2418 {
2419 MagickRealType
2420 gamma;
2421
2422 gamma=0.0;
2423 for (i=0; i < n; i++)
2424 {
cristybb503372010-05-27 20:51:26 +00002425 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002426 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002427 alpha=contribution[i].weight*QuantumScale*
2428 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002429 pixel.red+=alpha*(p+j)->red;
2430 pixel.green+=alpha*(p+j)->green;
2431 pixel.blue+=alpha*(p+j)->blue;
2432 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2433 gamma+=alpha;
2434 }
2435 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002436 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2437 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2438 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2439 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002440 if ((image->colorspace == CMYKColorspace) &&
2441 (resize_image->colorspace == CMYKColorspace))
2442 {
2443 for (i=0; i < n; i++)
2444 {
cristybb503372010-05-27 20:51:26 +00002445 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002446 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002447 alpha=contribution[i].weight*QuantumScale*
2448 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002449 pixel.index+=alpha*indexes[j];
2450 }
cristyce70c172010-01-07 17:15:30 +00002451 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2452 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002453 }
2454 }
2455 if ((resize_image->storage_class == PseudoClass) &&
2456 (image->storage_class == PseudoClass))
2457 {
cristybb503372010-05-27 20:51:26 +00002458 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002459 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002460 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002461 image->columns+x);
2462 resize_indexes[x]=indexes[j];
2463 }
2464 q++;
2465 }
2466 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2467 status=MagickFalse;
2468 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2469 {
2470 MagickBooleanType
2471 proceed;
2472
cristyb5d5f722009-11-04 03:03:49 +00002473#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002474 #pragma omp critical (MagickCore_VerticalFilter)
2475#endif
cristy9af9b5d2010-08-15 17:04:28 +00002476 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002477 if (proceed == MagickFalse)
2478 status=MagickFalse;
2479 }
2480 }
2481 resize_view=DestroyCacheView(resize_view);
2482 image_view=DestroyCacheView(image_view);
2483 contributions=DestroyContributionThreadSet(contributions);
2484 return(status);
2485}
2486
cristybb503372010-05-27 20:51:26 +00002487MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2488 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002489 ExceptionInfo *exception)
2490{
2491#define WorkLoadFactor 0.265
2492
2493 FilterTypes
2494 filter_type;
2495
2496 Image
2497 *filter_image,
2498 *resize_image;
2499
cristy9af9b5d2010-08-15 17:04:28 +00002500 MagickOffsetType
2501 offset;
2502
cristy3ed852e2009-09-05 21:47:34 +00002503 MagickRealType
2504 x_factor,
2505 y_factor;
2506
2507 MagickSizeType
2508 span;
2509
2510 MagickStatusType
2511 status;
2512
2513 ResizeFilter
2514 *resize_filter;
2515
cristy3ed852e2009-09-05 21:47:34 +00002516 /*
2517 Acquire resize image.
2518 */
2519 assert(image != (Image *) NULL);
2520 assert(image->signature == MagickSignature);
2521 if (image->debug != MagickFalse)
2522 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2523 assert(exception != (ExceptionInfo *) NULL);
2524 assert(exception->signature == MagickSignature);
2525 if ((columns == 0) || (rows == 0))
2526 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2527 if ((columns == image->columns) && (rows == image->rows) &&
2528 (filter == UndefinedFilter) && (blur == 1.0))
2529 return(CloneImage(image,0,0,MagickTrue,exception));
2530 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2531 if (resize_image == (Image *) NULL)
2532 return(resize_image);
2533 /*
2534 Acquire resize filter.
2535 */
2536 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2537 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2538 if ((x_factor*y_factor) > WorkLoadFactor)
2539 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2540 else
2541 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2542 if (filter_image == (Image *) NULL)
2543 return(DestroyImage(resize_image));
2544 filter_type=LanczosFilter;
2545 if (filter != UndefinedFilter)
2546 filter_type=filter;
2547 else
2548 if ((x_factor == 1.0) && (y_factor == 1.0))
2549 filter_type=PointFilter;
2550 else
2551 if ((image->storage_class == PseudoClass) ||
2552 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2553 filter_type=MitchellFilter;
2554 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2555 exception);
2556 /*
2557 Resize image.
2558 */
cristy9af9b5d2010-08-15 17:04:28 +00002559 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002560 if ((x_factor*y_factor) > WorkLoadFactor)
2561 {
2562 span=(MagickSizeType) (filter_image->columns+rows);
2563 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002564 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002565 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002566 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002567 }
2568 else
2569 {
2570 span=(MagickSizeType) (filter_image->rows+columns);
2571 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002572 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002573 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002574 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002575 }
2576 /*
2577 Free resources.
2578 */
2579 filter_image=DestroyImage(filter_image);
2580 resize_filter=DestroyResizeFilter(resize_filter);
2581 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2582 return((Image *) NULL);
2583 resize_image->type=image->type;
2584 return(resize_image);
2585}
2586
2587/*
2588%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2589% %
2590% %
2591% %
2592% S a m p l e I m a g e %
2593% %
2594% %
2595% %
2596%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2597%
2598% SampleImage() scales an image to the desired dimensions with pixel
2599% sampling. Unlike other scaling methods, this method does not introduce
2600% any additional color into the scaled image.
2601%
2602% The format of the SampleImage method is:
2603%
cristybb503372010-05-27 20:51:26 +00002604% Image *SampleImage(const Image *image,const size_t columns,
2605% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002606%
2607% A description of each parameter follows:
2608%
2609% o image: the image.
2610%
2611% o columns: the number of columns in the sampled image.
2612%
2613% o rows: the number of rows in the sampled image.
2614%
2615% o exception: return any errors or warnings in this structure.
2616%
2617*/
cristybb503372010-05-27 20:51:26 +00002618MagickExport Image *SampleImage(const Image *image,const size_t columns,
2619 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002620{
2621#define SampleImageTag "Sample/Image"
2622
cristyc4c8d132010-01-07 01:58:38 +00002623 CacheView
2624 *image_view,
2625 *sample_view;
2626
cristy3ed852e2009-09-05 21:47:34 +00002627 Image
2628 *sample_image;
2629
cristy3ed852e2009-09-05 21:47:34 +00002630 MagickBooleanType
2631 status;
2632
cristy5f959472010-05-27 22:19:46 +00002633 MagickOffsetType
2634 progress;
2635
cristybb503372010-05-27 20:51:26 +00002636 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002637 x;
2638
cristy5f959472010-05-27 22:19:46 +00002639 ssize_t
2640 *x_offset,
2641 y;
2642
cristy3ed852e2009-09-05 21:47:34 +00002643 /*
2644 Initialize sampled image attributes.
2645 */
2646 assert(image != (const Image *) NULL);
2647 assert(image->signature == MagickSignature);
2648 if (image->debug != MagickFalse)
2649 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2650 assert(exception != (ExceptionInfo *) NULL);
2651 assert(exception->signature == MagickSignature);
2652 if ((columns == 0) || (rows == 0))
2653 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2654 if ((columns == image->columns) && (rows == image->rows))
2655 return(CloneImage(image,0,0,MagickTrue,exception));
2656 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2657 if (sample_image == (Image *) NULL)
2658 return((Image *) NULL);
2659 /*
2660 Allocate scan line buffer and column offset buffers.
2661 */
cristybb503372010-05-27 20:51:26 +00002662 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002663 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002664 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002665 {
2666 sample_image=DestroyImage(sample_image);
2667 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2668 }
cristybb503372010-05-27 20:51:26 +00002669 for (x=0; x < (ssize_t) sample_image->columns; x++)
2670 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002671 sample_image->columns);
2672 /*
2673 Sample each row.
2674 */
2675 status=MagickTrue;
2676 progress=0;
2677 image_view=AcquireCacheView(image);
2678 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002679#if defined(MAGICKCORE_OPENMP_SUPPORT)
2680 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002681#endif
cristybb503372010-05-27 20:51:26 +00002682 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002683 {
cristy3ed852e2009-09-05 21:47:34 +00002684 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002685 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002686
2687 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002688 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002689
2690 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002691 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002692
cristy3ed852e2009-09-05 21:47:34 +00002693 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002694 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002695
cristy03dbbd22010-09-19 23:04:47 +00002696 register ssize_t
2697 x;
2698
cristy9af9b5d2010-08-15 17:04:28 +00002699 ssize_t
2700 y_offset;
2701
cristy3ed852e2009-09-05 21:47:34 +00002702 if (status == MagickFalse)
2703 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002704 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2705 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002706 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2707 exception);
2708 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2709 exception);
2710 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2711 {
2712 status=MagickFalse;
2713 continue;
2714 }
2715 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2716 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2717 /*
2718 Sample each column.
2719 */
cristybb503372010-05-27 20:51:26 +00002720 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002721 *q++=p[x_offset[x]];
2722 if ((image->storage_class == PseudoClass) ||
2723 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002724 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002725 sample_indexes[x]=indexes[x_offset[x]];
2726 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2727 status=MagickFalse;
2728 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2729 {
2730 MagickBooleanType
2731 proceed;
2732
cristyb5d5f722009-11-04 03:03:49 +00002733#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002734 #pragma omp critical (MagickCore_SampleImage)
2735#endif
2736 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2737 if (proceed == MagickFalse)
2738 status=MagickFalse;
2739 }
2740 }
2741 image_view=DestroyCacheView(image_view);
2742 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002743 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002744 sample_image->type=image->type;
2745 return(sample_image);
2746}
2747
2748/*
2749%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2750% %
2751% %
2752% %
2753% S c a l e I m a g e %
2754% %
2755% %
2756% %
2757%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2758%
2759% ScaleImage() changes the size of an image to the given dimensions.
2760%
2761% The format of the ScaleImage method is:
2762%
cristybb503372010-05-27 20:51:26 +00002763% Image *ScaleImage(const Image *image,const size_t columns,
2764% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002765%
2766% A description of each parameter follows:
2767%
2768% o image: the image.
2769%
2770% o columns: the number of columns in the scaled image.
2771%
2772% o rows: the number of rows in the scaled image.
2773%
2774% o exception: return any errors or warnings in this structure.
2775%
2776*/
cristybb503372010-05-27 20:51:26 +00002777MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2778 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002779{
2780#define ScaleImageTag "Scale/Image"
2781
cristyed6cb232010-01-20 03:07:53 +00002782 CacheView
2783 *image_view,
2784 *scale_view;
2785
cristy3ed852e2009-09-05 21:47:34 +00002786 Image
2787 *scale_image;
2788
cristy3ed852e2009-09-05 21:47:34 +00002789 MagickBooleanType
2790 next_column,
2791 next_row,
2792 proceed;
2793
2794 MagickPixelPacket
2795 pixel,
2796 *scale_scanline,
2797 *scanline,
2798 *x_vector,
2799 *y_vector,
2800 zero;
2801
cristy3ed852e2009-09-05 21:47:34 +00002802 PointInfo
2803 scale,
2804 span;
2805
cristybb503372010-05-27 20:51:26 +00002806 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002807 i;
2808
cristy9af9b5d2010-08-15 17:04:28 +00002809 ssize_t
2810 number_rows,
2811 y;
2812
cristy3ed852e2009-09-05 21:47:34 +00002813 /*
2814 Initialize scaled image attributes.
2815 */
2816 assert(image != (const Image *) NULL);
2817 assert(image->signature == MagickSignature);
2818 if (image->debug != MagickFalse)
2819 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2820 assert(exception != (ExceptionInfo *) NULL);
2821 assert(exception->signature == MagickSignature);
2822 if ((columns == 0) || (rows == 0))
2823 return((Image *) NULL);
2824 if ((columns == image->columns) && (rows == image->rows))
2825 return(CloneImage(image,0,0,MagickTrue,exception));
2826 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2827 if (scale_image == (Image *) NULL)
2828 return((Image *) NULL);
2829 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2830 {
2831 InheritException(exception,&scale_image->exception);
2832 scale_image=DestroyImage(scale_image);
2833 return((Image *) NULL);
2834 }
2835 /*
2836 Allocate memory.
2837 */
2838 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2839 sizeof(*x_vector));
2840 scanline=x_vector;
2841 if (image->rows != scale_image->rows)
2842 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2843 sizeof(*scanline));
2844 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2845 scale_image->columns,sizeof(*scale_scanline));
2846 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2847 sizeof(*y_vector));
2848 if ((scanline == (MagickPixelPacket *) NULL) ||
2849 (scale_scanline == (MagickPixelPacket *) NULL) ||
2850 (x_vector == (MagickPixelPacket *) NULL) ||
2851 (y_vector == (MagickPixelPacket *) NULL))
2852 {
2853 scale_image=DestroyImage(scale_image);
2854 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2855 }
2856 /*
2857 Scale image.
2858 */
2859 number_rows=0;
2860 next_row=MagickTrue;
2861 span.y=1.0;
2862 scale.y=(double) scale_image->rows/(double) image->rows;
2863 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2864 sizeof(*y_vector));
2865 GetMagickPixelPacket(image,&pixel);
2866 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2867 i=0;
cristyed6cb232010-01-20 03:07:53 +00002868 image_view=AcquireCacheView(image);
2869 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002870 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002871 {
2872 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002873 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002874
2875 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002876 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002877
2878 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002879 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002880
cristy3ed852e2009-09-05 21:47:34 +00002881 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002882 *restrict s,
2883 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002884
2885 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002886 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002887
cristy9af9b5d2010-08-15 17:04:28 +00002888 register ssize_t
2889 x;
2890
cristyed6cb232010-01-20 03:07:53 +00002891 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2892 exception);
cristy3ed852e2009-09-05 21:47:34 +00002893 if (q == (PixelPacket *) NULL)
2894 break;
2895 scale_indexes=GetAuthenticIndexQueue(scale_image);
2896 if (scale_image->rows == image->rows)
2897 {
2898 /*
2899 Read a new scanline.
2900 */
cristyed6cb232010-01-20 03:07:53 +00002901 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2902 exception);
cristy3ed852e2009-09-05 21:47:34 +00002903 if (p == (const PixelPacket *) NULL)
2904 break;
cristyed6cb232010-01-20 03:07:53 +00002905 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002906 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002907 {
cristyce70c172010-01-07 17:15:30 +00002908 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2909 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2910 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002911 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002912 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002913 if (indexes != (IndexPacket *) NULL)
2914 x_vector[x].index=(MagickRealType) indexes[x];
2915 p++;
2916 }
2917 }
2918 else
2919 {
2920 /*
2921 Scale Y direction.
2922 */
2923 while (scale.y < span.y)
2924 {
cristy9af9b5d2010-08-15 17:04:28 +00002925 if ((next_row != MagickFalse) &&
2926 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002927 {
2928 /*
2929 Read a new scanline.
2930 */
cristyed6cb232010-01-20 03:07:53 +00002931 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2932 exception);
cristy3ed852e2009-09-05 21:47:34 +00002933 if (p == (const PixelPacket *) NULL)
2934 break;
cristyed6cb232010-01-20 03:07:53 +00002935 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002936 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002937 {
cristyce70c172010-01-07 17:15:30 +00002938 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2939 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2940 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002941 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002942 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002943 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002944 if (indexes != (IndexPacket *) NULL)
2945 x_vector[x].index=(MagickRealType) indexes[x];
2946 p++;
2947 }
2948 number_rows++;
2949 }
cristybb503372010-05-27 20:51:26 +00002950 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002951 {
2952 y_vector[x].red+=scale.y*x_vector[x].red;
2953 y_vector[x].green+=scale.y*x_vector[x].green;
2954 y_vector[x].blue+=scale.y*x_vector[x].blue;
2955 if (scale_image->matte != MagickFalse)
2956 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2957 if (scale_indexes != (IndexPacket *) NULL)
2958 y_vector[x].index+=scale.y*x_vector[x].index;
2959 }
2960 span.y-=scale.y;
2961 scale.y=(double) scale_image->rows/(double) image->rows;
2962 next_row=MagickTrue;
2963 }
cristybb503372010-05-27 20:51:26 +00002964 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002965 {
2966 /*
2967 Read a new scanline.
2968 */
cristyed6cb232010-01-20 03:07:53 +00002969 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2970 exception);
cristy3ed852e2009-09-05 21:47:34 +00002971 if (p == (const PixelPacket *) NULL)
2972 break;
cristyed6cb232010-01-20 03:07:53 +00002973 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002974 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002975 {
cristyce70c172010-01-07 17:15:30 +00002976 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2977 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2978 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002979 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00002980 x_vector[x].opacity=(MagickRealType)
2981 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002982 if (indexes != (IndexPacket *) NULL)
2983 x_vector[x].index=(MagickRealType) indexes[x];
2984 p++;
2985 }
2986 number_rows++;
2987 next_row=MagickFalse;
2988 }
2989 s=scanline;
cristybb503372010-05-27 20:51:26 +00002990 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002991 {
2992 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
2993 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
2994 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
2995 if (image->matte != MagickFalse)
2996 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
2997 if (scale_indexes != (IndexPacket *) NULL)
2998 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
2999 s->red=pixel.red;
3000 s->green=pixel.green;
3001 s->blue=pixel.blue;
3002 if (scale_image->matte != MagickFalse)
3003 s->opacity=pixel.opacity;
3004 if (scale_indexes != (IndexPacket *) NULL)
3005 s->index=pixel.index;
3006 s++;
3007 y_vector[x]=zero;
3008 }
3009 scale.y-=span.y;
3010 if (scale.y <= 0)
3011 {
3012 scale.y=(double) scale_image->rows/(double) image->rows;
3013 next_row=MagickTrue;
3014 }
3015 span.y=1.0;
3016 }
3017 if (scale_image->columns == image->columns)
3018 {
3019 /*
3020 Transfer scanline to scaled image.
3021 */
3022 s=scanline;
cristybb503372010-05-27 20:51:26 +00003023 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003024 {
cristyce70c172010-01-07 17:15:30 +00003025 q->red=ClampToQuantum(s->red);
3026 q->green=ClampToQuantum(s->green);
3027 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003028 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003029 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003030 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003031 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003032 q++;
3033 s++;
3034 }
3035 }
3036 else
3037 {
3038 /*
3039 Scale X direction.
3040 */
3041 pixel=zero;
3042 next_column=MagickFalse;
3043 span.x=1.0;
3044 s=scanline;
3045 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003046 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003047 {
3048 scale.x=(double) scale_image->columns/(double) image->columns;
3049 while (scale.x >= span.x)
3050 {
3051 if (next_column != MagickFalse)
3052 {
3053 pixel=zero;
3054 t++;
3055 }
3056 pixel.red+=span.x*s->red;
3057 pixel.green+=span.x*s->green;
3058 pixel.blue+=span.x*s->blue;
3059 if (image->matte != MagickFalse)
3060 pixel.opacity+=span.x*s->opacity;
3061 if (scale_indexes != (IndexPacket *) NULL)
3062 pixel.index+=span.x*s->index;
3063 t->red=pixel.red;
3064 t->green=pixel.green;
3065 t->blue=pixel.blue;
3066 if (scale_image->matte != MagickFalse)
3067 t->opacity=pixel.opacity;
3068 if (scale_indexes != (IndexPacket *) NULL)
3069 t->index=pixel.index;
3070 scale.x-=span.x;
3071 span.x=1.0;
3072 next_column=MagickTrue;
3073 }
3074 if (scale.x > 0)
3075 {
3076 if (next_column != MagickFalse)
3077 {
3078 pixel=zero;
3079 next_column=MagickFalse;
3080 t++;
3081 }
3082 pixel.red+=scale.x*s->red;
3083 pixel.green+=scale.x*s->green;
3084 pixel.blue+=scale.x*s->blue;
3085 if (scale_image->matte != MagickFalse)
3086 pixel.opacity+=scale.x*s->opacity;
3087 if (scale_indexes != (IndexPacket *) NULL)
3088 pixel.index+=scale.x*s->index;
3089 span.x-=scale.x;
3090 }
3091 s++;
3092 }
3093 if (span.x > 0)
3094 {
3095 s--;
3096 pixel.red+=span.x*s->red;
3097 pixel.green+=span.x*s->green;
3098 pixel.blue+=span.x*s->blue;
3099 if (scale_image->matte != MagickFalse)
3100 pixel.opacity+=span.x*s->opacity;
3101 if (scale_indexes != (IndexPacket *) NULL)
3102 pixel.index+=span.x*s->index;
3103 }
3104 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003105 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003106 {
3107 t->red=pixel.red;
3108 t->green=pixel.green;
3109 t->blue=pixel.blue;
3110 if (scale_image->matte != MagickFalse)
3111 t->opacity=pixel.opacity;
3112 if (scale_indexes != (IndexPacket *) NULL)
3113 t->index=pixel.index;
3114 }
3115 /*
3116 Transfer scanline to scaled image.
3117 */
3118 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003119 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003120 {
cristyce70c172010-01-07 17:15:30 +00003121 q->red=ClampToQuantum(t->red);
3122 q->green=ClampToQuantum(t->green);
3123 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003124 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003125 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003126 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003127 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003128 t++;
3129 q++;
3130 }
3131 }
cristyed6cb232010-01-20 03:07:53 +00003132 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003133 break;
cristy96b16132010-08-29 17:19:52 +00003134 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3135 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003136 if (proceed == MagickFalse)
3137 break;
3138 }
cristyed6cb232010-01-20 03:07:53 +00003139 scale_view=DestroyCacheView(scale_view);
3140 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003141 /*
3142 Free allocated memory.
3143 */
3144 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3145 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3146 if (scale_image->rows != image->rows)
3147 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3148 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3149 scale_image->type=image->type;
3150 return(scale_image);
3151}
3152
anthony02b4cb42010-10-10 04:54:35 +00003153#if 0
3154 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003155/*
3156%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3157% %
3158% %
3159% %
3160+ S e t R e s i z e F i l t e r S u p p o r t %
3161% %
3162% %
3163% %
3164%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3165%
3166% SetResizeFilterSupport() specifies which IR filter to use to window
3167%
3168% The format of the SetResizeFilterSupport method is:
3169%
3170% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3171% const MagickRealType support)
3172%
3173% A description of each parameter follows:
3174%
3175% o resize_filter: the resize filter.
3176%
3177% o support: the filter spport radius.
3178%
3179*/
3180MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3181 const MagickRealType support)
3182{
3183 assert(resize_filter != (ResizeFilter *) NULL);
3184 assert(resize_filter->signature == MagickSignature);
3185 resize_filter->support=support;
3186}
anthony02b4cb42010-10-10 04:54:35 +00003187#endif
cristy3ed852e2009-09-05 21:47:34 +00003188
3189/*
3190%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3191% %
3192% %
3193% %
3194% T h u m b n a i l I m a g e %
3195% %
3196% %
3197% %
3198%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3199%
3200% ThumbnailImage() changes the size of an image to the given dimensions and
3201% removes any associated profiles. The goal is to produce small low cost
3202% thumbnail images suited for display on the Web.
3203%
3204% The format of the ThumbnailImage method is:
3205%
cristybb503372010-05-27 20:51:26 +00003206% Image *ThumbnailImage(const Image *image,const size_t columns,
3207% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003208%
3209% A description of each parameter follows:
3210%
3211% o image: the image.
3212%
3213% o columns: the number of columns in the scaled image.
3214%
3215% o rows: the number of rows in the scaled image.
3216%
3217% o exception: return any errors or warnings in this structure.
3218%
3219*/
cristy9af9b5d2010-08-15 17:04:28 +00003220MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3221 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003222{
3223#define SampleFactor 5
3224
3225 char
3226 value[MaxTextExtent];
3227
3228 const char
3229 *name;
3230
3231 Image
3232 *thumbnail_image;
3233
3234 MagickRealType
3235 x_factor,
3236 y_factor;
3237
cristybb503372010-05-27 20:51:26 +00003238 size_t
cristy3ed852e2009-09-05 21:47:34 +00003239 version;
3240
cristy9af9b5d2010-08-15 17:04:28 +00003241 struct stat
3242 attributes;
3243
cristy3ed852e2009-09-05 21:47:34 +00003244 assert(image != (Image *) NULL);
3245 assert(image->signature == MagickSignature);
3246 if (image->debug != MagickFalse)
3247 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3248 assert(exception != (ExceptionInfo *) NULL);
3249 assert(exception->signature == MagickSignature);
3250 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3251 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3252 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003253 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3254 exception);
cristy3ed852e2009-09-05 21:47:34 +00003255 else
3256 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003257 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3258 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003259 else
3260 {
3261 Image
3262 *sample_image;
3263
3264 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3265 exception);
3266 if (sample_image == (Image *) NULL)
3267 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003268 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3269 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003270 sample_image=DestroyImage(sample_image);
3271 }
3272 if (thumbnail_image == (Image *) NULL)
3273 return(thumbnail_image);
3274 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3275 if (thumbnail_image->matte == MagickFalse)
3276 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3277 thumbnail_image->depth=8;
3278 thumbnail_image->interlace=NoInterlace;
3279 /*
3280 Strip all profiles except color profiles.
3281 */
3282 ResetImageProfileIterator(thumbnail_image);
3283 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3284 {
3285 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3286 {
cristy2b726bd2010-01-11 01:05:39 +00003287 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003288 ResetImageProfileIterator(thumbnail_image);
3289 }
3290 name=GetNextImageProfile(thumbnail_image);
3291 }
3292 (void) DeleteImageProperty(thumbnail_image,"comment");
3293 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003294 if (strstr(image->magick_filename,"//") == (char *) NULL)
3295 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003296 image->magick_filename);
3297 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3298 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3299 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3300 {
cristye8c25f92010-06-03 00:53:06 +00003301 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003302 attributes.st_mtime);
3303 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3304 }
cristye8c25f92010-06-03 00:53:06 +00003305 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003306 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003307 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003308 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003309 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3310 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3311 LocaleLower(value);
3312 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3313 (void) SetImageProperty(thumbnail_image,"software",
3314 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003315 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3316 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003317 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003318 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003319 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003320 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003321 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3322 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003323 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3324 return(thumbnail_image);
3325}