blob: 07c0a6e3a5976df2d49daac8e4626cbef3337ab3 [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% %
20% Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization %
21% 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"
57#include "magick/pixel-private.h"
58#include "magick/property.h"
59#include "magick/monitor.h"
60#include "magick/monitor-private.h"
61#include "magick/pixel.h"
62#include "magick/option.h"
63#include "magick/resample.h"
64#include "magick/resize.h"
65#include "magick/resize-private.h"
66#include "magick/string_.h"
67#include "magick/thread-private.h"
68#include "magick/utility.h"
69#include "magick/version.h"
70#if defined(MAGICKCORE_LQR_DELEGATE)
71#include <lqr.h>
72#endif
73
74/*
75 Typedef declarations.
76*/
77struct _ResizeFilter
78{
79 MagickRealType
80 (*filter)(const MagickRealType,const ResizeFilter *),
81 (*window)(const MagickRealType,const ResizeFilter *),
82 support, /* filter region of support - the filter support limit */
83 window_support, /* window support, usally equal to support (expert only) */
84 scale, /* dimension to scale to fit window support (usally 1.0) */
85 blur, /* x-scale (blur-sharpen) */
86 cubic[8]; /* cubic coefficents for smooth Cubic filters */
87
88 unsigned long
89 signature;
90};
91
92/*
93 Forward declaractions.
94*/
95static MagickRealType
96 I0(MagickRealType x),
97 BesselOrderOne(MagickRealType);
98
99/*
100%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
101% %
102% %
103% %
104+ F i l t e r F u n c t i o n s %
105% %
106% %
107% %
108%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
109%
110% These are the various filter and windowing functions that are provided,
111%
112% They are all internal to this module only. See AcquireResizeFilterInfo()
113% for details of the access to these functions, via the
114% GetResizeFilterSupport() and GetResizeFilterWeight() API interface.
115%
116% The individual filter functions have this format...
117%
118% static MagickRealtype *FilterName(const MagickRealType x,
119% const MagickRealType support)
120%
121% o x: the distance from the sampling point
122% generally in the range of 0 to support
123% The GetResizeFilterWeight() ensures this a positive value.
124%
125% o resize_filter: Current Filter Information
126% This allows function to access support, and posibly other
127% pre-calculated information defineding the functions.
128%
129*/
130
131static MagickRealType Bessel(const MagickRealType x,
132 const ResizeFilter *magick_unused(resize_filter))
133{
134 /*
135 See Pratt "Digital Image Processing" p.97 for Bessel functions
136
137 This function actually a X-scaled Jinc(x) function.
138 http://mathworld.wolfram.com/JincFunction.html
139 And on page 11 of...
140 http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
141 */
142 if (x == 0.0)
143 return((MagickRealType) (MagickPI/4.0));
144 return(BesselOrderOne(MagickPI*x)/(2.0*x));
145}
146
147static MagickRealType Blackman(const MagickRealType x,
148 const ResizeFilter *magick_unused(resize_filter))
149{
150 /*
151 Blackman: 2rd Order cosine windowing function.
152 */
153 return(0.42+0.5*cos(MagickPI*(double) x)+0.08*cos(2.0*MagickPI*(double) x));
154}
155
156static MagickRealType Bohman(const MagickRealType x,
157 const ResizeFilter *magick_unused(resize_filter))
158{
159 /*
160 Bohman: 2rd Order cosine windowing function.
161 */
162 return((1-x)*cos(MagickPI*(double) x)+sin(MagickPI*(double) x)/MagickPI);
163}
164
165static MagickRealType Box(const MagickRealType magick_unused(x),
166 const ResizeFilter *magick_unused(resize_filter))
167{
168 /*
169 Just return 1.0, filter will still be clipped by its support window.
170 */
171 return(1.0);
172}
173
174static MagickRealType CubicBC(const MagickRealType x,
175 const ResizeFilter *resize_filter)
176{
177 /*
178 Cubic Filters using B,C determined values:
179
180 Mitchell-Netravali B=1/3 C=1/3 Qualitively ideal Cubic Filter
181 Catmull-Rom B= 0 C=1/2 Cublic Interpolation Function
182 Cubic B-Spline B= 1 C= 0 Spline Approximation of Gaussian
183 Hermite B= 0 C= 0 Quadratic Spline (support = 1)
184
185 See paper by Mitchell and Netravali,
186 Reconstruction Filters in Computer Graphics
187 Computer Graphics, Volume 22, Number 4, August 1988
188 http://www.cs.utexas.edu/users/fussell/courses/cs384g/
189 lectures/mitchell/Mitchell.pdf
190
191 Coefficents are determined from B,C values
192 P0 = ( 6 - 2*B )/6
193 P1 = 0
194 P2 = (-18 +12*B + 6*C )/6
195 P3 = ( 12 - 9*B - 6*C )/6
196 Q0 = ( 8*B +24*C )/6
197 Q1 = ( -12*B -48*C )/6
198 Q2 = ( 6*B +30*C )/6
199 Q3 = ( - 1*B - 6*C )/6
200
201 Which is used to define the filter...
202 P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
203 Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x <= 2
204
205 Which ensures function is continuous in value and derivative (slope).
206 */
207 if (x < 1.0)
208 return(resize_filter->cubic[0]+x*(resize_filter->cubic[1]+x*
209 (resize_filter->cubic[2]+x*resize_filter->cubic[3])));
210 if (x < 2.0)
211 return(resize_filter->cubic[4] +x*(resize_filter->cubic[5]+x*
212 (resize_filter->cubic[6] +x*resize_filter->cubic[7])));
213 return(0.0);
214}
215
216static MagickRealType Gaussian(const MagickRealType x,
217 const ResizeFilter *magick_unused(resize_filter))
218{
219 return(exp((double) (-2.0*x*x))*sqrt(2.0/MagickPI));
220}
221
222static MagickRealType Hanning(const MagickRealType x,
223 const ResizeFilter *magick_unused(resize_filter))
224{
225 /*
226 A Cosine windowing function.
227 */
228 return(0.5+0.5*cos(MagickPI*(double) x));
229}
230
231static MagickRealType Hamming(const MagickRealType x,
232 const ResizeFilter *magick_unused(resize_filter))
233{
234 /*
235 A offset Cosine windowing function.
236 */
237 return(0.54+0.46*cos(MagickPI*(double) x));
238}
239
240static MagickRealType Kaiser(const MagickRealType x,
241 const ResizeFilter *magick_unused(resize_filter))
242{
243#define Alpha 6.5
244#define I0A (1.0/I0(Alpha))
245
246 /*
247 Kaiser Windowing Function (bessel windowing):
248 Alpha is a free value from 5 to 8 (currently hardcoded to 6.5)
249 Future: make alphand the IOA pre-calculation, a 'expert' setting.
250 */
251 return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
252}
253
254static MagickRealType Lagrange(const MagickRealType x,
255 const ResizeFilter *resize_filter)
256{
257 long
258 n,
259 order;
260
261 MagickRealType
262 value;
263
264 register long
265 i;
266
267 /*
268 Lagrange Piece-Wise polynomial fit of Sinc:
269 N is the 'order' of the lagrange function and depends on
270 the overall support window size of the filter. That is for
271 a support of 2, gives a lagrange-4 or piece-wise cubic functions
272
273 Note that n is the specific piece of the piece-wise function to calculate.
274
275 See Survey: Interpolation Methods, IEEE Transactions on Medical Imaging,
276 Vol 18, No 11, November 1999, p1049-1075, -- Equation 27 on p1064
277 */
278 if (x > resize_filter->support)
279 return(0.0);
280 order=(long) (2.0*resize_filter->window_support); /* number of pieces */
281 n=(long) ((1.0*order)/2.0+x); /* which piece does x belong to */
282 value=1.0f;
283 for (i=0; i < order; i++)
284 if (i != n)
285 value*=(n-i-x)/(n-i);
286 return(value);
287}
288
289static MagickRealType Quadratic(const MagickRealType x,
290 const ResizeFilter *magick_unused(resize_filter))
291{
292 /*
293 2rd order (quadratic) B-Spline approximation of Gaussian.
294 */
295 if (x < 0.5)
296 return(0.75-x*x);
297 if (x < 1.5)
298 return(0.5*(x-1.5)*(x-1.5));
299 return(0.0);
300}
301
302static MagickRealType Sinc(const MagickRealType x,
303 const ResizeFilter *magick_unused(resize_filter))
304{
305 /*
306 This function actually a X-scaled Sinc(x) function.
307 */
308 if (x == 0.0)
309 return(1.0);
310 return(sin(MagickPI*(double) x)/(MagickPI*(double) x));
311}
312
313static MagickRealType Triangle(const MagickRealType x,
314 const ResizeFilter *magick_unused(resize_filter))
315{
316 /*
317 1rd order (linear) B-Spline, bilinear interpolation,
318 Tent 1D filter, or a Bartlett 2D Cone filter
319 */
320 if (x < 1.0)
321 return(1.0-x);
322 return(0.0);
323}
324
325static MagickRealType Welsh(const MagickRealType x,
326 const ResizeFilter *magick_unused(resize_filter))
327{
328 /*
329 Welsh parabolic windowing filter.
330 */
331 if (x < 1.0)
332 return(1.0-x*x);
333 return(0.0);
334}
335
336/*
337%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
338% %
339% %
340% %
341+ A c q u i r e R e s i z e F i l t e r %
342% %
343% %
344% %
345%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
346%
347% AcquireResizeFilter() allocates the ResizeFilter structure. Choose from
348% these filters:
349%
350% FIR (Finite impulse Response) Filters
351% Box Triangle Quadratic
352% Cubic Hermite Catrom
353% Mitchell
354%
355% IIR (Infinite impulse Response) Filters
356% Gaussian Sinc Bessel
357%
358% Windowed Sinc/Bessel Method
359% Blackman Hanning Hamming
360% Kaiser Lancos (Sinc)
361%
362% FIR filters are used as is, and are limited by that filters support window
363% (unless over-ridden). 'Gaussian' while classed as an IIR filter, is also
364% simply clipped by its support size (1.5).
365%
366% Requesting a windowed filter will return either a windowed Sinc, for a one
367% dimentional orthogonal filtering method, such as ResizeImage(), or a
368% windowed Bessel for image operations requiring a two dimentional
369% cylindrical filtering method, such a DistortImage(). Which function is
370% is used set by the "cylindrical" boolean argument.
371%
372% Directly requesting 'Sinc' or 'Bessel' will force the use of that filter
373% function, with a default 'Blackman' windowing method. This not however
374% recommended as it removes the correct filter selection for different
375% filtering image operations. Selecting a window filtering method is better.
376%
377% Lanczos is purely special case of a Sinc windowed Sinc, but defulting to
378% a 3 lobe support, rather that the default 4 lobe support.
379%
380% Special options can be used to override specific, or all the filter
381% settings. However doing so is not advisible unless you have expert
382% knowledge of the use of resampling filtered techniques. Extreme caution is
383% advised.
384%
385% "filter:filter" Select this function as the filter.
386% If a "filter:window" operation is not provided, then no windowing
387% will be performed on the selected filter, (support clipped)
388%
389% This can be used to force the use of a windowing method as filter,
390% request a 'Sinc' filter in a radially filtered operation, or the
391% 'Bessel' filter for a othogonal filtered operation.
392%
393% "filter:window" Select this windowing function for the filter.
394% While any filter could be used as a windowing function,
395% using that filters first lobe over the whole support window,
396% using a non-windowing method is not advisible.
397%
398% "filter:lobes" Number of lobes to use for the Sinc/Bessel filter.
399% This a simper method of setting filter support size that will
400% correctly handle the Sinc/Bessel switch for an operators filtering
401% requirements.
402%
403% "filter:support" Set the support size for filtering to the size given
404% This not recomented for Sinc/Bessel windowed filters, but is
405% used for simple filters like FIR filters, and the Gaussian Filter.
406% This will override any 'filter:lobes' option.
407%
408% "filter:blur" Scale the filter and support window by this amount.
409% A value >1 will generally result in a more burred image with
410% more ringing effects, while a value <1 will sharpen the
411% resulting image with more aliasing and Morie effects.
412%
413% "filter:win-support" Scale windowing function to this size instead.
414% This causes the windowing (or self-windowing Lagrange filter)
415% to act is if the support winodw it much much larger than what
416% is actually supplied to the calling operator. The filter however
417% is still clipped to the real support size given. If unset this
418% will equal the normal filter support size.
419%
420% "filter:b"
421% "filter:c" Override the preset B,C values for a Cubic type of filter
422% If only one of these are given it is assumes to be a 'Keys'
423% type of filter such that B+2C=1, where Keys 'alpha' value = C
424%
425% "filter:verbose" Output verbose plotting data for graphing the
426% resulting filter over the whole support range (with blur effect).
427%
428% Set a true un-windowed Sinc filter with 10 lobes (very slow)
429% -set option:filter:filter Sinc
430% -set option:filter:lobes 8
431%
432% For example force an 8 lobe Lanczos (Sinc or Bessel) filter...
433% -filter Lanczos
434% -set option:filter:lobes 8
435%
436% The format of the AcquireResizeFilter method is:
437%
438% ResizeFilter *AcquireResizeFilter(const Image *image,
439% const FilterTypes filter_type, const MagickBooleanType radial,
440% ExceptionInfo *exception)
441%
442% o image: the image.
443%
444% o filter: the filter type, defining a preset filter, window and support.
445%
446% o blur: blur the filter by this amount, use 1.0 if unknown.
447% Image artifact "filter:blur" will override this old usage
448%
449% o radial: 1D orthogonal filter (Sinc) or 2D radial filter (Bessel)
450%
451% o exception: return any errors or warnings in this structure.
452%
453*/
454MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
455 const FilterTypes filter, const MagickRealType blur,
456 const MagickBooleanType cylindrical,ExceptionInfo *exception)
457{
458 const char
459 *artifact;
460
461 FilterTypes
462 filter_type,
463 window_type;
464
465 long
466 filter_artifact;
467
468 MagickRealType
469 B,
470 C;
471
472 register ResizeFilter
473 *resize_filter;
474
475 /*
476 Table Mapping given Filter, into Weighting and Windowing functions.
477 A 'Box' windowing function means its a simble non-windowed filter.
478 A 'Sinc' filter function (must be windowed) could be upgraded to a
479 'Bessel' filter if a "cylindrical" filter is requested, unless a "Sinc"
480 filter specifically request.
481 */
482 static struct
483 {
484 FilterTypes
485 filter,
486 window;
487 } const mapping[SentinelFilter] =
488 {
489 { UndefinedFilter, BoxFilter }, /* undefined */
490 { PointFilter, BoxFilter }, /* special, nearest-neighbour filter */
491 { BoxFilter, BoxFilter }, /* Box averaging Filter */
492 { TriangleFilter, BoxFilter }, /* Linear Interpolation Filter */
493 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
494 { SincFilter, HanningFilter }, /* Hanning -- Cosine-Sinc */
495 { SincFilter, HammingFilter }, /* Hamming -- '' variation */
496 { SincFilter, BlackmanFilter }, /* Blackman -- 2*Cosine-Sinc */
497 { GaussianFilter, BoxFilter }, /* Gaussain Blurring filter */
498 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
499 { CubicFilter, BoxFilter }, /* Cubic Gaussian approximation */
500 { CatromFilter, BoxFilter }, /* Cubic Interpolator */
501 { MitchellFilter, BoxFilter }, /* 'ideal' Cubic Filter */
502 { LanczosFilter, SincFilter }, /* Special, 3 lobed Sinc-Sinc */
503 { BesselFilter, BlackmanFilter }, /* 3 lobed bessel -specific request */
504 { SincFilter, BlackmanFilter }, /* 4 lobed sinc - specific request */
505 { SincFilter, KaiserFilter }, /* Kaiser -- SqRoot-Sinc */
506 { SincFilter, WelshFilter }, /* Welsh -- Parabolic-Sinc */
507 { SincFilter, CubicFilter }, /* Parzen -- Cubic-Sinc */
508 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
509 { SincFilter, BohmanFilter }, /* Bohman -- 2*Cosine-Sinc */
510 { SincFilter, TriangleFilter } /* Bartlett -- Triangle-Sinc */
511 };
512 /*
513 Table maping the filter/window function from the above table to the actual
514 filter/window function call to use. The default support size for that
515 filter as a weighting function, and the point to scale when that function
516 is used as a windowing function (typ 1.0).
517 */
518 static struct
519 {
520 MagickRealType
521 (*function)(const MagickRealType, const ResizeFilter*),
522 support, /* default support size for function as a filter */
523 scale, /* size windowing function, for scaling windowing function */
524 B,
525 C; /* Cubic Filter factors for a CubicBC function, else ignored */
526 } const filters[SentinelFilter] =
527 {
528 { Box, 0.0f, 0.5f, 0.0f, 0.0f }, /* Undefined */
529 { Box, 0.0f, 0.5f, 0.0f, 0.0f }, /* Point */
530 { Box, 0.5f, 0.5f, 0.0f, 0.0f }, /* Box */
531 { Triangle, 1.0f, 1.0f, 0.0f, 0.0f }, /* Triangle */
532 { CubicBC, 1.0f, 1.0f, 0.0f, 0.0f }, /* Hermite, Cubic B=C=0 */
533 { Hanning, 1.0f, 1.0f, 0.0f, 0.0f }, /* Hanning, Cosine window */
534 { Hamming, 1.0f, 1.0f, 0.0f, 0.0f }, /* Hamming, '' variation */
535 { Blackman, 1.0f, 1.0f, 0.0f, 0.0f }, /* Blackman, 2*cos window */
536 { Gaussian, 1.5f, 1.5f, 0.0f, 0.0f }, /* Gaussian */
537 { Quadratic, 1.5f, 1.5f, 0.0f, 0.0f }, /* Quadratic Gaussian */
538 { CubicBC, 2.0f, 2.0f, 1.0f, 0.0f }, /* B-Spline of Gaussian B=1 C=0 */
539 { CubicBC, 2.0f, 1.0f, 0.0f, 0.5f }, /* Catmull-Rom B=0 C=1/2 */
540 { CubicBC, 2.0f, 1.0f, 1.0f/3.0f, 1.0f/3.0f }, /* Mitchel B=C=1/3 */
541 { Sinc, 3.0f, 1.0f, 0.0f, 0.0f }, /* Lanczos, 3 lobed Sinc-Sinc */
542 { Bessel, 3.2383f,1.2197f,.0f,.0f }, /* 3 lobed Blackman-Bessel */
543 { Sinc, 4.0f, 1.0f, 0.0f, 0.0f }, /* 4 lobed Blackman-Sinc */
544 { Kaiser, 1.0f, 1.0f, 0.0f, 0.0f }, /* Kaiser, sq-root windowing */
545 { Welsh, 1.0f, 1.0f, 0.0f, 0.0f }, /* Welsh, Parabolic windowing */
546 { CubicBC, 2.0f, 2.0f, 1.0f, 0.0f }, /* Parzen, B-Spline windowing */
547 { Lagrange, 2.0f, 1.0f, 0.0f, 0.0f }, /* Lagrangian Filter */
548 { Bohman, 1.0f, 1.0f, 0.0f, 0.0f }, /* Bohman, 2*Cosine windowing */
549 { Triangle, 1.0f, 1.0f, 0.0f, 0.0f } /* Bartlett, Triangle windowing */
550 };
551 /*
552 The known zero crossings of the Bessel() or the Jinc(x*PI) function
553 Found by using
554 http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
555 for Jv-function with v=1, then dividing X-roots by PI (tabled below)
556 */
557 static MagickRealType
558 bessel_zeros[16] =
559 {
560 1.21966989126651f,
561 2.23313059438153f,
562 3.23831548416624f,
563 4.24106286379607f,
564 5.24276437687019f,
565 6.24392168986449f,
566 7.24475986871996f,
567 8.24539491395205f,
568 9.24589268494948f,
569 10.2462933487549f,
570 11.2466227948779f,
571 12.2468984611381f,
572 13.2471325221811f,
573 14.2473337358069f,
574 15.2475085630373f,
575 16.247661874701f
576 };
577
578 assert(image != (const Image *) NULL);
579 assert(image->signature == MagickSignature);
580 if (image->debug != MagickFalse)
581 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
582 assert(UndefinedFilter < filter && filter < SentinelFilter);
583 assert(exception != (ExceptionInfo *) NULL);
584 assert(exception->signature == MagickSignature);
585
586 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
587 if (resize_filter == (ResizeFilter *) NULL)
588 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
589
590 /* defaults for the requested filter */
591 filter_type = mapping[filter].filter;
592 window_type = mapping[filter].window;
593
594
595 /* Filter blur -- scaling both filter and support window */
596 resize_filter->blur = blur;
597 artifact=GetImageArtifact(image,"filter:blur");
598 if (artifact != (const char *) NULL)
599 resize_filter->blur = atof(artifact);
600 if ( resize_filter->blur < MagickEpsilon )
601 resize_filter->blur = (MagickRealType) MagickEpsilon;
602
603 /* Modifications for Cylindrical filter use */
604 if ( cylindrical != MagickFalse && filter != SincFilter ) {
605 /* promote 1D Sinc Filter to a 2D Bessel filter */
606 if ( filter_type == SincFilter )
607 filter_type = BesselFilter;
608 /* Prompote Lanczos (Sinc-Sinc) to Lanczos (Bessel-Bessel) */
609 else if ( filter_type == LanczosFilter ) {
610 filter_type = BesselFilter;
611 window_type = BesselFilter;
612 }
613 /* Blur other filters appropriatally correct cylindrical usage */
614 else if ( filter_type == GaussianFilter )
615 /* Gaussian is scaled by 4*ln(2) and not 4*sqrt(2/MagickPI)
616 - according to Paul Heckbert's paper on EWA resampling */
617 resize_filter->blur *= 2.0*log(2.0)/sqrt(2.0/MagickPI);
618 else if ( filter_type != BesselFilter )
619 /* filters with a 1.0 zero root crossing by the first bessel_zero */
620 resize_filter->blur *= bessel_zeros[0];
621 }
622
623 /* Override Filter Selection */
624 artifact=GetImageArtifact(image,"filter:filter");
625 if (artifact != (const char *) NULL) {
626 /* raw filter request - no window function */
627 filter_artifact=ParseMagickOption(MagickFilterOptions,
628 MagickFalse,artifact);
629 if ( UndefinedFilter < filter_artifact &&
630 filter_artifact < SentinelFilter ) {
631 filter_type = (FilterTypes) filter_artifact;
632 window_type = BoxFilter;
633 }
634 /* Lanczos is nor a real filter but a self windowing Sinc/Bessel */
635 if ( filter_artifact == LanczosFilter ) {
636 filter_type = (cylindrical!=MagickFalse) ? BesselFilter : LanczosFilter;
637 window_type = (cylindrical!=MagickFalse) ? BesselFilter : SincFilter;
638 }
639 /* Filter overwide with a specific window function? */
640 artifact=GetImageArtifact(image,"filter:window");
641 if (artifact != (const char *) NULL) {
642 filter_artifact=ParseMagickOption(MagickFilterOptions,
643 MagickFalse,artifact);
644 if ( UndefinedFilter < filter_artifact &&
645 filter_artifact < SentinelFilter ) {
646 if ( filter_artifact != LanczosFilter )
647 window_type = (FilterTypes) filter_artifact;
648 else
649 window_type = (cylindrical!=MagickFalse) ? BesselFilter : SincFilter;
650 }
651 }
652 }
653 else {
654 /* window specified, but no filter function? Assume Sinc/Bessel */
655 artifact=GetImageArtifact(image,"filter:window");
656 if (artifact != (const char *) NULL) {
657 filter_artifact=ParseMagickOption(MagickFilterOptions,MagickFalse,
658 artifact);
659 if ( UndefinedFilter < filter_artifact &&
660 filter_artifact < SentinelFilter ) {
661 filter_type = (cylindrical!=MagickFalse) ? BesselFilter : SincFilter;
662 if ( filter_artifact != LanczosFilter )
663 window_type = (FilterTypes) filter_artifact;
664 else
665 window_type = filter_type;
666 }
667 }
668 }
669
670 resize_filter->filter = filters[filter_type].function;
671 resize_filter->support = filters[filter_type].support;
672 resize_filter->window = filters[window_type].function;
673 resize_filter->scale = filters[window_type].scale;
674 resize_filter->signature=MagickSignature;
675
676 /* Filter support overrides */
677 artifact=GetImageArtifact(image,"filter:lobes");
678 if (artifact != (const char *) NULL) {
679 long lobes = atol(artifact);
680 if ( lobes < 1 ) lobes = 1;
681 resize_filter->support = (MagickRealType) lobes;
682 if ( filter_type == BesselFilter ) {
683 if ( lobes > 16 ) lobes = 16;
684 resize_filter->support = bessel_zeros[lobes-1];
685 }
686 }
687 artifact=GetImageArtifact(image,"filter:support");
688 if (artifact != (const char *) NULL)
689 resize_filter->support = fabs(atof(artifact));
690
691 /* Scale windowing function separatally to the support 'clipping' window
692 that calling operator is planning to actually use. - Expert Use Only
693 */
694 resize_filter->window_support = resize_filter->support;
695 artifact=GetImageArtifact(image,"filter:win-support");
696 if (artifact != (const char *) NULL)
697 resize_filter->window_support = fabs(atof(artifact));
698
699 /* Set Cubic Spline B,C values, calculate Cubic coefficents */
700 B=0.0;
701 C=0.0;
702 if ( filters[filter_type].function == CubicBC
703 || filters[window_type].function == CubicBC ) {
704 if ( filters[filter_type].function == CubicBC ) {
705 B=filters[filter_type].B;
706 C=filters[filter_type].C;
707 }
708 else if ( filters[window_type].function == CubicBC ) {
709 B=filters[window_type].B;
710 C=filters[window_type].C;
711 }
712 artifact=GetImageArtifact(image,"filter:b");
713 if (artifact != (const char *) NULL) {
714 B=atof(artifact);
715 C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter */
716 artifact=GetImageArtifact(image,"filter:c");
717 if (artifact != (const char *) NULL)
718 C=atof(artifact);
719 }
720 else {
721 artifact=GetImageArtifact(image,"filter:c");
722 if (artifact != (const char *) NULL) {
723 C=atof(artifact);
724 B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter */
725 }
726 }
727 /* Convert B,C values into Cubic Coefficents - See CubicBC() */
728 resize_filter->cubic[0]=( 6.0 -2.0*B )/6.0;
729 resize_filter->cubic[1]=0.0;
730 resize_filter->cubic[2]=(-18.0+12.0*B+ 6.0*C)/6.0;
731 resize_filter->cubic[3]=( 12.0- 9.0*B- 6.0*C)/6.0;
732 resize_filter->cubic[4]=( 8.0*B+24.0*C)/6.0;
733 resize_filter->cubic[5]=( -12.0*B-48.0*C)/6.0;
734 resize_filter->cubic[6]=( 6.0*B+30.0*C)/6.0;
735 resize_filter->cubic[7]=( - 1.0*B- 6.0*C)/6.0;
736 }
737 artifact=GetImageArtifact(image,"filter:verbose");
738 if (artifact != (const char *) NULL)
739 {
740 double
741 support,
742 x;
743
744 /*
745 Output filter graph -- for graphing filter result.
746 */
747 support=GetResizeFilterSupport(resize_filter);
748 (void) printf("# support = %lg\n",support);
749 for (x=0.0; x <= support; x+=0.01f)
cristy4f3c0be2009-09-12 16:04:05 +0000750 (void) printf("%5.2lf\t%lf\n",x,(double) GetResizeFilterWeight(
751 resize_filter,x));
cristy3ed852e2009-09-05 21:47:34 +0000752 (void) printf("%5.2lf\t%lf\n",support,0.0);
753 }
754 return(resize_filter);
755}
756
757/*
758%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
759% %
760% %
761% %
762% A d a p t i v e R e s i z e I m a g e %
763% %
764% %
765% %
766%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
767%
768% AdaptiveResizeImage() adaptively resize image with pixel resampling.
769%
770% The format of the AdaptiveResizeImage method is:
771%
772% Image *AdaptiveResizeImage(const Image *image,
773% const unsigned long columns,const unsigned long rows,
774% ExceptionInfo *exception)
775%
776% A description of each parameter follows:
777%
778% o image: the image.
779%
780% o columns: the number of columns in the resized image.
781%
782% o rows: the number of rows in the resized image.
783%
784% o exception: return any errors or warnings in this structure.
785%
786*/
787MagickExport Image *AdaptiveResizeImage(const Image *image,
788 const unsigned long columns,const unsigned long rows,ExceptionInfo *exception)
789{
790#define AdaptiveResizeImageTag "Resize/Image"
791
792 Image
793 *resize_image;
794
795 long
796 y;
797
798 MagickBooleanType
799 proceed;
800
801 MagickPixelPacket
802 pixel;
803
804 PointInfo
805 offset;
806
807 ResampleFilter
808 *resample_filter;
809
810 CacheView
811 *resize_view;
812
813 /*
814 Adaptively resize image.
815 */
816 assert(image != (const Image *) NULL);
817 assert(image->signature == MagickSignature);
818 if (image->debug != MagickFalse)
819 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
820 assert(exception != (ExceptionInfo *) NULL);
821 assert(exception->signature == MagickSignature);
822 if ((columns == 0) || (rows == 0))
823 return((Image *) NULL);
824 if ((columns == image->columns) && (rows == image->rows))
825 return(CloneImage(image,0,0,MagickTrue,exception));
826 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
827 if (resize_image == (Image *) NULL)
828 return((Image *) NULL);
829 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
830 {
831 InheritException(exception,&resize_image->exception);
832 resize_image=DestroyImage(resize_image);
833 return((Image *) NULL);
834 }
835 GetMagickPixelPacket(image,&pixel);
836 resample_filter=AcquireResampleFilter(image,exception);
837 if (image->interpolate == UndefinedInterpolatePixel)
838 (void) SetResampleFilterInterpolateMethod(resample_filter,
839 MeshInterpolatePixel);
840 resize_view=AcquireCacheView(resize_image);
841 for (y=0; y < (long) resize_image->rows; y++)
842 {
843 register IndexPacket
844 *__restrict resize_indexes;
845
846 register long
847 x;
848
849 register PixelPacket
850 *__restrict q;
851
852 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
853 exception);
854 if (q == (PixelPacket *) NULL)
855 break;
856 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
857 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
858 for (x=0; x < (long) resize_image->columns; x++)
859 {
860 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
861 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
862 &pixel);
863 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
864 q++;
865 }
866 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
867 break;
868 proceed=SetImageProgress(image,AdaptiveResizeImageTag,y,image->rows);
869 if (proceed == MagickFalse)
870 break;
871 }
872 resample_filter=DestroyResampleFilter(resample_filter);
873 resize_view=DestroyCacheView(resize_view);
874 return(resize_image);
875}
876
877/*
878%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
879% %
880% %
881% %
882+ B e s s e l O r d e r O n e %
883% %
884% %
885% %
886%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
887%
888% BesselOrderOne() computes the Bessel function of x of the first kind of
889% order 0:
890%
891% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
892%
893% j1(x) = x*j1(x);
894%
895% For x in (8,inf)
896%
897% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
898%
899% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
900%
901% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
902% = 1/sqrt(2) * (sin(x) - cos(x))
903% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
904% = -1/sqrt(2) * (sin(x) + cos(x))
905%
906% The format of the BesselOrderOne method is:
907%
908% MagickRealType BesselOrderOne(MagickRealType x)
909%
910% A description of each parameter follows:
911%
912% o x: MagickRealType value.
913%
914*/
915
916#undef I0
917static MagickRealType I0(MagickRealType x)
918{
919 MagickRealType
920 sum,
921 t,
922 y;
923
924 register long
925 i;
926
927 /*
928 Zeroth order Bessel function of the first kind.
929 */
930 sum=1.0;
931 y=x*x/4.0;
932 t=y;
933 for (i=2; t > MagickEpsilon; i++)
934 {
935 sum+=t;
936 t*=y/((MagickRealType) i*i);
937 }
938 return(sum);
939}
940
941#undef J1
942static MagickRealType J1(MagickRealType x)
943{
944 MagickRealType
945 p,
946 q;
947
948 register long
949 i;
950
951 static const double
952 Pone[] =
953 {
954 0.581199354001606143928050809e+21,
955 -0.6672106568924916298020941484e+20,
956 0.2316433580634002297931815435e+19,
957 -0.3588817569910106050743641413e+17,
958 0.2908795263834775409737601689e+15,
959 -0.1322983480332126453125473247e+13,
960 0.3413234182301700539091292655e+10,
961 -0.4695753530642995859767162166e+7,
962 0.270112271089232341485679099e+4
963 },
964 Qone[] =
965 {
966 0.11623987080032122878585294e+22,
967 0.1185770712190320999837113348e+20,
968 0.6092061398917521746105196863e+17,
969 0.2081661221307607351240184229e+15,
970 0.5243710262167649715406728642e+12,
971 0.1013863514358673989967045588e+10,
972 0.1501793594998585505921097578e+7,
973 0.1606931573481487801970916749e+4,
974 0.1e+1
975 };
976
977 p=Pone[8];
978 q=Qone[8];
979 for (i=7; i >= 0; i--)
980 {
981 p=p*x*x+Pone[i];
982 q=q*x*x+Qone[i];
983 }
984 return(p/q);
985}
986
987#undef P1
988static MagickRealType P1(MagickRealType x)
989{
990 MagickRealType
991 p,
992 q;
993
994 register long
995 i;
996
997 static const double
998 Pone[] =
999 {
1000 0.352246649133679798341724373e+5,
1001 0.62758845247161281269005675e+5,
1002 0.313539631109159574238669888e+5,
1003 0.49854832060594338434500455e+4,
1004 0.2111529182853962382105718e+3,
1005 0.12571716929145341558495e+1
1006 },
1007 Qone[] =
1008 {
1009 0.352246649133679798068390431e+5,
1010 0.626943469593560511888833731e+5,
1011 0.312404063819041039923015703e+5,
1012 0.4930396490181088979386097e+4,
1013 0.2030775189134759322293574e+3,
1014 0.1e+1
1015 };
1016
1017 p=Pone[5];
1018 q=Qone[5];
1019 for (i=4; i >= 0; i--)
1020 {
1021 p=p*(8.0/x)*(8.0/x)+Pone[i];
1022 q=q*(8.0/x)*(8.0/x)+Qone[i];
1023 }
1024 return(p/q);
1025}
1026
1027#undef Q1
1028static MagickRealType Q1(MagickRealType x)
1029{
1030 MagickRealType
1031 p,
1032 q;
1033
1034 register long
1035 i;
1036
1037 static const double
1038 Pone[] =
1039 {
1040 0.3511751914303552822533318e+3,
1041 0.7210391804904475039280863e+3,
1042 0.4259873011654442389886993e+3,
1043 0.831898957673850827325226e+2,
1044 0.45681716295512267064405e+1,
1045 0.3532840052740123642735e-1
1046 },
1047 Qone[] =
1048 {
1049 0.74917374171809127714519505e+4,
1050 0.154141773392650970499848051e+5,
1051 0.91522317015169922705904727e+4,
1052 0.18111867005523513506724158e+4,
1053 0.1038187585462133728776636e+3,
1054 0.1e+1
1055 };
1056
1057 p=Pone[5];
1058 q=Qone[5];
1059 for (i=4; i >= 0; i--)
1060 {
1061 p=p*(8.0/x)*(8.0/x)+Pone[i];
1062 q=q*(8.0/x)*(8.0/x)+Qone[i];
1063 }
1064 return(p/q);
1065}
1066
1067static MagickRealType BesselOrderOne(MagickRealType x)
1068{
1069 MagickRealType
1070 p,
1071 q;
1072
1073 if (x == 0.0)
1074 return(0.0);
1075 p=x;
1076 if (x < 0.0)
1077 x=(-x);
1078 if (x < 8.0)
1079 return(p*J1(x));
1080 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1081 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1082 cos((double) x))));
1083 if (p < 0.0)
1084 q=(-q);
1085 return(q);
1086}
1087
1088/*
1089%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1090% %
1091% %
1092% %
1093+ D e s t r o y R e s i z e F i l t e r %
1094% %
1095% %
1096% %
1097%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1098%
1099% DestroyResizeFilter() destroy the resize filter.
1100%
1101% The format of the AcquireResizeFilter method is:
1102%
1103% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1104%
1105% A description of each parameter follows:
1106%
1107% o resize_filter: the resize filter.
1108%
1109*/
1110MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1111{
1112 assert(resize_filter != (ResizeFilter *) NULL);
1113 assert(resize_filter->signature == MagickSignature);
1114 resize_filter->signature=(~MagickSignature);
1115 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1116 return(resize_filter);
1117}
1118
1119/*
1120%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1121% %
1122% %
1123% %
1124+ G e t R e s i z e F i l t e r S u p p o r t %
1125% %
1126% %
1127% %
1128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1129%
1130% GetResizeFilterSupport() return the current support window size for this
1131% filter. Note that this may have been enlarged by filter:blur factor.
1132%
1133% The format of the GetResizeFilterSupport method is:
1134%
1135% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1136%
1137% A description of each parameter follows:
1138%
1139% o filter: Image filter to use.
1140%
1141*/
1142MagickExport MagickRealType GetResizeFilterSupport(
1143 const ResizeFilter *resize_filter)
1144{
1145 assert(resize_filter != (ResizeFilter *) NULL);
1146 assert(resize_filter->signature == MagickSignature);
1147 return(resize_filter->support*resize_filter->blur);
1148}
1149
1150/*
1151%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1152% %
1153% %
1154% %
1155+ G e t R e s i z e F i l t e r W e i g h t %
1156% %
1157% %
1158% %
1159%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1160%
1161% GetResizeFilterWeight evaluates the specified resize filter at the point x
1162% which usally lies between zero and the filters current 'support' and
1163% returns the weight of the filter function at that point.
1164%
1165% The format of the GetResizeFilterWeight method is:
1166%
1167% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1168% const MagickRealType x)
1169%
1170% A description of each parameter follows:
1171%
1172% o filter: the filter type.
1173%
1174% o x: the point.
1175%
1176*/
1177MagickExport MagickRealType GetResizeFilterWeight(
1178 const ResizeFilter *resize_filter,const MagickRealType x)
1179{
1180 MagickRealType
1181 blur,
1182 scale;
1183
1184 /*
1185 Windowing function - scale the weighting filter by this amount.
1186 */
1187 assert(resize_filter != (ResizeFilter *) NULL);
1188 assert(resize_filter->signature == MagickSignature);
1189 blur=fabs(x)/resize_filter->blur; /* X offset with blur scaling */
1190 if ((resize_filter->window_support < MagickEpsilon) ||
1191 (resize_filter->window == Box))
1192 scale=1.0; /* Point/Box Filter -- avoid division by zero */
1193 else
1194 {
1195 scale=resize_filter->scale/resize_filter->window_support;
1196 scale=resize_filter->window(blur*scale,resize_filter);
1197 }
1198 return(scale*resize_filter->filter(blur,resize_filter));
1199}
1200
1201/*
1202%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1203% %
1204% %
1205% %
1206% M a g n i f y I m a g e %
1207% %
1208% %
1209% %
1210%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1211%
1212% MagnifyImage() is a convenience method that scales an image proportionally
1213% to twice its size.
1214%
1215% The format of the MagnifyImage method is:
1216%
1217% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1218%
1219% A description of each parameter follows:
1220%
1221% o image: the image.
1222%
1223% o exception: return any errors or warnings in this structure.
1224%
1225*/
1226MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1227{
1228 Image
1229 *magnify_image;
1230
1231 assert(image != (Image *) NULL);
1232 assert(image->signature == MagickSignature);
1233 if (image->debug != MagickFalse)
1234 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1235 assert(exception != (ExceptionInfo *) NULL);
1236 assert(exception->signature == MagickSignature);
1237 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1238 1.0,exception);
1239 return(magnify_image);
1240}
1241
1242/*
1243%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1244% %
1245% %
1246% %
1247% M i n i f y I m a g e %
1248% %
1249% %
1250% %
1251%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1252%
1253% MinifyImage() is a convenience method that scales an image proportionally
1254% to half its size.
1255%
1256% The format of the MinifyImage method is:
1257%
1258% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1259%
1260% A description of each parameter follows:
1261%
1262% o image: the image.
1263%
1264% o exception: return any errors or warnings in this structure.
1265%
1266*/
1267MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1268{
1269 Image
1270 *minify_image;
1271
1272 assert(image != (Image *) NULL);
1273 assert(image->signature == MagickSignature);
1274 if (image->debug != MagickFalse)
1275 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1276 assert(exception != (ExceptionInfo *) NULL);
1277 assert(exception->signature == MagickSignature);
1278 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1279 1.0,exception);
1280 return(minify_image);
1281}
1282
1283/*
1284%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1285% %
1286% %
1287% %
1288% R e s a m p l e I m a g e %
1289% %
1290% %
1291% %
1292%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1293%
1294% ResampleImage() resize image in terms of its pixel size, so that when
1295% displayed at the given resolution it will be the same size in terms of
1296% real world units as the original image at the original resolution.
1297%
1298% The format of the ResampleImage method is:
1299%
1300% Image *ResampleImage(Image *image,const double x_resolution,
1301% const double y_resolution,const FilterTypes filter,const double blur,
1302% ExceptionInfo *exception)
1303%
1304% A description of each parameter follows:
1305%
1306% o image: the image to be resized to fit the given resolution.
1307%
1308% o x_resolution: the new image x resolution.
1309%
1310% o y_resolution: the new image y resolution.
1311%
1312% o filter: Image filter to use.
1313%
1314% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1315%
1316*/
1317MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1318 const double y_resolution,const FilterTypes filter,const double blur,
1319 ExceptionInfo *exception)
1320{
1321#define ResampleImageTag "Resample/Image"
1322
1323 Image
1324 *resample_image;
1325
1326 unsigned long
1327 height,
1328 width;
1329
1330 /*
1331 Initialize sampled image attributes.
1332 */
1333 assert(image != (const Image *) NULL);
1334 assert(image->signature == MagickSignature);
1335 if (image->debug != MagickFalse)
1336 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1337 assert(exception != (ExceptionInfo *) NULL);
1338 assert(exception->signature == MagickSignature);
1339 width=(unsigned long) (x_resolution*image->columns/
1340 (image->x_resolution == 0.0 ? 72.0 : image->x_resolution)+0.5);
1341 height=(unsigned long) (y_resolution*image->rows/
1342 (image->y_resolution == 0.0 ? 72.0 : image->y_resolution)+0.5);
1343 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1344 if (resample_image != (Image *) NULL)
1345 {
1346 resample_image->x_resolution=x_resolution;
1347 resample_image->y_resolution=y_resolution;
1348 }
1349 return(resample_image);
1350}
1351#if defined(MAGICKCORE_LQR_DELEGATE)
1352
1353/*
1354%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1355% %
1356% %
1357% %
1358% L i q u i d R e s c a l e I m a g e %
1359% %
1360% %
1361% %
1362%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1363%
1364% LiquidRescaleImage() rescales image with seam carving.
1365%
1366% The format of the LiquidRescaleImage method is:
1367%
1368% Image *LiquidRescaleImage(const Image *image,
1369% const unsigned long columns,const unsigned long rows,
1370% const double delta_x,const double rigidity,ExceptionInfo *exception)
1371%
1372% A description of each parameter follows:
1373%
1374% o image: the image.
1375%
1376% o columns: the number of columns in the rescaled image.
1377%
1378% o rows: the number of rows in the rescaled image.
1379%
1380% o delta_x: maximum seam transversal step (0 means straight seams).
1381%
1382% o rigidity: introduce a bias for non-straight seams (typically 0).
1383%
1384% o exception: return any errors or warnings in this structure.
1385%
1386*/
1387MagickExport Image *LiquidRescaleImage(const Image *image,
1388 const unsigned long columns,const unsigned long rows,
1389 const double delta_x,const double rigidity,ExceptionInfo *exception)
1390{
1391#define LiquidRescaleImageTag "Rescale/Image"
1392
1393 const char
1394 *map;
1395
1396 guchar
1397 *packet;
1398
1399 Image
1400 *rescale_image;
1401
1402 int
1403 x,
1404 y;
1405
1406 LqrCarver
1407 *carver;
1408
1409 LqrRetVal
1410 lqr_status;
1411
1412 MagickBooleanType
1413 status;
1414
1415 MagickPixelPacket
1416 pixel;
1417
1418 unsigned char
1419 *pixels;
1420
1421 /*
1422 Liquid rescale image.
1423 */
1424 assert(image != (const Image *) NULL);
1425 assert(image->signature == MagickSignature);
1426 if (image->debug != MagickFalse)
1427 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1428 assert(exception != (ExceptionInfo *) NULL);
1429 assert(exception->signature == MagickSignature);
1430 if ((columns == 0) || (rows == 0))
1431 return((Image *) NULL);
1432 if ((columns == image->columns) && (rows == image->rows))
1433 return(CloneImage(image,0,0,MagickTrue,exception));
1434 if ((columns <= 2) || (rows <= 2))
1435 return(ZoomImage(image,columns,rows,exception));
1436 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1437 {
1438 Image
1439 *resize_image;
1440
1441 unsigned long
1442 height,
1443 width;
1444
1445 /*
1446 Honor liquid resize size limitations.
1447 */
1448 for (width=image->columns; columns >= (2*width-1); width*=2);
1449 for (height=image->rows; rows >= (2*height-1); height*=2);
1450 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1451 exception);
1452 if (resize_image == (Image *) NULL)
1453 return((Image *) NULL);
1454 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1455 rigidity,exception);
1456 resize_image=DestroyImage(resize_image);
1457 return(rescale_image);
1458 }
1459 map="RGB";
1460 if (image->matte == MagickFalse)
1461 map="RGBA";
1462 if (image->colorspace == CMYKColorspace)
1463 {
1464 map="CMYK";
1465 if (image->matte == MagickFalse)
1466 map="CMYKA";
1467 }
1468 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1469 strlen(map)*sizeof(*pixels));
1470 if (pixels == (unsigned char *) NULL)
1471 return((Image *) NULL);
1472 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1473 pixels,exception);
1474 if (status == MagickFalse)
1475 {
1476 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1477 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1478 }
1479 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1480 if (carver == (LqrCarver *) NULL)
1481 {
1482 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1483 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1484 }
1485 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1486 lqr_status=lqr_carver_resize(carver,columns,rows);
1487 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1488 lqr_carver_get_height(carver),MagickTrue,exception);
1489 if (rescale_image == (Image *) NULL)
1490 {
1491 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1492 return((Image *) NULL);
1493 }
1494 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1495 {
1496 InheritException(exception,&rescale_image->exception);
1497 rescale_image=DestroyImage(rescale_image);
1498 return((Image *) NULL);
1499 }
1500 GetMagickPixelPacket(rescale_image,&pixel);
1501 (void) lqr_carver_scan_reset(carver);
1502 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1503 {
1504 register IndexPacket
1505 *__restrict rescale_indexes;
1506
1507 register PixelPacket
1508 *__restrict q;
1509
1510 q=QueueAuthenticPixels(rescale_image,x,y,1,1,exception);
1511 if (q == (PixelPacket *) NULL)
1512 break;
1513 rescale_indexes=GetAuthenticIndexQueue(rescale_image);
1514 pixel.red=QuantumRange*(packet[0]/255.0);
1515 pixel.green=QuantumRange*(packet[1]/255.0);
1516 pixel.blue=QuantumRange*(packet[2]/255.0);
1517 if (image->colorspace != CMYKColorspace)
1518 {
1519 if (image->matte == MagickFalse)
1520 pixel.opacity=QuantumRange*(packet[3]/255.0);
1521 }
1522 else
1523 {
1524 pixel.index=QuantumRange*(packet[3]/255.0);
1525 if (image->matte == MagickFalse)
1526 pixel.opacity=QuantumRange*(packet[4]/255.0);
1527 }
1528 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
1529 if (SyncAuthenticPixels(rescale_image,exception) == MagickFalse)
1530 break;
1531 }
1532 /*
1533 Relinquish resources.
1534 */
1535 lqr_carver_destroy(carver);
1536 return(rescale_image);
1537}
1538#else
1539MagickExport Image *LiquidRescaleImage(const Image *image,
1540 const unsigned long magick_unused(columns),
1541 const unsigned long magick_unused(rows),const double magick_unused(delta_x),
1542 const double magick_unused(rigidity),ExceptionInfo *exception)
1543{
1544 assert(image != (const Image *) NULL);
1545 assert(image->signature == MagickSignature);
1546 if (image->debug != MagickFalse)
1547 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1548 assert(exception != (ExceptionInfo *) NULL);
1549 assert(exception->signature == MagickSignature);
1550 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1551 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1552 return((Image *) NULL);
1553}
1554#endif
1555
1556/*
1557%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1558% %
1559% %
1560% %
1561% R e s i z e I m a g e %
1562% %
1563% %
1564% %
1565%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1566%
1567% ResizeImage() scales an image to the desired dimensions, using the given
1568% filter (see AcquireFilterInfo() ).
1569%
1570% If an undefined filter is given the filter defaults to Mitchell for a
1571% colormapped image, a image with a matte channel, or if the image is
1572% enlarged. Otherwise the filter defaults to a Lanczos.
1573%
1574% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1575%
1576% The format of the ResizeImage method is:
1577%
1578% Image *ResizeImage(Image *image,const unsigned long columns,
1579% const unsigned long rows,const FilterTypes filter,const double blur,
1580% ExceptionInfo *exception)
1581%
1582% A description of each parameter follows:
1583%
1584% o image: the image.
1585%
1586% o columns: the number of columns in the scaled image.
1587%
1588% o rows: the number of rows in the scaled image.
1589%
1590% o filter: Image filter to use.
1591%
1592% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1593% Typically set this to 1.0.
1594%
1595% o exception: return any errors or warnings in this structure.
1596%
1597*/
1598
1599typedef struct _ContributionInfo
1600{
1601 MagickRealType
1602 weight;
1603
1604 long
1605 pixel;
1606} ContributionInfo;
1607
1608static ContributionInfo **DestroyContributionThreadSet(
1609 ContributionInfo **contribution)
1610{
1611 register long
1612 i;
1613
1614 assert(contribution != (ContributionInfo **) NULL);
1615 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
1616 if (contribution[i] != (ContributionInfo *) NULL)
1617 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1618 contribution[i]);
1619 contribution=(ContributionInfo **) RelinquishAlignedMemory(contribution);
1620 return(contribution);
1621}
1622
1623static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1624{
1625 register long
1626 i;
1627
1628 ContributionInfo
1629 **contribution;
1630
1631 unsigned long
1632 number_threads;
1633
1634 number_threads=GetOpenMPMaximumThreads();
1635 contribution=(ContributionInfo **) AcquireAlignedMemory(number_threads,
1636 sizeof(*contribution));
1637 if (contribution == (ContributionInfo **) NULL)
1638 return((ContributionInfo **) NULL);
1639 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
1640 for (i=0; i < (long) number_threads; i++)
1641 {
1642 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1643 sizeof(**contribution));
1644 if (contribution[i] == (ContributionInfo *) NULL)
1645 return(DestroyContributionThreadSet(contribution));
1646 }
1647 return(contribution);
1648}
1649
1650static inline double MagickMax(const double x,const double y)
1651{
1652 if (x > y)
1653 return(x);
1654 return(y);
1655}
1656
1657static inline double MagickMin(const double x,const double y)
1658{
1659 if (x < y)
1660 return(x);
1661 return(y);
1662}
1663
1664static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
1665 const Image *image,Image *resize_image,const MagickRealType x_factor,
1666 const MagickSizeType span,MagickOffsetType *quantum,ExceptionInfo *exception)
1667{
1668#define ResizeImageTag "Resize/Image"
1669
1670 ClassType
1671 storage_class;
1672
1673 ContributionInfo
1674 **contributions;
1675
1676 long
1677 x;
1678
1679 MagickBooleanType
1680 status;
1681
1682 MagickPixelPacket
1683 zero;
1684
1685 MagickRealType
1686 scale,
1687 support;
1688
1689 CacheView
1690 *image_view,
1691 *resize_view;
1692
1693 /*
1694 Apply filter to resize horizontally from image to resize image.
1695 */
1696 scale=MagickMax(1.0/x_factor,1.0);
1697 support=scale*GetResizeFilterSupport(resize_filter);
1698 storage_class=support > 0.5 ? DirectClass : image->storage_class;
1699 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
1700 {
1701 InheritException(exception,&resize_image->exception);
1702 return(MagickFalse);
1703 }
1704 if (support < 0.5)
1705 {
1706 /*
1707 Support too small even for nearest neighbour: reduce to point sampling.
1708 */
1709 support=(MagickRealType) 0.5;
1710 scale=1.0;
1711 }
1712 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
1713 if (contributions == (ContributionInfo **) NULL)
1714 {
1715 (void) ThrowMagickException(exception,GetMagickModule(),
1716 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
1717 return(MagickFalse);
1718 }
1719 status=MagickTrue;
1720 scale=1.0/scale;
1721 (void) ResetMagickMemory(&zero,0,sizeof(zero));
1722 image_view=AcquireCacheView(image);
1723 resize_view=AcquireCacheView(resize_image);
cristy629a6c72009-09-13 23:28:22 +00001724#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy3ed852e2009-09-05 21:47:34 +00001725 #pragma omp parallel for shared(status)
1726#endif
1727 for (x=0; x < (long) resize_image->columns; x++)
1728 {
1729 long
1730 n,
1731 start,
1732 stop;
1733
1734 MagickRealType
1735 center,
1736 density;
1737
1738 register const IndexPacket
1739 *__restrict indexes;
1740
1741 register const PixelPacket
1742 *__restrict p;
1743
1744 register ContributionInfo
1745 *__restrict contribution;
1746
1747 register IndexPacket
1748 *__restrict resize_indexes;
1749
1750 register long
1751 y;
1752
1753 register PixelPacket
1754 *__restrict q;
1755
1756 if (status == MagickFalse)
1757 continue;
1758 center=(MagickRealType) (x+0.5)/x_factor;
1759 start=(long) (MagickMax(center-support-MagickEpsilon,0.0)+0.5);
1760 stop=(long) (MagickMin(center+support,(double) image->columns)+0.5);
1761 density=0.0;
1762 contribution=contributions[GetOpenMPThreadId()];
1763 for (n=0; n < (stop-start); n++)
1764 {
1765 contribution[n].pixel=start+n;
1766 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
1767 ((MagickRealType) (start+n)-center+0.5));
1768 density+=contribution[n].weight;
1769 }
1770 if ((density != 0.0) && (density != 1.0))
1771 {
1772 register long
1773 i;
1774
1775 /*
1776 Normalize.
1777 */
1778 density=1.0/density;
1779 for (i=0; i < n; i++)
1780 contribution[i].weight*=density;
1781 }
1782 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,
1783 (unsigned long) (contribution[n-1].pixel-contribution[0].pixel+1),
1784 image->rows,exception);
1785 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
1786 exception);
1787 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1788 {
1789 status=MagickFalse;
1790 continue;
1791 }
1792 indexes=GetCacheViewVirtualIndexQueue(image_view);
1793 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1794 for (y=0; y < (long) resize_image->rows; y++)
1795 {
1796 long
1797 j;
1798
1799 MagickPixelPacket
1800 pixel;
1801
1802 MagickRealType
1803 alpha;
1804
1805 register long
1806 i;
1807
1808 pixel=zero;
1809 if (image->matte == MagickFalse)
1810 {
1811 for (i=0; i < n; i++)
1812 {
1813 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
1814 (contribution[i].pixel-contribution[0].pixel);
1815 alpha=contribution[i].weight;
1816 pixel.red+=alpha*(p+j)->red;
1817 pixel.green+=alpha*(p+j)->green;
1818 pixel.blue+=alpha*(p+j)->blue;
1819 pixel.opacity+=alpha*(p+j)->opacity;
1820 }
1821 q->red=RoundToQuantum(pixel.red);
1822 q->green=RoundToQuantum(pixel.green);
1823 q->blue=RoundToQuantum(pixel.blue);
1824 q->opacity=RoundToQuantum(pixel.opacity);
1825 if ((image->colorspace == CMYKColorspace) &&
1826 (resize_image->colorspace == CMYKColorspace))
1827 {
1828 for (i=0; i < n; i++)
1829 {
1830 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
1831 (contribution[i].pixel-contribution[0].pixel);
1832 alpha=contribution[i].weight;
1833 pixel.index+=alpha*indexes[j];
1834 }
1835 resize_indexes[y]=(IndexPacket) RoundToQuantum(pixel.index);
1836 }
1837 }
1838 else
1839 {
1840 MagickRealType
1841 gamma;
1842
1843 gamma=0.0;
1844 for (i=0; i < n; i++)
1845 {
1846 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
1847 (contribution[i].pixel-contribution[0].pixel);
1848 alpha=contribution[i].weight*QuantumScale*((MagickRealType)
1849 QuantumRange-(p+j)->opacity);
1850 pixel.red+=alpha*(p+j)->red;
1851 pixel.green+=alpha*(p+j)->green;
1852 pixel.blue+=alpha*(p+j)->blue;
1853 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
1854 gamma+=alpha;
1855 }
1856 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1857 q->red=RoundToQuantum(gamma*pixel.red);
1858 q->green=RoundToQuantum(gamma*pixel.green);
1859 q->blue=RoundToQuantum(gamma*pixel.blue);
1860 q->opacity=RoundToQuantum(pixel.opacity);
1861 if ((image->colorspace == CMYKColorspace) &&
1862 (resize_image->colorspace == CMYKColorspace))
1863 {
1864 for (i=0; i < n; i++)
1865 {
1866 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
1867 (contribution[i].pixel-contribution[0].pixel);
1868 alpha=contribution[i].weight*QuantumScale*((MagickRealType)
1869 QuantumRange-(p+j)->opacity);
cristyc197d1c2009-10-13 19:56:53 +00001870 pixel.index+=alpha*indexes[j];
cristy3ed852e2009-09-05 21:47:34 +00001871 }
1872 resize_indexes[y]=(IndexPacket) RoundToQuantum(gamma*pixel.index);
1873 }
1874 }
1875 if ((resize_image->storage_class == PseudoClass) &&
1876 (image->storage_class == PseudoClass))
1877 {
1878 i=(long) (MagickMin(MagickMax(center,(double) start),(double) stop-
1879 1.0)+0.5);
1880 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
1881 (contribution[i-start].pixel-contribution[0].pixel);
1882 resize_indexes[y]=indexes[j];
1883 }
1884 q++;
1885 }
1886 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1887 status=MagickFalse;
1888 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1889 {
1890 MagickBooleanType
1891 proceed;
1892
cristy629a6c72009-09-13 23:28:22 +00001893#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy3ed852e2009-09-05 21:47:34 +00001894 #pragma omp critical (MagickCore_HorizontalFilter)
1895#endif
1896 proceed=SetImageProgress(image,ResizeImageTag,(*quantum)++,span);
1897 if (proceed == MagickFalse)
1898 status=MagickFalse;
1899 }
1900 }
1901 resize_view=DestroyCacheView(resize_view);
1902 image_view=DestroyCacheView(image_view);
1903 contributions=DestroyContributionThreadSet(contributions);
1904 return(status);
1905}
1906
1907static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
1908 const Image *image,Image *resize_image,const MagickRealType y_factor,
1909 const MagickSizeType span,MagickOffsetType *quantum,ExceptionInfo *exception)
1910{
1911 ClassType
1912 storage_class;
1913
1914 ContributionInfo
1915 **contributions;
1916
1917 long
1918 y;
1919
1920 MagickBooleanType
1921 status;
1922
1923 MagickPixelPacket
1924 zero;
1925
1926 MagickRealType
1927 scale,
1928 support;
1929
1930 CacheView
1931 *image_view,
1932 *resize_view;
1933
1934 /*
1935 Apply filter to resize vertically from image to resize_image.
1936 */
1937 scale=MagickMax(1.0/y_factor,1.0);
1938 support=scale*GetResizeFilterSupport(resize_filter);
1939 storage_class=support > 0.5 ? DirectClass : image->storage_class;
1940 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
1941 {
1942 InheritException(exception,&resize_image->exception);
1943 return(MagickFalse);
1944 }
1945 if (support < 0.5)
1946 {
1947 /*
1948 Support too small even for nearest neighbour: reduce to point sampling.
1949 */
1950 support=(MagickRealType) 0.5;
1951 scale=1.0;
1952 }
1953 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
1954 if (contributions == (ContributionInfo **) NULL)
1955 {
1956 (void) ThrowMagickException(exception,GetMagickModule(),
1957 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
1958 return(MagickFalse);
1959 }
1960 status=MagickTrue;
1961 scale=1.0/scale;
1962 (void) ResetMagickMemory(&zero,0,sizeof(zero));
1963 image_view=AcquireCacheView(image);
1964 resize_view=AcquireCacheView(resize_image);
cristy629a6c72009-09-13 23:28:22 +00001965#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy3ed852e2009-09-05 21:47:34 +00001966 #pragma omp parallel for shared(status)
1967#endif
1968 for (y=0; y < (long) resize_image->rows; y++)
1969 {
1970 long
1971 n,
1972 start,
1973 stop;
1974
1975 MagickRealType
1976 center,
1977 density;
1978
1979 register const IndexPacket
1980 *__restrict indexes;
1981
1982 register const PixelPacket
1983 *__restrict p;
1984
1985 register ContributionInfo
1986 *__restrict contribution;
1987
1988 register IndexPacket
1989 *__restrict resize_indexes;
1990
1991 register long
1992 x;
1993
1994 register PixelPacket
1995 *__restrict q;
1996
1997 if (status == MagickFalse)
1998 continue;
1999 center=(MagickRealType) (y+0.5)/y_factor;
2000 start=(long) (MagickMax(center-support-MagickEpsilon,0.0)+0.5);
2001 stop=(long) (MagickMin(center+support,(double) image->rows)+0.5);
2002 density=0.0;
2003 contribution=contributions[GetOpenMPThreadId()];
2004 for (n=0; n < (stop-start); n++)
2005 {
2006 contribution[n].pixel=start+n;
2007 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2008 ((MagickRealType) (start+n)-center+0.5));
2009 density+=contribution[n].weight;
2010 }
2011 if ((density != 0.0) && (density != 1.0))
2012 {
2013 register long
2014 i;
2015
2016 /*
2017 Normalize.
2018 */
2019 density=1.0/density;
2020 for (i=0; i < n; i++)
2021 contribution[i].weight*=density;
2022 }
2023 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
2024 image->columns,(unsigned long) (contribution[n-1].pixel-
2025 contribution[0].pixel+1),exception);
2026 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2027 exception);
2028 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2029 {
2030 status=MagickFalse;
2031 continue;
2032 }
2033 indexes=GetCacheViewVirtualIndexQueue(image_view);
2034 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2035 for (x=0; x < (long) resize_image->columns; x++)
2036 {
2037 long
2038 j;
2039
2040 MagickPixelPacket
2041 pixel;
2042
2043 MagickRealType
2044 alpha;
2045
2046 register long
2047 i;
2048
2049 pixel=zero;
2050 if (image->matte == MagickFalse)
2051 {
2052 for (i=0; i < n; i++)
2053 {
2054 j=(long) ((contribution[i].pixel-contribution[0].pixel)*
2055 image->columns+x);
2056 alpha=contribution[i].weight;
2057 pixel.red+=alpha*(p+j)->red;
2058 pixel.green+=alpha*(p+j)->green;
2059 pixel.blue+=alpha*(p+j)->blue;
2060 pixel.opacity+=alpha*(p+j)->opacity;
2061 }
2062 q->red=RoundToQuantum(pixel.red);
2063 q->green=RoundToQuantum(pixel.green);
2064 q->blue=RoundToQuantum(pixel.blue);
2065 q->opacity=RoundToQuantum(pixel.opacity);
2066 if ((image->colorspace == CMYKColorspace) &&
2067 (resize_image->colorspace == CMYKColorspace))
2068 {
2069 for (i=0; i < n; i++)
2070 {
2071 j=(long) ((contribution[i].pixel-contribution[0].pixel)*
2072 image->columns+x);
2073 alpha=contribution[i].weight;
2074 pixel.index+=alpha*indexes[j];
2075 }
2076 resize_indexes[x]=(IndexPacket) RoundToQuantum(pixel.index);
2077 }
2078 }
2079 else
2080 {
2081 MagickRealType
2082 gamma;
2083
2084 gamma=0.0;
2085 for (i=0; i < n; i++)
2086 {
2087 j=(long) ((contribution[i].pixel-contribution[0].pixel)*
2088 image->columns+x);
2089 alpha=contribution[i].weight*QuantumScale*((MagickRealType)
2090 QuantumRange-(p+j)->opacity);
2091 pixel.red+=alpha*(p+j)->red;
2092 pixel.green+=alpha*(p+j)->green;
2093 pixel.blue+=alpha*(p+j)->blue;
2094 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2095 gamma+=alpha;
2096 }
2097 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2098 q->red=RoundToQuantum(gamma*pixel.red);
2099 q->green=RoundToQuantum(gamma*pixel.green);
2100 q->blue=RoundToQuantum(gamma*pixel.blue);
2101 q->opacity=RoundToQuantum(pixel.opacity);
2102 if ((image->colorspace == CMYKColorspace) &&
2103 (resize_image->colorspace == CMYKColorspace))
2104 {
2105 for (i=0; i < n; i++)
2106 {
2107 j=(long) ((contribution[i].pixel-contribution[0].pixel)*
2108 image->columns+x);
2109 alpha=contribution[i].weight*QuantumScale*((MagickRealType)
2110 QuantumRange-(p+j)->opacity);
2111 pixel.index+=alpha*indexes[j];
2112 }
2113 resize_indexes[x]=(IndexPacket) RoundToQuantum(gamma*pixel.index);
2114 }
2115 }
2116 if ((resize_image->storage_class == PseudoClass) &&
2117 (image->storage_class == PseudoClass))
2118 {
2119 i=(long) (MagickMin(MagickMax(center,(double) start),(double) stop-
2120 1.0)+0.5);
2121 j=(long) ((contribution[i-start].pixel-contribution[0].pixel)*
2122 image->columns+x);
2123 resize_indexes[x]=indexes[j];
2124 }
2125 q++;
2126 }
2127 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2128 status=MagickFalse;
2129 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2130 {
2131 MagickBooleanType
2132 proceed;
2133
cristy629a6c72009-09-13 23:28:22 +00002134#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy3ed852e2009-09-05 21:47:34 +00002135 #pragma omp critical (MagickCore_VerticalFilter)
2136#endif
2137 proceed=SetImageProgress(image,ResizeImageTag,(*quantum)++,span);
2138 if (proceed == MagickFalse)
2139 status=MagickFalse;
2140 }
2141 }
2142 resize_view=DestroyCacheView(resize_view);
2143 image_view=DestroyCacheView(image_view);
2144 contributions=DestroyContributionThreadSet(contributions);
2145 return(status);
2146}
2147
2148MagickExport Image *ResizeImage(const Image *image,const unsigned long columns,
2149 const unsigned long rows,const FilterTypes filter,const double blur,
2150 ExceptionInfo *exception)
2151{
2152#define WorkLoadFactor 0.265
2153
2154 FilterTypes
2155 filter_type;
2156
2157 Image
2158 *filter_image,
2159 *resize_image;
2160
2161 MagickRealType
2162 x_factor,
2163 y_factor;
2164
2165 MagickSizeType
2166 span;
2167
2168 MagickStatusType
2169 status;
2170
2171 ResizeFilter
2172 *resize_filter;
2173
2174 MagickOffsetType
2175 quantum;
2176
2177 /*
2178 Acquire resize image.
2179 */
2180 assert(image != (Image *) NULL);
2181 assert(image->signature == MagickSignature);
2182 if (image->debug != MagickFalse)
2183 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2184 assert(exception != (ExceptionInfo *) NULL);
2185 assert(exception->signature == MagickSignature);
2186 if ((columns == 0) || (rows == 0))
2187 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2188 if ((columns == image->columns) && (rows == image->rows) &&
2189 (filter == UndefinedFilter) && (blur == 1.0))
2190 return(CloneImage(image,0,0,MagickTrue,exception));
2191 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2192 if (resize_image == (Image *) NULL)
2193 return(resize_image);
2194 /*
2195 Acquire resize filter.
2196 */
2197 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2198 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2199 if ((x_factor*y_factor) > WorkLoadFactor)
2200 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2201 else
2202 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2203 if (filter_image == (Image *) NULL)
2204 return(DestroyImage(resize_image));
2205 filter_type=LanczosFilter;
2206 if (filter != UndefinedFilter)
2207 filter_type=filter;
2208 else
2209 if ((x_factor == 1.0) && (y_factor == 1.0))
2210 filter_type=PointFilter;
2211 else
2212 if ((image->storage_class == PseudoClass) ||
2213 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2214 filter_type=MitchellFilter;
2215 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2216 exception);
2217 /*
2218 Resize image.
2219 */
2220 quantum=0;
2221 if ((x_factor*y_factor) > WorkLoadFactor)
2222 {
2223 span=(MagickSizeType) (filter_image->columns+rows);
2224 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
2225 &quantum,exception);
2226 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
2227 span,&quantum,exception);
2228 }
2229 else
2230 {
2231 span=(MagickSizeType) (filter_image->rows+columns);
2232 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
2233 &quantum,exception);
2234 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
2235 span,&quantum,exception);
2236 }
2237 /*
2238 Free resources.
2239 */
2240 filter_image=DestroyImage(filter_image);
2241 resize_filter=DestroyResizeFilter(resize_filter);
2242 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2243 return((Image *) NULL);
2244 resize_image->type=image->type;
2245 return(resize_image);
2246}
2247
2248/*
2249%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2250% %
2251% %
2252% %
2253% S a m p l e I m a g e %
2254% %
2255% %
2256% %
2257%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2258%
2259% SampleImage() scales an image to the desired dimensions with pixel
2260% sampling. Unlike other scaling methods, this method does not introduce
2261% any additional color into the scaled image.
2262%
2263% The format of the SampleImage method is:
2264%
2265% Image *SampleImage(const Image *image,const unsigned long columns,
2266% const unsigned long rows,ExceptionInfo *exception)
2267%
2268% A description of each parameter follows:
2269%
2270% o image: the image.
2271%
2272% o columns: the number of columns in the sampled image.
2273%
2274% o rows: the number of rows in the sampled image.
2275%
2276% o exception: return any errors or warnings in this structure.
2277%
2278*/
2279MagickExport Image *SampleImage(const Image *image,const unsigned long columns,
2280 const unsigned long rows,ExceptionInfo *exception)
2281{
2282#define SampleImageTag "Sample/Image"
2283
2284 Image
2285 *sample_image;
2286
2287 long
2288 progress,
2289 *x_offset,
2290 y;
2291
2292 MagickBooleanType
2293 status;
2294
2295 register long
2296 x;
2297
2298 CacheView
2299 *image_view,
2300 *sample_view;
2301
2302 /*
2303 Initialize sampled image attributes.
2304 */
2305 assert(image != (const Image *) NULL);
2306 assert(image->signature == MagickSignature);
2307 if (image->debug != MagickFalse)
2308 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2309 assert(exception != (ExceptionInfo *) NULL);
2310 assert(exception->signature == MagickSignature);
2311 if ((columns == 0) || (rows == 0))
2312 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2313 if ((columns == image->columns) && (rows == image->rows))
2314 return(CloneImage(image,0,0,MagickTrue,exception));
2315 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2316 if (sample_image == (Image *) NULL)
2317 return((Image *) NULL);
2318 /*
2319 Allocate scan line buffer and column offset buffers.
2320 */
2321 x_offset=(long *) AcquireQuantumMemory((size_t) sample_image->columns,
2322 sizeof(*x_offset));
2323 if (x_offset == (long *) NULL)
2324 {
2325 sample_image=DestroyImage(sample_image);
2326 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2327 }
2328 for (x=0; x < (long) sample_image->columns; x++)
2329 x_offset[x]=(long) (((MagickRealType) x+0.5)*image->columns/
2330 sample_image->columns);
2331 /*
2332 Sample each row.
2333 */
2334 status=MagickTrue;
2335 progress=0;
2336 image_view=AcquireCacheView(image);
2337 sample_view=AcquireCacheView(sample_image);
cristy629a6c72009-09-13 23:28:22 +00002338#if defined(_OPENMP) && (_OPENMP >= 200203)
cristye0f584d2009-10-11 00:59:14 +00002339 #pragma omp parallel for shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002340#endif
2341 for (y=0; y < (long) sample_image->rows; y++)
2342 {
2343 long
2344 y_offset;
2345
2346 register const IndexPacket
2347 *__restrict indexes;
2348
2349 register const PixelPacket
2350 *__restrict p;
2351
2352 register IndexPacket
2353 *__restrict sample_indexes;
2354
2355 register long
2356 x;
2357
2358 register PixelPacket
2359 *__restrict q;
2360
2361 if (status == MagickFalse)
2362 continue;
2363 y_offset=(long) (((MagickRealType) y+0.5)*image->rows/sample_image->rows);
2364 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2365 exception);
2366 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2367 exception);
2368 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2369 {
2370 status=MagickFalse;
2371 continue;
2372 }
2373 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2374 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2375 /*
2376 Sample each column.
2377 */
2378 for (x=0; x < (long) sample_image->columns; x++)
2379 *q++=p[x_offset[x]];
2380 if ((image->storage_class == PseudoClass) ||
2381 (image->colorspace == CMYKColorspace))
2382 for (x=0; x < (long) sample_image->columns; x++)
2383 sample_indexes[x]=indexes[x_offset[x]];
2384 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2385 status=MagickFalse;
2386 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2387 {
2388 MagickBooleanType
2389 proceed;
2390
cristy629a6c72009-09-13 23:28:22 +00002391#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy3ed852e2009-09-05 21:47:34 +00002392 #pragma omp critical (MagickCore_SampleImage)
2393#endif
2394 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2395 if (proceed == MagickFalse)
2396 status=MagickFalse;
2397 }
2398 }
2399 image_view=DestroyCacheView(image_view);
2400 sample_view=DestroyCacheView(sample_view);
2401 x_offset=(long *) RelinquishMagickMemory(x_offset);
2402 sample_image->type=image->type;
2403 return(sample_image);
2404}
2405
2406/*
2407%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2408% %
2409% %
2410% %
2411% S c a l e I m a g e %
2412% %
2413% %
2414% %
2415%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2416%
2417% ScaleImage() changes the size of an image to the given dimensions.
2418%
2419% The format of the ScaleImage method is:
2420%
2421% Image *ScaleImage(const Image *image,const unsigned long columns,
2422% const unsigned long rows,ExceptionInfo *exception)
2423%
2424% A description of each parameter follows:
2425%
2426% o image: the image.
2427%
2428% o columns: the number of columns in the scaled image.
2429%
2430% o rows: the number of rows in the scaled image.
2431%
2432% o exception: return any errors or warnings in this structure.
2433%
2434*/
2435MagickExport Image *ScaleImage(const Image *image,const unsigned long columns,
2436 const unsigned long rows,ExceptionInfo *exception)
2437{
2438#define ScaleImageTag "Scale/Image"
2439
2440 Image
2441 *scale_image;
2442
2443 long
2444 number_rows,
2445 y;
2446
2447 MagickBooleanType
2448 next_column,
2449 next_row,
2450 proceed;
2451
2452 MagickPixelPacket
2453 pixel,
2454 *scale_scanline,
2455 *scanline,
2456 *x_vector,
2457 *y_vector,
2458 zero;
2459
2460 MagickRealType
2461 alpha,
2462 gamma;
2463
2464 PointInfo
2465 scale,
2466 span;
2467
2468 register long
2469 i;
2470
2471 /*
2472 Initialize scaled image attributes.
2473 */
2474 assert(image != (const Image *) NULL);
2475 assert(image->signature == MagickSignature);
2476 if (image->debug != MagickFalse)
2477 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2478 assert(exception != (ExceptionInfo *) NULL);
2479 assert(exception->signature == MagickSignature);
2480 if ((columns == 0) || (rows == 0))
2481 return((Image *) NULL);
2482 if ((columns == image->columns) && (rows == image->rows))
2483 return(CloneImage(image,0,0,MagickTrue,exception));
2484 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2485 if (scale_image == (Image *) NULL)
2486 return((Image *) NULL);
2487 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2488 {
2489 InheritException(exception,&scale_image->exception);
2490 scale_image=DestroyImage(scale_image);
2491 return((Image *) NULL);
2492 }
2493 /*
2494 Allocate memory.
2495 */
2496 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2497 sizeof(*x_vector));
2498 scanline=x_vector;
2499 if (image->rows != scale_image->rows)
2500 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2501 sizeof(*scanline));
2502 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2503 scale_image->columns,sizeof(*scale_scanline));
2504 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2505 sizeof(*y_vector));
2506 if ((scanline == (MagickPixelPacket *) NULL) ||
2507 (scale_scanline == (MagickPixelPacket *) NULL) ||
2508 (x_vector == (MagickPixelPacket *) NULL) ||
2509 (y_vector == (MagickPixelPacket *) NULL))
2510 {
2511 scale_image=DestroyImage(scale_image);
2512 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2513 }
2514 /*
2515 Scale image.
2516 */
2517 number_rows=0;
2518 next_row=MagickTrue;
2519 span.y=1.0;
2520 scale.y=(double) scale_image->rows/(double) image->rows;
2521 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2522 sizeof(*y_vector));
2523 GetMagickPixelPacket(image,&pixel);
2524 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2525 i=0;
2526 for (y=0; y < (long) scale_image->rows; y++)
2527 {
2528 register const IndexPacket
2529 *__restrict indexes;
2530
2531 register const PixelPacket
2532 *__restrict p;
2533
2534 register IndexPacket
2535 *__restrict scale_indexes;
2536
2537 register long
2538 x;
2539
2540 register MagickPixelPacket
2541 *__restrict s,
2542 *__restrict t;
2543
2544 register PixelPacket
2545 *__restrict q;
2546
2547 q=QueueAuthenticPixels(scale_image,0,y,scale_image->columns,1,exception);
2548 if (q == (PixelPacket *) NULL)
2549 break;
2550 scale_indexes=GetAuthenticIndexQueue(scale_image);
2551 if (scale_image->rows == image->rows)
2552 {
2553 /*
2554 Read a new scanline.
2555 */
2556 p=GetVirtualPixels(image,0,i++,image->columns,1,exception);
2557 if (p == (const PixelPacket *) NULL)
2558 break;
2559 indexes=GetVirtualIndexQueue(image);
2560 for (x=0; x < (long) image->columns; x++)
2561 {
2562 x_vector[x].red=(MagickRealType) p->red;
2563 x_vector[x].green=(MagickRealType) p->green;
2564 x_vector[x].blue=(MagickRealType) p->blue;
2565 if (image->matte != MagickFalse)
2566 x_vector[x].opacity=(MagickRealType) p->opacity;
2567 if (indexes != (IndexPacket *) NULL)
2568 x_vector[x].index=(MagickRealType) indexes[x];
2569 p++;
2570 }
2571 }
2572 else
2573 {
2574 /*
2575 Scale Y direction.
2576 */
2577 while (scale.y < span.y)
2578 {
2579 if ((next_row != MagickFalse) && (number_rows < (long) image->rows))
2580 {
2581 /*
2582 Read a new scanline.
2583 */
2584 p=GetVirtualPixels(image,0,i++,image->columns,1,exception);
2585 if (p == (const PixelPacket *) NULL)
2586 break;
2587 indexes=GetVirtualIndexQueue(image);
2588 for (x=0; x < (long) image->columns; x++)
2589 {
2590 x_vector[x].red=(MagickRealType) p->red;
2591 x_vector[x].green=(MagickRealType) p->green;
2592 x_vector[x].blue=(MagickRealType) p->blue;
2593 if (image->matte != MagickFalse)
2594 x_vector[x].opacity=(MagickRealType) p->opacity;
2595 if (indexes != (IndexPacket *) NULL)
2596 x_vector[x].index=(MagickRealType) indexes[x];
2597 p++;
2598 }
2599 number_rows++;
2600 }
2601 for (x=0; x < (long) image->columns; x++)
2602 {
2603 y_vector[x].red+=scale.y*x_vector[x].red;
2604 y_vector[x].green+=scale.y*x_vector[x].green;
2605 y_vector[x].blue+=scale.y*x_vector[x].blue;
2606 if (scale_image->matte != MagickFalse)
2607 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2608 if (scale_indexes != (IndexPacket *) NULL)
2609 y_vector[x].index+=scale.y*x_vector[x].index;
2610 }
2611 span.y-=scale.y;
2612 scale.y=(double) scale_image->rows/(double) image->rows;
2613 next_row=MagickTrue;
2614 }
2615 if ((next_row != MagickFalse) && (number_rows < (long) image->rows))
2616 {
2617 /*
2618 Read a new scanline.
2619 */
2620 p=GetVirtualPixels(image,0,i++,image->columns,1,exception);
2621 if (p == (const PixelPacket *) NULL)
2622 break;
2623 indexes=GetVirtualIndexQueue(image);
2624 for (x=0; x < (long) image->columns; x++)
2625 {
2626 x_vector[x].red=(MagickRealType) p->red;
2627 x_vector[x].green=(MagickRealType) p->green;
2628 x_vector[x].blue=(MagickRealType) p->blue;
2629 if (image->matte != MagickFalse)
2630 x_vector[x].opacity=(MagickRealType) p->opacity;
2631 if (indexes != (IndexPacket *) NULL)
2632 x_vector[x].index=(MagickRealType) indexes[x];
2633 p++;
2634 }
2635 number_rows++;
2636 next_row=MagickFalse;
2637 }
2638 s=scanline;
2639 for (x=0; x < (long) image->columns; x++)
2640 {
2641 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
2642 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
2643 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
2644 if (image->matte != MagickFalse)
2645 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
2646 if (scale_indexes != (IndexPacket *) NULL)
2647 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
2648 s->red=pixel.red;
2649 s->green=pixel.green;
2650 s->blue=pixel.blue;
2651 if (scale_image->matte != MagickFalse)
2652 s->opacity=pixel.opacity;
2653 if (scale_indexes != (IndexPacket *) NULL)
2654 s->index=pixel.index;
2655 s++;
2656 y_vector[x]=zero;
2657 }
2658 scale.y-=span.y;
2659 if (scale.y <= 0)
2660 {
2661 scale.y=(double) scale_image->rows/(double) image->rows;
2662 next_row=MagickTrue;
2663 }
2664 span.y=1.0;
2665 }
2666 if (scale_image->columns == image->columns)
2667 {
2668 /*
2669 Transfer scanline to scaled image.
2670 */
2671 s=scanline;
2672 for (x=0; x < (long) scale_image->columns; x++)
2673 {
2674 q->red=RoundToQuantum(s->red);
2675 q->green=RoundToQuantum(s->green);
2676 q->blue=RoundToQuantum(s->blue);
2677 if (scale_image->matte != MagickFalse)
2678 q->opacity=RoundToQuantum(s->opacity);
2679 if (scale_indexes != (IndexPacket *) NULL)
2680 scale_indexes[x]=(IndexPacket) RoundToQuantum(s->index);
2681 q++;
2682 s++;
2683 }
2684 }
2685 else
2686 {
2687 /*
2688 Scale X direction.
2689 */
2690 pixel=zero;
2691 next_column=MagickFalse;
2692 span.x=1.0;
2693 s=scanline;
2694 t=scale_scanline;
2695 for (x=0; x < (long) image->columns; x++)
2696 {
2697 scale.x=(double) scale_image->columns/(double) image->columns;
2698 while (scale.x >= span.x)
2699 {
2700 if (next_column != MagickFalse)
2701 {
2702 pixel=zero;
2703 t++;
2704 }
2705 pixel.red+=span.x*s->red;
2706 pixel.green+=span.x*s->green;
2707 pixel.blue+=span.x*s->blue;
2708 if (image->matte != MagickFalse)
2709 pixel.opacity+=span.x*s->opacity;
2710 if (scale_indexes != (IndexPacket *) NULL)
2711 pixel.index+=span.x*s->index;
2712 t->red=pixel.red;
2713 t->green=pixel.green;
2714 t->blue=pixel.blue;
2715 if (scale_image->matte != MagickFalse)
2716 t->opacity=pixel.opacity;
2717 if (scale_indexes != (IndexPacket *) NULL)
2718 t->index=pixel.index;
2719 scale.x-=span.x;
2720 span.x=1.0;
2721 next_column=MagickTrue;
2722 }
2723 if (scale.x > 0)
2724 {
2725 if (next_column != MagickFalse)
2726 {
2727 pixel=zero;
2728 next_column=MagickFalse;
2729 t++;
2730 }
2731 pixel.red+=scale.x*s->red;
2732 pixel.green+=scale.x*s->green;
2733 pixel.blue+=scale.x*s->blue;
2734 if (scale_image->matte != MagickFalse)
2735 pixel.opacity+=scale.x*s->opacity;
2736 if (scale_indexes != (IndexPacket *) NULL)
2737 pixel.index+=scale.x*s->index;
2738 span.x-=scale.x;
2739 }
2740 s++;
2741 }
2742 if (span.x > 0)
2743 {
2744 s--;
2745 pixel.red+=span.x*s->red;
2746 pixel.green+=span.x*s->green;
2747 pixel.blue+=span.x*s->blue;
2748 if (scale_image->matte != MagickFalse)
2749 pixel.opacity+=span.x*s->opacity;
2750 if (scale_indexes != (IndexPacket *) NULL)
2751 pixel.index+=span.x*s->index;
2752 }
2753 if ((next_column == MagickFalse) &&
2754 ((long) (t-scale_scanline) < (long) scale_image->columns))
2755 {
2756 t->red=pixel.red;
2757 t->green=pixel.green;
2758 t->blue=pixel.blue;
2759 if (scale_image->matte != MagickFalse)
2760 t->opacity=pixel.opacity;
2761 if (scale_indexes != (IndexPacket *) NULL)
2762 t->index=pixel.index;
2763 }
2764 /*
2765 Transfer scanline to scaled image.
2766 */
2767 t=scale_scanline;
2768 for (x=0; x < (long) scale_image->columns; x++)
2769 {
2770 alpha=1.0;
2771 if (image->matte != MagickFalse)
2772 alpha=(MagickRealType) (QuantumScale*(QuantumRange-t->opacity));
2773 gamma=1.0/(fabs((double) alpha) <= MagickEpsilon ? 1.0 : alpha);
2774 q->red=RoundToQuantum(gamma*t->red);
2775 q->green=RoundToQuantum(gamma*t->green);
2776 q->blue=RoundToQuantum(gamma*t->blue);
2777 if (scale_image->matte != MagickFalse)
2778 q->opacity=RoundToQuantum(t->opacity);
2779 if (scale_indexes != (IndexPacket *) NULL)
2780 scale_indexes[x]=(IndexPacket) RoundToQuantum(gamma*t->index);
2781 t++;
2782 q++;
2783 }
2784 }
2785 if (SyncAuthenticPixels(scale_image,exception) == MagickFalse)
2786 break;
2787 proceed=SetImageProgress(image,ScaleImageTag,y,image->rows);
2788 if (proceed == MagickFalse)
2789 break;
2790 }
2791 /*
2792 Free allocated memory.
2793 */
2794 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
2795 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
2796 if (scale_image->rows != image->rows)
2797 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
2798 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
2799 scale_image->type=image->type;
2800 return(scale_image);
2801}
2802
2803/*
2804%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2805% %
2806% %
2807% %
2808+ S e t R e s i z e F i l t e r S u p p o r t %
2809% %
2810% %
2811% %
2812%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2813%
2814% SetResizeFilterSupport() specifies which IR filter to use to window
2815%
2816% The format of the SetResizeFilterSupport method is:
2817%
2818% void SetResizeFilterSupport(ResizeFilter *resize_filter,
2819% const MagickRealType support)
2820%
2821% A description of each parameter follows:
2822%
2823% o resize_filter: the resize filter.
2824%
2825% o support: the filter spport radius.
2826%
2827*/
2828MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
2829 const MagickRealType support)
2830{
2831 assert(resize_filter != (ResizeFilter *) NULL);
2832 assert(resize_filter->signature == MagickSignature);
2833 resize_filter->support=support;
2834}
2835
2836/*
2837%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2838% %
2839% %
2840% %
2841% T h u m b n a i l I m a g e %
2842% %
2843% %
2844% %
2845%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2846%
2847% ThumbnailImage() changes the size of an image to the given dimensions and
2848% removes any associated profiles. The goal is to produce small low cost
2849% thumbnail images suited for display on the Web.
2850%
2851% The format of the ThumbnailImage method is:
2852%
2853% Image *ThumbnailImage(const Image *image,const unsigned long columns,
2854% const unsigned long rows,ExceptionInfo *exception)
2855%
2856% A description of each parameter follows:
2857%
2858% o image: the image.
2859%
2860% o columns: the number of columns in the scaled image.
2861%
2862% o rows: the number of rows in the scaled image.
2863%
2864% o exception: return any errors or warnings in this structure.
2865%
2866*/
2867MagickExport Image *ThumbnailImage(const Image *image,
2868 const unsigned long columns,const unsigned long rows,ExceptionInfo *exception)
2869{
2870#define SampleFactor 5
2871
2872 char
2873 value[MaxTextExtent];
2874
2875 const char
2876 *name;
2877
2878 Image
2879 *thumbnail_image;
2880
2881 MagickRealType
2882 x_factor,
2883 y_factor;
2884
2885 struct stat
2886 attributes;
2887
2888 unsigned long
2889 version;
2890
2891 assert(image != (Image *) NULL);
2892 assert(image->signature == MagickSignature);
2893 if (image->debug != MagickFalse)
2894 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2895 assert(exception != (ExceptionInfo *) NULL);
2896 assert(exception->signature == MagickSignature);
2897 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2898 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2899 if ((x_factor*y_factor) > 0.1)
2900 thumbnail_image=ZoomImage(image,columns,rows,exception);
2901 else
2902 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
2903 thumbnail_image=ZoomImage(image,columns,rows,exception);
2904 else
2905 {
2906 Image
2907 *sample_image;
2908
2909 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
2910 exception);
2911 if (sample_image == (Image *) NULL)
2912 return((Image *) NULL);
2913 thumbnail_image=ZoomImage(sample_image,columns,rows,exception);
2914 sample_image=DestroyImage(sample_image);
2915 }
2916 if (thumbnail_image == (Image *) NULL)
2917 return(thumbnail_image);
2918 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
2919 if (thumbnail_image->matte == MagickFalse)
2920 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
2921 thumbnail_image->depth=8;
2922 thumbnail_image->interlace=NoInterlace;
2923 /*
2924 Strip all profiles except color profiles.
2925 */
2926 ResetImageProfileIterator(thumbnail_image);
2927 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
2928 {
2929 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
2930 {
2931 DeleteImageProfile(thumbnail_image,name);
2932 ResetImageProfileIterator(thumbnail_image);
2933 }
2934 name=GetNextImageProfile(thumbnail_image);
2935 }
2936 (void) DeleteImageProperty(thumbnail_image,"comment");
2937 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
2938 if (strstr(image->magick_filename,"///") == (char *) NULL)
2939 (void) FormatMagickString(value,MaxTextExtent,"file:///%s",
2940 image->magick_filename);
2941 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
2942 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
2943 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
2944 {
2945 (void) FormatMagickString(value,MaxTextExtent,"%ld",(long)
2946 attributes.st_mtime);
2947 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
2948 }
2949 (void) FormatMagickString(value,MaxTextExtent,"%ld",(long)
2950 attributes.st_mtime);
2951 (void) FormatMagickSize(GetBlobSize(image),value);
2952 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
2953 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
2954 LocaleLower(value);
2955 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
2956 (void) SetImageProperty(thumbnail_image,"software",
2957 GetMagickVersion(&version));
2958 (void) FormatMagickString(value,MaxTextExtent,"%lu",image->magick_columns);
2959 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
2960 (void) FormatMagickString(value,MaxTextExtent,"%lu",image->magick_rows);
2961 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
2962 (void) FormatMagickString(value,MaxTextExtent,"%lu",
2963 GetImageListLength(image));
2964 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
2965 return(thumbnail_image);
2966}
2967
2968/*
2969%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2970% %
2971% %
2972% %
2973% Z o o m I m a g e %
2974% %
2975% %
2976% %
2977%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2978%
2979% ZoomImage() creates a new image that is a scaled size of an existing one.
2980% It allocates the memory necessary for the new Image structure and returns a
2981% pointer to the new image. The Point filter gives fast pixel replication,
2982% Triangle is equivalent to bi-linear interpolation, and Mitchel giver slower,
2983% very high-quality results. See Graphic Gems III for details on this
2984% algorithm.
2985%
2986% The filter member of the Image structure specifies which image filter to
2987% use. Blur specifies the blur factor where > 1 is blurry, < 1 is sharp.
2988%
2989% The format of the ZoomImage method is:
2990%
2991% Image *ZoomImage(const Image *image,const unsigned long columns,
2992% const unsigned long rows,ExceptionInfo *exception)
2993%
2994% A description of each parameter follows:
2995%
2996% o image: the image.
2997%
2998% o columns: An integer that specifies the number of columns in the zoom
2999% image.
3000%
3001% o rows: An integer that specifies the number of rows in the scaled
3002% image.
3003%
3004% o exception: return any errors or warnings in this structure.
3005%
3006*/
3007MagickExport Image *ZoomImage(const Image *image,const unsigned long columns,
3008 const unsigned long rows,ExceptionInfo *exception)
3009{
3010 Image
3011 *zoom_image;
3012
3013 assert(image != (const Image *) NULL);
3014 assert(image->signature == MagickSignature);
3015 if (image->debug != MagickFalse)
3016 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3017 assert(exception != (ExceptionInfo *) NULL);
3018 assert(exception->signature == MagickSignature);
3019 zoom_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3020 exception);
3021 return(zoom_image);
3022}