blob: 096c0647c83f5ed2fa8576b690b6a4aa69289ed4 [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) */
88 cubic[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)
226 return(resize_filter->cubic[0]+x*(resize_filter->cubic[1]+x*
227 (resize_filter->cubic[2]+x*resize_filter->cubic[3])));
228 if (x < 2.0)
cristy679e6962010-03-18 00:42:45 +0000229 return(resize_filter->cubic[4]+x*(resize_filter->cubic[5]+x*
cristy03dbbd22010-09-19 23:04:47 +0000230 (resize_filter->cubic[6]+x*resize_filter->cubic[7])));
cristy3ed852e2009-09-05 21:47:34 +0000231 return(0.0);
232}
233
234static MagickRealType Gaussian(const MagickRealType x,
235 const ResizeFilter *magick_unused(resize_filter))
236{
cristy560d8182010-09-08 22:36:25 +0000237 /*
nicolas40477452010-09-27 23:42:08 +0000238 1D Gaussian with sigma=1/2:
anthonyb6d08c52010-09-13 01:17:04 +0000239 exp(-2 x^2)/sqrt(pi/2))
cristy560d8182010-09-08 22:36:25 +0000240 */
anthonyf21ee692010-09-15 12:06:44 +0000241 /*const MagickRealType alpha = (MagickRealType) (2.0/MagickSQ2PI);*/
cristy03dbbd22010-09-19 23:04:47 +0000242 return(exp((double) (-2.0*x*x)));
cristy3ed852e2009-09-05 21:47:34 +0000243}
244
245static MagickRealType Hanning(const MagickRealType x,
246 const ResizeFilter *magick_unused(resize_filter))
247{
248 /*
nicolas40477452010-09-27 23:42:08 +0000249 Cosine window function:
250 .5+.5cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000251 */
cristyc5c6f662010-09-22 14:23:02 +0000252 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000253 return(0.5+0.5*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000254}
255
256static MagickRealType Hamming(const MagickRealType x,
257 const ResizeFilter *magick_unused(resize_filter))
258{
259 /*
nicolas40477452010-09-27 23:42:08 +0000260 Offset cosine window function:
261 .54 + .46 cos(pi x).
cristy3ed852e2009-09-05 21:47:34 +0000262 */
cristyc5c6f662010-09-22 14:23:02 +0000263 const MagickRealType cospix = cos((double) (MagickPIL*x));
cristy560d8182010-09-08 22:36:25 +0000264 return(0.54+0.46*cospix);
cristy3ed852e2009-09-05 21:47:34 +0000265}
266
267static MagickRealType Kaiser(const MagickRealType x,
268 const ResizeFilter *magick_unused(resize_filter))
269{
270#define Alpha 6.5
271#define I0A (1.0/I0(Alpha))
272
273 /*
nicolas07bac812010-09-19 18:47:02 +0000274 Kaiser Windowing Function (bessel windowing): Alpha is a free
275 value from 5 to 8 (currently hardcoded to 6.5).
276 Future: make alpha the IOA pre-calculation, an 'expert' setting.
cristy3ed852e2009-09-05 21:47:34 +0000277 */
278 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
279}
280
281static MagickRealType Lagrange(const MagickRealType x,
282 const ResizeFilter *resize_filter)
283{
cristy3ed852e2009-09-05 21:47:34 +0000284 MagickRealType
285 value;
286
cristybb503372010-05-27 20:51:26 +0000287 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000288 i;
289
cristy9af9b5d2010-08-15 17:04:28 +0000290 ssize_t
291 n,
292 order;
293
cristy3ed852e2009-09-05 21:47:34 +0000294 /*
nicolas07bac812010-09-19 18:47:02 +0000295 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the
296 lagrange function and depends on the overall support window size
297 of the filter. That is: for a support of 2, it gives a lagrange-4
298 (piecewise cubic function).
cristy3ed852e2009-09-05 21:47:34 +0000299
nicolas07bac812010-09-19 18:47:02 +0000300 "n" identifies the piece of the piecewise polynomial.
cristy3ed852e2009-09-05 21:47:34 +0000301
nicolas07bac812010-09-19 18:47:02 +0000302 See Survey: Interpolation Methods, IEEE Transactions on Medical
303 Imaging, Vol 18, No 11, November 1999, p1049-1075, -- Equation 27
304 on p1064.
cristy3ed852e2009-09-05 21:47:34 +0000305 */
306 if (x > resize_filter->support)
307 return(0.0);
cristybb503372010-05-27 20:51:26 +0000308 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */
anthonyc2d07db2010-09-15 23:47:40 +0000309 /*n=(ssize_t)((1.0*order)/2.0+x); -- which piece does x belong to */
310 n = (ssize_t)(resize_filter->window_support + x);
cristy3ed852e2009-09-05 21:47:34 +0000311 value=1.0f;
312 for (i=0; i < order; i++)
313 if (i != n)
314 value*=(n-i-x)/(n-i);
315 return(value);
316}
317
318static MagickRealType Quadratic(const MagickRealType x,
319 const ResizeFilter *magick_unused(resize_filter))
320{
321 /*
322 2rd order (quadratic) B-Spline approximation of Gaussian.
323 */
324 if (x < 0.5)
325 return(0.75-x*x);
326 if (x < 1.5)
327 return(0.5*(x-1.5)*(x-1.5));
328 return(0.0);
329}
330
anthony07a3f7f2010-09-16 03:03:11 +0000331static MagickRealType Sinc(const MagickRealType x,
cristy3ed852e2009-09-05 21:47:34 +0000332 const ResizeFilter *magick_unused(resize_filter))
333{
anthony720660f2010-09-07 10:05:14 +0000334 /*
nicolas40477452010-09-27 23:42:08 +0000335 Scaled sinc(x) function using a trig call:
nicolas07bac812010-09-19 18:47:02 +0000336 sinc(x) == sin(pi x)/(pi x).
anthony720660f2010-09-07 10:05:14 +0000337 */
anthony2d9b8b52010-09-14 08:31:07 +0000338 if (x != 0.0)
cristy560d8182010-09-08 22:36:25 +0000339 {
cristyc5c6f662010-09-22 14:23:02 +0000340 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
nicolas07bac812010-09-19 18:47:02 +0000341 return(sin((double) pix)/pix);
cristy560d8182010-09-08 22:36:25 +0000342 }
nicolas2ffd3b22010-09-24 20:27:31 +0000343 return((MagickRealType) 1.0);
anthony720660f2010-09-07 10:05:14 +0000344}
345
anthonyba5a7c32010-09-15 02:42:25 +0000346static MagickRealType SincFast(const MagickRealType x,
anthony720660f2010-09-07 10:05:14 +0000347 const ResizeFilter *magick_unused(resize_filter))
348{
cristy560d8182010-09-08 22:36:25 +0000349 /*
350 Approximations of the sinc function sin(pi x)/(pi x) over the
351 interval [-4,4] constructed by Nicolas Robidoux and Chantal
352 Racette with funding from the Natural Sciences and Engineering
353 Research Council of Canada.
nicolas07bac812010-09-19 18:47:02 +0000354
355 Although the approximations are polynomials (for low order of
356 approximation) and quotients of polynomials (for higher order of
357 approximation) and consequently are similar in form to Taylor
358 polynomials/Pade approximants, the approximations are computed
359 with a completely different technique.
360
361 Summary: These approximations are "the best" in terms of bang
362 (accuracy) for the buck (flops). More specifically: Among the
363 polynomial quotients that can be computed using a fixed number of
364 flops (with a given "+ - * / budget"), the chosen polynomial
365 quotient is the one closest to the approximated function with
366 respect to maximum absolute relative error over the given
367 interval.
368
369 The Remez algorithm, as implemented in the boost library's minimax
nicolas3aab40c2010-09-19 21:14:15 +0000370 package, is the key to the construction:
nicolas07bac812010-09-19 18:47:02 +0000371 http://www.boost.org/doc/libs/1_36_0/libs/math/doc/...
372 ...sf_and_dist/html/math_toolkit/backgrounders/remez.html
cristy560d8182010-09-08 22:36:25 +0000373 */
nicolas3aab40c2010-09-19 21:14:15 +0000374 /*
375 If outside of the interval of approximation, use the standard trig
376 formula.
377 */
anthony2d9b8b52010-09-14 08:31:07 +0000378 if (x > 4.0)
cristy03dbbd22010-09-19 23:04:47 +0000379 {
cristyc5c6f662010-09-22 14:23:02 +0000380 const MagickRealType pix = (MagickRealType) (MagickPIL*x);
cristy03dbbd22010-09-19 23:04:47 +0000381 return(sin((double) pix)/pix);
382 }
anthony2d9b8b52010-09-14 08:31:07 +0000383 {
nicolas07bac812010-09-19 18:47:02 +0000384 /*
385 The approximations only depend on x^2 (sinc is an even
386 function).
387 */
388 const MagickRealType xx = x*x;
cristy83017922010-09-05 20:45:15 +0000389#if MAGICKCORE_QUANTUM_DEPTH <= 8
390 /*
anthony2d9b8b52010-09-14 08:31:07 +0000391 Maximum absolute relative error 6.3e-6 < 1/2^17.
cristy738e7562010-09-01 12:48:07 +0000392 */
393 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L;
394 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L;
395 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L;
396 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L;
397 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L;
398 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L;
399 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L;
400 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L;
nicolas3aab40c2010-09-19 21:14:15 +0000401 const MagickRealType p =
402 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
nicolas07bac812010-09-19 18:47:02 +0000403 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
anthony2d9b8b52010-09-14 08:31:07 +0000404#elif MAGICKCORE_QUANTUM_DEPTH <= 16
cristydbeb3eb2010-09-09 13:41:36 +0000405 /*
anthony2d9b8b52010-09-14 08:31:07 +0000406 Max. abs. rel. error 2.2e-8 < 1/2^25.
cristydbeb3eb2010-09-09 13:41:36 +0000407 */
408 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L;
409 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L;
nicolas3aab40c2010-09-19 21:14:15 +0000410 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L;
411 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L;
412 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L;
413 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L;
414 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L;
cristydbeb3eb2010-09-09 13:41:36 +0000415 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L;
416 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L;
417 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L;
nicolas3aab40c2010-09-19 21:14:15 +0000418 const MagickRealType p =
419 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 +0000420 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p);
nicolasc2d28f02010-09-27 18:56:15 +0000421#else
nicolas3aab40c2010-09-19 21:14:15 +0000422 /*
423 Max. abs. rel. error 1.2e-12 < 1/2^39.
424 */
425 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L;
426 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L;
427 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L;
428 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L;
429 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L;
430 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L;
431 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L;
432 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L;
433 const MagickRealType p =
434 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7))))));
435 const MagickRealType d0 = 1.0L;
436 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L;
437 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L;
438 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L;
439 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L;
440 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4)));
441 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p);
cristy657c6352010-08-29 14:05:08 +0000442#endif
cristy83017922010-09-05 20:45:15 +0000443 }
cristy3ed852e2009-09-05 21:47:34 +0000444}
445
446static MagickRealType Triangle(const MagickRealType x,
447 const ResizeFilter *magick_unused(resize_filter))
448{
449 /*
nicolas0edb0862010-09-19 18:56:19 +0000450 1st order (linear) B-Spline, bilinear interpolation, Tent 1D
451 filter, or a Bartlett 2D Cone filter.
cristy3ed852e2009-09-05 21:47:34 +0000452 */
453 if (x < 1.0)
454 return(1.0-x);
455 return(0.0);
456}
457
458static MagickRealType Welsh(const MagickRealType x,
459 const ResizeFilter *magick_unused(resize_filter))
460{
461 /*
462 Welsh parabolic windowing filter.
463 */
cristy560d8182010-09-08 22:36:25 +0000464 if (x < 1.0)
cristy3ed852e2009-09-05 21:47:34 +0000465 return(1.0-x*x);
466 return(0.0);
467}
468
469/*
470%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
471% %
472% %
473% %
474+ A c q u i r e R e s i z e F i l t e r %
475% %
476% %
477% %
478%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
479%
nicolas07bac812010-09-19 18:47:02 +0000480% AcquireResizeFilter() allocates the ResizeFilter structure. Choose
481% from these filters:
cristy3ed852e2009-09-05 21:47:34 +0000482%
483% FIR (Finite impulse Response) Filters
484% Box Triangle Quadratic
485% Cubic Hermite Catrom
486% Mitchell
487%
488% IIR (Infinite impulse Response) Filters
anthony48f77622010-10-03 14:32:31 +0000489% Gaussian Sinc Jinc (Bessel)
cristy3ed852e2009-09-05 21:47:34 +0000490%
anthony48f77622010-10-03 14:32:31 +0000491% Windowed Sinc/Jinc Filters
cristy3ed852e2009-09-05 21:47:34 +0000492% Blackman Hanning Hamming
anthony48f77622010-10-03 14:32:31 +0000493% Kaiser Lanczos
cristy3ed852e2009-09-05 21:47:34 +0000494%
anthony61b5ddd2010-10-05 02:33:31 +0000495% Special purpose Filters
496% SincFast Lanczos2D
497%
anthony48f77622010-10-03 14:32:31 +0000498% The users "-filter" selection is used to lookup the default 'expert'
499% settings for that filter from a internal table. However any provided
500% 'expert' settings (see below) may override this selection.
501%
502% FIR filters are used as is, and are limited to that filters support
nicolas07bac812010-09-19 18:47:02 +0000503% window (unless over-ridden). 'Gaussian' while classed as an IIR
anthonyc331dec2010-09-26 01:30:14 +0000504% filter, is also simply clipped by its support size (currently 1.5
anthony48f77622010-10-03 14:32:31 +0000505% ro approximatally 3*sigma as recommended by many references)
cristy3ed852e2009-09-05 21:47:34 +0000506%
anthony48f77622010-10-03 14:32:31 +0000507% The selection is typically either a windowed Sinc, or interpolated
nicolas07bac812010-09-19 18:47:02 +0000508% filter, for use by functions such as ResizeImage(). However if a
anthony48f77622010-10-03 14:32:31 +0000509% 'cylindrical' filter flag is requested, any default Sinc weighting
510% and windowing functions will be promoted to cylindrical Jinc form of
511% function.
cristy3ed852e2009-09-05 21:47:34 +0000512%
anthony48f77622010-10-03 14:32:31 +0000513% Directly requesting 'Sinc' or 'Jinc' will force the use of that
514% filter function without any windowing. This is not recommended,
515% except by image processing experts or in expert options. Selecting a
516% window filtering version of these functions is better.
cristy3ed852e2009-09-05 21:47:34 +0000517%
anthony48f77622010-10-03 14:32:31 +0000518% Lanczos is a special case of a Sinc-windowed Sinc, (or Jinc-Jinc for
519% the cylindrical case) but defaulting to 3-lobe support, rather that
520% the default 4 lobe support of the other windowed sinc/jinc filters.
cristy3ed852e2009-09-05 21:47:34 +0000521%
anthony48f77622010-10-03 14:32:31 +0000522% Two forms of the 'Sinc' function are available: Sinc and SincFast.
nicolas07bac812010-09-19 18:47:02 +0000523% Sinc is computed using the traditional sin(pi*x)/(pi*x); it is
524% selected if the user specifically specifies the use of a Sinc
525% filter. SincFast uses highly accurate (and fast) polynomial (low Q)
anthony48f77622010-10-03 14:32:31 +0000526% and rational (high Q) approximations, and will be used by default in
527% most cases.
anthonyba5a7c32010-09-15 02:42:25 +0000528%
nicolasaab02f32010-10-08 00:27:07 +0000529% The Lanczos2D filter is often just a normal 2-lobed Lanczos
530% filter. If selected as a cylindrical (radial) filter, the support
531% of the 2-lobed Jinc windowed Jinc is modified (through the blur
532% property) in order to make the results sharper.
anthony61b5ddd2010-10-05 02:33:31 +0000533%
nicolas07bac812010-09-19 18:47:02 +0000534% Special 'expert' options can be used to override any and all filter
535% settings. This is not advised unless you have expert knowledge of
536% the use of resampling filtered techniques. Check on the results of
537% your selections using the "filter:verbose" setting to make sure you
anthony61b5ddd2010-10-05 02:33:31 +0000538% get the exact filter that you are tring to achieve.
cristy3ed852e2009-09-05 21:47:34 +0000539%
anthony48f77622010-10-03 14:32:31 +0000540% "filter:filter" Select the main function associated with
541% this filter name, as the weighting function of the filter.
542% This can be used to set a windowing function as a weighting
543% function, for special purposes, such as graphing.
cristy3ed852e2009-09-05 21:47:34 +0000544%
anthony7bdc0ed2010-09-15 01:52:32 +0000545% If a "filter:window" operation has not been provided, then a 'Box'
546% windowing function will be set to denote that no windowing function
547% is being used.
cristy3ed852e2009-09-05 21:47:34 +0000548%
549% "filter:window" Select this windowing function for the filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000550% While any filter could be used as a windowing function, using the
551% 'first lobe' of that filter over the whole support window, using a
anthony48f77622010-10-03 14:32:31 +0000552% non-windowing function is not advisible. If no weighting filter
553% function is specifed a 'SincFast' filter will be used.
cristy3ed852e2009-09-05 21:47:34 +0000554%
anthony48f77622010-10-03 14:32:31 +0000555% "filter:lobes" Number of lobes to use for the Sinc/Jinc filter.
anthony7bdc0ed2010-09-15 01:52:32 +0000556% This a simpler method of setting filter support size that will
anthony48f77622010-10-03 14:32:31 +0000557% correctly handle the Sinc/Jinc switch for an operators filtering
anthony7bdc0ed2010-09-15 01:52:32 +0000558% requirements. Only integers should be given.
cristy3ed852e2009-09-05 21:47:34 +0000559%
560% "filter:support" Set the support size for filtering to the size given
anthony48f77622010-10-03 14:32:31 +0000561% This not recommended for Sinc/Jinc windowed filters (lobes should
anthony7bdc0ed2010-09-15 01:52:32 +0000562% be used instead). This will override any 'filter:lobes' option.
cristy3ed852e2009-09-05 21:47:34 +0000563%
anthonyb6d08c52010-09-13 01:17:04 +0000564% "filter:win-support" Scale windowing function to this size instead.
565% This causes the windowing (or self-windowing Lagrange filter) to act
566% is if the support window it much much larger than what is actually
567% supplied to the calling operator. The filter however is still
568% clipped to the real support size given, by the support range suppiled
569% to the caller. If unset this will equal the normal filter support
570% size.
571%
cristy3ed852e2009-09-05 21:47:34 +0000572% "filter:blur" Scale the filter and support window by this amount.
573% A value >1 will generally result in a more burred image with
574% more ringing effects, while a value <1 will sharpen the
575% resulting image with more aliasing and Morie effects.
576%
cristy3ed852e2009-09-05 21:47:34 +0000577% "filter:b"
578% "filter:c" Override the preset B,C values for a Cubic type of filter
579% If only one of these are given it is assumes to be a 'Keys'
580% type of filter such that B+2C=1, where Keys 'alpha' value = C
581%
anthonyb6d08c52010-09-13 01:17:04 +0000582% "filter:verbose" Output the exact results of the filter selections
583% made, as well as plotting data for graphing the resulting filter
584% over support range (blur adjusted).
cristy3ed852e2009-09-05 21:47:34 +0000585%
586% Set a true un-windowed Sinc filter with 10 lobes (very slow)
anthony48f77622010-10-03 14:32:31 +0000587% -define filter:filter=Sinc
588% -define filter:lobes=8
cristy3ed852e2009-09-05 21:47:34 +0000589%
anthony48f77622010-10-03 14:32:31 +0000590% For example force an 8 lobe Lanczos (Sinc or Jinc) filter...
cristy3ed852e2009-09-05 21:47:34 +0000591% -filter Lanczos
anthony48f77622010-10-03 14:32:31 +0000592% -define filter:lobes=8
anthony7bdc0ed2010-09-15 01:52:32 +0000593%
cristy3ed852e2009-09-05 21:47:34 +0000594% The format of the AcquireResizeFilter method is:
595%
596% ResizeFilter *AcquireResizeFilter(const Image *image,
597% const FilterTypes filter_type, const MagickBooleanType radial,
598% ExceptionInfo *exception)
599%
cristy33b1c162010-01-23 22:51:51 +0000600% A description of each parameter follows:
601%
cristy3ed852e2009-09-05 21:47:34 +0000602% o image: the image.
603%
nicolas07bac812010-09-19 18:47:02 +0000604% o filter: the filter type, defining a preset filter, window and
605% support. The artifact settings listed above will override
606% those selections.
cristy3ed852e2009-09-05 21:47:34 +0000607%
anthony48f77622010-10-03 14:32:31 +0000608% o blur: blur the filter by this amount, use 1.0 if unknown. Image
609% artifact "filter:blur" will override this API call usage, including
610% any internal change (such as for cylindrical usage).
cristy3ed852e2009-09-05 21:47:34 +0000611%
anthony48f77622010-10-03 14:32:31 +0000612% o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical
613% (radial) filter (Jinc)
cristy3ed852e2009-09-05 21:47:34 +0000614%
615% o exception: return any errors or warnings in this structure.
616%
617*/
618MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
cristyf59a8922010-02-28 19:51:23 +0000619 const FilterTypes filter,const MagickRealType blur,
cristy3ed852e2009-09-05 21:47:34 +0000620 const MagickBooleanType cylindrical,ExceptionInfo *exception)
621{
622 const char
623 *artifact;
624
625 FilterTypes
626 filter_type,
627 window_type;
628
cristy3ed852e2009-09-05 21:47:34 +0000629 MagickRealType
630 B,
631 C;
632
633 register ResizeFilter
634 *resize_filter;
635
cristy9af9b5d2010-08-15 17:04:28 +0000636 ssize_t
637 option;
638
cristy3ed852e2009-09-05 21:47:34 +0000639 /*
anthony48f77622010-10-03 14:32:31 +0000640 Table Mapping given Filter, into Weighting and Windowing functions.
nicolas07bac812010-09-19 18:47:02 +0000641 A 'Box' windowing function means its a simble non-windowed filter.
anthony48f77622010-10-03 14:32:31 +0000642 An 'SincFast' filter function could be upgraded to a 'Jinc' filter
643 if a "cylindrical", unless a 'Sinc' or 'SincFast' filter was
644 specifically requested.
anthony449887b2010-09-10 02:23:33 +0000645
nicolas07bac812010-09-19 18:47:02 +0000646 WARNING: The order of this tabel must match the order of the
647 FilterTypes enumeration specified in "resample.h", or the filter
648 names will not match the filter being setup.
anthony1e2d5ca2010-09-10 02:24:35 +0000649
650 You can check filter setups with the "filter:verbose" setting.
cristy3ed852e2009-09-05 21:47:34 +0000651 */
652 static struct
653 {
654 FilterTypes
655 filter,
656 window;
657 } const mapping[SentinelFilter] =
658 {
anthony462ee072010-09-27 12:34:02 +0000659 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */
660 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */
661 { BoxFilter, BoxFilter }, /* Box averaging filter */
662 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */
663 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
664 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */
665 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */
666 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */
667 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */
668 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
669 { CubicFilter, BoxFilter }, /* Cubic B-Spline */
670 { CatromFilter, BoxFilter }, /* Cubic interpolator */
671 { MitchellFilter, BoxFilter }, /* 'Ideal' cubic filter */
672 { LanczosFilter, SincFastFilter }, /* SPECIAL: 3-lobed sinc-sinc */
anthony48f77622010-10-03 14:32:31 +0000673 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */
674 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */
anthony462ee072010-09-27 12:34:02 +0000675 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */
676 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */
677 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */
678 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
679 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */
680 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */
681 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */
anthony61b5ddd2010-10-05 02:33:31 +0000682 { Lanczos2DFilter, JincFilter }, /* SPECIAL: 2-lobed jinc-jinc */
cristy3ed852e2009-09-05 21:47:34 +0000683 };
684 /*
nicolas32f44eb2010-09-20 01:23:12 +0000685 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000686 function. The default support size for that filter as a weighting
687 function, the range to scale with to use that function as a sinc
688 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000689
anthony07a3f7f2010-09-16 03:03:11 +0000690 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000691 SincFast(), and CubicBC() functions, which may have multiple
692 filter to function associations.
693
694 See "filter:verbose" handling below for the function -> filter
695 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000696 */
697 static struct
698 {
699 MagickRealType
700 (*function)(const MagickRealType, const ResizeFilter*),
anthony61b5ddd2010-10-05 02:33:31 +0000701 lobes, /* default lobes/support size of the weighting filter */
anthony2d9b8b52010-09-14 08:31:07 +0000702 scale, /* windowing function range, for scaling windowing function */
anthony933b4bf2010-09-10 02:31:37 +0000703 B,C; /* Cubic Filter factors for a CubicBC function, else ignored */
cristy3ed852e2009-09-05 21:47:34 +0000704 } const filters[SentinelFilter] =
705 {
anthony61b5ddd2010-10-05 02:33:31 +0000706 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
707 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
708 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
709 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
710 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
711 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
712 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
713 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
714 { Gaussian, 1.5, 1.5, 0.0, 0.0 }, /* Gaussian */
715 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
716 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
717 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
718 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
719 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed sinc-sinc */
720 { Jinc, 3.0, 1.21967, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
721 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
722 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
723 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
724 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
725 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
726 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
727 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
728 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
729 { Jinc, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2D, adjusted below */
cristy3ed852e2009-09-05 21:47:34 +0000730 };
731 /*
nicolase473f722010-10-07 00:05:13 +0000732 The known zero crossings of the Jinc() or more accurately the
733 Jinc(x*PI) function being used as a filter. It is used by the
734 "filter:lobes" for support selection, so users do not have to deal
735 with the highly irrational sizes of the 'lobes' of the Jinc
736 filter.
anthony48f77622010-10-03 14:32:31 +0000737
nicolase473f722010-10-07 00:05:13 +0000738 Values taken from
anthony48f77622010-10-03 14:32:31 +0000739 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000740 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000741 */
742 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000743 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000744 {
anthonyc2d07db2010-09-15 23:47:40 +0000745 1.21966989126651,
746 2.23313059438153,
747 3.23831548416624,
748 4.24106286379607,
749 5.24276437687019,
750 6.24392168986449,
751 7.24475986871996,
752 8.24539491395205,
753 9.24589268494948,
754 10.2462933487549,
755 11.2466227948779,
756 12.2468984611381,
757 13.2471325221811,
758 14.2473337358069,
759 15.2475085630373,
760 16.247661874701
cristy3ed852e2009-09-05 21:47:34 +0000761 };
762
cristy33b1c162010-01-23 22:51:51 +0000763 /*
764 Allocate resize filter.
765 */
cristy3ed852e2009-09-05 21:47:34 +0000766 assert(image != (const Image *) NULL);
767 assert(image->signature == MagickSignature);
768 if (image->debug != MagickFalse)
769 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
770 assert(UndefinedFilter < filter && filter < SentinelFilter);
771 assert(exception != (ExceptionInfo *) NULL);
772 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000773 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000774 if (resize_filter == (ResizeFilter *) NULL)
775 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000776 /*
777 Defaults for the requested filter.
778 */
779 filter_type=mapping[filter].filter;
780 window_type=mapping[filter].window;
anthony48f77622010-10-03 14:32:31 +0000781 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000782 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000783 switch (filter_type)
784 {
785 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000786 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000787 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000788 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000789 break;
anthonyba5a7c32010-09-15 02:42:25 +0000790 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000791 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000792 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000793 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000794 break;
anthony61b5ddd2010-10-05 02:33:31 +0000795
cristy33b1c162010-01-23 22:51:51 +0000796 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000797 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000798 filter_type=JincFilter;
799 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000800 break;
cristya782ecf2010-01-25 02:59:14 +0000801 default:
802 break;
cristy3ed852e2009-09-05 21:47:34 +0000803 }
anthony61b5ddd2010-10-05 02:33:31 +0000804 else
805 switch (filter_type)
806 {
807 case Lanczos2DFilter:
nicolas45b58a92010-10-07 15:46:39 +0000808 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000809 window_type=SincFastFilter;
810 break;
811 default:
812 break;
813 }
814
cristy3ed852e2009-09-05 21:47:34 +0000815 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000816 if (artifact != (const char *) NULL)
817 {
cristy9af9b5d2010-08-15 17:04:28 +0000818 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000819 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000820 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000821 filter_type=(FilterTypes) option;
822 window_type=BoxFilter;
823 }
824 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000825 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
826 filter_type=cylindrical != MagickFalse ? JincFilter : Lanczos2DFilter;
anthony48f77622010-10-03 14:32:31 +0000827 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000828 }
nicolas07bac812010-09-19 18:47:02 +0000829 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000830 artifact=GetImageArtifact(image,"filter:window");
831 if (artifact != (const char *) NULL)
832 {
cristy9af9b5d2010-08-15 17:04:28 +0000833 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000834 if ((UndefinedFilter < option) && (option < SentinelFilter))
835 {
836 if (option != LanczosFilter)
837 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000838 else
anthony48f77622010-10-03 14:32:31 +0000839 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000840 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000841 }
cristy33b1c162010-01-23 22:51:51 +0000842 }
cristy3ed852e2009-09-05 21:47:34 +0000843 }
cristy33b1c162010-01-23 22:51:51 +0000844 else
845 {
anthony48f77622010-10-03 14:32:31 +0000846 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000847 artifact=GetImageArtifact(image,"filter:window");
848 if (artifact != (const char *) NULL)
849 {
850 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
851 artifact);
852 if ((UndefinedFilter < option) && (option < SentinelFilter))
853 {
anthony61b5ddd2010-10-05 02:33:31 +0000854 filter_type=cylindrical != MagickFalse ?
855 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000856 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000857 }
858 }
859 }
nicolas07bac812010-09-19 18:47:02 +0000860 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000861 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000862 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000863 resize_filter->window=filters[window_type].function;
864 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000865 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000866
anthony10b8bc82010-10-02 12:48:46 +0000867 /* Filter blur -- scaling both filter and support window. */
868 resize_filter->blur=blur;
869 artifact=GetImageArtifact(image,"filter:blur");
870 if (artifact != (const char *) NULL)
871 resize_filter->blur=StringToDouble(artifact);
872 if (resize_filter->blur < MagickEpsilon)
873 resize_filter->blur=(MagickRealType) MagickEpsilon;
874
875 if (cylindrical != MagickFalse)
876 switch (filter_type)
877 {
878 case PointFilter:
879 case BoxFilter:
880 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000881 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000882 break;
883 case GaussianFilter:
884 /* Cylindrical Gaussian should have a sigma of sqrt(2)/2
885 * and not the default sigma of 1/2 - so use blur to enlarge
886 * and adjust support so actual practical support = 2.0 by default
887 */
888 resize_filter->blur *= MagickSQ2;
cristy1c9bb452010-10-03 16:48:33 +0000889 resize_filter->support = (MagickRealType) MagickSQ2; /* which times blur => 2.0 */
anthony10b8bc82010-10-02 12:48:46 +0000890 break;
anthony61b5ddd2010-10-05 02:33:31 +0000891 case Lanczos2DFilter:
nicolasddce59c2010-10-06 20:50:33 +0000892 /* Special 2-lobed cylindrical Jinc-Jinc filter with a special
nicolase473f722010-10-07 00:05:13 +0000893 * "blur" adjustment (actually, a "negative blur," since the
nicolasaab02f32010-10-08 00:27:07 +0000894 * support is scaled to make it slightly smaller, with the
895 * consequence that the results are slightly sharper).
nicolas6237c462010-10-05 06:11:49 +0000896 *
nicolasaab02f32010-10-08 00:27:07 +0000897 * Used as default filter for EWA Resampling and Distorts.
nicolase473f722010-10-07 00:05:13 +0000898 *
899 * Executive summary:
900 *
901 * This is the closest a two-lobe Jinc-Jinc used radially
nicolasaab02f32010-10-08 00:27:07 +0000902 * (with Clamped EWA) comes to preserving straight
903 * vertical---and straight horizontal---features.
nicolase473f722010-10-07 00:05:13 +0000904 *
905 * Details:
906 *
nicolasaab02f32010-10-08 00:27:07 +0000907 * Basically, this is the standard
908 *
909 * Lanczos2D(x) = Jinc(x)*Jinc(x*r1/r2) with support r2
910 *
nicolase473f722010-10-07 00:05:13 +0000911 * (where r1 is the first root of the Jinc function, and r2 is
nicolasaab02f32010-10-08 00:27:07 +0000912 * the second) rescaled so that images which are constant in
913 * the vertical direction, and images which are constant in
914 * the horizontal direction, are almost unchanged (the change
915 * being generically as small as possible) when the
916 * geometrical transformation applied to the image is is the
917 * identity (a.k.a. "no-op").
nicolase473f722010-10-07 00:05:13 +0000918 *
nicolasaab02f32010-10-08 00:27:07 +0000919 * Specifically, it is Lanczos2D(s*x) with support r2/s, where
920 * s is chosen so that
921 *
nicolasddce59c2010-10-06 20:50:33 +0000922 * Lanczos2D(s)=-2*Lanczos2D(s*sqrt(2))-Lanczos2D(s*2).
nicolas6237c462010-10-05 06:11:49 +0000923 *
nicolasaab02f32010-10-08 00:27:07 +0000924 * This value of s ensures that the value of a one-pixel-wide
925 * vertical line (equal to 1, say, on a black=0 background) is
926 * exactly preserved when no-op is applied to the image. It
927 * also ensures that---with no-op---the nearest two columns on
nicolas30731812010-10-07 00:07:53 +0000928 * either side are minimally changed (the farther columns are
nicolasaab02f32010-10-08 00:27:07 +0000929 * unchanged no matter what). Specifically, the nearest
930 * columns on the left and the right have values raised from
931 * zero to c, and the second closest columns on the left and
932 * right are lowered from 0 to minus c. (That is, the very
933 * closest columns are made slightly positive, and the second
934 * closest are made slightly negative, in equal amounts.) The
935 * size c of this blur/halo is .002042317. Consequently, image
936 * values between 0 and M which are constant on columns (or
937 * rows) are preserved by no-op to within 2Mc (less than one
938 * half of one percent of the dynamic range).
nicolas6237c462010-10-05 06:11:49 +0000939 *
nicolasaab02f32010-10-08 00:27:07 +0000940 * Note that "high frequency modes" which are not aligned with
nicolas4028f7a2010-10-08 00:30:57 +0000941 * image rows or columns are damped considerably. For example,
942 * the amplitude of the very highest energy mode, the
943 * so-called "checkerboard" mode, is reduced by almost 62%
nicolas53e19ab2010-10-08 00:31:18 +0000944 * (still less than with "standard" Lanczos2D or with a
945 * comparable Gaussian kernel).
nicolase473f722010-10-07 00:05:13 +0000946 *
nicolasaab02f32010-10-08 00:27:07 +0000947 * This "optimal" scaling was discovered by Nicolas Robidoux
948 * of Laurentian University.
949 *
950 * Below, resize_filter->blur is 1/s.
anthony61b5ddd2010-10-05 02:33:31 +0000951 */
nicolas6237c462010-10-05 06:11:49 +0000952 resize_filter->blur *= (MagickRealType) 0.958033808;
anthony81b8bf92010-10-02 13:54:34 +0000953 default:
954 break;
anthony10b8bc82010-10-02 12:48:46 +0000955 }
anthony61b5ddd2010-10-05 02:33:31 +0000956 else
957 switch (filter_type)
958 {
959 case Lanczos2DFilter:
nicolas3cdd3d22010-10-08 00:33:54 +0000960 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000961 resize_filter->filter=SincFast;
962 break;
963 default:
964 break;
965 }
966
anthony2d9b8b52010-09-14 08:31:07 +0000967 /* Filter support overrides. */
cristy3ed852e2009-09-05 21:47:34 +0000968 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000969 if (artifact != (const char *) NULL)
970 {
cristybb503372010-05-27 20:51:26 +0000971 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000972 lobes;
973
cristy96b16132010-08-29 17:19:52 +0000974 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000975 if (lobes < 1)
976 lobes=1;
977 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000978 }
anthony61b5ddd2010-10-05 02:33:31 +0000979 /* convert Jinc lobes to a real support value */
980 if (resize_filter->filter == Jinc)
981 {
982 if (resize_filter->support > 16)
983 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
984 else
985 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
986 }
987 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000988 artifact=GetImageArtifact(image,"filter:support");
989 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000990 resize_filter->support=fabs(StringToDouble(artifact));
991 /*
nicolas07bac812010-09-19 18:47:02 +0000992 Scale windowing function separatally to the support 'clipping'
993 window that calling operator is planning to actually use. (Expert
994 override)
cristy3ed852e2009-09-05 21:47:34 +0000995 */
anthony55f12332010-09-10 01:13:02 +0000996 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000997 artifact=GetImageArtifact(image,"filter:win-support");
998 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000999 resize_filter->window_support=fabs(StringToDouble(artifact));
1000 /*
anthony1f90a6b2010-09-14 08:56:31 +00001001 Adjust window function scaling to the windowing support for
1002 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +00001003 */
1004 resize_filter->scale /= resize_filter->window_support;
1005 /*
nicolas07bac812010-09-19 18:47:02 +00001006 Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +00001007 */
cristy3ed852e2009-09-05 21:47:34 +00001008 B=0.0;
1009 C=0.0;
cristy33b1c162010-01-23 22:51:51 +00001010 if ((filters[filter_type].function == CubicBC) ||
1011 (filters[window_type].function == CubicBC))
1012 {
anthony2d9b8b52010-09-14 08:31:07 +00001013 B=filters[filter_type].B;
1014 C=filters[filter_type].C;
1015 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +00001016 {
anthony2d9b8b52010-09-14 08:31:07 +00001017 B=filters[window_type].B;
1018 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +00001019 }
cristy33b1c162010-01-23 22:51:51 +00001020 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001021 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001022 {
1023 B=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001024 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001025 artifact=GetImageArtifact(image,"filter:c");
1026 if (artifact != (const char *) NULL)
1027 C=StringToDouble(artifact);
1028 }
1029 else
1030 {
1031 artifact=GetImageArtifact(image,"filter:c");
1032 if (artifact != (const char *) NULL)
1033 {
1034 C=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001035 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001036 }
1037 }
1038 /*
nicolas07bac812010-09-19 18:47:02 +00001039 Convert B,C values into Cubic Coefficents. See CubicBC().
cristy33b1c162010-01-23 22:51:51 +00001040 */
1041 resize_filter->cubic[0]=(6.0-2.0*B)/6.0;
cristy3ed852e2009-09-05 21:47:34 +00001042 resize_filter->cubic[1]=0.0;
cristy33b1c162010-01-23 22:51:51 +00001043 resize_filter->cubic[2]=(-18.0+12.0*B+6.0*C)/6.0;
1044 resize_filter->cubic[3]=(12.0-9.0*B-6.0*C)/6.0;
1045 resize_filter->cubic[4]=(8.0*B+24.0*C)/6.0;
1046 resize_filter->cubic[5]=(-12.0*B-48.0*C)/6.0;
1047 resize_filter->cubic[6]=(6.0*B+30.0*C)/6.0;
nicolas58c77cd2010-09-20 15:51:39 +00001048 resize_filter->cubic[7]=(-B-6.0*C)/6.0;
cristy3ed852e2009-09-05 21:47:34 +00001049 }
anthony55f12332010-09-10 01:13:02 +00001050 /*
nicolas07bac812010-09-19 18:47:02 +00001051 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001052 */
anthonye06e4c12010-09-15 04:03:52 +00001053#if defined(MAGICKCORE_OPENMP_SUPPORT)
anthony72949792010-10-08 04:44:56 +00001054 #pragma omp single
1055 {
anthonye06e4c12010-09-15 04:03:52 +00001056#endif
1057 artifact=GetImageArtifact(image,"filter:verbose");
1058 if (artifact != (const char *) NULL)
1059 {
1060 double
1061 support,
1062 x;
cristy3ed852e2009-09-05 21:47:34 +00001063
nicolas07bac812010-09-19 18:47:02 +00001064 /*
anthony463be1d2010-09-26 01:07:36 +00001065 Set the weighting function properly when the weighting
1066 function may not exactly match the filter of the same name.
1067 EG: a Point filter really uses a Box weighting function
1068 with a different support than is typically used.
1069
anthonye06e4c12010-09-15 04:03:52 +00001070 */
anthony463be1d2010-09-26 01:07:36 +00001071 if (resize_filter->filter == Box) filter_type=BoxFilter;
1072 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1073 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
anthony61b5ddd2010-10-05 02:33:31 +00001074 if (resize_filter->filter == Jinc) filter_type=JincFilter;
anthony463be1d2010-09-26 01:07:36 +00001075 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
anthonye06e4c12010-09-15 04:03:52 +00001076 /*
nicolas07bac812010-09-19 18:47:02 +00001077 Report Filter Details.
anthonye06e4c12010-09-15 04:03:52 +00001078 */
cristy03dbbd22010-09-19 23:04:47 +00001079 support=GetResizeFilterSupport(resize_filter); /* support range */
anthony61b5ddd2010-10-05 02:33:31 +00001080 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
cristy03dbbd22010-09-19 23:04:47 +00001081 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1082 MagickFilterOptions,filter_type));
1083 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
anthony463be1d2010-09-26 01:07:36 +00001084 MagickFilterOptions, window_type));
cristy03dbbd22010-09-19 23:04:47 +00001085 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
anthony463be1d2010-09-26 01:07:36 +00001086 (double) resize_filter->support);
cristy03dbbd22010-09-19 23:04:47 +00001087 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
anthony463be1d2010-09-26 01:07:36 +00001088 (double) resize_filter->window_support);
cristy03dbbd22010-09-19 23:04:47 +00001089 (void) fprintf(stdout,"# blur = %.*g\n",GetMagickPrecision(),
1090 (double) resize_filter->blur);
anthony463be1d2010-09-26 01:07:36 +00001091 (void) fprintf(stdout,"# blurred_support = %.*g\n",GetMagickPrecision(),
cristy03dbbd22010-09-19 23:04:47 +00001092 (double) support);
1093 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1094 (double) B,GetMagickPrecision(),(double) C);
anthony61b5ddd2010-10-05 02:33:31 +00001095 (void) fprintf(stdout,"\n");
anthonye06e4c12010-09-15 04:03:52 +00001096 /*
nicolas07bac812010-09-19 18:47:02 +00001097 Output values of resulting filter graph -- for graphing
1098 filter result.
anthonye06e4c12010-09-15 04:03:52 +00001099 */
1100 for (x=0.0; x <= support; x+=0.01f)
cristy03dbbd22010-09-19 23:04:47 +00001101 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1102 (double) GetResizeFilterWeight(resize_filter,x));
nicolas07bac812010-09-19 18:47:02 +00001103 /* A final value so gnuplot can graph the 'stop' properly. */
cristy03dbbd22010-09-19 23:04:47 +00001104 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1105 0.0);
anthonye06e4c12010-09-15 04:03:52 +00001106 }
anthony72949792010-10-08 04:44:56 +00001107 /* output the above once only for each image, and each setting */
1108 (void) DeleteImageArtifact(image,"filter:verbose");
anthonye06e4c12010-09-15 04:03:52 +00001109#if defined(MAGICKCORE_OPENMP_SUPPORT)
anthony61b5ddd2010-10-05 02:33:31 +00001110 }
anthonye06e4c12010-09-15 04:03:52 +00001111#endif
cristy3ed852e2009-09-05 21:47:34 +00001112 return(resize_filter);
1113}
1114
1115/*
1116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117% %
1118% %
1119% %
1120% A d a p t i v e R e s i z e I m a g e %
1121% %
1122% %
1123% %
1124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125%
1126% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1127%
1128% The format of the AdaptiveResizeImage method is:
1129%
cristy9af9b5d2010-08-15 17:04:28 +00001130% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1131% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001132%
1133% A description of each parameter follows:
1134%
1135% o image: the image.
1136%
1137% o columns: the number of columns in the resized image.
1138%
1139% o rows: the number of rows in the resized image.
1140%
1141% o exception: return any errors or warnings in this structure.
1142%
1143*/
1144MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001145 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001146{
1147#define AdaptiveResizeImageTag "Resize/Image"
1148
cristyc4c8d132010-01-07 01:58:38 +00001149 CacheView
1150 *resize_view;
1151
cristy3ed852e2009-09-05 21:47:34 +00001152 Image
1153 *resize_image;
1154
cristy3ed852e2009-09-05 21:47:34 +00001155 MagickBooleanType
1156 proceed;
1157
1158 MagickPixelPacket
1159 pixel;
1160
1161 PointInfo
1162 offset;
1163
1164 ResampleFilter
1165 *resample_filter;
1166
cristy9af9b5d2010-08-15 17:04:28 +00001167 ssize_t
1168 y;
1169
cristy3ed852e2009-09-05 21:47:34 +00001170 /*
1171 Adaptively resize image.
1172 */
1173 assert(image != (const Image *) NULL);
1174 assert(image->signature == MagickSignature);
1175 if (image->debug != MagickFalse)
1176 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1177 assert(exception != (ExceptionInfo *) NULL);
1178 assert(exception->signature == MagickSignature);
1179 if ((columns == 0) || (rows == 0))
1180 return((Image *) NULL);
1181 if ((columns == image->columns) && (rows == image->rows))
1182 return(CloneImage(image,0,0,MagickTrue,exception));
1183 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1184 if (resize_image == (Image *) NULL)
1185 return((Image *) NULL);
1186 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1187 {
1188 InheritException(exception,&resize_image->exception);
1189 resize_image=DestroyImage(resize_image);
1190 return((Image *) NULL);
1191 }
1192 GetMagickPixelPacket(image,&pixel);
1193 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001194 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001195 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001196 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001197 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001198 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001199 {
1200 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001201 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001202
cristybb503372010-05-27 20:51:26 +00001203 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001204 x;
1205
1206 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001207 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001208
1209 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1210 exception);
1211 if (q == (PixelPacket *) NULL)
1212 break;
1213 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1214 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001215 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001216 {
1217 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1218 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1219 &pixel);
1220 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1221 q++;
1222 }
1223 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1224 break;
cristy96b16132010-08-29 17:19:52 +00001225 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1226 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001227 if (proceed == MagickFalse)
1228 break;
1229 }
1230 resample_filter=DestroyResampleFilter(resample_filter);
1231 resize_view=DestroyCacheView(resize_view);
1232 return(resize_image);
1233}
1234
1235/*
1236%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1237% %
1238% %
1239% %
1240+ B e s s e l O r d e r O n e %
1241% %
1242% %
1243% %
1244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245%
1246% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001247% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001248%
1249% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1250%
1251% j1(x) = x*j1(x);
1252%
1253% For x in (8,inf)
1254%
1255% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1256%
1257% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1258%
1259% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1260% = 1/sqrt(2) * (sin(x) - cos(x))
1261% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1262% = -1/sqrt(2) * (sin(x) + cos(x))
1263%
1264% The format of the BesselOrderOne method is:
1265%
1266% MagickRealType BesselOrderOne(MagickRealType x)
1267%
1268% A description of each parameter follows:
1269%
1270% o x: MagickRealType value.
1271%
1272*/
1273
1274#undef I0
1275static MagickRealType I0(MagickRealType x)
1276{
1277 MagickRealType
1278 sum,
1279 t,
1280 y;
1281
cristybb503372010-05-27 20:51:26 +00001282 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001283 i;
1284
1285 /*
1286 Zeroth order Bessel function of the first kind.
1287 */
1288 sum=1.0;
1289 y=x*x/4.0;
1290 t=y;
1291 for (i=2; t > MagickEpsilon; i++)
1292 {
1293 sum+=t;
1294 t*=y/((MagickRealType) i*i);
1295 }
1296 return(sum);
1297}
1298
1299#undef J1
1300static MagickRealType J1(MagickRealType x)
1301{
1302 MagickRealType
1303 p,
1304 q;
1305
cristybb503372010-05-27 20:51:26 +00001306 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001307 i;
1308
1309 static const double
1310 Pone[] =
1311 {
1312 0.581199354001606143928050809e+21,
1313 -0.6672106568924916298020941484e+20,
1314 0.2316433580634002297931815435e+19,
1315 -0.3588817569910106050743641413e+17,
1316 0.2908795263834775409737601689e+15,
1317 -0.1322983480332126453125473247e+13,
1318 0.3413234182301700539091292655e+10,
1319 -0.4695753530642995859767162166e+7,
1320 0.270112271089232341485679099e+4
1321 },
1322 Qone[] =
1323 {
1324 0.11623987080032122878585294e+22,
1325 0.1185770712190320999837113348e+20,
1326 0.6092061398917521746105196863e+17,
1327 0.2081661221307607351240184229e+15,
1328 0.5243710262167649715406728642e+12,
1329 0.1013863514358673989967045588e+10,
1330 0.1501793594998585505921097578e+7,
1331 0.1606931573481487801970916749e+4,
1332 0.1e+1
1333 };
1334
1335 p=Pone[8];
1336 q=Qone[8];
1337 for (i=7; i >= 0; i--)
1338 {
1339 p=p*x*x+Pone[i];
1340 q=q*x*x+Qone[i];
1341 }
1342 return(p/q);
1343}
1344
1345#undef P1
1346static MagickRealType P1(MagickRealType x)
1347{
1348 MagickRealType
1349 p,
1350 q;
1351
cristybb503372010-05-27 20:51:26 +00001352 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001353 i;
1354
1355 static const double
1356 Pone[] =
1357 {
1358 0.352246649133679798341724373e+5,
1359 0.62758845247161281269005675e+5,
1360 0.313539631109159574238669888e+5,
1361 0.49854832060594338434500455e+4,
1362 0.2111529182853962382105718e+3,
1363 0.12571716929145341558495e+1
1364 },
1365 Qone[] =
1366 {
1367 0.352246649133679798068390431e+5,
1368 0.626943469593560511888833731e+5,
1369 0.312404063819041039923015703e+5,
1370 0.4930396490181088979386097e+4,
1371 0.2030775189134759322293574e+3,
1372 0.1e+1
1373 };
1374
1375 p=Pone[5];
1376 q=Qone[5];
1377 for (i=4; i >= 0; i--)
1378 {
1379 p=p*(8.0/x)*(8.0/x)+Pone[i];
1380 q=q*(8.0/x)*(8.0/x)+Qone[i];
1381 }
1382 return(p/q);
1383}
1384
1385#undef Q1
1386static MagickRealType Q1(MagickRealType x)
1387{
1388 MagickRealType
1389 p,
1390 q;
1391
cristybb503372010-05-27 20:51:26 +00001392 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001393 i;
1394
1395 static const double
1396 Pone[] =
1397 {
1398 0.3511751914303552822533318e+3,
1399 0.7210391804904475039280863e+3,
1400 0.4259873011654442389886993e+3,
1401 0.831898957673850827325226e+2,
1402 0.45681716295512267064405e+1,
1403 0.3532840052740123642735e-1
1404 },
1405 Qone[] =
1406 {
1407 0.74917374171809127714519505e+4,
1408 0.154141773392650970499848051e+5,
1409 0.91522317015169922705904727e+4,
1410 0.18111867005523513506724158e+4,
1411 0.1038187585462133728776636e+3,
1412 0.1e+1
1413 };
1414
1415 p=Pone[5];
1416 q=Qone[5];
1417 for (i=4; i >= 0; i--)
1418 {
1419 p=p*(8.0/x)*(8.0/x)+Pone[i];
1420 q=q*(8.0/x)*(8.0/x)+Qone[i];
1421 }
1422 return(p/q);
1423}
1424
1425static MagickRealType BesselOrderOne(MagickRealType x)
1426{
1427 MagickRealType
1428 p,
1429 q;
1430
1431 if (x == 0.0)
1432 return(0.0);
1433 p=x;
1434 if (x < 0.0)
1435 x=(-x);
1436 if (x < 8.0)
1437 return(p*J1(x));
1438 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1439 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1440 cos((double) x))));
1441 if (p < 0.0)
1442 q=(-q);
1443 return(q);
1444}
1445
1446/*
1447%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1448% %
1449% %
1450% %
1451+ D e s t r o y R e s i z e F i l t e r %
1452% %
1453% %
1454% %
1455%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1456%
1457% DestroyResizeFilter() destroy the resize filter.
1458%
cristya2ffd7e2010-03-10 20:50:30 +00001459% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001460%
1461% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1462%
1463% A description of each parameter follows:
1464%
1465% o resize_filter: the resize filter.
1466%
1467*/
1468MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1469{
1470 assert(resize_filter != (ResizeFilter *) NULL);
1471 assert(resize_filter->signature == MagickSignature);
1472 resize_filter->signature=(~MagickSignature);
1473 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1474 return(resize_filter);
1475}
1476
1477/*
1478%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1479% %
1480% %
1481% %
1482+ G e t R e s i z e F i l t e r S u p p o r t %
1483% %
1484% %
1485% %
1486%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1487%
1488% GetResizeFilterSupport() return the current support window size for this
1489% filter. Note that this may have been enlarged by filter:blur factor.
1490%
1491% The format of the GetResizeFilterSupport method is:
1492%
1493% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1494%
1495% A description of each parameter follows:
1496%
1497% o filter: Image filter to use.
1498%
1499*/
1500MagickExport MagickRealType GetResizeFilterSupport(
1501 const ResizeFilter *resize_filter)
1502{
1503 assert(resize_filter != (ResizeFilter *) NULL);
1504 assert(resize_filter->signature == MagickSignature);
1505 return(resize_filter->support*resize_filter->blur);
1506}
1507
1508/*
1509%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1510% %
1511% %
1512% %
1513+ G e t R e s i z e F i l t e r W e i g h t %
1514% %
1515% %
1516% %
1517%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1518%
1519% GetResizeFilterWeight evaluates the specified resize filter at the point x
1520% which usally lies between zero and the filters current 'support' and
1521% returns the weight of the filter function at that point.
1522%
1523% The format of the GetResizeFilterWeight method is:
1524%
1525% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1526% const MagickRealType x)
1527%
1528% A description of each parameter follows:
1529%
1530% o filter: the filter type.
1531%
1532% o x: the point.
1533%
1534*/
1535MagickExport MagickRealType GetResizeFilterWeight(
1536 const ResizeFilter *resize_filter,const MagickRealType x)
1537{
1538 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001539 scale,
1540 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001541
1542 /*
1543 Windowing function - scale the weighting filter by this amount.
1544 */
1545 assert(resize_filter != (ResizeFilter *) NULL);
1546 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001547 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001548 if ((resize_filter->window_support < MagickEpsilon) ||
1549 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001550 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001551 else
1552 {
anthony55f12332010-09-10 01:13:02 +00001553 scale=resize_filter->scale;
1554 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001555 }
anthony55f12332010-09-10 01:13:02 +00001556 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001557}
1558
1559/*
1560%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1561% %
1562% %
1563% %
1564% M a g n i f y I m a g e %
1565% %
1566% %
1567% %
1568%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1569%
1570% MagnifyImage() is a convenience method that scales an image proportionally
1571% to twice its size.
1572%
1573% The format of the MagnifyImage method is:
1574%
1575% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1576%
1577% A description of each parameter follows:
1578%
1579% o image: the image.
1580%
1581% o exception: return any errors or warnings in this structure.
1582%
1583*/
1584MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1585{
1586 Image
1587 *magnify_image;
1588
1589 assert(image != (Image *) NULL);
1590 assert(image->signature == MagickSignature);
1591 if (image->debug != MagickFalse)
1592 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1593 assert(exception != (ExceptionInfo *) NULL);
1594 assert(exception->signature == MagickSignature);
1595 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1596 1.0,exception);
1597 return(magnify_image);
1598}
1599
1600/*
1601%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1602% %
1603% %
1604% %
1605% M i n i f y I m a g e %
1606% %
1607% %
1608% %
1609%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1610%
1611% MinifyImage() is a convenience method that scales an image proportionally
1612% to half its size.
1613%
1614% The format of the MinifyImage method is:
1615%
1616% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1617%
1618% A description of each parameter follows:
1619%
1620% o image: the image.
1621%
1622% o exception: return any errors or warnings in this structure.
1623%
1624*/
1625MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1626{
1627 Image
1628 *minify_image;
1629
1630 assert(image != (Image *) NULL);
1631 assert(image->signature == MagickSignature);
1632 if (image->debug != MagickFalse)
1633 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1634 assert(exception != (ExceptionInfo *) NULL);
1635 assert(exception->signature == MagickSignature);
1636 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1637 1.0,exception);
1638 return(minify_image);
1639}
1640
1641/*
1642%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1643% %
1644% %
1645% %
1646% R e s a m p l e I m a g e %
1647% %
1648% %
1649% %
1650%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1651%
1652% ResampleImage() resize image in terms of its pixel size, so that when
1653% displayed at the given resolution it will be the same size in terms of
1654% real world units as the original image at the original resolution.
1655%
1656% The format of the ResampleImage method is:
1657%
1658% Image *ResampleImage(Image *image,const double x_resolution,
1659% const double y_resolution,const FilterTypes filter,const double blur,
1660% ExceptionInfo *exception)
1661%
1662% A description of each parameter follows:
1663%
1664% o image: the image to be resized to fit the given resolution.
1665%
1666% o x_resolution: the new image x resolution.
1667%
1668% o y_resolution: the new image y resolution.
1669%
1670% o filter: Image filter to use.
1671%
1672% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1673%
1674*/
1675MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1676 const double y_resolution,const FilterTypes filter,const double blur,
1677 ExceptionInfo *exception)
1678{
1679#define ResampleImageTag "Resample/Image"
1680
1681 Image
1682 *resample_image;
1683
cristybb503372010-05-27 20:51:26 +00001684 size_t
cristy3ed852e2009-09-05 21:47:34 +00001685 height,
1686 width;
1687
1688 /*
1689 Initialize sampled image attributes.
1690 */
1691 assert(image != (const Image *) NULL);
1692 assert(image->signature == MagickSignature);
1693 if (image->debug != MagickFalse)
1694 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1695 assert(exception != (ExceptionInfo *) NULL);
1696 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001697 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1698 72.0 : image->x_resolution)+0.5);
1699 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1700 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001701 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1702 if (resample_image != (Image *) NULL)
1703 {
1704 resample_image->x_resolution=x_resolution;
1705 resample_image->y_resolution=y_resolution;
1706 }
1707 return(resample_image);
1708}
1709#if defined(MAGICKCORE_LQR_DELEGATE)
1710
1711/*
1712%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1713% %
1714% %
1715% %
1716% L i q u i d R e s c a l e I m a g e %
1717% %
1718% %
1719% %
1720%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1721%
1722% LiquidRescaleImage() rescales image with seam carving.
1723%
1724% The format of the LiquidRescaleImage method is:
1725%
1726% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001727% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001728% const double delta_x,const double rigidity,ExceptionInfo *exception)
1729%
1730% A description of each parameter follows:
1731%
1732% o image: the image.
1733%
1734% o columns: the number of columns in the rescaled image.
1735%
1736% o rows: the number of rows in the rescaled image.
1737%
1738% o delta_x: maximum seam transversal step (0 means straight seams).
1739%
1740% o rigidity: introduce a bias for non-straight seams (typically 0).
1741%
1742% o exception: return any errors or warnings in this structure.
1743%
1744*/
cristy9af9b5d2010-08-15 17:04:28 +00001745MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1746 const size_t rows,const double delta_x,const double rigidity,
1747 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001748{
1749#define LiquidRescaleImageTag "Rescale/Image"
1750
cristyc5c6f662010-09-22 14:23:02 +00001751 CacheView
1752 *rescale_view;
1753
cristy3ed852e2009-09-05 21:47:34 +00001754 const char
1755 *map;
1756
1757 guchar
1758 *packet;
1759
1760 Image
1761 *rescale_image;
1762
1763 int
1764 x,
1765 y;
1766
1767 LqrCarver
1768 *carver;
1769
1770 LqrRetVal
1771 lqr_status;
1772
1773 MagickBooleanType
1774 status;
1775
1776 MagickPixelPacket
1777 pixel;
1778
1779 unsigned char
1780 *pixels;
1781
1782 /*
1783 Liquid rescale image.
1784 */
1785 assert(image != (const Image *) NULL);
1786 assert(image->signature == MagickSignature);
1787 if (image->debug != MagickFalse)
1788 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1789 assert(exception != (ExceptionInfo *) NULL);
1790 assert(exception->signature == MagickSignature);
1791 if ((columns == 0) || (rows == 0))
1792 return((Image *) NULL);
1793 if ((columns == image->columns) && (rows == image->rows))
1794 return(CloneImage(image,0,0,MagickTrue,exception));
1795 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001796 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001797 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1798 {
1799 Image
1800 *resize_image;
1801
cristybb503372010-05-27 20:51:26 +00001802 size_t
cristy3ed852e2009-09-05 21:47:34 +00001803 height,
1804 width;
1805
1806 /*
1807 Honor liquid resize size limitations.
1808 */
1809 for (width=image->columns; columns >= (2*width-1); width*=2);
1810 for (height=image->rows; rows >= (2*height-1); height*=2);
1811 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1812 exception);
1813 if (resize_image == (Image *) NULL)
1814 return((Image *) NULL);
1815 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1816 rigidity,exception);
1817 resize_image=DestroyImage(resize_image);
1818 return(rescale_image);
1819 }
1820 map="RGB";
1821 if (image->matte == MagickFalse)
1822 map="RGBA";
1823 if (image->colorspace == CMYKColorspace)
1824 {
1825 map="CMYK";
1826 if (image->matte == MagickFalse)
1827 map="CMYKA";
1828 }
1829 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1830 strlen(map)*sizeof(*pixels));
1831 if (pixels == (unsigned char *) NULL)
1832 return((Image *) NULL);
1833 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1834 pixels,exception);
1835 if (status == MagickFalse)
1836 {
1837 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1838 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1839 }
1840 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1841 if (carver == (LqrCarver *) NULL)
1842 {
1843 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1844 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1845 }
1846 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1847 lqr_status=lqr_carver_resize(carver,columns,rows);
1848 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1849 lqr_carver_get_height(carver),MagickTrue,exception);
1850 if (rescale_image == (Image *) NULL)
1851 {
1852 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1853 return((Image *) NULL);
1854 }
1855 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1856 {
1857 InheritException(exception,&rescale_image->exception);
1858 rescale_image=DestroyImage(rescale_image);
1859 return((Image *) NULL);
1860 }
1861 GetMagickPixelPacket(rescale_image,&pixel);
1862 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001863 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001864 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1865 {
1866 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001867 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001868
1869 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001870 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001871
anthony22aad252010-09-23 06:59:07 +00001872 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001873 if (q == (PixelPacket *) NULL)
1874 break;
cristyc5c6f662010-09-22 14:23:02 +00001875 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001876 pixel.red=QuantumRange*(packet[0]/255.0);
1877 pixel.green=QuantumRange*(packet[1]/255.0);
1878 pixel.blue=QuantumRange*(packet[2]/255.0);
1879 if (image->colorspace != CMYKColorspace)
1880 {
1881 if (image->matte == MagickFalse)
1882 pixel.opacity=QuantumRange*(packet[3]/255.0);
1883 }
1884 else
1885 {
1886 pixel.index=QuantumRange*(packet[3]/255.0);
1887 if (image->matte == MagickFalse)
1888 pixel.opacity=QuantumRange*(packet[4]/255.0);
1889 }
1890 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001891 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001892 break;
1893 }
cristyc5c6f662010-09-22 14:23:02 +00001894 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001895 /*
1896 Relinquish resources.
1897 */
1898 lqr_carver_destroy(carver);
1899 return(rescale_image);
1900}
1901#else
1902MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001903 const size_t magick_unused(columns),const size_t magick_unused(rows),
1904 const double magick_unused(delta_x),const double magick_unused(rigidity),
1905 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001906{
1907 assert(image != (const Image *) NULL);
1908 assert(image->signature == MagickSignature);
1909 if (image->debug != MagickFalse)
1910 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1911 assert(exception != (ExceptionInfo *) NULL);
1912 assert(exception->signature == MagickSignature);
1913 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1914 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1915 return((Image *) NULL);
1916}
1917#endif
1918
1919/*
1920%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1921% %
1922% %
1923% %
1924% R e s i z e I m a g e %
1925% %
1926% %
1927% %
1928%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1929%
1930% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001931% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001932%
1933% If an undefined filter is given the filter defaults to Mitchell for a
1934% colormapped image, a image with a matte channel, or if the image is
1935% enlarged. Otherwise the filter defaults to a Lanczos.
1936%
1937% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1938%
1939% The format of the ResizeImage method is:
1940%
cristybb503372010-05-27 20:51:26 +00001941% Image *ResizeImage(Image *image,const size_t columns,
1942% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001943% ExceptionInfo *exception)
1944%
1945% A description of each parameter follows:
1946%
1947% o image: the image.
1948%
1949% o columns: the number of columns in the scaled image.
1950%
1951% o rows: the number of rows in the scaled image.
1952%
1953% o filter: Image filter to use.
1954%
cristy9af9b5d2010-08-15 17:04:28 +00001955% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1956% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001957%
1958% o exception: return any errors or warnings in this structure.
1959%
1960*/
1961
1962typedef struct _ContributionInfo
1963{
1964 MagickRealType
1965 weight;
1966
cristybb503372010-05-27 20:51:26 +00001967 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001968 pixel;
1969} ContributionInfo;
1970
1971static ContributionInfo **DestroyContributionThreadSet(
1972 ContributionInfo **contribution)
1973{
cristybb503372010-05-27 20:51:26 +00001974 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001975 i;
1976
1977 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001978 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001979 if (contribution[i] != (ContributionInfo *) NULL)
1980 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1981 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001982 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001983 return(contribution);
1984}
1985
1986static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1987{
cristybb503372010-05-27 20:51:26 +00001988 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001989 i;
1990
1991 ContributionInfo
1992 **contribution;
1993
cristybb503372010-05-27 20:51:26 +00001994 size_t
cristy3ed852e2009-09-05 21:47:34 +00001995 number_threads;
1996
1997 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001998 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001999 sizeof(*contribution));
2000 if (contribution == (ContributionInfo **) NULL)
2001 return((ContributionInfo **) NULL);
2002 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00002003 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002004 {
2005 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2006 sizeof(**contribution));
2007 if (contribution[i] == (ContributionInfo *) NULL)
2008 return(DestroyContributionThreadSet(contribution));
2009 }
2010 return(contribution);
2011}
2012
2013static inline double MagickMax(const double x,const double y)
2014{
2015 if (x > y)
2016 return(x);
2017 return(y);
2018}
2019
2020static inline double MagickMin(const double x,const double y)
2021{
2022 if (x < y)
2023 return(x);
2024 return(y);
2025}
2026
2027static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2028 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002029 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002030{
2031#define ResizeImageTag "Resize/Image"
2032
cristyfa112112010-01-04 17:48:07 +00002033 CacheView
2034 *image_view,
2035 *resize_view;
2036
cristy3ed852e2009-09-05 21:47:34 +00002037 ClassType
2038 storage_class;
2039
2040 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002041 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002042
cristy3ed852e2009-09-05 21:47:34 +00002043 MagickBooleanType
2044 status;
2045
2046 MagickPixelPacket
2047 zero;
2048
2049 MagickRealType
2050 scale,
2051 support;
2052
cristy9af9b5d2010-08-15 17:04:28 +00002053 ssize_t
2054 x;
2055
cristy3ed852e2009-09-05 21:47:34 +00002056 /*
2057 Apply filter to resize horizontally from image to resize image.
2058 */
cristy5d824382010-09-06 14:00:17 +00002059 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002060 support=scale*GetResizeFilterSupport(resize_filter);
2061 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2062 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2063 {
2064 InheritException(exception,&resize_image->exception);
2065 return(MagickFalse);
2066 }
2067 if (support < 0.5)
2068 {
2069 /*
nicolas07bac812010-09-19 18:47:02 +00002070 Support too small even for nearest neighbour: Reduce to point
2071 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002072 */
2073 support=(MagickRealType) 0.5;
2074 scale=1.0;
2075 }
2076 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2077 if (contributions == (ContributionInfo **) NULL)
2078 {
2079 (void) ThrowMagickException(exception,GetMagickModule(),
2080 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2081 return(MagickFalse);
2082 }
2083 status=MagickTrue;
2084 scale=1.0/scale;
2085 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2086 image_view=AcquireCacheView(image);
2087 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002088#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002089 #pragma omp parallel for shared(status)
2090#endif
cristybb503372010-05-27 20:51:26 +00002091 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002092 {
cristy3ed852e2009-09-05 21:47:34 +00002093 MagickRealType
2094 center,
2095 density;
2096
2097 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002098 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002099
2100 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002101 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002102
cristy03dbbd22010-09-19 23:04:47 +00002103 register ContributionInfo
2104 *restrict contribution;
2105
cristy3ed852e2009-09-05 21:47:34 +00002106 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002107 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002108
cristy3ed852e2009-09-05 21:47:34 +00002109 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002110 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002111
cristy03dbbd22010-09-19 23:04:47 +00002112 register ssize_t
2113 y;
2114
cristy9af9b5d2010-08-15 17:04:28 +00002115 ssize_t
2116 n,
2117 start,
2118 stop;
2119
cristy3ed852e2009-09-05 21:47:34 +00002120 if (status == MagickFalse)
2121 continue;
2122 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002123 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2124 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002125 density=0.0;
2126 contribution=contributions[GetOpenMPThreadId()];
2127 for (n=0; n < (stop-start); n++)
2128 {
2129 contribution[n].pixel=start+n;
2130 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2131 ((MagickRealType) (start+n)-center+0.5));
2132 density+=contribution[n].weight;
2133 }
2134 if ((density != 0.0) && (density != 1.0))
2135 {
cristybb503372010-05-27 20:51:26 +00002136 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002137 i;
2138
2139 /*
2140 Normalize.
2141 */
2142 density=1.0/density;
2143 for (i=0; i < n; i++)
2144 contribution[i].weight*=density;
2145 }
cristy9af9b5d2010-08-15 17:04:28 +00002146 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2147 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002148 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2149 exception);
2150 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2151 {
2152 status=MagickFalse;
2153 continue;
2154 }
2155 indexes=GetCacheViewVirtualIndexQueue(image_view);
2156 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002157 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002158 {
cristy3ed852e2009-09-05 21:47:34 +00002159 MagickPixelPacket
2160 pixel;
2161
2162 MagickRealType
2163 alpha;
2164
cristybb503372010-05-27 20:51:26 +00002165 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002166 i;
2167
cristy9af9b5d2010-08-15 17:04:28 +00002168 ssize_t
2169 j;
2170
cristy3ed852e2009-09-05 21:47:34 +00002171 pixel=zero;
2172 if (image->matte == MagickFalse)
2173 {
2174 for (i=0; i < n; i++)
2175 {
2176 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2177 (contribution[i].pixel-contribution[0].pixel);
2178 alpha=contribution[i].weight;
2179 pixel.red+=alpha*(p+j)->red;
2180 pixel.green+=alpha*(p+j)->green;
2181 pixel.blue+=alpha*(p+j)->blue;
2182 pixel.opacity+=alpha*(p+j)->opacity;
2183 }
cristyce70c172010-01-07 17:15:30 +00002184 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2185 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2186 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2187 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002188 if ((image->colorspace == CMYKColorspace) &&
2189 (resize_image->colorspace == CMYKColorspace))
2190 {
2191 for (i=0; i < n; i++)
2192 {
2193 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2194 (contribution[i].pixel-contribution[0].pixel);
2195 alpha=contribution[i].weight;
2196 pixel.index+=alpha*indexes[j];
2197 }
cristyce70c172010-01-07 17:15:30 +00002198 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002199 }
2200 }
2201 else
2202 {
2203 MagickRealType
2204 gamma;
2205
2206 gamma=0.0;
2207 for (i=0; i < n; i++)
2208 {
2209 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2210 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002211 alpha=contribution[i].weight*QuantumScale*
2212 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002213 pixel.red+=alpha*(p+j)->red;
2214 pixel.green+=alpha*(p+j)->green;
2215 pixel.blue+=alpha*(p+j)->blue;
2216 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2217 gamma+=alpha;
2218 }
2219 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002220 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2221 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2222 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2223 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002224 if ((image->colorspace == CMYKColorspace) &&
2225 (resize_image->colorspace == CMYKColorspace))
2226 {
2227 for (i=0; i < n; i++)
2228 {
2229 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2230 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002231 alpha=contribution[i].weight*QuantumScale*
2232 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002233 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002234 }
cristyce70c172010-01-07 17:15:30 +00002235 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2236 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002237 }
2238 }
2239 if ((resize_image->storage_class == PseudoClass) &&
2240 (image->storage_class == PseudoClass))
2241 {
cristybb503372010-05-27 20:51:26 +00002242 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002243 1.0)+0.5);
2244 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2245 (contribution[i-start].pixel-contribution[0].pixel);
2246 resize_indexes[y]=indexes[j];
2247 }
2248 q++;
2249 }
2250 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2251 status=MagickFalse;
2252 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2253 {
2254 MagickBooleanType
2255 proceed;
2256
cristyb5d5f722009-11-04 03:03:49 +00002257#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002258 #pragma omp critical (MagickCore_HorizontalFilter)
2259#endif
cristy9af9b5d2010-08-15 17:04:28 +00002260 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002261 if (proceed == MagickFalse)
2262 status=MagickFalse;
2263 }
2264 }
2265 resize_view=DestroyCacheView(resize_view);
2266 image_view=DestroyCacheView(image_view);
2267 contributions=DestroyContributionThreadSet(contributions);
2268 return(status);
2269}
2270
2271static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2272 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002273 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002274{
cristyfa112112010-01-04 17:48:07 +00002275 CacheView
2276 *image_view,
2277 *resize_view;
2278
cristy3ed852e2009-09-05 21:47:34 +00002279 ClassType
2280 storage_class;
2281
2282 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002283 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002284
cristy3ed852e2009-09-05 21:47:34 +00002285 MagickBooleanType
2286 status;
2287
2288 MagickPixelPacket
2289 zero;
2290
2291 MagickRealType
2292 scale,
2293 support;
2294
cristy9af9b5d2010-08-15 17:04:28 +00002295 ssize_t
2296 y;
2297
cristy3ed852e2009-09-05 21:47:34 +00002298 /*
cristy9af9b5d2010-08-15 17:04:28 +00002299 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002300 */
cristy5d824382010-09-06 14:00:17 +00002301 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002302 support=scale*GetResizeFilterSupport(resize_filter);
2303 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2304 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2305 {
2306 InheritException(exception,&resize_image->exception);
2307 return(MagickFalse);
2308 }
2309 if (support < 0.5)
2310 {
2311 /*
nicolas07bac812010-09-19 18:47:02 +00002312 Support too small even for nearest neighbour: Reduce to point
2313 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002314 */
2315 support=(MagickRealType) 0.5;
2316 scale=1.0;
2317 }
2318 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2319 if (contributions == (ContributionInfo **) NULL)
2320 {
2321 (void) ThrowMagickException(exception,GetMagickModule(),
2322 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2323 return(MagickFalse);
2324 }
2325 status=MagickTrue;
2326 scale=1.0/scale;
2327 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2328 image_view=AcquireCacheView(image);
2329 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002330#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002331 #pragma omp parallel for shared(status)
2332#endif
cristybb503372010-05-27 20:51:26 +00002333 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002334 {
cristy3ed852e2009-09-05 21:47:34 +00002335 MagickRealType
2336 center,
2337 density;
2338
2339 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002340 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002341
2342 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002343 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002344
cristy03dbbd22010-09-19 23:04:47 +00002345 register ContributionInfo
2346 *restrict contribution;
2347
cristy3ed852e2009-09-05 21:47:34 +00002348 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002349 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002350
cristy9af9b5d2010-08-15 17:04:28 +00002351 register PixelPacket
2352 *restrict q;
2353
cristybb503372010-05-27 20:51:26 +00002354 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002355 x;
2356
cristy9af9b5d2010-08-15 17:04:28 +00002357 ssize_t
2358 n,
2359 start,
2360 stop;
cristy3ed852e2009-09-05 21:47:34 +00002361
2362 if (status == MagickFalse)
2363 continue;
cristy679e6962010-03-18 00:42:45 +00002364 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002365 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2366 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002367 density=0.0;
2368 contribution=contributions[GetOpenMPThreadId()];
2369 for (n=0; n < (stop-start); n++)
2370 {
2371 contribution[n].pixel=start+n;
2372 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2373 ((MagickRealType) (start+n)-center+0.5));
2374 density+=contribution[n].weight;
2375 }
2376 if ((density != 0.0) && (density != 1.0))
2377 {
cristybb503372010-05-27 20:51:26 +00002378 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002379 i;
2380
2381 /*
2382 Normalize.
2383 */
2384 density=1.0/density;
2385 for (i=0; i < n; i++)
2386 contribution[i].weight*=density;
2387 }
2388 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002389 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2390 exception);
cristy3ed852e2009-09-05 21:47:34 +00002391 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2392 exception);
2393 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2394 {
2395 status=MagickFalse;
2396 continue;
2397 }
2398 indexes=GetCacheViewVirtualIndexQueue(image_view);
2399 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002400 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002401 {
cristy3ed852e2009-09-05 21:47:34 +00002402 MagickPixelPacket
2403 pixel;
2404
2405 MagickRealType
2406 alpha;
2407
cristybb503372010-05-27 20:51:26 +00002408 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002409 i;
2410
cristy9af9b5d2010-08-15 17:04:28 +00002411 ssize_t
2412 j;
2413
cristy3ed852e2009-09-05 21:47:34 +00002414 pixel=zero;
2415 if (image->matte == MagickFalse)
2416 {
2417 for (i=0; i < n; i++)
2418 {
cristybb503372010-05-27 20:51:26 +00002419 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002420 image->columns+x);
2421 alpha=contribution[i].weight;
2422 pixel.red+=alpha*(p+j)->red;
2423 pixel.green+=alpha*(p+j)->green;
2424 pixel.blue+=alpha*(p+j)->blue;
2425 pixel.opacity+=alpha*(p+j)->opacity;
2426 }
cristyce70c172010-01-07 17:15:30 +00002427 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2428 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2429 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2430 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002431 if ((image->colorspace == CMYKColorspace) &&
2432 (resize_image->colorspace == CMYKColorspace))
2433 {
2434 for (i=0; i < n; i++)
2435 {
cristybb503372010-05-27 20:51:26 +00002436 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002437 image->columns+x);
2438 alpha=contribution[i].weight;
2439 pixel.index+=alpha*indexes[j];
2440 }
cristyce70c172010-01-07 17:15:30 +00002441 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002442 }
2443 }
2444 else
2445 {
2446 MagickRealType
2447 gamma;
2448
2449 gamma=0.0;
2450 for (i=0; i < n; i++)
2451 {
cristybb503372010-05-27 20:51:26 +00002452 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002453 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002454 alpha=contribution[i].weight*QuantumScale*
2455 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002456 pixel.red+=alpha*(p+j)->red;
2457 pixel.green+=alpha*(p+j)->green;
2458 pixel.blue+=alpha*(p+j)->blue;
2459 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2460 gamma+=alpha;
2461 }
2462 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002463 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2464 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2465 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2466 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002467 if ((image->colorspace == CMYKColorspace) &&
2468 (resize_image->colorspace == CMYKColorspace))
2469 {
2470 for (i=0; i < n; i++)
2471 {
cristybb503372010-05-27 20:51:26 +00002472 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002473 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002474 alpha=contribution[i].weight*QuantumScale*
2475 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002476 pixel.index+=alpha*indexes[j];
2477 }
cristyce70c172010-01-07 17:15:30 +00002478 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2479 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002480 }
2481 }
2482 if ((resize_image->storage_class == PseudoClass) &&
2483 (image->storage_class == PseudoClass))
2484 {
cristybb503372010-05-27 20:51:26 +00002485 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002486 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002487 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002488 image->columns+x);
2489 resize_indexes[x]=indexes[j];
2490 }
2491 q++;
2492 }
2493 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2494 status=MagickFalse;
2495 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2496 {
2497 MagickBooleanType
2498 proceed;
2499
cristyb5d5f722009-11-04 03:03:49 +00002500#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002501 #pragma omp critical (MagickCore_VerticalFilter)
2502#endif
cristy9af9b5d2010-08-15 17:04:28 +00002503 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002504 if (proceed == MagickFalse)
2505 status=MagickFalse;
2506 }
2507 }
2508 resize_view=DestroyCacheView(resize_view);
2509 image_view=DestroyCacheView(image_view);
2510 contributions=DestroyContributionThreadSet(contributions);
2511 return(status);
2512}
2513
cristybb503372010-05-27 20:51:26 +00002514MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2515 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002516 ExceptionInfo *exception)
2517{
2518#define WorkLoadFactor 0.265
2519
2520 FilterTypes
2521 filter_type;
2522
2523 Image
2524 *filter_image,
2525 *resize_image;
2526
cristy9af9b5d2010-08-15 17:04:28 +00002527 MagickOffsetType
2528 offset;
2529
cristy3ed852e2009-09-05 21:47:34 +00002530 MagickRealType
2531 x_factor,
2532 y_factor;
2533
2534 MagickSizeType
2535 span;
2536
2537 MagickStatusType
2538 status;
2539
2540 ResizeFilter
2541 *resize_filter;
2542
cristy3ed852e2009-09-05 21:47:34 +00002543 /*
2544 Acquire resize image.
2545 */
2546 assert(image != (Image *) NULL);
2547 assert(image->signature == MagickSignature);
2548 if (image->debug != MagickFalse)
2549 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2550 assert(exception != (ExceptionInfo *) NULL);
2551 assert(exception->signature == MagickSignature);
2552 if ((columns == 0) || (rows == 0))
2553 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2554 if ((columns == image->columns) && (rows == image->rows) &&
2555 (filter == UndefinedFilter) && (blur == 1.0))
2556 return(CloneImage(image,0,0,MagickTrue,exception));
2557 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2558 if (resize_image == (Image *) NULL)
2559 return(resize_image);
2560 /*
2561 Acquire resize filter.
2562 */
2563 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2564 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2565 if ((x_factor*y_factor) > WorkLoadFactor)
2566 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2567 else
2568 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2569 if (filter_image == (Image *) NULL)
2570 return(DestroyImage(resize_image));
2571 filter_type=LanczosFilter;
2572 if (filter != UndefinedFilter)
2573 filter_type=filter;
2574 else
2575 if ((x_factor == 1.0) && (y_factor == 1.0))
2576 filter_type=PointFilter;
2577 else
2578 if ((image->storage_class == PseudoClass) ||
2579 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2580 filter_type=MitchellFilter;
2581 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2582 exception);
2583 /*
2584 Resize image.
2585 */
cristy9af9b5d2010-08-15 17:04:28 +00002586 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002587 if ((x_factor*y_factor) > WorkLoadFactor)
2588 {
2589 span=(MagickSizeType) (filter_image->columns+rows);
2590 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002591 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002592 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002593 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002594 }
2595 else
2596 {
2597 span=(MagickSizeType) (filter_image->rows+columns);
2598 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002599 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002600 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002601 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002602 }
2603 /*
2604 Free resources.
2605 */
2606 filter_image=DestroyImage(filter_image);
2607 resize_filter=DestroyResizeFilter(resize_filter);
2608 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2609 return((Image *) NULL);
2610 resize_image->type=image->type;
2611 return(resize_image);
2612}
2613
2614/*
2615%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2616% %
2617% %
2618% %
2619% S a m p l e I m a g e %
2620% %
2621% %
2622% %
2623%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2624%
2625% SampleImage() scales an image to the desired dimensions with pixel
2626% sampling. Unlike other scaling methods, this method does not introduce
2627% any additional color into the scaled image.
2628%
2629% The format of the SampleImage method is:
2630%
cristybb503372010-05-27 20:51:26 +00002631% Image *SampleImage(const Image *image,const size_t columns,
2632% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002633%
2634% A description of each parameter follows:
2635%
2636% o image: the image.
2637%
2638% o columns: the number of columns in the sampled image.
2639%
2640% o rows: the number of rows in the sampled image.
2641%
2642% o exception: return any errors or warnings in this structure.
2643%
2644*/
cristybb503372010-05-27 20:51:26 +00002645MagickExport Image *SampleImage(const Image *image,const size_t columns,
2646 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002647{
2648#define SampleImageTag "Sample/Image"
2649
cristyc4c8d132010-01-07 01:58:38 +00002650 CacheView
2651 *image_view,
2652 *sample_view;
2653
cristy3ed852e2009-09-05 21:47:34 +00002654 Image
2655 *sample_image;
2656
cristy3ed852e2009-09-05 21:47:34 +00002657 MagickBooleanType
2658 status;
2659
cristy5f959472010-05-27 22:19:46 +00002660 MagickOffsetType
2661 progress;
2662
cristybb503372010-05-27 20:51:26 +00002663 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002664 x;
2665
cristy5f959472010-05-27 22:19:46 +00002666 ssize_t
2667 *x_offset,
2668 y;
2669
cristy3ed852e2009-09-05 21:47:34 +00002670 /*
2671 Initialize sampled image attributes.
2672 */
2673 assert(image != (const Image *) NULL);
2674 assert(image->signature == MagickSignature);
2675 if (image->debug != MagickFalse)
2676 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2677 assert(exception != (ExceptionInfo *) NULL);
2678 assert(exception->signature == MagickSignature);
2679 if ((columns == 0) || (rows == 0))
2680 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2681 if ((columns == image->columns) && (rows == image->rows))
2682 return(CloneImage(image,0,0,MagickTrue,exception));
2683 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2684 if (sample_image == (Image *) NULL)
2685 return((Image *) NULL);
2686 /*
2687 Allocate scan line buffer and column offset buffers.
2688 */
cristybb503372010-05-27 20:51:26 +00002689 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002690 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002691 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002692 {
2693 sample_image=DestroyImage(sample_image);
2694 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2695 }
cristybb503372010-05-27 20:51:26 +00002696 for (x=0; x < (ssize_t) sample_image->columns; x++)
2697 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002698 sample_image->columns);
2699 /*
2700 Sample each row.
2701 */
2702 status=MagickTrue;
2703 progress=0;
2704 image_view=AcquireCacheView(image);
2705 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002706#if defined(MAGICKCORE_OPENMP_SUPPORT)
2707 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002708#endif
cristybb503372010-05-27 20:51:26 +00002709 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002710 {
cristy3ed852e2009-09-05 21:47:34 +00002711 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002712 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002713
2714 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002715 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002716
2717 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002718 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002719
cristy3ed852e2009-09-05 21:47:34 +00002720 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002721 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002722
cristy03dbbd22010-09-19 23:04:47 +00002723 register ssize_t
2724 x;
2725
cristy9af9b5d2010-08-15 17:04:28 +00002726 ssize_t
2727 y_offset;
2728
cristy3ed852e2009-09-05 21:47:34 +00002729 if (status == MagickFalse)
2730 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002731 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2732 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002733 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2734 exception);
2735 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2736 exception);
2737 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2738 {
2739 status=MagickFalse;
2740 continue;
2741 }
2742 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2743 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2744 /*
2745 Sample each column.
2746 */
cristybb503372010-05-27 20:51:26 +00002747 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002748 *q++=p[x_offset[x]];
2749 if ((image->storage_class == PseudoClass) ||
2750 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002751 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002752 sample_indexes[x]=indexes[x_offset[x]];
2753 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2754 status=MagickFalse;
2755 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2756 {
2757 MagickBooleanType
2758 proceed;
2759
cristyb5d5f722009-11-04 03:03:49 +00002760#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002761 #pragma omp critical (MagickCore_SampleImage)
2762#endif
2763 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2764 if (proceed == MagickFalse)
2765 status=MagickFalse;
2766 }
2767 }
2768 image_view=DestroyCacheView(image_view);
2769 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002770 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002771 sample_image->type=image->type;
2772 return(sample_image);
2773}
2774
2775/*
2776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2777% %
2778% %
2779% %
2780% S c a l e I m a g e %
2781% %
2782% %
2783% %
2784%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2785%
2786% ScaleImage() changes the size of an image to the given dimensions.
2787%
2788% The format of the ScaleImage method is:
2789%
cristybb503372010-05-27 20:51:26 +00002790% Image *ScaleImage(const Image *image,const size_t columns,
2791% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002792%
2793% A description of each parameter follows:
2794%
2795% o image: the image.
2796%
2797% o columns: the number of columns in the scaled image.
2798%
2799% o rows: the number of rows in the scaled image.
2800%
2801% o exception: return any errors or warnings in this structure.
2802%
2803*/
cristybb503372010-05-27 20:51:26 +00002804MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2805 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002806{
2807#define ScaleImageTag "Scale/Image"
2808
cristyed6cb232010-01-20 03:07:53 +00002809 CacheView
2810 *image_view,
2811 *scale_view;
2812
cristy3ed852e2009-09-05 21:47:34 +00002813 Image
2814 *scale_image;
2815
cristy3ed852e2009-09-05 21:47:34 +00002816 MagickBooleanType
2817 next_column,
2818 next_row,
2819 proceed;
2820
2821 MagickPixelPacket
2822 pixel,
2823 *scale_scanline,
2824 *scanline,
2825 *x_vector,
2826 *y_vector,
2827 zero;
2828
cristy3ed852e2009-09-05 21:47:34 +00002829 PointInfo
2830 scale,
2831 span;
2832
cristybb503372010-05-27 20:51:26 +00002833 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002834 i;
2835
cristy9af9b5d2010-08-15 17:04:28 +00002836 ssize_t
2837 number_rows,
2838 y;
2839
cristy3ed852e2009-09-05 21:47:34 +00002840 /*
2841 Initialize scaled image attributes.
2842 */
2843 assert(image != (const Image *) NULL);
2844 assert(image->signature == MagickSignature);
2845 if (image->debug != MagickFalse)
2846 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2847 assert(exception != (ExceptionInfo *) NULL);
2848 assert(exception->signature == MagickSignature);
2849 if ((columns == 0) || (rows == 0))
2850 return((Image *) NULL);
2851 if ((columns == image->columns) && (rows == image->rows))
2852 return(CloneImage(image,0,0,MagickTrue,exception));
2853 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2854 if (scale_image == (Image *) NULL)
2855 return((Image *) NULL);
2856 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2857 {
2858 InheritException(exception,&scale_image->exception);
2859 scale_image=DestroyImage(scale_image);
2860 return((Image *) NULL);
2861 }
2862 /*
2863 Allocate memory.
2864 */
2865 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2866 sizeof(*x_vector));
2867 scanline=x_vector;
2868 if (image->rows != scale_image->rows)
2869 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2870 sizeof(*scanline));
2871 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2872 scale_image->columns,sizeof(*scale_scanline));
2873 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2874 sizeof(*y_vector));
2875 if ((scanline == (MagickPixelPacket *) NULL) ||
2876 (scale_scanline == (MagickPixelPacket *) NULL) ||
2877 (x_vector == (MagickPixelPacket *) NULL) ||
2878 (y_vector == (MagickPixelPacket *) NULL))
2879 {
2880 scale_image=DestroyImage(scale_image);
2881 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2882 }
2883 /*
2884 Scale image.
2885 */
2886 number_rows=0;
2887 next_row=MagickTrue;
2888 span.y=1.0;
2889 scale.y=(double) scale_image->rows/(double) image->rows;
2890 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2891 sizeof(*y_vector));
2892 GetMagickPixelPacket(image,&pixel);
2893 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2894 i=0;
cristyed6cb232010-01-20 03:07:53 +00002895 image_view=AcquireCacheView(image);
2896 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002897 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002898 {
2899 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002900 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002901
2902 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002903 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002904
2905 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002906 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002907
cristy3ed852e2009-09-05 21:47:34 +00002908 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002909 *restrict s,
2910 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002911
2912 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002913 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002914
cristy9af9b5d2010-08-15 17:04:28 +00002915 register ssize_t
2916 x;
2917
cristyed6cb232010-01-20 03:07:53 +00002918 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2919 exception);
cristy3ed852e2009-09-05 21:47:34 +00002920 if (q == (PixelPacket *) NULL)
2921 break;
2922 scale_indexes=GetAuthenticIndexQueue(scale_image);
2923 if (scale_image->rows == image->rows)
2924 {
2925 /*
2926 Read a new scanline.
2927 */
cristyed6cb232010-01-20 03:07:53 +00002928 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2929 exception);
cristy3ed852e2009-09-05 21:47:34 +00002930 if (p == (const PixelPacket *) NULL)
2931 break;
cristyed6cb232010-01-20 03:07:53 +00002932 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002933 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002934 {
cristyce70c172010-01-07 17:15:30 +00002935 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2936 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2937 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002938 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002939 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002940 if (indexes != (IndexPacket *) NULL)
2941 x_vector[x].index=(MagickRealType) indexes[x];
2942 p++;
2943 }
2944 }
2945 else
2946 {
2947 /*
2948 Scale Y direction.
2949 */
2950 while (scale.y < span.y)
2951 {
cristy9af9b5d2010-08-15 17:04:28 +00002952 if ((next_row != MagickFalse) &&
2953 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002954 {
2955 /*
2956 Read a new scanline.
2957 */
cristyed6cb232010-01-20 03:07:53 +00002958 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2959 exception);
cristy3ed852e2009-09-05 21:47:34 +00002960 if (p == (const PixelPacket *) NULL)
2961 break;
cristyed6cb232010-01-20 03:07:53 +00002962 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002963 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002964 {
cristyce70c172010-01-07 17:15:30 +00002965 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2966 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2967 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002968 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002969 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002970 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002971 if (indexes != (IndexPacket *) NULL)
2972 x_vector[x].index=(MagickRealType) indexes[x];
2973 p++;
2974 }
2975 number_rows++;
2976 }
cristybb503372010-05-27 20:51:26 +00002977 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002978 {
2979 y_vector[x].red+=scale.y*x_vector[x].red;
2980 y_vector[x].green+=scale.y*x_vector[x].green;
2981 y_vector[x].blue+=scale.y*x_vector[x].blue;
2982 if (scale_image->matte != MagickFalse)
2983 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2984 if (scale_indexes != (IndexPacket *) NULL)
2985 y_vector[x].index+=scale.y*x_vector[x].index;
2986 }
2987 span.y-=scale.y;
2988 scale.y=(double) scale_image->rows/(double) image->rows;
2989 next_row=MagickTrue;
2990 }
cristybb503372010-05-27 20:51:26 +00002991 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002992 {
2993 /*
2994 Read a new scanline.
2995 */
cristyed6cb232010-01-20 03:07:53 +00002996 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2997 exception);
cristy3ed852e2009-09-05 21:47:34 +00002998 if (p == (const PixelPacket *) NULL)
2999 break;
cristyed6cb232010-01-20 03:07:53 +00003000 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00003001 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003002 {
cristyce70c172010-01-07 17:15:30 +00003003 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
3004 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
3005 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003006 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00003007 x_vector[x].opacity=(MagickRealType)
3008 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003009 if (indexes != (IndexPacket *) NULL)
3010 x_vector[x].index=(MagickRealType) indexes[x];
3011 p++;
3012 }
3013 number_rows++;
3014 next_row=MagickFalse;
3015 }
3016 s=scanline;
cristybb503372010-05-27 20:51:26 +00003017 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003018 {
3019 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3020 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3021 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3022 if (image->matte != MagickFalse)
3023 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3024 if (scale_indexes != (IndexPacket *) NULL)
3025 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3026 s->red=pixel.red;
3027 s->green=pixel.green;
3028 s->blue=pixel.blue;
3029 if (scale_image->matte != MagickFalse)
3030 s->opacity=pixel.opacity;
3031 if (scale_indexes != (IndexPacket *) NULL)
3032 s->index=pixel.index;
3033 s++;
3034 y_vector[x]=zero;
3035 }
3036 scale.y-=span.y;
3037 if (scale.y <= 0)
3038 {
3039 scale.y=(double) scale_image->rows/(double) image->rows;
3040 next_row=MagickTrue;
3041 }
3042 span.y=1.0;
3043 }
3044 if (scale_image->columns == image->columns)
3045 {
3046 /*
3047 Transfer scanline to scaled image.
3048 */
3049 s=scanline;
cristybb503372010-05-27 20:51:26 +00003050 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003051 {
cristyce70c172010-01-07 17:15:30 +00003052 q->red=ClampToQuantum(s->red);
3053 q->green=ClampToQuantum(s->green);
3054 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003055 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003056 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003057 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003058 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003059 q++;
3060 s++;
3061 }
3062 }
3063 else
3064 {
3065 /*
3066 Scale X direction.
3067 */
3068 pixel=zero;
3069 next_column=MagickFalse;
3070 span.x=1.0;
3071 s=scanline;
3072 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003073 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003074 {
3075 scale.x=(double) scale_image->columns/(double) image->columns;
3076 while (scale.x >= span.x)
3077 {
3078 if (next_column != MagickFalse)
3079 {
3080 pixel=zero;
3081 t++;
3082 }
3083 pixel.red+=span.x*s->red;
3084 pixel.green+=span.x*s->green;
3085 pixel.blue+=span.x*s->blue;
3086 if (image->matte != MagickFalse)
3087 pixel.opacity+=span.x*s->opacity;
3088 if (scale_indexes != (IndexPacket *) NULL)
3089 pixel.index+=span.x*s->index;
3090 t->red=pixel.red;
3091 t->green=pixel.green;
3092 t->blue=pixel.blue;
3093 if (scale_image->matte != MagickFalse)
3094 t->opacity=pixel.opacity;
3095 if (scale_indexes != (IndexPacket *) NULL)
3096 t->index=pixel.index;
3097 scale.x-=span.x;
3098 span.x=1.0;
3099 next_column=MagickTrue;
3100 }
3101 if (scale.x > 0)
3102 {
3103 if (next_column != MagickFalse)
3104 {
3105 pixel=zero;
3106 next_column=MagickFalse;
3107 t++;
3108 }
3109 pixel.red+=scale.x*s->red;
3110 pixel.green+=scale.x*s->green;
3111 pixel.blue+=scale.x*s->blue;
3112 if (scale_image->matte != MagickFalse)
3113 pixel.opacity+=scale.x*s->opacity;
3114 if (scale_indexes != (IndexPacket *) NULL)
3115 pixel.index+=scale.x*s->index;
3116 span.x-=scale.x;
3117 }
3118 s++;
3119 }
3120 if (span.x > 0)
3121 {
3122 s--;
3123 pixel.red+=span.x*s->red;
3124 pixel.green+=span.x*s->green;
3125 pixel.blue+=span.x*s->blue;
3126 if (scale_image->matte != MagickFalse)
3127 pixel.opacity+=span.x*s->opacity;
3128 if (scale_indexes != (IndexPacket *) NULL)
3129 pixel.index+=span.x*s->index;
3130 }
3131 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003132 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003133 {
3134 t->red=pixel.red;
3135 t->green=pixel.green;
3136 t->blue=pixel.blue;
3137 if (scale_image->matte != MagickFalse)
3138 t->opacity=pixel.opacity;
3139 if (scale_indexes != (IndexPacket *) NULL)
3140 t->index=pixel.index;
3141 }
3142 /*
3143 Transfer scanline to scaled image.
3144 */
3145 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003146 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003147 {
cristyce70c172010-01-07 17:15:30 +00003148 q->red=ClampToQuantum(t->red);
3149 q->green=ClampToQuantum(t->green);
3150 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003151 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003152 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003153 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003154 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003155 t++;
3156 q++;
3157 }
3158 }
cristyed6cb232010-01-20 03:07:53 +00003159 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003160 break;
cristy96b16132010-08-29 17:19:52 +00003161 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3162 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003163 if (proceed == MagickFalse)
3164 break;
3165 }
cristyed6cb232010-01-20 03:07:53 +00003166 scale_view=DestroyCacheView(scale_view);
3167 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003168 /*
3169 Free allocated memory.
3170 */
3171 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3172 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3173 if (scale_image->rows != image->rows)
3174 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3175 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3176 scale_image->type=image->type;
3177 return(scale_image);
3178}
3179
3180/*
3181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3182% %
3183% %
3184% %
3185+ S e t R e s i z e F i l t e r S u p p o r t %
3186% %
3187% %
3188% %
3189%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3190%
3191% SetResizeFilterSupport() specifies which IR filter to use to window
3192%
3193% The format of the SetResizeFilterSupport method is:
3194%
3195% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3196% const MagickRealType support)
3197%
3198% A description of each parameter follows:
3199%
3200% o resize_filter: the resize filter.
3201%
3202% o support: the filter spport radius.
3203%
3204*/
3205MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3206 const MagickRealType support)
3207{
3208 assert(resize_filter != (ResizeFilter *) NULL);
3209 assert(resize_filter->signature == MagickSignature);
3210 resize_filter->support=support;
3211}
3212
3213/*
3214%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3215% %
3216% %
3217% %
3218% T h u m b n a i l I m a g e %
3219% %
3220% %
3221% %
3222%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3223%
3224% ThumbnailImage() changes the size of an image to the given dimensions and
3225% removes any associated profiles. The goal is to produce small low cost
3226% thumbnail images suited for display on the Web.
3227%
3228% The format of the ThumbnailImage method is:
3229%
cristybb503372010-05-27 20:51:26 +00003230% Image *ThumbnailImage(const Image *image,const size_t columns,
3231% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003232%
3233% A description of each parameter follows:
3234%
3235% o image: the image.
3236%
3237% o columns: the number of columns in the scaled image.
3238%
3239% o rows: the number of rows in the scaled image.
3240%
3241% o exception: return any errors or warnings in this structure.
3242%
3243*/
cristy9af9b5d2010-08-15 17:04:28 +00003244MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3245 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003246{
3247#define SampleFactor 5
3248
3249 char
3250 value[MaxTextExtent];
3251
3252 const char
3253 *name;
3254
3255 Image
3256 *thumbnail_image;
3257
3258 MagickRealType
3259 x_factor,
3260 y_factor;
3261
cristybb503372010-05-27 20:51:26 +00003262 size_t
cristy3ed852e2009-09-05 21:47:34 +00003263 version;
3264
cristy9af9b5d2010-08-15 17:04:28 +00003265 struct stat
3266 attributes;
3267
cristy3ed852e2009-09-05 21:47:34 +00003268 assert(image != (Image *) NULL);
3269 assert(image->signature == MagickSignature);
3270 if (image->debug != MagickFalse)
3271 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3272 assert(exception != (ExceptionInfo *) NULL);
3273 assert(exception->signature == MagickSignature);
3274 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3275 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3276 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003277 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3278 exception);
cristy3ed852e2009-09-05 21:47:34 +00003279 else
3280 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003281 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3282 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003283 else
3284 {
3285 Image
3286 *sample_image;
3287
3288 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3289 exception);
3290 if (sample_image == (Image *) NULL)
3291 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003292 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3293 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003294 sample_image=DestroyImage(sample_image);
3295 }
3296 if (thumbnail_image == (Image *) NULL)
3297 return(thumbnail_image);
3298 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3299 if (thumbnail_image->matte == MagickFalse)
3300 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3301 thumbnail_image->depth=8;
3302 thumbnail_image->interlace=NoInterlace;
3303 /*
3304 Strip all profiles except color profiles.
3305 */
3306 ResetImageProfileIterator(thumbnail_image);
3307 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3308 {
3309 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3310 {
cristy2b726bd2010-01-11 01:05:39 +00003311 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003312 ResetImageProfileIterator(thumbnail_image);
3313 }
3314 name=GetNextImageProfile(thumbnail_image);
3315 }
3316 (void) DeleteImageProperty(thumbnail_image,"comment");
3317 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003318 if (strstr(image->magick_filename,"//") == (char *) NULL)
3319 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003320 image->magick_filename);
3321 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3322 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3323 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3324 {
cristye8c25f92010-06-03 00:53:06 +00003325 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003326 attributes.st_mtime);
3327 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3328 }
cristye8c25f92010-06-03 00:53:06 +00003329 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003330 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003331 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003332 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003333 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3334 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3335 LocaleLower(value);
3336 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3337 (void) SetImageProperty(thumbnail_image,"software",
3338 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003339 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3340 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003341 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003342 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003343 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003344 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003345 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3346 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003347 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3348 return(thumbnail_image);
3349}