blob: 08e3ade4dd17cfd114bddfb45983b05c16771191 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% RRRR EEEEE SSSSS IIIII ZZZZZ EEEEE %
7% R R E SS I ZZ E %
8% RRRR EEE SSS I ZZZ EEE %
9% R R E SS I ZZ E %
10% R R EEEEE SSSSS IIIII ZZZZZ EEEEE %
11% %
12% %
13% MagickCore Image Resize Methods %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
42#include "magick/studio.h"
43#include "magick/artifact.h"
44#include "magick/blob.h"
45#include "magick/cache.h"
46#include "magick/cache-view.h"
47#include "magick/color.h"
48#include "magick/color-private.h"
49#include "magick/draw.h"
50#include "magick/exception.h"
51#include "magick/exception-private.h"
52#include "magick/gem.h"
53#include "magick/image.h"
54#include "magick/image-private.h"
55#include "magick/list.h"
56#include "magick/memory_.h"
anthony55f12332010-09-10 01:13:02 +000057#include "magick/magick.h"
cristy3ed852e2009-09-05 21:47:34 +000058#include "magick/pixel-private.h"
59#include "magick/property.h"
60#include "magick/monitor.h"
61#include "magick/monitor-private.h"
62#include "magick/pixel.h"
63#include "magick/option.h"
64#include "magick/resample.h"
65#include "magick/resize.h"
66#include "magick/resize-private.h"
67#include "magick/string_.h"
cristyf2f27272009-12-17 14:48:46 +000068#include "magick/string-private.h"
cristy3ed852e2009-09-05 21:47:34 +000069#include "magick/thread-private.h"
70#include "magick/utility.h"
71#include "magick/version.h"
72#if defined(MAGICKCORE_LQR_DELEGATE)
73#include <lqr.h>
74#endif
75
76/*
77 Typedef declarations.
78*/
79struct _ResizeFilter
80{
81 MagickRealType
82 (*filter)(const MagickRealType,const ResizeFilter *),
83 (*window)(const MagickRealType,const ResizeFilter *),
cristy33b1c162010-01-23 22:51:51 +000084 support, /* filter region of support - the filter support limit */
85 window_support, /* window support, usally equal to support (expert only) */
anthony55f12332010-09-10 01:13:02 +000086 scale, /* dimension scaling to fit window support (usally 1.0) */
cristy33b1c162010-01-23 22:51:51 +000087 blur, /* x-scale (blur-sharpen) */
anthonyf5e76ef2010-10-12 01:22:01 +000088 coeff[8]; /* cubic coefficents for smooth Cubic filters */
cristy3ed852e2009-09-05 21:47:34 +000089
cristybb503372010-05-27 20:51:26 +000090 size_t
cristy3ed852e2009-09-05 21:47:34 +000091 signature;
92};
93
94/*
95 Forward declaractions.
96*/
97static MagickRealType
98 I0(MagickRealType x),
anthonyb6d08c52010-09-13 01:17:04 +000099 BesselOrderOne(MagickRealType),
anthony07a3f7f2010-09-16 03:03:11 +0000100 Sinc(const MagickRealType, const ResizeFilter *),
anthonyba5a7c32010-09-15 02:42:25 +0000101 SincFast(const MagickRealType, const ResizeFilter *);
cristy3ed852e2009-09-05 21:47:34 +0000102
103/*
104%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
105% %
106% %
107% %
108+ F i l t e r F u n c t i o n s %
109% %
110% %
111% %
112%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
113%
cristy33b1c162010-01-23 22:51:51 +0000114% These are the various filter and windowing functions that are provided.
cristy3ed852e2009-09-05 21:47:34 +0000115%
cristy33b1c162010-01-23 22:51:51 +0000116% They are internal to this module only. See AcquireResizeFilterInfo() for
117% details of the access to these functions, via the GetResizeFilterSupport()
118% and GetResizeFilterWeight() API interface.
cristy3ed852e2009-09-05 21:47:34 +0000119%
120% The individual filter functions have this format...
121%
122% static MagickRealtype *FilterName(const MagickRealType x,
123% const MagickRealType support)
124%
cristy33b1c162010-01-23 22:51:51 +0000125% A description of each parameter follows:
cristy3ed852e2009-09-05 21:47:34 +0000126%
cristy33b1c162010-01-23 22:51:51 +0000127% o x: the distance from the sampling point generally in the range of 0 to
128% support. The GetResizeFilterWeight() ensures this a positive value.
129%
130% o resize_filter: current filter information. This allows function to
131% access support, and possibly other pre-calculated information defining
132% the functions.
cristy3ed852e2009-09-05 21:47:34 +0000133%
134*/
135
cristyc5c6f662010-09-22 14:23:02 +0000136#define MagickPIL ((MagickRealType) 3.14159265358979323846264338327950288420L)
cristy560d8182010-09-08 22:36:25 +0000137
anthony48f77622010-10-03 14:32:31 +0000138static MagickRealType Jinc(const MagickRealType x,
cristy3ed852e2009-09-05 21:47:34 +0000139 const ResizeFilter *magick_unused(resize_filter))
140{
141 /*
anthony48f77622010-10-03 14:32:31 +0000142 See Pratt "Digital Image Processing" p.97 for Jinc/Bessel functions.
cristy33b1c162010-01-23 22:51:51 +0000143 http://mathworld.wolfram.com/JincFunction.html and page 11 of
anthony48f77622010-10-03 14:32:31 +0000144 http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
145
nicolase473f722010-10-07 00:05:13 +0000146 The original "zoom" program by Paul Heckbert called this "Bessel".
147 But really it is more accurately named "Jinc".
cristy3ed852e2009-09-05 21:47:34 +0000148 */
149 if (x == 0.0)
nicolas5a36f342010-10-07 00:11:32 +0000150 return(0.5*MagickPIL);
151 return(BesselOrderOne(MagickPIL*x)/x);
cristy3ed852e2009-09-05 21:47:34 +0000152}
153
154static MagickRealType Blackman(const MagickRealType x,
cristyce70c172010-01-07 17:15:30 +0000155 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000156{
157 /*
cristy83017922010-09-05 20:45:15 +0000158 Blackman: 2nd order cosine windowing function:
cristy21ce88a2010-09-05 01:37:25 +0000159 0.42 + 0.5 cos(pi x) + 0.08 cos(2pi x)
cristy560d8182010-09-08 22:36:25 +0000160 Refactored by Chantal Racette and Nicolas Robidoux to one trig
161 call and five flops.
cristy3ed852e2009-09-05 21:47:34 +0000162 */
cristyc5c6f662010-09-22 14:23:02 +0000163 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000164 return(0.34+cospix*(0.5+cospix*0.16));
cristy3ed852e2009-09-05 21:47:34 +0000165}
166
167static MagickRealType Bohman(const MagickRealType x,
cristyce70c172010-01-07 17:15:30 +0000168 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000169{
170 /*
cristy560d8182010-09-08 22:36:25 +0000171 Bohman: 2rd Order cosine windowing function:
172 (1-x) cos(pi x) + sin(pi x) / pi.
nicolasa4f7b4a2010-09-20 20:28:38 +0000173 Refactored by Nicolas Robidoux to one trig call, one sqrt call,
174 and 7 flops, taking advantage of the fact that the support of
175 Bohman is 1 (so that we know that sin(pi x) >= 0).
cristy3ed852e2009-09-05 21:47:34 +0000176 */
cristyc5c6f662010-09-22 14:23:02 +0000177 const double cospix = cos((double) (MagickPIL*x));
nicolasa4f7b4a2010-09-20 20:28:38 +0000178 const double sinpix = sqrt(1.0-cospix*cospix);
nicolas2ffd3b22010-09-24 20:27:31 +0000179 return((1.0-x)*cospix+(1.0/MagickPIL)*sinpix);
cristy3ed852e2009-09-05 21:47:34 +0000180}
181
anthony463be1d2010-09-26 01:07:36 +0000182static MagickRealType Box(const MagickRealType magick_unused(x),
183 const ResizeFilter *magick_unused(resize_filter))
cristy3ed852e2009-09-05 21:47:34 +0000184{
185 /*
nicolas40477452010-09-27 23:42:08 +0000186 A Box filter is a equal weighting function (all weights equal).
anthonyc331dec2010-09-26 01:30:14 +0000187 DO NOT LIMIT results by support or resize point sampling will work
188 as it requests points beyond its normal 0.0 support size.
cristy3ed852e2009-09-05 21:47:34 +0000189 */
anthony463be1d2010-09-26 01:07:36 +0000190 return(1.0);
cristy3ed852e2009-09-05 21:47:34 +0000191}
192
193static MagickRealType CubicBC(const MagickRealType x,
194 const ResizeFilter *resize_filter)
195{
196 /*
197 Cubic Filters using B,C determined values:
cristyf59a8922010-02-28 19:51:23 +0000198 Mitchell-Netravali B=1/3 C=1/3 Qualitively ideal Cubic Filter
199 Catmull-Rom B= 0 C=1/2 Cublic Interpolation Function
200 Cubic B-Spline B= 1 C= 0 Spline Approximation of Gaussian
201 Hermite B= 0 C= 0 Quadratic Spline (support = 1)
cristy3ed852e2009-09-05 21:47:34 +0000202
cristy33b1c162010-01-23 22:51:51 +0000203 See paper by Mitchell and Netravali, Reconstruction Filters in Computer
204 Graphics Computer Graphics, Volume 22, Number 4, August 1988
205 http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/
cristyf59a8922010-02-28 19:51:23 +0000206 Mitchell.pdf.
cristy3ed852e2009-09-05 21:47:34 +0000207
cristy33b1c162010-01-23 22:51:51 +0000208 Coefficents are determined from B,C values:
cristy3ed852e2009-09-05 21:47:34 +0000209 P0 = ( 6 - 2*B )/6
210 P1 = 0
211 P2 = (-18 +12*B + 6*C )/6
212 P3 = ( 12 - 9*B - 6*C )/6
213 Q0 = ( 8*B +24*C )/6
214 Q1 = ( -12*B -48*C )/6
215 Q2 = ( 6*B +30*C )/6
216 Q3 = ( - 1*B - 6*C )/6
217
cristy33b1c162010-01-23 22:51:51 +0000218 which are used to define the filter:
cristyf59a8922010-02-28 19:51:23 +0000219
cristy3ed852e2009-09-05 21:47:34 +0000220 P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
221 Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x <= 2
222
cristy33b1c162010-01-23 22:51:51 +0000223 which ensures function is continuous in value and derivative (slope).
cristy3ed852e2009-09-05 21:47:34 +0000224 */
225 if (x < 1.0)
anthonyf5e76ef2010-10-12 01:22:01 +0000226 return(resize_filter->coeff[0]+x*(resize_filter->coeff[1]+x*
227 (resize_filter->coeff[2]+x*resize_filter->coeff[3])));
cristy3ed852e2009-09-05 21:47:34 +0000228 if (x < 2.0)
anthonyf5e76ef2010-10-12 01:22:01 +0000229 return(resize_filter->coeff[4]+x*(resize_filter->coeff[5]+x*
230 (resize_filter->coeff[6]+x*resize_filter->coeff[7])));
cristy3ed852e2009-09-05 21:47:34 +0000231 return(0.0);
232}
233
234static MagickRealType Gaussian(const MagickRealType x,
anthonyf5e76ef2010-10-12 01:22:01 +0000235 const ResizeFilter *resize_filter)
cristy3ed852e2009-09-05 21:47:34 +0000236{
cristy560d8182010-09-08 22:36:25 +0000237 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000238 Gaussian with a fixed sigma = 1/2
239
240 Gaussian Formula...
241 exp( -(x^2)/((2.0*sigma^2) ) / sqrt(2*PI*sigma^2)))
242 The constants are pre-calculated...
243 exp( -coeff[0]*(x^2)) ) * coeff[1]
244 However the multiplier coefficent is not needed and not used.
245
246 This separates the gaussian 'sigma' value from the 'blur/support' settings
247 allows for its use in special 'small sigma' gaussians, without the filter
248 'missing' pixels when blur and thus support becomes too small.
cristy560d8182010-09-08 22:36:25 +0000249 */
anthonyf5e76ef2010-10-12 01:22:01 +0000250 return(exp((double)(-resize_filter->coeff[0]*x*x))); }
cristy3ed852e2009-09-05 21:47:34 +0000251
252static MagickRealType Hanning(const MagickRealType x,
253 const ResizeFilter *magick_unused(resize_filter))
254{
255 /*
nicolas40477452010-09-27 23:42:08 +0000256 Cosine window function:
257 .5+.5cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000258 */
cristyc5c6f662010-09-22 14:23:02 +0000259 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000260 return(0.5+0.5*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000261}
262
263static MagickRealType Hamming(const MagickRealType x,
264 const ResizeFilter *magick_unused(resize_filter))
265{
266 /*
nicolas40477452010-09-27 23:42:08 +0000267 Offset cosine window function:
268 .54 + .46 cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000269 */
cristyc5c6f662010-09-22 14:23:02 +0000270 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000271 return(0.54+0.46*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000272}
273
274static MagickRealType Kaiser(const MagickRealType x,
275 const ResizeFilter *magick_unused(resize_filter))
276{
277#define Alpha 6.5
278#define I0A (1.0/I0(Alpha))
279
280 /*
nicolas07bac812010-09-19 18:47:02 +0000281 Kaiser Windowing Function (bessel windowing): Alpha is a free
282 value from 5 to 8 (currently hardcoded to 6.5).
283 Future: make alpha the IOA pre-calculation, an 'expert' setting.
cristy3ed852e2009-09-05 21:47:34 +0000284 */
285 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
286}
287
288static MagickRealType Lagrange(const MagickRealType x,
289 const ResizeFilter *resize_filter)
290{
cristy3ed852e2009-09-05 21:47:34 +0000291 MagickRealType
292 value;
293
cristybb503372010-05-27 20:51:26 +0000294 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000295 i;
296
cristy9af9b5d2010-08-15 17:04:28 +0000297 ssize_t
298 n,
299 order;
300
cristy3ed852e2009-09-05 21:47:34 +0000301 /*
nicolas07bac812010-09-19 18:47:02 +0000302 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the
303 lagrange function and depends on the overall support window size
304 of the filter. That is: for a support of 2, it gives a lagrange-4
305 (piecewise cubic function).
cristy3ed852e2009-09-05 21:47:34 +0000306
nicolas07bac812010-09-19 18:47:02 +0000307 "n" identifies the piece of the piecewise polynomial.
cristy3ed852e2009-09-05 21:47:34 +0000308
nicolas07bac812010-09-19 18:47:02 +0000309 See Survey: Interpolation Methods, IEEE Transactions on Medical
310 Imaging, Vol 18, No 11, November 1999, p1049-1075, -- Equation 27
311 on p1064.
cristy3ed852e2009-09-05 21:47:34 +0000312 */
313 if (x > resize_filter->support)
314 return(0.0);
cristybb503372010-05-27 20:51:26 +0000315 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
anthonyc2d07db2010-09-15 23:47:40 +0000316 /*n=(ssize_t)((1.0*order)/2.0+x); -- which piece does x belong to */
317 n = (ssize_t)(resize_filter->window_support + x);
cristy3ed852e2009-09-05 21:47:34 +0000318 value=1.0f;
319 for (i=0; i < order; i++)
320 if (i != n)
321 value*=(n-i-x)/(n-i);
322 return(value);
323}
324
325static MagickRealType Quadratic(const MagickRealType x,
326 const ResizeFilter *magick_unused(resize_filter))
327{
328 /*
329 2rd order (quadratic) B-Spline approximation of Gaussian.
330 */
331 if (x < 0.5)
332 return(0.75-x*x);
333 if (x < 1.5)
334 return(0.5*(x-1.5)*(x-1.5));
335 return(0.0);
336}
337
anthony07a3f7f2010-09-16 03:03:11 +0000338static MagickRealType Sinc(const MagickRealType x,
cristy3ed852e2009-09-05 21:47:34 +0000339 const ResizeFilter *magick_unused(resize_filter))
340{
anthony720660f2010-09-07 10:05:14 +0000341 /*
nicolas40477452010-09-27 23:42:08 +0000342 Scaled sinc(x) function using a trig call:
nicolas07bac812010-09-19 18:47:02 +0000343 sinc(x) == sin(pi x)/(pi x).
anthony720660f2010-09-07 10:05:14 +0000344 */
anthony2d9b8b52010-09-14 08:31:07 +0000345 if (x != 0.0)
cristy560d8182010-09-08 22:36:25 +0000346 {
cristyc5c6f662010-09-22 14:23:02 +0000347 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
nicolas07bac812010-09-19 18:47:02 +0000348 return(sin((double) pix)/pix);
cristy560d8182010-09-08 22:36:25 +0000349 }
nicolas2ffd3b22010-09-24 20:27:31 +0000350 return((MagickRealType) 1.0);
anthony720660f2010-09-07 10:05:14 +0000351}
352
anthonyba5a7c32010-09-15 02:42:25 +0000353static MagickRealType SincFast(const MagickRealType x,
anthony720660f2010-09-07 10:05:14 +0000354 const ResizeFilter *magick_unused(resize_filter))
355{
cristy560d8182010-09-08 22:36:25 +0000356 /*
357 Approximations of the sinc function sin(pi x)/(pi x) over the
358 interval [-4,4] constructed by Nicolas Robidoux and Chantal
359 Racette with funding from the Natural Sciences and Engineering
360 Research Council of Canada.
nicolas07bac812010-09-19 18:47:02 +0000361
362 Although the approximations are polynomials (for low order of
363 approximation) and quotients of polynomials (for higher order of
364 approximation) and consequently are similar in form to Taylor
365 polynomials/Pade approximants, the approximations are computed
366 with a completely different technique.
367
368 Summary: These approximations are "the best" in terms of bang
369 (accuracy) for the buck (flops). More specifically: Among the
370 polynomial quotients that can be computed using a fixed number of
371 flops (with a given "+ - * / budget"), the chosen polynomial
372 quotient is the one closest to the approximated function with
373 respect to maximum absolute relative error over the given
374 interval.
375
376 The Remez algorithm, as implemented in the boost library's minimax
nicolas3aab40c2010-09-19 21:14:15 +0000377 package, is the key to the construction:
nicolas07bac812010-09-19 18:47:02 +0000378 http://www.boost.org/doc/libs/1_36_0/libs/math/doc/...
379 ...sf_and_dist/html/math_toolkit/backgrounders/remez.html
cristy560d8182010-09-08 22:36:25 +0000380 */
nicolas3aab40c2010-09-19 21:14:15 +0000381 /*
382 If outside of the interval of approximation, use the standard trig
383 formula.
384 */
anthony2d9b8b52010-09-14 08:31:07 +0000385 if (x > 4.0)
cristy03dbbd22010-09-19 23:04:47 +0000386 {
cristyc5c6f662010-09-22 14:23:02 +0000387 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
cristy03dbbd22010-09-19 23:04:47 +0000388 return(sin((double) pix)/pix);
389 }
anthony2d9b8b52010-09-14 08:31:07 +0000390 {
nicolas07bac812010-09-19 18:47:02 +0000391 /*
392 The approximations only depend on x^2 (sinc is an even
393 function).
394 */
395 const MagickRealType xx = x*x;
cristy83017922010-09-05 20:45:15 +0000396#if MAGICKCORE_QUANTUM_DEPTH <= 8
397 /*
anthony2d9b8b52010-09-14 08:31:07 +0000398 Maximum absolute relative error 6.3e-6 < 1/2^17.
cristy738e7562010-09-01 12:48:07 +0000399 */
400 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
401 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
402 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
403 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
404 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
405 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
406 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
407 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
nicolas3aab40c2010-09-19 21:14:15 +0000408 const MagickRealType p =
409 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
nicolas07bac812010-09-19 18:47:02 +0000410 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
anthony2d9b8b52010-09-14 08:31:07 +0000411#elif MAGICKCORE_QUANTUM_DEPTH <= 16
cristydbeb3eb2010-09-09 13:41:36 +0000412 /*
anthony2d9b8b52010-09-14 08:31:07 +0000413 Max. abs. rel. error 2.2e-8 < 1/2^25.
cristydbeb3eb2010-09-09 13:41:36 +0000414 */
415 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
416 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
nicolas3aab40c2010-09-19 21:14:15 +0000417 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
418 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
419 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
420 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
421 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
cristydbeb3eb2010-09-09 13:41:36 +0000422 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
423 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
424 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
anthony853d6972010-10-08 06:01:31 +0000425 const MagickRealType p =
nicolas3aab40c2010-09-19 21:14:15 +0000426 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9))))))));
nicolas07bac812010-09-19 18:47:02 +0000427 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
nicolasc2d28f02010-09-27 18:56:15 +0000428#else
nicolas3aab40c2010-09-19 21:14:15 +0000429 /*
430 Max. abs. rel. error 1.2e-12 < 1/2^39.
431 */
432 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
433 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
434 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
435 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
436 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
437 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
438 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
439 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
440 const MagickRealType p =
441 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
442 const MagickRealType d0 = 1.0L;
443 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
444 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
445 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
446 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
447 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
448 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
cristy657c6352010-08-29 14:05:08 +0000449#endif
cristy83017922010-09-05 20:45:15 +0000450 }
cristy3ed852e2009-09-05 21:47:34 +0000451}
452
453static MagickRealType Triangle(const MagickRealType x,
454 const ResizeFilter *magick_unused(resize_filter))
455{
456 /*
nicolas0edb0862010-09-19 18:56:19 +0000457 1st order (linear) B-Spline, bilinear interpolation, Tent 1D
458 filter, or a Bartlett 2D Cone filter.
cristy3ed852e2009-09-05 21:47:34 +0000459 */
460 if (x < 1.0)
461 return(1.0-x);
462 return(0.0);
463}
464
465static MagickRealType Welsh(const MagickRealType x,
466 const ResizeFilter *magick_unused(resize_filter))
467{
468 /*
469 Welsh parabolic windowing filter.
470 */
cristy560d8182010-09-08 22:36:25 +0000471 if (x < 1.0)
cristy3ed852e2009-09-05 21:47:34 +0000472 return(1.0-x*x);
473 return(0.0);
474}
475
476/*
477%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
478% %
479% %
480% %
481+ A c q u i r e R e s i z e F i l t e r %
482% %
483% %
484% %
485%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
486%
nicolas07bac812010-09-19 18:47:02 +0000487% AcquireResizeFilter() allocates the ResizeFilter structure. Choose
488% from these filters:
cristy3ed852e2009-09-05 21:47:34 +0000489%
490% FIR (Finite impulse Response) Filters
491% Box Triangle Quadratic
492% Cubic Hermite Catrom
493% Mitchell
494%
495% IIR (Infinite impulse Response) Filters
anthony48f77622010-10-03 14:32:31 +0000496% Gaussian Sinc Jinc (Bessel)
cristy3ed852e2009-09-05 21:47:34 +0000497%
anthony48f77622010-10-03 14:32:31 +0000498% Windowed Sinc/Jinc Filters
cristy3ed852e2009-09-05 21:47:34 +0000499% Blackman Hanning Hamming
anthony48f77622010-10-03 14:32:31 +0000500% Kaiser Lanczos
cristy3ed852e2009-09-05 21:47:34 +0000501%
anthony61b5ddd2010-10-05 02:33:31 +0000502% Special purpose Filters
anthony853d6972010-10-08 06:01:31 +0000503% SincFast Lanczos2D Robidoux
anthony61b5ddd2010-10-05 02:33:31 +0000504%
anthony48f77622010-10-03 14:32:31 +0000505% The users "-filter" selection is used to lookup the default 'expert'
506% settings for that filter from a internal table. However any provided
507% 'expert' settings (see below) may override this selection.
508%
509% FIR filters are used as is, and are limited to that filters support
nicolas07bac812010-09-19 18:47:02 +0000510% window (unless over-ridden). 'Gaussian' while classed as an IIR
anthonyc331dec2010-09-26 01:30:14 +0000511% filter, is also simply clipped by its support size (currently 1.5
anthonyf5e76ef2010-10-12 01:22:01 +0000512% or approximatally 3*sigma as recommended by many references)
cristy3ed852e2009-09-05 21:47:34 +0000513%
anthony48f77622010-10-03 14:32:31 +0000514% The selection is typically either a windowed Sinc, or interpolated
nicolas07bac812010-09-19 18:47:02 +0000515% filter, for use by functions such as ResizeImage(). However if a
anthony48f77622010-10-03 14:32:31 +0000516% 'cylindrical' filter flag is requested, any default Sinc weighting
517% and windowing functions will be promoted to cylindrical Jinc form of
518% function.
cristy3ed852e2009-09-05 21:47:34 +0000519%
anthony48f77622010-10-03 14:32:31 +0000520% Directly requesting 'Sinc' or 'Jinc' will force the use of that
521% filter function without any windowing. This is not recommended,
522% except by image processing experts or in expert options. Selecting a
523% window filtering version of these functions is better.
cristy3ed852e2009-09-05 21:47:34 +0000524%
anthony48f77622010-10-03 14:32:31 +0000525% Lanczos is a special case of a Sinc-windowed Sinc, (or Jinc-Jinc for
526% the cylindrical case) but defaulting to 3-lobe support, rather that
527% the default 4 lobe support of the other windowed sinc/jinc filters.
cristy3ed852e2009-09-05 21:47:34 +0000528%
anthony48f77622010-10-03 14:32:31 +0000529% Two forms of the 'Sinc' function are available: Sinc and SincFast.
nicolas07bac812010-09-19 18:47:02 +0000530% Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
531% selected if the user specifically specifies the use of a Sinc
532% filter. SincFast uses highly accurate (and fast) polynomial (low Q)
anthony48f77622010-10-03 14:32:31 +0000533% and rational (high Q) approximations, and will be used by default in
534% most cases.
anthonyba5a7c32010-09-15 02:42:25 +0000535%
nicolas1eb2fcf2010-10-10 20:45:17 +0000536% The Lanczos2D and Robidoux filters are tuned for cylindrical
537% (radial) EWA (Elliptical Weighted Average) distortion. Lanczos2D
nicolasdff19b42010-10-10 20:53:13 +0000538% is a 2 lobe Lanczos-like filter using Jinc (for EWA) or Sinc.
nicolas6ca6e962010-10-10 20:49:01 +0000539% Robidoux used to be a sharpened version of Lanczos2D (with
nicolas72d96ea2010-10-20 20:36:03 +0000540% blur=0.958033808). Now, Robidoux is the unique cubic BC-spline
541% filter satisfying the following two conditions:
542% 1) Robidoux exactly preserves images with only vertical or
543% horizontal features when performing 'no-op" with EWA
544% distortion;
545% 2) Robidoux exactly preserves linear gradient data when the
546% density of the image is doubled by "vertex splitting" (this is
547% done in natural implementations of Box Filtering).
548% Because any cylindrical filter exactly preserves linear gradiant
549% data when the density of the image is doubled with the usual "reuse
550% the input locations and insert new pixels half-way" (this is done
551% in natural implementations of bilinear), this means that Robidoux
552% preserves linear gradient data when doubling the density in the two
553% most common conventions). The preservation of linear gradients is
554% what distinguishes Keys BC-splines from the others when used in a
555% tensor scheme; Robidoux approximately carries this property over to
556% the EWA context. It turns out that Robidoux is close to both plain
557% Mitchell and "sharpened" Lanczos2D.
anthony61b5ddd2010-10-05 02:33:31 +0000558%
nicolas07bac812010-09-19 18:47:02 +0000559% Special 'expert' options can be used to override any and all filter
560% settings. This is not advised unless you have expert knowledge of
561% the use of resampling filtered techniques. Check on the results of
562% your selections using the "filter:verbose" setting to make sure you
anthony61b5ddd2010-10-05 02:33:31 +0000563% get the exact filter that you are tring to achieve.
cristy3ed852e2009-09-05 21:47:34 +0000564%
anthony48f77622010-10-03 14:32:31 +0000565% "filter:filter" Select the main function associated with
566% this filter name, as the weighting function of the filter.
567% This can be used to set a windowing function as a weighting
568% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000569%
anthony7bdc0ed2010-09-15 01:52:32 +0000570% If a "filter:window" operation has not been provided, then a 'Box'
571% windowing function will be set to denote that no windowing function
572% is being used.
cristy3ed852e2009-09-05 21:47:34 +0000573%
574% "filter:window" Select this windowing function for the filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000575% While any filter could be used as a windowing function, using the
576% 'first lobe' of that filter over the whole support window, using a
anthony48f77622010-10-03 14:32:31 +0000577% non-windowing function is not advisible. If no weighting filter
578% function is specifed a 'SincFast' filter will be used.
cristy3ed852e2009-09-05 21:47:34 +0000579%
anthony48f77622010-10-03 14:32:31 +0000580% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000581% This a simpler method of setting filter support size that will
anthony48f77622010-10-03 14:32:31 +0000582% correctly handle the Sinc/Jinc switch for an operators filtering
anthony7bdc0ed2010-09-15 01:52:32 +0000583% requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000584%
585% "filter:support" Set the support size for filtering to the size given
anthony48f77622010-10-03 14:32:31 +0000586% This not recommended for Sinc/Jinc windowed filters (lobes should
anthony7bdc0ed2010-09-15 01:52:32 +0000587% be used instead). This will override any 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000588%
anthonyb6d08c52010-09-13 01:17:04 +0000589% "filter:win-support" Scale windowing function to this size instead.
590% This causes the windowing (or self-windowing Lagrange filter) to act
591% is if the support window it much much larger than what is actually
592% supplied to the calling operator. The filter however is still
593% clipped to the real support size given, by the support range suppiled
594% to the caller. If unset this will equal the normal filter support
595% size.
596%
cristy3ed852e2009-09-05 21:47:34 +0000597% "filter:blur" Scale the filter and support window by this amount.
598% A value >1 will generally result in a more burred image with
599% more ringing effects, while a value <1 will sharpen the
600% resulting image with more aliasing and Morie effects.
601%
anthonyf5e76ef2010-10-12 01:22:01 +0000602% "filter:sigma" The sigma value to use for the Gaussian filter only.
603% Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for cylindrical
604% usage. It effectially provides a alturnative to 'blur' for Gaussians
605% without it also effecting the final 'practical support' size.
606%
cristy3ed852e2009-09-05 21:47:34 +0000607% "filter:b"
608% "filter:c" Override the preset B,C values for a Cubic type of filter
609% If only one of these are given it is assumes to be a 'Keys'
610% type of filter such that B+2C=1, where Keys 'alpha' value = C
611%
anthonyb6d08c52010-09-13 01:17:04 +0000612% "filter:verbose" Output the exact results of the filter selections
613% made, as well as plotting data for graphing the resulting filter
614% over support range (blur adjusted).
cristy3ed852e2009-09-05 21:47:34 +0000615%
616% Set a true un-windowed Sinc filter with 10 lobes (very slow)
anthony48f77622010-10-03 14:32:31 +0000617% -define filter:filter=Sinc
618% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000619%
anthony48f77622010-10-03 14:32:31 +0000620% For example force an 8 lobe Lanczos (Sinc or Jinc) filter...
cristy3ed852e2009-09-05 21:47:34 +0000621% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000622% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000623%
cristy3ed852e2009-09-05 21:47:34 +0000624% The format of the AcquireResizeFilter method is:
625%
626% ResizeFilter *AcquireResizeFilter(const Image *image,
627% const FilterTypes filter_type, const MagickBooleanType radial,
628% ExceptionInfo *exception)
629%
cristy33b1c162010-01-23 22:51:51 +0000630% A description of each parameter follows:
631%
cristy3ed852e2009-09-05 21:47:34 +0000632% o image: the image.
633%
nicolas07bac812010-09-19 18:47:02 +0000634% o filter: the filter type, defining a preset filter, window and
635% support. The artifact settings listed above will override
636% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000637%
anthony48f77622010-10-03 14:32:31 +0000638% o blur: blur the filter by this amount, use 1.0 if unknown. Image
639% artifact "filter:blur" will override this API call usage, including
640% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000641%
anthony48f77622010-10-03 14:32:31 +0000642% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
643% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000644%
645% o exception: return any errors or warnings in this structure.
646%
647*/
648MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000649 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000650 const MagickBooleanType cylindrical,ExceptionInfo *exception)
651{
652 const char
653 *artifact;
654
655 FilterTypes
656 filter_type,
657 window_type;
658
cristy3ed852e2009-09-05 21:47:34 +0000659 MagickRealType
660 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000661 C,
662 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000663
664 register ResizeFilter
665 *resize_filter;
666
cristy9af9b5d2010-08-15 17:04:28 +0000667 ssize_t
668 option;
669
cristy3ed852e2009-09-05 21:47:34 +0000670 /*
anthony48f77622010-10-03 14:32:31 +0000671 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000672 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000673 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
674 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
675 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000676
nicolas07bac812010-09-19 18:47:02 +0000677 WARNING: The order of this tabel must match the order of the
678 FilterTypes enumeration specified in "resample.h", or the filter
679 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000680
681 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000682 */
683 static struct
684 {
685 FilterTypes
686 filter,
687 window;
688 } const mapping[SentinelFilter] =
689 {
anthony462ee072010-09-27 12:34:02 +0000690 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
691 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
692 { BoxFilter, BoxFilter }, /* Box averaging filter */
693 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
694 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
695 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
696 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
697 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
698 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
699 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
700 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
701 { CatromFilter, BoxFilter }, /* Cubic interpolator */
702 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
703 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
anthony48f77622010-10-03 14:32:31 +0000704 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
705 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony462ee072010-09-27 12:34:02 +0000706 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
707 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
708 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
709 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
710 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
711 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
712 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolas5835ee02010-10-10 20:40:15 +0000713 { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
anthonye5f06452010-10-12 05:48:17 +0000714 { Lanczos2DSharpFilter, JincFilter },/* SPECIAL: ditto sharpened */
715 { RobidouxFilter, BoxFilter }, /* SPECIAL: Keys cubic tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000716 };
717 /*
nicolas32f44eb2010-09-20 01:23:12 +0000718 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000719 function. The default support size for that filter as a weighting
720 function, the range to scale with to use that function as a sinc
721 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000722
anthony07a3f7f2010-09-16 03:03:11 +0000723 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000724 SincFast(), and CubicBC() functions, which may have multiple
725 filter to function associations.
726
727 See "filter:verbose" handling below for the function -> filter
728 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000729 */
730 static struct
731 {
732 MagickRealType
733 (*function)(const MagickRealType, const ResizeFilter*),
nicolas8eccc162010-10-16 19:48:13 +0000734 lobes, /* Default lobes/support size of the weighting filter. */
anthony450db502010-10-19 04:03:03 +0000735 scale, /* Support when function used as a windowing function
736 Typically equal to the location of the first zero crossing. */
737 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
cristy3ed852e2009-09-05 21:47:34 +0000738 } const filters[SentinelFilter] =
739 {
anthony61b5ddd2010-10-05 02:33:31 +0000740 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
741 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
742 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
743 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
744 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
745 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
746 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
747 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000748 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000749 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
750 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
751 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
anthony853d6972010-10-08 06:01:31 +0000752 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
753 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
nicolasf8689f42010-10-18 16:14:08 +0000754 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000755 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
756 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
757 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
758 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
759 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
760 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
761 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
762 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
nicolas8eccc162010-10-16 19:48:13 +0000763 { Jinc, 2.0, 1.2196698912665045, 0.0, 0.0 },
764 /* Lanczos2D (Jinc-Jinc) */
765 { Jinc, 2.0, 1.1684849904329952, 0.0, 0.0 },
766 /* Lanczos2D sharpened with blur=0.958033808 */
anthony450db502010-10-19 04:03:03 +0000767 { CubicBC, 2.0, 1.1685777620836932,
nicolas72d96ea2010-10-20 20:36:03 +0000768 0.36553056988673434, 0.30046494140705066 }
769 /* Robidoux: BC-spline cubic close to Lanczos2D sharpened */
cristy3ed852e2009-09-05 21:47:34 +0000770 };
771 /*
anthony9a98fc62010-10-11 02:47:19 +0000772 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
anthony450db502010-10-19 04:03:03 +0000773 function being used as a filter. It is used by the "filter:lobes" expert
774 setting and for 'lobes' for Jinc functions in the previous table. This
775 way users do not have to deal with the highly irrational lobe sizes of the
anthony9a98fc62010-10-11 02:47:19 +0000776 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000777
nicolase473f722010-10-07 00:05:13 +0000778 Values taken from
anthony48f77622010-10-03 14:32:31 +0000779 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000780 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000781 */
782 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000783 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000784 {
nicolas8eccc162010-10-16 19:48:13 +0000785 1.2196698912665045,
786 2.2331305943815286,
787 3.2383154841662362,
788 4.2410628637960699,
789 5.2427643768701817,
790 6.2439216898644877,
791 7.244759868719957,
792 8.2453949139520427,
793 9.2458926849494673,
794 10.246293348754916,
795 11.246622794877883,
796 12.246898461138105,
797 13.247132522181061,
798 14.247333735806849,
anthonyc2d07db2010-09-15 23:47:40 +0000799 15.2475085630373,
nicolas8eccc162010-10-16 19:48:13 +0000800 16.247661874700962
cristy3ed852e2009-09-05 21:47:34 +0000801 };
802
cristy33b1c162010-01-23 22:51:51 +0000803 /*
804 Allocate resize filter.
805 */
cristy3ed852e2009-09-05 21:47:34 +0000806 assert(image != (const Image *) NULL);
807 assert(image->signature == MagickSignature);
808 if (image->debug != MagickFalse)
809 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
810 assert(UndefinedFilter < filter && filter < SentinelFilter);
811 assert(exception != (ExceptionInfo *) NULL);
812 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000813 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000814 if (resize_filter == (ResizeFilter *) NULL)
815 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000816 /*
817 Defaults for the requested filter.
818 */
819 filter_type=mapping[filter].filter;
820 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000821 resize_filter->blur = blur;
822 sigma = 0.5;
anthony48f77622010-10-03 14:32:31 +0000823 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000824 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000825 switch (filter_type)
826 {
827 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000828 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000829 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000830 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000831 break;
anthonyba5a7c32010-09-15 02:42:25 +0000832 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000833 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000834 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000835 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000836 break;
cristy33b1c162010-01-23 22:51:51 +0000837 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000838 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000839 filter_type=JincFilter;
840 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000841 break;
anthony08958462010-10-12 06:48:35 +0000842 case Lanczos2DSharpFilter:
nicolas1f4c0512010-10-20 20:19:30 +0000843 /* Sharpened by Nicolas Robidoux so as to optimize for minimal
844 * blurring of orthogonal lines
anthony08958462010-10-12 06:48:35 +0000845 */
846 resize_filter->blur *= 0.958033808;
847 break;
anthonyf5e76ef2010-10-12 01:22:01 +0000848 case GaussianFilter:
cristy68f103e2010-10-14 01:19:07 +0000849 sigma = (MagickRealType) (MagickSQ2/2.0); /* Cylindrical Gaussian sigma is sqrt(2)/2 */
anthonyf5e76ef2010-10-12 01:22:01 +0000850 break;
cristya782ecf2010-01-25 02:59:14 +0000851 default:
852 break;
cristy3ed852e2009-09-05 21:47:34 +0000853 }
anthony61b5ddd2010-10-05 02:33:31 +0000854 else
855 switch (filter_type)
856 {
857 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000858 case Lanczos2DSharpFilter:
nicolas45b58a92010-10-07 15:46:39 +0000859 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000860 window_type=SincFastFilter;
861 break;
862 default:
863 break;
864 }
865
cristy3ed852e2009-09-05 21:47:34 +0000866 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000867 if (artifact != (const char *) NULL)
868 {
cristy9af9b5d2010-08-15 17:04:28 +0000869 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000870 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000871 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000872 filter_type=(FilterTypes) option;
873 window_type=BoxFilter;
874 }
875 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000876 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000877 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000878 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000879 }
nicolas07bac812010-09-19 18:47:02 +0000880 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000881 artifact=GetImageArtifact(image,"filter:window");
882 if (artifact != (const char *) NULL)
883 {
cristy9af9b5d2010-08-15 17:04:28 +0000884 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000885 if ((UndefinedFilter < option) && (option < SentinelFilter))
886 {
887 if (option != LanczosFilter)
888 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000889 else
anthony48f77622010-10-03 14:32:31 +0000890 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000891 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000892 }
cristy33b1c162010-01-23 22:51:51 +0000893 }
cristy3ed852e2009-09-05 21:47:34 +0000894 }
cristy33b1c162010-01-23 22:51:51 +0000895 else
896 {
anthony48f77622010-10-03 14:32:31 +0000897 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000898 artifact=GetImageArtifact(image,"filter:window");
899 if (artifact != (const char *) NULL)
900 {
901 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
902 artifact);
903 if ((UndefinedFilter < option) && (option < SentinelFilter))
904 {
anthony61b5ddd2010-10-05 02:33:31 +0000905 filter_type=cylindrical != MagickFalse ?
906 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000907 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000908 }
909 }
910 }
nicolas07bac812010-09-19 18:47:02 +0000911 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000912 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000913 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000914 resize_filter->window=filters[window_type].function;
915 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000916 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000917
anthonyf5e76ef2010-10-12 01:22:01 +0000918 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000919 if (cylindrical != MagickFalse)
920 switch (filter_type)
921 {
922 case PointFilter:
923 case BoxFilter:
924 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000925 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000926 break;
anthony81b8bf92010-10-02 13:54:34 +0000927 default:
928 break;
anthony10b8bc82010-10-02 12:48:46 +0000929 }
anthony61b5ddd2010-10-05 02:33:31 +0000930 else
931 switch (filter_type)
932 {
933 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000934 case Lanczos2DSharpFilter:
anthony853d6972010-10-08 06:01:31 +0000935 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000936 resize_filter->filter=SincFast;
937 break;
938 default:
939 break;
940 }
941
anthonyf5e76ef2010-10-12 01:22:01 +0000942 /*
943 ** More Expert Option Modifications
944 */
945
946 /* User Sigma Override - no support change */
947 artifact=GetImageArtifact(image,"filter:sigma");
948 if (artifact != (const char *) NULL)
949 sigma=StringToDouble(artifact);
950 /* Define coefficents for Gaussian (assumes no cubic window) */
951 if ( GaussianFilter ) {
952 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
cristy68f103e2010-10-14 01:19:07 +0000953 resize_filter->coeff[1] = (MagickRealType) (1.0/(Magick2PI*sigma*sigma)); /* unused */
anthonyf5e76ef2010-10-12 01:22:01 +0000954 }
955
956 /* Blur Override */
957 artifact=GetImageArtifact(image,"filter:blur");
958 if (artifact != (const char *) NULL)
959 resize_filter->blur=StringToDouble(artifact);
960 if (resize_filter->blur < MagickEpsilon)
961 resize_filter->blur=(MagickRealType) MagickEpsilon;
962
963 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000964 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000965 if (artifact != (const char *) NULL)
966 {
cristybb503372010-05-27 20:51:26 +0000967 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000968 lobes;
969
cristy96b16132010-08-29 17:19:52 +0000970 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000971 if (lobes < 1)
972 lobes=1;
973 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000974 }
anthony61b5ddd2010-10-05 02:33:31 +0000975 /* convert Jinc lobes to a real support value */
976 if (resize_filter->filter == Jinc)
977 {
978 if (resize_filter->support > 16)
979 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
980 else
981 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
982 }
983 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000984 artifact=GetImageArtifact(image,"filter:support");
985 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000986 resize_filter->support=fabs(StringToDouble(artifact));
987 /*
nicolas07bac812010-09-19 18:47:02 +0000988 Scale windowing function separatally to the support 'clipping'
989 window that calling operator is planning to actually use. (Expert
990 override)
cristy3ed852e2009-09-05 21:47:34 +0000991 */
anthony55f12332010-09-10 01:13:02 +0000992 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000993 artifact=GetImageArtifact(image,"filter:win-support");
994 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000995 resize_filter->window_support=fabs(StringToDouble(artifact));
996 /*
anthony1f90a6b2010-09-14 08:56:31 +0000997 Adjust window function scaling to the windowing support for
998 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000999 */
1000 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +00001001
anthony55f12332010-09-10 01:13:02 +00001002 /*
anthonyf5e76ef2010-10-12 01:22:01 +00001003 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +00001004 */
cristy3ed852e2009-09-05 21:47:34 +00001005 B=0.0;
1006 C=0.0;
cristy33b1c162010-01-23 22:51:51 +00001007 if ((filters[filter_type].function == CubicBC) ||
1008 (filters[window_type].function == CubicBC))
1009 {
anthony2d9b8b52010-09-14 08:31:07 +00001010 B=filters[filter_type].B;
1011 C=filters[filter_type].C;
1012 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +00001013 {
anthony2d9b8b52010-09-14 08:31:07 +00001014 B=filters[window_type].B;
1015 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +00001016 }
cristy33b1c162010-01-23 22:51:51 +00001017 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001018 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001019 {
1020 B=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001021 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001022 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001023 if (artifact != (const char *) NULL)
1024 C=StringToDouble(artifact);
1025 }
1026 else
1027 {
1028 artifact=GetImageArtifact(image,"filter:c");
1029 if (artifact != (const char *) NULL)
1030 {
1031 C=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001032 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001033 }
1034 }
anthonyf5e76ef2010-10-12 01:22:01 +00001035 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
1036 resize_filter->coeff[0]=(6.0-2.0*B)/6.0;
1037 resize_filter->coeff[1]=0.0;
1038 resize_filter->coeff[2]=(-18.0+12.0*B+6.0*C)/6.0;
1039 resize_filter->coeff[3]=(12.0-9.0*B-6.0*C)/6.0;
1040 resize_filter->coeff[4]=(8.0*B+24.0*C)/6.0;
1041 resize_filter->coeff[5]=(-12.0*B-48.0*C)/6.0;
1042 resize_filter->coeff[6]=(6.0*B+30.0*C)/6.0;
1043 resize_filter->coeff[7]=(-B-6.0*C)/6.0;
cristy3ed852e2009-09-05 21:47:34 +00001044 }
anthonyf5e76ef2010-10-12 01:22:01 +00001045
anthony55f12332010-09-10 01:13:02 +00001046 /*
nicolas07bac812010-09-19 18:47:02 +00001047 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001048 */
cristyf5b49372010-10-16 01:06:47 +00001049#if defined(MAGICKCORE_OPENMP_SUPPORT)
1050 #pragma omp master
1051 {
1052#endif
1053 artifact=GetImageArtifact(image,"filter:verbose");
1054 if (artifact != (const char *) NULL)
1055 {
1056 double
anthony450db502010-10-19 04:03:03 +00001057 support,
cristyf5b49372010-10-16 01:06:47 +00001058 x;
cristy3ed852e2009-09-05 21:47:34 +00001059
cristyf5b49372010-10-16 01:06:47 +00001060 /*
1061 Set the weighting function properly when the weighting
1062 function may not exactly match the filter of the same name.
1063 EG: a Point filter really uses a Box weighting function
1064 with a different support than is typically used.
anthony463be1d2010-09-26 01:07:36 +00001065
cristyf5b49372010-10-16 01:06:47 +00001066 */
1067 if (resize_filter->filter == Box) filter_type=BoxFilter;
1068 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1069 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1070 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1071 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1072 /*
1073 Report Filter Details.
1074 */
1075 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1076 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
1077 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1078 MagickFilterOptions,filter_type));
1079 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
1080 MagickFilterOptions, window_type));
1081 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
1082 (double) resize_filter->support);
1083 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
1084 (double) resize_filter->window_support);
1085 (void) fprintf(stdout,"# scale_blur = %.*g\n",GetMagickPrecision(),
1086 (double) resize_filter->blur);
1087 if ( filter_type == GaussianFilter )
1088 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",GetMagickPrecision(),
1089 (double) sigma);
1090 (void) fprintf(stdout,"# practical_support = %.*g\n",GetMagickPrecision(),
1091 (double) support);
1092 if ( filter_type == CubicFilter || window_type == CubicFilter )
1093 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1094 (double) B,GetMagickPrecision(),(double) C);
1095 (void) fprintf(stdout,"\n");
1096 /*
1097 Output values of resulting filter graph -- for graphing
1098 filter result.
1099 */
1100 for (x=0.0; x <= support; x+=0.01f)
1101 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1102 (double) GetResizeFilterWeight(resize_filter,x));
1103 /* A final value so gnuplot can graph the 'stop' properly. */
1104 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1105 0.0);
1106 }
1107 /* Output the above once only for each image - remove setting */
1108 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1109#if defined(MAGICKCORE_OPENMP_SUPPORT)
1110 }
1111#endif
cristy3ed852e2009-09-05 21:47:34 +00001112 return(resize_filter);
1113}
1114
1115/*
1116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117% %
1118% %
1119% %
1120% A d a p t i v e R e s i z e I m a g e %
1121% %
1122% %
1123% %
1124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125%
1126% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1127%
1128% The format of the AdaptiveResizeImage method is:
1129%
cristy9af9b5d2010-08-15 17:04:28 +00001130% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1131% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001132%
1133% A description of each parameter follows:
1134%
1135% o image: the image.
1136%
1137% o columns: the number of columns in the resized image.
1138%
1139% o rows: the number of rows in the resized image.
1140%
1141% o exception: return any errors or warnings in this structure.
1142%
1143*/
1144MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001145 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001146{
1147#define AdaptiveResizeImageTag "Resize/Image"
1148
cristyc4c8d132010-01-07 01:58:38 +00001149 CacheView
1150 *resize_view;
1151
cristy3ed852e2009-09-05 21:47:34 +00001152 Image
1153 *resize_image;
1154
cristy3ed852e2009-09-05 21:47:34 +00001155 MagickBooleanType
1156 proceed;
1157
1158 MagickPixelPacket
1159 pixel;
1160
1161 PointInfo
1162 offset;
1163
1164 ResampleFilter
1165 *resample_filter;
1166
cristy9af9b5d2010-08-15 17:04:28 +00001167 ssize_t
1168 y;
1169
cristy3ed852e2009-09-05 21:47:34 +00001170 /*
1171 Adaptively resize image.
1172 */
1173 assert(image != (const Image *) NULL);
1174 assert(image->signature == MagickSignature);
1175 if (image->debug != MagickFalse)
1176 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1177 assert(exception != (ExceptionInfo *) NULL);
1178 assert(exception->signature == MagickSignature);
1179 if ((columns == 0) || (rows == 0))
1180 return((Image *) NULL);
1181 if ((columns == image->columns) && (rows == image->rows))
1182 return(CloneImage(image,0,0,MagickTrue,exception));
1183 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1184 if (resize_image == (Image *) NULL)
1185 return((Image *) NULL);
1186 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1187 {
1188 InheritException(exception,&resize_image->exception);
1189 resize_image=DestroyImage(resize_image);
1190 return((Image *) NULL);
1191 }
1192 GetMagickPixelPacket(image,&pixel);
1193 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001194 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001195 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001196 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001197 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001198 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001199 {
1200 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001201 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001202
cristybb503372010-05-27 20:51:26 +00001203 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001204 x;
1205
1206 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001207 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001208
1209 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1210 exception);
1211 if (q == (PixelPacket *) NULL)
1212 break;
1213 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1214 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001215 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001216 {
1217 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1218 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1219 &pixel);
1220 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1221 q++;
1222 }
1223 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1224 break;
cristy96b16132010-08-29 17:19:52 +00001225 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1226 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001227 if (proceed == MagickFalse)
1228 break;
1229 }
1230 resample_filter=DestroyResampleFilter(resample_filter);
1231 resize_view=DestroyCacheView(resize_view);
1232 return(resize_image);
1233}
1234
1235/*
1236%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1237% %
1238% %
1239% %
1240+ B e s s e l O r d e r O n e %
1241% %
1242% %
1243% %
1244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245%
1246% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001247% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001248%
1249% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1250%
1251% j1(x) = x*j1(x);
1252%
1253% For x in (8,inf)
1254%
1255% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1256%
1257% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1258%
1259% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1260% = 1/sqrt(2) * (sin(x) - cos(x))
1261% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1262% = -1/sqrt(2) * (sin(x) + cos(x))
1263%
1264% The format of the BesselOrderOne method is:
1265%
1266% MagickRealType BesselOrderOne(MagickRealType x)
1267%
1268% A description of each parameter follows:
1269%
1270% o x: MagickRealType value.
1271%
1272*/
1273
1274#undef I0
1275static MagickRealType I0(MagickRealType x)
1276{
1277 MagickRealType
1278 sum,
1279 t,
1280 y;
1281
cristybb503372010-05-27 20:51:26 +00001282 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001283 i;
1284
1285 /*
1286 Zeroth order Bessel function of the first kind.
1287 */
1288 sum=1.0;
1289 y=x*x/4.0;
1290 t=y;
1291 for (i=2; t > MagickEpsilon; i++)
1292 {
1293 sum+=t;
1294 t*=y/((MagickRealType) i*i);
1295 }
1296 return(sum);
1297}
1298
1299#undef J1
1300static MagickRealType J1(MagickRealType x)
1301{
1302 MagickRealType
1303 p,
1304 q;
1305
cristybb503372010-05-27 20:51:26 +00001306 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001307 i;
1308
1309 static const double
1310 Pone[] =
1311 {
1312 0.581199354001606143928050809e+21,
1313 -0.6672106568924916298020941484e+20,
1314 0.2316433580634002297931815435e+19,
1315 -0.3588817569910106050743641413e+17,
1316 0.2908795263834775409737601689e+15,
1317 -0.1322983480332126453125473247e+13,
1318 0.3413234182301700539091292655e+10,
1319 -0.4695753530642995859767162166e+7,
1320 0.270112271089232341485679099e+4
1321 },
1322 Qone[] =
1323 {
1324 0.11623987080032122878585294e+22,
1325 0.1185770712190320999837113348e+20,
1326 0.6092061398917521746105196863e+17,
1327 0.2081661221307607351240184229e+15,
1328 0.5243710262167649715406728642e+12,
1329 0.1013863514358673989967045588e+10,
1330 0.1501793594998585505921097578e+7,
1331 0.1606931573481487801970916749e+4,
1332 0.1e+1
1333 };
1334
1335 p=Pone[8];
1336 q=Qone[8];
1337 for (i=7; i >= 0; i--)
1338 {
1339 p=p*x*x+Pone[i];
1340 q=q*x*x+Qone[i];
1341 }
1342 return(p/q);
1343}
1344
1345#undef P1
1346static MagickRealType P1(MagickRealType x)
1347{
1348 MagickRealType
1349 p,
1350 q;
1351
cristybb503372010-05-27 20:51:26 +00001352 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001353 i;
1354
1355 static const double
1356 Pone[] =
1357 {
1358 0.352246649133679798341724373e+5,
1359 0.62758845247161281269005675e+5,
1360 0.313539631109159574238669888e+5,
1361 0.49854832060594338434500455e+4,
1362 0.2111529182853962382105718e+3,
1363 0.12571716929145341558495e+1
1364 },
1365 Qone[] =
1366 {
1367 0.352246649133679798068390431e+5,
1368 0.626943469593560511888833731e+5,
1369 0.312404063819041039923015703e+5,
1370 0.4930396490181088979386097e+4,
1371 0.2030775189134759322293574e+3,
1372 0.1e+1
1373 };
1374
1375 p=Pone[5];
1376 q=Qone[5];
1377 for (i=4; i >= 0; i--)
1378 {
1379 p=p*(8.0/x)*(8.0/x)+Pone[i];
1380 q=q*(8.0/x)*(8.0/x)+Qone[i];
1381 }
1382 return(p/q);
1383}
1384
1385#undef Q1
1386static MagickRealType Q1(MagickRealType x)
1387{
1388 MagickRealType
1389 p,
1390 q;
1391
cristybb503372010-05-27 20:51:26 +00001392 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001393 i;
1394
1395 static const double
1396 Pone[] =
1397 {
1398 0.3511751914303552822533318e+3,
1399 0.7210391804904475039280863e+3,
1400 0.4259873011654442389886993e+3,
1401 0.831898957673850827325226e+2,
1402 0.45681716295512267064405e+1,
1403 0.3532840052740123642735e-1
1404 },
1405 Qone[] =
1406 {
1407 0.74917374171809127714519505e+4,
1408 0.154141773392650970499848051e+5,
1409 0.91522317015169922705904727e+4,
1410 0.18111867005523513506724158e+4,
1411 0.1038187585462133728776636e+3,
1412 0.1e+1
1413 };
1414
1415 p=Pone[5];
1416 q=Qone[5];
1417 for (i=4; i >= 0; i--)
1418 {
1419 p=p*(8.0/x)*(8.0/x)+Pone[i];
1420 q=q*(8.0/x)*(8.0/x)+Qone[i];
1421 }
1422 return(p/q);
1423}
1424
1425static MagickRealType BesselOrderOne(MagickRealType x)
1426{
1427 MagickRealType
1428 p,
1429 q;
1430
1431 if (x == 0.0)
1432 return(0.0);
1433 p=x;
1434 if (x < 0.0)
1435 x=(-x);
1436 if (x < 8.0)
1437 return(p*J1(x));
1438 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1439 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1440 cos((double) x))));
1441 if (p < 0.0)
1442 q=(-q);
1443 return(q);
1444}
1445
1446/*
1447%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1448% %
1449% %
1450% %
1451+ D e s t r o y R e s i z e F i l t e r %
1452% %
1453% %
1454% %
1455%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1456%
1457% DestroyResizeFilter() destroy the resize filter.
1458%
cristya2ffd7e2010-03-10 20:50:30 +00001459% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001460%
1461% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1462%
1463% A description of each parameter follows:
1464%
1465% o resize_filter: the resize filter.
1466%
1467*/
1468MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1469{
1470 assert(resize_filter != (ResizeFilter *) NULL);
1471 assert(resize_filter->signature == MagickSignature);
1472 resize_filter->signature=(~MagickSignature);
1473 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1474 return(resize_filter);
1475}
1476
1477/*
1478%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1479% %
1480% %
1481% %
1482+ G e t R e s i z e F i l t e r S u p p o r t %
1483% %
1484% %
1485% %
1486%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1487%
1488% GetResizeFilterSupport() return the current support window size for this
1489% filter. Note that this may have been enlarged by filter:blur factor.
1490%
1491% The format of the GetResizeFilterSupport method is:
1492%
1493% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1494%
1495% A description of each parameter follows:
1496%
1497% o filter: Image filter to use.
1498%
1499*/
1500MagickExport MagickRealType GetResizeFilterSupport(
1501 const ResizeFilter *resize_filter)
1502{
1503 assert(resize_filter != (ResizeFilter *) NULL);
1504 assert(resize_filter->signature == MagickSignature);
1505 return(resize_filter->support*resize_filter->blur);
1506}
1507
1508/*
1509%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1510% %
1511% %
1512% %
1513+ G e t R e s i z e F i l t e r W e i g h t %
1514% %
1515% %
1516% %
1517%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1518%
1519% GetResizeFilterWeight evaluates the specified resize filter at the point x
1520% which usally lies between zero and the filters current 'support' and
1521% returns the weight of the filter function at that point.
1522%
1523% The format of the GetResizeFilterWeight method is:
1524%
1525% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1526% const MagickRealType x)
1527%
1528% A description of each parameter follows:
1529%
1530% o filter: the filter type.
1531%
1532% o x: the point.
1533%
1534*/
1535MagickExport MagickRealType GetResizeFilterWeight(
1536 const ResizeFilter *resize_filter,const MagickRealType x)
1537{
1538 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001539 scale,
1540 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001541
1542 /*
1543 Windowing function - scale the weighting filter by this amount.
1544 */
1545 assert(resize_filter != (ResizeFilter *) NULL);
1546 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001547 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001548 if ((resize_filter->window_support < MagickEpsilon) ||
1549 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001550 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001551 else
1552 {
anthony55f12332010-09-10 01:13:02 +00001553 scale=resize_filter->scale;
1554 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001555 }
anthony55f12332010-09-10 01:13:02 +00001556 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001557}
1558
1559/*
1560%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1561% %
1562% %
1563% %
1564% M a g n i f y I m a g e %
1565% %
1566% %
1567% %
1568%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1569%
1570% MagnifyImage() is a convenience method that scales an image proportionally
1571% to twice its size.
1572%
1573% The format of the MagnifyImage method is:
1574%
1575% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1576%
1577% A description of each parameter follows:
1578%
1579% o image: the image.
1580%
1581% o exception: return any errors or warnings in this structure.
1582%
1583*/
1584MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1585{
1586 Image
1587 *magnify_image;
1588
1589 assert(image != (Image *) NULL);
1590 assert(image->signature == MagickSignature);
1591 if (image->debug != MagickFalse)
1592 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1593 assert(exception != (ExceptionInfo *) NULL);
1594 assert(exception->signature == MagickSignature);
1595 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1596 1.0,exception);
1597 return(magnify_image);
1598}
1599
1600/*
1601%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1602% %
1603% %
1604% %
1605% M i n i f y I m a g e %
1606% %
1607% %
1608% %
1609%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1610%
1611% MinifyImage() is a convenience method that scales an image proportionally
1612% to half its size.
1613%
1614% The format of the MinifyImage method is:
1615%
1616% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1617%
1618% A description of each parameter follows:
1619%
1620% o image: the image.
1621%
1622% o exception: return any errors or warnings in this structure.
1623%
1624*/
1625MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1626{
1627 Image
1628 *minify_image;
1629
1630 assert(image != (Image *) NULL);
1631 assert(image->signature == MagickSignature);
1632 if (image->debug != MagickFalse)
1633 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1634 assert(exception != (ExceptionInfo *) NULL);
1635 assert(exception->signature == MagickSignature);
1636 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1637 1.0,exception);
1638 return(minify_image);
1639}
1640
1641/*
1642%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1643% %
1644% %
1645% %
1646% R e s a m p l e I m a g e %
1647% %
1648% %
1649% %
1650%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1651%
1652% ResampleImage() resize image in terms of its pixel size, so that when
1653% displayed at the given resolution it will be the same size in terms of
1654% real world units as the original image at the original resolution.
1655%
1656% The format of the ResampleImage method is:
1657%
1658% Image *ResampleImage(Image *image,const double x_resolution,
1659% const double y_resolution,const FilterTypes filter,const double blur,
1660% ExceptionInfo *exception)
1661%
1662% A description of each parameter follows:
1663%
1664% o image: the image to be resized to fit the given resolution.
1665%
1666% o x_resolution: the new image x resolution.
1667%
1668% o y_resolution: the new image y resolution.
1669%
1670% o filter: Image filter to use.
1671%
1672% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1673%
1674*/
1675MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1676 const double y_resolution,const FilterTypes filter,const double blur,
1677 ExceptionInfo *exception)
1678{
1679#define ResampleImageTag "Resample/Image"
1680
1681 Image
1682 *resample_image;
1683
cristybb503372010-05-27 20:51:26 +00001684 size_t
cristy3ed852e2009-09-05 21:47:34 +00001685 height,
1686 width;
1687
1688 /*
1689 Initialize sampled image attributes.
1690 */
1691 assert(image != (const Image *) NULL);
1692 assert(image->signature == MagickSignature);
1693 if (image->debug != MagickFalse)
1694 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1695 assert(exception != (ExceptionInfo *) NULL);
1696 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001697 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1698 72.0 : image->x_resolution)+0.5);
1699 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1700 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001701 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1702 if (resample_image != (Image *) NULL)
1703 {
1704 resample_image->x_resolution=x_resolution;
1705 resample_image->y_resolution=y_resolution;
1706 }
1707 return(resample_image);
1708}
1709#if defined(MAGICKCORE_LQR_DELEGATE)
1710
1711/*
1712%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1713% %
1714% %
1715% %
1716% L i q u i d R e s c a l e I m a g e %
1717% %
1718% %
1719% %
1720%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1721%
1722% LiquidRescaleImage() rescales image with seam carving.
1723%
1724% The format of the LiquidRescaleImage method is:
1725%
1726% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001727% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001728% const double delta_x,const double rigidity,ExceptionInfo *exception)
1729%
1730% A description of each parameter follows:
1731%
1732% o image: the image.
1733%
1734% o columns: the number of columns in the rescaled image.
1735%
1736% o rows: the number of rows in the rescaled image.
1737%
1738% o delta_x: maximum seam transversal step (0 means straight seams).
1739%
1740% o rigidity: introduce a bias for non-straight seams (typically 0).
1741%
1742% o exception: return any errors or warnings in this structure.
1743%
1744*/
cristy9af9b5d2010-08-15 17:04:28 +00001745MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1746 const size_t rows,const double delta_x,const double rigidity,
1747 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001748{
1749#define LiquidRescaleImageTag "Rescale/Image"
1750
cristyc5c6f662010-09-22 14:23:02 +00001751 CacheView
1752 *rescale_view;
1753
cristy3ed852e2009-09-05 21:47:34 +00001754 const char
1755 *map;
1756
1757 guchar
1758 *packet;
1759
1760 Image
1761 *rescale_image;
1762
1763 int
1764 x,
1765 y;
1766
1767 LqrCarver
1768 *carver;
1769
1770 LqrRetVal
1771 lqr_status;
1772
1773 MagickBooleanType
1774 status;
1775
1776 MagickPixelPacket
1777 pixel;
1778
1779 unsigned char
1780 *pixels;
1781
1782 /*
1783 Liquid rescale image.
1784 */
1785 assert(image != (const Image *) NULL);
1786 assert(image->signature == MagickSignature);
1787 if (image->debug != MagickFalse)
1788 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1789 assert(exception != (ExceptionInfo *) NULL);
1790 assert(exception->signature == MagickSignature);
1791 if ((columns == 0) || (rows == 0))
1792 return((Image *) NULL);
1793 if ((columns == image->columns) && (rows == image->rows))
1794 return(CloneImage(image,0,0,MagickTrue,exception));
1795 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001796 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001797 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1798 {
1799 Image
1800 *resize_image;
1801
cristybb503372010-05-27 20:51:26 +00001802 size_t
cristy3ed852e2009-09-05 21:47:34 +00001803 height,
1804 width;
1805
1806 /*
1807 Honor liquid resize size limitations.
1808 */
1809 for (width=image->columns; columns >= (2*width-1); width*=2);
1810 for (height=image->rows; rows >= (2*height-1); height*=2);
1811 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1812 exception);
1813 if (resize_image == (Image *) NULL)
1814 return((Image *) NULL);
1815 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1816 rigidity,exception);
1817 resize_image=DestroyImage(resize_image);
1818 return(rescale_image);
1819 }
1820 map="RGB";
1821 if (image->matte == MagickFalse)
1822 map="RGBA";
1823 if (image->colorspace == CMYKColorspace)
1824 {
1825 map="CMYK";
1826 if (image->matte == MagickFalse)
1827 map="CMYKA";
1828 }
1829 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1830 strlen(map)*sizeof(*pixels));
1831 if (pixels == (unsigned char *) NULL)
1832 return((Image *) NULL);
1833 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1834 pixels,exception);
1835 if (status == MagickFalse)
1836 {
1837 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1838 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1839 }
1840 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1841 if (carver == (LqrCarver *) NULL)
1842 {
1843 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1844 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1845 }
1846 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1847 lqr_status=lqr_carver_resize(carver,columns,rows);
1848 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1849 lqr_carver_get_height(carver),MagickTrue,exception);
1850 if (rescale_image == (Image *) NULL)
1851 {
1852 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1853 return((Image *) NULL);
1854 }
1855 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1856 {
1857 InheritException(exception,&rescale_image->exception);
1858 rescale_image=DestroyImage(rescale_image);
1859 return((Image *) NULL);
1860 }
1861 GetMagickPixelPacket(rescale_image,&pixel);
1862 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001863 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001864 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1865 {
1866 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001867 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001868
1869 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001870 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001871
anthony22aad252010-09-23 06:59:07 +00001872 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001873 if (q == (PixelPacket *) NULL)
1874 break;
cristyc5c6f662010-09-22 14:23:02 +00001875 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001876 pixel.red=QuantumRange*(packet[0]/255.0);
1877 pixel.green=QuantumRange*(packet[1]/255.0);
1878 pixel.blue=QuantumRange*(packet[2]/255.0);
1879 if (image->colorspace != CMYKColorspace)
1880 {
1881 if (image->matte == MagickFalse)
1882 pixel.opacity=QuantumRange*(packet[3]/255.0);
1883 }
1884 else
1885 {
1886 pixel.index=QuantumRange*(packet[3]/255.0);
1887 if (image->matte == MagickFalse)
1888 pixel.opacity=QuantumRange*(packet[4]/255.0);
1889 }
1890 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001891 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001892 break;
1893 }
cristyc5c6f662010-09-22 14:23:02 +00001894 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001895 /*
1896 Relinquish resources.
1897 */
1898 lqr_carver_destroy(carver);
1899 return(rescale_image);
1900}
1901#else
1902MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001903 const size_t magick_unused(columns),const size_t magick_unused(rows),
1904 const double magick_unused(delta_x),const double magick_unused(rigidity),
1905 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001906{
1907 assert(image != (const Image *) NULL);
1908 assert(image->signature == MagickSignature);
1909 if (image->debug != MagickFalse)
1910 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1911 assert(exception != (ExceptionInfo *) NULL);
1912 assert(exception->signature == MagickSignature);
1913 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1914 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1915 return((Image *) NULL);
1916}
1917#endif
1918
1919/*
1920%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1921% %
1922% %
1923% %
1924% R e s i z e I m a g e %
1925% %
1926% %
1927% %
1928%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1929%
1930% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001931% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001932%
1933% If an undefined filter is given the filter defaults to Mitchell for a
1934% colormapped image, a image with a matte channel, or if the image is
1935% enlarged. Otherwise the filter defaults to a Lanczos.
1936%
1937% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1938%
1939% The format of the ResizeImage method is:
1940%
cristybb503372010-05-27 20:51:26 +00001941% Image *ResizeImage(Image *image,const size_t columns,
1942% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001943% ExceptionInfo *exception)
1944%
1945% A description of each parameter follows:
1946%
1947% o image: the image.
1948%
1949% o columns: the number of columns in the scaled image.
1950%
1951% o rows: the number of rows in the scaled image.
1952%
1953% o filter: Image filter to use.
1954%
cristy9af9b5d2010-08-15 17:04:28 +00001955% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1956% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001957%
1958% o exception: return any errors or warnings in this structure.
1959%
1960*/
1961
1962typedef struct _ContributionInfo
1963{
1964 MagickRealType
1965 weight;
1966
cristybb503372010-05-27 20:51:26 +00001967 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001968 pixel;
1969} ContributionInfo;
1970
1971static ContributionInfo **DestroyContributionThreadSet(
1972 ContributionInfo **contribution)
1973{
cristybb503372010-05-27 20:51:26 +00001974 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001975 i;
1976
1977 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001978 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001979 if (contribution[i] != (ContributionInfo *) NULL)
1980 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1981 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001982 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001983 return(contribution);
1984}
1985
1986static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1987{
cristybb503372010-05-27 20:51:26 +00001988 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001989 i;
1990
1991 ContributionInfo
1992 **contribution;
1993
cristybb503372010-05-27 20:51:26 +00001994 size_t
cristy3ed852e2009-09-05 21:47:34 +00001995 number_threads;
1996
1997 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001998 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001999 sizeof(*contribution));
2000 if (contribution == (ContributionInfo **) NULL)
2001 return((ContributionInfo **) NULL);
2002 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00002003 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002004 {
2005 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2006 sizeof(**contribution));
2007 if (contribution[i] == (ContributionInfo *) NULL)
2008 return(DestroyContributionThreadSet(contribution));
2009 }
2010 return(contribution);
2011}
2012
2013static inline double MagickMax(const double x,const double y)
2014{
2015 if (x > y)
2016 return(x);
2017 return(y);
2018}
2019
2020static inline double MagickMin(const double x,const double y)
2021{
2022 if (x < y)
2023 return(x);
2024 return(y);
2025}
2026
2027static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2028 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002029 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002030{
2031#define ResizeImageTag "Resize/Image"
2032
cristyfa112112010-01-04 17:48:07 +00002033 CacheView
2034 *image_view,
2035 *resize_view;
2036
cristy3ed852e2009-09-05 21:47:34 +00002037 ClassType
2038 storage_class;
2039
2040 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002041 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002042
cristy3ed852e2009-09-05 21:47:34 +00002043 MagickBooleanType
2044 status;
2045
2046 MagickPixelPacket
2047 zero;
2048
2049 MagickRealType
2050 scale,
2051 support;
2052
cristy9af9b5d2010-08-15 17:04:28 +00002053 ssize_t
2054 x;
2055
cristy3ed852e2009-09-05 21:47:34 +00002056 /*
2057 Apply filter to resize horizontally from image to resize image.
2058 */
cristy5d824382010-09-06 14:00:17 +00002059 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002060 support=scale*GetResizeFilterSupport(resize_filter);
2061 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2062 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2063 {
2064 InheritException(exception,&resize_image->exception);
2065 return(MagickFalse);
2066 }
2067 if (support < 0.5)
2068 {
2069 /*
nicolas07bac812010-09-19 18:47:02 +00002070 Support too small even for nearest neighbour: Reduce to point
2071 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002072 */
2073 support=(MagickRealType) 0.5;
2074 scale=1.0;
2075 }
2076 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2077 if (contributions == (ContributionInfo **) NULL)
2078 {
2079 (void) ThrowMagickException(exception,GetMagickModule(),
2080 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2081 return(MagickFalse);
2082 }
2083 status=MagickTrue;
2084 scale=1.0/scale;
2085 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2086 image_view=AcquireCacheView(image);
2087 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002088#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002089 #pragma omp parallel for shared(status)
2090#endif
cristybb503372010-05-27 20:51:26 +00002091 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002092 {
cristy3ed852e2009-09-05 21:47:34 +00002093 MagickRealType
2094 center,
2095 density;
2096
2097 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002098 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002099
2100 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002101 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002102
cristy03dbbd22010-09-19 23:04:47 +00002103 register ContributionInfo
2104 *restrict contribution;
2105
cristy3ed852e2009-09-05 21:47:34 +00002106 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002107 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002108
cristy3ed852e2009-09-05 21:47:34 +00002109 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002110 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002111
cristy03dbbd22010-09-19 23:04:47 +00002112 register ssize_t
2113 y;
2114
cristy9af9b5d2010-08-15 17:04:28 +00002115 ssize_t
2116 n,
2117 start,
2118 stop;
2119
cristy3ed852e2009-09-05 21:47:34 +00002120 if (status == MagickFalse)
2121 continue;
2122 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002123 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2124 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002125 density=0.0;
2126 contribution=contributions[GetOpenMPThreadId()];
2127 for (n=0; n < (stop-start); n++)
2128 {
2129 contribution[n].pixel=start+n;
2130 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2131 ((MagickRealType) (start+n)-center+0.5));
2132 density+=contribution[n].weight;
2133 }
2134 if ((density != 0.0) && (density != 1.0))
2135 {
cristybb503372010-05-27 20:51:26 +00002136 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002137 i;
2138
2139 /*
2140 Normalize.
2141 */
2142 density=1.0/density;
2143 for (i=0; i < n; i++)
2144 contribution[i].weight*=density;
2145 }
cristy9af9b5d2010-08-15 17:04:28 +00002146 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2147 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002148 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2149 exception);
2150 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2151 {
2152 status=MagickFalse;
2153 continue;
2154 }
2155 indexes=GetCacheViewVirtualIndexQueue(image_view);
2156 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002157 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002158 {
cristy3ed852e2009-09-05 21:47:34 +00002159 MagickPixelPacket
2160 pixel;
2161
2162 MagickRealType
2163 alpha;
2164
cristybb503372010-05-27 20:51:26 +00002165 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002166 i;
2167
cristy9af9b5d2010-08-15 17:04:28 +00002168 ssize_t
2169 j;
2170
cristy3ed852e2009-09-05 21:47:34 +00002171 pixel=zero;
2172 if (image->matte == MagickFalse)
2173 {
2174 for (i=0; i < n; i++)
2175 {
2176 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2177 (contribution[i].pixel-contribution[0].pixel);
2178 alpha=contribution[i].weight;
2179 pixel.red+=alpha*(p+j)->red;
2180 pixel.green+=alpha*(p+j)->green;
2181 pixel.blue+=alpha*(p+j)->blue;
2182 pixel.opacity+=alpha*(p+j)->opacity;
2183 }
cristyce70c172010-01-07 17:15:30 +00002184 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2185 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2186 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2187 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002188 if ((image->colorspace == CMYKColorspace) &&
2189 (resize_image->colorspace == CMYKColorspace))
2190 {
2191 for (i=0; i < n; i++)
2192 {
2193 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2194 (contribution[i].pixel-contribution[0].pixel);
2195 alpha=contribution[i].weight;
2196 pixel.index+=alpha*indexes[j];
2197 }
cristyce70c172010-01-07 17:15:30 +00002198 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002199 }
2200 }
2201 else
2202 {
2203 MagickRealType
2204 gamma;
2205
2206 gamma=0.0;
2207 for (i=0; i < n; i++)
2208 {
2209 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2210 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002211 alpha=contribution[i].weight*QuantumScale*
2212 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002213 pixel.red+=alpha*(p+j)->red;
2214 pixel.green+=alpha*(p+j)->green;
2215 pixel.blue+=alpha*(p+j)->blue;
2216 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2217 gamma+=alpha;
2218 }
2219 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002220 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2221 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2222 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2223 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002224 if ((image->colorspace == CMYKColorspace) &&
2225 (resize_image->colorspace == CMYKColorspace))
2226 {
2227 for (i=0; i < n; i++)
2228 {
2229 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2230 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002231 alpha=contribution[i].weight*QuantumScale*
2232 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002233 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002234 }
cristyce70c172010-01-07 17:15:30 +00002235 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2236 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002237 }
2238 }
2239 if ((resize_image->storage_class == PseudoClass) &&
2240 (image->storage_class == PseudoClass))
2241 {
cristybb503372010-05-27 20:51:26 +00002242 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002243 1.0)+0.5);
2244 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2245 (contribution[i-start].pixel-contribution[0].pixel);
2246 resize_indexes[y]=indexes[j];
2247 }
2248 q++;
2249 }
2250 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2251 status=MagickFalse;
2252 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2253 {
2254 MagickBooleanType
2255 proceed;
2256
cristyb5d5f722009-11-04 03:03:49 +00002257#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002258 #pragma omp critical (MagickCore_HorizontalFilter)
2259#endif
cristy9af9b5d2010-08-15 17:04:28 +00002260 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002261 if (proceed == MagickFalse)
2262 status=MagickFalse;
2263 }
2264 }
2265 resize_view=DestroyCacheView(resize_view);
2266 image_view=DestroyCacheView(image_view);
2267 contributions=DestroyContributionThreadSet(contributions);
2268 return(status);
2269}
2270
2271static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2272 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002273 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002274{
cristyfa112112010-01-04 17:48:07 +00002275 CacheView
2276 *image_view,
2277 *resize_view;
2278
cristy3ed852e2009-09-05 21:47:34 +00002279 ClassType
2280 storage_class;
2281
2282 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002283 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002284
cristy3ed852e2009-09-05 21:47:34 +00002285 MagickBooleanType
2286 status;
2287
2288 MagickPixelPacket
2289 zero;
2290
2291 MagickRealType
2292 scale,
2293 support;
2294
cristy9af9b5d2010-08-15 17:04:28 +00002295 ssize_t
2296 y;
2297
cristy3ed852e2009-09-05 21:47:34 +00002298 /*
cristy9af9b5d2010-08-15 17:04:28 +00002299 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002300 */
cristy5d824382010-09-06 14:00:17 +00002301 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002302 support=scale*GetResizeFilterSupport(resize_filter);
2303 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2304 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2305 {
2306 InheritException(exception,&resize_image->exception);
2307 return(MagickFalse);
2308 }
2309 if (support < 0.5)
2310 {
2311 /*
nicolas07bac812010-09-19 18:47:02 +00002312 Support too small even for nearest neighbour: Reduce to point
2313 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002314 */
2315 support=(MagickRealType) 0.5;
2316 scale=1.0;
2317 }
2318 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2319 if (contributions == (ContributionInfo **) NULL)
2320 {
2321 (void) ThrowMagickException(exception,GetMagickModule(),
2322 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2323 return(MagickFalse);
2324 }
2325 status=MagickTrue;
2326 scale=1.0/scale;
2327 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2328 image_view=AcquireCacheView(image);
2329 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002330#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002331 #pragma omp parallel for shared(status)
2332#endif
cristybb503372010-05-27 20:51:26 +00002333 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002334 {
cristy3ed852e2009-09-05 21:47:34 +00002335 MagickRealType
2336 center,
2337 density;
2338
2339 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002340 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002341
2342 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002343 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002344
cristy03dbbd22010-09-19 23:04:47 +00002345 register ContributionInfo
2346 *restrict contribution;
2347
cristy3ed852e2009-09-05 21:47:34 +00002348 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002349 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002350
cristy9af9b5d2010-08-15 17:04:28 +00002351 register PixelPacket
2352 *restrict q;
2353
cristybb503372010-05-27 20:51:26 +00002354 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002355 x;
2356
cristy9af9b5d2010-08-15 17:04:28 +00002357 ssize_t
2358 n,
2359 start,
2360 stop;
cristy3ed852e2009-09-05 21:47:34 +00002361
2362 if (status == MagickFalse)
2363 continue;
cristy679e6962010-03-18 00:42:45 +00002364 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002365 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2366 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002367 density=0.0;
2368 contribution=contributions[GetOpenMPThreadId()];
2369 for (n=0; n < (stop-start); n++)
2370 {
2371 contribution[n].pixel=start+n;
2372 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2373 ((MagickRealType) (start+n)-center+0.5));
2374 density+=contribution[n].weight;
2375 }
2376 if ((density != 0.0) && (density != 1.0))
2377 {
cristybb503372010-05-27 20:51:26 +00002378 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002379 i;
2380
2381 /*
2382 Normalize.
2383 */
2384 density=1.0/density;
2385 for (i=0; i < n; i++)
2386 contribution[i].weight*=density;
2387 }
2388 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002389 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2390 exception);
cristy3ed852e2009-09-05 21:47:34 +00002391 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2392 exception);
2393 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2394 {
2395 status=MagickFalse;
2396 continue;
2397 }
2398 indexes=GetCacheViewVirtualIndexQueue(image_view);
2399 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002400 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002401 {
cristy3ed852e2009-09-05 21:47:34 +00002402 MagickPixelPacket
2403 pixel;
2404
2405 MagickRealType
2406 alpha;
2407
cristybb503372010-05-27 20:51:26 +00002408 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002409 i;
2410
cristy9af9b5d2010-08-15 17:04:28 +00002411 ssize_t
2412 j;
2413
cristy3ed852e2009-09-05 21:47:34 +00002414 pixel=zero;
2415 if (image->matte == MagickFalse)
2416 {
2417 for (i=0; i < n; i++)
2418 {
cristybb503372010-05-27 20:51:26 +00002419 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002420 image->columns+x);
2421 alpha=contribution[i].weight;
2422 pixel.red+=alpha*(p+j)->red;
2423 pixel.green+=alpha*(p+j)->green;
2424 pixel.blue+=alpha*(p+j)->blue;
2425 pixel.opacity+=alpha*(p+j)->opacity;
2426 }
cristyce70c172010-01-07 17:15:30 +00002427 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2428 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2429 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2430 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002431 if ((image->colorspace == CMYKColorspace) &&
2432 (resize_image->colorspace == CMYKColorspace))
2433 {
2434 for (i=0; i < n; i++)
2435 {
cristybb503372010-05-27 20:51:26 +00002436 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002437 image->columns+x);
2438 alpha=contribution[i].weight;
2439 pixel.index+=alpha*indexes[j];
2440 }
cristyce70c172010-01-07 17:15:30 +00002441 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002442 }
2443 }
2444 else
2445 {
2446 MagickRealType
2447 gamma;
2448
2449 gamma=0.0;
2450 for (i=0; i < n; i++)
2451 {
cristybb503372010-05-27 20:51:26 +00002452 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002453 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002454 alpha=contribution[i].weight*QuantumScale*
2455 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002456 pixel.red+=alpha*(p+j)->red;
2457 pixel.green+=alpha*(p+j)->green;
2458 pixel.blue+=alpha*(p+j)->blue;
2459 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2460 gamma+=alpha;
2461 }
2462 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002463 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2464 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2465 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2466 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002467 if ((image->colorspace == CMYKColorspace) &&
2468 (resize_image->colorspace == CMYKColorspace))
2469 {
2470 for (i=0; i < n; i++)
2471 {
cristybb503372010-05-27 20:51:26 +00002472 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002473 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002474 alpha=contribution[i].weight*QuantumScale*
2475 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002476 pixel.index+=alpha*indexes[j];
2477 }
cristyce70c172010-01-07 17:15:30 +00002478 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2479 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002480 }
2481 }
2482 if ((resize_image->storage_class == PseudoClass) &&
2483 (image->storage_class == PseudoClass))
2484 {
cristybb503372010-05-27 20:51:26 +00002485 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002486 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002487 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002488 image->columns+x);
2489 resize_indexes[x]=indexes[j];
2490 }
2491 q++;
2492 }
2493 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2494 status=MagickFalse;
2495 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2496 {
2497 MagickBooleanType
2498 proceed;
2499
cristyb5d5f722009-11-04 03:03:49 +00002500#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002501 #pragma omp critical (MagickCore_VerticalFilter)
2502#endif
cristy9af9b5d2010-08-15 17:04:28 +00002503 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002504 if (proceed == MagickFalse)
2505 status=MagickFalse;
2506 }
2507 }
2508 resize_view=DestroyCacheView(resize_view);
2509 image_view=DestroyCacheView(image_view);
2510 contributions=DestroyContributionThreadSet(contributions);
2511 return(status);
2512}
2513
cristybb503372010-05-27 20:51:26 +00002514MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2515 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002516 ExceptionInfo *exception)
2517{
2518#define WorkLoadFactor 0.265
2519
2520 FilterTypes
2521 filter_type;
2522
2523 Image
2524 *filter_image,
2525 *resize_image;
2526
cristy9af9b5d2010-08-15 17:04:28 +00002527 MagickOffsetType
2528 offset;
2529
cristy3ed852e2009-09-05 21:47:34 +00002530 MagickRealType
2531 x_factor,
2532 y_factor;
2533
2534 MagickSizeType
2535 span;
2536
2537 MagickStatusType
2538 status;
2539
2540 ResizeFilter
2541 *resize_filter;
2542
cristy3ed852e2009-09-05 21:47:34 +00002543 /*
2544 Acquire resize image.
2545 */
2546 assert(image != (Image *) NULL);
2547 assert(image->signature == MagickSignature);
2548 if (image->debug != MagickFalse)
2549 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2550 assert(exception != (ExceptionInfo *) NULL);
2551 assert(exception->signature == MagickSignature);
2552 if ((columns == 0) || (rows == 0))
2553 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2554 if ((columns == image->columns) && (rows == image->rows) &&
2555 (filter == UndefinedFilter) && (blur == 1.0))
2556 return(CloneImage(image,0,0,MagickTrue,exception));
2557 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2558 if (resize_image == (Image *) NULL)
2559 return(resize_image);
2560 /*
2561 Acquire resize filter.
2562 */
2563 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2564 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2565 if ((x_factor*y_factor) > WorkLoadFactor)
2566 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2567 else
2568 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2569 if (filter_image == (Image *) NULL)
2570 return(DestroyImage(resize_image));
2571 filter_type=LanczosFilter;
2572 if (filter != UndefinedFilter)
2573 filter_type=filter;
2574 else
2575 if ((x_factor == 1.0) && (y_factor == 1.0))
2576 filter_type=PointFilter;
2577 else
2578 if ((image->storage_class == PseudoClass) ||
2579 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2580 filter_type=MitchellFilter;
2581 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2582 exception);
2583 /*
2584 Resize image.
2585 */
cristy9af9b5d2010-08-15 17:04:28 +00002586 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002587 if ((x_factor*y_factor) > WorkLoadFactor)
2588 {
2589 span=(MagickSizeType) (filter_image->columns+rows);
2590 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002591 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002592 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002593 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002594 }
2595 else
2596 {
2597 span=(MagickSizeType) (filter_image->rows+columns);
2598 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002599 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002600 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002601 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002602 }
2603 /*
2604 Free resources.
2605 */
2606 filter_image=DestroyImage(filter_image);
2607 resize_filter=DestroyResizeFilter(resize_filter);
2608 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2609 return((Image *) NULL);
2610 resize_image->type=image->type;
2611 return(resize_image);
2612}
2613
2614/*
2615%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2616% %
2617% %
2618% %
2619% S a m p l e I m a g e %
2620% %
2621% %
2622% %
2623%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2624%
2625% SampleImage() scales an image to the desired dimensions with pixel
2626% sampling. Unlike other scaling methods, this method does not introduce
2627% any additional color into the scaled image.
2628%
2629% The format of the SampleImage method is:
2630%
cristybb503372010-05-27 20:51:26 +00002631% Image *SampleImage(const Image *image,const size_t columns,
2632% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002633%
2634% A description of each parameter follows:
2635%
2636% o image: the image.
2637%
2638% o columns: the number of columns in the sampled image.
2639%
2640% o rows: the number of rows in the sampled image.
2641%
2642% o exception: return any errors or warnings in this structure.
2643%
2644*/
cristybb503372010-05-27 20:51:26 +00002645MagickExport Image *SampleImage(const Image *image,const size_t columns,
2646 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002647{
2648#define SampleImageTag "Sample/Image"
2649
cristyc4c8d132010-01-07 01:58:38 +00002650 CacheView
2651 *image_view,
2652 *sample_view;
2653
cristy3ed852e2009-09-05 21:47:34 +00002654 Image
2655 *sample_image;
2656
cristy3ed852e2009-09-05 21:47:34 +00002657 MagickBooleanType
2658 status;
2659
cristy5f959472010-05-27 22:19:46 +00002660 MagickOffsetType
2661 progress;
2662
cristybb503372010-05-27 20:51:26 +00002663 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002664 x;
2665
cristy5f959472010-05-27 22:19:46 +00002666 ssize_t
2667 *x_offset,
2668 y;
2669
cristy3ed852e2009-09-05 21:47:34 +00002670 /*
2671 Initialize sampled image attributes.
2672 */
2673 assert(image != (const Image *) NULL);
2674 assert(image->signature == MagickSignature);
2675 if (image->debug != MagickFalse)
2676 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2677 assert(exception != (ExceptionInfo *) NULL);
2678 assert(exception->signature == MagickSignature);
2679 if ((columns == 0) || (rows == 0))
2680 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2681 if ((columns == image->columns) && (rows == image->rows))
2682 return(CloneImage(image,0,0,MagickTrue,exception));
2683 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2684 if (sample_image == (Image *) NULL)
2685 return((Image *) NULL);
2686 /*
2687 Allocate scan line buffer and column offset buffers.
2688 */
cristybb503372010-05-27 20:51:26 +00002689 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002690 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002691 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002692 {
2693 sample_image=DestroyImage(sample_image);
2694 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2695 }
cristybb503372010-05-27 20:51:26 +00002696 for (x=0; x < (ssize_t) sample_image->columns; x++)
2697 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002698 sample_image->columns);
2699 /*
2700 Sample each row.
2701 */
2702 status=MagickTrue;
2703 progress=0;
2704 image_view=AcquireCacheView(image);
2705 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002706#if defined(MAGICKCORE_OPENMP_SUPPORT)
2707 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002708#endif
cristybb503372010-05-27 20:51:26 +00002709 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002710 {
cristy3ed852e2009-09-05 21:47:34 +00002711 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002712 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002713
2714 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002715 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002716
2717 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002718 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002719
cristy3ed852e2009-09-05 21:47:34 +00002720 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002721 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002722
cristy03dbbd22010-09-19 23:04:47 +00002723 register ssize_t
2724 x;
2725
cristy9af9b5d2010-08-15 17:04:28 +00002726 ssize_t
2727 y_offset;
2728
cristy3ed852e2009-09-05 21:47:34 +00002729 if (status == MagickFalse)
2730 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002731 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2732 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002733 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2734 exception);
2735 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2736 exception);
2737 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2738 {
2739 status=MagickFalse;
2740 continue;
2741 }
2742 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2743 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2744 /*
2745 Sample each column.
2746 */
cristybb503372010-05-27 20:51:26 +00002747 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002748 *q++=p[x_offset[x]];
2749 if ((image->storage_class == PseudoClass) ||
2750 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002751 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002752 sample_indexes[x]=indexes[x_offset[x]];
2753 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2754 status=MagickFalse;
2755 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2756 {
2757 MagickBooleanType
2758 proceed;
2759
cristyb5d5f722009-11-04 03:03:49 +00002760#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002761 #pragma omp critical (MagickCore_SampleImage)
2762#endif
2763 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2764 if (proceed == MagickFalse)
2765 status=MagickFalse;
2766 }
2767 }
2768 image_view=DestroyCacheView(image_view);
2769 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002770 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002771 sample_image->type=image->type;
2772 return(sample_image);
2773}
2774
2775/*
2776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2777% %
2778% %
2779% %
2780% S c a l e I m a g e %
2781% %
2782% %
2783% %
2784%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2785%
2786% ScaleImage() changes the size of an image to the given dimensions.
2787%
2788% The format of the ScaleImage method is:
2789%
cristybb503372010-05-27 20:51:26 +00002790% Image *ScaleImage(const Image *image,const size_t columns,
2791% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002792%
2793% A description of each parameter follows:
2794%
2795% o image: the image.
2796%
2797% o columns: the number of columns in the scaled image.
2798%
2799% o rows: the number of rows in the scaled image.
2800%
2801% o exception: return any errors or warnings in this structure.
2802%
2803*/
cristybb503372010-05-27 20:51:26 +00002804MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2805 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002806{
2807#define ScaleImageTag "Scale/Image"
2808
cristyed6cb232010-01-20 03:07:53 +00002809 CacheView
2810 *image_view,
2811 *scale_view;
2812
cristy3ed852e2009-09-05 21:47:34 +00002813 Image
2814 *scale_image;
2815
cristy3ed852e2009-09-05 21:47:34 +00002816 MagickBooleanType
2817 next_column,
2818 next_row,
2819 proceed;
2820
2821 MagickPixelPacket
2822 pixel,
2823 *scale_scanline,
2824 *scanline,
2825 *x_vector,
2826 *y_vector,
2827 zero;
2828
cristy3ed852e2009-09-05 21:47:34 +00002829 PointInfo
2830 scale,
2831 span;
2832
cristybb503372010-05-27 20:51:26 +00002833 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002834 i;
2835
cristy9af9b5d2010-08-15 17:04:28 +00002836 ssize_t
2837 number_rows,
2838 y;
2839
cristy3ed852e2009-09-05 21:47:34 +00002840 /*
2841 Initialize scaled image attributes.
2842 */
2843 assert(image != (const Image *) NULL);
2844 assert(image->signature == MagickSignature);
2845 if (image->debug != MagickFalse)
2846 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2847 assert(exception != (ExceptionInfo *) NULL);
2848 assert(exception->signature == MagickSignature);
2849 if ((columns == 0) || (rows == 0))
2850 return((Image *) NULL);
2851 if ((columns == image->columns) && (rows == image->rows))
2852 return(CloneImage(image,0,0,MagickTrue,exception));
2853 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2854 if (scale_image == (Image *) NULL)
2855 return((Image *) NULL);
2856 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2857 {
2858 InheritException(exception,&scale_image->exception);
2859 scale_image=DestroyImage(scale_image);
2860 return((Image *) NULL);
2861 }
2862 /*
2863 Allocate memory.
2864 */
2865 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2866 sizeof(*x_vector));
2867 scanline=x_vector;
2868 if (image->rows != scale_image->rows)
2869 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2870 sizeof(*scanline));
2871 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2872 scale_image->columns,sizeof(*scale_scanline));
2873 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2874 sizeof(*y_vector));
2875 if ((scanline == (MagickPixelPacket *) NULL) ||
2876 (scale_scanline == (MagickPixelPacket *) NULL) ||
2877 (x_vector == (MagickPixelPacket *) NULL) ||
2878 (y_vector == (MagickPixelPacket *) NULL))
2879 {
2880 scale_image=DestroyImage(scale_image);
2881 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2882 }
2883 /*
2884 Scale image.
2885 */
2886 number_rows=0;
2887 next_row=MagickTrue;
2888 span.y=1.0;
2889 scale.y=(double) scale_image->rows/(double) image->rows;
2890 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2891 sizeof(*y_vector));
2892 GetMagickPixelPacket(image,&pixel);
2893 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2894 i=0;
cristyed6cb232010-01-20 03:07:53 +00002895 image_view=AcquireCacheView(image);
2896 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002897 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002898 {
2899 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002900 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002901
2902 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002903 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002904
2905 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002906 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002907
cristy3ed852e2009-09-05 21:47:34 +00002908 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002909 *restrict s,
2910 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002911
2912 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002913 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002914
cristy9af9b5d2010-08-15 17:04:28 +00002915 register ssize_t
2916 x;
2917
cristyed6cb232010-01-20 03:07:53 +00002918 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2919 exception);
cristy3ed852e2009-09-05 21:47:34 +00002920 if (q == (PixelPacket *) NULL)
2921 break;
2922 scale_indexes=GetAuthenticIndexQueue(scale_image);
2923 if (scale_image->rows == image->rows)
2924 {
2925 /*
2926 Read a new scanline.
2927 */
cristyed6cb232010-01-20 03:07:53 +00002928 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2929 exception);
cristy3ed852e2009-09-05 21:47:34 +00002930 if (p == (const PixelPacket *) NULL)
2931 break;
cristyed6cb232010-01-20 03:07:53 +00002932 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002933 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002934 {
cristyce70c172010-01-07 17:15:30 +00002935 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2936 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2937 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002938 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002939 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002940 if (indexes != (IndexPacket *) NULL)
2941 x_vector[x].index=(MagickRealType) indexes[x];
2942 p++;
2943 }
2944 }
2945 else
2946 {
2947 /*
2948 Scale Y direction.
2949 */
2950 while (scale.y < span.y)
2951 {
cristy9af9b5d2010-08-15 17:04:28 +00002952 if ((next_row != MagickFalse) &&
2953 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002954 {
2955 /*
2956 Read a new scanline.
2957 */
cristyed6cb232010-01-20 03:07:53 +00002958 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2959 exception);
cristy3ed852e2009-09-05 21:47:34 +00002960 if (p == (const PixelPacket *) NULL)
2961 break;
cristyed6cb232010-01-20 03:07:53 +00002962 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002963 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002964 {
cristyce70c172010-01-07 17:15:30 +00002965 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2966 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2967 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002968 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002969 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002970 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002971 if (indexes != (IndexPacket *) NULL)
2972 x_vector[x].index=(MagickRealType) indexes[x];
2973 p++;
2974 }
2975 number_rows++;
2976 }
cristybb503372010-05-27 20:51:26 +00002977 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002978 {
2979 y_vector[x].red+=scale.y*x_vector[x].red;
2980 y_vector[x].green+=scale.y*x_vector[x].green;
2981 y_vector[x].blue+=scale.y*x_vector[x].blue;
2982 if (scale_image->matte != MagickFalse)
2983 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2984 if (scale_indexes != (IndexPacket *) NULL)
2985 y_vector[x].index+=scale.y*x_vector[x].index;
2986 }
2987 span.y-=scale.y;
2988 scale.y=(double) scale_image->rows/(double) image->rows;
2989 next_row=MagickTrue;
2990 }
cristybb503372010-05-27 20:51:26 +00002991 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002992 {
2993 /*
2994 Read a new scanline.
2995 */
cristyed6cb232010-01-20 03:07:53 +00002996 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2997 exception);
cristy3ed852e2009-09-05 21:47:34 +00002998 if (p == (const PixelPacket *) NULL)
2999 break;
cristyed6cb232010-01-20 03:07:53 +00003000 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00003001 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003002 {
cristyce70c172010-01-07 17:15:30 +00003003 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
3004 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
3005 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003006 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00003007 x_vector[x].opacity=(MagickRealType)
3008 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003009 if (indexes != (IndexPacket *) NULL)
3010 x_vector[x].index=(MagickRealType) indexes[x];
3011 p++;
3012 }
3013 number_rows++;
3014 next_row=MagickFalse;
3015 }
3016 s=scanline;
cristybb503372010-05-27 20:51:26 +00003017 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003018 {
3019 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3020 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3021 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3022 if (image->matte != MagickFalse)
3023 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3024 if (scale_indexes != (IndexPacket *) NULL)
3025 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3026 s->red=pixel.red;
3027 s->green=pixel.green;
3028 s->blue=pixel.blue;
3029 if (scale_image->matte != MagickFalse)
3030 s->opacity=pixel.opacity;
3031 if (scale_indexes != (IndexPacket *) NULL)
3032 s->index=pixel.index;
3033 s++;
3034 y_vector[x]=zero;
3035 }
3036 scale.y-=span.y;
3037 if (scale.y <= 0)
3038 {
3039 scale.y=(double) scale_image->rows/(double) image->rows;
3040 next_row=MagickTrue;
3041 }
3042 span.y=1.0;
3043 }
3044 if (scale_image->columns == image->columns)
3045 {
3046 /*
3047 Transfer scanline to scaled image.
3048 */
3049 s=scanline;
cristybb503372010-05-27 20:51:26 +00003050 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003051 {
cristyce70c172010-01-07 17:15:30 +00003052 q->red=ClampToQuantum(s->red);
3053 q->green=ClampToQuantum(s->green);
3054 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003055 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003056 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003057 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003058 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003059 q++;
3060 s++;
3061 }
3062 }
3063 else
3064 {
3065 /*
3066 Scale X direction.
3067 */
3068 pixel=zero;
3069 next_column=MagickFalse;
3070 span.x=1.0;
3071 s=scanline;
3072 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003073 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003074 {
3075 scale.x=(double) scale_image->columns/(double) image->columns;
3076 while (scale.x >= span.x)
3077 {
3078 if (next_column != MagickFalse)
3079 {
3080 pixel=zero;
3081 t++;
3082 }
3083 pixel.red+=span.x*s->red;
3084 pixel.green+=span.x*s->green;
3085 pixel.blue+=span.x*s->blue;
3086 if (image->matte != MagickFalse)
3087 pixel.opacity+=span.x*s->opacity;
3088 if (scale_indexes != (IndexPacket *) NULL)
3089 pixel.index+=span.x*s->index;
3090 t->red=pixel.red;
3091 t->green=pixel.green;
3092 t->blue=pixel.blue;
3093 if (scale_image->matte != MagickFalse)
3094 t->opacity=pixel.opacity;
3095 if (scale_indexes != (IndexPacket *) NULL)
3096 t->index=pixel.index;
3097 scale.x-=span.x;
3098 span.x=1.0;
3099 next_column=MagickTrue;
3100 }
3101 if (scale.x > 0)
3102 {
3103 if (next_column != MagickFalse)
3104 {
3105 pixel=zero;
3106 next_column=MagickFalse;
3107 t++;
3108 }
3109 pixel.red+=scale.x*s->red;
3110 pixel.green+=scale.x*s->green;
3111 pixel.blue+=scale.x*s->blue;
3112 if (scale_image->matte != MagickFalse)
3113 pixel.opacity+=scale.x*s->opacity;
3114 if (scale_indexes != (IndexPacket *) NULL)
3115 pixel.index+=scale.x*s->index;
3116 span.x-=scale.x;
3117 }
3118 s++;
3119 }
3120 if (span.x > 0)
3121 {
3122 s--;
3123 pixel.red+=span.x*s->red;
3124 pixel.green+=span.x*s->green;
3125 pixel.blue+=span.x*s->blue;
3126 if (scale_image->matte != MagickFalse)
3127 pixel.opacity+=span.x*s->opacity;
3128 if (scale_indexes != (IndexPacket *) NULL)
3129 pixel.index+=span.x*s->index;
3130 }
3131 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003132 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003133 {
3134 t->red=pixel.red;
3135 t->green=pixel.green;
3136 t->blue=pixel.blue;
3137 if (scale_image->matte != MagickFalse)
3138 t->opacity=pixel.opacity;
3139 if (scale_indexes != (IndexPacket *) NULL)
3140 t->index=pixel.index;
3141 }
3142 /*
3143 Transfer scanline to scaled image.
3144 */
3145 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003146 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003147 {
cristyce70c172010-01-07 17:15:30 +00003148 q->red=ClampToQuantum(t->red);
3149 q->green=ClampToQuantum(t->green);
3150 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003151 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003152 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003153 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003154 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003155 t++;
3156 q++;
3157 }
3158 }
cristyed6cb232010-01-20 03:07:53 +00003159 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003160 break;
cristy96b16132010-08-29 17:19:52 +00003161 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3162 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003163 if (proceed == MagickFalse)
3164 break;
3165 }
cristyed6cb232010-01-20 03:07:53 +00003166 scale_view=DestroyCacheView(scale_view);
3167 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003168 /*
3169 Free allocated memory.
3170 */
3171 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3172 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3173 if (scale_image->rows != image->rows)
3174 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3175 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3176 scale_image->type=image->type;
3177 return(scale_image);
3178}
3179
anthony02b4cb42010-10-10 04:54:35 +00003180#if 0
3181 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003182/*
3183%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3184% %
3185% %
3186% %
3187+ S e t R e s i z e F i l t e r S u p p o r t %
3188% %
3189% %
3190% %
3191%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3192%
3193% SetResizeFilterSupport() specifies which IR filter to use to window
3194%
3195% The format of the SetResizeFilterSupport method is:
3196%
3197% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3198% const MagickRealType support)
3199%
3200% A description of each parameter follows:
3201%
3202% o resize_filter: the resize filter.
3203%
3204% o support: the filter spport radius.
3205%
3206*/
3207MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3208 const MagickRealType support)
3209{
3210 assert(resize_filter != (ResizeFilter *) NULL);
3211 assert(resize_filter->signature == MagickSignature);
3212 resize_filter->support=support;
3213}
anthony02b4cb42010-10-10 04:54:35 +00003214#endif
cristy3ed852e2009-09-05 21:47:34 +00003215
3216/*
3217%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3218% %
3219% %
3220% %
3221% T h u m b n a i l I m a g e %
3222% %
3223% %
3224% %
3225%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3226%
3227% ThumbnailImage() changes the size of an image to the given dimensions and
3228% removes any associated profiles. The goal is to produce small low cost
3229% thumbnail images suited for display on the Web.
3230%
3231% The format of the ThumbnailImage method is:
3232%
cristybb503372010-05-27 20:51:26 +00003233% Image *ThumbnailImage(const Image *image,const size_t columns,
3234% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003235%
3236% A description of each parameter follows:
3237%
3238% o image: the image.
3239%
3240% o columns: the number of columns in the scaled image.
3241%
3242% o rows: the number of rows in the scaled image.
3243%
3244% o exception: return any errors or warnings in this structure.
3245%
3246*/
cristy9af9b5d2010-08-15 17:04:28 +00003247MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3248 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003249{
3250#define SampleFactor 5
3251
3252 char
3253 value[MaxTextExtent];
3254
3255 const char
3256 *name;
3257
3258 Image
3259 *thumbnail_image;
3260
3261 MagickRealType
3262 x_factor,
3263 y_factor;
3264
cristybb503372010-05-27 20:51:26 +00003265 size_t
cristy3ed852e2009-09-05 21:47:34 +00003266 version;
3267
cristy9af9b5d2010-08-15 17:04:28 +00003268 struct stat
3269 attributes;
3270
cristy3ed852e2009-09-05 21:47:34 +00003271 assert(image != (Image *) NULL);
3272 assert(image->signature == MagickSignature);
3273 if (image->debug != MagickFalse)
3274 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3275 assert(exception != (ExceptionInfo *) NULL);
3276 assert(exception->signature == MagickSignature);
3277 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3278 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3279 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003280 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3281 exception);
cristy3ed852e2009-09-05 21:47:34 +00003282 else
3283 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003284 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3285 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003286 else
3287 {
3288 Image
3289 *sample_image;
3290
3291 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3292 exception);
3293 if (sample_image == (Image *) NULL)
3294 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003295 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3296 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003297 sample_image=DestroyImage(sample_image);
3298 }
3299 if (thumbnail_image == (Image *) NULL)
3300 return(thumbnail_image);
3301 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3302 if (thumbnail_image->matte == MagickFalse)
3303 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3304 thumbnail_image->depth=8;
3305 thumbnail_image->interlace=NoInterlace;
3306 /*
3307 Strip all profiles except color profiles.
3308 */
3309 ResetImageProfileIterator(thumbnail_image);
3310 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3311 {
3312 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3313 {
cristy2b726bd2010-01-11 01:05:39 +00003314 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003315 ResetImageProfileIterator(thumbnail_image);
3316 }
3317 name=GetNextImageProfile(thumbnail_image);
3318 }
3319 (void) DeleteImageProperty(thumbnail_image,"comment");
3320 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003321 if (strstr(image->magick_filename,"//") == (char *) NULL)
3322 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003323 image->magick_filename);
3324 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3325 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3326 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3327 {
cristye8c25f92010-06-03 00:53:06 +00003328 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003329 attributes.st_mtime);
3330 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3331 }
cristye8c25f92010-06-03 00:53:06 +00003332 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003333 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003334 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003335 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003336 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3337 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3338 LocaleLower(value);
3339 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3340 (void) SetImageProperty(thumbnail_image,"software",
3341 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003342 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3343 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003344 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003345 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003346 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003347 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003348 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3349 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003350 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3351 return(thumbnail_image);
3352}