blob: 3129f1d32a7d760a9d74d7aea4ea24508f726f8a [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;
anthony853d6972010-10-08 06:01:31 +0000418 const MagickRealType p =
nicolas3aab40c2010-09-19 21:14:15 +0000419 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
anthony853d6972010-10-08 06:01:31 +0000496% SincFast Lanczos2D Robidoux
anthony61b5ddd2010-10-05 02:33:31 +0000497%
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%
anthony853d6972010-10-08 06:01:31 +0000529% The Lanczos2D filter is just 2-lobed Lanczos using Sinc/Jinc as
530% appropriate. The Robidoux is the same thing but is modified (through
531% the blur setting) in order to make the results sharper in the 'no-op' case.
532% (see notes below)
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 */
anthony853d6972010-10-08 06:01:31 +0000683 { RobidouxFilter, JincFilter }, /* SPECIAL: Lanzcos2D blurred */
cristy3ed852e2009-09-05 21:47:34 +0000684 };
685 /*
nicolas32f44eb2010-09-20 01:23:12 +0000686 Table mapping the filter/window from the above table to an actual
nicolas07bac812010-09-19 18:47:02 +0000687 function. The default support size for that filter as a weighting
688 function, the range to scale with to use that function as a sinc
689 windowing function, (typ 1.0).
anthony933b4bf2010-09-10 02:31:37 +0000690
anthony07a3f7f2010-09-16 03:03:11 +0000691 Note that the filter_type -> function is 1 to 1 except for Sinc(),
nicolas07bac812010-09-19 18:47:02 +0000692 SincFast(), and CubicBC() functions, which may have multiple
693 filter to function associations.
694
695 See "filter:verbose" handling below for the function -> filter
696 mapping.
cristy3ed852e2009-09-05 21:47:34 +0000697 */
698 static struct
699 {
700 MagickRealType
701 (*function)(const MagickRealType, const ResizeFilter*),
anthony61b5ddd2010-10-05 02:33:31 +0000702 lobes, /* default lobes/support size of the weighting filter */
anthony2d9b8b52010-09-14 08:31:07 +0000703 scale, /* windowing function range, for scaling windowing function */
anthony933b4bf2010-09-10 02:31:37 +0000704 B,C; /* Cubic Filter factors for a CubicBC function, else ignored */
cristy3ed852e2009-09-05 21:47:34 +0000705 } const filters[SentinelFilter] =
706 {
anthony61b5ddd2010-10-05 02:33:31 +0000707 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */
708 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */
709 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */
710 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */
711 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */
712 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */
713 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */
714 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */
715 { Gaussian, 1.5, 1.5, 0.0, 0.0 }, /* Gaussian */
716 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */
717 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */
718 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */
anthony853d6972010-10-08 06:01:31 +0000719 { CubicBC, 2.0, 1.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */
720 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */
721 { Jinc, 3.0, 1.21967, 0.0, 0.0 }, /* Raw 3-lobed Jinc */
anthony61b5ddd2010-10-05 02:33:31 +0000722 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */
723 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */
724 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */
725 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */
726 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */
727 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */
728 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */
729 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */
anthony853d6972010-10-08 06:01:31 +0000730 { Jinc, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2D, Jinc-Jinc */
731 { Jinc, 2.0, 1.0, 0.0, 0.0 }, /* Robidoux, blured Jinc-Jinc */
cristy3ed852e2009-09-05 21:47:34 +0000732 };
733 /*
nicolase473f722010-10-07 00:05:13 +0000734 The known zero crossings of the Jinc() or more accurately the
735 Jinc(x*PI) function being used as a filter. It is used by the
736 "filter:lobes" for support selection, so users do not have to deal
737 with the highly irrational sizes of the 'lobes' of the Jinc
738 filter.
anthony48f77622010-10-03 14:32:31 +0000739
nicolase473f722010-10-07 00:05:13 +0000740 Values taken from
anthony48f77622010-10-03 14:32:31 +0000741 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
nicolase473f722010-10-07 00:05:13 +0000742 using Jv-function with v=1, then dividing by PI.
cristy3ed852e2009-09-05 21:47:34 +0000743 */
744 static MagickRealType
anthony48f77622010-10-03 14:32:31 +0000745 jinc_zeros[16] =
cristy3ed852e2009-09-05 21:47:34 +0000746 {
anthonyc2d07db2010-09-15 23:47:40 +0000747 1.21966989126651,
748 2.23313059438153,
749 3.23831548416624,
750 4.24106286379607,
751 5.24276437687019,
752 6.24392168986449,
753 7.24475986871996,
754 8.24539491395205,
755 9.24589268494948,
756 10.2462933487549,
757 11.2466227948779,
758 12.2468984611381,
759 13.2471325221811,
760 14.2473337358069,
761 15.2475085630373,
762 16.247661874701
cristy3ed852e2009-09-05 21:47:34 +0000763 };
764
cristy33b1c162010-01-23 22:51:51 +0000765 /*
766 Allocate resize filter.
767 */
cristy3ed852e2009-09-05 21:47:34 +0000768 assert(image != (const Image *) NULL);
769 assert(image->signature == MagickSignature);
770 if (image->debug != MagickFalse)
771 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
772 assert(UndefinedFilter < filter && filter < SentinelFilter);
773 assert(exception != (ExceptionInfo *) NULL);
774 assert(exception->signature == MagickSignature);
cristy73bd4a52010-10-05 11:24:23 +0000775 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
cristy3ed852e2009-09-05 21:47:34 +0000776 if (resize_filter == (ResizeFilter *) NULL)
777 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
cristy33b1c162010-01-23 22:51:51 +0000778 /*
779 Defaults for the requested filter.
780 */
781 filter_type=mapping[filter].filter;
782 window_type=mapping[filter].window;
anthony48f77622010-10-03 14:32:31 +0000783 /* Cylindrical Filters should use Jinc instead of Sinc */
anthonyb6d08c52010-09-13 01:17:04 +0000784 if (cylindrical != MagickFalse)
cristy33b1c162010-01-23 22:51:51 +0000785 switch (filter_type)
786 {
787 case SincFilter:
anthony48f77622010-10-03 14:32:31 +0000788 /* Promote 1D Sinc Filter to a 2D Jinc filter. */
anthonyb6d08c52010-09-13 01:17:04 +0000789 if ( filter != SincFilter )
anthony48f77622010-10-03 14:32:31 +0000790 filter_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000791 break;
anthonyba5a7c32010-09-15 02:42:25 +0000792 case SincFastFilter:
nicolas07bac812010-09-19 18:47:02 +0000793 /* Ditto for SincFast variant */
anthonyba5a7c32010-09-15 02:42:25 +0000794 if ( filter != SincFastFilter )
anthony48f77622010-10-03 14:32:31 +0000795 filter_type=JincFilter;
anthony7bdc0ed2010-09-15 01:52:32 +0000796 break;
anthony61b5ddd2010-10-05 02:33:31 +0000797
cristy33b1c162010-01-23 22:51:51 +0000798 case LanczosFilter:
nicolas45b58a92010-10-07 15:46:39 +0000799 /* Promote Lanczos from a Sinc-Sinc to a Jinc-Jinc. */
anthony48f77622010-10-03 14:32:31 +0000800 filter_type=JincFilter;
801 window_type=JincFilter;
cristy33b1c162010-01-23 22:51:51 +0000802 break;
cristya782ecf2010-01-25 02:59:14 +0000803 default:
804 break;
cristy3ed852e2009-09-05 21:47:34 +0000805 }
anthony61b5ddd2010-10-05 02:33:31 +0000806 else
807 switch (filter_type)
808 {
809 case Lanczos2DFilter:
anthonyf2a5b062010-10-08 06:02:37 +0000810 case RobidouxFilter:
nicolas45b58a92010-10-07 15:46:39 +0000811 /* Demote to a 2-lobe Sinc-Sinc for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000812 window_type=SincFastFilter;
813 break;
814 default:
815 break;
816 }
817
cristy3ed852e2009-09-05 21:47:34 +0000818 artifact=GetImageArtifact(image,"filter:filter");
cristy33b1c162010-01-23 22:51:51 +0000819 if (artifact != (const char *) NULL)
820 {
cristy9af9b5d2010-08-15 17:04:28 +0000821 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000822 if ((UndefinedFilter < option) && (option < SentinelFilter))
anthony61b5ddd2010-10-05 02:33:31 +0000823 { /* Raw filter request - no window function. */
cristy33b1c162010-01-23 22:51:51 +0000824 filter_type=(FilterTypes) option;
825 window_type=BoxFilter;
826 }
827 if (option == LanczosFilter)
anthony61b5ddd2010-10-05 02:33:31 +0000828 { /* Lanczos is not a real filter but a self windowing Sinc/Jinc. */
anthony853d6972010-10-08 06:01:31 +0000829 filter_type=cylindrical != MagickFalse ? JincFilter : LanczosFilter;
anthony48f77622010-10-03 14:32:31 +0000830 window_type=cylindrical != MagickFalse ? JincFilter : SincFastFilter;
cristy33b1c162010-01-23 22:51:51 +0000831 }
nicolas07bac812010-09-19 18:47:02 +0000832 /* Filter override with a specific window function. */
cristy33b1c162010-01-23 22:51:51 +0000833 artifact=GetImageArtifact(image,"filter:window");
834 if (artifact != (const char *) NULL)
835 {
cristy9af9b5d2010-08-15 17:04:28 +0000836 option=ParseMagickOption(MagickFilterOptions,MagickFalse,artifact);
cristy33b1c162010-01-23 22:51:51 +0000837 if ((UndefinedFilter < option) && (option < SentinelFilter))
838 {
839 if (option != LanczosFilter)
840 window_type=(FilterTypes) option;
cristy9af9b5d2010-08-15 17:04:28 +0000841 else
anthony48f77622010-10-03 14:32:31 +0000842 window_type=cylindrical != MagickFalse ? JincFilter :
cristy03dbbd22010-09-19 23:04:47 +0000843 SincFastFilter;
cristy9af9b5d2010-08-15 17:04:28 +0000844 }
cristy33b1c162010-01-23 22:51:51 +0000845 }
cristy3ed852e2009-09-05 21:47:34 +0000846 }
cristy33b1c162010-01-23 22:51:51 +0000847 else
848 {
anthony48f77622010-10-03 14:32:31 +0000849 /* Window specified, but no filter function? Assume Sinc/Jinc. */
cristy33b1c162010-01-23 22:51:51 +0000850 artifact=GetImageArtifact(image,"filter:window");
851 if (artifact != (const char *) NULL)
852 {
853 option=ParseMagickOption(MagickFilterOptions,MagickFalse,
854 artifact);
855 if ((UndefinedFilter < option) && (option < SentinelFilter))
856 {
anthony61b5ddd2010-10-05 02:33:31 +0000857 filter_type=cylindrical != MagickFalse ?
858 JincFilter : SincFastFilter;
cristy19eb6412010-04-23 14:42:29 +0000859 window_type=(FilterTypes) option;
cristy33b1c162010-01-23 22:51:51 +0000860 }
861 }
862 }
nicolas07bac812010-09-19 18:47:02 +0000863 /* Assign the real functions to use for the filters selected. */
cristy33b1c162010-01-23 22:51:51 +0000864 resize_filter->filter=filters[filter_type].function;
anthony61b5ddd2010-10-05 02:33:31 +0000865 resize_filter->support=filters[filter_type].lobes;
cristy33b1c162010-01-23 22:51:51 +0000866 resize_filter->window=filters[window_type].function;
867 resize_filter->scale=filters[window_type].scale;
cristy3ed852e2009-09-05 21:47:34 +0000868 resize_filter->signature=MagickSignature;
anthony61b5ddd2010-10-05 02:33:31 +0000869
anthony10b8bc82010-10-02 12:48:46 +0000870 /* Filter blur -- scaling both filter and support window. */
871 resize_filter->blur=blur;
872 artifact=GetImageArtifact(image,"filter:blur");
873 if (artifact != (const char *) NULL)
874 resize_filter->blur=StringToDouble(artifact);
875 if (resize_filter->blur < MagickEpsilon)
876 resize_filter->blur=(MagickRealType) MagickEpsilon;
877
878 if (cylindrical != MagickFalse)
879 switch (filter_type)
880 {
881 case PointFilter:
882 case BoxFilter:
883 /* Support for Cylindrical Box should be sqrt(2)/2 */
cristy1c9bb452010-10-03 16:48:33 +0000884 resize_filter->support=(MagickRealType) MagickSQ1_2;
anthony10b8bc82010-10-02 12:48:46 +0000885 break;
886 case GaussianFilter:
887 /* Cylindrical Gaussian should have a sigma of sqrt(2)/2
888 * and not the default sigma of 1/2 - so use blur to enlarge
889 * and adjust support so actual practical support = 2.0 by default
890 */
891 resize_filter->blur *= MagickSQ2;
cristy1c9bb452010-10-03 16:48:33 +0000892 resize_filter->support = (MagickRealType) MagickSQ2; /* which times blur => 2.0 */
anthony10b8bc82010-10-02 12:48:46 +0000893 break;
anthony853d6972010-10-08 06:01:31 +0000894 case RobidouxFilter:
895 /* Special 2-lobed cylindrical Jinc-Jinc filter created by Nicolas
896 * Robidoux, Professor of Mathematics, Laurentian University.
nicolase473f722010-10-07 00:05:13 +0000897 *
anthony853d6972010-10-08 06:01:31 +0000898 * It is a "blur" (negative blur actually) adjusted 2-lobed
899 * Jinc-windowed-Jinc Cylindrical (radial) filter, designed to
900 * preserve straight vertical and horizontal features.
nicolasaab02f32010-10-08 00:27:07 +0000901 *
anthony853d6972010-10-08 06:01:31 +0000902 * Given the "Lanczos2D" filter defined by Andreas Gustafsson in his
903 * "Interactive Image Warping" (page 24)
904 * http://www.gson.org/thesis/warping-thesis.pdf
905 *
906 * Lanczos2D(x) = Jinc(x)*Jinc(x*r1/r2) with support r2
907 *
908 * (where r1 is the first root of the Jinc function, and r2 is the
909 * second), rescale the filter by a value 's'.
910 *
911 * Robidoux(x) = Lanczos2D(s*x) with support r2/s
912 *
913 * the value 's' is chossen so that images which are constant in the
914 * vertical direction, and images which are constant in the horizontal
915 * direction, are almost unchanged (the change being generically as
916 * small as possible) when the geometrical transformation applied to
917 * the image is the identity (a.k.a. "no-op" or no scaling ).
918 *
919 * As such this formula needs to hold true
920 *
921 * Lanczos2D(s)=-2*Lanczos2D(s*sqrt(2))-Lanczos2D(s*2).
922 *
923 * This value of s ensures that the value of a one-pixel-wide vertical
924 * line (equal to 1, say, on a black=0 background) is exactly
925 * preserved when no-op is applied to the image. It also ensures
926 * that, in the no-op case, the nearest two columns on either side are
927 * minimally changed (the farther columns are unchanged no matter
928 * what). Specifically, the nearest columns on the left and the right
929 * have values raised from zero to c, and the second closest columns
930 * on the left and right are lowered from 0 to minus c. That is, the
931 * very closest columns are made slightly positive, and the second
932 * closest are made slightly negative, in equal amounts. The size c
933 * of this blur/halo is .002042317. Consequently, image values
934 * between 0 and M which are constant on columns (or rows) are
935 * preserved by no-op to within 2Mc (less than one half of one percent
936 * of the dynamic range).
937 *
938 * Note that "high frequency modes" which are not aligned with image
939 * rows or columns are damped considerably. For example, the amplitude
940 * of the very highest energy mode, the so-called "checkerboard" mode,
941 * is reduced by almost 62% (still less than with "standard" Lanczos2D
942 * or with a comparable Gaussian kernel).
943 *
944 * This "optimal" scaling was discovered by Nicolas Robidoux of
945 * Laurentian University.
946 *
947 * Below, resize_filter->blur is 1/s.
anthony61b5ddd2010-10-05 02:33:31 +0000948 */
nicolas6237c462010-10-05 06:11:49 +0000949 resize_filter->blur *= (MagickRealType) 0.958033808;
anthony81b8bf92010-10-02 13:54:34 +0000950 default:
951 break;
anthony10b8bc82010-10-02 12:48:46 +0000952 }
anthony61b5ddd2010-10-05 02:33:31 +0000953 else
954 switch (filter_type)
955 {
956 case Lanczos2DFilter:
anthony853d6972010-10-08 06:01:31 +0000957 case RobidouxFilter:
958 /* Demote to a 2-lobe Lanczos (Sinc-Sinc) for orthogonal use. */
anthony61b5ddd2010-10-05 02:33:31 +0000959 resize_filter->filter=SincFast;
960 break;
961 default:
962 break;
963 }
964
anthony2d9b8b52010-09-14 08:31:07 +0000965 /* Filter support overrides. */
cristy3ed852e2009-09-05 21:47:34 +0000966 artifact=GetImageArtifact(image,"filter:lobes");
cristy33b1c162010-01-23 22:51:51 +0000967 if (artifact != (const char *) NULL)
968 {
cristybb503372010-05-27 20:51:26 +0000969 ssize_t
cristy33b1c162010-01-23 22:51:51 +0000970 lobes;
971
cristy96b16132010-08-29 17:19:52 +0000972 lobes=(ssize_t) StringToLong(artifact);
cristy33b1c162010-01-23 22:51:51 +0000973 if (lobes < 1)
974 lobes=1;
975 resize_filter->support=(MagickRealType) lobes;
cristy3ed852e2009-09-05 21:47:34 +0000976 }
anthony61b5ddd2010-10-05 02:33:31 +0000977 /* convert Jinc lobes to a real support value */
978 if (resize_filter->filter == Jinc)
979 {
980 if (resize_filter->support > 16)
981 resize_filter->support=jinc_zeros[15]; /* largest entry in table */
982 else
983 resize_filter->support = jinc_zeros[((long)resize_filter->support)-1];
984 }
985 /* expert override of the support setting */
cristy3ed852e2009-09-05 21:47:34 +0000986 artifact=GetImageArtifact(image,"filter:support");
987 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000988 resize_filter->support=fabs(StringToDouble(artifact));
989 /*
nicolas07bac812010-09-19 18:47:02 +0000990 Scale windowing function separatally to the support 'clipping'
991 window that calling operator is planning to actually use. (Expert
992 override)
cristy3ed852e2009-09-05 21:47:34 +0000993 */
anthony55f12332010-09-10 01:13:02 +0000994 resize_filter->window_support=resize_filter->support; /* default */
cristy3ed852e2009-09-05 21:47:34 +0000995 artifact=GetImageArtifact(image,"filter:win-support");
996 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +0000997 resize_filter->window_support=fabs(StringToDouble(artifact));
998 /*
anthony1f90a6b2010-09-14 08:56:31 +0000999 Adjust window function scaling to the windowing support for
1000 weighting function. This avoids a division on every filter call.
anthony55f12332010-09-10 01:13:02 +00001001 */
1002 resize_filter->scale /= resize_filter->window_support;
1003 /*
nicolas07bac812010-09-19 18:47:02 +00001004 Set Cubic Spline B,C values, calculate Cubic coefficients.
cristy33b1c162010-01-23 22:51:51 +00001005 */
cristy3ed852e2009-09-05 21:47:34 +00001006 B=0.0;
1007 C=0.0;
cristy33b1c162010-01-23 22:51:51 +00001008 if ((filters[filter_type].function == CubicBC) ||
1009 (filters[window_type].function == CubicBC))
1010 {
anthony2d9b8b52010-09-14 08:31:07 +00001011 B=filters[filter_type].B;
1012 C=filters[filter_type].C;
1013 if (filters[window_type].function == CubicBC)
cristy33b1c162010-01-23 22:51:51 +00001014 {
anthony2d9b8b52010-09-14 08:31:07 +00001015 B=filters[window_type].B;
1016 C=filters[window_type].C;
cristy33b1c162010-01-23 22:51:51 +00001017 }
cristy33b1c162010-01-23 22:51:51 +00001018 artifact=GetImageArtifact(image,"filter:b");
cristy3ed852e2009-09-05 21:47:34 +00001019 if (artifact != (const char *) NULL)
cristy33b1c162010-01-23 22:51:51 +00001020 {
1021 B=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001022 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001023 artifact=GetImageArtifact(image,"filter:c");
1024 if (artifact != (const char *) NULL)
1025 C=StringToDouble(artifact);
1026 }
1027 else
1028 {
1029 artifact=GetImageArtifact(image,"filter:c");
1030 if (artifact != (const char *) NULL)
1031 {
1032 C=StringToDouble(artifact);
nicolas07bac812010-09-19 18:47:02 +00001033 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter. */
cristy33b1c162010-01-23 22:51:51 +00001034 }
1035 }
1036 /*
nicolas07bac812010-09-19 18:47:02 +00001037 Convert B,C values into Cubic Coefficents. See CubicBC().
cristy33b1c162010-01-23 22:51:51 +00001038 */
1039 resize_filter->cubic[0]=(6.0-2.0*B)/6.0;
cristy3ed852e2009-09-05 21:47:34 +00001040 resize_filter->cubic[1]=0.0;
cristy33b1c162010-01-23 22:51:51 +00001041 resize_filter->cubic[2]=(-18.0+12.0*B+6.0*C)/6.0;
1042 resize_filter->cubic[3]=(12.0-9.0*B-6.0*C)/6.0;
1043 resize_filter->cubic[4]=(8.0*B+24.0*C)/6.0;
1044 resize_filter->cubic[5]=(-12.0*B-48.0*C)/6.0;
1045 resize_filter->cubic[6]=(6.0*B+30.0*C)/6.0;
nicolas58c77cd2010-09-20 15:51:39 +00001046 resize_filter->cubic[7]=(-B-6.0*C)/6.0;
cristy3ed852e2009-09-05 21:47:34 +00001047 }
anthony55f12332010-09-10 01:13:02 +00001048 /*
nicolas07bac812010-09-19 18:47:02 +00001049 Expert Option Request for verbose details of the resulting filter.
anthony55f12332010-09-10 01:13:02 +00001050 */
anthonye06e4c12010-09-15 04:03:52 +00001051#if defined(MAGICKCORE_OPENMP_SUPPORT)
anthony72949792010-10-08 04:44:56 +00001052 #pragma omp single
1053 {
anthonye06e4c12010-09-15 04:03:52 +00001054#endif
1055 artifact=GetImageArtifact(image,"filter:verbose");
1056 if (artifact != (const char *) NULL)
1057 {
1058 double
1059 support,
1060 x;
cristy3ed852e2009-09-05 21:47:34 +00001061
nicolas07bac812010-09-19 18:47:02 +00001062 /*
anthony463be1d2010-09-26 01:07:36 +00001063 Set the weighting function properly when the weighting
1064 function may not exactly match the filter of the same name.
1065 EG: a Point filter really uses a Box weighting function
1066 with a different support than is typically used.
1067
anthonye06e4c12010-09-15 04:03:52 +00001068 */
anthony463be1d2010-09-26 01:07:36 +00001069 if (resize_filter->filter == Box) filter_type=BoxFilter;
1070 if (resize_filter->filter == Sinc) filter_type=SincFilter;
1071 if (resize_filter->filter == SincFast) filter_type=SincFastFilter;
anthony61b5ddd2010-10-05 02:33:31 +00001072 if (resize_filter->filter == Jinc) filter_type=JincFilter;
anthony463be1d2010-09-26 01:07:36 +00001073 if (resize_filter->filter == CubicBC) filter_type=CubicFilter;
anthonye06e4c12010-09-15 04:03:52 +00001074 /*
nicolas07bac812010-09-19 18:47:02 +00001075 Report Filter Details.
anthonye06e4c12010-09-15 04:03:52 +00001076 */
cristy03dbbd22010-09-19 23:04:47 +00001077 support=GetResizeFilterSupport(resize_filter); /* support range */
anthony61b5ddd2010-10-05 02:33:31 +00001078 (void) fprintf(stdout,"# Resize Filter (for graphing)\n#\n");
cristy03dbbd22010-09-19 23:04:47 +00001079 (void) fprintf(stdout,"# filter = %s\n",MagickOptionToMnemonic(
1080 MagickFilterOptions,filter_type));
1081 (void) fprintf(stdout,"# window = %s\n",MagickOptionToMnemonic(
anthony463be1d2010-09-26 01:07:36 +00001082 MagickFilterOptions, window_type));
cristy03dbbd22010-09-19 23:04:47 +00001083 (void) fprintf(stdout,"# support = %.*g\n",GetMagickPrecision(),
anthony463be1d2010-09-26 01:07:36 +00001084 (double) resize_filter->support);
cristy03dbbd22010-09-19 23:04:47 +00001085 (void) fprintf(stdout,"# win-support = %.*g\n",GetMagickPrecision(),
anthony463be1d2010-09-26 01:07:36 +00001086 (double) resize_filter->window_support);
cristy03dbbd22010-09-19 23:04:47 +00001087 (void) fprintf(stdout,"# blur = %.*g\n",GetMagickPrecision(),
1088 (double) resize_filter->blur);
anthony463be1d2010-09-26 01:07:36 +00001089 (void) fprintf(stdout,"# blurred_support = %.*g\n",GetMagickPrecision(),
cristy03dbbd22010-09-19 23:04:47 +00001090 (double) support);
1091 (void) fprintf(stdout,"# B,C = %.*g,%.*g\n",GetMagickPrecision(),
1092 (double) B,GetMagickPrecision(),(double) C);
anthony61b5ddd2010-10-05 02:33:31 +00001093 (void) fprintf(stdout,"\n");
anthonye06e4c12010-09-15 04:03:52 +00001094 /*
nicolas07bac812010-09-19 18:47:02 +00001095 Output values of resulting filter graph -- for graphing
1096 filter result.
anthonye06e4c12010-09-15 04:03:52 +00001097 */
1098 for (x=0.0; x <= support; x+=0.01f)
cristy03dbbd22010-09-19 23:04:47 +00001099 (void) fprintf(stdout,"%5.2lf\t%.*g\n",x,GetMagickPrecision(),
1100 (double) GetResizeFilterWeight(resize_filter,x));
nicolas07bac812010-09-19 18:47:02 +00001101 /* A final value so gnuplot can graph the 'stop' properly. */
cristy03dbbd22010-09-19 23:04:47 +00001102 (void) fprintf(stdout,"%5.2lf\t%.*g\n",support,GetMagickPrecision(),
1103 0.0);
anthonye06e4c12010-09-15 04:03:52 +00001104 }
anthony72949792010-10-08 04:44:56 +00001105 /* output the above once only for each image, and each setting */
cristybb66d9c2010-10-09 01:40:31 +00001106 (void) DeleteImageArtifact((Image *) image,"filter:verbose");
anthonye06e4c12010-09-15 04:03:52 +00001107#if defined(MAGICKCORE_OPENMP_SUPPORT)
anthony61b5ddd2010-10-05 02:33:31 +00001108 }
anthonye06e4c12010-09-15 04:03:52 +00001109#endif
cristy3ed852e2009-09-05 21:47:34 +00001110 return(resize_filter);
1111}
1112
1113/*
1114%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1115% %
1116% %
1117% %
1118% A d a p t i v e R e s i z e I m a g e %
1119% %
1120% %
1121% %
1122%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1123%
1124% AdaptiveResizeImage() adaptively resize image with pixel resampling.
1125%
1126% The format of the AdaptiveResizeImage method is:
1127%
cristy9af9b5d2010-08-15 17:04:28 +00001128% Image *AdaptiveResizeImage(const Image *image,const size_t columns,
1129% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001130%
1131% A description of each parameter follows:
1132%
1133% o image: the image.
1134%
1135% o columns: the number of columns in the resized image.
1136%
1137% o rows: the number of rows in the resized image.
1138%
1139% o exception: return any errors or warnings in this structure.
1140%
1141*/
1142MagickExport Image *AdaptiveResizeImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001143 const size_t columns,const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001144{
1145#define AdaptiveResizeImageTag "Resize/Image"
1146
cristyc4c8d132010-01-07 01:58:38 +00001147 CacheView
1148 *resize_view;
1149
cristy3ed852e2009-09-05 21:47:34 +00001150 Image
1151 *resize_image;
1152
cristy3ed852e2009-09-05 21:47:34 +00001153 MagickBooleanType
1154 proceed;
1155
1156 MagickPixelPacket
1157 pixel;
1158
1159 PointInfo
1160 offset;
1161
1162 ResampleFilter
1163 *resample_filter;
1164
cristy9af9b5d2010-08-15 17:04:28 +00001165 ssize_t
1166 y;
1167
cristy3ed852e2009-09-05 21:47:34 +00001168 /*
1169 Adaptively resize image.
1170 */
1171 assert(image != (const Image *) NULL);
1172 assert(image->signature == MagickSignature);
1173 if (image->debug != MagickFalse)
1174 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1175 assert(exception != (ExceptionInfo *) NULL);
1176 assert(exception->signature == MagickSignature);
1177 if ((columns == 0) || (rows == 0))
1178 return((Image *) NULL);
1179 if ((columns == image->columns) && (rows == image->rows))
1180 return(CloneImage(image,0,0,MagickTrue,exception));
1181 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
1182 if (resize_image == (Image *) NULL)
1183 return((Image *) NULL);
1184 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
1185 {
1186 InheritException(exception,&resize_image->exception);
1187 resize_image=DestroyImage(resize_image);
1188 return((Image *) NULL);
1189 }
1190 GetMagickPixelPacket(image,&pixel);
1191 resample_filter=AcquireResampleFilter(image,exception);
cristy0c7e9eb2010-09-30 14:23:37 +00001192 (void) SetResampleFilter(resample_filter,PointFilter,1.0);
anthonyccf239d2010-09-30 04:23:46 +00001193 (void) SetResampleFilterInterpolateMethod(resample_filter,
cristy0c7e9eb2010-09-30 14:23:37 +00001194 MeshInterpolatePixel);
cristy3ed852e2009-09-05 21:47:34 +00001195 resize_view=AcquireCacheView(resize_image);
cristybb503372010-05-27 20:51:26 +00001196 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001197 {
1198 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001199 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001200
cristybb503372010-05-27 20:51:26 +00001201 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001202 x;
1203
1204 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001205 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001206
1207 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
1208 exception);
1209 if (q == (PixelPacket *) NULL)
1210 break;
1211 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1212 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
cristybb503372010-05-27 20:51:26 +00001213 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001214 {
1215 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
1216 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
1217 &pixel);
1218 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
1219 q++;
1220 }
1221 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1222 break;
cristy96b16132010-08-29 17:19:52 +00001223 proceed=SetImageProgress(image,AdaptiveResizeImageTag,(MagickOffsetType) y,
1224 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001225 if (proceed == MagickFalse)
1226 break;
1227 }
1228 resample_filter=DestroyResampleFilter(resample_filter);
1229 resize_view=DestroyCacheView(resize_view);
1230 return(resize_image);
1231}
1232
1233/*
1234%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1235% %
1236% %
1237% %
1238+ B e s s e l O r d e r O n e %
1239% %
1240% %
1241% %
1242%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1243%
1244% BesselOrderOne() computes the Bessel function of x of the first kind of
anthony48f77622010-10-03 14:32:31 +00001245% order 0. This is used to create the Jinc() filter function below.
cristy3ed852e2009-09-05 21:47:34 +00001246%
1247% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
1248%
1249% j1(x) = x*j1(x);
1250%
1251% For x in (8,inf)
1252%
1253% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
1254%
1255% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
1256%
1257% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
1258% = 1/sqrt(2) * (sin(x) - cos(x))
1259% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
1260% = -1/sqrt(2) * (sin(x) + cos(x))
1261%
1262% The format of the BesselOrderOne method is:
1263%
1264% MagickRealType BesselOrderOne(MagickRealType x)
1265%
1266% A description of each parameter follows:
1267%
1268% o x: MagickRealType value.
1269%
1270*/
1271
1272#undef I0
1273static MagickRealType I0(MagickRealType x)
1274{
1275 MagickRealType
1276 sum,
1277 t,
1278 y;
1279
cristybb503372010-05-27 20:51:26 +00001280 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001281 i;
1282
1283 /*
1284 Zeroth order Bessel function of the first kind.
1285 */
1286 sum=1.0;
1287 y=x*x/4.0;
1288 t=y;
1289 for (i=2; t > MagickEpsilon; i++)
1290 {
1291 sum+=t;
1292 t*=y/((MagickRealType) i*i);
1293 }
1294 return(sum);
1295}
1296
1297#undef J1
1298static MagickRealType J1(MagickRealType x)
1299{
1300 MagickRealType
1301 p,
1302 q;
1303
cristybb503372010-05-27 20:51:26 +00001304 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001305 i;
1306
1307 static const double
1308 Pone[] =
1309 {
1310 0.581199354001606143928050809e+21,
1311 -0.6672106568924916298020941484e+20,
1312 0.2316433580634002297931815435e+19,
1313 -0.3588817569910106050743641413e+17,
1314 0.2908795263834775409737601689e+15,
1315 -0.1322983480332126453125473247e+13,
1316 0.3413234182301700539091292655e+10,
1317 -0.4695753530642995859767162166e+7,
1318 0.270112271089232341485679099e+4
1319 },
1320 Qone[] =
1321 {
1322 0.11623987080032122878585294e+22,
1323 0.1185770712190320999837113348e+20,
1324 0.6092061398917521746105196863e+17,
1325 0.2081661221307607351240184229e+15,
1326 0.5243710262167649715406728642e+12,
1327 0.1013863514358673989967045588e+10,
1328 0.1501793594998585505921097578e+7,
1329 0.1606931573481487801970916749e+4,
1330 0.1e+1
1331 };
1332
1333 p=Pone[8];
1334 q=Qone[8];
1335 for (i=7; i >= 0; i--)
1336 {
1337 p=p*x*x+Pone[i];
1338 q=q*x*x+Qone[i];
1339 }
1340 return(p/q);
1341}
1342
1343#undef P1
1344static MagickRealType P1(MagickRealType x)
1345{
1346 MagickRealType
1347 p,
1348 q;
1349
cristybb503372010-05-27 20:51:26 +00001350 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001351 i;
1352
1353 static const double
1354 Pone[] =
1355 {
1356 0.352246649133679798341724373e+5,
1357 0.62758845247161281269005675e+5,
1358 0.313539631109159574238669888e+5,
1359 0.49854832060594338434500455e+4,
1360 0.2111529182853962382105718e+3,
1361 0.12571716929145341558495e+1
1362 },
1363 Qone[] =
1364 {
1365 0.352246649133679798068390431e+5,
1366 0.626943469593560511888833731e+5,
1367 0.312404063819041039923015703e+5,
1368 0.4930396490181088979386097e+4,
1369 0.2030775189134759322293574e+3,
1370 0.1e+1
1371 };
1372
1373 p=Pone[5];
1374 q=Qone[5];
1375 for (i=4; i >= 0; i--)
1376 {
1377 p=p*(8.0/x)*(8.0/x)+Pone[i];
1378 q=q*(8.0/x)*(8.0/x)+Qone[i];
1379 }
1380 return(p/q);
1381}
1382
1383#undef Q1
1384static MagickRealType Q1(MagickRealType x)
1385{
1386 MagickRealType
1387 p,
1388 q;
1389
cristybb503372010-05-27 20:51:26 +00001390 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001391 i;
1392
1393 static const double
1394 Pone[] =
1395 {
1396 0.3511751914303552822533318e+3,
1397 0.7210391804904475039280863e+3,
1398 0.4259873011654442389886993e+3,
1399 0.831898957673850827325226e+2,
1400 0.45681716295512267064405e+1,
1401 0.3532840052740123642735e-1
1402 },
1403 Qone[] =
1404 {
1405 0.74917374171809127714519505e+4,
1406 0.154141773392650970499848051e+5,
1407 0.91522317015169922705904727e+4,
1408 0.18111867005523513506724158e+4,
1409 0.1038187585462133728776636e+3,
1410 0.1e+1
1411 };
1412
1413 p=Pone[5];
1414 q=Qone[5];
1415 for (i=4; i >= 0; i--)
1416 {
1417 p=p*(8.0/x)*(8.0/x)+Pone[i];
1418 q=q*(8.0/x)*(8.0/x)+Qone[i];
1419 }
1420 return(p/q);
1421}
1422
1423static MagickRealType BesselOrderOne(MagickRealType x)
1424{
1425 MagickRealType
1426 p,
1427 q;
1428
1429 if (x == 0.0)
1430 return(0.0);
1431 p=x;
1432 if (x < 0.0)
1433 x=(-x);
1434 if (x < 8.0)
1435 return(p*J1(x));
1436 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1437 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1438 cos((double) x))));
1439 if (p < 0.0)
1440 q=(-q);
1441 return(q);
1442}
1443
1444/*
1445%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1446% %
1447% %
1448% %
1449+ D e s t r o y R e s i z e F i l t e r %
1450% %
1451% %
1452% %
1453%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1454%
1455% DestroyResizeFilter() destroy the resize filter.
1456%
cristya2ffd7e2010-03-10 20:50:30 +00001457% The format of the DestroyResizeFilter method is:
cristy3ed852e2009-09-05 21:47:34 +00001458%
1459% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1460%
1461% A description of each parameter follows:
1462%
1463% o resize_filter: the resize filter.
1464%
1465*/
1466MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1467{
1468 assert(resize_filter != (ResizeFilter *) NULL);
1469 assert(resize_filter->signature == MagickSignature);
1470 resize_filter->signature=(~MagickSignature);
1471 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1472 return(resize_filter);
1473}
1474
1475/*
1476%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1477% %
1478% %
1479% %
1480+ G e t R e s i z e F i l t e r S u p p o r t %
1481% %
1482% %
1483% %
1484%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1485%
1486% GetResizeFilterSupport() return the current support window size for this
1487% filter. Note that this may have been enlarged by filter:blur factor.
1488%
1489% The format of the GetResizeFilterSupport method is:
1490%
1491% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1492%
1493% A description of each parameter follows:
1494%
1495% o filter: Image filter to use.
1496%
1497*/
1498MagickExport MagickRealType GetResizeFilterSupport(
1499 const ResizeFilter *resize_filter)
1500{
1501 assert(resize_filter != (ResizeFilter *) NULL);
1502 assert(resize_filter->signature == MagickSignature);
1503 return(resize_filter->support*resize_filter->blur);
1504}
1505
1506/*
1507%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1508% %
1509% %
1510% %
1511+ G e t R e s i z e F i l t e r W e i g h t %
1512% %
1513% %
1514% %
1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1516%
1517% GetResizeFilterWeight evaluates the specified resize filter at the point x
1518% which usally lies between zero and the filters current 'support' and
1519% returns the weight of the filter function at that point.
1520%
1521% The format of the GetResizeFilterWeight method is:
1522%
1523% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1524% const MagickRealType x)
1525%
1526% A description of each parameter follows:
1527%
1528% o filter: the filter type.
1529%
1530% o x: the point.
1531%
1532*/
1533MagickExport MagickRealType GetResizeFilterWeight(
1534 const ResizeFilter *resize_filter,const MagickRealType x)
1535{
1536 MagickRealType
cristyb8385862010-09-26 01:27:51 +00001537 scale,
1538 x_blur;
cristy3ed852e2009-09-05 21:47:34 +00001539
1540 /*
1541 Windowing function - scale the weighting filter by this amount.
1542 */
1543 assert(resize_filter != (ResizeFilter *) NULL);
1544 assert(resize_filter->signature == MagickSignature);
anthony55f12332010-09-10 01:13:02 +00001545 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */
cristy3ed852e2009-09-05 21:47:34 +00001546 if ((resize_filter->window_support < MagickEpsilon) ||
1547 (resize_filter->window == Box))
anthony55f12332010-09-10 01:13:02 +00001548 scale=1.0; /* Point or Box Filter -- avoid division by zero */
cristy3ed852e2009-09-05 21:47:34 +00001549 else
1550 {
anthony55f12332010-09-10 01:13:02 +00001551 scale=resize_filter->scale;
1552 scale=resize_filter->window(x_blur*scale,resize_filter);
cristy3ed852e2009-09-05 21:47:34 +00001553 }
anthony55f12332010-09-10 01:13:02 +00001554 return(scale*resize_filter->filter(x_blur,resize_filter));
cristy3ed852e2009-09-05 21:47:34 +00001555}
1556
1557/*
1558%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1559% %
1560% %
1561% %
1562% M a g n i f y I m a g e %
1563% %
1564% %
1565% %
1566%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1567%
1568% MagnifyImage() is a convenience method that scales an image proportionally
1569% to twice its size.
1570%
1571% The format of the MagnifyImage method is:
1572%
1573% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1574%
1575% A description of each parameter follows:
1576%
1577% o image: the image.
1578%
1579% o exception: return any errors or warnings in this structure.
1580%
1581*/
1582MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1583{
1584 Image
1585 *magnify_image;
1586
1587 assert(image != (Image *) NULL);
1588 assert(image->signature == MagickSignature);
1589 if (image->debug != MagickFalse)
1590 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1591 assert(exception != (ExceptionInfo *) NULL);
1592 assert(exception->signature == MagickSignature);
1593 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1594 1.0,exception);
1595 return(magnify_image);
1596}
1597
1598/*
1599%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1600% %
1601% %
1602% %
1603% M i n i f y I m a g e %
1604% %
1605% %
1606% %
1607%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1608%
1609% MinifyImage() is a convenience method that scales an image proportionally
1610% to half its size.
1611%
1612% The format of the MinifyImage method is:
1613%
1614% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1615%
1616% A description of each parameter follows:
1617%
1618% o image: the image.
1619%
1620% o exception: return any errors or warnings in this structure.
1621%
1622*/
1623MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1624{
1625 Image
1626 *minify_image;
1627
1628 assert(image != (Image *) NULL);
1629 assert(image->signature == MagickSignature);
1630 if (image->debug != MagickFalse)
1631 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1632 assert(exception != (ExceptionInfo *) NULL);
1633 assert(exception->signature == MagickSignature);
1634 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1635 1.0,exception);
1636 return(minify_image);
1637}
1638
1639/*
1640%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1641% %
1642% %
1643% %
1644% R e s a m p l e I m a g e %
1645% %
1646% %
1647% %
1648%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1649%
1650% ResampleImage() resize image in terms of its pixel size, so that when
1651% displayed at the given resolution it will be the same size in terms of
1652% real world units as the original image at the original resolution.
1653%
1654% The format of the ResampleImage method is:
1655%
1656% Image *ResampleImage(Image *image,const double x_resolution,
1657% const double y_resolution,const FilterTypes filter,const double blur,
1658% ExceptionInfo *exception)
1659%
1660% A description of each parameter follows:
1661%
1662% o image: the image to be resized to fit the given resolution.
1663%
1664% o x_resolution: the new image x resolution.
1665%
1666% o y_resolution: the new image y resolution.
1667%
1668% o filter: Image filter to use.
1669%
1670% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1671%
1672*/
1673MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1674 const double y_resolution,const FilterTypes filter,const double blur,
1675 ExceptionInfo *exception)
1676{
1677#define ResampleImageTag "Resample/Image"
1678
1679 Image
1680 *resample_image;
1681
cristybb503372010-05-27 20:51:26 +00001682 size_t
cristy3ed852e2009-09-05 21:47:34 +00001683 height,
1684 width;
1685
1686 /*
1687 Initialize sampled image attributes.
1688 */
1689 assert(image != (const Image *) NULL);
1690 assert(image->signature == MagickSignature);
1691 if (image->debug != MagickFalse)
1692 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1693 assert(exception != (ExceptionInfo *) NULL);
1694 assert(exception->signature == MagickSignature);
cristy9af9b5d2010-08-15 17:04:28 +00001695 width=(size_t) (x_resolution*image->columns/(image->x_resolution == 0.0 ?
1696 72.0 : image->x_resolution)+0.5);
1697 height=(size_t) (y_resolution*image->rows/(image->y_resolution == 0.0 ?
1698 72.0 : image->y_resolution)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001699 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1700 if (resample_image != (Image *) NULL)
1701 {
1702 resample_image->x_resolution=x_resolution;
1703 resample_image->y_resolution=y_resolution;
1704 }
1705 return(resample_image);
1706}
1707#if defined(MAGICKCORE_LQR_DELEGATE)
1708
1709/*
1710%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1711% %
1712% %
1713% %
1714% L i q u i d R e s c a l e I m a g e %
1715% %
1716% %
1717% %
1718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1719%
1720% LiquidRescaleImage() rescales image with seam carving.
1721%
1722% The format of the LiquidRescaleImage method is:
1723%
1724% Image *LiquidRescaleImage(const Image *image,
cristybb503372010-05-27 20:51:26 +00001725% const size_t columns,const size_t rows,
cristy3ed852e2009-09-05 21:47:34 +00001726% const double delta_x,const double rigidity,ExceptionInfo *exception)
1727%
1728% A description of each parameter follows:
1729%
1730% o image: the image.
1731%
1732% o columns: the number of columns in the rescaled image.
1733%
1734% o rows: the number of rows in the rescaled image.
1735%
1736% o delta_x: maximum seam transversal step (0 means straight seams).
1737%
1738% o rigidity: introduce a bias for non-straight seams (typically 0).
1739%
1740% o exception: return any errors or warnings in this structure.
1741%
1742*/
cristy9af9b5d2010-08-15 17:04:28 +00001743MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns,
1744 const size_t rows,const double delta_x,const double rigidity,
1745 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001746{
1747#define LiquidRescaleImageTag "Rescale/Image"
1748
cristyc5c6f662010-09-22 14:23:02 +00001749 CacheView
1750 *rescale_view;
1751
cristy3ed852e2009-09-05 21:47:34 +00001752 const char
1753 *map;
1754
1755 guchar
1756 *packet;
1757
1758 Image
1759 *rescale_image;
1760
1761 int
1762 x,
1763 y;
1764
1765 LqrCarver
1766 *carver;
1767
1768 LqrRetVal
1769 lqr_status;
1770
1771 MagickBooleanType
1772 status;
1773
1774 MagickPixelPacket
1775 pixel;
1776
1777 unsigned char
1778 *pixels;
1779
1780 /*
1781 Liquid rescale image.
1782 */
1783 assert(image != (const Image *) NULL);
1784 assert(image->signature == MagickSignature);
1785 if (image->debug != MagickFalse)
1786 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1787 assert(exception != (ExceptionInfo *) NULL);
1788 assert(exception->signature == MagickSignature);
1789 if ((columns == 0) || (rows == 0))
1790 return((Image *) NULL);
1791 if ((columns == image->columns) && (rows == image->rows))
1792 return(CloneImage(image,0,0,MagickTrue,exception));
1793 if ((columns <= 2) || (rows <= 2))
cristyd1bb3bc2010-09-07 00:43:58 +00001794 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception));
cristy3ed852e2009-09-05 21:47:34 +00001795 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1796 {
1797 Image
1798 *resize_image;
1799
cristybb503372010-05-27 20:51:26 +00001800 size_t
cristy3ed852e2009-09-05 21:47:34 +00001801 height,
1802 width;
1803
1804 /*
1805 Honor liquid resize size limitations.
1806 */
1807 for (width=image->columns; columns >= (2*width-1); width*=2);
1808 for (height=image->rows; rows >= (2*height-1); height*=2);
1809 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1810 exception);
1811 if (resize_image == (Image *) NULL)
1812 return((Image *) NULL);
1813 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1814 rigidity,exception);
1815 resize_image=DestroyImage(resize_image);
1816 return(rescale_image);
1817 }
1818 map="RGB";
1819 if (image->matte == MagickFalse)
1820 map="RGBA";
1821 if (image->colorspace == CMYKColorspace)
1822 {
1823 map="CMYK";
1824 if (image->matte == MagickFalse)
1825 map="CMYKA";
1826 }
1827 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1828 strlen(map)*sizeof(*pixels));
1829 if (pixels == (unsigned char *) NULL)
1830 return((Image *) NULL);
1831 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1832 pixels,exception);
1833 if (status == MagickFalse)
1834 {
1835 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1836 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1837 }
1838 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1839 if (carver == (LqrCarver *) NULL)
1840 {
1841 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1842 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1843 }
1844 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1845 lqr_status=lqr_carver_resize(carver,columns,rows);
1846 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1847 lqr_carver_get_height(carver),MagickTrue,exception);
1848 if (rescale_image == (Image *) NULL)
1849 {
1850 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1851 return((Image *) NULL);
1852 }
1853 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1854 {
1855 InheritException(exception,&rescale_image->exception);
1856 rescale_image=DestroyImage(rescale_image);
1857 return((Image *) NULL);
1858 }
1859 GetMagickPixelPacket(rescale_image,&pixel);
1860 (void) lqr_carver_scan_reset(carver);
cristyc5c6f662010-09-22 14:23:02 +00001861 rescale_view=AcquireCacheView(rescale_image);
cristy3ed852e2009-09-05 21:47:34 +00001862 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1863 {
1864 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001865 *restrict rescale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00001866
1867 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001868 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001869
anthony22aad252010-09-23 06:59:07 +00001870 q=QueueCacheViewAuthenticPixels(rescale_view,x,y,1,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00001871 if (q == (PixelPacket *) NULL)
1872 break;
cristyc5c6f662010-09-22 14:23:02 +00001873 rescale_indexes=GetCacheViewAuthenticIndexQueue(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001874 pixel.red=QuantumRange*(packet[0]/255.0);
1875 pixel.green=QuantumRange*(packet[1]/255.0);
1876 pixel.blue=QuantumRange*(packet[2]/255.0);
1877 if (image->colorspace != CMYKColorspace)
1878 {
1879 if (image->matte == MagickFalse)
1880 pixel.opacity=QuantumRange*(packet[3]/255.0);
1881 }
1882 else
1883 {
1884 pixel.index=QuantumRange*(packet[3]/255.0);
1885 if (image->matte == MagickFalse)
1886 pixel.opacity=QuantumRange*(packet[4]/255.0);
1887 }
1888 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
cristyc5c6f662010-09-22 14:23:02 +00001889 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001890 break;
1891 }
cristyc5c6f662010-09-22 14:23:02 +00001892 rescale_view=DestroyCacheView(rescale_view);
cristy3ed852e2009-09-05 21:47:34 +00001893 /*
1894 Relinquish resources.
1895 */
1896 lqr_carver_destroy(carver);
1897 return(rescale_image);
1898}
1899#else
1900MagickExport Image *LiquidRescaleImage(const Image *image,
cristy9af9b5d2010-08-15 17:04:28 +00001901 const size_t magick_unused(columns),const size_t magick_unused(rows),
1902 const double magick_unused(delta_x),const double magick_unused(rigidity),
1903 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001904{
1905 assert(image != (const Image *) NULL);
1906 assert(image->signature == MagickSignature);
1907 if (image->debug != MagickFalse)
1908 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1909 assert(exception != (ExceptionInfo *) NULL);
1910 assert(exception->signature == MagickSignature);
1911 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1912 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1913 return((Image *) NULL);
1914}
1915#endif
1916
1917/*
1918%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1919% %
1920% %
1921% %
1922% R e s i z e I m a g e %
1923% %
1924% %
1925% %
1926%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1927%
1928% ResizeImage() scales an image to the desired dimensions, using the given
cristy5d824382010-09-06 14:00:17 +00001929% filter (see AcquireFilterInfo()).
cristy3ed852e2009-09-05 21:47:34 +00001930%
1931% If an undefined filter is given the filter defaults to Mitchell for a
1932% colormapped image, a image with a matte channel, or if the image is
1933% enlarged. Otherwise the filter defaults to a Lanczos.
1934%
1935% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1936%
1937% The format of the ResizeImage method is:
1938%
cristybb503372010-05-27 20:51:26 +00001939% Image *ResizeImage(Image *image,const size_t columns,
1940% const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00001941% ExceptionInfo *exception)
1942%
1943% A description of each parameter follows:
1944%
1945% o image: the image.
1946%
1947% o columns: the number of columns in the scaled image.
1948%
1949% o rows: the number of rows in the scaled image.
1950%
1951% o filter: Image filter to use.
1952%
cristy9af9b5d2010-08-15 17:04:28 +00001953% o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set
1954% this to 1.0.
cristy3ed852e2009-09-05 21:47:34 +00001955%
1956% o exception: return any errors or warnings in this structure.
1957%
1958*/
1959
1960typedef struct _ContributionInfo
1961{
1962 MagickRealType
1963 weight;
1964
cristybb503372010-05-27 20:51:26 +00001965 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001966 pixel;
1967} ContributionInfo;
1968
1969static ContributionInfo **DestroyContributionThreadSet(
1970 ContributionInfo **contribution)
1971{
cristybb503372010-05-27 20:51:26 +00001972 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001973 i;
1974
1975 assert(contribution != (ContributionInfo **) NULL);
cristybb503372010-05-27 20:51:26 +00001976 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
cristy3ed852e2009-09-05 21:47:34 +00001977 if (contribution[i] != (ContributionInfo *) NULL)
1978 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1979 contribution[i]);
cristyb41ee102010-10-04 16:46:15 +00001980 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution);
cristy3ed852e2009-09-05 21:47:34 +00001981 return(contribution);
1982}
1983
1984static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1985{
cristybb503372010-05-27 20:51:26 +00001986 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001987 i;
1988
1989 ContributionInfo
1990 **contribution;
1991
cristybb503372010-05-27 20:51:26 +00001992 size_t
cristy3ed852e2009-09-05 21:47:34 +00001993 number_threads;
1994
1995 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +00001996 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +00001997 sizeof(*contribution));
1998 if (contribution == (ContributionInfo **) NULL)
1999 return((ContributionInfo **) NULL);
2000 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
cristybb503372010-05-27 20:51:26 +00002001 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +00002002 {
2003 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
2004 sizeof(**contribution));
2005 if (contribution[i] == (ContributionInfo *) NULL)
2006 return(DestroyContributionThreadSet(contribution));
2007 }
2008 return(contribution);
2009}
2010
2011static inline double MagickMax(const double x,const double y)
2012{
2013 if (x > y)
2014 return(x);
2015 return(y);
2016}
2017
2018static inline double MagickMin(const double x,const double y)
2019{
2020 if (x < y)
2021 return(x);
2022 return(y);
2023}
2024
2025static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
2026 const Image *image,Image *resize_image,const MagickRealType x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002027 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002028{
2029#define ResizeImageTag "Resize/Image"
2030
cristyfa112112010-01-04 17:48:07 +00002031 CacheView
2032 *image_view,
2033 *resize_view;
2034
cristy3ed852e2009-09-05 21:47:34 +00002035 ClassType
2036 storage_class;
2037
2038 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002039 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002040
cristy3ed852e2009-09-05 21:47:34 +00002041 MagickBooleanType
2042 status;
2043
2044 MagickPixelPacket
2045 zero;
2046
2047 MagickRealType
2048 scale,
2049 support;
2050
cristy9af9b5d2010-08-15 17:04:28 +00002051 ssize_t
2052 x;
2053
cristy3ed852e2009-09-05 21:47:34 +00002054 /*
2055 Apply filter to resize horizontally from image to resize image.
2056 */
cristy5d824382010-09-06 14:00:17 +00002057 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002058 support=scale*GetResizeFilterSupport(resize_filter);
2059 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2060 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2061 {
2062 InheritException(exception,&resize_image->exception);
2063 return(MagickFalse);
2064 }
2065 if (support < 0.5)
2066 {
2067 /*
nicolas07bac812010-09-19 18:47:02 +00002068 Support too small even for nearest neighbour: Reduce to point
2069 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002070 */
2071 support=(MagickRealType) 0.5;
2072 scale=1.0;
2073 }
2074 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2075 if (contributions == (ContributionInfo **) NULL)
2076 {
2077 (void) ThrowMagickException(exception,GetMagickModule(),
2078 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2079 return(MagickFalse);
2080 }
2081 status=MagickTrue;
2082 scale=1.0/scale;
2083 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2084 image_view=AcquireCacheView(image);
2085 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002086#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002087 #pragma omp parallel for shared(status)
2088#endif
cristybb503372010-05-27 20:51:26 +00002089 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002090 {
cristy3ed852e2009-09-05 21:47:34 +00002091 MagickRealType
2092 center,
2093 density;
2094
2095 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002096 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002097
2098 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002099 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002100
cristy03dbbd22010-09-19 23:04:47 +00002101 register ContributionInfo
2102 *restrict contribution;
2103
cristy3ed852e2009-09-05 21:47:34 +00002104 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002105 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002106
cristy3ed852e2009-09-05 21:47:34 +00002107 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002108 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002109
cristy03dbbd22010-09-19 23:04:47 +00002110 register ssize_t
2111 y;
2112
cristy9af9b5d2010-08-15 17:04:28 +00002113 ssize_t
2114 n,
2115 start,
2116 stop;
2117
cristy3ed852e2009-09-05 21:47:34 +00002118 if (status == MagickFalse)
2119 continue;
2120 center=(MagickRealType) (x+0.5)/x_factor;
cristybb503372010-05-27 20:51:26 +00002121 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2122 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00002123 density=0.0;
2124 contribution=contributions[GetOpenMPThreadId()];
2125 for (n=0; n < (stop-start); n++)
2126 {
2127 contribution[n].pixel=start+n;
2128 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2129 ((MagickRealType) (start+n)-center+0.5));
2130 density+=contribution[n].weight;
2131 }
2132 if ((density != 0.0) && (density != 1.0))
2133 {
cristybb503372010-05-27 20:51:26 +00002134 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002135 i;
2136
2137 /*
2138 Normalize.
2139 */
2140 density=1.0/density;
2141 for (i=0; i < n; i++)
2142 contribution[i].weight*=density;
2143 }
cristy9af9b5d2010-08-15 17:04:28 +00002144 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t)
2145 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00002146 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
2147 exception);
2148 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2149 {
2150 status=MagickFalse;
2151 continue;
2152 }
2153 indexes=GetCacheViewVirtualIndexQueue(image_view);
2154 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002155 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002156 {
cristy3ed852e2009-09-05 21:47:34 +00002157 MagickPixelPacket
2158 pixel;
2159
2160 MagickRealType
2161 alpha;
2162
cristybb503372010-05-27 20:51:26 +00002163 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002164 i;
2165
cristy9af9b5d2010-08-15 17:04:28 +00002166 ssize_t
2167 j;
2168
cristy3ed852e2009-09-05 21:47:34 +00002169 pixel=zero;
2170 if (image->matte == MagickFalse)
2171 {
2172 for (i=0; i < n; i++)
2173 {
2174 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2175 (contribution[i].pixel-contribution[0].pixel);
2176 alpha=contribution[i].weight;
2177 pixel.red+=alpha*(p+j)->red;
2178 pixel.green+=alpha*(p+j)->green;
2179 pixel.blue+=alpha*(p+j)->blue;
2180 pixel.opacity+=alpha*(p+j)->opacity;
2181 }
cristyce70c172010-01-07 17:15:30 +00002182 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2183 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2184 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2185 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002186 if ((image->colorspace == CMYKColorspace) &&
2187 (resize_image->colorspace == CMYKColorspace))
2188 {
2189 for (i=0; i < n; i++)
2190 {
2191 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2192 (contribution[i].pixel-contribution[0].pixel);
2193 alpha=contribution[i].weight;
2194 pixel.index+=alpha*indexes[j];
2195 }
cristyce70c172010-01-07 17:15:30 +00002196 resize_indexes[y]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002197 }
2198 }
2199 else
2200 {
2201 MagickRealType
2202 gamma;
2203
2204 gamma=0.0;
2205 for (i=0; i < n; i++)
2206 {
2207 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2208 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002209 alpha=contribution[i].weight*QuantumScale*
2210 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002211 pixel.red+=alpha*(p+j)->red;
2212 pixel.green+=alpha*(p+j)->green;
2213 pixel.blue+=alpha*(p+j)->blue;
2214 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2215 gamma+=alpha;
2216 }
2217 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002218 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2219 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2220 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2221 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002222 if ((image->colorspace == CMYKColorspace) &&
2223 (resize_image->colorspace == CMYKColorspace))
2224 {
2225 for (i=0; i < n; i++)
2226 {
2227 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2228 (contribution[i].pixel-contribution[0].pixel);
cristy46f08202010-01-10 04:04:21 +00002229 alpha=contribution[i].weight*QuantumScale*
2230 GetAlphaPixelComponent(p+j);
cristyc197d1c2009-10-13 19:56:53 +00002231 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00002232 }
cristyce70c172010-01-07 17:15:30 +00002233 resize_indexes[y]=(IndexPacket) ClampToQuantum(gamma*
2234 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002235 }
2236 }
2237 if ((resize_image->storage_class == PseudoClass) &&
2238 (image->storage_class == PseudoClass))
2239 {
cristybb503372010-05-27 20:51:26 +00002240 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002241 1.0)+0.5);
2242 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
2243 (contribution[i-start].pixel-contribution[0].pixel);
2244 resize_indexes[y]=indexes[j];
2245 }
2246 q++;
2247 }
2248 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2249 status=MagickFalse;
2250 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2251 {
2252 MagickBooleanType
2253 proceed;
2254
cristyb5d5f722009-11-04 03:03:49 +00002255#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002256 #pragma omp critical (MagickCore_HorizontalFilter)
2257#endif
cristy9af9b5d2010-08-15 17:04:28 +00002258 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002259 if (proceed == MagickFalse)
2260 status=MagickFalse;
2261 }
2262 }
2263 resize_view=DestroyCacheView(resize_view);
2264 image_view=DestroyCacheView(image_view);
2265 contributions=DestroyContributionThreadSet(contributions);
2266 return(status);
2267}
2268
2269static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
2270 const Image *image,Image *resize_image,const MagickRealType y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002271 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002272{
cristyfa112112010-01-04 17:48:07 +00002273 CacheView
2274 *image_view,
2275 *resize_view;
2276
cristy3ed852e2009-09-05 21:47:34 +00002277 ClassType
2278 storage_class;
2279
2280 ContributionInfo
cristyfa112112010-01-04 17:48:07 +00002281 **restrict contributions;
cristy3ed852e2009-09-05 21:47:34 +00002282
cristy3ed852e2009-09-05 21:47:34 +00002283 MagickBooleanType
2284 status;
2285
2286 MagickPixelPacket
2287 zero;
2288
2289 MagickRealType
2290 scale,
2291 support;
2292
cristy9af9b5d2010-08-15 17:04:28 +00002293 ssize_t
2294 y;
2295
cristy3ed852e2009-09-05 21:47:34 +00002296 /*
cristy9af9b5d2010-08-15 17:04:28 +00002297 Apply filter to resize vertically from image to resize image.
cristy3ed852e2009-09-05 21:47:34 +00002298 */
cristy5d824382010-09-06 14:00:17 +00002299 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0);
cristy3ed852e2009-09-05 21:47:34 +00002300 support=scale*GetResizeFilterSupport(resize_filter);
2301 storage_class=support > 0.5 ? DirectClass : image->storage_class;
2302 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
2303 {
2304 InheritException(exception,&resize_image->exception);
2305 return(MagickFalse);
2306 }
2307 if (support < 0.5)
2308 {
2309 /*
nicolas07bac812010-09-19 18:47:02 +00002310 Support too small even for nearest neighbour: Reduce to point
2311 sampling.
cristy3ed852e2009-09-05 21:47:34 +00002312 */
2313 support=(MagickRealType) 0.5;
2314 scale=1.0;
2315 }
2316 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
2317 if (contributions == (ContributionInfo **) NULL)
2318 {
2319 (void) ThrowMagickException(exception,GetMagickModule(),
2320 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2321 return(MagickFalse);
2322 }
2323 status=MagickTrue;
2324 scale=1.0/scale;
2325 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2326 image_view=AcquireCacheView(image);
2327 resize_view=AcquireCacheView(resize_image);
cristyb5d5f722009-11-04 03:03:49 +00002328#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002329 #pragma omp parallel for shared(status)
2330#endif
cristybb503372010-05-27 20:51:26 +00002331 for (y=0; y < (ssize_t) resize_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002332 {
cristy3ed852e2009-09-05 21:47:34 +00002333 MagickRealType
2334 center,
2335 density;
2336
2337 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002338 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002339
2340 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002341 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002342
cristy03dbbd22010-09-19 23:04:47 +00002343 register ContributionInfo
2344 *restrict contribution;
2345
cristy3ed852e2009-09-05 21:47:34 +00002346 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002347 *restrict resize_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002348
cristy9af9b5d2010-08-15 17:04:28 +00002349 register PixelPacket
2350 *restrict q;
2351
cristybb503372010-05-27 20:51:26 +00002352 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002353 x;
2354
cristy9af9b5d2010-08-15 17:04:28 +00002355 ssize_t
2356 n,
2357 start,
2358 stop;
cristy3ed852e2009-09-05 21:47:34 +00002359
2360 if (status == MagickFalse)
2361 continue;
cristy679e6962010-03-18 00:42:45 +00002362 center=(MagickRealType) (y+0.5)/y_factor;
cristybb503372010-05-27 20:51:26 +00002363 start=(ssize_t) MagickMax(center-support+0.5,0.0);
2364 stop=(ssize_t) MagickMin(center+support+0.5,(double) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002365 density=0.0;
2366 contribution=contributions[GetOpenMPThreadId()];
2367 for (n=0; n < (stop-start); n++)
2368 {
2369 contribution[n].pixel=start+n;
2370 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2371 ((MagickRealType) (start+n)-center+0.5));
2372 density+=contribution[n].weight;
2373 }
2374 if ((density != 0.0) && (density != 1.0))
2375 {
cristybb503372010-05-27 20:51:26 +00002376 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002377 i;
2378
2379 /*
2380 Normalize.
2381 */
2382 density=1.0/density;
2383 for (i=0; i < n; i++)
2384 contribution[i].weight*=density;
2385 }
2386 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
cristy9af9b5d2010-08-15 17:04:28 +00002387 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1),
2388 exception);
cristy3ed852e2009-09-05 21:47:34 +00002389 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2390 exception);
2391 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2392 {
2393 status=MagickFalse;
2394 continue;
2395 }
2396 indexes=GetCacheViewVirtualIndexQueue(image_view);
2397 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
cristybb503372010-05-27 20:51:26 +00002398 for (x=0; x < (ssize_t) resize_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002399 {
cristy3ed852e2009-09-05 21:47:34 +00002400 MagickPixelPacket
2401 pixel;
2402
2403 MagickRealType
2404 alpha;
2405
cristybb503372010-05-27 20:51:26 +00002406 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002407 i;
2408
cristy9af9b5d2010-08-15 17:04:28 +00002409 ssize_t
2410 j;
2411
cristy3ed852e2009-09-05 21:47:34 +00002412 pixel=zero;
2413 if (image->matte == MagickFalse)
2414 {
2415 for (i=0; i < n; i++)
2416 {
cristybb503372010-05-27 20:51:26 +00002417 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002418 image->columns+x);
2419 alpha=contribution[i].weight;
2420 pixel.red+=alpha*(p+j)->red;
2421 pixel.green+=alpha*(p+j)->green;
2422 pixel.blue+=alpha*(p+j)->blue;
2423 pixel.opacity+=alpha*(p+j)->opacity;
2424 }
cristyce70c172010-01-07 17:15:30 +00002425 SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
2426 SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
2427 SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
2428 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002429 if ((image->colorspace == CMYKColorspace) &&
2430 (resize_image->colorspace == CMYKColorspace))
2431 {
2432 for (i=0; i < n; i++)
2433 {
cristybb503372010-05-27 20:51:26 +00002434 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002435 image->columns+x);
2436 alpha=contribution[i].weight;
2437 pixel.index+=alpha*indexes[j];
2438 }
cristyce70c172010-01-07 17:15:30 +00002439 resize_indexes[x]=(IndexPacket) ClampToQuantum(pixel.index);
cristy3ed852e2009-09-05 21:47:34 +00002440 }
2441 }
2442 else
2443 {
2444 MagickRealType
2445 gamma;
2446
2447 gamma=0.0;
2448 for (i=0; i < n; i++)
2449 {
cristybb503372010-05-27 20:51:26 +00002450 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002451 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002452 alpha=contribution[i].weight*QuantumScale*
2453 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002454 pixel.red+=alpha*(p+j)->red;
2455 pixel.green+=alpha*(p+j)->green;
2456 pixel.blue+=alpha*(p+j)->blue;
2457 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2458 gamma+=alpha;
2459 }
2460 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
cristyce70c172010-01-07 17:15:30 +00002461 q->red=ClampToQuantum(gamma*GetRedPixelComponent(&pixel));
2462 q->green=ClampToQuantum(gamma*GetGreenPixelComponent(&pixel));
2463 q->blue=ClampToQuantum(gamma*GetBluePixelComponent(&pixel));
2464 SetOpacityPixelComponent(q,ClampOpacityPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002465 if ((image->colorspace == CMYKColorspace) &&
2466 (resize_image->colorspace == CMYKColorspace))
2467 {
2468 for (i=0; i < n; i++)
2469 {
cristybb503372010-05-27 20:51:26 +00002470 j=(ssize_t) ((contribution[i].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002471 image->columns+x);
cristy46f08202010-01-10 04:04:21 +00002472 alpha=contribution[i].weight*QuantumScale*
2473 GetAlphaPixelComponent(p+j);
cristy3ed852e2009-09-05 21:47:34 +00002474 pixel.index+=alpha*indexes[j];
2475 }
cristyce70c172010-01-07 17:15:30 +00002476 resize_indexes[x]=(IndexPacket) ClampToQuantum(gamma*
2477 GetIndexPixelComponent(&pixel));
cristy3ed852e2009-09-05 21:47:34 +00002478 }
2479 }
2480 if ((resize_image->storage_class == PseudoClass) &&
2481 (image->storage_class == PseudoClass))
2482 {
cristybb503372010-05-27 20:51:26 +00002483 i=(ssize_t) (MagickMin(MagickMax(center,(double) start),(double) stop-
cristy3ed852e2009-09-05 21:47:34 +00002484 1.0)+0.5);
cristybb503372010-05-27 20:51:26 +00002485 j=(ssize_t) ((contribution[i-start].pixel-contribution[0].pixel)*
cristy3ed852e2009-09-05 21:47:34 +00002486 image->columns+x);
2487 resize_indexes[x]=indexes[j];
2488 }
2489 q++;
2490 }
2491 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2492 status=MagickFalse;
2493 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2494 {
2495 MagickBooleanType
2496 proceed;
2497
cristyb5d5f722009-11-04 03:03:49 +00002498#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002499 #pragma omp critical (MagickCore_VerticalFilter)
2500#endif
cristy9af9b5d2010-08-15 17:04:28 +00002501 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span);
cristy3ed852e2009-09-05 21:47:34 +00002502 if (proceed == MagickFalse)
2503 status=MagickFalse;
2504 }
2505 }
2506 resize_view=DestroyCacheView(resize_view);
2507 image_view=DestroyCacheView(image_view);
2508 contributions=DestroyContributionThreadSet(contributions);
2509 return(status);
2510}
2511
cristybb503372010-05-27 20:51:26 +00002512MagickExport Image *ResizeImage(const Image *image,const size_t columns,
2513 const size_t rows,const FilterTypes filter,const double blur,
cristy3ed852e2009-09-05 21:47:34 +00002514 ExceptionInfo *exception)
2515{
2516#define WorkLoadFactor 0.265
2517
2518 FilterTypes
2519 filter_type;
2520
2521 Image
2522 *filter_image,
2523 *resize_image;
2524
cristy9af9b5d2010-08-15 17:04:28 +00002525 MagickOffsetType
2526 offset;
2527
cristy3ed852e2009-09-05 21:47:34 +00002528 MagickRealType
2529 x_factor,
2530 y_factor;
2531
2532 MagickSizeType
2533 span;
2534
2535 MagickStatusType
2536 status;
2537
2538 ResizeFilter
2539 *resize_filter;
2540
cristy3ed852e2009-09-05 21:47:34 +00002541 /*
2542 Acquire resize image.
2543 */
2544 assert(image != (Image *) NULL);
2545 assert(image->signature == MagickSignature);
2546 if (image->debug != MagickFalse)
2547 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2548 assert(exception != (ExceptionInfo *) NULL);
2549 assert(exception->signature == MagickSignature);
2550 if ((columns == 0) || (rows == 0))
2551 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2552 if ((columns == image->columns) && (rows == image->rows) &&
2553 (filter == UndefinedFilter) && (blur == 1.0))
2554 return(CloneImage(image,0,0,MagickTrue,exception));
2555 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2556 if (resize_image == (Image *) NULL)
2557 return(resize_image);
2558 /*
2559 Acquire resize filter.
2560 */
2561 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2562 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2563 if ((x_factor*y_factor) > WorkLoadFactor)
2564 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2565 else
2566 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2567 if (filter_image == (Image *) NULL)
2568 return(DestroyImage(resize_image));
2569 filter_type=LanczosFilter;
2570 if (filter != UndefinedFilter)
2571 filter_type=filter;
2572 else
2573 if ((x_factor == 1.0) && (y_factor == 1.0))
2574 filter_type=PointFilter;
2575 else
2576 if ((image->storage_class == PseudoClass) ||
2577 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2578 filter_type=MitchellFilter;
2579 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2580 exception);
2581 /*
2582 Resize image.
2583 */
cristy9af9b5d2010-08-15 17:04:28 +00002584 offset=0;
cristy3ed852e2009-09-05 21:47:34 +00002585 if ((x_factor*y_factor) > WorkLoadFactor)
2586 {
2587 span=(MagickSizeType) (filter_image->columns+rows);
2588 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002589 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002590 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002591 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002592 }
2593 else
2594 {
2595 span=(MagickSizeType) (filter_image->rows+columns);
2596 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
cristy9af9b5d2010-08-15 17:04:28 +00002597 &offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002598 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
cristy9af9b5d2010-08-15 17:04:28 +00002599 span,&offset,exception);
cristy3ed852e2009-09-05 21:47:34 +00002600 }
2601 /*
2602 Free resources.
2603 */
2604 filter_image=DestroyImage(filter_image);
2605 resize_filter=DestroyResizeFilter(resize_filter);
2606 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2607 return((Image *) NULL);
2608 resize_image->type=image->type;
2609 return(resize_image);
2610}
2611
2612/*
2613%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2614% %
2615% %
2616% %
2617% S a m p l e I m a g e %
2618% %
2619% %
2620% %
2621%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2622%
2623% SampleImage() scales an image to the desired dimensions with pixel
2624% sampling. Unlike other scaling methods, this method does not introduce
2625% any additional color into the scaled image.
2626%
2627% The format of the SampleImage method is:
2628%
cristybb503372010-05-27 20:51:26 +00002629% Image *SampleImage(const Image *image,const size_t columns,
2630% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002631%
2632% A description of each parameter follows:
2633%
2634% o image: the image.
2635%
2636% o columns: the number of columns in the sampled image.
2637%
2638% o rows: the number of rows in the sampled image.
2639%
2640% o exception: return any errors or warnings in this structure.
2641%
2642*/
cristybb503372010-05-27 20:51:26 +00002643MagickExport Image *SampleImage(const Image *image,const size_t columns,
2644 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002645{
2646#define SampleImageTag "Sample/Image"
2647
cristyc4c8d132010-01-07 01:58:38 +00002648 CacheView
2649 *image_view,
2650 *sample_view;
2651
cristy3ed852e2009-09-05 21:47:34 +00002652 Image
2653 *sample_image;
2654
cristy3ed852e2009-09-05 21:47:34 +00002655 MagickBooleanType
2656 status;
2657
cristy5f959472010-05-27 22:19:46 +00002658 MagickOffsetType
2659 progress;
2660
cristybb503372010-05-27 20:51:26 +00002661 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002662 x;
2663
cristy5f959472010-05-27 22:19:46 +00002664 ssize_t
2665 *x_offset,
2666 y;
2667
cristy3ed852e2009-09-05 21:47:34 +00002668 /*
2669 Initialize sampled image attributes.
2670 */
2671 assert(image != (const Image *) NULL);
2672 assert(image->signature == MagickSignature);
2673 if (image->debug != MagickFalse)
2674 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2675 assert(exception != (ExceptionInfo *) NULL);
2676 assert(exception->signature == MagickSignature);
2677 if ((columns == 0) || (rows == 0))
2678 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2679 if ((columns == image->columns) && (rows == image->rows))
2680 return(CloneImage(image,0,0,MagickTrue,exception));
2681 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2682 if (sample_image == (Image *) NULL)
2683 return((Image *) NULL);
2684 /*
2685 Allocate scan line buffer and column offset buffers.
2686 */
cristybb503372010-05-27 20:51:26 +00002687 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns,
cristy3ed852e2009-09-05 21:47:34 +00002688 sizeof(*x_offset));
cristybb503372010-05-27 20:51:26 +00002689 if (x_offset == (ssize_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002690 {
2691 sample_image=DestroyImage(sample_image);
2692 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2693 }
cristybb503372010-05-27 20:51:26 +00002694 for (x=0; x < (ssize_t) sample_image->columns; x++)
2695 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/
cristy3ed852e2009-09-05 21:47:34 +00002696 sample_image->columns);
2697 /*
2698 Sample each row.
2699 */
2700 status=MagickTrue;
2701 progress=0;
2702 image_view=AcquireCacheView(image);
2703 sample_view=AcquireCacheView(sample_image);
cristyb5d5f722009-11-04 03:03:49 +00002704#if defined(MAGICKCORE_OPENMP_SUPPORT)
2705 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002706#endif
cristybb503372010-05-27 20:51:26 +00002707 for (y=0; y < (ssize_t) sample_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002708 {
cristy3ed852e2009-09-05 21:47:34 +00002709 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002710 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002711
2712 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002713 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002714
2715 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002716 *restrict sample_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002717
cristy3ed852e2009-09-05 21:47:34 +00002718 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002719 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002720
cristy03dbbd22010-09-19 23:04:47 +00002721 register ssize_t
2722 x;
2723
cristy9af9b5d2010-08-15 17:04:28 +00002724 ssize_t
2725 y_offset;
2726
cristy3ed852e2009-09-05 21:47:34 +00002727 if (status == MagickFalse)
2728 continue;
cristy9af9b5d2010-08-15 17:04:28 +00002729 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/
2730 sample_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00002731 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2732 exception);
2733 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2734 exception);
2735 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2736 {
2737 status=MagickFalse;
2738 continue;
2739 }
2740 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2741 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2742 /*
2743 Sample each column.
2744 */
cristybb503372010-05-27 20:51:26 +00002745 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002746 *q++=p[x_offset[x]];
2747 if ((image->storage_class == PseudoClass) ||
2748 (image->colorspace == CMYKColorspace))
cristybb503372010-05-27 20:51:26 +00002749 for (x=0; x < (ssize_t) sample_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002750 sample_indexes[x]=indexes[x_offset[x]];
2751 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2752 status=MagickFalse;
2753 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2754 {
2755 MagickBooleanType
2756 proceed;
2757
cristyb5d5f722009-11-04 03:03:49 +00002758#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002759 #pragma omp critical (MagickCore_SampleImage)
2760#endif
2761 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2762 if (proceed == MagickFalse)
2763 status=MagickFalse;
2764 }
2765 }
2766 image_view=DestroyCacheView(image_view);
2767 sample_view=DestroyCacheView(sample_view);
cristybb503372010-05-27 20:51:26 +00002768 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset);
cristy3ed852e2009-09-05 21:47:34 +00002769 sample_image->type=image->type;
2770 return(sample_image);
2771}
2772
2773/*
2774%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2775% %
2776% %
2777% %
2778% S c a l e I m a g e %
2779% %
2780% %
2781% %
2782%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2783%
2784% ScaleImage() changes the size of an image to the given dimensions.
2785%
2786% The format of the ScaleImage method is:
2787%
cristybb503372010-05-27 20:51:26 +00002788% Image *ScaleImage(const Image *image,const size_t columns,
2789% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002790%
2791% A description of each parameter follows:
2792%
2793% o image: the image.
2794%
2795% o columns: the number of columns in the scaled image.
2796%
2797% o rows: the number of rows in the scaled image.
2798%
2799% o exception: return any errors or warnings in this structure.
2800%
2801*/
cristybb503372010-05-27 20:51:26 +00002802MagickExport Image *ScaleImage(const Image *image,const size_t columns,
2803 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002804{
2805#define ScaleImageTag "Scale/Image"
2806
cristyed6cb232010-01-20 03:07:53 +00002807 CacheView
2808 *image_view,
2809 *scale_view;
2810
cristy3ed852e2009-09-05 21:47:34 +00002811 Image
2812 *scale_image;
2813
cristy3ed852e2009-09-05 21:47:34 +00002814 MagickBooleanType
2815 next_column,
2816 next_row,
2817 proceed;
2818
2819 MagickPixelPacket
2820 pixel,
2821 *scale_scanline,
2822 *scanline,
2823 *x_vector,
2824 *y_vector,
2825 zero;
2826
cristy3ed852e2009-09-05 21:47:34 +00002827 PointInfo
2828 scale,
2829 span;
2830
cristybb503372010-05-27 20:51:26 +00002831 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002832 i;
2833
cristy9af9b5d2010-08-15 17:04:28 +00002834 ssize_t
2835 number_rows,
2836 y;
2837
cristy3ed852e2009-09-05 21:47:34 +00002838 /*
2839 Initialize scaled image attributes.
2840 */
2841 assert(image != (const Image *) NULL);
2842 assert(image->signature == MagickSignature);
2843 if (image->debug != MagickFalse)
2844 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2845 assert(exception != (ExceptionInfo *) NULL);
2846 assert(exception->signature == MagickSignature);
2847 if ((columns == 0) || (rows == 0))
2848 return((Image *) NULL);
2849 if ((columns == image->columns) && (rows == image->rows))
2850 return(CloneImage(image,0,0,MagickTrue,exception));
2851 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2852 if (scale_image == (Image *) NULL)
2853 return((Image *) NULL);
2854 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2855 {
2856 InheritException(exception,&scale_image->exception);
2857 scale_image=DestroyImage(scale_image);
2858 return((Image *) NULL);
2859 }
2860 /*
2861 Allocate memory.
2862 */
2863 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2864 sizeof(*x_vector));
2865 scanline=x_vector;
2866 if (image->rows != scale_image->rows)
2867 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2868 sizeof(*scanline));
2869 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2870 scale_image->columns,sizeof(*scale_scanline));
2871 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2872 sizeof(*y_vector));
2873 if ((scanline == (MagickPixelPacket *) NULL) ||
2874 (scale_scanline == (MagickPixelPacket *) NULL) ||
2875 (x_vector == (MagickPixelPacket *) NULL) ||
2876 (y_vector == (MagickPixelPacket *) NULL))
2877 {
2878 scale_image=DestroyImage(scale_image);
2879 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2880 }
2881 /*
2882 Scale image.
2883 */
2884 number_rows=0;
2885 next_row=MagickTrue;
2886 span.y=1.0;
2887 scale.y=(double) scale_image->rows/(double) image->rows;
2888 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2889 sizeof(*y_vector));
2890 GetMagickPixelPacket(image,&pixel);
2891 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2892 i=0;
cristyed6cb232010-01-20 03:07:53 +00002893 image_view=AcquireCacheView(image);
2894 scale_view=AcquireCacheView(scale_image);
cristybb503372010-05-27 20:51:26 +00002895 for (y=0; y < (ssize_t) scale_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002896 {
2897 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002898 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002899
2900 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002901 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +00002902
2903 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002904 *restrict scale_indexes;
cristy3ed852e2009-09-05 21:47:34 +00002905
cristy3ed852e2009-09-05 21:47:34 +00002906 register MagickPixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002907 *restrict s,
2908 *restrict t;
cristy3ed852e2009-09-05 21:47:34 +00002909
2910 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002911 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002912
cristy9af9b5d2010-08-15 17:04:28 +00002913 register ssize_t
2914 x;
2915
cristyed6cb232010-01-20 03:07:53 +00002916 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1,
2917 exception);
cristy3ed852e2009-09-05 21:47:34 +00002918 if (q == (PixelPacket *) NULL)
2919 break;
2920 scale_indexes=GetAuthenticIndexQueue(scale_image);
2921 if (scale_image->rows == image->rows)
2922 {
2923 /*
2924 Read a new scanline.
2925 */
cristyed6cb232010-01-20 03:07:53 +00002926 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2927 exception);
cristy3ed852e2009-09-05 21:47:34 +00002928 if (p == (const PixelPacket *) NULL)
2929 break;
cristyed6cb232010-01-20 03:07:53 +00002930 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002931 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002932 {
cristyce70c172010-01-07 17:15:30 +00002933 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2934 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2935 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002936 if (image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00002937 x_vector[x].opacity=(MagickRealType) GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002938 if (indexes != (IndexPacket *) NULL)
2939 x_vector[x].index=(MagickRealType) indexes[x];
2940 p++;
2941 }
2942 }
2943 else
2944 {
2945 /*
2946 Scale Y direction.
2947 */
2948 while (scale.y < span.y)
2949 {
cristy9af9b5d2010-08-15 17:04:28 +00002950 if ((next_row != MagickFalse) &&
2951 (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002952 {
2953 /*
2954 Read a new scanline.
2955 */
cristyed6cb232010-01-20 03:07:53 +00002956 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2957 exception);
cristy3ed852e2009-09-05 21:47:34 +00002958 if (p == (const PixelPacket *) NULL)
2959 break;
cristyed6cb232010-01-20 03:07:53 +00002960 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002961 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002962 {
cristyce70c172010-01-07 17:15:30 +00002963 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
2964 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
2965 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002966 if (image->matte != MagickFalse)
cristyed6cb232010-01-20 03:07:53 +00002967 x_vector[x].opacity=(MagickRealType)
cristy9af9b5d2010-08-15 17:04:28 +00002968 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00002969 if (indexes != (IndexPacket *) NULL)
2970 x_vector[x].index=(MagickRealType) indexes[x];
2971 p++;
2972 }
2973 number_rows++;
2974 }
cristybb503372010-05-27 20:51:26 +00002975 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002976 {
2977 y_vector[x].red+=scale.y*x_vector[x].red;
2978 y_vector[x].green+=scale.y*x_vector[x].green;
2979 y_vector[x].blue+=scale.y*x_vector[x].blue;
2980 if (scale_image->matte != MagickFalse)
2981 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2982 if (scale_indexes != (IndexPacket *) NULL)
2983 y_vector[x].index+=scale.y*x_vector[x].index;
2984 }
2985 span.y-=scale.y;
2986 scale.y=(double) scale_image->rows/(double) image->rows;
2987 next_row=MagickTrue;
2988 }
cristybb503372010-05-27 20:51:26 +00002989 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +00002990 {
2991 /*
2992 Read a new scanline.
2993 */
cristyed6cb232010-01-20 03:07:53 +00002994 p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,
2995 exception);
cristy3ed852e2009-09-05 21:47:34 +00002996 if (p == (const PixelPacket *) NULL)
2997 break;
cristyed6cb232010-01-20 03:07:53 +00002998 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +00002999 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003000 {
cristyce70c172010-01-07 17:15:30 +00003001 x_vector[x].red=(MagickRealType) GetRedPixelComponent(p);
3002 x_vector[x].green=(MagickRealType) GetGreenPixelComponent(p);
3003 x_vector[x].blue=(MagickRealType) GetBluePixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003004 if (image->matte != MagickFalse)
cristy2b726bd2010-01-11 01:05:39 +00003005 x_vector[x].opacity=(MagickRealType)
3006 GetOpacityPixelComponent(p);
cristy3ed852e2009-09-05 21:47:34 +00003007 if (indexes != (IndexPacket *) NULL)
3008 x_vector[x].index=(MagickRealType) indexes[x];
3009 p++;
3010 }
3011 number_rows++;
3012 next_row=MagickFalse;
3013 }
3014 s=scanline;
cristybb503372010-05-27 20:51:26 +00003015 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003016 {
3017 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
3018 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
3019 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
3020 if (image->matte != MagickFalse)
3021 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
3022 if (scale_indexes != (IndexPacket *) NULL)
3023 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
3024 s->red=pixel.red;
3025 s->green=pixel.green;
3026 s->blue=pixel.blue;
3027 if (scale_image->matte != MagickFalse)
3028 s->opacity=pixel.opacity;
3029 if (scale_indexes != (IndexPacket *) NULL)
3030 s->index=pixel.index;
3031 s++;
3032 y_vector[x]=zero;
3033 }
3034 scale.y-=span.y;
3035 if (scale.y <= 0)
3036 {
3037 scale.y=(double) scale_image->rows/(double) image->rows;
3038 next_row=MagickTrue;
3039 }
3040 span.y=1.0;
3041 }
3042 if (scale_image->columns == image->columns)
3043 {
3044 /*
3045 Transfer scanline to scaled image.
3046 */
3047 s=scanline;
cristybb503372010-05-27 20:51:26 +00003048 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003049 {
cristyce70c172010-01-07 17:15:30 +00003050 q->red=ClampToQuantum(s->red);
3051 q->green=ClampToQuantum(s->green);
3052 q->blue=ClampToQuantum(s->blue);
cristy3ed852e2009-09-05 21:47:34 +00003053 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003054 q->opacity=ClampToQuantum(s->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003055 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003056 scale_indexes[x]=(IndexPacket) ClampToQuantum(s->index);
cristy3ed852e2009-09-05 21:47:34 +00003057 q++;
3058 s++;
3059 }
3060 }
3061 else
3062 {
3063 /*
3064 Scale X direction.
3065 */
3066 pixel=zero;
3067 next_column=MagickFalse;
3068 span.x=1.0;
3069 s=scanline;
3070 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003071 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003072 {
3073 scale.x=(double) scale_image->columns/(double) image->columns;
3074 while (scale.x >= span.x)
3075 {
3076 if (next_column != MagickFalse)
3077 {
3078 pixel=zero;
3079 t++;
3080 }
3081 pixel.red+=span.x*s->red;
3082 pixel.green+=span.x*s->green;
3083 pixel.blue+=span.x*s->blue;
3084 if (image->matte != MagickFalse)
3085 pixel.opacity+=span.x*s->opacity;
3086 if (scale_indexes != (IndexPacket *) NULL)
3087 pixel.index+=span.x*s->index;
3088 t->red=pixel.red;
3089 t->green=pixel.green;
3090 t->blue=pixel.blue;
3091 if (scale_image->matte != MagickFalse)
3092 t->opacity=pixel.opacity;
3093 if (scale_indexes != (IndexPacket *) NULL)
3094 t->index=pixel.index;
3095 scale.x-=span.x;
3096 span.x=1.0;
3097 next_column=MagickTrue;
3098 }
3099 if (scale.x > 0)
3100 {
3101 if (next_column != MagickFalse)
3102 {
3103 pixel=zero;
3104 next_column=MagickFalse;
3105 t++;
3106 }
3107 pixel.red+=scale.x*s->red;
3108 pixel.green+=scale.x*s->green;
3109 pixel.blue+=scale.x*s->blue;
3110 if (scale_image->matte != MagickFalse)
3111 pixel.opacity+=scale.x*s->opacity;
3112 if (scale_indexes != (IndexPacket *) NULL)
3113 pixel.index+=scale.x*s->index;
3114 span.x-=scale.x;
3115 }
3116 s++;
3117 }
3118 if (span.x > 0)
3119 {
3120 s--;
3121 pixel.red+=span.x*s->red;
3122 pixel.green+=span.x*s->green;
3123 pixel.blue+=span.x*s->blue;
3124 if (scale_image->matte != MagickFalse)
3125 pixel.opacity+=span.x*s->opacity;
3126 if (scale_indexes != (IndexPacket *) NULL)
3127 pixel.index+=span.x*s->index;
3128 }
3129 if ((next_column == MagickFalse) &&
cristybb503372010-05-27 20:51:26 +00003130 ((ssize_t) (t-scale_scanline) < (ssize_t) scale_image->columns))
cristy3ed852e2009-09-05 21:47:34 +00003131 {
3132 t->red=pixel.red;
3133 t->green=pixel.green;
3134 t->blue=pixel.blue;
3135 if (scale_image->matte != MagickFalse)
3136 t->opacity=pixel.opacity;
3137 if (scale_indexes != (IndexPacket *) NULL)
3138 t->index=pixel.index;
3139 }
3140 /*
3141 Transfer scanline to scaled image.
3142 */
3143 t=scale_scanline;
cristybb503372010-05-27 20:51:26 +00003144 for (x=0; x < (ssize_t) scale_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00003145 {
cristyce70c172010-01-07 17:15:30 +00003146 q->red=ClampToQuantum(t->red);
3147 q->green=ClampToQuantum(t->green);
3148 q->blue=ClampToQuantum(t->blue);
cristy3ed852e2009-09-05 21:47:34 +00003149 if (scale_image->matte != MagickFalse)
cristyce70c172010-01-07 17:15:30 +00003150 q->opacity=ClampToQuantum(t->opacity);
cristy3ed852e2009-09-05 21:47:34 +00003151 if (scale_indexes != (IndexPacket *) NULL)
cristyce70c172010-01-07 17:15:30 +00003152 scale_indexes[x]=(IndexPacket) ClampToQuantum(t->index);
cristy3ed852e2009-09-05 21:47:34 +00003153 t++;
3154 q++;
3155 }
3156 }
cristyed6cb232010-01-20 03:07:53 +00003157 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003158 break;
cristy96b16132010-08-29 17:19:52 +00003159 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y,
3160 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00003161 if (proceed == MagickFalse)
3162 break;
3163 }
cristyed6cb232010-01-20 03:07:53 +00003164 scale_view=DestroyCacheView(scale_view);
3165 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00003166 /*
3167 Free allocated memory.
3168 */
3169 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
3170 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
3171 if (scale_image->rows != image->rows)
3172 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
3173 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
3174 scale_image->type=image->type;
3175 return(scale_image);
3176}
3177
3178/*
3179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3180% %
3181% %
3182% %
3183+ S e t R e s i z e F i l t e r S u p p o r t %
3184% %
3185% %
3186% %
3187%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3188%
3189% SetResizeFilterSupport() specifies which IR filter to use to window
3190%
3191% The format of the SetResizeFilterSupport method is:
3192%
3193% void SetResizeFilterSupport(ResizeFilter *resize_filter,
3194% const MagickRealType support)
3195%
3196% A description of each parameter follows:
3197%
3198% o resize_filter: the resize filter.
3199%
3200% o support: the filter spport radius.
3201%
3202*/
3203MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
3204 const MagickRealType support)
3205{
3206 assert(resize_filter != (ResizeFilter *) NULL);
3207 assert(resize_filter->signature == MagickSignature);
3208 resize_filter->support=support;
3209}
3210
3211/*
3212%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3213% %
3214% %
3215% %
3216% T h u m b n a i l I m a g e %
3217% %
3218% %
3219% %
3220%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3221%
3222% ThumbnailImage() changes the size of an image to the given dimensions and
3223% removes any associated profiles. The goal is to produce small low cost
3224% thumbnail images suited for display on the Web.
3225%
3226% The format of the ThumbnailImage method is:
3227%
cristybb503372010-05-27 20:51:26 +00003228% Image *ThumbnailImage(const Image *image,const size_t columns,
3229% const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003230%
3231% A description of each parameter follows:
3232%
3233% o image: the image.
3234%
3235% o columns: the number of columns in the scaled image.
3236%
3237% o rows: the number of rows in the scaled image.
3238%
3239% o exception: return any errors or warnings in this structure.
3240%
3241*/
cristy9af9b5d2010-08-15 17:04:28 +00003242MagickExport Image *ThumbnailImage(const Image *image,const size_t columns,
3243 const size_t rows,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003244{
3245#define SampleFactor 5
3246
3247 char
3248 value[MaxTextExtent];
3249
3250 const char
3251 *name;
3252
3253 Image
3254 *thumbnail_image;
3255
3256 MagickRealType
3257 x_factor,
3258 y_factor;
3259
cristybb503372010-05-27 20:51:26 +00003260 size_t
cristy3ed852e2009-09-05 21:47:34 +00003261 version;
3262
cristy9af9b5d2010-08-15 17:04:28 +00003263 struct stat
3264 attributes;
3265
cristy3ed852e2009-09-05 21:47:34 +00003266 assert(image != (Image *) NULL);
3267 assert(image->signature == MagickSignature);
3268 if (image->debug != MagickFalse)
3269 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3270 assert(exception != (ExceptionInfo *) NULL);
3271 assert(exception->signature == MagickSignature);
3272 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
3273 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
3274 if ((x_factor*y_factor) > 0.1)
cristyd1bb3bc2010-09-07 00:43:58 +00003275 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3276 exception);
cristy3ed852e2009-09-05 21:47:34 +00003277 else
3278 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
cristyd1bb3bc2010-09-07 00:43:58 +00003279 thumbnail_image=ResizeImage(image,columns,rows,image->filter,
3280 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003281 else
3282 {
3283 Image
3284 *sample_image;
3285
3286 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
3287 exception);
3288 if (sample_image == (Image *) NULL)
3289 return((Image *) NULL);
cristyd1bb3bc2010-09-07 00:43:58 +00003290 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter,
3291 image->blur,exception);
cristy3ed852e2009-09-05 21:47:34 +00003292 sample_image=DestroyImage(sample_image);
3293 }
3294 if (thumbnail_image == (Image *) NULL)
3295 return(thumbnail_image);
3296 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
3297 if (thumbnail_image->matte == MagickFalse)
3298 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
3299 thumbnail_image->depth=8;
3300 thumbnail_image->interlace=NoInterlace;
3301 /*
3302 Strip all profiles except color profiles.
3303 */
3304 ResetImageProfileIterator(thumbnail_image);
3305 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
3306 {
3307 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
3308 {
cristy2b726bd2010-01-11 01:05:39 +00003309 (void) DeleteImageProfile(thumbnail_image,name);
cristy3ed852e2009-09-05 21:47:34 +00003310 ResetImageProfileIterator(thumbnail_image);
3311 }
3312 name=GetNextImageProfile(thumbnail_image);
3313 }
3314 (void) DeleteImageProperty(thumbnail_image,"comment");
3315 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
cristy7b63d662009-11-13 01:58:56 +00003316 if (strstr(image->magick_filename,"//") == (char *) NULL)
3317 (void) FormatMagickString(value,MaxTextExtent,"file://%s",
cristy3ed852e2009-09-05 21:47:34 +00003318 image->magick_filename);
3319 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
3320 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
3321 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
3322 {
cristye8c25f92010-06-03 00:53:06 +00003323 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003324 attributes.st_mtime);
3325 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
3326 }
cristye8c25f92010-06-03 00:53:06 +00003327 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003328 attributes.st_mtime);
cristyb9080c92009-12-01 20:13:26 +00003329 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value);
cristy2ce15c92010-03-12 14:03:41 +00003330 (void) ConcatenateMagickString(value,"B",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00003331 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
3332 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
3333 LocaleLower(value);
3334 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
3335 (void) SetImageProperty(thumbnail_image,"software",
3336 GetMagickVersion(&version));
cristye8c25f92010-06-03 00:53:06 +00003337 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3338 image->magick_columns);
cristy3ed852e2009-09-05 21:47:34 +00003339 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
cristye8c25f92010-06-03 00:53:06 +00003340 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00003341 image->magick_rows);
cristy3ed852e2009-09-05 21:47:34 +00003342 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
cristye8c25f92010-06-03 00:53:06 +00003343 (void) FormatMagickString(value,MaxTextExtent,"%.20g",(double)
3344 GetImageListLength(image));
cristy3ed852e2009-09-05 21:47:34 +00003345 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
3346 return(thumbnail_image);
3347}