blob: dd89256d8bf6c29cff47f646b664bacffa994242 [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;
cristy9d314ff2011-03-09 01:30:28 +0000842
cristy9af9b5d2010-08-15 17:04:28 +0000843 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000844 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000845 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000846 filter_type=(FilterTypes) option;
847 window_type=BoxFilter;
848 }
nicolas07bac812010-09-19 18:47:02 +0000849 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000850 artifact=GetImageArtifact(image,"filter:window");
851 if (artifact != (const char *) NULL)
852 {
cristy9af9b5d2010-08-15 17:04:28 +0000853 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000854 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony152700d2010-10-28 02:43:18 +0000855 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000856 }
cristy3ed852e2009-09-05 21:47:34 +0000857 }
cristy33b1c162010-01-23 22:51:51 +0000858 else
859 {
anthony48f77622010-10-03 14:32:31 +0000860 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000861 artifact=GetImageArtifact(image,"filter:window");
862 if (artifact != (const char *) NULL)
863 {
anthony152700d2010-10-28 02:43:18 +0000864 ssize_t
865 option;
cristy9d314ff2011-03-09 01:30:28 +0000866
cristy33b1c162010-01-23 22:51:51 +0000867 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
868 artifact);
869 if ((UndefinedFilter < option) && (option < SentinelFilter))
870 {
anthony61b5ddd2010-10-05 02:33:31 +0000871 filter_type=cylindrical != MagickFalse ?
872 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000873 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000874 }
875 }
876 }
anthony152700d2010-10-28 02:43:18 +0000877
nicolas07bac812010-09-19 18:47:02 +0000878 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000879 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000880 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000881 resize_filter->window=filters[window_type].function;
882 resize_filter->scale=filters[window_type].scale;
anthony029ba0e2010-10-29 00:54:24 +0000883 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000884
anthonyf5e76ef2010-10-12 01:22:01 +0000885 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000886 if (cylindrical != MagickFalse)
887 switch (filter_type)
888 {
anthony10b8bc82010-10-02 12:48:46 +0000889 case BoxFilter:
890 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000891 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000892 break;
anthony152700d2010-10-28 02:43:18 +0000893 case LanczosFilter:
894 case LanczosSharpFilter:
895 case Lanczos2Filter:
896 case Lanczos2SharpFilter:
897 resize_filter->filter=filters[JincFilter].function;
898 resize_filter->window=filters[JincFilter].function;
anthony029ba0e2010-10-29 00:54:24 +0000899 resize_filter->scale=filters[JincFilter].scale;
900 /* number of lobes (support window size) remain unchanged */
anthony152700d2010-10-28 02:43:18 +0000901 break;
anthony81b8bf92010-10-02 13:54:34 +0000902 default:
903 break;
anthony10b8bc82010-10-02 12:48:46 +0000904 }
anthony152700d2010-10-28 02:43:18 +0000905 /* Global Sharpening (regardless of orthoginal/cylindrical) */
906 switch (filter_type)
907 {
908 case LanczosSharpFilter:
nicolasaf6ee7c2010-11-13 03:34:22 +0000909 resize_filter->blur *= 0.9812505644269356;
anthony152700d2010-10-28 02:43:18 +0000910 break;
911 case Lanczos2SharpFilter:
nicolasca48bce2010-11-14 17:54:53 +0000912 resize_filter->blur *= 0.9549963639785485;
anthony152700d2010-10-28 02:43:18 +0000913 break;
914 default:
915 break;
916 }
anthony61b5ddd2010-10-05 02:33:31 +0000917
anthonyf5e76ef2010-10-12 01:22:01 +0000918 /*
anthony06b1edf2010-10-25 01:19:50 +0000919 ** Other Expert Option Modifications
anthonyf5e76ef2010-10-12 01:22:01 +0000920 */
921
922 /* User Sigma Override - no support change */
923 artifact=GetImageArtifact(image,"filter:sigma");
924 if (artifact != (const char *) NULL)
925 sigma=StringToDouble(artifact);
anthony993d8382011-02-06 12:35:36 +0000926 /* Define coefficents for Gaussian */
anthonyf5e76ef2010-10-12 01:22:01 +0000927 if ( GaussianFilter ) {
cristy5cce74b2010-11-15 03:24:28 +0000928 resize_filter->coefficient[0]=1.0/(2.0*sigma*sigma);
929 resize_filter->coefficient[1]=(MagickRealType) (1.0/(Magick2PI*sigma*
anthony993d8382011-02-06 12:35:36 +0000930 sigma)); /* Normalization Multiplier - unneeded for filters */
anthonyf5e76ef2010-10-12 01:22:01 +0000931 }
932
933 /* Blur Override */
934 artifact=GetImageArtifact(image,"filter:blur");
935 if (artifact != (const char *) NULL)
936 resize_filter->blur=StringToDouble(artifact);
937 if (resize_filter->blur < MagickEpsilon)
938 resize_filter->blur=(MagickRealType) MagickEpsilon;
939
940 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000941 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000942 if (artifact != (const char *) NULL)
943 {
cristybb503372010-05-27 20:51:26 +0000944 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000945 lobes;
946
cristy96b16132010-08-29 17:19:52 +0000947 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000948 if (lobes < 1)
949 lobes=1;
950 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000951 }
anthony152700d2010-10-28 02:43:18 +0000952 /* Convert a Jinc function lobes value to a real support value */
anthony61b5ddd2010-10-05 02:33:31 +0000953 if (resize_filter->filter == Jinc)
954 {
955 if (resize_filter->support > 16)
956 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
957 else
958 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
959 }
960 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000961 artifact=GetImageArtifact(image,"filter:support");
962 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000963 resize_filter->support=fabs(StringToDouble(artifact));
964 /*
nicolas07bac812010-09-19 18:47:02 +0000965 Scale windowing function separatally to the support 'clipping'
966 window that calling operator is planning to actually use. (Expert
967 override)
cristy3ed852e2009-09-05 21:47:34 +0000968 */
anthony55f12332010-09-10 01:13:02 +0000969 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000970 artifact=GetImageArtifact(image,"filter:win-support");
971 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000972 resize_filter->window_support=fabs(StringToDouble(artifact));
973 /*
anthony029ba0e2010-10-29 00:54:24 +0000974 Adjust window function scaling to match windowing support for
anthony1f90a6b2010-09-14 08:56:31 +0000975 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000976 */
977 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +0000978
anthony55f12332010-09-10 01:13:02 +0000979 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000980 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000981 */
cristy3ed852e2009-09-05 21:47:34 +0000982 B=0.0;
983 C=0.0;
cristy33b1c162010-01-23 22:51:51 +0000984 if ((filters[filter_type].function == CubicBC) ||
985 (filters[window_type].function == CubicBC))
986 {
anthony2d9b8b52010-09-14 08:31:07 +0000987 B=filters[filter_type].B;
988 C=filters[filter_type].C;
989 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +0000990 {
anthony2d9b8b52010-09-14 08:31:07 +0000991 B=filters[window_type].B;
992 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +0000993 }
cristy33b1c162010-01-23 22:51:51 +0000994 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +0000995 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000996 {
997 B=StringToDouble(artifact);
nicolasd15ee9a2010-10-24 18:39:45 +0000998 C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +0000999 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001000 if (artifact != (const char *) NULL)
1001 C=StringToDouble(artifact);
1002 }
1003 else
1004 {
1005 artifact=GetImageArtifact(image,"filter:c");
1006 if (artifact != (const char *) NULL)
1007 {
1008 C=StringToDouble(artifact);
nicolasb7dff642010-10-25 02:04:14 +00001009 B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001010 }
1011 }
nicolasc6bac3b2010-10-24 18:10:45 +00001012 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
nicolasd15ee9a2010-10-24 18:39:45 +00001013 {
anthony06b1edf2010-10-25 01:19:50 +00001014 const double twoB = B+B;
cristy5cce74b2010-11-15 03:24:28 +00001015 resize_filter->coefficient[0]=1.0-(1.0/3.0)*B;
1016 resize_filter->coefficient[1]=-3.0+twoB+C;
1017 resize_filter->coefficient[2]=2.0-1.5*B-C;
1018 resize_filter->coefficient[3]=(4.0/3.0)*B+4.0*C;
1019 resize_filter->coefficient[4]=-8.0*C-twoB;
1020 resize_filter->coefficient[5]=B+5.0*C;
1021 resize_filter->coefficient[6]=(-1.0/6.0)*B-C;
nicolasd15ee9a2010-10-24 18:39:45 +00001022 }
nicolasc6bac3b2010-10-24 18:10:45 +00001023 }
anthonyf5e76ef2010-10-12 01:22:01 +00001024
anthony55f12332010-09-10 01:13:02 +00001025 /*
nicolas07bac812010-09-19 18:47:02 +00001026 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001027 */
cristyf5b49372010-10-16 01:06:47 +00001028#if defined(MAGICKCORE_OPENMP_SUPPORT)
1029 #pragma omp master
1030 {
1031#endif
1032 artifact=GetImageArtifact(image,"filter:verbose");
anthony28ad1d72010-10-26 06:30:24 +00001033 if (IsMagickTrue(artifact))
cristyf5b49372010-10-16 01:06:47 +00001034 {
1035 double
anthony06b1edf2010-10-25 01:19:50 +00001036 support,
cristyf5b49372010-10-16 01:06:47 +00001037 x;
cristy3ed852e2009-09-05 21:47:34 +00001038
cristyf5b49372010-10-16 01:06:47 +00001039 /*
1040 Set the weighting function properly when the weighting
1041 function may not exactly match the filter of the same name.
anthony06b1edf2010-10-25 01:19:50 +00001042 EG: a Point filter is really uses a Box weighting function
cristyf5b49372010-10-16 01:06:47 +00001043 with a different support than is typically used.
cristyf5b49372010-10-16 01:06:47 +00001044 */
1045 if (resize_filter->filter == Box) filter_type=BoxFilter;
1046 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1047 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1048 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1049 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
anthony1ad20052010-10-29 01:53:03 +00001050 if (resize_filter->window == Box) window_type=BoxFilter;
anthony152700d2010-10-28 02:43:18 +00001051 if (resize_filter->window == Sinc) window_type=SincFilter;
1052 if (resize_filter->window == SincFast) window_type=SincFastFilter;
anthony1ad20052010-10-29 01:53:03 +00001053 if (resize_filter->window == Jinc) window_type=JincFilter;
anthony152700d2010-10-28 02:43:18 +00001054 if (resize_filter->window == CubicBC) window_type=CubicFilter;
cristyf5b49372010-10-16 01:06:47 +00001055 /*
1056 Report Filter Details.
1057 */
1058 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1059 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
anthony06b1edf2010-10-25 01:19:50 +00001060 (void) fprintf(stdout,"# filter = %s\n",
1061 MagickOptionToMnemonic(MagickFilterOptions,filter_type));
1062 (void) fprintf(stdout,"# window = %s\n",
1063 MagickOptionToMnemonic(MagickFilterOptions, window_type));
1064 (void) fprintf(stdout,"# support = %.*g\n",
1065 GetMagickPrecision(),(double) resize_filter->support);
1066 (void) fprintf(stdout,"# win-support = %.*g\n",
1067 GetMagickPrecision(),(double) resize_filter->window_support);
1068 (void) fprintf(stdout,"# scale_blur = %.*g\n",
1069 GetMagickPrecision(), (double)resize_filter->blur);
cristyf5b49372010-10-16 01:06:47 +00001070 if ( filter_type == GaussianFilter )
anthony06b1edf2010-10-25 01:19:50 +00001071 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",
1072 GetMagickPrecision(), (double)sigma);
1073 (void) fprintf(stdout,"# practical_support = %.*g\n",
1074 GetMagickPrecision(), (double)support);
cristyf5b49372010-10-16 01:06:47 +00001075 if ( filter_type == CubicFilter || window_type == CubicFilter )
anthony06b1edf2010-10-25 01:19:50 +00001076 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",
1077 GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C);
cristyf5b49372010-10-16 01:06:47 +00001078 (void) fprintf(stdout,"\n");
1079 /*
1080 Output values of resulting filter graph -- for graphing
1081 filter result.
1082 */
1083 for (x=0.0; x <= support; x+=0.01f)
1084 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1085 (double) GetResizeFilterWeight(resize_filter,x));
1086 /* A final value so gnuplot can graph the 'stop' properly. */
1087 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1088 0.0);
1089 }
1090 /* Output the above once only for each image - remove setting */
1091 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1092#if defined(MAGICKCORE_OPENMP_SUPPORT)
1093 }
1094#endif
cristy3ed852e2009-09-05 21:47:34 +00001095 return(resize_filter);
1096}
1097
1098/*
1099%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1100% %
1101% %
1102% %
1103% A d a p t i v e R e s i z e I m a g e %
1104% %
1105% %
1106% %
1107%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1108%
1109% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1110%
1111% The format of the AdaptiveResizeImage method is:
1112%
cristy9af9b5d2010-08-15 17:04:28 +00001113% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1114% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001115%
1116% A description of each parameter follows:
1117%
1118% o image: the image.
1119%
1120% o columns: the number of columns in the resized image.
1121%
1122% o rows: the number of rows in the resized image.
1123%
1124% o exception: return any errors or warnings in this structure.
1125%
1126*/
1127MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001128 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001129{
1130#define AdaptiveResizeImageTag "Resize/Image"
1131
cristyc4c8d132010-01-07 01:58:38 +00001132 CacheView
1133 *resize_view;
1134
cristy3ed852e2009-09-05 21:47:34 +00001135 Image
1136 *resize_image;
1137
cristy3ed852e2009-09-05 21:47:34 +00001138 MagickBooleanType
cristy5cce74b2010-11-15 03:24:28 +00001139 status;
cristy3ed852e2009-09-05 21:47:34 +00001140
cristy5cce74b2010-11-15 03:24:28 +00001141 MagickOffsetType
1142 progress;
cristy3ed852e2009-09-05 21:47:34 +00001143
1144 ResampleFilter
cristya6a18782010-11-15 01:56:25 +00001145 **resample_filter;
cristy3ed852e2009-09-05 21:47:34 +00001146
cristy9af9b5d2010-08-15 17:04:28 +00001147 ssize_t
1148 y;
1149
cristy3ed852e2009-09-05 21:47:34 +00001150 /*
1151 Adaptively resize image.
1152 */
1153 assert(image != (const Image *) NULL);
1154 assert(image->signature == MagickSignature);
1155 if (image->debug != MagickFalse)
1156 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1157 assert(exception != (ExceptionInfo *) NULL);
1158 assert(exception->signature == MagickSignature);
1159 if ((columns == 0) || (rows == 0))
1160 return((Image *) NULL);
1161 if ((columns == image->columns) && (rows == image->rows))
1162 return(CloneImage(image,0,0,MagickTrue,exception));
1163 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1164 if (resize_image == (Image *) NULL)
1165 return((Image *) NULL);
1166 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1167 {
1168 InheritException(exception,&resize_image->exception);
1169 resize_image=DestroyImage(resize_image);
1170 return((Image *) NULL);
1171 }
cristy5cce74b2010-11-15 03:24:28 +00001172 status=MagickTrue;
1173 progress=0;
cristya6a18782010-11-15 01:56:25 +00001174 resample_filter=AcquireResampleFilterThreadSet(image,
1175 UndefinedVirtualPixelMethod,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +00001176 resize_view=AcquireCacheView(resize_image);
cristy5cce74b2010-11-15 03:24:28 +00001177#if defined(MAGICKCORE_OPENMP_SUPPORT)
1178 #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
1179#endif
cristybb503372010-05-27 20:51:26 +00001180 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001181 {
cristya6a18782010-11-15 01:56:25 +00001182 const int
1183 id = GetOpenMPThreadId();
1184
cristy5cce74b2010-11-15 03:24:28 +00001185 MagickPixelPacket
1186 pixel;
1187
1188 PointInfo
1189 offset;
1190
cristy3ed852e2009-09-05 21:47:34 +00001191 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001192 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001193
cristy3ed852e2009-09-05 21:47:34 +00001194 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001195 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001196
cristyb2a9be02010-11-15 15:00:14 +00001197 register ssize_t
1198 x;
1199
cristy5cce74b2010-11-15 03:24:28 +00001200 if (status == MagickFalse)
1201 continue;
cristy3ed852e2009-09-05 21:47:34 +00001202 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1203 exception);
1204 if (q == (PixelPacket *) NULL)
cristy5cce74b2010-11-15 03:24:28 +00001205 continue;
cristy3ed852e2009-09-05 21:47:34 +00001206 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1207 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristy5cce74b2010-11-15 03:24:28 +00001208 GetMagickPixelPacket(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001209 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001210 {
1211 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
cristya6a18782010-11-15 01:56:25 +00001212 (void) ResamplePixelColor(resample_filter[id],offset.x-0.5,offset.y-0.5,
cristy3ed852e2009-09-05 21:47:34 +00001213 &pixel);
1214 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1215 q++;
1216 }
1217 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
cristy5cce74b2010-11-15 03:24:28 +00001218 continue;
1219 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1220 {
1221 MagickBooleanType
1222 proceed;
1223
1224#if defined(MAGICKCORE_OPENMP_SUPPORT)
1225 #pragma omp critical (MagickCore_AdaptiveResizeImage)
1226#endif
1227 proceed=SetImageProgress(image,AdaptiveResizeImageTag,progress++,
1228 image->rows);
1229 if (proceed == MagickFalse)
1230 status=MagickFalse;
1231 }
cristy3ed852e2009-09-05 21:47:34 +00001232 }
cristya6a18782010-11-15 01:56:25 +00001233 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
cristy3ed852e2009-09-05 21:47:34 +00001234 resize_view=DestroyCacheView(resize_view);
cristy5cce74b2010-11-15 03:24:28 +00001235 if (status == MagickFalse)
1236 resize_image=DestroyImage(resize_image);
cristy3ed852e2009-09-05 21:47:34 +00001237 return(resize_image);
1238}
1239
1240/*
1241%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1242% %
1243% %
1244% %
1245+ B e s s e l O r d e r O n e %
1246% %
1247% %
1248% %
1249%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1250%
1251% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001252% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001253%
1254% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1255%
1256% j1(x) = x*j1(x);
1257%
1258% For x in (8,inf)
1259%
1260% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1261%
1262% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1263%
1264% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1265% = 1/sqrt(2) * (sin(x) - cos(x))
1266% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1267% = -1/sqrt(2) * (sin(x) + cos(x))
1268%
1269% The format of the BesselOrderOne method is:
1270%
1271% MagickRealType BesselOrderOne(MagickRealType x)
1272%
1273% A description of each parameter follows:
1274%
1275% o x: MagickRealType value.
1276%
1277*/
1278
1279#undef I0
1280static MagickRealType I0(MagickRealType x)
1281{
1282 MagickRealType
1283 sum,
1284 t,
1285 y;
1286
cristybb503372010-05-27 20:51:26 +00001287 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001288 i;
1289
1290 /*
1291 Zeroth order Bessel function of the first kind.
1292 */
1293 sum=1.0;
1294 y=x*x/4.0;
1295 t=y;
1296 for (i=2; t > MagickEpsilon; i++)
1297 {
1298 sum+=t;
1299 t*=y/((MagickRealType) i*i);
1300 }
1301 return(sum);
1302}
1303
1304#undef J1
1305static MagickRealType J1(MagickRealType x)
1306{
1307 MagickRealType
1308 p,
1309 q;
1310
cristybb503372010-05-27 20:51:26 +00001311 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001312 i;
1313
1314 static const double
1315 Pone[] =
1316 {
1317 0.581199354001606143928050809e+21,
1318 -0.6672106568924916298020941484e+20,
1319 0.2316433580634002297931815435e+19,
1320 -0.3588817569910106050743641413e+17,
1321 0.2908795263834775409737601689e+15,
1322 -0.1322983480332126453125473247e+13,
1323 0.3413234182301700539091292655e+10,
1324 -0.4695753530642995859767162166e+7,
1325 0.270112271089232341485679099e+4
1326 },
1327 Qone[] =
1328 {
1329 0.11623987080032122878585294e+22,
1330 0.1185770712190320999837113348e+20,
1331 0.6092061398917521746105196863e+17,
1332 0.2081661221307607351240184229e+15,
1333 0.5243710262167649715406728642e+12,
1334 0.1013863514358673989967045588e+10,
1335 0.1501793594998585505921097578e+7,
1336 0.1606931573481487801970916749e+4,
1337 0.1e+1
1338 };
1339
1340 p=Pone[8];
1341 q=Qone[8];
1342 for (i=7; i >= 0; i--)
1343 {
1344 p=p*x*x+Pone[i];
1345 q=q*x*x+Qone[i];
1346 }
1347 return(p/q);
1348}
1349
1350#undef P1
1351static MagickRealType P1(MagickRealType x)
1352{
1353 MagickRealType
1354 p,
1355 q;
1356
cristybb503372010-05-27 20:51:26 +00001357 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001358 i;
1359
1360 static const double
1361 Pone[] =
1362 {
1363 0.352246649133679798341724373e+5,
1364 0.62758845247161281269005675e+5,
1365 0.313539631109159574238669888e+5,
1366 0.49854832060594338434500455e+4,
1367 0.2111529182853962382105718e+3,
1368 0.12571716929145341558495e+1
1369 },
1370 Qone[] =
1371 {
1372 0.352246649133679798068390431e+5,
1373 0.626943469593560511888833731e+5,
1374 0.312404063819041039923015703e+5,
1375 0.4930396490181088979386097e+4,
1376 0.2030775189134759322293574e+3,
1377 0.1e+1
1378 };
1379
1380 p=Pone[5];
1381 q=Qone[5];
1382 for (i=4; i >= 0; i--)
1383 {
1384 p=p*(8.0/x)*(8.0/x)+Pone[i];
1385 q=q*(8.0/x)*(8.0/x)+Qone[i];
1386 }
1387 return(p/q);
1388}
1389
1390#undef Q1
1391static MagickRealType Q1(MagickRealType x)
1392{
1393 MagickRealType
1394 p,
1395 q;
1396
cristybb503372010-05-27 20:51:26 +00001397 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001398 i;
1399
1400 static const double
1401 Pone[] =
1402 {
1403 0.3511751914303552822533318e+3,
1404 0.7210391804904475039280863e+3,
1405 0.4259873011654442389886993e+3,
1406 0.831898957673850827325226e+2,
1407 0.45681716295512267064405e+1,
1408 0.3532840052740123642735e-1
1409 },
1410 Qone[] =
1411 {
1412 0.74917374171809127714519505e+4,
1413 0.154141773392650970499848051e+5,
1414 0.91522317015169922705904727e+4,
1415 0.18111867005523513506724158e+4,
1416 0.1038187585462133728776636e+3,
1417 0.1e+1
1418 };
1419
1420 p=Pone[5];
1421 q=Qone[5];
1422 for (i=4; i >= 0; i--)
1423 {
1424 p=p*(8.0/x)*(8.0/x)+Pone[i];
1425 q=q*(8.0/x)*(8.0/x)+Qone[i];
1426 }
1427 return(p/q);
1428}
1429
1430static MagickRealType BesselOrderOne(MagickRealType x)
1431{
1432 MagickRealType
1433 p,
1434 q;
1435
1436 if (x == 0.0)
1437 return(0.0);
1438 p=x;
1439 if (x < 0.0)
1440 x=(-x);
1441 if (x < 8.0)
1442 return(p*J1(x));
1443 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1444 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1445 cos((double) x))));
1446 if (p < 0.0)
1447 q=(-q);
1448 return(q);
1449}
1450
1451/*
1452%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1453% %
1454% %
1455% %
1456+ D e s t r o y R e s i z e F i l t e r %
1457% %
1458% %
1459% %
1460%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1461%
1462% DestroyResizeFilter() destroy the resize filter.
1463%
cristya2ffd7e2010-03-10 20:50:30 +00001464% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001465%
1466% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1467%
1468% A description of each parameter follows:
1469%
1470% o resize_filter: the resize filter.
1471%
1472*/
1473MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1474{
1475 assert(resize_filter != (ResizeFilter *) NULL);
1476 assert(resize_filter->signature == MagickSignature);
1477 resize_filter->signature=(~MagickSignature);
1478 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1479 return(resize_filter);
1480}
1481
1482/*
1483%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1484% %
1485% %
1486% %
1487+ G e t R e s i z e F i l t e r S u p p o r t %
1488% %
1489% %
1490% %
1491%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1492%
1493% GetResizeFilterSupport() return the current support window size for this
1494% filter. Note that this may have been enlarged by filter:blur factor.
1495%
1496% The format of the GetResizeFilterSupport method is:
1497%
1498% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1499%
1500% A description of each parameter follows:
1501%
1502% o filter: Image filter to use.
1503%
1504*/
1505MagickExport MagickRealType GetResizeFilterSupport(
1506 const ResizeFilter *resize_filter)
1507{
1508 assert(resize_filter != (ResizeFilter *) NULL);
1509 assert(resize_filter->signature == MagickSignature);
1510 return(resize_filter->support*resize_filter->blur);
1511}
1512
1513/*
1514%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1515% %
1516% %
1517% %
1518+ G e t R e s i z e F i l t e r W e i g h t %
1519% %
1520% %
1521% %
1522%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1523%
1524% GetResizeFilterWeight evaluates the specified resize filter at the point x
1525% which usally lies between zero and the filters current 'support' and
1526% returns the weight of the filter function at that point.
1527%
1528% The format of the GetResizeFilterWeight method is:
1529%
1530% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1531% const MagickRealType x)
1532%
1533% A description of each parameter follows:
1534%
1535% o filter: the filter type.
1536%
1537% o x: the point.
1538%
1539*/
1540MagickExport MagickRealType GetResizeFilterWeight(
1541 const ResizeFilter *resize_filter,const MagickRealType x)
1542{
1543 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001544 scale,
cristy0eefadc2011-02-24 01:19:39 +00001545 weight,
cristyb8385862010-09-26 01:27:51 +00001546 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001547
1548 /*
1549 Windowing function - scale the weighting filter by this amount.
1550 */
1551 assert(resize_filter != (ResizeFilter *) NULL);
1552 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001553 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001554 if ((resize_filter->window_support < MagickEpsilon) ||
1555 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001556 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001557 else
1558 {
anthony55f12332010-09-10 01:13:02 +00001559 scale=resize_filter->scale;
1560 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001561 }
cristy0eefadc2011-02-24 01:19:39 +00001562 weight=scale*resize_filter->filter(x_blur,resize_filter);
cristybcf91042011-02-24 02:57:28 +00001563 return(weight);
cristy3ed852e2009-09-05 21:47:34 +00001564}
1565
1566/*
1567%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1568% %
1569% %
1570% %
1571% M a g n i f y I m a g e %
1572% %
1573% %
1574% %
1575%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1576%
1577% MagnifyImage() is a convenience method that scales an image proportionally
1578% to twice its size.
1579%
1580% The format of the MagnifyImage method is:
1581%
1582% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1583%
1584% A description of each parameter follows:
1585%
1586% o image: the image.
1587%
1588% o exception: return any errors or warnings in this structure.
1589%
1590*/
1591MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1592{
1593 Image
1594 *magnify_image;
1595
1596 assert(image != (Image *) NULL);
1597 assert(image->signature == MagickSignature);
1598 if (image->debug != MagickFalse)
1599 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1600 assert(exception != (ExceptionInfo *) NULL);
1601 assert(exception->signature == MagickSignature);
1602 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1603 1.0,exception);
1604 return(magnify_image);
1605}
1606
1607/*
1608%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1609% %
1610% %
1611% %
1612% M i n i f y I m a g e %
1613% %
1614% %
1615% %
1616%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1617%
1618% MinifyImage() is a convenience method that scales an image proportionally
1619% to half its size.
1620%
1621% The format of the MinifyImage method is:
1622%
1623% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1624%
1625% A description of each parameter follows:
1626%
1627% o image: the image.
1628%
1629% o exception: return any errors or warnings in this structure.
1630%
1631*/
1632MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1633{
1634 Image
1635 *minify_image;
1636
1637 assert(image != (Image *) NULL);
1638 assert(image->signature == MagickSignature);
1639 if (image->debug != MagickFalse)
1640 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1641 assert(exception != (ExceptionInfo *) NULL);
1642 assert(exception->signature == MagickSignature);
cristy31456282011-02-06 21:07:04 +00001643 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,1.0,
1644 exception);
cristy3ed852e2009-09-05 21:47:34 +00001645 return(minify_image);
1646}
1647
1648/*
1649%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1650% %
1651% %
1652% %
1653% R e s a m p l e I m a g e %
1654% %
1655% %
1656% %
1657%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1658%
1659% ResampleImage() resize image in terms of its pixel size, so that when
1660% displayed at the given resolution it will be the same size in terms of
1661% real world units as the original image at the original resolution.
1662%
1663% The format of the ResampleImage method is:
1664%
1665% Image *ResampleImage(Image *image,const double x_resolution,
1666% const double y_resolution,const FilterTypes filter,const double blur,
1667% ExceptionInfo *exception)
1668%
1669% A description of each parameter follows:
1670%
1671% o image: the image to be resized to fit the given resolution.
1672%
1673% o x_resolution: the new image x resolution.
1674%
1675% o y_resolution: the new image y resolution.
1676%
1677% o filter: Image filter to use.
1678%
1679% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1680%
1681*/
1682MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1683 const double y_resolution,const FilterTypes filter,const double blur,
1684 ExceptionInfo *exception)
1685{
1686#define ResampleImageTag "Resample/Image"
1687
1688 Image
1689 *resample_image;
1690
cristybb503372010-05-27 20:51:26 +00001691 size_t
cristy3ed852e2009-09-05 21:47:34 +00001692 height,
1693 width;
1694
1695 /*
1696 Initialize sampled image attributes.
1697 */
1698 assert(image != (const Image *) NULL);
1699 assert(image->signature == MagickSignature);
1700 if (image->debug != MagickFalse)
1701 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1702 assert(exception != (ExceptionInfo *) NULL);
1703 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001704 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1705 72.0 : image->x_resolution)+0.5);
1706 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1707 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001708 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1709 if (resample_image != (Image *) NULL)
1710 {
1711 resample_image->x_resolution=x_resolution;
1712 resample_image->y_resolution=y_resolution;
1713 }
1714 return(resample_image);
1715}
1716#if defined(MAGICKCORE_LQR_DELEGATE)
1717
1718/*
1719%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1720% %
1721% %
1722% %
1723% L i q u i d R e s c a l e I m a g e %
1724% %
1725% %
1726% %
1727%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1728%
1729% LiquidRescaleImage() rescales image with seam carving.
1730%
1731% The format of the LiquidRescaleImage method is:
1732%
1733% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001734% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001735% const double delta_x,const double rigidity,ExceptionInfo *exception)
1736%
1737% A description of each parameter follows:
1738%
1739% o image: the image.
1740%
1741% o columns: the number of columns in the rescaled image.
1742%
1743% o rows: the number of rows in the rescaled image.
1744%
1745% o delta_x: maximum seam transversal step (0 means straight seams).
1746%
1747% o rigidity: introduce a bias for non-straight seams (typically 0).
1748%
1749% o exception: return any errors or warnings in this structure.
1750%
1751*/
cristy9af9b5d2010-08-15 17:04:28 +00001752MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1753 const size_t rows,const double delta_x,const double rigidity,
1754 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001755{
1756#define LiquidRescaleImageTag "Rescale/Image"
1757
cristyc5c6f662010-09-22 14:23:02 +00001758 CacheView
1759 *rescale_view;
1760
cristy3ed852e2009-09-05 21:47:34 +00001761 const char
1762 *map;
1763
1764 guchar
1765 *packet;
1766
1767 Image
1768 *rescale_image;
1769
1770 int
1771 x,
1772 y;
1773
1774 LqrCarver
1775 *carver;
1776
1777 LqrRetVal
1778 lqr_status;
1779
1780 MagickBooleanType
1781 status;
1782
1783 MagickPixelPacket
1784 pixel;
1785
1786 unsigned char
1787 *pixels;
1788
1789 /*
1790 Liquid rescale image.
1791 */
1792 assert(image != (const Image *) NULL);
1793 assert(image->signature == MagickSignature);
1794 if (image->debug != MagickFalse)
1795 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1796 assert(exception != (ExceptionInfo *) NULL);
1797 assert(exception->signature == MagickSignature);
1798 if ((columns == 0) || (rows == 0))
1799 return((Image *) NULL);
1800 if ((columns == image->columns) && (rows == image->rows))
1801 return(CloneImage(image,0,0,MagickTrue,exception));
1802 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001803 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001804 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1805 {
1806 Image
1807 *resize_image;
1808
cristybb503372010-05-27 20:51:26 +00001809 size_t
cristy3ed852e2009-09-05 21:47:34 +00001810 height,
1811 width;
1812
1813 /*
1814 Honor liquid resize size limitations.
1815 */
1816 for (width=image->columns; columns >= (2*width-1); width*=2);
1817 for (height=image->rows; rows >= (2*height-1); height*=2);
1818 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1819 exception);
1820 if (resize_image == (Image *) NULL)
1821 return((Image *) NULL);
1822 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1823 rigidity,exception);
1824 resize_image=DestroyImage(resize_image);
1825 return(rescale_image);
1826 }
1827 map="RGB";
1828 if (image->matte == MagickFalse)
1829 map="RGBA";
1830 if (image->colorspace == CMYKColorspace)
1831 {
1832 map="CMYK";
1833 if (image->matte == MagickFalse)
1834 map="CMYKA";
1835 }
1836 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1837 strlen(map)*sizeof(*pixels));
1838 if (pixels == (unsigned char *) NULL)
1839 return((Image *) NULL);
1840 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1841 pixels,exception);
1842 if (status == MagickFalse)
1843 {
1844 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1845 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1846 }
1847 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1848 if (carver == (LqrCarver *) NULL)
1849 {
1850 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1851 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1852 }
1853 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1854 lqr_status=lqr_carver_resize(carver,columns,rows);
1855 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1856 lqr_carver_get_height(carver),MagickTrue,exception);
1857 if (rescale_image == (Image *) NULL)
1858 {
1859 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1860 return((Image *) NULL);
1861 }
1862 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1863 {
1864 InheritException(exception,&rescale_image->exception);
1865 rescale_image=DestroyImage(rescale_image);
1866 return((Image *) NULL);
1867 }
1868 GetMagickPixelPacket(rescale_image,&pixel);
1869 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001870 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001871 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1872 {
1873 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001874 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001875
1876 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001877 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001878
anthony22aad252010-09-23 06:59:07 +00001879 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001880 if (q == (PixelPacket *) NULL)
1881 break;
cristyc5c6f662010-09-22 14:23:02 +00001882 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001883 pixel.red=QuantumRange*(packet[0]/255.0);
1884 pixel.green=QuantumRange*(packet[1]/255.0);
1885 pixel.blue=QuantumRange*(packet[2]/255.0);
1886 if (image->colorspace != CMYKColorspace)
1887 {
1888 if (image->matte == MagickFalse)
1889 pixel.opacity=QuantumRange*(packet[3]/255.0);
1890 }
1891 else
1892 {
1893 pixel.index=QuantumRange*(packet[3]/255.0);
1894 if (image->matte == MagickFalse)
1895 pixel.opacity=QuantumRange*(packet[4]/255.0);
1896 }
1897 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001898 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001899 break;
1900 }
cristyc5c6f662010-09-22 14:23:02 +00001901 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001902 /*
1903 Relinquish resources.
1904 */
1905 lqr_carver_destroy(carver);
1906 return(rescale_image);
1907}
1908#else
1909MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001910 const size_t magick_unused(columns),const size_t magick_unused(rows),
1911 const double magick_unused(delta_x),const double magick_unused(rigidity),
1912 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001913{
1914 assert(image != (const Image *) NULL);
1915 assert(image->signature == MagickSignature);
1916 if (image->debug != MagickFalse)
1917 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1918 assert(exception != (ExceptionInfo *) NULL);
1919 assert(exception->signature == MagickSignature);
1920 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1921 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1922 return((Image *) NULL);
1923}
1924#endif
1925
1926/*
1927%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1928% %
1929% %
1930% %
1931% R e s i z e I m a g e %
1932% %
1933% %
1934% %
1935%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1936%
1937% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001938% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001939%
1940% If an undefined filter is given the filter defaults to Mitchell for a
1941% colormapped image, a image with a matte channel, or if the image is
1942% enlarged. Otherwise the filter defaults to a Lanczos.
1943%
1944% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1945%
1946% The format of the ResizeImage method is:
1947%
cristybb503372010-05-27 20:51:26 +00001948% Image *ResizeImage(Image *image,const size_t columns,
1949% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001950% ExceptionInfo *exception)
1951%
1952% A description of each parameter follows:
1953%
1954% o image: the image.
1955%
1956% o columns: the number of columns in the scaled image.
1957%
1958% o rows: the number of rows in the scaled image.
1959%
1960% o filter: Image filter to use.
1961%
cristy9af9b5d2010-08-15 17:04:28 +00001962% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1963% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001964%
1965% o exception: return any errors or warnings in this structure.
1966%
1967*/
1968
1969typedef struct _ContributionInfo
1970{
1971 MagickRealType
1972 weight;
1973
cristybb503372010-05-27 20:51:26 +00001974 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001975 pixel;
1976} ContributionInfo;
1977
1978static ContributionInfo **DestroyContributionThreadSet(
1979 ContributionInfo **contribution)
1980{
cristybb503372010-05-27 20:51:26 +00001981 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001982 i;
1983
1984 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001985 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001986 if (contribution[i] != (ContributionInfo *) NULL)
1987 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1988 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001989 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001990 return(contribution);
1991}
1992
1993static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1994{
cristybb503372010-05-27 20:51:26 +00001995 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001996 i;
1997
1998 ContributionInfo
1999 **contribution;
2000
cristybb503372010-05-27 20:51:26 +00002001 size_t
cristy3ed852e2009-09-05 21:47:34 +00002002 number_threads;
2003
2004 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00002005 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00002006 sizeof(*contribution));
2007 if (contribution == (ContributionInfo **) NULL)
2008 return((ContributionInfo **) NULL);
2009 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00002010 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002011 {
2012 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2013 sizeof(**contribution));
2014 if (contribution[i] == (ContributionInfo *) NULL)
2015 return(DestroyContributionThreadSet(contribution));
2016 }
2017 return(contribution);
2018}
2019
2020static inline double MagickMax(const double x,const double y)
2021{
2022 if (x > y)
2023 return(x);
2024 return(y);
2025}
2026
2027static inline double MagickMin(const double x,const double y)
2028{
2029 if (x < y)
2030 return(x);
2031 return(y);
2032}
2033
2034static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2035 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002036 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002037{
2038#define ResizeImageTag "Resize/Image"
2039
cristyfa112112010-01-04 17:48:07 +00002040 CacheView
2041 *image_view,
2042 *resize_view;
2043
cristy3ed852e2009-09-05 21:47:34 +00002044 ClassType
2045 storage_class;
2046
2047 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002048 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002049
cristy3ed852e2009-09-05 21:47:34 +00002050 MagickBooleanType
2051 status;
2052
2053 MagickPixelPacket
2054 zero;
2055
2056 MagickRealType
2057 scale,
2058 support;
2059
cristy9af9b5d2010-08-15 17:04:28 +00002060 ssize_t
2061 x;
2062
cristy3ed852e2009-09-05 21:47:34 +00002063 /*
2064 Apply filter to resize horizontally from image to resize image.
2065 */
cristy5d824382010-09-06 14:00:17 +00002066 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002067 support=scale*GetResizeFilterSupport(resize_filter);
2068 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2069 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2070 {
2071 InheritException(exception,&resize_image->exception);
2072 return(MagickFalse);
2073 }
2074 if (support < 0.5)
2075 {
2076 /*
nicolas07bac812010-09-19 18:47:02 +00002077 Support too small even for nearest neighbour: Reduce to point
2078 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002079 */
2080 support=(MagickRealType) 0.5;
2081 scale=1.0;
2082 }
2083 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2084 if (contributions == (ContributionInfo **) NULL)
2085 {
2086 (void) ThrowMagickException(exception,GetMagickModule(),
2087 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2088 return(MagickFalse);
2089 }
2090 status=MagickTrue;
2091 scale=1.0/scale;
2092 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2093 image_view=AcquireCacheView(image);
2094 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002095#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002096 #pragma omp parallel for shared(status)
2097#endif
cristybb503372010-05-27 20:51:26 +00002098 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002099 {
cristy3ed852e2009-09-05 21:47:34 +00002100 MagickRealType
2101 center,
2102 density;
2103
2104 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002105 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002106
2107 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002108 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002109
cristy03dbbd22010-09-19 23:04:47 +00002110 register ContributionInfo
2111 *restrict contribution;
2112
cristy3ed852e2009-09-05 21:47:34 +00002113 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002114 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002115
cristy3ed852e2009-09-05 21:47:34 +00002116 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002117 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002118
cristy03dbbd22010-09-19 23:04:47 +00002119 register ssize_t
2120 y;
2121
cristy9af9b5d2010-08-15 17:04:28 +00002122 ssize_t
2123 n,
2124 start,
2125 stop;
2126
cristy3ed852e2009-09-05 21:47:34 +00002127 if (status == MagickFalse)
2128 continue;
2129 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002130 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2131 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002132 density=0.0;
2133 contribution=contributions[GetOpenMPThreadId()];
2134 for (n=0; n < (stop-start); n++)
2135 {
2136 contribution[n].pixel=start+n;
2137 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2138 ((MagickRealType) (start+n)-center+0.5));
2139 density+=contribution[n].weight;
2140 }
2141 if ((density != 0.0) && (density != 1.0))
2142 {
cristybb503372010-05-27 20:51:26 +00002143 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002144 i;
2145
2146 /*
2147 Normalize.
2148 */
2149 density=1.0/density;
2150 for (i=0; i < n; i++)
2151 contribution[i].weight*=density;
2152 }
cristy9af9b5d2010-08-15 17:04:28 +00002153 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2154 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002155 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2156 exception);
2157 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2158 {
2159 status=MagickFalse;
2160 continue;
2161 }
2162 indexes=GetCacheViewVirtualIndexQueue(image_view);
2163 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002164 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002165 {
cristy3ed852e2009-09-05 21:47:34 +00002166 MagickPixelPacket
2167 pixel;
2168
2169 MagickRealType
2170 alpha;
2171
cristybb503372010-05-27 20:51:26 +00002172 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002173 i;
2174
cristy9af9b5d2010-08-15 17:04:28 +00002175 ssize_t
2176 j;
2177
cristy3ed852e2009-09-05 21:47:34 +00002178 pixel=zero;
2179 if (image->matte == MagickFalse)
2180 {
2181 for (i=0; i < n; i++)
2182 {
2183 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2184 (contribution[i].pixel-contribution[0].pixel);
2185 alpha=contribution[i].weight;
2186 pixel.red+=alpha*(p+j)->red;
2187 pixel.green+=alpha*(p+j)->green;
2188 pixel.blue+=alpha*(p+j)->blue;
2189 pixel.opacity+=alpha*(p+j)->opacity;
2190 }
cristyce70c172010-01-07 17:15:30 +00002191 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2192 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2193 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2194 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002195 if ((image->colorspace == CMYKColorspace) &&
2196 (resize_image->colorspace == CMYKColorspace))
2197 {
2198 for (i=0; i < n; i++)
2199 {
2200 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2201 (contribution[i].pixel-contribution[0].pixel);
2202 alpha=contribution[i].weight;
2203 pixel.index+=alpha*indexes[j];
2204 }
cristyce70c172010-01-07 17:15:30 +00002205 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002206 }
2207 }
2208 else
2209 {
2210 MagickRealType
2211 gamma;
2212
2213 gamma=0.0;
2214 for (i=0; i < n; i++)
2215 {
2216 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2217 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002218 alpha=contribution[i].weight*QuantumScale*
2219 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002220 pixel.red+=alpha*(p+j)->red;
2221 pixel.green+=alpha*(p+j)->green;
2222 pixel.blue+=alpha*(p+j)->blue;
2223 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2224 gamma+=alpha;
2225 }
2226 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002227 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2228 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2229 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2230 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002231 if ((image->colorspace == CMYKColorspace) &&
2232 (resize_image->colorspace == CMYKColorspace))
2233 {
2234 for (i=0; i < n; i++)
2235 {
2236 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2237 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002238 alpha=contribution[i].weight*QuantumScale*
2239 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002240 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002241 }
cristyce70c172010-01-07 17:15:30 +00002242 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
cristy1545b342011-02-24 00:22:57 +00002243 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002244 }
2245 }
2246 if ((resize_image->storage_class == PseudoClass) &&
2247 (image->storage_class == PseudoClass))
2248 {
cristybb503372010-05-27 20:51:26 +00002249 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002250 1.0)+0.5);
2251 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2252 (contribution[i-start].pixel-contribution[0].pixel);
2253 resize_indexes[y]=indexes[j];
2254 }
2255 q++;
2256 }
2257 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2258 status=MagickFalse;
2259 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2260 {
2261 MagickBooleanType
2262 proceed;
2263
cristyb5d5f722009-11-04 03:03:49 +00002264#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002265 #pragma omp critical (MagickCore_HorizontalFilter)
2266#endif
cristy9af9b5d2010-08-15 17:04:28 +00002267 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002268 if (proceed == MagickFalse)
2269 status=MagickFalse;
2270 }
2271 }
2272 resize_view=DestroyCacheView(resize_view);
2273 image_view=DestroyCacheView(image_view);
2274 contributions=DestroyContributionThreadSet(contributions);
2275 return(status);
2276}
2277
2278static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2279 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002280 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002281{
cristyfa112112010-01-04 17:48:07 +00002282 CacheView
2283 *image_view,
2284 *resize_view;
2285
cristy3ed852e2009-09-05 21:47:34 +00002286 ClassType
2287 storage_class;
2288
2289 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002290 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002291
cristy3ed852e2009-09-05 21:47:34 +00002292 MagickBooleanType
2293 status;
2294
2295 MagickPixelPacket
2296 zero;
2297
2298 MagickRealType
2299 scale,
2300 support;
2301
cristy9af9b5d2010-08-15 17:04:28 +00002302 ssize_t
2303 y;
2304
cristy3ed852e2009-09-05 21:47:34 +00002305 /*
cristy9af9b5d2010-08-15 17:04:28 +00002306 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002307 */
cristy5d824382010-09-06 14:00:17 +00002308 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002309 support=scale*GetResizeFilterSupport(resize_filter);
2310 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2311 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2312 {
2313 InheritException(exception,&resize_image->exception);
2314 return(MagickFalse);
2315 }
2316 if (support < 0.5)
2317 {
2318 /*
nicolas07bac812010-09-19 18:47:02 +00002319 Support too small even for nearest neighbour: Reduce to point
2320 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002321 */
2322 support=(MagickRealType) 0.5;
2323 scale=1.0;
2324 }
2325 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2326 if (contributions == (ContributionInfo **) NULL)
2327 {
2328 (void) ThrowMagickException(exception,GetMagickModule(),
2329 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2330 return(MagickFalse);
2331 }
2332 status=MagickTrue;
2333 scale=1.0/scale;
2334 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2335 image_view=AcquireCacheView(image);
2336 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002337#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002338 #pragma omp parallel for shared(status)
2339#endif
cristybb503372010-05-27 20:51:26 +00002340 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002341 {
cristy3ed852e2009-09-05 21:47:34 +00002342 MagickRealType
2343 center,
2344 density;
2345
2346 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002347 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002348
2349 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002350 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002351
cristy03dbbd22010-09-19 23:04:47 +00002352 register ContributionInfo
2353 *restrict contribution;
2354
cristy3ed852e2009-09-05 21:47:34 +00002355 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002356 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002357
cristy9af9b5d2010-08-15 17:04:28 +00002358 register PixelPacket
2359 *restrict q;
2360
cristybb503372010-05-27 20:51:26 +00002361 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002362 x;
2363
cristy9af9b5d2010-08-15 17:04:28 +00002364 ssize_t
2365 n,
2366 start,
2367 stop;
cristy3ed852e2009-09-05 21:47:34 +00002368
2369 if (status == MagickFalse)
2370 continue;
cristy679e6962010-03-18 00:42:45 +00002371 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002372 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2373 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002374 density=0.0;
2375 contribution=contributions[GetOpenMPThreadId()];
2376 for (n=0; n < (stop-start); n++)
2377 {
2378 contribution[n].pixel=start+n;
2379 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2380 ((MagickRealType) (start+n)-center+0.5));
2381 density+=contribution[n].weight;
2382 }
2383 if ((density != 0.0) && (density != 1.0))
2384 {
cristybb503372010-05-27 20:51:26 +00002385 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002386 i;
2387
2388 /*
2389 Normalize.
2390 */
2391 density=1.0/density;
2392 for (i=0; i < n; i++)
2393 contribution[i].weight*=density;
2394 }
2395 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002396 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2397 exception);
cristy3ed852e2009-09-05 21:47:34 +00002398 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2399 exception);
2400 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2401 {
2402 status=MagickFalse;
2403 continue;
2404 }
2405 indexes=GetCacheViewVirtualIndexQueue(image_view);
2406 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002407 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002408 {
cristy3ed852e2009-09-05 21:47:34 +00002409 MagickPixelPacket
2410 pixel;
2411
2412 MagickRealType
2413 alpha;
2414
cristybb503372010-05-27 20:51:26 +00002415 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002416 i;
2417
cristy9af9b5d2010-08-15 17:04:28 +00002418 ssize_t
2419 j;
2420
cristy3ed852e2009-09-05 21:47:34 +00002421 pixel=zero;
2422 if (image->matte == MagickFalse)
2423 {
2424 for (i=0; i < n; i++)
2425 {
cristybb503372010-05-27 20:51:26 +00002426 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002427 image->columns+x);
2428 alpha=contribution[i].weight;
2429 pixel.red+=alpha*(p+j)->red;
2430 pixel.green+=alpha*(p+j)->green;
2431 pixel.blue+=alpha*(p+j)->blue;
2432 pixel.opacity+=alpha*(p+j)->opacity;
2433 }
cristyce70c172010-01-07 17:15:30 +00002434 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2435 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2436 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2437 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002438 if ((image->colorspace == CMYKColorspace) &&
2439 (resize_image->colorspace == CMYKColorspace))
2440 {
2441 for (i=0; i < n; i++)
2442 {
cristybb503372010-05-27 20:51:26 +00002443 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002444 image->columns+x);
2445 alpha=contribution[i].weight;
2446 pixel.index+=alpha*indexes[j];
2447 }
cristyce70c172010-01-07 17:15:30 +00002448 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002449 }
2450 }
2451 else
2452 {
2453 MagickRealType
2454 gamma;
2455
2456 gamma=0.0;
2457 for (i=0; i < n; i++)
2458 {
cristybb503372010-05-27 20:51:26 +00002459 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002460 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002461 alpha=contribution[i].weight*QuantumScale*
2462 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002463 pixel.red+=alpha*(p+j)->red;
2464 pixel.green+=alpha*(p+j)->green;
2465 pixel.blue+=alpha*(p+j)->blue;
2466 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2467 gamma+=alpha;
2468 }
2469 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002470 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2471 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2472 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2473 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002474 if ((image->colorspace == CMYKColorspace) &&
2475 (resize_image->colorspace == CMYKColorspace))
2476 {
2477 for (i=0; i < n; i++)
2478 {
cristybb503372010-05-27 20:51:26 +00002479 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002480 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002481 alpha=contribution[i].weight*QuantumScale*
2482 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002483 pixel.index+=alpha*indexes[j];
2484 }
cristyce70c172010-01-07 17:15:30 +00002485 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2486 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002487 }
2488 }
2489 if ((resize_image->storage_class == PseudoClass) &&
2490 (image->storage_class == PseudoClass))
2491 {
cristybb503372010-05-27 20:51:26 +00002492 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002493 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002494 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002495 image->columns+x);
2496 resize_indexes[x]=indexes[j];
2497 }
2498 q++;
2499 }
2500 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2501 status=MagickFalse;
2502 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2503 {
2504 MagickBooleanType
2505 proceed;
2506
cristyb5d5f722009-11-04 03:03:49 +00002507#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002508 #pragma omp critical (MagickCore_VerticalFilter)
2509#endif
cristy9af9b5d2010-08-15 17:04:28 +00002510 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002511 if (proceed == MagickFalse)
2512 status=MagickFalse;
2513 }
2514 }
2515 resize_view=DestroyCacheView(resize_view);
2516 image_view=DestroyCacheView(image_view);
2517 contributions=DestroyContributionThreadSet(contributions);
2518 return(status);
2519}
2520
cristybb503372010-05-27 20:51:26 +00002521MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2522 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002523 ExceptionInfo *exception)
2524{
2525#define WorkLoadFactor 0.265
2526
2527 FilterTypes
2528 filter_type;
2529
2530 Image
2531 *filter_image,
2532 *resize_image;
2533
cristy9af9b5d2010-08-15 17:04:28 +00002534 MagickOffsetType
2535 offset;
2536
cristy3ed852e2009-09-05 21:47:34 +00002537 MagickRealType
2538 x_factor,
2539 y_factor;
2540
2541 MagickSizeType
2542 span;
2543
2544 MagickStatusType
2545 status;
2546
2547 ResizeFilter
2548 *resize_filter;
2549
cristy3ed852e2009-09-05 21:47:34 +00002550 /*
2551 Acquire resize image.
2552 */
2553 assert(image != (Image *) NULL);
2554 assert(image->signature == MagickSignature);
2555 if (image->debug != MagickFalse)
2556 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2557 assert(exception != (ExceptionInfo *) NULL);
2558 assert(exception->signature == MagickSignature);
2559 if ((columns == 0) || (rows == 0))
2560 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2561 if ((columns == image->columns) && (rows == image->rows) &&
2562 (filter == UndefinedFilter) && (blur == 1.0))
2563 return(CloneImage(image,0,0,MagickTrue,exception));
2564 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2565 if (resize_image == (Image *) NULL)
2566 return(resize_image);
2567 /*
2568 Acquire resize filter.
2569 */
2570 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2571 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2572 if ((x_factor*y_factor) > WorkLoadFactor)
2573 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2574 else
2575 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2576 if (filter_image == (Image *) NULL)
2577 return(DestroyImage(resize_image));
2578 filter_type=LanczosFilter;
2579 if (filter != UndefinedFilter)
2580 filter_type=filter;
2581 else
2582 if ((x_factor == 1.0) && (y_factor == 1.0))
2583 filter_type=PointFilter;
2584 else
2585 if ((image->storage_class == PseudoClass) ||
2586 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2587 filter_type=MitchellFilter;
2588 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2589 exception);
2590 /*
2591 Resize image.
2592 */
cristy9af9b5d2010-08-15 17:04:28 +00002593 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002594 if ((x_factor*y_factor) > WorkLoadFactor)
2595 {
2596 span=(MagickSizeType) (filter_image->columns+rows);
2597 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002598 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002599 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002600 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002601 }
2602 else
2603 {
2604 span=(MagickSizeType) (filter_image->rows+columns);
2605 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002606 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002607 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002608 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002609 }
2610 /*
2611 Free resources.
2612 */
2613 filter_image=DestroyImage(filter_image);
2614 resize_filter=DestroyResizeFilter(resize_filter);
2615 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2616 return((Image *) NULL);
2617 resize_image->type=image->type;
2618 return(resize_image);
2619}
2620
2621/*
2622%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2623% %
2624% %
2625% %
2626% S a m p l e I m a g e %
2627% %
2628% %
2629% %
2630%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2631%
2632% SampleImage() scales an image to the desired dimensions with pixel
2633% sampling. Unlike other scaling methods, this method does not introduce
2634% any additional color into the scaled image.
2635%
2636% The format of the SampleImage method is:
2637%
cristybb503372010-05-27 20:51:26 +00002638% Image *SampleImage(const Image *image,const size_t columns,
2639% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002640%
2641% A description of each parameter follows:
2642%
2643% o image: the image.
2644%
2645% o columns: the number of columns in the sampled image.
2646%
2647% o rows: the number of rows in the sampled image.
2648%
2649% o exception: return any errors or warnings in this structure.
2650%
2651*/
cristybb503372010-05-27 20:51:26 +00002652MagickExport Image *SampleImage(const Image *image,const size_t columns,
2653 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002654{
2655#define SampleImageTag "Sample/Image"
2656
cristyc4c8d132010-01-07 01:58:38 +00002657 CacheView
2658 *image_view,
2659 *sample_view;
2660
cristy3ed852e2009-09-05 21:47:34 +00002661 Image
2662 *sample_image;
2663
cristy3ed852e2009-09-05 21:47:34 +00002664 MagickBooleanType
2665 status;
2666
cristy5f959472010-05-27 22:19:46 +00002667 MagickOffsetType
2668 progress;
2669
cristybb503372010-05-27 20:51:26 +00002670 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002671 x;
2672
cristy5f959472010-05-27 22:19:46 +00002673 ssize_t
2674 *x_offset,
2675 y;
2676
cristy3ed852e2009-09-05 21:47:34 +00002677 /*
2678 Initialize sampled image attributes.
2679 */
2680 assert(image != (const Image *) NULL);
2681 assert(image->signature == MagickSignature);
2682 if (image->debug != MagickFalse)
2683 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2684 assert(exception != (ExceptionInfo *) NULL);
2685 assert(exception->signature == MagickSignature);
2686 if ((columns == 0) || (rows == 0))
2687 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2688 if ((columns == image->columns) && (rows == image->rows))
2689 return(CloneImage(image,0,0,MagickTrue,exception));
2690 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2691 if (sample_image == (Image *) NULL)
2692 return((Image *) NULL);
2693 /*
2694 Allocate scan line buffer and column offset buffers.
2695 */
cristybb503372010-05-27 20:51:26 +00002696 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002697 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002698 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002699 {
2700 sample_image=DestroyImage(sample_image);
2701 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2702 }
cristybb503372010-05-27 20:51:26 +00002703 for (x=0; x < (ssize_t) sample_image->columns; x++)
2704 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002705 sample_image->columns);
2706 /*
2707 Sample each row.
2708 */
2709 status=MagickTrue;
2710 progress=0;
2711 image_view=AcquireCacheView(image);
2712 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002713#if defined(MAGICKCORE_OPENMP_SUPPORT)
2714 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002715#endif
cristybb503372010-05-27 20:51:26 +00002716 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002717 {
cristy3ed852e2009-09-05 21:47:34 +00002718 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002719 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002720
2721 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002722 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002723
2724 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002725 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002726
cristy3ed852e2009-09-05 21:47:34 +00002727 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002728 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002729
cristy03dbbd22010-09-19 23:04:47 +00002730 register ssize_t
2731 x;
2732
cristy9af9b5d2010-08-15 17:04:28 +00002733 ssize_t
2734 y_offset;
2735
cristy3ed852e2009-09-05 21:47:34 +00002736 if (status == MagickFalse)
2737 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002738 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2739 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002740 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2741 exception);
2742 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2743 exception);
2744 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2745 {
2746 status=MagickFalse;
2747 continue;
2748 }
2749 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2750 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2751 /*
2752 Sample each column.
2753 */
cristybb503372010-05-27 20:51:26 +00002754 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002755 *q++=p[x_offset[x]];
2756 if ((image->storage_class == PseudoClass) ||
2757 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002758 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002759 sample_indexes[x]=indexes[x_offset[x]];
2760 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2761 status=MagickFalse;
2762 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2763 {
2764 MagickBooleanType
2765 proceed;
2766
cristyb5d5f722009-11-04 03:03:49 +00002767#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002768 #pragma omp critical (MagickCore_SampleImage)
2769#endif
2770 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2771 if (proceed == MagickFalse)
2772 status=MagickFalse;
2773 }
2774 }
2775 image_view=DestroyCacheView(image_view);
2776 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002777 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002778 sample_image->type=image->type;
2779 return(sample_image);
2780}
2781
2782/*
2783%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2784% %
2785% %
2786% %
2787% S c a l e I m a g e %
2788% %
2789% %
2790% %
2791%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2792%
2793% ScaleImage() changes the size of an image to the given dimensions.
2794%
2795% The format of the ScaleImage method is:
2796%
cristybb503372010-05-27 20:51:26 +00002797% Image *ScaleImage(const Image *image,const size_t columns,
2798% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002799%
2800% A description of each parameter follows:
2801%
2802% o image: the image.
2803%
2804% o columns: the number of columns in the scaled image.
2805%
2806% o rows: the number of rows in the scaled image.
2807%
2808% o exception: return any errors or warnings in this structure.
2809%
2810*/
cristybb503372010-05-27 20:51:26 +00002811MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2812 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002813{
2814#define ScaleImageTag "Scale/Image"
2815
cristyed6cb232010-01-20 03:07:53 +00002816 CacheView
2817 *image_view,
2818 *scale_view;
2819
cristy3ed852e2009-09-05 21:47:34 +00002820 Image
2821 *scale_image;
2822
cristy3ed852e2009-09-05 21:47:34 +00002823 MagickBooleanType
2824 next_column,
2825 next_row,
2826 proceed;
2827
2828 MagickPixelPacket
2829 pixel,
2830 *scale_scanline,
2831 *scanline,
2832 *x_vector,
2833 *y_vector,
2834 zero;
2835
cristy3ed852e2009-09-05 21:47:34 +00002836 PointInfo
2837 scale,
2838 span;
2839
cristybb503372010-05-27 20:51:26 +00002840 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002841 i;
2842
cristy9af9b5d2010-08-15 17:04:28 +00002843 ssize_t
2844 number_rows,
2845 y;
2846
cristy3ed852e2009-09-05 21:47:34 +00002847 /*
2848 Initialize scaled image attributes.
2849 */
2850 assert(image != (const Image *) NULL);
2851 assert(image->signature == MagickSignature);
2852 if (image->debug != MagickFalse)
2853 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2854 assert(exception != (ExceptionInfo *) NULL);
2855 assert(exception->signature == MagickSignature);
2856 if ((columns == 0) || (rows == 0))
2857 return((Image *) NULL);
2858 if ((columns == image->columns) && (rows == image->rows))
2859 return(CloneImage(image,0,0,MagickTrue,exception));
2860 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2861 if (scale_image == (Image *) NULL)
2862 return((Image *) NULL);
2863 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2864 {
2865 InheritException(exception,&scale_image->exception);
2866 scale_image=DestroyImage(scale_image);
2867 return((Image *) NULL);
2868 }
2869 /*
2870 Allocate memory.
2871 */
2872 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2873 sizeof(*x_vector));
2874 scanline=x_vector;
2875 if (image->rows != scale_image->rows)
2876 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2877 sizeof(*scanline));
2878 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2879 scale_image->columns,sizeof(*scale_scanline));
2880 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2881 sizeof(*y_vector));
2882 if ((scanline == (MagickPixelPacket *) NULL) ||
2883 (scale_scanline == (MagickPixelPacket *) NULL) ||
2884 (x_vector == (MagickPixelPacket *) NULL) ||
2885 (y_vector == (MagickPixelPacket *) NULL))
2886 {
2887 scale_image=DestroyImage(scale_image);
2888 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2889 }
2890 /*
2891 Scale image.
2892 */
2893 number_rows=0;
2894 next_row=MagickTrue;
2895 span.y=1.0;
2896 scale.y=(double) scale_image->rows/(double) image->rows;
2897 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2898 sizeof(*y_vector));
2899 GetMagickPixelPacket(image,&pixel);
2900 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2901 i=0;
cristyed6cb232010-01-20 03:07:53 +00002902 image_view=AcquireCacheView(image);
2903 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002904 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002905 {
2906 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002907 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002908
2909 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002910 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002911
2912 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002913 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002914
cristy3ed852e2009-09-05 21:47:34 +00002915 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002916 *restrict s,
2917 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002918
2919 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002920 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002921
cristy9af9b5d2010-08-15 17:04:28 +00002922 register ssize_t
2923 x;
2924
cristyed6cb232010-01-20 03:07:53 +00002925 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2926 exception);
cristy3ed852e2009-09-05 21:47:34 +00002927 if (q == (PixelPacket *) NULL)
2928 break;
cristyba9a1e22010-11-10 23:14:28 +00002929 scale_indexes=GetCacheViewAuthenticIndexQueue(scale_view);
cristy3ed852e2009-09-05 21:47:34 +00002930 if (scale_image->rows == image->rows)
2931 {
2932 /*
2933 Read a new scanline.
2934 */
cristyed6cb232010-01-20 03:07:53 +00002935 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2936 exception);
cristy3ed852e2009-09-05 21:47:34 +00002937 if (p == (const PixelPacket *) NULL)
2938 break;
cristyed6cb232010-01-20 03:07:53 +00002939 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002940 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002941 {
cristyce70c172010-01-07 17:15:30 +00002942 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2943 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2944 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002945 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002946 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002947 if (indexes != (IndexPacket *) NULL)
2948 x_vector[x].index=(MagickRealType) indexes[x];
2949 p++;
2950 }
2951 }
2952 else
2953 {
2954 /*
2955 Scale Y direction.
2956 */
2957 while (scale.y < span.y)
2958 {
cristy9af9b5d2010-08-15 17:04:28 +00002959 if ((next_row != MagickFalse) &&
2960 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002961 {
2962 /*
2963 Read a new scanline.
2964 */
cristyed6cb232010-01-20 03:07:53 +00002965 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2966 exception);
cristy3ed852e2009-09-05 21:47:34 +00002967 if (p == (const PixelPacket *) NULL)
2968 break;
cristyed6cb232010-01-20 03:07:53 +00002969 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002970 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002971 {
cristyce70c172010-01-07 17:15:30 +00002972 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2973 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2974 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002975 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002976 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002977 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002978 if (indexes != (IndexPacket *) NULL)
2979 x_vector[x].index=(MagickRealType) indexes[x];
2980 p++;
2981 }
2982 number_rows++;
2983 }
cristybb503372010-05-27 20:51:26 +00002984 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002985 {
2986 y_vector[x].red+=scale.y*x_vector[x].red;
2987 y_vector[x].green+=scale.y*x_vector[x].green;
2988 y_vector[x].blue+=scale.y*x_vector[x].blue;
2989 if (scale_image->matte != MagickFalse)
2990 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2991 if (scale_indexes != (IndexPacket *) NULL)
2992 y_vector[x].index+=scale.y*x_vector[x].index;
2993 }
2994 span.y-=scale.y;
2995 scale.y=(double) scale_image->rows/(double) image->rows;
2996 next_row=MagickTrue;
2997 }
cristybb503372010-05-27 20:51:26 +00002998 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002999 {
3000 /*
3001 Read a new scanline.
3002 */
cristyed6cb232010-01-20 03:07:53 +00003003 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
3004 exception);
cristy3ed852e2009-09-05 21:47:34 +00003005 if (p == (const PixelPacket *) NULL)
3006 break;
cristyed6cb232010-01-20 03:07:53 +00003007 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00003008 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003009 {
cristyce70c172010-01-07 17:15:30 +00003010 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
3011 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
3012 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003013 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00003014 x_vector[x].opacity=(MagickRealType)
3015 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003016 if (indexes != (IndexPacket *) NULL)
3017 x_vector[x].index=(MagickRealType) indexes[x];
3018 p++;
3019 }
3020 number_rows++;
3021 next_row=MagickFalse;
3022 }
3023 s=scanline;
cristybb503372010-05-27 20:51:26 +00003024 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003025 {
3026 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3027 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3028 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3029 if (image->matte != MagickFalse)
3030 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3031 if (scale_indexes != (IndexPacket *) NULL)
3032 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3033 s->red=pixel.red;
3034 s->green=pixel.green;
3035 s->blue=pixel.blue;
3036 if (scale_image->matte != MagickFalse)
3037 s->opacity=pixel.opacity;
3038 if (scale_indexes != (IndexPacket *) NULL)
3039 s->index=pixel.index;
3040 s++;
3041 y_vector[x]=zero;
3042 }
3043 scale.y-=span.y;
3044 if (scale.y <= 0)
3045 {
3046 scale.y=(double) scale_image->rows/(double) image->rows;
3047 next_row=MagickTrue;
3048 }
3049 span.y=1.0;
3050 }
3051 if (scale_image->columns == image->columns)
3052 {
3053 /*
3054 Transfer scanline to scaled image.
3055 */
3056 s=scanline;
cristybb503372010-05-27 20:51:26 +00003057 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003058 {
cristyce70c172010-01-07 17:15:30 +00003059 q->red=ClampToQuantum(s->red);
3060 q->green=ClampToQuantum(s->green);
3061 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003062 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003063 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003064 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003065 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003066 q++;
3067 s++;
3068 }
3069 }
3070 else
3071 {
3072 /*
3073 Scale X direction.
3074 */
3075 pixel=zero;
3076 next_column=MagickFalse;
3077 span.x=1.0;
3078 s=scanline;
3079 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003080 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003081 {
3082 scale.x=(double) scale_image->columns/(double) image->columns;
3083 while (scale.x >= span.x)
3084 {
3085 if (next_column != MagickFalse)
3086 {
3087 pixel=zero;
3088 t++;
3089 }
3090 pixel.red+=span.x*s->red;
3091 pixel.green+=span.x*s->green;
3092 pixel.blue+=span.x*s->blue;
3093 if (image->matte != MagickFalse)
3094 pixel.opacity+=span.x*s->opacity;
3095 if (scale_indexes != (IndexPacket *) NULL)
3096 pixel.index+=span.x*s->index;
3097 t->red=pixel.red;
3098 t->green=pixel.green;
3099 t->blue=pixel.blue;
3100 if (scale_image->matte != MagickFalse)
3101 t->opacity=pixel.opacity;
3102 if (scale_indexes != (IndexPacket *) NULL)
3103 t->index=pixel.index;
3104 scale.x-=span.x;
3105 span.x=1.0;
3106 next_column=MagickTrue;
3107 }
3108 if (scale.x > 0)
3109 {
3110 if (next_column != MagickFalse)
3111 {
3112 pixel=zero;
3113 next_column=MagickFalse;
3114 t++;
3115 }
3116 pixel.red+=scale.x*s->red;
3117 pixel.green+=scale.x*s->green;
3118 pixel.blue+=scale.x*s->blue;
3119 if (scale_image->matte != MagickFalse)
3120 pixel.opacity+=scale.x*s->opacity;
3121 if (scale_indexes != (IndexPacket *) NULL)
3122 pixel.index+=scale.x*s->index;
3123 span.x-=scale.x;
3124 }
3125 s++;
3126 }
3127 if (span.x > 0)
3128 {
3129 s--;
3130 pixel.red+=span.x*s->red;
3131 pixel.green+=span.x*s->green;
3132 pixel.blue+=span.x*s->blue;
3133 if (scale_image->matte != MagickFalse)
3134 pixel.opacity+=span.x*s->opacity;
3135 if (scale_indexes != (IndexPacket *) NULL)
3136 pixel.index+=span.x*s->index;
3137 }
3138 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003139 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003140 {
3141 t->red=pixel.red;
3142 t->green=pixel.green;
3143 t->blue=pixel.blue;
3144 if (scale_image->matte != MagickFalse)
3145 t->opacity=pixel.opacity;
3146 if (scale_indexes != (IndexPacket *) NULL)
3147 t->index=pixel.index;
3148 }
3149 /*
3150 Transfer scanline to scaled image.
3151 */
3152 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003153 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003154 {
cristyce70c172010-01-07 17:15:30 +00003155 q->red=ClampToQuantum(t->red);
3156 q->green=ClampToQuantum(t->green);
3157 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003158 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003159 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003160 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003161 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003162 t++;
3163 q++;
3164 }
3165 }
cristyed6cb232010-01-20 03:07:53 +00003166 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003167 break;
cristy96b16132010-08-29 17:19:52 +00003168 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3169 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003170 if (proceed == MagickFalse)
3171 break;
3172 }
cristyed6cb232010-01-20 03:07:53 +00003173 scale_view=DestroyCacheView(scale_view);
3174 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003175 /*
3176 Free allocated memory.
3177 */
3178 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3179 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3180 if (scale_image->rows != image->rows)
3181 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3182 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3183 scale_image->type=image->type;
3184 return(scale_image);
3185}
3186
anthony02b4cb42010-10-10 04:54:35 +00003187#if 0
3188 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003189/*
3190%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3191% %
3192% %
3193% %
3194+ S e t R e s i z e F i l t e r S u p p o r t %
3195% %
3196% %
3197% %
3198%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3199%
3200% SetResizeFilterSupport() specifies which IR filter to use to window
3201%
3202% The format of the SetResizeFilterSupport method is:
3203%
3204% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3205% const MagickRealType support)
3206%
3207% A description of each parameter follows:
3208%
3209% o resize_filter: the resize filter.
3210%
3211% o support: the filter spport radius.
3212%
3213*/
3214MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3215 const MagickRealType support)
3216{
3217 assert(resize_filter != (ResizeFilter *) NULL);
3218 assert(resize_filter->signature == MagickSignature);
3219 resize_filter->support=support;
3220}
anthony02b4cb42010-10-10 04:54:35 +00003221#endif
cristy3ed852e2009-09-05 21:47:34 +00003222
3223/*
3224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3225% %
3226% %
3227% %
3228% T h u m b n a i l I m a g e %
3229% %
3230% %
3231% %
3232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3233%
3234% ThumbnailImage() changes the size of an image to the given dimensions and
3235% removes any associated profiles. The goal is to produce small low cost
3236% thumbnail images suited for display on the Web.
3237%
3238% The format of the ThumbnailImage method is:
3239%
cristybb503372010-05-27 20:51:26 +00003240% Image *ThumbnailImage(const Image *image,const size_t columns,
3241% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003242%
3243% A description of each parameter follows:
3244%
3245% o image: the image.
3246%
3247% o columns: the number of columns in the scaled image.
3248%
3249% o rows: the number of rows in the scaled image.
3250%
3251% o exception: return any errors or warnings in this structure.
3252%
3253*/
cristy9af9b5d2010-08-15 17:04:28 +00003254MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3255 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003256{
3257#define SampleFactor 5
3258
3259 char
3260 value[MaxTextExtent];
3261
3262 const char
3263 *name;
3264
3265 Image
3266 *thumbnail_image;
3267
3268 MagickRealType
3269 x_factor,
3270 y_factor;
3271
cristybb503372010-05-27 20:51:26 +00003272 size_t
cristy3ed852e2009-09-05 21:47:34 +00003273 version;
3274
cristy9af9b5d2010-08-15 17:04:28 +00003275 struct stat
3276 attributes;
3277
cristy3ed852e2009-09-05 21:47:34 +00003278 assert(image != (Image *) NULL);
3279 assert(image->signature == MagickSignature);
3280 if (image->debug != MagickFalse)
3281 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3282 assert(exception != (ExceptionInfo *) NULL);
3283 assert(exception->signature == MagickSignature);
3284 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3285 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3286 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003287 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3288 exception);
cristy3ed852e2009-09-05 21:47:34 +00003289 else
3290 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003291 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3292 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003293 else
3294 {
3295 Image
3296 *sample_image;
3297
3298 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3299 exception);
3300 if (sample_image == (Image *) NULL)
3301 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003302 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3303 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003304 sample_image=DestroyImage(sample_image);
3305 }
3306 if (thumbnail_image == (Image *) NULL)
3307 return(thumbnail_image);
3308 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3309 if (thumbnail_image->matte == MagickFalse)
3310 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3311 thumbnail_image->depth=8;
3312 thumbnail_image->interlace=NoInterlace;
3313 /*
3314 Strip all profiles except color profiles.
3315 */
3316 ResetImageProfileIterator(thumbnail_image);
3317 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3318 {
3319 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3320 {
cristy2b726bd2010-01-11 01:05:39 +00003321 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003322 ResetImageProfileIterator(thumbnail_image);
3323 }
3324 name=GetNextImageProfile(thumbnail_image);
3325 }
3326 (void) DeleteImageProperty(thumbnail_image,"comment");
3327 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003328 if (strstr(image->magick_filename,"//") == (char *) NULL)
3329 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003330 image->magick_filename);
3331 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3332 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3333 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3334 {
cristye8c25f92010-06-03 00:53:06 +00003335 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003336 attributes.st_mtime);
3337 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3338 }
cristye8c25f92010-06-03 00:53:06 +00003339 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003340 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003341 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003342 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003343 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3344 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3345 LocaleLower(value);
3346 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3347 (void) SetImageProperty(thumbnail_image,"software",
3348 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003349 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3350 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003351 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003352 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003353 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003354 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003355 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3356 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003357 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3358 return(thumbnail_image);
3359}