blob: ef4bbf35c3e1feb07879144d60b74d96a9e18c8c [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
nicolas0d5e5322010-10-22 15:29:30 +0000540% blur=0.958033808). Now, Robidoux is the unique Keys cubic spline
541% filter satisfying the following condition:
542%
543% Robidoux exactly preserves images with only vertical or
544% horizontal features when performing 'no-op" with EWA distortion.
545%
546% That is, Robidoux is the BC-Spline with B=(-108 sqrt 2 + 228)/199
547% and C=(108 sqrt 2-29)/398. Robidoux turns out to be close to both
548% plain Mitchell and "sharpened" Lanczos2D. For example, it's first
549% crossing is (36 sqrt 2 + 123)/(72 sqrt 2 + 47) which is almost
550% identical to the first crossing of the other two.
anthony61b5ddd2010-10-05 02:33:31 +0000551%
nicolas07bac812010-09-19 18:47:02 +0000552% Special 'expert' options can be used to override any and all filter
553% settings. This is not advised unless you have expert knowledge of
554% the use of resampling filtered techniques. Check on the results of
555% your selections using the "filter:verbose" setting to make sure you
anthony61b5ddd2010-10-05 02:33:31 +0000556% get the exact filter that you are tring to achieve.
cristy3ed852e2009-09-05 21:47:34 +0000557%
anthony48f77622010-10-03 14:32:31 +0000558% "filter:filter" Select the main function associated with
559% this filter name, as the weighting function of the filter.
560% This can be used to set a windowing function as a weighting
561% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000562%
anthony7bdc0ed2010-09-15 01:52:32 +0000563% If a "filter:window" operation has not been provided, then a 'Box'
564% windowing function will be set to denote that no windowing function
565% is being used.
cristy3ed852e2009-09-05 21:47:34 +0000566%
567% "filter:window" Select this windowing function for the filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000568% While any filter could be used as a windowing function, using the
569% 'first lobe' of that filter over the whole support window, using a
anthony48f77622010-10-03 14:32:31 +0000570% non-windowing function is not advisible. If no weighting filter
571% function is specifed a 'SincFast' filter will be used.
cristy3ed852e2009-09-05 21:47:34 +0000572%
anthony48f77622010-10-03 14:32:31 +0000573% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000574% This a simpler method of setting filter support size that will
anthony48f77622010-10-03 14:32:31 +0000575% correctly handle the Sinc/Jinc switch for an operators filtering
anthony7bdc0ed2010-09-15 01:52:32 +0000576% requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000577%
578% "filter:support" Set the support size for filtering to the size given
anthony48f77622010-10-03 14:32:31 +0000579% This not recommended for Sinc/Jinc windowed filters (lobes should
anthony7bdc0ed2010-09-15 01:52:32 +0000580% be used instead). This will override any 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000581%
anthonyb6d08c52010-09-13 01:17:04 +0000582% "filter:win-support" Scale windowing function to this size instead.
583% This causes the windowing (or self-windowing Lagrange filter) to act
584% is if the support window it much much larger than what is actually
585% supplied to the calling operator. The filter however is still
586% clipped to the real support size given, by the support range suppiled
587% to the caller. If unset this will equal the normal filter support
588% size.
589%
cristy3ed852e2009-09-05 21:47:34 +0000590% "filter:blur" Scale the filter and support window by this amount.
591% A value >1 will generally result in a more burred image with
592% more ringing effects, while a value <1 will sharpen the
593% resulting image with more aliasing and Morie effects.
594%
anthonyf5e76ef2010-10-12 01:22:01 +0000595% "filter:sigma" The sigma value to use for the Gaussian filter only.
596% Defaults to '1/2' for orthogonal and 'sqrt(2)/2' for cylindrical
597% usage. It effectially provides a alturnative to 'blur' for Gaussians
598% without it also effecting the final 'practical support' size.
599%
cristy3ed852e2009-09-05 21:47:34 +0000600% "filter:b"
601% "filter:c" Override the preset B,C values for a Cubic type of filter
602% If only one of these are given it is assumes to be a 'Keys'
603% type of filter such that B+2C=1, where Keys 'alpha' value = C
604%
anthonyb6d08c52010-09-13 01:17:04 +0000605% "filter:verbose" Output the exact results of the filter selections
606% made, as well as plotting data for graphing the resulting filter
607% over support range (blur adjusted).
cristy3ed852e2009-09-05 21:47:34 +0000608%
609% Set a true un-windowed Sinc filter with 10 lobes (very slow)
anthony48f77622010-10-03 14:32:31 +0000610% -define filter:filter=Sinc
611% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000612%
anthony48f77622010-10-03 14:32:31 +0000613% For example force an 8 lobe Lanczos (Sinc or Jinc) filter...
cristy3ed852e2009-09-05 21:47:34 +0000614% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000615% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000616%
cristy3ed852e2009-09-05 21:47:34 +0000617% The format of the AcquireResizeFilter method is:
618%
619% ResizeFilter *AcquireResizeFilter(const Image *image,
620% const FilterTypes filter_type, const MagickBooleanType radial,
621% ExceptionInfo *exception)
622%
cristy33b1c162010-01-23 22:51:51 +0000623% A description of each parameter follows:
624%
cristy3ed852e2009-09-05 21:47:34 +0000625% o image: the image.
626%
nicolas07bac812010-09-19 18:47:02 +0000627% o filter: the filter type, defining a preset filter, window and
628% support. The artifact settings listed above will override
629% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000630%
anthony48f77622010-10-03 14:32:31 +0000631% o blur: blur the filter by this amount, use 1.0 if unknown. Image
632% artifact "filter:blur" will override this API call usage, including
633% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000634%
anthony48f77622010-10-03 14:32:31 +0000635% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
636% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000637%
638% o exception: return any errors or warnings in this structure.
639%
640*/
641MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000642 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000643 const MagickBooleanType cylindrical,ExceptionInfo *exception)
644{
645 const char
646 *artifact;
647
648 FilterTypes
649 filter_type,
650 window_type;
651
cristy3ed852e2009-09-05 21:47:34 +0000652 MagickRealType
653 B,
anthonyf5e76ef2010-10-12 01:22:01 +0000654 C,
655 sigma;
cristy3ed852e2009-09-05 21:47:34 +0000656
657 register ResizeFilter
658 *resize_filter;
659
cristy9af9b5d2010-08-15 17:04:28 +0000660 ssize_t
661 option;
662
cristy3ed852e2009-09-05 21:47:34 +0000663 /*
anthony48f77622010-10-03 14:32:31 +0000664 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000665 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000666 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
667 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
668 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000669
nicolas07bac812010-09-19 18:47:02 +0000670 WARNING: The order of this tabel must match the order of the
671 FilterTypes enumeration specified in "resample.h", or the filter
672 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000673
674 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000675 */
676 static struct
677 {
678 FilterTypes
679 filter,
680 window;
681 } const mapping[SentinelFilter] =
682 {
anthony462ee072010-09-27 12:34:02 +0000683 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
684 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
685 { BoxFilter, BoxFilter }, /* Box averaging filter */
686 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
687 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
688 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
689 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
690 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
691 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
692 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
693 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
694 { CatromFilter, BoxFilter }, /* Cubic interpolator */
695 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
696 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
anthony48f77622010-10-03 14:32:31 +0000697 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
698 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony462ee072010-09-27 12:34:02 +0000699 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
700 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
701 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
702 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
703 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
704 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
705 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
nicolas5835ee02010-10-10 20:40:15 +0000706 { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
anthonye5f06452010-10-12 05:48:17 +0000707 { Lanczos2DSharpFilter, JincFilter },/* SPECIAL: ditto sharpened */
708 { RobidouxFilter, BoxFilter }, /* SPECIAL: Keys cubic tuned for EWA */
cristy3ed852e2009-09-05 21:47:34 +0000709 };
710 /*
nicolas32f44eb2010-09-20 01:23:12 +0000711 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000712 function. The default support size for that filter as a weighting
713 function, the range to scale with to use that function as a sinc
714 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000715
anthony07a3f7f2010-09-16 03:03:11 +0000716 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000717 SincFast(), and CubicBC() functions, which may have multiple
718 filter to function associations.
719
720 See "filter:verbose" handling below for the function -> filter
721 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000722 */
723 static struct
724 {
725 MagickRealType
726 (*function)(const MagickRealType, const ResizeFilter*),
nicolas8eccc162010-10-16 19:48:13 +0000727 lobes, /* Default lobes/support size of the weighting filter. */
anthony450db502010-10-19 04:03:03 +0000728 scale, /* Support when function used as a windowing function
729 Typically equal to the location of the first zero crossing. */
730 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */
cristy3ed852e2009-09-05 21:47:34 +0000731 } const filters[SentinelFilter] =
732 {
anthony61b5ddd2010-10-05 02:33:31 +0000733 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
734 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
735 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
736 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
737 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
738 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
739 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
740 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
anthonyf5e76ef2010-10-12 01:22:01 +0000741 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */
anthony61b5ddd2010-10-05 02:33:31 +0000742 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
743 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
744 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
anthony853d6972010-10-08 06:01:31 +0000745 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
746 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
nicolasf8689f42010-10-18 16:14:08 +0000747 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000748 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
749 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
750 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
751 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
752 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
753 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
754 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
755 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
nicolas8eccc162010-10-16 19:48:13 +0000756 { Jinc, 2.0, 1.2196698912665045, 0.0, 0.0 },
757 /* Lanczos2D (Jinc-Jinc) */
758 { Jinc, 2.0, 1.1684849904329952, 0.0, 0.0 },
759 /* Lanczos2D sharpened with blur=0.958033808 */
anthony450db502010-10-19 04:03:03 +0000760 { CubicBC, 2.0, 1.1685777620836932,
nicolas0d5e5322010-10-22 15:29:30 +0000761 0.37821575509399867, 0.31089212245300067 }
762 /* Robidoux: Keys cubic close to Lanczos2D sharpened */
cristy3ed852e2009-09-05 21:47:34 +0000763 };
764 /*
anthony9a98fc62010-10-11 02:47:19 +0000765 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI)
anthony450db502010-10-19 04:03:03 +0000766 function being used as a filter. It is used by the "filter:lobes" expert
767 setting and for 'lobes' for Jinc functions in the previous table. This
768 way users do not have to deal with the highly irrational lobe sizes of the
anthony9a98fc62010-10-11 02:47:19 +0000769 Jinc filter.
anthony48f77622010-10-03 14:32:31 +0000770
nicolase473f722010-10-07 00:05:13 +0000771 Values taken from
anthony48f77622010-10-03 14:32:31 +0000772 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000773 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000774 */
775 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000776 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000777 {
nicolas8eccc162010-10-16 19:48:13 +0000778 1.2196698912665045,
779 2.2331305943815286,
780 3.2383154841662362,
781 4.2410628637960699,
782 5.2427643768701817,
783 6.2439216898644877,
784 7.244759868719957,
785 8.2453949139520427,
786 9.2458926849494673,
787 10.246293348754916,
788 11.246622794877883,
789 12.246898461138105,
790 13.247132522181061,
791 14.247333735806849,
anthonyc2d07db2010-09-15 23:47:40 +0000792 15.2475085630373,
nicolas8eccc162010-10-16 19:48:13 +0000793 16.247661874700962
cristy3ed852e2009-09-05 21:47:34 +0000794 };
795
cristy33b1c162010-01-23 22:51:51 +0000796 /*
797 Allocate resize filter.
798 */
cristy3ed852e2009-09-05 21:47:34 +0000799 assert(image != (const Image *) NULL);
800 assert(image->signature == MagickSignature);
801 if (image->debug != MagickFalse)
802 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
803 assert(UndefinedFilter < filter && filter < SentinelFilter);
804 assert(exception != (ExceptionInfo *) NULL);
805 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000806 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000807 if (resize_filter == (ResizeFilter *) NULL)
808 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000809 /*
810 Defaults for the requested filter.
811 */
812 filter_type=mapping[filter].filter;
813 window_type=mapping[filter].window;
anthonyf5e76ef2010-10-12 01:22:01 +0000814 resize_filter->blur = blur;
815 sigma = 0.5;
anthony48f77622010-10-03 14:32:31 +0000816 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000817 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000818 switch (filter_type)
819 {
820 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000821 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000822 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000823 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000824 break;
anthonyba5a7c32010-09-15 02:42:25 +0000825 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000826 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000827 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000828 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000829 break;
cristy33b1c162010-01-23 22:51:51 +0000830 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000831 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000832 filter_type=JincFilter;
833 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000834 break;
anthony08958462010-10-12 06:48:35 +0000835 case Lanczos2DSharpFilter:
nicolas1f4c0512010-10-20 20:19:30 +0000836 /* Sharpened by Nicolas Robidoux so as to optimize for minimal
837 * blurring of orthogonal lines
anthony08958462010-10-12 06:48:35 +0000838 */
839 resize_filter->blur *= 0.958033808;
840 break;
anthonyf5e76ef2010-10-12 01:22:01 +0000841 case GaussianFilter:
cristy68f103e2010-10-14 01:19:07 +0000842 sigma = (MagickRealType) (MagickSQ2/2.0); /* Cylindrical Gaussian sigma is sqrt(2)/2 */
anthonyf5e76ef2010-10-12 01:22:01 +0000843 break;
cristya782ecf2010-01-25 02:59:14 +0000844 default:
845 break;
cristy3ed852e2009-09-05 21:47:34 +0000846 }
anthony61b5ddd2010-10-05 02:33:31 +0000847 else
848 switch (filter_type)
849 {
850 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000851 case Lanczos2DSharpFilter:
nicolas45b58a92010-10-07 15:46:39 +0000852 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000853 window_type=SincFastFilter;
854 break;
855 default:
856 break;
857 }
858
cristy3ed852e2009-09-05 21:47:34 +0000859 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000860 if (artifact != (const char *) NULL)
861 {
cristy9af9b5d2010-08-15 17:04:28 +0000862 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000863 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000864 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000865 filter_type=(FilterTypes) option;
866 window_type=BoxFilter;
867 }
868 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000869 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000870 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000871 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000872 }
nicolas07bac812010-09-19 18:47:02 +0000873 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000874 artifact=GetImageArtifact(image,"filter:window");
875 if (artifact != (const char *) NULL)
876 {
cristy9af9b5d2010-08-15 17:04:28 +0000877 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000878 if ((UndefinedFilter < option) && (option < SentinelFilter))
879 {
880 if (option != LanczosFilter)
881 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000882 else
anthony48f77622010-10-03 14:32:31 +0000883 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000884 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000885 }
cristy33b1c162010-01-23 22:51:51 +0000886 }
cristy3ed852e2009-09-05 21:47:34 +0000887 }
cristy33b1c162010-01-23 22:51:51 +0000888 else
889 {
anthony48f77622010-10-03 14:32:31 +0000890 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000891 artifact=GetImageArtifact(image,"filter:window");
892 if (artifact != (const char *) NULL)
893 {
894 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
895 artifact);
896 if ((UndefinedFilter < option) && (option < SentinelFilter))
897 {
anthony61b5ddd2010-10-05 02:33:31 +0000898 filter_type=cylindrical != MagickFalse ?
899 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000900 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000901 }
902 }
903 }
nicolas07bac812010-09-19 18:47:02 +0000904 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000905 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000906 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000907 resize_filter->window=filters[window_type].function;
908 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000909 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000910
anthonyf5e76ef2010-10-12 01:22:01 +0000911 /* Filter Modifications for orthogonal/cylindrical usage */
anthony10b8bc82010-10-02 12:48:46 +0000912 if (cylindrical != MagickFalse)
913 switch (filter_type)
914 {
915 case PointFilter:
916 case BoxFilter:
917 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000918 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000919 break;
anthony81b8bf92010-10-02 13:54:34 +0000920 default:
921 break;
anthony10b8bc82010-10-02 12:48:46 +0000922 }
anthony61b5ddd2010-10-05 02:33:31 +0000923 else
924 switch (filter_type)
925 {
926 case Lanczos2DFilter:
anthonye5f06452010-10-12 05:48:17 +0000927 case Lanczos2DSharpFilter:
anthony853d6972010-10-08 06:01:31 +0000928 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000929 resize_filter->filter=SincFast;
930 break;
931 default:
932 break;
933 }
934
anthonyf5e76ef2010-10-12 01:22:01 +0000935 /*
936 ** More Expert Option Modifications
937 */
938
939 /* User Sigma Override - no support change */
940 artifact=GetImageArtifact(image,"filter:sigma");
941 if (artifact != (const char *) NULL)
942 sigma=StringToDouble(artifact);
943 /* Define coefficents for Gaussian (assumes no cubic window) */
944 if ( GaussianFilter ) {
945 resize_filter->coeff[0] = 1.0/(2.0*sigma*sigma);
cristy68f103e2010-10-14 01:19:07 +0000946 resize_filter->coeff[1] = (MagickRealType) (1.0/(Magick2PI*sigma*sigma)); /* unused */
anthonyf5e76ef2010-10-12 01:22:01 +0000947 }
948
949 /* Blur Override */
950 artifact=GetImageArtifact(image,"filter:blur");
951 if (artifact != (const char *) NULL)
952 resize_filter->blur=StringToDouble(artifact);
953 if (resize_filter->blur < MagickEpsilon)
954 resize_filter->blur=(MagickRealType) MagickEpsilon;
955
956 /* Support Overrides */
cristy3ed852e2009-09-05 21:47:34 +0000957 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000958 if (artifact != (const char *) NULL)
959 {
cristybb503372010-05-27 20:51:26 +0000960 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000961 lobes;
962
cristy96b16132010-08-29 17:19:52 +0000963 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000964 if (lobes < 1)
965 lobes=1;
966 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000967 }
anthony61b5ddd2010-10-05 02:33:31 +0000968 /* convert Jinc lobes to a real support value */
969 if (resize_filter->filter == Jinc)
970 {
971 if (resize_filter->support > 16)
972 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
973 else
974 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
975 }
976 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000977 artifact=GetImageArtifact(image,"filter:support");
978 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000979 resize_filter->support=fabs(StringToDouble(artifact));
980 /*
nicolas07bac812010-09-19 18:47:02 +0000981 Scale windowing function separatally to the support 'clipping'
982 window that calling operator is planning to actually use. (Expert
983 override)
cristy3ed852e2009-09-05 21:47:34 +0000984 */
anthony55f12332010-09-10 01:13:02 +0000985 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000986 artifact=GetImageArtifact(image,"filter:win-support");
987 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000988 resize_filter->window_support=fabs(StringToDouble(artifact));
989 /*
anthony1f90a6b2010-09-14 08:56:31 +0000990 Adjust window function scaling to the windowing support for
991 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +0000992 */
993 resize_filter->scale /= resize_filter->window_support;
anthonyf5e76ef2010-10-12 01:22:01 +0000994
anthony55f12332010-09-10 01:13:02 +0000995 /*
anthonyf5e76ef2010-10-12 01:22:01 +0000996 * Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +0000997 */
cristy3ed852e2009-09-05 21:47:34 +0000998 B=0.0;
999 C=0.0;
cristy33b1c162010-01-23 22:51:51 +00001000 if ((filters[filter_type].function == CubicBC) ||
1001 (filters[window_type].function == CubicBC))
1002 {
anthony2d9b8b52010-09-14 08:31:07 +00001003 B=filters[filter_type].B;
1004 C=filters[filter_type].C;
1005 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +00001006 {
anthony2d9b8b52010-09-14 08:31:07 +00001007 B=filters[window_type].B;
1008 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +00001009 }
cristy33b1c162010-01-23 22:51:51 +00001010 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001011 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001012 {
1013 B=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001014 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
anthonyf5e76ef2010-10-12 01:22:01 +00001015 artifact=GetImageArtifact(image,"filter:c"); /* user C override */
cristy33b1c162010-01-23 22:51:51 +00001016 if (artifact != (const char *) NULL)
1017 C=StringToDouble(artifact);
1018 }
1019 else
1020 {
1021 artifact=GetImageArtifact(image,"filter:c");
1022 if (artifact != (const char *) NULL)
1023 {
1024 C=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001025 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001026 }
1027 }
anthonyf5e76ef2010-10-12 01:22:01 +00001028 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */
1029 resize_filter->coeff[0]=(6.0-2.0*B)/6.0;
1030 resize_filter->coeff[1]=0.0;
1031 resize_filter->coeff[2]=(-18.0+12.0*B+6.0*C)/6.0;
1032 resize_filter->coeff[3]=(12.0-9.0*B-6.0*C)/6.0;
1033 resize_filter->coeff[4]=(8.0*B+24.0*C)/6.0;
1034 resize_filter->coeff[5]=(-12.0*B-48.0*C)/6.0;
1035 resize_filter->coeff[6]=(6.0*B+30.0*C)/6.0;
1036 resize_filter->coeff[7]=(-B-6.0*C)/6.0;
cristy3ed852e2009-09-05 21:47:34 +00001037 }
anthonyf5e76ef2010-10-12 01:22:01 +00001038
anthony55f12332010-09-10 01:13:02 +00001039 /*
nicolas07bac812010-09-19 18:47:02 +00001040 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001041 */
cristyf5b49372010-10-16 01:06:47 +00001042#if defined(MAGICKCORE_OPENMP_SUPPORT)
1043 #pragma omp master
1044 {
1045#endif
1046 artifact=GetImageArtifact(image,"filter:verbose");
1047 if (artifact != (const char *) NULL)
1048 {
1049 double
anthony450db502010-10-19 04:03:03 +00001050 support,
cristyf5b49372010-10-16 01:06:47 +00001051 x;
cristy3ed852e2009-09-05 21:47:34 +00001052
cristyf5b49372010-10-16 01:06:47 +00001053 /*
1054 Set the weighting function properly when the weighting
1055 function may not exactly match the filter of the same name.
1056 EG: a Point filter really uses a Box weighting function
1057 with a different support than is typically used.
anthony463be1d2010-09-26 01:07:36 +00001058
cristyf5b49372010-10-16 01:06:47 +00001059 */
1060 if (resize_filter->filter == Box) filter_type=BoxFilter;
1061 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1062 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
1063 if (resize_filter->filter == Jinc) filter_type=JincFilter;
1064 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
1065 /*
1066 Report Filter Details.
1067 */
1068 support=GetResizeFilterSupport(resize_filter); /* practical_support */
1069 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
1070 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1071 MagickFilterOptions,filter_type));
1072 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
1073 MagickFilterOptions, window_type));
1074 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
1075 (double) resize_filter->support);
1076 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
1077 (double) resize_filter->window_support);
1078 (void) fprintf(stdout,"# scale_blur = %.*g\n",GetMagickPrecision(),
1079 (double) resize_filter->blur);
1080 if ( filter_type == GaussianFilter )
1081 (void) fprintf(stdout,"# gaussian_sigma = %.*g\n",GetMagickPrecision(),
1082 (double) sigma);
1083 (void) fprintf(stdout,"# practical_support = %.*g\n",GetMagickPrecision(),
1084 (double) support);
1085 if ( filter_type == CubicFilter || window_type == CubicFilter )
1086 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1087 (double) B,GetMagickPrecision(),(double) C);
1088 (void) fprintf(stdout,"\n");
1089 /*
1090 Output values of resulting filter graph -- for graphing
1091 filter result.
1092 */
1093 for (x=0.0; x <= support; x+=0.01f)
1094 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1095 (double) GetResizeFilterWeight(resize_filter,x));
1096 /* A final value so gnuplot can graph the 'stop' properly. */
1097 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1098 0.0);
1099 }
1100 /* Output the above once only for each image - remove setting */
1101 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
1102#if defined(MAGICKCORE_OPENMP_SUPPORT)
1103 }
1104#endif
cristy3ed852e2009-09-05 21:47:34 +00001105 return(resize_filter);
1106}
1107
1108/*
1109%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1110% %
1111% %
1112% %
1113% A d a p t i v e R e s i z e I m a g e %
1114% %
1115% %
1116% %
1117%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1118%
1119% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1120%
1121% The format of the AdaptiveResizeImage method is:
1122%
cristy9af9b5d2010-08-15 17:04:28 +00001123% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1124% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001125%
1126% A description of each parameter follows:
1127%
1128% o image: the image.
1129%
1130% o columns: the number of columns in the resized image.
1131%
1132% o rows: the number of rows in the resized image.
1133%
1134% o exception: return any errors or warnings in this structure.
1135%
1136*/
1137MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001138 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001139{
1140#define AdaptiveResizeImageTag "Resize/Image"
1141
cristyc4c8d132010-01-07 01:58:38 +00001142 CacheView
1143 *resize_view;
1144
cristy3ed852e2009-09-05 21:47:34 +00001145 Image
1146 *resize_image;
1147
cristy3ed852e2009-09-05 21:47:34 +00001148 MagickBooleanType
1149 proceed;
1150
1151 MagickPixelPacket
1152 pixel;
1153
1154 PointInfo
1155 offset;
1156
1157 ResampleFilter
1158 *resample_filter;
1159
cristy9af9b5d2010-08-15 17:04:28 +00001160 ssize_t
1161 y;
1162
cristy3ed852e2009-09-05 21:47:34 +00001163 /*
1164 Adaptively resize image.
1165 */
1166 assert(image != (const Image *) NULL);
1167 assert(image->signature == MagickSignature);
1168 if (image->debug != MagickFalse)
1169 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1170 assert(exception != (ExceptionInfo *) NULL);
1171 assert(exception->signature == MagickSignature);
1172 if ((columns == 0) || (rows == 0))
1173 return((Image *) NULL);
1174 if ((columns == image->columns) && (rows == image->rows))
1175 return(CloneImage(image,0,0,MagickTrue,exception));
1176 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1177 if (resize_image == (Image *) NULL)
1178 return((Image *) NULL);
1179 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1180 {
1181 InheritException(exception,&resize_image->exception);
1182 resize_image=DestroyImage(resize_image);
1183 return((Image *) NULL);
1184 }
1185 GetMagickPixelPacket(image,&pixel);
1186 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001187 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001188 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001189 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001190 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001191 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001192 {
1193 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001194 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001195
cristybb503372010-05-27 20:51:26 +00001196 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001197 x;
1198
1199 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001200 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001201
1202 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1203 exception);
1204 if (q == (PixelPacket *) NULL)
1205 break;
1206 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1207 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001208 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001209 {
1210 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1211 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1212 &pixel);
1213 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1214 q++;
1215 }
1216 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1217 break;
cristy96b16132010-08-29 17:19:52 +00001218 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1219 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001220 if (proceed == MagickFalse)
1221 break;
1222 }
1223 resample_filter=DestroyResampleFilter(resample_filter);
1224 resize_view=DestroyCacheView(resize_view);
1225 return(resize_image);
1226}
1227
1228/*
1229%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1230% %
1231% %
1232% %
1233+ B e s s e l O r d e r O n e %
1234% %
1235% %
1236% %
1237%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1238%
1239% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001240% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001241%
1242% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1243%
1244% j1(x) = x*j1(x);
1245%
1246% For x in (8,inf)
1247%
1248% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1249%
1250% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1251%
1252% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1253% = 1/sqrt(2) * (sin(x) - cos(x))
1254% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1255% = -1/sqrt(2) * (sin(x) + cos(x))
1256%
1257% The format of the BesselOrderOne method is:
1258%
1259% MagickRealType BesselOrderOne(MagickRealType x)
1260%
1261% A description of each parameter follows:
1262%
1263% o x: MagickRealType value.
1264%
1265*/
1266
1267#undef I0
1268static MagickRealType I0(MagickRealType x)
1269{
1270 MagickRealType
1271 sum,
1272 t,
1273 y;
1274
cristybb503372010-05-27 20:51:26 +00001275 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001276 i;
1277
1278 /*
1279 Zeroth order Bessel function of the first kind.
1280 */
1281 sum=1.0;
1282 y=x*x/4.0;
1283 t=y;
1284 for (i=2; t > MagickEpsilon; i++)
1285 {
1286 sum+=t;
1287 t*=y/((MagickRealType) i*i);
1288 }
1289 return(sum);
1290}
1291
1292#undef J1
1293static MagickRealType J1(MagickRealType x)
1294{
1295 MagickRealType
1296 p,
1297 q;
1298
cristybb503372010-05-27 20:51:26 +00001299 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001300 i;
1301
1302 static const double
1303 Pone[] =
1304 {
1305 0.581199354001606143928050809e+21,
1306 -0.6672106568924916298020941484e+20,
1307 0.2316433580634002297931815435e+19,
1308 -0.3588817569910106050743641413e+17,
1309 0.2908795263834775409737601689e+15,
1310 -0.1322983480332126453125473247e+13,
1311 0.3413234182301700539091292655e+10,
1312 -0.4695753530642995859767162166e+7,
1313 0.270112271089232341485679099e+4
1314 },
1315 Qone[] =
1316 {
1317 0.11623987080032122878585294e+22,
1318 0.1185770712190320999837113348e+20,
1319 0.6092061398917521746105196863e+17,
1320 0.2081661221307607351240184229e+15,
1321 0.5243710262167649715406728642e+12,
1322 0.1013863514358673989967045588e+10,
1323 0.1501793594998585505921097578e+7,
1324 0.1606931573481487801970916749e+4,
1325 0.1e+1
1326 };
1327
1328 p=Pone[8];
1329 q=Qone[8];
1330 for (i=7; i >= 0; i--)
1331 {
1332 p=p*x*x+Pone[i];
1333 q=q*x*x+Qone[i];
1334 }
1335 return(p/q);
1336}
1337
1338#undef P1
1339static MagickRealType P1(MagickRealType x)
1340{
1341 MagickRealType
1342 p,
1343 q;
1344
cristybb503372010-05-27 20:51:26 +00001345 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001346 i;
1347
1348 static const double
1349 Pone[] =
1350 {
1351 0.352246649133679798341724373e+5,
1352 0.62758845247161281269005675e+5,
1353 0.313539631109159574238669888e+5,
1354 0.49854832060594338434500455e+4,
1355 0.2111529182853962382105718e+3,
1356 0.12571716929145341558495e+1
1357 },
1358 Qone[] =
1359 {
1360 0.352246649133679798068390431e+5,
1361 0.626943469593560511888833731e+5,
1362 0.312404063819041039923015703e+5,
1363 0.4930396490181088979386097e+4,
1364 0.2030775189134759322293574e+3,
1365 0.1e+1
1366 };
1367
1368 p=Pone[5];
1369 q=Qone[5];
1370 for (i=4; i >= 0; i--)
1371 {
1372 p=p*(8.0/x)*(8.0/x)+Pone[i];
1373 q=q*(8.0/x)*(8.0/x)+Qone[i];
1374 }
1375 return(p/q);
1376}
1377
1378#undef Q1
1379static MagickRealType Q1(MagickRealType x)
1380{
1381 MagickRealType
1382 p,
1383 q;
1384
cristybb503372010-05-27 20:51:26 +00001385 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001386 i;
1387
1388 static const double
1389 Pone[] =
1390 {
1391 0.3511751914303552822533318e+3,
1392 0.7210391804904475039280863e+3,
1393 0.4259873011654442389886993e+3,
1394 0.831898957673850827325226e+2,
1395 0.45681716295512267064405e+1,
1396 0.3532840052740123642735e-1
1397 },
1398 Qone[] =
1399 {
1400 0.74917374171809127714519505e+4,
1401 0.154141773392650970499848051e+5,
1402 0.91522317015169922705904727e+4,
1403 0.18111867005523513506724158e+4,
1404 0.1038187585462133728776636e+3,
1405 0.1e+1
1406 };
1407
1408 p=Pone[5];
1409 q=Qone[5];
1410 for (i=4; i >= 0; i--)
1411 {
1412 p=p*(8.0/x)*(8.0/x)+Pone[i];
1413 q=q*(8.0/x)*(8.0/x)+Qone[i];
1414 }
1415 return(p/q);
1416}
1417
1418static MagickRealType BesselOrderOne(MagickRealType x)
1419{
1420 MagickRealType
1421 p,
1422 q;
1423
1424 if (x == 0.0)
1425 return(0.0);
1426 p=x;
1427 if (x < 0.0)
1428 x=(-x);
1429 if (x < 8.0)
1430 return(p*J1(x));
1431 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1432 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1433 cos((double) x))));
1434 if (p < 0.0)
1435 q=(-q);
1436 return(q);
1437}
1438
1439/*
1440%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1441% %
1442% %
1443% %
1444+ D e s t r o y R e s i z e F i l t e r %
1445% %
1446% %
1447% %
1448%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1449%
1450% DestroyResizeFilter() destroy the resize filter.
1451%
cristya2ffd7e2010-03-10 20:50:30 +00001452% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001453%
1454% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1455%
1456% A description of each parameter follows:
1457%
1458% o resize_filter: the resize filter.
1459%
1460*/
1461MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1462{
1463 assert(resize_filter != (ResizeFilter *) NULL);
1464 assert(resize_filter->signature == MagickSignature);
1465 resize_filter->signature=(~MagickSignature);
1466 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1467 return(resize_filter);
1468}
1469
1470/*
1471%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1472% %
1473% %
1474% %
1475+ G e t R e s i z e F i l t e r S u p p o r t %
1476% %
1477% %
1478% %
1479%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1480%
1481% GetResizeFilterSupport() return the current support window size for this
1482% filter. Note that this may have been enlarged by filter:blur factor.
1483%
1484% The format of the GetResizeFilterSupport method is:
1485%
1486% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1487%
1488% A description of each parameter follows:
1489%
1490% o filter: Image filter to use.
1491%
1492*/
1493MagickExport MagickRealType GetResizeFilterSupport(
1494 const ResizeFilter *resize_filter)
1495{
1496 assert(resize_filter != (ResizeFilter *) NULL);
1497 assert(resize_filter->signature == MagickSignature);
1498 return(resize_filter->support*resize_filter->blur);
1499}
1500
1501/*
1502%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1503% %
1504% %
1505% %
1506+ G e t R e s i z e F i l t e r W e i g h t %
1507% %
1508% %
1509% %
1510%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1511%
1512% GetResizeFilterWeight evaluates the specified resize filter at the point x
1513% which usally lies between zero and the filters current 'support' and
1514% returns the weight of the filter function at that point.
1515%
1516% The format of the GetResizeFilterWeight method is:
1517%
1518% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1519% const MagickRealType x)
1520%
1521% A description of each parameter follows:
1522%
1523% o filter: the filter type.
1524%
1525% o x: the point.
1526%
1527*/
1528MagickExport MagickRealType GetResizeFilterWeight(
1529 const ResizeFilter *resize_filter,const MagickRealType x)
1530{
1531 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001532 scale,
1533 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001534
1535 /*
1536 Windowing function - scale the weighting filter by this amount.
1537 */
1538 assert(resize_filter != (ResizeFilter *) NULL);
1539 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001540 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001541 if ((resize_filter->window_support < MagickEpsilon) ||
1542 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001543 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001544 else
1545 {
anthony55f12332010-09-10 01:13:02 +00001546 scale=resize_filter->scale;
1547 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001548 }
anthony55f12332010-09-10 01:13:02 +00001549 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001550}
1551
1552/*
1553%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1554% %
1555% %
1556% %
1557% M a g n i f y I m a g e %
1558% %
1559% %
1560% %
1561%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1562%
1563% MagnifyImage() is a convenience method that scales an image proportionally
1564% to twice its size.
1565%
1566% The format of the MagnifyImage method is:
1567%
1568% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1569%
1570% A description of each parameter follows:
1571%
1572% o image: the image.
1573%
1574% o exception: return any errors or warnings in this structure.
1575%
1576*/
1577MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1578{
1579 Image
1580 *magnify_image;
1581
1582 assert(image != (Image *) NULL);
1583 assert(image->signature == MagickSignature);
1584 if (image->debug != MagickFalse)
1585 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1586 assert(exception != (ExceptionInfo *) NULL);
1587 assert(exception->signature == MagickSignature);
1588 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1589 1.0,exception);
1590 return(magnify_image);
1591}
1592
1593/*
1594%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1595% %
1596% %
1597% %
1598% M i n i f y I m a g e %
1599% %
1600% %
1601% %
1602%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1603%
1604% MinifyImage() is a convenience method that scales an image proportionally
1605% to half its size.
1606%
1607% The format of the MinifyImage method is:
1608%
1609% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1610%
1611% A description of each parameter follows:
1612%
1613% o image: the image.
1614%
1615% o exception: return any errors or warnings in this structure.
1616%
1617*/
1618MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1619{
1620 Image
1621 *minify_image;
1622
1623 assert(image != (Image *) NULL);
1624 assert(image->signature == MagickSignature);
1625 if (image->debug != MagickFalse)
1626 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1627 assert(exception != (ExceptionInfo *) NULL);
1628 assert(exception->signature == MagickSignature);
1629 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1630 1.0,exception);
1631 return(minify_image);
1632}
1633
1634/*
1635%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1636% %
1637% %
1638% %
1639% R e s a m p l e I m a g e %
1640% %
1641% %
1642% %
1643%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1644%
1645% ResampleImage() resize image in terms of its pixel size, so that when
1646% displayed at the given resolution it will be the same size in terms of
1647% real world units as the original image at the original resolution.
1648%
1649% The format of the ResampleImage method is:
1650%
1651% Image *ResampleImage(Image *image,const double x_resolution,
1652% const double y_resolution,const FilterTypes filter,const double blur,
1653% ExceptionInfo *exception)
1654%
1655% A description of each parameter follows:
1656%
1657% o image: the image to be resized to fit the given resolution.
1658%
1659% o x_resolution: the new image x resolution.
1660%
1661% o y_resolution: the new image y resolution.
1662%
1663% o filter: Image filter to use.
1664%
1665% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1666%
1667*/
1668MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1669 const double y_resolution,const FilterTypes filter,const double blur,
1670 ExceptionInfo *exception)
1671{
1672#define ResampleImageTag "Resample/Image"
1673
1674 Image
1675 *resample_image;
1676
cristybb503372010-05-27 20:51:26 +00001677 size_t
cristy3ed852e2009-09-05 21:47:34 +00001678 height,
1679 width;
1680
1681 /*
1682 Initialize sampled image attributes.
1683 */
1684 assert(image != (const Image *) NULL);
1685 assert(image->signature == MagickSignature);
1686 if (image->debug != MagickFalse)
1687 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1688 assert(exception != (ExceptionInfo *) NULL);
1689 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001690 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1691 72.0 : image->x_resolution)+0.5);
1692 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1693 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001694 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1695 if (resample_image != (Image *) NULL)
1696 {
1697 resample_image->x_resolution=x_resolution;
1698 resample_image->y_resolution=y_resolution;
1699 }
1700 return(resample_image);
1701}
1702#if defined(MAGICKCORE_LQR_DELEGATE)
1703
1704/*
1705%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1706% %
1707% %
1708% %
1709% L i q u i d R e s c a l e I m a g e %
1710% %
1711% %
1712% %
1713%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1714%
1715% LiquidRescaleImage() rescales image with seam carving.
1716%
1717% The format of the LiquidRescaleImage method is:
1718%
1719% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001720% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001721% const double delta_x,const double rigidity,ExceptionInfo *exception)
1722%
1723% A description of each parameter follows:
1724%
1725% o image: the image.
1726%
1727% o columns: the number of columns in the rescaled image.
1728%
1729% o rows: the number of rows in the rescaled image.
1730%
1731% o delta_x: maximum seam transversal step (0 means straight seams).
1732%
1733% o rigidity: introduce a bias for non-straight seams (typically 0).
1734%
1735% o exception: return any errors or warnings in this structure.
1736%
1737*/
cristy9af9b5d2010-08-15 17:04:28 +00001738MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1739 const size_t rows,const double delta_x,const double rigidity,
1740 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001741{
1742#define LiquidRescaleImageTag "Rescale/Image"
1743
cristyc5c6f662010-09-22 14:23:02 +00001744 CacheView
1745 *rescale_view;
1746
cristy3ed852e2009-09-05 21:47:34 +00001747 const char
1748 *map;
1749
1750 guchar
1751 *packet;
1752
1753 Image
1754 *rescale_image;
1755
1756 int
1757 x,
1758 y;
1759
1760 LqrCarver
1761 *carver;
1762
1763 LqrRetVal
1764 lqr_status;
1765
1766 MagickBooleanType
1767 status;
1768
1769 MagickPixelPacket
1770 pixel;
1771
1772 unsigned char
1773 *pixels;
1774
1775 /*
1776 Liquid rescale image.
1777 */
1778 assert(image != (const Image *) NULL);
1779 assert(image->signature == MagickSignature);
1780 if (image->debug != MagickFalse)
1781 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1782 assert(exception != (ExceptionInfo *) NULL);
1783 assert(exception->signature == MagickSignature);
1784 if ((columns == 0) || (rows == 0))
1785 return((Image *) NULL);
1786 if ((columns == image->columns) && (rows == image->rows))
1787 return(CloneImage(image,0,0,MagickTrue,exception));
1788 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001789 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001790 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1791 {
1792 Image
1793 *resize_image;
1794
cristybb503372010-05-27 20:51:26 +00001795 size_t
cristy3ed852e2009-09-05 21:47:34 +00001796 height,
1797 width;
1798
1799 /*
1800 Honor liquid resize size limitations.
1801 */
1802 for (width=image->columns; columns >= (2*width-1); width*=2);
1803 for (height=image->rows; rows >= (2*height-1); height*=2);
1804 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1805 exception);
1806 if (resize_image == (Image *) NULL)
1807 return((Image *) NULL);
1808 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1809 rigidity,exception);
1810 resize_image=DestroyImage(resize_image);
1811 return(rescale_image);
1812 }
1813 map="RGB";
1814 if (image->matte == MagickFalse)
1815 map="RGBA";
1816 if (image->colorspace == CMYKColorspace)
1817 {
1818 map="CMYK";
1819 if (image->matte == MagickFalse)
1820 map="CMYKA";
1821 }
1822 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1823 strlen(map)*sizeof(*pixels));
1824 if (pixels == (unsigned char *) NULL)
1825 return((Image *) NULL);
1826 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1827 pixels,exception);
1828 if (status == MagickFalse)
1829 {
1830 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1831 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1832 }
1833 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1834 if (carver == (LqrCarver *) NULL)
1835 {
1836 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1837 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1838 }
1839 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1840 lqr_status=lqr_carver_resize(carver,columns,rows);
1841 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1842 lqr_carver_get_height(carver),MagickTrue,exception);
1843 if (rescale_image == (Image *) NULL)
1844 {
1845 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1846 return((Image *) NULL);
1847 }
1848 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1849 {
1850 InheritException(exception,&rescale_image->exception);
1851 rescale_image=DestroyImage(rescale_image);
1852 return((Image *) NULL);
1853 }
1854 GetMagickPixelPacket(rescale_image,&pixel);
1855 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001856 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001857 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1858 {
1859 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001860 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001861
1862 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001863 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001864
anthony22aad252010-09-23 06:59:07 +00001865 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001866 if (q == (PixelPacket *) NULL)
1867 break;
cristyc5c6f662010-09-22 14:23:02 +00001868 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001869 pixel.red=QuantumRange*(packet[0]/255.0);
1870 pixel.green=QuantumRange*(packet[1]/255.0);
1871 pixel.blue=QuantumRange*(packet[2]/255.0);
1872 if (image->colorspace != CMYKColorspace)
1873 {
1874 if (image->matte == MagickFalse)
1875 pixel.opacity=QuantumRange*(packet[3]/255.0);
1876 }
1877 else
1878 {
1879 pixel.index=QuantumRange*(packet[3]/255.0);
1880 if (image->matte == MagickFalse)
1881 pixel.opacity=QuantumRange*(packet[4]/255.0);
1882 }
1883 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001884 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001885 break;
1886 }
cristyc5c6f662010-09-22 14:23:02 +00001887 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001888 /*
1889 Relinquish resources.
1890 */
1891 lqr_carver_destroy(carver);
1892 return(rescale_image);
1893}
1894#else
1895MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001896 const size_t magick_unused(columns),const size_t magick_unused(rows),
1897 const double magick_unused(delta_x),const double magick_unused(rigidity),
1898 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001899{
1900 assert(image != (const Image *) NULL);
1901 assert(image->signature == MagickSignature);
1902 if (image->debug != MagickFalse)
1903 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1904 assert(exception != (ExceptionInfo *) NULL);
1905 assert(exception->signature == MagickSignature);
1906 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1907 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1908 return((Image *) NULL);
1909}
1910#endif
1911
1912/*
1913%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1914% %
1915% %
1916% %
1917% R e s i z e I m a g e %
1918% %
1919% %
1920% %
1921%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1922%
1923% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001924% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001925%
1926% If an undefined filter is given the filter defaults to Mitchell for a
1927% colormapped image, a image with a matte channel, or if the image is
1928% enlarged. Otherwise the filter defaults to a Lanczos.
1929%
1930% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1931%
1932% The format of the ResizeImage method is:
1933%
cristybb503372010-05-27 20:51:26 +00001934% Image *ResizeImage(Image *image,const size_t columns,
1935% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001936% ExceptionInfo *exception)
1937%
1938% A description of each parameter follows:
1939%
1940% o image: the image.
1941%
1942% o columns: the number of columns in the scaled image.
1943%
1944% o rows: the number of rows in the scaled image.
1945%
1946% o filter: Image filter to use.
1947%
cristy9af9b5d2010-08-15 17:04:28 +00001948% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1949% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001950%
1951% o exception: return any errors or warnings in this structure.
1952%
1953*/
1954
1955typedef struct _ContributionInfo
1956{
1957 MagickRealType
1958 weight;
1959
cristybb503372010-05-27 20:51:26 +00001960 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001961 pixel;
1962} ContributionInfo;
1963
1964static ContributionInfo **DestroyContributionThreadSet(
1965 ContributionInfo **contribution)
1966{
cristybb503372010-05-27 20:51:26 +00001967 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001968 i;
1969
1970 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001971 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001972 if (contribution[i] != (ContributionInfo *) NULL)
1973 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1974 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001975 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001976 return(contribution);
1977}
1978
1979static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1980{
cristybb503372010-05-27 20:51:26 +00001981 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001982 i;
1983
1984 ContributionInfo
1985 **contribution;
1986
cristybb503372010-05-27 20:51:26 +00001987 size_t
cristy3ed852e2009-09-05 21:47:34 +00001988 number_threads;
1989
1990 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001991 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001992 sizeof(*contribution));
1993 if (contribution == (ContributionInfo **) NULL)
1994 return((ContributionInfo **) NULL);
1995 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00001996 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00001997 {
1998 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1999 sizeof(**contribution));
2000 if (contribution[i] == (ContributionInfo *) NULL)
2001 return(DestroyContributionThreadSet(contribution));
2002 }
2003 return(contribution);
2004}
2005
2006static inline double MagickMax(const double x,const double y)
2007{
2008 if (x > y)
2009 return(x);
2010 return(y);
2011}
2012
2013static inline double MagickMin(const double x,const double y)
2014{
2015 if (x < y)
2016 return(x);
2017 return(y);
2018}
2019
2020static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2021 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002022 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002023{
2024#define ResizeImageTag "Resize/Image"
2025
cristyfa112112010-01-04 17:48:07 +00002026 CacheView
2027 *image_view,
2028 *resize_view;
2029
cristy3ed852e2009-09-05 21:47:34 +00002030 ClassType
2031 storage_class;
2032
2033 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002034 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002035
cristy3ed852e2009-09-05 21:47:34 +00002036 MagickBooleanType
2037 status;
2038
2039 MagickPixelPacket
2040 zero;
2041
2042 MagickRealType
2043 scale,
2044 support;
2045
cristy9af9b5d2010-08-15 17:04:28 +00002046 ssize_t
2047 x;
2048
cristy3ed852e2009-09-05 21:47:34 +00002049 /*
2050 Apply filter to resize horizontally from image to resize image.
2051 */
cristy5d824382010-09-06 14:00:17 +00002052 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002053 support=scale*GetResizeFilterSupport(resize_filter);
2054 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2055 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2056 {
2057 InheritException(exception,&resize_image->exception);
2058 return(MagickFalse);
2059 }
2060 if (support < 0.5)
2061 {
2062 /*
nicolas07bac812010-09-19 18:47:02 +00002063 Support too small even for nearest neighbour: Reduce to point
2064 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002065 */
2066 support=(MagickRealType) 0.5;
2067 scale=1.0;
2068 }
2069 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2070 if (contributions == (ContributionInfo **) NULL)
2071 {
2072 (void) ThrowMagickException(exception,GetMagickModule(),
2073 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2074 return(MagickFalse);
2075 }
2076 status=MagickTrue;
2077 scale=1.0/scale;
2078 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2079 image_view=AcquireCacheView(image);
2080 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002081#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002082 #pragma omp parallel for shared(status)
2083#endif
cristybb503372010-05-27 20:51:26 +00002084 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002085 {
cristy3ed852e2009-09-05 21:47:34 +00002086 MagickRealType
2087 center,
2088 density;
2089
2090 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002091 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002092
2093 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002094 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002095
cristy03dbbd22010-09-19 23:04:47 +00002096 register ContributionInfo
2097 *restrict contribution;
2098
cristy3ed852e2009-09-05 21:47:34 +00002099 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002100 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002101
cristy3ed852e2009-09-05 21:47:34 +00002102 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002103 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002104
cristy03dbbd22010-09-19 23:04:47 +00002105 register ssize_t
2106 y;
2107
cristy9af9b5d2010-08-15 17:04:28 +00002108 ssize_t
2109 n,
2110 start,
2111 stop;
2112
cristy3ed852e2009-09-05 21:47:34 +00002113 if (status == MagickFalse)
2114 continue;
2115 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002116 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2117 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002118 density=0.0;
2119 contribution=contributions[GetOpenMPThreadId()];
2120 for (n=0; n < (stop-start); n++)
2121 {
2122 contribution[n].pixel=start+n;
2123 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2124 ((MagickRealType) (start+n)-center+0.5));
2125 density+=contribution[n].weight;
2126 }
2127 if ((density != 0.0) && (density != 1.0))
2128 {
cristybb503372010-05-27 20:51:26 +00002129 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002130 i;
2131
2132 /*
2133 Normalize.
2134 */
2135 density=1.0/density;
2136 for (i=0; i < n; i++)
2137 contribution[i].weight*=density;
2138 }
cristy9af9b5d2010-08-15 17:04:28 +00002139 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2140 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002141 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2142 exception);
2143 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2144 {
2145 status=MagickFalse;
2146 continue;
2147 }
2148 indexes=GetCacheViewVirtualIndexQueue(image_view);
2149 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002150 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002151 {
cristy3ed852e2009-09-05 21:47:34 +00002152 MagickPixelPacket
2153 pixel;
2154
2155 MagickRealType
2156 alpha;
2157
cristybb503372010-05-27 20:51:26 +00002158 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002159 i;
2160
cristy9af9b5d2010-08-15 17:04:28 +00002161 ssize_t
2162 j;
2163
cristy3ed852e2009-09-05 21:47:34 +00002164 pixel=zero;
2165 if (image->matte == MagickFalse)
2166 {
2167 for (i=0; i < n; i++)
2168 {
2169 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2170 (contribution[i].pixel-contribution[0].pixel);
2171 alpha=contribution[i].weight;
2172 pixel.red+=alpha*(p+j)->red;
2173 pixel.green+=alpha*(p+j)->green;
2174 pixel.blue+=alpha*(p+j)->blue;
2175 pixel.opacity+=alpha*(p+j)->opacity;
2176 }
cristyce70c172010-01-07 17:15:30 +00002177 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2178 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2179 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2180 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002181 if ((image->colorspace == CMYKColorspace) &&
2182 (resize_image->colorspace == CMYKColorspace))
2183 {
2184 for (i=0; i < n; i++)
2185 {
2186 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2187 (contribution[i].pixel-contribution[0].pixel);
2188 alpha=contribution[i].weight;
2189 pixel.index+=alpha*indexes[j];
2190 }
cristyce70c172010-01-07 17:15:30 +00002191 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002192 }
2193 }
2194 else
2195 {
2196 MagickRealType
2197 gamma;
2198
2199 gamma=0.0;
2200 for (i=0; i < n; i++)
2201 {
2202 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2203 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002204 alpha=contribution[i].weight*QuantumScale*
2205 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002206 pixel.red+=alpha*(p+j)->red;
2207 pixel.green+=alpha*(p+j)->green;
2208 pixel.blue+=alpha*(p+j)->blue;
2209 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2210 gamma+=alpha;
2211 }
2212 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002213 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2214 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2215 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2216 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002217 if ((image->colorspace == CMYKColorspace) &&
2218 (resize_image->colorspace == CMYKColorspace))
2219 {
2220 for (i=0; i < n; i++)
2221 {
2222 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2223 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002224 alpha=contribution[i].weight*QuantumScale*
2225 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002226 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002227 }
cristyce70c172010-01-07 17:15:30 +00002228 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2229 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002230 }
2231 }
2232 if ((resize_image->storage_class == PseudoClass) &&
2233 (image->storage_class == PseudoClass))
2234 {
cristybb503372010-05-27 20:51:26 +00002235 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002236 1.0)+0.5);
2237 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2238 (contribution[i-start].pixel-contribution[0].pixel);
2239 resize_indexes[y]=indexes[j];
2240 }
2241 q++;
2242 }
2243 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2244 status=MagickFalse;
2245 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2246 {
2247 MagickBooleanType
2248 proceed;
2249
cristyb5d5f722009-11-04 03:03:49 +00002250#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002251 #pragma omp critical (MagickCore_HorizontalFilter)
2252#endif
cristy9af9b5d2010-08-15 17:04:28 +00002253 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002254 if (proceed == MagickFalse)
2255 status=MagickFalse;
2256 }
2257 }
2258 resize_view=DestroyCacheView(resize_view);
2259 image_view=DestroyCacheView(image_view);
2260 contributions=DestroyContributionThreadSet(contributions);
2261 return(status);
2262}
2263
2264static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2265 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002266 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002267{
cristyfa112112010-01-04 17:48:07 +00002268 CacheView
2269 *image_view,
2270 *resize_view;
2271
cristy3ed852e2009-09-05 21:47:34 +00002272 ClassType
2273 storage_class;
2274
2275 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002276 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002277
cristy3ed852e2009-09-05 21:47:34 +00002278 MagickBooleanType
2279 status;
2280
2281 MagickPixelPacket
2282 zero;
2283
2284 MagickRealType
2285 scale,
2286 support;
2287
cristy9af9b5d2010-08-15 17:04:28 +00002288 ssize_t
2289 y;
2290
cristy3ed852e2009-09-05 21:47:34 +00002291 /*
cristy9af9b5d2010-08-15 17:04:28 +00002292 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002293 */
cristy5d824382010-09-06 14:00:17 +00002294 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002295 support=scale*GetResizeFilterSupport(resize_filter);
2296 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2297 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2298 {
2299 InheritException(exception,&resize_image->exception);
2300 return(MagickFalse);
2301 }
2302 if (support < 0.5)
2303 {
2304 /*
nicolas07bac812010-09-19 18:47:02 +00002305 Support too small even for nearest neighbour: Reduce to point
2306 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002307 */
2308 support=(MagickRealType) 0.5;
2309 scale=1.0;
2310 }
2311 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2312 if (contributions == (ContributionInfo **) NULL)
2313 {
2314 (void) ThrowMagickException(exception,GetMagickModule(),
2315 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2316 return(MagickFalse);
2317 }
2318 status=MagickTrue;
2319 scale=1.0/scale;
2320 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2321 image_view=AcquireCacheView(image);
2322 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002323#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002324 #pragma omp parallel for shared(status)
2325#endif
cristybb503372010-05-27 20:51:26 +00002326 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002327 {
cristy3ed852e2009-09-05 21:47:34 +00002328 MagickRealType
2329 center,
2330 density;
2331
2332 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002333 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002334
2335 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002336 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002337
cristy03dbbd22010-09-19 23:04:47 +00002338 register ContributionInfo
2339 *restrict contribution;
2340
cristy3ed852e2009-09-05 21:47:34 +00002341 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002342 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002343
cristy9af9b5d2010-08-15 17:04:28 +00002344 register PixelPacket
2345 *restrict q;
2346
cristybb503372010-05-27 20:51:26 +00002347 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002348 x;
2349
cristy9af9b5d2010-08-15 17:04:28 +00002350 ssize_t
2351 n,
2352 start,
2353 stop;
cristy3ed852e2009-09-05 21:47:34 +00002354
2355 if (status == MagickFalse)
2356 continue;
cristy679e6962010-03-18 00:42:45 +00002357 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002358 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2359 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002360 density=0.0;
2361 contribution=contributions[GetOpenMPThreadId()];
2362 for (n=0; n < (stop-start); n++)
2363 {
2364 contribution[n].pixel=start+n;
2365 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2366 ((MagickRealType) (start+n)-center+0.5));
2367 density+=contribution[n].weight;
2368 }
2369 if ((density != 0.0) && (density != 1.0))
2370 {
cristybb503372010-05-27 20:51:26 +00002371 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002372 i;
2373
2374 /*
2375 Normalize.
2376 */
2377 density=1.0/density;
2378 for (i=0; i < n; i++)
2379 contribution[i].weight*=density;
2380 }
2381 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002382 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2383 exception);
cristy3ed852e2009-09-05 21:47:34 +00002384 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2385 exception);
2386 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2387 {
2388 status=MagickFalse;
2389 continue;
2390 }
2391 indexes=GetCacheViewVirtualIndexQueue(image_view);
2392 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002393 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002394 {
cristy3ed852e2009-09-05 21:47:34 +00002395 MagickPixelPacket
2396 pixel;
2397
2398 MagickRealType
2399 alpha;
2400
cristybb503372010-05-27 20:51:26 +00002401 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002402 i;
2403
cristy9af9b5d2010-08-15 17:04:28 +00002404 ssize_t
2405 j;
2406
cristy3ed852e2009-09-05 21:47:34 +00002407 pixel=zero;
2408 if (image->matte == MagickFalse)
2409 {
2410 for (i=0; i < n; i++)
2411 {
cristybb503372010-05-27 20:51:26 +00002412 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002413 image->columns+x);
2414 alpha=contribution[i].weight;
2415 pixel.red+=alpha*(p+j)->red;
2416 pixel.green+=alpha*(p+j)->green;
2417 pixel.blue+=alpha*(p+j)->blue;
2418 pixel.opacity+=alpha*(p+j)->opacity;
2419 }
cristyce70c172010-01-07 17:15:30 +00002420 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2421 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2422 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2423 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002424 if ((image->colorspace == CMYKColorspace) &&
2425 (resize_image->colorspace == CMYKColorspace))
2426 {
2427 for (i=0; i < n; i++)
2428 {
cristybb503372010-05-27 20:51:26 +00002429 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002430 image->columns+x);
2431 alpha=contribution[i].weight;
2432 pixel.index+=alpha*indexes[j];
2433 }
cristyce70c172010-01-07 17:15:30 +00002434 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002435 }
2436 }
2437 else
2438 {
2439 MagickRealType
2440 gamma;
2441
2442 gamma=0.0;
2443 for (i=0; i < n; i++)
2444 {
cristybb503372010-05-27 20:51:26 +00002445 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002446 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002447 alpha=contribution[i].weight*QuantumScale*
2448 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002449 pixel.red+=alpha*(p+j)->red;
2450 pixel.green+=alpha*(p+j)->green;
2451 pixel.blue+=alpha*(p+j)->blue;
2452 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2453 gamma+=alpha;
2454 }
2455 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002456 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2457 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2458 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2459 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002460 if ((image->colorspace == CMYKColorspace) &&
2461 (resize_image->colorspace == CMYKColorspace))
2462 {
2463 for (i=0; i < n; i++)
2464 {
cristybb503372010-05-27 20:51:26 +00002465 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002466 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002467 alpha=contribution[i].weight*QuantumScale*
2468 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002469 pixel.index+=alpha*indexes[j];
2470 }
cristyce70c172010-01-07 17:15:30 +00002471 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2472 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002473 }
2474 }
2475 if ((resize_image->storage_class == PseudoClass) &&
2476 (image->storage_class == PseudoClass))
2477 {
cristybb503372010-05-27 20:51:26 +00002478 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002479 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002480 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002481 image->columns+x);
2482 resize_indexes[x]=indexes[j];
2483 }
2484 q++;
2485 }
2486 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2487 status=MagickFalse;
2488 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2489 {
2490 MagickBooleanType
2491 proceed;
2492
cristyb5d5f722009-11-04 03:03:49 +00002493#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002494 #pragma omp critical (MagickCore_VerticalFilter)
2495#endif
cristy9af9b5d2010-08-15 17:04:28 +00002496 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002497 if (proceed == MagickFalse)
2498 status=MagickFalse;
2499 }
2500 }
2501 resize_view=DestroyCacheView(resize_view);
2502 image_view=DestroyCacheView(image_view);
2503 contributions=DestroyContributionThreadSet(contributions);
2504 return(status);
2505}
2506
cristybb503372010-05-27 20:51:26 +00002507MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2508 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002509 ExceptionInfo *exception)
2510{
2511#define WorkLoadFactor 0.265
2512
2513 FilterTypes
2514 filter_type;
2515
2516 Image
2517 *filter_image,
2518 *resize_image;
2519
cristy9af9b5d2010-08-15 17:04:28 +00002520 MagickOffsetType
2521 offset;
2522
cristy3ed852e2009-09-05 21:47:34 +00002523 MagickRealType
2524 x_factor,
2525 y_factor;
2526
2527 MagickSizeType
2528 span;
2529
2530 MagickStatusType
2531 status;
2532
2533 ResizeFilter
2534 *resize_filter;
2535
cristy3ed852e2009-09-05 21:47:34 +00002536 /*
2537 Acquire resize image.
2538 */
2539 assert(image != (Image *) NULL);
2540 assert(image->signature == MagickSignature);
2541 if (image->debug != MagickFalse)
2542 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2543 assert(exception != (ExceptionInfo *) NULL);
2544 assert(exception->signature == MagickSignature);
2545 if ((columns == 0) || (rows == 0))
2546 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2547 if ((columns == image->columns) && (rows == image->rows) &&
2548 (filter == UndefinedFilter) && (blur == 1.0))
2549 return(CloneImage(image,0,0,MagickTrue,exception));
2550 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2551 if (resize_image == (Image *) NULL)
2552 return(resize_image);
2553 /*
2554 Acquire resize filter.
2555 */
2556 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2557 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2558 if ((x_factor*y_factor) > WorkLoadFactor)
2559 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2560 else
2561 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2562 if (filter_image == (Image *) NULL)
2563 return(DestroyImage(resize_image));
2564 filter_type=LanczosFilter;
2565 if (filter != UndefinedFilter)
2566 filter_type=filter;
2567 else
2568 if ((x_factor == 1.0) && (y_factor == 1.0))
2569 filter_type=PointFilter;
2570 else
2571 if ((image->storage_class == PseudoClass) ||
2572 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2573 filter_type=MitchellFilter;
2574 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2575 exception);
2576 /*
2577 Resize image.
2578 */
cristy9af9b5d2010-08-15 17:04:28 +00002579 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002580 if ((x_factor*y_factor) > WorkLoadFactor)
2581 {
2582 span=(MagickSizeType) (filter_image->columns+rows);
2583 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002584 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002585 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002586 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002587 }
2588 else
2589 {
2590 span=(MagickSizeType) (filter_image->rows+columns);
2591 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002592 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002593 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002594 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002595 }
2596 /*
2597 Free resources.
2598 */
2599 filter_image=DestroyImage(filter_image);
2600 resize_filter=DestroyResizeFilter(resize_filter);
2601 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2602 return((Image *) NULL);
2603 resize_image->type=image->type;
2604 return(resize_image);
2605}
2606
2607/*
2608%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2609% %
2610% %
2611% %
2612% S a m p l e I m a g e %
2613% %
2614% %
2615% %
2616%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2617%
2618% SampleImage() scales an image to the desired dimensions with pixel
2619% sampling. Unlike other scaling methods, this method does not introduce
2620% any additional color into the scaled image.
2621%
2622% The format of the SampleImage method is:
2623%
cristybb503372010-05-27 20:51:26 +00002624% Image *SampleImage(const Image *image,const size_t columns,
2625% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002626%
2627% A description of each parameter follows:
2628%
2629% o image: the image.
2630%
2631% o columns: the number of columns in the sampled image.
2632%
2633% o rows: the number of rows in the sampled image.
2634%
2635% o exception: return any errors or warnings in this structure.
2636%
2637*/
cristybb503372010-05-27 20:51:26 +00002638MagickExport Image *SampleImage(const Image *image,const size_t columns,
2639 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002640{
2641#define SampleImageTag "Sample/Image"
2642
cristyc4c8d132010-01-07 01:58:38 +00002643 CacheView
2644 *image_view,
2645 *sample_view;
2646
cristy3ed852e2009-09-05 21:47:34 +00002647 Image
2648 *sample_image;
2649
cristy3ed852e2009-09-05 21:47:34 +00002650 MagickBooleanType
2651 status;
2652
cristy5f959472010-05-27 22:19:46 +00002653 MagickOffsetType
2654 progress;
2655
cristybb503372010-05-27 20:51:26 +00002656 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002657 x;
2658
cristy5f959472010-05-27 22:19:46 +00002659 ssize_t
2660 *x_offset,
2661 y;
2662
cristy3ed852e2009-09-05 21:47:34 +00002663 /*
2664 Initialize sampled image attributes.
2665 */
2666 assert(image != (const Image *) NULL);
2667 assert(image->signature == MagickSignature);
2668 if (image->debug != MagickFalse)
2669 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2670 assert(exception != (ExceptionInfo *) NULL);
2671 assert(exception->signature == MagickSignature);
2672 if ((columns == 0) || (rows == 0))
2673 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2674 if ((columns == image->columns) && (rows == image->rows))
2675 return(CloneImage(image,0,0,MagickTrue,exception));
2676 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2677 if (sample_image == (Image *) NULL)
2678 return((Image *) NULL);
2679 /*
2680 Allocate scan line buffer and column offset buffers.
2681 */
cristybb503372010-05-27 20:51:26 +00002682 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002683 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002684 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002685 {
2686 sample_image=DestroyImage(sample_image);
2687 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2688 }
cristybb503372010-05-27 20:51:26 +00002689 for (x=0; x < (ssize_t) sample_image->columns; x++)
2690 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002691 sample_image->columns);
2692 /*
2693 Sample each row.
2694 */
2695 status=MagickTrue;
2696 progress=0;
2697 image_view=AcquireCacheView(image);
2698 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002699#if defined(MAGICKCORE_OPENMP_SUPPORT)
2700 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002701#endif
cristybb503372010-05-27 20:51:26 +00002702 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002703 {
cristy3ed852e2009-09-05 21:47:34 +00002704 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002705 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002706
2707 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002708 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002709
2710 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002711 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002712
cristy3ed852e2009-09-05 21:47:34 +00002713 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002714 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002715
cristy03dbbd22010-09-19 23:04:47 +00002716 register ssize_t
2717 x;
2718
cristy9af9b5d2010-08-15 17:04:28 +00002719 ssize_t
2720 y_offset;
2721
cristy3ed852e2009-09-05 21:47:34 +00002722 if (status == MagickFalse)
2723 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002724 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2725 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002726 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2727 exception);
2728 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2729 exception);
2730 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2731 {
2732 status=MagickFalse;
2733 continue;
2734 }
2735 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2736 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2737 /*
2738 Sample each column.
2739 */
cristybb503372010-05-27 20:51:26 +00002740 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002741 *q++=p[x_offset[x]];
2742 if ((image->storage_class == PseudoClass) ||
2743 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002744 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002745 sample_indexes[x]=indexes[x_offset[x]];
2746 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2747 status=MagickFalse;
2748 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2749 {
2750 MagickBooleanType
2751 proceed;
2752
cristyb5d5f722009-11-04 03:03:49 +00002753#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002754 #pragma omp critical (MagickCore_SampleImage)
2755#endif
2756 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2757 if (proceed == MagickFalse)
2758 status=MagickFalse;
2759 }
2760 }
2761 image_view=DestroyCacheView(image_view);
2762 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002763 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002764 sample_image->type=image->type;
2765 return(sample_image);
2766}
2767
2768/*
2769%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2770% %
2771% %
2772% %
2773% S c a l e I m a g e %
2774% %
2775% %
2776% %
2777%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2778%
2779% ScaleImage() changes the size of an image to the given dimensions.
2780%
2781% The format of the ScaleImage method is:
2782%
cristybb503372010-05-27 20:51:26 +00002783% Image *ScaleImage(const Image *image,const size_t columns,
2784% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002785%
2786% A description of each parameter follows:
2787%
2788% o image: the image.
2789%
2790% o columns: the number of columns in the scaled image.
2791%
2792% o rows: the number of rows in the scaled image.
2793%
2794% o exception: return any errors or warnings in this structure.
2795%
2796*/
cristybb503372010-05-27 20:51:26 +00002797MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2798 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002799{
2800#define ScaleImageTag "Scale/Image"
2801
cristyed6cb232010-01-20 03:07:53 +00002802 CacheView
2803 *image_view,
2804 *scale_view;
2805
cristy3ed852e2009-09-05 21:47:34 +00002806 Image
2807 *scale_image;
2808
cristy3ed852e2009-09-05 21:47:34 +00002809 MagickBooleanType
2810 next_column,
2811 next_row,
2812 proceed;
2813
2814 MagickPixelPacket
2815 pixel,
2816 *scale_scanline,
2817 *scanline,
2818 *x_vector,
2819 *y_vector,
2820 zero;
2821
cristy3ed852e2009-09-05 21:47:34 +00002822 PointInfo
2823 scale,
2824 span;
2825
cristybb503372010-05-27 20:51:26 +00002826 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002827 i;
2828
cristy9af9b5d2010-08-15 17:04:28 +00002829 ssize_t
2830 number_rows,
2831 y;
2832
cristy3ed852e2009-09-05 21:47:34 +00002833 /*
2834 Initialize scaled image attributes.
2835 */
2836 assert(image != (const Image *) NULL);
2837 assert(image->signature == MagickSignature);
2838 if (image->debug != MagickFalse)
2839 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2840 assert(exception != (ExceptionInfo *) NULL);
2841 assert(exception->signature == MagickSignature);
2842 if ((columns == 0) || (rows == 0))
2843 return((Image *) NULL);
2844 if ((columns == image->columns) && (rows == image->rows))
2845 return(CloneImage(image,0,0,MagickTrue,exception));
2846 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2847 if (scale_image == (Image *) NULL)
2848 return((Image *) NULL);
2849 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2850 {
2851 InheritException(exception,&scale_image->exception);
2852 scale_image=DestroyImage(scale_image);
2853 return((Image *) NULL);
2854 }
2855 /*
2856 Allocate memory.
2857 */
2858 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2859 sizeof(*x_vector));
2860 scanline=x_vector;
2861 if (image->rows != scale_image->rows)
2862 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2863 sizeof(*scanline));
2864 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2865 scale_image->columns,sizeof(*scale_scanline));
2866 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2867 sizeof(*y_vector));
2868 if ((scanline == (MagickPixelPacket *) NULL) ||
2869 (scale_scanline == (MagickPixelPacket *) NULL) ||
2870 (x_vector == (MagickPixelPacket *) NULL) ||
2871 (y_vector == (MagickPixelPacket *) NULL))
2872 {
2873 scale_image=DestroyImage(scale_image);
2874 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2875 }
2876 /*
2877 Scale image.
2878 */
2879 number_rows=0;
2880 next_row=MagickTrue;
2881 span.y=1.0;
2882 scale.y=(double) scale_image->rows/(double) image->rows;
2883 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2884 sizeof(*y_vector));
2885 GetMagickPixelPacket(image,&pixel);
2886 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2887 i=0;
cristyed6cb232010-01-20 03:07:53 +00002888 image_view=AcquireCacheView(image);
2889 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002890 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002891 {
2892 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002893 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002894
2895 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002896 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002897
2898 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002899 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002900
cristy3ed852e2009-09-05 21:47:34 +00002901 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002902 *restrict s,
2903 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002904
2905 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002906 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002907
cristy9af9b5d2010-08-15 17:04:28 +00002908 register ssize_t
2909 x;
2910
cristyed6cb232010-01-20 03:07:53 +00002911 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2912 exception);
cristy3ed852e2009-09-05 21:47:34 +00002913 if (q == (PixelPacket *) NULL)
2914 break;
2915 scale_indexes=GetAuthenticIndexQueue(scale_image);
2916 if (scale_image->rows == image->rows)
2917 {
2918 /*
2919 Read a new scanline.
2920 */
cristyed6cb232010-01-20 03:07:53 +00002921 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2922 exception);
cristy3ed852e2009-09-05 21:47:34 +00002923 if (p == (const PixelPacket *) NULL)
2924 break;
cristyed6cb232010-01-20 03:07:53 +00002925 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002926 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002927 {
cristyce70c172010-01-07 17:15:30 +00002928 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2929 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2930 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002931 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002932 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002933 if (indexes != (IndexPacket *) NULL)
2934 x_vector[x].index=(MagickRealType) indexes[x];
2935 p++;
2936 }
2937 }
2938 else
2939 {
2940 /*
2941 Scale Y direction.
2942 */
2943 while (scale.y < span.y)
2944 {
cristy9af9b5d2010-08-15 17:04:28 +00002945 if ((next_row != MagickFalse) &&
2946 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002947 {
2948 /*
2949 Read a new scanline.
2950 */
cristyed6cb232010-01-20 03:07:53 +00002951 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2952 exception);
cristy3ed852e2009-09-05 21:47:34 +00002953 if (p == (const PixelPacket *) NULL)
2954 break;
cristyed6cb232010-01-20 03:07:53 +00002955 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002956 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002957 {
cristyce70c172010-01-07 17:15:30 +00002958 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2959 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2960 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002961 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002962 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002963 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002964 if (indexes != (IndexPacket *) NULL)
2965 x_vector[x].index=(MagickRealType) indexes[x];
2966 p++;
2967 }
2968 number_rows++;
2969 }
cristybb503372010-05-27 20:51:26 +00002970 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002971 {
2972 y_vector[x].red+=scale.y*x_vector[x].red;
2973 y_vector[x].green+=scale.y*x_vector[x].green;
2974 y_vector[x].blue+=scale.y*x_vector[x].blue;
2975 if (scale_image->matte != MagickFalse)
2976 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2977 if (scale_indexes != (IndexPacket *) NULL)
2978 y_vector[x].index+=scale.y*x_vector[x].index;
2979 }
2980 span.y-=scale.y;
2981 scale.y=(double) scale_image->rows/(double) image->rows;
2982 next_row=MagickTrue;
2983 }
cristybb503372010-05-27 20:51:26 +00002984 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002985 {
2986 /*
2987 Read a new scanline.
2988 */
cristyed6cb232010-01-20 03:07:53 +00002989 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2990 exception);
cristy3ed852e2009-09-05 21:47:34 +00002991 if (p == (const PixelPacket *) NULL)
2992 break;
cristyed6cb232010-01-20 03:07:53 +00002993 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002994 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002995 {
cristyce70c172010-01-07 17:15:30 +00002996 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2997 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2998 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002999 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00003000 x_vector[x].opacity=(MagickRealType)
3001 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003002 if (indexes != (IndexPacket *) NULL)
3003 x_vector[x].index=(MagickRealType) indexes[x];
3004 p++;
3005 }
3006 number_rows++;
3007 next_row=MagickFalse;
3008 }
3009 s=scanline;
cristybb503372010-05-27 20:51:26 +00003010 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003011 {
3012 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3013 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3014 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3015 if (image->matte != MagickFalse)
3016 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3017 if (scale_indexes != (IndexPacket *) NULL)
3018 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3019 s->red=pixel.red;
3020 s->green=pixel.green;
3021 s->blue=pixel.blue;
3022 if (scale_image->matte != MagickFalse)
3023 s->opacity=pixel.opacity;
3024 if (scale_indexes != (IndexPacket *) NULL)
3025 s->index=pixel.index;
3026 s++;
3027 y_vector[x]=zero;
3028 }
3029 scale.y-=span.y;
3030 if (scale.y <= 0)
3031 {
3032 scale.y=(double) scale_image->rows/(double) image->rows;
3033 next_row=MagickTrue;
3034 }
3035 span.y=1.0;
3036 }
3037 if (scale_image->columns == image->columns)
3038 {
3039 /*
3040 Transfer scanline to scaled image.
3041 */
3042 s=scanline;
cristybb503372010-05-27 20:51:26 +00003043 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003044 {
cristyce70c172010-01-07 17:15:30 +00003045 q->red=ClampToQuantum(s->red);
3046 q->green=ClampToQuantum(s->green);
3047 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003048 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003049 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003050 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003051 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003052 q++;
3053 s++;
3054 }
3055 }
3056 else
3057 {
3058 /*
3059 Scale X direction.
3060 */
3061 pixel=zero;
3062 next_column=MagickFalse;
3063 span.x=1.0;
3064 s=scanline;
3065 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003066 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003067 {
3068 scale.x=(double) scale_image->columns/(double) image->columns;
3069 while (scale.x >= span.x)
3070 {
3071 if (next_column != MagickFalse)
3072 {
3073 pixel=zero;
3074 t++;
3075 }
3076 pixel.red+=span.x*s->red;
3077 pixel.green+=span.x*s->green;
3078 pixel.blue+=span.x*s->blue;
3079 if (image->matte != MagickFalse)
3080 pixel.opacity+=span.x*s->opacity;
3081 if (scale_indexes != (IndexPacket *) NULL)
3082 pixel.index+=span.x*s->index;
3083 t->red=pixel.red;
3084 t->green=pixel.green;
3085 t->blue=pixel.blue;
3086 if (scale_image->matte != MagickFalse)
3087 t->opacity=pixel.opacity;
3088 if (scale_indexes != (IndexPacket *) NULL)
3089 t->index=pixel.index;
3090 scale.x-=span.x;
3091 span.x=1.0;
3092 next_column=MagickTrue;
3093 }
3094 if (scale.x > 0)
3095 {
3096 if (next_column != MagickFalse)
3097 {
3098 pixel=zero;
3099 next_column=MagickFalse;
3100 t++;
3101 }
3102 pixel.red+=scale.x*s->red;
3103 pixel.green+=scale.x*s->green;
3104 pixel.blue+=scale.x*s->blue;
3105 if (scale_image->matte != MagickFalse)
3106 pixel.opacity+=scale.x*s->opacity;
3107 if (scale_indexes != (IndexPacket *) NULL)
3108 pixel.index+=scale.x*s->index;
3109 span.x-=scale.x;
3110 }
3111 s++;
3112 }
3113 if (span.x > 0)
3114 {
3115 s--;
3116 pixel.red+=span.x*s->red;
3117 pixel.green+=span.x*s->green;
3118 pixel.blue+=span.x*s->blue;
3119 if (scale_image->matte != MagickFalse)
3120 pixel.opacity+=span.x*s->opacity;
3121 if (scale_indexes != (IndexPacket *) NULL)
3122 pixel.index+=span.x*s->index;
3123 }
3124 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003125 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003126 {
3127 t->red=pixel.red;
3128 t->green=pixel.green;
3129 t->blue=pixel.blue;
3130 if (scale_image->matte != MagickFalse)
3131 t->opacity=pixel.opacity;
3132 if (scale_indexes != (IndexPacket *) NULL)
3133 t->index=pixel.index;
3134 }
3135 /*
3136 Transfer scanline to scaled image.
3137 */
3138 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003139 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003140 {
cristyce70c172010-01-07 17:15:30 +00003141 q->red=ClampToQuantum(t->red);
3142 q->green=ClampToQuantum(t->green);
3143 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003144 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003145 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003146 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003147 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003148 t++;
3149 q++;
3150 }
3151 }
cristyed6cb232010-01-20 03:07:53 +00003152 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003153 break;
cristy96b16132010-08-29 17:19:52 +00003154 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3155 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003156 if (proceed == MagickFalse)
3157 break;
3158 }
cristyed6cb232010-01-20 03:07:53 +00003159 scale_view=DestroyCacheView(scale_view);
3160 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003161 /*
3162 Free allocated memory.
3163 */
3164 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3165 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3166 if (scale_image->rows != image->rows)
3167 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3168 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3169 scale_image->type=image->type;
3170 return(scale_image);
3171}
3172
anthony02b4cb42010-10-10 04:54:35 +00003173#if 0
3174 THIS IS NOT USED -- to be removed
cristy3ed852e2009-09-05 21:47:34 +00003175/*
3176%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3177% %
3178% %
3179% %
3180+ S e t R e s i z e F i l t e r S u p p o r t %
3181% %
3182% %
3183% %
3184%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3185%
3186% SetResizeFilterSupport() specifies which IR filter to use to window
3187%
3188% The format of the SetResizeFilterSupport method is:
3189%
3190% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3191% const MagickRealType support)
3192%
3193% A description of each parameter follows:
3194%
3195% o resize_filter: the resize filter.
3196%
3197% o support: the filter spport radius.
3198%
3199*/
3200MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3201 const MagickRealType support)
3202{
3203 assert(resize_filter != (ResizeFilter *) NULL);
3204 assert(resize_filter->signature == MagickSignature);
3205 resize_filter->support=support;
3206}
anthony02b4cb42010-10-10 04:54:35 +00003207#endif
cristy3ed852e2009-09-05 21:47:34 +00003208
3209/*
3210%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3211% %
3212% %
3213% %
3214% T h u m b n a i l I m a g e %
3215% %
3216% %
3217% %
3218%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3219%
3220% ThumbnailImage() changes the size of an image to the given dimensions and
3221% removes any associated profiles. The goal is to produce small low cost
3222% thumbnail images suited for display on the Web.
3223%
3224% The format of the ThumbnailImage method is:
3225%
cristybb503372010-05-27 20:51:26 +00003226% Image *ThumbnailImage(const Image *image,const size_t columns,
3227% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003228%
3229% A description of each parameter follows:
3230%
3231% o image: the image.
3232%
3233% o columns: the number of columns in the scaled image.
3234%
3235% o rows: the number of rows in the scaled image.
3236%
3237% o exception: return any errors or warnings in this structure.
3238%
3239*/
cristy9af9b5d2010-08-15 17:04:28 +00003240MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3241 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003242{
3243#define SampleFactor 5
3244
3245 char
3246 value[MaxTextExtent];
3247
3248 const char
3249 *name;
3250
3251 Image
3252 *thumbnail_image;
3253
3254 MagickRealType
3255 x_factor,
3256 y_factor;
3257
cristybb503372010-05-27 20:51:26 +00003258 size_t
cristy3ed852e2009-09-05 21:47:34 +00003259 version;
3260
cristy9af9b5d2010-08-15 17:04:28 +00003261 struct stat
3262 attributes;
3263
cristy3ed852e2009-09-05 21:47:34 +00003264 assert(image != (Image *) NULL);
3265 assert(image->signature == MagickSignature);
3266 if (image->debug != MagickFalse)
3267 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3268 assert(exception != (ExceptionInfo *) NULL);
3269 assert(exception->signature == MagickSignature);
3270 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3271 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3272 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003273 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3274 exception);
cristy3ed852e2009-09-05 21:47:34 +00003275 else
3276 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003277 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3278 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003279 else
3280 {
3281 Image
3282 *sample_image;
3283
3284 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3285 exception);
3286 if (sample_image == (Image *) NULL)
3287 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003288 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3289 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003290 sample_image=DestroyImage(sample_image);
3291 }
3292 if (thumbnail_image == (Image *) NULL)
3293 return(thumbnail_image);
3294 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3295 if (thumbnail_image->matte == MagickFalse)
3296 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3297 thumbnail_image->depth=8;
3298 thumbnail_image->interlace=NoInterlace;
3299 /*
3300 Strip all profiles except color profiles.
3301 */
3302 ResetImageProfileIterator(thumbnail_image);
3303 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3304 {
3305 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3306 {
cristy2b726bd2010-01-11 01:05:39 +00003307 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003308 ResetImageProfileIterator(thumbnail_image);
3309 }
3310 name=GetNextImageProfile(thumbnail_image);
3311 }
3312 (void) DeleteImageProperty(thumbnail_image,"comment");
3313 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003314 if (strstr(image->magick_filename,"//") == (char *) NULL)
3315 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003316 image->magick_filename);
3317 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3318 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3319 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3320 {
cristye8c25f92010-06-03 00:53:06 +00003321 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003322 attributes.st_mtime);
3323 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3324 }
cristye8c25f92010-06-03 00:53:06 +00003325 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003326 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003327 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003328 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003329 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3330 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3331 LocaleLower(value);
3332 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3333 (void) SetImageProperty(thumbnail_image,"software",
3334 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003335 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3336 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003337 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003338 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003339 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003340 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003341 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3342 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003343 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3344 return(thumbnail_image);
3345}