blob: d78e9496fef53be6b6382dcfac090f4a73e934b1 [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*),
nicolas43829bb2010-10-16 18:58:17 +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
nicolas69793ef2010-10-16 19:24:30 +0000723 windowed function. Typically equal to the location
724 of the first crossing. */
nicolas43829bb2010-10-16 18:58:17 +0000725 B,C; /* Cubic 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 */
743 { Jinc, 3.0, 1.21967, 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) */
anthony08958462010-10-12 06:48:35 +0000752 { Jinc, 2.0, 1.21966989, 0.0, 0.0 }, /* Lanczos2D (Jinc-Jinc) */
753 { Jinc, 2.0, 1.16848499, 0.0, 0.0 }, /* Lanczos2D Sharpened */
nicolas198004e2010-10-16 19:19:35 +0000754 { CubicBC, 2.0, 1.1685777620836932, 0.37821575509399867,
755 0.31089212245300067 }
nicolas0134bbe2010-10-10 15:55:42 +0000756 /* Robidoux: Keys cubic close to Lanczos2D with blur=0.958033808 */
cristy3ed852e2009-09-05 21:47:34 +0000757 };
758 /*
anthony9a98fc62010-10-11 02:47:19 +0000759 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
760 function being used as a filter. It is used by the "filter:lobes" and for
761 the 'lobes' number in the above, the for support selection, so users do
762 not have to deal with the highly irrational sizes of the 'lobes' of the
763 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000764
nicolase473f722010-10-07 00:05:13 +0000765 Values taken from
anthony48f77622010-10-03 14:32:31 +0000766 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000767 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000768 */
769 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000770 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000771 {
anthonyc2d07db2010-09-15 23:47:40 +0000772 1.21966989126651,
773 2.23313059438153,
774 3.23831548416624,
775 4.24106286379607,
776 5.24276437687019,
777 6.24392168986449,
778 7.24475986871996,
779 8.24539491395205,
780 9.24589268494948,
781 10.2462933487549,
782 11.2466227948779,
783 12.2468984611381,
784 13.2471325221811,
785 14.2473337358069,
786 15.2475085630373,
787 16.247661874701
cristy3ed852e2009-09-05 21:47:34 +0000788 };
789
cristy33b1c162010-01-23 22:51:51 +0000790 /*
791 Allocate resize filter.
792 */
cristy3ed852e2009-09-05 21:47:34 +0000793 assert(image != (const Image *) NULL);
794 assert(image->signature == MagickSignature);
795 if (image->debug != MagickFalse)
796 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
797 assert(UndefinedFilter < filter && filter < SentinelFilter);
798 assert(exception != (ExceptionInfo *) NULL);
799 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000800 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000801 if (resize_filter == (ResizeFilter *) NULL)
802 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000803 /*
804 Defaults for the requested filter.
805 */
806 filter_type=mapping[filter].filter;
807 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000808 resize_filter->blur = blur;
809 sigma = 0.5;
anthony48f77622010-10-03 14:32:31 +0000810 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000811 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000812 switch (filter_type)
813 {
814 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000815 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000816 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000817 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000818 break;
anthonyba5a7c32010-09-15 02:42:25 +0000819 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000820 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000821 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000822 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000823 break;
cristy33b1c162010-01-23 22:51:51 +0000824 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000825 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000826 filter_type=JincFilter;
827 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000828 break;
anthony08958462010-10-12 06:48:35 +0000829 case Lanczos2DSharpFilter:
830 /* Sharpened by Nicholas Robidoux so as to optimize for
831 * minimal blurring of orthogonal lines
832 */
833 resize_filter->blur *= 0.958033808;
834 break;
anthonyf5e76ef2010-10-12 01:22:01 +0000835 case GaussianFilter:
cristy68f103e2010-10-14 01:19:07 +0000836 sigma = (MagickRealType) (MagickSQ2/2.0); /* Cylindrical Gaussian sigma is sqrt(2)/2 */
anthonyf5e76ef2010-10-12 01:22:01 +0000837 break;
cristya782ecf2010-01-25 02:59:14 +0000838 default:
839 break;
cristy3ed852e2009-09-05 21:47:34 +0000840 }
anthony61b5ddd2010-10-05 02:33:31 +0000841 else
842 switch (filter_type)
843 {
844 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000845 case Lanczos2DSharpFilter:
nicolas45b58a92010-10-07 15:46:39 +0000846 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000847 window_type=SincFastFilter;
848 break;
849 default:
850 break;
851 }
852
cristy3ed852e2009-09-05 21:47:34 +0000853 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000854 if (artifact != (const char *) NULL)
855 {
cristy9af9b5d2010-08-15 17:04:28 +0000856 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000857 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000858 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000859 filter_type=(FilterTypes) option;
860 window_type=BoxFilter;
861 }
862 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000863 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000864 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000865 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000866 }
nicolas07bac812010-09-19 18:47:02 +0000867 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000868 artifact=GetImageArtifact(image,"filter:window");
869 if (artifact != (const char *) NULL)
870 {
cristy9af9b5d2010-08-15 17:04:28 +0000871 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000872 if ((UndefinedFilter < option) && (option < SentinelFilter))
873 {
874 if (option != LanczosFilter)
875 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000876 else
anthony48f77622010-10-03 14:32:31 +0000877 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000878 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000879 }
cristy33b1c162010-01-23 22:51:51 +0000880 }
cristy3ed852e2009-09-05 21:47:34 +0000881 }
cristy33b1c162010-01-23 22:51:51 +0000882 else
883 {
anthony48f77622010-10-03 14:32:31 +0000884 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000885 artifact=GetImageArtifact(image,"filter:window");
886 if (artifact != (const char *) NULL)
887 {
888 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
889 artifact);
890 if ((UndefinedFilter < option) && (option < SentinelFilter))
891 {
anthony61b5ddd2010-10-05 02:33:31 +0000892 filter_type=cylindrical != MagickFalse ?
893 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000894 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000895 }
896 }
897 }
nicolas07bac812010-09-19 18:47:02 +0000898 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000899 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000900 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000901 resize_filter->window=filters[window_type].function;
902 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000903 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000904
anthonyf5e76ef2010-10-12 01:22:01 +0000905 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000906 if (cylindrical != MagickFalse)
907 switch (filter_type)
908 {
909 case PointFilter:
910 case BoxFilter:
911 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000912 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000913 break;
anthony81b8bf92010-10-02 13:54:34 +0000914 default:
915 break;
anthony10b8bc82010-10-02 12:48:46 +0000916 }
anthony61b5ddd2010-10-05 02:33:31 +0000917 else
918 switch (filter_type)
919 {
920 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000921 case Lanczos2DSharpFilter:
anthony853d6972010-10-08 06:01:31 +0000922 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000923 resize_filter->filter=SincFast;
924 break;
925 default:
926 break;
927 }
928
anthonyf5e76ef2010-10-12 01:22:01 +0000929 /*
930 ** More Expert Option Modifications
931 */
932
933 /* User Sigma Override - no support change */
934 artifact=GetImageArtifact(image,"filter:sigma");
935 if (artifact != (const char *) NULL)
936 sigma=StringToDouble(artifact);
937 /* Define coefficents for Gaussian (assumes no cubic window) */
938 if ( GaussianFilter ) {
939 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
cristy68f103e2010-10-14 01:19:07 +0000940 resize_filter->coeff[1] = (MagickRealType) (1.0/(Magick2PI*sigma*sigma)); /* unused */
anthonyf5e76ef2010-10-12 01:22:01 +0000941 }
942
943 /* Blur Override */
944 artifact=GetImageArtifact(image,"filter:blur");
945 if (artifact != (const char *) NULL)
946 resize_filter->blur=StringToDouble(artifact);
947 if (resize_filter->blur < MagickEpsilon)
948 resize_filter->blur=(MagickRealType) MagickEpsilon;
949
950 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000951 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000952 if (artifact != (const char *) NULL)
953 {
cristybb503372010-05-27 20:51:26 +0000954 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000955 lobes;
956
cristy96b16132010-08-29 17:19:52 +0000957 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000958 if (lobes < 1)
959 lobes=1;
960 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000961 }
anthony61b5ddd2010-10-05 02:33:31 +0000962 /* convert Jinc lobes to a real support value */
963 if (resize_filter->filter == Jinc)
964 {
965 if (resize_filter->support > 16)
966 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
967 else
968 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
969 }
970 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000971 artifact=GetImageArtifact(image,"filter:support");
972 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000973 resize_filter->support=fabs(StringToDouble(artifact));
974 /*
nicolas07bac812010-09-19 18:47:02 +0000975 Scale windowing function separatally to the support 'clipping'
976 window that calling operator is planning to actually use. (Expert
977 override)
cristy3ed852e2009-09-05 21:47:34 +0000978 */
anthony55f12332010-09-10 01:13:02 +0000979 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000980 artifact=GetImageArtifact(image,"filter:win-support");
981 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000982 resize_filter->window_support=fabs(StringToDouble(artifact));
983 /*
anthony1f90a6b2010-09-14 08:56:31 +0000984 Adjust window function scaling to the windowing support for
985 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000986 */
987 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +0000988
anthony55f12332010-09-10 01:13:02 +0000989 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000990 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000991 */
cristy3ed852e2009-09-05 21:47:34 +0000992 B=0.0;
993 C=0.0;
cristy33b1c162010-01-23 22:51:51 +0000994 if ((filters[filter_type].function == CubicBC) ||
995 (filters[window_type].function == CubicBC))
996 {
anthony2d9b8b52010-09-14 08:31:07 +0000997 B=filters[filter_type].B;
998 C=filters[filter_type].C;
999 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +00001000 {
anthony2d9b8b52010-09-14 08:31:07 +00001001 B=filters[window_type].B;
1002 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +00001003 }
cristy33b1c162010-01-23 22:51:51 +00001004 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001005 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001006 {
1007 B=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001008 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001009 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001010 if (artifact != (const char *) NULL)
1011 C=StringToDouble(artifact);
1012 }
1013 else
1014 {
1015 artifact=GetImageArtifact(image,"filter:c");
1016 if (artifact != (const char *) NULL)
1017 {
1018 C=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001019 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001020 }
1021 }
anthonyf5e76ef2010-10-12 01:22:01 +00001022 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
1023 resize_filter->coeff[0]=(6.0-2.0*B)/6.0;
1024 resize_filter->coeff[1]=0.0;
1025 resize_filter->coeff[2]=(-18.0+12.0*B+6.0*C)/6.0;
1026 resize_filter->coeff[3]=(12.0-9.0*B-6.0*C)/6.0;
1027 resize_filter->coeff[4]=(8.0*B+24.0*C)/6.0;
1028 resize_filter->coeff[5]=(-12.0*B-48.0*C)/6.0;
1029 resize_filter->coeff[6]=(6.0*B+30.0*C)/6.0;
1030 resize_filter->coeff[7]=(-B-6.0*C)/6.0;
cristy3ed852e2009-09-05 21:47:34 +00001031 }
anthonyf5e76ef2010-10-12 01:22:01 +00001032
anthony55f12332010-09-10 01:13:02 +00001033 /*
nicolas07bac812010-09-19 18:47:02 +00001034 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001035 */
cristyf5b49372010-10-16 01:06:47 +00001036#if defined(MAGICKCORE_OPENMP_SUPPORT)
1037 #pragma omp master
1038 {
1039#endif
1040 artifact=GetImageArtifact(image,"filter:verbose");
1041 if (artifact != (const char *) NULL)
1042 {
1043 double
1044 support,
1045 x;
cristy3ed852e2009-09-05 21:47:34 +00001046
cristyf5b49372010-10-16 01:06:47 +00001047 /*
1048 Set the weighting function properly when the weighting
1049 function may not exactly match the filter of the same name.
1050 EG: a Point filter really uses a Box weighting function
1051 with a different support than is typically used.
anthony463be1d2010-09-26 01:07:36 +00001052
cristyf5b49372010-10-16 01:06:47 +00001053 */
1054 if (resize_filter->filter == Box) filter_type=BoxFilter;
1055 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1056 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1057 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1058 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1059 /*
1060 Report Filter Details.
1061 */
1062 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1063 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
1064 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1065 MagickFilterOptions,filter_type));
1066 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
1067 MagickFilterOptions, window_type));
1068 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
1069 (double) resize_filter->support);
1070 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
1071 (double) resize_filter->window_support);
1072 (void) fprintf(stdout,"# scale_blur = %.*g\n",GetMagickPrecision(),
1073 (double) resize_filter->blur);
1074 if ( filter_type == GaussianFilter )
1075 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",GetMagickPrecision(),
1076 (double) sigma);
1077 (void) fprintf(stdout,"# practical_support = %.*g\n",GetMagickPrecision(),
1078 (double) support);
1079 if ( filter_type == CubicFilter || window_type == CubicFilter )
1080 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1081 (double) B,GetMagickPrecision(),(double) C);
1082 (void) fprintf(stdout,"\n");
1083 /*
1084 Output values of resulting filter graph -- for graphing
1085 filter result.
1086 */
1087 for (x=0.0; x <= support; x+=0.01f)
1088 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1089 (double) GetResizeFilterWeight(resize_filter,x));
1090 /* A final value so gnuplot can graph the 'stop' properly. */
1091 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1092 0.0);
1093 }
1094 /* Output the above once only for each image - remove setting */
1095 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1096#if defined(MAGICKCORE_OPENMP_SUPPORT)
1097 }
1098#endif
cristy3ed852e2009-09-05 21:47:34 +00001099 return(resize_filter);
1100}
1101
1102/*
1103%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1104% %
1105% %
1106% %
1107% A d a p t i v e R e s i z e I m a g e %
1108% %
1109% %
1110% %
1111%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1112%
1113% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1114%
1115% The format of the AdaptiveResizeImage method is:
1116%
cristy9af9b5d2010-08-15 17:04:28 +00001117% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1118% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001119%
1120% A description of each parameter follows:
1121%
1122% o image: the image.
1123%
1124% o columns: the number of columns in the resized image.
1125%
1126% o rows: the number of rows in the resized image.
1127%
1128% o exception: return any errors or warnings in this structure.
1129%
1130*/
1131MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001132 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001133{
1134#define AdaptiveResizeImageTag "Resize/Image"
1135
cristyc4c8d132010-01-07 01:58:38 +00001136 CacheView
1137 *resize_view;
1138
cristy3ed852e2009-09-05 21:47:34 +00001139 Image
1140 *resize_image;
1141
cristy3ed852e2009-09-05 21:47:34 +00001142 MagickBooleanType
1143 proceed;
1144
1145 MagickPixelPacket
1146 pixel;
1147
1148 PointInfo
1149 offset;
1150
1151 ResampleFilter
1152 *resample_filter;
1153
cristy9af9b5d2010-08-15 17:04:28 +00001154 ssize_t
1155 y;
1156
cristy3ed852e2009-09-05 21:47:34 +00001157 /*
1158 Adaptively resize image.
1159 */
1160 assert(image != (const Image *) NULL);
1161 assert(image->signature == MagickSignature);
1162 if (image->debug != MagickFalse)
1163 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1164 assert(exception != (ExceptionInfo *) NULL);
1165 assert(exception->signature == MagickSignature);
1166 if ((columns == 0) || (rows == 0))
1167 return((Image *) NULL);
1168 if ((columns == image->columns) && (rows == image->rows))
1169 return(CloneImage(image,0,0,MagickTrue,exception));
1170 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1171 if (resize_image == (Image *) NULL)
1172 return((Image *) NULL);
1173 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1174 {
1175 InheritException(exception,&resize_image->exception);
1176 resize_image=DestroyImage(resize_image);
1177 return((Image *) NULL);
1178 }
1179 GetMagickPixelPacket(image,&pixel);
1180 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001181 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001182 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001183 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001184 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001185 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001186 {
1187 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001188 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001189
cristybb503372010-05-27 20:51:26 +00001190 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001191 x;
1192
1193 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001194 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001195
1196 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1197 exception);
1198 if (q == (PixelPacket *) NULL)
1199 break;
1200 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1201 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001202 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001203 {
1204 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1205 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1206 &pixel);
1207 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1208 q++;
1209 }
1210 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1211 break;
cristy96b16132010-08-29 17:19:52 +00001212 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1213 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001214 if (proceed == MagickFalse)
1215 break;
1216 }
1217 resample_filter=DestroyResampleFilter(resample_filter);
1218 resize_view=DestroyCacheView(resize_view);
1219 return(resize_image);
1220}
1221
1222/*
1223%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1224% %
1225% %
1226% %
1227+ B e s s e l O r d e r O n e %
1228% %
1229% %
1230% %
1231%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1232%
1233% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001234% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001235%
1236% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1237%
1238% j1(x) = x*j1(x);
1239%
1240% For x in (8,inf)
1241%
1242% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1243%
1244% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1245%
1246% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1247% = 1/sqrt(2) * (sin(x) - cos(x))
1248% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1249% = -1/sqrt(2) * (sin(x) + cos(x))
1250%
1251% The format of the BesselOrderOne method is:
1252%
1253% MagickRealType BesselOrderOne(MagickRealType x)
1254%
1255% A description of each parameter follows:
1256%
1257% o x: MagickRealType value.
1258%
1259*/
1260
1261#undef I0
1262static MagickRealType I0(MagickRealType x)
1263{
1264 MagickRealType
1265 sum,
1266 t,
1267 y;
1268
cristybb503372010-05-27 20:51:26 +00001269 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001270 i;
1271
1272 /*
1273 Zeroth order Bessel function of the first kind.
1274 */
1275 sum=1.0;
1276 y=x*x/4.0;
1277 t=y;
1278 for (i=2; t > MagickEpsilon; i++)
1279 {
1280 sum+=t;
1281 t*=y/((MagickRealType) i*i);
1282 }
1283 return(sum);
1284}
1285
1286#undef J1
1287static MagickRealType J1(MagickRealType x)
1288{
1289 MagickRealType
1290 p,
1291 q;
1292
cristybb503372010-05-27 20:51:26 +00001293 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001294 i;
1295
1296 static const double
1297 Pone[] =
1298 {
1299 0.581199354001606143928050809e+21,
1300 -0.6672106568924916298020941484e+20,
1301 0.2316433580634002297931815435e+19,
1302 -0.3588817569910106050743641413e+17,
1303 0.2908795263834775409737601689e+15,
1304 -0.1322983480332126453125473247e+13,
1305 0.3413234182301700539091292655e+10,
1306 -0.4695753530642995859767162166e+7,
1307 0.270112271089232341485679099e+4
1308 },
1309 Qone[] =
1310 {
1311 0.11623987080032122878585294e+22,
1312 0.1185770712190320999837113348e+20,
1313 0.6092061398917521746105196863e+17,
1314 0.2081661221307607351240184229e+15,
1315 0.5243710262167649715406728642e+12,
1316 0.1013863514358673989967045588e+10,
1317 0.1501793594998585505921097578e+7,
1318 0.1606931573481487801970916749e+4,
1319 0.1e+1
1320 };
1321
1322 p=Pone[8];
1323 q=Qone[8];
1324 for (i=7; i >= 0; i--)
1325 {
1326 p=p*x*x+Pone[i];
1327 q=q*x*x+Qone[i];
1328 }
1329 return(p/q);
1330}
1331
1332#undef P1
1333static MagickRealType P1(MagickRealType x)
1334{
1335 MagickRealType
1336 p,
1337 q;
1338
cristybb503372010-05-27 20:51:26 +00001339 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001340 i;
1341
1342 static const double
1343 Pone[] =
1344 {
1345 0.352246649133679798341724373e+5,
1346 0.62758845247161281269005675e+5,
1347 0.313539631109159574238669888e+5,
1348 0.49854832060594338434500455e+4,
1349 0.2111529182853962382105718e+3,
1350 0.12571716929145341558495e+1
1351 },
1352 Qone[] =
1353 {
1354 0.352246649133679798068390431e+5,
1355 0.626943469593560511888833731e+5,
1356 0.312404063819041039923015703e+5,
1357 0.4930396490181088979386097e+4,
1358 0.2030775189134759322293574e+3,
1359 0.1e+1
1360 };
1361
1362 p=Pone[5];
1363 q=Qone[5];
1364 for (i=4; i >= 0; i--)
1365 {
1366 p=p*(8.0/x)*(8.0/x)+Pone[i];
1367 q=q*(8.0/x)*(8.0/x)+Qone[i];
1368 }
1369 return(p/q);
1370}
1371
1372#undef Q1
1373static MagickRealType Q1(MagickRealType x)
1374{
1375 MagickRealType
1376 p,
1377 q;
1378
cristybb503372010-05-27 20:51:26 +00001379 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001380 i;
1381
1382 static const double
1383 Pone[] =
1384 {
1385 0.3511751914303552822533318e+3,
1386 0.7210391804904475039280863e+3,
1387 0.4259873011654442389886993e+3,
1388 0.831898957673850827325226e+2,
1389 0.45681716295512267064405e+1,
1390 0.3532840052740123642735e-1
1391 },
1392 Qone[] =
1393 {
1394 0.74917374171809127714519505e+4,
1395 0.154141773392650970499848051e+5,
1396 0.91522317015169922705904727e+4,
1397 0.18111867005523513506724158e+4,
1398 0.1038187585462133728776636e+3,
1399 0.1e+1
1400 };
1401
1402 p=Pone[5];
1403 q=Qone[5];
1404 for (i=4; i >= 0; i--)
1405 {
1406 p=p*(8.0/x)*(8.0/x)+Pone[i];
1407 q=q*(8.0/x)*(8.0/x)+Qone[i];
1408 }
1409 return(p/q);
1410}
1411
1412static MagickRealType BesselOrderOne(MagickRealType x)
1413{
1414 MagickRealType
1415 p,
1416 q;
1417
1418 if (x == 0.0)
1419 return(0.0);
1420 p=x;
1421 if (x < 0.0)
1422 x=(-x);
1423 if (x < 8.0)
1424 return(p*J1(x));
1425 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1426 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1427 cos((double) x))));
1428 if (p < 0.0)
1429 q=(-q);
1430 return(q);
1431}
1432
1433/*
1434%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1435% %
1436% %
1437% %
1438+ D e s t r o y R e s i z e F i l t e r %
1439% %
1440% %
1441% %
1442%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1443%
1444% DestroyResizeFilter() destroy the resize filter.
1445%
cristya2ffd7e2010-03-10 20:50:30 +00001446% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001447%
1448% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1449%
1450% A description of each parameter follows:
1451%
1452% o resize_filter: the resize filter.
1453%
1454*/
1455MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1456{
1457 assert(resize_filter != (ResizeFilter *) NULL);
1458 assert(resize_filter->signature == MagickSignature);
1459 resize_filter->signature=(~MagickSignature);
1460 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1461 return(resize_filter);
1462}
1463
1464/*
1465%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1466% %
1467% %
1468% %
1469+ G e t R e s i z e F i l t e r S u p p o r t %
1470% %
1471% %
1472% %
1473%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1474%
1475% GetResizeFilterSupport() return the current support window size for this
1476% filter. Note that this may have been enlarged by filter:blur factor.
1477%
1478% The format of the GetResizeFilterSupport method is:
1479%
1480% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1481%
1482% A description of each parameter follows:
1483%
1484% o filter: Image filter to use.
1485%
1486*/
1487MagickExport MagickRealType GetResizeFilterSupport(
1488 const ResizeFilter *resize_filter)
1489{
1490 assert(resize_filter != (ResizeFilter *) NULL);
1491 assert(resize_filter->signature == MagickSignature);
1492 return(resize_filter->support*resize_filter->blur);
1493}
1494
1495/*
1496%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1497% %
1498% %
1499% %
1500+ G e t R e s i z e F i l t e r W e i g h t %
1501% %
1502% %
1503% %
1504%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1505%
1506% GetResizeFilterWeight evaluates the specified resize filter at the point x
1507% which usally lies between zero and the filters current 'support' and
1508% returns the weight of the filter function at that point.
1509%
1510% The format of the GetResizeFilterWeight method is:
1511%
1512% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1513% const MagickRealType x)
1514%
1515% A description of each parameter follows:
1516%
1517% o filter: the filter type.
1518%
1519% o x: the point.
1520%
1521*/
1522MagickExport MagickRealType GetResizeFilterWeight(
1523 const ResizeFilter *resize_filter,const MagickRealType x)
1524{
1525 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001526 scale,
1527 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001528
1529 /*
1530 Windowing function - scale the weighting filter by this amount.
1531 */
1532 assert(resize_filter != (ResizeFilter *) NULL);
1533 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001534 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001535 if ((resize_filter->window_support < MagickEpsilon) ||
1536 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001537 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001538 else
1539 {
anthony55f12332010-09-10 01:13:02 +00001540 scale=resize_filter->scale;
1541 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001542 }
anthony55f12332010-09-10 01:13:02 +00001543 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001544}
1545
1546/*
1547%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1548% %
1549% %
1550% %
1551% M a g n i f y I m a g e %
1552% %
1553% %
1554% %
1555%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1556%
1557% MagnifyImage() is a convenience method that scales an image proportionally
1558% to twice its size.
1559%
1560% The format of the MagnifyImage method is:
1561%
1562% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1563%
1564% A description of each parameter follows:
1565%
1566% o image: the image.
1567%
1568% o exception: return any errors or warnings in this structure.
1569%
1570*/
1571MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1572{
1573 Image
1574 *magnify_image;
1575
1576 assert(image != (Image *) NULL);
1577 assert(image->signature == MagickSignature);
1578 if (image->debug != MagickFalse)
1579 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1580 assert(exception != (ExceptionInfo *) NULL);
1581 assert(exception->signature == MagickSignature);
1582 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1583 1.0,exception);
1584 return(magnify_image);
1585}
1586
1587/*
1588%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1589% %
1590% %
1591% %
1592% M i n i f y I m a g e %
1593% %
1594% %
1595% %
1596%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1597%
1598% MinifyImage() is a convenience method that scales an image proportionally
1599% to half its size.
1600%
1601% The format of the MinifyImage method is:
1602%
1603% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1604%
1605% A description of each parameter follows:
1606%
1607% o image: the image.
1608%
1609% o exception: return any errors or warnings in this structure.
1610%
1611*/
1612MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1613{
1614 Image
1615 *minify_image;
1616
1617 assert(image != (Image *) NULL);
1618 assert(image->signature == MagickSignature);
1619 if (image->debug != MagickFalse)
1620 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1621 assert(exception != (ExceptionInfo *) NULL);
1622 assert(exception->signature == MagickSignature);
1623 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1624 1.0,exception);
1625 return(minify_image);
1626}
1627
1628/*
1629%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1630% %
1631% %
1632% %
1633% R e s a m p l e I m a g e %
1634% %
1635% %
1636% %
1637%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1638%
1639% ResampleImage() resize image in terms of its pixel size, so that when
1640% displayed at the given resolution it will be the same size in terms of
1641% real world units as the original image at the original resolution.
1642%
1643% The format of the ResampleImage method is:
1644%
1645% Image *ResampleImage(Image *image,const double x_resolution,
1646% const double y_resolution,const FilterTypes filter,const double blur,
1647% ExceptionInfo *exception)
1648%
1649% A description of each parameter follows:
1650%
1651% o image: the image to be resized to fit the given resolution.
1652%
1653% o x_resolution: the new image x resolution.
1654%
1655% o y_resolution: the new image y resolution.
1656%
1657% o filter: Image filter to use.
1658%
1659% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1660%
1661*/
1662MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1663 const double y_resolution,const FilterTypes filter,const double blur,
1664 ExceptionInfo *exception)
1665{
1666#define ResampleImageTag "Resample/Image"
1667
1668 Image
1669 *resample_image;
1670
cristybb503372010-05-27 20:51:26 +00001671 size_t
cristy3ed852e2009-09-05 21:47:34 +00001672 height,
1673 width;
1674
1675 /*
1676 Initialize sampled image attributes.
1677 */
1678 assert(image != (const Image *) NULL);
1679 assert(image->signature == MagickSignature);
1680 if (image->debug != MagickFalse)
1681 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1682 assert(exception != (ExceptionInfo *) NULL);
1683 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001684 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1685 72.0 : image->x_resolution)+0.5);
1686 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1687 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001688 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1689 if (resample_image != (Image *) NULL)
1690 {
1691 resample_image->x_resolution=x_resolution;
1692 resample_image->y_resolution=y_resolution;
1693 }
1694 return(resample_image);
1695}
1696#if defined(MAGICKCORE_LQR_DELEGATE)
1697
1698/*
1699%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1700% %
1701% %
1702% %
1703% L i q u i d R e s c a l e I m a g e %
1704% %
1705% %
1706% %
1707%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1708%
1709% LiquidRescaleImage() rescales image with seam carving.
1710%
1711% The format of the LiquidRescaleImage method is:
1712%
1713% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001714% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001715% const double delta_x,const double rigidity,ExceptionInfo *exception)
1716%
1717% A description of each parameter follows:
1718%
1719% o image: the image.
1720%
1721% o columns: the number of columns in the rescaled image.
1722%
1723% o rows: the number of rows in the rescaled image.
1724%
1725% o delta_x: maximum seam transversal step (0 means straight seams).
1726%
1727% o rigidity: introduce a bias for non-straight seams (typically 0).
1728%
1729% o exception: return any errors or warnings in this structure.
1730%
1731*/
cristy9af9b5d2010-08-15 17:04:28 +00001732MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1733 const size_t rows,const double delta_x,const double rigidity,
1734 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001735{
1736#define LiquidRescaleImageTag "Rescale/Image"
1737
cristyc5c6f662010-09-22 14:23:02 +00001738 CacheView
1739 *rescale_view;
1740
cristy3ed852e2009-09-05 21:47:34 +00001741 const char
1742 *map;
1743
1744 guchar
1745 *packet;
1746
1747 Image
1748 *rescale_image;
1749
1750 int
1751 x,
1752 y;
1753
1754 LqrCarver
1755 *carver;
1756
1757 LqrRetVal
1758 lqr_status;
1759
1760 MagickBooleanType
1761 status;
1762
1763 MagickPixelPacket
1764 pixel;
1765
1766 unsigned char
1767 *pixels;
1768
1769 /*
1770 Liquid rescale image.
1771 */
1772 assert(image != (const Image *) NULL);
1773 assert(image->signature == MagickSignature);
1774 if (image->debug != MagickFalse)
1775 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1776 assert(exception != (ExceptionInfo *) NULL);
1777 assert(exception->signature == MagickSignature);
1778 if ((columns == 0) || (rows == 0))
1779 return((Image *) NULL);
1780 if ((columns == image->columns) && (rows == image->rows))
1781 return(CloneImage(image,0,0,MagickTrue,exception));
1782 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001783 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001784 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1785 {
1786 Image
1787 *resize_image;
1788
cristybb503372010-05-27 20:51:26 +00001789 size_t
cristy3ed852e2009-09-05 21:47:34 +00001790 height,
1791 width;
1792
1793 /*
1794 Honor liquid resize size limitations.
1795 */
1796 for (width=image->columns; columns >= (2*width-1); width*=2);
1797 for (height=image->rows; rows >= (2*height-1); height*=2);
1798 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1799 exception);
1800 if (resize_image == (Image *) NULL)
1801 return((Image *) NULL);
1802 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1803 rigidity,exception);
1804 resize_image=DestroyImage(resize_image);
1805 return(rescale_image);
1806 }
1807 map="RGB";
1808 if (image->matte == MagickFalse)
1809 map="RGBA";
1810 if (image->colorspace == CMYKColorspace)
1811 {
1812 map="CMYK";
1813 if (image->matte == MagickFalse)
1814 map="CMYKA";
1815 }
1816 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1817 strlen(map)*sizeof(*pixels));
1818 if (pixels == (unsigned char *) NULL)
1819 return((Image *) NULL);
1820 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1821 pixels,exception);
1822 if (status == MagickFalse)
1823 {
1824 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1825 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1826 }
1827 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1828 if (carver == (LqrCarver *) NULL)
1829 {
1830 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1831 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1832 }
1833 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1834 lqr_status=lqr_carver_resize(carver,columns,rows);
1835 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1836 lqr_carver_get_height(carver),MagickTrue,exception);
1837 if (rescale_image == (Image *) NULL)
1838 {
1839 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1840 return((Image *) NULL);
1841 }
1842 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1843 {
1844 InheritException(exception,&rescale_image->exception);
1845 rescale_image=DestroyImage(rescale_image);
1846 return((Image *) NULL);
1847 }
1848 GetMagickPixelPacket(rescale_image,&pixel);
1849 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001850 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001851 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1852 {
1853 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001854 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001855
1856 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001857 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001858
anthony22aad252010-09-23 06:59:07 +00001859 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001860 if (q == (PixelPacket *) NULL)
1861 break;
cristyc5c6f662010-09-22 14:23:02 +00001862 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001863 pixel.red=QuantumRange*(packet[0]/255.0);
1864 pixel.green=QuantumRange*(packet[1]/255.0);
1865 pixel.blue=QuantumRange*(packet[2]/255.0);
1866 if (image->colorspace != CMYKColorspace)
1867 {
1868 if (image->matte == MagickFalse)
1869 pixel.opacity=QuantumRange*(packet[3]/255.0);
1870 }
1871 else
1872 {
1873 pixel.index=QuantumRange*(packet[3]/255.0);
1874 if (image->matte == MagickFalse)
1875 pixel.opacity=QuantumRange*(packet[4]/255.0);
1876 }
1877 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001878 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001879 break;
1880 }
cristyc5c6f662010-09-22 14:23:02 +00001881 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001882 /*
1883 Relinquish resources.
1884 */
1885 lqr_carver_destroy(carver);
1886 return(rescale_image);
1887}
1888#else
1889MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001890 const size_t magick_unused(columns),const size_t magick_unused(rows),
1891 const double magick_unused(delta_x),const double magick_unused(rigidity),
1892 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001893{
1894 assert(image != (const Image *) NULL);
1895 assert(image->signature == MagickSignature);
1896 if (image->debug != MagickFalse)
1897 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1898 assert(exception != (ExceptionInfo *) NULL);
1899 assert(exception->signature == MagickSignature);
1900 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1901 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1902 return((Image *) NULL);
1903}
1904#endif
1905
1906/*
1907%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1908% %
1909% %
1910% %
1911% R e s i z e I m a g e %
1912% %
1913% %
1914% %
1915%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1916%
1917% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001918% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001919%
1920% If an undefined filter is given the filter defaults to Mitchell for a
1921% colormapped image, a image with a matte channel, or if the image is
1922% enlarged. Otherwise the filter defaults to a Lanczos.
1923%
1924% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1925%
1926% The format of the ResizeImage method is:
1927%
cristybb503372010-05-27 20:51:26 +00001928% Image *ResizeImage(Image *image,const size_t columns,
1929% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001930% ExceptionInfo *exception)
1931%
1932% A description of each parameter follows:
1933%
1934% o image: the image.
1935%
1936% o columns: the number of columns in the scaled image.
1937%
1938% o rows: the number of rows in the scaled image.
1939%
1940% o filter: Image filter to use.
1941%
cristy9af9b5d2010-08-15 17:04:28 +00001942% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1943% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001944%
1945% o exception: return any errors or warnings in this structure.
1946%
1947*/
1948
1949typedef struct _ContributionInfo
1950{
1951 MagickRealType
1952 weight;
1953
cristybb503372010-05-27 20:51:26 +00001954 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001955 pixel;
1956} ContributionInfo;
1957
1958static ContributionInfo **DestroyContributionThreadSet(
1959 ContributionInfo **contribution)
1960{
cristybb503372010-05-27 20:51:26 +00001961 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001962 i;
1963
1964 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001965 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001966 if (contribution[i] != (ContributionInfo *) NULL)
1967 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1968 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001969 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001970 return(contribution);
1971}
1972
1973static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1974{
cristybb503372010-05-27 20:51:26 +00001975 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001976 i;
1977
1978 ContributionInfo
1979 **contribution;
1980
cristybb503372010-05-27 20:51:26 +00001981 size_t
cristy3ed852e2009-09-05 21:47:34 +00001982 number_threads;
1983
1984 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001985 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001986 sizeof(*contribution));
1987 if (contribution == (ContributionInfo **) NULL)
1988 return((ContributionInfo **) NULL);
1989 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00001990 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00001991 {
1992 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1993 sizeof(**contribution));
1994 if (contribution[i] == (ContributionInfo *) NULL)
1995 return(DestroyContributionThreadSet(contribution));
1996 }
1997 return(contribution);
1998}
1999
2000static inline double MagickMax(const double x,const double y)
2001{
2002 if (x > y)
2003 return(x);
2004 return(y);
2005}
2006
2007static inline double MagickMin(const double x,const double y)
2008{
2009 if (x < y)
2010 return(x);
2011 return(y);
2012}
2013
2014static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2015 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002016 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002017{
2018#define ResizeImageTag "Resize/Image"
2019
cristyfa112112010-01-04 17:48:07 +00002020 CacheView
2021 *image_view,
2022 *resize_view;
2023
cristy3ed852e2009-09-05 21:47:34 +00002024 ClassType
2025 storage_class;
2026
2027 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002028 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002029
cristy3ed852e2009-09-05 21:47:34 +00002030 MagickBooleanType
2031 status;
2032
2033 MagickPixelPacket
2034 zero;
2035
2036 MagickRealType
2037 scale,
2038 support;
2039
cristy9af9b5d2010-08-15 17:04:28 +00002040 ssize_t
2041 x;
2042
cristy3ed852e2009-09-05 21:47:34 +00002043 /*
2044 Apply filter to resize horizontally from image to resize image.
2045 */
cristy5d824382010-09-06 14:00:17 +00002046 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002047 support=scale*GetResizeFilterSupport(resize_filter);
2048 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2049 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2050 {
2051 InheritException(exception,&resize_image->exception);
2052 return(MagickFalse);
2053 }
2054 if (support < 0.5)
2055 {
2056 /*
nicolas07bac812010-09-19 18:47:02 +00002057 Support too small even for nearest neighbour: Reduce to point
2058 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002059 */
2060 support=(MagickRealType) 0.5;
2061 scale=1.0;
2062 }
2063 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2064 if (contributions == (ContributionInfo **) NULL)
2065 {
2066 (void) ThrowMagickException(exception,GetMagickModule(),
2067 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2068 return(MagickFalse);
2069 }
2070 status=MagickTrue;
2071 scale=1.0/scale;
2072 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2073 image_view=AcquireCacheView(image);
2074 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002075#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002076 #pragma omp parallel for shared(status)
2077#endif
cristybb503372010-05-27 20:51:26 +00002078 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002079 {
cristy3ed852e2009-09-05 21:47:34 +00002080 MagickRealType
2081 center,
2082 density;
2083
2084 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002085 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002086
2087 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002088 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002089
cristy03dbbd22010-09-19 23:04:47 +00002090 register ContributionInfo
2091 *restrict contribution;
2092
cristy3ed852e2009-09-05 21:47:34 +00002093 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002094 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002095
cristy3ed852e2009-09-05 21:47:34 +00002096 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002097 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002098
cristy03dbbd22010-09-19 23:04:47 +00002099 register ssize_t
2100 y;
2101
cristy9af9b5d2010-08-15 17:04:28 +00002102 ssize_t
2103 n,
2104 start,
2105 stop;
2106
cristy3ed852e2009-09-05 21:47:34 +00002107 if (status == MagickFalse)
2108 continue;
2109 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002110 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2111 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002112 density=0.0;
2113 contribution=contributions[GetOpenMPThreadId()];
2114 for (n=0; n < (stop-start); n++)
2115 {
2116 contribution[n].pixel=start+n;
2117 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2118 ((MagickRealType) (start+n)-center+0.5));
2119 density+=contribution[n].weight;
2120 }
2121 if ((density != 0.0) && (density != 1.0))
2122 {
cristybb503372010-05-27 20:51:26 +00002123 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002124 i;
2125
2126 /*
2127 Normalize.
2128 */
2129 density=1.0/density;
2130 for (i=0; i < n; i++)
2131 contribution[i].weight*=density;
2132 }
cristy9af9b5d2010-08-15 17:04:28 +00002133 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2134 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002135 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2136 exception);
2137 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2138 {
2139 status=MagickFalse;
2140 continue;
2141 }
2142 indexes=GetCacheViewVirtualIndexQueue(image_view);
2143 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002144 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002145 {
cristy3ed852e2009-09-05 21:47:34 +00002146 MagickPixelPacket
2147 pixel;
2148
2149 MagickRealType
2150 alpha;
2151
cristybb503372010-05-27 20:51:26 +00002152 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002153 i;
2154
cristy9af9b5d2010-08-15 17:04:28 +00002155 ssize_t
2156 j;
2157
cristy3ed852e2009-09-05 21:47:34 +00002158 pixel=zero;
2159 if (image->matte == MagickFalse)
2160 {
2161 for (i=0; i < n; i++)
2162 {
2163 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2164 (contribution[i].pixel-contribution[0].pixel);
2165 alpha=contribution[i].weight;
2166 pixel.red+=alpha*(p+j)->red;
2167 pixel.green+=alpha*(p+j)->green;
2168 pixel.blue+=alpha*(p+j)->blue;
2169 pixel.opacity+=alpha*(p+j)->opacity;
2170 }
cristyce70c172010-01-07 17:15:30 +00002171 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2172 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2173 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2174 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002175 if ((image->colorspace == CMYKColorspace) &&
2176 (resize_image->colorspace == CMYKColorspace))
2177 {
2178 for (i=0; i < n; i++)
2179 {
2180 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2181 (contribution[i].pixel-contribution[0].pixel);
2182 alpha=contribution[i].weight;
2183 pixel.index+=alpha*indexes[j];
2184 }
cristyce70c172010-01-07 17:15:30 +00002185 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002186 }
2187 }
2188 else
2189 {
2190 MagickRealType
2191 gamma;
2192
2193 gamma=0.0;
2194 for (i=0; i < n; i++)
2195 {
2196 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2197 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002198 alpha=contribution[i].weight*QuantumScale*
2199 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002200 pixel.red+=alpha*(p+j)->red;
2201 pixel.green+=alpha*(p+j)->green;
2202 pixel.blue+=alpha*(p+j)->blue;
2203 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2204 gamma+=alpha;
2205 }
2206 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002207 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2208 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2209 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2210 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002211 if ((image->colorspace == CMYKColorspace) &&
2212 (resize_image->colorspace == CMYKColorspace))
2213 {
2214 for (i=0; i < n; i++)
2215 {
2216 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2217 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002218 alpha=contribution[i].weight*QuantumScale*
2219 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002220 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002221 }
cristyce70c172010-01-07 17:15:30 +00002222 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2223 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002224 }
2225 }
2226 if ((resize_image->storage_class == PseudoClass) &&
2227 (image->storage_class == PseudoClass))
2228 {
cristybb503372010-05-27 20:51:26 +00002229 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002230 1.0)+0.5);
2231 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2232 (contribution[i-start].pixel-contribution[0].pixel);
2233 resize_indexes[y]=indexes[j];
2234 }
2235 q++;
2236 }
2237 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2238 status=MagickFalse;
2239 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2240 {
2241 MagickBooleanType
2242 proceed;
2243
cristyb5d5f722009-11-04 03:03:49 +00002244#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002245 #pragma omp critical (MagickCore_HorizontalFilter)
2246#endif
cristy9af9b5d2010-08-15 17:04:28 +00002247 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002248 if (proceed == MagickFalse)
2249 status=MagickFalse;
2250 }
2251 }
2252 resize_view=DestroyCacheView(resize_view);
2253 image_view=DestroyCacheView(image_view);
2254 contributions=DestroyContributionThreadSet(contributions);
2255 return(status);
2256}
2257
2258static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2259 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002260 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002261{
cristyfa112112010-01-04 17:48:07 +00002262 CacheView
2263 *image_view,
2264 *resize_view;
2265
cristy3ed852e2009-09-05 21:47:34 +00002266 ClassType
2267 storage_class;
2268
2269 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002270 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002271
cristy3ed852e2009-09-05 21:47:34 +00002272 MagickBooleanType
2273 status;
2274
2275 MagickPixelPacket
2276 zero;
2277
2278 MagickRealType
2279 scale,
2280 support;
2281
cristy9af9b5d2010-08-15 17:04:28 +00002282 ssize_t
2283 y;
2284
cristy3ed852e2009-09-05 21:47:34 +00002285 /*
cristy9af9b5d2010-08-15 17:04:28 +00002286 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002287 */
cristy5d824382010-09-06 14:00:17 +00002288 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002289 support=scale*GetResizeFilterSupport(resize_filter);
2290 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2291 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2292 {
2293 InheritException(exception,&resize_image->exception);
2294 return(MagickFalse);
2295 }
2296 if (support < 0.5)
2297 {
2298 /*
nicolas07bac812010-09-19 18:47:02 +00002299 Support too small even for nearest neighbour: Reduce to point
2300 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002301 */
2302 support=(MagickRealType) 0.5;
2303 scale=1.0;
2304 }
2305 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2306 if (contributions == (ContributionInfo **) NULL)
2307 {
2308 (void) ThrowMagickException(exception,GetMagickModule(),
2309 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2310 return(MagickFalse);
2311 }
2312 status=MagickTrue;
2313 scale=1.0/scale;
2314 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2315 image_view=AcquireCacheView(image);
2316 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002317#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002318 #pragma omp parallel for shared(status)
2319#endif
cristybb503372010-05-27 20:51:26 +00002320 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002321 {
cristy3ed852e2009-09-05 21:47:34 +00002322 MagickRealType
2323 center,
2324 density;
2325
2326 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002327 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002328
2329 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002330 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002331
cristy03dbbd22010-09-19 23:04:47 +00002332 register ContributionInfo
2333 *restrict contribution;
2334
cristy3ed852e2009-09-05 21:47:34 +00002335 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002336 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002337
cristy9af9b5d2010-08-15 17:04:28 +00002338 register PixelPacket
2339 *restrict q;
2340
cristybb503372010-05-27 20:51:26 +00002341 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002342 x;
2343
cristy9af9b5d2010-08-15 17:04:28 +00002344 ssize_t
2345 n,
2346 start,
2347 stop;
cristy3ed852e2009-09-05 21:47:34 +00002348
2349 if (status == MagickFalse)
2350 continue;
cristy679e6962010-03-18 00:42:45 +00002351 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002352 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2353 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002354 density=0.0;
2355 contribution=contributions[GetOpenMPThreadId()];
2356 for (n=0; n < (stop-start); n++)
2357 {
2358 contribution[n].pixel=start+n;
2359 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2360 ((MagickRealType) (start+n)-center+0.5));
2361 density+=contribution[n].weight;
2362 }
2363 if ((density != 0.0) && (density != 1.0))
2364 {
cristybb503372010-05-27 20:51:26 +00002365 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002366 i;
2367
2368 /*
2369 Normalize.
2370 */
2371 density=1.0/density;
2372 for (i=0; i < n; i++)
2373 contribution[i].weight*=density;
2374 }
2375 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002376 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2377 exception);
cristy3ed852e2009-09-05 21:47:34 +00002378 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2379 exception);
2380 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2381 {
2382 status=MagickFalse;
2383 continue;
2384 }
2385 indexes=GetCacheViewVirtualIndexQueue(image_view);
2386 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002387 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002388 {
cristy3ed852e2009-09-05 21:47:34 +00002389 MagickPixelPacket
2390 pixel;
2391
2392 MagickRealType
2393 alpha;
2394
cristybb503372010-05-27 20:51:26 +00002395 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002396 i;
2397
cristy9af9b5d2010-08-15 17:04:28 +00002398 ssize_t
2399 j;
2400
cristy3ed852e2009-09-05 21:47:34 +00002401 pixel=zero;
2402 if (image->matte == MagickFalse)
2403 {
2404 for (i=0; i < n; i++)
2405 {
cristybb503372010-05-27 20:51:26 +00002406 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002407 image->columns+x);
2408 alpha=contribution[i].weight;
2409 pixel.red+=alpha*(p+j)->red;
2410 pixel.green+=alpha*(p+j)->green;
2411 pixel.blue+=alpha*(p+j)->blue;
2412 pixel.opacity+=alpha*(p+j)->opacity;
2413 }
cristyce70c172010-01-07 17:15:30 +00002414 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2415 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2416 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2417 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002418 if ((image->colorspace == CMYKColorspace) &&
2419 (resize_image->colorspace == CMYKColorspace))
2420 {
2421 for (i=0; i < n; i++)
2422 {
cristybb503372010-05-27 20:51:26 +00002423 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002424 image->columns+x);
2425 alpha=contribution[i].weight;
2426 pixel.index+=alpha*indexes[j];
2427 }
cristyce70c172010-01-07 17:15:30 +00002428 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002429 }
2430 }
2431 else
2432 {
2433 MagickRealType
2434 gamma;
2435
2436 gamma=0.0;
2437 for (i=0; i < n; i++)
2438 {
cristybb503372010-05-27 20:51:26 +00002439 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002440 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002441 alpha=contribution[i].weight*QuantumScale*
2442 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002443 pixel.red+=alpha*(p+j)->red;
2444 pixel.green+=alpha*(p+j)->green;
2445 pixel.blue+=alpha*(p+j)->blue;
2446 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2447 gamma+=alpha;
2448 }
2449 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002450 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2451 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2452 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2453 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002454 if ((image->colorspace == CMYKColorspace) &&
2455 (resize_image->colorspace == CMYKColorspace))
2456 {
2457 for (i=0; i < n; i++)
2458 {
cristybb503372010-05-27 20:51:26 +00002459 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002460 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002461 alpha=contribution[i].weight*QuantumScale*
2462 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002463 pixel.index+=alpha*indexes[j];
2464 }
cristyce70c172010-01-07 17:15:30 +00002465 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2466 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002467 }
2468 }
2469 if ((resize_image->storage_class == PseudoClass) &&
2470 (image->storage_class == PseudoClass))
2471 {
cristybb503372010-05-27 20:51:26 +00002472 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002473 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002474 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002475 image->columns+x);
2476 resize_indexes[x]=indexes[j];
2477 }
2478 q++;
2479 }
2480 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2481 status=MagickFalse;
2482 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2483 {
2484 MagickBooleanType
2485 proceed;
2486
cristyb5d5f722009-11-04 03:03:49 +00002487#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002488 #pragma omp critical (MagickCore_VerticalFilter)
2489#endif
cristy9af9b5d2010-08-15 17:04:28 +00002490 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002491 if (proceed == MagickFalse)
2492 status=MagickFalse;
2493 }
2494 }
2495 resize_view=DestroyCacheView(resize_view);
2496 image_view=DestroyCacheView(image_view);
2497 contributions=DestroyContributionThreadSet(contributions);
2498 return(status);
2499}
2500
cristybb503372010-05-27 20:51:26 +00002501MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2502 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002503 ExceptionInfo *exception)
2504{
2505#define WorkLoadFactor 0.265
2506
2507 FilterTypes
2508 filter_type;
2509
2510 Image
2511 *filter_image,
2512 *resize_image;
2513
cristy9af9b5d2010-08-15 17:04:28 +00002514 MagickOffsetType
2515 offset;
2516
cristy3ed852e2009-09-05 21:47:34 +00002517 MagickRealType
2518 x_factor,
2519 y_factor;
2520
2521 MagickSizeType
2522 span;
2523
2524 MagickStatusType
2525 status;
2526
2527 ResizeFilter
2528 *resize_filter;
2529
cristy3ed852e2009-09-05 21:47:34 +00002530 /*
2531 Acquire resize image.
2532 */
2533 assert(image != (Image *) NULL);
2534 assert(image->signature == MagickSignature);
2535 if (image->debug != MagickFalse)
2536 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2537 assert(exception != (ExceptionInfo *) NULL);
2538 assert(exception->signature == MagickSignature);
2539 if ((columns == 0) || (rows == 0))
2540 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2541 if ((columns == image->columns) && (rows == image->rows) &&
2542 (filter == UndefinedFilter) && (blur == 1.0))
2543 return(CloneImage(image,0,0,MagickTrue,exception));
2544 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2545 if (resize_image == (Image *) NULL)
2546 return(resize_image);
2547 /*
2548 Acquire resize filter.
2549 */
2550 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2551 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2552 if ((x_factor*y_factor) > WorkLoadFactor)
2553 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2554 else
2555 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2556 if (filter_image == (Image *) NULL)
2557 return(DestroyImage(resize_image));
2558 filter_type=LanczosFilter;
2559 if (filter != UndefinedFilter)
2560 filter_type=filter;
2561 else
2562 if ((x_factor == 1.0) && (y_factor == 1.0))
2563 filter_type=PointFilter;
2564 else
2565 if ((image->storage_class == PseudoClass) ||
2566 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2567 filter_type=MitchellFilter;
2568 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2569 exception);
2570 /*
2571 Resize image.
2572 */
cristy9af9b5d2010-08-15 17:04:28 +00002573 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002574 if ((x_factor*y_factor) > WorkLoadFactor)
2575 {
2576 span=(MagickSizeType) (filter_image->columns+rows);
2577 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002578 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002579 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002580 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002581 }
2582 else
2583 {
2584 span=(MagickSizeType) (filter_image->rows+columns);
2585 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002586 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002587 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002588 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002589 }
2590 /*
2591 Free resources.
2592 */
2593 filter_image=DestroyImage(filter_image);
2594 resize_filter=DestroyResizeFilter(resize_filter);
2595 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2596 return((Image *) NULL);
2597 resize_image->type=image->type;
2598 return(resize_image);
2599}
2600
2601/*
2602%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2603% %
2604% %
2605% %
2606% S a m p l e I m a g e %
2607% %
2608% %
2609% %
2610%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2611%
2612% SampleImage() scales an image to the desired dimensions with pixel
2613% sampling. Unlike other scaling methods, this method does not introduce
2614% any additional color into the scaled image.
2615%
2616% The format of the SampleImage method is:
2617%
cristybb503372010-05-27 20:51:26 +00002618% Image *SampleImage(const Image *image,const size_t columns,
2619% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002620%
2621% A description of each parameter follows:
2622%
2623% o image: the image.
2624%
2625% o columns: the number of columns in the sampled image.
2626%
2627% o rows: the number of rows in the sampled image.
2628%
2629% o exception: return any errors or warnings in this structure.
2630%
2631*/
cristybb503372010-05-27 20:51:26 +00002632MagickExport Image *SampleImage(const Image *image,const size_t columns,
2633 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002634{
2635#define SampleImageTag "Sample/Image"
2636
cristyc4c8d132010-01-07 01:58:38 +00002637 CacheView
2638 *image_view,
2639 *sample_view;
2640
cristy3ed852e2009-09-05 21:47:34 +00002641 Image
2642 *sample_image;
2643
cristy3ed852e2009-09-05 21:47:34 +00002644 MagickBooleanType
2645 status;
2646
cristy5f959472010-05-27 22:19:46 +00002647 MagickOffsetType
2648 progress;
2649
cristybb503372010-05-27 20:51:26 +00002650 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002651 x;
2652
cristy5f959472010-05-27 22:19:46 +00002653 ssize_t
2654 *x_offset,
2655 y;
2656
cristy3ed852e2009-09-05 21:47:34 +00002657 /*
2658 Initialize sampled image attributes.
2659 */
2660 assert(image != (const Image *) NULL);
2661 assert(image->signature == MagickSignature);
2662 if (image->debug != MagickFalse)
2663 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2664 assert(exception != (ExceptionInfo *) NULL);
2665 assert(exception->signature == MagickSignature);
2666 if ((columns == 0) || (rows == 0))
2667 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2668 if ((columns == image->columns) && (rows == image->rows))
2669 return(CloneImage(image,0,0,MagickTrue,exception));
2670 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2671 if (sample_image == (Image *) NULL)
2672 return((Image *) NULL);
2673 /*
2674 Allocate scan line buffer and column offset buffers.
2675 */
cristybb503372010-05-27 20:51:26 +00002676 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002677 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002678 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002679 {
2680 sample_image=DestroyImage(sample_image);
2681 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2682 }
cristybb503372010-05-27 20:51:26 +00002683 for (x=0; x < (ssize_t) sample_image->columns; x++)
2684 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002685 sample_image->columns);
2686 /*
2687 Sample each row.
2688 */
2689 status=MagickTrue;
2690 progress=0;
2691 image_view=AcquireCacheView(image);
2692 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002693#if defined(MAGICKCORE_OPENMP_SUPPORT)
2694 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002695#endif
cristybb503372010-05-27 20:51:26 +00002696 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002697 {
cristy3ed852e2009-09-05 21:47:34 +00002698 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002699 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002700
2701 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002702 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002703
2704 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002705 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002706
cristy3ed852e2009-09-05 21:47:34 +00002707 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002708 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002709
cristy03dbbd22010-09-19 23:04:47 +00002710 register ssize_t
2711 x;
2712
cristy9af9b5d2010-08-15 17:04:28 +00002713 ssize_t
2714 y_offset;
2715
cristy3ed852e2009-09-05 21:47:34 +00002716 if (status == MagickFalse)
2717 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002718 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2719 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002720 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2721 exception);
2722 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2723 exception);
2724 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2725 {
2726 status=MagickFalse;
2727 continue;
2728 }
2729 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2730 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2731 /*
2732 Sample each column.
2733 */
cristybb503372010-05-27 20:51:26 +00002734 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002735 *q++=p[x_offset[x]];
2736 if ((image->storage_class == PseudoClass) ||
2737 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002738 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002739 sample_indexes[x]=indexes[x_offset[x]];
2740 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2741 status=MagickFalse;
2742 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2743 {
2744 MagickBooleanType
2745 proceed;
2746
cristyb5d5f722009-11-04 03:03:49 +00002747#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002748 #pragma omp critical (MagickCore_SampleImage)
2749#endif
2750 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2751 if (proceed == MagickFalse)
2752 status=MagickFalse;
2753 }
2754 }
2755 image_view=DestroyCacheView(image_view);
2756 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002757 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002758 sample_image->type=image->type;
2759 return(sample_image);
2760}
2761
2762/*
2763%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2764% %
2765% %
2766% %
2767% S c a l e I m a g e %
2768% %
2769% %
2770% %
2771%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2772%
2773% ScaleImage() changes the size of an image to the given dimensions.
2774%
2775% The format of the ScaleImage method is:
2776%
cristybb503372010-05-27 20:51:26 +00002777% Image *ScaleImage(const Image *image,const size_t columns,
2778% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002779%
2780% A description of each parameter follows:
2781%
2782% o image: the image.
2783%
2784% o columns: the number of columns in the scaled image.
2785%
2786% o rows: the number of rows in the scaled image.
2787%
2788% o exception: return any errors or warnings in this structure.
2789%
2790*/
cristybb503372010-05-27 20:51:26 +00002791MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2792 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002793{
2794#define ScaleImageTag "Scale/Image"
2795
cristyed6cb232010-01-20 03:07:53 +00002796 CacheView
2797 *image_view,
2798 *scale_view;
2799
cristy3ed852e2009-09-05 21:47:34 +00002800 Image
2801 *scale_image;
2802
cristy3ed852e2009-09-05 21:47:34 +00002803 MagickBooleanType
2804 next_column,
2805 next_row,
2806 proceed;
2807
2808 MagickPixelPacket
2809 pixel,
2810 *scale_scanline,
2811 *scanline,
2812 *x_vector,
2813 *y_vector,
2814 zero;
2815
cristy3ed852e2009-09-05 21:47:34 +00002816 PointInfo
2817 scale,
2818 span;
2819
cristybb503372010-05-27 20:51:26 +00002820 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002821 i;
2822
cristy9af9b5d2010-08-15 17:04:28 +00002823 ssize_t
2824 number_rows,
2825 y;
2826
cristy3ed852e2009-09-05 21:47:34 +00002827 /*
2828 Initialize scaled image attributes.
2829 */
2830 assert(image != (const Image *) NULL);
2831 assert(image->signature == MagickSignature);
2832 if (image->debug != MagickFalse)
2833 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2834 assert(exception != (ExceptionInfo *) NULL);
2835 assert(exception->signature == MagickSignature);
2836 if ((columns == 0) || (rows == 0))
2837 return((Image *) NULL);
2838 if ((columns == image->columns) && (rows == image->rows))
2839 return(CloneImage(image,0,0,MagickTrue,exception));
2840 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2841 if (scale_image == (Image *) NULL)
2842 return((Image *) NULL);
2843 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2844 {
2845 InheritException(exception,&scale_image->exception);
2846 scale_image=DestroyImage(scale_image);
2847 return((Image *) NULL);
2848 }
2849 /*
2850 Allocate memory.
2851 */
2852 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2853 sizeof(*x_vector));
2854 scanline=x_vector;
2855 if (image->rows != scale_image->rows)
2856 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2857 sizeof(*scanline));
2858 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2859 scale_image->columns,sizeof(*scale_scanline));
2860 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2861 sizeof(*y_vector));
2862 if ((scanline == (MagickPixelPacket *) NULL) ||
2863 (scale_scanline == (MagickPixelPacket *) NULL) ||
2864 (x_vector == (MagickPixelPacket *) NULL) ||
2865 (y_vector == (MagickPixelPacket *) NULL))
2866 {
2867 scale_image=DestroyImage(scale_image);
2868 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2869 }
2870 /*
2871 Scale image.
2872 */
2873 number_rows=0;
2874 next_row=MagickTrue;
2875 span.y=1.0;
2876 scale.y=(double) scale_image->rows/(double) image->rows;
2877 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2878 sizeof(*y_vector));
2879 GetMagickPixelPacket(image,&pixel);
2880 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2881 i=0;
cristyed6cb232010-01-20 03:07:53 +00002882 image_view=AcquireCacheView(image);
2883 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002884 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002885 {
2886 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002887 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002888
2889 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002890 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002891
2892 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002893 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002894
cristy3ed852e2009-09-05 21:47:34 +00002895 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002896 *restrict s,
2897 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002898
2899 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002900 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002901
cristy9af9b5d2010-08-15 17:04:28 +00002902 register ssize_t
2903 x;
2904
cristyed6cb232010-01-20 03:07:53 +00002905 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2906 exception);
cristy3ed852e2009-09-05 21:47:34 +00002907 if (q == (PixelPacket *) NULL)
2908 break;
2909 scale_indexes=GetAuthenticIndexQueue(scale_image);
2910 if (scale_image->rows == image->rows)
2911 {
2912 /*
2913 Read a new scanline.
2914 */
cristyed6cb232010-01-20 03:07:53 +00002915 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2916 exception);
cristy3ed852e2009-09-05 21:47:34 +00002917 if (p == (const PixelPacket *) NULL)
2918 break;
cristyed6cb232010-01-20 03:07:53 +00002919 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002920 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002921 {
cristyce70c172010-01-07 17:15:30 +00002922 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2923 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2924 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002925 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002926 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002927 if (indexes != (IndexPacket *) NULL)
2928 x_vector[x].index=(MagickRealType) indexes[x];
2929 p++;
2930 }
2931 }
2932 else
2933 {
2934 /*
2935 Scale Y direction.
2936 */
2937 while (scale.y < span.y)
2938 {
cristy9af9b5d2010-08-15 17:04:28 +00002939 if ((next_row != MagickFalse) &&
2940 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002941 {
2942 /*
2943 Read a new scanline.
2944 */
cristyed6cb232010-01-20 03:07:53 +00002945 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2946 exception);
cristy3ed852e2009-09-05 21:47:34 +00002947 if (p == (const PixelPacket *) NULL)
2948 break;
cristyed6cb232010-01-20 03:07:53 +00002949 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002950 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002951 {
cristyce70c172010-01-07 17:15:30 +00002952 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2953 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2954 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002955 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002956 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002957 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002958 if (indexes != (IndexPacket *) NULL)
2959 x_vector[x].index=(MagickRealType) indexes[x];
2960 p++;
2961 }
2962 number_rows++;
2963 }
cristybb503372010-05-27 20:51:26 +00002964 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002965 {
2966 y_vector[x].red+=scale.y*x_vector[x].red;
2967 y_vector[x].green+=scale.y*x_vector[x].green;
2968 y_vector[x].blue+=scale.y*x_vector[x].blue;
2969 if (scale_image->matte != MagickFalse)
2970 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2971 if (scale_indexes != (IndexPacket *) NULL)
2972 y_vector[x].index+=scale.y*x_vector[x].index;
2973 }
2974 span.y-=scale.y;
2975 scale.y=(double) scale_image->rows/(double) image->rows;
2976 next_row=MagickTrue;
2977 }
cristybb503372010-05-27 20:51:26 +00002978 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002979 {
2980 /*
2981 Read a new scanline.
2982 */
cristyed6cb232010-01-20 03:07:53 +00002983 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2984 exception);
cristy3ed852e2009-09-05 21:47:34 +00002985 if (p == (const PixelPacket *) NULL)
2986 break;
cristyed6cb232010-01-20 03:07:53 +00002987 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002988 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002989 {
cristyce70c172010-01-07 17:15:30 +00002990 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2991 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2992 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002993 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00002994 x_vector[x].opacity=(MagickRealType)
2995 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002996 if (indexes != (IndexPacket *) NULL)
2997 x_vector[x].index=(MagickRealType) indexes[x];
2998 p++;
2999 }
3000 number_rows++;
3001 next_row=MagickFalse;
3002 }
3003 s=scanline;
cristybb503372010-05-27 20:51:26 +00003004 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003005 {
3006 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3007 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3008 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3009 if (image->matte != MagickFalse)
3010 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3011 if (scale_indexes != (IndexPacket *) NULL)
3012 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3013 s->red=pixel.red;
3014 s->green=pixel.green;
3015 s->blue=pixel.blue;
3016 if (scale_image->matte != MagickFalse)
3017 s->opacity=pixel.opacity;
3018 if (scale_indexes != (IndexPacket *) NULL)
3019 s->index=pixel.index;
3020 s++;
3021 y_vector[x]=zero;
3022 }
3023 scale.y-=span.y;
3024 if (scale.y <= 0)
3025 {
3026 scale.y=(double) scale_image->rows/(double) image->rows;
3027 next_row=MagickTrue;
3028 }
3029 span.y=1.0;
3030 }
3031 if (scale_image->columns == image->columns)
3032 {
3033 /*
3034 Transfer scanline to scaled image.
3035 */
3036 s=scanline;
cristybb503372010-05-27 20:51:26 +00003037 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003038 {
cristyce70c172010-01-07 17:15:30 +00003039 q->red=ClampToQuantum(s->red);
3040 q->green=ClampToQuantum(s->green);
3041 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003042 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003043 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003044 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003045 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003046 q++;
3047 s++;
3048 }
3049 }
3050 else
3051 {
3052 /*
3053 Scale X direction.
3054 */
3055 pixel=zero;
3056 next_column=MagickFalse;
3057 span.x=1.0;
3058 s=scanline;
3059 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003060 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003061 {
3062 scale.x=(double) scale_image->columns/(double) image->columns;
3063 while (scale.x >= span.x)
3064 {
3065 if (next_column != MagickFalse)
3066 {
3067 pixel=zero;
3068 t++;
3069 }
3070 pixel.red+=span.x*s->red;
3071 pixel.green+=span.x*s->green;
3072 pixel.blue+=span.x*s->blue;
3073 if (image->matte != MagickFalse)
3074 pixel.opacity+=span.x*s->opacity;
3075 if (scale_indexes != (IndexPacket *) NULL)
3076 pixel.index+=span.x*s->index;
3077 t->red=pixel.red;
3078 t->green=pixel.green;
3079 t->blue=pixel.blue;
3080 if (scale_image->matte != MagickFalse)
3081 t->opacity=pixel.opacity;
3082 if (scale_indexes != (IndexPacket *) NULL)
3083 t->index=pixel.index;
3084 scale.x-=span.x;
3085 span.x=1.0;
3086 next_column=MagickTrue;
3087 }
3088 if (scale.x > 0)
3089 {
3090 if (next_column != MagickFalse)
3091 {
3092 pixel=zero;
3093 next_column=MagickFalse;
3094 t++;
3095 }
3096 pixel.red+=scale.x*s->red;
3097 pixel.green+=scale.x*s->green;
3098 pixel.blue+=scale.x*s->blue;
3099 if (scale_image->matte != MagickFalse)
3100 pixel.opacity+=scale.x*s->opacity;
3101 if (scale_indexes != (IndexPacket *) NULL)
3102 pixel.index+=scale.x*s->index;
3103 span.x-=scale.x;
3104 }
3105 s++;
3106 }
3107 if (span.x > 0)
3108 {
3109 s--;
3110 pixel.red+=span.x*s->red;
3111 pixel.green+=span.x*s->green;
3112 pixel.blue+=span.x*s->blue;
3113 if (scale_image->matte != MagickFalse)
3114 pixel.opacity+=span.x*s->opacity;
3115 if (scale_indexes != (IndexPacket *) NULL)
3116 pixel.index+=span.x*s->index;
3117 }
3118 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003119 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003120 {
3121 t->red=pixel.red;
3122 t->green=pixel.green;
3123 t->blue=pixel.blue;
3124 if (scale_image->matte != MagickFalse)
3125 t->opacity=pixel.opacity;
3126 if (scale_indexes != (IndexPacket *) NULL)
3127 t->index=pixel.index;
3128 }
3129 /*
3130 Transfer scanline to scaled image.
3131 */
3132 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003133 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003134 {
cristyce70c172010-01-07 17:15:30 +00003135 q->red=ClampToQuantum(t->red);
3136 q->green=ClampToQuantum(t->green);
3137 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003138 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003139 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003140 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003141 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003142 t++;
3143 q++;
3144 }
3145 }
cristyed6cb232010-01-20 03:07:53 +00003146 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003147 break;
cristy96b16132010-08-29 17:19:52 +00003148 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3149 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003150 if (proceed == MagickFalse)
3151 break;
3152 }
cristyed6cb232010-01-20 03:07:53 +00003153 scale_view=DestroyCacheView(scale_view);
3154 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003155 /*
3156 Free allocated memory.
3157 */
3158 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3159 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3160 if (scale_image->rows != image->rows)
3161 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3162 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3163 scale_image->type=image->type;
3164 return(scale_image);
3165}
3166
anthony02b4cb42010-10-10 04:54:35 +00003167#if 0
3168 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003169/*
3170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3171% %
3172% %
3173% %
3174+ S e t R e s i z e F i l t e r S u p p o r t %
3175% %
3176% %
3177% %
3178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3179%
3180% SetResizeFilterSupport() specifies which IR filter to use to window
3181%
3182% The format of the SetResizeFilterSupport method is:
3183%
3184% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3185% const MagickRealType support)
3186%
3187% A description of each parameter follows:
3188%
3189% o resize_filter: the resize filter.
3190%
3191% o support: the filter spport radius.
3192%
3193*/
3194MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3195 const MagickRealType support)
3196{
3197 assert(resize_filter != (ResizeFilter *) NULL);
3198 assert(resize_filter->signature == MagickSignature);
3199 resize_filter->support=support;
3200}
anthony02b4cb42010-10-10 04:54:35 +00003201#endif
cristy3ed852e2009-09-05 21:47:34 +00003202
3203/*
3204%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3205% %
3206% %
3207% %
3208% T h u m b n a i l I m a g e %
3209% %
3210% %
3211% %
3212%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3213%
3214% ThumbnailImage() changes the size of an image to the given dimensions and
3215% removes any associated profiles. The goal is to produce small low cost
3216% thumbnail images suited for display on the Web.
3217%
3218% The format of the ThumbnailImage method is:
3219%
cristybb503372010-05-27 20:51:26 +00003220% Image *ThumbnailImage(const Image *image,const size_t columns,
3221% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003222%
3223% A description of each parameter follows:
3224%
3225% o image: the image.
3226%
3227% o columns: the number of columns in the scaled image.
3228%
3229% o rows: the number of rows in the scaled image.
3230%
3231% o exception: return any errors or warnings in this structure.
3232%
3233*/
cristy9af9b5d2010-08-15 17:04:28 +00003234MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3235 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003236{
3237#define SampleFactor 5
3238
3239 char
3240 value[MaxTextExtent];
3241
3242 const char
3243 *name;
3244
3245 Image
3246 *thumbnail_image;
3247
3248 MagickRealType
3249 x_factor,
3250 y_factor;
3251
cristybb503372010-05-27 20:51:26 +00003252 size_t
cristy3ed852e2009-09-05 21:47:34 +00003253 version;
3254
cristy9af9b5d2010-08-15 17:04:28 +00003255 struct stat
3256 attributes;
3257
cristy3ed852e2009-09-05 21:47:34 +00003258 assert(image != (Image *) NULL);
3259 assert(image->signature == MagickSignature);
3260 if (image->debug != MagickFalse)
3261 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3262 assert(exception != (ExceptionInfo *) NULL);
3263 assert(exception->signature == MagickSignature);
3264 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3265 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3266 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003267 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3268 exception);
cristy3ed852e2009-09-05 21:47:34 +00003269 else
3270 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003271 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3272 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003273 else
3274 {
3275 Image
3276 *sample_image;
3277
3278 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3279 exception);
3280 if (sample_image == (Image *) NULL)
3281 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003282 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3283 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003284 sample_image=DestroyImage(sample_image);
3285 }
3286 if (thumbnail_image == (Image *) NULL)
3287 return(thumbnail_image);
3288 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3289 if (thumbnail_image->matte == MagickFalse)
3290 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3291 thumbnail_image->depth=8;
3292 thumbnail_image->interlace=NoInterlace;
3293 /*
3294 Strip all profiles except color profiles.
3295 */
3296 ResetImageProfileIterator(thumbnail_image);
3297 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3298 {
3299 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3300 {
cristy2b726bd2010-01-11 01:05:39 +00003301 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003302 ResetImageProfileIterator(thumbnail_image);
3303 }
3304 name=GetNextImageProfile(thumbnail_image);
3305 }
3306 (void) DeleteImageProperty(thumbnail_image,"comment");
3307 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003308 if (strstr(image->magick_filename,"//") == (char *) NULL)
3309 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003310 image->magick_filename);
3311 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3312 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3313 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3314 {
cristye8c25f92010-06-03 00:53:06 +00003315 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003316 attributes.st_mtime);
3317 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3318 }
cristye8c25f92010-06-03 00:53:06 +00003319 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003320 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003321 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003322 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003323 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3324 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3325 LocaleLower(value);
3326 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3327 (void) SetImageProperty(thumbnail_image,"software",
3328 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003329 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3330 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003331 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003332 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003333 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003334 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003335 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3336 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003337 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3338 return(thumbnail_image);
3339}