blob: 65519ec97e4adba8044e69016f2b3032d862e641 [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
nicolas0d5e5322010-10-22 15:29:30 +0000540% blur=0.958033808). Now, Robidoux is the unique Keys cubic spline
541% filter satisfying the following condition:
542%
543% Robidoux exactly preserves images with only vertical or
544% horizontal features when performing 'no-op" with EWA distortion.
545%
546% That is, Robidoux is the BC-Spline with B=(-108 sqrt 2 + 228)/199
547% and C=(108 sqrt 2-29)/398. Robidoux turns out to be close to both
548% plain Mitchell and "sharpened" Lanczos2D. For example, it's first
549% crossing is (36 sqrt 2 + 123)/(72 sqrt 2 + 47) which is almost
550% identical to the first crossing of the other two.
anthony61b5ddd2010-10-05 02:33:31 +0000551%
nicolasce6dc292010-10-22 16:23:07 +0000552% 'EXPERT' OPTIONS
553%
nicolas07bac812010-09-19 18:47:02 +0000554% Special 'expert' options can be used to override any and all filter
555% settings. This is not advised unless you have expert knowledge of
556% the use of resampling filtered techniques. Check on the results of
557% your selections using the "filter:verbose" setting to make sure you
nicolasce6dc292010-10-22 16:23:07 +0000558% get the exact filter you want.
cristy3ed852e2009-09-05 21:47:34 +0000559%
anthony48f77622010-10-03 14:32:31 +0000560% "filter:filter" Select the main function associated with
561% this filter name, as the weighting function of the filter.
562% This can be used to set a windowing function as a weighting
563% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000564%
anthony7bdc0ed2010-09-15 01:52:32 +0000565% If a "filter:window" operation has not been provided, then a 'Box'
566% windowing function will be set to denote that no windowing function
567% is being used.
cristy3ed852e2009-09-05 21:47:34 +0000568%
569% "filter:window" Select this windowing function for the filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000570% While any filter could be used as a windowing function, using the
571% 'first lobe' of that filter over the whole support window, using a
anthony48f77622010-10-03 14:32:31 +0000572% non-windowing function is not advisible. If no weighting filter
573% function is specifed a 'SincFast' filter will be used.
cristy3ed852e2009-09-05 21:47:34 +0000574%
anthony48f77622010-10-03 14:32:31 +0000575% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000576% This a simpler method of setting filter support size that will
anthony48f77622010-10-03 14:32:31 +0000577% correctly handle the Sinc/Jinc switch for an operators filtering
anthony7bdc0ed2010-09-15 01:52:32 +0000578% requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000579%
580% "filter:support" Set the support size for filtering to the size given
anthony48f77622010-10-03 14:32:31 +0000581% This not recommended for Sinc/Jinc windowed filters (lobes should
anthony7bdc0ed2010-09-15 01:52:32 +0000582% be used instead). This will override any 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000583%
anthonyb6d08c52010-09-13 01:17:04 +0000584% "filter:win-support" Scale windowing function to this size instead.
585% This causes the windowing (or self-windowing Lagrange filter) to act
586% is if the support window it much much larger than what is actually
587% supplied to the calling operator. The filter however is still
588% clipped to the real support size given, by the support range suppiled
589% to the caller. If unset this will equal the normal filter support
590% size.
591%
cristy3ed852e2009-09-05 21:47:34 +0000592% "filter:blur" Scale the filter and support window by this amount.
593% A value >1 will generally result in a more burred image with
594% more ringing effects, while a value <1 will sharpen the
595% resulting image with more aliasing and Morie effects.
596%
anthonyf5e76ef2010-10-12 01:22:01 +0000597% "filter:sigma" The sigma value to use for the Gaussian filter only.
598% Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for cylindrical
599% usage. It effectially provides a alturnative to 'blur' for Gaussians
600% without it also effecting the final 'practical support' size.
601%
cristy3ed852e2009-09-05 21:47:34 +0000602% "filter:b"
603% "filter:c" Override the preset B,C values for a Cubic type of filter
604% If only one of these are given it is assumes to be a 'Keys'
605% type of filter such that B+2C=1, where Keys 'alpha' value = C
606%
anthonyb6d08c52010-09-13 01:17:04 +0000607% "filter:verbose" Output the exact results of the filter selections
608% made, as well as plotting data for graphing the resulting filter
609% over support range (blur adjusted).
cristy3ed852e2009-09-05 21:47:34 +0000610%
611% Set a true un-windowed Sinc filter with 10 lobes (very slow)
anthony48f77622010-10-03 14:32:31 +0000612% -define filter:filter=Sinc
613% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000614%
anthony48f77622010-10-03 14:32:31 +0000615% For example force an 8 lobe Lanczos (Sinc or Jinc) filter...
cristy3ed852e2009-09-05 21:47:34 +0000616% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000617% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000618%
cristy3ed852e2009-09-05 21:47:34 +0000619% The format of the AcquireResizeFilter method is:
620%
621% ResizeFilter *AcquireResizeFilter(const Image *image,
622% const FilterTypes filter_type, const MagickBooleanType radial,
623% ExceptionInfo *exception)
624%
cristy33b1c162010-01-23 22:51:51 +0000625% A description of each parameter follows:
626%
cristy3ed852e2009-09-05 21:47:34 +0000627% o image: the image.
628%
nicolas07bac812010-09-19 18:47:02 +0000629% o filter: the filter type, defining a preset filter, window and
630% support. The artifact settings listed above will override
631% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000632%
anthony48f77622010-10-03 14:32:31 +0000633% o blur: blur the filter by this amount, use 1.0 if unknown. Image
634% artifact "filter:blur" will override this API call usage, including
635% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000636%
anthony48f77622010-10-03 14:32:31 +0000637% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
638% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000639%
640% o exception: return any errors or warnings in this structure.
641%
642*/
643MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000644 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000645 const MagickBooleanType cylindrical,ExceptionInfo *exception)
646{
647 const char
648 *artifact;
649
650 FilterTypes
651 filter_type,
652 window_type;
653
cristy3ed852e2009-09-05 21:47:34 +0000654 MagickRealType
655 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000656 C,
657 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000658
659 register ResizeFilter
660 *resize_filter;
661
cristy9af9b5d2010-08-15 17:04:28 +0000662 ssize_t
663 option;
664
cristy3ed852e2009-09-05 21:47:34 +0000665 /*
anthony48f77622010-10-03 14:32:31 +0000666 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000667 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000668 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
669 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
670 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000671
nicolas07bac812010-09-19 18:47:02 +0000672 WARNING: The order of this tabel must match the order of the
673 FilterTypes enumeration specified in "resample.h", or the filter
674 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000675
676 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000677 */
678 static struct
679 {
680 FilterTypes
681 filter,
682 window;
683 } const mapping[SentinelFilter] =
684 {
anthony462ee072010-09-27 12:34:02 +0000685 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
686 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
687 { BoxFilter, BoxFilter }, /* Box averaging filter */
688 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
689 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
690 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
691 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
692 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
693 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
694 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
695 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
696 { CatromFilter, BoxFilter }, /* Cubic interpolator */
697 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
698 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
anthony48f77622010-10-03 14:32:31 +0000699 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
700 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony462ee072010-09-27 12:34:02 +0000701 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
702 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
703 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
704 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
705 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
706 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
707 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolas5835ee02010-10-10 20:40:15 +0000708 { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
anthonye5f06452010-10-12 05:48:17 +0000709 { Lanczos2DSharpFilter, JincFilter },/* SPECIAL: ditto sharpened */
710 { RobidouxFilter, BoxFilter }, /* SPECIAL: Keys cubic tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000711 };
712 /*
nicolas32f44eb2010-09-20 01:23:12 +0000713 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000714 function. The default support size for that filter as a weighting
715 function, the range to scale with to use that function as a sinc
716 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000717
anthony07a3f7f2010-09-16 03:03:11 +0000718 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000719 SincFast(), and CubicBC() functions, which may have multiple
720 filter to function associations.
721
722 See "filter:verbose" handling below for the function -> filter
723 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000724 */
725 static struct
726 {
727 MagickRealType
728 (*function)(const MagickRealType, const ResizeFilter*),
nicolas8eccc162010-10-16 19:48:13 +0000729 lobes, /* Default lobes/support size of the weighting filter. */
anthony450db502010-10-19 04:03:03 +0000730 scale, /* Support when function used as a windowing function
731 Typically equal to the location of the first zero crossing. */
732 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
cristy3ed852e2009-09-05 21:47:34 +0000733 } const filters[SentinelFilter] =
734 {
anthony61b5ddd2010-10-05 02:33:31 +0000735 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
736 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
737 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
738 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
739 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
740 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
741 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
742 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000743 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000744 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
745 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
746 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
anthony853d6972010-10-08 06:01:31 +0000747 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
748 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
nicolasf8689f42010-10-18 16:14:08 +0000749 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000750 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
751 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
752 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
753 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
754 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
755 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
756 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
757 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
nicolas8eccc162010-10-16 19:48:13 +0000758 { Jinc, 2.0, 1.2196698912665045, 0.0, 0.0 },
759 /* Lanczos2D (Jinc-Jinc) */
760 { Jinc, 2.0, 1.1684849904329952, 0.0, 0.0 },
761 /* Lanczos2D sharpened with blur=0.958033808 */
anthony450db502010-10-19 04:03:03 +0000762 { CubicBC, 2.0, 1.1685777620836932,
nicolas0d5e5322010-10-22 15:29:30 +0000763 0.37821575509399867, 0.31089212245300067 }
764 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
cristy3ed852e2009-09-05 21:47:34 +0000765 };
766 /*
anthony9a98fc62010-10-11 02:47:19 +0000767 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
anthony450db502010-10-19 04:03:03 +0000768 function being used as a filter. It is used by the "filter:lobes" expert
769 setting and for 'lobes' for Jinc functions in the previous table. This
770 way users do not have to deal with the highly irrational lobe sizes of the
anthony9a98fc62010-10-11 02:47:19 +0000771 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000772
nicolase473f722010-10-07 00:05:13 +0000773 Values taken from
anthony48f77622010-10-03 14:32:31 +0000774 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000775 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000776 */
777 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000778 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000779 {
nicolas8eccc162010-10-16 19:48:13 +0000780 1.2196698912665045,
781 2.2331305943815286,
782 3.2383154841662362,
783 4.2410628637960699,
784 5.2427643768701817,
785 6.2439216898644877,
786 7.244759868719957,
787 8.2453949139520427,
788 9.2458926849494673,
789 10.246293348754916,
790 11.246622794877883,
791 12.246898461138105,
792 13.247132522181061,
793 14.247333735806849,
anthonyc2d07db2010-09-15 23:47:40 +0000794 15.2475085630373,
nicolas8eccc162010-10-16 19:48:13 +0000795 16.247661874700962
cristy3ed852e2009-09-05 21:47:34 +0000796 };
797
cristy33b1c162010-01-23 22:51:51 +0000798 /*
799 Allocate resize filter.
800 */
cristy3ed852e2009-09-05 21:47:34 +0000801 assert(image != (const Image *) NULL);
802 assert(image->signature == MagickSignature);
803 if (image->debug != MagickFalse)
804 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
805 assert(UndefinedFilter < filter && filter < SentinelFilter);
806 assert(exception != (ExceptionInfo *) NULL);
807 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000808 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000809 if (resize_filter == (ResizeFilter *) NULL)
810 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000811 /*
812 Defaults for the requested filter.
813 */
814 filter_type=mapping[filter].filter;
815 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000816 resize_filter->blur = blur;
817 sigma = 0.5;
anthony48f77622010-10-03 14:32:31 +0000818 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000819 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000820 switch (filter_type)
821 {
822 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000823 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000824 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000825 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000826 break;
anthonyba5a7c32010-09-15 02:42:25 +0000827 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000828 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000829 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000830 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000831 break;
cristy33b1c162010-01-23 22:51:51 +0000832 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000833 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000834 filter_type=JincFilter;
835 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000836 break;
anthony08958462010-10-12 06:48:35 +0000837 case Lanczos2DSharpFilter:
nicolas1f4c0512010-10-20 20:19:30 +0000838 /* Sharpened by Nicolas Robidoux so as to optimize for minimal
839 * blurring of orthogonal lines
anthony08958462010-10-12 06:48:35 +0000840 */
841 resize_filter->blur *= 0.958033808;
842 break;
anthonyf5e76ef2010-10-12 01:22:01 +0000843 case GaussianFilter:
cristy68f103e2010-10-14 01:19:07 +0000844 sigma = (MagickRealType) (MagickSQ2/2.0); /* Cylindrical Gaussian sigma is sqrt(2)/2 */
anthonyf5e76ef2010-10-12 01:22:01 +0000845 break;
cristya782ecf2010-01-25 02:59:14 +0000846 default:
847 break;
cristy3ed852e2009-09-05 21:47:34 +0000848 }
anthony61b5ddd2010-10-05 02:33:31 +0000849 else
850 switch (filter_type)
851 {
852 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000853 case Lanczos2DSharpFilter:
nicolas45b58a92010-10-07 15:46:39 +0000854 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000855 window_type=SincFastFilter;
856 break;
857 default:
858 break;
859 }
860
cristy3ed852e2009-09-05 21:47:34 +0000861 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000862 if (artifact != (const char *) NULL)
863 {
cristy9af9b5d2010-08-15 17:04:28 +0000864 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000865 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000866 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000867 filter_type=(FilterTypes) option;
868 window_type=BoxFilter;
869 }
870 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000871 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000872 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000873 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000874 }
nicolas07bac812010-09-19 18:47:02 +0000875 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000876 artifact=GetImageArtifact(image,"filter:window");
877 if (artifact != (const char *) NULL)
878 {
cristy9af9b5d2010-08-15 17:04:28 +0000879 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000880 if ((UndefinedFilter < option) && (option < SentinelFilter))
881 {
882 if (option != LanczosFilter)
883 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000884 else
anthony48f77622010-10-03 14:32:31 +0000885 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000886 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000887 }
cristy33b1c162010-01-23 22:51:51 +0000888 }
cristy3ed852e2009-09-05 21:47:34 +0000889 }
cristy33b1c162010-01-23 22:51:51 +0000890 else
891 {
anthony48f77622010-10-03 14:32:31 +0000892 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000893 artifact=GetImageArtifact(image,"filter:window");
894 if (artifact != (const char *) NULL)
895 {
896 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
897 artifact);
898 if ((UndefinedFilter < option) && (option < SentinelFilter))
899 {
anthony61b5ddd2010-10-05 02:33:31 +0000900 filter_type=cylindrical != MagickFalse ?
901 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000902 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000903 }
904 }
905 }
nicolas07bac812010-09-19 18:47:02 +0000906 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000907 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000908 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000909 resize_filter->window=filters[window_type].function;
910 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000911 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000912
anthonyf5e76ef2010-10-12 01:22:01 +0000913 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000914 if (cylindrical != MagickFalse)
915 switch (filter_type)
916 {
917 case PointFilter:
918 case BoxFilter:
919 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000920 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000921 break;
anthony81b8bf92010-10-02 13:54:34 +0000922 default:
923 break;
anthony10b8bc82010-10-02 12:48:46 +0000924 }
anthony61b5ddd2010-10-05 02:33:31 +0000925 else
926 switch (filter_type)
927 {
928 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000929 case Lanczos2DSharpFilter:
anthony853d6972010-10-08 06:01:31 +0000930 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000931 resize_filter->filter=SincFast;
932 break;
933 default:
934 break;
935 }
936
anthonyf5e76ef2010-10-12 01:22:01 +0000937 /*
938 ** More Expert Option Modifications
939 */
940
941 /* User Sigma Override - no support change */
942 artifact=GetImageArtifact(image,"filter:sigma");
943 if (artifact != (const char *) NULL)
944 sigma=StringToDouble(artifact);
945 /* Define coefficents for Gaussian (assumes no cubic window) */
946 if ( GaussianFilter ) {
947 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
cristy68f103e2010-10-14 01:19:07 +0000948 resize_filter->coeff[1] = (MagickRealType) (1.0/(Magick2PI*sigma*sigma)); /* unused */
anthonyf5e76ef2010-10-12 01:22:01 +0000949 }
950
951 /* Blur Override */
952 artifact=GetImageArtifact(image,"filter:blur");
953 if (artifact != (const char *) NULL)
954 resize_filter->blur=StringToDouble(artifact);
955 if (resize_filter->blur < MagickEpsilon)
956 resize_filter->blur=(MagickRealType) MagickEpsilon;
957
958 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000959 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000960 if (artifact != (const char *) NULL)
961 {
cristybb503372010-05-27 20:51:26 +0000962 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000963 lobes;
964
cristy96b16132010-08-29 17:19:52 +0000965 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000966 if (lobes < 1)
967 lobes=1;
968 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000969 }
anthony61b5ddd2010-10-05 02:33:31 +0000970 /* convert Jinc lobes to a real support value */
971 if (resize_filter->filter == Jinc)
972 {
973 if (resize_filter->support > 16)
974 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
975 else
976 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
977 }
978 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000979 artifact=GetImageArtifact(image,"filter:support");
980 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000981 resize_filter->support=fabs(StringToDouble(artifact));
982 /*
nicolas07bac812010-09-19 18:47:02 +0000983 Scale windowing function separatally to the support 'clipping'
984 window that calling operator is planning to actually use. (Expert
985 override)
cristy3ed852e2009-09-05 21:47:34 +0000986 */
anthony55f12332010-09-10 01:13:02 +0000987 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000988 artifact=GetImageArtifact(image,"filter:win-support");
989 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000990 resize_filter->window_support=fabs(StringToDouble(artifact));
991 /*
anthony1f90a6b2010-09-14 08:56:31 +0000992 Adjust window function scaling to the windowing support for
993 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000994 */
995 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +0000996
anthony55f12332010-09-10 01:13:02 +0000997 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000998 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000999 */
cristy3ed852e2009-09-05 21:47:34 +00001000 B=0.0;
1001 C=0.0;
cristy33b1c162010-01-23 22:51:51 +00001002 if ((filters[filter_type].function == CubicBC) ||
1003 (filters[window_type].function == CubicBC))
1004 {
anthony2d9b8b52010-09-14 08:31:07 +00001005 B=filters[filter_type].B;
1006 C=filters[filter_type].C;
1007 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +00001008 {
anthony2d9b8b52010-09-14 08:31:07 +00001009 B=filters[window_type].B;
1010 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +00001011 }
cristy33b1c162010-01-23 22:51:51 +00001012 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001013 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001014 {
1015 B=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001016 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001017 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001018 if (artifact != (const char *) NULL)
1019 C=StringToDouble(artifact);
1020 }
1021 else
1022 {
1023 artifact=GetImageArtifact(image,"filter:c");
1024 if (artifact != (const char *) NULL)
1025 {
1026 C=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001027 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001028 }
1029 }
anthonyf5e76ef2010-10-12 01:22:01 +00001030 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
1031 resize_filter->coeff[0]=(6.0-2.0*B)/6.0;
1032 resize_filter->coeff[1]=0.0;
1033 resize_filter->coeff[2]=(-18.0+12.0*B+6.0*C)/6.0;
1034 resize_filter->coeff[3]=(12.0-9.0*B-6.0*C)/6.0;
1035 resize_filter->coeff[4]=(8.0*B+24.0*C)/6.0;
1036 resize_filter->coeff[5]=(-12.0*B-48.0*C)/6.0;
1037 resize_filter->coeff[6]=(6.0*B+30.0*C)/6.0;
1038 resize_filter->coeff[7]=(-B-6.0*C)/6.0;
cristy3ed852e2009-09-05 21:47:34 +00001039 }
anthonyf5e76ef2010-10-12 01:22:01 +00001040
anthony55f12332010-09-10 01:13:02 +00001041 /*
nicolas07bac812010-09-19 18:47:02 +00001042 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001043 */
cristyf5b49372010-10-16 01:06:47 +00001044#if defined(MAGICKCORE_OPENMP_SUPPORT)
1045 #pragma omp master
1046 {
1047#endif
1048 artifact=GetImageArtifact(image,"filter:verbose");
1049 if (artifact != (const char *) NULL)
1050 {
1051 double
anthony450db502010-10-19 04:03:03 +00001052 support,
cristyf5b49372010-10-16 01:06:47 +00001053 x;
cristy3ed852e2009-09-05 21:47:34 +00001054
cristyf5b49372010-10-16 01:06:47 +00001055 /*
1056 Set the weighting function properly when the weighting
1057 function may not exactly match the filter of the same name.
1058 EG: a Point filter really uses a Box weighting function
1059 with a different support than is typically used.
anthony463be1d2010-09-26 01:07:36 +00001060
cristyf5b49372010-10-16 01:06:47 +00001061 */
1062 if (resize_filter->filter == Box) filter_type=BoxFilter;
1063 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1064 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1065 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1066 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1067 /*
1068 Report Filter Details.
1069 */
1070 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1071 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
1072 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1073 MagickFilterOptions,filter_type));
1074 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
1075 MagickFilterOptions, window_type));
1076 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
1077 (double) resize_filter->support);
1078 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
1079 (double) resize_filter->window_support);
1080 (void) fprintf(stdout,"# scale_blur = %.*g\n",GetMagickPrecision(),
1081 (double) resize_filter->blur);
1082 if ( filter_type == GaussianFilter )
1083 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",GetMagickPrecision(),
1084 (double) sigma);
1085 (void) fprintf(stdout,"# practical_support = %.*g\n",GetMagickPrecision(),
1086 (double) support);
1087 if ( filter_type == CubicFilter || window_type == CubicFilter )
1088 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1089 (double) B,GetMagickPrecision(),(double) C);
1090 (void) fprintf(stdout,"\n");
1091 /*
1092 Output values of resulting filter graph -- for graphing
1093 filter result.
1094 */
1095 for (x=0.0; x <= support; x+=0.01f)
1096 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1097 (double) GetResizeFilterWeight(resize_filter,x));
1098 /* A final value so gnuplot can graph the 'stop' properly. */
1099 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1100 0.0);
1101 }
1102 /* Output the above once only for each image - remove setting */
1103 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1104#if defined(MAGICKCORE_OPENMP_SUPPORT)
1105 }
1106#endif
cristy3ed852e2009-09-05 21:47:34 +00001107 return(resize_filter);
1108}
1109
1110/*
1111%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1112% %
1113% %
1114% %
1115% A d a p t i v e R e s i z e I m a g e %
1116% %
1117% %
1118% %
1119%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1120%
1121% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1122%
1123% The format of the AdaptiveResizeImage method is:
1124%
cristy9af9b5d2010-08-15 17:04:28 +00001125% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1126% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001127%
1128% A description of each parameter follows:
1129%
1130% o image: the image.
1131%
1132% o columns: the number of columns in the resized image.
1133%
1134% o rows: the number of rows in the resized image.
1135%
1136% o exception: return any errors or warnings in this structure.
1137%
1138*/
1139MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001140 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001141{
1142#define AdaptiveResizeImageTag "Resize/Image"
1143
cristyc4c8d132010-01-07 01:58:38 +00001144 CacheView
1145 *resize_view;
1146
cristy3ed852e2009-09-05 21:47:34 +00001147 Image
1148 *resize_image;
1149
cristy3ed852e2009-09-05 21:47:34 +00001150 MagickBooleanType
1151 proceed;
1152
1153 MagickPixelPacket
1154 pixel;
1155
1156 PointInfo
1157 offset;
1158
1159 ResampleFilter
1160 *resample_filter;
1161
cristy9af9b5d2010-08-15 17:04:28 +00001162 ssize_t
1163 y;
1164
cristy3ed852e2009-09-05 21:47:34 +00001165 /*
1166 Adaptively resize image.
1167 */
1168 assert(image != (const Image *) NULL);
1169 assert(image->signature == MagickSignature);
1170 if (image->debug != MagickFalse)
1171 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1172 assert(exception != (ExceptionInfo *) NULL);
1173 assert(exception->signature == MagickSignature);
1174 if ((columns == 0) || (rows == 0))
1175 return((Image *) NULL);
1176 if ((columns == image->columns) && (rows == image->rows))
1177 return(CloneImage(image,0,0,MagickTrue,exception));
1178 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1179 if (resize_image == (Image *) NULL)
1180 return((Image *) NULL);
1181 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1182 {
1183 InheritException(exception,&resize_image->exception);
1184 resize_image=DestroyImage(resize_image);
1185 return((Image *) NULL);
1186 }
1187 GetMagickPixelPacket(image,&pixel);
1188 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001189 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001190 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001191 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001192 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001193 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001194 {
1195 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001196 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001197
cristybb503372010-05-27 20:51:26 +00001198 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001199 x;
1200
1201 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001202 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001203
1204 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1205 exception);
1206 if (q == (PixelPacket *) NULL)
1207 break;
1208 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1209 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001210 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001211 {
1212 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1213 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1214 &pixel);
1215 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1216 q++;
1217 }
1218 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1219 break;
cristy96b16132010-08-29 17:19:52 +00001220 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1221 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001222 if (proceed == MagickFalse)
1223 break;
1224 }
1225 resample_filter=DestroyResampleFilter(resample_filter);
1226 resize_view=DestroyCacheView(resize_view);
1227 return(resize_image);
1228}
1229
1230/*
1231%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1232% %
1233% %
1234% %
1235+ B e s s e l O r d e r O n e %
1236% %
1237% %
1238% %
1239%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1240%
1241% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001242% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001243%
1244% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1245%
1246% j1(x) = x*j1(x);
1247%
1248% For x in (8,inf)
1249%
1250% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1251%
1252% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1253%
1254% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1255% = 1/sqrt(2) * (sin(x) - cos(x))
1256% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1257% = -1/sqrt(2) * (sin(x) + cos(x))
1258%
1259% The format of the BesselOrderOne method is:
1260%
1261% MagickRealType BesselOrderOne(MagickRealType x)
1262%
1263% A description of each parameter follows:
1264%
1265% o x: MagickRealType value.
1266%
1267*/
1268
1269#undef I0
1270static MagickRealType I0(MagickRealType x)
1271{
1272 MagickRealType
1273 sum,
1274 t,
1275 y;
1276
cristybb503372010-05-27 20:51:26 +00001277 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001278 i;
1279
1280 /*
1281 Zeroth order Bessel function of the first kind.
1282 */
1283 sum=1.0;
1284 y=x*x/4.0;
1285 t=y;
1286 for (i=2; t > MagickEpsilon; i++)
1287 {
1288 sum+=t;
1289 t*=y/((MagickRealType) i*i);
1290 }
1291 return(sum);
1292}
1293
1294#undef J1
1295static MagickRealType J1(MagickRealType x)
1296{
1297 MagickRealType
1298 p,
1299 q;
1300
cristybb503372010-05-27 20:51:26 +00001301 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001302 i;
1303
1304 static const double
1305 Pone[] =
1306 {
1307 0.581199354001606143928050809e+21,
1308 -0.6672106568924916298020941484e+20,
1309 0.2316433580634002297931815435e+19,
1310 -0.3588817569910106050743641413e+17,
1311 0.2908795263834775409737601689e+15,
1312 -0.1322983480332126453125473247e+13,
1313 0.3413234182301700539091292655e+10,
1314 -0.4695753530642995859767162166e+7,
1315 0.270112271089232341485679099e+4
1316 },
1317 Qone[] =
1318 {
1319 0.11623987080032122878585294e+22,
1320 0.1185770712190320999837113348e+20,
1321 0.6092061398917521746105196863e+17,
1322 0.2081661221307607351240184229e+15,
1323 0.5243710262167649715406728642e+12,
1324 0.1013863514358673989967045588e+10,
1325 0.1501793594998585505921097578e+7,
1326 0.1606931573481487801970916749e+4,
1327 0.1e+1
1328 };
1329
1330 p=Pone[8];
1331 q=Qone[8];
1332 for (i=7; i >= 0; i--)
1333 {
1334 p=p*x*x+Pone[i];
1335 q=q*x*x+Qone[i];
1336 }
1337 return(p/q);
1338}
1339
1340#undef P1
1341static MagickRealType P1(MagickRealType x)
1342{
1343 MagickRealType
1344 p,
1345 q;
1346
cristybb503372010-05-27 20:51:26 +00001347 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001348 i;
1349
1350 static const double
1351 Pone[] =
1352 {
1353 0.352246649133679798341724373e+5,
1354 0.62758845247161281269005675e+5,
1355 0.313539631109159574238669888e+5,
1356 0.49854832060594338434500455e+4,
1357 0.2111529182853962382105718e+3,
1358 0.12571716929145341558495e+1
1359 },
1360 Qone[] =
1361 {
1362 0.352246649133679798068390431e+5,
1363 0.626943469593560511888833731e+5,
1364 0.312404063819041039923015703e+5,
1365 0.4930396490181088979386097e+4,
1366 0.2030775189134759322293574e+3,
1367 0.1e+1
1368 };
1369
1370 p=Pone[5];
1371 q=Qone[5];
1372 for (i=4; i >= 0; i--)
1373 {
1374 p=p*(8.0/x)*(8.0/x)+Pone[i];
1375 q=q*(8.0/x)*(8.0/x)+Qone[i];
1376 }
1377 return(p/q);
1378}
1379
1380#undef Q1
1381static MagickRealType Q1(MagickRealType x)
1382{
1383 MagickRealType
1384 p,
1385 q;
1386
cristybb503372010-05-27 20:51:26 +00001387 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001388 i;
1389
1390 static const double
1391 Pone[] =
1392 {
1393 0.3511751914303552822533318e+3,
1394 0.7210391804904475039280863e+3,
1395 0.4259873011654442389886993e+3,
1396 0.831898957673850827325226e+2,
1397 0.45681716295512267064405e+1,
1398 0.3532840052740123642735e-1
1399 },
1400 Qone[] =
1401 {
1402 0.74917374171809127714519505e+4,
1403 0.154141773392650970499848051e+5,
1404 0.91522317015169922705904727e+4,
1405 0.18111867005523513506724158e+4,
1406 0.1038187585462133728776636e+3,
1407 0.1e+1
1408 };
1409
1410 p=Pone[5];
1411 q=Qone[5];
1412 for (i=4; i >= 0; i--)
1413 {
1414 p=p*(8.0/x)*(8.0/x)+Pone[i];
1415 q=q*(8.0/x)*(8.0/x)+Qone[i];
1416 }
1417 return(p/q);
1418}
1419
1420static MagickRealType BesselOrderOne(MagickRealType x)
1421{
1422 MagickRealType
1423 p,
1424 q;
1425
1426 if (x == 0.0)
1427 return(0.0);
1428 p=x;
1429 if (x < 0.0)
1430 x=(-x);
1431 if (x < 8.0)
1432 return(p*J1(x));
1433 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1434 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1435 cos((double) x))));
1436 if (p < 0.0)
1437 q=(-q);
1438 return(q);
1439}
1440
1441/*
1442%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1443% %
1444% %
1445% %
1446+ D e s t r o y R e s i z e F i l t e r %
1447% %
1448% %
1449% %
1450%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1451%
1452% DestroyResizeFilter() destroy the resize filter.
1453%
cristya2ffd7e2010-03-10 20:50:30 +00001454% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001455%
1456% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1457%
1458% A description of each parameter follows:
1459%
1460% o resize_filter: the resize filter.
1461%
1462*/
1463MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1464{
1465 assert(resize_filter != (ResizeFilter *) NULL);
1466 assert(resize_filter->signature == MagickSignature);
1467 resize_filter->signature=(~MagickSignature);
1468 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1469 return(resize_filter);
1470}
1471
1472/*
1473%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1474% %
1475% %
1476% %
1477+ G e t R e s i z e F i l t e r S u p p o r t %
1478% %
1479% %
1480% %
1481%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1482%
1483% GetResizeFilterSupport() return the current support window size for this
1484% filter. Note that this may have been enlarged by filter:blur factor.
1485%
1486% The format of the GetResizeFilterSupport method is:
1487%
1488% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1489%
1490% A description of each parameter follows:
1491%
1492% o filter: Image filter to use.
1493%
1494*/
1495MagickExport MagickRealType GetResizeFilterSupport(
1496 const ResizeFilter *resize_filter)
1497{
1498 assert(resize_filter != (ResizeFilter *) NULL);
1499 assert(resize_filter->signature == MagickSignature);
1500 return(resize_filter->support*resize_filter->blur);
1501}
1502
1503/*
1504%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1505% %
1506% %
1507% %
1508+ G e t R e s i z e F i l t e r W e i g h t %
1509% %
1510% %
1511% %
1512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1513%
1514% GetResizeFilterWeight evaluates the specified resize filter at the point x
1515% which usally lies between zero and the filters current 'support' and
1516% returns the weight of the filter function at that point.
1517%
1518% The format of the GetResizeFilterWeight method is:
1519%
1520% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1521% const MagickRealType x)
1522%
1523% A description of each parameter follows:
1524%
1525% o filter: the filter type.
1526%
1527% o x: the point.
1528%
1529*/
1530MagickExport MagickRealType GetResizeFilterWeight(
1531 const ResizeFilter *resize_filter,const MagickRealType x)
1532{
1533 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001534 scale,
1535 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001536
1537 /*
1538 Windowing function - scale the weighting filter by this amount.
1539 */
1540 assert(resize_filter != (ResizeFilter *) NULL);
1541 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001542 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001543 if ((resize_filter->window_support < MagickEpsilon) ||
1544 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001545 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001546 else
1547 {
anthony55f12332010-09-10 01:13:02 +00001548 scale=resize_filter->scale;
1549 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001550 }
anthony55f12332010-09-10 01:13:02 +00001551 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001552}
1553
1554/*
1555%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1556% %
1557% %
1558% %
1559% M a g n i f y I m a g e %
1560% %
1561% %
1562% %
1563%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1564%
1565% MagnifyImage() is a convenience method that scales an image proportionally
1566% to twice its size.
1567%
1568% The format of the MagnifyImage method is:
1569%
1570% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1571%
1572% A description of each parameter follows:
1573%
1574% o image: the image.
1575%
1576% o exception: return any errors or warnings in this structure.
1577%
1578*/
1579MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1580{
1581 Image
1582 *magnify_image;
1583
1584 assert(image != (Image *) NULL);
1585 assert(image->signature == MagickSignature);
1586 if (image->debug != MagickFalse)
1587 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1588 assert(exception != (ExceptionInfo *) NULL);
1589 assert(exception->signature == MagickSignature);
1590 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1591 1.0,exception);
1592 return(magnify_image);
1593}
1594
1595/*
1596%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1597% %
1598% %
1599% %
1600% M i n i f y I m a g e %
1601% %
1602% %
1603% %
1604%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1605%
1606% MinifyImage() is a convenience method that scales an image proportionally
1607% to half its size.
1608%
1609% The format of the MinifyImage method is:
1610%
1611% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1612%
1613% A description of each parameter follows:
1614%
1615% o image: the image.
1616%
1617% o exception: return any errors or warnings in this structure.
1618%
1619*/
1620MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1621{
1622 Image
1623 *minify_image;
1624
1625 assert(image != (Image *) NULL);
1626 assert(image->signature == MagickSignature);
1627 if (image->debug != MagickFalse)
1628 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1629 assert(exception != (ExceptionInfo *) NULL);
1630 assert(exception->signature == MagickSignature);
1631 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1632 1.0,exception);
1633 return(minify_image);
1634}
1635
1636/*
1637%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1638% %
1639% %
1640% %
1641% R e s a m p l e I m a g e %
1642% %
1643% %
1644% %
1645%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1646%
1647% ResampleImage() resize image in terms of its pixel size, so that when
1648% displayed at the given resolution it will be the same size in terms of
1649% real world units as the original image at the original resolution.
1650%
1651% The format of the ResampleImage method is:
1652%
1653% Image *ResampleImage(Image *image,const double x_resolution,
1654% const double y_resolution,const FilterTypes filter,const double blur,
1655% ExceptionInfo *exception)
1656%
1657% A description of each parameter follows:
1658%
1659% o image: the image to be resized to fit the given resolution.
1660%
1661% o x_resolution: the new image x resolution.
1662%
1663% o y_resolution: the new image y resolution.
1664%
1665% o filter: Image filter to use.
1666%
1667% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1668%
1669*/
1670MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1671 const double y_resolution,const FilterTypes filter,const double blur,
1672 ExceptionInfo *exception)
1673{
1674#define ResampleImageTag "Resample/Image"
1675
1676 Image
1677 *resample_image;
1678
cristybb503372010-05-27 20:51:26 +00001679 size_t
cristy3ed852e2009-09-05 21:47:34 +00001680 height,
1681 width;
1682
1683 /*
1684 Initialize sampled image attributes.
1685 */
1686 assert(image != (const Image *) NULL);
1687 assert(image->signature == MagickSignature);
1688 if (image->debug != MagickFalse)
1689 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1690 assert(exception != (ExceptionInfo *) NULL);
1691 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001692 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1693 72.0 : image->x_resolution)+0.5);
1694 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1695 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001696 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1697 if (resample_image != (Image *) NULL)
1698 {
1699 resample_image->x_resolution=x_resolution;
1700 resample_image->y_resolution=y_resolution;
1701 }
1702 return(resample_image);
1703}
1704#if defined(MAGICKCORE_LQR_DELEGATE)
1705
1706/*
1707%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1708% %
1709% %
1710% %
1711% L i q u i d R e s c a l e I m a g e %
1712% %
1713% %
1714% %
1715%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1716%
1717% LiquidRescaleImage() rescales image with seam carving.
1718%
1719% The format of the LiquidRescaleImage method is:
1720%
1721% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001722% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001723% const double delta_x,const double rigidity,ExceptionInfo *exception)
1724%
1725% A description of each parameter follows:
1726%
1727% o image: the image.
1728%
1729% o columns: the number of columns in the rescaled image.
1730%
1731% o rows: the number of rows in the rescaled image.
1732%
1733% o delta_x: maximum seam transversal step (0 means straight seams).
1734%
1735% o rigidity: introduce a bias for non-straight seams (typically 0).
1736%
1737% o exception: return any errors or warnings in this structure.
1738%
1739*/
cristy9af9b5d2010-08-15 17:04:28 +00001740MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1741 const size_t rows,const double delta_x,const double rigidity,
1742 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001743{
1744#define LiquidRescaleImageTag "Rescale/Image"
1745
cristyc5c6f662010-09-22 14:23:02 +00001746 CacheView
1747 *rescale_view;
1748
cristy3ed852e2009-09-05 21:47:34 +00001749 const char
1750 *map;
1751
1752 guchar
1753 *packet;
1754
1755 Image
1756 *rescale_image;
1757
1758 int
1759 x,
1760 y;
1761
1762 LqrCarver
1763 *carver;
1764
1765 LqrRetVal
1766 lqr_status;
1767
1768 MagickBooleanType
1769 status;
1770
1771 MagickPixelPacket
1772 pixel;
1773
1774 unsigned char
1775 *pixels;
1776
1777 /*
1778 Liquid rescale image.
1779 */
1780 assert(image != (const Image *) NULL);
1781 assert(image->signature == MagickSignature);
1782 if (image->debug != MagickFalse)
1783 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1784 assert(exception != (ExceptionInfo *) NULL);
1785 assert(exception->signature == MagickSignature);
1786 if ((columns == 0) || (rows == 0))
1787 return((Image *) NULL);
1788 if ((columns == image->columns) && (rows == image->rows))
1789 return(CloneImage(image,0,0,MagickTrue,exception));
1790 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001791 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001792 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1793 {
1794 Image
1795 *resize_image;
1796
cristybb503372010-05-27 20:51:26 +00001797 size_t
cristy3ed852e2009-09-05 21:47:34 +00001798 height,
1799 width;
1800
1801 /*
1802 Honor liquid resize size limitations.
1803 */
1804 for (width=image->columns; columns >= (2*width-1); width*=2);
1805 for (height=image->rows; rows >= (2*height-1); height*=2);
1806 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1807 exception);
1808 if (resize_image == (Image *) NULL)
1809 return((Image *) NULL);
1810 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1811 rigidity,exception);
1812 resize_image=DestroyImage(resize_image);
1813 return(rescale_image);
1814 }
1815 map="RGB";
1816 if (image->matte == MagickFalse)
1817 map="RGBA";
1818 if (image->colorspace == CMYKColorspace)
1819 {
1820 map="CMYK";
1821 if (image->matte == MagickFalse)
1822 map="CMYKA";
1823 }
1824 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1825 strlen(map)*sizeof(*pixels));
1826 if (pixels == (unsigned char *) NULL)
1827 return((Image *) NULL);
1828 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1829 pixels,exception);
1830 if (status == MagickFalse)
1831 {
1832 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1833 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1834 }
1835 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1836 if (carver == (LqrCarver *) NULL)
1837 {
1838 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1839 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1840 }
1841 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1842 lqr_status=lqr_carver_resize(carver,columns,rows);
1843 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1844 lqr_carver_get_height(carver),MagickTrue,exception);
1845 if (rescale_image == (Image *) NULL)
1846 {
1847 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1848 return((Image *) NULL);
1849 }
1850 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1851 {
1852 InheritException(exception,&rescale_image->exception);
1853 rescale_image=DestroyImage(rescale_image);
1854 return((Image *) NULL);
1855 }
1856 GetMagickPixelPacket(rescale_image,&pixel);
1857 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001858 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001859 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1860 {
1861 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001862 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001863
1864 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001865 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001866
anthony22aad252010-09-23 06:59:07 +00001867 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001868 if (q == (PixelPacket *) NULL)
1869 break;
cristyc5c6f662010-09-22 14:23:02 +00001870 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001871 pixel.red=QuantumRange*(packet[0]/255.0);
1872 pixel.green=QuantumRange*(packet[1]/255.0);
1873 pixel.blue=QuantumRange*(packet[2]/255.0);
1874 if (image->colorspace != CMYKColorspace)
1875 {
1876 if (image->matte == MagickFalse)
1877 pixel.opacity=QuantumRange*(packet[3]/255.0);
1878 }
1879 else
1880 {
1881 pixel.index=QuantumRange*(packet[3]/255.0);
1882 if (image->matte == MagickFalse)
1883 pixel.opacity=QuantumRange*(packet[4]/255.0);
1884 }
1885 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001886 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001887 break;
1888 }
cristyc5c6f662010-09-22 14:23:02 +00001889 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001890 /*
1891 Relinquish resources.
1892 */
1893 lqr_carver_destroy(carver);
1894 return(rescale_image);
1895}
1896#else
1897MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001898 const size_t magick_unused(columns),const size_t magick_unused(rows),
1899 const double magick_unused(delta_x),const double magick_unused(rigidity),
1900 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001901{
1902 assert(image != (const Image *) NULL);
1903 assert(image->signature == MagickSignature);
1904 if (image->debug != MagickFalse)
1905 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1906 assert(exception != (ExceptionInfo *) NULL);
1907 assert(exception->signature == MagickSignature);
1908 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1909 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1910 return((Image *) NULL);
1911}
1912#endif
1913
1914/*
1915%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1916% %
1917% %
1918% %
1919% R e s i z e I m a g e %
1920% %
1921% %
1922% %
1923%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1924%
1925% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001926% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001927%
1928% If an undefined filter is given the filter defaults to Mitchell for a
1929% colormapped image, a image with a matte channel, or if the image is
1930% enlarged. Otherwise the filter defaults to a Lanczos.
1931%
1932% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1933%
1934% The format of the ResizeImage method is:
1935%
cristybb503372010-05-27 20:51:26 +00001936% Image *ResizeImage(Image *image,const size_t columns,
1937% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001938% ExceptionInfo *exception)
1939%
1940% A description of each parameter follows:
1941%
1942% o image: the image.
1943%
1944% o columns: the number of columns in the scaled image.
1945%
1946% o rows: the number of rows in the scaled image.
1947%
1948% o filter: Image filter to use.
1949%
cristy9af9b5d2010-08-15 17:04:28 +00001950% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1951% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001952%
1953% o exception: return any errors or warnings in this structure.
1954%
1955*/
1956
1957typedef struct _ContributionInfo
1958{
1959 MagickRealType
1960 weight;
1961
cristybb503372010-05-27 20:51:26 +00001962 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001963 pixel;
1964} ContributionInfo;
1965
1966static ContributionInfo **DestroyContributionThreadSet(
1967 ContributionInfo **contribution)
1968{
cristybb503372010-05-27 20:51:26 +00001969 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001970 i;
1971
1972 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001973 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001974 if (contribution[i] != (ContributionInfo *) NULL)
1975 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1976 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001977 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001978 return(contribution);
1979}
1980
1981static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1982{
cristybb503372010-05-27 20:51:26 +00001983 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001984 i;
1985
1986 ContributionInfo
1987 **contribution;
1988
cristybb503372010-05-27 20:51:26 +00001989 size_t
cristy3ed852e2009-09-05 21:47:34 +00001990 number_threads;
1991
1992 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001993 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001994 sizeof(*contribution));
1995 if (contribution == (ContributionInfo **) NULL)
1996 return((ContributionInfo **) NULL);
1997 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00001998 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00001999 {
2000 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2001 sizeof(**contribution));
2002 if (contribution[i] == (ContributionInfo *) NULL)
2003 return(DestroyContributionThreadSet(contribution));
2004 }
2005 return(contribution);
2006}
2007
2008static inline double MagickMax(const double x,const double y)
2009{
2010 if (x > y)
2011 return(x);
2012 return(y);
2013}
2014
2015static inline double MagickMin(const double x,const double y)
2016{
2017 if (x < y)
2018 return(x);
2019 return(y);
2020}
2021
2022static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2023 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002024 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002025{
2026#define ResizeImageTag "Resize/Image"
2027
cristyfa112112010-01-04 17:48:07 +00002028 CacheView
2029 *image_view,
2030 *resize_view;
2031
cristy3ed852e2009-09-05 21:47:34 +00002032 ClassType
2033 storage_class;
2034
2035 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002036 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002037
cristy3ed852e2009-09-05 21:47:34 +00002038 MagickBooleanType
2039 status;
2040
2041 MagickPixelPacket
2042 zero;
2043
2044 MagickRealType
2045 scale,
2046 support;
2047
cristy9af9b5d2010-08-15 17:04:28 +00002048 ssize_t
2049 x;
2050
cristy3ed852e2009-09-05 21:47:34 +00002051 /*
2052 Apply filter to resize horizontally from image to resize image.
2053 */
cristy5d824382010-09-06 14:00:17 +00002054 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002055 support=scale*GetResizeFilterSupport(resize_filter);
2056 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2057 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2058 {
2059 InheritException(exception,&resize_image->exception);
2060 return(MagickFalse);
2061 }
2062 if (support < 0.5)
2063 {
2064 /*
nicolas07bac812010-09-19 18:47:02 +00002065 Support too small even for nearest neighbour: Reduce to point
2066 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002067 */
2068 support=(MagickRealType) 0.5;
2069 scale=1.0;
2070 }
2071 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2072 if (contributions == (ContributionInfo **) NULL)
2073 {
2074 (void) ThrowMagickException(exception,GetMagickModule(),
2075 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2076 return(MagickFalse);
2077 }
2078 status=MagickTrue;
2079 scale=1.0/scale;
2080 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2081 image_view=AcquireCacheView(image);
2082 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002083#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002084 #pragma omp parallel for shared(status)
2085#endif
cristybb503372010-05-27 20:51:26 +00002086 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002087 {
cristy3ed852e2009-09-05 21:47:34 +00002088 MagickRealType
2089 center,
2090 density;
2091
2092 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002093 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002094
2095 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002096 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002097
cristy03dbbd22010-09-19 23:04:47 +00002098 register ContributionInfo
2099 *restrict contribution;
2100
cristy3ed852e2009-09-05 21:47:34 +00002101 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002102 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002103
cristy3ed852e2009-09-05 21:47:34 +00002104 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002105 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002106
cristy03dbbd22010-09-19 23:04:47 +00002107 register ssize_t
2108 y;
2109
cristy9af9b5d2010-08-15 17:04:28 +00002110 ssize_t
2111 n,
2112 start,
2113 stop;
2114
cristy3ed852e2009-09-05 21:47:34 +00002115 if (status == MagickFalse)
2116 continue;
2117 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002118 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2119 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002120 density=0.0;
2121 contribution=contributions[GetOpenMPThreadId()];
2122 for (n=0; n < (stop-start); n++)
2123 {
2124 contribution[n].pixel=start+n;
2125 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2126 ((MagickRealType) (start+n)-center+0.5));
2127 density+=contribution[n].weight;
2128 }
2129 if ((density != 0.0) && (density != 1.0))
2130 {
cristybb503372010-05-27 20:51:26 +00002131 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002132 i;
2133
2134 /*
2135 Normalize.
2136 */
2137 density=1.0/density;
2138 for (i=0; i < n; i++)
2139 contribution[i].weight*=density;
2140 }
cristy9af9b5d2010-08-15 17:04:28 +00002141 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2142 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002143 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2144 exception);
2145 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2146 {
2147 status=MagickFalse;
2148 continue;
2149 }
2150 indexes=GetCacheViewVirtualIndexQueue(image_view);
2151 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002152 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002153 {
cristy3ed852e2009-09-05 21:47:34 +00002154 MagickPixelPacket
2155 pixel;
2156
2157 MagickRealType
2158 alpha;
2159
cristybb503372010-05-27 20:51:26 +00002160 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002161 i;
2162
cristy9af9b5d2010-08-15 17:04:28 +00002163 ssize_t
2164 j;
2165
cristy3ed852e2009-09-05 21:47:34 +00002166 pixel=zero;
2167 if (image->matte == MagickFalse)
2168 {
2169 for (i=0; i < n; i++)
2170 {
2171 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2172 (contribution[i].pixel-contribution[0].pixel);
2173 alpha=contribution[i].weight;
2174 pixel.red+=alpha*(p+j)->red;
2175 pixel.green+=alpha*(p+j)->green;
2176 pixel.blue+=alpha*(p+j)->blue;
2177 pixel.opacity+=alpha*(p+j)->opacity;
2178 }
cristyce70c172010-01-07 17:15:30 +00002179 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2180 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2181 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2182 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002183 if ((image->colorspace == CMYKColorspace) &&
2184 (resize_image->colorspace == CMYKColorspace))
2185 {
2186 for (i=0; i < n; i++)
2187 {
2188 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2189 (contribution[i].pixel-contribution[0].pixel);
2190 alpha=contribution[i].weight;
2191 pixel.index+=alpha*indexes[j];
2192 }
cristyce70c172010-01-07 17:15:30 +00002193 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002194 }
2195 }
2196 else
2197 {
2198 MagickRealType
2199 gamma;
2200
2201 gamma=0.0;
2202 for (i=0; i < n; i++)
2203 {
2204 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2205 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002206 alpha=contribution[i].weight*QuantumScale*
2207 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002208 pixel.red+=alpha*(p+j)->red;
2209 pixel.green+=alpha*(p+j)->green;
2210 pixel.blue+=alpha*(p+j)->blue;
2211 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2212 gamma+=alpha;
2213 }
2214 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002215 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2216 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2217 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2218 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002219 if ((image->colorspace == CMYKColorspace) &&
2220 (resize_image->colorspace == CMYKColorspace))
2221 {
2222 for (i=0; i < n; i++)
2223 {
2224 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2225 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002226 alpha=contribution[i].weight*QuantumScale*
2227 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002228 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002229 }
cristyce70c172010-01-07 17:15:30 +00002230 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2231 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002232 }
2233 }
2234 if ((resize_image->storage_class == PseudoClass) &&
2235 (image->storage_class == PseudoClass))
2236 {
cristybb503372010-05-27 20:51:26 +00002237 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002238 1.0)+0.5);
2239 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2240 (contribution[i-start].pixel-contribution[0].pixel);
2241 resize_indexes[y]=indexes[j];
2242 }
2243 q++;
2244 }
2245 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2246 status=MagickFalse;
2247 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2248 {
2249 MagickBooleanType
2250 proceed;
2251
cristyb5d5f722009-11-04 03:03:49 +00002252#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002253 #pragma omp critical (MagickCore_HorizontalFilter)
2254#endif
cristy9af9b5d2010-08-15 17:04:28 +00002255 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002256 if (proceed == MagickFalse)
2257 status=MagickFalse;
2258 }
2259 }
2260 resize_view=DestroyCacheView(resize_view);
2261 image_view=DestroyCacheView(image_view);
2262 contributions=DestroyContributionThreadSet(contributions);
2263 return(status);
2264}
2265
2266static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2267 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002268 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002269{
cristyfa112112010-01-04 17:48:07 +00002270 CacheView
2271 *image_view,
2272 *resize_view;
2273
cristy3ed852e2009-09-05 21:47:34 +00002274 ClassType
2275 storage_class;
2276
2277 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002278 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002279
cristy3ed852e2009-09-05 21:47:34 +00002280 MagickBooleanType
2281 status;
2282
2283 MagickPixelPacket
2284 zero;
2285
2286 MagickRealType
2287 scale,
2288 support;
2289
cristy9af9b5d2010-08-15 17:04:28 +00002290 ssize_t
2291 y;
2292
cristy3ed852e2009-09-05 21:47:34 +00002293 /*
cristy9af9b5d2010-08-15 17:04:28 +00002294 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002295 */
cristy5d824382010-09-06 14:00:17 +00002296 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002297 support=scale*GetResizeFilterSupport(resize_filter);
2298 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2299 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2300 {
2301 InheritException(exception,&resize_image->exception);
2302 return(MagickFalse);
2303 }
2304 if (support < 0.5)
2305 {
2306 /*
nicolas07bac812010-09-19 18:47:02 +00002307 Support too small even for nearest neighbour: Reduce to point
2308 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002309 */
2310 support=(MagickRealType) 0.5;
2311 scale=1.0;
2312 }
2313 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2314 if (contributions == (ContributionInfo **) NULL)
2315 {
2316 (void) ThrowMagickException(exception,GetMagickModule(),
2317 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2318 return(MagickFalse);
2319 }
2320 status=MagickTrue;
2321 scale=1.0/scale;
2322 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2323 image_view=AcquireCacheView(image);
2324 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002325#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002326 #pragma omp parallel for shared(status)
2327#endif
cristybb503372010-05-27 20:51:26 +00002328 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002329 {
cristy3ed852e2009-09-05 21:47:34 +00002330 MagickRealType
2331 center,
2332 density;
2333
2334 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002335 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002336
2337 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002338 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002339
cristy03dbbd22010-09-19 23:04:47 +00002340 register ContributionInfo
2341 *restrict contribution;
2342
cristy3ed852e2009-09-05 21:47:34 +00002343 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002344 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002345
cristy9af9b5d2010-08-15 17:04:28 +00002346 register PixelPacket
2347 *restrict q;
2348
cristybb503372010-05-27 20:51:26 +00002349 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002350 x;
2351
cristy9af9b5d2010-08-15 17:04:28 +00002352 ssize_t
2353 n,
2354 start,
2355 stop;
cristy3ed852e2009-09-05 21:47:34 +00002356
2357 if (status == MagickFalse)
2358 continue;
cristy679e6962010-03-18 00:42:45 +00002359 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002360 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2361 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002362 density=0.0;
2363 contribution=contributions[GetOpenMPThreadId()];
2364 for (n=0; n < (stop-start); n++)
2365 {
2366 contribution[n].pixel=start+n;
2367 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2368 ((MagickRealType) (start+n)-center+0.5));
2369 density+=contribution[n].weight;
2370 }
2371 if ((density != 0.0) && (density != 1.0))
2372 {
cristybb503372010-05-27 20:51:26 +00002373 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002374 i;
2375
2376 /*
2377 Normalize.
2378 */
2379 density=1.0/density;
2380 for (i=0; i < n; i++)
2381 contribution[i].weight*=density;
2382 }
2383 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002384 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2385 exception);
cristy3ed852e2009-09-05 21:47:34 +00002386 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2387 exception);
2388 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2389 {
2390 status=MagickFalse;
2391 continue;
2392 }
2393 indexes=GetCacheViewVirtualIndexQueue(image_view);
2394 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002395 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002396 {
cristy3ed852e2009-09-05 21:47:34 +00002397 MagickPixelPacket
2398 pixel;
2399
2400 MagickRealType
2401 alpha;
2402
cristybb503372010-05-27 20:51:26 +00002403 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002404 i;
2405
cristy9af9b5d2010-08-15 17:04:28 +00002406 ssize_t
2407 j;
2408
cristy3ed852e2009-09-05 21:47:34 +00002409 pixel=zero;
2410 if (image->matte == MagickFalse)
2411 {
2412 for (i=0; i < n; i++)
2413 {
cristybb503372010-05-27 20:51:26 +00002414 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002415 image->columns+x);
2416 alpha=contribution[i].weight;
2417 pixel.red+=alpha*(p+j)->red;
2418 pixel.green+=alpha*(p+j)->green;
2419 pixel.blue+=alpha*(p+j)->blue;
2420 pixel.opacity+=alpha*(p+j)->opacity;
2421 }
cristyce70c172010-01-07 17:15:30 +00002422 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2423 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2424 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2425 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002426 if ((image->colorspace == CMYKColorspace) &&
2427 (resize_image->colorspace == CMYKColorspace))
2428 {
2429 for (i=0; i < n; i++)
2430 {
cristybb503372010-05-27 20:51:26 +00002431 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002432 image->columns+x);
2433 alpha=contribution[i].weight;
2434 pixel.index+=alpha*indexes[j];
2435 }
cristyce70c172010-01-07 17:15:30 +00002436 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002437 }
2438 }
2439 else
2440 {
2441 MagickRealType
2442 gamma;
2443
2444 gamma=0.0;
2445 for (i=0; i < n; i++)
2446 {
cristybb503372010-05-27 20:51:26 +00002447 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002448 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002449 alpha=contribution[i].weight*QuantumScale*
2450 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002451 pixel.red+=alpha*(p+j)->red;
2452 pixel.green+=alpha*(p+j)->green;
2453 pixel.blue+=alpha*(p+j)->blue;
2454 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2455 gamma+=alpha;
2456 }
2457 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002458 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2459 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2460 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2461 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002462 if ((image->colorspace == CMYKColorspace) &&
2463 (resize_image->colorspace == CMYKColorspace))
2464 {
2465 for (i=0; i < n; i++)
2466 {
cristybb503372010-05-27 20:51:26 +00002467 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002468 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002469 alpha=contribution[i].weight*QuantumScale*
2470 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002471 pixel.index+=alpha*indexes[j];
2472 }
cristyce70c172010-01-07 17:15:30 +00002473 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2474 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002475 }
2476 }
2477 if ((resize_image->storage_class == PseudoClass) &&
2478 (image->storage_class == PseudoClass))
2479 {
cristybb503372010-05-27 20:51:26 +00002480 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002481 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002482 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002483 image->columns+x);
2484 resize_indexes[x]=indexes[j];
2485 }
2486 q++;
2487 }
2488 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2489 status=MagickFalse;
2490 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2491 {
2492 MagickBooleanType
2493 proceed;
2494
cristyb5d5f722009-11-04 03:03:49 +00002495#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002496 #pragma omp critical (MagickCore_VerticalFilter)
2497#endif
cristy9af9b5d2010-08-15 17:04:28 +00002498 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002499 if (proceed == MagickFalse)
2500 status=MagickFalse;
2501 }
2502 }
2503 resize_view=DestroyCacheView(resize_view);
2504 image_view=DestroyCacheView(image_view);
2505 contributions=DestroyContributionThreadSet(contributions);
2506 return(status);
2507}
2508
cristybb503372010-05-27 20:51:26 +00002509MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2510 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002511 ExceptionInfo *exception)
2512{
2513#define WorkLoadFactor 0.265
2514
2515 FilterTypes
2516 filter_type;
2517
2518 Image
2519 *filter_image,
2520 *resize_image;
2521
cristy9af9b5d2010-08-15 17:04:28 +00002522 MagickOffsetType
2523 offset;
2524
cristy3ed852e2009-09-05 21:47:34 +00002525 MagickRealType
2526 x_factor,
2527 y_factor;
2528
2529 MagickSizeType
2530 span;
2531
2532 MagickStatusType
2533 status;
2534
2535 ResizeFilter
2536 *resize_filter;
2537
cristy3ed852e2009-09-05 21:47:34 +00002538 /*
2539 Acquire resize image.
2540 */
2541 assert(image != (Image *) NULL);
2542 assert(image->signature == MagickSignature);
2543 if (image->debug != MagickFalse)
2544 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2545 assert(exception != (ExceptionInfo *) NULL);
2546 assert(exception->signature == MagickSignature);
2547 if ((columns == 0) || (rows == 0))
2548 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2549 if ((columns == image->columns) && (rows == image->rows) &&
2550 (filter == UndefinedFilter) && (blur == 1.0))
2551 return(CloneImage(image,0,0,MagickTrue,exception));
2552 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2553 if (resize_image == (Image *) NULL)
2554 return(resize_image);
2555 /*
2556 Acquire resize filter.
2557 */
2558 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2559 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2560 if ((x_factor*y_factor) > WorkLoadFactor)
2561 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2562 else
2563 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2564 if (filter_image == (Image *) NULL)
2565 return(DestroyImage(resize_image));
2566 filter_type=LanczosFilter;
2567 if (filter != UndefinedFilter)
2568 filter_type=filter;
2569 else
2570 if ((x_factor == 1.0) && (y_factor == 1.0))
2571 filter_type=PointFilter;
2572 else
2573 if ((image->storage_class == PseudoClass) ||
2574 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2575 filter_type=MitchellFilter;
2576 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2577 exception);
2578 /*
2579 Resize image.
2580 */
cristy9af9b5d2010-08-15 17:04:28 +00002581 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002582 if ((x_factor*y_factor) > WorkLoadFactor)
2583 {
2584 span=(MagickSizeType) (filter_image->columns+rows);
2585 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002586 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002587 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002588 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002589 }
2590 else
2591 {
2592 span=(MagickSizeType) (filter_image->rows+columns);
2593 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002594 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002595 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002596 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002597 }
2598 /*
2599 Free resources.
2600 */
2601 filter_image=DestroyImage(filter_image);
2602 resize_filter=DestroyResizeFilter(resize_filter);
2603 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2604 return((Image *) NULL);
2605 resize_image->type=image->type;
2606 return(resize_image);
2607}
2608
2609/*
2610%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2611% %
2612% %
2613% %
2614% S a m p l e I m a g e %
2615% %
2616% %
2617% %
2618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2619%
2620% SampleImage() scales an image to the desired dimensions with pixel
2621% sampling. Unlike other scaling methods, this method does not introduce
2622% any additional color into the scaled image.
2623%
2624% The format of the SampleImage method is:
2625%
cristybb503372010-05-27 20:51:26 +00002626% Image *SampleImage(const Image *image,const size_t columns,
2627% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002628%
2629% A description of each parameter follows:
2630%
2631% o image: the image.
2632%
2633% o columns: the number of columns in the sampled image.
2634%
2635% o rows: the number of rows in the sampled image.
2636%
2637% o exception: return any errors or warnings in this structure.
2638%
2639*/
cristybb503372010-05-27 20:51:26 +00002640MagickExport Image *SampleImage(const Image *image,const size_t columns,
2641 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002642{
2643#define SampleImageTag "Sample/Image"
2644
cristyc4c8d132010-01-07 01:58:38 +00002645 CacheView
2646 *image_view,
2647 *sample_view;
2648
cristy3ed852e2009-09-05 21:47:34 +00002649 Image
2650 *sample_image;
2651
cristy3ed852e2009-09-05 21:47:34 +00002652 MagickBooleanType
2653 status;
2654
cristy5f959472010-05-27 22:19:46 +00002655 MagickOffsetType
2656 progress;
2657
cristybb503372010-05-27 20:51:26 +00002658 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002659 x;
2660
cristy5f959472010-05-27 22:19:46 +00002661 ssize_t
2662 *x_offset,
2663 y;
2664
cristy3ed852e2009-09-05 21:47:34 +00002665 /*
2666 Initialize sampled image attributes.
2667 */
2668 assert(image != (const Image *) NULL);
2669 assert(image->signature == MagickSignature);
2670 if (image->debug != MagickFalse)
2671 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2672 assert(exception != (ExceptionInfo *) NULL);
2673 assert(exception->signature == MagickSignature);
2674 if ((columns == 0) || (rows == 0))
2675 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2676 if ((columns == image->columns) && (rows == image->rows))
2677 return(CloneImage(image,0,0,MagickTrue,exception));
2678 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2679 if (sample_image == (Image *) NULL)
2680 return((Image *) NULL);
2681 /*
2682 Allocate scan line buffer and column offset buffers.
2683 */
cristybb503372010-05-27 20:51:26 +00002684 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002685 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002686 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002687 {
2688 sample_image=DestroyImage(sample_image);
2689 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2690 }
cristybb503372010-05-27 20:51:26 +00002691 for (x=0; x < (ssize_t) sample_image->columns; x++)
2692 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002693 sample_image->columns);
2694 /*
2695 Sample each row.
2696 */
2697 status=MagickTrue;
2698 progress=0;
2699 image_view=AcquireCacheView(image);
2700 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002701#if defined(MAGICKCORE_OPENMP_SUPPORT)
2702 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002703#endif
cristybb503372010-05-27 20:51:26 +00002704 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002705 {
cristy3ed852e2009-09-05 21:47:34 +00002706 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002707 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002708
2709 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002710 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002711
2712 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002713 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002714
cristy3ed852e2009-09-05 21:47:34 +00002715 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002716 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002717
cristy03dbbd22010-09-19 23:04:47 +00002718 register ssize_t
2719 x;
2720
cristy9af9b5d2010-08-15 17:04:28 +00002721 ssize_t
2722 y_offset;
2723
cristy3ed852e2009-09-05 21:47:34 +00002724 if (status == MagickFalse)
2725 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002726 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2727 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002728 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2729 exception);
2730 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2731 exception);
2732 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2733 {
2734 status=MagickFalse;
2735 continue;
2736 }
2737 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2738 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2739 /*
2740 Sample each column.
2741 */
cristybb503372010-05-27 20:51:26 +00002742 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002743 *q++=p[x_offset[x]];
2744 if ((image->storage_class == PseudoClass) ||
2745 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002746 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002747 sample_indexes[x]=indexes[x_offset[x]];
2748 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2749 status=MagickFalse;
2750 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2751 {
2752 MagickBooleanType
2753 proceed;
2754
cristyb5d5f722009-11-04 03:03:49 +00002755#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002756 #pragma omp critical (MagickCore_SampleImage)
2757#endif
2758 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2759 if (proceed == MagickFalse)
2760 status=MagickFalse;
2761 }
2762 }
2763 image_view=DestroyCacheView(image_view);
2764 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002765 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002766 sample_image->type=image->type;
2767 return(sample_image);
2768}
2769
2770/*
2771%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2772% %
2773% %
2774% %
2775% S c a l e I m a g e %
2776% %
2777% %
2778% %
2779%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2780%
2781% ScaleImage() changes the size of an image to the given dimensions.
2782%
2783% The format of the ScaleImage method is:
2784%
cristybb503372010-05-27 20:51:26 +00002785% Image *ScaleImage(const Image *image,const size_t columns,
2786% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002787%
2788% A description of each parameter follows:
2789%
2790% o image: the image.
2791%
2792% o columns: the number of columns in the scaled image.
2793%
2794% o rows: the number of rows in the scaled image.
2795%
2796% o exception: return any errors or warnings in this structure.
2797%
2798*/
cristybb503372010-05-27 20:51:26 +00002799MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2800 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002801{
2802#define ScaleImageTag "Scale/Image"
2803
cristyed6cb232010-01-20 03:07:53 +00002804 CacheView
2805 *image_view,
2806 *scale_view;
2807
cristy3ed852e2009-09-05 21:47:34 +00002808 Image
2809 *scale_image;
2810
cristy3ed852e2009-09-05 21:47:34 +00002811 MagickBooleanType
2812 next_column,
2813 next_row,
2814 proceed;
2815
2816 MagickPixelPacket
2817 pixel,
2818 *scale_scanline,
2819 *scanline,
2820 *x_vector,
2821 *y_vector,
2822 zero;
2823
cristy3ed852e2009-09-05 21:47:34 +00002824 PointInfo
2825 scale,
2826 span;
2827
cristybb503372010-05-27 20:51:26 +00002828 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002829 i;
2830
cristy9af9b5d2010-08-15 17:04:28 +00002831 ssize_t
2832 number_rows,
2833 y;
2834
cristy3ed852e2009-09-05 21:47:34 +00002835 /*
2836 Initialize scaled image attributes.
2837 */
2838 assert(image != (const Image *) NULL);
2839 assert(image->signature == MagickSignature);
2840 if (image->debug != MagickFalse)
2841 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2842 assert(exception != (ExceptionInfo *) NULL);
2843 assert(exception->signature == MagickSignature);
2844 if ((columns == 0) || (rows == 0))
2845 return((Image *) NULL);
2846 if ((columns == image->columns) && (rows == image->rows))
2847 return(CloneImage(image,0,0,MagickTrue,exception));
2848 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2849 if (scale_image == (Image *) NULL)
2850 return((Image *) NULL);
2851 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2852 {
2853 InheritException(exception,&scale_image->exception);
2854 scale_image=DestroyImage(scale_image);
2855 return((Image *) NULL);
2856 }
2857 /*
2858 Allocate memory.
2859 */
2860 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2861 sizeof(*x_vector));
2862 scanline=x_vector;
2863 if (image->rows != scale_image->rows)
2864 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2865 sizeof(*scanline));
2866 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2867 scale_image->columns,sizeof(*scale_scanline));
2868 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2869 sizeof(*y_vector));
2870 if ((scanline == (MagickPixelPacket *) NULL) ||
2871 (scale_scanline == (MagickPixelPacket *) NULL) ||
2872 (x_vector == (MagickPixelPacket *) NULL) ||
2873 (y_vector == (MagickPixelPacket *) NULL))
2874 {
2875 scale_image=DestroyImage(scale_image);
2876 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2877 }
2878 /*
2879 Scale image.
2880 */
2881 number_rows=0;
2882 next_row=MagickTrue;
2883 span.y=1.0;
2884 scale.y=(double) scale_image->rows/(double) image->rows;
2885 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2886 sizeof(*y_vector));
2887 GetMagickPixelPacket(image,&pixel);
2888 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2889 i=0;
cristyed6cb232010-01-20 03:07:53 +00002890 image_view=AcquireCacheView(image);
2891 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002892 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002893 {
2894 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002895 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002896
2897 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002898 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002899
2900 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002901 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002902
cristy3ed852e2009-09-05 21:47:34 +00002903 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002904 *restrict s,
2905 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002906
2907 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002908 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002909
cristy9af9b5d2010-08-15 17:04:28 +00002910 register ssize_t
2911 x;
2912
cristyed6cb232010-01-20 03:07:53 +00002913 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2914 exception);
cristy3ed852e2009-09-05 21:47:34 +00002915 if (q == (PixelPacket *) NULL)
2916 break;
2917 scale_indexes=GetAuthenticIndexQueue(scale_image);
2918 if (scale_image->rows == image->rows)
2919 {
2920 /*
2921 Read a new scanline.
2922 */
cristyed6cb232010-01-20 03:07:53 +00002923 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2924 exception);
cristy3ed852e2009-09-05 21:47:34 +00002925 if (p == (const PixelPacket *) NULL)
2926 break;
cristyed6cb232010-01-20 03:07:53 +00002927 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002928 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002929 {
cristyce70c172010-01-07 17:15:30 +00002930 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2931 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2932 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002933 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002934 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002935 if (indexes != (IndexPacket *) NULL)
2936 x_vector[x].index=(MagickRealType) indexes[x];
2937 p++;
2938 }
2939 }
2940 else
2941 {
2942 /*
2943 Scale Y direction.
2944 */
2945 while (scale.y < span.y)
2946 {
cristy9af9b5d2010-08-15 17:04:28 +00002947 if ((next_row != MagickFalse) &&
2948 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002949 {
2950 /*
2951 Read a new scanline.
2952 */
cristyed6cb232010-01-20 03:07:53 +00002953 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2954 exception);
cristy3ed852e2009-09-05 21:47:34 +00002955 if (p == (const PixelPacket *) NULL)
2956 break;
cristyed6cb232010-01-20 03:07:53 +00002957 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002958 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002959 {
cristyce70c172010-01-07 17:15:30 +00002960 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2961 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2962 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002963 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002964 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002965 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002966 if (indexes != (IndexPacket *) NULL)
2967 x_vector[x].index=(MagickRealType) indexes[x];
2968 p++;
2969 }
2970 number_rows++;
2971 }
cristybb503372010-05-27 20:51:26 +00002972 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002973 {
2974 y_vector[x].red+=scale.y*x_vector[x].red;
2975 y_vector[x].green+=scale.y*x_vector[x].green;
2976 y_vector[x].blue+=scale.y*x_vector[x].blue;
2977 if (scale_image->matte != MagickFalse)
2978 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2979 if (scale_indexes != (IndexPacket *) NULL)
2980 y_vector[x].index+=scale.y*x_vector[x].index;
2981 }
2982 span.y-=scale.y;
2983 scale.y=(double) scale_image->rows/(double) image->rows;
2984 next_row=MagickTrue;
2985 }
cristybb503372010-05-27 20:51:26 +00002986 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002987 {
2988 /*
2989 Read a new scanline.
2990 */
cristyed6cb232010-01-20 03:07:53 +00002991 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2992 exception);
cristy3ed852e2009-09-05 21:47:34 +00002993 if (p == (const PixelPacket *) NULL)
2994 break;
cristyed6cb232010-01-20 03:07:53 +00002995 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002996 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002997 {
cristyce70c172010-01-07 17:15:30 +00002998 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2999 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
3000 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003001 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00003002 x_vector[x].opacity=(MagickRealType)
3003 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003004 if (indexes != (IndexPacket *) NULL)
3005 x_vector[x].index=(MagickRealType) indexes[x];
3006 p++;
3007 }
3008 number_rows++;
3009 next_row=MagickFalse;
3010 }
3011 s=scanline;
cristybb503372010-05-27 20:51:26 +00003012 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003013 {
3014 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3015 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3016 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3017 if (image->matte != MagickFalse)
3018 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3019 if (scale_indexes != (IndexPacket *) NULL)
3020 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3021 s->red=pixel.red;
3022 s->green=pixel.green;
3023 s->blue=pixel.blue;
3024 if (scale_image->matte != MagickFalse)
3025 s->opacity=pixel.opacity;
3026 if (scale_indexes != (IndexPacket *) NULL)
3027 s->index=pixel.index;
3028 s++;
3029 y_vector[x]=zero;
3030 }
3031 scale.y-=span.y;
3032 if (scale.y <= 0)
3033 {
3034 scale.y=(double) scale_image->rows/(double) image->rows;
3035 next_row=MagickTrue;
3036 }
3037 span.y=1.0;
3038 }
3039 if (scale_image->columns == image->columns)
3040 {
3041 /*
3042 Transfer scanline to scaled image.
3043 */
3044 s=scanline;
cristybb503372010-05-27 20:51:26 +00003045 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003046 {
cristyce70c172010-01-07 17:15:30 +00003047 q->red=ClampToQuantum(s->red);
3048 q->green=ClampToQuantum(s->green);
3049 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003050 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003051 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003052 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003053 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003054 q++;
3055 s++;
3056 }
3057 }
3058 else
3059 {
3060 /*
3061 Scale X direction.
3062 */
3063 pixel=zero;
3064 next_column=MagickFalse;
3065 span.x=1.0;
3066 s=scanline;
3067 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003068 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003069 {
3070 scale.x=(double) scale_image->columns/(double) image->columns;
3071 while (scale.x >= span.x)
3072 {
3073 if (next_column != MagickFalse)
3074 {
3075 pixel=zero;
3076 t++;
3077 }
3078 pixel.red+=span.x*s->red;
3079 pixel.green+=span.x*s->green;
3080 pixel.blue+=span.x*s->blue;
3081 if (image->matte != MagickFalse)
3082 pixel.opacity+=span.x*s->opacity;
3083 if (scale_indexes != (IndexPacket *) NULL)
3084 pixel.index+=span.x*s->index;
3085 t->red=pixel.red;
3086 t->green=pixel.green;
3087 t->blue=pixel.blue;
3088 if (scale_image->matte != MagickFalse)
3089 t->opacity=pixel.opacity;
3090 if (scale_indexes != (IndexPacket *) NULL)
3091 t->index=pixel.index;
3092 scale.x-=span.x;
3093 span.x=1.0;
3094 next_column=MagickTrue;
3095 }
3096 if (scale.x > 0)
3097 {
3098 if (next_column != MagickFalse)
3099 {
3100 pixel=zero;
3101 next_column=MagickFalse;
3102 t++;
3103 }
3104 pixel.red+=scale.x*s->red;
3105 pixel.green+=scale.x*s->green;
3106 pixel.blue+=scale.x*s->blue;
3107 if (scale_image->matte != MagickFalse)
3108 pixel.opacity+=scale.x*s->opacity;
3109 if (scale_indexes != (IndexPacket *) NULL)
3110 pixel.index+=scale.x*s->index;
3111 span.x-=scale.x;
3112 }
3113 s++;
3114 }
3115 if (span.x > 0)
3116 {
3117 s--;
3118 pixel.red+=span.x*s->red;
3119 pixel.green+=span.x*s->green;
3120 pixel.blue+=span.x*s->blue;
3121 if (scale_image->matte != MagickFalse)
3122 pixel.opacity+=span.x*s->opacity;
3123 if (scale_indexes != (IndexPacket *) NULL)
3124 pixel.index+=span.x*s->index;
3125 }
3126 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003127 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003128 {
3129 t->red=pixel.red;
3130 t->green=pixel.green;
3131 t->blue=pixel.blue;
3132 if (scale_image->matte != MagickFalse)
3133 t->opacity=pixel.opacity;
3134 if (scale_indexes != (IndexPacket *) NULL)
3135 t->index=pixel.index;
3136 }
3137 /*
3138 Transfer scanline to scaled image.
3139 */
3140 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003141 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003142 {
cristyce70c172010-01-07 17:15:30 +00003143 q->red=ClampToQuantum(t->red);
3144 q->green=ClampToQuantum(t->green);
3145 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003146 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003147 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003148 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003149 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003150 t++;
3151 q++;
3152 }
3153 }
cristyed6cb232010-01-20 03:07:53 +00003154 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003155 break;
cristy96b16132010-08-29 17:19:52 +00003156 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3157 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003158 if (proceed == MagickFalse)
3159 break;
3160 }
cristyed6cb232010-01-20 03:07:53 +00003161 scale_view=DestroyCacheView(scale_view);
3162 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003163 /*
3164 Free allocated memory.
3165 */
3166 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3167 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3168 if (scale_image->rows != image->rows)
3169 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3170 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3171 scale_image->type=image->type;
3172 return(scale_image);
3173}
3174
anthony02b4cb42010-10-10 04:54:35 +00003175#if 0
3176 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003177/*
3178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3179% %
3180% %
3181% %
3182+ S e t R e s i z e F i l t e r S u p p o r t %
3183% %
3184% %
3185% %
3186%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3187%
3188% SetResizeFilterSupport() specifies which IR filter to use to window
3189%
3190% The format of the SetResizeFilterSupport method is:
3191%
3192% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3193% const MagickRealType support)
3194%
3195% A description of each parameter follows:
3196%
3197% o resize_filter: the resize filter.
3198%
3199% o support: the filter spport radius.
3200%
3201*/
3202MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3203 const MagickRealType support)
3204{
3205 assert(resize_filter != (ResizeFilter *) NULL);
3206 assert(resize_filter->signature == MagickSignature);
3207 resize_filter->support=support;
3208}
anthony02b4cb42010-10-10 04:54:35 +00003209#endif
cristy3ed852e2009-09-05 21:47:34 +00003210
3211/*
3212%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3213% %
3214% %
3215% %
3216% T h u m b n a i l I m a g e %
3217% %
3218% %
3219% %
3220%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3221%
3222% ThumbnailImage() changes the size of an image to the given dimensions and
3223% removes any associated profiles. The goal is to produce small low cost
3224% thumbnail images suited for display on the Web.
3225%
3226% The format of the ThumbnailImage method is:
3227%
cristybb503372010-05-27 20:51:26 +00003228% Image *ThumbnailImage(const Image *image,const size_t columns,
3229% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003230%
3231% A description of each parameter follows:
3232%
3233% o image: the image.
3234%
3235% o columns: the number of columns in the scaled image.
3236%
3237% o rows: the number of rows in the scaled image.
3238%
3239% o exception: return any errors or warnings in this structure.
3240%
3241*/
cristy9af9b5d2010-08-15 17:04:28 +00003242MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3243 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003244{
3245#define SampleFactor 5
3246
3247 char
3248 value[MaxTextExtent];
3249
3250 const char
3251 *name;
3252
3253 Image
3254 *thumbnail_image;
3255
3256 MagickRealType
3257 x_factor,
3258 y_factor;
3259
cristybb503372010-05-27 20:51:26 +00003260 size_t
cristy3ed852e2009-09-05 21:47:34 +00003261 version;
3262
cristy9af9b5d2010-08-15 17:04:28 +00003263 struct stat
3264 attributes;
3265
cristy3ed852e2009-09-05 21:47:34 +00003266 assert(image != (Image *) NULL);
3267 assert(image->signature == MagickSignature);
3268 if (image->debug != MagickFalse)
3269 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3270 assert(exception != (ExceptionInfo *) NULL);
3271 assert(exception->signature == MagickSignature);
3272 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3273 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3274 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003275 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3276 exception);
cristy3ed852e2009-09-05 21:47:34 +00003277 else
3278 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003279 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3280 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003281 else
3282 {
3283 Image
3284 *sample_image;
3285
3286 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3287 exception);
3288 if (sample_image == (Image *) NULL)
3289 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003290 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3291 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003292 sample_image=DestroyImage(sample_image);
3293 }
3294 if (thumbnail_image == (Image *) NULL)
3295 return(thumbnail_image);
3296 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3297 if (thumbnail_image->matte == MagickFalse)
3298 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3299 thumbnail_image->depth=8;
3300 thumbnail_image->interlace=NoInterlace;
3301 /*
3302 Strip all profiles except color profiles.
3303 */
3304 ResetImageProfileIterator(thumbnail_image);
3305 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3306 {
3307 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3308 {
cristy2b726bd2010-01-11 01:05:39 +00003309 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003310 ResetImageProfileIterator(thumbnail_image);
3311 }
3312 name=GetNextImageProfile(thumbnail_image);
3313 }
3314 (void) DeleteImageProperty(thumbnail_image,"comment");
3315 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003316 if (strstr(image->magick_filename,"//") == (char *) NULL)
3317 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003318 image->magick_filename);
3319 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3320 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3321 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3322 {
cristye8c25f92010-06-03 00:53:06 +00003323 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003324 attributes.st_mtime);
3325 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3326 }
cristye8c25f92010-06-03 00:53:06 +00003327 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003328 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003329 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003330 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003331 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3332 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3333 LocaleLower(value);
3334 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3335 (void) SetImageProperty(thumbnail_image,"software",
3336 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003337 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3338 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003339 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003340 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003341 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003342 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003343 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3344 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003345 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3346 return(thumbnail_image);
3347}