blob: 2a804eaa562c38c3762ab27d842bde7aa07fe090 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% RRRR EEEEE SSSSS IIIII ZZZZZ EEEEE %
7% R R E SS I ZZ E %
8% RRRR EEE SSS I ZZZ EEE %
9% R R E SS I ZZ E %
10% R R EEEEE SSSSS IIIII ZZZZZ EEEEE %
11% %
12% %
13% MagickCore Image Resize Methods %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
42#include "magick/studio.h"
43#include "magick/artifact.h"
44#include "magick/blob.h"
45#include "magick/cache.h"
46#include "magick/cache-view.h"
47#include "magick/color.h"
48#include "magick/color-private.h"
49#include "magick/draw.h"
50#include "magick/exception.h"
51#include "magick/exception-private.h"
52#include "magick/gem.h"
53#include "magick/image.h"
54#include "magick/image-private.h"
55#include "magick/list.h"
56#include "magick/memory_.h"
anthony55f12332010-09-10 01:13:02 +000057#include "magick/magick.h"
cristy3ed852e2009-09-05 21:47:34 +000058#include "magick/pixel-private.h"
59#include "magick/property.h"
60#include "magick/monitor.h"
61#include "magick/monitor-private.h"
62#include "magick/pixel.h"
63#include "magick/option.h"
64#include "magick/resample.h"
65#include "magick/resize.h"
66#include "magick/resize-private.h"
67#include "magick/string_.h"
cristyf2f27272009-12-17 14:48:46 +000068#include "magick/string-private.h"
cristy3ed852e2009-09-05 21:47:34 +000069#include "magick/thread-private.h"
70#include "magick/utility.h"
71#include "magick/version.h"
72#if defined(MAGICKCORE_LQR_DELEGATE)
73#include <lqr.h>
74#endif
75
76/*
77 Typedef declarations.
78*/
79struct _ResizeFilter
80{
81 MagickRealType
82 (*filter)(const MagickRealType,const ResizeFilter *),
83 (*window)(const MagickRealType,const ResizeFilter *),
cristy33b1c162010-01-23 22:51:51 +000084 support, /* filter region of support - the filter support limit */
85 window_support, /* window support, usally equal to support (expert only) */
anthony55f12332010-09-10 01:13:02 +000086 scale, /* dimension scaling to fit window support (usally 1.0) */
cristy33b1c162010-01-23 22:51:51 +000087 blur, /* x-scale (blur-sharpen) */
anthonyf5e76ef2010-10-12 01:22:01 +000088 coeff[8]; /* cubic coefficents for smooth Cubic filters */
cristy3ed852e2009-09-05 21:47:34 +000089
cristybb503372010-05-27 20:51:26 +000090 size_t
cristy3ed852e2009-09-05 21:47:34 +000091 signature;
92};
93
94/*
95 Forward declaractions.
96*/
97static MagickRealType
98 I0(MagickRealType x),
anthonyb6d08c52010-09-13 01:17:04 +000099 BesselOrderOne(MagickRealType),
anthony07a3f7f2010-09-16 03:03:11 +0000100 Sinc(const MagickRealType, const ResizeFilter *),
anthonyba5a7c32010-09-15 02:42:25 +0000101 SincFast(const MagickRealType, const ResizeFilter *);
cristy3ed852e2009-09-05 21:47:34 +0000102
103/*
104%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
105% %
106% %
107% %
108+ F i l t e r F u n c t i o n s %
109% %
110% %
111% %
112%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
113%
cristy33b1c162010-01-23 22:51:51 +0000114% These are the various filter and windowing functions that are provided.
cristy3ed852e2009-09-05 21:47:34 +0000115%
cristy33b1c162010-01-23 22:51:51 +0000116% They are internal to this module only. See AcquireResizeFilterInfo() for
117% details of the access to these functions, via the GetResizeFilterSupport()
118% and GetResizeFilterWeight() API interface.
cristy3ed852e2009-09-05 21:47:34 +0000119%
120% The individual filter functions have this format...
121%
122% static MagickRealtype *FilterName(const MagickRealType x,
123% const MagickRealType support)
124%
cristy33b1c162010-01-23 22:51:51 +0000125% A description of each parameter follows:
cristy3ed852e2009-09-05 21:47:34 +0000126%
cristy33b1c162010-01-23 22:51:51 +0000127% o x: the distance from the sampling point generally in the range of 0 to
128% support. The GetResizeFilterWeight() ensures this a positive value.
129%
130% o resize_filter: current filter information. This allows function to
131% access support, and possibly other pre-calculated information defining
132% the functions.
cristy3ed852e2009-09-05 21:47:34 +0000133%
134*/
135
cristyc5c6f662010-09-22 14:23:02 +0000136#define MagickPIL ((MagickRealType) 3.14159265358979323846264338327950288420L)
cristy560d8182010-09-08 22:36:25 +0000137
anthony48f77622010-10-03 14:32:31 +0000138static MagickRealType Jinc(const MagickRealType x,
cristy3ed852e2009-09-05 21:47:34 +0000139 const ResizeFilter *magick_unused(resize_filter))
140{
141 /*
anthony48f77622010-10-03 14:32:31 +0000142 See Pratt "Digital Image Processing" p.97 for Jinc/Bessel functions.
cristy33b1c162010-01-23 22:51:51 +0000143 http://mathworld.wolfram.com/JincFunction.html and page 11 of
anthony48f77622010-10-03 14:32:31 +0000144 http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
145
nicolase473f722010-10-07 00:05:13 +0000146 The original "zoom" program by Paul Heckbert called this "Bessel".
147 But really it is more accurately named "Jinc".
cristy3ed852e2009-09-05 21:47:34 +0000148 */
149 if (x == 0.0)
nicolas5a36f342010-10-07 00:11:32 +0000150 return(0.5*MagickPIL);
151 return(BesselOrderOne(MagickPIL*x)/x);
cristy3ed852e2009-09-05 21:47:34 +0000152}
153
154static MagickRealType Blackman(const MagickRealType x,
cristyce70c172010-01-07 17:15:30 +0000155 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000156{
157 /*
cristy83017922010-09-05 20:45:15 +0000158 Blackman: 2nd order cosine windowing function:
cristy21ce88a2010-09-05 01:37:25 +0000159 0.42 + 0.5 cos(pi x) + 0.08 cos(2pi x)
cristy560d8182010-09-08 22:36:25 +0000160 Refactored by Chantal Racette and Nicolas Robidoux to one trig
161 call and five flops.
cristy3ed852e2009-09-05 21:47:34 +0000162 */
cristyc5c6f662010-09-22 14:23:02 +0000163 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000164 return(0.34+cospix*(0.5+cospix*0.16));
cristy3ed852e2009-09-05 21:47:34 +0000165}
166
167static MagickRealType Bohman(const MagickRealType x,
cristyce70c172010-01-07 17:15:30 +0000168 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000169{
170 /*
cristy560d8182010-09-08 22:36:25 +0000171 Bohman: 2rd Order cosine windowing function:
172 (1-x) cos(pi x) + sin(pi x) / pi.
nicolasa4f7b4a2010-09-20 20:28:38 +0000173 Refactored by Nicolas Robidoux to one trig call, one sqrt call,
174 and 7 flops, taking advantage of the fact that the support of
175 Bohman is 1 (so that we know that sin(pi x) >= 0).
cristy3ed852e2009-09-05 21:47:34 +0000176 */
cristyc5c6f662010-09-22 14:23:02 +0000177 const double cospix = cos((double) (MagickPIL*x));
nicolasa4f7b4a2010-09-20 20:28:38 +0000178 const double sinpix = sqrt(1.0-cospix*cospix);
nicolas2ffd3b22010-09-24 20:27:31 +0000179 return((1.0-x)*cospix+(1.0/MagickPIL)*sinpix);
cristy3ed852e2009-09-05 21:47:34 +0000180}
181
anthony463be1d2010-09-26 01:07:36 +0000182static MagickRealType Box(const MagickRealType magick_unused(x),
183 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000184{
185 /*
nicolas40477452010-09-27 23:42:08 +0000186 A Box filter is a equal weighting function (all weights equal).
anthonyc331dec2010-09-26 01:30:14 +0000187 DO NOT LIMIT results by support or resize point sampling will work
188 as it requests points beyond its normal 0.0 support size.
cristy3ed852e2009-09-05 21:47:34 +0000189 */
anthony463be1d2010-09-26 01:07:36 +0000190 return(1.0);
cristy3ed852e2009-09-05 21:47:34 +0000191}
192
193static MagickRealType CubicBC(const MagickRealType x,
194 const ResizeFilter *resize_filter)
195{
196 /*
197 Cubic Filters using B,C determined values:
cristyf59a8922010-02-28 19:51:23 +0000198 Mitchell-Netravali B=1/3 C=1/3 Qualitively ideal Cubic Filter
199 Catmull-Rom B= 0 C=1/2 Cublic Interpolation Function
200 Cubic B-Spline B= 1 C= 0 Spline Approximation of Gaussian
201 Hermite B= 0 C= 0 Quadratic Spline (support = 1)
cristy3ed852e2009-09-05 21:47:34 +0000202
cristy33b1c162010-01-23 22:51:51 +0000203 See paper by Mitchell and Netravali, Reconstruction Filters in Computer
204 Graphics Computer Graphics, Volume 22, Number 4, August 1988
205 http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/
cristyf59a8922010-02-28 19:51:23 +0000206 Mitchell.pdf.
cristy3ed852e2009-09-05 21:47:34 +0000207
cristy33b1c162010-01-23 22:51:51 +0000208 Coefficents are determined from B,C values:
cristy3ed852e2009-09-05 21:47:34 +0000209 P0 = ( 6 - 2*B )/6
210 P1 = 0
211 P2 = (-18 +12*B + 6*C )/6
212 P3 = ( 12 - 9*B - 6*C )/6
213 Q0 = ( 8*B +24*C )/6
214 Q1 = ( -12*B -48*C )/6
215 Q2 = ( 6*B +30*C )/6
216 Q3 = ( - 1*B - 6*C )/6
217
cristy33b1c162010-01-23 22:51:51 +0000218 which are used to define the filter:
cristyf59a8922010-02-28 19:51:23 +0000219
cristy3ed852e2009-09-05 21:47:34 +0000220 P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
221 Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x <= 2
222
cristy33b1c162010-01-23 22:51:51 +0000223 which ensures function is continuous in value and derivative (slope).
cristy3ed852e2009-09-05 21:47:34 +0000224 */
225 if (x < 1.0)
anthonyf5e76ef2010-10-12 01:22:01 +0000226 return(resize_filter->coeff[0]+x*(resize_filter->coeff[1]+x*
227 (resize_filter->coeff[2]+x*resize_filter->coeff[3])));
cristy3ed852e2009-09-05 21:47:34 +0000228 if (x < 2.0)
anthonyf5e76ef2010-10-12 01:22:01 +0000229 return(resize_filter->coeff[4]+x*(resize_filter->coeff[5]+x*
230 (resize_filter->coeff[6]+x*resize_filter->coeff[7])));
cristy3ed852e2009-09-05 21:47:34 +0000231 return(0.0);
232}
233
234static MagickRealType Gaussian(const MagickRealType x,
anthonyf5e76ef2010-10-12 01:22:01 +0000235 const ResizeFilter *resize_filter)
cristy3ed852e2009-09-05 21:47:34 +0000236{
cristy560d8182010-09-08 22:36:25 +0000237 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000238 Gaussian with a fixed sigma = 1/2
239
240 Gaussian Formula...
241 exp( -(x^2)/((2.0*sigma^2) ) / sqrt(2*PI*sigma^2)))
242 The constants are pre-calculated...
243 exp( -coeff[0]*(x^2)) ) * coeff[1]
244 However the multiplier coefficent is not needed and not used.
245
246 This separates the gaussian 'sigma' value from the 'blur/support' settings
247 allows for its use in special 'small sigma' gaussians, without the filter
248 'missing' pixels when blur and thus support becomes too small.
cristy560d8182010-09-08 22:36:25 +0000249 */
anthonyf5e76ef2010-10-12 01:22:01 +0000250 return(exp((double)(-resize_filter->coeff[0]*x*x))); }
cristy3ed852e2009-09-05 21:47:34 +0000251
252static MagickRealType Hanning(const MagickRealType x,
253 const ResizeFilter *magick_unused(resize_filter))
254{
255 /*
nicolas40477452010-09-27 23:42:08 +0000256 Cosine window function:
257 .5+.5cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000258 */
cristyc5c6f662010-09-22 14:23:02 +0000259 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000260 return(0.5+0.5*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000261}
262
263static MagickRealType Hamming(const MagickRealType x,
264 const ResizeFilter *magick_unused(resize_filter))
265{
266 /*
nicolas40477452010-09-27 23:42:08 +0000267 Offset cosine window function:
268 .54 + .46 cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000269 */
cristyc5c6f662010-09-22 14:23:02 +0000270 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000271 return(0.54+0.46*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000272}
273
274static MagickRealType Kaiser(const MagickRealType x,
275 const ResizeFilter *magick_unused(resize_filter))
276{
277#define Alpha 6.5
278#define I0A (1.0/I0(Alpha))
279
280 /*
nicolas07bac812010-09-19 18:47:02 +0000281 Kaiser Windowing Function (bessel windowing): Alpha is a free
282 value from 5 to 8 (currently hardcoded to 6.5).
283 Future: make alpha the IOA pre-calculation, an 'expert' setting.
cristy3ed852e2009-09-05 21:47:34 +0000284 */
285 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
286}
287
288static MagickRealType Lagrange(const MagickRealType x,
289 const ResizeFilter *resize_filter)
290{
cristy3ed852e2009-09-05 21:47:34 +0000291 MagickRealType
292 value;
293
cristybb503372010-05-27 20:51:26 +0000294 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000295 i;
296
cristy9af9b5d2010-08-15 17:04:28 +0000297 ssize_t
298 n,
299 order;
300
cristy3ed852e2009-09-05 21:47:34 +0000301 /*
nicolas07bac812010-09-19 18:47:02 +0000302 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the
303 lagrange function and depends on the overall support window size
304 of the filter. That is: for a support of 2, it gives a lagrange-4
305 (piecewise cubic function).
cristy3ed852e2009-09-05 21:47:34 +0000306
nicolas07bac812010-09-19 18:47:02 +0000307 "n" identifies the piece of the piecewise polynomial.
cristy3ed852e2009-09-05 21:47:34 +0000308
nicolas07bac812010-09-19 18:47:02 +0000309 See Survey: Interpolation Methods, IEEE Transactions on Medical
310 Imaging, Vol 18, No 11, November 1999, p1049-1075, -- Equation 27
311 on p1064.
cristy3ed852e2009-09-05 21:47:34 +0000312 */
313 if (x > resize_filter->support)
314 return(0.0);
cristybb503372010-05-27 20:51:26 +0000315 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
anthonyc2d07db2010-09-15 23:47:40 +0000316 /*n=(ssize_t)((1.0*order)/2.0+x); -- which piece does x belong to */
317 n = (ssize_t)(resize_filter->window_support + x);
cristy3ed852e2009-09-05 21:47:34 +0000318 value=1.0f;
319 for (i=0; i < order; i++)
320 if (i != n)
321 value*=(n-i-x)/(n-i);
322 return(value);
323}
324
325static MagickRealType Quadratic(const MagickRealType x,
326 const ResizeFilter *magick_unused(resize_filter))
327{
328 /*
329 2rd order (quadratic) B-Spline approximation of Gaussian.
330 */
331 if (x < 0.5)
332 return(0.75-x*x);
333 if (x < 1.5)
334 return(0.5*(x-1.5)*(x-1.5));
335 return(0.0);
336}
337
anthony07a3f7f2010-09-16 03:03:11 +0000338static MagickRealType Sinc(const MagickRealType x,
cristy3ed852e2009-09-05 21:47:34 +0000339 const ResizeFilter *magick_unused(resize_filter))
340{
anthony720660f2010-09-07 10:05:14 +0000341 /*
nicolas40477452010-09-27 23:42:08 +0000342 Scaled sinc(x) function using a trig call:
nicolas07bac812010-09-19 18:47:02 +0000343 sinc(x) == sin(pi x)/(pi x).
anthony720660f2010-09-07 10:05:14 +0000344 */
anthony2d9b8b52010-09-14 08:31:07 +0000345 if (x != 0.0)
cristy560d8182010-09-08 22:36:25 +0000346 {
cristyc5c6f662010-09-22 14:23:02 +0000347 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
nicolas07bac812010-09-19 18:47:02 +0000348 return(sin((double) pix)/pix);
cristy560d8182010-09-08 22:36:25 +0000349 }
nicolas2ffd3b22010-09-24 20:27:31 +0000350 return((MagickRealType) 1.0);
anthony720660f2010-09-07 10:05:14 +0000351}
352
anthonyba5a7c32010-09-15 02:42:25 +0000353static MagickRealType SincFast(const MagickRealType x,
anthony720660f2010-09-07 10:05:14 +0000354 const ResizeFilter *magick_unused(resize_filter))
355{
cristy560d8182010-09-08 22:36:25 +0000356 /*
357 Approximations of the sinc function sin(pi x)/(pi x) over the
358 interval [-4,4] constructed by Nicolas Robidoux and Chantal
359 Racette with funding from the Natural Sciences and Engineering
360 Research Council of Canada.
nicolas07bac812010-09-19 18:47:02 +0000361
362 Although the approximations are polynomials (for low order of
363 approximation) and quotients of polynomials (for higher order of
364 approximation) and consequently are similar in form to Taylor
365 polynomials/Pade approximants, the approximations are computed
366 with a completely different technique.
367
368 Summary: These approximations are "the best" in terms of bang
369 (accuracy) for the buck (flops). More specifically: Among the
370 polynomial quotients that can be computed using a fixed number of
371 flops (with a given "+ - * / budget"), the chosen polynomial
372 quotient is the one closest to the approximated function with
373 respect to maximum absolute relative error over the given
374 interval.
375
376 The Remez algorithm, as implemented in the boost library's minimax
nicolas3aab40c2010-09-19 21:14:15 +0000377 package, is the key to the construction:
nicolas07bac812010-09-19 18:47:02 +0000378 http://www.boost.org/doc/libs/1_36_0/libs/math/doc/...
379 ...sf_and_dist/html/math_toolkit/backgrounders/remez.html
cristy560d8182010-09-08 22:36:25 +0000380 */
nicolas3aab40c2010-09-19 21:14:15 +0000381 /*
382 If outside of the interval of approximation, use the standard trig
383 formula.
384 */
anthony2d9b8b52010-09-14 08:31:07 +0000385 if (x > 4.0)
cristy03dbbd22010-09-19 23:04:47 +0000386 {
cristyc5c6f662010-09-22 14:23:02 +0000387 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
cristy03dbbd22010-09-19 23:04:47 +0000388 return(sin((double) pix)/pix);
389 }
anthony2d9b8b52010-09-14 08:31:07 +0000390 {
nicolas07bac812010-09-19 18:47:02 +0000391 /*
392 The approximations only depend on x^2 (sinc is an even
393 function).
394 */
395 const MagickRealType xx = x*x;
cristy83017922010-09-05 20:45:15 +0000396#if MAGICKCORE_QUANTUM_DEPTH <= 8
397 /*
anthony2d9b8b52010-09-14 08:31:07 +0000398 Maximum absolute relative error 6.3e-6 < 1/2^17.
cristy738e7562010-09-01 12:48:07 +0000399 */
400 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
401 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
402 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
403 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
404 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
405 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
406 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
407 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
nicolas3aab40c2010-09-19 21:14:15 +0000408 const MagickRealType p =
409 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
nicolas07bac812010-09-19 18:47:02 +0000410 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
anthony2d9b8b52010-09-14 08:31:07 +0000411#elif MAGICKCORE_QUANTUM_DEPTH <= 16
cristydbeb3eb2010-09-09 13:41:36 +0000412 /*
anthony2d9b8b52010-09-14 08:31:07 +0000413 Max. abs. rel. error 2.2e-8 < 1/2^25.
cristydbeb3eb2010-09-09 13:41:36 +0000414 */
415 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
416 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
nicolas3aab40c2010-09-19 21:14:15 +0000417 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
418 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
419 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
420 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
421 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
cristydbeb3eb2010-09-09 13:41:36 +0000422 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
423 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
424 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
anthony853d6972010-10-08 06:01:31 +0000425 const MagickRealType p =
nicolas3aab40c2010-09-19 21:14:15 +0000426 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
nicolas07bac812010-09-19 18:47:02 +0000427 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
nicolasc2d28f02010-09-27 18:56:15 +0000428#else
nicolas3aab40c2010-09-19 21:14:15 +0000429 /*
430 Max. abs. rel. error 1.2e-12 < 1/2^39.
431 */
432 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
433 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
434 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
435 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
436 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
437 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
438 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
439 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
440 const MagickRealType p =
441 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
442 const MagickRealType d0 = 1.0L;
443 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
444 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
445 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
446 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
447 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
448 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
cristy657c6352010-08-29 14:05:08 +0000449#endif
cristy83017922010-09-05 20:45:15 +0000450 }
cristy3ed852e2009-09-05 21:47:34 +0000451}
452
453static MagickRealType Triangle(const MagickRealType x,
454 const ResizeFilter *magick_unused(resize_filter))
455{
456 /*
nicolas0edb0862010-09-19 18:56:19 +0000457 1st order (linear) B-Spline, bilinear interpolation, Tent 1D
458 filter, or a Bartlett 2D Cone filter.
cristy3ed852e2009-09-05 21:47:34 +0000459 */
460 if (x < 1.0)
461 return(1.0-x);
462 return(0.0);
463}
464
465static MagickRealType Welsh(const MagickRealType x,
466 const ResizeFilter *magick_unused(resize_filter))
467{
468 /*
469 Welsh parabolic windowing filter.
470 */
cristy560d8182010-09-08 22:36:25 +0000471 if (x < 1.0)
cristy3ed852e2009-09-05 21:47:34 +0000472 return(1.0-x*x);
473 return(0.0);
474}
475
476/*
477%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
478% %
479% %
480% %
481+ A c q u i r e R e s i z e F i l t e r %
482% %
483% %
484% %
485%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
486%
nicolas07bac812010-09-19 18:47:02 +0000487% AcquireResizeFilter() allocates the ResizeFilter structure. Choose
488% from these filters:
cristy3ed852e2009-09-05 21:47:34 +0000489%
490% FIR (Finite impulse Response) Filters
491% Box Triangle Quadratic
492% Cubic Hermite Catrom
493% Mitchell
494%
495% IIR (Infinite impulse Response) Filters
anthony48f77622010-10-03 14:32:31 +0000496% Gaussian Sinc Jinc (Bessel)
cristy3ed852e2009-09-05 21:47:34 +0000497%
anthony48f77622010-10-03 14:32:31 +0000498% Windowed Sinc/Jinc Filters
cristy3ed852e2009-09-05 21:47:34 +0000499% Blackman Hanning Hamming
anthony48f77622010-10-03 14:32:31 +0000500% Kaiser Lanczos
cristy3ed852e2009-09-05 21:47:34 +0000501%
anthony61b5ddd2010-10-05 02:33:31 +0000502% Special purpose Filters
anthony853d6972010-10-08 06:01:31 +0000503% SincFast Lanczos2D Robidoux
anthony61b5ddd2010-10-05 02:33:31 +0000504%
anthony48f77622010-10-03 14:32:31 +0000505% The users "-filter" selection is used to lookup the default 'expert'
506% settings for that filter from a internal table. However any provided
507% 'expert' settings (see below) may override this selection.
508%
509% FIR filters are used as is, and are limited to that filters support
nicolas07bac812010-09-19 18:47:02 +0000510% window (unless over-ridden). 'Gaussian' while classed as an IIR
anthonyc331dec2010-09-26 01:30:14 +0000511% filter, is also simply clipped by its support size (currently 1.5
anthonyf5e76ef2010-10-12 01:22:01 +0000512% or approximatally 3*sigma as recommended by many references)
cristy3ed852e2009-09-05 21:47:34 +0000513%
anthony48f77622010-10-03 14:32:31 +0000514% The selection is typically either a windowed Sinc, or interpolated
nicolas07bac812010-09-19 18:47:02 +0000515% filter, for use by functions such as ResizeImage(). However if a
anthony48f77622010-10-03 14:32:31 +0000516% 'cylindrical' filter flag is requested, any default Sinc weighting
517% and windowing functions will be promoted to cylindrical Jinc form of
518% function.
cristy3ed852e2009-09-05 21:47:34 +0000519%
anthony48f77622010-10-03 14:32:31 +0000520% Directly requesting 'Sinc' or 'Jinc' will force the use of that
521% filter function without any windowing. This is not recommended,
522% except by image processing experts or in expert options. Selecting a
523% window filtering version of these functions is better.
cristy3ed852e2009-09-05 21:47:34 +0000524%
anthony48f77622010-10-03 14:32:31 +0000525% Lanczos is a special case of a Sinc-windowed Sinc, (or Jinc-Jinc for
526% the cylindrical case) but defaulting to 3-lobe support, rather that
527% the default 4 lobe support of the other windowed sinc/jinc filters.
cristy3ed852e2009-09-05 21:47:34 +0000528%
anthony48f77622010-10-03 14:32:31 +0000529% Two forms of the 'Sinc' function are available: Sinc and SincFast.
nicolas07bac812010-09-19 18:47:02 +0000530% Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
531% selected if the user specifically specifies the use of a Sinc
532% filter. SincFast uses highly accurate (and fast) polynomial (low Q)
anthony48f77622010-10-03 14:32:31 +0000533% and rational (high Q) approximations, and will be used by default in
534% most cases.
anthonyba5a7c32010-09-15 02:42:25 +0000535%
nicolas1eb2fcf2010-10-10 20:45:17 +0000536% The Lanczos2D and Robidoux filters are tuned for cylindrical
537% (radial) EWA (Elliptical Weighted Average) distortion. Lanczos2D
nicolasdff19b42010-10-10 20:53:13 +0000538% is a 2 lobe Lanczos-like filter using Jinc (for EWA) or Sinc.
nicolas6ca6e962010-10-10 20:49:01 +0000539% Robidoux used to be a sharpened version of Lanczos2D (with
nicolasdff19b42010-10-10 20:53:13 +0000540% blur=0.958033808). Now, it is the unique Cubic 'Keys' filter that
541% exactly preserves images with only vertical or horizontal features
542% when performing 'no-op" with EWA distortion. It turns out to be
nicolas219dd842010-10-10 21:02:40 +0000543% close to both plain Mitchell and "sharpened" Lanczos2D.
anthony61b5ddd2010-10-05 02:33:31 +0000544%
nicolas07bac812010-09-19 18:47:02 +0000545% Special 'expert' options can be used to override any and all filter
546% settings. This is not advised unless you have expert knowledge of
547% the use of resampling filtered techniques. Check on the results of
548% your selections using the "filter:verbose" setting to make sure you
anthony61b5ddd2010-10-05 02:33:31 +0000549% get the exact filter that you are tring to achieve.
cristy3ed852e2009-09-05 21:47:34 +0000550%
anthony48f77622010-10-03 14:32:31 +0000551% "filter:filter" Select the main function associated with
552% this filter name, as the weighting function of the filter.
553% This can be used to set a windowing function as a weighting
554% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000555%
anthony7bdc0ed2010-09-15 01:52:32 +0000556% If a "filter:window" operation has not been provided, then a 'Box'
557% windowing function will be set to denote that no windowing function
558% is being used.
cristy3ed852e2009-09-05 21:47:34 +0000559%
560% "filter:window" Select this windowing function for the filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000561% While any filter could be used as a windowing function, using the
562% 'first lobe' of that filter over the whole support window, using a
anthony48f77622010-10-03 14:32:31 +0000563% non-windowing function is not advisible. If no weighting filter
564% function is specifed a 'SincFast' filter will be used.
cristy3ed852e2009-09-05 21:47:34 +0000565%
anthony48f77622010-10-03 14:32:31 +0000566% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000567% This a simpler method of setting filter support size that will
anthony48f77622010-10-03 14:32:31 +0000568% correctly handle the Sinc/Jinc switch for an operators filtering
anthony7bdc0ed2010-09-15 01:52:32 +0000569% requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000570%
571% "filter:support" Set the support size for filtering to the size given
anthony48f77622010-10-03 14:32:31 +0000572% This not recommended for Sinc/Jinc windowed filters (lobes should
anthony7bdc0ed2010-09-15 01:52:32 +0000573% be used instead). This will override any 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000574%
anthonyb6d08c52010-09-13 01:17:04 +0000575% "filter:win-support" Scale windowing function to this size instead.
576% This causes the windowing (or self-windowing Lagrange filter) to act
577% is if the support window it much much larger than what is actually
578% supplied to the calling operator. The filter however is still
579% clipped to the real support size given, by the support range suppiled
580% to the caller. If unset this will equal the normal filter support
581% size.
582%
cristy3ed852e2009-09-05 21:47:34 +0000583% "filter:blur" Scale the filter and support window by this amount.
584% A value >1 will generally result in a more burred image with
585% more ringing effects, while a value <1 will sharpen the
586% resulting image with more aliasing and Morie effects.
587%
anthonyf5e76ef2010-10-12 01:22:01 +0000588% "filter:sigma" The sigma value to use for the Gaussian filter only.
589% Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for cylindrical
590% usage. It effectially provides a alturnative to 'blur' for Gaussians
591% without it also effecting the final 'practical support' size.
592%
cristy3ed852e2009-09-05 21:47:34 +0000593% "filter:b"
594% "filter:c" Override the preset B,C values for a Cubic type of filter
595% If only one of these are given it is assumes to be a 'Keys'
596% type of filter such that B+2C=1, where Keys 'alpha' value = C
597%
anthonyb6d08c52010-09-13 01:17:04 +0000598% "filter:verbose" Output the exact results of the filter selections
599% made, as well as plotting data for graphing the resulting filter
600% over support range (blur adjusted).
cristy3ed852e2009-09-05 21:47:34 +0000601%
602% Set a true un-windowed Sinc filter with 10 lobes (very slow)
anthony48f77622010-10-03 14:32:31 +0000603% -define filter:filter=Sinc
604% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000605%
anthony48f77622010-10-03 14:32:31 +0000606% For example force an 8 lobe Lanczos (Sinc or Jinc) filter...
cristy3ed852e2009-09-05 21:47:34 +0000607% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000608% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000609%
cristy3ed852e2009-09-05 21:47:34 +0000610% The format of the AcquireResizeFilter method is:
611%
612% ResizeFilter *AcquireResizeFilter(const Image *image,
613% const FilterTypes filter_type, const MagickBooleanType radial,
614% ExceptionInfo *exception)
615%
cristy33b1c162010-01-23 22:51:51 +0000616% A description of each parameter follows:
617%
cristy3ed852e2009-09-05 21:47:34 +0000618% o image: the image.
619%
nicolas07bac812010-09-19 18:47:02 +0000620% o filter: the filter type, defining a preset filter, window and
621% support. The artifact settings listed above will override
622% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000623%
anthony48f77622010-10-03 14:32:31 +0000624% o blur: blur the filter by this amount, use 1.0 if unknown. Image
625% artifact "filter:blur" will override this API call usage, including
626% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000627%
anthony48f77622010-10-03 14:32:31 +0000628% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
629% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000630%
631% o exception: return any errors or warnings in this structure.
632%
633*/
634MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000635 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000636 const MagickBooleanType cylindrical,ExceptionInfo *exception)
637{
638 const char
639 *artifact;
640
641 FilterTypes
642 filter_type,
643 window_type;
644
cristy3ed852e2009-09-05 21:47:34 +0000645 MagickRealType
646 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000647 C,
648 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000649
650 register ResizeFilter
651 *resize_filter;
652
cristy9af9b5d2010-08-15 17:04:28 +0000653 ssize_t
654 option;
655
cristy3ed852e2009-09-05 21:47:34 +0000656 /*
anthony48f77622010-10-03 14:32:31 +0000657 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000658 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000659 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
660 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
661 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000662
nicolas07bac812010-09-19 18:47:02 +0000663 WARNING: The order of this tabel must match the order of the
664 FilterTypes enumeration specified in "resample.h", or the filter
665 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000666
667 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000668 */
669 static struct
670 {
671 FilterTypes
672 filter,
673 window;
674 } const mapping[SentinelFilter] =
675 {
anthony462ee072010-09-27 12:34:02 +0000676 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
677 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
678 { BoxFilter, BoxFilter }, /* Box averaging filter */
679 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
680 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
681 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
682 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
683 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
684 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
685 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
686 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
687 { CatromFilter, BoxFilter }, /* Cubic interpolator */
688 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
689 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
anthony48f77622010-10-03 14:32:31 +0000690 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
691 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony462ee072010-09-27 12:34:02 +0000692 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
693 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
694 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
695 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
696 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
697 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
698 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolas5835ee02010-10-10 20:40:15 +0000699 { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
anthonye5f06452010-10-12 05:48:17 +0000700 { Lanczos2DSharpFilter, JincFilter },/* SPECIAL: ditto sharpened */
701 { RobidouxFilter, BoxFilter }, /* SPECIAL: Keys cubic tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000702 };
703 /*
nicolas32f44eb2010-09-20 01:23:12 +0000704 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000705 function. The default support size for that filter as a weighting
706 function, the range to scale with to use that function as a sinc
707 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000708
anthony07a3f7f2010-09-16 03:03:11 +0000709 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000710 SincFast(), and CubicBC() functions, which may have multiple
711 filter to function associations.
712
713 See "filter:verbose" handling below for the function -> filter
714 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000715 */
716 static struct
717 {
718 MagickRealType
719 (*function)(const MagickRealType, const ResizeFilter*),
anthony61b5ddd2010-10-05 02:33:31 +0000720 lobes, /* default lobes/support size of the weighting filter */
anthony2d9b8b52010-09-14 08:31:07 +0000721 scale, /* windowing function range, for scaling windowing function */
anthony933b4bf2010-09-10 02:31:37 +0000722 B,C; /* Cubic Filter factors for a CubicBC function, else ignored */
cristy3ed852e2009-09-05 21:47:34 +0000723 } const filters[SentinelFilter] =
724 {
anthony61b5ddd2010-10-05 02:33:31 +0000725 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
726 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
727 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
728 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
729 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
730 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
731 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
732 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000733 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000734 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
735 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
736 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
anthony853d6972010-10-08 06:01:31 +0000737 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
738 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
739 { Jinc, 3.0, 1.21967, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000740 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
741 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
742 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
743 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
744 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
745 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
746 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
747 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
anthony08958462010-10-12 06:48:35 +0000748 { Jinc, 2.0, 1.21966989, 0.0, 0.0 }, /* Lanczos2D (Jinc-Jinc) */
749 { Jinc, 2.0, 1.16848499, 0.0, 0.0 }, /* Lanczos2D Sharpened */
750 { CubicBC, 2.0, 1.16848499, 0.37821575509399862, 0.31089212245300069 }
nicolas0134bbe2010-10-10 15:55:42 +0000751 /* Robidoux: Keys cubic close to Lanczos2D with blur=0.958033808 */
cristy3ed852e2009-09-05 21:47:34 +0000752 };
753 /*
anthony9a98fc62010-10-11 02:47:19 +0000754 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
755 function being used as a filter. It is used by the "filter:lobes" and for
756 the 'lobes' number in the above, the for support selection, so users do
757 not have to deal with the highly irrational sizes of the 'lobes' of the
758 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000759
nicolase473f722010-10-07 00:05:13 +0000760 Values taken from
anthony48f77622010-10-03 14:32:31 +0000761 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000762 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000763 */
764 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000765 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000766 {
anthonyc2d07db2010-09-15 23:47:40 +0000767 1.21966989126651,
768 2.23313059438153,
769 3.23831548416624,
770 4.24106286379607,
771 5.24276437687019,
772 6.24392168986449,
773 7.24475986871996,
774 8.24539491395205,
775 9.24589268494948,
776 10.2462933487549,
777 11.2466227948779,
778 12.2468984611381,
779 13.2471325221811,
780 14.2473337358069,
781 15.2475085630373,
782 16.247661874701
cristy3ed852e2009-09-05 21:47:34 +0000783 };
784
cristy33b1c162010-01-23 22:51:51 +0000785 /*
786 Allocate resize filter.
787 */
cristy3ed852e2009-09-05 21:47:34 +0000788 assert(image != (const Image *) NULL);
789 assert(image->signature == MagickSignature);
790 if (image->debug != MagickFalse)
791 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
792 assert(UndefinedFilter < filter && filter < SentinelFilter);
793 assert(exception != (ExceptionInfo *) NULL);
794 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000795 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000796 if (resize_filter == (ResizeFilter *) NULL)
797 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000798 /*
799 Defaults for the requested filter.
800 */
801 filter_type=mapping[filter].filter;
802 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000803 resize_filter->blur = blur;
804 sigma = 0.5;
anthony48f77622010-10-03 14:32:31 +0000805 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000806 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000807 switch (filter_type)
808 {
809 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000810 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000811 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000812 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000813 break;
anthonyba5a7c32010-09-15 02:42:25 +0000814 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000815 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000816 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000817 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000818 break;
cristy33b1c162010-01-23 22:51:51 +0000819 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000820 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000821 filter_type=JincFilter;
822 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000823 break;
anthony08958462010-10-12 06:48:35 +0000824 case Lanczos2DSharpFilter:
825 /* Sharpened by Nicholas Robidoux so as to optimize for
826 * minimal blurring of orthogonal lines
827 */
828 resize_filter->blur *= 0.958033808;
829 break;
anthonyf5e76ef2010-10-12 01:22:01 +0000830 case GaussianFilter:
cristy68f103e2010-10-14 01:19:07 +0000831 sigma = (MagickRealType) (MagickSQ2/2.0); /* Cylindrical Gaussian sigma is sqrt(2)/2 */
anthonyf5e76ef2010-10-12 01:22:01 +0000832 break;
cristya782ecf2010-01-25 02:59:14 +0000833 default:
834 break;
cristy3ed852e2009-09-05 21:47:34 +0000835 }
anthony61b5ddd2010-10-05 02:33:31 +0000836 else
837 switch (filter_type)
838 {
839 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000840 case Lanczos2DSharpFilter:
nicolas45b58a92010-10-07 15:46:39 +0000841 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000842 window_type=SincFastFilter;
843 break;
844 default:
845 break;
846 }
847
cristy3ed852e2009-09-05 21:47:34 +0000848 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000849 if (artifact != (const char *) NULL)
850 {
cristy9af9b5d2010-08-15 17:04:28 +0000851 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000852 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000853 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000854 filter_type=(FilterTypes) option;
855 window_type=BoxFilter;
856 }
857 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000858 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000859 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000860 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000861 }
nicolas07bac812010-09-19 18:47:02 +0000862 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000863 artifact=GetImageArtifact(image,"filter:window");
864 if (artifact != (const char *) NULL)
865 {
cristy9af9b5d2010-08-15 17:04:28 +0000866 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000867 if ((UndefinedFilter < option) && (option < SentinelFilter))
868 {
869 if (option != LanczosFilter)
870 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000871 else
anthony48f77622010-10-03 14:32:31 +0000872 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000873 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000874 }
cristy33b1c162010-01-23 22:51:51 +0000875 }
cristy3ed852e2009-09-05 21:47:34 +0000876 }
cristy33b1c162010-01-23 22:51:51 +0000877 else
878 {
anthony48f77622010-10-03 14:32:31 +0000879 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000880 artifact=GetImageArtifact(image,"filter:window");
881 if (artifact != (const char *) NULL)
882 {
883 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
884 artifact);
885 if ((UndefinedFilter < option) && (option < SentinelFilter))
886 {
anthony61b5ddd2010-10-05 02:33:31 +0000887 filter_type=cylindrical != MagickFalse ?
888 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000889 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000890 }
891 }
892 }
nicolas07bac812010-09-19 18:47:02 +0000893 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000894 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000895 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000896 resize_filter->window=filters[window_type].function;
897 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000898 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000899
anthonyf5e76ef2010-10-12 01:22:01 +0000900 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000901 if (cylindrical != MagickFalse)
902 switch (filter_type)
903 {
904 case PointFilter:
905 case BoxFilter:
906 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000907 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000908 break;
anthony81b8bf92010-10-02 13:54:34 +0000909 default:
910 break;
anthony10b8bc82010-10-02 12:48:46 +0000911 }
anthony61b5ddd2010-10-05 02:33:31 +0000912 else
913 switch (filter_type)
914 {
915 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000916 case Lanczos2DSharpFilter:
anthony853d6972010-10-08 06:01:31 +0000917 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000918 resize_filter->filter=SincFast;
919 break;
920 default:
921 break;
922 }
923
anthonyf5e76ef2010-10-12 01:22:01 +0000924 /*
925 ** More Expert Option Modifications
926 */
927
928 /* User Sigma Override - no support change */
929 artifact=GetImageArtifact(image,"filter:sigma");
930 if (artifact != (const char *) NULL)
931 sigma=StringToDouble(artifact);
932 /* Define coefficents for Gaussian (assumes no cubic window) */
933 if ( GaussianFilter ) {
934 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
cristy68f103e2010-10-14 01:19:07 +0000935 resize_filter->coeff[1] = (MagickRealType) (1.0/(Magick2PI*sigma*sigma)); /* unused */
anthonyf5e76ef2010-10-12 01:22:01 +0000936 }
937
938 /* Blur Override */
939 artifact=GetImageArtifact(image,"filter:blur");
940 if (artifact != (const char *) NULL)
941 resize_filter->blur=StringToDouble(artifact);
942 if (resize_filter->blur < MagickEpsilon)
943 resize_filter->blur=(MagickRealType) MagickEpsilon;
944
945 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000946 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000947 if (artifact != (const char *) NULL)
948 {
cristybb503372010-05-27 20:51:26 +0000949 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000950 lobes;
951
cristy96b16132010-08-29 17:19:52 +0000952 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000953 if (lobes < 1)
954 lobes=1;
955 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000956 }
anthony61b5ddd2010-10-05 02:33:31 +0000957 /* convert Jinc lobes to a real support value */
958 if (resize_filter->filter == Jinc)
959 {
960 if (resize_filter->support > 16)
961 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
962 else
963 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
964 }
965 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000966 artifact=GetImageArtifact(image,"filter:support");
967 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000968 resize_filter->support=fabs(StringToDouble(artifact));
969 /*
nicolas07bac812010-09-19 18:47:02 +0000970 Scale windowing function separatally to the support 'clipping'
971 window that calling operator is planning to actually use. (Expert
972 override)
cristy3ed852e2009-09-05 21:47:34 +0000973 */
anthony55f12332010-09-10 01:13:02 +0000974 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000975 artifact=GetImageArtifact(image,"filter:win-support");
976 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000977 resize_filter->window_support=fabs(StringToDouble(artifact));
978 /*
anthony1f90a6b2010-09-14 08:56:31 +0000979 Adjust window function scaling to the windowing support for
980 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000981 */
982 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +0000983
anthony55f12332010-09-10 01:13:02 +0000984 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000985 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000986 */
cristy3ed852e2009-09-05 21:47:34 +0000987 B=0.0;
988 C=0.0;
cristy33b1c162010-01-23 22:51:51 +0000989 if ((filters[filter_type].function == CubicBC) ||
990 (filters[window_type].function == CubicBC))
991 {
anthony2d9b8b52010-09-14 08:31:07 +0000992 B=filters[filter_type].B;
993 C=filters[filter_type].C;
994 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +0000995 {
anthony2d9b8b52010-09-14 08:31:07 +0000996 B=filters[window_type].B;
997 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +0000998 }
cristy33b1c162010-01-23 22:51:51 +0000999 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001000 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001001 {
1002 B=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001003 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001004 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001005 if (artifact != (const char *) NULL)
1006 C=StringToDouble(artifact);
1007 }
1008 else
1009 {
1010 artifact=GetImageArtifact(image,"filter:c");
1011 if (artifact != (const char *) NULL)
1012 {
1013 C=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001014 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001015 }
1016 }
anthonyf5e76ef2010-10-12 01:22:01 +00001017 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
1018 resize_filter->coeff[0]=(6.0-2.0*B)/6.0;
1019 resize_filter->coeff[1]=0.0;
1020 resize_filter->coeff[2]=(-18.0+12.0*B+6.0*C)/6.0;
1021 resize_filter->coeff[3]=(12.0-9.0*B-6.0*C)/6.0;
1022 resize_filter->coeff[4]=(8.0*B+24.0*C)/6.0;
1023 resize_filter->coeff[5]=(-12.0*B-48.0*C)/6.0;
1024 resize_filter->coeff[6]=(6.0*B+30.0*C)/6.0;
1025 resize_filter->coeff[7]=(-B-6.0*C)/6.0;
cristy3ed852e2009-09-05 21:47:34 +00001026 }
anthonyf5e76ef2010-10-12 01:22:01 +00001027
anthony55f12332010-09-10 01:13:02 +00001028 /*
nicolas07bac812010-09-19 18:47:02 +00001029 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001030 */
cristyf5b49372010-10-16 01:06:47 +00001031#if defined(MAGICKCORE_OPENMP_SUPPORT)
1032 #pragma omp master
1033 {
1034#endif
1035 artifact=GetImageArtifact(image,"filter:verbose");
1036 if (artifact != (const char *) NULL)
1037 {
1038 double
1039 support,
1040 x;
cristy3ed852e2009-09-05 21:47:34 +00001041
cristyf5b49372010-10-16 01:06:47 +00001042 /*
1043 Set the weighting function properly when the weighting
1044 function may not exactly match the filter of the same name.
1045 EG: a Point filter really uses a Box weighting function
1046 with a different support than is typically used.
anthony463be1d2010-09-26 01:07:36 +00001047
cristyf5b49372010-10-16 01:06:47 +00001048 */
1049 if (resize_filter->filter == Box) filter_type=BoxFilter;
1050 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1051 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1052 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1053 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1054 /*
1055 Report Filter Details.
1056 */
1057 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1058 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
1059 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1060 MagickFilterOptions,filter_type));
1061 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
1062 MagickFilterOptions, window_type));
1063 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
1064 (double) resize_filter->support);
1065 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
1066 (double) resize_filter->window_support);
1067 (void) fprintf(stdout,"# scale_blur = %.*g\n",GetMagickPrecision(),
1068 (double) resize_filter->blur);
1069 if ( filter_type == GaussianFilter )
1070 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",GetMagickPrecision(),
1071 (double) sigma);
1072 (void) fprintf(stdout,"# practical_support = %.*g\n",GetMagickPrecision(),
1073 (double) support);
1074 if ( filter_type == CubicFilter || window_type == CubicFilter )
1075 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1076 (double) B,GetMagickPrecision(),(double) C);
1077 (void) fprintf(stdout,"\n");
1078 /*
1079 Output values of resulting filter graph -- for graphing
1080 filter result.
1081 */
1082 for (x=0.0; x <= support; x+=0.01f)
1083 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1084 (double) GetResizeFilterWeight(resize_filter,x));
1085 /* A final value so gnuplot can graph the 'stop' properly. */
1086 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1087 0.0);
1088 }
1089 /* Output the above once only for each image - remove setting */
1090 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1091#if defined(MAGICKCORE_OPENMP_SUPPORT)
1092 }
1093#endif
cristy3ed852e2009-09-05 21:47:34 +00001094 return(resize_filter);
1095}
1096
1097/*
1098%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1099% %
1100% %
1101% %
1102% A d a p t i v e R e s i z e I m a g e %
1103% %
1104% %
1105% %
1106%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1107%
1108% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1109%
1110% The format of the AdaptiveResizeImage method is:
1111%
cristy9af9b5d2010-08-15 17:04:28 +00001112% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1113% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001114%
1115% A description of each parameter follows:
1116%
1117% o image: the image.
1118%
1119% o columns: the number of columns in the resized image.
1120%
1121% o rows: the number of rows in the resized image.
1122%
1123% o exception: return any errors or warnings in this structure.
1124%
1125*/
1126MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001127 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001128{
1129#define AdaptiveResizeImageTag "Resize/Image"
1130
cristyc4c8d132010-01-07 01:58:38 +00001131 CacheView
1132 *resize_view;
1133
cristy3ed852e2009-09-05 21:47:34 +00001134 Image
1135 *resize_image;
1136
cristy3ed852e2009-09-05 21:47:34 +00001137 MagickBooleanType
1138 proceed;
1139
1140 MagickPixelPacket
1141 pixel;
1142
1143 PointInfo
1144 offset;
1145
1146 ResampleFilter
1147 *resample_filter;
1148
cristy9af9b5d2010-08-15 17:04:28 +00001149 ssize_t
1150 y;
1151
cristy3ed852e2009-09-05 21:47:34 +00001152 /*
1153 Adaptively resize image.
1154 */
1155 assert(image != (const Image *) NULL);
1156 assert(image->signature == MagickSignature);
1157 if (image->debug != MagickFalse)
1158 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1159 assert(exception != (ExceptionInfo *) NULL);
1160 assert(exception->signature == MagickSignature);
1161 if ((columns == 0) || (rows == 0))
1162 return((Image *) NULL);
1163 if ((columns == image->columns) && (rows == image->rows))
1164 return(CloneImage(image,0,0,MagickTrue,exception));
1165 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1166 if (resize_image == (Image *) NULL)
1167 return((Image *) NULL);
1168 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1169 {
1170 InheritException(exception,&resize_image->exception);
1171 resize_image=DestroyImage(resize_image);
1172 return((Image *) NULL);
1173 }
1174 GetMagickPixelPacket(image,&pixel);
1175 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001176 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001177 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001178 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001179 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001180 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001181 {
1182 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001183 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001184
cristybb503372010-05-27 20:51:26 +00001185 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001186 x;
1187
1188 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001189 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001190
1191 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1192 exception);
1193 if (q == (PixelPacket *) NULL)
1194 break;
1195 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1196 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001197 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001198 {
1199 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1200 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1201 &pixel);
1202 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1203 q++;
1204 }
1205 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1206 break;
cristy96b16132010-08-29 17:19:52 +00001207 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1208 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001209 if (proceed == MagickFalse)
1210 break;
1211 }
1212 resample_filter=DestroyResampleFilter(resample_filter);
1213 resize_view=DestroyCacheView(resize_view);
1214 return(resize_image);
1215}
1216
1217/*
1218%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1219% %
1220% %
1221% %
1222+ B e s s e l O r d e r O n e %
1223% %
1224% %
1225% %
1226%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1227%
1228% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001229% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001230%
1231% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1232%
1233% j1(x) = x*j1(x);
1234%
1235% For x in (8,inf)
1236%
1237% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1238%
1239% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1240%
1241% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1242% = 1/sqrt(2) * (sin(x) - cos(x))
1243% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1244% = -1/sqrt(2) * (sin(x) + cos(x))
1245%
1246% The format of the BesselOrderOne method is:
1247%
1248% MagickRealType BesselOrderOne(MagickRealType x)
1249%
1250% A description of each parameter follows:
1251%
1252% o x: MagickRealType value.
1253%
1254*/
1255
1256#undef I0
1257static MagickRealType I0(MagickRealType x)
1258{
1259 MagickRealType
1260 sum,
1261 t,
1262 y;
1263
cristybb503372010-05-27 20:51:26 +00001264 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001265 i;
1266
1267 /*
1268 Zeroth order Bessel function of the first kind.
1269 */
1270 sum=1.0;
1271 y=x*x/4.0;
1272 t=y;
1273 for (i=2; t > MagickEpsilon; i++)
1274 {
1275 sum+=t;
1276 t*=y/((MagickRealType) i*i);
1277 }
1278 return(sum);
1279}
1280
1281#undef J1
1282static MagickRealType J1(MagickRealType x)
1283{
1284 MagickRealType
1285 p,
1286 q;
1287
cristybb503372010-05-27 20:51:26 +00001288 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001289 i;
1290
1291 static const double
1292 Pone[] =
1293 {
1294 0.581199354001606143928050809e+21,
1295 -0.6672106568924916298020941484e+20,
1296 0.2316433580634002297931815435e+19,
1297 -0.3588817569910106050743641413e+17,
1298 0.2908795263834775409737601689e+15,
1299 -0.1322983480332126453125473247e+13,
1300 0.3413234182301700539091292655e+10,
1301 -0.4695753530642995859767162166e+7,
1302 0.270112271089232341485679099e+4
1303 },
1304 Qone[] =
1305 {
1306 0.11623987080032122878585294e+22,
1307 0.1185770712190320999837113348e+20,
1308 0.6092061398917521746105196863e+17,
1309 0.2081661221307607351240184229e+15,
1310 0.5243710262167649715406728642e+12,
1311 0.1013863514358673989967045588e+10,
1312 0.1501793594998585505921097578e+7,
1313 0.1606931573481487801970916749e+4,
1314 0.1e+1
1315 };
1316
1317 p=Pone[8];
1318 q=Qone[8];
1319 for (i=7; i >= 0; i--)
1320 {
1321 p=p*x*x+Pone[i];
1322 q=q*x*x+Qone[i];
1323 }
1324 return(p/q);
1325}
1326
1327#undef P1
1328static MagickRealType P1(MagickRealType x)
1329{
1330 MagickRealType
1331 p,
1332 q;
1333
cristybb503372010-05-27 20:51:26 +00001334 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001335 i;
1336
1337 static const double
1338 Pone[] =
1339 {
1340 0.352246649133679798341724373e+5,
1341 0.62758845247161281269005675e+5,
1342 0.313539631109159574238669888e+5,
1343 0.49854832060594338434500455e+4,
1344 0.2111529182853962382105718e+3,
1345 0.12571716929145341558495e+1
1346 },
1347 Qone[] =
1348 {
1349 0.352246649133679798068390431e+5,
1350 0.626943469593560511888833731e+5,
1351 0.312404063819041039923015703e+5,
1352 0.4930396490181088979386097e+4,
1353 0.2030775189134759322293574e+3,
1354 0.1e+1
1355 };
1356
1357 p=Pone[5];
1358 q=Qone[5];
1359 for (i=4; i >= 0; i--)
1360 {
1361 p=p*(8.0/x)*(8.0/x)+Pone[i];
1362 q=q*(8.0/x)*(8.0/x)+Qone[i];
1363 }
1364 return(p/q);
1365}
1366
1367#undef Q1
1368static MagickRealType Q1(MagickRealType x)
1369{
1370 MagickRealType
1371 p,
1372 q;
1373
cristybb503372010-05-27 20:51:26 +00001374 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001375 i;
1376
1377 static const double
1378 Pone[] =
1379 {
1380 0.3511751914303552822533318e+3,
1381 0.7210391804904475039280863e+3,
1382 0.4259873011654442389886993e+3,
1383 0.831898957673850827325226e+2,
1384 0.45681716295512267064405e+1,
1385 0.3532840052740123642735e-1
1386 },
1387 Qone[] =
1388 {
1389 0.74917374171809127714519505e+4,
1390 0.154141773392650970499848051e+5,
1391 0.91522317015169922705904727e+4,
1392 0.18111867005523513506724158e+4,
1393 0.1038187585462133728776636e+3,
1394 0.1e+1
1395 };
1396
1397 p=Pone[5];
1398 q=Qone[5];
1399 for (i=4; i >= 0; i--)
1400 {
1401 p=p*(8.0/x)*(8.0/x)+Pone[i];
1402 q=q*(8.0/x)*(8.0/x)+Qone[i];
1403 }
1404 return(p/q);
1405}
1406
1407static MagickRealType BesselOrderOne(MagickRealType x)
1408{
1409 MagickRealType
1410 p,
1411 q;
1412
1413 if (x == 0.0)
1414 return(0.0);
1415 p=x;
1416 if (x < 0.0)
1417 x=(-x);
1418 if (x < 8.0)
1419 return(p*J1(x));
1420 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1421 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1422 cos((double) x))));
1423 if (p < 0.0)
1424 q=(-q);
1425 return(q);
1426}
1427
1428/*
1429%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1430% %
1431% %
1432% %
1433+ D e s t r o y R e s i z e F i l t e r %
1434% %
1435% %
1436% %
1437%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1438%
1439% DestroyResizeFilter() destroy the resize filter.
1440%
cristya2ffd7e2010-03-10 20:50:30 +00001441% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001442%
1443% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1444%
1445% A description of each parameter follows:
1446%
1447% o resize_filter: the resize filter.
1448%
1449*/
1450MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1451{
1452 assert(resize_filter != (ResizeFilter *) NULL);
1453 assert(resize_filter->signature == MagickSignature);
1454 resize_filter->signature=(~MagickSignature);
1455 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1456 return(resize_filter);
1457}
1458
1459/*
1460%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1461% %
1462% %
1463% %
1464+ G e t R e s i z e F i l t e r S u p p o r t %
1465% %
1466% %
1467% %
1468%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1469%
1470% GetResizeFilterSupport() return the current support window size for this
1471% filter. Note that this may have been enlarged by filter:blur factor.
1472%
1473% The format of the GetResizeFilterSupport method is:
1474%
1475% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1476%
1477% A description of each parameter follows:
1478%
1479% o filter: Image filter to use.
1480%
1481*/
1482MagickExport MagickRealType GetResizeFilterSupport(
1483 const ResizeFilter *resize_filter)
1484{
1485 assert(resize_filter != (ResizeFilter *) NULL);
1486 assert(resize_filter->signature == MagickSignature);
1487 return(resize_filter->support*resize_filter->blur);
1488}
1489
1490/*
1491%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1492% %
1493% %
1494% %
1495+ G e t R e s i z e F i l t e r W e i g h t %
1496% %
1497% %
1498% %
1499%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1500%
1501% GetResizeFilterWeight evaluates the specified resize filter at the point x
1502% which usally lies between zero and the filters current 'support' and
1503% returns the weight of the filter function at that point.
1504%
1505% The format of the GetResizeFilterWeight method is:
1506%
1507% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1508% const MagickRealType x)
1509%
1510% A description of each parameter follows:
1511%
1512% o filter: the filter type.
1513%
1514% o x: the point.
1515%
1516*/
1517MagickExport MagickRealType GetResizeFilterWeight(
1518 const ResizeFilter *resize_filter,const MagickRealType x)
1519{
1520 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001521 scale,
1522 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001523
1524 /*
1525 Windowing function - scale the weighting filter by this amount.
1526 */
1527 assert(resize_filter != (ResizeFilter *) NULL);
1528 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001529 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001530 if ((resize_filter->window_support < MagickEpsilon) ||
1531 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001532 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001533 else
1534 {
anthony55f12332010-09-10 01:13:02 +00001535 scale=resize_filter->scale;
1536 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001537 }
anthony55f12332010-09-10 01:13:02 +00001538 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001539}
1540
1541/*
1542%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1543% %
1544% %
1545% %
1546% M a g n i f y I m a g e %
1547% %
1548% %
1549% %
1550%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1551%
1552% MagnifyImage() is a convenience method that scales an image proportionally
1553% to twice its size.
1554%
1555% The format of the MagnifyImage method is:
1556%
1557% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1558%
1559% A description of each parameter follows:
1560%
1561% o image: the image.
1562%
1563% o exception: return any errors or warnings in this structure.
1564%
1565*/
1566MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1567{
1568 Image
1569 *magnify_image;
1570
1571 assert(image != (Image *) NULL);
1572 assert(image->signature == MagickSignature);
1573 if (image->debug != MagickFalse)
1574 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1575 assert(exception != (ExceptionInfo *) NULL);
1576 assert(exception->signature == MagickSignature);
1577 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1578 1.0,exception);
1579 return(magnify_image);
1580}
1581
1582/*
1583%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1584% %
1585% %
1586% %
1587% M i n i f y I m a g e %
1588% %
1589% %
1590% %
1591%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1592%
1593% MinifyImage() is a convenience method that scales an image proportionally
1594% to half its size.
1595%
1596% The format of the MinifyImage method is:
1597%
1598% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1599%
1600% A description of each parameter follows:
1601%
1602% o image: the image.
1603%
1604% o exception: return any errors or warnings in this structure.
1605%
1606*/
1607MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1608{
1609 Image
1610 *minify_image;
1611
1612 assert(image != (Image *) NULL);
1613 assert(image->signature == MagickSignature);
1614 if (image->debug != MagickFalse)
1615 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1616 assert(exception != (ExceptionInfo *) NULL);
1617 assert(exception->signature == MagickSignature);
1618 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1619 1.0,exception);
1620 return(minify_image);
1621}
1622
1623/*
1624%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1625% %
1626% %
1627% %
1628% R e s a m p l e I m a g e %
1629% %
1630% %
1631% %
1632%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1633%
1634% ResampleImage() resize image in terms of its pixel size, so that when
1635% displayed at the given resolution it will be the same size in terms of
1636% real world units as the original image at the original resolution.
1637%
1638% The format of the ResampleImage method is:
1639%
1640% Image *ResampleImage(Image *image,const double x_resolution,
1641% const double y_resolution,const FilterTypes filter,const double blur,
1642% ExceptionInfo *exception)
1643%
1644% A description of each parameter follows:
1645%
1646% o image: the image to be resized to fit the given resolution.
1647%
1648% o x_resolution: the new image x resolution.
1649%
1650% o y_resolution: the new image y resolution.
1651%
1652% o filter: Image filter to use.
1653%
1654% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1655%
1656*/
1657MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1658 const double y_resolution,const FilterTypes filter,const double blur,
1659 ExceptionInfo *exception)
1660{
1661#define ResampleImageTag "Resample/Image"
1662
1663 Image
1664 *resample_image;
1665
cristybb503372010-05-27 20:51:26 +00001666 size_t
cristy3ed852e2009-09-05 21:47:34 +00001667 height,
1668 width;
1669
1670 /*
1671 Initialize sampled image attributes.
1672 */
1673 assert(image != (const Image *) NULL);
1674 assert(image->signature == MagickSignature);
1675 if (image->debug != MagickFalse)
1676 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1677 assert(exception != (ExceptionInfo *) NULL);
1678 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001679 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1680 72.0 : image->x_resolution)+0.5);
1681 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1682 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001683 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1684 if (resample_image != (Image *) NULL)
1685 {
1686 resample_image->x_resolution=x_resolution;
1687 resample_image->y_resolution=y_resolution;
1688 }
1689 return(resample_image);
1690}
1691#if defined(MAGICKCORE_LQR_DELEGATE)
1692
1693/*
1694%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1695% %
1696% %
1697% %
1698% L i q u i d R e s c a l e I m a g e %
1699% %
1700% %
1701% %
1702%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1703%
1704% LiquidRescaleImage() rescales image with seam carving.
1705%
1706% The format of the LiquidRescaleImage method is:
1707%
1708% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001709% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001710% const double delta_x,const double rigidity,ExceptionInfo *exception)
1711%
1712% A description of each parameter follows:
1713%
1714% o image: the image.
1715%
1716% o columns: the number of columns in the rescaled image.
1717%
1718% o rows: the number of rows in the rescaled image.
1719%
1720% o delta_x: maximum seam transversal step (0 means straight seams).
1721%
1722% o rigidity: introduce a bias for non-straight seams (typically 0).
1723%
1724% o exception: return any errors or warnings in this structure.
1725%
1726*/
cristy9af9b5d2010-08-15 17:04:28 +00001727MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1728 const size_t rows,const double delta_x,const double rigidity,
1729 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001730{
1731#define LiquidRescaleImageTag "Rescale/Image"
1732
cristyc5c6f662010-09-22 14:23:02 +00001733 CacheView
1734 *rescale_view;
1735
cristy3ed852e2009-09-05 21:47:34 +00001736 const char
1737 *map;
1738
1739 guchar
1740 *packet;
1741
1742 Image
1743 *rescale_image;
1744
1745 int
1746 x,
1747 y;
1748
1749 LqrCarver
1750 *carver;
1751
1752 LqrRetVal
1753 lqr_status;
1754
1755 MagickBooleanType
1756 status;
1757
1758 MagickPixelPacket
1759 pixel;
1760
1761 unsigned char
1762 *pixels;
1763
1764 /*
1765 Liquid rescale image.
1766 */
1767 assert(image != (const Image *) NULL);
1768 assert(image->signature == MagickSignature);
1769 if (image->debug != MagickFalse)
1770 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1771 assert(exception != (ExceptionInfo *) NULL);
1772 assert(exception->signature == MagickSignature);
1773 if ((columns == 0) || (rows == 0))
1774 return((Image *) NULL);
1775 if ((columns == image->columns) && (rows == image->rows))
1776 return(CloneImage(image,0,0,MagickTrue,exception));
1777 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001778 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001779 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1780 {
1781 Image
1782 *resize_image;
1783
cristybb503372010-05-27 20:51:26 +00001784 size_t
cristy3ed852e2009-09-05 21:47:34 +00001785 height,
1786 width;
1787
1788 /*
1789 Honor liquid resize size limitations.
1790 */
1791 for (width=image->columns; columns >= (2*width-1); width*=2);
1792 for (height=image->rows; rows >= (2*height-1); height*=2);
1793 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1794 exception);
1795 if (resize_image == (Image *) NULL)
1796 return((Image *) NULL);
1797 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1798 rigidity,exception);
1799 resize_image=DestroyImage(resize_image);
1800 return(rescale_image);
1801 }
1802 map="RGB";
1803 if (image->matte == MagickFalse)
1804 map="RGBA";
1805 if (image->colorspace == CMYKColorspace)
1806 {
1807 map="CMYK";
1808 if (image->matte == MagickFalse)
1809 map="CMYKA";
1810 }
1811 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1812 strlen(map)*sizeof(*pixels));
1813 if (pixels == (unsigned char *) NULL)
1814 return((Image *) NULL);
1815 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1816 pixels,exception);
1817 if (status == MagickFalse)
1818 {
1819 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1820 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1821 }
1822 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1823 if (carver == (LqrCarver *) NULL)
1824 {
1825 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1826 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1827 }
1828 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1829 lqr_status=lqr_carver_resize(carver,columns,rows);
1830 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1831 lqr_carver_get_height(carver),MagickTrue,exception);
1832 if (rescale_image == (Image *) NULL)
1833 {
1834 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1835 return((Image *) NULL);
1836 }
1837 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1838 {
1839 InheritException(exception,&rescale_image->exception);
1840 rescale_image=DestroyImage(rescale_image);
1841 return((Image *) NULL);
1842 }
1843 GetMagickPixelPacket(rescale_image,&pixel);
1844 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001845 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001846 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1847 {
1848 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001849 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001850
1851 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001852 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001853
anthony22aad252010-09-23 06:59:07 +00001854 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001855 if (q == (PixelPacket *) NULL)
1856 break;
cristyc5c6f662010-09-22 14:23:02 +00001857 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001858 pixel.red=QuantumRange*(packet[0]/255.0);
1859 pixel.green=QuantumRange*(packet[1]/255.0);
1860 pixel.blue=QuantumRange*(packet[2]/255.0);
1861 if (image->colorspace != CMYKColorspace)
1862 {
1863 if (image->matte == MagickFalse)
1864 pixel.opacity=QuantumRange*(packet[3]/255.0);
1865 }
1866 else
1867 {
1868 pixel.index=QuantumRange*(packet[3]/255.0);
1869 if (image->matte == MagickFalse)
1870 pixel.opacity=QuantumRange*(packet[4]/255.0);
1871 }
1872 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001873 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001874 break;
1875 }
cristyc5c6f662010-09-22 14:23:02 +00001876 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001877 /*
1878 Relinquish resources.
1879 */
1880 lqr_carver_destroy(carver);
1881 return(rescale_image);
1882}
1883#else
1884MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001885 const size_t magick_unused(columns),const size_t magick_unused(rows),
1886 const double magick_unused(delta_x),const double magick_unused(rigidity),
1887 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001888{
1889 assert(image != (const Image *) NULL);
1890 assert(image->signature == MagickSignature);
1891 if (image->debug != MagickFalse)
1892 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1893 assert(exception != (ExceptionInfo *) NULL);
1894 assert(exception->signature == MagickSignature);
1895 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1896 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1897 return((Image *) NULL);
1898}
1899#endif
1900
1901/*
1902%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1903% %
1904% %
1905% %
1906% R e s i z e I m a g e %
1907% %
1908% %
1909% %
1910%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1911%
1912% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001913% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001914%
1915% If an undefined filter is given the filter defaults to Mitchell for a
1916% colormapped image, a image with a matte channel, or if the image is
1917% enlarged. Otherwise the filter defaults to a Lanczos.
1918%
1919% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1920%
1921% The format of the ResizeImage method is:
1922%
cristybb503372010-05-27 20:51:26 +00001923% Image *ResizeImage(Image *image,const size_t columns,
1924% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001925% ExceptionInfo *exception)
1926%
1927% A description of each parameter follows:
1928%
1929% o image: the image.
1930%
1931% o columns: the number of columns in the scaled image.
1932%
1933% o rows: the number of rows in the scaled image.
1934%
1935% o filter: Image filter to use.
1936%
cristy9af9b5d2010-08-15 17:04:28 +00001937% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1938% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001939%
1940% o exception: return any errors or warnings in this structure.
1941%
1942*/
1943
1944typedef struct _ContributionInfo
1945{
1946 MagickRealType
1947 weight;
1948
cristybb503372010-05-27 20:51:26 +00001949 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001950 pixel;
1951} ContributionInfo;
1952
1953static ContributionInfo **DestroyContributionThreadSet(
1954 ContributionInfo **contribution)
1955{
cristybb503372010-05-27 20:51:26 +00001956 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001957 i;
1958
1959 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001960 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001961 if (contribution[i] != (ContributionInfo *) NULL)
1962 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1963 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001964 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001965 return(contribution);
1966}
1967
1968static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1969{
cristybb503372010-05-27 20:51:26 +00001970 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001971 i;
1972
1973 ContributionInfo
1974 **contribution;
1975
cristybb503372010-05-27 20:51:26 +00001976 size_t
cristy3ed852e2009-09-05 21:47:34 +00001977 number_threads;
1978
1979 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001980 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001981 sizeof(*contribution));
1982 if (contribution == (ContributionInfo **) NULL)
1983 return((ContributionInfo **) NULL);
1984 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00001985 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00001986 {
1987 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1988 sizeof(**contribution));
1989 if (contribution[i] == (ContributionInfo *) NULL)
1990 return(DestroyContributionThreadSet(contribution));
1991 }
1992 return(contribution);
1993}
1994
1995static inline double MagickMax(const double x,const double y)
1996{
1997 if (x > y)
1998 return(x);
1999 return(y);
2000}
2001
2002static inline double MagickMin(const double x,const double y)
2003{
2004 if (x < y)
2005 return(x);
2006 return(y);
2007}
2008
2009static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2010 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002011 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002012{
2013#define ResizeImageTag "Resize/Image"
2014
cristyfa112112010-01-04 17:48:07 +00002015 CacheView
2016 *image_view,
2017 *resize_view;
2018
cristy3ed852e2009-09-05 21:47:34 +00002019 ClassType
2020 storage_class;
2021
2022 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002023 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002024
cristy3ed852e2009-09-05 21:47:34 +00002025 MagickBooleanType
2026 status;
2027
2028 MagickPixelPacket
2029 zero;
2030
2031 MagickRealType
2032 scale,
2033 support;
2034
cristy9af9b5d2010-08-15 17:04:28 +00002035 ssize_t
2036 x;
2037
cristy3ed852e2009-09-05 21:47:34 +00002038 /*
2039 Apply filter to resize horizontally from image to resize image.
2040 */
cristy5d824382010-09-06 14:00:17 +00002041 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002042 support=scale*GetResizeFilterSupport(resize_filter);
2043 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2044 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2045 {
2046 InheritException(exception,&resize_image->exception);
2047 return(MagickFalse);
2048 }
2049 if (support < 0.5)
2050 {
2051 /*
nicolas07bac812010-09-19 18:47:02 +00002052 Support too small even for nearest neighbour: Reduce to point
2053 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002054 */
2055 support=(MagickRealType) 0.5;
2056 scale=1.0;
2057 }
2058 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2059 if (contributions == (ContributionInfo **) NULL)
2060 {
2061 (void) ThrowMagickException(exception,GetMagickModule(),
2062 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2063 return(MagickFalse);
2064 }
2065 status=MagickTrue;
2066 scale=1.0/scale;
2067 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2068 image_view=AcquireCacheView(image);
2069 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002070#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002071 #pragma omp parallel for shared(status)
2072#endif
cristybb503372010-05-27 20:51:26 +00002073 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002074 {
cristy3ed852e2009-09-05 21:47:34 +00002075 MagickRealType
2076 center,
2077 density;
2078
2079 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002080 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002081
2082 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002083 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002084
cristy03dbbd22010-09-19 23:04:47 +00002085 register ContributionInfo
2086 *restrict contribution;
2087
cristy3ed852e2009-09-05 21:47:34 +00002088 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002089 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002090
cristy3ed852e2009-09-05 21:47:34 +00002091 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002092 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002093
cristy03dbbd22010-09-19 23:04:47 +00002094 register ssize_t
2095 y;
2096
cristy9af9b5d2010-08-15 17:04:28 +00002097 ssize_t
2098 n,
2099 start,
2100 stop;
2101
cristy3ed852e2009-09-05 21:47:34 +00002102 if (status == MagickFalse)
2103 continue;
2104 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002105 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2106 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002107 density=0.0;
2108 contribution=contributions[GetOpenMPThreadId()];
2109 for (n=0; n < (stop-start); n++)
2110 {
2111 contribution[n].pixel=start+n;
2112 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2113 ((MagickRealType) (start+n)-center+0.5));
2114 density+=contribution[n].weight;
2115 }
2116 if ((density != 0.0) && (density != 1.0))
2117 {
cristybb503372010-05-27 20:51:26 +00002118 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002119 i;
2120
2121 /*
2122 Normalize.
2123 */
2124 density=1.0/density;
2125 for (i=0; i < n; i++)
2126 contribution[i].weight*=density;
2127 }
cristy9af9b5d2010-08-15 17:04:28 +00002128 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2129 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002130 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2131 exception);
2132 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2133 {
2134 status=MagickFalse;
2135 continue;
2136 }
2137 indexes=GetCacheViewVirtualIndexQueue(image_view);
2138 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002139 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002140 {
cristy3ed852e2009-09-05 21:47:34 +00002141 MagickPixelPacket
2142 pixel;
2143
2144 MagickRealType
2145 alpha;
2146
cristybb503372010-05-27 20:51:26 +00002147 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002148 i;
2149
cristy9af9b5d2010-08-15 17:04:28 +00002150 ssize_t
2151 j;
2152
cristy3ed852e2009-09-05 21:47:34 +00002153 pixel=zero;
2154 if (image->matte == MagickFalse)
2155 {
2156 for (i=0; i < n; i++)
2157 {
2158 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2159 (contribution[i].pixel-contribution[0].pixel);
2160 alpha=contribution[i].weight;
2161 pixel.red+=alpha*(p+j)->red;
2162 pixel.green+=alpha*(p+j)->green;
2163 pixel.blue+=alpha*(p+j)->blue;
2164 pixel.opacity+=alpha*(p+j)->opacity;
2165 }
cristyce70c172010-01-07 17:15:30 +00002166 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2167 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2168 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2169 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002170 if ((image->colorspace == CMYKColorspace) &&
2171 (resize_image->colorspace == CMYKColorspace))
2172 {
2173 for (i=0; i < n; i++)
2174 {
2175 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2176 (contribution[i].pixel-contribution[0].pixel);
2177 alpha=contribution[i].weight;
2178 pixel.index+=alpha*indexes[j];
2179 }
cristyce70c172010-01-07 17:15:30 +00002180 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002181 }
2182 }
2183 else
2184 {
2185 MagickRealType
2186 gamma;
2187
2188 gamma=0.0;
2189 for (i=0; i < n; i++)
2190 {
2191 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2192 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002193 alpha=contribution[i].weight*QuantumScale*
2194 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002195 pixel.red+=alpha*(p+j)->red;
2196 pixel.green+=alpha*(p+j)->green;
2197 pixel.blue+=alpha*(p+j)->blue;
2198 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2199 gamma+=alpha;
2200 }
2201 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002202 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2203 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2204 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2205 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002206 if ((image->colorspace == CMYKColorspace) &&
2207 (resize_image->colorspace == CMYKColorspace))
2208 {
2209 for (i=0; i < n; i++)
2210 {
2211 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2212 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002213 alpha=contribution[i].weight*QuantumScale*
2214 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002215 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002216 }
cristyce70c172010-01-07 17:15:30 +00002217 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2218 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002219 }
2220 }
2221 if ((resize_image->storage_class == PseudoClass) &&
2222 (image->storage_class == PseudoClass))
2223 {
cristybb503372010-05-27 20:51:26 +00002224 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002225 1.0)+0.5);
2226 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2227 (contribution[i-start].pixel-contribution[0].pixel);
2228 resize_indexes[y]=indexes[j];
2229 }
2230 q++;
2231 }
2232 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2233 status=MagickFalse;
2234 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2235 {
2236 MagickBooleanType
2237 proceed;
2238
cristyb5d5f722009-11-04 03:03:49 +00002239#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002240 #pragma omp critical (MagickCore_HorizontalFilter)
2241#endif
cristy9af9b5d2010-08-15 17:04:28 +00002242 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002243 if (proceed == MagickFalse)
2244 status=MagickFalse;
2245 }
2246 }
2247 resize_view=DestroyCacheView(resize_view);
2248 image_view=DestroyCacheView(image_view);
2249 contributions=DestroyContributionThreadSet(contributions);
2250 return(status);
2251}
2252
2253static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2254 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002255 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002256{
cristyfa112112010-01-04 17:48:07 +00002257 CacheView
2258 *image_view,
2259 *resize_view;
2260
cristy3ed852e2009-09-05 21:47:34 +00002261 ClassType
2262 storage_class;
2263
2264 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002265 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002266
cristy3ed852e2009-09-05 21:47:34 +00002267 MagickBooleanType
2268 status;
2269
2270 MagickPixelPacket
2271 zero;
2272
2273 MagickRealType
2274 scale,
2275 support;
2276
cristy9af9b5d2010-08-15 17:04:28 +00002277 ssize_t
2278 y;
2279
cristy3ed852e2009-09-05 21:47:34 +00002280 /*
cristy9af9b5d2010-08-15 17:04:28 +00002281 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002282 */
cristy5d824382010-09-06 14:00:17 +00002283 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002284 support=scale*GetResizeFilterSupport(resize_filter);
2285 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2286 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2287 {
2288 InheritException(exception,&resize_image->exception);
2289 return(MagickFalse);
2290 }
2291 if (support < 0.5)
2292 {
2293 /*
nicolas07bac812010-09-19 18:47:02 +00002294 Support too small even for nearest neighbour: Reduce to point
2295 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002296 */
2297 support=(MagickRealType) 0.5;
2298 scale=1.0;
2299 }
2300 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2301 if (contributions == (ContributionInfo **) NULL)
2302 {
2303 (void) ThrowMagickException(exception,GetMagickModule(),
2304 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2305 return(MagickFalse);
2306 }
2307 status=MagickTrue;
2308 scale=1.0/scale;
2309 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2310 image_view=AcquireCacheView(image);
2311 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002312#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002313 #pragma omp parallel for shared(status)
2314#endif
cristybb503372010-05-27 20:51:26 +00002315 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002316 {
cristy3ed852e2009-09-05 21:47:34 +00002317 MagickRealType
2318 center,
2319 density;
2320
2321 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002322 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002323
2324 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002325 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002326
cristy03dbbd22010-09-19 23:04:47 +00002327 register ContributionInfo
2328 *restrict contribution;
2329
cristy3ed852e2009-09-05 21:47:34 +00002330 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002331 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002332
cristy9af9b5d2010-08-15 17:04:28 +00002333 register PixelPacket
2334 *restrict q;
2335
cristybb503372010-05-27 20:51:26 +00002336 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002337 x;
2338
cristy9af9b5d2010-08-15 17:04:28 +00002339 ssize_t
2340 n,
2341 start,
2342 stop;
cristy3ed852e2009-09-05 21:47:34 +00002343
2344 if (status == MagickFalse)
2345 continue;
cristy679e6962010-03-18 00:42:45 +00002346 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002347 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2348 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002349 density=0.0;
2350 contribution=contributions[GetOpenMPThreadId()];
2351 for (n=0; n < (stop-start); n++)
2352 {
2353 contribution[n].pixel=start+n;
2354 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2355 ((MagickRealType) (start+n)-center+0.5));
2356 density+=contribution[n].weight;
2357 }
2358 if ((density != 0.0) && (density != 1.0))
2359 {
cristybb503372010-05-27 20:51:26 +00002360 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002361 i;
2362
2363 /*
2364 Normalize.
2365 */
2366 density=1.0/density;
2367 for (i=0; i < n; i++)
2368 contribution[i].weight*=density;
2369 }
2370 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002371 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2372 exception);
cristy3ed852e2009-09-05 21:47:34 +00002373 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2374 exception);
2375 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2376 {
2377 status=MagickFalse;
2378 continue;
2379 }
2380 indexes=GetCacheViewVirtualIndexQueue(image_view);
2381 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002382 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002383 {
cristy3ed852e2009-09-05 21:47:34 +00002384 MagickPixelPacket
2385 pixel;
2386
2387 MagickRealType
2388 alpha;
2389
cristybb503372010-05-27 20:51:26 +00002390 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002391 i;
2392
cristy9af9b5d2010-08-15 17:04:28 +00002393 ssize_t
2394 j;
2395
cristy3ed852e2009-09-05 21:47:34 +00002396 pixel=zero;
2397 if (image->matte == MagickFalse)
2398 {
2399 for (i=0; i < n; i++)
2400 {
cristybb503372010-05-27 20:51:26 +00002401 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002402 image->columns+x);
2403 alpha=contribution[i].weight;
2404 pixel.red+=alpha*(p+j)->red;
2405 pixel.green+=alpha*(p+j)->green;
2406 pixel.blue+=alpha*(p+j)->blue;
2407 pixel.opacity+=alpha*(p+j)->opacity;
2408 }
cristyce70c172010-01-07 17:15:30 +00002409 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2410 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2411 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2412 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002413 if ((image->colorspace == CMYKColorspace) &&
2414 (resize_image->colorspace == CMYKColorspace))
2415 {
2416 for (i=0; i < n; i++)
2417 {
cristybb503372010-05-27 20:51:26 +00002418 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002419 image->columns+x);
2420 alpha=contribution[i].weight;
2421 pixel.index+=alpha*indexes[j];
2422 }
cristyce70c172010-01-07 17:15:30 +00002423 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002424 }
2425 }
2426 else
2427 {
2428 MagickRealType
2429 gamma;
2430
2431 gamma=0.0;
2432 for (i=0; i < n; i++)
2433 {
cristybb503372010-05-27 20:51:26 +00002434 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002435 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002436 alpha=contribution[i].weight*QuantumScale*
2437 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002438 pixel.red+=alpha*(p+j)->red;
2439 pixel.green+=alpha*(p+j)->green;
2440 pixel.blue+=alpha*(p+j)->blue;
2441 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2442 gamma+=alpha;
2443 }
2444 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002445 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2446 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2447 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2448 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002449 if ((image->colorspace == CMYKColorspace) &&
2450 (resize_image->colorspace == CMYKColorspace))
2451 {
2452 for (i=0; i < n; i++)
2453 {
cristybb503372010-05-27 20:51:26 +00002454 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002455 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002456 alpha=contribution[i].weight*QuantumScale*
2457 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002458 pixel.index+=alpha*indexes[j];
2459 }
cristyce70c172010-01-07 17:15:30 +00002460 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2461 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002462 }
2463 }
2464 if ((resize_image->storage_class == PseudoClass) &&
2465 (image->storage_class == PseudoClass))
2466 {
cristybb503372010-05-27 20:51:26 +00002467 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002468 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002469 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002470 image->columns+x);
2471 resize_indexes[x]=indexes[j];
2472 }
2473 q++;
2474 }
2475 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2476 status=MagickFalse;
2477 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2478 {
2479 MagickBooleanType
2480 proceed;
2481
cristyb5d5f722009-11-04 03:03:49 +00002482#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002483 #pragma omp critical (MagickCore_VerticalFilter)
2484#endif
cristy9af9b5d2010-08-15 17:04:28 +00002485 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002486 if (proceed == MagickFalse)
2487 status=MagickFalse;
2488 }
2489 }
2490 resize_view=DestroyCacheView(resize_view);
2491 image_view=DestroyCacheView(image_view);
2492 contributions=DestroyContributionThreadSet(contributions);
2493 return(status);
2494}
2495
cristybb503372010-05-27 20:51:26 +00002496MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2497 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002498 ExceptionInfo *exception)
2499{
2500#define WorkLoadFactor 0.265
2501
2502 FilterTypes
2503 filter_type;
2504
2505 Image
2506 *filter_image,
2507 *resize_image;
2508
cristy9af9b5d2010-08-15 17:04:28 +00002509 MagickOffsetType
2510 offset;
2511
cristy3ed852e2009-09-05 21:47:34 +00002512 MagickRealType
2513 x_factor,
2514 y_factor;
2515
2516 MagickSizeType
2517 span;
2518
2519 MagickStatusType
2520 status;
2521
2522 ResizeFilter
2523 *resize_filter;
2524
cristy3ed852e2009-09-05 21:47:34 +00002525 /*
2526 Acquire resize image.
2527 */
2528 assert(image != (Image *) NULL);
2529 assert(image->signature == MagickSignature);
2530 if (image->debug != MagickFalse)
2531 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2532 assert(exception != (ExceptionInfo *) NULL);
2533 assert(exception->signature == MagickSignature);
2534 if ((columns == 0) || (rows == 0))
2535 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2536 if ((columns == image->columns) && (rows == image->rows) &&
2537 (filter == UndefinedFilter) && (blur == 1.0))
2538 return(CloneImage(image,0,0,MagickTrue,exception));
2539 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2540 if (resize_image == (Image *) NULL)
2541 return(resize_image);
2542 /*
2543 Acquire resize filter.
2544 */
2545 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2546 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2547 if ((x_factor*y_factor) > WorkLoadFactor)
2548 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2549 else
2550 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2551 if (filter_image == (Image *) NULL)
2552 return(DestroyImage(resize_image));
2553 filter_type=LanczosFilter;
2554 if (filter != UndefinedFilter)
2555 filter_type=filter;
2556 else
2557 if ((x_factor == 1.0) && (y_factor == 1.0))
2558 filter_type=PointFilter;
2559 else
2560 if ((image->storage_class == PseudoClass) ||
2561 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2562 filter_type=MitchellFilter;
2563 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2564 exception);
2565 /*
2566 Resize image.
2567 */
cristy9af9b5d2010-08-15 17:04:28 +00002568 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002569 if ((x_factor*y_factor) > WorkLoadFactor)
2570 {
2571 span=(MagickSizeType) (filter_image->columns+rows);
2572 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002573 &offset,exception);
cristy065f8be2010-10-16 00:21:58 +00002574 resize_filter=DestroyResizeFilter(resize_filter);
2575 resize_filter=AcquireResizeFilter(filter_image,filter_type,blur,
2576 MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00002577 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002578 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002579 }
2580 else
2581 {
2582 span=(MagickSizeType) (filter_image->rows+columns);
2583 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002584 &offset,exception);
cristy065f8be2010-10-16 00:21:58 +00002585 resize_filter=DestroyResizeFilter(resize_filter);
2586 resize_filter=AcquireResizeFilter(filter_image,filter_type,blur,
2587 MagickFalse,exception);
cristy3ed852e2009-09-05 21:47:34 +00002588 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002589 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002590 }
2591 /*
2592 Free resources.
2593 */
2594 filter_image=DestroyImage(filter_image);
2595 resize_filter=DestroyResizeFilter(resize_filter);
2596 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2597 return((Image *) NULL);
2598 resize_image->type=image->type;
2599 return(resize_image);
2600}
2601
2602/*
2603%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2604% %
2605% %
2606% %
2607% S a m p l e I m a g e %
2608% %
2609% %
2610% %
2611%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2612%
2613% SampleImage() scales an image to the desired dimensions with pixel
2614% sampling. Unlike other scaling methods, this method does not introduce
2615% any additional color into the scaled image.
2616%
2617% The format of the SampleImage method is:
2618%
cristybb503372010-05-27 20:51:26 +00002619% Image *SampleImage(const Image *image,const size_t columns,
2620% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002621%
2622% A description of each parameter follows:
2623%
2624% o image: the image.
2625%
2626% o columns: the number of columns in the sampled image.
2627%
2628% o rows: the number of rows in the sampled image.
2629%
2630% o exception: return any errors or warnings in this structure.
2631%
2632*/
cristybb503372010-05-27 20:51:26 +00002633MagickExport Image *SampleImage(const Image *image,const size_t columns,
2634 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002635{
2636#define SampleImageTag "Sample/Image"
2637
cristyc4c8d132010-01-07 01:58:38 +00002638 CacheView
2639 *image_view,
2640 *sample_view;
2641
cristy3ed852e2009-09-05 21:47:34 +00002642 Image
2643 *sample_image;
2644
cristy3ed852e2009-09-05 21:47:34 +00002645 MagickBooleanType
2646 status;
2647
cristy5f959472010-05-27 22:19:46 +00002648 MagickOffsetType
2649 progress;
2650
cristybb503372010-05-27 20:51:26 +00002651 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002652 x;
2653
cristy5f959472010-05-27 22:19:46 +00002654 ssize_t
2655 *x_offset,
2656 y;
2657
cristy3ed852e2009-09-05 21:47:34 +00002658 /*
2659 Initialize sampled image attributes.
2660 */
2661 assert(image != (const Image *) NULL);
2662 assert(image->signature == MagickSignature);
2663 if (image->debug != MagickFalse)
2664 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2665 assert(exception != (ExceptionInfo *) NULL);
2666 assert(exception->signature == MagickSignature);
2667 if ((columns == 0) || (rows == 0))
2668 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2669 if ((columns == image->columns) && (rows == image->rows))
2670 return(CloneImage(image,0,0,MagickTrue,exception));
2671 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2672 if (sample_image == (Image *) NULL)
2673 return((Image *) NULL);
2674 /*
2675 Allocate scan line buffer and column offset buffers.
2676 */
cristybb503372010-05-27 20:51:26 +00002677 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002678 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002679 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002680 {
2681 sample_image=DestroyImage(sample_image);
2682 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2683 }
cristybb503372010-05-27 20:51:26 +00002684 for (x=0; x < (ssize_t) sample_image->columns; x++)
2685 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002686 sample_image->columns);
2687 /*
2688 Sample each row.
2689 */
2690 status=MagickTrue;
2691 progress=0;
2692 image_view=AcquireCacheView(image);
2693 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002694#if defined(MAGICKCORE_OPENMP_SUPPORT)
2695 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002696#endif
cristybb503372010-05-27 20:51:26 +00002697 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002698 {
cristy3ed852e2009-09-05 21:47:34 +00002699 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002700 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002701
2702 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002703 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002704
2705 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002706 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002707
cristy3ed852e2009-09-05 21:47:34 +00002708 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002709 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002710
cristy03dbbd22010-09-19 23:04:47 +00002711 register ssize_t
2712 x;
2713
cristy9af9b5d2010-08-15 17:04:28 +00002714 ssize_t
2715 y_offset;
2716
cristy3ed852e2009-09-05 21:47:34 +00002717 if (status == MagickFalse)
2718 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002719 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2720 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002721 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2722 exception);
2723 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2724 exception);
2725 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2726 {
2727 status=MagickFalse;
2728 continue;
2729 }
2730 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2731 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2732 /*
2733 Sample each column.
2734 */
cristybb503372010-05-27 20:51:26 +00002735 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002736 *q++=p[x_offset[x]];
2737 if ((image->storage_class == PseudoClass) ||
2738 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002739 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002740 sample_indexes[x]=indexes[x_offset[x]];
2741 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2742 status=MagickFalse;
2743 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2744 {
2745 MagickBooleanType
2746 proceed;
2747
cristyb5d5f722009-11-04 03:03:49 +00002748#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002749 #pragma omp critical (MagickCore_SampleImage)
2750#endif
2751 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2752 if (proceed == MagickFalse)
2753 status=MagickFalse;
2754 }
2755 }
2756 image_view=DestroyCacheView(image_view);
2757 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002758 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002759 sample_image->type=image->type;
2760 return(sample_image);
2761}
2762
2763/*
2764%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2765% %
2766% %
2767% %
2768% S c a l e I m a g e %
2769% %
2770% %
2771% %
2772%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2773%
2774% ScaleImage() changes the size of an image to the given dimensions.
2775%
2776% The format of the ScaleImage method is:
2777%
cristybb503372010-05-27 20:51:26 +00002778% Image *ScaleImage(const Image *image,const size_t columns,
2779% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002780%
2781% A description of each parameter follows:
2782%
2783% o image: the image.
2784%
2785% o columns: the number of columns in the scaled image.
2786%
2787% o rows: the number of rows in the scaled image.
2788%
2789% o exception: return any errors or warnings in this structure.
2790%
2791*/
cristybb503372010-05-27 20:51:26 +00002792MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2793 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002794{
2795#define ScaleImageTag "Scale/Image"
2796
cristyed6cb232010-01-20 03:07:53 +00002797 CacheView
2798 *image_view,
2799 *scale_view;
2800
cristy3ed852e2009-09-05 21:47:34 +00002801 Image
2802 *scale_image;
2803
cristy3ed852e2009-09-05 21:47:34 +00002804 MagickBooleanType
2805 next_column,
2806 next_row,
2807 proceed;
2808
2809 MagickPixelPacket
2810 pixel,
2811 *scale_scanline,
2812 *scanline,
2813 *x_vector,
2814 *y_vector,
2815 zero;
2816
cristy3ed852e2009-09-05 21:47:34 +00002817 PointInfo
2818 scale,
2819 span;
2820
cristybb503372010-05-27 20:51:26 +00002821 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002822 i;
2823
cristy9af9b5d2010-08-15 17:04:28 +00002824 ssize_t
2825 number_rows,
2826 y;
2827
cristy3ed852e2009-09-05 21:47:34 +00002828 /*
2829 Initialize scaled image attributes.
2830 */
2831 assert(image != (const Image *) NULL);
2832 assert(image->signature == MagickSignature);
2833 if (image->debug != MagickFalse)
2834 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2835 assert(exception != (ExceptionInfo *) NULL);
2836 assert(exception->signature == MagickSignature);
2837 if ((columns == 0) || (rows == 0))
2838 return((Image *) NULL);
2839 if ((columns == image->columns) && (rows == image->rows))
2840 return(CloneImage(image,0,0,MagickTrue,exception));
2841 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2842 if (scale_image == (Image *) NULL)
2843 return((Image *) NULL);
2844 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2845 {
2846 InheritException(exception,&scale_image->exception);
2847 scale_image=DestroyImage(scale_image);
2848 return((Image *) NULL);
2849 }
2850 /*
2851 Allocate memory.
2852 */
2853 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2854 sizeof(*x_vector));
2855 scanline=x_vector;
2856 if (image->rows != scale_image->rows)
2857 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2858 sizeof(*scanline));
2859 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2860 scale_image->columns,sizeof(*scale_scanline));
2861 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2862 sizeof(*y_vector));
2863 if ((scanline == (MagickPixelPacket *) NULL) ||
2864 (scale_scanline == (MagickPixelPacket *) NULL) ||
2865 (x_vector == (MagickPixelPacket *) NULL) ||
2866 (y_vector == (MagickPixelPacket *) NULL))
2867 {
2868 scale_image=DestroyImage(scale_image);
2869 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2870 }
2871 /*
2872 Scale image.
2873 */
2874 number_rows=0;
2875 next_row=MagickTrue;
2876 span.y=1.0;
2877 scale.y=(double) scale_image->rows/(double) image->rows;
2878 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2879 sizeof(*y_vector));
2880 GetMagickPixelPacket(image,&pixel);
2881 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2882 i=0;
cristyed6cb232010-01-20 03:07:53 +00002883 image_view=AcquireCacheView(image);
2884 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002885 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002886 {
2887 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002888 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002889
2890 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002891 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002892
2893 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002894 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002895
cristy3ed852e2009-09-05 21:47:34 +00002896 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002897 *restrict s,
2898 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002899
2900 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002901 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002902
cristy9af9b5d2010-08-15 17:04:28 +00002903 register ssize_t
2904 x;
2905
cristyed6cb232010-01-20 03:07:53 +00002906 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2907 exception);
cristy3ed852e2009-09-05 21:47:34 +00002908 if (q == (PixelPacket *) NULL)
2909 break;
2910 scale_indexes=GetAuthenticIndexQueue(scale_image);
2911 if (scale_image->rows == image->rows)
2912 {
2913 /*
2914 Read a new scanline.
2915 */
cristyed6cb232010-01-20 03:07:53 +00002916 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2917 exception);
cristy3ed852e2009-09-05 21:47:34 +00002918 if (p == (const PixelPacket *) NULL)
2919 break;
cristyed6cb232010-01-20 03:07:53 +00002920 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002921 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002922 {
cristyce70c172010-01-07 17:15:30 +00002923 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2924 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2925 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002926 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002927 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002928 if (indexes != (IndexPacket *) NULL)
2929 x_vector[x].index=(MagickRealType) indexes[x];
2930 p++;
2931 }
2932 }
2933 else
2934 {
2935 /*
2936 Scale Y direction.
2937 */
2938 while (scale.y < span.y)
2939 {
cristy9af9b5d2010-08-15 17:04:28 +00002940 if ((next_row != MagickFalse) &&
2941 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002942 {
2943 /*
2944 Read a new scanline.
2945 */
cristyed6cb232010-01-20 03:07:53 +00002946 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2947 exception);
cristy3ed852e2009-09-05 21:47:34 +00002948 if (p == (const PixelPacket *) NULL)
2949 break;
cristyed6cb232010-01-20 03:07:53 +00002950 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002951 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002952 {
cristyce70c172010-01-07 17:15:30 +00002953 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2954 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2955 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002956 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002957 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002958 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002959 if (indexes != (IndexPacket *) NULL)
2960 x_vector[x].index=(MagickRealType) indexes[x];
2961 p++;
2962 }
2963 number_rows++;
2964 }
cristybb503372010-05-27 20:51:26 +00002965 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002966 {
2967 y_vector[x].red+=scale.y*x_vector[x].red;
2968 y_vector[x].green+=scale.y*x_vector[x].green;
2969 y_vector[x].blue+=scale.y*x_vector[x].blue;
2970 if (scale_image->matte != MagickFalse)
2971 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2972 if (scale_indexes != (IndexPacket *) NULL)
2973 y_vector[x].index+=scale.y*x_vector[x].index;
2974 }
2975 span.y-=scale.y;
2976 scale.y=(double) scale_image->rows/(double) image->rows;
2977 next_row=MagickTrue;
2978 }
cristybb503372010-05-27 20:51:26 +00002979 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002980 {
2981 /*
2982 Read a new scanline.
2983 */
cristyed6cb232010-01-20 03:07:53 +00002984 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2985 exception);
cristy3ed852e2009-09-05 21:47:34 +00002986 if (p == (const PixelPacket *) NULL)
2987 break;
cristyed6cb232010-01-20 03:07:53 +00002988 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002989 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002990 {
cristyce70c172010-01-07 17:15:30 +00002991 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2992 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2993 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002994 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00002995 x_vector[x].opacity=(MagickRealType)
2996 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002997 if (indexes != (IndexPacket *) NULL)
2998 x_vector[x].index=(MagickRealType) indexes[x];
2999 p++;
3000 }
3001 number_rows++;
3002 next_row=MagickFalse;
3003 }
3004 s=scanline;
cristybb503372010-05-27 20:51:26 +00003005 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003006 {
3007 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3008 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3009 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3010 if (image->matte != MagickFalse)
3011 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3012 if (scale_indexes != (IndexPacket *) NULL)
3013 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3014 s->red=pixel.red;
3015 s->green=pixel.green;
3016 s->blue=pixel.blue;
3017 if (scale_image->matte != MagickFalse)
3018 s->opacity=pixel.opacity;
3019 if (scale_indexes != (IndexPacket *) NULL)
3020 s->index=pixel.index;
3021 s++;
3022 y_vector[x]=zero;
3023 }
3024 scale.y-=span.y;
3025 if (scale.y <= 0)
3026 {
3027 scale.y=(double) scale_image->rows/(double) image->rows;
3028 next_row=MagickTrue;
3029 }
3030 span.y=1.0;
3031 }
3032 if (scale_image->columns == image->columns)
3033 {
3034 /*
3035 Transfer scanline to scaled image.
3036 */
3037 s=scanline;
cristybb503372010-05-27 20:51:26 +00003038 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003039 {
cristyce70c172010-01-07 17:15:30 +00003040 q->red=ClampToQuantum(s->red);
3041 q->green=ClampToQuantum(s->green);
3042 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003043 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003044 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003045 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003046 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003047 q++;
3048 s++;
3049 }
3050 }
3051 else
3052 {
3053 /*
3054 Scale X direction.
3055 */
3056 pixel=zero;
3057 next_column=MagickFalse;
3058 span.x=1.0;
3059 s=scanline;
3060 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003061 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003062 {
3063 scale.x=(double) scale_image->columns/(double) image->columns;
3064 while (scale.x >= span.x)
3065 {
3066 if (next_column != MagickFalse)
3067 {
3068 pixel=zero;
3069 t++;
3070 }
3071 pixel.red+=span.x*s->red;
3072 pixel.green+=span.x*s->green;
3073 pixel.blue+=span.x*s->blue;
3074 if (image->matte != MagickFalse)
3075 pixel.opacity+=span.x*s->opacity;
3076 if (scale_indexes != (IndexPacket *) NULL)
3077 pixel.index+=span.x*s->index;
3078 t->red=pixel.red;
3079 t->green=pixel.green;
3080 t->blue=pixel.blue;
3081 if (scale_image->matte != MagickFalse)
3082 t->opacity=pixel.opacity;
3083 if (scale_indexes != (IndexPacket *) NULL)
3084 t->index=pixel.index;
3085 scale.x-=span.x;
3086 span.x=1.0;
3087 next_column=MagickTrue;
3088 }
3089 if (scale.x > 0)
3090 {
3091 if (next_column != MagickFalse)
3092 {
3093 pixel=zero;
3094 next_column=MagickFalse;
3095 t++;
3096 }
3097 pixel.red+=scale.x*s->red;
3098 pixel.green+=scale.x*s->green;
3099 pixel.blue+=scale.x*s->blue;
3100 if (scale_image->matte != MagickFalse)
3101 pixel.opacity+=scale.x*s->opacity;
3102 if (scale_indexes != (IndexPacket *) NULL)
3103 pixel.index+=scale.x*s->index;
3104 span.x-=scale.x;
3105 }
3106 s++;
3107 }
3108 if (span.x > 0)
3109 {
3110 s--;
3111 pixel.red+=span.x*s->red;
3112 pixel.green+=span.x*s->green;
3113 pixel.blue+=span.x*s->blue;
3114 if (scale_image->matte != MagickFalse)
3115 pixel.opacity+=span.x*s->opacity;
3116 if (scale_indexes != (IndexPacket *) NULL)
3117 pixel.index+=span.x*s->index;
3118 }
3119 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003120 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003121 {
3122 t->red=pixel.red;
3123 t->green=pixel.green;
3124 t->blue=pixel.blue;
3125 if (scale_image->matte != MagickFalse)
3126 t->opacity=pixel.opacity;
3127 if (scale_indexes != (IndexPacket *) NULL)
3128 t->index=pixel.index;
3129 }
3130 /*
3131 Transfer scanline to scaled image.
3132 */
3133 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003134 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003135 {
cristyce70c172010-01-07 17:15:30 +00003136 q->red=ClampToQuantum(t->red);
3137 q->green=ClampToQuantum(t->green);
3138 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003139 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003140 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003141 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003142 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003143 t++;
3144 q++;
3145 }
3146 }
cristyed6cb232010-01-20 03:07:53 +00003147 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003148 break;
cristy96b16132010-08-29 17:19:52 +00003149 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3150 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003151 if (proceed == MagickFalse)
3152 break;
3153 }
cristyed6cb232010-01-20 03:07:53 +00003154 scale_view=DestroyCacheView(scale_view);
3155 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003156 /*
3157 Free allocated memory.
3158 */
3159 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3160 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3161 if (scale_image->rows != image->rows)
3162 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3163 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3164 scale_image->type=image->type;
3165 return(scale_image);
3166}
3167
anthony02b4cb42010-10-10 04:54:35 +00003168#if 0
3169 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003170/*
3171%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3172% %
3173% %
3174% %
3175+ S e t R e s i z e F i l t e r S u p p o r t %
3176% %
3177% %
3178% %
3179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3180%
3181% SetResizeFilterSupport() specifies which IR filter to use to window
3182%
3183% The format of the SetResizeFilterSupport method is:
3184%
3185% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3186% const MagickRealType support)
3187%
3188% A description of each parameter follows:
3189%
3190% o resize_filter: the resize filter.
3191%
3192% o support: the filter spport radius.
3193%
3194*/
3195MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3196 const MagickRealType support)
3197{
3198 assert(resize_filter != (ResizeFilter *) NULL);
3199 assert(resize_filter->signature == MagickSignature);
3200 resize_filter->support=support;
3201}
anthony02b4cb42010-10-10 04:54:35 +00003202#endif
cristy3ed852e2009-09-05 21:47:34 +00003203
3204/*
3205%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3206% %
3207% %
3208% %
3209% T h u m b n a i l I m a g e %
3210% %
3211% %
3212% %
3213%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3214%
3215% ThumbnailImage() changes the size of an image to the given dimensions and
3216% removes any associated profiles. The goal is to produce small low cost
3217% thumbnail images suited for display on the Web.
3218%
3219% The format of the ThumbnailImage method is:
3220%
cristybb503372010-05-27 20:51:26 +00003221% Image *ThumbnailImage(const Image *image,const size_t columns,
3222% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003223%
3224% A description of each parameter follows:
3225%
3226% o image: the image.
3227%
3228% o columns: the number of columns in the scaled image.
3229%
3230% o rows: the number of rows in the scaled image.
3231%
3232% o exception: return any errors or warnings in this structure.
3233%
3234*/
cristy9af9b5d2010-08-15 17:04:28 +00003235MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3236 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003237{
3238#define SampleFactor 5
3239
3240 char
3241 value[MaxTextExtent];
3242
3243 const char
3244 *name;
3245
3246 Image
3247 *thumbnail_image;
3248
3249 MagickRealType
3250 x_factor,
3251 y_factor;
3252
cristybb503372010-05-27 20:51:26 +00003253 size_t
cristy3ed852e2009-09-05 21:47:34 +00003254 version;
3255
cristy9af9b5d2010-08-15 17:04:28 +00003256 struct stat
3257 attributes;
3258
cristy3ed852e2009-09-05 21:47:34 +00003259 assert(image != (Image *) NULL);
3260 assert(image->signature == MagickSignature);
3261 if (image->debug != MagickFalse)
3262 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3263 assert(exception != (ExceptionInfo *) NULL);
3264 assert(exception->signature == MagickSignature);
3265 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3266 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3267 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003268 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3269 exception);
cristy3ed852e2009-09-05 21:47:34 +00003270 else
3271 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003272 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3273 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003274 else
3275 {
3276 Image
3277 *sample_image;
3278
3279 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3280 exception);
3281 if (sample_image == (Image *) NULL)
3282 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003283 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3284 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003285 sample_image=DestroyImage(sample_image);
3286 }
3287 if (thumbnail_image == (Image *) NULL)
3288 return(thumbnail_image);
3289 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3290 if (thumbnail_image->matte == MagickFalse)
3291 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3292 thumbnail_image->depth=8;
3293 thumbnail_image->interlace=NoInterlace;
3294 /*
3295 Strip all profiles except color profiles.
3296 */
3297 ResetImageProfileIterator(thumbnail_image);
3298 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3299 {
3300 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3301 {
cristy2b726bd2010-01-11 01:05:39 +00003302 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003303 ResetImageProfileIterator(thumbnail_image);
3304 }
3305 name=GetNextImageProfile(thumbnail_image);
3306 }
3307 (void) DeleteImageProperty(thumbnail_image,"comment");
3308 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003309 if (strstr(image->magick_filename,"//") == (char *) NULL)
3310 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003311 image->magick_filename);
3312 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3313 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3314 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3315 {
cristye8c25f92010-06-03 00:53:06 +00003316 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003317 attributes.st_mtime);
3318 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3319 }
cristye8c25f92010-06-03 00:53:06 +00003320 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003321 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003322 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003323 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003324 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3325 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3326 LocaleLower(value);
3327 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3328 (void) SetImageProperty(thumbnail_image,"software",
3329 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003330 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3331 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003332 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003333 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003334 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003335 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003336 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3337 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003338 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3339 return(thumbnail_image);
3340}