blob: d06c797f199d5ebf0c990055ea8030dad6526fee [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% RRRR EEEEE SSSSS IIIII ZZZZZ EEEEE %
7% R R E SS I ZZ E %
8% RRRR EEE SSS I ZZZ EEE %
9% R R E SS I ZZ E %
10% R R EEEEE SSSSS IIIII ZZZZZ EEEEE %
11% %
12% %
13% MagickCore Image Resize Methods %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
42#include "magick/studio.h"
43#include "magick/artifact.h"
44#include "magick/blob.h"
45#include "magick/cache.h"
46#include "magick/cache-view.h"
47#include "magick/color.h"
48#include "magick/color-private.h"
49#include "magick/draw.h"
50#include "magick/exception.h"
51#include "magick/exception-private.h"
52#include "magick/gem.h"
53#include "magick/image.h"
54#include "magick/image-private.h"
55#include "magick/list.h"
56#include "magick/memory_.h"
anthony55f12332010-09-10 01:13:02 +000057#include "magick/magick.h"
cristy3ed852e2009-09-05 21:47:34 +000058#include "magick/pixel-private.h"
59#include "magick/property.h"
60#include "magick/monitor.h"
61#include "magick/monitor-private.h"
62#include "magick/pixel.h"
63#include "magick/option.h"
64#include "magick/resample.h"
65#include "magick/resize.h"
66#include "magick/resize-private.h"
67#include "magick/string_.h"
cristyf2f27272009-12-17 14:48:46 +000068#include "magick/string-private.h"
cristy3ed852e2009-09-05 21:47:34 +000069#include "magick/thread-private.h"
70#include "magick/utility.h"
71#include "magick/version.h"
72#if defined(MAGICKCORE_LQR_DELEGATE)
73#include <lqr.h>
74#endif
75
76/*
77 Typedef declarations.
78*/
79struct _ResizeFilter
80{
81 MagickRealType
82 (*filter)(const MagickRealType,const ResizeFilter *),
83 (*window)(const MagickRealType,const ResizeFilter *),
cristy33b1c162010-01-23 22:51:51 +000084 support, /* filter region of support - the filter support limit */
85 window_support, /* window support, usally equal to support (expert only) */
anthony55f12332010-09-10 01:13:02 +000086 scale, /* dimension scaling to fit window support (usally 1.0) */
cristy33b1c162010-01-23 22:51:51 +000087 blur, /* x-scale (blur-sharpen) */
anthonyf5e76ef2010-10-12 01:22:01 +000088 coeff[8]; /* cubic coefficents for smooth Cubic filters */
cristy3ed852e2009-09-05 21:47:34 +000089
cristybb503372010-05-27 20:51:26 +000090 size_t
cristy3ed852e2009-09-05 21:47:34 +000091 signature;
92};
93
94/*
95 Forward declaractions.
96*/
97static MagickRealType
98 I0(MagickRealType x),
anthonyb6d08c52010-09-13 01:17:04 +000099 BesselOrderOne(MagickRealType),
anthony07a3f7f2010-09-16 03:03:11 +0000100 Sinc(const MagickRealType, const ResizeFilter *),
anthonyba5a7c32010-09-15 02:42:25 +0000101 SincFast(const MagickRealType, const ResizeFilter *);
cristy3ed852e2009-09-05 21:47:34 +0000102
103/*
104%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
105% %
106% %
107% %
108+ F i l t e r F u n c t i o n s %
109% %
110% %
111% %
112%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
113%
cristy33b1c162010-01-23 22:51:51 +0000114% These are the various filter and windowing functions that are provided.
cristy3ed852e2009-09-05 21:47:34 +0000115%
cristy33b1c162010-01-23 22:51:51 +0000116% They are internal to this module only. See AcquireResizeFilterInfo() for
117% details of the access to these functions, via the GetResizeFilterSupport()
118% and GetResizeFilterWeight() API interface.
cristy3ed852e2009-09-05 21:47:34 +0000119%
120% The individual filter functions have this format...
121%
122% static MagickRealtype *FilterName(const MagickRealType x,
123% const MagickRealType support)
124%
cristy33b1c162010-01-23 22:51:51 +0000125% A description of each parameter follows:
cristy3ed852e2009-09-05 21:47:34 +0000126%
cristy33b1c162010-01-23 22:51:51 +0000127% o x: the distance from the sampling point generally in the range of 0 to
128% support. The GetResizeFilterWeight() ensures this a positive value.
129%
130% o resize_filter: current filter information. This allows function to
131% access support, and possibly other pre-calculated information defining
132% the functions.
cristy3ed852e2009-09-05 21:47:34 +0000133%
134*/
135
cristyc5c6f662010-09-22 14:23:02 +0000136#define MagickPIL ((MagickRealType) 3.14159265358979323846264338327950288420L)
cristy560d8182010-09-08 22:36:25 +0000137
anthony48f77622010-10-03 14:32:31 +0000138static MagickRealType Jinc(const MagickRealType x,
cristy3ed852e2009-09-05 21:47:34 +0000139 const ResizeFilter *magick_unused(resize_filter))
140{
141 /*
anthony48f77622010-10-03 14:32:31 +0000142 See Pratt "Digital Image Processing" p.97 for Jinc/Bessel functions.
cristy33b1c162010-01-23 22:51:51 +0000143 http://mathworld.wolfram.com/JincFunction.html and page 11 of
anthony48f77622010-10-03 14:32:31 +0000144 http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
145
nicolase473f722010-10-07 00:05:13 +0000146 The original "zoom" program by Paul Heckbert called this "Bessel".
147 But really it is more accurately named "Jinc".
cristy3ed852e2009-09-05 21:47:34 +0000148 */
149 if (x == 0.0)
nicolas5a36f342010-10-07 00:11:32 +0000150 return(0.5*MagickPIL);
151 return(BesselOrderOne(MagickPIL*x)/x);
cristy3ed852e2009-09-05 21:47:34 +0000152}
153
154static MagickRealType Blackman(const MagickRealType x,
cristyce70c172010-01-07 17:15:30 +0000155 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000156{
157 /*
cristy83017922010-09-05 20:45:15 +0000158 Blackman: 2nd order cosine windowing function:
cristy21ce88a2010-09-05 01:37:25 +0000159 0.42 + 0.5 cos(pi x) + 0.08 cos(2pi x)
cristy560d8182010-09-08 22:36:25 +0000160 Refactored by Chantal Racette and Nicolas Robidoux to one trig
161 call and five flops.
cristy3ed852e2009-09-05 21:47:34 +0000162 */
cristyc5c6f662010-09-22 14:23:02 +0000163 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000164 return(0.34+cospix*(0.5+cospix*0.16));
cristy3ed852e2009-09-05 21:47:34 +0000165}
166
167static MagickRealType Bohman(const MagickRealType x,
cristyce70c172010-01-07 17:15:30 +0000168 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000169{
170 /*
cristy560d8182010-09-08 22:36:25 +0000171 Bohman: 2rd Order cosine windowing function:
172 (1-x) cos(pi x) + sin(pi x) / pi.
nicolasa4f7b4a2010-09-20 20:28:38 +0000173 Refactored by Nicolas Robidoux to one trig call, one sqrt call,
174 and 7 flops, taking advantage of the fact that the support of
175 Bohman is 1 (so that we know that sin(pi x) >= 0).
cristy3ed852e2009-09-05 21:47:34 +0000176 */
cristyc5c6f662010-09-22 14:23:02 +0000177 const double cospix = cos((double) (MagickPIL*x));
nicolasa4f7b4a2010-09-20 20:28:38 +0000178 const double sinpix = sqrt(1.0-cospix*cospix);
nicolas2ffd3b22010-09-24 20:27:31 +0000179 return((1.0-x)*cospix+(1.0/MagickPIL)*sinpix);
cristy3ed852e2009-09-05 21:47:34 +0000180}
181
anthony463be1d2010-09-26 01:07:36 +0000182static MagickRealType Box(const MagickRealType magick_unused(x),
183 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000184{
185 /*
nicolas40477452010-09-27 23:42:08 +0000186 A Box filter is a equal weighting function (all weights equal).
anthonyc331dec2010-09-26 01:30:14 +0000187 DO NOT LIMIT results by support or resize point sampling will work
188 as it requests points beyond its normal 0.0 support size.
cristy3ed852e2009-09-05 21:47:34 +0000189 */
anthony463be1d2010-09-26 01:07:36 +0000190 return(1.0);
cristy3ed852e2009-09-05 21:47:34 +0000191}
192
193static MagickRealType CubicBC(const MagickRealType x,
194 const ResizeFilter *resize_filter)
195{
196 /*
197 Cubic Filters using B,C determined values:
cristyf59a8922010-02-28 19:51:23 +0000198 Mitchell-Netravali B=1/3 C=1/3 Qualitively ideal Cubic Filter
199 Catmull-Rom B= 0 C=1/2 Cublic Interpolation Function
200 Cubic B-Spline B= 1 C= 0 Spline Approximation of Gaussian
201 Hermite B= 0 C= 0 Quadratic Spline (support = 1)
cristy3ed852e2009-09-05 21:47:34 +0000202
cristy33b1c162010-01-23 22:51:51 +0000203 See paper by Mitchell and Netravali, Reconstruction Filters in Computer
204 Graphics Computer Graphics, Volume 22, Number 4, August 1988
205 http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/
cristyf59a8922010-02-28 19:51:23 +0000206 Mitchell.pdf.
cristy3ed852e2009-09-05 21:47:34 +0000207
cristy33b1c162010-01-23 22:51:51 +0000208 Coefficents are determined from B,C values:
cristy3ed852e2009-09-05 21:47:34 +0000209 P0 = ( 6 - 2*B )/6
210 P1 = 0
211 P2 = (-18 +12*B + 6*C )/6
212 P3 = ( 12 - 9*B - 6*C )/6
213 Q0 = ( 8*B +24*C )/6
214 Q1 = ( -12*B -48*C )/6
215 Q2 = ( 6*B +30*C )/6
216 Q3 = ( - 1*B - 6*C )/6
217
cristy33b1c162010-01-23 22:51:51 +0000218 which are used to define the filter:
cristyf59a8922010-02-28 19:51:23 +0000219
cristy3ed852e2009-09-05 21:47:34 +0000220 P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
221 Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x <= 2
222
cristy33b1c162010-01-23 22:51:51 +0000223 which ensures function is continuous in value and derivative (slope).
cristy3ed852e2009-09-05 21:47:34 +0000224 */
225 if (x < 1.0)
anthonyf5e76ef2010-10-12 01:22:01 +0000226 return(resize_filter->coeff[0]+x*(resize_filter->coeff[1]+x*
227 (resize_filter->coeff[2]+x*resize_filter->coeff[3])));
cristy3ed852e2009-09-05 21:47:34 +0000228 if (x < 2.0)
anthonyf5e76ef2010-10-12 01:22:01 +0000229 return(resize_filter->coeff[4]+x*(resize_filter->coeff[5]+x*
230 (resize_filter->coeff[6]+x*resize_filter->coeff[7])));
cristy3ed852e2009-09-05 21:47:34 +0000231 return(0.0);
232}
233
234static MagickRealType Gaussian(const MagickRealType x,
anthonyf5e76ef2010-10-12 01:22:01 +0000235 const ResizeFilter *resize_filter)
cristy3ed852e2009-09-05 21:47:34 +0000236{
cristy560d8182010-09-08 22:36:25 +0000237 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000238 Gaussian with a fixed sigma = 1/2
239
240 Gaussian Formula...
241 exp( -(x^2)/((2.0*sigma^2) ) / sqrt(2*PI*sigma^2)))
242 The constants are pre-calculated...
243 exp( -coeff[0]*(x^2)) ) * coeff[1]
244 However the multiplier coefficent is not needed and not used.
245
246 This separates the gaussian 'sigma' value from the 'blur/support' settings
247 allows for its use in special 'small sigma' gaussians, without the filter
248 'missing' pixels when blur and thus support becomes too small.
cristy560d8182010-09-08 22:36:25 +0000249 */
anthonyf5e76ef2010-10-12 01:22:01 +0000250 return(exp((double)(-resize_filter->coeff[0]*x*x))); }
cristy3ed852e2009-09-05 21:47:34 +0000251
252static MagickRealType Hanning(const MagickRealType x,
253 const ResizeFilter *magick_unused(resize_filter))
254{
255 /*
nicolas40477452010-09-27 23:42:08 +0000256 Cosine window function:
257 .5+.5cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000258 */
cristyc5c6f662010-09-22 14:23:02 +0000259 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000260 return(0.5+0.5*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000261}
262
263static MagickRealType Hamming(const MagickRealType x,
264 const ResizeFilter *magick_unused(resize_filter))
265{
266 /*
nicolas40477452010-09-27 23:42:08 +0000267 Offset cosine window function:
268 .54 + .46 cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000269 */
cristyc5c6f662010-09-22 14:23:02 +0000270 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000271 return(0.54+0.46*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000272}
273
274static MagickRealType Kaiser(const MagickRealType x,
275 const ResizeFilter *magick_unused(resize_filter))
276{
277#define Alpha 6.5
278#define I0A (1.0/I0(Alpha))
279
280 /*
nicolas07bac812010-09-19 18:47:02 +0000281 Kaiser Windowing Function (bessel windowing): Alpha is a free
282 value from 5 to 8 (currently hardcoded to 6.5).
283 Future: make alpha the IOA pre-calculation, an 'expert' setting.
cristy3ed852e2009-09-05 21:47:34 +0000284 */
285 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
286}
287
288static MagickRealType Lagrange(const MagickRealType x,
289 const ResizeFilter *resize_filter)
290{
cristy3ed852e2009-09-05 21:47:34 +0000291 MagickRealType
292 value;
293
cristybb503372010-05-27 20:51:26 +0000294 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000295 i;
296
cristy9af9b5d2010-08-15 17:04:28 +0000297 ssize_t
298 n,
299 order;
300
cristy3ed852e2009-09-05 21:47:34 +0000301 /*
nicolas07bac812010-09-19 18:47:02 +0000302 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the
303 lagrange function and depends on the overall support window size
304 of the filter. That is: for a support of 2, it gives a lagrange-4
305 (piecewise cubic function).
cristy3ed852e2009-09-05 21:47:34 +0000306
nicolas07bac812010-09-19 18:47:02 +0000307 "n" identifies the piece of the piecewise polynomial.
cristy3ed852e2009-09-05 21:47:34 +0000308
nicolas07bac812010-09-19 18:47:02 +0000309 See Survey: Interpolation Methods, IEEE Transactions on Medical
310 Imaging, Vol 18, No 11, November 1999, p1049-1075, -- Equation 27
311 on p1064.
cristy3ed852e2009-09-05 21:47:34 +0000312 */
313 if (x > resize_filter->support)
314 return(0.0);
cristybb503372010-05-27 20:51:26 +0000315 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
anthonyc2d07db2010-09-15 23:47:40 +0000316 /*n=(ssize_t)((1.0*order)/2.0+x); -- which piece does x belong to */
317 n = (ssize_t)(resize_filter->window_support + x);
cristy3ed852e2009-09-05 21:47:34 +0000318 value=1.0f;
319 for (i=0; i < order; i++)
320 if (i != n)
321 value*=(n-i-x)/(n-i);
322 return(value);
323}
324
325static MagickRealType Quadratic(const MagickRealType x,
326 const ResizeFilter *magick_unused(resize_filter))
327{
328 /*
329 2rd order (quadratic) B-Spline approximation of Gaussian.
330 */
331 if (x < 0.5)
332 return(0.75-x*x);
333 if (x < 1.5)
334 return(0.5*(x-1.5)*(x-1.5));
335 return(0.0);
336}
337
anthony07a3f7f2010-09-16 03:03:11 +0000338static MagickRealType Sinc(const MagickRealType x,
cristy3ed852e2009-09-05 21:47:34 +0000339 const ResizeFilter *magick_unused(resize_filter))
340{
anthony720660f2010-09-07 10:05:14 +0000341 /*
nicolas40477452010-09-27 23:42:08 +0000342 Scaled sinc(x) function using a trig call:
nicolas07bac812010-09-19 18:47:02 +0000343 sinc(x) == sin(pi x)/(pi x).
anthony720660f2010-09-07 10:05:14 +0000344 */
anthony2d9b8b52010-09-14 08:31:07 +0000345 if (x != 0.0)
cristy560d8182010-09-08 22:36:25 +0000346 {
cristyc5c6f662010-09-22 14:23:02 +0000347 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
nicolas07bac812010-09-19 18:47:02 +0000348 return(sin((double) pix)/pix);
cristy560d8182010-09-08 22:36:25 +0000349 }
nicolas2ffd3b22010-09-24 20:27:31 +0000350 return((MagickRealType) 1.0);
anthony720660f2010-09-07 10:05:14 +0000351}
352
anthonyba5a7c32010-09-15 02:42:25 +0000353static MagickRealType SincFast(const MagickRealType x,
anthony720660f2010-09-07 10:05:14 +0000354 const ResizeFilter *magick_unused(resize_filter))
355{
cristy560d8182010-09-08 22:36:25 +0000356 /*
357 Approximations of the sinc function sin(pi x)/(pi x) over the
358 interval [-4,4] constructed by Nicolas Robidoux and Chantal
359 Racette with funding from the Natural Sciences and Engineering
360 Research Council of Canada.
nicolas07bac812010-09-19 18:47:02 +0000361
362 Although the approximations are polynomials (for low order of
363 approximation) and quotients of polynomials (for higher order of
364 approximation) and consequently are similar in form to Taylor
365 polynomials/Pade approximants, the approximations are computed
366 with a completely different technique.
367
368 Summary: These approximations are "the best" in terms of bang
369 (accuracy) for the buck (flops). More specifically: Among the
370 polynomial quotients that can be computed using a fixed number of
371 flops (with a given "+ - * / budget"), the chosen polynomial
372 quotient is the one closest to the approximated function with
373 respect to maximum absolute relative error over the given
374 interval.
375
376 The Remez algorithm, as implemented in the boost library's minimax
nicolas3aab40c2010-09-19 21:14:15 +0000377 package, is the key to the construction:
nicolas07bac812010-09-19 18:47:02 +0000378 http://www.boost.org/doc/libs/1_36_0/libs/math/doc/...
379 ...sf_and_dist/html/math_toolkit/backgrounders/remez.html
cristy560d8182010-09-08 22:36:25 +0000380 */
nicolas3aab40c2010-09-19 21:14:15 +0000381 /*
382 If outside of the interval of approximation, use the standard trig
383 formula.
384 */
anthony2d9b8b52010-09-14 08:31:07 +0000385 if (x > 4.0)
cristy03dbbd22010-09-19 23:04:47 +0000386 {
cristyc5c6f662010-09-22 14:23:02 +0000387 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
cristy03dbbd22010-09-19 23:04:47 +0000388 return(sin((double) pix)/pix);
389 }
anthony2d9b8b52010-09-14 08:31:07 +0000390 {
nicolas07bac812010-09-19 18:47:02 +0000391 /*
392 The approximations only depend on x^2 (sinc is an even
393 function).
394 */
395 const MagickRealType xx = x*x;
cristy83017922010-09-05 20:45:15 +0000396#if MAGICKCORE_QUANTUM_DEPTH <= 8
397 /*
anthony2d9b8b52010-09-14 08:31:07 +0000398 Maximum absolute relative error 6.3e-6 < 1/2^17.
cristy738e7562010-09-01 12:48:07 +0000399 */
400 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
401 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
402 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
403 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
404 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
405 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
406 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
407 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
nicolas3aab40c2010-09-19 21:14:15 +0000408 const MagickRealType p =
409 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
nicolas07bac812010-09-19 18:47:02 +0000410 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
anthony2d9b8b52010-09-14 08:31:07 +0000411#elif MAGICKCORE_QUANTUM_DEPTH <= 16
cristydbeb3eb2010-09-09 13:41:36 +0000412 /*
anthony2d9b8b52010-09-14 08:31:07 +0000413 Max. abs. rel. error 2.2e-8 < 1/2^25.
cristydbeb3eb2010-09-09 13:41:36 +0000414 */
415 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
416 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
nicolas3aab40c2010-09-19 21:14:15 +0000417 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
418 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
419 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
420 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
421 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
cristydbeb3eb2010-09-09 13:41:36 +0000422 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
423 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
424 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
anthony853d6972010-10-08 06:01:31 +0000425 const MagickRealType p =
nicolas3aab40c2010-09-19 21:14:15 +0000426 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
nicolas07bac812010-09-19 18:47:02 +0000427 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
nicolasc2d28f02010-09-27 18:56:15 +0000428#else
nicolas3aab40c2010-09-19 21:14:15 +0000429 /*
430 Max. abs. rel. error 1.2e-12 < 1/2^39.
431 */
432 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
433 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
434 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
435 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
436 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
437 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
438 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
439 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
440 const MagickRealType p =
441 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
442 const MagickRealType d0 = 1.0L;
443 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
444 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
445 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
446 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
447 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
448 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
cristy657c6352010-08-29 14:05:08 +0000449#endif
cristy83017922010-09-05 20:45:15 +0000450 }
cristy3ed852e2009-09-05 21:47:34 +0000451}
452
453static MagickRealType Triangle(const MagickRealType x,
454 const ResizeFilter *magick_unused(resize_filter))
455{
456 /*
nicolas0edb0862010-09-19 18:56:19 +0000457 1st order (linear) B-Spline, bilinear interpolation, Tent 1D
458 filter, or a Bartlett 2D Cone filter.
cristy3ed852e2009-09-05 21:47:34 +0000459 */
460 if (x < 1.0)
461 return(1.0-x);
462 return(0.0);
463}
464
465static MagickRealType Welsh(const MagickRealType x,
466 const ResizeFilter *magick_unused(resize_filter))
467{
468 /*
469 Welsh parabolic windowing filter.
470 */
cristy560d8182010-09-08 22:36:25 +0000471 if (x < 1.0)
cristy3ed852e2009-09-05 21:47:34 +0000472 return(1.0-x*x);
473 return(0.0);
474}
475
476/*
477%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
478% %
479% %
480% %
481+ A c q u i r e R e s i z e F i l t e r %
482% %
483% %
484% %
485%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
486%
nicolas07bac812010-09-19 18:47:02 +0000487% AcquireResizeFilter() allocates the ResizeFilter structure. Choose
488% from these filters:
cristy3ed852e2009-09-05 21:47:34 +0000489%
490% FIR (Finite impulse Response) Filters
491% Box Triangle Quadratic
492% Cubic Hermite Catrom
493% Mitchell
494%
495% IIR (Infinite impulse Response) Filters
anthony48f77622010-10-03 14:32:31 +0000496% Gaussian Sinc Jinc (Bessel)
cristy3ed852e2009-09-05 21:47:34 +0000497%
anthony48f77622010-10-03 14:32:31 +0000498% Windowed Sinc/Jinc Filters
cristy3ed852e2009-09-05 21:47:34 +0000499% Blackman Hanning Hamming
anthony48f77622010-10-03 14:32:31 +0000500% Kaiser Lanczos
cristy3ed852e2009-09-05 21:47:34 +0000501%
anthony61b5ddd2010-10-05 02:33:31 +0000502% Special purpose Filters
anthony853d6972010-10-08 06:01:31 +0000503% SincFast Lanczos2D Robidoux
anthony61b5ddd2010-10-05 02:33:31 +0000504%
anthony48f77622010-10-03 14:32:31 +0000505% The users "-filter" selection is used to lookup the default 'expert'
506% settings for that filter from a internal table. However any provided
507% 'expert' settings (see below) may override this selection.
508%
509% FIR filters are used as is, and are limited to that filters support
nicolas07bac812010-09-19 18:47:02 +0000510% window (unless over-ridden). 'Gaussian' while classed as an IIR
anthonyc331dec2010-09-26 01:30:14 +0000511% filter, is also simply clipped by its support size (currently 1.5
anthonyf5e76ef2010-10-12 01:22:01 +0000512% or approximatally 3*sigma as recommended by many references)
cristy3ed852e2009-09-05 21:47:34 +0000513%
anthony48f77622010-10-03 14:32:31 +0000514% The selection is typically either a windowed Sinc, or interpolated
nicolas07bac812010-09-19 18:47:02 +0000515% filter, for use by functions such as ResizeImage(). However if a
anthony48f77622010-10-03 14:32:31 +0000516% 'cylindrical' filter flag is requested, any default Sinc weighting
517% and windowing functions will be promoted to cylindrical Jinc form of
518% function.
cristy3ed852e2009-09-05 21:47:34 +0000519%
anthony48f77622010-10-03 14:32:31 +0000520% Directly requesting 'Sinc' or 'Jinc' will force the use of that
521% filter function without any windowing. This is not recommended,
522% except by image processing experts or in expert options. Selecting a
523% window filtering version of these functions is better.
cristy3ed852e2009-09-05 21:47:34 +0000524%
anthony48f77622010-10-03 14:32:31 +0000525% Lanczos is a special case of a Sinc-windowed Sinc, (or Jinc-Jinc for
526% the cylindrical case) but defaulting to 3-lobe support, rather that
527% the default 4 lobe support of the other windowed sinc/jinc filters.
cristy3ed852e2009-09-05 21:47:34 +0000528%
anthony48f77622010-10-03 14:32:31 +0000529% Two forms of the 'Sinc' function are available: Sinc and SincFast.
nicolas07bac812010-09-19 18:47:02 +0000530% Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
531% selected if the user specifically specifies the use of a Sinc
532% filter. SincFast uses highly accurate (and fast) polynomial (low Q)
anthony48f77622010-10-03 14:32:31 +0000533% and rational (high Q) approximations, and will be used by default in
534% most cases.
anthonyba5a7c32010-09-15 02:42:25 +0000535%
nicolas1eb2fcf2010-10-10 20:45:17 +0000536% The Lanczos2D and Robidoux filters are tuned for cylindrical
537% (radial) EWA (Elliptical Weighted Average) distortion. Lanczos2D
nicolasdff19b42010-10-10 20:53:13 +0000538% is a 2 lobe Lanczos-like filter using Jinc (for EWA) or Sinc.
nicolas6ca6e962010-10-10 20:49:01 +0000539% Robidoux used to be a sharpened version of Lanczos2D (with
nicolasdff19b42010-10-10 20:53:13 +0000540% blur=0.958033808). Now, it is the unique Cubic 'Keys' filter that
541% exactly preserves images with only vertical or horizontal features
542% when performing 'no-op" with EWA distortion. It turns out to be
nicolas219dd842010-10-10 21:02:40 +0000543% close to both plain Mitchell and "sharpened" Lanczos2D.
anthony61b5ddd2010-10-05 02:33:31 +0000544%
nicolas07bac812010-09-19 18:47:02 +0000545% Special 'expert' options can be used to override any and all filter
546% settings. This is not advised unless you have expert knowledge of
547% the use of resampling filtered techniques. Check on the results of
548% your selections using the "filter:verbose" setting to make sure you
anthony61b5ddd2010-10-05 02:33:31 +0000549% get the exact filter that you are tring to achieve.
cristy3ed852e2009-09-05 21:47:34 +0000550%
anthony48f77622010-10-03 14:32:31 +0000551% "filter:filter" Select the main function associated with
552% this filter name, as the weighting function of the filter.
553% This can be used to set a windowing function as a weighting
554% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000555%
anthony7bdc0ed2010-09-15 01:52:32 +0000556% If a "filter:window" operation has not been provided, then a 'Box'
557% windowing function will be set to denote that no windowing function
558% is being used.
cristy3ed852e2009-09-05 21:47:34 +0000559%
560% "filter:window" Select this windowing function for the filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000561% While any filter could be used as a windowing function, using the
562% 'first lobe' of that filter over the whole support window, using a
anthony48f77622010-10-03 14:32:31 +0000563% non-windowing function is not advisible. If no weighting filter
564% function is specifed a 'SincFast' filter will be used.
cristy3ed852e2009-09-05 21:47:34 +0000565%
anthony48f77622010-10-03 14:32:31 +0000566% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000567% This a simpler method of setting filter support size that will
anthony48f77622010-10-03 14:32:31 +0000568% correctly handle the Sinc/Jinc switch for an operators filtering
anthony7bdc0ed2010-09-15 01:52:32 +0000569% requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000570%
571% "filter:support" Set the support size for filtering to the size given
anthony48f77622010-10-03 14:32:31 +0000572% This not recommended for Sinc/Jinc windowed filters (lobes should
anthony7bdc0ed2010-09-15 01:52:32 +0000573% be used instead). This will override any 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000574%
anthonyb6d08c52010-09-13 01:17:04 +0000575% "filter:win-support" Scale windowing function to this size instead.
576% This causes the windowing (or self-windowing Lagrange filter) to act
577% is if the support window it much much larger than what is actually
578% supplied to the calling operator. The filter however is still
579% clipped to the real support size given, by the support range suppiled
580% to the caller. If unset this will equal the normal filter support
581% size.
582%
cristy3ed852e2009-09-05 21:47:34 +0000583% "filter:blur" Scale the filter and support window by this amount.
584% A value >1 will generally result in a more burred image with
585% more ringing effects, while a value <1 will sharpen the
586% resulting image with more aliasing and Morie effects.
587%
anthonyf5e76ef2010-10-12 01:22:01 +0000588% "filter:sigma" The sigma value to use for the Gaussian filter only.
589% Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for cylindrical
590% usage. It effectially provides a alturnative to 'blur' for Gaussians
591% without it also effecting the final 'practical support' size.
592%
cristy3ed852e2009-09-05 21:47:34 +0000593% "filter:b"
594% "filter:c" Override the preset B,C values for a Cubic type of filter
595% If only one of these are given it is assumes to be a 'Keys'
596% type of filter such that B+2C=1, where Keys 'alpha' value = C
597%
anthonyb6d08c52010-09-13 01:17:04 +0000598% "filter:verbose" Output the exact results of the filter selections
599% made, as well as plotting data for graphing the resulting filter
600% over support range (blur adjusted).
cristy3ed852e2009-09-05 21:47:34 +0000601%
602% Set a true un-windowed Sinc filter with 10 lobes (very slow)
anthony48f77622010-10-03 14:32:31 +0000603% -define filter:filter=Sinc
604% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000605%
anthony48f77622010-10-03 14:32:31 +0000606% For example force an 8 lobe Lanczos (Sinc or Jinc) filter...
cristy3ed852e2009-09-05 21:47:34 +0000607% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000608% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000609%
cristy3ed852e2009-09-05 21:47:34 +0000610% The format of the AcquireResizeFilter method is:
611%
612% ResizeFilter *AcquireResizeFilter(const Image *image,
613% const FilterTypes filter_type, const MagickBooleanType radial,
614% ExceptionInfo *exception)
615%
cristy33b1c162010-01-23 22:51:51 +0000616% A description of each parameter follows:
617%
cristy3ed852e2009-09-05 21:47:34 +0000618% o image: the image.
619%
nicolas07bac812010-09-19 18:47:02 +0000620% o filter: the filter type, defining a preset filter, window and
621% support. The artifact settings listed above will override
622% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000623%
anthony48f77622010-10-03 14:32:31 +0000624% o blur: blur the filter by this amount, use 1.0 if unknown. Image
625% artifact "filter:blur" will override this API call usage, including
626% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000627%
anthony48f77622010-10-03 14:32:31 +0000628% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
629% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000630%
631% o exception: return any errors or warnings in this structure.
632%
633*/
634MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000635 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000636 const MagickBooleanType cylindrical,ExceptionInfo *exception)
637{
638 const char
639 *artifact;
640
641 FilterTypes
642 filter_type,
643 window_type;
644
cristy3ed852e2009-09-05 21:47:34 +0000645 MagickRealType
646 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000647 C,
648 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000649
650 register ResizeFilter
651 *resize_filter;
652
cristy9af9b5d2010-08-15 17:04:28 +0000653 ssize_t
654 option;
655
cristy3ed852e2009-09-05 21:47:34 +0000656 /*
anthony48f77622010-10-03 14:32:31 +0000657 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000658 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000659 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
660 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
661 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000662
nicolas07bac812010-09-19 18:47:02 +0000663 WARNING: The order of this tabel must match the order of the
664 FilterTypes enumeration specified in "resample.h", or the filter
665 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000666
667 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000668 */
669 static struct
670 {
671 FilterTypes
672 filter,
673 window;
674 } const mapping[SentinelFilter] =
675 {
anthony462ee072010-09-27 12:34:02 +0000676 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
677 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
678 { BoxFilter, BoxFilter }, /* Box averaging filter */
679 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
680 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
681 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
682 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
683 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
684 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
685 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
686 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
687 { CatromFilter, BoxFilter }, /* Cubic interpolator */
688 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
689 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
anthony48f77622010-10-03 14:32:31 +0000690 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
691 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony462ee072010-09-27 12:34:02 +0000692 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
693 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
694 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
695 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
696 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
697 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
698 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolas5835ee02010-10-10 20:40:15 +0000699 { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
anthonye5f06452010-10-12 05:48:17 +0000700 { Lanczos2DSharpFilter, JincFilter },/* SPECIAL: ditto sharpened */
701 { RobidouxFilter, BoxFilter }, /* SPECIAL: Keys cubic tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000702 };
703 /*
nicolas32f44eb2010-09-20 01:23:12 +0000704 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000705 function. The default support size for that filter as a weighting
706 function, the range to scale with to use that function as a sinc
707 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000708
anthony07a3f7f2010-09-16 03:03:11 +0000709 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000710 SincFast(), and CubicBC() functions, which may have multiple
711 filter to function associations.
712
713 See "filter:verbose" handling below for the function -> filter
714 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000715 */
716 static struct
717 {
718 MagickRealType
719 (*function)(const MagickRealType, const ResizeFilter*),
nicolas8eccc162010-10-16 19:48:13 +0000720 lobes, /* Default lobes/support size of the weighting filter. */
721 scale, /* Support when used as a windowing function, equal to
722 the scaling needed to match the support of the
723 windowed function. Typically equal to the location of
724 the first crossing. */
725 B,C; /* BC-spline coefficients, ignored if not a CubicBC
726 filter. */
cristy3ed852e2009-09-05 21:47:34 +0000727 } const filters[SentinelFilter] =
728 {
anthony61b5ddd2010-10-05 02:33:31 +0000729 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
730 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
731 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
732 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
733 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
734 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
735 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
736 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000737 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000738 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
739 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
740 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
anthony853d6972010-10-08 06:01:31 +0000741 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
742 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
nicolasf8689f42010-10-18 16:14:08 +0000743 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000744 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
745 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
746 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
747 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
748 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
749 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
750 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
751 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
nicolas8eccc162010-10-16 19:48:13 +0000752 { Jinc, 2.0, 1.2196698912665045, 0.0, 0.0 },
753 /* Lanczos2D (Jinc-Jinc) */
754 { Jinc, 2.0, 1.1684849904329952, 0.0, 0.0 },
755 /* Lanczos2D sharpened with blur=0.958033808 */
nicolas198004e2010-10-16 19:19:35 +0000756 { CubicBC, 2.0, 1.1685777620836932, 0.37821575509399867,
nicolas8eccc162010-10-16 19:48:13 +0000757 0.31089212245300067 }
758 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
cristy3ed852e2009-09-05 21:47:34 +0000759 };
760 /*
anthony9a98fc62010-10-11 02:47:19 +0000761 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
762 function being used as a filter. It is used by the "filter:lobes" and for
763 the 'lobes' number in the above, the for support selection, so users do
764 not have to deal with the highly irrational sizes of the 'lobes' of the
765 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000766
nicolase473f722010-10-07 00:05:13 +0000767 Values taken from
anthony48f77622010-10-03 14:32:31 +0000768 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000769 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000770 */
771 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000772 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000773 {
nicolas8eccc162010-10-16 19:48:13 +0000774 1.2196698912665045,
775 2.2331305943815286,
776 3.2383154841662362,
777 4.2410628637960699,
778 5.2427643768701817,
779 6.2439216898644877,
780 7.244759868719957,
781 8.2453949139520427,
782 9.2458926849494673,
783 10.246293348754916,
784 11.246622794877883,
785 12.246898461138105,
786 13.247132522181061,
787 14.247333735806849,
anthonyc2d07db2010-09-15 23:47:40 +0000788 15.2475085630373,
nicolas8eccc162010-10-16 19:48:13 +0000789 16.247661874700962
cristy3ed852e2009-09-05 21:47:34 +0000790 };
791
cristy33b1c162010-01-23 22:51:51 +0000792 /*
793 Allocate resize filter.
794 */
cristy3ed852e2009-09-05 21:47:34 +0000795 assert(image != (const Image *) NULL);
796 assert(image->signature == MagickSignature);
797 if (image->debug != MagickFalse)
798 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
799 assert(UndefinedFilter < filter && filter < SentinelFilter);
800 assert(exception != (ExceptionInfo *) NULL);
801 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000802 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000803 if (resize_filter == (ResizeFilter *) NULL)
804 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000805 /*
806 Defaults for the requested filter.
807 */
808 filter_type=mapping[filter].filter;
809 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000810 resize_filter->blur = blur;
811 sigma = 0.5;
anthony48f77622010-10-03 14:32:31 +0000812 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000813 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000814 switch (filter_type)
815 {
816 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000817 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000818 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000819 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000820 break;
anthonyba5a7c32010-09-15 02:42:25 +0000821 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000822 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000823 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000824 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000825 break;
cristy33b1c162010-01-23 22:51:51 +0000826 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000827 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000828 filter_type=JincFilter;
829 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000830 break;
anthony08958462010-10-12 06:48:35 +0000831 case Lanczos2DSharpFilter:
832 /* Sharpened by Nicholas Robidoux so as to optimize for
833 * minimal blurring of orthogonal lines
834 */
835 resize_filter->blur *= 0.958033808;
836 break;
anthonyf5e76ef2010-10-12 01:22:01 +0000837 case GaussianFilter:
cristy68f103e2010-10-14 01:19:07 +0000838 sigma = (MagickRealType) (MagickSQ2/2.0); /* Cylindrical Gaussian sigma is sqrt(2)/2 */
anthonyf5e76ef2010-10-12 01:22:01 +0000839 break;
cristya782ecf2010-01-25 02:59:14 +0000840 default:
841 break;
cristy3ed852e2009-09-05 21:47:34 +0000842 }
anthony61b5ddd2010-10-05 02:33:31 +0000843 else
844 switch (filter_type)
845 {
846 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000847 case Lanczos2DSharpFilter:
nicolas45b58a92010-10-07 15:46:39 +0000848 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000849 window_type=SincFastFilter;
850 break;
851 default:
852 break;
853 }
854
cristy3ed852e2009-09-05 21:47:34 +0000855 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000856 if (artifact != (const char *) NULL)
857 {
cristy9af9b5d2010-08-15 17:04:28 +0000858 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000859 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000860 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000861 filter_type=(FilterTypes) option;
862 window_type=BoxFilter;
863 }
864 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000865 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000866 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000867 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000868 }
nicolas07bac812010-09-19 18:47:02 +0000869 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000870 artifact=GetImageArtifact(image,"filter:window");
871 if (artifact != (const char *) NULL)
872 {
cristy9af9b5d2010-08-15 17:04:28 +0000873 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000874 if ((UndefinedFilter < option) && (option < SentinelFilter))
875 {
876 if (option != LanczosFilter)
877 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000878 else
anthony48f77622010-10-03 14:32:31 +0000879 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000880 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000881 }
cristy33b1c162010-01-23 22:51:51 +0000882 }
cristy3ed852e2009-09-05 21:47:34 +0000883 }
cristy33b1c162010-01-23 22:51:51 +0000884 else
885 {
anthony48f77622010-10-03 14:32:31 +0000886 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000887 artifact=GetImageArtifact(image,"filter:window");
888 if (artifact != (const char *) NULL)
889 {
890 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
891 artifact);
892 if ((UndefinedFilter < option) && (option < SentinelFilter))
893 {
anthony61b5ddd2010-10-05 02:33:31 +0000894 filter_type=cylindrical != MagickFalse ?
895 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000896 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000897 }
898 }
899 }
nicolas07bac812010-09-19 18:47:02 +0000900 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000901 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000902 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000903 resize_filter->window=filters[window_type].function;
904 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000905 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000906
anthonyf5e76ef2010-10-12 01:22:01 +0000907 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000908 if (cylindrical != MagickFalse)
909 switch (filter_type)
910 {
911 case PointFilter:
912 case BoxFilter:
913 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000914 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000915 break;
anthony81b8bf92010-10-02 13:54:34 +0000916 default:
917 break;
anthony10b8bc82010-10-02 12:48:46 +0000918 }
anthony61b5ddd2010-10-05 02:33:31 +0000919 else
920 switch (filter_type)
921 {
922 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000923 case Lanczos2DSharpFilter:
anthony853d6972010-10-08 06:01:31 +0000924 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000925 resize_filter->filter=SincFast;
926 break;
927 default:
928 break;
929 }
930
anthonyf5e76ef2010-10-12 01:22:01 +0000931 /*
932 ** More Expert Option Modifications
933 */
934
935 /* User Sigma Override - no support change */
936 artifact=GetImageArtifact(image,"filter:sigma");
937 if (artifact != (const char *) NULL)
938 sigma=StringToDouble(artifact);
939 /* Define coefficents for Gaussian (assumes no cubic window) */
940 if ( GaussianFilter ) {
941 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
cristy68f103e2010-10-14 01:19:07 +0000942 resize_filter->coeff[1] = (MagickRealType) (1.0/(Magick2PI*sigma*sigma)); /* unused */
anthonyf5e76ef2010-10-12 01:22:01 +0000943 }
944
945 /* Blur Override */
946 artifact=GetImageArtifact(image,"filter:blur");
947 if (artifact != (const char *) NULL)
948 resize_filter->blur=StringToDouble(artifact);
949 if (resize_filter->blur < MagickEpsilon)
950 resize_filter->blur=(MagickRealType) MagickEpsilon;
951
952 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000953 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000954 if (artifact != (const char *) NULL)
955 {
cristybb503372010-05-27 20:51:26 +0000956 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000957 lobes;
958
cristy96b16132010-08-29 17:19:52 +0000959 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000960 if (lobes < 1)
961 lobes=1;
962 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000963 }
anthony61b5ddd2010-10-05 02:33:31 +0000964 /* convert Jinc lobes to a real support value */
965 if (resize_filter->filter == Jinc)
966 {
967 if (resize_filter->support > 16)
968 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
969 else
970 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
971 }
972 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000973 artifact=GetImageArtifact(image,"filter:support");
974 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000975 resize_filter->support=fabs(StringToDouble(artifact));
976 /*
nicolas07bac812010-09-19 18:47:02 +0000977 Scale windowing function separatally to the support 'clipping'
978 window that calling operator is planning to actually use. (Expert
979 override)
cristy3ed852e2009-09-05 21:47:34 +0000980 */
anthony55f12332010-09-10 01:13:02 +0000981 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000982 artifact=GetImageArtifact(image,"filter:win-support");
983 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000984 resize_filter->window_support=fabs(StringToDouble(artifact));
985 /*
anthony1f90a6b2010-09-14 08:56:31 +0000986 Adjust window function scaling to the windowing support for
987 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000988 */
989 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +0000990
anthony55f12332010-09-10 01:13:02 +0000991 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000992 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000993 */
cristy3ed852e2009-09-05 21:47:34 +0000994 B=0.0;
995 C=0.0;
cristy33b1c162010-01-23 22:51:51 +0000996 if ((filters[filter_type].function == CubicBC) ||
997 (filters[window_type].function == CubicBC))
998 {
anthony2d9b8b52010-09-14 08:31:07 +0000999 B=filters[filter_type].B;
1000 C=filters[filter_type].C;
1001 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +00001002 {
anthony2d9b8b52010-09-14 08:31:07 +00001003 B=filters[window_type].B;
1004 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +00001005 }
cristy33b1c162010-01-23 22:51:51 +00001006 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001007 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001008 {
1009 B=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001010 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001011 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001012 if (artifact != (const char *) NULL)
1013 C=StringToDouble(artifact);
1014 }
1015 else
1016 {
1017 artifact=GetImageArtifact(image,"filter:c");
1018 if (artifact != (const char *) NULL)
1019 {
1020 C=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001021 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001022 }
1023 }
anthonyf5e76ef2010-10-12 01:22:01 +00001024 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
1025 resize_filter->coeff[0]=(6.0-2.0*B)/6.0;
1026 resize_filter->coeff[1]=0.0;
1027 resize_filter->coeff[2]=(-18.0+12.0*B+6.0*C)/6.0;
1028 resize_filter->coeff[3]=(12.0-9.0*B-6.0*C)/6.0;
1029 resize_filter->coeff[4]=(8.0*B+24.0*C)/6.0;
1030 resize_filter->coeff[5]=(-12.0*B-48.0*C)/6.0;
1031 resize_filter->coeff[6]=(6.0*B+30.0*C)/6.0;
1032 resize_filter->coeff[7]=(-B-6.0*C)/6.0;
cristy3ed852e2009-09-05 21:47:34 +00001033 }
anthonyf5e76ef2010-10-12 01:22:01 +00001034
anthony55f12332010-09-10 01:13:02 +00001035 /*
nicolas07bac812010-09-19 18:47:02 +00001036 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001037 */
cristyf5b49372010-10-16 01:06:47 +00001038#if defined(MAGICKCORE_OPENMP_SUPPORT)
1039 #pragma omp master
1040 {
1041#endif
1042 artifact=GetImageArtifact(image,"filter:verbose");
1043 if (artifact != (const char *) NULL)
1044 {
1045 double
1046 support,
1047 x;
cristy3ed852e2009-09-05 21:47:34 +00001048
cristyf5b49372010-10-16 01:06:47 +00001049 /*
1050 Set the weighting function properly when the weighting
1051 function may not exactly match the filter of the same name.
1052 EG: a Point filter really uses a Box weighting function
1053 with a different support than is typically used.
anthony463be1d2010-09-26 01:07:36 +00001054
cristyf5b49372010-10-16 01:06:47 +00001055 */
1056 if (resize_filter->filter == Box) filter_type=BoxFilter;
1057 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1058 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1059 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1060 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1061 /*
1062 Report Filter Details.
1063 */
1064 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1065 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
1066 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1067 MagickFilterOptions,filter_type));
1068 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
1069 MagickFilterOptions, window_type));
1070 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
1071 (double) resize_filter->support);
1072 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
1073 (double) resize_filter->window_support);
1074 (void) fprintf(stdout,"# scale_blur = %.*g\n",GetMagickPrecision(),
1075 (double) resize_filter->blur);
1076 if ( filter_type == GaussianFilter )
1077 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",GetMagickPrecision(),
1078 (double) sigma);
1079 (void) fprintf(stdout,"# practical_support = %.*g\n",GetMagickPrecision(),
1080 (double) support);
1081 if ( filter_type == CubicFilter || window_type == CubicFilter )
1082 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1083 (double) B,GetMagickPrecision(),(double) C);
1084 (void) fprintf(stdout,"\n");
1085 /*
1086 Output values of resulting filter graph -- for graphing
1087 filter result.
1088 */
1089 for (x=0.0; x <= support; x+=0.01f)
1090 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1091 (double) GetResizeFilterWeight(resize_filter,x));
1092 /* A final value so gnuplot can graph the 'stop' properly. */
1093 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1094 0.0);
1095 }
1096 /* Output the above once only for each image - remove setting */
1097 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1098#if defined(MAGICKCORE_OPENMP_SUPPORT)
1099 }
1100#endif
cristy3ed852e2009-09-05 21:47:34 +00001101 return(resize_filter);
1102}
1103
1104/*
1105%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1106% %
1107% %
1108% %
1109% A d a p t i v e R e s i z e I m a g e %
1110% %
1111% %
1112% %
1113%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1114%
1115% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1116%
1117% The format of the AdaptiveResizeImage method is:
1118%
cristy9af9b5d2010-08-15 17:04:28 +00001119% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1120% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001121%
1122% A description of each parameter follows:
1123%
1124% o image: the image.
1125%
1126% o columns: the number of columns in the resized image.
1127%
1128% o rows: the number of rows in the resized image.
1129%
1130% o exception: return any errors or warnings in this structure.
1131%
1132*/
1133MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001134 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001135{
1136#define AdaptiveResizeImageTag "Resize/Image"
1137
cristyc4c8d132010-01-07 01:58:38 +00001138 CacheView
1139 *resize_view;
1140
cristy3ed852e2009-09-05 21:47:34 +00001141 Image
1142 *resize_image;
1143
cristy3ed852e2009-09-05 21:47:34 +00001144 MagickBooleanType
1145 proceed;
1146
1147 MagickPixelPacket
1148 pixel;
1149
1150 PointInfo
1151 offset;
1152
1153 ResampleFilter
1154 *resample_filter;
1155
cristy9af9b5d2010-08-15 17:04:28 +00001156 ssize_t
1157 y;
1158
cristy3ed852e2009-09-05 21:47:34 +00001159 /*
1160 Adaptively resize image.
1161 */
1162 assert(image != (const Image *) NULL);
1163 assert(image->signature == MagickSignature);
1164 if (image->debug != MagickFalse)
1165 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1166 assert(exception != (ExceptionInfo *) NULL);
1167 assert(exception->signature == MagickSignature);
1168 if ((columns == 0) || (rows == 0))
1169 return((Image *) NULL);
1170 if ((columns == image->columns) && (rows == image->rows))
1171 return(CloneImage(image,0,0,MagickTrue,exception));
1172 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1173 if (resize_image == (Image *) NULL)
1174 return((Image *) NULL);
1175 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1176 {
1177 InheritException(exception,&resize_image->exception);
1178 resize_image=DestroyImage(resize_image);
1179 return((Image *) NULL);
1180 }
1181 GetMagickPixelPacket(image,&pixel);
1182 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001183 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001184 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001185 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001186 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001187 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001188 {
1189 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001190 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001191
cristybb503372010-05-27 20:51:26 +00001192 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001193 x;
1194
1195 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001196 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001197
1198 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1199 exception);
1200 if (q == (PixelPacket *) NULL)
1201 break;
1202 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1203 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001204 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001205 {
1206 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1207 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1208 &pixel);
1209 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1210 q++;
1211 }
1212 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1213 break;
cristy96b16132010-08-29 17:19:52 +00001214 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1215 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001216 if (proceed == MagickFalse)
1217 break;
1218 }
1219 resample_filter=DestroyResampleFilter(resample_filter);
1220 resize_view=DestroyCacheView(resize_view);
1221 return(resize_image);
1222}
1223
1224/*
1225%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1226% %
1227% %
1228% %
1229+ B e s s e l O r d e r O n e %
1230% %
1231% %
1232% %
1233%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1234%
1235% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001236% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001237%
1238% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1239%
1240% j1(x) = x*j1(x);
1241%
1242% For x in (8,inf)
1243%
1244% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1245%
1246% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1247%
1248% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1249% = 1/sqrt(2) * (sin(x) - cos(x))
1250% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1251% = -1/sqrt(2) * (sin(x) + cos(x))
1252%
1253% The format of the BesselOrderOne method is:
1254%
1255% MagickRealType BesselOrderOne(MagickRealType x)
1256%
1257% A description of each parameter follows:
1258%
1259% o x: MagickRealType value.
1260%
1261*/
1262
1263#undef I0
1264static MagickRealType I0(MagickRealType x)
1265{
1266 MagickRealType
1267 sum,
1268 t,
1269 y;
1270
cristybb503372010-05-27 20:51:26 +00001271 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001272 i;
1273
1274 /*
1275 Zeroth order Bessel function of the first kind.
1276 */
1277 sum=1.0;
1278 y=x*x/4.0;
1279 t=y;
1280 for (i=2; t > MagickEpsilon; i++)
1281 {
1282 sum+=t;
1283 t*=y/((MagickRealType) i*i);
1284 }
1285 return(sum);
1286}
1287
1288#undef J1
1289static MagickRealType J1(MagickRealType x)
1290{
1291 MagickRealType
1292 p,
1293 q;
1294
cristybb503372010-05-27 20:51:26 +00001295 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001296 i;
1297
1298 static const double
1299 Pone[] =
1300 {
1301 0.581199354001606143928050809e+21,
1302 -0.6672106568924916298020941484e+20,
1303 0.2316433580634002297931815435e+19,
1304 -0.3588817569910106050743641413e+17,
1305 0.2908795263834775409737601689e+15,
1306 -0.1322983480332126453125473247e+13,
1307 0.3413234182301700539091292655e+10,
1308 -0.4695753530642995859767162166e+7,
1309 0.270112271089232341485679099e+4
1310 },
1311 Qone[] =
1312 {
1313 0.11623987080032122878585294e+22,
1314 0.1185770712190320999837113348e+20,
1315 0.6092061398917521746105196863e+17,
1316 0.2081661221307607351240184229e+15,
1317 0.5243710262167649715406728642e+12,
1318 0.1013863514358673989967045588e+10,
1319 0.1501793594998585505921097578e+7,
1320 0.1606931573481487801970916749e+4,
1321 0.1e+1
1322 };
1323
1324 p=Pone[8];
1325 q=Qone[8];
1326 for (i=7; i >= 0; i--)
1327 {
1328 p=p*x*x+Pone[i];
1329 q=q*x*x+Qone[i];
1330 }
1331 return(p/q);
1332}
1333
1334#undef P1
1335static MagickRealType P1(MagickRealType x)
1336{
1337 MagickRealType
1338 p,
1339 q;
1340
cristybb503372010-05-27 20:51:26 +00001341 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001342 i;
1343
1344 static const double
1345 Pone[] =
1346 {
1347 0.352246649133679798341724373e+5,
1348 0.62758845247161281269005675e+5,
1349 0.313539631109159574238669888e+5,
1350 0.49854832060594338434500455e+4,
1351 0.2111529182853962382105718e+3,
1352 0.12571716929145341558495e+1
1353 },
1354 Qone[] =
1355 {
1356 0.352246649133679798068390431e+5,
1357 0.626943469593560511888833731e+5,
1358 0.312404063819041039923015703e+5,
1359 0.4930396490181088979386097e+4,
1360 0.2030775189134759322293574e+3,
1361 0.1e+1
1362 };
1363
1364 p=Pone[5];
1365 q=Qone[5];
1366 for (i=4; i >= 0; i--)
1367 {
1368 p=p*(8.0/x)*(8.0/x)+Pone[i];
1369 q=q*(8.0/x)*(8.0/x)+Qone[i];
1370 }
1371 return(p/q);
1372}
1373
1374#undef Q1
1375static MagickRealType Q1(MagickRealType x)
1376{
1377 MagickRealType
1378 p,
1379 q;
1380
cristybb503372010-05-27 20:51:26 +00001381 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001382 i;
1383
1384 static const double
1385 Pone[] =
1386 {
1387 0.3511751914303552822533318e+3,
1388 0.7210391804904475039280863e+3,
1389 0.4259873011654442389886993e+3,
1390 0.831898957673850827325226e+2,
1391 0.45681716295512267064405e+1,
1392 0.3532840052740123642735e-1
1393 },
1394 Qone[] =
1395 {
1396 0.74917374171809127714519505e+4,
1397 0.154141773392650970499848051e+5,
1398 0.91522317015169922705904727e+4,
1399 0.18111867005523513506724158e+4,
1400 0.1038187585462133728776636e+3,
1401 0.1e+1
1402 };
1403
1404 p=Pone[5];
1405 q=Qone[5];
1406 for (i=4; i >= 0; i--)
1407 {
1408 p=p*(8.0/x)*(8.0/x)+Pone[i];
1409 q=q*(8.0/x)*(8.0/x)+Qone[i];
1410 }
1411 return(p/q);
1412}
1413
1414static MagickRealType BesselOrderOne(MagickRealType x)
1415{
1416 MagickRealType
1417 p,
1418 q;
1419
1420 if (x == 0.0)
1421 return(0.0);
1422 p=x;
1423 if (x < 0.0)
1424 x=(-x);
1425 if (x < 8.0)
1426 return(p*J1(x));
1427 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1428 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1429 cos((double) x))));
1430 if (p < 0.0)
1431 q=(-q);
1432 return(q);
1433}
1434
1435/*
1436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1437% %
1438% %
1439% %
1440+ D e s t r o y R e s i z e F i l t e r %
1441% %
1442% %
1443% %
1444%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1445%
1446% DestroyResizeFilter() destroy the resize filter.
1447%
cristya2ffd7e2010-03-10 20:50:30 +00001448% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001449%
1450% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1451%
1452% A description of each parameter follows:
1453%
1454% o resize_filter: the resize filter.
1455%
1456*/
1457MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1458{
1459 assert(resize_filter != (ResizeFilter *) NULL);
1460 assert(resize_filter->signature == MagickSignature);
1461 resize_filter->signature=(~MagickSignature);
1462 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1463 return(resize_filter);
1464}
1465
1466/*
1467%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1468% %
1469% %
1470% %
1471+ G e t R e s i z e F i l t e r S u p p o r t %
1472% %
1473% %
1474% %
1475%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1476%
1477% GetResizeFilterSupport() return the current support window size for this
1478% filter. Note that this may have been enlarged by filter:blur factor.
1479%
1480% The format of the GetResizeFilterSupport method is:
1481%
1482% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1483%
1484% A description of each parameter follows:
1485%
1486% o filter: Image filter to use.
1487%
1488*/
1489MagickExport MagickRealType GetResizeFilterSupport(
1490 const ResizeFilter *resize_filter)
1491{
1492 assert(resize_filter != (ResizeFilter *) NULL);
1493 assert(resize_filter->signature == MagickSignature);
1494 return(resize_filter->support*resize_filter->blur);
1495}
1496
1497/*
1498%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1499% %
1500% %
1501% %
1502+ G e t R e s i z e F i l t e r W e i g h t %
1503% %
1504% %
1505% %
1506%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1507%
1508% GetResizeFilterWeight evaluates the specified resize filter at the point x
1509% which usally lies between zero and the filters current 'support' and
1510% returns the weight of the filter function at that point.
1511%
1512% The format of the GetResizeFilterWeight method is:
1513%
1514% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1515% const MagickRealType x)
1516%
1517% A description of each parameter follows:
1518%
1519% o filter: the filter type.
1520%
1521% o x: the point.
1522%
1523*/
1524MagickExport MagickRealType GetResizeFilterWeight(
1525 const ResizeFilter *resize_filter,const MagickRealType x)
1526{
1527 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001528 scale,
1529 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001530
1531 /*
1532 Windowing function - scale the weighting filter by this amount.
1533 */
1534 assert(resize_filter != (ResizeFilter *) NULL);
1535 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001536 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001537 if ((resize_filter->window_support < MagickEpsilon) ||
1538 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001539 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001540 else
1541 {
anthony55f12332010-09-10 01:13:02 +00001542 scale=resize_filter->scale;
1543 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001544 }
anthony55f12332010-09-10 01:13:02 +00001545 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001546}
1547
1548/*
1549%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1550% %
1551% %
1552% %
1553% M a g n i f y I m a g e %
1554% %
1555% %
1556% %
1557%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1558%
1559% MagnifyImage() is a convenience method that scales an image proportionally
1560% to twice its size.
1561%
1562% The format of the MagnifyImage method is:
1563%
1564% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1565%
1566% A description of each parameter follows:
1567%
1568% o image: the image.
1569%
1570% o exception: return any errors or warnings in this structure.
1571%
1572*/
1573MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1574{
1575 Image
1576 *magnify_image;
1577
1578 assert(image != (Image *) NULL);
1579 assert(image->signature == MagickSignature);
1580 if (image->debug != MagickFalse)
1581 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1582 assert(exception != (ExceptionInfo *) NULL);
1583 assert(exception->signature == MagickSignature);
1584 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1585 1.0,exception);
1586 return(magnify_image);
1587}
1588
1589/*
1590%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1591% %
1592% %
1593% %
1594% M i n i f y I m a g e %
1595% %
1596% %
1597% %
1598%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1599%
1600% MinifyImage() is a convenience method that scales an image proportionally
1601% to half its size.
1602%
1603% The format of the MinifyImage method is:
1604%
1605% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1606%
1607% A description of each parameter follows:
1608%
1609% o image: the image.
1610%
1611% o exception: return any errors or warnings in this structure.
1612%
1613*/
1614MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1615{
1616 Image
1617 *minify_image;
1618
1619 assert(image != (Image *) NULL);
1620 assert(image->signature == MagickSignature);
1621 if (image->debug != MagickFalse)
1622 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1623 assert(exception != (ExceptionInfo *) NULL);
1624 assert(exception->signature == MagickSignature);
1625 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1626 1.0,exception);
1627 return(minify_image);
1628}
1629
1630/*
1631%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1632% %
1633% %
1634% %
1635% R e s a m p l e I m a g e %
1636% %
1637% %
1638% %
1639%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1640%
1641% ResampleImage() resize image in terms of its pixel size, so that when
1642% displayed at the given resolution it will be the same size in terms of
1643% real world units as the original image at the original resolution.
1644%
1645% The format of the ResampleImage method is:
1646%
1647% Image *ResampleImage(Image *image,const double x_resolution,
1648% const double y_resolution,const FilterTypes filter,const double blur,
1649% ExceptionInfo *exception)
1650%
1651% A description of each parameter follows:
1652%
1653% o image: the image to be resized to fit the given resolution.
1654%
1655% o x_resolution: the new image x resolution.
1656%
1657% o y_resolution: the new image y resolution.
1658%
1659% o filter: Image filter to use.
1660%
1661% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1662%
1663*/
1664MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1665 const double y_resolution,const FilterTypes filter,const double blur,
1666 ExceptionInfo *exception)
1667{
1668#define ResampleImageTag "Resample/Image"
1669
1670 Image
1671 *resample_image;
1672
cristybb503372010-05-27 20:51:26 +00001673 size_t
cristy3ed852e2009-09-05 21:47:34 +00001674 height,
1675 width;
1676
1677 /*
1678 Initialize sampled image attributes.
1679 */
1680 assert(image != (const Image *) NULL);
1681 assert(image->signature == MagickSignature);
1682 if (image->debug != MagickFalse)
1683 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1684 assert(exception != (ExceptionInfo *) NULL);
1685 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001686 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1687 72.0 : image->x_resolution)+0.5);
1688 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1689 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001690 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1691 if (resample_image != (Image *) NULL)
1692 {
1693 resample_image->x_resolution=x_resolution;
1694 resample_image->y_resolution=y_resolution;
1695 }
1696 return(resample_image);
1697}
1698#if defined(MAGICKCORE_LQR_DELEGATE)
1699
1700/*
1701%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1702% %
1703% %
1704% %
1705% L i q u i d R e s c a l e I m a g e %
1706% %
1707% %
1708% %
1709%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1710%
1711% LiquidRescaleImage() rescales image with seam carving.
1712%
1713% The format of the LiquidRescaleImage method is:
1714%
1715% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001716% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001717% const double delta_x,const double rigidity,ExceptionInfo *exception)
1718%
1719% A description of each parameter follows:
1720%
1721% o image: the image.
1722%
1723% o columns: the number of columns in the rescaled image.
1724%
1725% o rows: the number of rows in the rescaled image.
1726%
1727% o delta_x: maximum seam transversal step (0 means straight seams).
1728%
1729% o rigidity: introduce a bias for non-straight seams (typically 0).
1730%
1731% o exception: return any errors or warnings in this structure.
1732%
1733*/
cristy9af9b5d2010-08-15 17:04:28 +00001734MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1735 const size_t rows,const double delta_x,const double rigidity,
1736 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001737{
1738#define LiquidRescaleImageTag "Rescale/Image"
1739
cristyc5c6f662010-09-22 14:23:02 +00001740 CacheView
1741 *rescale_view;
1742
cristy3ed852e2009-09-05 21:47:34 +00001743 const char
1744 *map;
1745
1746 guchar
1747 *packet;
1748
1749 Image
1750 *rescale_image;
1751
1752 int
1753 x,
1754 y;
1755
1756 LqrCarver
1757 *carver;
1758
1759 LqrRetVal
1760 lqr_status;
1761
1762 MagickBooleanType
1763 status;
1764
1765 MagickPixelPacket
1766 pixel;
1767
1768 unsigned char
1769 *pixels;
1770
1771 /*
1772 Liquid rescale image.
1773 */
1774 assert(image != (const Image *) NULL);
1775 assert(image->signature == MagickSignature);
1776 if (image->debug != MagickFalse)
1777 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1778 assert(exception != (ExceptionInfo *) NULL);
1779 assert(exception->signature == MagickSignature);
1780 if ((columns == 0) || (rows == 0))
1781 return((Image *) NULL);
1782 if ((columns == image->columns) && (rows == image->rows))
1783 return(CloneImage(image,0,0,MagickTrue,exception));
1784 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001785 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001786 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1787 {
1788 Image
1789 *resize_image;
1790
cristybb503372010-05-27 20:51:26 +00001791 size_t
cristy3ed852e2009-09-05 21:47:34 +00001792 height,
1793 width;
1794
1795 /*
1796 Honor liquid resize size limitations.
1797 */
1798 for (width=image->columns; columns >= (2*width-1); width*=2);
1799 for (height=image->rows; rows >= (2*height-1); height*=2);
1800 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1801 exception);
1802 if (resize_image == (Image *) NULL)
1803 return((Image *) NULL);
1804 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1805 rigidity,exception);
1806 resize_image=DestroyImage(resize_image);
1807 return(rescale_image);
1808 }
1809 map="RGB";
1810 if (image->matte == MagickFalse)
1811 map="RGBA";
1812 if (image->colorspace == CMYKColorspace)
1813 {
1814 map="CMYK";
1815 if (image->matte == MagickFalse)
1816 map="CMYKA";
1817 }
1818 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1819 strlen(map)*sizeof(*pixels));
1820 if (pixels == (unsigned char *) NULL)
1821 return((Image *) NULL);
1822 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1823 pixels,exception);
1824 if (status == MagickFalse)
1825 {
1826 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1827 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1828 }
1829 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1830 if (carver == (LqrCarver *) NULL)
1831 {
1832 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1833 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1834 }
1835 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1836 lqr_status=lqr_carver_resize(carver,columns,rows);
1837 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1838 lqr_carver_get_height(carver),MagickTrue,exception);
1839 if (rescale_image == (Image *) NULL)
1840 {
1841 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1842 return((Image *) NULL);
1843 }
1844 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1845 {
1846 InheritException(exception,&rescale_image->exception);
1847 rescale_image=DestroyImage(rescale_image);
1848 return((Image *) NULL);
1849 }
1850 GetMagickPixelPacket(rescale_image,&pixel);
1851 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001852 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001853 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1854 {
1855 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001856 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001857
1858 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001859 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001860
anthony22aad252010-09-23 06:59:07 +00001861 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001862 if (q == (PixelPacket *) NULL)
1863 break;
cristyc5c6f662010-09-22 14:23:02 +00001864 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001865 pixel.red=QuantumRange*(packet[0]/255.0);
1866 pixel.green=QuantumRange*(packet[1]/255.0);
1867 pixel.blue=QuantumRange*(packet[2]/255.0);
1868 if (image->colorspace != CMYKColorspace)
1869 {
1870 if (image->matte == MagickFalse)
1871 pixel.opacity=QuantumRange*(packet[3]/255.0);
1872 }
1873 else
1874 {
1875 pixel.index=QuantumRange*(packet[3]/255.0);
1876 if (image->matte == MagickFalse)
1877 pixel.opacity=QuantumRange*(packet[4]/255.0);
1878 }
1879 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001880 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001881 break;
1882 }
cristyc5c6f662010-09-22 14:23:02 +00001883 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001884 /*
1885 Relinquish resources.
1886 */
1887 lqr_carver_destroy(carver);
1888 return(rescale_image);
1889}
1890#else
1891MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001892 const size_t magick_unused(columns),const size_t magick_unused(rows),
1893 const double magick_unused(delta_x),const double magick_unused(rigidity),
1894 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001895{
1896 assert(image != (const Image *) NULL);
1897 assert(image->signature == MagickSignature);
1898 if (image->debug != MagickFalse)
1899 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1900 assert(exception != (ExceptionInfo *) NULL);
1901 assert(exception->signature == MagickSignature);
1902 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1903 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1904 return((Image *) NULL);
1905}
1906#endif
1907
1908/*
1909%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1910% %
1911% %
1912% %
1913% R e s i z e I m a g e %
1914% %
1915% %
1916% %
1917%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1918%
1919% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001920% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001921%
1922% If an undefined filter is given the filter defaults to Mitchell for a
1923% colormapped image, a image with a matte channel, or if the image is
1924% enlarged. Otherwise the filter defaults to a Lanczos.
1925%
1926% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1927%
1928% The format of the ResizeImage method is:
1929%
cristybb503372010-05-27 20:51:26 +00001930% Image *ResizeImage(Image *image,const size_t columns,
1931% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001932% ExceptionInfo *exception)
1933%
1934% A description of each parameter follows:
1935%
1936% o image: the image.
1937%
1938% o columns: the number of columns in the scaled image.
1939%
1940% o rows: the number of rows in the scaled image.
1941%
1942% o filter: Image filter to use.
1943%
cristy9af9b5d2010-08-15 17:04:28 +00001944% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1945% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001946%
1947% o exception: return any errors or warnings in this structure.
1948%
1949*/
1950
1951typedef struct _ContributionInfo
1952{
1953 MagickRealType
1954 weight;
1955
cristybb503372010-05-27 20:51:26 +00001956 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001957 pixel;
1958} ContributionInfo;
1959
1960static ContributionInfo **DestroyContributionThreadSet(
1961 ContributionInfo **contribution)
1962{
cristybb503372010-05-27 20:51:26 +00001963 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001964 i;
1965
1966 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001967 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001968 if (contribution[i] != (ContributionInfo *) NULL)
1969 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1970 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001971 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001972 return(contribution);
1973}
1974
1975static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1976{
cristybb503372010-05-27 20:51:26 +00001977 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001978 i;
1979
1980 ContributionInfo
1981 **contribution;
1982
cristybb503372010-05-27 20:51:26 +00001983 size_t
cristy3ed852e2009-09-05 21:47:34 +00001984 number_threads;
1985
1986 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001987 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001988 sizeof(*contribution));
1989 if (contribution == (ContributionInfo **) NULL)
1990 return((ContributionInfo **) NULL);
1991 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00001992 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00001993 {
1994 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1995 sizeof(**contribution));
1996 if (contribution[i] == (ContributionInfo *) NULL)
1997 return(DestroyContributionThreadSet(contribution));
1998 }
1999 return(contribution);
2000}
2001
2002static inline double MagickMax(const double x,const double y)
2003{
2004 if (x > y)
2005 return(x);
2006 return(y);
2007}
2008
2009static inline double MagickMin(const double x,const double y)
2010{
2011 if (x < y)
2012 return(x);
2013 return(y);
2014}
2015
2016static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2017 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002018 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002019{
2020#define ResizeImageTag "Resize/Image"
2021
cristyfa112112010-01-04 17:48:07 +00002022 CacheView
2023 *image_view,
2024 *resize_view;
2025
cristy3ed852e2009-09-05 21:47:34 +00002026 ClassType
2027 storage_class;
2028
2029 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002030 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002031
cristy3ed852e2009-09-05 21:47:34 +00002032 MagickBooleanType
2033 status;
2034
2035 MagickPixelPacket
2036 zero;
2037
2038 MagickRealType
2039 scale,
2040 support;
2041
cristy9af9b5d2010-08-15 17:04:28 +00002042 ssize_t
2043 x;
2044
cristy3ed852e2009-09-05 21:47:34 +00002045 /*
2046 Apply filter to resize horizontally from image to resize image.
2047 */
cristy5d824382010-09-06 14:00:17 +00002048 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002049 support=scale*GetResizeFilterSupport(resize_filter);
2050 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2051 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2052 {
2053 InheritException(exception,&resize_image->exception);
2054 return(MagickFalse);
2055 }
2056 if (support < 0.5)
2057 {
2058 /*
nicolas07bac812010-09-19 18:47:02 +00002059 Support too small even for nearest neighbour: Reduce to point
2060 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002061 */
2062 support=(MagickRealType) 0.5;
2063 scale=1.0;
2064 }
2065 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2066 if (contributions == (ContributionInfo **) NULL)
2067 {
2068 (void) ThrowMagickException(exception,GetMagickModule(),
2069 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2070 return(MagickFalse);
2071 }
2072 status=MagickTrue;
2073 scale=1.0/scale;
2074 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2075 image_view=AcquireCacheView(image);
2076 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002077#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002078 #pragma omp parallel for shared(status)
2079#endif
cristybb503372010-05-27 20:51:26 +00002080 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002081 {
cristy3ed852e2009-09-05 21:47:34 +00002082 MagickRealType
2083 center,
2084 density;
2085
2086 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002087 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002088
2089 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002090 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002091
cristy03dbbd22010-09-19 23:04:47 +00002092 register ContributionInfo
2093 *restrict contribution;
2094
cristy3ed852e2009-09-05 21:47:34 +00002095 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002096 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002097
cristy3ed852e2009-09-05 21:47:34 +00002098 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002099 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002100
cristy03dbbd22010-09-19 23:04:47 +00002101 register ssize_t
2102 y;
2103
cristy9af9b5d2010-08-15 17:04:28 +00002104 ssize_t
2105 n,
2106 start,
2107 stop;
2108
cristy3ed852e2009-09-05 21:47:34 +00002109 if (status == MagickFalse)
2110 continue;
2111 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002112 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2113 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002114 density=0.0;
2115 contribution=contributions[GetOpenMPThreadId()];
2116 for (n=0; n < (stop-start); n++)
2117 {
2118 contribution[n].pixel=start+n;
2119 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2120 ((MagickRealType) (start+n)-center+0.5));
2121 density+=contribution[n].weight;
2122 }
2123 if ((density != 0.0) && (density != 1.0))
2124 {
cristybb503372010-05-27 20:51:26 +00002125 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002126 i;
2127
2128 /*
2129 Normalize.
2130 */
2131 density=1.0/density;
2132 for (i=0; i < n; i++)
2133 contribution[i].weight*=density;
2134 }
cristy9af9b5d2010-08-15 17:04:28 +00002135 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2136 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002137 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2138 exception);
2139 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2140 {
2141 status=MagickFalse;
2142 continue;
2143 }
2144 indexes=GetCacheViewVirtualIndexQueue(image_view);
2145 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002146 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002147 {
cristy3ed852e2009-09-05 21:47:34 +00002148 MagickPixelPacket
2149 pixel;
2150
2151 MagickRealType
2152 alpha;
2153
cristybb503372010-05-27 20:51:26 +00002154 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002155 i;
2156
cristy9af9b5d2010-08-15 17:04:28 +00002157 ssize_t
2158 j;
2159
cristy3ed852e2009-09-05 21:47:34 +00002160 pixel=zero;
2161 if (image->matte == MagickFalse)
2162 {
2163 for (i=0; i < n; i++)
2164 {
2165 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2166 (contribution[i].pixel-contribution[0].pixel);
2167 alpha=contribution[i].weight;
2168 pixel.red+=alpha*(p+j)->red;
2169 pixel.green+=alpha*(p+j)->green;
2170 pixel.blue+=alpha*(p+j)->blue;
2171 pixel.opacity+=alpha*(p+j)->opacity;
2172 }
cristyce70c172010-01-07 17:15:30 +00002173 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2174 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2175 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2176 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002177 if ((image->colorspace == CMYKColorspace) &&
2178 (resize_image->colorspace == CMYKColorspace))
2179 {
2180 for (i=0; i < n; i++)
2181 {
2182 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2183 (contribution[i].pixel-contribution[0].pixel);
2184 alpha=contribution[i].weight;
2185 pixel.index+=alpha*indexes[j];
2186 }
cristyce70c172010-01-07 17:15:30 +00002187 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002188 }
2189 }
2190 else
2191 {
2192 MagickRealType
2193 gamma;
2194
2195 gamma=0.0;
2196 for (i=0; i < n; i++)
2197 {
2198 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2199 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002200 alpha=contribution[i].weight*QuantumScale*
2201 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002202 pixel.red+=alpha*(p+j)->red;
2203 pixel.green+=alpha*(p+j)->green;
2204 pixel.blue+=alpha*(p+j)->blue;
2205 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2206 gamma+=alpha;
2207 }
2208 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002209 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2210 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2211 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2212 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002213 if ((image->colorspace == CMYKColorspace) &&
2214 (resize_image->colorspace == CMYKColorspace))
2215 {
2216 for (i=0; i < n; i++)
2217 {
2218 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2219 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002220 alpha=contribution[i].weight*QuantumScale*
2221 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002222 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002223 }
cristyce70c172010-01-07 17:15:30 +00002224 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2225 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002226 }
2227 }
2228 if ((resize_image->storage_class == PseudoClass) &&
2229 (image->storage_class == PseudoClass))
2230 {
cristybb503372010-05-27 20:51:26 +00002231 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002232 1.0)+0.5);
2233 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2234 (contribution[i-start].pixel-contribution[0].pixel);
2235 resize_indexes[y]=indexes[j];
2236 }
2237 q++;
2238 }
2239 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2240 status=MagickFalse;
2241 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2242 {
2243 MagickBooleanType
2244 proceed;
2245
cristyb5d5f722009-11-04 03:03:49 +00002246#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002247 #pragma omp critical (MagickCore_HorizontalFilter)
2248#endif
cristy9af9b5d2010-08-15 17:04:28 +00002249 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002250 if (proceed == MagickFalse)
2251 status=MagickFalse;
2252 }
2253 }
2254 resize_view=DestroyCacheView(resize_view);
2255 image_view=DestroyCacheView(image_view);
2256 contributions=DestroyContributionThreadSet(contributions);
2257 return(status);
2258}
2259
2260static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2261 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002262 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002263{
cristyfa112112010-01-04 17:48:07 +00002264 CacheView
2265 *image_view,
2266 *resize_view;
2267
cristy3ed852e2009-09-05 21:47:34 +00002268 ClassType
2269 storage_class;
2270
2271 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002272 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002273
cristy3ed852e2009-09-05 21:47:34 +00002274 MagickBooleanType
2275 status;
2276
2277 MagickPixelPacket
2278 zero;
2279
2280 MagickRealType
2281 scale,
2282 support;
2283
cristy9af9b5d2010-08-15 17:04:28 +00002284 ssize_t
2285 y;
2286
cristy3ed852e2009-09-05 21:47:34 +00002287 /*
cristy9af9b5d2010-08-15 17:04:28 +00002288 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002289 */
cristy5d824382010-09-06 14:00:17 +00002290 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002291 support=scale*GetResizeFilterSupport(resize_filter);
2292 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2293 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2294 {
2295 InheritException(exception,&resize_image->exception);
2296 return(MagickFalse);
2297 }
2298 if (support < 0.5)
2299 {
2300 /*
nicolas07bac812010-09-19 18:47:02 +00002301 Support too small even for nearest neighbour: Reduce to point
2302 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002303 */
2304 support=(MagickRealType) 0.5;
2305 scale=1.0;
2306 }
2307 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2308 if (contributions == (ContributionInfo **) NULL)
2309 {
2310 (void) ThrowMagickException(exception,GetMagickModule(),
2311 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2312 return(MagickFalse);
2313 }
2314 status=MagickTrue;
2315 scale=1.0/scale;
2316 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2317 image_view=AcquireCacheView(image);
2318 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002319#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002320 #pragma omp parallel for shared(status)
2321#endif
cristybb503372010-05-27 20:51:26 +00002322 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002323 {
cristy3ed852e2009-09-05 21:47:34 +00002324 MagickRealType
2325 center,
2326 density;
2327
2328 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002329 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002330
2331 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002332 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002333
cristy03dbbd22010-09-19 23:04:47 +00002334 register ContributionInfo
2335 *restrict contribution;
2336
cristy3ed852e2009-09-05 21:47:34 +00002337 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002338 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002339
cristy9af9b5d2010-08-15 17:04:28 +00002340 register PixelPacket
2341 *restrict q;
2342
cristybb503372010-05-27 20:51:26 +00002343 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002344 x;
2345
cristy9af9b5d2010-08-15 17:04:28 +00002346 ssize_t
2347 n,
2348 start,
2349 stop;
cristy3ed852e2009-09-05 21:47:34 +00002350
2351 if (status == MagickFalse)
2352 continue;
cristy679e6962010-03-18 00:42:45 +00002353 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002354 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2355 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002356 density=0.0;
2357 contribution=contributions[GetOpenMPThreadId()];
2358 for (n=0; n < (stop-start); n++)
2359 {
2360 contribution[n].pixel=start+n;
2361 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2362 ((MagickRealType) (start+n)-center+0.5));
2363 density+=contribution[n].weight;
2364 }
2365 if ((density != 0.0) && (density != 1.0))
2366 {
cristybb503372010-05-27 20:51:26 +00002367 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002368 i;
2369
2370 /*
2371 Normalize.
2372 */
2373 density=1.0/density;
2374 for (i=0; i < n; i++)
2375 contribution[i].weight*=density;
2376 }
2377 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002378 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2379 exception);
cristy3ed852e2009-09-05 21:47:34 +00002380 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2381 exception);
2382 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2383 {
2384 status=MagickFalse;
2385 continue;
2386 }
2387 indexes=GetCacheViewVirtualIndexQueue(image_view);
2388 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002389 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002390 {
cristy3ed852e2009-09-05 21:47:34 +00002391 MagickPixelPacket
2392 pixel;
2393
2394 MagickRealType
2395 alpha;
2396
cristybb503372010-05-27 20:51:26 +00002397 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002398 i;
2399
cristy9af9b5d2010-08-15 17:04:28 +00002400 ssize_t
2401 j;
2402
cristy3ed852e2009-09-05 21:47:34 +00002403 pixel=zero;
2404 if (image->matte == MagickFalse)
2405 {
2406 for (i=0; i < n; i++)
2407 {
cristybb503372010-05-27 20:51:26 +00002408 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002409 image->columns+x);
2410 alpha=contribution[i].weight;
2411 pixel.red+=alpha*(p+j)->red;
2412 pixel.green+=alpha*(p+j)->green;
2413 pixel.blue+=alpha*(p+j)->blue;
2414 pixel.opacity+=alpha*(p+j)->opacity;
2415 }
cristyce70c172010-01-07 17:15:30 +00002416 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2417 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2418 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2419 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002420 if ((image->colorspace == CMYKColorspace) &&
2421 (resize_image->colorspace == CMYKColorspace))
2422 {
2423 for (i=0; i < n; i++)
2424 {
cristybb503372010-05-27 20:51:26 +00002425 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002426 image->columns+x);
2427 alpha=contribution[i].weight;
2428 pixel.index+=alpha*indexes[j];
2429 }
cristyce70c172010-01-07 17:15:30 +00002430 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002431 }
2432 }
2433 else
2434 {
2435 MagickRealType
2436 gamma;
2437
2438 gamma=0.0;
2439 for (i=0; i < n; i++)
2440 {
cristybb503372010-05-27 20:51:26 +00002441 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002442 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002443 alpha=contribution[i].weight*QuantumScale*
2444 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002445 pixel.red+=alpha*(p+j)->red;
2446 pixel.green+=alpha*(p+j)->green;
2447 pixel.blue+=alpha*(p+j)->blue;
2448 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2449 gamma+=alpha;
2450 }
2451 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002452 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2453 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2454 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2455 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002456 if ((image->colorspace == CMYKColorspace) &&
2457 (resize_image->colorspace == CMYKColorspace))
2458 {
2459 for (i=0; i < n; i++)
2460 {
cristybb503372010-05-27 20:51:26 +00002461 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002462 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002463 alpha=contribution[i].weight*QuantumScale*
2464 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002465 pixel.index+=alpha*indexes[j];
2466 }
cristyce70c172010-01-07 17:15:30 +00002467 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2468 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002469 }
2470 }
2471 if ((resize_image->storage_class == PseudoClass) &&
2472 (image->storage_class == PseudoClass))
2473 {
cristybb503372010-05-27 20:51:26 +00002474 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002475 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002476 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002477 image->columns+x);
2478 resize_indexes[x]=indexes[j];
2479 }
2480 q++;
2481 }
2482 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2483 status=MagickFalse;
2484 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2485 {
2486 MagickBooleanType
2487 proceed;
2488
cristyb5d5f722009-11-04 03:03:49 +00002489#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002490 #pragma omp critical (MagickCore_VerticalFilter)
2491#endif
cristy9af9b5d2010-08-15 17:04:28 +00002492 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002493 if (proceed == MagickFalse)
2494 status=MagickFalse;
2495 }
2496 }
2497 resize_view=DestroyCacheView(resize_view);
2498 image_view=DestroyCacheView(image_view);
2499 contributions=DestroyContributionThreadSet(contributions);
2500 return(status);
2501}
2502
cristybb503372010-05-27 20:51:26 +00002503MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2504 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002505 ExceptionInfo *exception)
2506{
2507#define WorkLoadFactor 0.265
2508
2509 FilterTypes
2510 filter_type;
2511
2512 Image
2513 *filter_image,
2514 *resize_image;
2515
cristy9af9b5d2010-08-15 17:04:28 +00002516 MagickOffsetType
2517 offset;
2518
cristy3ed852e2009-09-05 21:47:34 +00002519 MagickRealType
2520 x_factor,
2521 y_factor;
2522
2523 MagickSizeType
2524 span;
2525
2526 MagickStatusType
2527 status;
2528
2529 ResizeFilter
2530 *resize_filter;
2531
cristy3ed852e2009-09-05 21:47:34 +00002532 /*
2533 Acquire resize image.
2534 */
2535 assert(image != (Image *) NULL);
2536 assert(image->signature == MagickSignature);
2537 if (image->debug != MagickFalse)
2538 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2539 assert(exception != (ExceptionInfo *) NULL);
2540 assert(exception->signature == MagickSignature);
2541 if ((columns == 0) || (rows == 0))
2542 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2543 if ((columns == image->columns) && (rows == image->rows) &&
2544 (filter == UndefinedFilter) && (blur == 1.0))
2545 return(CloneImage(image,0,0,MagickTrue,exception));
2546 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2547 if (resize_image == (Image *) NULL)
2548 return(resize_image);
2549 /*
2550 Acquire resize filter.
2551 */
2552 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2553 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2554 if ((x_factor*y_factor) > WorkLoadFactor)
2555 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2556 else
2557 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2558 if (filter_image == (Image *) NULL)
2559 return(DestroyImage(resize_image));
2560 filter_type=LanczosFilter;
2561 if (filter != UndefinedFilter)
2562 filter_type=filter;
2563 else
2564 if ((x_factor == 1.0) && (y_factor == 1.0))
2565 filter_type=PointFilter;
2566 else
2567 if ((image->storage_class == PseudoClass) ||
2568 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2569 filter_type=MitchellFilter;
2570 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2571 exception);
2572 /*
2573 Resize image.
2574 */
cristy9af9b5d2010-08-15 17:04:28 +00002575 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002576 if ((x_factor*y_factor) > WorkLoadFactor)
2577 {
2578 span=(MagickSizeType) (filter_image->columns+rows);
2579 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002580 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002581 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002582 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002583 }
2584 else
2585 {
2586 span=(MagickSizeType) (filter_image->rows+columns);
2587 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002588 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002589 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002590 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002591 }
2592 /*
2593 Free resources.
2594 */
2595 filter_image=DestroyImage(filter_image);
2596 resize_filter=DestroyResizeFilter(resize_filter);
2597 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2598 return((Image *) NULL);
2599 resize_image->type=image->type;
2600 return(resize_image);
2601}
2602
2603/*
2604%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2605% %
2606% %
2607% %
2608% S a m p l e I m a g e %
2609% %
2610% %
2611% %
2612%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2613%
2614% SampleImage() scales an image to the desired dimensions with pixel
2615% sampling. Unlike other scaling methods, this method does not introduce
2616% any additional color into the scaled image.
2617%
2618% The format of the SampleImage method is:
2619%
cristybb503372010-05-27 20:51:26 +00002620% Image *SampleImage(const Image *image,const size_t columns,
2621% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002622%
2623% A description of each parameter follows:
2624%
2625% o image: the image.
2626%
2627% o columns: the number of columns in the sampled image.
2628%
2629% o rows: the number of rows in the sampled image.
2630%
2631% o exception: return any errors or warnings in this structure.
2632%
2633*/
cristybb503372010-05-27 20:51:26 +00002634MagickExport Image *SampleImage(const Image *image,const size_t columns,
2635 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002636{
2637#define SampleImageTag "Sample/Image"
2638
cristyc4c8d132010-01-07 01:58:38 +00002639 CacheView
2640 *image_view,
2641 *sample_view;
2642
cristy3ed852e2009-09-05 21:47:34 +00002643 Image
2644 *sample_image;
2645
cristy3ed852e2009-09-05 21:47:34 +00002646 MagickBooleanType
2647 status;
2648
cristy5f959472010-05-27 22:19:46 +00002649 MagickOffsetType
2650 progress;
2651
cristybb503372010-05-27 20:51:26 +00002652 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002653 x;
2654
cristy5f959472010-05-27 22:19:46 +00002655 ssize_t
2656 *x_offset,
2657 y;
2658
cristy3ed852e2009-09-05 21:47:34 +00002659 /*
2660 Initialize sampled image attributes.
2661 */
2662 assert(image != (const Image *) NULL);
2663 assert(image->signature == MagickSignature);
2664 if (image->debug != MagickFalse)
2665 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2666 assert(exception != (ExceptionInfo *) NULL);
2667 assert(exception->signature == MagickSignature);
2668 if ((columns == 0) || (rows == 0))
2669 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2670 if ((columns == image->columns) && (rows == image->rows))
2671 return(CloneImage(image,0,0,MagickTrue,exception));
2672 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2673 if (sample_image == (Image *) NULL)
2674 return((Image *) NULL);
2675 /*
2676 Allocate scan line buffer and column offset buffers.
2677 */
cristybb503372010-05-27 20:51:26 +00002678 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002679 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002680 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002681 {
2682 sample_image=DestroyImage(sample_image);
2683 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2684 }
cristybb503372010-05-27 20:51:26 +00002685 for (x=0; x < (ssize_t) sample_image->columns; x++)
2686 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002687 sample_image->columns);
2688 /*
2689 Sample each row.
2690 */
2691 status=MagickTrue;
2692 progress=0;
2693 image_view=AcquireCacheView(image);
2694 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002695#if defined(MAGICKCORE_OPENMP_SUPPORT)
2696 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002697#endif
cristybb503372010-05-27 20:51:26 +00002698 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002699 {
cristy3ed852e2009-09-05 21:47:34 +00002700 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002701 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002702
2703 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002704 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002705
2706 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002707 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002708
cristy3ed852e2009-09-05 21:47:34 +00002709 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002710 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002711
cristy03dbbd22010-09-19 23:04:47 +00002712 register ssize_t
2713 x;
2714
cristy9af9b5d2010-08-15 17:04:28 +00002715 ssize_t
2716 y_offset;
2717
cristy3ed852e2009-09-05 21:47:34 +00002718 if (status == MagickFalse)
2719 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002720 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2721 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002722 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2723 exception);
2724 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2725 exception);
2726 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2727 {
2728 status=MagickFalse;
2729 continue;
2730 }
2731 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2732 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2733 /*
2734 Sample each column.
2735 */
cristybb503372010-05-27 20:51:26 +00002736 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002737 *q++=p[x_offset[x]];
2738 if ((image->storage_class == PseudoClass) ||
2739 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002740 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002741 sample_indexes[x]=indexes[x_offset[x]];
2742 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2743 status=MagickFalse;
2744 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2745 {
2746 MagickBooleanType
2747 proceed;
2748
cristyb5d5f722009-11-04 03:03:49 +00002749#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002750 #pragma omp critical (MagickCore_SampleImage)
2751#endif
2752 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2753 if (proceed == MagickFalse)
2754 status=MagickFalse;
2755 }
2756 }
2757 image_view=DestroyCacheView(image_view);
2758 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002759 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002760 sample_image->type=image->type;
2761 return(sample_image);
2762}
2763
2764/*
2765%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2766% %
2767% %
2768% %
2769% S c a l e I m a g e %
2770% %
2771% %
2772% %
2773%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2774%
2775% ScaleImage() changes the size of an image to the given dimensions.
2776%
2777% The format of the ScaleImage method is:
2778%
cristybb503372010-05-27 20:51:26 +00002779% Image *ScaleImage(const Image *image,const size_t columns,
2780% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002781%
2782% A description of each parameter follows:
2783%
2784% o image: the image.
2785%
2786% o columns: the number of columns in the scaled image.
2787%
2788% o rows: the number of rows in the scaled image.
2789%
2790% o exception: return any errors or warnings in this structure.
2791%
2792*/
cristybb503372010-05-27 20:51:26 +00002793MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2794 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002795{
2796#define ScaleImageTag "Scale/Image"
2797
cristyed6cb232010-01-20 03:07:53 +00002798 CacheView
2799 *image_view,
2800 *scale_view;
2801
cristy3ed852e2009-09-05 21:47:34 +00002802 Image
2803 *scale_image;
2804
cristy3ed852e2009-09-05 21:47:34 +00002805 MagickBooleanType
2806 next_column,
2807 next_row,
2808 proceed;
2809
2810 MagickPixelPacket
2811 pixel,
2812 *scale_scanline,
2813 *scanline,
2814 *x_vector,
2815 *y_vector,
2816 zero;
2817
cristy3ed852e2009-09-05 21:47:34 +00002818 PointInfo
2819 scale,
2820 span;
2821
cristybb503372010-05-27 20:51:26 +00002822 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002823 i;
2824
cristy9af9b5d2010-08-15 17:04:28 +00002825 ssize_t
2826 number_rows,
2827 y;
2828
cristy3ed852e2009-09-05 21:47:34 +00002829 /*
2830 Initialize scaled image attributes.
2831 */
2832 assert(image != (const Image *) NULL);
2833 assert(image->signature == MagickSignature);
2834 if (image->debug != MagickFalse)
2835 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2836 assert(exception != (ExceptionInfo *) NULL);
2837 assert(exception->signature == MagickSignature);
2838 if ((columns == 0) || (rows == 0))
2839 return((Image *) NULL);
2840 if ((columns == image->columns) && (rows == image->rows))
2841 return(CloneImage(image,0,0,MagickTrue,exception));
2842 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2843 if (scale_image == (Image *) NULL)
2844 return((Image *) NULL);
2845 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2846 {
2847 InheritException(exception,&scale_image->exception);
2848 scale_image=DestroyImage(scale_image);
2849 return((Image *) NULL);
2850 }
2851 /*
2852 Allocate memory.
2853 */
2854 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2855 sizeof(*x_vector));
2856 scanline=x_vector;
2857 if (image->rows != scale_image->rows)
2858 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2859 sizeof(*scanline));
2860 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2861 scale_image->columns,sizeof(*scale_scanline));
2862 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2863 sizeof(*y_vector));
2864 if ((scanline == (MagickPixelPacket *) NULL) ||
2865 (scale_scanline == (MagickPixelPacket *) NULL) ||
2866 (x_vector == (MagickPixelPacket *) NULL) ||
2867 (y_vector == (MagickPixelPacket *) NULL))
2868 {
2869 scale_image=DestroyImage(scale_image);
2870 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2871 }
2872 /*
2873 Scale image.
2874 */
2875 number_rows=0;
2876 next_row=MagickTrue;
2877 span.y=1.0;
2878 scale.y=(double) scale_image->rows/(double) image->rows;
2879 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2880 sizeof(*y_vector));
2881 GetMagickPixelPacket(image,&pixel);
2882 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2883 i=0;
cristyed6cb232010-01-20 03:07:53 +00002884 image_view=AcquireCacheView(image);
2885 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002886 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002887 {
2888 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002889 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002890
2891 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002892 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002893
2894 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002895 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002896
cristy3ed852e2009-09-05 21:47:34 +00002897 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002898 *restrict s,
2899 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002900
2901 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002902 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002903
cristy9af9b5d2010-08-15 17:04:28 +00002904 register ssize_t
2905 x;
2906
cristyed6cb232010-01-20 03:07:53 +00002907 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2908 exception);
cristy3ed852e2009-09-05 21:47:34 +00002909 if (q == (PixelPacket *) NULL)
2910 break;
2911 scale_indexes=GetAuthenticIndexQueue(scale_image);
2912 if (scale_image->rows == image->rows)
2913 {
2914 /*
2915 Read a new scanline.
2916 */
cristyed6cb232010-01-20 03:07:53 +00002917 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2918 exception);
cristy3ed852e2009-09-05 21:47:34 +00002919 if (p == (const PixelPacket *) NULL)
2920 break;
cristyed6cb232010-01-20 03:07:53 +00002921 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002922 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002923 {
cristyce70c172010-01-07 17:15:30 +00002924 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2925 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2926 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002927 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002928 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002929 if (indexes != (IndexPacket *) NULL)
2930 x_vector[x].index=(MagickRealType) indexes[x];
2931 p++;
2932 }
2933 }
2934 else
2935 {
2936 /*
2937 Scale Y direction.
2938 */
2939 while (scale.y < span.y)
2940 {
cristy9af9b5d2010-08-15 17:04:28 +00002941 if ((next_row != MagickFalse) &&
2942 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002943 {
2944 /*
2945 Read a new scanline.
2946 */
cristyed6cb232010-01-20 03:07:53 +00002947 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2948 exception);
cristy3ed852e2009-09-05 21:47:34 +00002949 if (p == (const PixelPacket *) NULL)
2950 break;
cristyed6cb232010-01-20 03:07:53 +00002951 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002952 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002953 {
cristyce70c172010-01-07 17:15:30 +00002954 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2955 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2956 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002957 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002958 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002959 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002960 if (indexes != (IndexPacket *) NULL)
2961 x_vector[x].index=(MagickRealType) indexes[x];
2962 p++;
2963 }
2964 number_rows++;
2965 }
cristybb503372010-05-27 20:51:26 +00002966 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002967 {
2968 y_vector[x].red+=scale.y*x_vector[x].red;
2969 y_vector[x].green+=scale.y*x_vector[x].green;
2970 y_vector[x].blue+=scale.y*x_vector[x].blue;
2971 if (scale_image->matte != MagickFalse)
2972 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2973 if (scale_indexes != (IndexPacket *) NULL)
2974 y_vector[x].index+=scale.y*x_vector[x].index;
2975 }
2976 span.y-=scale.y;
2977 scale.y=(double) scale_image->rows/(double) image->rows;
2978 next_row=MagickTrue;
2979 }
cristybb503372010-05-27 20:51:26 +00002980 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002981 {
2982 /*
2983 Read a new scanline.
2984 */
cristyed6cb232010-01-20 03:07:53 +00002985 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2986 exception);
cristy3ed852e2009-09-05 21:47:34 +00002987 if (p == (const PixelPacket *) NULL)
2988 break;
cristyed6cb232010-01-20 03:07:53 +00002989 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002990 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002991 {
cristyce70c172010-01-07 17:15:30 +00002992 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2993 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2994 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002995 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00002996 x_vector[x].opacity=(MagickRealType)
2997 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002998 if (indexes != (IndexPacket *) NULL)
2999 x_vector[x].index=(MagickRealType) indexes[x];
3000 p++;
3001 }
3002 number_rows++;
3003 next_row=MagickFalse;
3004 }
3005 s=scanline;
cristybb503372010-05-27 20:51:26 +00003006 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003007 {
3008 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3009 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3010 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3011 if (image->matte != MagickFalse)
3012 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3013 if (scale_indexes != (IndexPacket *) NULL)
3014 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3015 s->red=pixel.red;
3016 s->green=pixel.green;
3017 s->blue=pixel.blue;
3018 if (scale_image->matte != MagickFalse)
3019 s->opacity=pixel.opacity;
3020 if (scale_indexes != (IndexPacket *) NULL)
3021 s->index=pixel.index;
3022 s++;
3023 y_vector[x]=zero;
3024 }
3025 scale.y-=span.y;
3026 if (scale.y <= 0)
3027 {
3028 scale.y=(double) scale_image->rows/(double) image->rows;
3029 next_row=MagickTrue;
3030 }
3031 span.y=1.0;
3032 }
3033 if (scale_image->columns == image->columns)
3034 {
3035 /*
3036 Transfer scanline to scaled image.
3037 */
3038 s=scanline;
cristybb503372010-05-27 20:51:26 +00003039 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003040 {
cristyce70c172010-01-07 17:15:30 +00003041 q->red=ClampToQuantum(s->red);
3042 q->green=ClampToQuantum(s->green);
3043 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003044 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003045 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003046 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003047 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003048 q++;
3049 s++;
3050 }
3051 }
3052 else
3053 {
3054 /*
3055 Scale X direction.
3056 */
3057 pixel=zero;
3058 next_column=MagickFalse;
3059 span.x=1.0;
3060 s=scanline;
3061 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003062 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003063 {
3064 scale.x=(double) scale_image->columns/(double) image->columns;
3065 while (scale.x >= span.x)
3066 {
3067 if (next_column != MagickFalse)
3068 {
3069 pixel=zero;
3070 t++;
3071 }
3072 pixel.red+=span.x*s->red;
3073 pixel.green+=span.x*s->green;
3074 pixel.blue+=span.x*s->blue;
3075 if (image->matte != MagickFalse)
3076 pixel.opacity+=span.x*s->opacity;
3077 if (scale_indexes != (IndexPacket *) NULL)
3078 pixel.index+=span.x*s->index;
3079 t->red=pixel.red;
3080 t->green=pixel.green;
3081 t->blue=pixel.blue;
3082 if (scale_image->matte != MagickFalse)
3083 t->opacity=pixel.opacity;
3084 if (scale_indexes != (IndexPacket *) NULL)
3085 t->index=pixel.index;
3086 scale.x-=span.x;
3087 span.x=1.0;
3088 next_column=MagickTrue;
3089 }
3090 if (scale.x > 0)
3091 {
3092 if (next_column != MagickFalse)
3093 {
3094 pixel=zero;
3095 next_column=MagickFalse;
3096 t++;
3097 }
3098 pixel.red+=scale.x*s->red;
3099 pixel.green+=scale.x*s->green;
3100 pixel.blue+=scale.x*s->blue;
3101 if (scale_image->matte != MagickFalse)
3102 pixel.opacity+=scale.x*s->opacity;
3103 if (scale_indexes != (IndexPacket *) NULL)
3104 pixel.index+=scale.x*s->index;
3105 span.x-=scale.x;
3106 }
3107 s++;
3108 }
3109 if (span.x > 0)
3110 {
3111 s--;
3112 pixel.red+=span.x*s->red;
3113 pixel.green+=span.x*s->green;
3114 pixel.blue+=span.x*s->blue;
3115 if (scale_image->matte != MagickFalse)
3116 pixel.opacity+=span.x*s->opacity;
3117 if (scale_indexes != (IndexPacket *) NULL)
3118 pixel.index+=span.x*s->index;
3119 }
3120 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003121 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003122 {
3123 t->red=pixel.red;
3124 t->green=pixel.green;
3125 t->blue=pixel.blue;
3126 if (scale_image->matte != MagickFalse)
3127 t->opacity=pixel.opacity;
3128 if (scale_indexes != (IndexPacket *) NULL)
3129 t->index=pixel.index;
3130 }
3131 /*
3132 Transfer scanline to scaled image.
3133 */
3134 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003135 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003136 {
cristyce70c172010-01-07 17:15:30 +00003137 q->red=ClampToQuantum(t->red);
3138 q->green=ClampToQuantum(t->green);
3139 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003140 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003141 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003142 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003143 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003144 t++;
3145 q++;
3146 }
3147 }
cristyed6cb232010-01-20 03:07:53 +00003148 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003149 break;
cristy96b16132010-08-29 17:19:52 +00003150 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3151 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003152 if (proceed == MagickFalse)
3153 break;
3154 }
cristyed6cb232010-01-20 03:07:53 +00003155 scale_view=DestroyCacheView(scale_view);
3156 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003157 /*
3158 Free allocated memory.
3159 */
3160 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3161 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3162 if (scale_image->rows != image->rows)
3163 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3164 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3165 scale_image->type=image->type;
3166 return(scale_image);
3167}
3168
anthony02b4cb42010-10-10 04:54:35 +00003169#if 0
3170 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003171/*
3172%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3173% %
3174% %
3175% %
3176+ S e t R e s i z e F i l t e r S u p p o r t %
3177% %
3178% %
3179% %
3180%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3181%
3182% SetResizeFilterSupport() specifies which IR filter to use to window
3183%
3184% The format of the SetResizeFilterSupport method is:
3185%
3186% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3187% const MagickRealType support)
3188%
3189% A description of each parameter follows:
3190%
3191% o resize_filter: the resize filter.
3192%
3193% o support: the filter spport radius.
3194%
3195*/
3196MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3197 const MagickRealType support)
3198{
3199 assert(resize_filter != (ResizeFilter *) NULL);
3200 assert(resize_filter->signature == MagickSignature);
3201 resize_filter->support=support;
3202}
anthony02b4cb42010-10-10 04:54:35 +00003203#endif
cristy3ed852e2009-09-05 21:47:34 +00003204
3205/*
3206%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3207% %
3208% %
3209% %
3210% T h u m b n a i l I m a g e %
3211% %
3212% %
3213% %
3214%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3215%
3216% ThumbnailImage() changes the size of an image to the given dimensions and
3217% removes any associated profiles. The goal is to produce small low cost
3218% thumbnail images suited for display on the Web.
3219%
3220% The format of the ThumbnailImage method is:
3221%
cristybb503372010-05-27 20:51:26 +00003222% Image *ThumbnailImage(const Image *image,const size_t columns,
3223% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003224%
3225% A description of each parameter follows:
3226%
3227% o image: the image.
3228%
3229% o columns: the number of columns in the scaled image.
3230%
3231% o rows: the number of rows in the scaled image.
3232%
3233% o exception: return any errors or warnings in this structure.
3234%
3235*/
cristy9af9b5d2010-08-15 17:04:28 +00003236MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3237 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003238{
3239#define SampleFactor 5
3240
3241 char
3242 value[MaxTextExtent];
3243
3244 const char
3245 *name;
3246
3247 Image
3248 *thumbnail_image;
3249
3250 MagickRealType
3251 x_factor,
3252 y_factor;
3253
cristybb503372010-05-27 20:51:26 +00003254 size_t
cristy3ed852e2009-09-05 21:47:34 +00003255 version;
3256
cristy9af9b5d2010-08-15 17:04:28 +00003257 struct stat
3258 attributes;
3259
cristy3ed852e2009-09-05 21:47:34 +00003260 assert(image != (Image *) NULL);
3261 assert(image->signature == MagickSignature);
3262 if (image->debug != MagickFalse)
3263 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3264 assert(exception != (ExceptionInfo *) NULL);
3265 assert(exception->signature == MagickSignature);
3266 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3267 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3268 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003269 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3270 exception);
cristy3ed852e2009-09-05 21:47:34 +00003271 else
3272 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003273 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3274 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003275 else
3276 {
3277 Image
3278 *sample_image;
3279
3280 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3281 exception);
3282 if (sample_image == (Image *) NULL)
3283 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003284 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3285 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003286 sample_image=DestroyImage(sample_image);
3287 }
3288 if (thumbnail_image == (Image *) NULL)
3289 return(thumbnail_image);
3290 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3291 if (thumbnail_image->matte == MagickFalse)
3292 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3293 thumbnail_image->depth=8;
3294 thumbnail_image->interlace=NoInterlace;
3295 /*
3296 Strip all profiles except color profiles.
3297 */
3298 ResetImageProfileIterator(thumbnail_image);
3299 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3300 {
3301 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3302 {
cristy2b726bd2010-01-11 01:05:39 +00003303 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003304 ResetImageProfileIterator(thumbnail_image);
3305 }
3306 name=GetNextImageProfile(thumbnail_image);
3307 }
3308 (void) DeleteImageProperty(thumbnail_image,"comment");
3309 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003310 if (strstr(image->magick_filename,"//") == (char *) NULL)
3311 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003312 image->magick_filename);
3313 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3314 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3315 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3316 {
cristye8c25f92010-06-03 00:53:06 +00003317 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003318 attributes.st_mtime);
3319 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3320 }
cristye8c25f92010-06-03 00:53:06 +00003321 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003322 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003323 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003324 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003325 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3326 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3327 LocaleLower(value);
3328 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3329 (void) SetImageProperty(thumbnail_image,"software",
3330 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003331 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3332 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003333 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003334 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003335 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003336 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003337 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3338 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003339 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3340 return(thumbnail_image);
3341}