blob: 04fe02ce18cf4530209444e47e6c647927173658 [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%
cristy31456282011-02-06 21:07:04 +0000497% AcquireResizeFilter() allocates the ResizeFilter structure. Choose from
498% 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%
cristy31456282011-02-06 21:07:04 +0000519% FIR filters are used as is, and are limited to that filters support window
520% (unless over-ridden). 'Gaussian' while classed as an IIR filter, is also
521% simply clipped by its support size (currently 1.5 or approximatally 3*sigma
522% as recommended by many references)
cristy3ed852e2009-09-05 21:47:34 +0000523%
cristy31456282011-02-06 21:07:04 +0000524% The special a 'cylindrical' filter flag will promote the default 4-lobed
525% Windowed Sinc filter to a 3-lobed Windowed Jinc equivelent, which is better
526% suited to this style of image resampling. This typically happens when using
527% such a filter for images distortions.
cristy3ed852e2009-09-05 21:47:34 +0000528%
cristy31456282011-02-06 21:07:04 +0000529% Directly requesting 'Sinc', 'Jinc' function as a filter will force the use
530% of function without any windowing, or promotion for cylindrical usage. This
531% is not recommended, except by image processing experts, especially as part
532% of expert option filter function selection.
anthony06b1edf2010-10-25 01:19:50 +0000533%
cristy31456282011-02-06 21:07:04 +0000534% Two forms of the 'Sinc' function are available: Sinc and SincFast. Sinc is
535% computed using the traditional sin(pi*x)/(pi*x); it is selected if the user
536% specifically specifies the use of a Sinc filter. SincFast uses highly
537% accurate (and fast) polynomial (low Q) and rational (high Q) approximations,
538% and will be used by default in most cases.
anthonyba5a7c32010-09-15 02:42:25 +0000539%
cristy31456282011-02-06 21:07:04 +0000540% The Lanczos filter is a special 3-lobed Sinc-windowed Sinc filter (promoted
541% to Jinc-windowed Jinc for cylindrical (Elliptical Weighted Average) use).
542% The Sinc version is the most popular windowed filter.
anthony152700d2010-10-28 02:43:18 +0000543%
cristy31456282011-02-06 21:07:04 +0000544% LanczosSharp is a slightly sharpened (blur=0.9812505644269356 < 1) form of
545% the Lanczos filter, specifically designed for EWA distortion (as a
546% Jinc-Jinc); it can also be used as a slightly sharper orthogonal Lanczos
547% (Sinc-Sinc) filter. The chosen blur value comes as close as possible to
548% satisfying the following condition without changing the character of the
549% corresponding EWA filter:
anthony152700d2010-10-28 02:43:18 +0000550%
cristy31456282011-02-06 21:07:04 +0000551% 'No-Op' Vertical and Horizontal Line Preservation Condition: Images with
552% only vertical or horizontal features are preserved when performing 'no-op"
553% with EWA distortion.
anthony152700d2010-10-28 02:43:18 +0000554%
cristy31456282011-02-06 21:07:04 +0000555% The Lanczos2 and Lanczos2Sharp filters are 2-lobe versions of the Lanczos
556% filters. The 'sharp' version uses a blur factor of 0.9549963639785485,
557% again chosen because the resulting EWA filter comes as close as possible to
558% satisfying the above condition.
anthony06b1edf2010-10-25 01:19:50 +0000559%
cristy31456282011-02-06 21:07:04 +0000560% Robidoux is another filter tuned for EWA. It is the Keys cubic filter
561% defined by B=(228 - 108 sqrt(2))/199. Robidoux satisfies the "'No-Op'
562% Vertical and Horizontal Line Preservation Condition" exactly, and it
563% moderately blurs high frequency 'pixel-hash' patterns under no-op. It turns
564% out to be close to both Mitchell and Lanczos2Sharp. For example, its first
565% crossing is at (36 sqrt(2) + 123)/(72 sqrt(2) + 47), almost the same as the
566% first crossing of Mitchell and Lanczos2Sharp.
anthony61b5ddd2010-10-05 02:33:31 +0000567%
nicolas3061b8a2010-10-22 16:34:52 +0000568% 'EXPERT' OPTIONS:
nicolasce6dc292010-10-22 16:23:07 +0000569%
cristy31456282011-02-06 21:07:04 +0000570% These artifact "defines" are not recommended for production use without
571% expert knowledge of resampling, filtering, and the effects they have on the
572% resulting resampled (resize ro distorted) image.
nicolas3061b8a2010-10-22 16:34:52 +0000573%
anthony152700d2010-10-28 02:43:18 +0000574% They can be used to override any and all filter default, and it is
cristy31456282011-02-06 21:07:04 +0000575% recommended you make good use of "filter:verbose" to make sure that the
576% overall effect of your selection (before and after) is as expected.
nicolas3061b8a2010-10-22 16:34:52 +0000577%
cristy31456282011-02-06 21:07:04 +0000578% "filter:verbose" controls whether to output the exact results of the
579% filter selections made, as well as plotting data for graphing the
580% resulting filter over the filters support range.
cristy3ed852e2009-09-05 21:47:34 +0000581%
cristy31456282011-02-06 21:07:04 +0000582% "filter:filter" select the main function associated with this filter
583% name, as the weighting function of the filter. This can be used to
584% set a windowing function as a weighting function, for special
585% purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000586%
cristy31456282011-02-06 21:07:04 +0000587% If a "filter:window" operation has not been provided, a 'Box'
588% windowing function will be set to denote that no windowing function is
589% being used.
cristy3ed852e2009-09-05 21:47:34 +0000590%
cristy31456282011-02-06 21:07:04 +0000591% "filter:window" Select this windowing function for the filter. While any
592% filter could be used as a windowing function, using the 'first lobe' of
593% that filter over the whole support window, using a non-windowing
594% function is not advisible. If no weighting filter function is specifed
595% a 'SincFast' filter is used.
cristy3ed852e2009-09-05 21:47:34 +0000596%
cristy31456282011-02-06 21:07:04 +0000597% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter. This a
598% simpler method of setting filter support size that will correctly
599% handle the Sinc/Jinc switch for an operators filtering requirements.
600% Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000601%
cristy31456282011-02-06 21:07:04 +0000602% "filter:support" Set the support size for filtering to the size given.
603% This not recommended for Sinc/Jinc windowed filters (lobes should be
604% used instead). This will override any 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000605%
cristy31456282011-02-06 21:07:04 +0000606% "filter:win-support" Scale windowing function to this size instead. This
607% causes the windowing (or self-windowing Lagrange filter) to act is if
608% the support window it much much larger than what is actually supplied
609% to the calling operator. The filter however is still clipped to the
610% real support size given, by the support range suppiled to the caller.
611% If unset this will equal the normal filter support size.
anthonyb6d08c52010-09-13 01:17:04 +0000612%
cristy31456282011-02-06 21:07:04 +0000613% "filter:blur" Scale the filter and support window by this amount. A value
614% > 1 will generally result in a more burred image with more ringing
615% effects, while a value <1 will sharpen the resulting image with more
616% aliasing effects.
cristy3ed852e2009-09-05 21:47:34 +0000617%
cristy31456282011-02-06 21:07:04 +0000618% "filter:sigma" The sigma value to use for the Gaussian filter only.
619% Defaults to '1/2'. Using a different sigma effectively provides a
620% method of using the filter as a 'blur' convolution. Particularly when
621% using it for Distort.
anthonyf5e76ef2010-10-12 01:22:01 +0000622%
cristy3ed852e2009-09-05 21:47:34 +0000623% "filter:b"
cristy31456282011-02-06 21:07:04 +0000624% "filter:c" Override the preset B,C values for a Cubic type of filter.
625% If only one of these are given it is assumes to be a 'Keys' type of
626% filter such that B+2C=1, where Keys 'alpha' value = C.
cristy3ed852e2009-09-05 21:47:34 +0000627%
anthony06b1edf2010-10-25 01:19:50 +0000628% Examples:
cristy3ed852e2009-09-05 21:47:34 +0000629%
nicolas6e1267a2010-10-22 16:35:52 +0000630% Set a true un-windowed Sinc filter with 10 lobes (very slow):
anthony48f77622010-10-03 14:32:31 +0000631% -define filter:filter=Sinc
632% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000633%
nicolas6e1267a2010-10-22 16:35:52 +0000634% Set an 8 lobe Lanczos (Sinc or Jinc) filter:
cristy3ed852e2009-09-05 21:47:34 +0000635% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000636% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000637%
cristy3ed852e2009-09-05 21:47:34 +0000638% The format of the AcquireResizeFilter method is:
639%
640% ResizeFilter *AcquireResizeFilter(const Image *image,
641% const FilterTypes filter_type, const MagickBooleanType radial,
642% ExceptionInfo *exception)
643%
cristy33b1c162010-01-23 22:51:51 +0000644% A description of each parameter follows:
645%
cristy3ed852e2009-09-05 21:47:34 +0000646% o image: the image.
647%
cristy31456282011-02-06 21:07:04 +0000648% o filter: the filter type, defining a preset filter, window and support.
649% The artifact settings listed above will override those selections.
cristy3ed852e2009-09-05 21:47:34 +0000650%
anthony48f77622010-10-03 14:32:31 +0000651% o blur: blur the filter by this amount, use 1.0 if unknown. Image
cristy31456282011-02-06 21:07:04 +0000652% artifact "filter:blur" will override this API call usage, including any
653% internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000654%
cristy31456282011-02-06 21:07:04 +0000655% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical (radial)
656% filter (Jinc).
cristy3ed852e2009-09-05 21:47:34 +0000657%
658% o exception: return any errors or warnings in this structure.
659%
660*/
661MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000662 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000663 const MagickBooleanType cylindrical,ExceptionInfo *exception)
664{
665 const char
666 *artifact;
667
668 FilterTypes
669 filter_type,
670 window_type;
671
cristy3ed852e2009-09-05 21:47:34 +0000672 MagickRealType
673 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000674 C,
675 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000676
677 register ResizeFilter
678 *resize_filter;
679
680 /*
cristy31456282011-02-06 21:07:04 +0000681 Table Mapping given Filter, into Weighting and Windowing functions. A
682 'Box' windowing function means its a simble non-windowed filter. An
683 'SincFast' filter function could be upgraded to a 'Jinc' filter if a
684 "cylindrical", unless a 'Sinc' or 'SincFast' filter was specifically
685 requested.
anthony449887b2010-09-10 02:23:33 +0000686
cristy31456282011-02-06 21:07:04 +0000687 WARNING: The order of this tabel must match the order of the FilterTypes
688 enumeration specified in "resample.h", or the filter names will not match
689 the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000690
691 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000692 */
693 static struct
694 {
695 FilterTypes
696 filter,
697 window;
698 } const mapping[SentinelFilter] =
699 {
nicolasb7dff642010-10-25 02:04:14 +0000700 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
701 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
702 { BoxFilter, BoxFilter }, /* Box averaging filter */
703 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
704 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
705 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
706 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
anthony06b1edf2010-10-25 01:19:50 +0000707 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
nicolasb7dff642010-10-25 02:04:14 +0000708 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
709 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approx */
710 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
711 { CatromFilter, BoxFilter }, /* Cubic-Keys interpolator */
712 { MitchellFilter, BoxFilter }, /* 'Ideal' Cubic-Keys filter */
nicolasb7dff642010-10-25 02:04:14 +0000713 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
714 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony152700d2010-10-28 02:43:18 +0000715 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolasb7dff642010-10-25 02:04:14 +0000716 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
717 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
718 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
nicolasb7dff642010-10-25 02:04:14 +0000719 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
anthony06b1edf2010-10-25 01:19:50 +0000720 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
anthony152700d2010-10-28 02:43:18 +0000721 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing */
722 { LanczosFilter, LanczosFilter }, /* Lanczos Sinc-Sinc filters */
723 { LanczosSharpFilter, LanczosSharpFilter }, /* | these require */
724 { Lanczos2Filter, Lanczos2Filter }, /* | special handling */
725 { Lanczos2SharpFilter,Lanczos2SharpFilter },
726 { RobidouxFilter, BoxFilter }, /* Cubic Keys tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000727 };
728 /*
cristy31456282011-02-06 21:07:04 +0000729 Table mapping the filter/window from the above table to an actual function.
730 The default support size for that filter as a weighting function, the range
731 to scale with to use that function as a sinc windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000732
anthony07a3f7f2010-09-16 03:03:11 +0000733 Note that the filter_type -> function is 1 to 1 except for Sinc(),
cristy31456282011-02-06 21:07:04 +0000734 SincFast(), and CubicBC() functions, which may have multiple filter to
735 function associations.
nicolas07bac812010-09-19 18:47:02 +0000736
cristy31456282011-02-06 21:07:04 +0000737 See "filter:verbose" handling below for the function -> filter mapping.
cristy3ed852e2009-09-05 21:47:34 +0000738 */
739 static struct
740 {
741 MagickRealType
742 (*function)(const MagickRealType, const ResizeFilter*),
nicolas8eccc162010-10-16 19:48:13 +0000743 lobes, /* Default lobes/support size of the weighting filter. */
anthony450db502010-10-19 04:03:03 +0000744 scale, /* Support when function used as a windowing function
745 Typically equal to the location of the first zero crossing. */
746 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
cristy3ed852e2009-09-05 21:47:34 +0000747 } const filters[SentinelFilter] =
748 {
anthony61b5ddd2010-10-05 02:33:31 +0000749 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
750 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
751 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
752 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
753 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
754 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
755 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
756 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000757 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000758 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
759 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
760 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
nicolasd349be62010-10-28 18:57:38 +0000761 { CubicBC, 2.0, 8.0/7.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
nicolasf8689f42010-10-18 16:14:08 +0000762 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000763 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
anthony06b1edf2010-10-25 01:19:50 +0000764 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
anthony61b5ddd2010-10-05 02:33:31 +0000765 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
766 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
767 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
anthony61b5ddd2010-10-05 02:33:31 +0000768 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
769 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
anthony152700d2010-10-28 02:43:18 +0000770 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
771 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
772 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* lanczos, Sharpened */
773 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos, 2-lobed */
774 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2, sharpened */
anthony450db502010-10-19 04:03:03 +0000775 { CubicBC, 2.0, 1.1685777620836932,
nicolas0d5e5322010-10-22 15:29:30 +0000776 0.37821575509399867, 0.31089212245300067 }
777 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
cristy3ed852e2009-09-05 21:47:34 +0000778 };
779 /*
anthony9a98fc62010-10-11 02:47:19 +0000780 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
anthony450db502010-10-19 04:03:03 +0000781 function being used as a filter. It is used by the "filter:lobes" expert
cristy31456282011-02-06 21:07:04 +0000782 setting and for 'lobes' for Jinc functions in the previous table. This way
783 users do not have to deal with the highly irrational lobe sizes of the Jinc
784 filter.
anthony48f77622010-10-03 14:32:31 +0000785
nicolase473f722010-10-07 00:05:13 +0000786 Values taken from
cristy31456282011-02-06 21:07:04 +0000787 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp using
788 Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000789 */
790 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000791 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000792 {
nicolas8eccc162010-10-16 19:48:13 +0000793 1.2196698912665045,
794 2.2331305943815286,
795 3.2383154841662362,
796 4.2410628637960699,
797 5.2427643768701817,
798 6.2439216898644877,
799 7.244759868719957,
800 8.2453949139520427,
801 9.2458926849494673,
802 10.246293348754916,
803 11.246622794877883,
804 12.246898461138105,
805 13.247132522181061,
806 14.247333735806849,
anthonyc2d07db2010-09-15 23:47:40 +0000807 15.2475085630373,
nicolas8eccc162010-10-16 19:48:13 +0000808 16.247661874700962
cristy3ed852e2009-09-05 21:47:34 +0000809 };
810
cristy33b1c162010-01-23 22:51:51 +0000811 /*
812 Allocate resize filter.
813 */
cristy3ed852e2009-09-05 21:47:34 +0000814 assert(image != (const Image *) NULL);
815 assert(image->signature == MagickSignature);
816 if (image->debug != MagickFalse)
817 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
818 assert(UndefinedFilter < filter && filter < SentinelFilter);
819 assert(exception != (ExceptionInfo *) NULL);
820 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000821 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000822 if (resize_filter == (ResizeFilter *) NULL)
823 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000824 /*
825 Defaults for the requested filter.
826 */
827 filter_type=mapping[filter].filter;
828 window_type=mapping[filter].window;
anthony993d8382011-02-06 12:35:36 +0000829 resize_filter->blur = blur; /* function argument blur factor */
830 sigma = 0.5; /* guassian sigma of half a pixel by default */
anthony152700d2010-10-28 02:43:18 +0000831 /* Promote 1D Windowed Sinc Filters to a 2D Windowed Jinc filters */
832 if (cylindrical != MagickFalse && filter_type == SincFastFilter
833 && filter != SincFastFilter )
834 filter_type=JincFilter;
anthony61b5ddd2010-10-05 02:33:31 +0000835
anthony152700d2010-10-28 02:43:18 +0000836 /* Expert filter setting override */
cristy3ed852e2009-09-05 21:47:34 +0000837 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000838 if (artifact != (const char *) NULL)
839 {
anthony152700d2010-10-28 02:43:18 +0000840 ssize_t
841 option;
cristy9af9b5d2010-08-15 17:04:28 +0000842 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000843 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000844 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000845 filter_type=(FilterTypes) option;
846 window_type=BoxFilter;
847 }
nicolas07bac812010-09-19 18:47:02 +0000848 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000849 artifact=GetImageArtifact(image,"filter:window");
850 if (artifact != (const char *) NULL)
851 {
cristy9af9b5d2010-08-15 17:04:28 +0000852 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000853 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony152700d2010-10-28 02:43:18 +0000854 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000855 }
cristy3ed852e2009-09-05 21:47:34 +0000856 }
cristy33b1c162010-01-23 22:51:51 +0000857 else
858 {
anthony48f77622010-10-03 14:32:31 +0000859 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000860 artifact=GetImageArtifact(image,"filter:window");
861 if (artifact != (const char *) NULL)
862 {
anthony152700d2010-10-28 02:43:18 +0000863 ssize_t
864 option;
cristy33b1c162010-01-23 22:51:51 +0000865 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
866 artifact);
867 if ((UndefinedFilter < option) && (option < SentinelFilter))
868 {
anthony61b5ddd2010-10-05 02:33:31 +0000869 filter_type=cylindrical != MagickFalse ?
870 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000871 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000872 }
873 }
874 }
anthony152700d2010-10-28 02:43:18 +0000875
nicolas07bac812010-09-19 18:47:02 +0000876 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000877 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000878 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000879 resize_filter->window=filters[window_type].function;
880 resize_filter->scale=filters[window_type].scale;
anthony029ba0e2010-10-29 00:54:24 +0000881 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000882
anthonyf5e76ef2010-10-12 01:22:01 +0000883 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000884 if (cylindrical != MagickFalse)
885 switch (filter_type)
886 {
anthony10b8bc82010-10-02 12:48:46 +0000887 case BoxFilter:
888 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000889 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000890 break;
anthony152700d2010-10-28 02:43:18 +0000891 case LanczosFilter:
892 case LanczosSharpFilter:
893 case Lanczos2Filter:
894 case Lanczos2SharpFilter:
895 resize_filter->filter=filters[JincFilter].function;
896 resize_filter->window=filters[JincFilter].function;
anthony029ba0e2010-10-29 00:54:24 +0000897 resize_filter->scale=filters[JincFilter].scale;
898 /* number of lobes (support window size) remain unchanged */
anthony152700d2010-10-28 02:43:18 +0000899 break;
anthony81b8bf92010-10-02 13:54:34 +0000900 default:
901 break;
anthony10b8bc82010-10-02 12:48:46 +0000902 }
anthony152700d2010-10-28 02:43:18 +0000903 /* Global Sharpening (regardless of orthoginal/cylindrical) */
904 switch (filter_type)
905 {
906 case LanczosSharpFilter:
nicolasaf6ee7c2010-11-13 03:34:22 +0000907 resize_filter->blur *= 0.9812505644269356;
anthony152700d2010-10-28 02:43:18 +0000908 break;
909 case Lanczos2SharpFilter:
nicolasca48bce2010-11-14 17:54:53 +0000910 resize_filter->blur *= 0.9549963639785485;
anthony152700d2010-10-28 02:43:18 +0000911 break;
912 default:
913 break;
914 }
anthony61b5ddd2010-10-05 02:33:31 +0000915
anthonyf5e76ef2010-10-12 01:22:01 +0000916 /*
anthony06b1edf2010-10-25 01:19:50 +0000917 ** Other Expert Option Modifications
anthonyf5e76ef2010-10-12 01:22:01 +0000918 */
919
920 /* User Sigma Override - no support change */
921 artifact=GetImageArtifact(image,"filter:sigma");
922 if (artifact != (const char *) NULL)
923 sigma=StringToDouble(artifact);
anthony993d8382011-02-06 12:35:36 +0000924 /* Define coefficents for Gaussian */
anthonyf5e76ef2010-10-12 01:22:01 +0000925 if ( GaussianFilter ) {
cristy5cce74b2010-11-15 03:24:28 +0000926 resize_filter->coefficient[0]=1.0/(2.0*sigma*sigma);
927 resize_filter->coefficient[1]=(MagickRealType) (1.0/(Magick2PI*sigma*
anthony993d8382011-02-06 12:35:36 +0000928 sigma)); /* Normalization Multiplier - unneeded for filters */
anthonyf5e76ef2010-10-12 01:22:01 +0000929 }
930
931 /* Blur Override */
932 artifact=GetImageArtifact(image,"filter:blur");
933 if (artifact != (const char *) NULL)
934 resize_filter->blur=StringToDouble(artifact);
935 if (resize_filter->blur < MagickEpsilon)
936 resize_filter->blur=(MagickRealType) MagickEpsilon;
937
938 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000939 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000940 if (artifact != (const char *) NULL)
941 {
cristybb503372010-05-27 20:51:26 +0000942 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000943 lobes;
944
cristy96b16132010-08-29 17:19:52 +0000945 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000946 if (lobes < 1)
947 lobes=1;
948 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000949 }
anthony152700d2010-10-28 02:43:18 +0000950 /* Convert a Jinc function lobes value to a real support value */
anthony61b5ddd2010-10-05 02:33:31 +0000951 if (resize_filter->filter == Jinc)
952 {
953 if (resize_filter->support > 16)
954 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
955 else
956 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
957 }
958 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000959 artifact=GetImageArtifact(image,"filter:support");
960 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000961 resize_filter->support=fabs(StringToDouble(artifact));
962 /*
nicolas07bac812010-09-19 18:47:02 +0000963 Scale windowing function separatally to the support 'clipping'
964 window that calling operator is planning to actually use. (Expert
965 override)
cristy3ed852e2009-09-05 21:47:34 +0000966 */
anthony55f12332010-09-10 01:13:02 +0000967 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000968 artifact=GetImageArtifact(image,"filter:win-support");
969 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000970 resize_filter->window_support=fabs(StringToDouble(artifact));
971 /*
anthony029ba0e2010-10-29 00:54:24 +0000972 Adjust window function scaling to match windowing support for
anthony1f90a6b2010-09-14 08:56:31 +0000973 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000974 */
975 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +0000976
anthony55f12332010-09-10 01:13:02 +0000977 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000978 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000979 */
cristy3ed852e2009-09-05 21:47:34 +0000980 B=0.0;
981 C=0.0;
cristy33b1c162010-01-23 22:51:51 +0000982 if ((filters[filter_type].function == CubicBC) ||
983 (filters[window_type].function == CubicBC))
984 {
anthony2d9b8b52010-09-14 08:31:07 +0000985 B=filters[filter_type].B;
986 C=filters[filter_type].C;
987 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +0000988 {
anthony2d9b8b52010-09-14 08:31:07 +0000989 B=filters[window_type].B;
990 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +0000991 }
cristy33b1c162010-01-23 22:51:51 +0000992 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +0000993 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000994 {
995 B=StringToDouble(artifact);
nicolasd15ee9a2010-10-24 18:39:45 +0000996 C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +0000997 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +0000998 if (artifact != (const char *) NULL)
999 C=StringToDouble(artifact);
1000 }
1001 else
1002 {
1003 artifact=GetImageArtifact(image,"filter:c");
1004 if (artifact != (const char *) NULL)
1005 {
1006 C=StringToDouble(artifact);
nicolasb7dff642010-10-25 02:04:14 +00001007 B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001008 }
1009 }
nicolasc6bac3b2010-10-24 18:10:45 +00001010 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
nicolasd15ee9a2010-10-24 18:39:45 +00001011 {
anthony06b1edf2010-10-25 01:19:50 +00001012 const double twoB = B+B;
cristy5cce74b2010-11-15 03:24:28 +00001013 resize_filter->coefficient[0]=1.0-(1.0/3.0)*B;
1014 resize_filter->coefficient[1]=-3.0+twoB+C;
1015 resize_filter->coefficient[2]=2.0-1.5*B-C;
1016 resize_filter->coefficient[3]=(4.0/3.0)*B+4.0*C;
1017 resize_filter->coefficient[4]=-8.0*C-twoB;
1018 resize_filter->coefficient[5]=B+5.0*C;
1019 resize_filter->coefficient[6]=(-1.0/6.0)*B-C;
nicolasd15ee9a2010-10-24 18:39:45 +00001020 }
nicolasc6bac3b2010-10-24 18:10:45 +00001021 }
anthonyf5e76ef2010-10-12 01:22:01 +00001022
anthony55f12332010-09-10 01:13:02 +00001023 /*
nicolas07bac812010-09-19 18:47:02 +00001024 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001025 */
cristyf5b49372010-10-16 01:06:47 +00001026#if defined(MAGICKCORE_OPENMP_SUPPORT)
1027 #pragma omp master
1028 {
1029#endif
1030 artifact=GetImageArtifact(image,"filter:verbose");
anthony28ad1d72010-10-26 06:30:24 +00001031 if (IsMagickTrue(artifact))
cristyf5b49372010-10-16 01:06:47 +00001032 {
1033 double
anthony06b1edf2010-10-25 01:19:50 +00001034 support,
cristyf5b49372010-10-16 01:06:47 +00001035 x;
cristy3ed852e2009-09-05 21:47:34 +00001036
cristyf5b49372010-10-16 01:06:47 +00001037 /*
1038 Set the weighting function properly when the weighting
1039 function may not exactly match the filter of the same name.
anthony06b1edf2010-10-25 01:19:50 +00001040 EG: a Point filter is really uses a Box weighting function
cristyf5b49372010-10-16 01:06:47 +00001041 with a different support than is typically used.
cristyf5b49372010-10-16 01:06:47 +00001042 */
1043 if (resize_filter->filter == Box) filter_type=BoxFilter;
1044 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1045 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1046 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1047 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
anthony1ad20052010-10-29 01:53:03 +00001048 if (resize_filter->window == Box) window_type=BoxFilter;
anthony152700d2010-10-28 02:43:18 +00001049 if (resize_filter->window == Sinc) window_type=SincFilter;
1050 if (resize_filter->window == SincFast) window_type=SincFastFilter;
anthony1ad20052010-10-29 01:53:03 +00001051 if (resize_filter->window == Jinc) window_type=JincFilter;
anthony152700d2010-10-28 02:43:18 +00001052 if (resize_filter->window == CubicBC) window_type=CubicFilter;
cristyf5b49372010-10-16 01:06:47 +00001053 /*
1054 Report Filter Details.
1055 */
1056 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1057 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
anthony06b1edf2010-10-25 01:19:50 +00001058 (void) fprintf(stdout,"# filter = %s\n",
1059 MagickOptionToMnemonic(MagickFilterOptions,filter_type));
1060 (void) fprintf(stdout,"# window = %s\n",
1061 MagickOptionToMnemonic(MagickFilterOptions, window_type));
1062 (void) fprintf(stdout,"# support = %.*g\n",
1063 GetMagickPrecision(),(double) resize_filter->support);
1064 (void) fprintf(stdout,"# win-support = %.*g\n",
1065 GetMagickPrecision(),(double) resize_filter->window_support);
1066 (void) fprintf(stdout,"# scale_blur = %.*g\n",
1067 GetMagickPrecision(), (double)resize_filter->blur);
cristyf5b49372010-10-16 01:06:47 +00001068 if ( filter_type == GaussianFilter )
anthony06b1edf2010-10-25 01:19:50 +00001069 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",
1070 GetMagickPrecision(), (double)sigma);
1071 (void) fprintf(stdout,"# practical_support = %.*g\n",
1072 GetMagickPrecision(), (double)support);
cristyf5b49372010-10-16 01:06:47 +00001073 if ( filter_type == CubicFilter || window_type == CubicFilter )
anthony06b1edf2010-10-25 01:19:50 +00001074 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",
1075 GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
cristyf5b49372010-10-16 01:06:47 +00001076 (void) fprintf(stdout,"\n");
1077 /*
1078 Output values of resulting filter graph -- for graphing
1079 filter result.
1080 */
1081 for (x=0.0; x <= support; x+=0.01f)
1082 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1083 (double) GetResizeFilterWeight(resize_filter,x));
1084 /* A final value so gnuplot can graph the 'stop' properly. */
1085 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1086 0.0);
1087 }
1088 /* Output the above once only for each image - remove setting */
1089 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1090#if defined(MAGICKCORE_OPENMP_SUPPORT)
1091 }
1092#endif
cristy3ed852e2009-09-05 21:47:34 +00001093 return(resize_filter);
1094}
1095
1096/*
1097%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1098% %
1099% %
1100% %
1101% A d a p t i v e R e s i z e I m a g e %
1102% %
1103% %
1104% %
1105%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1106%
1107% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1108%
1109% The format of the AdaptiveResizeImage method is:
1110%
cristy9af9b5d2010-08-15 17:04:28 +00001111% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1112% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001113%
1114% A description of each parameter follows:
1115%
1116% o image: the image.
1117%
1118% o columns: the number of columns in the resized image.
1119%
1120% o rows: the number of rows in the resized image.
1121%
1122% o exception: return any errors or warnings in this structure.
1123%
1124*/
1125MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001126 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001127{
1128#define AdaptiveResizeImageTag "Resize/Image"
1129
cristyc4c8d132010-01-07 01:58:38 +00001130 CacheView
1131 *resize_view;
1132
cristy3ed852e2009-09-05 21:47:34 +00001133 Image
1134 *resize_image;
1135
cristy3ed852e2009-09-05 21:47:34 +00001136 MagickBooleanType
cristy5cce74b2010-11-15 03:24:28 +00001137 status;
cristy3ed852e2009-09-05 21:47:34 +00001138
cristy5cce74b2010-11-15 03:24:28 +00001139 MagickOffsetType
1140 progress;
cristy3ed852e2009-09-05 21:47:34 +00001141
1142 ResampleFilter
cristya6a18782010-11-15 01:56:25 +00001143 **resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00001144
cristy9af9b5d2010-08-15 17:04:28 +00001145 ssize_t
1146 y;
1147
cristy3ed852e2009-09-05 21:47:34 +00001148 /*
1149 Adaptively resize image.
1150 */
1151 assert(image != (const Image *) NULL);
1152 assert(image->signature == MagickSignature);
1153 if (image->debug != MagickFalse)
1154 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1155 assert(exception != (ExceptionInfo *) NULL);
1156 assert(exception->signature == MagickSignature);
1157 if ((columns == 0) || (rows == 0))
1158 return((Image *) NULL);
1159 if ((columns == image->columns) && (rows == image->rows))
1160 return(CloneImage(image,0,0,MagickTrue,exception));
1161 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1162 if (resize_image == (Image *) NULL)
1163 return((Image *) NULL);
1164 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1165 {
1166 InheritException(exception,&resize_image->exception);
1167 resize_image=DestroyImage(resize_image);
1168 return((Image *) NULL);
1169 }
cristy5cce74b2010-11-15 03:24:28 +00001170 status=MagickTrue;
1171 progress=0;
cristya6a18782010-11-15 01:56:25 +00001172 resample_filter=AcquireResampleFilterThreadSet(image,
1173 UndefinedVirtualPixelMethod,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00001174 resize_view=AcquireCacheView(resize_image);
cristy5cce74b2010-11-15 03:24:28 +00001175#if defined(MAGICKCORE_OPENMP_SUPPORT)
1176 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
1177#endif
cristybb503372010-05-27 20:51:26 +00001178 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001179 {
cristya6a18782010-11-15 01:56:25 +00001180 const int
1181 id = GetOpenMPThreadId();
1182
cristy5cce74b2010-11-15 03:24:28 +00001183 MagickPixelPacket
1184 pixel;
1185
1186 PointInfo
1187 offset;
1188
cristy3ed852e2009-09-05 21:47:34 +00001189 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001190 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001191
cristy3ed852e2009-09-05 21:47:34 +00001192 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001193 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001194
cristyb2a9be02010-11-15 15:00:14 +00001195 register ssize_t
1196 x;
1197
cristy5cce74b2010-11-15 03:24:28 +00001198 if (status == MagickFalse)
1199 continue;
cristy3ed852e2009-09-05 21:47:34 +00001200 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1201 exception);
1202 if (q == (PixelPacket *) NULL)
cristy5cce74b2010-11-15 03:24:28 +00001203 continue;
cristy3ed852e2009-09-05 21:47:34 +00001204 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1205 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristy5cce74b2010-11-15 03:24:28 +00001206 GetMagickPixelPacket(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001207 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001208 {
1209 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
cristya6a18782010-11-15 01:56:25 +00001210 (void) ResamplePixelColor(resample_filter[id],offset.x-0.5,offset.y-0.5,
cristy3ed852e2009-09-05 21:47:34 +00001211 &pixel);
1212 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1213 q++;
1214 }
1215 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
cristy5cce74b2010-11-15 03:24:28 +00001216 continue;
1217 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1218 {
1219 MagickBooleanType
1220 proceed;
1221
1222#if defined(MAGICKCORE_OPENMP_SUPPORT)
1223 #pragma omp critical (MagickCore_AdaptiveResizeImage)
1224#endif
1225 proceed=SetImageProgress(image,AdaptiveResizeImageTag,progress++,
1226 image->rows);
1227 if (proceed == MagickFalse)
1228 status=MagickFalse;
1229 }
cristy3ed852e2009-09-05 21:47:34 +00001230 }
cristya6a18782010-11-15 01:56:25 +00001231 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
cristy3ed852e2009-09-05 21:47:34 +00001232 resize_view=DestroyCacheView(resize_view);
cristy5cce74b2010-11-15 03:24:28 +00001233 if (status == MagickFalse)
1234 resize_image=DestroyImage(resize_image);
cristy3ed852e2009-09-05 21:47:34 +00001235 return(resize_image);
1236}
1237
1238/*
1239%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1240% %
1241% %
1242% %
1243+ B e s s e l O r d e r O n e %
1244% %
1245% %
1246% %
1247%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1248%
1249% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001250% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001251%
1252% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1253%
1254% j1(x) = x*j1(x);
1255%
1256% For x in (8,inf)
1257%
1258% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1259%
1260% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1261%
1262% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1263% = 1/sqrt(2) * (sin(x) - cos(x))
1264% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1265% = -1/sqrt(2) * (sin(x) + cos(x))
1266%
1267% The format of the BesselOrderOne method is:
1268%
1269% MagickRealType BesselOrderOne(MagickRealType x)
1270%
1271% A description of each parameter follows:
1272%
1273% o x: MagickRealType value.
1274%
1275*/
1276
1277#undef I0
1278static MagickRealType I0(MagickRealType x)
1279{
1280 MagickRealType
1281 sum,
1282 t,
1283 y;
1284
cristybb503372010-05-27 20:51:26 +00001285 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001286 i;
1287
1288 /*
1289 Zeroth order Bessel function of the first kind.
1290 */
1291 sum=1.0;
1292 y=x*x/4.0;
1293 t=y;
1294 for (i=2; t > MagickEpsilon; i++)
1295 {
1296 sum+=t;
1297 t*=y/((MagickRealType) i*i);
1298 }
1299 return(sum);
1300}
1301
1302#undef J1
1303static MagickRealType J1(MagickRealType x)
1304{
1305 MagickRealType
1306 p,
1307 q;
1308
cristybb503372010-05-27 20:51:26 +00001309 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001310 i;
1311
1312 static const double
1313 Pone[] =
1314 {
1315 0.581199354001606143928050809e+21,
1316 -0.6672106568924916298020941484e+20,
1317 0.2316433580634002297931815435e+19,
1318 -0.3588817569910106050743641413e+17,
1319 0.2908795263834775409737601689e+15,
1320 -0.1322983480332126453125473247e+13,
1321 0.3413234182301700539091292655e+10,
1322 -0.4695753530642995859767162166e+7,
1323 0.270112271089232341485679099e+4
1324 },
1325 Qone[] =
1326 {
1327 0.11623987080032122878585294e+22,
1328 0.1185770712190320999837113348e+20,
1329 0.6092061398917521746105196863e+17,
1330 0.2081661221307607351240184229e+15,
1331 0.5243710262167649715406728642e+12,
1332 0.1013863514358673989967045588e+10,
1333 0.1501793594998585505921097578e+7,
1334 0.1606931573481487801970916749e+4,
1335 0.1e+1
1336 };
1337
1338 p=Pone[8];
1339 q=Qone[8];
1340 for (i=7; i >= 0; i--)
1341 {
1342 p=p*x*x+Pone[i];
1343 q=q*x*x+Qone[i];
1344 }
1345 return(p/q);
1346}
1347
1348#undef P1
1349static MagickRealType P1(MagickRealType x)
1350{
1351 MagickRealType
1352 p,
1353 q;
1354
cristybb503372010-05-27 20:51:26 +00001355 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001356 i;
1357
1358 static const double
1359 Pone[] =
1360 {
1361 0.352246649133679798341724373e+5,
1362 0.62758845247161281269005675e+5,
1363 0.313539631109159574238669888e+5,
1364 0.49854832060594338434500455e+4,
1365 0.2111529182853962382105718e+3,
1366 0.12571716929145341558495e+1
1367 },
1368 Qone[] =
1369 {
1370 0.352246649133679798068390431e+5,
1371 0.626943469593560511888833731e+5,
1372 0.312404063819041039923015703e+5,
1373 0.4930396490181088979386097e+4,
1374 0.2030775189134759322293574e+3,
1375 0.1e+1
1376 };
1377
1378 p=Pone[5];
1379 q=Qone[5];
1380 for (i=4; i >= 0; i--)
1381 {
1382 p=p*(8.0/x)*(8.0/x)+Pone[i];
1383 q=q*(8.0/x)*(8.0/x)+Qone[i];
1384 }
1385 return(p/q);
1386}
1387
1388#undef Q1
1389static MagickRealType Q1(MagickRealType x)
1390{
1391 MagickRealType
1392 p,
1393 q;
1394
cristybb503372010-05-27 20:51:26 +00001395 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001396 i;
1397
1398 static const double
1399 Pone[] =
1400 {
1401 0.3511751914303552822533318e+3,
1402 0.7210391804904475039280863e+3,
1403 0.4259873011654442389886993e+3,
1404 0.831898957673850827325226e+2,
1405 0.45681716295512267064405e+1,
1406 0.3532840052740123642735e-1
1407 },
1408 Qone[] =
1409 {
1410 0.74917374171809127714519505e+4,
1411 0.154141773392650970499848051e+5,
1412 0.91522317015169922705904727e+4,
1413 0.18111867005523513506724158e+4,
1414 0.1038187585462133728776636e+3,
1415 0.1e+1
1416 };
1417
1418 p=Pone[5];
1419 q=Qone[5];
1420 for (i=4; i >= 0; i--)
1421 {
1422 p=p*(8.0/x)*(8.0/x)+Pone[i];
1423 q=q*(8.0/x)*(8.0/x)+Qone[i];
1424 }
1425 return(p/q);
1426}
1427
1428static MagickRealType BesselOrderOne(MagickRealType x)
1429{
1430 MagickRealType
1431 p,
1432 q;
1433
1434 if (x == 0.0)
1435 return(0.0);
1436 p=x;
1437 if (x < 0.0)
1438 x=(-x);
1439 if (x < 8.0)
1440 return(p*J1(x));
1441 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1442 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1443 cos((double) x))));
1444 if (p < 0.0)
1445 q=(-q);
1446 return(q);
1447}
1448
1449/*
1450%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1451% %
1452% %
1453% %
1454+ D e s t r o y R e s i z e F i l t e r %
1455% %
1456% %
1457% %
1458%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1459%
1460% DestroyResizeFilter() destroy the resize filter.
1461%
cristya2ffd7e2010-03-10 20:50:30 +00001462% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001463%
1464% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1465%
1466% A description of each parameter follows:
1467%
1468% o resize_filter: the resize filter.
1469%
1470*/
1471MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1472{
1473 assert(resize_filter != (ResizeFilter *) NULL);
1474 assert(resize_filter->signature == MagickSignature);
1475 resize_filter->signature=(~MagickSignature);
1476 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1477 return(resize_filter);
1478}
1479
1480/*
1481%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1482% %
1483% %
1484% %
1485+ G e t R e s i z e F i l t e r S u p p o r t %
1486% %
1487% %
1488% %
1489%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1490%
1491% GetResizeFilterSupport() return the current support window size for this
1492% filter. Note that this may have been enlarged by filter:blur factor.
1493%
1494% The format of the GetResizeFilterSupport method is:
1495%
1496% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1497%
1498% A description of each parameter follows:
1499%
1500% o filter: Image filter to use.
1501%
1502*/
1503MagickExport MagickRealType GetResizeFilterSupport(
1504 const ResizeFilter *resize_filter)
1505{
1506 assert(resize_filter != (ResizeFilter *) NULL);
1507 assert(resize_filter->signature == MagickSignature);
1508 return(resize_filter->support*resize_filter->blur);
1509}
1510
1511/*
1512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1513% %
1514% %
1515% %
1516+ G e t R e s i z e F i l t e r W e i g h t %
1517% %
1518% %
1519% %
1520%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1521%
1522% GetResizeFilterWeight evaluates the specified resize filter at the point x
1523% which usally lies between zero and the filters current 'support' and
1524% returns the weight of the filter function at that point.
1525%
1526% The format of the GetResizeFilterWeight method is:
1527%
1528% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1529% const MagickRealType x)
1530%
1531% A description of each parameter follows:
1532%
1533% o filter: the filter type.
1534%
1535% o x: the point.
1536%
1537*/
1538MagickExport MagickRealType GetResizeFilterWeight(
1539 const ResizeFilter *resize_filter,const MagickRealType x)
1540{
1541 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001542 scale,
cristy0eefadc2011-02-24 01:19:39 +00001543 weight,
cristyb8385862010-09-26 01:27:51 +00001544 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001545
1546 /*
1547 Windowing function - scale the weighting filter by this amount.
1548 */
1549 assert(resize_filter != (ResizeFilter *) NULL);
1550 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001551 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001552 if ((resize_filter->window_support < MagickEpsilon) ||
1553 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001554 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001555 else
1556 {
anthony55f12332010-09-10 01:13:02 +00001557 scale=resize_filter->scale;
1558 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001559 }
cristy0eefadc2011-02-24 01:19:39 +00001560 weight=scale*resize_filter->filter(x_blur,resize_filter);
cristybcf91042011-02-24 02:57:28 +00001561 return(weight);
cristy3ed852e2009-09-05 21:47:34 +00001562}
1563
1564/*
1565%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1566% %
1567% %
1568% %
1569% M a g n i f y I m a g e %
1570% %
1571% %
1572% %
1573%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1574%
1575% MagnifyImage() is a convenience method that scales an image proportionally
1576% to twice its size.
1577%
1578% The format of the MagnifyImage method is:
1579%
1580% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1581%
1582% A description of each parameter follows:
1583%
1584% o image: the image.
1585%
1586% o exception: return any errors or warnings in this structure.
1587%
1588*/
1589MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1590{
1591 Image
1592 *magnify_image;
1593
1594 assert(image != (Image *) NULL);
1595 assert(image->signature == MagickSignature);
1596 if (image->debug != MagickFalse)
1597 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1598 assert(exception != (ExceptionInfo *) NULL);
1599 assert(exception->signature == MagickSignature);
1600 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1601 1.0,exception);
1602 return(magnify_image);
1603}
1604
1605/*
1606%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1607% %
1608% %
1609% %
1610% M i n i f y I m a g e %
1611% %
1612% %
1613% %
1614%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1615%
1616% MinifyImage() is a convenience method that scales an image proportionally
1617% to half its size.
1618%
1619% The format of the MinifyImage method is:
1620%
1621% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1622%
1623% A description of each parameter follows:
1624%
1625% o image: the image.
1626%
1627% o exception: return any errors or warnings in this structure.
1628%
1629*/
1630MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1631{
1632 Image
1633 *minify_image;
1634
1635 assert(image != (Image *) NULL);
1636 assert(image->signature == MagickSignature);
1637 if (image->debug != MagickFalse)
1638 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1639 assert(exception != (ExceptionInfo *) NULL);
1640 assert(exception->signature == MagickSignature);
cristy31456282011-02-06 21:07:04 +00001641 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,1.0,
1642 exception);
cristy3ed852e2009-09-05 21:47:34 +00001643 return(minify_image);
1644}
1645
1646/*
1647%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1648% %
1649% %
1650% %
1651% R e s a m p l e I m a g e %
1652% %
1653% %
1654% %
1655%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1656%
1657% ResampleImage() resize image in terms of its pixel size, so that when
1658% displayed at the given resolution it will be the same size in terms of
1659% real world units as the original image at the original resolution.
1660%
1661% The format of the ResampleImage method is:
1662%
1663% Image *ResampleImage(Image *image,const double x_resolution,
1664% const double y_resolution,const FilterTypes filter,const double blur,
1665% ExceptionInfo *exception)
1666%
1667% A description of each parameter follows:
1668%
1669% o image: the image to be resized to fit the given resolution.
1670%
1671% o x_resolution: the new image x resolution.
1672%
1673% o y_resolution: the new image y resolution.
1674%
1675% o filter: Image filter to use.
1676%
1677% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1678%
1679*/
1680MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1681 const double y_resolution,const FilterTypes filter,const double blur,
1682 ExceptionInfo *exception)
1683{
1684#define ResampleImageTag "Resample/Image"
1685
1686 Image
1687 *resample_image;
1688
cristybb503372010-05-27 20:51:26 +00001689 size_t
cristy3ed852e2009-09-05 21:47:34 +00001690 height,
1691 width;
1692
1693 /*
1694 Initialize sampled image attributes.
1695 */
1696 assert(image != (const Image *) NULL);
1697 assert(image->signature == MagickSignature);
1698 if (image->debug != MagickFalse)
1699 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1700 assert(exception != (ExceptionInfo *) NULL);
1701 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001702 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1703 72.0 : image->x_resolution)+0.5);
1704 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1705 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001706 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1707 if (resample_image != (Image *) NULL)
1708 {
1709 resample_image->x_resolution=x_resolution;
1710 resample_image->y_resolution=y_resolution;
1711 }
1712 return(resample_image);
1713}
1714#if defined(MAGICKCORE_LQR_DELEGATE)
1715
1716/*
1717%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1718% %
1719% %
1720% %
1721% L i q u i d R e s c a l e I m a g e %
1722% %
1723% %
1724% %
1725%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1726%
1727% LiquidRescaleImage() rescales image with seam carving.
1728%
1729% The format of the LiquidRescaleImage method is:
1730%
1731% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001732% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001733% const double delta_x,const double rigidity,ExceptionInfo *exception)
1734%
1735% A description of each parameter follows:
1736%
1737% o image: the image.
1738%
1739% o columns: the number of columns in the rescaled image.
1740%
1741% o rows: the number of rows in the rescaled image.
1742%
1743% o delta_x: maximum seam transversal step (0 means straight seams).
1744%
1745% o rigidity: introduce a bias for non-straight seams (typically 0).
1746%
1747% o exception: return any errors or warnings in this structure.
1748%
1749*/
cristy9af9b5d2010-08-15 17:04:28 +00001750MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1751 const size_t rows,const double delta_x,const double rigidity,
1752 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001753{
1754#define LiquidRescaleImageTag "Rescale/Image"
1755
cristyc5c6f662010-09-22 14:23:02 +00001756 CacheView
1757 *rescale_view;
1758
cristy3ed852e2009-09-05 21:47:34 +00001759 const char
1760 *map;
1761
1762 guchar
1763 *packet;
1764
1765 Image
1766 *rescale_image;
1767
1768 int
1769 x,
1770 y;
1771
1772 LqrCarver
1773 *carver;
1774
1775 LqrRetVal
1776 lqr_status;
1777
1778 MagickBooleanType
1779 status;
1780
1781 MagickPixelPacket
1782 pixel;
1783
1784 unsigned char
1785 *pixels;
1786
1787 /*
1788 Liquid rescale image.
1789 */
1790 assert(image != (const Image *) NULL);
1791 assert(image->signature == MagickSignature);
1792 if (image->debug != MagickFalse)
1793 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1794 assert(exception != (ExceptionInfo *) NULL);
1795 assert(exception->signature == MagickSignature);
1796 if ((columns == 0) || (rows == 0))
1797 return((Image *) NULL);
1798 if ((columns == image->columns) && (rows == image->rows))
1799 return(CloneImage(image,0,0,MagickTrue,exception));
1800 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001801 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001802 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1803 {
1804 Image
1805 *resize_image;
1806
cristybb503372010-05-27 20:51:26 +00001807 size_t
cristy3ed852e2009-09-05 21:47:34 +00001808 height,
1809 width;
1810
1811 /*
1812 Honor liquid resize size limitations.
1813 */
1814 for (width=image->columns; columns >= (2*width-1); width*=2);
1815 for (height=image->rows; rows >= (2*height-1); height*=2);
1816 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1817 exception);
1818 if (resize_image == (Image *) NULL)
1819 return((Image *) NULL);
1820 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1821 rigidity,exception);
1822 resize_image=DestroyImage(resize_image);
1823 return(rescale_image);
1824 }
1825 map="RGB";
1826 if (image->matte == MagickFalse)
1827 map="RGBA";
1828 if (image->colorspace == CMYKColorspace)
1829 {
1830 map="CMYK";
1831 if (image->matte == MagickFalse)
1832 map="CMYKA";
1833 }
1834 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1835 strlen(map)*sizeof(*pixels));
1836 if (pixels == (unsigned char *) NULL)
1837 return((Image *) NULL);
1838 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1839 pixels,exception);
1840 if (status == MagickFalse)
1841 {
1842 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1843 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1844 }
1845 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1846 if (carver == (LqrCarver *) NULL)
1847 {
1848 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1849 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1850 }
1851 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1852 lqr_status=lqr_carver_resize(carver,columns,rows);
1853 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1854 lqr_carver_get_height(carver),MagickTrue,exception);
1855 if (rescale_image == (Image *) NULL)
1856 {
1857 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1858 return((Image *) NULL);
1859 }
1860 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1861 {
1862 InheritException(exception,&rescale_image->exception);
1863 rescale_image=DestroyImage(rescale_image);
1864 return((Image *) NULL);
1865 }
1866 GetMagickPixelPacket(rescale_image,&pixel);
1867 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001868 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001869 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1870 {
1871 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001872 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001873
1874 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001875 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001876
anthony22aad252010-09-23 06:59:07 +00001877 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001878 if (q == (PixelPacket *) NULL)
1879 break;
cristyc5c6f662010-09-22 14:23:02 +00001880 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001881 pixel.red=QuantumRange*(packet[0]/255.0);
1882 pixel.green=QuantumRange*(packet[1]/255.0);
1883 pixel.blue=QuantumRange*(packet[2]/255.0);
1884 if (image->colorspace != CMYKColorspace)
1885 {
1886 if (image->matte == MagickFalse)
1887 pixel.opacity=QuantumRange*(packet[3]/255.0);
1888 }
1889 else
1890 {
1891 pixel.index=QuantumRange*(packet[3]/255.0);
1892 if (image->matte == MagickFalse)
1893 pixel.opacity=QuantumRange*(packet[4]/255.0);
1894 }
1895 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001896 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001897 break;
1898 }
cristyc5c6f662010-09-22 14:23:02 +00001899 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001900 /*
1901 Relinquish resources.
1902 */
1903 lqr_carver_destroy(carver);
1904 return(rescale_image);
1905}
1906#else
1907MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001908 const size_t magick_unused(columns),const size_t magick_unused(rows),
1909 const double magick_unused(delta_x),const double magick_unused(rigidity),
1910 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001911{
1912 assert(image != (const Image *) NULL);
1913 assert(image->signature == MagickSignature);
1914 if (image->debug != MagickFalse)
1915 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1916 assert(exception != (ExceptionInfo *) NULL);
1917 assert(exception->signature == MagickSignature);
1918 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1919 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1920 return((Image *) NULL);
1921}
1922#endif
1923
1924/*
1925%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1926% %
1927% %
1928% %
1929% R e s i z e I m a g e %
1930% %
1931% %
1932% %
1933%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1934%
1935% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001936% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001937%
1938% If an undefined filter is given the filter defaults to Mitchell for a
1939% colormapped image, a image with a matte channel, or if the image is
1940% enlarged. Otherwise the filter defaults to a Lanczos.
1941%
1942% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1943%
1944% The format of the ResizeImage method is:
1945%
cristybb503372010-05-27 20:51:26 +00001946% Image *ResizeImage(Image *image,const size_t columns,
1947% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001948% ExceptionInfo *exception)
1949%
1950% A description of each parameter follows:
1951%
1952% o image: the image.
1953%
1954% o columns: the number of columns in the scaled image.
1955%
1956% o rows: the number of rows in the scaled image.
1957%
1958% o filter: Image filter to use.
1959%
cristy9af9b5d2010-08-15 17:04:28 +00001960% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1961% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001962%
1963% o exception: return any errors or warnings in this structure.
1964%
1965*/
1966
1967typedef struct _ContributionInfo
1968{
1969 MagickRealType
1970 weight;
1971
cristybb503372010-05-27 20:51:26 +00001972 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001973 pixel;
1974} ContributionInfo;
1975
1976static ContributionInfo **DestroyContributionThreadSet(
1977 ContributionInfo **contribution)
1978{
cristybb503372010-05-27 20:51:26 +00001979 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001980 i;
1981
1982 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001983 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001984 if (contribution[i] != (ContributionInfo *) NULL)
1985 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1986 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001987 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001988 return(contribution);
1989}
1990
1991static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1992{
cristybb503372010-05-27 20:51:26 +00001993 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001994 i;
1995
1996 ContributionInfo
1997 **contribution;
1998
cristybb503372010-05-27 20:51:26 +00001999 size_t
cristy3ed852e2009-09-05 21:47:34 +00002000 number_threads;
2001
2002 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002003 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00002004 sizeof(*contribution));
2005 if (contribution == (ContributionInfo **) NULL)
2006 return((ContributionInfo **) NULL);
2007 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00002008 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002009 {
2010 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2011 sizeof(**contribution));
2012 if (contribution[i] == (ContributionInfo *) NULL)
2013 return(DestroyContributionThreadSet(contribution));
2014 }
2015 return(contribution);
2016}
2017
2018static inline double MagickMax(const double x,const double y)
2019{
2020 if (x > y)
2021 return(x);
2022 return(y);
2023}
2024
2025static inline double MagickMin(const double x,const double y)
2026{
2027 if (x < y)
2028 return(x);
2029 return(y);
2030}
2031
2032static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2033 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002034 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002035{
2036#define ResizeImageTag "Resize/Image"
2037
cristyfa112112010-01-04 17:48:07 +00002038 CacheView
2039 *image_view,
2040 *resize_view;
2041
cristy3ed852e2009-09-05 21:47:34 +00002042 ClassType
2043 storage_class;
2044
2045 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002046 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002047
cristy3ed852e2009-09-05 21:47:34 +00002048 MagickBooleanType
2049 status;
2050
2051 MagickPixelPacket
2052 zero;
2053
2054 MagickRealType
2055 scale,
2056 support;
2057
cristy9af9b5d2010-08-15 17:04:28 +00002058 ssize_t
2059 x;
2060
cristy3ed852e2009-09-05 21:47:34 +00002061 /*
2062 Apply filter to resize horizontally from image to resize image.
2063 */
cristy5d824382010-09-06 14:00:17 +00002064 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002065 support=scale*GetResizeFilterSupport(resize_filter);
2066 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2067 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2068 {
2069 InheritException(exception,&resize_image->exception);
2070 return(MagickFalse);
2071 }
2072 if (support < 0.5)
2073 {
2074 /*
nicolas07bac812010-09-19 18:47:02 +00002075 Support too small even for nearest neighbour: Reduce to point
2076 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002077 */
2078 support=(MagickRealType) 0.5;
2079 scale=1.0;
2080 }
2081 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2082 if (contributions == (ContributionInfo **) NULL)
2083 {
2084 (void) ThrowMagickException(exception,GetMagickModule(),
2085 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2086 return(MagickFalse);
2087 }
2088 status=MagickTrue;
2089 scale=1.0/scale;
2090 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2091 image_view=AcquireCacheView(image);
2092 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002093#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002094 #pragma omp parallel for shared(status)
2095#endif
cristybb503372010-05-27 20:51:26 +00002096 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002097 {
cristy3ed852e2009-09-05 21:47:34 +00002098 MagickRealType
2099 center,
2100 density;
2101
2102 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002103 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002104
2105 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002106 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002107
cristy03dbbd22010-09-19 23:04:47 +00002108 register ContributionInfo
2109 *restrict contribution;
2110
cristy3ed852e2009-09-05 21:47:34 +00002111 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002112 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002113
cristy3ed852e2009-09-05 21:47:34 +00002114 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002115 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002116
cristy03dbbd22010-09-19 23:04:47 +00002117 register ssize_t
2118 y;
2119
cristy9af9b5d2010-08-15 17:04:28 +00002120 ssize_t
2121 n,
2122 start,
2123 stop;
2124
cristy3ed852e2009-09-05 21:47:34 +00002125 if (status == MagickFalse)
2126 continue;
2127 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002128 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2129 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002130 density=0.0;
2131 contribution=contributions[GetOpenMPThreadId()];
2132 for (n=0; n < (stop-start); n++)
2133 {
2134 contribution[n].pixel=start+n;
2135 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2136 ((MagickRealType) (start+n)-center+0.5));
2137 density+=contribution[n].weight;
2138 }
2139 if ((density != 0.0) && (density != 1.0))
2140 {
cristybb503372010-05-27 20:51:26 +00002141 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002142 i;
2143
2144 /*
2145 Normalize.
2146 */
2147 density=1.0/density;
2148 for (i=0; i < n; i++)
2149 contribution[i].weight*=density;
2150 }
cristy9af9b5d2010-08-15 17:04:28 +00002151 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2152 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002153 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2154 exception);
2155 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2156 {
2157 status=MagickFalse;
2158 continue;
2159 }
2160 indexes=GetCacheViewVirtualIndexQueue(image_view);
2161 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002162 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002163 {
cristy3ed852e2009-09-05 21:47:34 +00002164 MagickPixelPacket
2165 pixel;
2166
2167 MagickRealType
2168 alpha;
2169
cristybb503372010-05-27 20:51:26 +00002170 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002171 i;
2172
cristy9af9b5d2010-08-15 17:04:28 +00002173 ssize_t
2174 j;
2175
cristy3ed852e2009-09-05 21:47:34 +00002176 pixel=zero;
2177 if (image->matte == MagickFalse)
2178 {
2179 for (i=0; i < n; i++)
2180 {
2181 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2182 (contribution[i].pixel-contribution[0].pixel);
2183 alpha=contribution[i].weight;
2184 pixel.red+=alpha*(p+j)->red;
2185 pixel.green+=alpha*(p+j)->green;
2186 pixel.blue+=alpha*(p+j)->blue;
2187 pixel.opacity+=alpha*(p+j)->opacity;
2188 }
cristyce70c172010-01-07 17:15:30 +00002189 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2190 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2191 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2192 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002193 if ((image->colorspace == CMYKColorspace) &&
2194 (resize_image->colorspace == CMYKColorspace))
2195 {
2196 for (i=0; i < n; i++)
2197 {
2198 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2199 (contribution[i].pixel-contribution[0].pixel);
2200 alpha=contribution[i].weight;
2201 pixel.index+=alpha*indexes[j];
2202 }
cristyce70c172010-01-07 17:15:30 +00002203 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002204 }
2205 }
2206 else
2207 {
2208 MagickRealType
2209 gamma;
2210
2211 gamma=0.0;
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);
cristy46f08202010-01-10 04:04:21 +00002216 alpha=contribution[i].weight*QuantumScale*
2217 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002218 pixel.red+=alpha*(p+j)->red;
2219 pixel.green+=alpha*(p+j)->green;
2220 pixel.blue+=alpha*(p+j)->blue;
2221 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2222 gamma+=alpha;
2223 }
2224 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002225 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2226 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2227 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2228 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002229 if ((image->colorspace == CMYKColorspace) &&
2230 (resize_image->colorspace == CMYKColorspace))
2231 {
2232 for (i=0; i < n; i++)
2233 {
2234 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2235 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002236 alpha=contribution[i].weight*QuantumScale*
2237 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002238 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002239 }
cristyce70c172010-01-07 17:15:30 +00002240 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
cristy1545b342011-02-24 00:22:57 +00002241 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002242 }
2243 }
2244 if ((resize_image->storage_class == PseudoClass) &&
2245 (image->storage_class == PseudoClass))
2246 {
cristybb503372010-05-27 20:51:26 +00002247 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002248 1.0)+0.5);
2249 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2250 (contribution[i-start].pixel-contribution[0].pixel);
2251 resize_indexes[y]=indexes[j];
2252 }
2253 q++;
2254 }
2255 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2256 status=MagickFalse;
2257 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2258 {
2259 MagickBooleanType
2260 proceed;
2261
cristyb5d5f722009-11-04 03:03:49 +00002262#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002263 #pragma omp critical (MagickCore_HorizontalFilter)
2264#endif
cristy9af9b5d2010-08-15 17:04:28 +00002265 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002266 if (proceed == MagickFalse)
2267 status=MagickFalse;
2268 }
2269 }
2270 resize_view=DestroyCacheView(resize_view);
2271 image_view=DestroyCacheView(image_view);
2272 contributions=DestroyContributionThreadSet(contributions);
2273 return(status);
2274}
2275
2276static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2277 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002278 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002279{
cristyfa112112010-01-04 17:48:07 +00002280 CacheView
2281 *image_view,
2282 *resize_view;
2283
cristy3ed852e2009-09-05 21:47:34 +00002284 ClassType
2285 storage_class;
2286
2287 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002288 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002289
cristy3ed852e2009-09-05 21:47:34 +00002290 MagickBooleanType
2291 status;
2292
2293 MagickPixelPacket
2294 zero;
2295
2296 MagickRealType
2297 scale,
2298 support;
2299
cristy9af9b5d2010-08-15 17:04:28 +00002300 ssize_t
2301 y;
2302
cristy3ed852e2009-09-05 21:47:34 +00002303 /*
cristy9af9b5d2010-08-15 17:04:28 +00002304 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002305 */
cristy5d824382010-09-06 14:00:17 +00002306 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002307 support=scale*GetResizeFilterSupport(resize_filter);
2308 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2309 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2310 {
2311 InheritException(exception,&resize_image->exception);
2312 return(MagickFalse);
2313 }
2314 if (support < 0.5)
2315 {
2316 /*
nicolas07bac812010-09-19 18:47:02 +00002317 Support too small even for nearest neighbour: Reduce to point
2318 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002319 */
2320 support=(MagickRealType) 0.5;
2321 scale=1.0;
2322 }
2323 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2324 if (contributions == (ContributionInfo **) NULL)
2325 {
2326 (void) ThrowMagickException(exception,GetMagickModule(),
2327 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2328 return(MagickFalse);
2329 }
2330 status=MagickTrue;
2331 scale=1.0/scale;
2332 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2333 image_view=AcquireCacheView(image);
2334 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002335#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002336 #pragma omp parallel for shared(status)
2337#endif
cristybb503372010-05-27 20:51:26 +00002338 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002339 {
cristy3ed852e2009-09-05 21:47:34 +00002340 MagickRealType
2341 center,
2342 density;
2343
2344 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002345 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002346
2347 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002348 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002349
cristy03dbbd22010-09-19 23:04:47 +00002350 register ContributionInfo
2351 *restrict contribution;
2352
cristy3ed852e2009-09-05 21:47:34 +00002353 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002354 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002355
cristy9af9b5d2010-08-15 17:04:28 +00002356 register PixelPacket
2357 *restrict q;
2358
cristybb503372010-05-27 20:51:26 +00002359 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002360 x;
2361
cristy9af9b5d2010-08-15 17:04:28 +00002362 ssize_t
2363 n,
2364 start,
2365 stop;
cristy3ed852e2009-09-05 21:47:34 +00002366
2367 if (status == MagickFalse)
2368 continue;
cristy679e6962010-03-18 00:42:45 +00002369 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002370 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2371 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002372 density=0.0;
2373 contribution=contributions[GetOpenMPThreadId()];
2374 for (n=0; n < (stop-start); n++)
2375 {
2376 contribution[n].pixel=start+n;
2377 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2378 ((MagickRealType) (start+n)-center+0.5));
2379 density+=contribution[n].weight;
2380 }
2381 if ((density != 0.0) && (density != 1.0))
2382 {
cristybb503372010-05-27 20:51:26 +00002383 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002384 i;
2385
2386 /*
2387 Normalize.
2388 */
2389 density=1.0/density;
2390 for (i=0; i < n; i++)
2391 contribution[i].weight*=density;
2392 }
2393 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002394 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2395 exception);
cristy3ed852e2009-09-05 21:47:34 +00002396 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2397 exception);
2398 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2399 {
2400 status=MagickFalse;
2401 continue;
2402 }
2403 indexes=GetCacheViewVirtualIndexQueue(image_view);
2404 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002405 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002406 {
cristy3ed852e2009-09-05 21:47:34 +00002407 MagickPixelPacket
2408 pixel;
2409
2410 MagickRealType
2411 alpha;
2412
cristybb503372010-05-27 20:51:26 +00002413 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002414 i;
2415
cristy9af9b5d2010-08-15 17:04:28 +00002416 ssize_t
2417 j;
2418
cristy3ed852e2009-09-05 21:47:34 +00002419 pixel=zero;
2420 if (image->matte == MagickFalse)
2421 {
2422 for (i=0; i < n; i++)
2423 {
cristybb503372010-05-27 20:51:26 +00002424 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002425 image->columns+x);
2426 alpha=contribution[i].weight;
2427 pixel.red+=alpha*(p+j)->red;
2428 pixel.green+=alpha*(p+j)->green;
2429 pixel.blue+=alpha*(p+j)->blue;
2430 pixel.opacity+=alpha*(p+j)->opacity;
2431 }
cristyce70c172010-01-07 17:15:30 +00002432 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2433 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2434 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2435 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002436 if ((image->colorspace == CMYKColorspace) &&
2437 (resize_image->colorspace == CMYKColorspace))
2438 {
2439 for (i=0; i < n; i++)
2440 {
cristybb503372010-05-27 20:51:26 +00002441 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002442 image->columns+x);
2443 alpha=contribution[i].weight;
2444 pixel.index+=alpha*indexes[j];
2445 }
cristyce70c172010-01-07 17:15:30 +00002446 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002447 }
2448 }
2449 else
2450 {
2451 MagickRealType
2452 gamma;
2453
2454 gamma=0.0;
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);
cristy46f08202010-01-10 04:04:21 +00002459 alpha=contribution[i].weight*QuantumScale*
2460 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002461 pixel.red+=alpha*(p+j)->red;
2462 pixel.green+=alpha*(p+j)->green;
2463 pixel.blue+=alpha*(p+j)->blue;
2464 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2465 gamma+=alpha;
2466 }
2467 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002468 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2469 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2470 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2471 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002472 if ((image->colorspace == CMYKColorspace) &&
2473 (resize_image->colorspace == CMYKColorspace))
2474 {
2475 for (i=0; i < n; i++)
2476 {
cristybb503372010-05-27 20:51:26 +00002477 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002478 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002479 alpha=contribution[i].weight*QuantumScale*
2480 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002481 pixel.index+=alpha*indexes[j];
2482 }
cristyce70c172010-01-07 17:15:30 +00002483 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2484 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002485 }
2486 }
2487 if ((resize_image->storage_class == PseudoClass) &&
2488 (image->storage_class == PseudoClass))
2489 {
cristybb503372010-05-27 20:51:26 +00002490 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002491 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002492 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002493 image->columns+x);
2494 resize_indexes[x]=indexes[j];
2495 }
2496 q++;
2497 }
2498 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2499 status=MagickFalse;
2500 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2501 {
2502 MagickBooleanType
2503 proceed;
2504
cristyb5d5f722009-11-04 03:03:49 +00002505#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002506 #pragma omp critical (MagickCore_VerticalFilter)
2507#endif
cristy9af9b5d2010-08-15 17:04:28 +00002508 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002509 if (proceed == MagickFalse)
2510 status=MagickFalse;
2511 }
2512 }
2513 resize_view=DestroyCacheView(resize_view);
2514 image_view=DestroyCacheView(image_view);
2515 contributions=DestroyContributionThreadSet(contributions);
2516 return(status);
2517}
2518
cristybb503372010-05-27 20:51:26 +00002519MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2520 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002521 ExceptionInfo *exception)
2522{
2523#define WorkLoadFactor 0.265
2524
2525 FilterTypes
2526 filter_type;
2527
2528 Image
2529 *filter_image,
2530 *resize_image;
2531
cristy9af9b5d2010-08-15 17:04:28 +00002532 MagickOffsetType
2533 offset;
2534
cristy3ed852e2009-09-05 21:47:34 +00002535 MagickRealType
2536 x_factor,
2537 y_factor;
2538
2539 MagickSizeType
2540 span;
2541
2542 MagickStatusType
2543 status;
2544
2545 ResizeFilter
2546 *resize_filter;
2547
cristy3ed852e2009-09-05 21:47:34 +00002548 /*
2549 Acquire resize image.
2550 */
2551 assert(image != (Image *) NULL);
2552 assert(image->signature == MagickSignature);
2553 if (image->debug != MagickFalse)
2554 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2555 assert(exception != (ExceptionInfo *) NULL);
2556 assert(exception->signature == MagickSignature);
2557 if ((columns == 0) || (rows == 0))
2558 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2559 if ((columns == image->columns) && (rows == image->rows) &&
2560 (filter == UndefinedFilter) && (blur == 1.0))
2561 return(CloneImage(image,0,0,MagickTrue,exception));
2562 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2563 if (resize_image == (Image *) NULL)
2564 return(resize_image);
2565 /*
2566 Acquire resize filter.
2567 */
2568 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2569 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2570 if ((x_factor*y_factor) > WorkLoadFactor)
2571 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2572 else
2573 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2574 if (filter_image == (Image *) NULL)
2575 return(DestroyImage(resize_image));
2576 filter_type=LanczosFilter;
2577 if (filter != UndefinedFilter)
2578 filter_type=filter;
2579 else
2580 if ((x_factor == 1.0) && (y_factor == 1.0))
2581 filter_type=PointFilter;
2582 else
2583 if ((image->storage_class == PseudoClass) ||
2584 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2585 filter_type=MitchellFilter;
2586 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2587 exception);
2588 /*
2589 Resize image.
2590 */
cristy9af9b5d2010-08-15 17:04:28 +00002591 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002592 if ((x_factor*y_factor) > WorkLoadFactor)
2593 {
2594 span=(MagickSizeType) (filter_image->columns+rows);
2595 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002596 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002597 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002598 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002599 }
2600 else
2601 {
2602 span=(MagickSizeType) (filter_image->rows+columns);
2603 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002604 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002605 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002606 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002607 }
2608 /*
2609 Free resources.
2610 */
2611 filter_image=DestroyImage(filter_image);
2612 resize_filter=DestroyResizeFilter(resize_filter);
2613 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2614 return((Image *) NULL);
2615 resize_image->type=image->type;
2616 return(resize_image);
2617}
2618
2619/*
2620%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2621% %
2622% %
2623% %
2624% S a m p l e I m a g e %
2625% %
2626% %
2627% %
2628%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2629%
2630% SampleImage() scales an image to the desired dimensions with pixel
2631% sampling. Unlike other scaling methods, this method does not introduce
2632% any additional color into the scaled image.
2633%
2634% The format of the SampleImage method is:
2635%
cristybb503372010-05-27 20:51:26 +00002636% Image *SampleImage(const Image *image,const size_t columns,
2637% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002638%
2639% A description of each parameter follows:
2640%
2641% o image: the image.
2642%
2643% o columns: the number of columns in the sampled image.
2644%
2645% o rows: the number of rows in the sampled image.
2646%
2647% o exception: return any errors or warnings in this structure.
2648%
2649*/
cristybb503372010-05-27 20:51:26 +00002650MagickExport Image *SampleImage(const Image *image,const size_t columns,
2651 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002652{
2653#define SampleImageTag "Sample/Image"
2654
cristyc4c8d132010-01-07 01:58:38 +00002655 CacheView
2656 *image_view,
2657 *sample_view;
2658
cristy3ed852e2009-09-05 21:47:34 +00002659 Image
2660 *sample_image;
2661
cristy3ed852e2009-09-05 21:47:34 +00002662 MagickBooleanType
2663 status;
2664
cristy5f959472010-05-27 22:19:46 +00002665 MagickOffsetType
2666 progress;
2667
cristybb503372010-05-27 20:51:26 +00002668 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002669 x;
2670
cristy5f959472010-05-27 22:19:46 +00002671 ssize_t
2672 *x_offset,
2673 y;
2674
cristy3ed852e2009-09-05 21:47:34 +00002675 /*
2676 Initialize sampled image attributes.
2677 */
2678 assert(image != (const Image *) NULL);
2679 assert(image->signature == MagickSignature);
2680 if (image->debug != MagickFalse)
2681 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2682 assert(exception != (ExceptionInfo *) NULL);
2683 assert(exception->signature == MagickSignature);
2684 if ((columns == 0) || (rows == 0))
2685 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2686 if ((columns == image->columns) && (rows == image->rows))
2687 return(CloneImage(image,0,0,MagickTrue,exception));
2688 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2689 if (sample_image == (Image *) NULL)
2690 return((Image *) NULL);
2691 /*
2692 Allocate scan line buffer and column offset buffers.
2693 */
cristybb503372010-05-27 20:51:26 +00002694 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002695 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002696 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002697 {
2698 sample_image=DestroyImage(sample_image);
2699 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2700 }
cristybb503372010-05-27 20:51:26 +00002701 for (x=0; x < (ssize_t) sample_image->columns; x++)
2702 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002703 sample_image->columns);
2704 /*
2705 Sample each row.
2706 */
2707 status=MagickTrue;
2708 progress=0;
2709 image_view=AcquireCacheView(image);
2710 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002711#if defined(MAGICKCORE_OPENMP_SUPPORT)
2712 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002713#endif
cristybb503372010-05-27 20:51:26 +00002714 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002715 {
cristy3ed852e2009-09-05 21:47:34 +00002716 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002717 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002718
2719 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002720 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002721
2722 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002723 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002724
cristy3ed852e2009-09-05 21:47:34 +00002725 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002726 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002727
cristy03dbbd22010-09-19 23:04:47 +00002728 register ssize_t
2729 x;
2730
cristy9af9b5d2010-08-15 17:04:28 +00002731 ssize_t
2732 y_offset;
2733
cristy3ed852e2009-09-05 21:47:34 +00002734 if (status == MagickFalse)
2735 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002736 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2737 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002738 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2739 exception);
2740 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2741 exception);
2742 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2743 {
2744 status=MagickFalse;
2745 continue;
2746 }
2747 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2748 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2749 /*
2750 Sample each column.
2751 */
cristybb503372010-05-27 20:51:26 +00002752 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002753 *q++=p[x_offset[x]];
2754 if ((image->storage_class == PseudoClass) ||
2755 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002756 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002757 sample_indexes[x]=indexes[x_offset[x]];
2758 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2759 status=MagickFalse;
2760 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2761 {
2762 MagickBooleanType
2763 proceed;
2764
cristyb5d5f722009-11-04 03:03:49 +00002765#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002766 #pragma omp critical (MagickCore_SampleImage)
2767#endif
2768 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2769 if (proceed == MagickFalse)
2770 status=MagickFalse;
2771 }
2772 }
2773 image_view=DestroyCacheView(image_view);
2774 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002775 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002776 sample_image->type=image->type;
2777 return(sample_image);
2778}
2779
2780/*
2781%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2782% %
2783% %
2784% %
2785% S c a l e I m a g e %
2786% %
2787% %
2788% %
2789%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2790%
2791% ScaleImage() changes the size of an image to the given dimensions.
2792%
2793% The format of the ScaleImage method is:
2794%
cristybb503372010-05-27 20:51:26 +00002795% Image *ScaleImage(const Image *image,const size_t columns,
2796% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002797%
2798% A description of each parameter follows:
2799%
2800% o image: the image.
2801%
2802% o columns: the number of columns in the scaled image.
2803%
2804% o rows: the number of rows in the scaled image.
2805%
2806% o exception: return any errors or warnings in this structure.
2807%
2808*/
cristybb503372010-05-27 20:51:26 +00002809MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2810 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002811{
2812#define ScaleImageTag "Scale/Image"
2813
cristyed6cb232010-01-20 03:07:53 +00002814 CacheView
2815 *image_view,
2816 *scale_view;
2817
cristy3ed852e2009-09-05 21:47:34 +00002818 Image
2819 *scale_image;
2820
cristy3ed852e2009-09-05 21:47:34 +00002821 MagickBooleanType
2822 next_column,
2823 next_row,
2824 proceed;
2825
2826 MagickPixelPacket
2827 pixel,
2828 *scale_scanline,
2829 *scanline,
2830 *x_vector,
2831 *y_vector,
2832 zero;
2833
cristy3ed852e2009-09-05 21:47:34 +00002834 PointInfo
2835 scale,
2836 span;
2837
cristybb503372010-05-27 20:51:26 +00002838 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002839 i;
2840
cristy9af9b5d2010-08-15 17:04:28 +00002841 ssize_t
2842 number_rows,
2843 y;
2844
cristy3ed852e2009-09-05 21:47:34 +00002845 /*
2846 Initialize scaled image attributes.
2847 */
2848 assert(image != (const Image *) NULL);
2849 assert(image->signature == MagickSignature);
2850 if (image->debug != MagickFalse)
2851 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2852 assert(exception != (ExceptionInfo *) NULL);
2853 assert(exception->signature == MagickSignature);
2854 if ((columns == 0) || (rows == 0))
2855 return((Image *) NULL);
2856 if ((columns == image->columns) && (rows == image->rows))
2857 return(CloneImage(image,0,0,MagickTrue,exception));
2858 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2859 if (scale_image == (Image *) NULL)
2860 return((Image *) NULL);
2861 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2862 {
2863 InheritException(exception,&scale_image->exception);
2864 scale_image=DestroyImage(scale_image);
2865 return((Image *) NULL);
2866 }
2867 /*
2868 Allocate memory.
2869 */
2870 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2871 sizeof(*x_vector));
2872 scanline=x_vector;
2873 if (image->rows != scale_image->rows)
2874 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2875 sizeof(*scanline));
2876 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2877 scale_image->columns,sizeof(*scale_scanline));
2878 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2879 sizeof(*y_vector));
2880 if ((scanline == (MagickPixelPacket *) NULL) ||
2881 (scale_scanline == (MagickPixelPacket *) NULL) ||
2882 (x_vector == (MagickPixelPacket *) NULL) ||
2883 (y_vector == (MagickPixelPacket *) NULL))
2884 {
2885 scale_image=DestroyImage(scale_image);
2886 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2887 }
2888 /*
2889 Scale image.
2890 */
2891 number_rows=0;
2892 next_row=MagickTrue;
2893 span.y=1.0;
2894 scale.y=(double) scale_image->rows/(double) image->rows;
2895 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2896 sizeof(*y_vector));
2897 GetMagickPixelPacket(image,&pixel);
2898 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2899 i=0;
cristyed6cb232010-01-20 03:07:53 +00002900 image_view=AcquireCacheView(image);
2901 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002902 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002903 {
2904 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002905 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002906
2907 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002908 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002909
2910 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002911 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002912
cristy3ed852e2009-09-05 21:47:34 +00002913 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002914 *restrict s,
2915 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002916
2917 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002918 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002919
cristy9af9b5d2010-08-15 17:04:28 +00002920 register ssize_t
2921 x;
2922
cristyed6cb232010-01-20 03:07:53 +00002923 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2924 exception);
cristy3ed852e2009-09-05 21:47:34 +00002925 if (q == (PixelPacket *) NULL)
2926 break;
cristyba9a1e22010-11-10 23:14:28 +00002927 scale_indexes=GetCacheViewAuthenticIndexQueue(scale_view);
cristy3ed852e2009-09-05 21:47:34 +00002928 if (scale_image->rows == image->rows)
2929 {
2930 /*
2931 Read a new scanline.
2932 */
cristyed6cb232010-01-20 03:07:53 +00002933 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2934 exception);
cristy3ed852e2009-09-05 21:47:34 +00002935 if (p == (const PixelPacket *) NULL)
2936 break;
cristyed6cb232010-01-20 03:07:53 +00002937 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002938 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002939 {
cristyce70c172010-01-07 17:15:30 +00002940 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2941 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2942 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002943 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002944 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002945 if (indexes != (IndexPacket *) NULL)
2946 x_vector[x].index=(MagickRealType) indexes[x];
2947 p++;
2948 }
2949 }
2950 else
2951 {
2952 /*
2953 Scale Y direction.
2954 */
2955 while (scale.y < span.y)
2956 {
cristy9af9b5d2010-08-15 17:04:28 +00002957 if ((next_row != MagickFalse) &&
2958 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002959 {
2960 /*
2961 Read a new scanline.
2962 */
cristyed6cb232010-01-20 03:07:53 +00002963 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2964 exception);
cristy3ed852e2009-09-05 21:47:34 +00002965 if (p == (const PixelPacket *) NULL)
2966 break;
cristyed6cb232010-01-20 03:07:53 +00002967 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002968 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002969 {
cristyce70c172010-01-07 17:15:30 +00002970 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2971 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2972 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002973 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002974 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002975 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002976 if (indexes != (IndexPacket *) NULL)
2977 x_vector[x].index=(MagickRealType) indexes[x];
2978 p++;
2979 }
2980 number_rows++;
2981 }
cristybb503372010-05-27 20:51:26 +00002982 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002983 {
2984 y_vector[x].red+=scale.y*x_vector[x].red;
2985 y_vector[x].green+=scale.y*x_vector[x].green;
2986 y_vector[x].blue+=scale.y*x_vector[x].blue;
2987 if (scale_image->matte != MagickFalse)
2988 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2989 if (scale_indexes != (IndexPacket *) NULL)
2990 y_vector[x].index+=scale.y*x_vector[x].index;
2991 }
2992 span.y-=scale.y;
2993 scale.y=(double) scale_image->rows/(double) image->rows;
2994 next_row=MagickTrue;
2995 }
cristybb503372010-05-27 20:51:26 +00002996 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002997 {
2998 /*
2999 Read a new scanline.
3000 */
cristyed6cb232010-01-20 03:07:53 +00003001 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
3002 exception);
cristy3ed852e2009-09-05 21:47:34 +00003003 if (p == (const PixelPacket *) NULL)
3004 break;
cristyed6cb232010-01-20 03:07:53 +00003005 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00003006 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003007 {
cristyce70c172010-01-07 17:15:30 +00003008 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
3009 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
3010 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003011 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00003012 x_vector[x].opacity=(MagickRealType)
3013 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003014 if (indexes != (IndexPacket *) NULL)
3015 x_vector[x].index=(MagickRealType) indexes[x];
3016 p++;
3017 }
3018 number_rows++;
3019 next_row=MagickFalse;
3020 }
3021 s=scanline;
cristybb503372010-05-27 20:51:26 +00003022 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003023 {
3024 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3025 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3026 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3027 if (image->matte != MagickFalse)
3028 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3029 if (scale_indexes != (IndexPacket *) NULL)
3030 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3031 s->red=pixel.red;
3032 s->green=pixel.green;
3033 s->blue=pixel.blue;
3034 if (scale_image->matte != MagickFalse)
3035 s->opacity=pixel.opacity;
3036 if (scale_indexes != (IndexPacket *) NULL)
3037 s->index=pixel.index;
3038 s++;
3039 y_vector[x]=zero;
3040 }
3041 scale.y-=span.y;
3042 if (scale.y <= 0)
3043 {
3044 scale.y=(double) scale_image->rows/(double) image->rows;
3045 next_row=MagickTrue;
3046 }
3047 span.y=1.0;
3048 }
3049 if (scale_image->columns == image->columns)
3050 {
3051 /*
3052 Transfer scanline to scaled image.
3053 */
3054 s=scanline;
cristybb503372010-05-27 20:51:26 +00003055 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003056 {
cristyce70c172010-01-07 17:15:30 +00003057 q->red=ClampToQuantum(s->red);
3058 q->green=ClampToQuantum(s->green);
3059 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003060 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003061 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003062 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003063 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003064 q++;
3065 s++;
3066 }
3067 }
3068 else
3069 {
3070 /*
3071 Scale X direction.
3072 */
3073 pixel=zero;
3074 next_column=MagickFalse;
3075 span.x=1.0;
3076 s=scanline;
3077 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003078 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003079 {
3080 scale.x=(double) scale_image->columns/(double) image->columns;
3081 while (scale.x >= span.x)
3082 {
3083 if (next_column != MagickFalse)
3084 {
3085 pixel=zero;
3086 t++;
3087 }
3088 pixel.red+=span.x*s->red;
3089 pixel.green+=span.x*s->green;
3090 pixel.blue+=span.x*s->blue;
3091 if (image->matte != MagickFalse)
3092 pixel.opacity+=span.x*s->opacity;
3093 if (scale_indexes != (IndexPacket *) NULL)
3094 pixel.index+=span.x*s->index;
3095 t->red=pixel.red;
3096 t->green=pixel.green;
3097 t->blue=pixel.blue;
3098 if (scale_image->matte != MagickFalse)
3099 t->opacity=pixel.opacity;
3100 if (scale_indexes != (IndexPacket *) NULL)
3101 t->index=pixel.index;
3102 scale.x-=span.x;
3103 span.x=1.0;
3104 next_column=MagickTrue;
3105 }
3106 if (scale.x > 0)
3107 {
3108 if (next_column != MagickFalse)
3109 {
3110 pixel=zero;
3111 next_column=MagickFalse;
3112 t++;
3113 }
3114 pixel.red+=scale.x*s->red;
3115 pixel.green+=scale.x*s->green;
3116 pixel.blue+=scale.x*s->blue;
3117 if (scale_image->matte != MagickFalse)
3118 pixel.opacity+=scale.x*s->opacity;
3119 if (scale_indexes != (IndexPacket *) NULL)
3120 pixel.index+=scale.x*s->index;
3121 span.x-=scale.x;
3122 }
3123 s++;
3124 }
3125 if (span.x > 0)
3126 {
3127 s--;
3128 pixel.red+=span.x*s->red;
3129 pixel.green+=span.x*s->green;
3130 pixel.blue+=span.x*s->blue;
3131 if (scale_image->matte != MagickFalse)
3132 pixel.opacity+=span.x*s->opacity;
3133 if (scale_indexes != (IndexPacket *) NULL)
3134 pixel.index+=span.x*s->index;
3135 }
3136 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003137 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003138 {
3139 t->red=pixel.red;
3140 t->green=pixel.green;
3141 t->blue=pixel.blue;
3142 if (scale_image->matte != MagickFalse)
3143 t->opacity=pixel.opacity;
3144 if (scale_indexes != (IndexPacket *) NULL)
3145 t->index=pixel.index;
3146 }
3147 /*
3148 Transfer scanline to scaled image.
3149 */
3150 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003151 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003152 {
cristyce70c172010-01-07 17:15:30 +00003153 q->red=ClampToQuantum(t->red);
3154 q->green=ClampToQuantum(t->green);
3155 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003156 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003157 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003158 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003159 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003160 t++;
3161 q++;
3162 }
3163 }
cristyed6cb232010-01-20 03:07:53 +00003164 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003165 break;
cristy96b16132010-08-29 17:19:52 +00003166 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3167 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003168 if (proceed == MagickFalse)
3169 break;
3170 }
cristyed6cb232010-01-20 03:07:53 +00003171 scale_view=DestroyCacheView(scale_view);
3172 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003173 /*
3174 Free allocated memory.
3175 */
3176 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3177 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3178 if (scale_image->rows != image->rows)
3179 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3180 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3181 scale_image->type=image->type;
3182 return(scale_image);
3183}
3184
anthony02b4cb42010-10-10 04:54:35 +00003185#if 0
3186 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003187/*
3188%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3189% %
3190% %
3191% %
3192+ S e t R e s i z e F i l t e r S u p p o r t %
3193% %
3194% %
3195% %
3196%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3197%
3198% SetResizeFilterSupport() specifies which IR filter to use to window
3199%
3200% The format of the SetResizeFilterSupport method is:
3201%
3202% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3203% const MagickRealType support)
3204%
3205% A description of each parameter follows:
3206%
3207% o resize_filter: the resize filter.
3208%
3209% o support: the filter spport radius.
3210%
3211*/
3212MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3213 const MagickRealType support)
3214{
3215 assert(resize_filter != (ResizeFilter *) NULL);
3216 assert(resize_filter->signature == MagickSignature);
3217 resize_filter->support=support;
3218}
anthony02b4cb42010-10-10 04:54:35 +00003219#endif
cristy3ed852e2009-09-05 21:47:34 +00003220
3221/*
3222%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3223% %
3224% %
3225% %
3226% T h u m b n a i l I m a g e %
3227% %
3228% %
3229% %
3230%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3231%
3232% ThumbnailImage() changes the size of an image to the given dimensions and
3233% removes any associated profiles. The goal is to produce small low cost
3234% thumbnail images suited for display on the Web.
3235%
3236% The format of the ThumbnailImage method is:
3237%
cristybb503372010-05-27 20:51:26 +00003238% Image *ThumbnailImage(const Image *image,const size_t columns,
3239% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003240%
3241% A description of each parameter follows:
3242%
3243% o image: the image.
3244%
3245% o columns: the number of columns in the scaled image.
3246%
3247% o rows: the number of rows in the scaled image.
3248%
3249% o exception: return any errors or warnings in this structure.
3250%
3251*/
cristy9af9b5d2010-08-15 17:04:28 +00003252MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3253 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003254{
3255#define SampleFactor 5
3256
3257 char
3258 value[MaxTextExtent];
3259
3260 const char
3261 *name;
3262
3263 Image
3264 *thumbnail_image;
3265
3266 MagickRealType
3267 x_factor,
3268 y_factor;
3269
cristybb503372010-05-27 20:51:26 +00003270 size_t
cristy3ed852e2009-09-05 21:47:34 +00003271 version;
3272
cristy9af9b5d2010-08-15 17:04:28 +00003273 struct stat
3274 attributes;
3275
cristy3ed852e2009-09-05 21:47:34 +00003276 assert(image != (Image *) NULL);
3277 assert(image->signature == MagickSignature);
3278 if (image->debug != MagickFalse)
3279 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3280 assert(exception != (ExceptionInfo *) NULL);
3281 assert(exception->signature == MagickSignature);
3282 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3283 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3284 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003285 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3286 exception);
cristy3ed852e2009-09-05 21:47:34 +00003287 else
3288 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003289 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3290 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003291 else
3292 {
3293 Image
3294 *sample_image;
3295
3296 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3297 exception);
3298 if (sample_image == (Image *) NULL)
3299 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003300 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3301 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003302 sample_image=DestroyImage(sample_image);
3303 }
3304 if (thumbnail_image == (Image *) NULL)
3305 return(thumbnail_image);
3306 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3307 if (thumbnail_image->matte == MagickFalse)
3308 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3309 thumbnail_image->depth=8;
3310 thumbnail_image->interlace=NoInterlace;
3311 /*
3312 Strip all profiles except color profiles.
3313 */
3314 ResetImageProfileIterator(thumbnail_image);
3315 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3316 {
3317 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3318 {
cristy2b726bd2010-01-11 01:05:39 +00003319 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003320 ResetImageProfileIterator(thumbnail_image);
3321 }
3322 name=GetNextImageProfile(thumbnail_image);
3323 }
3324 (void) DeleteImageProperty(thumbnail_image,"comment");
3325 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003326 if (strstr(image->magick_filename,"//") == (char *) NULL)
3327 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003328 image->magick_filename);
3329 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3330 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3331 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3332 {
cristye8c25f92010-06-03 00:53:06 +00003333 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003334 attributes.st_mtime);
3335 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3336 }
cristye8c25f92010-06-03 00:53:06 +00003337 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003338 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003339 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003340 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003341 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3342 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3343 LocaleLower(value);
3344 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3345 (void) SetImageProperty(thumbnail_image,"software",
3346 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003347 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3348 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003349 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003350 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003351 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003352 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003353 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3354 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003355 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3356 return(thumbnail_image);
3357}