blob: e06d60408fbba141a7b63f1ae690c07964bbf9da [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
nicolas1f4c0512010-10-20 20:19:30 +0000540% blur=0.958033808). Now, it is the unique cubic BC-spline filter
541% that exactly preserves images with only vertical or horizontal
542% features when performing 'no-op" with EWA distortion, and that
543% exactly preserves linear gradient data when the density of the
544% output image is doubled by "vertex splitting." It turns out to be
nicolas219dd842010-10-10 21:02:40 +0000545% close to both plain Mitchell and "sharpened" Lanczos2D.
anthony61b5ddd2010-10-05 02:33:31 +0000546%
nicolas07bac812010-09-19 18:47:02 +0000547% Special 'expert' options can be used to override any and all filter
548% settings. This is not advised unless you have expert knowledge of
549% the use of resampling filtered techniques. Check on the results of
550% your selections using the "filter:verbose" setting to make sure you
anthony61b5ddd2010-10-05 02:33:31 +0000551% get the exact filter that you are tring to achieve.
cristy3ed852e2009-09-05 21:47:34 +0000552%
anthony48f77622010-10-03 14:32:31 +0000553% "filter:filter" Select the main function associated with
554% this filter name, as the weighting function of the filter.
555% This can be used to set a windowing function as a weighting
556% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000557%
anthony7bdc0ed2010-09-15 01:52:32 +0000558% If a "filter:window" operation has not been provided, then a 'Box'
559% windowing function will be set to denote that no windowing function
560% is being used.
cristy3ed852e2009-09-05 21:47:34 +0000561%
562% "filter:window" Select this windowing function for the filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000563% While any filter could be used as a windowing function, using the
564% 'first lobe' of that filter over the whole support window, using a
anthony48f77622010-10-03 14:32:31 +0000565% non-windowing function is not advisible. If no weighting filter
566% function is specifed a 'SincFast' filter will be used.
cristy3ed852e2009-09-05 21:47:34 +0000567%
anthony48f77622010-10-03 14:32:31 +0000568% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000569% This a simpler method of setting filter support size that will
anthony48f77622010-10-03 14:32:31 +0000570% correctly handle the Sinc/Jinc switch for an operators filtering
anthony7bdc0ed2010-09-15 01:52:32 +0000571% requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000572%
573% "filter:support" Set the support size for filtering to the size given
anthony48f77622010-10-03 14:32:31 +0000574% This not recommended for Sinc/Jinc windowed filters (lobes should
anthony7bdc0ed2010-09-15 01:52:32 +0000575% be used instead). This will override any 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000576%
anthonyb6d08c52010-09-13 01:17:04 +0000577% "filter:win-support" Scale windowing function to this size instead.
578% This causes the windowing (or self-windowing Lagrange filter) to act
579% is if the support window it much much larger than what is actually
580% supplied to the calling operator. The filter however is still
581% clipped to the real support size given, by the support range suppiled
582% to the caller. If unset this will equal the normal filter support
583% size.
584%
cristy3ed852e2009-09-05 21:47:34 +0000585% "filter:blur" Scale the filter and support window by this amount.
586% A value >1 will generally result in a more burred image with
587% more ringing effects, while a value <1 will sharpen the
588% resulting image with more aliasing and Morie effects.
589%
anthonyf5e76ef2010-10-12 01:22:01 +0000590% "filter:sigma" The sigma value to use for the Gaussian filter only.
591% Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for cylindrical
592% usage. It effectially provides a alturnative to 'blur' for Gaussians
593% without it also effecting the final 'practical support' size.
594%
cristy3ed852e2009-09-05 21:47:34 +0000595% "filter:b"
596% "filter:c" Override the preset B,C values for a Cubic type of filter
597% If only one of these are given it is assumes to be a 'Keys'
598% type of filter such that B+2C=1, where Keys 'alpha' value = C
599%
anthonyb6d08c52010-09-13 01:17:04 +0000600% "filter:verbose" Output the exact results of the filter selections
601% made, as well as plotting data for graphing the resulting filter
602% over support range (blur adjusted).
cristy3ed852e2009-09-05 21:47:34 +0000603%
604% Set a true un-windowed Sinc filter with 10 lobes (very slow)
anthony48f77622010-10-03 14:32:31 +0000605% -define filter:filter=Sinc
606% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000607%
anthony48f77622010-10-03 14:32:31 +0000608% For example force an 8 lobe Lanczos (Sinc or Jinc) filter...
cristy3ed852e2009-09-05 21:47:34 +0000609% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000610% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000611%
cristy3ed852e2009-09-05 21:47:34 +0000612% The format of the AcquireResizeFilter method is:
613%
614% ResizeFilter *AcquireResizeFilter(const Image *image,
615% const FilterTypes filter_type, const MagickBooleanType radial,
616% ExceptionInfo *exception)
617%
cristy33b1c162010-01-23 22:51:51 +0000618% A description of each parameter follows:
619%
cristy3ed852e2009-09-05 21:47:34 +0000620% o image: the image.
621%
nicolas07bac812010-09-19 18:47:02 +0000622% o filter: the filter type, defining a preset filter, window and
623% support. The artifact settings listed above will override
624% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000625%
anthony48f77622010-10-03 14:32:31 +0000626% o blur: blur the filter by this amount, use 1.0 if unknown. Image
627% artifact "filter:blur" will override this API call usage, including
628% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000629%
anthony48f77622010-10-03 14:32:31 +0000630% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
631% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000632%
633% o exception: return any errors or warnings in this structure.
634%
635*/
636MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000637 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000638 const MagickBooleanType cylindrical,ExceptionInfo *exception)
639{
640 const char
641 *artifact;
642
643 FilterTypes
644 filter_type,
645 window_type;
646
cristy3ed852e2009-09-05 21:47:34 +0000647 MagickRealType
648 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000649 C,
650 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000651
652 register ResizeFilter
653 *resize_filter;
654
cristy9af9b5d2010-08-15 17:04:28 +0000655 ssize_t
656 option;
657
cristy3ed852e2009-09-05 21:47:34 +0000658 /*
anthony48f77622010-10-03 14:32:31 +0000659 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000660 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000661 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
662 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
663 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000664
nicolas07bac812010-09-19 18:47:02 +0000665 WARNING: The order of this tabel must match the order of the
666 FilterTypes enumeration specified in "resample.h", or the filter
667 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000668
669 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000670 */
671 static struct
672 {
673 FilterTypes
674 filter,
675 window;
676 } const mapping[SentinelFilter] =
677 {
anthony462ee072010-09-27 12:34:02 +0000678 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
679 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
680 { BoxFilter, BoxFilter }, /* Box averaging filter */
681 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
682 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
683 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
684 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
685 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
686 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
687 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
688 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
689 { CatromFilter, BoxFilter }, /* Cubic interpolator */
690 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
691 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
anthony48f77622010-10-03 14:32:31 +0000692 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
693 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony462ee072010-09-27 12:34:02 +0000694 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
695 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
696 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
697 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
698 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
699 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
700 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolas5835ee02010-10-10 20:40:15 +0000701 { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
anthonye5f06452010-10-12 05:48:17 +0000702 { Lanczos2DSharpFilter, JincFilter },/* SPECIAL: ditto sharpened */
703 { RobidouxFilter, BoxFilter }, /* SPECIAL: Keys cubic tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000704 };
705 /*
nicolas32f44eb2010-09-20 01:23:12 +0000706 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000707 function. The default support size for that filter as a weighting
708 function, the range to scale with to use that function as a sinc
709 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000710
anthony07a3f7f2010-09-16 03:03:11 +0000711 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000712 SincFast(), and CubicBC() functions, which may have multiple
713 filter to function associations.
714
715 See "filter:verbose" handling below for the function -> filter
716 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000717 */
718 static struct
719 {
720 MagickRealType
721 (*function)(const MagickRealType, const ResizeFilter*),
nicolas8eccc162010-10-16 19:48:13 +0000722 lobes, /* Default lobes/support size of the weighting filter. */
anthony450db502010-10-19 04:03:03 +0000723 scale, /* Support when function used as a windowing function
724 Typically equal to the location of the first zero crossing. */
725 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
cristy3ed852e2009-09-05 21:47:34 +0000726 } const filters[SentinelFilter] =
727 {
anthony61b5ddd2010-10-05 02:33:31 +0000728 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
729 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
730 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
731 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
732 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
733 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
734 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
735 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000736 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000737 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
738 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
739 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
anthony853d6972010-10-08 06:01:31 +0000740 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
741 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
nicolasf8689f42010-10-18 16:14:08 +0000742 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000743 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
744 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
745 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
746 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
747 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
748 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
749 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
750 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
nicolas8eccc162010-10-16 19:48:13 +0000751 { Jinc, 2.0, 1.2196698912665045, 0.0, 0.0 },
752 /* Lanczos2D (Jinc-Jinc) */
753 { Jinc, 2.0, 1.1684849904329952, 0.0, 0.0 },
754 /* Lanczos2D sharpened with blur=0.958033808 */
anthony450db502010-10-19 04:03:03 +0000755 { CubicBC, 2.0, 1.1685777620836932,
nicolas1f4c0512010-10-20 20:19:30 +0000756 0.36553056988673434, 0.30046494140705066 }
757 /* Robidoux: BC-spline cubic close to Lanczos2D sharpened */
cristy3ed852e2009-09-05 21:47:34 +0000758 };
759 /*
anthony9a98fc62010-10-11 02:47:19 +0000760 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
anthony450db502010-10-19 04:03:03 +0000761 function being used as a filter. It is used by the "filter:lobes" expert
762 setting and for 'lobes' for Jinc functions in the previous table. This
763 way users do not have to deal with the highly irrational lobe sizes of the
anthony9a98fc62010-10-11 02:47:19 +0000764 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000765
nicolase473f722010-10-07 00:05:13 +0000766 Values taken from
anthony48f77622010-10-03 14:32:31 +0000767 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000768 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000769 */
770 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000771 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000772 {
nicolas8eccc162010-10-16 19:48:13 +0000773 1.2196698912665045,
774 2.2331305943815286,
775 3.2383154841662362,
776 4.2410628637960699,
777 5.2427643768701817,
778 6.2439216898644877,
779 7.244759868719957,
780 8.2453949139520427,
781 9.2458926849494673,
782 10.246293348754916,
783 11.246622794877883,
784 12.246898461138105,
785 13.247132522181061,
786 14.247333735806849,
anthonyc2d07db2010-09-15 23:47:40 +0000787 15.2475085630373,
nicolas8eccc162010-10-16 19:48:13 +0000788 16.247661874700962
cristy3ed852e2009-09-05 21:47:34 +0000789 };
790
cristy33b1c162010-01-23 22:51:51 +0000791 /*
792 Allocate resize filter.
793 */
cristy3ed852e2009-09-05 21:47:34 +0000794 assert(image != (const Image *) NULL);
795 assert(image->signature == MagickSignature);
796 if (image->debug != MagickFalse)
797 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
798 assert(UndefinedFilter < filter && filter < SentinelFilter);
799 assert(exception != (ExceptionInfo *) NULL);
800 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000801 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000802 if (resize_filter == (ResizeFilter *) NULL)
803 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000804 /*
805 Defaults for the requested filter.
806 */
807 filter_type=mapping[filter].filter;
808 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000809 resize_filter->blur = blur;
810 sigma = 0.5;
anthony48f77622010-10-03 14:32:31 +0000811 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000812 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000813 switch (filter_type)
814 {
815 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000816 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000817 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000818 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000819 break;
anthonyba5a7c32010-09-15 02:42:25 +0000820 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000821 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000822 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000823 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000824 break;
cristy33b1c162010-01-23 22:51:51 +0000825 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000826 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000827 filter_type=JincFilter;
828 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000829 break;
anthony08958462010-10-12 06:48:35 +0000830 case Lanczos2DSharpFilter:
nicolas1f4c0512010-10-20 20:19:30 +0000831 /* Sharpened by Nicolas Robidoux so as to optimize for minimal
832 * blurring of orthogonal lines
anthony08958462010-10-12 06:48:35 +0000833 */
834 resize_filter->blur *= 0.958033808;
835 break;
anthonyf5e76ef2010-10-12 01:22:01 +0000836 case GaussianFilter:
cristy68f103e2010-10-14 01:19:07 +0000837 sigma = (MagickRealType) (MagickSQ2/2.0); /* Cylindrical Gaussian sigma is sqrt(2)/2 */
anthonyf5e76ef2010-10-12 01:22:01 +0000838 break;
cristya782ecf2010-01-25 02:59:14 +0000839 default:
840 break;
cristy3ed852e2009-09-05 21:47:34 +0000841 }
anthony61b5ddd2010-10-05 02:33:31 +0000842 else
843 switch (filter_type)
844 {
845 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000846 case Lanczos2DSharpFilter:
nicolas45b58a92010-10-07 15:46:39 +0000847 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000848 window_type=SincFastFilter;
849 break;
850 default:
851 break;
852 }
853
cristy3ed852e2009-09-05 21:47:34 +0000854 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000855 if (artifact != (const char *) NULL)
856 {
cristy9af9b5d2010-08-15 17:04:28 +0000857 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000858 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000859 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000860 filter_type=(FilterTypes) option;
861 window_type=BoxFilter;
862 }
863 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000864 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000865 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000866 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000867 }
nicolas07bac812010-09-19 18:47:02 +0000868 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000869 artifact=GetImageArtifact(image,"filter:window");
870 if (artifact != (const char *) NULL)
871 {
cristy9af9b5d2010-08-15 17:04:28 +0000872 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000873 if ((UndefinedFilter < option) && (option < SentinelFilter))
874 {
875 if (option != LanczosFilter)
876 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000877 else
anthony48f77622010-10-03 14:32:31 +0000878 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000879 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000880 }
cristy33b1c162010-01-23 22:51:51 +0000881 }
cristy3ed852e2009-09-05 21:47:34 +0000882 }
cristy33b1c162010-01-23 22:51:51 +0000883 else
884 {
anthony48f77622010-10-03 14:32:31 +0000885 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000886 artifact=GetImageArtifact(image,"filter:window");
887 if (artifact != (const char *) NULL)
888 {
889 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
890 artifact);
891 if ((UndefinedFilter < option) && (option < SentinelFilter))
892 {
anthony61b5ddd2010-10-05 02:33:31 +0000893 filter_type=cylindrical != MagickFalse ?
894 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000895 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000896 }
897 }
898 }
nicolas07bac812010-09-19 18:47:02 +0000899 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000900 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000901 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000902 resize_filter->window=filters[window_type].function;
903 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000904 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000905
anthonyf5e76ef2010-10-12 01:22:01 +0000906 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000907 if (cylindrical != MagickFalse)
908 switch (filter_type)
909 {
910 case PointFilter:
911 case BoxFilter:
912 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000913 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000914 break;
anthony81b8bf92010-10-02 13:54:34 +0000915 default:
916 break;
anthony10b8bc82010-10-02 12:48:46 +0000917 }
anthony61b5ddd2010-10-05 02:33:31 +0000918 else
919 switch (filter_type)
920 {
921 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000922 case Lanczos2DSharpFilter:
anthony853d6972010-10-08 06:01:31 +0000923 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000924 resize_filter->filter=SincFast;
925 break;
926 default:
927 break;
928 }
929
anthonyf5e76ef2010-10-12 01:22:01 +0000930 /*
931 ** More Expert Option Modifications
932 */
933
934 /* User Sigma Override - no support change */
935 artifact=GetImageArtifact(image,"filter:sigma");
936 if (artifact != (const char *) NULL)
937 sigma=StringToDouble(artifact);
938 /* Define coefficents for Gaussian (assumes no cubic window) */
939 if ( GaussianFilter ) {
940 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
cristy68f103e2010-10-14 01:19:07 +0000941 resize_filter->coeff[1] = (MagickRealType) (1.0/(Magick2PI*sigma*sigma)); /* unused */
anthonyf5e76ef2010-10-12 01:22:01 +0000942 }
943
944 /* Blur Override */
945 artifact=GetImageArtifact(image,"filter:blur");
946 if (artifact != (const char *) NULL)
947 resize_filter->blur=StringToDouble(artifact);
948 if (resize_filter->blur < MagickEpsilon)
949 resize_filter->blur=(MagickRealType) MagickEpsilon;
950
951 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000952 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000953 if (artifact != (const char *) NULL)
954 {
cristybb503372010-05-27 20:51:26 +0000955 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000956 lobes;
957
cristy96b16132010-08-29 17:19:52 +0000958 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000959 if (lobes < 1)
960 lobes=1;
961 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000962 }
anthony61b5ddd2010-10-05 02:33:31 +0000963 /* convert Jinc lobes to a real support value */
964 if (resize_filter->filter == Jinc)
965 {
966 if (resize_filter->support > 16)
967 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
968 else
969 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
970 }
971 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000972 artifact=GetImageArtifact(image,"filter:support");
973 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000974 resize_filter->support=fabs(StringToDouble(artifact));
975 /*
nicolas07bac812010-09-19 18:47:02 +0000976 Scale windowing function separatally to the support 'clipping'
977 window that calling operator is planning to actually use. (Expert
978 override)
cristy3ed852e2009-09-05 21:47:34 +0000979 */
anthony55f12332010-09-10 01:13:02 +0000980 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000981 artifact=GetImageArtifact(image,"filter:win-support");
982 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000983 resize_filter->window_support=fabs(StringToDouble(artifact));
984 /*
anthony1f90a6b2010-09-14 08:56:31 +0000985 Adjust window function scaling to the windowing support for
986 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000987 */
988 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +0000989
anthony55f12332010-09-10 01:13:02 +0000990 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000991 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000992 */
cristy3ed852e2009-09-05 21:47:34 +0000993 B=0.0;
994 C=0.0;
cristy33b1c162010-01-23 22:51:51 +0000995 if ((filters[filter_type].function == CubicBC) ||
996 (filters[window_type].function == CubicBC))
997 {
anthony2d9b8b52010-09-14 08:31:07 +0000998 B=filters[filter_type].B;
999 C=filters[filter_type].C;
1000 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +00001001 {
anthony2d9b8b52010-09-14 08:31:07 +00001002 B=filters[window_type].B;
1003 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +00001004 }
cristy33b1c162010-01-23 22:51:51 +00001005 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001006 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001007 {
1008 B=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001009 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001010 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001011 if (artifact != (const char *) NULL)
1012 C=StringToDouble(artifact);
1013 }
1014 else
1015 {
1016 artifact=GetImageArtifact(image,"filter:c");
1017 if (artifact != (const char *) NULL)
1018 {
1019 C=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001020 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001021 }
1022 }
anthonyf5e76ef2010-10-12 01:22:01 +00001023 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
1024 resize_filter->coeff[0]=(6.0-2.0*B)/6.0;
1025 resize_filter->coeff[1]=0.0;
1026 resize_filter->coeff[2]=(-18.0+12.0*B+6.0*C)/6.0;
1027 resize_filter->coeff[3]=(12.0-9.0*B-6.0*C)/6.0;
1028 resize_filter->coeff[4]=(8.0*B+24.0*C)/6.0;
1029 resize_filter->coeff[5]=(-12.0*B-48.0*C)/6.0;
1030 resize_filter->coeff[6]=(6.0*B+30.0*C)/6.0;
1031 resize_filter->coeff[7]=(-B-6.0*C)/6.0;
cristy3ed852e2009-09-05 21:47:34 +00001032 }
anthonyf5e76ef2010-10-12 01:22:01 +00001033
anthony55f12332010-09-10 01:13:02 +00001034 /*
nicolas07bac812010-09-19 18:47:02 +00001035 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001036 */
cristyf5b49372010-10-16 01:06:47 +00001037#if defined(MAGICKCORE_OPENMP_SUPPORT)
1038 #pragma omp master
1039 {
1040#endif
1041 artifact=GetImageArtifact(image,"filter:verbose");
1042 if (artifact != (const char *) NULL)
1043 {
1044 double
anthony450db502010-10-19 04:03:03 +00001045 support,
cristyf5b49372010-10-16 01:06:47 +00001046 x;
cristy3ed852e2009-09-05 21:47:34 +00001047
cristyf5b49372010-10-16 01:06:47 +00001048 /*
1049 Set the weighting function properly when the weighting
1050 function may not exactly match the filter of the same name.
1051 EG: a Point filter really uses a Box weighting function
1052 with a different support than is typically used.
anthony463be1d2010-09-26 01:07:36 +00001053
cristyf5b49372010-10-16 01:06:47 +00001054 */
1055 if (resize_filter->filter == Box) filter_type=BoxFilter;
1056 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1057 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1058 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1059 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1060 /*
1061 Report Filter Details.
1062 */
1063 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1064 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
1065 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1066 MagickFilterOptions,filter_type));
1067 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
1068 MagickFilterOptions, window_type));
1069 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
1070 (double) resize_filter->support);
1071 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
1072 (double) resize_filter->window_support);
1073 (void) fprintf(stdout,"# scale_blur = %.*g\n",GetMagickPrecision(),
1074 (double) resize_filter->blur);
1075 if ( filter_type == GaussianFilter )
1076 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",GetMagickPrecision(),
1077 (double) sigma);
1078 (void) fprintf(stdout,"# practical_support = %.*g\n",GetMagickPrecision(),
1079 (double) support);
1080 if ( filter_type == CubicFilter || window_type == CubicFilter )
1081 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1082 (double) B,GetMagickPrecision(),(double) C);
1083 (void) fprintf(stdout,"\n");
1084 /*
1085 Output values of resulting filter graph -- for graphing
1086 filter result.
1087 */
1088 for (x=0.0; x <= support; x+=0.01f)
1089 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1090 (double) GetResizeFilterWeight(resize_filter,x));
1091 /* A final value so gnuplot can graph the 'stop' properly. */
1092 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1093 0.0);
1094 }
1095 /* Output the above once only for each image - remove setting */
1096 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1097#if defined(MAGICKCORE_OPENMP_SUPPORT)
1098 }
1099#endif
cristy3ed852e2009-09-05 21:47:34 +00001100 return(resize_filter);
1101}
1102
1103/*
1104%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1105% %
1106% %
1107% %
1108% A d a p t i v e R e s i z e I m a g e %
1109% %
1110% %
1111% %
1112%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1113%
1114% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1115%
1116% The format of the AdaptiveResizeImage method is:
1117%
cristy9af9b5d2010-08-15 17:04:28 +00001118% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1119% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001120%
1121% A description of each parameter follows:
1122%
1123% o image: the image.
1124%
1125% o columns: the number of columns in the resized image.
1126%
1127% o rows: the number of rows in the resized image.
1128%
1129% o exception: return any errors or warnings in this structure.
1130%
1131*/
1132MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001133 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001134{
1135#define AdaptiveResizeImageTag "Resize/Image"
1136
cristyc4c8d132010-01-07 01:58:38 +00001137 CacheView
1138 *resize_view;
1139
cristy3ed852e2009-09-05 21:47:34 +00001140 Image
1141 *resize_image;
1142
cristy3ed852e2009-09-05 21:47:34 +00001143 MagickBooleanType
1144 proceed;
1145
1146 MagickPixelPacket
1147 pixel;
1148
1149 PointInfo
1150 offset;
1151
1152 ResampleFilter
1153 *resample_filter;
1154
cristy9af9b5d2010-08-15 17:04:28 +00001155 ssize_t
1156 y;
1157
cristy3ed852e2009-09-05 21:47:34 +00001158 /*
1159 Adaptively resize image.
1160 */
1161 assert(image != (const Image *) NULL);
1162 assert(image->signature == MagickSignature);
1163 if (image->debug != MagickFalse)
1164 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1165 assert(exception != (ExceptionInfo *) NULL);
1166 assert(exception->signature == MagickSignature);
1167 if ((columns == 0) || (rows == 0))
1168 return((Image *) NULL);
1169 if ((columns == image->columns) && (rows == image->rows))
1170 return(CloneImage(image,0,0,MagickTrue,exception));
1171 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1172 if (resize_image == (Image *) NULL)
1173 return((Image *) NULL);
1174 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1175 {
1176 InheritException(exception,&resize_image->exception);
1177 resize_image=DestroyImage(resize_image);
1178 return((Image *) NULL);
1179 }
1180 GetMagickPixelPacket(image,&pixel);
1181 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001182 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001183 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001184 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001185 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001186 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001187 {
1188 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001189 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001190
cristybb503372010-05-27 20:51:26 +00001191 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001192 x;
1193
1194 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001195 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001196
1197 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1198 exception);
1199 if (q == (PixelPacket *) NULL)
1200 break;
1201 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1202 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001203 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001204 {
1205 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1206 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1207 &pixel);
1208 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1209 q++;
1210 }
1211 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1212 break;
cristy96b16132010-08-29 17:19:52 +00001213 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1214 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001215 if (proceed == MagickFalse)
1216 break;
1217 }
1218 resample_filter=DestroyResampleFilter(resample_filter);
1219 resize_view=DestroyCacheView(resize_view);
1220 return(resize_image);
1221}
1222
1223/*
1224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1225% %
1226% %
1227% %
1228+ B e s s e l O r d e r O n e %
1229% %
1230% %
1231% %
1232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1233%
1234% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001235% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001236%
1237% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1238%
1239% j1(x) = x*j1(x);
1240%
1241% For x in (8,inf)
1242%
1243% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1244%
1245% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1246%
1247% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1248% = 1/sqrt(2) * (sin(x) - cos(x))
1249% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1250% = -1/sqrt(2) * (sin(x) + cos(x))
1251%
1252% The format of the BesselOrderOne method is:
1253%
1254% MagickRealType BesselOrderOne(MagickRealType x)
1255%
1256% A description of each parameter follows:
1257%
1258% o x: MagickRealType value.
1259%
1260*/
1261
1262#undef I0
1263static MagickRealType I0(MagickRealType x)
1264{
1265 MagickRealType
1266 sum,
1267 t,
1268 y;
1269
cristybb503372010-05-27 20:51:26 +00001270 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001271 i;
1272
1273 /*
1274 Zeroth order Bessel function of the first kind.
1275 */
1276 sum=1.0;
1277 y=x*x/4.0;
1278 t=y;
1279 for (i=2; t > MagickEpsilon; i++)
1280 {
1281 sum+=t;
1282 t*=y/((MagickRealType) i*i);
1283 }
1284 return(sum);
1285}
1286
1287#undef J1
1288static MagickRealType J1(MagickRealType x)
1289{
1290 MagickRealType
1291 p,
1292 q;
1293
cristybb503372010-05-27 20:51:26 +00001294 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001295 i;
1296
1297 static const double
1298 Pone[] =
1299 {
1300 0.581199354001606143928050809e+21,
1301 -0.6672106568924916298020941484e+20,
1302 0.2316433580634002297931815435e+19,
1303 -0.3588817569910106050743641413e+17,
1304 0.2908795263834775409737601689e+15,
1305 -0.1322983480332126453125473247e+13,
1306 0.3413234182301700539091292655e+10,
1307 -0.4695753530642995859767162166e+7,
1308 0.270112271089232341485679099e+4
1309 },
1310 Qone[] =
1311 {
1312 0.11623987080032122878585294e+22,
1313 0.1185770712190320999837113348e+20,
1314 0.6092061398917521746105196863e+17,
1315 0.2081661221307607351240184229e+15,
1316 0.5243710262167649715406728642e+12,
1317 0.1013863514358673989967045588e+10,
1318 0.1501793594998585505921097578e+7,
1319 0.1606931573481487801970916749e+4,
1320 0.1e+1
1321 };
1322
1323 p=Pone[8];
1324 q=Qone[8];
1325 for (i=7; i >= 0; i--)
1326 {
1327 p=p*x*x+Pone[i];
1328 q=q*x*x+Qone[i];
1329 }
1330 return(p/q);
1331}
1332
1333#undef P1
1334static MagickRealType P1(MagickRealType x)
1335{
1336 MagickRealType
1337 p,
1338 q;
1339
cristybb503372010-05-27 20:51:26 +00001340 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001341 i;
1342
1343 static const double
1344 Pone[] =
1345 {
1346 0.352246649133679798341724373e+5,
1347 0.62758845247161281269005675e+5,
1348 0.313539631109159574238669888e+5,
1349 0.49854832060594338434500455e+4,
1350 0.2111529182853962382105718e+3,
1351 0.12571716929145341558495e+1
1352 },
1353 Qone[] =
1354 {
1355 0.352246649133679798068390431e+5,
1356 0.626943469593560511888833731e+5,
1357 0.312404063819041039923015703e+5,
1358 0.4930396490181088979386097e+4,
1359 0.2030775189134759322293574e+3,
1360 0.1e+1
1361 };
1362
1363 p=Pone[5];
1364 q=Qone[5];
1365 for (i=4; i >= 0; i--)
1366 {
1367 p=p*(8.0/x)*(8.0/x)+Pone[i];
1368 q=q*(8.0/x)*(8.0/x)+Qone[i];
1369 }
1370 return(p/q);
1371}
1372
1373#undef Q1
1374static MagickRealType Q1(MagickRealType x)
1375{
1376 MagickRealType
1377 p,
1378 q;
1379
cristybb503372010-05-27 20:51:26 +00001380 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001381 i;
1382
1383 static const double
1384 Pone[] =
1385 {
1386 0.3511751914303552822533318e+3,
1387 0.7210391804904475039280863e+3,
1388 0.4259873011654442389886993e+3,
1389 0.831898957673850827325226e+2,
1390 0.45681716295512267064405e+1,
1391 0.3532840052740123642735e-1
1392 },
1393 Qone[] =
1394 {
1395 0.74917374171809127714519505e+4,
1396 0.154141773392650970499848051e+5,
1397 0.91522317015169922705904727e+4,
1398 0.18111867005523513506724158e+4,
1399 0.1038187585462133728776636e+3,
1400 0.1e+1
1401 };
1402
1403 p=Pone[5];
1404 q=Qone[5];
1405 for (i=4; i >= 0; i--)
1406 {
1407 p=p*(8.0/x)*(8.0/x)+Pone[i];
1408 q=q*(8.0/x)*(8.0/x)+Qone[i];
1409 }
1410 return(p/q);
1411}
1412
1413static MagickRealType BesselOrderOne(MagickRealType x)
1414{
1415 MagickRealType
1416 p,
1417 q;
1418
1419 if (x == 0.0)
1420 return(0.0);
1421 p=x;
1422 if (x < 0.0)
1423 x=(-x);
1424 if (x < 8.0)
1425 return(p*J1(x));
1426 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1427 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1428 cos((double) x))));
1429 if (p < 0.0)
1430 q=(-q);
1431 return(q);
1432}
1433
1434/*
1435%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1436% %
1437% %
1438% %
1439+ D e s t r o y R e s i z e F i l t e r %
1440% %
1441% %
1442% %
1443%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1444%
1445% DestroyResizeFilter() destroy the resize filter.
1446%
cristya2ffd7e2010-03-10 20:50:30 +00001447% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001448%
1449% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1450%
1451% A description of each parameter follows:
1452%
1453% o resize_filter: the resize filter.
1454%
1455*/
1456MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1457{
1458 assert(resize_filter != (ResizeFilter *) NULL);
1459 assert(resize_filter->signature == MagickSignature);
1460 resize_filter->signature=(~MagickSignature);
1461 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1462 return(resize_filter);
1463}
1464
1465/*
1466%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1467% %
1468% %
1469% %
1470+ G e t R e s i z e F i l t e r S u p p o r t %
1471% %
1472% %
1473% %
1474%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1475%
1476% GetResizeFilterSupport() return the current support window size for this
1477% filter. Note that this may have been enlarged by filter:blur factor.
1478%
1479% The format of the GetResizeFilterSupport method is:
1480%
1481% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1482%
1483% A description of each parameter follows:
1484%
1485% o filter: Image filter to use.
1486%
1487*/
1488MagickExport MagickRealType GetResizeFilterSupport(
1489 const ResizeFilter *resize_filter)
1490{
1491 assert(resize_filter != (ResizeFilter *) NULL);
1492 assert(resize_filter->signature == MagickSignature);
1493 return(resize_filter->support*resize_filter->blur);
1494}
1495
1496/*
1497%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1498% %
1499% %
1500% %
1501+ G e t R e s i z e F i l t e r W e i g h t %
1502% %
1503% %
1504% %
1505%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1506%
1507% GetResizeFilterWeight evaluates the specified resize filter at the point x
1508% which usally lies between zero and the filters current 'support' and
1509% returns the weight of the filter function at that point.
1510%
1511% The format of the GetResizeFilterWeight method is:
1512%
1513% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1514% const MagickRealType x)
1515%
1516% A description of each parameter follows:
1517%
1518% o filter: the filter type.
1519%
1520% o x: the point.
1521%
1522*/
1523MagickExport MagickRealType GetResizeFilterWeight(
1524 const ResizeFilter *resize_filter,const MagickRealType x)
1525{
1526 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001527 scale,
1528 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001529
1530 /*
1531 Windowing function - scale the weighting filter by this amount.
1532 */
1533 assert(resize_filter != (ResizeFilter *) NULL);
1534 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001535 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001536 if ((resize_filter->window_support < MagickEpsilon) ||
1537 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001538 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001539 else
1540 {
anthony55f12332010-09-10 01:13:02 +00001541 scale=resize_filter->scale;
1542 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001543 }
anthony55f12332010-09-10 01:13:02 +00001544 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001545}
1546
1547/*
1548%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1549% %
1550% %
1551% %
1552% M a g n i f y I m a g e %
1553% %
1554% %
1555% %
1556%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1557%
1558% MagnifyImage() is a convenience method that scales an image proportionally
1559% to twice its size.
1560%
1561% The format of the MagnifyImage method is:
1562%
1563% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1564%
1565% A description of each parameter follows:
1566%
1567% o image: the image.
1568%
1569% o exception: return any errors or warnings in this structure.
1570%
1571*/
1572MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1573{
1574 Image
1575 *magnify_image;
1576
1577 assert(image != (Image *) NULL);
1578 assert(image->signature == MagickSignature);
1579 if (image->debug != MagickFalse)
1580 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1581 assert(exception != (ExceptionInfo *) NULL);
1582 assert(exception->signature == MagickSignature);
1583 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1584 1.0,exception);
1585 return(magnify_image);
1586}
1587
1588/*
1589%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1590% %
1591% %
1592% %
1593% M i n i f y I m a g e %
1594% %
1595% %
1596% %
1597%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1598%
1599% MinifyImage() is a convenience method that scales an image proportionally
1600% to half its size.
1601%
1602% The format of the MinifyImage method is:
1603%
1604% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1605%
1606% A description of each parameter follows:
1607%
1608% o image: the image.
1609%
1610% o exception: return any errors or warnings in this structure.
1611%
1612*/
1613MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1614{
1615 Image
1616 *minify_image;
1617
1618 assert(image != (Image *) NULL);
1619 assert(image->signature == MagickSignature);
1620 if (image->debug != MagickFalse)
1621 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1622 assert(exception != (ExceptionInfo *) NULL);
1623 assert(exception->signature == MagickSignature);
1624 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1625 1.0,exception);
1626 return(minify_image);
1627}
1628
1629/*
1630%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1631% %
1632% %
1633% %
1634% R e s a m p l e I m a g e %
1635% %
1636% %
1637% %
1638%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1639%
1640% ResampleImage() resize image in terms of its pixel size, so that when
1641% displayed at the given resolution it will be the same size in terms of
1642% real world units as the original image at the original resolution.
1643%
1644% The format of the ResampleImage method is:
1645%
1646% Image *ResampleImage(Image *image,const double x_resolution,
1647% const double y_resolution,const FilterTypes filter,const double blur,
1648% ExceptionInfo *exception)
1649%
1650% A description of each parameter follows:
1651%
1652% o image: the image to be resized to fit the given resolution.
1653%
1654% o x_resolution: the new image x resolution.
1655%
1656% o y_resolution: the new image y resolution.
1657%
1658% o filter: Image filter to use.
1659%
1660% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1661%
1662*/
1663MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1664 const double y_resolution,const FilterTypes filter,const double blur,
1665 ExceptionInfo *exception)
1666{
1667#define ResampleImageTag "Resample/Image"
1668
1669 Image
1670 *resample_image;
1671
cristybb503372010-05-27 20:51:26 +00001672 size_t
cristy3ed852e2009-09-05 21:47:34 +00001673 height,
1674 width;
1675
1676 /*
1677 Initialize sampled image attributes.
1678 */
1679 assert(image != (const Image *) NULL);
1680 assert(image->signature == MagickSignature);
1681 if (image->debug != MagickFalse)
1682 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1683 assert(exception != (ExceptionInfo *) NULL);
1684 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001685 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1686 72.0 : image->x_resolution)+0.5);
1687 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1688 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001689 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1690 if (resample_image != (Image *) NULL)
1691 {
1692 resample_image->x_resolution=x_resolution;
1693 resample_image->y_resolution=y_resolution;
1694 }
1695 return(resample_image);
1696}
1697#if defined(MAGICKCORE_LQR_DELEGATE)
1698
1699/*
1700%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1701% %
1702% %
1703% %
1704% L i q u i d R e s c a l e I m a g e %
1705% %
1706% %
1707% %
1708%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1709%
1710% LiquidRescaleImage() rescales image with seam carving.
1711%
1712% The format of the LiquidRescaleImage method is:
1713%
1714% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001715% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001716% const double delta_x,const double rigidity,ExceptionInfo *exception)
1717%
1718% A description of each parameter follows:
1719%
1720% o image: the image.
1721%
1722% o columns: the number of columns in the rescaled image.
1723%
1724% o rows: the number of rows in the rescaled image.
1725%
1726% o delta_x: maximum seam transversal step (0 means straight seams).
1727%
1728% o rigidity: introduce a bias for non-straight seams (typically 0).
1729%
1730% o exception: return any errors or warnings in this structure.
1731%
1732*/
cristy9af9b5d2010-08-15 17:04:28 +00001733MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1734 const size_t rows,const double delta_x,const double rigidity,
1735 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001736{
1737#define LiquidRescaleImageTag "Rescale/Image"
1738
cristyc5c6f662010-09-22 14:23:02 +00001739 CacheView
1740 *rescale_view;
1741
cristy3ed852e2009-09-05 21:47:34 +00001742 const char
1743 *map;
1744
1745 guchar
1746 *packet;
1747
1748 Image
1749 *rescale_image;
1750
1751 int
1752 x,
1753 y;
1754
1755 LqrCarver
1756 *carver;
1757
1758 LqrRetVal
1759 lqr_status;
1760
1761 MagickBooleanType
1762 status;
1763
1764 MagickPixelPacket
1765 pixel;
1766
1767 unsigned char
1768 *pixels;
1769
1770 /*
1771 Liquid rescale image.
1772 */
1773 assert(image != (const Image *) NULL);
1774 assert(image->signature == MagickSignature);
1775 if (image->debug != MagickFalse)
1776 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1777 assert(exception != (ExceptionInfo *) NULL);
1778 assert(exception->signature == MagickSignature);
1779 if ((columns == 0) || (rows == 0))
1780 return((Image *) NULL);
1781 if ((columns == image->columns) && (rows == image->rows))
1782 return(CloneImage(image,0,0,MagickTrue,exception));
1783 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001784 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001785 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1786 {
1787 Image
1788 *resize_image;
1789
cristybb503372010-05-27 20:51:26 +00001790 size_t
cristy3ed852e2009-09-05 21:47:34 +00001791 height,
1792 width;
1793
1794 /*
1795 Honor liquid resize size limitations.
1796 */
1797 for (width=image->columns; columns >= (2*width-1); width*=2);
1798 for (height=image->rows; rows >= (2*height-1); height*=2);
1799 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1800 exception);
1801 if (resize_image == (Image *) NULL)
1802 return((Image *) NULL);
1803 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1804 rigidity,exception);
1805 resize_image=DestroyImage(resize_image);
1806 return(rescale_image);
1807 }
1808 map="RGB";
1809 if (image->matte == MagickFalse)
1810 map="RGBA";
1811 if (image->colorspace == CMYKColorspace)
1812 {
1813 map="CMYK";
1814 if (image->matte == MagickFalse)
1815 map="CMYKA";
1816 }
1817 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1818 strlen(map)*sizeof(*pixels));
1819 if (pixels == (unsigned char *) NULL)
1820 return((Image *) NULL);
1821 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1822 pixels,exception);
1823 if (status == MagickFalse)
1824 {
1825 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1826 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1827 }
1828 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1829 if (carver == (LqrCarver *) NULL)
1830 {
1831 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1832 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1833 }
1834 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1835 lqr_status=lqr_carver_resize(carver,columns,rows);
1836 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1837 lqr_carver_get_height(carver),MagickTrue,exception);
1838 if (rescale_image == (Image *) NULL)
1839 {
1840 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1841 return((Image *) NULL);
1842 }
1843 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1844 {
1845 InheritException(exception,&rescale_image->exception);
1846 rescale_image=DestroyImage(rescale_image);
1847 return((Image *) NULL);
1848 }
1849 GetMagickPixelPacket(rescale_image,&pixel);
1850 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001851 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001852 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1853 {
1854 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001855 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001856
1857 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001858 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001859
anthony22aad252010-09-23 06:59:07 +00001860 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001861 if (q == (PixelPacket *) NULL)
1862 break;
cristyc5c6f662010-09-22 14:23:02 +00001863 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001864 pixel.red=QuantumRange*(packet[0]/255.0);
1865 pixel.green=QuantumRange*(packet[1]/255.0);
1866 pixel.blue=QuantumRange*(packet[2]/255.0);
1867 if (image->colorspace != CMYKColorspace)
1868 {
1869 if (image->matte == MagickFalse)
1870 pixel.opacity=QuantumRange*(packet[3]/255.0);
1871 }
1872 else
1873 {
1874 pixel.index=QuantumRange*(packet[3]/255.0);
1875 if (image->matte == MagickFalse)
1876 pixel.opacity=QuantumRange*(packet[4]/255.0);
1877 }
1878 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001879 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001880 break;
1881 }
cristyc5c6f662010-09-22 14:23:02 +00001882 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001883 /*
1884 Relinquish resources.
1885 */
1886 lqr_carver_destroy(carver);
1887 return(rescale_image);
1888}
1889#else
1890MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001891 const size_t magick_unused(columns),const size_t magick_unused(rows),
1892 const double magick_unused(delta_x),const double magick_unused(rigidity),
1893 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001894{
1895 assert(image != (const Image *) NULL);
1896 assert(image->signature == MagickSignature);
1897 if (image->debug != MagickFalse)
1898 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1899 assert(exception != (ExceptionInfo *) NULL);
1900 assert(exception->signature == MagickSignature);
1901 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1902 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1903 return((Image *) NULL);
1904}
1905#endif
1906
1907/*
1908%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1909% %
1910% %
1911% %
1912% R e s i z e I m a g e %
1913% %
1914% %
1915% %
1916%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1917%
1918% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001919% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001920%
1921% If an undefined filter is given the filter defaults to Mitchell for a
1922% colormapped image, a image with a matte channel, or if the image is
1923% enlarged. Otherwise the filter defaults to a Lanczos.
1924%
1925% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1926%
1927% The format of the ResizeImage method is:
1928%
cristybb503372010-05-27 20:51:26 +00001929% Image *ResizeImage(Image *image,const size_t columns,
1930% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001931% ExceptionInfo *exception)
1932%
1933% A description of each parameter follows:
1934%
1935% o image: the image.
1936%
1937% o columns: the number of columns in the scaled image.
1938%
1939% o rows: the number of rows in the scaled image.
1940%
1941% o filter: Image filter to use.
1942%
cristy9af9b5d2010-08-15 17:04:28 +00001943% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1944% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001945%
1946% o exception: return any errors or warnings in this structure.
1947%
1948*/
1949
1950typedef struct _ContributionInfo
1951{
1952 MagickRealType
1953 weight;
1954
cristybb503372010-05-27 20:51:26 +00001955 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001956 pixel;
1957} ContributionInfo;
1958
1959static ContributionInfo **DestroyContributionThreadSet(
1960 ContributionInfo **contribution)
1961{
cristybb503372010-05-27 20:51:26 +00001962 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001963 i;
1964
1965 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001966 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001967 if (contribution[i] != (ContributionInfo *) NULL)
1968 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1969 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001970 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001971 return(contribution);
1972}
1973
1974static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1975{
cristybb503372010-05-27 20:51:26 +00001976 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001977 i;
1978
1979 ContributionInfo
1980 **contribution;
1981
cristybb503372010-05-27 20:51:26 +00001982 size_t
cristy3ed852e2009-09-05 21:47:34 +00001983 number_threads;
1984
1985 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001986 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001987 sizeof(*contribution));
1988 if (contribution == (ContributionInfo **) NULL)
1989 return((ContributionInfo **) NULL);
1990 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00001991 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00001992 {
1993 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1994 sizeof(**contribution));
1995 if (contribution[i] == (ContributionInfo *) NULL)
1996 return(DestroyContributionThreadSet(contribution));
1997 }
1998 return(contribution);
1999}
2000
2001static inline double MagickMax(const double x,const double y)
2002{
2003 if (x > y)
2004 return(x);
2005 return(y);
2006}
2007
2008static inline double MagickMin(const double x,const double y)
2009{
2010 if (x < y)
2011 return(x);
2012 return(y);
2013}
2014
2015static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2016 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002017 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002018{
2019#define ResizeImageTag "Resize/Image"
2020
cristyfa112112010-01-04 17:48:07 +00002021 CacheView
2022 *image_view,
2023 *resize_view;
2024
cristy3ed852e2009-09-05 21:47:34 +00002025 ClassType
2026 storage_class;
2027
2028 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002029 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002030
cristy3ed852e2009-09-05 21:47:34 +00002031 MagickBooleanType
2032 status;
2033
2034 MagickPixelPacket
2035 zero;
2036
2037 MagickRealType
2038 scale,
2039 support;
2040
cristy9af9b5d2010-08-15 17:04:28 +00002041 ssize_t
2042 x;
2043
cristy3ed852e2009-09-05 21:47:34 +00002044 /*
2045 Apply filter to resize horizontally from image to resize image.
2046 */
cristy5d824382010-09-06 14:00:17 +00002047 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002048 support=scale*GetResizeFilterSupport(resize_filter);
2049 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2050 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2051 {
2052 InheritException(exception,&resize_image->exception);
2053 return(MagickFalse);
2054 }
2055 if (support < 0.5)
2056 {
2057 /*
nicolas07bac812010-09-19 18:47:02 +00002058 Support too small even for nearest neighbour: Reduce to point
2059 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002060 */
2061 support=(MagickRealType) 0.5;
2062 scale=1.0;
2063 }
2064 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2065 if (contributions == (ContributionInfo **) NULL)
2066 {
2067 (void) ThrowMagickException(exception,GetMagickModule(),
2068 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2069 return(MagickFalse);
2070 }
2071 status=MagickTrue;
2072 scale=1.0/scale;
2073 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2074 image_view=AcquireCacheView(image);
2075 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002076#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002077 #pragma omp parallel for shared(status)
2078#endif
cristybb503372010-05-27 20:51:26 +00002079 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002080 {
cristy3ed852e2009-09-05 21:47:34 +00002081 MagickRealType
2082 center,
2083 density;
2084
2085 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002086 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002087
2088 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002089 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002090
cristy03dbbd22010-09-19 23:04:47 +00002091 register ContributionInfo
2092 *restrict contribution;
2093
cristy3ed852e2009-09-05 21:47:34 +00002094 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002095 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002096
cristy3ed852e2009-09-05 21:47:34 +00002097 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002098 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002099
cristy03dbbd22010-09-19 23:04:47 +00002100 register ssize_t
2101 y;
2102
cristy9af9b5d2010-08-15 17:04:28 +00002103 ssize_t
2104 n,
2105 start,
2106 stop;
2107
cristy3ed852e2009-09-05 21:47:34 +00002108 if (status == MagickFalse)
2109 continue;
2110 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002111 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2112 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002113 density=0.0;
2114 contribution=contributions[GetOpenMPThreadId()];
2115 for (n=0; n < (stop-start); n++)
2116 {
2117 contribution[n].pixel=start+n;
2118 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2119 ((MagickRealType) (start+n)-center+0.5));
2120 density+=contribution[n].weight;
2121 }
2122 if ((density != 0.0) && (density != 1.0))
2123 {
cristybb503372010-05-27 20:51:26 +00002124 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002125 i;
2126
2127 /*
2128 Normalize.
2129 */
2130 density=1.0/density;
2131 for (i=0; i < n; i++)
2132 contribution[i].weight*=density;
2133 }
cristy9af9b5d2010-08-15 17:04:28 +00002134 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2135 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002136 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2137 exception);
2138 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2139 {
2140 status=MagickFalse;
2141 continue;
2142 }
2143 indexes=GetCacheViewVirtualIndexQueue(image_view);
2144 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002145 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002146 {
cristy3ed852e2009-09-05 21:47:34 +00002147 MagickPixelPacket
2148 pixel;
2149
2150 MagickRealType
2151 alpha;
2152
cristybb503372010-05-27 20:51:26 +00002153 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002154 i;
2155
cristy9af9b5d2010-08-15 17:04:28 +00002156 ssize_t
2157 j;
2158
cristy3ed852e2009-09-05 21:47:34 +00002159 pixel=zero;
2160 if (image->matte == MagickFalse)
2161 {
2162 for (i=0; i < n; i++)
2163 {
2164 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2165 (contribution[i].pixel-contribution[0].pixel);
2166 alpha=contribution[i].weight;
2167 pixel.red+=alpha*(p+j)->red;
2168 pixel.green+=alpha*(p+j)->green;
2169 pixel.blue+=alpha*(p+j)->blue;
2170 pixel.opacity+=alpha*(p+j)->opacity;
2171 }
cristyce70c172010-01-07 17:15:30 +00002172 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2173 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2174 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2175 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002176 if ((image->colorspace == CMYKColorspace) &&
2177 (resize_image->colorspace == CMYKColorspace))
2178 {
2179 for (i=0; i < n; i++)
2180 {
2181 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2182 (contribution[i].pixel-contribution[0].pixel);
2183 alpha=contribution[i].weight;
2184 pixel.index+=alpha*indexes[j];
2185 }
cristyce70c172010-01-07 17:15:30 +00002186 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002187 }
2188 }
2189 else
2190 {
2191 MagickRealType
2192 gamma;
2193
2194 gamma=0.0;
2195 for (i=0; i < n; i++)
2196 {
2197 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2198 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002199 alpha=contribution[i].weight*QuantumScale*
2200 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002201 pixel.red+=alpha*(p+j)->red;
2202 pixel.green+=alpha*(p+j)->green;
2203 pixel.blue+=alpha*(p+j)->blue;
2204 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2205 gamma+=alpha;
2206 }
2207 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002208 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2209 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2210 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2211 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002212 if ((image->colorspace == CMYKColorspace) &&
2213 (resize_image->colorspace == CMYKColorspace))
2214 {
2215 for (i=0; i < n; i++)
2216 {
2217 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2218 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002219 alpha=contribution[i].weight*QuantumScale*
2220 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002221 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002222 }
cristyce70c172010-01-07 17:15:30 +00002223 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2224 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002225 }
2226 }
2227 if ((resize_image->storage_class == PseudoClass) &&
2228 (image->storage_class == PseudoClass))
2229 {
cristybb503372010-05-27 20:51:26 +00002230 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002231 1.0)+0.5);
2232 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2233 (contribution[i-start].pixel-contribution[0].pixel);
2234 resize_indexes[y]=indexes[j];
2235 }
2236 q++;
2237 }
2238 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2239 status=MagickFalse;
2240 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2241 {
2242 MagickBooleanType
2243 proceed;
2244
cristyb5d5f722009-11-04 03:03:49 +00002245#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002246 #pragma omp critical (MagickCore_HorizontalFilter)
2247#endif
cristy9af9b5d2010-08-15 17:04:28 +00002248 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002249 if (proceed == MagickFalse)
2250 status=MagickFalse;
2251 }
2252 }
2253 resize_view=DestroyCacheView(resize_view);
2254 image_view=DestroyCacheView(image_view);
2255 contributions=DestroyContributionThreadSet(contributions);
2256 return(status);
2257}
2258
2259static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2260 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002261 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002262{
cristyfa112112010-01-04 17:48:07 +00002263 CacheView
2264 *image_view,
2265 *resize_view;
2266
cristy3ed852e2009-09-05 21:47:34 +00002267 ClassType
2268 storage_class;
2269
2270 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002271 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002272
cristy3ed852e2009-09-05 21:47:34 +00002273 MagickBooleanType
2274 status;
2275
2276 MagickPixelPacket
2277 zero;
2278
2279 MagickRealType
2280 scale,
2281 support;
2282
cristy9af9b5d2010-08-15 17:04:28 +00002283 ssize_t
2284 y;
2285
cristy3ed852e2009-09-05 21:47:34 +00002286 /*
cristy9af9b5d2010-08-15 17:04:28 +00002287 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002288 */
cristy5d824382010-09-06 14:00:17 +00002289 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002290 support=scale*GetResizeFilterSupport(resize_filter);
2291 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2292 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2293 {
2294 InheritException(exception,&resize_image->exception);
2295 return(MagickFalse);
2296 }
2297 if (support < 0.5)
2298 {
2299 /*
nicolas07bac812010-09-19 18:47:02 +00002300 Support too small even for nearest neighbour: Reduce to point
2301 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002302 */
2303 support=(MagickRealType) 0.5;
2304 scale=1.0;
2305 }
2306 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2307 if (contributions == (ContributionInfo **) NULL)
2308 {
2309 (void) ThrowMagickException(exception,GetMagickModule(),
2310 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2311 return(MagickFalse);
2312 }
2313 status=MagickTrue;
2314 scale=1.0/scale;
2315 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2316 image_view=AcquireCacheView(image);
2317 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002318#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002319 #pragma omp parallel for shared(status)
2320#endif
cristybb503372010-05-27 20:51:26 +00002321 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002322 {
cristy3ed852e2009-09-05 21:47:34 +00002323 MagickRealType
2324 center,
2325 density;
2326
2327 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002328 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002329
2330 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002331 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002332
cristy03dbbd22010-09-19 23:04:47 +00002333 register ContributionInfo
2334 *restrict contribution;
2335
cristy3ed852e2009-09-05 21:47:34 +00002336 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002337 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002338
cristy9af9b5d2010-08-15 17:04:28 +00002339 register PixelPacket
2340 *restrict q;
2341
cristybb503372010-05-27 20:51:26 +00002342 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002343 x;
2344
cristy9af9b5d2010-08-15 17:04:28 +00002345 ssize_t
2346 n,
2347 start,
2348 stop;
cristy3ed852e2009-09-05 21:47:34 +00002349
2350 if (status == MagickFalse)
2351 continue;
cristy679e6962010-03-18 00:42:45 +00002352 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002353 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2354 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002355 density=0.0;
2356 contribution=contributions[GetOpenMPThreadId()];
2357 for (n=0; n < (stop-start); n++)
2358 {
2359 contribution[n].pixel=start+n;
2360 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2361 ((MagickRealType) (start+n)-center+0.5));
2362 density+=contribution[n].weight;
2363 }
2364 if ((density != 0.0) && (density != 1.0))
2365 {
cristybb503372010-05-27 20:51:26 +00002366 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002367 i;
2368
2369 /*
2370 Normalize.
2371 */
2372 density=1.0/density;
2373 for (i=0; i < n; i++)
2374 contribution[i].weight*=density;
2375 }
2376 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002377 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2378 exception);
cristy3ed852e2009-09-05 21:47:34 +00002379 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2380 exception);
2381 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2382 {
2383 status=MagickFalse;
2384 continue;
2385 }
2386 indexes=GetCacheViewVirtualIndexQueue(image_view);
2387 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002388 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002389 {
cristy3ed852e2009-09-05 21:47:34 +00002390 MagickPixelPacket
2391 pixel;
2392
2393 MagickRealType
2394 alpha;
2395
cristybb503372010-05-27 20:51:26 +00002396 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002397 i;
2398
cristy9af9b5d2010-08-15 17:04:28 +00002399 ssize_t
2400 j;
2401
cristy3ed852e2009-09-05 21:47:34 +00002402 pixel=zero;
2403 if (image->matte == MagickFalse)
2404 {
2405 for (i=0; i < n; i++)
2406 {
cristybb503372010-05-27 20:51:26 +00002407 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002408 image->columns+x);
2409 alpha=contribution[i].weight;
2410 pixel.red+=alpha*(p+j)->red;
2411 pixel.green+=alpha*(p+j)->green;
2412 pixel.blue+=alpha*(p+j)->blue;
2413 pixel.opacity+=alpha*(p+j)->opacity;
2414 }
cristyce70c172010-01-07 17:15:30 +00002415 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2416 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2417 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2418 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002419 if ((image->colorspace == CMYKColorspace) &&
2420 (resize_image->colorspace == CMYKColorspace))
2421 {
2422 for (i=0; i < n; i++)
2423 {
cristybb503372010-05-27 20:51:26 +00002424 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002425 image->columns+x);
2426 alpha=contribution[i].weight;
2427 pixel.index+=alpha*indexes[j];
2428 }
cristyce70c172010-01-07 17:15:30 +00002429 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002430 }
2431 }
2432 else
2433 {
2434 MagickRealType
2435 gamma;
2436
2437 gamma=0.0;
2438 for (i=0; i < n; i++)
2439 {
cristybb503372010-05-27 20:51:26 +00002440 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002441 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002442 alpha=contribution[i].weight*QuantumScale*
2443 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002444 pixel.red+=alpha*(p+j)->red;
2445 pixel.green+=alpha*(p+j)->green;
2446 pixel.blue+=alpha*(p+j)->blue;
2447 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2448 gamma+=alpha;
2449 }
2450 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002451 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2452 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2453 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2454 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002455 if ((image->colorspace == CMYKColorspace) &&
2456 (resize_image->colorspace == CMYKColorspace))
2457 {
2458 for (i=0; i < n; i++)
2459 {
cristybb503372010-05-27 20:51:26 +00002460 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002461 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002462 alpha=contribution[i].weight*QuantumScale*
2463 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002464 pixel.index+=alpha*indexes[j];
2465 }
cristyce70c172010-01-07 17:15:30 +00002466 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2467 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002468 }
2469 }
2470 if ((resize_image->storage_class == PseudoClass) &&
2471 (image->storage_class == PseudoClass))
2472 {
cristybb503372010-05-27 20:51:26 +00002473 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002474 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002475 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002476 image->columns+x);
2477 resize_indexes[x]=indexes[j];
2478 }
2479 q++;
2480 }
2481 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2482 status=MagickFalse;
2483 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2484 {
2485 MagickBooleanType
2486 proceed;
2487
cristyb5d5f722009-11-04 03:03:49 +00002488#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002489 #pragma omp critical (MagickCore_VerticalFilter)
2490#endif
cristy9af9b5d2010-08-15 17:04:28 +00002491 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002492 if (proceed == MagickFalse)
2493 status=MagickFalse;
2494 }
2495 }
2496 resize_view=DestroyCacheView(resize_view);
2497 image_view=DestroyCacheView(image_view);
2498 contributions=DestroyContributionThreadSet(contributions);
2499 return(status);
2500}
2501
cristybb503372010-05-27 20:51:26 +00002502MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2503 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002504 ExceptionInfo *exception)
2505{
2506#define WorkLoadFactor 0.265
2507
2508 FilterTypes
2509 filter_type;
2510
2511 Image
2512 *filter_image,
2513 *resize_image;
2514
cristy9af9b5d2010-08-15 17:04:28 +00002515 MagickOffsetType
2516 offset;
2517
cristy3ed852e2009-09-05 21:47:34 +00002518 MagickRealType
2519 x_factor,
2520 y_factor;
2521
2522 MagickSizeType
2523 span;
2524
2525 MagickStatusType
2526 status;
2527
2528 ResizeFilter
2529 *resize_filter;
2530
cristy3ed852e2009-09-05 21:47:34 +00002531 /*
2532 Acquire resize image.
2533 */
2534 assert(image != (Image *) NULL);
2535 assert(image->signature == MagickSignature);
2536 if (image->debug != MagickFalse)
2537 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2538 assert(exception != (ExceptionInfo *) NULL);
2539 assert(exception->signature == MagickSignature);
2540 if ((columns == 0) || (rows == 0))
2541 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2542 if ((columns == image->columns) && (rows == image->rows) &&
2543 (filter == UndefinedFilter) && (blur == 1.0))
2544 return(CloneImage(image,0,0,MagickTrue,exception));
2545 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2546 if (resize_image == (Image *) NULL)
2547 return(resize_image);
2548 /*
2549 Acquire resize filter.
2550 */
2551 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2552 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2553 if ((x_factor*y_factor) > WorkLoadFactor)
2554 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2555 else
2556 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2557 if (filter_image == (Image *) NULL)
2558 return(DestroyImage(resize_image));
2559 filter_type=LanczosFilter;
2560 if (filter != UndefinedFilter)
2561 filter_type=filter;
2562 else
2563 if ((x_factor == 1.0) && (y_factor == 1.0))
2564 filter_type=PointFilter;
2565 else
2566 if ((image->storage_class == PseudoClass) ||
2567 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2568 filter_type=MitchellFilter;
2569 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2570 exception);
2571 /*
2572 Resize image.
2573 */
cristy9af9b5d2010-08-15 17:04:28 +00002574 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002575 if ((x_factor*y_factor) > WorkLoadFactor)
2576 {
2577 span=(MagickSizeType) (filter_image->columns+rows);
2578 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002579 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002580 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002581 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002582 }
2583 else
2584 {
2585 span=(MagickSizeType) (filter_image->rows+columns);
2586 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002587 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002588 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002589 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002590 }
2591 /*
2592 Free resources.
2593 */
2594 filter_image=DestroyImage(filter_image);
2595 resize_filter=DestroyResizeFilter(resize_filter);
2596 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2597 return((Image *) NULL);
2598 resize_image->type=image->type;
2599 return(resize_image);
2600}
2601
2602/*
2603%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2604% %
2605% %
2606% %
2607% S a m p l e I m a g e %
2608% %
2609% %
2610% %
2611%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2612%
2613% SampleImage() scales an image to the desired dimensions with pixel
2614% sampling. Unlike other scaling methods, this method does not introduce
2615% any additional color into the scaled image.
2616%
2617% The format of the SampleImage method is:
2618%
cristybb503372010-05-27 20:51:26 +00002619% Image *SampleImage(const Image *image,const size_t columns,
2620% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002621%
2622% A description of each parameter follows:
2623%
2624% o image: the image.
2625%
2626% o columns: the number of columns in the sampled image.
2627%
2628% o rows: the number of rows in the sampled image.
2629%
2630% o exception: return any errors or warnings in this structure.
2631%
2632*/
cristybb503372010-05-27 20:51:26 +00002633MagickExport Image *SampleImage(const Image *image,const size_t columns,
2634 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002635{
2636#define SampleImageTag "Sample/Image"
2637
cristyc4c8d132010-01-07 01:58:38 +00002638 CacheView
2639 *image_view,
2640 *sample_view;
2641
cristy3ed852e2009-09-05 21:47:34 +00002642 Image
2643 *sample_image;
2644
cristy3ed852e2009-09-05 21:47:34 +00002645 MagickBooleanType
2646 status;
2647
cristy5f959472010-05-27 22:19:46 +00002648 MagickOffsetType
2649 progress;
2650
cristybb503372010-05-27 20:51:26 +00002651 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002652 x;
2653
cristy5f959472010-05-27 22:19:46 +00002654 ssize_t
2655 *x_offset,
2656 y;
2657
cristy3ed852e2009-09-05 21:47:34 +00002658 /*
2659 Initialize sampled image attributes.
2660 */
2661 assert(image != (const Image *) NULL);
2662 assert(image->signature == MagickSignature);
2663 if (image->debug != MagickFalse)
2664 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2665 assert(exception != (ExceptionInfo *) NULL);
2666 assert(exception->signature == MagickSignature);
2667 if ((columns == 0) || (rows == 0))
2668 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2669 if ((columns == image->columns) && (rows == image->rows))
2670 return(CloneImage(image,0,0,MagickTrue,exception));
2671 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2672 if (sample_image == (Image *) NULL)
2673 return((Image *) NULL);
2674 /*
2675 Allocate scan line buffer and column offset buffers.
2676 */
cristybb503372010-05-27 20:51:26 +00002677 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002678 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002679 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002680 {
2681 sample_image=DestroyImage(sample_image);
2682 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2683 }
cristybb503372010-05-27 20:51:26 +00002684 for (x=0; x < (ssize_t) sample_image->columns; x++)
2685 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002686 sample_image->columns);
2687 /*
2688 Sample each row.
2689 */
2690 status=MagickTrue;
2691 progress=0;
2692 image_view=AcquireCacheView(image);
2693 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002694#if defined(MAGICKCORE_OPENMP_SUPPORT)
2695 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002696#endif
cristybb503372010-05-27 20:51:26 +00002697 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002698 {
cristy3ed852e2009-09-05 21:47:34 +00002699 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002700 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002701
2702 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002703 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002704
2705 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002706 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002707
cristy3ed852e2009-09-05 21:47:34 +00002708 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002709 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002710
cristy03dbbd22010-09-19 23:04:47 +00002711 register ssize_t
2712 x;
2713
cristy9af9b5d2010-08-15 17:04:28 +00002714 ssize_t
2715 y_offset;
2716
cristy3ed852e2009-09-05 21:47:34 +00002717 if (status == MagickFalse)
2718 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002719 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2720 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002721 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2722 exception);
2723 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2724 exception);
2725 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2726 {
2727 status=MagickFalse;
2728 continue;
2729 }
2730 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2731 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2732 /*
2733 Sample each column.
2734 */
cristybb503372010-05-27 20:51:26 +00002735 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002736 *q++=p[x_offset[x]];
2737 if ((image->storage_class == PseudoClass) ||
2738 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002739 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002740 sample_indexes[x]=indexes[x_offset[x]];
2741 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2742 status=MagickFalse;
2743 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2744 {
2745 MagickBooleanType
2746 proceed;
2747
cristyb5d5f722009-11-04 03:03:49 +00002748#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002749 #pragma omp critical (MagickCore_SampleImage)
2750#endif
2751 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2752 if (proceed == MagickFalse)
2753 status=MagickFalse;
2754 }
2755 }
2756 image_view=DestroyCacheView(image_view);
2757 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002758 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002759 sample_image->type=image->type;
2760 return(sample_image);
2761}
2762
2763/*
2764%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2765% %
2766% %
2767% %
2768% S c a l e I m a g e %
2769% %
2770% %
2771% %
2772%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2773%
2774% ScaleImage() changes the size of an image to the given dimensions.
2775%
2776% The format of the ScaleImage method is:
2777%
cristybb503372010-05-27 20:51:26 +00002778% Image *ScaleImage(const Image *image,const size_t columns,
2779% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002780%
2781% A description of each parameter follows:
2782%
2783% o image: the image.
2784%
2785% o columns: the number of columns in the scaled image.
2786%
2787% o rows: the number of rows in the scaled image.
2788%
2789% o exception: return any errors or warnings in this structure.
2790%
2791*/
cristybb503372010-05-27 20:51:26 +00002792MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2793 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002794{
2795#define ScaleImageTag "Scale/Image"
2796
cristyed6cb232010-01-20 03:07:53 +00002797 CacheView
2798 *image_view,
2799 *scale_view;
2800
cristy3ed852e2009-09-05 21:47:34 +00002801 Image
2802 *scale_image;
2803
cristy3ed852e2009-09-05 21:47:34 +00002804 MagickBooleanType
2805 next_column,
2806 next_row,
2807 proceed;
2808
2809 MagickPixelPacket
2810 pixel,
2811 *scale_scanline,
2812 *scanline,
2813 *x_vector,
2814 *y_vector,
2815 zero;
2816
cristy3ed852e2009-09-05 21:47:34 +00002817 PointInfo
2818 scale,
2819 span;
2820
cristybb503372010-05-27 20:51:26 +00002821 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002822 i;
2823
cristy9af9b5d2010-08-15 17:04:28 +00002824 ssize_t
2825 number_rows,
2826 y;
2827
cristy3ed852e2009-09-05 21:47:34 +00002828 /*
2829 Initialize scaled image attributes.
2830 */
2831 assert(image != (const Image *) NULL);
2832 assert(image->signature == MagickSignature);
2833 if (image->debug != MagickFalse)
2834 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2835 assert(exception != (ExceptionInfo *) NULL);
2836 assert(exception->signature == MagickSignature);
2837 if ((columns == 0) || (rows == 0))
2838 return((Image *) NULL);
2839 if ((columns == image->columns) && (rows == image->rows))
2840 return(CloneImage(image,0,0,MagickTrue,exception));
2841 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2842 if (scale_image == (Image *) NULL)
2843 return((Image *) NULL);
2844 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2845 {
2846 InheritException(exception,&scale_image->exception);
2847 scale_image=DestroyImage(scale_image);
2848 return((Image *) NULL);
2849 }
2850 /*
2851 Allocate memory.
2852 */
2853 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2854 sizeof(*x_vector));
2855 scanline=x_vector;
2856 if (image->rows != scale_image->rows)
2857 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2858 sizeof(*scanline));
2859 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2860 scale_image->columns,sizeof(*scale_scanline));
2861 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2862 sizeof(*y_vector));
2863 if ((scanline == (MagickPixelPacket *) NULL) ||
2864 (scale_scanline == (MagickPixelPacket *) NULL) ||
2865 (x_vector == (MagickPixelPacket *) NULL) ||
2866 (y_vector == (MagickPixelPacket *) NULL))
2867 {
2868 scale_image=DestroyImage(scale_image);
2869 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2870 }
2871 /*
2872 Scale image.
2873 */
2874 number_rows=0;
2875 next_row=MagickTrue;
2876 span.y=1.0;
2877 scale.y=(double) scale_image->rows/(double) image->rows;
2878 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2879 sizeof(*y_vector));
2880 GetMagickPixelPacket(image,&pixel);
2881 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2882 i=0;
cristyed6cb232010-01-20 03:07:53 +00002883 image_view=AcquireCacheView(image);
2884 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002885 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002886 {
2887 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002888 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002889
2890 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002891 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002892
2893 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002894 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002895
cristy3ed852e2009-09-05 21:47:34 +00002896 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002897 *restrict s,
2898 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002899
2900 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002901 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002902
cristy9af9b5d2010-08-15 17:04:28 +00002903 register ssize_t
2904 x;
2905
cristyed6cb232010-01-20 03:07:53 +00002906 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2907 exception);
cristy3ed852e2009-09-05 21:47:34 +00002908 if (q == (PixelPacket *) NULL)
2909 break;
2910 scale_indexes=GetAuthenticIndexQueue(scale_image);
2911 if (scale_image->rows == image->rows)
2912 {
2913 /*
2914 Read a new scanline.
2915 */
cristyed6cb232010-01-20 03:07:53 +00002916 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2917 exception);
cristy3ed852e2009-09-05 21:47:34 +00002918 if (p == (const PixelPacket *) NULL)
2919 break;
cristyed6cb232010-01-20 03:07:53 +00002920 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002921 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002922 {
cristyce70c172010-01-07 17:15:30 +00002923 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2924 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2925 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002926 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002927 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002928 if (indexes != (IndexPacket *) NULL)
2929 x_vector[x].index=(MagickRealType) indexes[x];
2930 p++;
2931 }
2932 }
2933 else
2934 {
2935 /*
2936 Scale Y direction.
2937 */
2938 while (scale.y < span.y)
2939 {
cristy9af9b5d2010-08-15 17:04:28 +00002940 if ((next_row != MagickFalse) &&
2941 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002942 {
2943 /*
2944 Read a new scanline.
2945 */
cristyed6cb232010-01-20 03:07:53 +00002946 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2947 exception);
cristy3ed852e2009-09-05 21:47:34 +00002948 if (p == (const PixelPacket *) NULL)
2949 break;
cristyed6cb232010-01-20 03:07:53 +00002950 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002951 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002952 {
cristyce70c172010-01-07 17:15:30 +00002953 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2954 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2955 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002956 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002957 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002958 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002959 if (indexes != (IndexPacket *) NULL)
2960 x_vector[x].index=(MagickRealType) indexes[x];
2961 p++;
2962 }
2963 number_rows++;
2964 }
cristybb503372010-05-27 20:51:26 +00002965 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002966 {
2967 y_vector[x].red+=scale.y*x_vector[x].red;
2968 y_vector[x].green+=scale.y*x_vector[x].green;
2969 y_vector[x].blue+=scale.y*x_vector[x].blue;
2970 if (scale_image->matte != MagickFalse)
2971 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2972 if (scale_indexes != (IndexPacket *) NULL)
2973 y_vector[x].index+=scale.y*x_vector[x].index;
2974 }
2975 span.y-=scale.y;
2976 scale.y=(double) scale_image->rows/(double) image->rows;
2977 next_row=MagickTrue;
2978 }
cristybb503372010-05-27 20:51:26 +00002979 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002980 {
2981 /*
2982 Read a new scanline.
2983 */
cristyed6cb232010-01-20 03:07:53 +00002984 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2985 exception);
cristy3ed852e2009-09-05 21:47:34 +00002986 if (p == (const PixelPacket *) NULL)
2987 break;
cristyed6cb232010-01-20 03:07:53 +00002988 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002989 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002990 {
cristyce70c172010-01-07 17:15:30 +00002991 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2992 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2993 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002994 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00002995 x_vector[x].opacity=(MagickRealType)
2996 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002997 if (indexes != (IndexPacket *) NULL)
2998 x_vector[x].index=(MagickRealType) indexes[x];
2999 p++;
3000 }
3001 number_rows++;
3002 next_row=MagickFalse;
3003 }
3004 s=scanline;
cristybb503372010-05-27 20:51:26 +00003005 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003006 {
3007 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3008 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3009 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3010 if (image->matte != MagickFalse)
3011 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3012 if (scale_indexes != (IndexPacket *) NULL)
3013 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3014 s->red=pixel.red;
3015 s->green=pixel.green;
3016 s->blue=pixel.blue;
3017 if (scale_image->matte != MagickFalse)
3018 s->opacity=pixel.opacity;
3019 if (scale_indexes != (IndexPacket *) NULL)
3020 s->index=pixel.index;
3021 s++;
3022 y_vector[x]=zero;
3023 }
3024 scale.y-=span.y;
3025 if (scale.y <= 0)
3026 {
3027 scale.y=(double) scale_image->rows/(double) image->rows;
3028 next_row=MagickTrue;
3029 }
3030 span.y=1.0;
3031 }
3032 if (scale_image->columns == image->columns)
3033 {
3034 /*
3035 Transfer scanline to scaled image.
3036 */
3037 s=scanline;
cristybb503372010-05-27 20:51:26 +00003038 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003039 {
cristyce70c172010-01-07 17:15:30 +00003040 q->red=ClampToQuantum(s->red);
3041 q->green=ClampToQuantum(s->green);
3042 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003043 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003044 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003045 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003046 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003047 q++;
3048 s++;
3049 }
3050 }
3051 else
3052 {
3053 /*
3054 Scale X direction.
3055 */
3056 pixel=zero;
3057 next_column=MagickFalse;
3058 span.x=1.0;
3059 s=scanline;
3060 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003061 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003062 {
3063 scale.x=(double) scale_image->columns/(double) image->columns;
3064 while (scale.x >= span.x)
3065 {
3066 if (next_column != MagickFalse)
3067 {
3068 pixel=zero;
3069 t++;
3070 }
3071 pixel.red+=span.x*s->red;
3072 pixel.green+=span.x*s->green;
3073 pixel.blue+=span.x*s->blue;
3074 if (image->matte != MagickFalse)
3075 pixel.opacity+=span.x*s->opacity;
3076 if (scale_indexes != (IndexPacket *) NULL)
3077 pixel.index+=span.x*s->index;
3078 t->red=pixel.red;
3079 t->green=pixel.green;
3080 t->blue=pixel.blue;
3081 if (scale_image->matte != MagickFalse)
3082 t->opacity=pixel.opacity;
3083 if (scale_indexes != (IndexPacket *) NULL)
3084 t->index=pixel.index;
3085 scale.x-=span.x;
3086 span.x=1.0;
3087 next_column=MagickTrue;
3088 }
3089 if (scale.x > 0)
3090 {
3091 if (next_column != MagickFalse)
3092 {
3093 pixel=zero;
3094 next_column=MagickFalse;
3095 t++;
3096 }
3097 pixel.red+=scale.x*s->red;
3098 pixel.green+=scale.x*s->green;
3099 pixel.blue+=scale.x*s->blue;
3100 if (scale_image->matte != MagickFalse)
3101 pixel.opacity+=scale.x*s->opacity;
3102 if (scale_indexes != (IndexPacket *) NULL)
3103 pixel.index+=scale.x*s->index;
3104 span.x-=scale.x;
3105 }
3106 s++;
3107 }
3108 if (span.x > 0)
3109 {
3110 s--;
3111 pixel.red+=span.x*s->red;
3112 pixel.green+=span.x*s->green;
3113 pixel.blue+=span.x*s->blue;
3114 if (scale_image->matte != MagickFalse)
3115 pixel.opacity+=span.x*s->opacity;
3116 if (scale_indexes != (IndexPacket *) NULL)
3117 pixel.index+=span.x*s->index;
3118 }
3119 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003120 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003121 {
3122 t->red=pixel.red;
3123 t->green=pixel.green;
3124 t->blue=pixel.blue;
3125 if (scale_image->matte != MagickFalse)
3126 t->opacity=pixel.opacity;
3127 if (scale_indexes != (IndexPacket *) NULL)
3128 t->index=pixel.index;
3129 }
3130 /*
3131 Transfer scanline to scaled image.
3132 */
3133 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003134 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003135 {
cristyce70c172010-01-07 17:15:30 +00003136 q->red=ClampToQuantum(t->red);
3137 q->green=ClampToQuantum(t->green);
3138 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003139 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003140 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003141 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003142 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003143 t++;
3144 q++;
3145 }
3146 }
cristyed6cb232010-01-20 03:07:53 +00003147 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003148 break;
cristy96b16132010-08-29 17:19:52 +00003149 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3150 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003151 if (proceed == MagickFalse)
3152 break;
3153 }
cristyed6cb232010-01-20 03:07:53 +00003154 scale_view=DestroyCacheView(scale_view);
3155 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003156 /*
3157 Free allocated memory.
3158 */
3159 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3160 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3161 if (scale_image->rows != image->rows)
3162 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3163 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3164 scale_image->type=image->type;
3165 return(scale_image);
3166}
3167
anthony02b4cb42010-10-10 04:54:35 +00003168#if 0
3169 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003170/*
3171%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3172% %
3173% %
3174% %
3175+ S e t R e s i z e F i l t e r S u p p o r t %
3176% %
3177% %
3178% %
3179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3180%
3181% SetResizeFilterSupport() specifies which IR filter to use to window
3182%
3183% The format of the SetResizeFilterSupport method is:
3184%
3185% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3186% const MagickRealType support)
3187%
3188% A description of each parameter follows:
3189%
3190% o resize_filter: the resize filter.
3191%
3192% o support: the filter spport radius.
3193%
3194*/
3195MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3196 const MagickRealType support)
3197{
3198 assert(resize_filter != (ResizeFilter *) NULL);
3199 assert(resize_filter->signature == MagickSignature);
3200 resize_filter->support=support;
3201}
anthony02b4cb42010-10-10 04:54:35 +00003202#endif
cristy3ed852e2009-09-05 21:47:34 +00003203
3204/*
3205%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3206% %
3207% %
3208% %
3209% T h u m b n a i l I m a g e %
3210% %
3211% %
3212% %
3213%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3214%
3215% ThumbnailImage() changes the size of an image to the given dimensions and
3216% removes any associated profiles. The goal is to produce small low cost
3217% thumbnail images suited for display on the Web.
3218%
3219% The format of the ThumbnailImage method is:
3220%
cristybb503372010-05-27 20:51:26 +00003221% Image *ThumbnailImage(const Image *image,const size_t columns,
3222% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003223%
3224% A description of each parameter follows:
3225%
3226% o image: the image.
3227%
3228% o columns: the number of columns in the scaled image.
3229%
3230% o rows: the number of rows in the scaled image.
3231%
3232% o exception: return any errors or warnings in this structure.
3233%
3234*/
cristy9af9b5d2010-08-15 17:04:28 +00003235MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3236 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003237{
3238#define SampleFactor 5
3239
3240 char
3241 value[MaxTextExtent];
3242
3243 const char
3244 *name;
3245
3246 Image
3247 *thumbnail_image;
3248
3249 MagickRealType
3250 x_factor,
3251 y_factor;
3252
cristybb503372010-05-27 20:51:26 +00003253 size_t
cristy3ed852e2009-09-05 21:47:34 +00003254 version;
3255
cristy9af9b5d2010-08-15 17:04:28 +00003256 struct stat
3257 attributes;
3258
cristy3ed852e2009-09-05 21:47:34 +00003259 assert(image != (Image *) NULL);
3260 assert(image->signature == MagickSignature);
3261 if (image->debug != MagickFalse)
3262 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3263 assert(exception != (ExceptionInfo *) NULL);
3264 assert(exception->signature == MagickSignature);
3265 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3266 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3267 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003268 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3269 exception);
cristy3ed852e2009-09-05 21:47:34 +00003270 else
3271 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003272 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3273 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003274 else
3275 {
3276 Image
3277 *sample_image;
3278
3279 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3280 exception);
3281 if (sample_image == (Image *) NULL)
3282 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003283 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3284 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003285 sample_image=DestroyImage(sample_image);
3286 }
3287 if (thumbnail_image == (Image *) NULL)
3288 return(thumbnail_image);
3289 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3290 if (thumbnail_image->matte == MagickFalse)
3291 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3292 thumbnail_image->depth=8;
3293 thumbnail_image->interlace=NoInterlace;
3294 /*
3295 Strip all profiles except color profiles.
3296 */
3297 ResetImageProfileIterator(thumbnail_image);
3298 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3299 {
3300 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3301 {
cristy2b726bd2010-01-11 01:05:39 +00003302 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003303 ResetImageProfileIterator(thumbnail_image);
3304 }
3305 name=GetNextImageProfile(thumbnail_image);
3306 }
3307 (void) DeleteImageProperty(thumbnail_image,"comment");
3308 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003309 if (strstr(image->magick_filename,"//") == (char *) NULL)
3310 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003311 image->magick_filename);
3312 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3313 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3314 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3315 {
cristye8c25f92010-06-03 00:53:06 +00003316 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003317 attributes.st_mtime);
3318 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3319 }
cristye8c25f92010-06-03 00:53:06 +00003320 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003321 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003322 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003323 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003324 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3325 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3326 LocaleLower(value);
3327 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3328 (void) SetImageProperty(thumbnail_image,"software",
3329 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003330 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3331 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003332 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003333 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003334 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003335 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003336 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3337 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003338 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3339 return(thumbnail_image);
3340}