blob: ba89bc0cb79fab69845113dc2f20502952b30267 [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%
nicolas3061b8a2010-10-22 16:34:52 +0000552% 'EXPERT' OPTIONS:
nicolasce6dc292010-10-22 16:23:07 +0000553%
nicolas3061b8a2010-10-22 16:34:52 +0000554% (Not recommended without expert knowledge of resampling and
555% filtering.)
556%
557% You can override any and all filter settings. Use "filter:verbose"
558% to make sure that the overall effect of your selections is as
559% expected.
560%
561% "filter:verbose" Output the exact results of the filter
562% selections made, as well as plotting data for graphing the
563% resulting filter over support range (blur adjusted).
cristy3ed852e2009-09-05 21:47:34 +0000564%
anthony48f77622010-10-03 14:32:31 +0000565% "filter:filter" Select the main function associated with
566% this filter name, as the weighting function of the filter.
567% This can be used to set a windowing function as a weighting
568% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000569%
nicolas3061b8a2010-10-22 16:34:52 +0000570% If a "filter:window" operation has not been provided, then a
571% 'Box' windowing function will be set to denote that no
572% windowing function is being used.
cristy3ed852e2009-09-05 21:47:34 +0000573%
nicolas3061b8a2010-10-22 16:34:52 +0000574% "filter:window" Select this windowing function for the filter.
575% While any filter could be used as a windowing function, using
576% the 'first lobe' of that filter over the whole support
577% window, using a non-windowing function is not advisible. If
578% no weighting filter function is specifed a 'SincFast' filter
579% will be used.
cristy3ed852e2009-09-05 21:47:34 +0000580%
nicolas3061b8a2010-10-22 16:34:52 +0000581% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
582% This a simpler method of setting filter support size that
583% will correctly handle the Sinc/Jinc switch for an operators
584% filtering requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000585%
nicolas3061b8a2010-10-22 16:34:52 +0000586% "filter:support" Set the support size for filtering to the size
587% given This not recommended for Sinc/Jinc windowed filters
588% (lobes should be used instead). This will override any
589% 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000590%
nicolas3061b8a2010-10-22 16:34:52 +0000591% "filter:win-support" Scale windowing function to this size
592% instead. This causes the windowing (or self-windowing
593% Lagrange filter) to act is if the support window it much much
594% larger than what is actually supplied to the calling
595% operator. The filter however is still clipped to the real
596% support size given, by the support range suppiled to the
597% caller. If unset this will equal the normal filter support
anthonyb6d08c52010-09-13 01:17:04 +0000598% size.
599%
nicolas3061b8a2010-10-22 16:34:52 +0000600% "filter:blur" Scale the filter and support window by this amount.
cristy3ed852e2009-09-05 21:47:34 +0000601% A value >1 will generally result in a more burred image with
602% more ringing effects, while a value <1 will sharpen the
603% resulting image with more aliasing and Morie effects.
604%
nicolas3061b8a2010-10-22 16:34:52 +0000605% "filter:sigma" The sigma value to use for the Gaussian filter
606% only. Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for
607% cylindrical usage. It effectially provides a alturnative to
608% 'blur' for Gaussians without it also effecting the final
609% 'practical support' size.
anthonyf5e76ef2010-10-12 01:22:01 +0000610%
cristy3ed852e2009-09-05 21:47:34 +0000611% "filter:b"
nicolas3061b8a2010-10-22 16:34:52 +0000612% "filter:c" Override the preset B,C values for a Cubic type of
613% filter If only one of these are given it is assumes to be a
614% 'Keys' type of filter such that B+2C=1, where Keys 'alpha'
615% value = C
cristy3ed852e2009-09-05 21:47:34 +0000616%
nicolas3061b8a2010-10-22 16:34:52 +0000617% Examples:
cristy3ed852e2009-09-05 21:47:34 +0000618%
nicolas6e1267a2010-10-22 16:35:52 +0000619% Set a true un-windowed Sinc filter with 10 lobes (very slow):
anthony48f77622010-10-03 14:32:31 +0000620% -define filter:filter=Sinc
621% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000622%
nicolas6e1267a2010-10-22 16:35:52 +0000623% Set an 8 lobe Lanczos (Sinc or Jinc) filter:
cristy3ed852e2009-09-05 21:47:34 +0000624% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000625% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000626%
cristy3ed852e2009-09-05 21:47:34 +0000627% The format of the AcquireResizeFilter method is:
628%
629% ResizeFilter *AcquireResizeFilter(const Image *image,
630% const FilterTypes filter_type, const MagickBooleanType radial,
631% ExceptionInfo *exception)
632%
cristy33b1c162010-01-23 22:51:51 +0000633% A description of each parameter follows:
634%
cristy3ed852e2009-09-05 21:47:34 +0000635% o image: the image.
636%
nicolas07bac812010-09-19 18:47:02 +0000637% o filter: the filter type, defining a preset filter, window and
638% support. The artifact settings listed above will override
639% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000640%
anthony48f77622010-10-03 14:32:31 +0000641% o blur: blur the filter by this amount, use 1.0 if unknown. Image
642% artifact "filter:blur" will override this API call usage, including
643% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000644%
anthony48f77622010-10-03 14:32:31 +0000645% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
646% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000647%
648% o exception: return any errors or warnings in this structure.
649%
650*/
651MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000652 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000653 const MagickBooleanType cylindrical,ExceptionInfo *exception)
654{
655 const char
656 *artifact;
657
658 FilterTypes
659 filter_type,
660 window_type;
661
cristy3ed852e2009-09-05 21:47:34 +0000662 MagickRealType
663 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000664 C,
665 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000666
667 register ResizeFilter
668 *resize_filter;
669
cristy9af9b5d2010-08-15 17:04:28 +0000670 ssize_t
671 option;
672
cristy3ed852e2009-09-05 21:47:34 +0000673 /*
anthony48f77622010-10-03 14:32:31 +0000674 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000675 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000676 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
677 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
678 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000679
nicolas07bac812010-09-19 18:47:02 +0000680 WARNING: The order of this tabel must match the order of the
681 FilterTypes enumeration specified in "resample.h", or the filter
682 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000683
684 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000685 */
686 static struct
687 {
688 FilterTypes
689 filter,
690 window;
691 } const mapping[SentinelFilter] =
692 {
anthony462ee072010-09-27 12:34:02 +0000693 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
694 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
695 { BoxFilter, BoxFilter }, /* Box averaging filter */
696 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
697 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
698 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
699 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
700 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
701 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
702 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
703 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
704 { CatromFilter, BoxFilter }, /* Cubic interpolator */
705 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
706 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
anthony48f77622010-10-03 14:32:31 +0000707 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
708 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony462ee072010-09-27 12:34:02 +0000709 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
710 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
711 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
712 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
713 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
714 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
715 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolas5835ee02010-10-10 20:40:15 +0000716 { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
anthonye5f06452010-10-12 05:48:17 +0000717 { Lanczos2DSharpFilter, JincFilter },/* SPECIAL: ditto sharpened */
718 { RobidouxFilter, BoxFilter }, /* SPECIAL: Keys cubic tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000719 };
720 /*
nicolas32f44eb2010-09-20 01:23:12 +0000721 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000722 function. The default support size for that filter as a weighting
723 function, the range to scale with to use that function as a sinc
724 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000725
anthony07a3f7f2010-09-16 03:03:11 +0000726 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000727 SincFast(), and CubicBC() functions, which may have multiple
728 filter to function associations.
729
730 See "filter:verbose" handling below for the function -> filter
731 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000732 */
733 static struct
734 {
735 MagickRealType
736 (*function)(const MagickRealType, const ResizeFilter*),
nicolas8eccc162010-10-16 19:48:13 +0000737 lobes, /* Default lobes/support size of the weighting filter. */
anthony450db502010-10-19 04:03:03 +0000738 scale, /* Support when function used as a windowing function
739 Typically equal to the location of the first zero crossing. */
740 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
cristy3ed852e2009-09-05 21:47:34 +0000741 } const filters[SentinelFilter] =
742 {
anthony61b5ddd2010-10-05 02:33:31 +0000743 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
744 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
745 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
746 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
747 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
748 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
749 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
750 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000751 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000752 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
753 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
754 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
anthony853d6972010-10-08 06:01:31 +0000755 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
756 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
nicolasf8689f42010-10-18 16:14:08 +0000757 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000758 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
759 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
760 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
761 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
762 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
763 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
764 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
765 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
nicolas8eccc162010-10-16 19:48:13 +0000766 { Jinc, 2.0, 1.2196698912665045, 0.0, 0.0 },
767 /* Lanczos2D (Jinc-Jinc) */
768 { Jinc, 2.0, 1.1684849904329952, 0.0, 0.0 },
769 /* Lanczos2D sharpened with blur=0.958033808 */
anthony450db502010-10-19 04:03:03 +0000770 { CubicBC, 2.0, 1.1685777620836932,
nicolas0d5e5322010-10-22 15:29:30 +0000771 0.37821575509399867, 0.31089212245300067 }
772 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
cristy3ed852e2009-09-05 21:47:34 +0000773 };
774 /*
anthony9a98fc62010-10-11 02:47:19 +0000775 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
anthony450db502010-10-19 04:03:03 +0000776 function being used as a filter. It is used by the "filter:lobes" expert
777 setting and for 'lobes' for Jinc functions in the previous table. This
778 way users do not have to deal with the highly irrational lobe sizes of the
anthony9a98fc62010-10-11 02:47:19 +0000779 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000780
nicolase473f722010-10-07 00:05:13 +0000781 Values taken from
anthony48f77622010-10-03 14:32:31 +0000782 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000783 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000784 */
785 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000786 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000787 {
nicolas8eccc162010-10-16 19:48:13 +0000788 1.2196698912665045,
789 2.2331305943815286,
790 3.2383154841662362,
791 4.2410628637960699,
792 5.2427643768701817,
793 6.2439216898644877,
794 7.244759868719957,
795 8.2453949139520427,
796 9.2458926849494673,
797 10.246293348754916,
798 11.246622794877883,
799 12.246898461138105,
800 13.247132522181061,
801 14.247333735806849,
anthonyc2d07db2010-09-15 23:47:40 +0000802 15.2475085630373,
nicolas8eccc162010-10-16 19:48:13 +0000803 16.247661874700962
cristy3ed852e2009-09-05 21:47:34 +0000804 };
805
cristy33b1c162010-01-23 22:51:51 +0000806 /*
807 Allocate resize filter.
808 */
cristy3ed852e2009-09-05 21:47:34 +0000809 assert(image != (const Image *) NULL);
810 assert(image->signature == MagickSignature);
811 if (image->debug != MagickFalse)
812 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
813 assert(UndefinedFilter < filter && filter < SentinelFilter);
814 assert(exception != (ExceptionInfo *) NULL);
815 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000816 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000817 if (resize_filter == (ResizeFilter *) NULL)
818 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000819 /*
820 Defaults for the requested filter.
821 */
822 filter_type=mapping[filter].filter;
823 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000824 resize_filter->blur = blur;
825 sigma = 0.5;
anthony48f77622010-10-03 14:32:31 +0000826 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000827 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000828 switch (filter_type)
829 {
830 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000831 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000832 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000833 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000834 break;
anthonyba5a7c32010-09-15 02:42:25 +0000835 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000836 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000837 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000838 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000839 break;
cristy33b1c162010-01-23 22:51:51 +0000840 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000841 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000842 filter_type=JincFilter;
843 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000844 break;
anthony08958462010-10-12 06:48:35 +0000845 case Lanczos2DSharpFilter:
nicolas1f4c0512010-10-20 20:19:30 +0000846 /* Sharpened by Nicolas Robidoux so as to optimize for minimal
847 * blurring of orthogonal lines
anthony08958462010-10-12 06:48:35 +0000848 */
849 resize_filter->blur *= 0.958033808;
850 break;
anthonyf5e76ef2010-10-12 01:22:01 +0000851 case GaussianFilter:
cristy68f103e2010-10-14 01:19:07 +0000852 sigma = (MagickRealType) (MagickSQ2/2.0); /* Cylindrical Gaussian sigma is sqrt(2)/2 */
anthonyf5e76ef2010-10-12 01:22:01 +0000853 break;
cristya782ecf2010-01-25 02:59:14 +0000854 default:
855 break;
cristy3ed852e2009-09-05 21:47:34 +0000856 }
anthony61b5ddd2010-10-05 02:33:31 +0000857 else
858 switch (filter_type)
859 {
860 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000861 case Lanczos2DSharpFilter:
nicolas45b58a92010-10-07 15:46:39 +0000862 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000863 window_type=SincFastFilter;
864 break;
865 default:
866 break;
867 }
868
cristy3ed852e2009-09-05 21:47:34 +0000869 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000870 if (artifact != (const char *) NULL)
871 {
cristy9af9b5d2010-08-15 17:04:28 +0000872 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000873 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000874 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000875 filter_type=(FilterTypes) option;
876 window_type=BoxFilter;
877 }
878 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000879 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000880 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000881 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000882 }
nicolas07bac812010-09-19 18:47:02 +0000883 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000884 artifact=GetImageArtifact(image,"filter:window");
885 if (artifact != (const char *) NULL)
886 {
cristy9af9b5d2010-08-15 17:04:28 +0000887 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000888 if ((UndefinedFilter < option) && (option < SentinelFilter))
889 {
890 if (option != LanczosFilter)
891 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000892 else
anthony48f77622010-10-03 14:32:31 +0000893 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000894 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000895 }
cristy33b1c162010-01-23 22:51:51 +0000896 }
cristy3ed852e2009-09-05 21:47:34 +0000897 }
cristy33b1c162010-01-23 22:51:51 +0000898 else
899 {
anthony48f77622010-10-03 14:32:31 +0000900 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000901 artifact=GetImageArtifact(image,"filter:window");
902 if (artifact != (const char *) NULL)
903 {
904 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
905 artifact);
906 if ((UndefinedFilter < option) && (option < SentinelFilter))
907 {
anthony61b5ddd2010-10-05 02:33:31 +0000908 filter_type=cylindrical != MagickFalse ?
909 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000910 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000911 }
912 }
913 }
nicolas07bac812010-09-19 18:47:02 +0000914 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000915 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000916 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000917 resize_filter->window=filters[window_type].function;
918 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000919 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000920
anthonyf5e76ef2010-10-12 01:22:01 +0000921 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000922 if (cylindrical != MagickFalse)
923 switch (filter_type)
924 {
925 case PointFilter:
926 case BoxFilter:
927 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000928 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000929 break;
anthony81b8bf92010-10-02 13:54:34 +0000930 default:
931 break;
anthony10b8bc82010-10-02 12:48:46 +0000932 }
anthony61b5ddd2010-10-05 02:33:31 +0000933 else
934 switch (filter_type)
935 {
936 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000937 case Lanczos2DSharpFilter:
anthony853d6972010-10-08 06:01:31 +0000938 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000939 resize_filter->filter=SincFast;
940 break;
941 default:
942 break;
943 }
944
anthonyf5e76ef2010-10-12 01:22:01 +0000945 /*
946 ** More Expert Option Modifications
947 */
948
949 /* User Sigma Override - no support change */
950 artifact=GetImageArtifact(image,"filter:sigma");
951 if (artifact != (const char *) NULL)
952 sigma=StringToDouble(artifact);
953 /* Define coefficents for Gaussian (assumes no cubic window) */
954 if ( GaussianFilter ) {
955 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
cristy68f103e2010-10-14 01:19:07 +0000956 resize_filter->coeff[1] = (MagickRealType) (1.0/(Magick2PI*sigma*sigma)); /* unused */
anthonyf5e76ef2010-10-12 01:22:01 +0000957 }
958
959 /* Blur Override */
960 artifact=GetImageArtifact(image,"filter:blur");
961 if (artifact != (const char *) NULL)
962 resize_filter->blur=StringToDouble(artifact);
963 if (resize_filter->blur < MagickEpsilon)
964 resize_filter->blur=(MagickRealType) MagickEpsilon;
965
966 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000967 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000968 if (artifact != (const char *) NULL)
969 {
cristybb503372010-05-27 20:51:26 +0000970 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000971 lobes;
972
cristy96b16132010-08-29 17:19:52 +0000973 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000974 if (lobes < 1)
975 lobes=1;
976 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000977 }
anthony61b5ddd2010-10-05 02:33:31 +0000978 /* convert Jinc lobes to a real support value */
979 if (resize_filter->filter == Jinc)
980 {
981 if (resize_filter->support > 16)
982 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
983 else
984 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
985 }
986 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000987 artifact=GetImageArtifact(image,"filter:support");
988 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000989 resize_filter->support=fabs(StringToDouble(artifact));
990 /*
nicolas07bac812010-09-19 18:47:02 +0000991 Scale windowing function separatally to the support 'clipping'
992 window that calling operator is planning to actually use. (Expert
993 override)
cristy3ed852e2009-09-05 21:47:34 +0000994 */
anthony55f12332010-09-10 01:13:02 +0000995 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000996 artifact=GetImageArtifact(image,"filter:win-support");
997 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000998 resize_filter->window_support=fabs(StringToDouble(artifact));
999 /*
anthony1f90a6b2010-09-14 08:56:31 +00001000 Adjust window function scaling to the windowing support for
1001 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +00001002 */
1003 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +00001004
anthony55f12332010-09-10 01:13:02 +00001005 /*
anthonyf5e76ef2010-10-12 01:22:01 +00001006 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +00001007 */
cristy3ed852e2009-09-05 21:47:34 +00001008 B=0.0;
1009 C=0.0;
cristy33b1c162010-01-23 22:51:51 +00001010 if ((filters[filter_type].function == CubicBC) ||
1011 (filters[window_type].function == CubicBC))
1012 {
anthony2d9b8b52010-09-14 08:31:07 +00001013 B=filters[filter_type].B;
1014 C=filters[filter_type].C;
1015 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +00001016 {
anthony2d9b8b52010-09-14 08:31:07 +00001017 B=filters[window_type].B;
1018 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +00001019 }
cristy33b1c162010-01-23 22:51:51 +00001020 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001021 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001022 {
1023 B=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001024 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001025 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001026 if (artifact != (const char *) NULL)
1027 C=StringToDouble(artifact);
1028 }
1029 else
1030 {
1031 artifact=GetImageArtifact(image,"filter:c");
1032 if (artifact != (const char *) NULL)
1033 {
1034 C=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001035 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001036 }
1037 }
anthonyf5e76ef2010-10-12 01:22:01 +00001038 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
1039 resize_filter->coeff[0]=(6.0-2.0*B)/6.0;
1040 resize_filter->coeff[1]=0.0;
1041 resize_filter->coeff[2]=(-18.0+12.0*B+6.0*C)/6.0;
1042 resize_filter->coeff[3]=(12.0-9.0*B-6.0*C)/6.0;
1043 resize_filter->coeff[4]=(8.0*B+24.0*C)/6.0;
1044 resize_filter->coeff[5]=(-12.0*B-48.0*C)/6.0;
1045 resize_filter->coeff[6]=(6.0*B+30.0*C)/6.0;
1046 resize_filter->coeff[7]=(-B-6.0*C)/6.0;
cristy3ed852e2009-09-05 21:47:34 +00001047 }
anthonyf5e76ef2010-10-12 01:22:01 +00001048
anthony55f12332010-09-10 01:13:02 +00001049 /*
nicolas07bac812010-09-19 18:47:02 +00001050 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001051 */
cristyf5b49372010-10-16 01:06:47 +00001052#if defined(MAGICKCORE_OPENMP_SUPPORT)
1053 #pragma omp master
1054 {
1055#endif
1056 artifact=GetImageArtifact(image,"filter:verbose");
1057 if (artifact != (const char *) NULL)
1058 {
1059 double
anthony450db502010-10-19 04:03:03 +00001060 support,
cristyf5b49372010-10-16 01:06:47 +00001061 x;
cristy3ed852e2009-09-05 21:47:34 +00001062
cristyf5b49372010-10-16 01:06:47 +00001063 /*
1064 Set the weighting function properly when the weighting
1065 function may not exactly match the filter of the same name.
1066 EG: a Point filter really uses a Box weighting function
1067 with a different support than is typically used.
anthony463be1d2010-09-26 01:07:36 +00001068
cristyf5b49372010-10-16 01:06:47 +00001069 */
1070 if (resize_filter->filter == Box) filter_type=BoxFilter;
1071 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1072 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1073 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1074 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1075 /*
1076 Report Filter Details.
1077 */
1078 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1079 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
1080 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1081 MagickFilterOptions,filter_type));
1082 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
1083 MagickFilterOptions, window_type));
1084 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
1085 (double) resize_filter->support);
1086 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
1087 (double) resize_filter->window_support);
1088 (void) fprintf(stdout,"# scale_blur = %.*g\n",GetMagickPrecision(),
1089 (double) resize_filter->blur);
1090 if ( filter_type == GaussianFilter )
1091 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",GetMagickPrecision(),
1092 (double) sigma);
1093 (void) fprintf(stdout,"# practical_support = %.*g\n",GetMagickPrecision(),
1094 (double) support);
1095 if ( filter_type == CubicFilter || window_type == CubicFilter )
1096 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1097 (double) B,GetMagickPrecision(),(double) C);
1098 (void) fprintf(stdout,"\n");
1099 /*
1100 Output values of resulting filter graph -- for graphing
1101 filter result.
1102 */
1103 for (x=0.0; x <= support; x+=0.01f)
1104 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1105 (double) GetResizeFilterWeight(resize_filter,x));
1106 /* A final value so gnuplot can graph the 'stop' properly. */
1107 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1108 0.0);
1109 }
1110 /* Output the above once only for each image - remove setting */
1111 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1112#if defined(MAGICKCORE_OPENMP_SUPPORT)
1113 }
1114#endif
cristy3ed852e2009-09-05 21:47:34 +00001115 return(resize_filter);
1116}
1117
1118/*
1119%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1120% %
1121% %
1122% %
1123% A d a p t i v e R e s i z e I m a g e %
1124% %
1125% %
1126% %
1127%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1128%
1129% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1130%
1131% The format of the AdaptiveResizeImage method is:
1132%
cristy9af9b5d2010-08-15 17:04:28 +00001133% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1134% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001135%
1136% A description of each parameter follows:
1137%
1138% o image: the image.
1139%
1140% o columns: the number of columns in the resized image.
1141%
1142% o rows: the number of rows in the resized image.
1143%
1144% o exception: return any errors or warnings in this structure.
1145%
1146*/
1147MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001148 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001149{
1150#define AdaptiveResizeImageTag "Resize/Image"
1151
cristyc4c8d132010-01-07 01:58:38 +00001152 CacheView
1153 *resize_view;
1154
cristy3ed852e2009-09-05 21:47:34 +00001155 Image
1156 *resize_image;
1157
cristy3ed852e2009-09-05 21:47:34 +00001158 MagickBooleanType
1159 proceed;
1160
1161 MagickPixelPacket
1162 pixel;
1163
1164 PointInfo
1165 offset;
1166
1167 ResampleFilter
1168 *resample_filter;
1169
cristy9af9b5d2010-08-15 17:04:28 +00001170 ssize_t
1171 y;
1172
cristy3ed852e2009-09-05 21:47:34 +00001173 /*
1174 Adaptively resize image.
1175 */
1176 assert(image != (const Image *) NULL);
1177 assert(image->signature == MagickSignature);
1178 if (image->debug != MagickFalse)
1179 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1180 assert(exception != (ExceptionInfo *) NULL);
1181 assert(exception->signature == MagickSignature);
1182 if ((columns == 0) || (rows == 0))
1183 return((Image *) NULL);
1184 if ((columns == image->columns) && (rows == image->rows))
1185 return(CloneImage(image,0,0,MagickTrue,exception));
1186 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1187 if (resize_image == (Image *) NULL)
1188 return((Image *) NULL);
1189 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1190 {
1191 InheritException(exception,&resize_image->exception);
1192 resize_image=DestroyImage(resize_image);
1193 return((Image *) NULL);
1194 }
1195 GetMagickPixelPacket(image,&pixel);
1196 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001197 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001198 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001199 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001200 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001201 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001202 {
1203 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001204 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001205
cristybb503372010-05-27 20:51:26 +00001206 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001207 x;
1208
1209 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001210 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001211
1212 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1213 exception);
1214 if (q == (PixelPacket *) NULL)
1215 break;
1216 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1217 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001218 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001219 {
1220 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1221 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1222 &pixel);
1223 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1224 q++;
1225 }
1226 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1227 break;
cristy96b16132010-08-29 17:19:52 +00001228 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1229 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001230 if (proceed == MagickFalse)
1231 break;
1232 }
1233 resample_filter=DestroyResampleFilter(resample_filter);
1234 resize_view=DestroyCacheView(resize_view);
1235 return(resize_image);
1236}
1237
1238/*
1239%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1240% %
1241% %
1242% %
1243+ B e s s e l O r d e r O n e %
1244% %
1245% %
1246% %
1247%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1248%
1249% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001250% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001251%
1252% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1253%
1254% j1(x) = x*j1(x);
1255%
1256% For x in (8,inf)
1257%
1258% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1259%
1260% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1261%
1262% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1263% = 1/sqrt(2) * (sin(x) - cos(x))
1264% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1265% = -1/sqrt(2) * (sin(x) + cos(x))
1266%
1267% The format of the BesselOrderOne method is:
1268%
1269% MagickRealType BesselOrderOne(MagickRealType x)
1270%
1271% A description of each parameter follows:
1272%
1273% o x: MagickRealType value.
1274%
1275*/
1276
1277#undef I0
1278static MagickRealType I0(MagickRealType x)
1279{
1280 MagickRealType
1281 sum,
1282 t,
1283 y;
1284
cristybb503372010-05-27 20:51:26 +00001285 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001286 i;
1287
1288 /*
1289 Zeroth order Bessel function of the first kind.
1290 */
1291 sum=1.0;
1292 y=x*x/4.0;
1293 t=y;
1294 for (i=2; t > MagickEpsilon; i++)
1295 {
1296 sum+=t;
1297 t*=y/((MagickRealType) i*i);
1298 }
1299 return(sum);
1300}
1301
1302#undef J1
1303static MagickRealType J1(MagickRealType x)
1304{
1305 MagickRealType
1306 p,
1307 q;
1308
cristybb503372010-05-27 20:51:26 +00001309 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001310 i;
1311
1312 static const double
1313 Pone[] =
1314 {
1315 0.581199354001606143928050809e+21,
1316 -0.6672106568924916298020941484e+20,
1317 0.2316433580634002297931815435e+19,
1318 -0.3588817569910106050743641413e+17,
1319 0.2908795263834775409737601689e+15,
1320 -0.1322983480332126453125473247e+13,
1321 0.3413234182301700539091292655e+10,
1322 -0.4695753530642995859767162166e+7,
1323 0.270112271089232341485679099e+4
1324 },
1325 Qone[] =
1326 {
1327 0.11623987080032122878585294e+22,
1328 0.1185770712190320999837113348e+20,
1329 0.6092061398917521746105196863e+17,
1330 0.2081661221307607351240184229e+15,
1331 0.5243710262167649715406728642e+12,
1332 0.1013863514358673989967045588e+10,
1333 0.1501793594998585505921097578e+7,
1334 0.1606931573481487801970916749e+4,
1335 0.1e+1
1336 };
1337
1338 p=Pone[8];
1339 q=Qone[8];
1340 for (i=7; i >= 0; i--)
1341 {
1342 p=p*x*x+Pone[i];
1343 q=q*x*x+Qone[i];
1344 }
1345 return(p/q);
1346}
1347
1348#undef P1
1349static MagickRealType P1(MagickRealType x)
1350{
1351 MagickRealType
1352 p,
1353 q;
1354
cristybb503372010-05-27 20:51:26 +00001355 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001356 i;
1357
1358 static const double
1359 Pone[] =
1360 {
1361 0.352246649133679798341724373e+5,
1362 0.62758845247161281269005675e+5,
1363 0.313539631109159574238669888e+5,
1364 0.49854832060594338434500455e+4,
1365 0.2111529182853962382105718e+3,
1366 0.12571716929145341558495e+1
1367 },
1368 Qone[] =
1369 {
1370 0.352246649133679798068390431e+5,
1371 0.626943469593560511888833731e+5,
1372 0.312404063819041039923015703e+5,
1373 0.4930396490181088979386097e+4,
1374 0.2030775189134759322293574e+3,
1375 0.1e+1
1376 };
1377
1378 p=Pone[5];
1379 q=Qone[5];
1380 for (i=4; i >= 0; i--)
1381 {
1382 p=p*(8.0/x)*(8.0/x)+Pone[i];
1383 q=q*(8.0/x)*(8.0/x)+Qone[i];
1384 }
1385 return(p/q);
1386}
1387
1388#undef Q1
1389static MagickRealType Q1(MagickRealType x)
1390{
1391 MagickRealType
1392 p,
1393 q;
1394
cristybb503372010-05-27 20:51:26 +00001395 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001396 i;
1397
1398 static const double
1399 Pone[] =
1400 {
1401 0.3511751914303552822533318e+3,
1402 0.7210391804904475039280863e+3,
1403 0.4259873011654442389886993e+3,
1404 0.831898957673850827325226e+2,
1405 0.45681716295512267064405e+1,
1406 0.3532840052740123642735e-1
1407 },
1408 Qone[] =
1409 {
1410 0.74917374171809127714519505e+4,
1411 0.154141773392650970499848051e+5,
1412 0.91522317015169922705904727e+4,
1413 0.18111867005523513506724158e+4,
1414 0.1038187585462133728776636e+3,
1415 0.1e+1
1416 };
1417
1418 p=Pone[5];
1419 q=Qone[5];
1420 for (i=4; i >= 0; i--)
1421 {
1422 p=p*(8.0/x)*(8.0/x)+Pone[i];
1423 q=q*(8.0/x)*(8.0/x)+Qone[i];
1424 }
1425 return(p/q);
1426}
1427
1428static MagickRealType BesselOrderOne(MagickRealType x)
1429{
1430 MagickRealType
1431 p,
1432 q;
1433
1434 if (x == 0.0)
1435 return(0.0);
1436 p=x;
1437 if (x < 0.0)
1438 x=(-x);
1439 if (x < 8.0)
1440 return(p*J1(x));
1441 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1442 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1443 cos((double) x))));
1444 if (p < 0.0)
1445 q=(-q);
1446 return(q);
1447}
1448
1449/*
1450%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1451% %
1452% %
1453% %
1454+ D e s t r o y R e s i z e F i l t e r %
1455% %
1456% %
1457% %
1458%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1459%
1460% DestroyResizeFilter() destroy the resize filter.
1461%
cristya2ffd7e2010-03-10 20:50:30 +00001462% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001463%
1464% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1465%
1466% A description of each parameter follows:
1467%
1468% o resize_filter: the resize filter.
1469%
1470*/
1471MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1472{
1473 assert(resize_filter != (ResizeFilter *) NULL);
1474 assert(resize_filter->signature == MagickSignature);
1475 resize_filter->signature=(~MagickSignature);
1476 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1477 return(resize_filter);
1478}
1479
1480/*
1481%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1482% %
1483% %
1484% %
1485+ G e t R e s i z e F i l t e r S u p p o r t %
1486% %
1487% %
1488% %
1489%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1490%
1491% GetResizeFilterSupport() return the current support window size for this
1492% filter. Note that this may have been enlarged by filter:blur factor.
1493%
1494% The format of the GetResizeFilterSupport method is:
1495%
1496% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1497%
1498% A description of each parameter follows:
1499%
1500% o filter: Image filter to use.
1501%
1502*/
1503MagickExport MagickRealType GetResizeFilterSupport(
1504 const ResizeFilter *resize_filter)
1505{
1506 assert(resize_filter != (ResizeFilter *) NULL);
1507 assert(resize_filter->signature == MagickSignature);
1508 return(resize_filter->support*resize_filter->blur);
1509}
1510
1511/*
1512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1513% %
1514% %
1515% %
1516+ G e t R e s i z e F i l t e r W e i g h t %
1517% %
1518% %
1519% %
1520%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1521%
1522% GetResizeFilterWeight evaluates the specified resize filter at the point x
1523% which usally lies between zero and the filters current 'support' and
1524% returns the weight of the filter function at that point.
1525%
1526% The format of the GetResizeFilterWeight method is:
1527%
1528% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1529% const MagickRealType x)
1530%
1531% A description of each parameter follows:
1532%
1533% o filter: the filter type.
1534%
1535% o x: the point.
1536%
1537*/
1538MagickExport MagickRealType GetResizeFilterWeight(
1539 const ResizeFilter *resize_filter,const MagickRealType x)
1540{
1541 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001542 scale,
1543 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001544
1545 /*
1546 Windowing function - scale the weighting filter by this amount.
1547 */
1548 assert(resize_filter != (ResizeFilter *) NULL);
1549 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001550 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001551 if ((resize_filter->window_support < MagickEpsilon) ||
1552 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001553 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001554 else
1555 {
anthony55f12332010-09-10 01:13:02 +00001556 scale=resize_filter->scale;
1557 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001558 }
anthony55f12332010-09-10 01:13:02 +00001559 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001560}
1561
1562/*
1563%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1564% %
1565% %
1566% %
1567% M a g n i f y I m a g e %
1568% %
1569% %
1570% %
1571%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1572%
1573% MagnifyImage() is a convenience method that scales an image proportionally
1574% to twice its size.
1575%
1576% The format of the MagnifyImage method is:
1577%
1578% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1579%
1580% A description of each parameter follows:
1581%
1582% o image: the image.
1583%
1584% o exception: return any errors or warnings in this structure.
1585%
1586*/
1587MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1588{
1589 Image
1590 *magnify_image;
1591
1592 assert(image != (Image *) NULL);
1593 assert(image->signature == MagickSignature);
1594 if (image->debug != MagickFalse)
1595 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1596 assert(exception != (ExceptionInfo *) NULL);
1597 assert(exception->signature == MagickSignature);
1598 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1599 1.0,exception);
1600 return(magnify_image);
1601}
1602
1603/*
1604%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1605% %
1606% %
1607% %
1608% M i n i f y I m a g e %
1609% %
1610% %
1611% %
1612%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1613%
1614% MinifyImage() is a convenience method that scales an image proportionally
1615% to half its size.
1616%
1617% The format of the MinifyImage method is:
1618%
1619% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1620%
1621% A description of each parameter follows:
1622%
1623% o image: the image.
1624%
1625% o exception: return any errors or warnings in this structure.
1626%
1627*/
1628MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1629{
1630 Image
1631 *minify_image;
1632
1633 assert(image != (Image *) NULL);
1634 assert(image->signature == MagickSignature);
1635 if (image->debug != MagickFalse)
1636 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1637 assert(exception != (ExceptionInfo *) NULL);
1638 assert(exception->signature == MagickSignature);
1639 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1640 1.0,exception);
1641 return(minify_image);
1642}
1643
1644/*
1645%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1646% %
1647% %
1648% %
1649% R e s a m p l e I m a g e %
1650% %
1651% %
1652% %
1653%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1654%
1655% ResampleImage() resize image in terms of its pixel size, so that when
1656% displayed at the given resolution it will be the same size in terms of
1657% real world units as the original image at the original resolution.
1658%
1659% The format of the ResampleImage method is:
1660%
1661% Image *ResampleImage(Image *image,const double x_resolution,
1662% const double y_resolution,const FilterTypes filter,const double blur,
1663% ExceptionInfo *exception)
1664%
1665% A description of each parameter follows:
1666%
1667% o image: the image to be resized to fit the given resolution.
1668%
1669% o x_resolution: the new image x resolution.
1670%
1671% o y_resolution: the new image y resolution.
1672%
1673% o filter: Image filter to use.
1674%
1675% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1676%
1677*/
1678MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1679 const double y_resolution,const FilterTypes filter,const double blur,
1680 ExceptionInfo *exception)
1681{
1682#define ResampleImageTag "Resample/Image"
1683
1684 Image
1685 *resample_image;
1686
cristybb503372010-05-27 20:51:26 +00001687 size_t
cristy3ed852e2009-09-05 21:47:34 +00001688 height,
1689 width;
1690
1691 /*
1692 Initialize sampled image attributes.
1693 */
1694 assert(image != (const Image *) NULL);
1695 assert(image->signature == MagickSignature);
1696 if (image->debug != MagickFalse)
1697 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1698 assert(exception != (ExceptionInfo *) NULL);
1699 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001700 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1701 72.0 : image->x_resolution)+0.5);
1702 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1703 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001704 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1705 if (resample_image != (Image *) NULL)
1706 {
1707 resample_image->x_resolution=x_resolution;
1708 resample_image->y_resolution=y_resolution;
1709 }
1710 return(resample_image);
1711}
1712#if defined(MAGICKCORE_LQR_DELEGATE)
1713
1714/*
1715%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1716% %
1717% %
1718% %
1719% L i q u i d R e s c a l e I m a g e %
1720% %
1721% %
1722% %
1723%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1724%
1725% LiquidRescaleImage() rescales image with seam carving.
1726%
1727% The format of the LiquidRescaleImage method is:
1728%
1729% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001730% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001731% const double delta_x,const double rigidity,ExceptionInfo *exception)
1732%
1733% A description of each parameter follows:
1734%
1735% o image: the image.
1736%
1737% o columns: the number of columns in the rescaled image.
1738%
1739% o rows: the number of rows in the rescaled image.
1740%
1741% o delta_x: maximum seam transversal step (0 means straight seams).
1742%
1743% o rigidity: introduce a bias for non-straight seams (typically 0).
1744%
1745% o exception: return any errors or warnings in this structure.
1746%
1747*/
cristy9af9b5d2010-08-15 17:04:28 +00001748MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1749 const size_t rows,const double delta_x,const double rigidity,
1750 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001751{
1752#define LiquidRescaleImageTag "Rescale/Image"
1753
cristyc5c6f662010-09-22 14:23:02 +00001754 CacheView
1755 *rescale_view;
1756
cristy3ed852e2009-09-05 21:47:34 +00001757 const char
1758 *map;
1759
1760 guchar
1761 *packet;
1762
1763 Image
1764 *rescale_image;
1765
1766 int
1767 x,
1768 y;
1769
1770 LqrCarver
1771 *carver;
1772
1773 LqrRetVal
1774 lqr_status;
1775
1776 MagickBooleanType
1777 status;
1778
1779 MagickPixelPacket
1780 pixel;
1781
1782 unsigned char
1783 *pixels;
1784
1785 /*
1786 Liquid rescale image.
1787 */
1788 assert(image != (const Image *) NULL);
1789 assert(image->signature == MagickSignature);
1790 if (image->debug != MagickFalse)
1791 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1792 assert(exception != (ExceptionInfo *) NULL);
1793 assert(exception->signature == MagickSignature);
1794 if ((columns == 0) || (rows == 0))
1795 return((Image *) NULL);
1796 if ((columns == image->columns) && (rows == image->rows))
1797 return(CloneImage(image,0,0,MagickTrue,exception));
1798 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001799 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001800 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1801 {
1802 Image
1803 *resize_image;
1804
cristybb503372010-05-27 20:51:26 +00001805 size_t
cristy3ed852e2009-09-05 21:47:34 +00001806 height,
1807 width;
1808
1809 /*
1810 Honor liquid resize size limitations.
1811 */
1812 for (width=image->columns; columns >= (2*width-1); width*=2);
1813 for (height=image->rows; rows >= (2*height-1); height*=2);
1814 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1815 exception);
1816 if (resize_image == (Image *) NULL)
1817 return((Image *) NULL);
1818 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1819 rigidity,exception);
1820 resize_image=DestroyImage(resize_image);
1821 return(rescale_image);
1822 }
1823 map="RGB";
1824 if (image->matte == MagickFalse)
1825 map="RGBA";
1826 if (image->colorspace == CMYKColorspace)
1827 {
1828 map="CMYK";
1829 if (image->matte == MagickFalse)
1830 map="CMYKA";
1831 }
1832 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1833 strlen(map)*sizeof(*pixels));
1834 if (pixels == (unsigned char *) NULL)
1835 return((Image *) NULL);
1836 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1837 pixels,exception);
1838 if (status == MagickFalse)
1839 {
1840 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1841 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1842 }
1843 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1844 if (carver == (LqrCarver *) NULL)
1845 {
1846 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1847 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1848 }
1849 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1850 lqr_status=lqr_carver_resize(carver,columns,rows);
1851 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1852 lqr_carver_get_height(carver),MagickTrue,exception);
1853 if (rescale_image == (Image *) NULL)
1854 {
1855 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1856 return((Image *) NULL);
1857 }
1858 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1859 {
1860 InheritException(exception,&rescale_image->exception);
1861 rescale_image=DestroyImage(rescale_image);
1862 return((Image *) NULL);
1863 }
1864 GetMagickPixelPacket(rescale_image,&pixel);
1865 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001866 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001867 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1868 {
1869 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001870 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001871
1872 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001873 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001874
anthony22aad252010-09-23 06:59:07 +00001875 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001876 if (q == (PixelPacket *) NULL)
1877 break;
cristyc5c6f662010-09-22 14:23:02 +00001878 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001879 pixel.red=QuantumRange*(packet[0]/255.0);
1880 pixel.green=QuantumRange*(packet[1]/255.0);
1881 pixel.blue=QuantumRange*(packet[2]/255.0);
1882 if (image->colorspace != CMYKColorspace)
1883 {
1884 if (image->matte == MagickFalse)
1885 pixel.opacity=QuantumRange*(packet[3]/255.0);
1886 }
1887 else
1888 {
1889 pixel.index=QuantumRange*(packet[3]/255.0);
1890 if (image->matte == MagickFalse)
1891 pixel.opacity=QuantumRange*(packet[4]/255.0);
1892 }
1893 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001894 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001895 break;
1896 }
cristyc5c6f662010-09-22 14:23:02 +00001897 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001898 /*
1899 Relinquish resources.
1900 */
1901 lqr_carver_destroy(carver);
1902 return(rescale_image);
1903}
1904#else
1905MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001906 const size_t magick_unused(columns),const size_t magick_unused(rows),
1907 const double magick_unused(delta_x),const double magick_unused(rigidity),
1908 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001909{
1910 assert(image != (const Image *) NULL);
1911 assert(image->signature == MagickSignature);
1912 if (image->debug != MagickFalse)
1913 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1914 assert(exception != (ExceptionInfo *) NULL);
1915 assert(exception->signature == MagickSignature);
1916 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1917 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1918 return((Image *) NULL);
1919}
1920#endif
1921
1922/*
1923%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1924% %
1925% %
1926% %
1927% R e s i z e I m a g e %
1928% %
1929% %
1930% %
1931%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1932%
1933% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001934% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001935%
1936% If an undefined filter is given the filter defaults to Mitchell for a
1937% colormapped image, a image with a matte channel, or if the image is
1938% enlarged. Otherwise the filter defaults to a Lanczos.
1939%
1940% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1941%
1942% The format of the ResizeImage method is:
1943%
cristybb503372010-05-27 20:51:26 +00001944% Image *ResizeImage(Image *image,const size_t columns,
1945% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001946% ExceptionInfo *exception)
1947%
1948% A description of each parameter follows:
1949%
1950% o image: the image.
1951%
1952% o columns: the number of columns in the scaled image.
1953%
1954% o rows: the number of rows in the scaled image.
1955%
1956% o filter: Image filter to use.
1957%
cristy9af9b5d2010-08-15 17:04:28 +00001958% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1959% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001960%
1961% o exception: return any errors or warnings in this structure.
1962%
1963*/
1964
1965typedef struct _ContributionInfo
1966{
1967 MagickRealType
1968 weight;
1969
cristybb503372010-05-27 20:51:26 +00001970 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001971 pixel;
1972} ContributionInfo;
1973
1974static ContributionInfo **DestroyContributionThreadSet(
1975 ContributionInfo **contribution)
1976{
cristybb503372010-05-27 20:51:26 +00001977 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001978 i;
1979
1980 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001981 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001982 if (contribution[i] != (ContributionInfo *) NULL)
1983 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1984 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001985 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001986 return(contribution);
1987}
1988
1989static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1990{
cristybb503372010-05-27 20:51:26 +00001991 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001992 i;
1993
1994 ContributionInfo
1995 **contribution;
1996
cristybb503372010-05-27 20:51:26 +00001997 size_t
cristy3ed852e2009-09-05 21:47:34 +00001998 number_threads;
1999
2000 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002001 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00002002 sizeof(*contribution));
2003 if (contribution == (ContributionInfo **) NULL)
2004 return((ContributionInfo **) NULL);
2005 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00002006 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002007 {
2008 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2009 sizeof(**contribution));
2010 if (contribution[i] == (ContributionInfo *) NULL)
2011 return(DestroyContributionThreadSet(contribution));
2012 }
2013 return(contribution);
2014}
2015
2016static inline double MagickMax(const double x,const double y)
2017{
2018 if (x > y)
2019 return(x);
2020 return(y);
2021}
2022
2023static inline double MagickMin(const double x,const double y)
2024{
2025 if (x < y)
2026 return(x);
2027 return(y);
2028}
2029
2030static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2031 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002032 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002033{
2034#define ResizeImageTag "Resize/Image"
2035
cristyfa112112010-01-04 17:48:07 +00002036 CacheView
2037 *image_view,
2038 *resize_view;
2039
cristy3ed852e2009-09-05 21:47:34 +00002040 ClassType
2041 storage_class;
2042
2043 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002044 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002045
cristy3ed852e2009-09-05 21:47:34 +00002046 MagickBooleanType
2047 status;
2048
2049 MagickPixelPacket
2050 zero;
2051
2052 MagickRealType
2053 scale,
2054 support;
2055
cristy9af9b5d2010-08-15 17:04:28 +00002056 ssize_t
2057 x;
2058
cristy3ed852e2009-09-05 21:47:34 +00002059 /*
2060 Apply filter to resize horizontally from image to resize image.
2061 */
cristy5d824382010-09-06 14:00:17 +00002062 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002063 support=scale*GetResizeFilterSupport(resize_filter);
2064 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2065 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2066 {
2067 InheritException(exception,&resize_image->exception);
2068 return(MagickFalse);
2069 }
2070 if (support < 0.5)
2071 {
2072 /*
nicolas07bac812010-09-19 18:47:02 +00002073 Support too small even for nearest neighbour: Reduce to point
2074 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002075 */
2076 support=(MagickRealType) 0.5;
2077 scale=1.0;
2078 }
2079 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2080 if (contributions == (ContributionInfo **) NULL)
2081 {
2082 (void) ThrowMagickException(exception,GetMagickModule(),
2083 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2084 return(MagickFalse);
2085 }
2086 status=MagickTrue;
2087 scale=1.0/scale;
2088 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2089 image_view=AcquireCacheView(image);
2090 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002091#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002092 #pragma omp parallel for shared(status)
2093#endif
cristybb503372010-05-27 20:51:26 +00002094 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002095 {
cristy3ed852e2009-09-05 21:47:34 +00002096 MagickRealType
2097 center,
2098 density;
2099
2100 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002101 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002102
2103 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002104 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002105
cristy03dbbd22010-09-19 23:04:47 +00002106 register ContributionInfo
2107 *restrict contribution;
2108
cristy3ed852e2009-09-05 21:47:34 +00002109 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002110 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002111
cristy3ed852e2009-09-05 21:47:34 +00002112 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002113 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002114
cristy03dbbd22010-09-19 23:04:47 +00002115 register ssize_t
2116 y;
2117
cristy9af9b5d2010-08-15 17:04:28 +00002118 ssize_t
2119 n,
2120 start,
2121 stop;
2122
cristy3ed852e2009-09-05 21:47:34 +00002123 if (status == MagickFalse)
2124 continue;
2125 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002126 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2127 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002128 density=0.0;
2129 contribution=contributions[GetOpenMPThreadId()];
2130 for (n=0; n < (stop-start); n++)
2131 {
2132 contribution[n].pixel=start+n;
2133 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2134 ((MagickRealType) (start+n)-center+0.5));
2135 density+=contribution[n].weight;
2136 }
2137 if ((density != 0.0) && (density != 1.0))
2138 {
cristybb503372010-05-27 20:51:26 +00002139 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002140 i;
2141
2142 /*
2143 Normalize.
2144 */
2145 density=1.0/density;
2146 for (i=0; i < n; i++)
2147 contribution[i].weight*=density;
2148 }
cristy9af9b5d2010-08-15 17:04:28 +00002149 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2150 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002151 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2152 exception);
2153 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2154 {
2155 status=MagickFalse;
2156 continue;
2157 }
2158 indexes=GetCacheViewVirtualIndexQueue(image_view);
2159 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002160 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002161 {
cristy3ed852e2009-09-05 21:47:34 +00002162 MagickPixelPacket
2163 pixel;
2164
2165 MagickRealType
2166 alpha;
2167
cristybb503372010-05-27 20:51:26 +00002168 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002169 i;
2170
cristy9af9b5d2010-08-15 17:04:28 +00002171 ssize_t
2172 j;
2173
cristy3ed852e2009-09-05 21:47:34 +00002174 pixel=zero;
2175 if (image->matte == MagickFalse)
2176 {
2177 for (i=0; i < n; i++)
2178 {
2179 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2180 (contribution[i].pixel-contribution[0].pixel);
2181 alpha=contribution[i].weight;
2182 pixel.red+=alpha*(p+j)->red;
2183 pixel.green+=alpha*(p+j)->green;
2184 pixel.blue+=alpha*(p+j)->blue;
2185 pixel.opacity+=alpha*(p+j)->opacity;
2186 }
cristyce70c172010-01-07 17:15:30 +00002187 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2188 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2189 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2190 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002191 if ((image->colorspace == CMYKColorspace) &&
2192 (resize_image->colorspace == CMYKColorspace))
2193 {
2194 for (i=0; i < n; i++)
2195 {
2196 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2197 (contribution[i].pixel-contribution[0].pixel);
2198 alpha=contribution[i].weight;
2199 pixel.index+=alpha*indexes[j];
2200 }
cristyce70c172010-01-07 17:15:30 +00002201 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002202 }
2203 }
2204 else
2205 {
2206 MagickRealType
2207 gamma;
2208
2209 gamma=0.0;
2210 for (i=0; i < n; i++)
2211 {
2212 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2213 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002214 alpha=contribution[i].weight*QuantumScale*
2215 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002216 pixel.red+=alpha*(p+j)->red;
2217 pixel.green+=alpha*(p+j)->green;
2218 pixel.blue+=alpha*(p+j)->blue;
2219 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2220 gamma+=alpha;
2221 }
2222 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002223 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2224 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2225 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2226 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002227 if ((image->colorspace == CMYKColorspace) &&
2228 (resize_image->colorspace == CMYKColorspace))
2229 {
2230 for (i=0; i < n; i++)
2231 {
2232 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2233 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002234 alpha=contribution[i].weight*QuantumScale*
2235 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002236 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002237 }
cristyce70c172010-01-07 17:15:30 +00002238 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2239 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002240 }
2241 }
2242 if ((resize_image->storage_class == PseudoClass) &&
2243 (image->storage_class == PseudoClass))
2244 {
cristybb503372010-05-27 20:51:26 +00002245 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002246 1.0)+0.5);
2247 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2248 (contribution[i-start].pixel-contribution[0].pixel);
2249 resize_indexes[y]=indexes[j];
2250 }
2251 q++;
2252 }
2253 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2254 status=MagickFalse;
2255 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2256 {
2257 MagickBooleanType
2258 proceed;
2259
cristyb5d5f722009-11-04 03:03:49 +00002260#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002261 #pragma omp critical (MagickCore_HorizontalFilter)
2262#endif
cristy9af9b5d2010-08-15 17:04:28 +00002263 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002264 if (proceed == MagickFalse)
2265 status=MagickFalse;
2266 }
2267 }
2268 resize_view=DestroyCacheView(resize_view);
2269 image_view=DestroyCacheView(image_view);
2270 contributions=DestroyContributionThreadSet(contributions);
2271 return(status);
2272}
2273
2274static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2275 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002276 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002277{
cristyfa112112010-01-04 17:48:07 +00002278 CacheView
2279 *image_view,
2280 *resize_view;
2281
cristy3ed852e2009-09-05 21:47:34 +00002282 ClassType
2283 storage_class;
2284
2285 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002286 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002287
cristy3ed852e2009-09-05 21:47:34 +00002288 MagickBooleanType
2289 status;
2290
2291 MagickPixelPacket
2292 zero;
2293
2294 MagickRealType
2295 scale,
2296 support;
2297
cristy9af9b5d2010-08-15 17:04:28 +00002298 ssize_t
2299 y;
2300
cristy3ed852e2009-09-05 21:47:34 +00002301 /*
cristy9af9b5d2010-08-15 17:04:28 +00002302 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002303 */
cristy5d824382010-09-06 14:00:17 +00002304 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002305 support=scale*GetResizeFilterSupport(resize_filter);
2306 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2307 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2308 {
2309 InheritException(exception,&resize_image->exception);
2310 return(MagickFalse);
2311 }
2312 if (support < 0.5)
2313 {
2314 /*
nicolas07bac812010-09-19 18:47:02 +00002315 Support too small even for nearest neighbour: Reduce to point
2316 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002317 */
2318 support=(MagickRealType) 0.5;
2319 scale=1.0;
2320 }
2321 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2322 if (contributions == (ContributionInfo **) NULL)
2323 {
2324 (void) ThrowMagickException(exception,GetMagickModule(),
2325 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2326 return(MagickFalse);
2327 }
2328 status=MagickTrue;
2329 scale=1.0/scale;
2330 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2331 image_view=AcquireCacheView(image);
2332 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002333#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002334 #pragma omp parallel for shared(status)
2335#endif
cristybb503372010-05-27 20:51:26 +00002336 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002337 {
cristy3ed852e2009-09-05 21:47:34 +00002338 MagickRealType
2339 center,
2340 density;
2341
2342 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002343 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002344
2345 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002346 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002347
cristy03dbbd22010-09-19 23:04:47 +00002348 register ContributionInfo
2349 *restrict contribution;
2350
cristy3ed852e2009-09-05 21:47:34 +00002351 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002352 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002353
cristy9af9b5d2010-08-15 17:04:28 +00002354 register PixelPacket
2355 *restrict q;
2356
cristybb503372010-05-27 20:51:26 +00002357 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002358 x;
2359
cristy9af9b5d2010-08-15 17:04:28 +00002360 ssize_t
2361 n,
2362 start,
2363 stop;
cristy3ed852e2009-09-05 21:47:34 +00002364
2365 if (status == MagickFalse)
2366 continue;
cristy679e6962010-03-18 00:42:45 +00002367 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002368 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2369 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002370 density=0.0;
2371 contribution=contributions[GetOpenMPThreadId()];
2372 for (n=0; n < (stop-start); n++)
2373 {
2374 contribution[n].pixel=start+n;
2375 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2376 ((MagickRealType) (start+n)-center+0.5));
2377 density+=contribution[n].weight;
2378 }
2379 if ((density != 0.0) && (density != 1.0))
2380 {
cristybb503372010-05-27 20:51:26 +00002381 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002382 i;
2383
2384 /*
2385 Normalize.
2386 */
2387 density=1.0/density;
2388 for (i=0; i < n; i++)
2389 contribution[i].weight*=density;
2390 }
2391 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002392 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2393 exception);
cristy3ed852e2009-09-05 21:47:34 +00002394 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2395 exception);
2396 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2397 {
2398 status=MagickFalse;
2399 continue;
2400 }
2401 indexes=GetCacheViewVirtualIndexQueue(image_view);
2402 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002403 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002404 {
cristy3ed852e2009-09-05 21:47:34 +00002405 MagickPixelPacket
2406 pixel;
2407
2408 MagickRealType
2409 alpha;
2410
cristybb503372010-05-27 20:51:26 +00002411 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002412 i;
2413
cristy9af9b5d2010-08-15 17:04:28 +00002414 ssize_t
2415 j;
2416
cristy3ed852e2009-09-05 21:47:34 +00002417 pixel=zero;
2418 if (image->matte == MagickFalse)
2419 {
2420 for (i=0; i < n; i++)
2421 {
cristybb503372010-05-27 20:51:26 +00002422 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002423 image->columns+x);
2424 alpha=contribution[i].weight;
2425 pixel.red+=alpha*(p+j)->red;
2426 pixel.green+=alpha*(p+j)->green;
2427 pixel.blue+=alpha*(p+j)->blue;
2428 pixel.opacity+=alpha*(p+j)->opacity;
2429 }
cristyce70c172010-01-07 17:15:30 +00002430 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2431 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2432 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2433 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002434 if ((image->colorspace == CMYKColorspace) &&
2435 (resize_image->colorspace == CMYKColorspace))
2436 {
2437 for (i=0; i < n; i++)
2438 {
cristybb503372010-05-27 20:51:26 +00002439 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002440 image->columns+x);
2441 alpha=contribution[i].weight;
2442 pixel.index+=alpha*indexes[j];
2443 }
cristyce70c172010-01-07 17:15:30 +00002444 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002445 }
2446 }
2447 else
2448 {
2449 MagickRealType
2450 gamma;
2451
2452 gamma=0.0;
2453 for (i=0; i < n; i++)
2454 {
cristybb503372010-05-27 20:51:26 +00002455 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002456 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002457 alpha=contribution[i].weight*QuantumScale*
2458 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002459 pixel.red+=alpha*(p+j)->red;
2460 pixel.green+=alpha*(p+j)->green;
2461 pixel.blue+=alpha*(p+j)->blue;
2462 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2463 gamma+=alpha;
2464 }
2465 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002466 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2467 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2468 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2469 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002470 if ((image->colorspace == CMYKColorspace) &&
2471 (resize_image->colorspace == CMYKColorspace))
2472 {
2473 for (i=0; i < n; i++)
2474 {
cristybb503372010-05-27 20:51:26 +00002475 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002476 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002477 alpha=contribution[i].weight*QuantumScale*
2478 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002479 pixel.index+=alpha*indexes[j];
2480 }
cristyce70c172010-01-07 17:15:30 +00002481 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2482 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002483 }
2484 }
2485 if ((resize_image->storage_class == PseudoClass) &&
2486 (image->storage_class == PseudoClass))
2487 {
cristybb503372010-05-27 20:51:26 +00002488 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002489 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002490 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002491 image->columns+x);
2492 resize_indexes[x]=indexes[j];
2493 }
2494 q++;
2495 }
2496 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2497 status=MagickFalse;
2498 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2499 {
2500 MagickBooleanType
2501 proceed;
2502
cristyb5d5f722009-11-04 03:03:49 +00002503#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002504 #pragma omp critical (MagickCore_VerticalFilter)
2505#endif
cristy9af9b5d2010-08-15 17:04:28 +00002506 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002507 if (proceed == MagickFalse)
2508 status=MagickFalse;
2509 }
2510 }
2511 resize_view=DestroyCacheView(resize_view);
2512 image_view=DestroyCacheView(image_view);
2513 contributions=DestroyContributionThreadSet(contributions);
2514 return(status);
2515}
2516
cristybb503372010-05-27 20:51:26 +00002517MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2518 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002519 ExceptionInfo *exception)
2520{
2521#define WorkLoadFactor 0.265
2522
2523 FilterTypes
2524 filter_type;
2525
2526 Image
2527 *filter_image,
2528 *resize_image;
2529
cristy9af9b5d2010-08-15 17:04:28 +00002530 MagickOffsetType
2531 offset;
2532
cristy3ed852e2009-09-05 21:47:34 +00002533 MagickRealType
2534 x_factor,
2535 y_factor;
2536
2537 MagickSizeType
2538 span;
2539
2540 MagickStatusType
2541 status;
2542
2543 ResizeFilter
2544 *resize_filter;
2545
cristy3ed852e2009-09-05 21:47:34 +00002546 /*
2547 Acquire resize image.
2548 */
2549 assert(image != (Image *) NULL);
2550 assert(image->signature == MagickSignature);
2551 if (image->debug != MagickFalse)
2552 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2553 assert(exception != (ExceptionInfo *) NULL);
2554 assert(exception->signature == MagickSignature);
2555 if ((columns == 0) || (rows == 0))
2556 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2557 if ((columns == image->columns) && (rows == image->rows) &&
2558 (filter == UndefinedFilter) && (blur == 1.0))
2559 return(CloneImage(image,0,0,MagickTrue,exception));
2560 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2561 if (resize_image == (Image *) NULL)
2562 return(resize_image);
2563 /*
2564 Acquire resize filter.
2565 */
2566 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2567 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2568 if ((x_factor*y_factor) > WorkLoadFactor)
2569 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2570 else
2571 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2572 if (filter_image == (Image *) NULL)
2573 return(DestroyImage(resize_image));
2574 filter_type=LanczosFilter;
2575 if (filter != UndefinedFilter)
2576 filter_type=filter;
2577 else
2578 if ((x_factor == 1.0) && (y_factor == 1.0))
2579 filter_type=PointFilter;
2580 else
2581 if ((image->storage_class == PseudoClass) ||
2582 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2583 filter_type=MitchellFilter;
2584 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2585 exception);
2586 /*
2587 Resize image.
2588 */
cristy9af9b5d2010-08-15 17:04:28 +00002589 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002590 if ((x_factor*y_factor) > WorkLoadFactor)
2591 {
2592 span=(MagickSizeType) (filter_image->columns+rows);
2593 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002594 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002595 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002596 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002597 }
2598 else
2599 {
2600 span=(MagickSizeType) (filter_image->rows+columns);
2601 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002602 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002603 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002604 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002605 }
2606 /*
2607 Free resources.
2608 */
2609 filter_image=DestroyImage(filter_image);
2610 resize_filter=DestroyResizeFilter(resize_filter);
2611 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2612 return((Image *) NULL);
2613 resize_image->type=image->type;
2614 return(resize_image);
2615}
2616
2617/*
2618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2619% %
2620% %
2621% %
2622% S a m p l e I m a g e %
2623% %
2624% %
2625% %
2626%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2627%
2628% SampleImage() scales an image to the desired dimensions with pixel
2629% sampling. Unlike other scaling methods, this method does not introduce
2630% any additional color into the scaled image.
2631%
2632% The format of the SampleImage method is:
2633%
cristybb503372010-05-27 20:51:26 +00002634% Image *SampleImage(const Image *image,const size_t columns,
2635% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002636%
2637% A description of each parameter follows:
2638%
2639% o image: the image.
2640%
2641% o columns: the number of columns in the sampled image.
2642%
2643% o rows: the number of rows in the sampled image.
2644%
2645% o exception: return any errors or warnings in this structure.
2646%
2647*/
cristybb503372010-05-27 20:51:26 +00002648MagickExport Image *SampleImage(const Image *image,const size_t columns,
2649 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002650{
2651#define SampleImageTag "Sample/Image"
2652
cristyc4c8d132010-01-07 01:58:38 +00002653 CacheView
2654 *image_view,
2655 *sample_view;
2656
cristy3ed852e2009-09-05 21:47:34 +00002657 Image
2658 *sample_image;
2659
cristy3ed852e2009-09-05 21:47:34 +00002660 MagickBooleanType
2661 status;
2662
cristy5f959472010-05-27 22:19:46 +00002663 MagickOffsetType
2664 progress;
2665
cristybb503372010-05-27 20:51:26 +00002666 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002667 x;
2668
cristy5f959472010-05-27 22:19:46 +00002669 ssize_t
2670 *x_offset,
2671 y;
2672
cristy3ed852e2009-09-05 21:47:34 +00002673 /*
2674 Initialize sampled image attributes.
2675 */
2676 assert(image != (const Image *) NULL);
2677 assert(image->signature == MagickSignature);
2678 if (image->debug != MagickFalse)
2679 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2680 assert(exception != (ExceptionInfo *) NULL);
2681 assert(exception->signature == MagickSignature);
2682 if ((columns == 0) || (rows == 0))
2683 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2684 if ((columns == image->columns) && (rows == image->rows))
2685 return(CloneImage(image,0,0,MagickTrue,exception));
2686 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2687 if (sample_image == (Image *) NULL)
2688 return((Image *) NULL);
2689 /*
2690 Allocate scan line buffer and column offset buffers.
2691 */
cristybb503372010-05-27 20:51:26 +00002692 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002693 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002694 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002695 {
2696 sample_image=DestroyImage(sample_image);
2697 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2698 }
cristybb503372010-05-27 20:51:26 +00002699 for (x=0; x < (ssize_t) sample_image->columns; x++)
2700 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002701 sample_image->columns);
2702 /*
2703 Sample each row.
2704 */
2705 status=MagickTrue;
2706 progress=0;
2707 image_view=AcquireCacheView(image);
2708 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002709#if defined(MAGICKCORE_OPENMP_SUPPORT)
2710 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002711#endif
cristybb503372010-05-27 20:51:26 +00002712 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002713 {
cristy3ed852e2009-09-05 21:47:34 +00002714 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002715 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002716
2717 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002718 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002719
2720 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002721 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002722
cristy3ed852e2009-09-05 21:47:34 +00002723 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002724 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002725
cristy03dbbd22010-09-19 23:04:47 +00002726 register ssize_t
2727 x;
2728
cristy9af9b5d2010-08-15 17:04:28 +00002729 ssize_t
2730 y_offset;
2731
cristy3ed852e2009-09-05 21:47:34 +00002732 if (status == MagickFalse)
2733 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002734 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2735 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002736 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2737 exception);
2738 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2739 exception);
2740 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2741 {
2742 status=MagickFalse;
2743 continue;
2744 }
2745 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2746 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2747 /*
2748 Sample each column.
2749 */
cristybb503372010-05-27 20:51:26 +00002750 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002751 *q++=p[x_offset[x]];
2752 if ((image->storage_class == PseudoClass) ||
2753 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002754 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002755 sample_indexes[x]=indexes[x_offset[x]];
2756 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2757 status=MagickFalse;
2758 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2759 {
2760 MagickBooleanType
2761 proceed;
2762
cristyb5d5f722009-11-04 03:03:49 +00002763#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002764 #pragma omp critical (MagickCore_SampleImage)
2765#endif
2766 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2767 if (proceed == MagickFalse)
2768 status=MagickFalse;
2769 }
2770 }
2771 image_view=DestroyCacheView(image_view);
2772 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002773 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002774 sample_image->type=image->type;
2775 return(sample_image);
2776}
2777
2778/*
2779%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2780% %
2781% %
2782% %
2783% S c a l e I m a g e %
2784% %
2785% %
2786% %
2787%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2788%
2789% ScaleImage() changes the size of an image to the given dimensions.
2790%
2791% The format of the ScaleImage method is:
2792%
cristybb503372010-05-27 20:51:26 +00002793% Image *ScaleImage(const Image *image,const size_t columns,
2794% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002795%
2796% A description of each parameter follows:
2797%
2798% o image: the image.
2799%
2800% o columns: the number of columns in the scaled image.
2801%
2802% o rows: the number of rows in the scaled image.
2803%
2804% o exception: return any errors or warnings in this structure.
2805%
2806*/
cristybb503372010-05-27 20:51:26 +00002807MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2808 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002809{
2810#define ScaleImageTag "Scale/Image"
2811
cristyed6cb232010-01-20 03:07:53 +00002812 CacheView
2813 *image_view,
2814 *scale_view;
2815
cristy3ed852e2009-09-05 21:47:34 +00002816 Image
2817 *scale_image;
2818
cristy3ed852e2009-09-05 21:47:34 +00002819 MagickBooleanType
2820 next_column,
2821 next_row,
2822 proceed;
2823
2824 MagickPixelPacket
2825 pixel,
2826 *scale_scanline,
2827 *scanline,
2828 *x_vector,
2829 *y_vector,
2830 zero;
2831
cristy3ed852e2009-09-05 21:47:34 +00002832 PointInfo
2833 scale,
2834 span;
2835
cristybb503372010-05-27 20:51:26 +00002836 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002837 i;
2838
cristy9af9b5d2010-08-15 17:04:28 +00002839 ssize_t
2840 number_rows,
2841 y;
2842
cristy3ed852e2009-09-05 21:47:34 +00002843 /*
2844 Initialize scaled image attributes.
2845 */
2846 assert(image != (const Image *) NULL);
2847 assert(image->signature == MagickSignature);
2848 if (image->debug != MagickFalse)
2849 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2850 assert(exception != (ExceptionInfo *) NULL);
2851 assert(exception->signature == MagickSignature);
2852 if ((columns == 0) || (rows == 0))
2853 return((Image *) NULL);
2854 if ((columns == image->columns) && (rows == image->rows))
2855 return(CloneImage(image,0,0,MagickTrue,exception));
2856 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2857 if (scale_image == (Image *) NULL)
2858 return((Image *) NULL);
2859 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2860 {
2861 InheritException(exception,&scale_image->exception);
2862 scale_image=DestroyImage(scale_image);
2863 return((Image *) NULL);
2864 }
2865 /*
2866 Allocate memory.
2867 */
2868 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2869 sizeof(*x_vector));
2870 scanline=x_vector;
2871 if (image->rows != scale_image->rows)
2872 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2873 sizeof(*scanline));
2874 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2875 scale_image->columns,sizeof(*scale_scanline));
2876 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2877 sizeof(*y_vector));
2878 if ((scanline == (MagickPixelPacket *) NULL) ||
2879 (scale_scanline == (MagickPixelPacket *) NULL) ||
2880 (x_vector == (MagickPixelPacket *) NULL) ||
2881 (y_vector == (MagickPixelPacket *) NULL))
2882 {
2883 scale_image=DestroyImage(scale_image);
2884 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2885 }
2886 /*
2887 Scale image.
2888 */
2889 number_rows=0;
2890 next_row=MagickTrue;
2891 span.y=1.0;
2892 scale.y=(double) scale_image->rows/(double) image->rows;
2893 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2894 sizeof(*y_vector));
2895 GetMagickPixelPacket(image,&pixel);
2896 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2897 i=0;
cristyed6cb232010-01-20 03:07:53 +00002898 image_view=AcquireCacheView(image);
2899 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002900 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002901 {
2902 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002903 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002904
2905 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002906 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002907
2908 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002909 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002910
cristy3ed852e2009-09-05 21:47:34 +00002911 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002912 *restrict s,
2913 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002914
2915 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002916 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002917
cristy9af9b5d2010-08-15 17:04:28 +00002918 register ssize_t
2919 x;
2920
cristyed6cb232010-01-20 03:07:53 +00002921 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2922 exception);
cristy3ed852e2009-09-05 21:47:34 +00002923 if (q == (PixelPacket *) NULL)
2924 break;
2925 scale_indexes=GetAuthenticIndexQueue(scale_image);
2926 if (scale_image->rows == image->rows)
2927 {
2928 /*
2929 Read a new scanline.
2930 */
cristyed6cb232010-01-20 03:07:53 +00002931 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2932 exception);
cristy3ed852e2009-09-05 21:47:34 +00002933 if (p == (const PixelPacket *) NULL)
2934 break;
cristyed6cb232010-01-20 03:07:53 +00002935 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002936 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002937 {
cristyce70c172010-01-07 17:15:30 +00002938 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2939 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2940 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002941 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002942 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002943 if (indexes != (IndexPacket *) NULL)
2944 x_vector[x].index=(MagickRealType) indexes[x];
2945 p++;
2946 }
2947 }
2948 else
2949 {
2950 /*
2951 Scale Y direction.
2952 */
2953 while (scale.y < span.y)
2954 {
cristy9af9b5d2010-08-15 17:04:28 +00002955 if ((next_row != MagickFalse) &&
2956 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002957 {
2958 /*
2959 Read a new scanline.
2960 */
cristyed6cb232010-01-20 03:07:53 +00002961 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2962 exception);
cristy3ed852e2009-09-05 21:47:34 +00002963 if (p == (const PixelPacket *) NULL)
2964 break;
cristyed6cb232010-01-20 03:07:53 +00002965 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002966 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002967 {
cristyce70c172010-01-07 17:15:30 +00002968 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2969 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2970 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002971 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002972 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002973 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002974 if (indexes != (IndexPacket *) NULL)
2975 x_vector[x].index=(MagickRealType) indexes[x];
2976 p++;
2977 }
2978 number_rows++;
2979 }
cristybb503372010-05-27 20:51:26 +00002980 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002981 {
2982 y_vector[x].red+=scale.y*x_vector[x].red;
2983 y_vector[x].green+=scale.y*x_vector[x].green;
2984 y_vector[x].blue+=scale.y*x_vector[x].blue;
2985 if (scale_image->matte != MagickFalse)
2986 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2987 if (scale_indexes != (IndexPacket *) NULL)
2988 y_vector[x].index+=scale.y*x_vector[x].index;
2989 }
2990 span.y-=scale.y;
2991 scale.y=(double) scale_image->rows/(double) image->rows;
2992 next_row=MagickTrue;
2993 }
cristybb503372010-05-27 20:51:26 +00002994 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002995 {
2996 /*
2997 Read a new scanline.
2998 */
cristyed6cb232010-01-20 03:07:53 +00002999 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
3000 exception);
cristy3ed852e2009-09-05 21:47:34 +00003001 if (p == (const PixelPacket *) NULL)
3002 break;
cristyed6cb232010-01-20 03:07:53 +00003003 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00003004 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003005 {
cristyce70c172010-01-07 17:15:30 +00003006 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
3007 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
3008 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003009 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00003010 x_vector[x].opacity=(MagickRealType)
3011 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003012 if (indexes != (IndexPacket *) NULL)
3013 x_vector[x].index=(MagickRealType) indexes[x];
3014 p++;
3015 }
3016 number_rows++;
3017 next_row=MagickFalse;
3018 }
3019 s=scanline;
cristybb503372010-05-27 20:51:26 +00003020 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003021 {
3022 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3023 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3024 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3025 if (image->matte != MagickFalse)
3026 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3027 if (scale_indexes != (IndexPacket *) NULL)
3028 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3029 s->red=pixel.red;
3030 s->green=pixel.green;
3031 s->blue=pixel.blue;
3032 if (scale_image->matte != MagickFalse)
3033 s->opacity=pixel.opacity;
3034 if (scale_indexes != (IndexPacket *) NULL)
3035 s->index=pixel.index;
3036 s++;
3037 y_vector[x]=zero;
3038 }
3039 scale.y-=span.y;
3040 if (scale.y <= 0)
3041 {
3042 scale.y=(double) scale_image->rows/(double) image->rows;
3043 next_row=MagickTrue;
3044 }
3045 span.y=1.0;
3046 }
3047 if (scale_image->columns == image->columns)
3048 {
3049 /*
3050 Transfer scanline to scaled image.
3051 */
3052 s=scanline;
cristybb503372010-05-27 20:51:26 +00003053 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003054 {
cristyce70c172010-01-07 17:15:30 +00003055 q->red=ClampToQuantum(s->red);
3056 q->green=ClampToQuantum(s->green);
3057 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003058 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003059 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003060 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003061 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003062 q++;
3063 s++;
3064 }
3065 }
3066 else
3067 {
3068 /*
3069 Scale X direction.
3070 */
3071 pixel=zero;
3072 next_column=MagickFalse;
3073 span.x=1.0;
3074 s=scanline;
3075 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003076 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003077 {
3078 scale.x=(double) scale_image->columns/(double) image->columns;
3079 while (scale.x >= span.x)
3080 {
3081 if (next_column != MagickFalse)
3082 {
3083 pixel=zero;
3084 t++;
3085 }
3086 pixel.red+=span.x*s->red;
3087 pixel.green+=span.x*s->green;
3088 pixel.blue+=span.x*s->blue;
3089 if (image->matte != MagickFalse)
3090 pixel.opacity+=span.x*s->opacity;
3091 if (scale_indexes != (IndexPacket *) NULL)
3092 pixel.index+=span.x*s->index;
3093 t->red=pixel.red;
3094 t->green=pixel.green;
3095 t->blue=pixel.blue;
3096 if (scale_image->matte != MagickFalse)
3097 t->opacity=pixel.opacity;
3098 if (scale_indexes != (IndexPacket *) NULL)
3099 t->index=pixel.index;
3100 scale.x-=span.x;
3101 span.x=1.0;
3102 next_column=MagickTrue;
3103 }
3104 if (scale.x > 0)
3105 {
3106 if (next_column != MagickFalse)
3107 {
3108 pixel=zero;
3109 next_column=MagickFalse;
3110 t++;
3111 }
3112 pixel.red+=scale.x*s->red;
3113 pixel.green+=scale.x*s->green;
3114 pixel.blue+=scale.x*s->blue;
3115 if (scale_image->matte != MagickFalse)
3116 pixel.opacity+=scale.x*s->opacity;
3117 if (scale_indexes != (IndexPacket *) NULL)
3118 pixel.index+=scale.x*s->index;
3119 span.x-=scale.x;
3120 }
3121 s++;
3122 }
3123 if (span.x > 0)
3124 {
3125 s--;
3126 pixel.red+=span.x*s->red;
3127 pixel.green+=span.x*s->green;
3128 pixel.blue+=span.x*s->blue;
3129 if (scale_image->matte != MagickFalse)
3130 pixel.opacity+=span.x*s->opacity;
3131 if (scale_indexes != (IndexPacket *) NULL)
3132 pixel.index+=span.x*s->index;
3133 }
3134 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003135 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003136 {
3137 t->red=pixel.red;
3138 t->green=pixel.green;
3139 t->blue=pixel.blue;
3140 if (scale_image->matte != MagickFalse)
3141 t->opacity=pixel.opacity;
3142 if (scale_indexes != (IndexPacket *) NULL)
3143 t->index=pixel.index;
3144 }
3145 /*
3146 Transfer scanline to scaled image.
3147 */
3148 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003149 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003150 {
cristyce70c172010-01-07 17:15:30 +00003151 q->red=ClampToQuantum(t->red);
3152 q->green=ClampToQuantum(t->green);
3153 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003154 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003155 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003156 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003157 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003158 t++;
3159 q++;
3160 }
3161 }
cristyed6cb232010-01-20 03:07:53 +00003162 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003163 break;
cristy96b16132010-08-29 17:19:52 +00003164 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3165 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003166 if (proceed == MagickFalse)
3167 break;
3168 }
cristyed6cb232010-01-20 03:07:53 +00003169 scale_view=DestroyCacheView(scale_view);
3170 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003171 /*
3172 Free allocated memory.
3173 */
3174 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3175 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3176 if (scale_image->rows != image->rows)
3177 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3178 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3179 scale_image->type=image->type;
3180 return(scale_image);
3181}
3182
anthony02b4cb42010-10-10 04:54:35 +00003183#if 0
3184 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003185/*
3186%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3187% %
3188% %
3189% %
3190+ S e t R e s i z e F i l t e r S u p p o r t %
3191% %
3192% %
3193% %
3194%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3195%
3196% SetResizeFilterSupport() specifies which IR filter to use to window
3197%
3198% The format of the SetResizeFilterSupport method is:
3199%
3200% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3201% const MagickRealType support)
3202%
3203% A description of each parameter follows:
3204%
3205% o resize_filter: the resize filter.
3206%
3207% o support: the filter spport radius.
3208%
3209*/
3210MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3211 const MagickRealType support)
3212{
3213 assert(resize_filter != (ResizeFilter *) NULL);
3214 assert(resize_filter->signature == MagickSignature);
3215 resize_filter->support=support;
3216}
anthony02b4cb42010-10-10 04:54:35 +00003217#endif
cristy3ed852e2009-09-05 21:47:34 +00003218
3219/*
3220%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3221% %
3222% %
3223% %
3224% T h u m b n a i l I m a g e %
3225% %
3226% %
3227% %
3228%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3229%
3230% ThumbnailImage() changes the size of an image to the given dimensions and
3231% removes any associated profiles. The goal is to produce small low cost
3232% thumbnail images suited for display on the Web.
3233%
3234% The format of the ThumbnailImage method is:
3235%
cristybb503372010-05-27 20:51:26 +00003236% Image *ThumbnailImage(const Image *image,const size_t columns,
3237% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003238%
3239% A description of each parameter follows:
3240%
3241% o image: the image.
3242%
3243% o columns: the number of columns in the scaled image.
3244%
3245% o rows: the number of rows in the scaled image.
3246%
3247% o exception: return any errors or warnings in this structure.
3248%
3249*/
cristy9af9b5d2010-08-15 17:04:28 +00003250MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3251 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003252{
3253#define SampleFactor 5
3254
3255 char
3256 value[MaxTextExtent];
3257
3258 const char
3259 *name;
3260
3261 Image
3262 *thumbnail_image;
3263
3264 MagickRealType
3265 x_factor,
3266 y_factor;
3267
cristybb503372010-05-27 20:51:26 +00003268 size_t
cristy3ed852e2009-09-05 21:47:34 +00003269 version;
3270
cristy9af9b5d2010-08-15 17:04:28 +00003271 struct stat
3272 attributes;
3273
cristy3ed852e2009-09-05 21:47:34 +00003274 assert(image != (Image *) NULL);
3275 assert(image->signature == MagickSignature);
3276 if (image->debug != MagickFalse)
3277 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3278 assert(exception != (ExceptionInfo *) NULL);
3279 assert(exception->signature == MagickSignature);
3280 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3281 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3282 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003283 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3284 exception);
cristy3ed852e2009-09-05 21:47:34 +00003285 else
3286 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003287 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3288 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003289 else
3290 {
3291 Image
3292 *sample_image;
3293
3294 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3295 exception);
3296 if (sample_image == (Image *) NULL)
3297 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003298 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3299 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003300 sample_image=DestroyImage(sample_image);
3301 }
3302 if (thumbnail_image == (Image *) NULL)
3303 return(thumbnail_image);
3304 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3305 if (thumbnail_image->matte == MagickFalse)
3306 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3307 thumbnail_image->depth=8;
3308 thumbnail_image->interlace=NoInterlace;
3309 /*
3310 Strip all profiles except color profiles.
3311 */
3312 ResetImageProfileIterator(thumbnail_image);
3313 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3314 {
3315 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3316 {
cristy2b726bd2010-01-11 01:05:39 +00003317 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003318 ResetImageProfileIterator(thumbnail_image);
3319 }
3320 name=GetNextImageProfile(thumbnail_image);
3321 }
3322 (void) DeleteImageProperty(thumbnail_image,"comment");
3323 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003324 if (strstr(image->magick_filename,"//") == (char *) NULL)
3325 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003326 image->magick_filename);
3327 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3328 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3329 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3330 {
cristye8c25f92010-06-03 00:53:06 +00003331 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003332 attributes.st_mtime);
3333 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3334 }
cristye8c25f92010-06-03 00:53:06 +00003335 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003336 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003337 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003338 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003339 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3340 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3341 LocaleLower(value);
3342 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3343 (void) SetImageProperty(thumbnail_image,"software",
3344 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003345 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3346 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003347 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003348 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003349 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003350 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003351 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3352 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003353 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3354 return(thumbnail_image);
3355}