blob: c476573cee78aef81dd80d9ce10979d5ce467427 [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
nicolasdff19b42010-10-10 20:53:13 +0000540% blur=0.958033808). Now, it is the unique Cubic 'Keys' filter that
541% exactly preserves images with only vertical or horizontal features
542% when performing 'no-op" with EWA distortion. It turns out to be
nicolas219dd842010-10-10 21:02:40 +0000543% close to both plain Mitchell and "sharpened" Lanczos2D.
anthony61b5ddd2010-10-05 02:33:31 +0000544%
nicolas07bac812010-09-19 18:47:02 +0000545% Special 'expert' options can be used to override any and all filter
546% settings. This is not advised unless you have expert knowledge of
547% the use of resampling filtered techniques. Check on the results of
548% your selections using the "filter:verbose" setting to make sure you
anthony61b5ddd2010-10-05 02:33:31 +0000549% get the exact filter that you are tring to achieve.
cristy3ed852e2009-09-05 21:47:34 +0000550%
anthony48f77622010-10-03 14:32:31 +0000551% "filter:filter" Select the main function associated with
552% this filter name, as the weighting function of the filter.
553% This can be used to set a windowing function as a weighting
554% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000555%
anthony7bdc0ed2010-09-15 01:52:32 +0000556% If a "filter:window" operation has not been provided, then a 'Box'
557% windowing function will be set to denote that no windowing function
558% is being used.
cristy3ed852e2009-09-05 21:47:34 +0000559%
560% "filter:window" Select this windowing function for the filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000561% While any filter could be used as a windowing function, using the
562% 'first lobe' of that filter over the whole support window, using a
anthony48f77622010-10-03 14:32:31 +0000563% non-windowing function is not advisible. If no weighting filter
564% function is specifed a 'SincFast' filter will be used.
cristy3ed852e2009-09-05 21:47:34 +0000565%
anthony48f77622010-10-03 14:32:31 +0000566% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000567% This a simpler method of setting filter support size that will
anthony48f77622010-10-03 14:32:31 +0000568% correctly handle the Sinc/Jinc switch for an operators filtering
anthony7bdc0ed2010-09-15 01:52:32 +0000569% requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000570%
571% "filter:support" Set the support size for filtering to the size given
anthony48f77622010-10-03 14:32:31 +0000572% This not recommended for Sinc/Jinc windowed filters (lobes should
anthony7bdc0ed2010-09-15 01:52:32 +0000573% be used instead). This will override any 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000574%
anthonyb6d08c52010-09-13 01:17:04 +0000575% "filter:win-support" Scale windowing function to this size instead.
576% This causes the windowing (or self-windowing Lagrange filter) to act
577% is if the support window it much much larger than what is actually
578% supplied to the calling operator. The filter however is still
579% clipped to the real support size given, by the support range suppiled
580% to the caller. If unset this will equal the normal filter support
581% size.
582%
cristy3ed852e2009-09-05 21:47:34 +0000583% "filter:blur" Scale the filter and support window by this amount.
584% A value >1 will generally result in a more burred image with
585% more ringing effects, while a value <1 will sharpen the
586% resulting image with more aliasing and Morie effects.
587%
anthonyf5e76ef2010-10-12 01:22:01 +0000588% "filter:sigma" The sigma value to use for the Gaussian filter only.
589% Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for cylindrical
590% usage. It effectially provides a alturnative to 'blur' for Gaussians
591% without it also effecting the final 'practical support' size.
592%
cristy3ed852e2009-09-05 21:47:34 +0000593% "filter:b"
594% "filter:c" Override the preset B,C values for a Cubic type of filter
595% If only one of these are given it is assumes to be a 'Keys'
596% type of filter such that B+2C=1, where Keys 'alpha' value = C
597%
anthonyb6d08c52010-09-13 01:17:04 +0000598% "filter:verbose" Output the exact results of the filter selections
599% made, as well as plotting data for graphing the resulting filter
600% over support range (blur adjusted).
cristy3ed852e2009-09-05 21:47:34 +0000601%
602% Set a true un-windowed Sinc filter with 10 lobes (very slow)
anthony48f77622010-10-03 14:32:31 +0000603% -define filter:filter=Sinc
604% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000605%
anthony48f77622010-10-03 14:32:31 +0000606% For example force an 8 lobe Lanczos (Sinc or Jinc) filter...
cristy3ed852e2009-09-05 21:47:34 +0000607% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000608% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000609%
cristy3ed852e2009-09-05 21:47:34 +0000610% The format of the AcquireResizeFilter method is:
611%
612% ResizeFilter *AcquireResizeFilter(const Image *image,
613% const FilterTypes filter_type, const MagickBooleanType radial,
614% ExceptionInfo *exception)
615%
cristy33b1c162010-01-23 22:51:51 +0000616% A description of each parameter follows:
617%
cristy3ed852e2009-09-05 21:47:34 +0000618% o image: the image.
619%
nicolas07bac812010-09-19 18:47:02 +0000620% o filter: the filter type, defining a preset filter, window and
621% support. The artifact settings listed above will override
622% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000623%
anthony48f77622010-10-03 14:32:31 +0000624% o blur: blur the filter by this amount, use 1.0 if unknown. Image
625% artifact "filter:blur" will override this API call usage, including
626% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000627%
anthony48f77622010-10-03 14:32:31 +0000628% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
629% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000630%
631% o exception: return any errors or warnings in this structure.
632%
633*/
634MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000635 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000636 const MagickBooleanType cylindrical,ExceptionInfo *exception)
637{
638 const char
639 *artifact;
640
641 FilterTypes
642 filter_type,
643 window_type;
644
cristy3ed852e2009-09-05 21:47:34 +0000645 MagickRealType
646 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000647 C,
648 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000649
650 register ResizeFilter
651 *resize_filter;
652
cristy9af9b5d2010-08-15 17:04:28 +0000653 ssize_t
654 option;
655
cristy3ed852e2009-09-05 21:47:34 +0000656 /*
anthony48f77622010-10-03 14:32:31 +0000657 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000658 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000659 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
660 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
661 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000662
nicolas07bac812010-09-19 18:47:02 +0000663 WARNING: The order of this tabel must match the order of the
664 FilterTypes enumeration specified in "resample.h", or the filter
665 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000666
667 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000668 */
669 static struct
670 {
671 FilterTypes
672 filter,
673 window;
674 } const mapping[SentinelFilter] =
675 {
anthony462ee072010-09-27 12:34:02 +0000676 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
677 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
678 { BoxFilter, BoxFilter }, /* Box averaging filter */
679 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
680 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
681 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
682 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
683 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
684 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
685 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
686 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
687 { CatromFilter, BoxFilter }, /* Cubic interpolator */
688 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
689 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
anthony48f77622010-10-03 14:32:31 +0000690 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
691 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony462ee072010-09-27 12:34:02 +0000692 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
693 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
694 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
695 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
696 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
697 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
698 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolas5835ee02010-10-10 20:40:15 +0000699 { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
nicolas1eb2fcf2010-10-10 20:45:17 +0000700 { RobidouxFilter, BoxFilter }, /* SPECIAL: Keys cubic tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000701 };
702 /*
nicolas32f44eb2010-09-20 01:23:12 +0000703 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000704 function. The default support size for that filter as a weighting
705 function, the range to scale with to use that function as a sinc
706 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000707
anthony07a3f7f2010-09-16 03:03:11 +0000708 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000709 SincFast(), and CubicBC() functions, which may have multiple
710 filter to function associations.
711
712 See "filter:verbose" handling below for the function -> filter
713 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000714 */
715 static struct
716 {
717 MagickRealType
718 (*function)(const MagickRealType, const ResizeFilter*),
anthony61b5ddd2010-10-05 02:33:31 +0000719 lobes, /* default lobes/support size of the weighting filter */
anthony2d9b8b52010-09-14 08:31:07 +0000720 scale, /* windowing function range, for scaling windowing function */
anthony933b4bf2010-09-10 02:31:37 +0000721 B,C; /* Cubic Filter factors for a CubicBC function, else ignored */
cristy3ed852e2009-09-05 21:47:34 +0000722 } const filters[SentinelFilter] =
723 {
anthony61b5ddd2010-10-05 02:33:31 +0000724 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
725 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
726 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
727 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
728 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
729 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
730 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
731 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000732 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000733 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
734 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
735 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
anthony853d6972010-10-08 06:01:31 +0000736 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
737 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
738 { Jinc, 3.0, 1.21967, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000739 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
740 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
741 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
742 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
743 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
744 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
745 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
746 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
nicolas0134bbe2010-10-10 15:55:42 +0000747 { Jinc, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2D (Jinc-Jinc) */
anthony02b4cb42010-10-10 04:54:35 +0000748 { CubicBC, 2.0, 1.0, 0.37821575509399862, 0.31089212245300069 }
nicolas0134bbe2010-10-10 15:55:42 +0000749 /* Robidoux: Keys cubic close to Lanczos2D with blur=0.958033808 */
cristy3ed852e2009-09-05 21:47:34 +0000750 };
751 /*
anthony9a98fc62010-10-11 02:47:19 +0000752 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
753 function being used as a filter. It is used by the "filter:lobes" and for
754 the 'lobes' number in the above, the for support selection, so users do
755 not have to deal with the highly irrational sizes of the 'lobes' of the
756 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000757
nicolase473f722010-10-07 00:05:13 +0000758 Values taken from
anthony48f77622010-10-03 14:32:31 +0000759 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000760 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000761 */
762 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000763 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000764 {
anthonyc2d07db2010-09-15 23:47:40 +0000765 1.21966989126651,
766 2.23313059438153,
767 3.23831548416624,
768 4.24106286379607,
769 5.24276437687019,
770 6.24392168986449,
771 7.24475986871996,
772 8.24539491395205,
773 9.24589268494948,
774 10.2462933487549,
775 11.2466227948779,
776 12.2468984611381,
777 13.2471325221811,
778 14.2473337358069,
779 15.2475085630373,
780 16.247661874701
cristy3ed852e2009-09-05 21:47:34 +0000781 };
782
cristy33b1c162010-01-23 22:51:51 +0000783 /*
784 Allocate resize filter.
785 */
cristy3ed852e2009-09-05 21:47:34 +0000786 assert(image != (const Image *) NULL);
787 assert(image->signature == MagickSignature);
788 if (image->debug != MagickFalse)
789 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
790 assert(UndefinedFilter < filter && filter < SentinelFilter);
791 assert(exception != (ExceptionInfo *) NULL);
792 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000793 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000794 if (resize_filter == (ResizeFilter *) NULL)
795 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000796 /*
797 Defaults for the requested filter.
798 */
799 filter_type=mapping[filter].filter;
800 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000801 resize_filter->blur = blur;
802 sigma = 0.5;
anthony48f77622010-10-03 14:32:31 +0000803 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000804 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000805 switch (filter_type)
806 {
807 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000808 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000809 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000810 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000811 break;
anthonyba5a7c32010-09-15 02:42:25 +0000812 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000813 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000814 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000815 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000816 break;
anthony61b5ddd2010-10-05 02:33:31 +0000817
cristy33b1c162010-01-23 22:51:51 +0000818 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000819 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000820 filter_type=JincFilter;
821 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000822 break;
anthonyf5e76ef2010-10-12 01:22:01 +0000823 case GaussianFilter:
824 sigma = MagickSQ2/2; /* Cylindrical Gaussian sigma is sqrt(2)/2 */
825 break;
cristya782ecf2010-01-25 02:59:14 +0000826 default:
827 break;
cristy3ed852e2009-09-05 21:47:34 +0000828 }
anthony61b5ddd2010-10-05 02:33:31 +0000829 else
830 switch (filter_type)
831 {
832 case Lanczos2DFilter:
nicolas45b58a92010-10-07 15:46:39 +0000833 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000834 window_type=SincFastFilter;
835 break;
836 default:
837 break;
838 }
839
cristy3ed852e2009-09-05 21:47:34 +0000840 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000841 if (artifact != (const char *) NULL)
842 {
cristy9af9b5d2010-08-15 17:04:28 +0000843 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000844 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000845 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000846 filter_type=(FilterTypes) option;
847 window_type=BoxFilter;
848 }
849 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000850 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000851 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000852 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000853 }
nicolas07bac812010-09-19 18:47:02 +0000854 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000855 artifact=GetImageArtifact(image,"filter:window");
856 if (artifact != (const char *) NULL)
857 {
cristy9af9b5d2010-08-15 17:04:28 +0000858 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000859 if ((UndefinedFilter < option) && (option < SentinelFilter))
860 {
861 if (option != LanczosFilter)
862 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000863 else
anthony48f77622010-10-03 14:32:31 +0000864 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000865 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000866 }
cristy33b1c162010-01-23 22:51:51 +0000867 }
cristy3ed852e2009-09-05 21:47:34 +0000868 }
cristy33b1c162010-01-23 22:51:51 +0000869 else
870 {
anthony48f77622010-10-03 14:32:31 +0000871 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000872 artifact=GetImageArtifact(image,"filter:window");
873 if (artifact != (const char *) NULL)
874 {
875 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
876 artifact);
877 if ((UndefinedFilter < option) && (option < SentinelFilter))
878 {
anthony61b5ddd2010-10-05 02:33:31 +0000879 filter_type=cylindrical != MagickFalse ?
880 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000881 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000882 }
883 }
884 }
nicolas07bac812010-09-19 18:47:02 +0000885 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000886 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000887 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000888 resize_filter->window=filters[window_type].function;
889 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000890 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000891
anthonyf5e76ef2010-10-12 01:22:01 +0000892 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000893 if (cylindrical != MagickFalse)
894 switch (filter_type)
895 {
896 case PointFilter:
897 case BoxFilter:
898 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000899 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000900 break;
anthony81b8bf92010-10-02 13:54:34 +0000901 default:
902 break;
anthony10b8bc82010-10-02 12:48:46 +0000903 }
anthony61b5ddd2010-10-05 02:33:31 +0000904 else
905 switch (filter_type)
906 {
907 case Lanczos2DFilter:
anthony853d6972010-10-08 06:01:31 +0000908 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000909 resize_filter->filter=SincFast;
910 break;
911 default:
912 break;
913 }
914
anthonyf5e76ef2010-10-12 01:22:01 +0000915 /*
916 ** More Expert Option Modifications
917 */
918
919 /* User Sigma Override - no support change */
920 artifact=GetImageArtifact(image,"filter:sigma");
921 if (artifact != (const char *) NULL)
922 sigma=StringToDouble(artifact);
923 /* Define coefficents for Gaussian (assumes no cubic window) */
924 if ( GaussianFilter ) {
925 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
926 resize_filter->coeff[1] = 1.0/(Magick2PI*sigma*sigma); /* unused */
927 }
928
929 /* Blur Override */
930 artifact=GetImageArtifact(image,"filter:blur");
931 if (artifact != (const char *) NULL)
932 resize_filter->blur=StringToDouble(artifact);
933 if (resize_filter->blur < MagickEpsilon)
934 resize_filter->blur=(MagickRealType) MagickEpsilon;
935
936 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000937 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000938 if (artifact != (const char *) NULL)
939 {
cristybb503372010-05-27 20:51:26 +0000940 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000941 lobes;
942
cristy96b16132010-08-29 17:19:52 +0000943 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000944 if (lobes < 1)
945 lobes=1;
946 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000947 }
anthony61b5ddd2010-10-05 02:33:31 +0000948 /* convert Jinc lobes to a real support value */
949 if (resize_filter->filter == Jinc)
950 {
951 if (resize_filter->support > 16)
952 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
953 else
954 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
955 }
956 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000957 artifact=GetImageArtifact(image,"filter:support");
958 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000959 resize_filter->support=fabs(StringToDouble(artifact));
960 /*
nicolas07bac812010-09-19 18:47:02 +0000961 Scale windowing function separatally to the support 'clipping'
962 window that calling operator is planning to actually use. (Expert
963 override)
cristy3ed852e2009-09-05 21:47:34 +0000964 */
anthony55f12332010-09-10 01:13:02 +0000965 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000966 artifact=GetImageArtifact(image,"filter:win-support");
967 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000968 resize_filter->window_support=fabs(StringToDouble(artifact));
969 /*
anthony1f90a6b2010-09-14 08:56:31 +0000970 Adjust window function scaling to the windowing support for
971 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000972 */
973 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +0000974
anthony55f12332010-09-10 01:13:02 +0000975 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000976 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000977 */
cristy3ed852e2009-09-05 21:47:34 +0000978 B=0.0;
979 C=0.0;
cristy33b1c162010-01-23 22:51:51 +0000980 if ((filters[filter_type].function == CubicBC) ||
981 (filters[window_type].function == CubicBC))
982 {
anthony2d9b8b52010-09-14 08:31:07 +0000983 B=filters[filter_type].B;
984 C=filters[filter_type].C;
985 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +0000986 {
anthony2d9b8b52010-09-14 08:31:07 +0000987 B=filters[window_type].B;
988 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +0000989 }
cristy33b1c162010-01-23 22:51:51 +0000990 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +0000991 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000992 {
993 B=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +0000994 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +0000995 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +0000996 if (artifact != (const char *) NULL)
997 C=StringToDouble(artifact);
998 }
999 else
1000 {
1001 artifact=GetImageArtifact(image,"filter:c");
1002 if (artifact != (const char *) NULL)
1003 {
1004 C=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001005 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001006 }
1007 }
anthonyf5e76ef2010-10-12 01:22:01 +00001008 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
1009 resize_filter->coeff[0]=(6.0-2.0*B)/6.0;
1010 resize_filter->coeff[1]=0.0;
1011 resize_filter->coeff[2]=(-18.0+12.0*B+6.0*C)/6.0;
1012 resize_filter->coeff[3]=(12.0-9.0*B-6.0*C)/6.0;
1013 resize_filter->coeff[4]=(8.0*B+24.0*C)/6.0;
1014 resize_filter->coeff[5]=(-12.0*B-48.0*C)/6.0;
1015 resize_filter->coeff[6]=(6.0*B+30.0*C)/6.0;
1016 resize_filter->coeff[7]=(-B-6.0*C)/6.0;
cristy3ed852e2009-09-05 21:47:34 +00001017 }
anthonyf5e76ef2010-10-12 01:22:01 +00001018
anthony55f12332010-09-10 01:13:02 +00001019 /*
nicolas07bac812010-09-19 18:47:02 +00001020 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001021 */
anthonye06e4c12010-09-15 04:03:52 +00001022#if defined(MAGICKCORE_OPENMP_SUPPORT)
anthony72949792010-10-08 04:44:56 +00001023 #pragma omp single
1024 {
anthonye06e4c12010-09-15 04:03:52 +00001025#endif
1026 artifact=GetImageArtifact(image,"filter:verbose");
1027 if (artifact != (const char *) NULL)
1028 {
1029 double
1030 support,
1031 x;
cristy3ed852e2009-09-05 21:47:34 +00001032
nicolas07bac812010-09-19 18:47:02 +00001033 /*
anthony463be1d2010-09-26 01:07:36 +00001034 Set the weighting function properly when the weighting
1035 function may not exactly match the filter of the same name.
1036 EG: a Point filter really uses a Box weighting function
1037 with a different support than is typically used.
1038
anthonye06e4c12010-09-15 04:03:52 +00001039 */
anthony463be1d2010-09-26 01:07:36 +00001040 if (resize_filter->filter == Box) filter_type=BoxFilter;
1041 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1042 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
anthony61b5ddd2010-10-05 02:33:31 +00001043 if (resize_filter->filter == Jinc) filter_type=JincFilter;
anthony463be1d2010-09-26 01:07:36 +00001044 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
anthonye06e4c12010-09-15 04:03:52 +00001045 /*
nicolas07bac812010-09-19 18:47:02 +00001046 Report Filter Details.
anthonye06e4c12010-09-15 04:03:52 +00001047 */
anthonyf5e76ef2010-10-12 01:22:01 +00001048 support=GetResizeFilterSupport(resize_filter); /* practical_support */
anthony61b5ddd2010-10-05 02:33:31 +00001049 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
cristy03dbbd22010-09-19 23:04:47 +00001050 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1051 MagickFilterOptions,filter_type));
1052 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
anthony463be1d2010-09-26 01:07:36 +00001053 MagickFilterOptions, window_type));
cristy03dbbd22010-09-19 23:04:47 +00001054 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
anthony463be1d2010-09-26 01:07:36 +00001055 (double) resize_filter->support);
cristy03dbbd22010-09-19 23:04:47 +00001056 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
anthony463be1d2010-09-26 01:07:36 +00001057 (double) resize_filter->window_support);
anthonyf5e76ef2010-10-12 01:22:01 +00001058 (void) fprintf(stdout,"# scale_blur = %.*g\n",GetMagickPrecision(),
cristy03dbbd22010-09-19 23:04:47 +00001059 (double) resize_filter->blur);
anthonyf5e76ef2010-10-12 01:22:01 +00001060 if ( filter_type == GaussianFilter )
1061 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",GetMagickPrecision(),
1062 (double) sigma);
1063 (void) fprintf(stdout,"# practical_support = %.*g\n",GetMagickPrecision(),
cristy03dbbd22010-09-19 23:04:47 +00001064 (double) support);
anthonydc592432010-10-12 02:59:44 +00001065 if ( filter_type == CubicFilter || window_type == CubicFilter )
1066 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1067 (double) B,GetMagickPrecision(),(double) C);
anthony61b5ddd2010-10-05 02:33:31 +00001068 (void) fprintf(stdout,"\n");
anthonye06e4c12010-09-15 04:03:52 +00001069 /*
nicolas07bac812010-09-19 18:47:02 +00001070 Output values of resulting filter graph -- for graphing
1071 filter result.
anthonye06e4c12010-09-15 04:03:52 +00001072 */
1073 for (x=0.0; x <= support; x+=0.01f)
cristy03dbbd22010-09-19 23:04:47 +00001074 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1075 (double) GetResizeFilterWeight(resize_filter,x));
nicolas07bac812010-09-19 18:47:02 +00001076 /* A final value so gnuplot can graph the 'stop' properly. */
cristy03dbbd22010-09-19 23:04:47 +00001077 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1078 0.0);
anthonye06e4c12010-09-15 04:03:52 +00001079 }
anthonyf5e76ef2010-10-12 01:22:01 +00001080 /* Output the above once only for each image - remove setting */
cristybb66d9c2010-10-09 01:40:31 +00001081 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
anthonye06e4c12010-09-15 04:03:52 +00001082#if defined(MAGICKCORE_OPENMP_SUPPORT)
anthony61b5ddd2010-10-05 02:33:31 +00001083 }
anthonye06e4c12010-09-15 04:03:52 +00001084#endif
anthonyf5e76ef2010-10-12 01:22:01 +00001085
cristy3ed852e2009-09-05 21:47:34 +00001086 return(resize_filter);
1087}
1088
1089/*
1090%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1091% %
1092% %
1093% %
1094% A d a p t i v e R e s i z e I m a g e %
1095% %
1096% %
1097% %
1098%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1099%
1100% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1101%
1102% The format of the AdaptiveResizeImage method is:
1103%
cristy9af9b5d2010-08-15 17:04:28 +00001104% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1105% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001106%
1107% A description of each parameter follows:
1108%
1109% o image: the image.
1110%
1111% o columns: the number of columns in the resized image.
1112%
1113% o rows: the number of rows in the resized image.
1114%
1115% o exception: return any errors or warnings in this structure.
1116%
1117*/
1118MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001119 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001120{
1121#define AdaptiveResizeImageTag "Resize/Image"
1122
cristyc4c8d132010-01-07 01:58:38 +00001123 CacheView
1124 *resize_view;
1125
cristy3ed852e2009-09-05 21:47:34 +00001126 Image
1127 *resize_image;
1128
cristy3ed852e2009-09-05 21:47:34 +00001129 MagickBooleanType
1130 proceed;
1131
1132 MagickPixelPacket
1133 pixel;
1134
1135 PointInfo
1136 offset;
1137
1138 ResampleFilter
1139 *resample_filter;
1140
cristy9af9b5d2010-08-15 17:04:28 +00001141 ssize_t
1142 y;
1143
cristy3ed852e2009-09-05 21:47:34 +00001144 /*
1145 Adaptively resize image.
1146 */
1147 assert(image != (const Image *) NULL);
1148 assert(image->signature == MagickSignature);
1149 if (image->debug != MagickFalse)
1150 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1151 assert(exception != (ExceptionInfo *) NULL);
1152 assert(exception->signature == MagickSignature);
1153 if ((columns == 0) || (rows == 0))
1154 return((Image *) NULL);
1155 if ((columns == image->columns) && (rows == image->rows))
1156 return(CloneImage(image,0,0,MagickTrue,exception));
1157 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1158 if (resize_image == (Image *) NULL)
1159 return((Image *) NULL);
1160 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1161 {
1162 InheritException(exception,&resize_image->exception);
1163 resize_image=DestroyImage(resize_image);
1164 return((Image *) NULL);
1165 }
1166 GetMagickPixelPacket(image,&pixel);
1167 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001168 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001169 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001170 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001171 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001172 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001173 {
1174 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001175 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001176
cristybb503372010-05-27 20:51:26 +00001177 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001178 x;
1179
1180 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001181 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001182
1183 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1184 exception);
1185 if (q == (PixelPacket *) NULL)
1186 break;
1187 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1188 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001189 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001190 {
1191 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1192 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1193 &pixel);
1194 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1195 q++;
1196 }
1197 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1198 break;
cristy96b16132010-08-29 17:19:52 +00001199 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1200 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001201 if (proceed == MagickFalse)
1202 break;
1203 }
1204 resample_filter=DestroyResampleFilter(resample_filter);
1205 resize_view=DestroyCacheView(resize_view);
1206 return(resize_image);
1207}
1208
1209/*
1210%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1211% %
1212% %
1213% %
1214+ B e s s e l O r d e r O n e %
1215% %
1216% %
1217% %
1218%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1219%
1220% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001221% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001222%
1223% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1224%
1225% j1(x) = x*j1(x);
1226%
1227% For x in (8,inf)
1228%
1229% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1230%
1231% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1232%
1233% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1234% = 1/sqrt(2) * (sin(x) - cos(x))
1235% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1236% = -1/sqrt(2) * (sin(x) + cos(x))
1237%
1238% The format of the BesselOrderOne method is:
1239%
1240% MagickRealType BesselOrderOne(MagickRealType x)
1241%
1242% A description of each parameter follows:
1243%
1244% o x: MagickRealType value.
1245%
1246*/
1247
1248#undef I0
1249static MagickRealType I0(MagickRealType x)
1250{
1251 MagickRealType
1252 sum,
1253 t,
1254 y;
1255
cristybb503372010-05-27 20:51:26 +00001256 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001257 i;
1258
1259 /*
1260 Zeroth order Bessel function of the first kind.
1261 */
1262 sum=1.0;
1263 y=x*x/4.0;
1264 t=y;
1265 for (i=2; t > MagickEpsilon; i++)
1266 {
1267 sum+=t;
1268 t*=y/((MagickRealType) i*i);
1269 }
1270 return(sum);
1271}
1272
1273#undef J1
1274static MagickRealType J1(MagickRealType x)
1275{
1276 MagickRealType
1277 p,
1278 q;
1279
cristybb503372010-05-27 20:51:26 +00001280 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001281 i;
1282
1283 static const double
1284 Pone[] =
1285 {
1286 0.581199354001606143928050809e+21,
1287 -0.6672106568924916298020941484e+20,
1288 0.2316433580634002297931815435e+19,
1289 -0.3588817569910106050743641413e+17,
1290 0.2908795263834775409737601689e+15,
1291 -0.1322983480332126453125473247e+13,
1292 0.3413234182301700539091292655e+10,
1293 -0.4695753530642995859767162166e+7,
1294 0.270112271089232341485679099e+4
1295 },
1296 Qone[] =
1297 {
1298 0.11623987080032122878585294e+22,
1299 0.1185770712190320999837113348e+20,
1300 0.6092061398917521746105196863e+17,
1301 0.2081661221307607351240184229e+15,
1302 0.5243710262167649715406728642e+12,
1303 0.1013863514358673989967045588e+10,
1304 0.1501793594998585505921097578e+7,
1305 0.1606931573481487801970916749e+4,
1306 0.1e+1
1307 };
1308
1309 p=Pone[8];
1310 q=Qone[8];
1311 for (i=7; i >= 0; i--)
1312 {
1313 p=p*x*x+Pone[i];
1314 q=q*x*x+Qone[i];
1315 }
1316 return(p/q);
1317}
1318
1319#undef P1
1320static MagickRealType P1(MagickRealType x)
1321{
1322 MagickRealType
1323 p,
1324 q;
1325
cristybb503372010-05-27 20:51:26 +00001326 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001327 i;
1328
1329 static const double
1330 Pone[] =
1331 {
1332 0.352246649133679798341724373e+5,
1333 0.62758845247161281269005675e+5,
1334 0.313539631109159574238669888e+5,
1335 0.49854832060594338434500455e+4,
1336 0.2111529182853962382105718e+3,
1337 0.12571716929145341558495e+1
1338 },
1339 Qone[] =
1340 {
1341 0.352246649133679798068390431e+5,
1342 0.626943469593560511888833731e+5,
1343 0.312404063819041039923015703e+5,
1344 0.4930396490181088979386097e+4,
1345 0.2030775189134759322293574e+3,
1346 0.1e+1
1347 };
1348
1349 p=Pone[5];
1350 q=Qone[5];
1351 for (i=4; i >= 0; i--)
1352 {
1353 p=p*(8.0/x)*(8.0/x)+Pone[i];
1354 q=q*(8.0/x)*(8.0/x)+Qone[i];
1355 }
1356 return(p/q);
1357}
1358
1359#undef Q1
1360static MagickRealType Q1(MagickRealType x)
1361{
1362 MagickRealType
1363 p,
1364 q;
1365
cristybb503372010-05-27 20:51:26 +00001366 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001367 i;
1368
1369 static const double
1370 Pone[] =
1371 {
1372 0.3511751914303552822533318e+3,
1373 0.7210391804904475039280863e+3,
1374 0.4259873011654442389886993e+3,
1375 0.831898957673850827325226e+2,
1376 0.45681716295512267064405e+1,
1377 0.3532840052740123642735e-1
1378 },
1379 Qone[] =
1380 {
1381 0.74917374171809127714519505e+4,
1382 0.154141773392650970499848051e+5,
1383 0.91522317015169922705904727e+4,
1384 0.18111867005523513506724158e+4,
1385 0.1038187585462133728776636e+3,
1386 0.1e+1
1387 };
1388
1389 p=Pone[5];
1390 q=Qone[5];
1391 for (i=4; i >= 0; i--)
1392 {
1393 p=p*(8.0/x)*(8.0/x)+Pone[i];
1394 q=q*(8.0/x)*(8.0/x)+Qone[i];
1395 }
1396 return(p/q);
1397}
1398
1399static MagickRealType BesselOrderOne(MagickRealType x)
1400{
1401 MagickRealType
1402 p,
1403 q;
1404
1405 if (x == 0.0)
1406 return(0.0);
1407 p=x;
1408 if (x < 0.0)
1409 x=(-x);
1410 if (x < 8.0)
1411 return(p*J1(x));
1412 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1413 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1414 cos((double) x))));
1415 if (p < 0.0)
1416 q=(-q);
1417 return(q);
1418}
1419
1420/*
1421%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1422% %
1423% %
1424% %
1425+ D e s t r o y R e s i z e F i l t e r %
1426% %
1427% %
1428% %
1429%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1430%
1431% DestroyResizeFilter() destroy the resize filter.
1432%
cristya2ffd7e2010-03-10 20:50:30 +00001433% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001434%
1435% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1436%
1437% A description of each parameter follows:
1438%
1439% o resize_filter: the resize filter.
1440%
1441*/
1442MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1443{
1444 assert(resize_filter != (ResizeFilter *) NULL);
1445 assert(resize_filter->signature == MagickSignature);
1446 resize_filter->signature=(~MagickSignature);
1447 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1448 return(resize_filter);
1449}
1450
1451/*
1452%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1453% %
1454% %
1455% %
1456+ G e t R e s i z e F i l t e r S u p p o r t %
1457% %
1458% %
1459% %
1460%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1461%
1462% GetResizeFilterSupport() return the current support window size for this
1463% filter. Note that this may have been enlarged by filter:blur factor.
1464%
1465% The format of the GetResizeFilterSupport method is:
1466%
1467% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1468%
1469% A description of each parameter follows:
1470%
1471% o filter: Image filter to use.
1472%
1473*/
1474MagickExport MagickRealType GetResizeFilterSupport(
1475 const ResizeFilter *resize_filter)
1476{
1477 assert(resize_filter != (ResizeFilter *) NULL);
1478 assert(resize_filter->signature == MagickSignature);
1479 return(resize_filter->support*resize_filter->blur);
1480}
1481
1482/*
1483%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1484% %
1485% %
1486% %
1487+ G e t R e s i z e F i l t e r W e i g h t %
1488% %
1489% %
1490% %
1491%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1492%
1493% GetResizeFilterWeight evaluates the specified resize filter at the point x
1494% which usally lies between zero and the filters current 'support' and
1495% returns the weight of the filter function at that point.
1496%
1497% The format of the GetResizeFilterWeight method is:
1498%
1499% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1500% const MagickRealType x)
1501%
1502% A description of each parameter follows:
1503%
1504% o filter: the filter type.
1505%
1506% o x: the point.
1507%
1508*/
1509MagickExport MagickRealType GetResizeFilterWeight(
1510 const ResizeFilter *resize_filter,const MagickRealType x)
1511{
1512 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001513 scale,
1514 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001515
1516 /*
1517 Windowing function - scale the weighting filter by this amount.
1518 */
1519 assert(resize_filter != (ResizeFilter *) NULL);
1520 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001521 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001522 if ((resize_filter->window_support < MagickEpsilon) ||
1523 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001524 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001525 else
1526 {
anthony55f12332010-09-10 01:13:02 +00001527 scale=resize_filter->scale;
1528 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001529 }
anthony55f12332010-09-10 01:13:02 +00001530 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001531}
1532
1533/*
1534%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1535% %
1536% %
1537% %
1538% M a g n i f y I m a g e %
1539% %
1540% %
1541% %
1542%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1543%
1544% MagnifyImage() is a convenience method that scales an image proportionally
1545% to twice its size.
1546%
1547% The format of the MagnifyImage method is:
1548%
1549% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1550%
1551% A description of each parameter follows:
1552%
1553% o image: the image.
1554%
1555% o exception: return any errors or warnings in this structure.
1556%
1557*/
1558MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1559{
1560 Image
1561 *magnify_image;
1562
1563 assert(image != (Image *) NULL);
1564 assert(image->signature == MagickSignature);
1565 if (image->debug != MagickFalse)
1566 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1567 assert(exception != (ExceptionInfo *) NULL);
1568 assert(exception->signature == MagickSignature);
1569 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1570 1.0,exception);
1571 return(magnify_image);
1572}
1573
1574/*
1575%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1576% %
1577% %
1578% %
1579% M i n i f y I m a g e %
1580% %
1581% %
1582% %
1583%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1584%
1585% MinifyImage() is a convenience method that scales an image proportionally
1586% to half its size.
1587%
1588% The format of the MinifyImage method is:
1589%
1590% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1591%
1592% A description of each parameter follows:
1593%
1594% o image: the image.
1595%
1596% o exception: return any errors or warnings in this structure.
1597%
1598*/
1599MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1600{
1601 Image
1602 *minify_image;
1603
1604 assert(image != (Image *) NULL);
1605 assert(image->signature == MagickSignature);
1606 if (image->debug != MagickFalse)
1607 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1608 assert(exception != (ExceptionInfo *) NULL);
1609 assert(exception->signature == MagickSignature);
1610 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1611 1.0,exception);
1612 return(minify_image);
1613}
1614
1615/*
1616%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1617% %
1618% %
1619% %
1620% R e s a m p l e I m a g e %
1621% %
1622% %
1623% %
1624%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1625%
1626% ResampleImage() resize image in terms of its pixel size, so that when
1627% displayed at the given resolution it will be the same size in terms of
1628% real world units as the original image at the original resolution.
1629%
1630% The format of the ResampleImage method is:
1631%
1632% Image *ResampleImage(Image *image,const double x_resolution,
1633% const double y_resolution,const FilterTypes filter,const double blur,
1634% ExceptionInfo *exception)
1635%
1636% A description of each parameter follows:
1637%
1638% o image: the image to be resized to fit the given resolution.
1639%
1640% o x_resolution: the new image x resolution.
1641%
1642% o y_resolution: the new image y resolution.
1643%
1644% o filter: Image filter to use.
1645%
1646% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1647%
1648*/
1649MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1650 const double y_resolution,const FilterTypes filter,const double blur,
1651 ExceptionInfo *exception)
1652{
1653#define ResampleImageTag "Resample/Image"
1654
1655 Image
1656 *resample_image;
1657
cristybb503372010-05-27 20:51:26 +00001658 size_t
cristy3ed852e2009-09-05 21:47:34 +00001659 height,
1660 width;
1661
1662 /*
1663 Initialize sampled image attributes.
1664 */
1665 assert(image != (const Image *) NULL);
1666 assert(image->signature == MagickSignature);
1667 if (image->debug != MagickFalse)
1668 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1669 assert(exception != (ExceptionInfo *) NULL);
1670 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001671 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1672 72.0 : image->x_resolution)+0.5);
1673 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1674 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001675 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1676 if (resample_image != (Image *) NULL)
1677 {
1678 resample_image->x_resolution=x_resolution;
1679 resample_image->y_resolution=y_resolution;
1680 }
1681 return(resample_image);
1682}
1683#if defined(MAGICKCORE_LQR_DELEGATE)
1684
1685/*
1686%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1687% %
1688% %
1689% %
1690% L i q u i d R e s c a l e I m a g e %
1691% %
1692% %
1693% %
1694%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1695%
1696% LiquidRescaleImage() rescales image with seam carving.
1697%
1698% The format of the LiquidRescaleImage method is:
1699%
1700% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001701% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001702% const double delta_x,const double rigidity,ExceptionInfo *exception)
1703%
1704% A description of each parameter follows:
1705%
1706% o image: the image.
1707%
1708% o columns: the number of columns in the rescaled image.
1709%
1710% o rows: the number of rows in the rescaled image.
1711%
1712% o delta_x: maximum seam transversal step (0 means straight seams).
1713%
1714% o rigidity: introduce a bias for non-straight seams (typically 0).
1715%
1716% o exception: return any errors or warnings in this structure.
1717%
1718*/
cristy9af9b5d2010-08-15 17:04:28 +00001719MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1720 const size_t rows,const double delta_x,const double rigidity,
1721 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001722{
1723#define LiquidRescaleImageTag "Rescale/Image"
1724
cristyc5c6f662010-09-22 14:23:02 +00001725 CacheView
1726 *rescale_view;
1727
cristy3ed852e2009-09-05 21:47:34 +00001728 const char
1729 *map;
1730
1731 guchar
1732 *packet;
1733
1734 Image
1735 *rescale_image;
1736
1737 int
1738 x,
1739 y;
1740
1741 LqrCarver
1742 *carver;
1743
1744 LqrRetVal
1745 lqr_status;
1746
1747 MagickBooleanType
1748 status;
1749
1750 MagickPixelPacket
1751 pixel;
1752
1753 unsigned char
1754 *pixels;
1755
1756 /*
1757 Liquid rescale image.
1758 */
1759 assert(image != (const Image *) NULL);
1760 assert(image->signature == MagickSignature);
1761 if (image->debug != MagickFalse)
1762 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1763 assert(exception != (ExceptionInfo *) NULL);
1764 assert(exception->signature == MagickSignature);
1765 if ((columns == 0) || (rows == 0))
1766 return((Image *) NULL);
1767 if ((columns == image->columns) && (rows == image->rows))
1768 return(CloneImage(image,0,0,MagickTrue,exception));
1769 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001770 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001771 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1772 {
1773 Image
1774 *resize_image;
1775
cristybb503372010-05-27 20:51:26 +00001776 size_t
cristy3ed852e2009-09-05 21:47:34 +00001777 height,
1778 width;
1779
1780 /*
1781 Honor liquid resize size limitations.
1782 */
1783 for (width=image->columns; columns >= (2*width-1); width*=2);
1784 for (height=image->rows; rows >= (2*height-1); height*=2);
1785 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1786 exception);
1787 if (resize_image == (Image *) NULL)
1788 return((Image *) NULL);
1789 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1790 rigidity,exception);
1791 resize_image=DestroyImage(resize_image);
1792 return(rescale_image);
1793 }
1794 map="RGB";
1795 if (image->matte == MagickFalse)
1796 map="RGBA";
1797 if (image->colorspace == CMYKColorspace)
1798 {
1799 map="CMYK";
1800 if (image->matte == MagickFalse)
1801 map="CMYKA";
1802 }
1803 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1804 strlen(map)*sizeof(*pixels));
1805 if (pixels == (unsigned char *) NULL)
1806 return((Image *) NULL);
1807 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1808 pixels,exception);
1809 if (status == MagickFalse)
1810 {
1811 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1812 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1813 }
1814 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1815 if (carver == (LqrCarver *) NULL)
1816 {
1817 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1818 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1819 }
1820 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1821 lqr_status=lqr_carver_resize(carver,columns,rows);
1822 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1823 lqr_carver_get_height(carver),MagickTrue,exception);
1824 if (rescale_image == (Image *) NULL)
1825 {
1826 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1827 return((Image *) NULL);
1828 }
1829 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1830 {
1831 InheritException(exception,&rescale_image->exception);
1832 rescale_image=DestroyImage(rescale_image);
1833 return((Image *) NULL);
1834 }
1835 GetMagickPixelPacket(rescale_image,&pixel);
1836 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001837 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001838 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1839 {
1840 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001841 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001842
1843 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001844 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001845
anthony22aad252010-09-23 06:59:07 +00001846 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001847 if (q == (PixelPacket *) NULL)
1848 break;
cristyc5c6f662010-09-22 14:23:02 +00001849 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001850 pixel.red=QuantumRange*(packet[0]/255.0);
1851 pixel.green=QuantumRange*(packet[1]/255.0);
1852 pixel.blue=QuantumRange*(packet[2]/255.0);
1853 if (image->colorspace != CMYKColorspace)
1854 {
1855 if (image->matte == MagickFalse)
1856 pixel.opacity=QuantumRange*(packet[3]/255.0);
1857 }
1858 else
1859 {
1860 pixel.index=QuantumRange*(packet[3]/255.0);
1861 if (image->matte == MagickFalse)
1862 pixel.opacity=QuantumRange*(packet[4]/255.0);
1863 }
1864 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001865 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001866 break;
1867 }
cristyc5c6f662010-09-22 14:23:02 +00001868 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001869 /*
1870 Relinquish resources.
1871 */
1872 lqr_carver_destroy(carver);
1873 return(rescale_image);
1874}
1875#else
1876MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001877 const size_t magick_unused(columns),const size_t magick_unused(rows),
1878 const double magick_unused(delta_x),const double magick_unused(rigidity),
1879 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001880{
1881 assert(image != (const Image *) NULL);
1882 assert(image->signature == MagickSignature);
1883 if (image->debug != MagickFalse)
1884 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1885 assert(exception != (ExceptionInfo *) NULL);
1886 assert(exception->signature == MagickSignature);
1887 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1888 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1889 return((Image *) NULL);
1890}
1891#endif
1892
1893/*
1894%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1895% %
1896% %
1897% %
1898% R e s i z e I m a g e %
1899% %
1900% %
1901% %
1902%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1903%
1904% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001905% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001906%
1907% If an undefined filter is given the filter defaults to Mitchell for a
1908% colormapped image, a image with a matte channel, or if the image is
1909% enlarged. Otherwise the filter defaults to a Lanczos.
1910%
1911% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1912%
1913% The format of the ResizeImage method is:
1914%
cristybb503372010-05-27 20:51:26 +00001915% Image *ResizeImage(Image *image,const size_t columns,
1916% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001917% ExceptionInfo *exception)
1918%
1919% A description of each parameter follows:
1920%
1921% o image: the image.
1922%
1923% o columns: the number of columns in the scaled image.
1924%
1925% o rows: the number of rows in the scaled image.
1926%
1927% o filter: Image filter to use.
1928%
cristy9af9b5d2010-08-15 17:04:28 +00001929% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1930% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001931%
1932% o exception: return any errors or warnings in this structure.
1933%
1934*/
1935
1936typedef struct _ContributionInfo
1937{
1938 MagickRealType
1939 weight;
1940
cristybb503372010-05-27 20:51:26 +00001941 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001942 pixel;
1943} ContributionInfo;
1944
1945static ContributionInfo **DestroyContributionThreadSet(
1946 ContributionInfo **contribution)
1947{
cristybb503372010-05-27 20:51:26 +00001948 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001949 i;
1950
1951 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001952 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001953 if (contribution[i] != (ContributionInfo *) NULL)
1954 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1955 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001956 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001957 return(contribution);
1958}
1959
1960static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1961{
cristybb503372010-05-27 20:51:26 +00001962 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001963 i;
1964
1965 ContributionInfo
1966 **contribution;
1967
cristybb503372010-05-27 20:51:26 +00001968 size_t
cristy3ed852e2009-09-05 21:47:34 +00001969 number_threads;
1970
1971 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001972 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001973 sizeof(*contribution));
1974 if (contribution == (ContributionInfo **) NULL)
1975 return((ContributionInfo **) NULL);
1976 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00001977 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00001978 {
1979 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1980 sizeof(**contribution));
1981 if (contribution[i] == (ContributionInfo *) NULL)
1982 return(DestroyContributionThreadSet(contribution));
1983 }
1984 return(contribution);
1985}
1986
1987static inline double MagickMax(const double x,const double y)
1988{
1989 if (x > y)
1990 return(x);
1991 return(y);
1992}
1993
1994static inline double MagickMin(const double x,const double y)
1995{
1996 if (x < y)
1997 return(x);
1998 return(y);
1999}
2000
2001static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2002 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002003 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002004{
2005#define ResizeImageTag "Resize/Image"
2006
cristyfa112112010-01-04 17:48:07 +00002007 CacheView
2008 *image_view,
2009 *resize_view;
2010
cristy3ed852e2009-09-05 21:47:34 +00002011 ClassType
2012 storage_class;
2013
2014 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002015 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002016
cristy3ed852e2009-09-05 21:47:34 +00002017 MagickBooleanType
2018 status;
2019
2020 MagickPixelPacket
2021 zero;
2022
2023 MagickRealType
2024 scale,
2025 support;
2026
cristy9af9b5d2010-08-15 17:04:28 +00002027 ssize_t
2028 x;
2029
cristy3ed852e2009-09-05 21:47:34 +00002030 /*
2031 Apply filter to resize horizontally from image to resize image.
2032 */
cristy5d824382010-09-06 14:00:17 +00002033 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002034 support=scale*GetResizeFilterSupport(resize_filter);
2035 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2036 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2037 {
2038 InheritException(exception,&resize_image->exception);
2039 return(MagickFalse);
2040 }
2041 if (support < 0.5)
2042 {
2043 /*
nicolas07bac812010-09-19 18:47:02 +00002044 Support too small even for nearest neighbour: Reduce to point
2045 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002046 */
2047 support=(MagickRealType) 0.5;
2048 scale=1.0;
2049 }
2050 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2051 if (contributions == (ContributionInfo **) NULL)
2052 {
2053 (void) ThrowMagickException(exception,GetMagickModule(),
2054 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2055 return(MagickFalse);
2056 }
2057 status=MagickTrue;
2058 scale=1.0/scale;
2059 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2060 image_view=AcquireCacheView(image);
2061 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002062#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002063 #pragma omp parallel for shared(status)
2064#endif
cristybb503372010-05-27 20:51:26 +00002065 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002066 {
cristy3ed852e2009-09-05 21:47:34 +00002067 MagickRealType
2068 center,
2069 density;
2070
2071 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002072 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002073
2074 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002075 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002076
cristy03dbbd22010-09-19 23:04:47 +00002077 register ContributionInfo
2078 *restrict contribution;
2079
cristy3ed852e2009-09-05 21:47:34 +00002080 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002081 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002082
cristy3ed852e2009-09-05 21:47:34 +00002083 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002084 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002085
cristy03dbbd22010-09-19 23:04:47 +00002086 register ssize_t
2087 y;
2088
cristy9af9b5d2010-08-15 17:04:28 +00002089 ssize_t
2090 n,
2091 start,
2092 stop;
2093
cristy3ed852e2009-09-05 21:47:34 +00002094 if (status == MagickFalse)
2095 continue;
2096 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002097 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2098 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002099 density=0.0;
2100 contribution=contributions[GetOpenMPThreadId()];
2101 for (n=0; n < (stop-start); n++)
2102 {
2103 contribution[n].pixel=start+n;
2104 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2105 ((MagickRealType) (start+n)-center+0.5));
2106 density+=contribution[n].weight;
2107 }
2108 if ((density != 0.0) && (density != 1.0))
2109 {
cristybb503372010-05-27 20:51:26 +00002110 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002111 i;
2112
2113 /*
2114 Normalize.
2115 */
2116 density=1.0/density;
2117 for (i=0; i < n; i++)
2118 contribution[i].weight*=density;
2119 }
cristy9af9b5d2010-08-15 17:04:28 +00002120 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2121 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002122 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2123 exception);
2124 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2125 {
2126 status=MagickFalse;
2127 continue;
2128 }
2129 indexes=GetCacheViewVirtualIndexQueue(image_view);
2130 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002131 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002132 {
cristy3ed852e2009-09-05 21:47:34 +00002133 MagickPixelPacket
2134 pixel;
2135
2136 MagickRealType
2137 alpha;
2138
cristybb503372010-05-27 20:51:26 +00002139 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002140 i;
2141
cristy9af9b5d2010-08-15 17:04:28 +00002142 ssize_t
2143 j;
2144
cristy3ed852e2009-09-05 21:47:34 +00002145 pixel=zero;
2146 if (image->matte == MagickFalse)
2147 {
2148 for (i=0; i < n; i++)
2149 {
2150 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2151 (contribution[i].pixel-contribution[0].pixel);
2152 alpha=contribution[i].weight;
2153 pixel.red+=alpha*(p+j)->red;
2154 pixel.green+=alpha*(p+j)->green;
2155 pixel.blue+=alpha*(p+j)->blue;
2156 pixel.opacity+=alpha*(p+j)->opacity;
2157 }
cristyce70c172010-01-07 17:15:30 +00002158 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2159 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2160 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2161 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002162 if ((image->colorspace == CMYKColorspace) &&
2163 (resize_image->colorspace == CMYKColorspace))
2164 {
2165 for (i=0; i < n; i++)
2166 {
2167 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2168 (contribution[i].pixel-contribution[0].pixel);
2169 alpha=contribution[i].weight;
2170 pixel.index+=alpha*indexes[j];
2171 }
cristyce70c172010-01-07 17:15:30 +00002172 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002173 }
2174 }
2175 else
2176 {
2177 MagickRealType
2178 gamma;
2179
2180 gamma=0.0;
2181 for (i=0; i < n; i++)
2182 {
2183 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2184 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002185 alpha=contribution[i].weight*QuantumScale*
2186 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002187 pixel.red+=alpha*(p+j)->red;
2188 pixel.green+=alpha*(p+j)->green;
2189 pixel.blue+=alpha*(p+j)->blue;
2190 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2191 gamma+=alpha;
2192 }
2193 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002194 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2195 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2196 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2197 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002198 if ((image->colorspace == CMYKColorspace) &&
2199 (resize_image->colorspace == CMYKColorspace))
2200 {
2201 for (i=0; i < n; i++)
2202 {
2203 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2204 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002205 alpha=contribution[i].weight*QuantumScale*
2206 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002207 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002208 }
cristyce70c172010-01-07 17:15:30 +00002209 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2210 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002211 }
2212 }
2213 if ((resize_image->storage_class == PseudoClass) &&
2214 (image->storage_class == PseudoClass))
2215 {
cristybb503372010-05-27 20:51:26 +00002216 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002217 1.0)+0.5);
2218 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2219 (contribution[i-start].pixel-contribution[0].pixel);
2220 resize_indexes[y]=indexes[j];
2221 }
2222 q++;
2223 }
2224 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2225 status=MagickFalse;
2226 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2227 {
2228 MagickBooleanType
2229 proceed;
2230
cristyb5d5f722009-11-04 03:03:49 +00002231#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002232 #pragma omp critical (MagickCore_HorizontalFilter)
2233#endif
cristy9af9b5d2010-08-15 17:04:28 +00002234 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002235 if (proceed == MagickFalse)
2236 status=MagickFalse;
2237 }
2238 }
2239 resize_view=DestroyCacheView(resize_view);
2240 image_view=DestroyCacheView(image_view);
2241 contributions=DestroyContributionThreadSet(contributions);
2242 return(status);
2243}
2244
2245static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2246 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002247 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002248{
cristyfa112112010-01-04 17:48:07 +00002249 CacheView
2250 *image_view,
2251 *resize_view;
2252
cristy3ed852e2009-09-05 21:47:34 +00002253 ClassType
2254 storage_class;
2255
2256 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002257 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002258
cristy3ed852e2009-09-05 21:47:34 +00002259 MagickBooleanType
2260 status;
2261
2262 MagickPixelPacket
2263 zero;
2264
2265 MagickRealType
2266 scale,
2267 support;
2268
cristy9af9b5d2010-08-15 17:04:28 +00002269 ssize_t
2270 y;
2271
cristy3ed852e2009-09-05 21:47:34 +00002272 /*
cristy9af9b5d2010-08-15 17:04:28 +00002273 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002274 */
cristy5d824382010-09-06 14:00:17 +00002275 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002276 support=scale*GetResizeFilterSupport(resize_filter);
2277 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2278 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2279 {
2280 InheritException(exception,&resize_image->exception);
2281 return(MagickFalse);
2282 }
2283 if (support < 0.5)
2284 {
2285 /*
nicolas07bac812010-09-19 18:47:02 +00002286 Support too small even for nearest neighbour: Reduce to point
2287 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002288 */
2289 support=(MagickRealType) 0.5;
2290 scale=1.0;
2291 }
2292 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2293 if (contributions == (ContributionInfo **) NULL)
2294 {
2295 (void) ThrowMagickException(exception,GetMagickModule(),
2296 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2297 return(MagickFalse);
2298 }
2299 status=MagickTrue;
2300 scale=1.0/scale;
2301 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2302 image_view=AcquireCacheView(image);
2303 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002304#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002305 #pragma omp parallel for shared(status)
2306#endif
cristybb503372010-05-27 20:51:26 +00002307 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002308 {
cristy3ed852e2009-09-05 21:47:34 +00002309 MagickRealType
2310 center,
2311 density;
2312
2313 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002314 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002315
2316 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002317 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002318
cristy03dbbd22010-09-19 23:04:47 +00002319 register ContributionInfo
2320 *restrict contribution;
2321
cristy3ed852e2009-09-05 21:47:34 +00002322 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002323 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002324
cristy9af9b5d2010-08-15 17:04:28 +00002325 register PixelPacket
2326 *restrict q;
2327
cristybb503372010-05-27 20:51:26 +00002328 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002329 x;
2330
cristy9af9b5d2010-08-15 17:04:28 +00002331 ssize_t
2332 n,
2333 start,
2334 stop;
cristy3ed852e2009-09-05 21:47:34 +00002335
2336 if (status == MagickFalse)
2337 continue;
cristy679e6962010-03-18 00:42:45 +00002338 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002339 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2340 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002341 density=0.0;
2342 contribution=contributions[GetOpenMPThreadId()];
2343 for (n=0; n < (stop-start); n++)
2344 {
2345 contribution[n].pixel=start+n;
2346 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2347 ((MagickRealType) (start+n)-center+0.5));
2348 density+=contribution[n].weight;
2349 }
2350 if ((density != 0.0) && (density != 1.0))
2351 {
cristybb503372010-05-27 20:51:26 +00002352 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002353 i;
2354
2355 /*
2356 Normalize.
2357 */
2358 density=1.0/density;
2359 for (i=0; i < n; i++)
2360 contribution[i].weight*=density;
2361 }
2362 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002363 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2364 exception);
cristy3ed852e2009-09-05 21:47:34 +00002365 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2366 exception);
2367 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2368 {
2369 status=MagickFalse;
2370 continue;
2371 }
2372 indexes=GetCacheViewVirtualIndexQueue(image_view);
2373 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002374 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002375 {
cristy3ed852e2009-09-05 21:47:34 +00002376 MagickPixelPacket
2377 pixel;
2378
2379 MagickRealType
2380 alpha;
2381
cristybb503372010-05-27 20:51:26 +00002382 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002383 i;
2384
cristy9af9b5d2010-08-15 17:04:28 +00002385 ssize_t
2386 j;
2387
cristy3ed852e2009-09-05 21:47:34 +00002388 pixel=zero;
2389 if (image->matte == MagickFalse)
2390 {
2391 for (i=0; i < n; i++)
2392 {
cristybb503372010-05-27 20:51:26 +00002393 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002394 image->columns+x);
2395 alpha=contribution[i].weight;
2396 pixel.red+=alpha*(p+j)->red;
2397 pixel.green+=alpha*(p+j)->green;
2398 pixel.blue+=alpha*(p+j)->blue;
2399 pixel.opacity+=alpha*(p+j)->opacity;
2400 }
cristyce70c172010-01-07 17:15:30 +00002401 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2402 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2403 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2404 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002405 if ((image->colorspace == CMYKColorspace) &&
2406 (resize_image->colorspace == CMYKColorspace))
2407 {
2408 for (i=0; i < n; i++)
2409 {
cristybb503372010-05-27 20:51:26 +00002410 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002411 image->columns+x);
2412 alpha=contribution[i].weight;
2413 pixel.index+=alpha*indexes[j];
2414 }
cristyce70c172010-01-07 17:15:30 +00002415 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002416 }
2417 }
2418 else
2419 {
2420 MagickRealType
2421 gamma;
2422
2423 gamma=0.0;
2424 for (i=0; i < n; i++)
2425 {
cristybb503372010-05-27 20:51:26 +00002426 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002427 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002428 alpha=contribution[i].weight*QuantumScale*
2429 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002430 pixel.red+=alpha*(p+j)->red;
2431 pixel.green+=alpha*(p+j)->green;
2432 pixel.blue+=alpha*(p+j)->blue;
2433 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2434 gamma+=alpha;
2435 }
2436 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002437 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2438 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2439 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2440 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002441 if ((image->colorspace == CMYKColorspace) &&
2442 (resize_image->colorspace == CMYKColorspace))
2443 {
2444 for (i=0; i < n; i++)
2445 {
cristybb503372010-05-27 20:51:26 +00002446 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002447 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002448 alpha=contribution[i].weight*QuantumScale*
2449 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002450 pixel.index+=alpha*indexes[j];
2451 }
cristyce70c172010-01-07 17:15:30 +00002452 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2453 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002454 }
2455 }
2456 if ((resize_image->storage_class == PseudoClass) &&
2457 (image->storage_class == PseudoClass))
2458 {
cristybb503372010-05-27 20:51:26 +00002459 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002460 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002461 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002462 image->columns+x);
2463 resize_indexes[x]=indexes[j];
2464 }
2465 q++;
2466 }
2467 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2468 status=MagickFalse;
2469 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2470 {
2471 MagickBooleanType
2472 proceed;
2473
cristyb5d5f722009-11-04 03:03:49 +00002474#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002475 #pragma omp critical (MagickCore_VerticalFilter)
2476#endif
cristy9af9b5d2010-08-15 17:04:28 +00002477 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002478 if (proceed == MagickFalse)
2479 status=MagickFalse;
2480 }
2481 }
2482 resize_view=DestroyCacheView(resize_view);
2483 image_view=DestroyCacheView(image_view);
2484 contributions=DestroyContributionThreadSet(contributions);
2485 return(status);
2486}
2487
cristybb503372010-05-27 20:51:26 +00002488MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2489 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002490 ExceptionInfo *exception)
2491{
2492#define WorkLoadFactor 0.265
2493
2494 FilterTypes
2495 filter_type;
2496
2497 Image
2498 *filter_image,
2499 *resize_image;
2500
cristy9af9b5d2010-08-15 17:04:28 +00002501 MagickOffsetType
2502 offset;
2503
cristy3ed852e2009-09-05 21:47:34 +00002504 MagickRealType
2505 x_factor,
2506 y_factor;
2507
2508 MagickSizeType
2509 span;
2510
2511 MagickStatusType
2512 status;
2513
2514 ResizeFilter
2515 *resize_filter;
2516
cristy3ed852e2009-09-05 21:47:34 +00002517 /*
2518 Acquire resize image.
2519 */
2520 assert(image != (Image *) NULL);
2521 assert(image->signature == MagickSignature);
2522 if (image->debug != MagickFalse)
2523 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2524 assert(exception != (ExceptionInfo *) NULL);
2525 assert(exception->signature == MagickSignature);
2526 if ((columns == 0) || (rows == 0))
2527 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2528 if ((columns == image->columns) && (rows == image->rows) &&
2529 (filter == UndefinedFilter) && (blur == 1.0))
2530 return(CloneImage(image,0,0,MagickTrue,exception));
2531 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2532 if (resize_image == (Image *) NULL)
2533 return(resize_image);
2534 /*
2535 Acquire resize filter.
2536 */
2537 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2538 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2539 if ((x_factor*y_factor) > WorkLoadFactor)
2540 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2541 else
2542 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2543 if (filter_image == (Image *) NULL)
2544 return(DestroyImage(resize_image));
2545 filter_type=LanczosFilter;
2546 if (filter != UndefinedFilter)
2547 filter_type=filter;
2548 else
2549 if ((x_factor == 1.0) && (y_factor == 1.0))
2550 filter_type=PointFilter;
2551 else
2552 if ((image->storage_class == PseudoClass) ||
2553 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2554 filter_type=MitchellFilter;
2555 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2556 exception);
2557 /*
2558 Resize image.
2559 */
cristy9af9b5d2010-08-15 17:04:28 +00002560 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002561 if ((x_factor*y_factor) > WorkLoadFactor)
2562 {
2563 span=(MagickSizeType) (filter_image->columns+rows);
2564 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002565 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002566 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002567 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002568 }
2569 else
2570 {
2571 span=(MagickSizeType) (filter_image->rows+columns);
2572 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002573 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002574 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002575 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002576 }
2577 /*
2578 Free resources.
2579 */
2580 filter_image=DestroyImage(filter_image);
2581 resize_filter=DestroyResizeFilter(resize_filter);
2582 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2583 return((Image *) NULL);
2584 resize_image->type=image->type;
2585 return(resize_image);
2586}
2587
2588/*
2589%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2590% %
2591% %
2592% %
2593% S a m p l e I m a g e %
2594% %
2595% %
2596% %
2597%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2598%
2599% SampleImage() scales an image to the desired dimensions with pixel
2600% sampling. Unlike other scaling methods, this method does not introduce
2601% any additional color into the scaled image.
2602%
2603% The format of the SampleImage method is:
2604%
cristybb503372010-05-27 20:51:26 +00002605% Image *SampleImage(const Image *image,const size_t columns,
2606% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002607%
2608% A description of each parameter follows:
2609%
2610% o image: the image.
2611%
2612% o columns: the number of columns in the sampled image.
2613%
2614% o rows: the number of rows in the sampled image.
2615%
2616% o exception: return any errors or warnings in this structure.
2617%
2618*/
cristybb503372010-05-27 20:51:26 +00002619MagickExport Image *SampleImage(const Image *image,const size_t columns,
2620 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002621{
2622#define SampleImageTag "Sample/Image"
2623
cristyc4c8d132010-01-07 01:58:38 +00002624 CacheView
2625 *image_view,
2626 *sample_view;
2627
cristy3ed852e2009-09-05 21:47:34 +00002628 Image
2629 *sample_image;
2630
cristy3ed852e2009-09-05 21:47:34 +00002631 MagickBooleanType
2632 status;
2633
cristy5f959472010-05-27 22:19:46 +00002634 MagickOffsetType
2635 progress;
2636
cristybb503372010-05-27 20:51:26 +00002637 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002638 x;
2639
cristy5f959472010-05-27 22:19:46 +00002640 ssize_t
2641 *x_offset,
2642 y;
2643
cristy3ed852e2009-09-05 21:47:34 +00002644 /*
2645 Initialize sampled image attributes.
2646 */
2647 assert(image != (const Image *) NULL);
2648 assert(image->signature == MagickSignature);
2649 if (image->debug != MagickFalse)
2650 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2651 assert(exception != (ExceptionInfo *) NULL);
2652 assert(exception->signature == MagickSignature);
2653 if ((columns == 0) || (rows == 0))
2654 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2655 if ((columns == image->columns) && (rows == image->rows))
2656 return(CloneImage(image,0,0,MagickTrue,exception));
2657 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2658 if (sample_image == (Image *) NULL)
2659 return((Image *) NULL);
2660 /*
2661 Allocate scan line buffer and column offset buffers.
2662 */
cristybb503372010-05-27 20:51:26 +00002663 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002664 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002665 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002666 {
2667 sample_image=DestroyImage(sample_image);
2668 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2669 }
cristybb503372010-05-27 20:51:26 +00002670 for (x=0; x < (ssize_t) sample_image->columns; x++)
2671 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002672 sample_image->columns);
2673 /*
2674 Sample each row.
2675 */
2676 status=MagickTrue;
2677 progress=0;
2678 image_view=AcquireCacheView(image);
2679 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002680#if defined(MAGICKCORE_OPENMP_SUPPORT)
2681 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002682#endif
cristybb503372010-05-27 20:51:26 +00002683 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002684 {
cristy3ed852e2009-09-05 21:47:34 +00002685 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002686 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002687
2688 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002689 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002690
2691 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002692 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002693
cristy3ed852e2009-09-05 21:47:34 +00002694 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002695 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002696
cristy03dbbd22010-09-19 23:04:47 +00002697 register ssize_t
2698 x;
2699
cristy9af9b5d2010-08-15 17:04:28 +00002700 ssize_t
2701 y_offset;
2702
cristy3ed852e2009-09-05 21:47:34 +00002703 if (status == MagickFalse)
2704 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002705 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2706 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002707 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2708 exception);
2709 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2710 exception);
2711 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2712 {
2713 status=MagickFalse;
2714 continue;
2715 }
2716 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2717 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2718 /*
2719 Sample each column.
2720 */
cristybb503372010-05-27 20:51:26 +00002721 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002722 *q++=p[x_offset[x]];
2723 if ((image->storage_class == PseudoClass) ||
2724 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002725 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002726 sample_indexes[x]=indexes[x_offset[x]];
2727 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2728 status=MagickFalse;
2729 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2730 {
2731 MagickBooleanType
2732 proceed;
2733
cristyb5d5f722009-11-04 03:03:49 +00002734#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002735 #pragma omp critical (MagickCore_SampleImage)
2736#endif
2737 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2738 if (proceed == MagickFalse)
2739 status=MagickFalse;
2740 }
2741 }
2742 image_view=DestroyCacheView(image_view);
2743 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002744 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002745 sample_image->type=image->type;
2746 return(sample_image);
2747}
2748
2749/*
2750%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2751% %
2752% %
2753% %
2754% S c a l e I m a g e %
2755% %
2756% %
2757% %
2758%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2759%
2760% ScaleImage() changes the size of an image to the given dimensions.
2761%
2762% The format of the ScaleImage method is:
2763%
cristybb503372010-05-27 20:51:26 +00002764% Image *ScaleImage(const Image *image,const size_t columns,
2765% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002766%
2767% A description of each parameter follows:
2768%
2769% o image: the image.
2770%
2771% o columns: the number of columns in the scaled image.
2772%
2773% o rows: the number of rows in the scaled image.
2774%
2775% o exception: return any errors or warnings in this structure.
2776%
2777*/
cristybb503372010-05-27 20:51:26 +00002778MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2779 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002780{
2781#define ScaleImageTag "Scale/Image"
2782
cristyed6cb232010-01-20 03:07:53 +00002783 CacheView
2784 *image_view,
2785 *scale_view;
2786
cristy3ed852e2009-09-05 21:47:34 +00002787 Image
2788 *scale_image;
2789
cristy3ed852e2009-09-05 21:47:34 +00002790 MagickBooleanType
2791 next_column,
2792 next_row,
2793 proceed;
2794
2795 MagickPixelPacket
2796 pixel,
2797 *scale_scanline,
2798 *scanline,
2799 *x_vector,
2800 *y_vector,
2801 zero;
2802
cristy3ed852e2009-09-05 21:47:34 +00002803 PointInfo
2804 scale,
2805 span;
2806
cristybb503372010-05-27 20:51:26 +00002807 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002808 i;
2809
cristy9af9b5d2010-08-15 17:04:28 +00002810 ssize_t
2811 number_rows,
2812 y;
2813
cristy3ed852e2009-09-05 21:47:34 +00002814 /*
2815 Initialize scaled image attributes.
2816 */
2817 assert(image != (const Image *) NULL);
2818 assert(image->signature == MagickSignature);
2819 if (image->debug != MagickFalse)
2820 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2821 assert(exception != (ExceptionInfo *) NULL);
2822 assert(exception->signature == MagickSignature);
2823 if ((columns == 0) || (rows == 0))
2824 return((Image *) NULL);
2825 if ((columns == image->columns) && (rows == image->rows))
2826 return(CloneImage(image,0,0,MagickTrue,exception));
2827 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2828 if (scale_image == (Image *) NULL)
2829 return((Image *) NULL);
2830 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2831 {
2832 InheritException(exception,&scale_image->exception);
2833 scale_image=DestroyImage(scale_image);
2834 return((Image *) NULL);
2835 }
2836 /*
2837 Allocate memory.
2838 */
2839 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2840 sizeof(*x_vector));
2841 scanline=x_vector;
2842 if (image->rows != scale_image->rows)
2843 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2844 sizeof(*scanline));
2845 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2846 scale_image->columns,sizeof(*scale_scanline));
2847 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2848 sizeof(*y_vector));
2849 if ((scanline == (MagickPixelPacket *) NULL) ||
2850 (scale_scanline == (MagickPixelPacket *) NULL) ||
2851 (x_vector == (MagickPixelPacket *) NULL) ||
2852 (y_vector == (MagickPixelPacket *) NULL))
2853 {
2854 scale_image=DestroyImage(scale_image);
2855 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2856 }
2857 /*
2858 Scale image.
2859 */
2860 number_rows=0;
2861 next_row=MagickTrue;
2862 span.y=1.0;
2863 scale.y=(double) scale_image->rows/(double) image->rows;
2864 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2865 sizeof(*y_vector));
2866 GetMagickPixelPacket(image,&pixel);
2867 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2868 i=0;
cristyed6cb232010-01-20 03:07:53 +00002869 image_view=AcquireCacheView(image);
2870 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002871 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002872 {
2873 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002874 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002875
2876 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002877 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002878
2879 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002880 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002881
cristy3ed852e2009-09-05 21:47:34 +00002882 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002883 *restrict s,
2884 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002885
2886 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002887 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002888
cristy9af9b5d2010-08-15 17:04:28 +00002889 register ssize_t
2890 x;
2891
cristyed6cb232010-01-20 03:07:53 +00002892 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2893 exception);
cristy3ed852e2009-09-05 21:47:34 +00002894 if (q == (PixelPacket *) NULL)
2895 break;
2896 scale_indexes=GetAuthenticIndexQueue(scale_image);
2897 if (scale_image->rows == image->rows)
2898 {
2899 /*
2900 Read a new scanline.
2901 */
cristyed6cb232010-01-20 03:07:53 +00002902 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2903 exception);
cristy3ed852e2009-09-05 21:47:34 +00002904 if (p == (const PixelPacket *) NULL)
2905 break;
cristyed6cb232010-01-20 03:07:53 +00002906 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002907 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002908 {
cristyce70c172010-01-07 17:15:30 +00002909 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2910 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2911 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002912 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002913 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002914 if (indexes != (IndexPacket *) NULL)
2915 x_vector[x].index=(MagickRealType) indexes[x];
2916 p++;
2917 }
2918 }
2919 else
2920 {
2921 /*
2922 Scale Y direction.
2923 */
2924 while (scale.y < span.y)
2925 {
cristy9af9b5d2010-08-15 17:04:28 +00002926 if ((next_row != MagickFalse) &&
2927 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002928 {
2929 /*
2930 Read a new scanline.
2931 */
cristyed6cb232010-01-20 03:07:53 +00002932 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2933 exception);
cristy3ed852e2009-09-05 21:47:34 +00002934 if (p == (const PixelPacket *) NULL)
2935 break;
cristyed6cb232010-01-20 03:07:53 +00002936 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002937 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002938 {
cristyce70c172010-01-07 17:15:30 +00002939 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2940 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2941 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002942 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002943 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002944 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002945 if (indexes != (IndexPacket *) NULL)
2946 x_vector[x].index=(MagickRealType) indexes[x];
2947 p++;
2948 }
2949 number_rows++;
2950 }
cristybb503372010-05-27 20:51:26 +00002951 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002952 {
2953 y_vector[x].red+=scale.y*x_vector[x].red;
2954 y_vector[x].green+=scale.y*x_vector[x].green;
2955 y_vector[x].blue+=scale.y*x_vector[x].blue;
2956 if (scale_image->matte != MagickFalse)
2957 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2958 if (scale_indexes != (IndexPacket *) NULL)
2959 y_vector[x].index+=scale.y*x_vector[x].index;
2960 }
2961 span.y-=scale.y;
2962 scale.y=(double) scale_image->rows/(double) image->rows;
2963 next_row=MagickTrue;
2964 }
cristybb503372010-05-27 20:51:26 +00002965 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002966 {
2967 /*
2968 Read a new scanline.
2969 */
cristyed6cb232010-01-20 03:07:53 +00002970 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2971 exception);
cristy3ed852e2009-09-05 21:47:34 +00002972 if (p == (const PixelPacket *) NULL)
2973 break;
cristyed6cb232010-01-20 03:07:53 +00002974 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002975 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002976 {
cristyce70c172010-01-07 17:15:30 +00002977 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2978 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2979 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002980 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00002981 x_vector[x].opacity=(MagickRealType)
2982 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002983 if (indexes != (IndexPacket *) NULL)
2984 x_vector[x].index=(MagickRealType) indexes[x];
2985 p++;
2986 }
2987 number_rows++;
2988 next_row=MagickFalse;
2989 }
2990 s=scanline;
cristybb503372010-05-27 20:51:26 +00002991 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002992 {
2993 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
2994 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
2995 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
2996 if (image->matte != MagickFalse)
2997 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
2998 if (scale_indexes != (IndexPacket *) NULL)
2999 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3000 s->red=pixel.red;
3001 s->green=pixel.green;
3002 s->blue=pixel.blue;
3003 if (scale_image->matte != MagickFalse)
3004 s->opacity=pixel.opacity;
3005 if (scale_indexes != (IndexPacket *) NULL)
3006 s->index=pixel.index;
3007 s++;
3008 y_vector[x]=zero;
3009 }
3010 scale.y-=span.y;
3011 if (scale.y <= 0)
3012 {
3013 scale.y=(double) scale_image->rows/(double) image->rows;
3014 next_row=MagickTrue;
3015 }
3016 span.y=1.0;
3017 }
3018 if (scale_image->columns == image->columns)
3019 {
3020 /*
3021 Transfer scanline to scaled image.
3022 */
3023 s=scanline;
cristybb503372010-05-27 20:51:26 +00003024 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003025 {
cristyce70c172010-01-07 17:15:30 +00003026 q->red=ClampToQuantum(s->red);
3027 q->green=ClampToQuantum(s->green);
3028 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003029 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003030 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003031 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003032 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003033 q++;
3034 s++;
3035 }
3036 }
3037 else
3038 {
3039 /*
3040 Scale X direction.
3041 */
3042 pixel=zero;
3043 next_column=MagickFalse;
3044 span.x=1.0;
3045 s=scanline;
3046 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003047 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003048 {
3049 scale.x=(double) scale_image->columns/(double) image->columns;
3050 while (scale.x >= span.x)
3051 {
3052 if (next_column != MagickFalse)
3053 {
3054 pixel=zero;
3055 t++;
3056 }
3057 pixel.red+=span.x*s->red;
3058 pixel.green+=span.x*s->green;
3059 pixel.blue+=span.x*s->blue;
3060 if (image->matte != MagickFalse)
3061 pixel.opacity+=span.x*s->opacity;
3062 if (scale_indexes != (IndexPacket *) NULL)
3063 pixel.index+=span.x*s->index;
3064 t->red=pixel.red;
3065 t->green=pixel.green;
3066 t->blue=pixel.blue;
3067 if (scale_image->matte != MagickFalse)
3068 t->opacity=pixel.opacity;
3069 if (scale_indexes != (IndexPacket *) NULL)
3070 t->index=pixel.index;
3071 scale.x-=span.x;
3072 span.x=1.0;
3073 next_column=MagickTrue;
3074 }
3075 if (scale.x > 0)
3076 {
3077 if (next_column != MagickFalse)
3078 {
3079 pixel=zero;
3080 next_column=MagickFalse;
3081 t++;
3082 }
3083 pixel.red+=scale.x*s->red;
3084 pixel.green+=scale.x*s->green;
3085 pixel.blue+=scale.x*s->blue;
3086 if (scale_image->matte != MagickFalse)
3087 pixel.opacity+=scale.x*s->opacity;
3088 if (scale_indexes != (IndexPacket *) NULL)
3089 pixel.index+=scale.x*s->index;
3090 span.x-=scale.x;
3091 }
3092 s++;
3093 }
3094 if (span.x > 0)
3095 {
3096 s--;
3097 pixel.red+=span.x*s->red;
3098 pixel.green+=span.x*s->green;
3099 pixel.blue+=span.x*s->blue;
3100 if (scale_image->matte != MagickFalse)
3101 pixel.opacity+=span.x*s->opacity;
3102 if (scale_indexes != (IndexPacket *) NULL)
3103 pixel.index+=span.x*s->index;
3104 }
3105 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003106 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003107 {
3108 t->red=pixel.red;
3109 t->green=pixel.green;
3110 t->blue=pixel.blue;
3111 if (scale_image->matte != MagickFalse)
3112 t->opacity=pixel.opacity;
3113 if (scale_indexes != (IndexPacket *) NULL)
3114 t->index=pixel.index;
3115 }
3116 /*
3117 Transfer scanline to scaled image.
3118 */
3119 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003120 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003121 {
cristyce70c172010-01-07 17:15:30 +00003122 q->red=ClampToQuantum(t->red);
3123 q->green=ClampToQuantum(t->green);
3124 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003125 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003126 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003127 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003128 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003129 t++;
3130 q++;
3131 }
3132 }
cristyed6cb232010-01-20 03:07:53 +00003133 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003134 break;
cristy96b16132010-08-29 17:19:52 +00003135 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3136 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003137 if (proceed == MagickFalse)
3138 break;
3139 }
cristyed6cb232010-01-20 03:07:53 +00003140 scale_view=DestroyCacheView(scale_view);
3141 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003142 /*
3143 Free allocated memory.
3144 */
3145 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3146 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3147 if (scale_image->rows != image->rows)
3148 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3149 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3150 scale_image->type=image->type;
3151 return(scale_image);
3152}
3153
anthony02b4cb42010-10-10 04:54:35 +00003154#if 0
3155 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003156/*
3157%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3158% %
3159% %
3160% %
3161+ S e t R e s i z e F i l t e r S u p p o r t %
3162% %
3163% %
3164% %
3165%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3166%
3167% SetResizeFilterSupport() specifies which IR filter to use to window
3168%
3169% The format of the SetResizeFilterSupport method is:
3170%
3171% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3172% const MagickRealType support)
3173%
3174% A description of each parameter follows:
3175%
3176% o resize_filter: the resize filter.
3177%
3178% o support: the filter spport radius.
3179%
3180*/
3181MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3182 const MagickRealType support)
3183{
3184 assert(resize_filter != (ResizeFilter *) NULL);
3185 assert(resize_filter->signature == MagickSignature);
3186 resize_filter->support=support;
3187}
anthony02b4cb42010-10-10 04:54:35 +00003188#endif
cristy3ed852e2009-09-05 21:47:34 +00003189
3190/*
3191%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3192% %
3193% %
3194% %
3195% T h u m b n a i l I m a g e %
3196% %
3197% %
3198% %
3199%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3200%
3201% ThumbnailImage() changes the size of an image to the given dimensions and
3202% removes any associated profiles. The goal is to produce small low cost
3203% thumbnail images suited for display on the Web.
3204%
3205% The format of the ThumbnailImage method is:
3206%
cristybb503372010-05-27 20:51:26 +00003207% Image *ThumbnailImage(const Image *image,const size_t columns,
3208% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003209%
3210% A description of each parameter follows:
3211%
3212% o image: the image.
3213%
3214% o columns: the number of columns in the scaled image.
3215%
3216% o rows: the number of rows in the scaled image.
3217%
3218% o exception: return any errors or warnings in this structure.
3219%
3220*/
cristy9af9b5d2010-08-15 17:04:28 +00003221MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3222 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003223{
3224#define SampleFactor 5
3225
3226 char
3227 value[MaxTextExtent];
3228
3229 const char
3230 *name;
3231
3232 Image
3233 *thumbnail_image;
3234
3235 MagickRealType
3236 x_factor,
3237 y_factor;
3238
cristybb503372010-05-27 20:51:26 +00003239 size_t
cristy3ed852e2009-09-05 21:47:34 +00003240 version;
3241
cristy9af9b5d2010-08-15 17:04:28 +00003242 struct stat
3243 attributes;
3244
cristy3ed852e2009-09-05 21:47:34 +00003245 assert(image != (Image *) NULL);
3246 assert(image->signature == MagickSignature);
3247 if (image->debug != MagickFalse)
3248 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3249 assert(exception != (ExceptionInfo *) NULL);
3250 assert(exception->signature == MagickSignature);
3251 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3252 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3253 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003254 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3255 exception);
cristy3ed852e2009-09-05 21:47:34 +00003256 else
3257 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003258 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3259 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003260 else
3261 {
3262 Image
3263 *sample_image;
3264
3265 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3266 exception);
3267 if (sample_image == (Image *) NULL)
3268 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003269 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3270 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003271 sample_image=DestroyImage(sample_image);
3272 }
3273 if (thumbnail_image == (Image *) NULL)
3274 return(thumbnail_image);
3275 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3276 if (thumbnail_image->matte == MagickFalse)
3277 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3278 thumbnail_image->depth=8;
3279 thumbnail_image->interlace=NoInterlace;
3280 /*
3281 Strip all profiles except color profiles.
3282 */
3283 ResetImageProfileIterator(thumbnail_image);
3284 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3285 {
3286 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3287 {
cristy2b726bd2010-01-11 01:05:39 +00003288 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003289 ResetImageProfileIterator(thumbnail_image);
3290 }
3291 name=GetNextImageProfile(thumbnail_image);
3292 }
3293 (void) DeleteImageProperty(thumbnail_image,"comment");
3294 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003295 if (strstr(image->magick_filename,"//") == (char *) NULL)
3296 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003297 image->magick_filename);
3298 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3299 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3300 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3301 {
cristye8c25f92010-06-03 00:53:06 +00003302 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003303 attributes.st_mtime);
3304 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3305 }
cristye8c25f92010-06-03 00:53:06 +00003306 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003307 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003308 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003309 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003310 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3311 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3312 LocaleLower(value);
3313 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3314 (void) SetImageProperty(thumbnail_image,"software",
3315 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003316 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3317 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003318 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003319 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003320 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003321 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003322 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3323 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003324 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3325 return(thumbnail_image);
3326}