blob: 2a441de8dc12830e21098bc2676c531d68ed8610 [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% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 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"
cristya6a18782010-11-15 01:56:25 +000065#include "magick/resample-private.h"
cristy3ed852e2009-09-05 21:47:34 +000066#include "magick/resize.h"
67#include "magick/resize-private.h"
68#include "magick/string_.h"
cristyf2f27272009-12-17 14:48:46 +000069#include "magick/string-private.h"
cristy3ed852e2009-09-05 21:47:34 +000070#include "magick/thread-private.h"
71#include "magick/utility.h"
72#include "magick/version.h"
73#if defined(MAGICKCORE_LQR_DELEGATE)
74#include <lqr.h>
75#endif
76
77/*
78 Typedef declarations.
79*/
80struct _ResizeFilter
81{
82 MagickRealType
83 (*filter)(const MagickRealType,const ResizeFilter *),
84 (*window)(const MagickRealType,const ResizeFilter *),
cristy33b1c162010-01-23 22:51:51 +000085 support, /* filter region of support - the filter support limit */
86 window_support, /* window support, usally equal to support (expert only) */
anthony55f12332010-09-10 01:13:02 +000087 scale, /* dimension scaling to fit window support (usally 1.0) */
cristy33b1c162010-01-23 22:51:51 +000088 blur, /* x-scale (blur-sharpen) */
cristy5cce74b2010-11-15 03:24:28 +000089 coefficient[7]; /* cubic coefficents for BC-cubic spline filters */
cristy3ed852e2009-09-05 21:47:34 +000090
cristybb503372010-05-27 20:51:26 +000091 size_t
cristy3ed852e2009-09-05 21:47:34 +000092 signature;
93};
94
95/*
96 Forward declaractions.
97*/
98static MagickRealType
99 I0(MagickRealType x),
anthonyb6d08c52010-09-13 01:17:04 +0000100 BesselOrderOne(MagickRealType),
anthony07a3f7f2010-09-16 03:03:11 +0000101 Sinc(const MagickRealType, const ResizeFilter *),
anthonyba5a7c32010-09-15 02:42:25 +0000102 SincFast(const MagickRealType, const ResizeFilter *);
cristy3ed852e2009-09-05 21:47:34 +0000103
104/*
105%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106% %
107% %
108% %
109+ F i l t e r F u n c t i o n s %
110% %
111% %
112% %
113%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114%
cristy33b1c162010-01-23 22:51:51 +0000115% These are the various filter and windowing functions that are provided.
cristy3ed852e2009-09-05 21:47:34 +0000116%
cristy33b1c162010-01-23 22:51:51 +0000117% They are internal to this module only. See AcquireResizeFilterInfo() for
118% details of the access to these functions, via the GetResizeFilterSupport()
119% and GetResizeFilterWeight() API interface.
cristy3ed852e2009-09-05 21:47:34 +0000120%
121% The individual filter functions have this format...
122%
123% static MagickRealtype *FilterName(const MagickRealType x,
124% const MagickRealType support)
125%
cristy33b1c162010-01-23 22:51:51 +0000126% A description of each parameter follows:
cristy3ed852e2009-09-05 21:47:34 +0000127%
cristy33b1c162010-01-23 22:51:51 +0000128% o x: the distance from the sampling point generally in the range of 0 to
129% support. The GetResizeFilterWeight() ensures this a positive value.
130%
131% o resize_filter: current filter information. This allows function to
132% access support, and possibly other pre-calculated information defining
133% the functions.
cristy3ed852e2009-09-05 21:47:34 +0000134%
135*/
136
cristyc5c6f662010-09-22 14:23:02 +0000137#define MagickPIL ((MagickRealType) 3.14159265358979323846264338327950288420L)
cristy560d8182010-09-08 22:36:25 +0000138
anthony48f77622010-10-03 14:32:31 +0000139static MagickRealType Jinc(const MagickRealType x,
cristy3ed852e2009-09-05 21:47:34 +0000140 const ResizeFilter *magick_unused(resize_filter))
141{
142 /*
anthony48f77622010-10-03 14:32:31 +0000143 See Pratt "Digital Image Processing" p.97 for Jinc/Bessel functions.
cristy33b1c162010-01-23 22:51:51 +0000144 http://mathworld.wolfram.com/JincFunction.html and page 11 of
anthony48f77622010-10-03 14:32:31 +0000145 http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
146
nicolase473f722010-10-07 00:05:13 +0000147 The original "zoom" program by Paul Heckbert called this "Bessel".
148 But really it is more accurately named "Jinc".
cristy3ed852e2009-09-05 21:47:34 +0000149 */
150 if (x == 0.0)
nicolas5a36f342010-10-07 00:11:32 +0000151 return(0.5*MagickPIL);
152 return(BesselOrderOne(MagickPIL*x)/x);
cristy3ed852e2009-09-05 21:47:34 +0000153}
154
155static MagickRealType Blackman(const MagickRealType x,
cristyce70c172010-01-07 17:15:30 +0000156 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000157{
158 /*
cristy83017922010-09-05 20:45:15 +0000159 Blackman: 2nd order cosine windowing function:
cristy21ce88a2010-09-05 01:37:25 +0000160 0.42 + 0.5 cos(pi x) + 0.08 cos(2pi x)
cristy560d8182010-09-08 22:36:25 +0000161 Refactored by Chantal Racette and Nicolas Robidoux to one trig
162 call and five flops.
cristy3ed852e2009-09-05 21:47:34 +0000163 */
cristyc5c6f662010-09-22 14:23:02 +0000164 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000165 return(0.34+cospix*(0.5+cospix*0.16));
cristy3ed852e2009-09-05 21:47:34 +0000166}
167
168static MagickRealType Bohman(const MagickRealType x,
cristyce70c172010-01-07 17:15:30 +0000169 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000170{
171 /*
cristy560d8182010-09-08 22:36:25 +0000172 Bohman: 2rd Order cosine windowing function:
173 (1-x) cos(pi x) + sin(pi x) / pi.
nicolasa4f7b4a2010-09-20 20:28:38 +0000174 Refactored by Nicolas Robidoux to one trig call, one sqrt call,
175 and 7 flops, taking advantage of the fact that the support of
176 Bohman is 1 (so that we know that sin(pi x) >= 0).
cristy3ed852e2009-09-05 21:47:34 +0000177 */
cristyc5c6f662010-09-22 14:23:02 +0000178 const double cospix = cos((double) (MagickPIL*x));
nicolasa4f7b4a2010-09-20 20:28:38 +0000179 const double sinpix = sqrt(1.0-cospix*cospix);
nicolas2ffd3b22010-09-24 20:27:31 +0000180 return((1.0-x)*cospix+(1.0/MagickPIL)*sinpix);
cristy3ed852e2009-09-05 21:47:34 +0000181}
182
anthony463be1d2010-09-26 01:07:36 +0000183static MagickRealType Box(const MagickRealType magick_unused(x),
184 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000185{
186 /*
nicolas40477452010-09-27 23:42:08 +0000187 A Box filter is a equal weighting function (all weights equal).
anthonyc331dec2010-09-26 01:30:14 +0000188 DO NOT LIMIT results by support or resize point sampling will work
189 as it requests points beyond its normal 0.0 support size.
cristy3ed852e2009-09-05 21:47:34 +0000190 */
anthony463be1d2010-09-26 01:07:36 +0000191 return(1.0);
cristy3ed852e2009-09-05 21:47:34 +0000192}
193
194static MagickRealType CubicBC(const MagickRealType x,
195 const ResizeFilter *resize_filter)
196{
197 /*
198 Cubic Filters using B,C determined values:
nicolase3b9eca2010-10-24 19:48:22 +0000199 Mitchell-Netravali B= 1/3 C= 1/3 "Balanced" cubic spline filter
200 Catmull-Rom B= 0 C= 1/2 Interpolatory and exact on linears
201 Cubic B-Spline B= 1 C= 0 Spline approximation of Gaussian
202 Hermite B= 0 C= 0 Spline with small support (= 1)
cristy3ed852e2009-09-05 21:47:34 +0000203
cristy33b1c162010-01-23 22:51:51 +0000204 See paper by Mitchell and Netravali, Reconstruction Filters in Computer
205 Graphics Computer Graphics, Volume 22, Number 4, August 1988
206 http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/
cristyf59a8922010-02-28 19:51:23 +0000207 Mitchell.pdf.
cristy3ed852e2009-09-05 21:47:34 +0000208
cristy33b1c162010-01-23 22:51:51 +0000209 Coefficents are determined from B,C values:
anthony06b1edf2010-10-25 01:19:50 +0000210 P0 = ( 6 - 2*B )/6 = coeff[0]
cristy3ed852e2009-09-05 21:47:34 +0000211 P1 = 0
anthony06b1edf2010-10-25 01:19:50 +0000212 P2 = (-18 +12*B + 6*C )/6 = coeff[1]
213 P3 = ( 12 - 9*B - 6*C )/6 = coeff[2]
214 Q0 = ( 8*B +24*C )/6 = coeff[3]
215 Q1 = ( -12*B -48*C )/6 = coeff[4]
216 Q2 = ( 6*B +30*C )/6 = coeff[5]
217 Q3 = ( - 1*B - 6*C )/6 = coeff[6]
cristy3ed852e2009-09-05 21:47:34 +0000218
cristy33b1c162010-01-23 22:51:51 +0000219 which are used to define the filter:
cristyf59a8922010-02-28 19:51:23 +0000220
cristy3ed852e2009-09-05 21:47:34 +0000221 P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
nicolase3b9eca2010-10-24 19:48:22 +0000222 Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x < 2
cristy3ed852e2009-09-05 21:47:34 +0000223
nicolase3b9eca2010-10-24 19:48:22 +0000224 which ensures function is continuous in value and derivative
nicolas89d73f92010-10-24 20:11:54 +0000225 (slope).
cristy3ed852e2009-09-05 21:47:34 +0000226 */
227 if (x < 1.0)
cristy5cce74b2010-11-15 03:24:28 +0000228 return(resize_filter->coefficient[0]+x*(x*
229 (resize_filter->coefficient[1]+x*resize_filter->coefficient[2])));
cristy3ed852e2009-09-05 21:47:34 +0000230 if (x < 2.0)
cristy5cce74b2010-11-15 03:24:28 +0000231 return(resize_filter->coefficient[3]+x*(resize_filter->coefficient[4]+x*
232 (resize_filter->coefficient[5]+x*resize_filter->coefficient[6])));
cristy3ed852e2009-09-05 21:47:34 +0000233 return(0.0);
234}
235
236static MagickRealType Gaussian(const MagickRealType x,
anthonyf5e76ef2010-10-12 01:22:01 +0000237 const ResizeFilter *resize_filter)
cristy3ed852e2009-09-05 21:47:34 +0000238{
cristy560d8182010-09-08 22:36:25 +0000239 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000240 Gaussian with a fixed sigma = 1/2
241
anthony993d8382011-02-06 12:35:36 +0000242 Gaussian Formula (1D) ...
243 exp( -(x^2)/((2.0*sigma^2) ) / sqrt(2*PI)sigma^2))
anthonyf5e76ef2010-10-12 01:22:01 +0000244 The constants are pre-calculated...
245 exp( -coeff[0]*(x^2)) ) * coeff[1]
anthony06b1edf2010-10-25 01:19:50 +0000246 However the multiplier coefficent (1) is not needed and not used.
anthonyf5e76ef2010-10-12 01:22:01 +0000247
anthony993d8382011-02-06 12:35:36 +0000248 Gaussian Formula (2D) ...
249 exp( -(x^2)/((2.0*sigma^2) ) / (PI*sigma^2) )
250 Note that it is only a change in the normalization multiplier
251 which is not needed or used when gausian is used as a filter.
252
253 This separates the gaussian 'sigma' value from the 'blur/support'
254 settings allowing for its use in special 'small sigma' gaussians,
255 without the filter 'missing' pixels because the support becomes too
256 small.
cristy560d8182010-09-08 22:36:25 +0000257 */
cristy5cce74b2010-11-15 03:24:28 +0000258 return(exp((double)(-resize_filter->coefficient[0]*x*x)));
anthony06b1edf2010-10-25 01:19:50 +0000259}
cristy3ed852e2009-09-05 21:47:34 +0000260
261static MagickRealType Hanning(const MagickRealType x,
262 const ResizeFilter *magick_unused(resize_filter))
263{
264 /*
nicolas40477452010-09-27 23:42:08 +0000265 Cosine window function:
266 .5+.5cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000267 */
cristyc5c6f662010-09-22 14:23:02 +0000268 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000269 return(0.5+0.5*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000270}
271
272static MagickRealType Hamming(const MagickRealType x,
273 const ResizeFilter *magick_unused(resize_filter))
274{
275 /*
nicolas40477452010-09-27 23:42:08 +0000276 Offset cosine window function:
277 .54 + .46 cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000278 */
cristyc5c6f662010-09-22 14:23:02 +0000279 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000280 return(0.54+0.46*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000281}
282
283static MagickRealType Kaiser(const MagickRealType x,
284 const ResizeFilter *magick_unused(resize_filter))
285{
286#define Alpha 6.5
287#define I0A (1.0/I0(Alpha))
288
289 /*
nicolas07bac812010-09-19 18:47:02 +0000290 Kaiser Windowing Function (bessel windowing): Alpha is a free
291 value from 5 to 8 (currently hardcoded to 6.5).
292 Future: make alpha the IOA pre-calculation, an 'expert' setting.
cristy3ed852e2009-09-05 21:47:34 +0000293 */
294 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
295}
296
297static MagickRealType Lagrange(const MagickRealType x,
298 const ResizeFilter *resize_filter)
299{
cristy3ed852e2009-09-05 21:47:34 +0000300 MagickRealType
301 value;
302
cristybb503372010-05-27 20:51:26 +0000303 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000304 i;
305
cristy9af9b5d2010-08-15 17:04:28 +0000306 ssize_t
307 n,
308 order;
309
cristy3ed852e2009-09-05 21:47:34 +0000310 /*
nicolas07bac812010-09-19 18:47:02 +0000311 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the
312 lagrange function and depends on the overall support window size
313 of the filter. That is: for a support of 2, it gives a lagrange-4
314 (piecewise cubic function).
cristy3ed852e2009-09-05 21:47:34 +0000315
nicolas07bac812010-09-19 18:47:02 +0000316 "n" identifies the piece of the piecewise polynomial.
cristy3ed852e2009-09-05 21:47:34 +0000317
nicolas07bac812010-09-19 18:47:02 +0000318 See Survey: Interpolation Methods, IEEE Transactions on Medical
319 Imaging, Vol 18, No 11, November 1999, p1049-1075, -- Equation 27
320 on p1064.
cristy3ed852e2009-09-05 21:47:34 +0000321 */
322 if (x > resize_filter->support)
323 return(0.0);
cristybb503372010-05-27 20:51:26 +0000324 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
anthonyc2d07db2010-09-15 23:47:40 +0000325 /*n=(ssize_t)((1.0*order)/2.0+x); -- which piece does x belong to */
326 n = (ssize_t)(resize_filter->window_support + x);
cristy3ed852e2009-09-05 21:47:34 +0000327 value=1.0f;
328 for (i=0; i < order; i++)
329 if (i != n)
330 value*=(n-i-x)/(n-i);
331 return(value);
332}
333
334static MagickRealType Quadratic(const MagickRealType x,
335 const ResizeFilter *magick_unused(resize_filter))
336{
337 /*
338 2rd order (quadratic) B-Spline approximation of Gaussian.
339 */
340 if (x < 0.5)
341 return(0.75-x*x);
342 if (x < 1.5)
343 return(0.5*(x-1.5)*(x-1.5));
344 return(0.0);
345}
346
anthony07a3f7f2010-09-16 03:03:11 +0000347static MagickRealType Sinc(const MagickRealType x,
cristy3ed852e2009-09-05 21:47:34 +0000348 const ResizeFilter *magick_unused(resize_filter))
349{
anthony720660f2010-09-07 10:05:14 +0000350 /*
nicolas40477452010-09-27 23:42:08 +0000351 Scaled sinc(x) function using a trig call:
nicolas07bac812010-09-19 18:47:02 +0000352 sinc(x) == sin(pi x)/(pi x).
anthony720660f2010-09-07 10:05:14 +0000353 */
anthony2d9b8b52010-09-14 08:31:07 +0000354 if (x != 0.0)
cristy560d8182010-09-08 22:36:25 +0000355 {
cristyc5c6f662010-09-22 14:23:02 +0000356 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
nicolas07bac812010-09-19 18:47:02 +0000357 return(sin((double) pix)/pix);
cristy560d8182010-09-08 22:36:25 +0000358 }
nicolas2ffd3b22010-09-24 20:27:31 +0000359 return((MagickRealType) 1.0);
anthony720660f2010-09-07 10:05:14 +0000360}
361
anthonyba5a7c32010-09-15 02:42:25 +0000362static MagickRealType SincFast(const MagickRealType x,
anthony720660f2010-09-07 10:05:14 +0000363 const ResizeFilter *magick_unused(resize_filter))
364{
cristy560d8182010-09-08 22:36:25 +0000365 /*
366 Approximations of the sinc function sin(pi x)/(pi x) over the
367 interval [-4,4] constructed by Nicolas Robidoux and Chantal
368 Racette with funding from the Natural Sciences and Engineering
369 Research Council of Canada.
nicolas07bac812010-09-19 18:47:02 +0000370
371 Although the approximations are polynomials (for low order of
372 approximation) and quotients of polynomials (for higher order of
373 approximation) and consequently are similar in form to Taylor
374 polynomials/Pade approximants, the approximations are computed
375 with a completely different technique.
376
377 Summary: These approximations are "the best" in terms of bang
378 (accuracy) for the buck (flops). More specifically: Among the
379 polynomial quotients that can be computed using a fixed number of
380 flops (with a given "+ - * / budget"), the chosen polynomial
381 quotient is the one closest to the approximated function with
382 respect to maximum absolute relative error over the given
383 interval.
384
385 The Remez algorithm, as implemented in the boost library's minimax
nicolas3aab40c2010-09-19 21:14:15 +0000386 package, is the key to the construction:
nicolas07bac812010-09-19 18:47:02 +0000387 http://www.boost.org/doc/libs/1_36_0/libs/math/doc/...
388 ...sf_and_dist/html/math_toolkit/backgrounders/remez.html
cristy560d8182010-09-08 22:36:25 +0000389 */
nicolas3aab40c2010-09-19 21:14:15 +0000390 /*
391 If outside of the interval of approximation, use the standard trig
392 formula.
393 */
anthony2d9b8b52010-09-14 08:31:07 +0000394 if (x > 4.0)
cristy03dbbd22010-09-19 23:04:47 +0000395 {
cristyc5c6f662010-09-22 14:23:02 +0000396 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
cristy03dbbd22010-09-19 23:04:47 +0000397 return(sin((double) pix)/pix);
398 }
anthony2d9b8b52010-09-14 08:31:07 +0000399 {
nicolas07bac812010-09-19 18:47:02 +0000400 /*
401 The approximations only depend on x^2 (sinc is an even
402 function).
403 */
404 const MagickRealType xx = x*x;
cristy83017922010-09-05 20:45:15 +0000405#if MAGICKCORE_QUANTUM_DEPTH <= 8
406 /*
anthony2d9b8b52010-09-14 08:31:07 +0000407 Maximum absolute relative error 6.3e-6 < 1/2^17.
cristy738e7562010-09-01 12:48:07 +0000408 */
409 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
410 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
411 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
412 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
413 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
414 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
415 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
416 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
nicolas3aab40c2010-09-19 21:14:15 +0000417 const MagickRealType p =
418 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
nicolas07bac812010-09-19 18:47:02 +0000419 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
anthony2d9b8b52010-09-14 08:31:07 +0000420#elif MAGICKCORE_QUANTUM_DEPTH <= 16
cristydbeb3eb2010-09-09 13:41:36 +0000421 /*
anthony2d9b8b52010-09-14 08:31:07 +0000422 Max. abs. rel. error 2.2e-8 < 1/2^25.
cristydbeb3eb2010-09-09 13:41:36 +0000423 */
424 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
425 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
nicolas3aab40c2010-09-19 21:14:15 +0000426 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
427 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
428 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
429 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
430 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
cristydbeb3eb2010-09-09 13:41:36 +0000431 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
432 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
433 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
anthony853d6972010-10-08 06:01:31 +0000434 const MagickRealType p =
nicolas3aab40c2010-09-19 21:14:15 +0000435 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 +0000436 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
nicolasc2d28f02010-09-27 18:56:15 +0000437#else
nicolas3aab40c2010-09-19 21:14:15 +0000438 /*
439 Max. abs. rel. error 1.2e-12 < 1/2^39.
440 */
441 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
442 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
443 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
444 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
445 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
446 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
447 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
448 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
449 const MagickRealType p =
450 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
451 const MagickRealType d0 = 1.0L;
452 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
453 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
454 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
455 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
456 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
457 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
cristy657c6352010-08-29 14:05:08 +0000458#endif
cristy83017922010-09-05 20:45:15 +0000459 }
cristy3ed852e2009-09-05 21:47:34 +0000460}
461
462static MagickRealType Triangle(const MagickRealType x,
463 const ResizeFilter *magick_unused(resize_filter))
464{
465 /*
nicolas0edb0862010-09-19 18:56:19 +0000466 1st order (linear) B-Spline, bilinear interpolation, Tent 1D
anthony06b1edf2010-10-25 01:19:50 +0000467 filter, or a Bartlett 2D Cone filter. Also used as a
468 Bartlett Windowing function for Sinc().
cristy3ed852e2009-09-05 21:47:34 +0000469 */
470 if (x < 1.0)
471 return(1.0-x);
472 return(0.0);
473}
474
475static MagickRealType Welsh(const MagickRealType x,
476 const ResizeFilter *magick_unused(resize_filter))
477{
478 /*
479 Welsh parabolic windowing filter.
480 */
cristy560d8182010-09-08 22:36:25 +0000481 if (x < 1.0)
cristy3ed852e2009-09-05 21:47:34 +0000482 return(1.0-x*x);
483 return(0.0);
484}
485
486/*
487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
488% %
489% %
490% %
491+ A c q u i r e R e s i z e F i l t e r %
492% %
493% %
494% %
495%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
496%
nicolas07bac812010-09-19 18:47:02 +0000497% AcquireResizeFilter() allocates the ResizeFilter structure. Choose
498% from these filters:
cristy3ed852e2009-09-05 21:47:34 +0000499%
500% FIR (Finite impulse Response) Filters
501% Box Triangle Quadratic
502% Cubic Hermite Catrom
503% Mitchell
504%
505% IIR (Infinite impulse Response) Filters
anthony48f77622010-10-03 14:32:31 +0000506% Gaussian Sinc Jinc (Bessel)
cristy3ed852e2009-09-05 21:47:34 +0000507%
anthony48f77622010-10-03 14:32:31 +0000508% Windowed Sinc/Jinc Filters
cristy3ed852e2009-09-05 21:47:34 +0000509% Blackman Hanning Hamming
anthony48f77622010-10-03 14:32:31 +0000510% Kaiser Lanczos
cristy3ed852e2009-09-05 21:47:34 +0000511%
anthony61b5ddd2010-10-05 02:33:31 +0000512% Special purpose Filters
anthony06b1edf2010-10-25 01:19:50 +0000513% SincFast LanczosSharp Lanczos2D Lanczos2DSharp Robidoux
anthony61b5ddd2010-10-05 02:33:31 +0000514%
anthony48f77622010-10-03 14:32:31 +0000515% The users "-filter" selection is used to lookup the default 'expert'
516% settings for that filter from a internal table. However any provided
517% 'expert' settings (see below) may override this selection.
518%
519% FIR filters are used as is, and are limited to that filters support
nicolas07bac812010-09-19 18:47:02 +0000520% window (unless over-ridden). 'Gaussian' while classed as an IIR
anthonyc331dec2010-09-26 01:30:14 +0000521% filter, is also simply clipped by its support size (currently 1.5
anthonyf5e76ef2010-10-12 01:22:01 +0000522% or approximatally 3*sigma as recommended by many references)
cristy3ed852e2009-09-05 21:47:34 +0000523%
anthony152700d2010-10-28 02:43:18 +0000524% The special a 'cylindrical' filter flag will promote the default
525% 4-lobed Windowed Sinc filter to a 3-lobed Windowed Jinc equivelent,
526% which is better suited to this style of image resampling. This
527% typically happens when using such a filter for images distortions.
cristy3ed852e2009-09-05 21:47:34 +0000528%
anthony152700d2010-10-28 02:43:18 +0000529% Directly requesting 'Sinc', 'Jinc' function as a filter will force
530% the use of function without any windowing, or promotion for
531% cylindrical usage. This is not recommended, except by image
532% processing experts, especially as part of expert option filter
533% function selection.
anthony06b1edf2010-10-25 01:19:50 +0000534%
anthony48f77622010-10-03 14:32:31 +0000535% Two forms of the 'Sinc' function are available: Sinc and SincFast.
nicolas07bac812010-09-19 18:47:02 +0000536% Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
537% selected if the user specifically specifies the use of a Sinc
538% filter. SincFast uses highly accurate (and fast) polynomial (low Q)
anthony48f77622010-10-03 14:32:31 +0000539% and rational (high Q) approximations, and will be used by default in
540% most cases.
anthonyba5a7c32010-09-15 02:42:25 +0000541%
nicolasfc612942010-11-14 17:23:29 +0000542% The Lanczos filter is a special 3-lobed Sinc-windowed Sinc filter
543% (promoted to Jinc-windowed Jinc for cylindrical (Elliptical
544% Weighted Average) use). The Sinc version is the most popular
545% windowed filter.
anthony152700d2010-10-28 02:43:18 +0000546%
nicolasfc612942010-11-14 17:23:29 +0000547% LanczosSharp is a slightly sharpened (blur=0.9812505644269356 < 1)
nicolas2ab65102010-11-15 06:03:11 +0000548% form of the Lanczos filter, specifically designed for EWA
549% distortion (as a Jinc-Jinc); it can also be used as a slightly
550% sharper orthogonal Lanczos (Sinc-Sinc) filter. The chosen blur
551% value comes as close as possible to satisfying the following
552% condition without changing the character of the corresponding EWA
553% filter:
anthony152700d2010-10-28 02:43:18 +0000554%
555% 'No-Op' Vertical and Horizontal Line Preservation Condition:
556% Images with only vertical or horizontal features are preserved
557% when performing 'no-op" with EWA distortion.
558%
nicolas2d3579a2010-11-13 15:31:28 +0000559% The Lanczos2 and Lanczos2Sharp filters are 2-lobe versions of the
560% Lanczos filters. The 'sharp' version uses a blur factor of
nicolasca48bce2010-11-14 17:54:53 +0000561% 0.9549963639785485, again chosen because the resulting EWA filter
nicolasc22cc712010-11-13 16:39:10 +0000562% comes as close as possible to satisfying the above
nicolasca48bce2010-11-14 17:54:53 +0000563% condition.
anthony06b1edf2010-10-25 01:19:50 +0000564%
nicolasb7dff642010-10-25 02:04:14 +0000565% Robidoux is another filter tuned for EWA. It is the Keys cubic
anthony152700d2010-10-28 02:43:18 +0000566% filter defined by B=(228 - 108 sqrt(2))/199. Robidoux satisfies the
nicolasb7dff642010-10-25 02:04:14 +0000567% "'No-Op' Vertical and Horizontal Line Preservation Condition"
nicolasc22cc712010-11-13 16:39:10 +0000568% exactly, and it moderately blurs high frequency 'pixel-hash'
569% patterns under no-op. It turns out to be close to both Mitchell
570% and Lanczos2Sharp. For example, its first crossing is at (36
571% sqrt(2) + 123)/(72 sqrt(2) + 47), almost the same as the first
572% crossing of Mitchell and Lanczos2Sharp.
anthony152700d2010-10-28 02:43:18 +0000573%
anthony61b5ddd2010-10-05 02:33:31 +0000574%
nicolas3061b8a2010-10-22 16:34:52 +0000575% 'EXPERT' OPTIONS:
nicolasce6dc292010-10-22 16:23:07 +0000576%
anthony152700d2010-10-28 02:43:18 +0000577% These artifact "defines" are not recommended for production use
578% without expert knowledge of resampling, filtering, and the effects
579% they have on the resulting resampled (resize ro distorted) image.
nicolas3061b8a2010-10-22 16:34:52 +0000580%
anthony152700d2010-10-28 02:43:18 +0000581% They can be used to override any and all filter default, and it is
582% recommended you make good use of "filter:verbose" to make sure that
583% the overall effect of your selection (before and after) is as
584% expected.
nicolas3061b8a2010-10-22 16:34:52 +0000585%
anthony28ad1d72010-10-26 06:30:24 +0000586% "filter:verbose" controls whether to output the exact results of
587% the filter selections made, as well as plotting data for
588% graphing the resulting filter over the filters support range.
cristy3ed852e2009-09-05 21:47:34 +0000589%
anthony48f77622010-10-03 14:32:31 +0000590% "filter:filter" Select the main function associated with
591% this filter name, as the weighting function of the filter.
592% This can be used to set a windowing function as a weighting
593% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000594%
nicolas3061b8a2010-10-22 16:34:52 +0000595% If a "filter:window" operation has not been provided, then a
596% 'Box' windowing function will be set to denote that no
597% windowing function is being used.
cristy3ed852e2009-09-05 21:47:34 +0000598%
nicolas3061b8a2010-10-22 16:34:52 +0000599% "filter:window" Select this windowing function for the filter.
600% While any filter could be used as a windowing function, using
601% the 'first lobe' of that filter over the whole support
602% window, using a non-windowing function is not advisible. If
603% no weighting filter function is specifed a 'SincFast' filter
604% will be used.
cristy3ed852e2009-09-05 21:47:34 +0000605%
nicolas3061b8a2010-10-22 16:34:52 +0000606% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
607% This a simpler method of setting filter support size that
608% will correctly handle the Sinc/Jinc switch for an operators
609% filtering requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000610%
nicolas3061b8a2010-10-22 16:34:52 +0000611% "filter:support" Set the support size for filtering to the size
612% given This not recommended for Sinc/Jinc windowed filters
613% (lobes should be used instead). This will override any
614% 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000615%
nicolas3061b8a2010-10-22 16:34:52 +0000616% "filter:win-support" Scale windowing function to this size
617% instead. This causes the windowing (or self-windowing
618% Lagrange filter) to act is if the support window it much much
619% larger than what is actually supplied to the calling
620% operator. The filter however is still clipped to the real
621% support size given, by the support range suppiled to the
622% caller. If unset this will equal the normal filter support
anthonyb6d08c52010-09-13 01:17:04 +0000623% size.
624%
nicolas3061b8a2010-10-22 16:34:52 +0000625% "filter:blur" Scale the filter and support window by this amount.
cristy3ed852e2009-09-05 21:47:34 +0000626% A value >1 will generally result in a more burred image with
627% more ringing effects, while a value <1 will sharpen the
anthony152700d2010-10-28 02:43:18 +0000628% resulting image with more aliasing effects.
cristy3ed852e2009-09-05 21:47:34 +0000629%
nicolas3061b8a2010-10-22 16:34:52 +0000630% "filter:sigma" The sigma value to use for the Gaussian filter
anthony993d8382011-02-06 12:35:36 +0000631% only. Defaults to '1/2'. Using a different sigme effectially
632% provides a method of using the filter as a 'blur' convolution.
633% Particularly when using it for Distort.
anthonyf5e76ef2010-10-12 01:22:01 +0000634%
cristy3ed852e2009-09-05 21:47:34 +0000635% "filter:b"
nicolas3061b8a2010-10-22 16:34:52 +0000636% "filter:c" Override the preset B,C values for a Cubic type of
637% filter If only one of these are given it is assumes to be a
638% 'Keys' type of filter such that B+2C=1, where Keys 'alpha'
639% value = C
cristy3ed852e2009-09-05 21:47:34 +0000640%
anthony06b1edf2010-10-25 01:19:50 +0000641% Examples:
cristy3ed852e2009-09-05 21:47:34 +0000642%
nicolas6e1267a2010-10-22 16:35:52 +0000643% Set a true un-windowed Sinc filter with 10 lobes (very slow):
anthony48f77622010-10-03 14:32:31 +0000644% -define filter:filter=Sinc
645% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000646%
nicolas6e1267a2010-10-22 16:35:52 +0000647% Set an 8 lobe Lanczos (Sinc or Jinc) filter:
cristy3ed852e2009-09-05 21:47:34 +0000648% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000649% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000650%
anthony06b1edf2010-10-25 01:19:50 +0000651%
cristy3ed852e2009-09-05 21:47:34 +0000652% The format of the AcquireResizeFilter method is:
653%
654% ResizeFilter *AcquireResizeFilter(const Image *image,
655% const FilterTypes filter_type, const MagickBooleanType radial,
656% ExceptionInfo *exception)
657%
cristy33b1c162010-01-23 22:51:51 +0000658% A description of each parameter follows:
659%
cristy3ed852e2009-09-05 21:47:34 +0000660% o image: the image.
661%
nicolas07bac812010-09-19 18:47:02 +0000662% o filter: the filter type, defining a preset filter, window and
663% support. The artifact settings listed above will override
664% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000665%
anthony48f77622010-10-03 14:32:31 +0000666% o blur: blur the filter by this amount, use 1.0 if unknown. Image
667% artifact "filter:blur" will override this API call usage, including
668% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000669%
anthony48f77622010-10-03 14:32:31 +0000670% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
671% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000672%
673% o exception: return any errors or warnings in this structure.
674%
675*/
676MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000677 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000678 const MagickBooleanType cylindrical,ExceptionInfo *exception)
679{
680 const char
681 *artifact;
682
683 FilterTypes
684 filter_type,
685 window_type;
686
cristy3ed852e2009-09-05 21:47:34 +0000687 MagickRealType
688 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000689 C,
690 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000691
692 register ResizeFilter
693 *resize_filter;
694
cristy9af9b5d2010-08-15 17:04:28 +0000695
cristy3ed852e2009-09-05 21:47:34 +0000696 /*
anthony48f77622010-10-03 14:32:31 +0000697 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000698 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000699 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
700 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
701 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000702
nicolas07bac812010-09-19 18:47:02 +0000703 WARNING: The order of this tabel must match the order of the
704 FilterTypes enumeration specified in "resample.h", or the filter
705 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000706
707 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000708 */
709 static struct
710 {
711 FilterTypes
712 filter,
713 window;
714 } const mapping[SentinelFilter] =
715 {
nicolasb7dff642010-10-25 02:04:14 +0000716 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
717 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
718 { BoxFilter, BoxFilter }, /* Box averaging filter */
719 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
720 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
721 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
722 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
anthony06b1edf2010-10-25 01:19:50 +0000723 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
nicolasb7dff642010-10-25 02:04:14 +0000724 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
725 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approx */
726 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
727 { CatromFilter, BoxFilter }, /* Cubic-Keys interpolator */
728 { MitchellFilter, BoxFilter }, /* 'Ideal' Cubic-Keys filter */
nicolasb7dff642010-10-25 02:04:14 +0000729 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
730 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony152700d2010-10-28 02:43:18 +0000731 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolasb7dff642010-10-25 02:04:14 +0000732 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
733 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
734 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
nicolasb7dff642010-10-25 02:04:14 +0000735 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
anthony06b1edf2010-10-25 01:19:50 +0000736 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
anthony152700d2010-10-28 02:43:18 +0000737 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing */
738 { LanczosFilter, LanczosFilter }, /* Lanczos Sinc-Sinc filters */
739 { LanczosSharpFilter, LanczosSharpFilter }, /* | these require */
740 { Lanczos2Filter, Lanczos2Filter }, /* | special handling */
741 { Lanczos2SharpFilter,Lanczos2SharpFilter },
742 { RobidouxFilter, BoxFilter }, /* Cubic Keys tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000743 };
744 /*
nicolas32f44eb2010-09-20 01:23:12 +0000745 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000746 function. The default support size for that filter as a weighting
747 function, the range to scale with to use that function as a sinc
748 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000749
anthony07a3f7f2010-09-16 03:03:11 +0000750 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000751 SincFast(), and CubicBC() functions, which may have multiple
752 filter to function associations.
753
754 See "filter:verbose" handling below for the function -> filter
755 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000756 */
757 static struct
758 {
759 MagickRealType
760 (*function)(const MagickRealType, const ResizeFilter*),
nicolas8eccc162010-10-16 19:48:13 +0000761 lobes, /* Default lobes/support size of the weighting filter. */
anthony450db502010-10-19 04:03:03 +0000762 scale, /* Support when function used as a windowing function
763 Typically equal to the location of the first zero crossing. */
764 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
cristy3ed852e2009-09-05 21:47:34 +0000765 } const filters[SentinelFilter] =
766 {
anthony61b5ddd2010-10-05 02:33:31 +0000767 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
768 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
769 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
770 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
771 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
772 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
773 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
774 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000775 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000776 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
777 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
778 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
nicolasd349be62010-10-28 18:57:38 +0000779 { CubicBC, 2.0, 8.0/7.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
nicolasf8689f42010-10-18 16:14:08 +0000780 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000781 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
anthony06b1edf2010-10-25 01:19:50 +0000782 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
anthony61b5ddd2010-10-05 02:33:31 +0000783 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
784 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
785 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
anthony61b5ddd2010-10-05 02:33:31 +0000786 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
787 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
anthony152700d2010-10-28 02:43:18 +0000788 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
789 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
790 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* lanczos, Sharpened */
791 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos, 2-lobed */
792 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2, sharpened */
anthony450db502010-10-19 04:03:03 +0000793 { CubicBC, 2.0, 1.1685777620836932,
nicolas0d5e5322010-10-22 15:29:30 +0000794 0.37821575509399867, 0.31089212245300067 }
795 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
cristy3ed852e2009-09-05 21:47:34 +0000796 };
797 /*
anthony9a98fc62010-10-11 02:47:19 +0000798 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
anthony450db502010-10-19 04:03:03 +0000799 function being used as a filter. It is used by the "filter:lobes" expert
800 setting and for 'lobes' for Jinc functions in the previous table. This
801 way users do not have to deal with the highly irrational lobe sizes of the
anthony9a98fc62010-10-11 02:47:19 +0000802 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000803
nicolase473f722010-10-07 00:05:13 +0000804 Values taken from
anthony48f77622010-10-03 14:32:31 +0000805 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000806 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000807 */
808 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000809 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000810 {
nicolas8eccc162010-10-16 19:48:13 +0000811 1.2196698912665045,
812 2.2331305943815286,
813 3.2383154841662362,
814 4.2410628637960699,
815 5.2427643768701817,
816 6.2439216898644877,
817 7.244759868719957,
818 8.2453949139520427,
819 9.2458926849494673,
820 10.246293348754916,
821 11.246622794877883,
822 12.246898461138105,
823 13.247132522181061,
824 14.247333735806849,
anthonyc2d07db2010-09-15 23:47:40 +0000825 15.2475085630373,
nicolas8eccc162010-10-16 19:48:13 +0000826 16.247661874700962
cristy3ed852e2009-09-05 21:47:34 +0000827 };
828
cristy33b1c162010-01-23 22:51:51 +0000829 /*
830 Allocate resize filter.
831 */
cristy3ed852e2009-09-05 21:47:34 +0000832 assert(image != (const Image *) NULL);
833 assert(image->signature == MagickSignature);
834 if (image->debug != MagickFalse)
835 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
836 assert(UndefinedFilter < filter && filter < SentinelFilter);
837 assert(exception != (ExceptionInfo *) NULL);
838 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000839 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000840 if (resize_filter == (ResizeFilter *) NULL)
841 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000842 /*
843 Defaults for the requested filter.
844 */
845 filter_type=mapping[filter].filter;
846 window_type=mapping[filter].window;
anthony993d8382011-02-06 12:35:36 +0000847 resize_filter->blur = blur; /* function argument blur factor */
848 sigma = 0.5; /* guassian sigma of half a pixel by default */
anthony152700d2010-10-28 02:43:18 +0000849 /* Promote 1D Windowed Sinc Filters to a 2D Windowed Jinc filters */
850 if (cylindrical != MagickFalse && filter_type == SincFastFilter
851 && filter != SincFastFilter )
852 filter_type=JincFilter;
anthony61b5ddd2010-10-05 02:33:31 +0000853
anthony152700d2010-10-28 02:43:18 +0000854 /* Expert filter setting override */
cristy3ed852e2009-09-05 21:47:34 +0000855 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000856 if (artifact != (const char *) NULL)
857 {
anthony152700d2010-10-28 02:43:18 +0000858 ssize_t
859 option;
cristy9af9b5d2010-08-15 17:04:28 +0000860 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000861 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000862 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000863 filter_type=(FilterTypes) option;
864 window_type=BoxFilter;
865 }
nicolas07bac812010-09-19 18:47:02 +0000866 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000867 artifact=GetImageArtifact(image,"filter:window");
868 if (artifact != (const char *) NULL)
869 {
cristy9af9b5d2010-08-15 17:04:28 +0000870 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000871 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony152700d2010-10-28 02:43:18 +0000872 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000873 }
cristy3ed852e2009-09-05 21:47:34 +0000874 }
cristy33b1c162010-01-23 22:51:51 +0000875 else
876 {
anthony48f77622010-10-03 14:32:31 +0000877 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000878 artifact=GetImageArtifact(image,"filter:window");
879 if (artifact != (const char *) NULL)
880 {
anthony152700d2010-10-28 02:43:18 +0000881 ssize_t
882 option;
cristy33b1c162010-01-23 22:51:51 +0000883 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
884 artifact);
885 if ((UndefinedFilter < option) && (option < SentinelFilter))
886 {
anthony61b5ddd2010-10-05 02:33:31 +0000887 filter_type=cylindrical != MagickFalse ?
888 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000889 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000890 }
891 }
892 }
anthony152700d2010-10-28 02:43:18 +0000893
nicolas07bac812010-09-19 18:47:02 +0000894 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000895 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000896 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000897 resize_filter->window=filters[window_type].function;
898 resize_filter->scale=filters[window_type].scale;
anthony029ba0e2010-10-29 00:54:24 +0000899 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000900
anthonyf5e76ef2010-10-12 01:22:01 +0000901 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000902 if (cylindrical != MagickFalse)
903 switch (filter_type)
904 {
anthony10b8bc82010-10-02 12:48:46 +0000905 case BoxFilter:
906 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000907 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000908 break;
anthony152700d2010-10-28 02:43:18 +0000909 case LanczosFilter:
910 case LanczosSharpFilter:
911 case Lanczos2Filter:
912 case Lanczos2SharpFilter:
913 resize_filter->filter=filters[JincFilter].function;
914 resize_filter->window=filters[JincFilter].function;
anthony029ba0e2010-10-29 00:54:24 +0000915 resize_filter->scale=filters[JincFilter].scale;
916 /* number of lobes (support window size) remain unchanged */
anthony152700d2010-10-28 02:43:18 +0000917 break;
anthony81b8bf92010-10-02 13:54:34 +0000918 default:
919 break;
anthony10b8bc82010-10-02 12:48:46 +0000920 }
anthony152700d2010-10-28 02:43:18 +0000921 /* Global Sharpening (regardless of orthoginal/cylindrical) */
922 switch (filter_type)
923 {
924 case LanczosSharpFilter:
nicolasaf6ee7c2010-11-13 03:34:22 +0000925 resize_filter->blur *= 0.9812505644269356;
anthony152700d2010-10-28 02:43:18 +0000926 break;
927 case Lanczos2SharpFilter:
nicolasca48bce2010-11-14 17:54:53 +0000928 resize_filter->blur *= 0.9549963639785485;
anthony152700d2010-10-28 02:43:18 +0000929 break;
930 default:
931 break;
932 }
anthony61b5ddd2010-10-05 02:33:31 +0000933
anthonyf5e76ef2010-10-12 01:22:01 +0000934 /*
anthony06b1edf2010-10-25 01:19:50 +0000935 ** Other Expert Option Modifications
anthonyf5e76ef2010-10-12 01:22:01 +0000936 */
937
938 /* User Sigma Override - no support change */
939 artifact=GetImageArtifact(image,"filter:sigma");
940 if (artifact != (const char *) NULL)
941 sigma=StringToDouble(artifact);
anthony993d8382011-02-06 12:35:36 +0000942 /* Define coefficents for Gaussian */
anthonyf5e76ef2010-10-12 01:22:01 +0000943 if ( GaussianFilter ) {
cristy5cce74b2010-11-15 03:24:28 +0000944 resize_filter->coefficient[0]=1.0/(2.0*sigma*sigma);
945 resize_filter->coefficient[1]=(MagickRealType) (1.0/(Magick2PI*sigma*
anthony993d8382011-02-06 12:35:36 +0000946 sigma)); /* Normalization Multiplier - unneeded for filters */
anthonyf5e76ef2010-10-12 01:22:01 +0000947 }
948
949 /* Blur Override */
950 artifact=GetImageArtifact(image,"filter:blur");
951 if (artifact != (const char *) NULL)
952 resize_filter->blur=StringToDouble(artifact);
953 if (resize_filter->blur < MagickEpsilon)
954 resize_filter->blur=(MagickRealType) MagickEpsilon;
955
956 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000957 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000958 if (artifact != (const char *) NULL)
959 {
cristybb503372010-05-27 20:51:26 +0000960 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000961 lobes;
962
cristy96b16132010-08-29 17:19:52 +0000963 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000964 if (lobes < 1)
965 lobes=1;
966 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000967 }
anthony152700d2010-10-28 02:43:18 +0000968 /* Convert a Jinc function lobes value to a real support value */
anthony61b5ddd2010-10-05 02:33:31 +0000969 if (resize_filter->filter == Jinc)
970 {
971 if (resize_filter->support > 16)
972 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
973 else
974 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
975 }
976 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000977 artifact=GetImageArtifact(image,"filter:support");
978 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000979 resize_filter->support=fabs(StringToDouble(artifact));
980 /*
nicolas07bac812010-09-19 18:47:02 +0000981 Scale windowing function separatally to the support 'clipping'
982 window that calling operator is planning to actually use. (Expert
983 override)
cristy3ed852e2009-09-05 21:47:34 +0000984 */
anthony55f12332010-09-10 01:13:02 +0000985 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000986 artifact=GetImageArtifact(image,"filter:win-support");
987 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000988 resize_filter->window_support=fabs(StringToDouble(artifact));
989 /*
anthony029ba0e2010-10-29 00:54:24 +0000990 Adjust window function scaling to match windowing support for
anthony1f90a6b2010-09-14 08:56:31 +0000991 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000992 */
993 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +0000994
anthony55f12332010-09-10 01:13:02 +0000995 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000996 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000997 */
cristy3ed852e2009-09-05 21:47:34 +0000998 B=0.0;
999 C=0.0;
cristy33b1c162010-01-23 22:51:51 +00001000 if ((filters[filter_type].function == CubicBC) ||
1001 (filters[window_type].function == CubicBC))
1002 {
anthony2d9b8b52010-09-14 08:31:07 +00001003 B=filters[filter_type].B;
1004 C=filters[filter_type].C;
1005 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +00001006 {
anthony2d9b8b52010-09-14 08:31:07 +00001007 B=filters[window_type].B;
1008 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +00001009 }
cristy33b1c162010-01-23 22:51:51 +00001010 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001011 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001012 {
1013 B=StringToDouble(artifact);
nicolasd15ee9a2010-10-24 18:39:45 +00001014 C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001015 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001016 if (artifact != (const char *) NULL)
1017 C=StringToDouble(artifact);
1018 }
1019 else
1020 {
1021 artifact=GetImageArtifact(image,"filter:c");
1022 if (artifact != (const char *) NULL)
1023 {
1024 C=StringToDouble(artifact);
nicolasb7dff642010-10-25 02:04:14 +00001025 B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001026 }
1027 }
nicolasc6bac3b2010-10-24 18:10:45 +00001028 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
nicolasd15ee9a2010-10-24 18:39:45 +00001029 {
anthony06b1edf2010-10-25 01:19:50 +00001030 const double twoB = B+B;
cristy5cce74b2010-11-15 03:24:28 +00001031 resize_filter->coefficient[0]=1.0-(1.0/3.0)*B;
1032 resize_filter->coefficient[1]=-3.0+twoB+C;
1033 resize_filter->coefficient[2]=2.0-1.5*B-C;
1034 resize_filter->coefficient[3]=(4.0/3.0)*B+4.0*C;
1035 resize_filter->coefficient[4]=-8.0*C-twoB;
1036 resize_filter->coefficient[5]=B+5.0*C;
1037 resize_filter->coefficient[6]=(-1.0/6.0)*B-C;
nicolasd15ee9a2010-10-24 18:39:45 +00001038 }
nicolasc6bac3b2010-10-24 18:10:45 +00001039 }
anthonyf5e76ef2010-10-12 01:22:01 +00001040
anthony55f12332010-09-10 01:13:02 +00001041 /*
nicolas07bac812010-09-19 18:47:02 +00001042 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001043 */
cristyf5b49372010-10-16 01:06:47 +00001044#if defined(MAGICKCORE_OPENMP_SUPPORT)
1045 #pragma omp master
1046 {
1047#endif
1048 artifact=GetImageArtifact(image,"filter:verbose");
anthony28ad1d72010-10-26 06:30:24 +00001049 if (IsMagickTrue(artifact))
cristyf5b49372010-10-16 01:06:47 +00001050 {
1051 double
anthony06b1edf2010-10-25 01:19:50 +00001052 support,
cristyf5b49372010-10-16 01:06:47 +00001053 x;
cristy3ed852e2009-09-05 21:47:34 +00001054
cristyf5b49372010-10-16 01:06:47 +00001055 /*
1056 Set the weighting function properly when the weighting
1057 function may not exactly match the filter of the same name.
anthony06b1edf2010-10-25 01:19:50 +00001058 EG: a Point filter is really uses a Box weighting function
cristyf5b49372010-10-16 01:06:47 +00001059 with a different support than is typically used.
cristyf5b49372010-10-16 01:06:47 +00001060 */
1061 if (resize_filter->filter == Box) filter_type=BoxFilter;
1062 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1063 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1064 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1065 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
anthony1ad20052010-10-29 01:53:03 +00001066 if (resize_filter->window == Box) window_type=BoxFilter;
anthony152700d2010-10-28 02:43:18 +00001067 if (resize_filter->window == Sinc) window_type=SincFilter;
1068 if (resize_filter->window == SincFast) window_type=SincFastFilter;
anthony1ad20052010-10-29 01:53:03 +00001069 if (resize_filter->window == Jinc) window_type=JincFilter;
anthony152700d2010-10-28 02:43:18 +00001070 if (resize_filter->window == CubicBC) window_type=CubicFilter;
cristyf5b49372010-10-16 01:06:47 +00001071 /*
1072 Report Filter Details.
1073 */
1074 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1075 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
anthony06b1edf2010-10-25 01:19:50 +00001076 (void) fprintf(stdout,"# filter = %s\n",
1077 MagickOptionToMnemonic(MagickFilterOptions,filter_type));
1078 (void) fprintf(stdout,"# window = %s\n",
1079 MagickOptionToMnemonic(MagickFilterOptions, window_type));
1080 (void) fprintf(stdout,"# support = %.*g\n",
1081 GetMagickPrecision(),(double) resize_filter->support);
1082 (void) fprintf(stdout,"# win-support = %.*g\n",
1083 GetMagickPrecision(),(double) resize_filter->window_support);
1084 (void) fprintf(stdout,"# scale_blur = %.*g\n",
1085 GetMagickPrecision(), (double)resize_filter->blur);
cristyf5b49372010-10-16 01:06:47 +00001086 if ( filter_type == GaussianFilter )
anthony06b1edf2010-10-25 01:19:50 +00001087 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",
1088 GetMagickPrecision(), (double)sigma);
1089 (void) fprintf(stdout,"# practical_support = %.*g\n",
1090 GetMagickPrecision(), (double)support);
cristyf5b49372010-10-16 01:06:47 +00001091 if ( filter_type == CubicFilter || window_type == CubicFilter )
anthony06b1edf2010-10-25 01:19:50 +00001092 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",
1093 GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
cristyf5b49372010-10-16 01:06:47 +00001094 (void) fprintf(stdout,"\n");
1095 /*
1096 Output values of resulting filter graph -- for graphing
1097 filter result.
1098 */
1099 for (x=0.0; x <= support; x+=0.01f)
1100 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1101 (double) GetResizeFilterWeight(resize_filter,x));
1102 /* A final value so gnuplot can graph the 'stop' properly. */
1103 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1104 0.0);
1105 }
1106 /* Output the above once only for each image - remove setting */
1107 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1108#if defined(MAGICKCORE_OPENMP_SUPPORT)
1109 }
1110#endif
cristy3ed852e2009-09-05 21:47:34 +00001111 return(resize_filter);
1112}
1113
1114/*
1115%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1116% %
1117% %
1118% %
1119% A d a p t i v e R e s i z e I m a g e %
1120% %
1121% %
1122% %
1123%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1124%
1125% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1126%
1127% The format of the AdaptiveResizeImage method is:
1128%
cristy9af9b5d2010-08-15 17:04:28 +00001129% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1130% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001131%
1132% A description of each parameter follows:
1133%
1134% o image: the image.
1135%
1136% o columns: the number of columns in the resized image.
1137%
1138% o rows: the number of rows in the resized image.
1139%
1140% o exception: return any errors or warnings in this structure.
1141%
1142*/
1143MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001144 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001145{
1146#define AdaptiveResizeImageTag "Resize/Image"
1147
cristyc4c8d132010-01-07 01:58:38 +00001148 CacheView
1149 *resize_view;
1150
cristy3ed852e2009-09-05 21:47:34 +00001151 Image
1152 *resize_image;
1153
cristy3ed852e2009-09-05 21:47:34 +00001154 MagickBooleanType
cristy5cce74b2010-11-15 03:24:28 +00001155 status;
cristy3ed852e2009-09-05 21:47:34 +00001156
cristy5cce74b2010-11-15 03:24:28 +00001157 MagickOffsetType
1158 progress;
cristy3ed852e2009-09-05 21:47:34 +00001159
1160 ResampleFilter
cristya6a18782010-11-15 01:56:25 +00001161 **resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00001162
cristy9af9b5d2010-08-15 17:04:28 +00001163 ssize_t
1164 y;
1165
cristy3ed852e2009-09-05 21:47:34 +00001166 /*
1167 Adaptively resize image.
1168 */
1169 assert(image != (const Image *) NULL);
1170 assert(image->signature == MagickSignature);
1171 if (image->debug != MagickFalse)
1172 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1173 assert(exception != (ExceptionInfo *) NULL);
1174 assert(exception->signature == MagickSignature);
1175 if ((columns == 0) || (rows == 0))
1176 return((Image *) NULL);
1177 if ((columns == image->columns) && (rows == image->rows))
1178 return(CloneImage(image,0,0,MagickTrue,exception));
1179 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1180 if (resize_image == (Image *) NULL)
1181 return((Image *) NULL);
1182 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1183 {
1184 InheritException(exception,&resize_image->exception);
1185 resize_image=DestroyImage(resize_image);
1186 return((Image *) NULL);
1187 }
cristy5cce74b2010-11-15 03:24:28 +00001188 status=MagickTrue;
1189 progress=0;
cristya6a18782010-11-15 01:56:25 +00001190 resample_filter=AcquireResampleFilterThreadSet(image,
1191 UndefinedVirtualPixelMethod,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00001192 resize_view=AcquireCacheView(resize_image);
cristy5cce74b2010-11-15 03:24:28 +00001193#if defined(MAGICKCORE_OPENMP_SUPPORT)
1194 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
1195#endif
cristybb503372010-05-27 20:51:26 +00001196 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001197 {
cristya6a18782010-11-15 01:56:25 +00001198 const int
1199 id = GetOpenMPThreadId();
1200
cristy5cce74b2010-11-15 03:24:28 +00001201 MagickPixelPacket
1202 pixel;
1203
1204 PointInfo
1205 offset;
1206
cristy3ed852e2009-09-05 21:47:34 +00001207 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001208 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001209
cristy3ed852e2009-09-05 21:47:34 +00001210 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001211 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001212
cristyb2a9be02010-11-15 15:00:14 +00001213 register ssize_t
1214 x;
1215
cristy5cce74b2010-11-15 03:24:28 +00001216 if (status == MagickFalse)
1217 continue;
cristy3ed852e2009-09-05 21:47:34 +00001218 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1219 exception);
1220 if (q == (PixelPacket *) NULL)
cristy5cce74b2010-11-15 03:24:28 +00001221 continue;
cristy3ed852e2009-09-05 21:47:34 +00001222 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1223 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristy5cce74b2010-11-15 03:24:28 +00001224 GetMagickPixelPacket(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001225 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001226 {
1227 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
cristya6a18782010-11-15 01:56:25 +00001228 (void) ResamplePixelColor(resample_filter[id],offset.x-0.5,offset.y-0.5,
cristy3ed852e2009-09-05 21:47:34 +00001229 &pixel);
1230 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1231 q++;
1232 }
1233 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
cristy5cce74b2010-11-15 03:24:28 +00001234 continue;
1235 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1236 {
1237 MagickBooleanType
1238 proceed;
1239
1240#if defined(MAGICKCORE_OPENMP_SUPPORT)
1241 #pragma omp critical (MagickCore_AdaptiveResizeImage)
1242#endif
1243 proceed=SetImageProgress(image,AdaptiveResizeImageTag,progress++,
1244 image->rows);
1245 if (proceed == MagickFalse)
1246 status=MagickFalse;
1247 }
cristy3ed852e2009-09-05 21:47:34 +00001248 }
cristya6a18782010-11-15 01:56:25 +00001249 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
cristy3ed852e2009-09-05 21:47:34 +00001250 resize_view=DestroyCacheView(resize_view);
cristy5cce74b2010-11-15 03:24:28 +00001251 if (status == MagickFalse)
1252 resize_image=DestroyImage(resize_image);
cristy3ed852e2009-09-05 21:47:34 +00001253 return(resize_image);
1254}
1255
1256/*
1257%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1258% %
1259% %
1260% %
1261+ B e s s e l O r d e r O n e %
1262% %
1263% %
1264% %
1265%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1266%
1267% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001268% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001269%
1270% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1271%
1272% j1(x) = x*j1(x);
1273%
1274% For x in (8,inf)
1275%
1276% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1277%
1278% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1279%
1280% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1281% = 1/sqrt(2) * (sin(x) - cos(x))
1282% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1283% = -1/sqrt(2) * (sin(x) + cos(x))
1284%
1285% The format of the BesselOrderOne method is:
1286%
1287% MagickRealType BesselOrderOne(MagickRealType x)
1288%
1289% A description of each parameter follows:
1290%
1291% o x: MagickRealType value.
1292%
1293*/
1294
1295#undef I0
1296static MagickRealType I0(MagickRealType x)
1297{
1298 MagickRealType
1299 sum,
1300 t,
1301 y;
1302
cristybb503372010-05-27 20:51:26 +00001303 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001304 i;
1305
1306 /*
1307 Zeroth order Bessel function of the first kind.
1308 */
1309 sum=1.0;
1310 y=x*x/4.0;
1311 t=y;
1312 for (i=2; t > MagickEpsilon; i++)
1313 {
1314 sum+=t;
1315 t*=y/((MagickRealType) i*i);
1316 }
1317 return(sum);
1318}
1319
1320#undef J1
1321static MagickRealType J1(MagickRealType x)
1322{
1323 MagickRealType
1324 p,
1325 q;
1326
cristybb503372010-05-27 20:51:26 +00001327 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001328 i;
1329
1330 static const double
1331 Pone[] =
1332 {
1333 0.581199354001606143928050809e+21,
1334 -0.6672106568924916298020941484e+20,
1335 0.2316433580634002297931815435e+19,
1336 -0.3588817569910106050743641413e+17,
1337 0.2908795263834775409737601689e+15,
1338 -0.1322983480332126453125473247e+13,
1339 0.3413234182301700539091292655e+10,
1340 -0.4695753530642995859767162166e+7,
1341 0.270112271089232341485679099e+4
1342 },
1343 Qone[] =
1344 {
1345 0.11623987080032122878585294e+22,
1346 0.1185770712190320999837113348e+20,
1347 0.6092061398917521746105196863e+17,
1348 0.2081661221307607351240184229e+15,
1349 0.5243710262167649715406728642e+12,
1350 0.1013863514358673989967045588e+10,
1351 0.1501793594998585505921097578e+7,
1352 0.1606931573481487801970916749e+4,
1353 0.1e+1
1354 };
1355
1356 p=Pone[8];
1357 q=Qone[8];
1358 for (i=7; i >= 0; i--)
1359 {
1360 p=p*x*x+Pone[i];
1361 q=q*x*x+Qone[i];
1362 }
1363 return(p/q);
1364}
1365
1366#undef P1
1367static MagickRealType P1(MagickRealType x)
1368{
1369 MagickRealType
1370 p,
1371 q;
1372
cristybb503372010-05-27 20:51:26 +00001373 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001374 i;
1375
1376 static const double
1377 Pone[] =
1378 {
1379 0.352246649133679798341724373e+5,
1380 0.62758845247161281269005675e+5,
1381 0.313539631109159574238669888e+5,
1382 0.49854832060594338434500455e+4,
1383 0.2111529182853962382105718e+3,
1384 0.12571716929145341558495e+1
1385 },
1386 Qone[] =
1387 {
1388 0.352246649133679798068390431e+5,
1389 0.626943469593560511888833731e+5,
1390 0.312404063819041039923015703e+5,
1391 0.4930396490181088979386097e+4,
1392 0.2030775189134759322293574e+3,
1393 0.1e+1
1394 };
1395
1396 p=Pone[5];
1397 q=Qone[5];
1398 for (i=4; i >= 0; i--)
1399 {
1400 p=p*(8.0/x)*(8.0/x)+Pone[i];
1401 q=q*(8.0/x)*(8.0/x)+Qone[i];
1402 }
1403 return(p/q);
1404}
1405
1406#undef Q1
1407static MagickRealType Q1(MagickRealType x)
1408{
1409 MagickRealType
1410 p,
1411 q;
1412
cristybb503372010-05-27 20:51:26 +00001413 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001414 i;
1415
1416 static const double
1417 Pone[] =
1418 {
1419 0.3511751914303552822533318e+3,
1420 0.7210391804904475039280863e+3,
1421 0.4259873011654442389886993e+3,
1422 0.831898957673850827325226e+2,
1423 0.45681716295512267064405e+1,
1424 0.3532840052740123642735e-1
1425 },
1426 Qone[] =
1427 {
1428 0.74917374171809127714519505e+4,
1429 0.154141773392650970499848051e+5,
1430 0.91522317015169922705904727e+4,
1431 0.18111867005523513506724158e+4,
1432 0.1038187585462133728776636e+3,
1433 0.1e+1
1434 };
1435
1436 p=Pone[5];
1437 q=Qone[5];
1438 for (i=4; i >= 0; i--)
1439 {
1440 p=p*(8.0/x)*(8.0/x)+Pone[i];
1441 q=q*(8.0/x)*(8.0/x)+Qone[i];
1442 }
1443 return(p/q);
1444}
1445
1446static MagickRealType BesselOrderOne(MagickRealType x)
1447{
1448 MagickRealType
1449 p,
1450 q;
1451
1452 if (x == 0.0)
1453 return(0.0);
1454 p=x;
1455 if (x < 0.0)
1456 x=(-x);
1457 if (x < 8.0)
1458 return(p*J1(x));
1459 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1460 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1461 cos((double) x))));
1462 if (p < 0.0)
1463 q=(-q);
1464 return(q);
1465}
1466
1467/*
1468%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1469% %
1470% %
1471% %
1472+ D e s t r o y R e s i z e F i l t e r %
1473% %
1474% %
1475% %
1476%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1477%
1478% DestroyResizeFilter() destroy the resize filter.
1479%
cristya2ffd7e2010-03-10 20:50:30 +00001480% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001481%
1482% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1483%
1484% A description of each parameter follows:
1485%
1486% o resize_filter: the resize filter.
1487%
1488*/
1489MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1490{
1491 assert(resize_filter != (ResizeFilter *) NULL);
1492 assert(resize_filter->signature == MagickSignature);
1493 resize_filter->signature=(~MagickSignature);
1494 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1495 return(resize_filter);
1496}
1497
1498/*
1499%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1500% %
1501% %
1502% %
1503+ G e t R e s i z e F i l t e r S u p p o r t %
1504% %
1505% %
1506% %
1507%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1508%
1509% GetResizeFilterSupport() return the current support window size for this
1510% filter. Note that this may have been enlarged by filter:blur factor.
1511%
1512% The format of the GetResizeFilterSupport method is:
1513%
1514% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1515%
1516% A description of each parameter follows:
1517%
1518% o filter: Image filter to use.
1519%
1520*/
1521MagickExport MagickRealType GetResizeFilterSupport(
1522 const ResizeFilter *resize_filter)
1523{
1524 assert(resize_filter != (ResizeFilter *) NULL);
1525 assert(resize_filter->signature == MagickSignature);
1526 return(resize_filter->support*resize_filter->blur);
1527}
1528
1529/*
1530%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1531% %
1532% %
1533% %
1534+ G e t R e s i z e F i l t e r W e i g h t %
1535% %
1536% %
1537% %
1538%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1539%
1540% GetResizeFilterWeight evaluates the specified resize filter at the point x
1541% which usally lies between zero and the filters current 'support' and
1542% returns the weight of the filter function at that point.
1543%
1544% The format of the GetResizeFilterWeight method is:
1545%
1546% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1547% const MagickRealType x)
1548%
1549% A description of each parameter follows:
1550%
1551% o filter: the filter type.
1552%
1553% o x: the point.
1554%
1555*/
1556MagickExport MagickRealType GetResizeFilterWeight(
1557 const ResizeFilter *resize_filter,const MagickRealType x)
1558{
1559 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001560 scale,
1561 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001562
1563 /*
1564 Windowing function - scale the weighting filter by this amount.
1565 */
1566 assert(resize_filter != (ResizeFilter *) NULL);
1567 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001568 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001569 if ((resize_filter->window_support < MagickEpsilon) ||
1570 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001571 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001572 else
1573 {
anthony55f12332010-09-10 01:13:02 +00001574 scale=resize_filter->scale;
1575 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001576 }
anthony55f12332010-09-10 01:13:02 +00001577 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001578}
1579
1580/*
1581%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1582% %
1583% %
1584% %
1585% M a g n i f y I m a g e %
1586% %
1587% %
1588% %
1589%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1590%
1591% MagnifyImage() is a convenience method that scales an image proportionally
1592% to twice its size.
1593%
1594% The format of the MagnifyImage method is:
1595%
1596% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1597%
1598% A description of each parameter follows:
1599%
1600% o image: the image.
1601%
1602% o exception: return any errors or warnings in this structure.
1603%
1604*/
1605MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1606{
1607 Image
1608 *magnify_image;
1609
1610 assert(image != (Image *) NULL);
1611 assert(image->signature == MagickSignature);
1612 if (image->debug != MagickFalse)
1613 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1614 assert(exception != (ExceptionInfo *) NULL);
1615 assert(exception->signature == MagickSignature);
1616 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1617 1.0,exception);
1618 return(magnify_image);
1619}
1620
1621/*
1622%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1623% %
1624% %
1625% %
1626% M i n i f y I m a g e %
1627% %
1628% %
1629% %
1630%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1631%
1632% MinifyImage() is a convenience method that scales an image proportionally
1633% to half its size.
1634%
1635% The format of the MinifyImage method is:
1636%
1637% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1638%
1639% A description of each parameter follows:
1640%
1641% o image: the image.
1642%
1643% o exception: return any errors or warnings in this structure.
1644%
1645*/
1646MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1647{
1648 Image
1649 *minify_image;
1650
1651 assert(image != (Image *) NULL);
1652 assert(image->signature == MagickSignature);
1653 if (image->debug != MagickFalse)
1654 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1655 assert(exception != (ExceptionInfo *) NULL);
1656 assert(exception->signature == MagickSignature);
1657 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1658 1.0,exception);
1659 return(minify_image);
1660}
1661
1662/*
1663%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1664% %
1665% %
1666% %
1667% R e s a m p l e I m a g e %
1668% %
1669% %
1670% %
1671%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1672%
1673% ResampleImage() resize image in terms of its pixel size, so that when
1674% displayed at the given resolution it will be the same size in terms of
1675% real world units as the original image at the original resolution.
1676%
1677% The format of the ResampleImage method is:
1678%
1679% Image *ResampleImage(Image *image,const double x_resolution,
1680% const double y_resolution,const FilterTypes filter,const double blur,
1681% ExceptionInfo *exception)
1682%
1683% A description of each parameter follows:
1684%
1685% o image: the image to be resized to fit the given resolution.
1686%
1687% o x_resolution: the new image x resolution.
1688%
1689% o y_resolution: the new image y resolution.
1690%
1691% o filter: Image filter to use.
1692%
1693% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1694%
1695*/
1696MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1697 const double y_resolution,const FilterTypes filter,const double blur,
1698 ExceptionInfo *exception)
1699{
1700#define ResampleImageTag "Resample/Image"
1701
1702 Image
1703 *resample_image;
1704
cristybb503372010-05-27 20:51:26 +00001705 size_t
cristy3ed852e2009-09-05 21:47:34 +00001706 height,
1707 width;
1708
1709 /*
1710 Initialize sampled image attributes.
1711 */
1712 assert(image != (const Image *) NULL);
1713 assert(image->signature == MagickSignature);
1714 if (image->debug != MagickFalse)
1715 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1716 assert(exception != (ExceptionInfo *) NULL);
1717 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001718 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1719 72.0 : image->x_resolution)+0.5);
1720 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1721 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001722 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1723 if (resample_image != (Image *) NULL)
1724 {
1725 resample_image->x_resolution=x_resolution;
1726 resample_image->y_resolution=y_resolution;
1727 }
1728 return(resample_image);
1729}
1730#if defined(MAGICKCORE_LQR_DELEGATE)
1731
1732/*
1733%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1734% %
1735% %
1736% %
1737% L i q u i d R e s c a l e I m a g e %
1738% %
1739% %
1740% %
1741%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1742%
1743% LiquidRescaleImage() rescales image with seam carving.
1744%
1745% The format of the LiquidRescaleImage method is:
1746%
1747% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001748% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001749% const double delta_x,const double rigidity,ExceptionInfo *exception)
1750%
1751% A description of each parameter follows:
1752%
1753% o image: the image.
1754%
1755% o columns: the number of columns in the rescaled image.
1756%
1757% o rows: the number of rows in the rescaled image.
1758%
1759% o delta_x: maximum seam transversal step (0 means straight seams).
1760%
1761% o rigidity: introduce a bias for non-straight seams (typically 0).
1762%
1763% o exception: return any errors or warnings in this structure.
1764%
1765*/
cristy9af9b5d2010-08-15 17:04:28 +00001766MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1767 const size_t rows,const double delta_x,const double rigidity,
1768 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001769{
1770#define LiquidRescaleImageTag "Rescale/Image"
1771
cristyc5c6f662010-09-22 14:23:02 +00001772 CacheView
1773 *rescale_view;
1774
cristy3ed852e2009-09-05 21:47:34 +00001775 const char
1776 *map;
1777
1778 guchar
1779 *packet;
1780
1781 Image
1782 *rescale_image;
1783
1784 int
1785 x,
1786 y;
1787
1788 LqrCarver
1789 *carver;
1790
1791 LqrRetVal
1792 lqr_status;
1793
1794 MagickBooleanType
1795 status;
1796
1797 MagickPixelPacket
1798 pixel;
1799
1800 unsigned char
1801 *pixels;
1802
1803 /*
1804 Liquid rescale image.
1805 */
1806 assert(image != (const Image *) NULL);
1807 assert(image->signature == MagickSignature);
1808 if (image->debug != MagickFalse)
1809 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1810 assert(exception != (ExceptionInfo *) NULL);
1811 assert(exception->signature == MagickSignature);
1812 if ((columns == 0) || (rows == 0))
1813 return((Image *) NULL);
1814 if ((columns == image->columns) && (rows == image->rows))
1815 return(CloneImage(image,0,0,MagickTrue,exception));
1816 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001817 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001818 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1819 {
1820 Image
1821 *resize_image;
1822
cristybb503372010-05-27 20:51:26 +00001823 size_t
cristy3ed852e2009-09-05 21:47:34 +00001824 height,
1825 width;
1826
1827 /*
1828 Honor liquid resize size limitations.
1829 */
1830 for (width=image->columns; columns >= (2*width-1); width*=2);
1831 for (height=image->rows; rows >= (2*height-1); height*=2);
1832 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1833 exception);
1834 if (resize_image == (Image *) NULL)
1835 return((Image *) NULL);
1836 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1837 rigidity,exception);
1838 resize_image=DestroyImage(resize_image);
1839 return(rescale_image);
1840 }
1841 map="RGB";
1842 if (image->matte == MagickFalse)
1843 map="RGBA";
1844 if (image->colorspace == CMYKColorspace)
1845 {
1846 map="CMYK";
1847 if (image->matte == MagickFalse)
1848 map="CMYKA";
1849 }
1850 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1851 strlen(map)*sizeof(*pixels));
1852 if (pixels == (unsigned char *) NULL)
1853 return((Image *) NULL);
1854 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1855 pixels,exception);
1856 if (status == MagickFalse)
1857 {
1858 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1859 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1860 }
1861 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1862 if (carver == (LqrCarver *) NULL)
1863 {
1864 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1865 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1866 }
1867 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1868 lqr_status=lqr_carver_resize(carver,columns,rows);
1869 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1870 lqr_carver_get_height(carver),MagickTrue,exception);
1871 if (rescale_image == (Image *) NULL)
1872 {
1873 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1874 return((Image *) NULL);
1875 }
1876 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1877 {
1878 InheritException(exception,&rescale_image->exception);
1879 rescale_image=DestroyImage(rescale_image);
1880 return((Image *) NULL);
1881 }
1882 GetMagickPixelPacket(rescale_image,&pixel);
1883 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001884 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001885 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1886 {
1887 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001888 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001889
1890 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001891 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001892
anthony22aad252010-09-23 06:59:07 +00001893 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001894 if (q == (PixelPacket *) NULL)
1895 break;
cristyc5c6f662010-09-22 14:23:02 +00001896 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001897 pixel.red=QuantumRange*(packet[0]/255.0);
1898 pixel.green=QuantumRange*(packet[1]/255.0);
1899 pixel.blue=QuantumRange*(packet[2]/255.0);
1900 if (image->colorspace != CMYKColorspace)
1901 {
1902 if (image->matte == MagickFalse)
1903 pixel.opacity=QuantumRange*(packet[3]/255.0);
1904 }
1905 else
1906 {
1907 pixel.index=QuantumRange*(packet[3]/255.0);
1908 if (image->matte == MagickFalse)
1909 pixel.opacity=QuantumRange*(packet[4]/255.0);
1910 }
1911 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001912 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001913 break;
1914 }
cristyc5c6f662010-09-22 14:23:02 +00001915 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001916 /*
1917 Relinquish resources.
1918 */
1919 lqr_carver_destroy(carver);
1920 return(rescale_image);
1921}
1922#else
1923MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001924 const size_t magick_unused(columns),const size_t magick_unused(rows),
1925 const double magick_unused(delta_x),const double magick_unused(rigidity),
1926 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001927{
1928 assert(image != (const Image *) NULL);
1929 assert(image->signature == MagickSignature);
1930 if (image->debug != MagickFalse)
1931 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1932 assert(exception != (ExceptionInfo *) NULL);
1933 assert(exception->signature == MagickSignature);
1934 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1935 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1936 return((Image *) NULL);
1937}
1938#endif
1939
1940/*
1941%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1942% %
1943% %
1944% %
1945% R e s i z e I m a g e %
1946% %
1947% %
1948% %
1949%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1950%
1951% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001952% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001953%
1954% If an undefined filter is given the filter defaults to Mitchell for a
1955% colormapped image, a image with a matte channel, or if the image is
1956% enlarged. Otherwise the filter defaults to a Lanczos.
1957%
1958% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1959%
1960% The format of the ResizeImage method is:
1961%
cristybb503372010-05-27 20:51:26 +00001962% Image *ResizeImage(Image *image,const size_t columns,
1963% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001964% ExceptionInfo *exception)
1965%
1966% A description of each parameter follows:
1967%
1968% o image: the image.
1969%
1970% o columns: the number of columns in the scaled image.
1971%
1972% o rows: the number of rows in the scaled image.
1973%
1974% o filter: Image filter to use.
1975%
cristy9af9b5d2010-08-15 17:04:28 +00001976% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1977% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001978%
1979% o exception: return any errors or warnings in this structure.
1980%
1981*/
1982
1983typedef struct _ContributionInfo
1984{
1985 MagickRealType
1986 weight;
1987
cristybb503372010-05-27 20:51:26 +00001988 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001989 pixel;
1990} ContributionInfo;
1991
1992static ContributionInfo **DestroyContributionThreadSet(
1993 ContributionInfo **contribution)
1994{
cristybb503372010-05-27 20:51:26 +00001995 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001996 i;
1997
1998 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001999 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00002000 if (contribution[i] != (ContributionInfo *) NULL)
2001 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
2002 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00002003 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00002004 return(contribution);
2005}
2006
2007static ContributionInfo **AcquireContributionThreadSet(const size_t count)
2008{
cristybb503372010-05-27 20:51:26 +00002009 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002010 i;
2011
2012 ContributionInfo
2013 **contribution;
2014
cristybb503372010-05-27 20:51:26 +00002015 size_t
cristy3ed852e2009-09-05 21:47:34 +00002016 number_threads;
2017
2018 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002019 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00002020 sizeof(*contribution));
2021 if (contribution == (ContributionInfo **) NULL)
2022 return((ContributionInfo **) NULL);
2023 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00002024 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002025 {
2026 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2027 sizeof(**contribution));
2028 if (contribution[i] == (ContributionInfo *) NULL)
2029 return(DestroyContributionThreadSet(contribution));
2030 }
2031 return(contribution);
2032}
2033
2034static inline double MagickMax(const double x,const double y)
2035{
2036 if (x > y)
2037 return(x);
2038 return(y);
2039}
2040
2041static inline double MagickMin(const double x,const double y)
2042{
2043 if (x < y)
2044 return(x);
2045 return(y);
2046}
2047
2048static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2049 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002050 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002051{
2052#define ResizeImageTag "Resize/Image"
2053
cristyfa112112010-01-04 17:48:07 +00002054 CacheView
2055 *image_view,
2056 *resize_view;
2057
cristy3ed852e2009-09-05 21:47:34 +00002058 ClassType
2059 storage_class;
2060
2061 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002062 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002063
cristy3ed852e2009-09-05 21:47:34 +00002064 MagickBooleanType
2065 status;
2066
2067 MagickPixelPacket
2068 zero;
2069
2070 MagickRealType
2071 scale,
2072 support;
2073
cristy9af9b5d2010-08-15 17:04:28 +00002074 ssize_t
2075 x;
2076
cristy3ed852e2009-09-05 21:47:34 +00002077 /*
2078 Apply filter to resize horizontally from image to resize image.
2079 */
cristy5d824382010-09-06 14:00:17 +00002080 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002081 support=scale*GetResizeFilterSupport(resize_filter);
2082 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2083 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2084 {
2085 InheritException(exception,&resize_image->exception);
2086 return(MagickFalse);
2087 }
2088 if (support < 0.5)
2089 {
2090 /*
nicolas07bac812010-09-19 18:47:02 +00002091 Support too small even for nearest neighbour: Reduce to point
2092 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002093 */
2094 support=(MagickRealType) 0.5;
2095 scale=1.0;
2096 }
2097 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2098 if (contributions == (ContributionInfo **) NULL)
2099 {
2100 (void) ThrowMagickException(exception,GetMagickModule(),
2101 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2102 return(MagickFalse);
2103 }
2104 status=MagickTrue;
2105 scale=1.0/scale;
2106 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2107 image_view=AcquireCacheView(image);
2108 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002109#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002110 #pragma omp parallel for shared(status)
2111#endif
cristybb503372010-05-27 20:51:26 +00002112 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002113 {
cristy3ed852e2009-09-05 21:47:34 +00002114 MagickRealType
2115 center,
2116 density;
2117
2118 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002119 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002120
2121 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002122 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002123
cristy03dbbd22010-09-19 23:04:47 +00002124 register ContributionInfo
2125 *restrict contribution;
2126
cristy3ed852e2009-09-05 21:47:34 +00002127 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002128 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002129
cristy3ed852e2009-09-05 21:47:34 +00002130 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002131 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002132
cristy03dbbd22010-09-19 23:04:47 +00002133 register ssize_t
2134 y;
2135
cristy9af9b5d2010-08-15 17:04:28 +00002136 ssize_t
2137 n,
2138 start,
2139 stop;
2140
cristy3ed852e2009-09-05 21:47:34 +00002141 if (status == MagickFalse)
2142 continue;
2143 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002144 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2145 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002146 density=0.0;
2147 contribution=contributions[GetOpenMPThreadId()];
2148 for (n=0; n < (stop-start); n++)
2149 {
2150 contribution[n].pixel=start+n;
2151 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2152 ((MagickRealType) (start+n)-center+0.5));
2153 density+=contribution[n].weight;
2154 }
2155 if ((density != 0.0) && (density != 1.0))
2156 {
cristybb503372010-05-27 20:51:26 +00002157 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002158 i;
2159
2160 /*
2161 Normalize.
2162 */
2163 density=1.0/density;
2164 for (i=0; i < n; i++)
2165 contribution[i].weight*=density;
2166 }
cristy9af9b5d2010-08-15 17:04:28 +00002167 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2168 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002169 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2170 exception);
2171 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2172 {
2173 status=MagickFalse;
2174 continue;
2175 }
2176 indexes=GetCacheViewVirtualIndexQueue(image_view);
2177 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002178 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002179 {
cristy3ed852e2009-09-05 21:47:34 +00002180 MagickPixelPacket
2181 pixel;
2182
2183 MagickRealType
2184 alpha;
2185
cristybb503372010-05-27 20:51:26 +00002186 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002187 i;
2188
cristy9af9b5d2010-08-15 17:04:28 +00002189 ssize_t
2190 j;
2191
cristy3ed852e2009-09-05 21:47:34 +00002192 pixel=zero;
2193 if (image->matte == MagickFalse)
2194 {
2195 for (i=0; i < n; i++)
2196 {
2197 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2198 (contribution[i].pixel-contribution[0].pixel);
2199 alpha=contribution[i].weight;
2200 pixel.red+=alpha*(p+j)->red;
2201 pixel.green+=alpha*(p+j)->green;
2202 pixel.blue+=alpha*(p+j)->blue;
2203 pixel.opacity+=alpha*(p+j)->opacity;
2204 }
cristyce70c172010-01-07 17:15:30 +00002205 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2206 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2207 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2208 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002209 if ((image->colorspace == CMYKColorspace) &&
2210 (resize_image->colorspace == CMYKColorspace))
2211 {
2212 for (i=0; i < n; i++)
2213 {
2214 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2215 (contribution[i].pixel-contribution[0].pixel);
2216 alpha=contribution[i].weight;
2217 pixel.index+=alpha*indexes[j];
2218 }
cristyce70c172010-01-07 17:15:30 +00002219 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002220 }
2221 }
2222 else
2223 {
2224 MagickRealType
2225 gamma;
2226
2227 gamma=0.0;
2228 for (i=0; i < n; i++)
2229 {
2230 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2231 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002232 alpha=contribution[i].weight*QuantumScale*
2233 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002234 pixel.red+=alpha*(p+j)->red;
2235 pixel.green+=alpha*(p+j)->green;
2236 pixel.blue+=alpha*(p+j)->blue;
2237 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2238 gamma+=alpha;
2239 }
2240 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002241 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2242 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2243 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2244 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002245 if ((image->colorspace == CMYKColorspace) &&
2246 (resize_image->colorspace == CMYKColorspace))
2247 {
2248 for (i=0; i < n; i++)
2249 {
2250 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2251 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002252 alpha=contribution[i].weight*QuantumScale*
2253 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002254 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002255 }
cristyce70c172010-01-07 17:15:30 +00002256 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2257 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002258 }
2259 }
2260 if ((resize_image->storage_class == PseudoClass) &&
2261 (image->storage_class == PseudoClass))
2262 {
cristybb503372010-05-27 20:51:26 +00002263 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002264 1.0)+0.5);
2265 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2266 (contribution[i-start].pixel-contribution[0].pixel);
2267 resize_indexes[y]=indexes[j];
2268 }
2269 q++;
2270 }
2271 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2272 status=MagickFalse;
2273 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2274 {
2275 MagickBooleanType
2276 proceed;
2277
cristyb5d5f722009-11-04 03:03:49 +00002278#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002279 #pragma omp critical (MagickCore_HorizontalFilter)
2280#endif
cristy9af9b5d2010-08-15 17:04:28 +00002281 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002282 if (proceed == MagickFalse)
2283 status=MagickFalse;
2284 }
2285 }
2286 resize_view=DestroyCacheView(resize_view);
2287 image_view=DestroyCacheView(image_view);
2288 contributions=DestroyContributionThreadSet(contributions);
2289 return(status);
2290}
2291
2292static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2293 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002294 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002295{
cristyfa112112010-01-04 17:48:07 +00002296 CacheView
2297 *image_view,
2298 *resize_view;
2299
cristy3ed852e2009-09-05 21:47:34 +00002300 ClassType
2301 storage_class;
2302
2303 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002304 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002305
cristy3ed852e2009-09-05 21:47:34 +00002306 MagickBooleanType
2307 status;
2308
2309 MagickPixelPacket
2310 zero;
2311
2312 MagickRealType
2313 scale,
2314 support;
2315
cristy9af9b5d2010-08-15 17:04:28 +00002316 ssize_t
2317 y;
2318
cristy3ed852e2009-09-05 21:47:34 +00002319 /*
cristy9af9b5d2010-08-15 17:04:28 +00002320 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002321 */
cristy5d824382010-09-06 14:00:17 +00002322 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002323 support=scale*GetResizeFilterSupport(resize_filter);
2324 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2325 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2326 {
2327 InheritException(exception,&resize_image->exception);
2328 return(MagickFalse);
2329 }
2330 if (support < 0.5)
2331 {
2332 /*
nicolas07bac812010-09-19 18:47:02 +00002333 Support too small even for nearest neighbour: Reduce to point
2334 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002335 */
2336 support=(MagickRealType) 0.5;
2337 scale=1.0;
2338 }
2339 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2340 if (contributions == (ContributionInfo **) NULL)
2341 {
2342 (void) ThrowMagickException(exception,GetMagickModule(),
2343 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2344 return(MagickFalse);
2345 }
2346 status=MagickTrue;
2347 scale=1.0/scale;
2348 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2349 image_view=AcquireCacheView(image);
2350 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002351#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002352 #pragma omp parallel for shared(status)
2353#endif
cristybb503372010-05-27 20:51:26 +00002354 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002355 {
cristy3ed852e2009-09-05 21:47:34 +00002356 MagickRealType
2357 center,
2358 density;
2359
2360 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002361 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002362
2363 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002364 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002365
cristy03dbbd22010-09-19 23:04:47 +00002366 register ContributionInfo
2367 *restrict contribution;
2368
cristy3ed852e2009-09-05 21:47:34 +00002369 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002370 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002371
cristy9af9b5d2010-08-15 17:04:28 +00002372 register PixelPacket
2373 *restrict q;
2374
cristybb503372010-05-27 20:51:26 +00002375 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002376 x;
2377
cristy9af9b5d2010-08-15 17:04:28 +00002378 ssize_t
2379 n,
2380 start,
2381 stop;
cristy3ed852e2009-09-05 21:47:34 +00002382
2383 if (status == MagickFalse)
2384 continue;
cristy679e6962010-03-18 00:42:45 +00002385 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002386 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2387 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002388 density=0.0;
2389 contribution=contributions[GetOpenMPThreadId()];
2390 for (n=0; n < (stop-start); n++)
2391 {
2392 contribution[n].pixel=start+n;
2393 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2394 ((MagickRealType) (start+n)-center+0.5));
2395 density+=contribution[n].weight;
2396 }
2397 if ((density != 0.0) && (density != 1.0))
2398 {
cristybb503372010-05-27 20:51:26 +00002399 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002400 i;
2401
2402 /*
2403 Normalize.
2404 */
2405 density=1.0/density;
2406 for (i=0; i < n; i++)
2407 contribution[i].weight*=density;
2408 }
2409 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002410 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2411 exception);
cristy3ed852e2009-09-05 21:47:34 +00002412 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2413 exception);
2414 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2415 {
2416 status=MagickFalse;
2417 continue;
2418 }
2419 indexes=GetCacheViewVirtualIndexQueue(image_view);
2420 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002421 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002422 {
cristy3ed852e2009-09-05 21:47:34 +00002423 MagickPixelPacket
2424 pixel;
2425
2426 MagickRealType
2427 alpha;
2428
cristybb503372010-05-27 20:51:26 +00002429 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002430 i;
2431
cristy9af9b5d2010-08-15 17:04:28 +00002432 ssize_t
2433 j;
2434
cristy3ed852e2009-09-05 21:47:34 +00002435 pixel=zero;
2436 if (image->matte == MagickFalse)
2437 {
2438 for (i=0; i < n; i++)
2439 {
cristybb503372010-05-27 20:51:26 +00002440 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002441 image->columns+x);
2442 alpha=contribution[i].weight;
2443 pixel.red+=alpha*(p+j)->red;
2444 pixel.green+=alpha*(p+j)->green;
2445 pixel.blue+=alpha*(p+j)->blue;
2446 pixel.opacity+=alpha*(p+j)->opacity;
2447 }
cristyce70c172010-01-07 17:15:30 +00002448 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2449 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2450 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2451 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002452 if ((image->colorspace == CMYKColorspace) &&
2453 (resize_image->colorspace == CMYKColorspace))
2454 {
2455 for (i=0; i < n; i++)
2456 {
cristybb503372010-05-27 20:51:26 +00002457 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002458 image->columns+x);
2459 alpha=contribution[i].weight;
2460 pixel.index+=alpha*indexes[j];
2461 }
cristyce70c172010-01-07 17:15:30 +00002462 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002463 }
2464 }
2465 else
2466 {
2467 MagickRealType
2468 gamma;
2469
2470 gamma=0.0;
2471 for (i=0; i < n; i++)
2472 {
cristybb503372010-05-27 20:51:26 +00002473 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002474 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002475 alpha=contribution[i].weight*QuantumScale*
2476 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002477 pixel.red+=alpha*(p+j)->red;
2478 pixel.green+=alpha*(p+j)->green;
2479 pixel.blue+=alpha*(p+j)->blue;
2480 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2481 gamma+=alpha;
2482 }
2483 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002484 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2485 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2486 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2487 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002488 if ((image->colorspace == CMYKColorspace) &&
2489 (resize_image->colorspace == CMYKColorspace))
2490 {
2491 for (i=0; i < n; i++)
2492 {
cristybb503372010-05-27 20:51:26 +00002493 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002494 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002495 alpha=contribution[i].weight*QuantumScale*
2496 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002497 pixel.index+=alpha*indexes[j];
2498 }
cristyce70c172010-01-07 17:15:30 +00002499 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2500 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002501 }
2502 }
2503 if ((resize_image->storage_class == PseudoClass) &&
2504 (image->storage_class == PseudoClass))
2505 {
cristybb503372010-05-27 20:51:26 +00002506 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002507 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002508 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002509 image->columns+x);
2510 resize_indexes[x]=indexes[j];
2511 }
2512 q++;
2513 }
2514 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2515 status=MagickFalse;
2516 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2517 {
2518 MagickBooleanType
2519 proceed;
2520
cristyb5d5f722009-11-04 03:03:49 +00002521#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002522 #pragma omp critical (MagickCore_VerticalFilter)
2523#endif
cristy9af9b5d2010-08-15 17:04:28 +00002524 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002525 if (proceed == MagickFalse)
2526 status=MagickFalse;
2527 }
2528 }
2529 resize_view=DestroyCacheView(resize_view);
2530 image_view=DestroyCacheView(image_view);
2531 contributions=DestroyContributionThreadSet(contributions);
2532 return(status);
2533}
2534
cristybb503372010-05-27 20:51:26 +00002535MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2536 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002537 ExceptionInfo *exception)
2538{
2539#define WorkLoadFactor 0.265
2540
2541 FilterTypes
2542 filter_type;
2543
2544 Image
2545 *filter_image,
2546 *resize_image;
2547
cristy9af9b5d2010-08-15 17:04:28 +00002548 MagickOffsetType
2549 offset;
2550
cristy3ed852e2009-09-05 21:47:34 +00002551 MagickRealType
2552 x_factor,
2553 y_factor;
2554
2555 MagickSizeType
2556 span;
2557
2558 MagickStatusType
2559 status;
2560
2561 ResizeFilter
2562 *resize_filter;
2563
cristy3ed852e2009-09-05 21:47:34 +00002564 /*
2565 Acquire resize image.
2566 */
2567 assert(image != (Image *) NULL);
2568 assert(image->signature == MagickSignature);
2569 if (image->debug != MagickFalse)
2570 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2571 assert(exception != (ExceptionInfo *) NULL);
2572 assert(exception->signature == MagickSignature);
2573 if ((columns == 0) || (rows == 0))
2574 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2575 if ((columns == image->columns) && (rows == image->rows) &&
2576 (filter == UndefinedFilter) && (blur == 1.0))
2577 return(CloneImage(image,0,0,MagickTrue,exception));
2578 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2579 if (resize_image == (Image *) NULL)
2580 return(resize_image);
2581 /*
2582 Acquire resize filter.
2583 */
2584 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2585 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2586 if ((x_factor*y_factor) > WorkLoadFactor)
2587 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2588 else
2589 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2590 if (filter_image == (Image *) NULL)
2591 return(DestroyImage(resize_image));
2592 filter_type=LanczosFilter;
2593 if (filter != UndefinedFilter)
2594 filter_type=filter;
2595 else
2596 if ((x_factor == 1.0) && (y_factor == 1.0))
2597 filter_type=PointFilter;
2598 else
2599 if ((image->storage_class == PseudoClass) ||
2600 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2601 filter_type=MitchellFilter;
2602 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2603 exception);
2604 /*
2605 Resize image.
2606 */
cristy9af9b5d2010-08-15 17:04:28 +00002607 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002608 if ((x_factor*y_factor) > WorkLoadFactor)
2609 {
2610 span=(MagickSizeType) (filter_image->columns+rows);
2611 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002612 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002613 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002614 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002615 }
2616 else
2617 {
2618 span=(MagickSizeType) (filter_image->rows+columns);
2619 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002620 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002621 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002622 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002623 }
2624 /*
2625 Free resources.
2626 */
2627 filter_image=DestroyImage(filter_image);
2628 resize_filter=DestroyResizeFilter(resize_filter);
2629 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2630 return((Image *) NULL);
2631 resize_image->type=image->type;
2632 return(resize_image);
2633}
2634
2635/*
2636%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2637% %
2638% %
2639% %
2640% S a m p l e I m a g e %
2641% %
2642% %
2643% %
2644%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2645%
2646% SampleImage() scales an image to the desired dimensions with pixel
2647% sampling. Unlike other scaling methods, this method does not introduce
2648% any additional color into the scaled image.
2649%
2650% The format of the SampleImage method is:
2651%
cristybb503372010-05-27 20:51:26 +00002652% Image *SampleImage(const Image *image,const size_t columns,
2653% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002654%
2655% A description of each parameter follows:
2656%
2657% o image: the image.
2658%
2659% o columns: the number of columns in the sampled image.
2660%
2661% o rows: the number of rows in the sampled image.
2662%
2663% o exception: return any errors or warnings in this structure.
2664%
2665*/
cristybb503372010-05-27 20:51:26 +00002666MagickExport Image *SampleImage(const Image *image,const size_t columns,
2667 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002668{
2669#define SampleImageTag "Sample/Image"
2670
cristyc4c8d132010-01-07 01:58:38 +00002671 CacheView
2672 *image_view,
2673 *sample_view;
2674
cristy3ed852e2009-09-05 21:47:34 +00002675 Image
2676 *sample_image;
2677
cristy3ed852e2009-09-05 21:47:34 +00002678 MagickBooleanType
2679 status;
2680
cristy5f959472010-05-27 22:19:46 +00002681 MagickOffsetType
2682 progress;
2683
cristybb503372010-05-27 20:51:26 +00002684 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002685 x;
2686
cristy5f959472010-05-27 22:19:46 +00002687 ssize_t
2688 *x_offset,
2689 y;
2690
cristy3ed852e2009-09-05 21:47:34 +00002691 /*
2692 Initialize sampled image attributes.
2693 */
2694 assert(image != (const Image *) NULL);
2695 assert(image->signature == MagickSignature);
2696 if (image->debug != MagickFalse)
2697 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2698 assert(exception != (ExceptionInfo *) NULL);
2699 assert(exception->signature == MagickSignature);
2700 if ((columns == 0) || (rows == 0))
2701 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2702 if ((columns == image->columns) && (rows == image->rows))
2703 return(CloneImage(image,0,0,MagickTrue,exception));
2704 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2705 if (sample_image == (Image *) NULL)
2706 return((Image *) NULL);
2707 /*
2708 Allocate scan line buffer and column offset buffers.
2709 */
cristybb503372010-05-27 20:51:26 +00002710 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002711 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002712 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002713 {
2714 sample_image=DestroyImage(sample_image);
2715 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2716 }
cristybb503372010-05-27 20:51:26 +00002717 for (x=0; x < (ssize_t) sample_image->columns; x++)
2718 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002719 sample_image->columns);
2720 /*
2721 Sample each row.
2722 */
2723 status=MagickTrue;
2724 progress=0;
2725 image_view=AcquireCacheView(image);
2726 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002727#if defined(MAGICKCORE_OPENMP_SUPPORT)
2728 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002729#endif
cristybb503372010-05-27 20:51:26 +00002730 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002731 {
cristy3ed852e2009-09-05 21:47:34 +00002732 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002733 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002734
2735 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002736 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002737
2738 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002739 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002740
cristy3ed852e2009-09-05 21:47:34 +00002741 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002742 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002743
cristy03dbbd22010-09-19 23:04:47 +00002744 register ssize_t
2745 x;
2746
cristy9af9b5d2010-08-15 17:04:28 +00002747 ssize_t
2748 y_offset;
2749
cristy3ed852e2009-09-05 21:47:34 +00002750 if (status == MagickFalse)
2751 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002752 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2753 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002754 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2755 exception);
2756 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2757 exception);
2758 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2759 {
2760 status=MagickFalse;
2761 continue;
2762 }
2763 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2764 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2765 /*
2766 Sample each column.
2767 */
cristybb503372010-05-27 20:51:26 +00002768 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002769 *q++=p[x_offset[x]];
2770 if ((image->storage_class == PseudoClass) ||
2771 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002772 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002773 sample_indexes[x]=indexes[x_offset[x]];
2774 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2775 status=MagickFalse;
2776 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2777 {
2778 MagickBooleanType
2779 proceed;
2780
cristyb5d5f722009-11-04 03:03:49 +00002781#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002782 #pragma omp critical (MagickCore_SampleImage)
2783#endif
2784 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2785 if (proceed == MagickFalse)
2786 status=MagickFalse;
2787 }
2788 }
2789 image_view=DestroyCacheView(image_view);
2790 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002791 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002792 sample_image->type=image->type;
2793 return(sample_image);
2794}
2795
2796/*
2797%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2798% %
2799% %
2800% %
2801% S c a l e I m a g e %
2802% %
2803% %
2804% %
2805%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2806%
2807% ScaleImage() changes the size of an image to the given dimensions.
2808%
2809% The format of the ScaleImage method is:
2810%
cristybb503372010-05-27 20:51:26 +00002811% Image *ScaleImage(const Image *image,const size_t columns,
2812% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002813%
2814% A description of each parameter follows:
2815%
2816% o image: the image.
2817%
2818% o columns: the number of columns in the scaled image.
2819%
2820% o rows: the number of rows in the scaled image.
2821%
2822% o exception: return any errors or warnings in this structure.
2823%
2824*/
cristybb503372010-05-27 20:51:26 +00002825MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2826 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002827{
2828#define ScaleImageTag "Scale/Image"
2829
cristyed6cb232010-01-20 03:07:53 +00002830 CacheView
2831 *image_view,
2832 *scale_view;
2833
cristy3ed852e2009-09-05 21:47:34 +00002834 Image
2835 *scale_image;
2836
cristy3ed852e2009-09-05 21:47:34 +00002837 MagickBooleanType
2838 next_column,
2839 next_row,
2840 proceed;
2841
2842 MagickPixelPacket
2843 pixel,
2844 *scale_scanline,
2845 *scanline,
2846 *x_vector,
2847 *y_vector,
2848 zero;
2849
cristy3ed852e2009-09-05 21:47:34 +00002850 PointInfo
2851 scale,
2852 span;
2853
cristybb503372010-05-27 20:51:26 +00002854 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002855 i;
2856
cristy9af9b5d2010-08-15 17:04:28 +00002857 ssize_t
2858 number_rows,
2859 y;
2860
cristy3ed852e2009-09-05 21:47:34 +00002861 /*
2862 Initialize scaled image attributes.
2863 */
2864 assert(image != (const Image *) NULL);
2865 assert(image->signature == MagickSignature);
2866 if (image->debug != MagickFalse)
2867 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2868 assert(exception != (ExceptionInfo *) NULL);
2869 assert(exception->signature == MagickSignature);
2870 if ((columns == 0) || (rows == 0))
2871 return((Image *) NULL);
2872 if ((columns == image->columns) && (rows == image->rows))
2873 return(CloneImage(image,0,0,MagickTrue,exception));
2874 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2875 if (scale_image == (Image *) NULL)
2876 return((Image *) NULL);
2877 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2878 {
2879 InheritException(exception,&scale_image->exception);
2880 scale_image=DestroyImage(scale_image);
2881 return((Image *) NULL);
2882 }
2883 /*
2884 Allocate memory.
2885 */
2886 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2887 sizeof(*x_vector));
2888 scanline=x_vector;
2889 if (image->rows != scale_image->rows)
2890 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2891 sizeof(*scanline));
2892 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2893 scale_image->columns,sizeof(*scale_scanline));
2894 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2895 sizeof(*y_vector));
2896 if ((scanline == (MagickPixelPacket *) NULL) ||
2897 (scale_scanline == (MagickPixelPacket *) NULL) ||
2898 (x_vector == (MagickPixelPacket *) NULL) ||
2899 (y_vector == (MagickPixelPacket *) NULL))
2900 {
2901 scale_image=DestroyImage(scale_image);
2902 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2903 }
2904 /*
2905 Scale image.
2906 */
2907 number_rows=0;
2908 next_row=MagickTrue;
2909 span.y=1.0;
2910 scale.y=(double) scale_image->rows/(double) image->rows;
2911 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2912 sizeof(*y_vector));
2913 GetMagickPixelPacket(image,&pixel);
2914 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2915 i=0;
cristyed6cb232010-01-20 03:07:53 +00002916 image_view=AcquireCacheView(image);
2917 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002918 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002919 {
2920 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002921 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002922
2923 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002924 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002925
2926 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002927 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002928
cristy3ed852e2009-09-05 21:47:34 +00002929 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002930 *restrict s,
2931 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002932
2933 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002934 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002935
cristy9af9b5d2010-08-15 17:04:28 +00002936 register ssize_t
2937 x;
2938
cristyed6cb232010-01-20 03:07:53 +00002939 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2940 exception);
cristy3ed852e2009-09-05 21:47:34 +00002941 if (q == (PixelPacket *) NULL)
2942 break;
cristyba9a1e22010-11-10 23:14:28 +00002943 scale_indexes=GetCacheViewAuthenticIndexQueue(scale_view);
cristy3ed852e2009-09-05 21:47:34 +00002944 if (scale_image->rows == image->rows)
2945 {
2946 /*
2947 Read a new scanline.
2948 */
cristyed6cb232010-01-20 03:07:53 +00002949 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2950 exception);
cristy3ed852e2009-09-05 21:47:34 +00002951 if (p == (const PixelPacket *) NULL)
2952 break;
cristyed6cb232010-01-20 03:07:53 +00002953 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002954 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002955 {
cristyce70c172010-01-07 17:15:30 +00002956 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2957 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2958 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002959 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002960 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002961 if (indexes != (IndexPacket *) NULL)
2962 x_vector[x].index=(MagickRealType) indexes[x];
2963 p++;
2964 }
2965 }
2966 else
2967 {
2968 /*
2969 Scale Y direction.
2970 */
2971 while (scale.y < span.y)
2972 {
cristy9af9b5d2010-08-15 17:04:28 +00002973 if ((next_row != MagickFalse) &&
2974 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002975 {
2976 /*
2977 Read a new scanline.
2978 */
cristyed6cb232010-01-20 03:07:53 +00002979 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2980 exception);
cristy3ed852e2009-09-05 21:47:34 +00002981 if (p == (const PixelPacket *) NULL)
2982 break;
cristyed6cb232010-01-20 03:07:53 +00002983 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002984 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002985 {
cristyce70c172010-01-07 17:15:30 +00002986 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2987 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2988 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002989 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002990 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002991 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002992 if (indexes != (IndexPacket *) NULL)
2993 x_vector[x].index=(MagickRealType) indexes[x];
2994 p++;
2995 }
2996 number_rows++;
2997 }
cristybb503372010-05-27 20:51:26 +00002998 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002999 {
3000 y_vector[x].red+=scale.y*x_vector[x].red;
3001 y_vector[x].green+=scale.y*x_vector[x].green;
3002 y_vector[x].blue+=scale.y*x_vector[x].blue;
3003 if (scale_image->matte != MagickFalse)
3004 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
3005 if (scale_indexes != (IndexPacket *) NULL)
3006 y_vector[x].index+=scale.y*x_vector[x].index;
3007 }
3008 span.y-=scale.y;
3009 scale.y=(double) scale_image->rows/(double) image->rows;
3010 next_row=MagickTrue;
3011 }
cristybb503372010-05-27 20:51:26 +00003012 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00003013 {
3014 /*
3015 Read a new scanline.
3016 */
cristyed6cb232010-01-20 03:07:53 +00003017 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
3018 exception);
cristy3ed852e2009-09-05 21:47:34 +00003019 if (p == (const PixelPacket *) NULL)
3020 break;
cristyed6cb232010-01-20 03:07:53 +00003021 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00003022 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003023 {
cristyce70c172010-01-07 17:15:30 +00003024 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
3025 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
3026 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003027 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00003028 x_vector[x].opacity=(MagickRealType)
3029 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003030 if (indexes != (IndexPacket *) NULL)
3031 x_vector[x].index=(MagickRealType) indexes[x];
3032 p++;
3033 }
3034 number_rows++;
3035 next_row=MagickFalse;
3036 }
3037 s=scanline;
cristybb503372010-05-27 20:51:26 +00003038 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003039 {
3040 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3041 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3042 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3043 if (image->matte != MagickFalse)
3044 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3045 if (scale_indexes != (IndexPacket *) NULL)
3046 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3047 s->red=pixel.red;
3048 s->green=pixel.green;
3049 s->blue=pixel.blue;
3050 if (scale_image->matte != MagickFalse)
3051 s->opacity=pixel.opacity;
3052 if (scale_indexes != (IndexPacket *) NULL)
3053 s->index=pixel.index;
3054 s++;
3055 y_vector[x]=zero;
3056 }
3057 scale.y-=span.y;
3058 if (scale.y <= 0)
3059 {
3060 scale.y=(double) scale_image->rows/(double) image->rows;
3061 next_row=MagickTrue;
3062 }
3063 span.y=1.0;
3064 }
3065 if (scale_image->columns == image->columns)
3066 {
3067 /*
3068 Transfer scanline to scaled image.
3069 */
3070 s=scanline;
cristybb503372010-05-27 20:51:26 +00003071 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003072 {
cristyce70c172010-01-07 17:15:30 +00003073 q->red=ClampToQuantum(s->red);
3074 q->green=ClampToQuantum(s->green);
3075 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003076 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003077 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003078 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003079 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003080 q++;
3081 s++;
3082 }
3083 }
3084 else
3085 {
3086 /*
3087 Scale X direction.
3088 */
3089 pixel=zero;
3090 next_column=MagickFalse;
3091 span.x=1.0;
3092 s=scanline;
3093 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003094 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003095 {
3096 scale.x=(double) scale_image->columns/(double) image->columns;
3097 while (scale.x >= span.x)
3098 {
3099 if (next_column != MagickFalse)
3100 {
3101 pixel=zero;
3102 t++;
3103 }
3104 pixel.red+=span.x*s->red;
3105 pixel.green+=span.x*s->green;
3106 pixel.blue+=span.x*s->blue;
3107 if (image->matte != MagickFalse)
3108 pixel.opacity+=span.x*s->opacity;
3109 if (scale_indexes != (IndexPacket *) NULL)
3110 pixel.index+=span.x*s->index;
3111 t->red=pixel.red;
3112 t->green=pixel.green;
3113 t->blue=pixel.blue;
3114 if (scale_image->matte != MagickFalse)
3115 t->opacity=pixel.opacity;
3116 if (scale_indexes != (IndexPacket *) NULL)
3117 t->index=pixel.index;
3118 scale.x-=span.x;
3119 span.x=1.0;
3120 next_column=MagickTrue;
3121 }
3122 if (scale.x > 0)
3123 {
3124 if (next_column != MagickFalse)
3125 {
3126 pixel=zero;
3127 next_column=MagickFalse;
3128 t++;
3129 }
3130 pixel.red+=scale.x*s->red;
3131 pixel.green+=scale.x*s->green;
3132 pixel.blue+=scale.x*s->blue;
3133 if (scale_image->matte != MagickFalse)
3134 pixel.opacity+=scale.x*s->opacity;
3135 if (scale_indexes != (IndexPacket *) NULL)
3136 pixel.index+=scale.x*s->index;
3137 span.x-=scale.x;
3138 }
3139 s++;
3140 }
3141 if (span.x > 0)
3142 {
3143 s--;
3144 pixel.red+=span.x*s->red;
3145 pixel.green+=span.x*s->green;
3146 pixel.blue+=span.x*s->blue;
3147 if (scale_image->matte != MagickFalse)
3148 pixel.opacity+=span.x*s->opacity;
3149 if (scale_indexes != (IndexPacket *) NULL)
3150 pixel.index+=span.x*s->index;
3151 }
3152 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003153 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003154 {
3155 t->red=pixel.red;
3156 t->green=pixel.green;
3157 t->blue=pixel.blue;
3158 if (scale_image->matte != MagickFalse)
3159 t->opacity=pixel.opacity;
3160 if (scale_indexes != (IndexPacket *) NULL)
3161 t->index=pixel.index;
3162 }
3163 /*
3164 Transfer scanline to scaled image.
3165 */
3166 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003167 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003168 {
cristyce70c172010-01-07 17:15:30 +00003169 q->red=ClampToQuantum(t->red);
3170 q->green=ClampToQuantum(t->green);
3171 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003172 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003173 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003174 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003175 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003176 t++;
3177 q++;
3178 }
3179 }
cristyed6cb232010-01-20 03:07:53 +00003180 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003181 break;
cristy96b16132010-08-29 17:19:52 +00003182 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3183 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003184 if (proceed == MagickFalse)
3185 break;
3186 }
cristyed6cb232010-01-20 03:07:53 +00003187 scale_view=DestroyCacheView(scale_view);
3188 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003189 /*
3190 Free allocated memory.
3191 */
3192 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3193 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3194 if (scale_image->rows != image->rows)
3195 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3196 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3197 scale_image->type=image->type;
3198 return(scale_image);
3199}
3200
anthony02b4cb42010-10-10 04:54:35 +00003201#if 0
3202 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003203/*
3204%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3205% %
3206% %
3207% %
3208+ S e t R e s i z e F i l t e r S u p p o r t %
3209% %
3210% %
3211% %
3212%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3213%
3214% SetResizeFilterSupport() specifies which IR filter to use to window
3215%
3216% The format of the SetResizeFilterSupport method is:
3217%
3218% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3219% const MagickRealType support)
3220%
3221% A description of each parameter follows:
3222%
3223% o resize_filter: the resize filter.
3224%
3225% o support: the filter spport radius.
3226%
3227*/
3228MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3229 const MagickRealType support)
3230{
3231 assert(resize_filter != (ResizeFilter *) NULL);
3232 assert(resize_filter->signature == MagickSignature);
3233 resize_filter->support=support;
3234}
anthony02b4cb42010-10-10 04:54:35 +00003235#endif
cristy3ed852e2009-09-05 21:47:34 +00003236
3237/*
3238%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3239% %
3240% %
3241% %
3242% T h u m b n a i l I m a g e %
3243% %
3244% %
3245% %
3246%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3247%
3248% ThumbnailImage() changes the size of an image to the given dimensions and
3249% removes any associated profiles. The goal is to produce small low cost
3250% thumbnail images suited for display on the Web.
3251%
3252% The format of the ThumbnailImage method is:
3253%
cristybb503372010-05-27 20:51:26 +00003254% Image *ThumbnailImage(const Image *image,const size_t columns,
3255% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003256%
3257% A description of each parameter follows:
3258%
3259% o image: the image.
3260%
3261% o columns: the number of columns in the scaled image.
3262%
3263% o rows: the number of rows in the scaled image.
3264%
3265% o exception: return any errors or warnings in this structure.
3266%
3267*/
cristy9af9b5d2010-08-15 17:04:28 +00003268MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3269 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003270{
3271#define SampleFactor 5
3272
3273 char
3274 value[MaxTextExtent];
3275
3276 const char
3277 *name;
3278
3279 Image
3280 *thumbnail_image;
3281
3282 MagickRealType
3283 x_factor,
3284 y_factor;
3285
cristybb503372010-05-27 20:51:26 +00003286 size_t
cristy3ed852e2009-09-05 21:47:34 +00003287 version;
3288
cristy9af9b5d2010-08-15 17:04:28 +00003289 struct stat
3290 attributes;
3291
cristy3ed852e2009-09-05 21:47:34 +00003292 assert(image != (Image *) NULL);
3293 assert(image->signature == MagickSignature);
3294 if (image->debug != MagickFalse)
3295 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3296 assert(exception != (ExceptionInfo *) NULL);
3297 assert(exception->signature == MagickSignature);
3298 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3299 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3300 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003301 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3302 exception);
cristy3ed852e2009-09-05 21:47:34 +00003303 else
3304 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003305 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3306 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003307 else
3308 {
3309 Image
3310 *sample_image;
3311
3312 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3313 exception);
3314 if (sample_image == (Image *) NULL)
3315 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003316 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3317 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003318 sample_image=DestroyImage(sample_image);
3319 }
3320 if (thumbnail_image == (Image *) NULL)
3321 return(thumbnail_image);
3322 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3323 if (thumbnail_image->matte == MagickFalse)
3324 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3325 thumbnail_image->depth=8;
3326 thumbnail_image->interlace=NoInterlace;
3327 /*
3328 Strip all profiles except color profiles.
3329 */
3330 ResetImageProfileIterator(thumbnail_image);
3331 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3332 {
3333 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3334 {
cristy2b726bd2010-01-11 01:05:39 +00003335 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003336 ResetImageProfileIterator(thumbnail_image);
3337 }
3338 name=GetNextImageProfile(thumbnail_image);
3339 }
3340 (void) DeleteImageProperty(thumbnail_image,"comment");
3341 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003342 if (strstr(image->magick_filename,"//") == (char *) NULL)
3343 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003344 image->magick_filename);
3345 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3346 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3347 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3348 {
cristye8c25f92010-06-03 00:53:06 +00003349 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003350 attributes.st_mtime);
3351 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3352 }
cristye8c25f92010-06-03 00:53:06 +00003353 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003354 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003355 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003356 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003357 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3358 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3359 LocaleLower(value);
3360 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3361 (void) SetImageProperty(thumbnail_image,"software",
3362 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003363 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3364 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003365 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003366 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003367 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003368 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003369 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3370 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003371 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3372 return(thumbnail_image);
3373}