blob: f763c996efb599ec07a8a38785cbe8e536771b3e [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)
750 (void) printf("%5.2lf\t%lf\n",x,GetResizeFilterWeight(resize_filter,x));
751 (void) printf("%5.2lf\t%lf\n",support,0.0);
752 }
753 return(resize_filter);
754}
755
756/*
757%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
758% %
759% %
760% %
761% A d a p t i v e R e s i z e I m a g e %
762% %
763% %
764% %
765%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
766%
767% AdaptiveResizeImage() adaptively resize image with pixel resampling.
768%
769% The format of the AdaptiveResizeImage method is:
770%
771% Image *AdaptiveResizeImage(const Image *image,
772% const unsigned long columns,const unsigned long rows,
773% ExceptionInfo *exception)
774%
775% A description of each parameter follows:
776%
777% o image: the image.
778%
779% o columns: the number of columns in the resized image.
780%
781% o rows: the number of rows in the resized image.
782%
783% o exception: return any errors or warnings in this structure.
784%
785*/
786MagickExport Image *AdaptiveResizeImage(const Image *image,
787 const unsigned long columns,const unsigned long rows,ExceptionInfo *exception)
788{
789#define AdaptiveResizeImageTag "Resize/Image"
790
791 Image
792 *resize_image;
793
794 long
795 y;
796
797 MagickBooleanType
798 proceed;
799
800 MagickPixelPacket
801 pixel;
802
803 PointInfo
804 offset;
805
806 ResampleFilter
807 *resample_filter;
808
809 CacheView
810 *resize_view;
811
812 /*
813 Adaptively resize image.
814 */
815 assert(image != (const Image *) NULL);
816 assert(image->signature == MagickSignature);
817 if (image->debug != MagickFalse)
818 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
819 assert(exception != (ExceptionInfo *) NULL);
820 assert(exception->signature == MagickSignature);
821 if ((columns == 0) || (rows == 0))
822 return((Image *) NULL);
823 if ((columns == image->columns) && (rows == image->rows))
824 return(CloneImage(image,0,0,MagickTrue,exception));
825 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
826 if (resize_image == (Image *) NULL)
827 return((Image *) NULL);
828 if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
829 {
830 InheritException(exception,&resize_image->exception);
831 resize_image=DestroyImage(resize_image);
832 return((Image *) NULL);
833 }
834 GetMagickPixelPacket(image,&pixel);
835 resample_filter=AcquireResampleFilter(image,exception);
836 if (image->interpolate == UndefinedInterpolatePixel)
837 (void) SetResampleFilterInterpolateMethod(resample_filter,
838 MeshInterpolatePixel);
839 resize_view=AcquireCacheView(resize_image);
840 for (y=0; y < (long) resize_image->rows; y++)
841 {
842 register IndexPacket
843 *__restrict resize_indexes;
844
845 register long
846 x;
847
848 register PixelPacket
849 *__restrict q;
850
851 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
852 exception);
853 if (q == (PixelPacket *) NULL)
854 break;
855 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
856 offset.y=((MagickRealType) y*image->rows/resize_image->rows);
857 for (x=0; x < (long) resize_image->columns; x++)
858 {
859 offset.x=((MagickRealType) x*image->columns/resize_image->columns);
860 (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
861 &pixel);
862 SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
863 q++;
864 }
865 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
866 break;
867 proceed=SetImageProgress(image,AdaptiveResizeImageTag,y,image->rows);
868 if (proceed == MagickFalse)
869 break;
870 }
871 resample_filter=DestroyResampleFilter(resample_filter);
872 resize_view=DestroyCacheView(resize_view);
873 return(resize_image);
874}
875
876/*
877%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
878% %
879% %
880% %
881+ B e s s e l O r d e r O n e %
882% %
883% %
884% %
885%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
886%
887% BesselOrderOne() computes the Bessel function of x of the first kind of
888% order 0:
889%
890% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
891%
892% j1(x) = x*j1(x);
893%
894% For x in (8,inf)
895%
896% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
897%
898% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
899%
900% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
901% = 1/sqrt(2) * (sin(x) - cos(x))
902% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
903% = -1/sqrt(2) * (sin(x) + cos(x))
904%
905% The format of the BesselOrderOne method is:
906%
907% MagickRealType BesselOrderOne(MagickRealType x)
908%
909% A description of each parameter follows:
910%
911% o x: MagickRealType value.
912%
913*/
914
915#undef I0
916static MagickRealType I0(MagickRealType x)
917{
918 MagickRealType
919 sum,
920 t,
921 y;
922
923 register long
924 i;
925
926 /*
927 Zeroth order Bessel function of the first kind.
928 */
929 sum=1.0;
930 y=x*x/4.0;
931 t=y;
932 for (i=2; t > MagickEpsilon; i++)
933 {
934 sum+=t;
935 t*=y/((MagickRealType) i*i);
936 }
937 return(sum);
938}
939
940#undef J1
941static MagickRealType J1(MagickRealType x)
942{
943 MagickRealType
944 p,
945 q;
946
947 register long
948 i;
949
950 static const double
951 Pone[] =
952 {
953 0.581199354001606143928050809e+21,
954 -0.6672106568924916298020941484e+20,
955 0.2316433580634002297931815435e+19,
956 -0.3588817569910106050743641413e+17,
957 0.2908795263834775409737601689e+15,
958 -0.1322983480332126453125473247e+13,
959 0.3413234182301700539091292655e+10,
960 -0.4695753530642995859767162166e+7,
961 0.270112271089232341485679099e+4
962 },
963 Qone[] =
964 {
965 0.11623987080032122878585294e+22,
966 0.1185770712190320999837113348e+20,
967 0.6092061398917521746105196863e+17,
968 0.2081661221307607351240184229e+15,
969 0.5243710262167649715406728642e+12,
970 0.1013863514358673989967045588e+10,
971 0.1501793594998585505921097578e+7,
972 0.1606931573481487801970916749e+4,
973 0.1e+1
974 };
975
976 p=Pone[8];
977 q=Qone[8];
978 for (i=7; i >= 0; i--)
979 {
980 p=p*x*x+Pone[i];
981 q=q*x*x+Qone[i];
982 }
983 return(p/q);
984}
985
986#undef P1
987static MagickRealType P1(MagickRealType x)
988{
989 MagickRealType
990 p,
991 q;
992
993 register long
994 i;
995
996 static const double
997 Pone[] =
998 {
999 0.352246649133679798341724373e+5,
1000 0.62758845247161281269005675e+5,
1001 0.313539631109159574238669888e+5,
1002 0.49854832060594338434500455e+4,
1003 0.2111529182853962382105718e+3,
1004 0.12571716929145341558495e+1
1005 },
1006 Qone[] =
1007 {
1008 0.352246649133679798068390431e+5,
1009 0.626943469593560511888833731e+5,
1010 0.312404063819041039923015703e+5,
1011 0.4930396490181088979386097e+4,
1012 0.2030775189134759322293574e+3,
1013 0.1e+1
1014 };
1015
1016 p=Pone[5];
1017 q=Qone[5];
1018 for (i=4; i >= 0; i--)
1019 {
1020 p=p*(8.0/x)*(8.0/x)+Pone[i];
1021 q=q*(8.0/x)*(8.0/x)+Qone[i];
1022 }
1023 return(p/q);
1024}
1025
1026#undef Q1
1027static MagickRealType Q1(MagickRealType x)
1028{
1029 MagickRealType
1030 p,
1031 q;
1032
1033 register long
1034 i;
1035
1036 static const double
1037 Pone[] =
1038 {
1039 0.3511751914303552822533318e+3,
1040 0.7210391804904475039280863e+3,
1041 0.4259873011654442389886993e+3,
1042 0.831898957673850827325226e+2,
1043 0.45681716295512267064405e+1,
1044 0.3532840052740123642735e-1
1045 },
1046 Qone[] =
1047 {
1048 0.74917374171809127714519505e+4,
1049 0.154141773392650970499848051e+5,
1050 0.91522317015169922705904727e+4,
1051 0.18111867005523513506724158e+4,
1052 0.1038187585462133728776636e+3,
1053 0.1e+1
1054 };
1055
1056 p=Pone[5];
1057 q=Qone[5];
1058 for (i=4; i >= 0; i--)
1059 {
1060 p=p*(8.0/x)*(8.0/x)+Pone[i];
1061 q=q*(8.0/x)*(8.0/x)+Qone[i];
1062 }
1063 return(p/q);
1064}
1065
1066static MagickRealType BesselOrderOne(MagickRealType x)
1067{
1068 MagickRealType
1069 p,
1070 q;
1071
1072 if (x == 0.0)
1073 return(0.0);
1074 p=x;
1075 if (x < 0.0)
1076 x=(-x);
1077 if (x < 8.0)
1078 return(p*J1(x));
1079 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
1080 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
1081 cos((double) x))));
1082 if (p < 0.0)
1083 q=(-q);
1084 return(q);
1085}
1086
1087/*
1088%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1089% %
1090% %
1091% %
1092+ D e s t r o y R e s i z e F i l t e r %
1093% %
1094% %
1095% %
1096%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1097%
1098% DestroyResizeFilter() destroy the resize filter.
1099%
1100% The format of the AcquireResizeFilter method is:
1101%
1102% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1103%
1104% A description of each parameter follows:
1105%
1106% o resize_filter: the resize filter.
1107%
1108*/
1109MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
1110{
1111 assert(resize_filter != (ResizeFilter *) NULL);
1112 assert(resize_filter->signature == MagickSignature);
1113 resize_filter->signature=(~MagickSignature);
1114 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
1115 return(resize_filter);
1116}
1117
1118/*
1119%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1120% %
1121% %
1122% %
1123+ G e t R e s i z e F i l t e r S u p p o r t %
1124% %
1125% %
1126% %
1127%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1128%
1129% GetResizeFilterSupport() return the current support window size for this
1130% filter. Note that this may have been enlarged by filter:blur factor.
1131%
1132% The format of the GetResizeFilterSupport method is:
1133%
1134% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
1135%
1136% A description of each parameter follows:
1137%
1138% o filter: Image filter to use.
1139%
1140*/
1141MagickExport MagickRealType GetResizeFilterSupport(
1142 const ResizeFilter *resize_filter)
1143{
1144 assert(resize_filter != (ResizeFilter *) NULL);
1145 assert(resize_filter->signature == MagickSignature);
1146 return(resize_filter->support*resize_filter->blur);
1147}
1148
1149/*
1150%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1151% %
1152% %
1153% %
1154+ G e t R e s i z e F i l t e r W e i g h t %
1155% %
1156% %
1157% %
1158%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1159%
1160% GetResizeFilterWeight evaluates the specified resize filter at the point x
1161% which usally lies between zero and the filters current 'support' and
1162% returns the weight of the filter function at that point.
1163%
1164% The format of the GetResizeFilterWeight method is:
1165%
1166% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
1167% const MagickRealType x)
1168%
1169% A description of each parameter follows:
1170%
1171% o filter: the filter type.
1172%
1173% o x: the point.
1174%
1175*/
1176MagickExport MagickRealType GetResizeFilterWeight(
1177 const ResizeFilter *resize_filter,const MagickRealType x)
1178{
1179 MagickRealType
1180 blur,
1181 scale;
1182
1183 /*
1184 Windowing function - scale the weighting filter by this amount.
1185 */
1186 assert(resize_filter != (ResizeFilter *) NULL);
1187 assert(resize_filter->signature == MagickSignature);
1188 blur=fabs(x)/resize_filter->blur; /* X offset with blur scaling */
1189 if ((resize_filter->window_support < MagickEpsilon) ||
1190 (resize_filter->window == Box))
1191 scale=1.0; /* Point/Box Filter -- avoid division by zero */
1192 else
1193 {
1194 scale=resize_filter->scale/resize_filter->window_support;
1195 scale=resize_filter->window(blur*scale,resize_filter);
1196 }
1197 return(scale*resize_filter->filter(blur,resize_filter));
1198}
1199
1200/*
1201%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1202% %
1203% %
1204% %
1205% M a g n i f y I m a g e %
1206% %
1207% %
1208% %
1209%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1210%
1211% MagnifyImage() is a convenience method that scales an image proportionally
1212% to twice its size.
1213%
1214% The format of the MagnifyImage method is:
1215%
1216% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1217%
1218% A description of each parameter follows:
1219%
1220% o image: the image.
1221%
1222% o exception: return any errors or warnings in this structure.
1223%
1224*/
1225MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
1226{
1227 Image
1228 *magnify_image;
1229
1230 assert(image != (Image *) NULL);
1231 assert(image->signature == MagickSignature);
1232 if (image->debug != MagickFalse)
1233 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1234 assert(exception != (ExceptionInfo *) NULL);
1235 assert(exception->signature == MagickSignature);
1236 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
1237 1.0,exception);
1238 return(magnify_image);
1239}
1240
1241/*
1242%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1243% %
1244% %
1245% %
1246% M i n i f y I m a g e %
1247% %
1248% %
1249% %
1250%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1251%
1252% MinifyImage() is a convenience method that scales an image proportionally
1253% to half its size.
1254%
1255% The format of the MinifyImage method is:
1256%
1257% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1258%
1259% A description of each parameter follows:
1260%
1261% o image: the image.
1262%
1263% o exception: return any errors or warnings in this structure.
1264%
1265*/
1266MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
1267{
1268 Image
1269 *minify_image;
1270
1271 assert(image != (Image *) NULL);
1272 assert(image->signature == MagickSignature);
1273 if (image->debug != MagickFalse)
1274 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1275 assert(exception != (ExceptionInfo *) NULL);
1276 assert(exception->signature == MagickSignature);
1277 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
1278 1.0,exception);
1279 return(minify_image);
1280}
1281
1282/*
1283%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1284% %
1285% %
1286% %
1287% R e s a m p l e I m a g e %
1288% %
1289% %
1290% %
1291%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1292%
1293% ResampleImage() resize image in terms of its pixel size, so that when
1294% displayed at the given resolution it will be the same size in terms of
1295% real world units as the original image at the original resolution.
1296%
1297% The format of the ResampleImage method is:
1298%
1299% Image *ResampleImage(Image *image,const double x_resolution,
1300% const double y_resolution,const FilterTypes filter,const double blur,
1301% ExceptionInfo *exception)
1302%
1303% A description of each parameter follows:
1304%
1305% o image: the image to be resized to fit the given resolution.
1306%
1307% o x_resolution: the new image x resolution.
1308%
1309% o y_resolution: the new image y resolution.
1310%
1311% o filter: Image filter to use.
1312%
1313% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1314%
1315*/
1316MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
1317 const double y_resolution,const FilterTypes filter,const double blur,
1318 ExceptionInfo *exception)
1319{
1320#define ResampleImageTag "Resample/Image"
1321
1322 Image
1323 *resample_image;
1324
1325 unsigned long
1326 height,
1327 width;
1328
1329 /*
1330 Initialize sampled image attributes.
1331 */
1332 assert(image != (const Image *) NULL);
1333 assert(image->signature == MagickSignature);
1334 if (image->debug != MagickFalse)
1335 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1336 assert(exception != (ExceptionInfo *) NULL);
1337 assert(exception->signature == MagickSignature);
1338 width=(unsigned long) (x_resolution*image->columns/
1339 (image->x_resolution == 0.0 ? 72.0 : image->x_resolution)+0.5);
1340 height=(unsigned long) (y_resolution*image->rows/
1341 (image->y_resolution == 0.0 ? 72.0 : image->y_resolution)+0.5);
1342 resample_image=ResizeImage(image,width,height,filter,blur,exception);
1343 if (resample_image != (Image *) NULL)
1344 {
1345 resample_image->x_resolution=x_resolution;
1346 resample_image->y_resolution=y_resolution;
1347 }
1348 return(resample_image);
1349}
1350#if defined(MAGICKCORE_LQR_DELEGATE)
1351
1352/*
1353%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1354% %
1355% %
1356% %
1357% L i q u i d R e s c a l e I m a g e %
1358% %
1359% %
1360% %
1361%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1362%
1363% LiquidRescaleImage() rescales image with seam carving.
1364%
1365% The format of the LiquidRescaleImage method is:
1366%
1367% Image *LiquidRescaleImage(const Image *image,
1368% const unsigned long columns,const unsigned long rows,
1369% const double delta_x,const double rigidity,ExceptionInfo *exception)
1370%
1371% A description of each parameter follows:
1372%
1373% o image: the image.
1374%
1375% o columns: the number of columns in the rescaled image.
1376%
1377% o rows: the number of rows in the rescaled image.
1378%
1379% o delta_x: maximum seam transversal step (0 means straight seams).
1380%
1381% o rigidity: introduce a bias for non-straight seams (typically 0).
1382%
1383% o exception: return any errors or warnings in this structure.
1384%
1385*/
1386MagickExport Image *LiquidRescaleImage(const Image *image,
1387 const unsigned long columns,const unsigned long rows,
1388 const double delta_x,const double rigidity,ExceptionInfo *exception)
1389{
1390#define LiquidRescaleImageTag "Rescale/Image"
1391
1392 const char
1393 *map;
1394
1395 guchar
1396 *packet;
1397
1398 Image
1399 *rescale_image;
1400
1401 int
1402 x,
1403 y;
1404
1405 LqrCarver
1406 *carver;
1407
1408 LqrRetVal
1409 lqr_status;
1410
1411 MagickBooleanType
1412 status;
1413
1414 MagickPixelPacket
1415 pixel;
1416
1417 unsigned char
1418 *pixels;
1419
1420 /*
1421 Liquid rescale image.
1422 */
1423 assert(image != (const Image *) NULL);
1424 assert(image->signature == MagickSignature);
1425 if (image->debug != MagickFalse)
1426 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1427 assert(exception != (ExceptionInfo *) NULL);
1428 assert(exception->signature == MagickSignature);
1429 if ((columns == 0) || (rows == 0))
1430 return((Image *) NULL);
1431 if ((columns == image->columns) && (rows == image->rows))
1432 return(CloneImage(image,0,0,MagickTrue,exception));
1433 if ((columns <= 2) || (rows <= 2))
1434 return(ZoomImage(image,columns,rows,exception));
1435 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
1436 {
1437 Image
1438 *resize_image;
1439
1440 unsigned long
1441 height,
1442 width;
1443
1444 /*
1445 Honor liquid resize size limitations.
1446 */
1447 for (width=image->columns; columns >= (2*width-1); width*=2);
1448 for (height=image->rows; rows >= (2*height-1); height*=2);
1449 resize_image=ResizeImage(image,width,height,image->filter,image->blur,
1450 exception);
1451 if (resize_image == (Image *) NULL)
1452 return((Image *) NULL);
1453 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
1454 rigidity,exception);
1455 resize_image=DestroyImage(resize_image);
1456 return(rescale_image);
1457 }
1458 map="RGB";
1459 if (image->matte == MagickFalse)
1460 map="RGBA";
1461 if (image->colorspace == CMYKColorspace)
1462 {
1463 map="CMYK";
1464 if (image->matte == MagickFalse)
1465 map="CMYKA";
1466 }
1467 pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
1468 strlen(map)*sizeof(*pixels));
1469 if (pixels == (unsigned char *) NULL)
1470 return((Image *) NULL);
1471 status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
1472 pixels,exception);
1473 if (status == MagickFalse)
1474 {
1475 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1476 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1477 }
1478 carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
1479 if (carver == (LqrCarver *) NULL)
1480 {
1481 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1482 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1483 }
1484 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
1485 lqr_status=lqr_carver_resize(carver,columns,rows);
1486 rescale_image=CloneImage(image,lqr_carver_get_width(carver),
1487 lqr_carver_get_height(carver),MagickTrue,exception);
1488 if (rescale_image == (Image *) NULL)
1489 {
1490 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1491 return((Image *) NULL);
1492 }
1493 if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
1494 {
1495 InheritException(exception,&rescale_image->exception);
1496 rescale_image=DestroyImage(rescale_image);
1497 return((Image *) NULL);
1498 }
1499 GetMagickPixelPacket(rescale_image,&pixel);
1500 (void) lqr_carver_scan_reset(carver);
1501 while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
1502 {
1503 register IndexPacket
1504 *__restrict rescale_indexes;
1505
1506 register PixelPacket
1507 *__restrict q;
1508
1509 q=QueueAuthenticPixels(rescale_image,x,y,1,1,exception);
1510 if (q == (PixelPacket *) NULL)
1511 break;
1512 rescale_indexes=GetAuthenticIndexQueue(rescale_image);
1513 pixel.red=QuantumRange*(packet[0]/255.0);
1514 pixel.green=QuantumRange*(packet[1]/255.0);
1515 pixel.blue=QuantumRange*(packet[2]/255.0);
1516 if (image->colorspace != CMYKColorspace)
1517 {
1518 if (image->matte == MagickFalse)
1519 pixel.opacity=QuantumRange*(packet[3]/255.0);
1520 }
1521 else
1522 {
1523 pixel.index=QuantumRange*(packet[3]/255.0);
1524 if (image->matte == MagickFalse)
1525 pixel.opacity=QuantumRange*(packet[4]/255.0);
1526 }
1527 SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
1528 if (SyncAuthenticPixels(rescale_image,exception) == MagickFalse)
1529 break;
1530 }
1531 /*
1532 Relinquish resources.
1533 */
1534 lqr_carver_destroy(carver);
1535 return(rescale_image);
1536}
1537#else
1538MagickExport Image *LiquidRescaleImage(const Image *image,
1539 const unsigned long magick_unused(columns),
1540 const unsigned long magick_unused(rows),const double magick_unused(delta_x),
1541 const double magick_unused(rigidity),ExceptionInfo *exception)
1542{
1543 assert(image != (const Image *) NULL);
1544 assert(image->signature == MagickSignature);
1545 if (image->debug != MagickFalse)
1546 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1547 assert(exception != (ExceptionInfo *) NULL);
1548 assert(exception->signature == MagickSignature);
1549 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
1550 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
1551 return((Image *) NULL);
1552}
1553#endif
1554
1555/*
1556%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1557% %
1558% %
1559% %
1560% R e s i z e I m a g e %
1561% %
1562% %
1563% %
1564%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1565%
1566% ResizeImage() scales an image to the desired dimensions, using the given
1567% filter (see AcquireFilterInfo() ).
1568%
1569% If an undefined filter is given the filter defaults to Mitchell for a
1570% colormapped image, a image with a matte channel, or if the image is
1571% enlarged. Otherwise the filter defaults to a Lanczos.
1572%
1573% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
1574%
1575% The format of the ResizeImage method is:
1576%
1577% Image *ResizeImage(Image *image,const unsigned long columns,
1578% const unsigned long rows,const FilterTypes filter,const double blur,
1579% ExceptionInfo *exception)
1580%
1581% A description of each parameter follows:
1582%
1583% o image: the image.
1584%
1585% o columns: the number of columns in the scaled image.
1586%
1587% o rows: the number of rows in the scaled image.
1588%
1589% o filter: Image filter to use.
1590%
1591% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
1592% Typically set this to 1.0.
1593%
1594% o exception: return any errors or warnings in this structure.
1595%
1596*/
1597
1598typedef struct _ContributionInfo
1599{
1600 MagickRealType
1601 weight;
1602
1603 long
1604 pixel;
1605} ContributionInfo;
1606
1607static ContributionInfo **DestroyContributionThreadSet(
1608 ContributionInfo **contribution)
1609{
1610 register long
1611 i;
1612
1613 assert(contribution != (ContributionInfo **) NULL);
1614 for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
1615 if (contribution[i] != (ContributionInfo *) NULL)
1616 contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
1617 contribution[i]);
1618 contribution=(ContributionInfo **) RelinquishAlignedMemory(contribution);
1619 return(contribution);
1620}
1621
1622static ContributionInfo **AcquireContributionThreadSet(const size_t count)
1623{
1624 register long
1625 i;
1626
1627 ContributionInfo
1628 **contribution;
1629
1630 unsigned long
1631 number_threads;
1632
1633 number_threads=GetOpenMPMaximumThreads();
1634 contribution=(ContributionInfo **) AcquireAlignedMemory(number_threads,
1635 sizeof(*contribution));
1636 if (contribution == (ContributionInfo **) NULL)
1637 return((ContributionInfo **) NULL);
1638 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
1639 for (i=0; i < (long) number_threads; i++)
1640 {
1641 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
1642 sizeof(**contribution));
1643 if (contribution[i] == (ContributionInfo *) NULL)
1644 return(DestroyContributionThreadSet(contribution));
1645 }
1646 return(contribution);
1647}
1648
1649static inline double MagickMax(const double x,const double y)
1650{
1651 if (x > y)
1652 return(x);
1653 return(y);
1654}
1655
1656static inline double MagickMin(const double x,const double y)
1657{
1658 if (x < y)
1659 return(x);
1660 return(y);
1661}
1662
1663static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
1664 const Image *image,Image *resize_image,const MagickRealType x_factor,
1665 const MagickSizeType span,MagickOffsetType *quantum,ExceptionInfo *exception)
1666{
1667#define ResizeImageTag "Resize/Image"
1668
1669 ClassType
1670 storage_class;
1671
1672 ContributionInfo
1673 **contributions;
1674
1675 long
1676 x;
1677
1678 MagickBooleanType
1679 status;
1680
1681 MagickPixelPacket
1682 zero;
1683
1684 MagickRealType
1685 scale,
1686 support;
1687
1688 CacheView
1689 *image_view,
1690 *resize_view;
1691
1692 /*
1693 Apply filter to resize horizontally from image to resize image.
1694 */
1695 scale=MagickMax(1.0/x_factor,1.0);
1696 support=scale*GetResizeFilterSupport(resize_filter);
1697 storage_class=support > 0.5 ? DirectClass : image->storage_class;
1698 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
1699 {
1700 InheritException(exception,&resize_image->exception);
1701 return(MagickFalse);
1702 }
1703 if (support < 0.5)
1704 {
1705 /*
1706 Support too small even for nearest neighbour: reduce to point sampling.
1707 */
1708 support=(MagickRealType) 0.5;
1709 scale=1.0;
1710 }
1711 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
1712 if (contributions == (ContributionInfo **) NULL)
1713 {
1714 (void) ThrowMagickException(exception,GetMagickModule(),
1715 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
1716 return(MagickFalse);
1717 }
1718 status=MagickTrue;
1719 scale=1.0/scale;
1720 (void) ResetMagickMemory(&zero,0,sizeof(zero));
1721 image_view=AcquireCacheView(image);
1722 resize_view=AcquireCacheView(resize_image);
1723#if defined(MAGICKCORE_OPENMP_SUPPORT)
1724 #pragma omp parallel for shared(status)
1725#endif
1726 for (x=0; x < (long) resize_image->columns; x++)
1727 {
1728 long
1729 n,
1730 start,
1731 stop;
1732
1733 MagickRealType
1734 center,
1735 density;
1736
1737 register const IndexPacket
1738 *__restrict indexes;
1739
1740 register const PixelPacket
1741 *__restrict p;
1742
1743 register ContributionInfo
1744 *__restrict contribution;
1745
1746 register IndexPacket
1747 *__restrict resize_indexes;
1748
1749 register long
1750 y;
1751
1752 register PixelPacket
1753 *__restrict q;
1754
1755 if (status == MagickFalse)
1756 continue;
1757 center=(MagickRealType) (x+0.5)/x_factor;
1758 start=(long) (MagickMax(center-support-MagickEpsilon,0.0)+0.5);
1759 stop=(long) (MagickMin(center+support,(double) image->columns)+0.5);
1760 density=0.0;
1761 contribution=contributions[GetOpenMPThreadId()];
1762 for (n=0; n < (stop-start); n++)
1763 {
1764 contribution[n].pixel=start+n;
1765 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
1766 ((MagickRealType) (start+n)-center+0.5));
1767 density+=contribution[n].weight;
1768 }
1769 if ((density != 0.0) && (density != 1.0))
1770 {
1771 register long
1772 i;
1773
1774 /*
1775 Normalize.
1776 */
1777 density=1.0/density;
1778 for (i=0; i < n; i++)
1779 contribution[i].weight*=density;
1780 }
1781 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,
1782 (unsigned long) (contribution[n-1].pixel-contribution[0].pixel+1),
1783 image->rows,exception);
1784 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
1785 exception);
1786 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1787 {
1788 status=MagickFalse;
1789 continue;
1790 }
1791 indexes=GetCacheViewVirtualIndexQueue(image_view);
1792 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
1793 for (y=0; y < (long) resize_image->rows; y++)
1794 {
1795 long
1796 j;
1797
1798 MagickPixelPacket
1799 pixel;
1800
1801 MagickRealType
1802 alpha;
1803
1804 register long
1805 i;
1806
1807 pixel=zero;
1808 if (image->matte == MagickFalse)
1809 {
1810 for (i=0; i < n; i++)
1811 {
1812 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
1813 (contribution[i].pixel-contribution[0].pixel);
1814 alpha=contribution[i].weight;
1815 pixel.red+=alpha*(p+j)->red;
1816 pixel.green+=alpha*(p+j)->green;
1817 pixel.blue+=alpha*(p+j)->blue;
1818 pixel.opacity+=alpha*(p+j)->opacity;
1819 }
1820 q->red=RoundToQuantum(pixel.red);
1821 q->green=RoundToQuantum(pixel.green);
1822 q->blue=RoundToQuantum(pixel.blue);
1823 q->opacity=RoundToQuantum(pixel.opacity);
1824 if ((image->colorspace == CMYKColorspace) &&
1825 (resize_image->colorspace == CMYKColorspace))
1826 {
1827 for (i=0; i < n; i++)
1828 {
1829 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
1830 (contribution[i].pixel-contribution[0].pixel);
1831 alpha=contribution[i].weight;
1832 pixel.index+=alpha*indexes[j];
1833 }
1834 resize_indexes[y]=(IndexPacket) RoundToQuantum(pixel.index);
1835 }
1836 }
1837 else
1838 {
1839 MagickRealType
1840 gamma;
1841
1842 gamma=0.0;
1843 for (i=0; i < n; i++)
1844 {
1845 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
1846 (contribution[i].pixel-contribution[0].pixel);
1847 alpha=contribution[i].weight*QuantumScale*((MagickRealType)
1848 QuantumRange-(p+j)->opacity);
1849 pixel.red+=alpha*(p+j)->red;
1850 pixel.green+=alpha*(p+j)->green;
1851 pixel.blue+=alpha*(p+j)->blue;
1852 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
1853 gamma+=alpha;
1854 }
1855 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1856 q->red=RoundToQuantum(gamma*pixel.red);
1857 q->green=RoundToQuantum(gamma*pixel.green);
1858 q->blue=RoundToQuantum(gamma*pixel.blue);
1859 q->opacity=RoundToQuantum(pixel.opacity);
1860 if ((image->colorspace == CMYKColorspace) &&
1861 (resize_image->colorspace == CMYKColorspace))
1862 {
1863 for (i=0; i < n; i++)
1864 {
1865 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
1866 (contribution[i].pixel-contribution[0].pixel);
1867 alpha=contribution[i].weight*QuantumScale*((MagickRealType)
1868 QuantumRange-(p+j)->opacity);
1869 gamma+=alpha;
1870 }
1871 resize_indexes[y]=(IndexPacket) RoundToQuantum(gamma*pixel.index);
1872 }
1873 }
1874 if ((resize_image->storage_class == PseudoClass) &&
1875 (image->storage_class == PseudoClass))
1876 {
1877 i=(long) (MagickMin(MagickMax(center,(double) start),(double) stop-
1878 1.0)+0.5);
1879 j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
1880 (contribution[i-start].pixel-contribution[0].pixel);
1881 resize_indexes[y]=indexes[j];
1882 }
1883 q++;
1884 }
1885 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
1886 status=MagickFalse;
1887 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1888 {
1889 MagickBooleanType
1890 proceed;
1891
1892#if defined(MAGICKCORE_OPENMP_SUPPORT)
1893 #pragma omp critical (MagickCore_HorizontalFilter)
1894#endif
1895 proceed=SetImageProgress(image,ResizeImageTag,(*quantum)++,span);
1896 if (proceed == MagickFalse)
1897 status=MagickFalse;
1898 }
1899 }
1900 resize_view=DestroyCacheView(resize_view);
1901 image_view=DestroyCacheView(image_view);
1902 contributions=DestroyContributionThreadSet(contributions);
1903 return(status);
1904}
1905
1906static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
1907 const Image *image,Image *resize_image,const MagickRealType y_factor,
1908 const MagickSizeType span,MagickOffsetType *quantum,ExceptionInfo *exception)
1909{
1910 ClassType
1911 storage_class;
1912
1913 ContributionInfo
1914 **contributions;
1915
1916 long
1917 y;
1918
1919 MagickBooleanType
1920 status;
1921
1922 MagickPixelPacket
1923 zero;
1924
1925 MagickRealType
1926 scale,
1927 support;
1928
1929 CacheView
1930 *image_view,
1931 *resize_view;
1932
1933 /*
1934 Apply filter to resize vertically from image to resize_image.
1935 */
1936 scale=MagickMax(1.0/y_factor,1.0);
1937 support=scale*GetResizeFilterSupport(resize_filter);
1938 storage_class=support > 0.5 ? DirectClass : image->storage_class;
1939 if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
1940 {
1941 InheritException(exception,&resize_image->exception);
1942 return(MagickFalse);
1943 }
1944 if (support < 0.5)
1945 {
1946 /*
1947 Support too small even for nearest neighbour: reduce to point sampling.
1948 */
1949 support=(MagickRealType) 0.5;
1950 scale=1.0;
1951 }
1952 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
1953 if (contributions == (ContributionInfo **) NULL)
1954 {
1955 (void) ThrowMagickException(exception,GetMagickModule(),
1956 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
1957 return(MagickFalse);
1958 }
1959 status=MagickTrue;
1960 scale=1.0/scale;
1961 (void) ResetMagickMemory(&zero,0,sizeof(zero));
1962 image_view=AcquireCacheView(image);
1963 resize_view=AcquireCacheView(resize_image);
1964#if defined(MAGICKCORE_OPENMP_SUPPORT)
1965 #pragma omp parallel for shared(status)
1966#endif
1967 for (y=0; y < (long) resize_image->rows; y++)
1968 {
1969 long
1970 n,
1971 start,
1972 stop;
1973
1974 MagickRealType
1975 center,
1976 density;
1977
1978 register const IndexPacket
1979 *__restrict indexes;
1980
1981 register const PixelPacket
1982 *__restrict p;
1983
1984 register ContributionInfo
1985 *__restrict contribution;
1986
1987 register IndexPacket
1988 *__restrict resize_indexes;
1989
1990 register long
1991 x;
1992
1993 register PixelPacket
1994 *__restrict q;
1995
1996 if (status == MagickFalse)
1997 continue;
1998 center=(MagickRealType) (y+0.5)/y_factor;
1999 start=(long) (MagickMax(center-support-MagickEpsilon,0.0)+0.5);
2000 stop=(long) (MagickMin(center+support,(double) image->rows)+0.5);
2001 density=0.0;
2002 contribution=contributions[GetOpenMPThreadId()];
2003 for (n=0; n < (stop-start); n++)
2004 {
2005 contribution[n].pixel=start+n;
2006 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
2007 ((MagickRealType) (start+n)-center+0.5));
2008 density+=contribution[n].weight;
2009 }
2010 if ((density != 0.0) && (density != 1.0))
2011 {
2012 register long
2013 i;
2014
2015 /*
2016 Normalize.
2017 */
2018 density=1.0/density;
2019 for (i=0; i < n; i++)
2020 contribution[i].weight*=density;
2021 }
2022 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
2023 image->columns,(unsigned long) (contribution[n-1].pixel-
2024 contribution[0].pixel+1),exception);
2025 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
2026 exception);
2027 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2028 {
2029 status=MagickFalse;
2030 continue;
2031 }
2032 indexes=GetCacheViewVirtualIndexQueue(image_view);
2033 resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
2034 for (x=0; x < (long) resize_image->columns; x++)
2035 {
2036 long
2037 j;
2038
2039 MagickPixelPacket
2040 pixel;
2041
2042 MagickRealType
2043 alpha;
2044
2045 register long
2046 i;
2047
2048 pixel=zero;
2049 if (image->matte == MagickFalse)
2050 {
2051 for (i=0; i < n; i++)
2052 {
2053 j=(long) ((contribution[i].pixel-contribution[0].pixel)*
2054 image->columns+x);
2055 alpha=contribution[i].weight;
2056 pixel.red+=alpha*(p+j)->red;
2057 pixel.green+=alpha*(p+j)->green;
2058 pixel.blue+=alpha*(p+j)->blue;
2059 pixel.opacity+=alpha*(p+j)->opacity;
2060 }
2061 q->red=RoundToQuantum(pixel.red);
2062 q->green=RoundToQuantum(pixel.green);
2063 q->blue=RoundToQuantum(pixel.blue);
2064 q->opacity=RoundToQuantum(pixel.opacity);
2065 if ((image->colorspace == CMYKColorspace) &&
2066 (resize_image->colorspace == CMYKColorspace))
2067 {
2068 for (i=0; i < n; i++)
2069 {
2070 j=(long) ((contribution[i].pixel-contribution[0].pixel)*
2071 image->columns+x);
2072 alpha=contribution[i].weight;
2073 pixel.index+=alpha*indexes[j];
2074 }
2075 resize_indexes[x]=(IndexPacket) RoundToQuantum(pixel.index);
2076 }
2077 }
2078 else
2079 {
2080 MagickRealType
2081 gamma;
2082
2083 gamma=0.0;
2084 for (i=0; i < n; i++)
2085 {
2086 j=(long) ((contribution[i].pixel-contribution[0].pixel)*
2087 image->columns+x);
2088 alpha=contribution[i].weight*QuantumScale*((MagickRealType)
2089 QuantumRange-(p+j)->opacity);
2090 pixel.red+=alpha*(p+j)->red;
2091 pixel.green+=alpha*(p+j)->green;
2092 pixel.blue+=alpha*(p+j)->blue;
2093 pixel.opacity+=contribution[i].weight*(p+j)->opacity;
2094 gamma+=alpha;
2095 }
2096 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2097 q->red=RoundToQuantum(gamma*pixel.red);
2098 q->green=RoundToQuantum(gamma*pixel.green);
2099 q->blue=RoundToQuantum(gamma*pixel.blue);
2100 q->opacity=RoundToQuantum(pixel.opacity);
2101 if ((image->colorspace == CMYKColorspace) &&
2102 (resize_image->colorspace == CMYKColorspace))
2103 {
2104 for (i=0; i < n; i++)
2105 {
2106 j=(long) ((contribution[i].pixel-contribution[0].pixel)*
2107 image->columns+x);
2108 alpha=contribution[i].weight*QuantumScale*((MagickRealType)
2109 QuantumRange-(p+j)->opacity);
2110 pixel.index+=alpha*indexes[j];
2111 }
2112 resize_indexes[x]=(IndexPacket) RoundToQuantum(gamma*pixel.index);
2113 }
2114 }
2115 if ((resize_image->storage_class == PseudoClass) &&
2116 (image->storage_class == PseudoClass))
2117 {
2118 i=(long) (MagickMin(MagickMax(center,(double) start),(double) stop-
2119 1.0)+0.5);
2120 j=(long) ((contribution[i-start].pixel-contribution[0].pixel)*
2121 image->columns+x);
2122 resize_indexes[x]=indexes[j];
2123 }
2124 q++;
2125 }
2126 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
2127 status=MagickFalse;
2128 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2129 {
2130 MagickBooleanType
2131 proceed;
2132
2133#if defined(MAGICKCORE_OPENMP_SUPPORT)
2134 #pragma omp critical (MagickCore_VerticalFilter)
2135#endif
2136 proceed=SetImageProgress(image,ResizeImageTag,(*quantum)++,span);
2137 if (proceed == MagickFalse)
2138 status=MagickFalse;
2139 }
2140 }
2141 resize_view=DestroyCacheView(resize_view);
2142 image_view=DestroyCacheView(image_view);
2143 contributions=DestroyContributionThreadSet(contributions);
2144 return(status);
2145}
2146
2147MagickExport Image *ResizeImage(const Image *image,const unsigned long columns,
2148 const unsigned long rows,const FilterTypes filter,const double blur,
2149 ExceptionInfo *exception)
2150{
2151#define WorkLoadFactor 0.265
2152
2153 FilterTypes
2154 filter_type;
2155
2156 Image
2157 *filter_image,
2158 *resize_image;
2159
2160 MagickRealType
2161 x_factor,
2162 y_factor;
2163
2164 MagickSizeType
2165 span;
2166
2167 MagickStatusType
2168 status;
2169
2170 ResizeFilter
2171 *resize_filter;
2172
2173 MagickOffsetType
2174 quantum;
2175
2176 /*
2177 Acquire resize image.
2178 */
2179 assert(image != (Image *) NULL);
2180 assert(image->signature == MagickSignature);
2181 if (image->debug != MagickFalse)
2182 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2183 assert(exception != (ExceptionInfo *) NULL);
2184 assert(exception->signature == MagickSignature);
2185 if ((columns == 0) || (rows == 0))
2186 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2187 if ((columns == image->columns) && (rows == image->rows) &&
2188 (filter == UndefinedFilter) && (blur == 1.0))
2189 return(CloneImage(image,0,0,MagickTrue,exception));
2190 resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
2191 if (resize_image == (Image *) NULL)
2192 return(resize_image);
2193 /*
2194 Acquire resize filter.
2195 */
2196 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2197 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2198 if ((x_factor*y_factor) > WorkLoadFactor)
2199 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
2200 else
2201 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
2202 if (filter_image == (Image *) NULL)
2203 return(DestroyImage(resize_image));
2204 filter_type=LanczosFilter;
2205 if (filter != UndefinedFilter)
2206 filter_type=filter;
2207 else
2208 if ((x_factor == 1.0) && (y_factor == 1.0))
2209 filter_type=PointFilter;
2210 else
2211 if ((image->storage_class == PseudoClass) ||
2212 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
2213 filter_type=MitchellFilter;
2214 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
2215 exception);
2216 /*
2217 Resize image.
2218 */
2219 quantum=0;
2220 if ((x_factor*y_factor) > WorkLoadFactor)
2221 {
2222 span=(MagickSizeType) (filter_image->columns+rows);
2223 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
2224 &quantum,exception);
2225 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
2226 span,&quantum,exception);
2227 }
2228 else
2229 {
2230 span=(MagickSizeType) (filter_image->rows+columns);
2231 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
2232 &quantum,exception);
2233 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
2234 span,&quantum,exception);
2235 }
2236 /*
2237 Free resources.
2238 */
2239 filter_image=DestroyImage(filter_image);
2240 resize_filter=DestroyResizeFilter(resize_filter);
2241 if ((status == MagickFalse) || (resize_image == (Image *) NULL))
2242 return((Image *) NULL);
2243 resize_image->type=image->type;
2244 return(resize_image);
2245}
2246
2247/*
2248%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2249% %
2250% %
2251% %
2252% S a m p l e I m a g e %
2253% %
2254% %
2255% %
2256%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2257%
2258% SampleImage() scales an image to the desired dimensions with pixel
2259% sampling. Unlike other scaling methods, this method does not introduce
2260% any additional color into the scaled image.
2261%
2262% The format of the SampleImage method is:
2263%
2264% Image *SampleImage(const Image *image,const unsigned long columns,
2265% const unsigned long rows,ExceptionInfo *exception)
2266%
2267% A description of each parameter follows:
2268%
2269% o image: the image.
2270%
2271% o columns: the number of columns in the sampled image.
2272%
2273% o rows: the number of rows in the sampled image.
2274%
2275% o exception: return any errors or warnings in this structure.
2276%
2277*/
2278MagickExport Image *SampleImage(const Image *image,const unsigned long columns,
2279 const unsigned long rows,ExceptionInfo *exception)
2280{
2281#define SampleImageTag "Sample/Image"
2282
2283 Image
2284 *sample_image;
2285
2286 long
2287 progress,
2288 *x_offset,
2289 y;
2290
2291 MagickBooleanType
2292 status;
2293
2294 register long
2295 x;
2296
2297 CacheView
2298 *image_view,
2299 *sample_view;
2300
2301 /*
2302 Initialize sampled image attributes.
2303 */
2304 assert(image != (const Image *) NULL);
2305 assert(image->signature == MagickSignature);
2306 if (image->debug != MagickFalse)
2307 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2308 assert(exception != (ExceptionInfo *) NULL);
2309 assert(exception->signature == MagickSignature);
2310 if ((columns == 0) || (rows == 0))
2311 ThrowImageException(ImageError,"NegativeOrZeroImageSize");
2312 if ((columns == image->columns) && (rows == image->rows))
2313 return(CloneImage(image,0,0,MagickTrue,exception));
2314 sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
2315 if (sample_image == (Image *) NULL)
2316 return((Image *) NULL);
2317 /*
2318 Allocate scan line buffer and column offset buffers.
2319 */
2320 x_offset=(long *) AcquireQuantumMemory((size_t) sample_image->columns,
2321 sizeof(*x_offset));
2322 if (x_offset == (long *) NULL)
2323 {
2324 sample_image=DestroyImage(sample_image);
2325 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2326 }
2327 for (x=0; x < (long) sample_image->columns; x++)
2328 x_offset[x]=(long) (((MagickRealType) x+0.5)*image->columns/
2329 sample_image->columns);
2330 /*
2331 Sample each row.
2332 */
2333 status=MagickTrue;
2334 progress=0;
2335 image_view=AcquireCacheView(image);
2336 sample_view=AcquireCacheView(sample_image);
2337#if defined(MAGICKCORE_OPENMP_SUPPORT)
2338 #pragma omp parallel for schedule(dynamic) shared(progress,status)
2339#endif
2340 for (y=0; y < (long) sample_image->rows; y++)
2341 {
2342 long
2343 y_offset;
2344
2345 register const IndexPacket
2346 *__restrict indexes;
2347
2348 register const PixelPacket
2349 *__restrict p;
2350
2351 register IndexPacket
2352 *__restrict sample_indexes;
2353
2354 register long
2355 x;
2356
2357 register PixelPacket
2358 *__restrict q;
2359
2360 if (status == MagickFalse)
2361 continue;
2362 y_offset=(long) (((MagickRealType) y+0.5)*image->rows/sample_image->rows);
2363 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
2364 exception);
2365 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
2366 exception);
2367 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
2368 {
2369 status=MagickFalse;
2370 continue;
2371 }
2372 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2373 sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
2374 /*
2375 Sample each column.
2376 */
2377 for (x=0; x < (long) sample_image->columns; x++)
2378 *q++=p[x_offset[x]];
2379 if ((image->storage_class == PseudoClass) ||
2380 (image->colorspace == CMYKColorspace))
2381 for (x=0; x < (long) sample_image->columns; x++)
2382 sample_indexes[x]=indexes[x_offset[x]];
2383 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
2384 status=MagickFalse;
2385 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2386 {
2387 MagickBooleanType
2388 proceed;
2389
2390#if defined(MAGICKCORE_OPENMP_SUPPORT)
2391 #pragma omp critical (MagickCore_SampleImage)
2392#endif
2393 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
2394 if (proceed == MagickFalse)
2395 status=MagickFalse;
2396 }
2397 }
2398 image_view=DestroyCacheView(image_view);
2399 sample_view=DestroyCacheView(sample_view);
2400 x_offset=(long *) RelinquishMagickMemory(x_offset);
2401 sample_image->type=image->type;
2402 return(sample_image);
2403}
2404
2405/*
2406%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2407% %
2408% %
2409% %
2410% S c a l e I m a g e %
2411% %
2412% %
2413% %
2414%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2415%
2416% ScaleImage() changes the size of an image to the given dimensions.
2417%
2418% The format of the ScaleImage method is:
2419%
2420% Image *ScaleImage(const Image *image,const unsigned long columns,
2421% const unsigned long rows,ExceptionInfo *exception)
2422%
2423% A description of each parameter follows:
2424%
2425% o image: the image.
2426%
2427% o columns: the number of columns in the scaled image.
2428%
2429% o rows: the number of rows in the scaled image.
2430%
2431% o exception: return any errors or warnings in this structure.
2432%
2433*/
2434MagickExport Image *ScaleImage(const Image *image,const unsigned long columns,
2435 const unsigned long rows,ExceptionInfo *exception)
2436{
2437#define ScaleImageTag "Scale/Image"
2438
2439 Image
2440 *scale_image;
2441
2442 long
2443 number_rows,
2444 y;
2445
2446 MagickBooleanType
2447 next_column,
2448 next_row,
2449 proceed;
2450
2451 MagickPixelPacket
2452 pixel,
2453 *scale_scanline,
2454 *scanline,
2455 *x_vector,
2456 *y_vector,
2457 zero;
2458
2459 MagickRealType
2460 alpha,
2461 gamma;
2462
2463 PointInfo
2464 scale,
2465 span;
2466
2467 register long
2468 i;
2469
2470 /*
2471 Initialize scaled image attributes.
2472 */
2473 assert(image != (const Image *) NULL);
2474 assert(image->signature == MagickSignature);
2475 if (image->debug != MagickFalse)
2476 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2477 assert(exception != (ExceptionInfo *) NULL);
2478 assert(exception->signature == MagickSignature);
2479 if ((columns == 0) || (rows == 0))
2480 return((Image *) NULL);
2481 if ((columns == image->columns) && (rows == image->rows))
2482 return(CloneImage(image,0,0,MagickTrue,exception));
2483 scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
2484 if (scale_image == (Image *) NULL)
2485 return((Image *) NULL);
2486 if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
2487 {
2488 InheritException(exception,&scale_image->exception);
2489 scale_image=DestroyImage(scale_image);
2490 return((Image *) NULL);
2491 }
2492 /*
2493 Allocate memory.
2494 */
2495 x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2496 sizeof(*x_vector));
2497 scanline=x_vector;
2498 if (image->rows != scale_image->rows)
2499 scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2500 sizeof(*scanline));
2501 scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
2502 scale_image->columns,sizeof(*scale_scanline));
2503 y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
2504 sizeof(*y_vector));
2505 if ((scanline == (MagickPixelPacket *) NULL) ||
2506 (scale_scanline == (MagickPixelPacket *) NULL) ||
2507 (x_vector == (MagickPixelPacket *) NULL) ||
2508 (y_vector == (MagickPixelPacket *) NULL))
2509 {
2510 scale_image=DestroyImage(scale_image);
2511 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2512 }
2513 /*
2514 Scale image.
2515 */
2516 number_rows=0;
2517 next_row=MagickTrue;
2518 span.y=1.0;
2519 scale.y=(double) scale_image->rows/(double) image->rows;
2520 (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
2521 sizeof(*y_vector));
2522 GetMagickPixelPacket(image,&pixel);
2523 (void) ResetMagickMemory(&zero,0,sizeof(zero));
2524 i=0;
2525 for (y=0; y < (long) scale_image->rows; y++)
2526 {
2527 register const IndexPacket
2528 *__restrict indexes;
2529
2530 register const PixelPacket
2531 *__restrict p;
2532
2533 register IndexPacket
2534 *__restrict scale_indexes;
2535
2536 register long
2537 x;
2538
2539 register MagickPixelPacket
2540 *__restrict s,
2541 *__restrict t;
2542
2543 register PixelPacket
2544 *__restrict q;
2545
2546 q=QueueAuthenticPixels(scale_image,0,y,scale_image->columns,1,exception);
2547 if (q == (PixelPacket *) NULL)
2548 break;
2549 scale_indexes=GetAuthenticIndexQueue(scale_image);
2550 if (scale_image->rows == image->rows)
2551 {
2552 /*
2553 Read a new scanline.
2554 */
2555 p=GetVirtualPixels(image,0,i++,image->columns,1,exception);
2556 if (p == (const PixelPacket *) NULL)
2557 break;
2558 indexes=GetVirtualIndexQueue(image);
2559 for (x=0; x < (long) image->columns; x++)
2560 {
2561 x_vector[x].red=(MagickRealType) p->red;
2562 x_vector[x].green=(MagickRealType) p->green;
2563 x_vector[x].blue=(MagickRealType) p->blue;
2564 if (image->matte != MagickFalse)
2565 x_vector[x].opacity=(MagickRealType) p->opacity;
2566 if (indexes != (IndexPacket *) NULL)
2567 x_vector[x].index=(MagickRealType) indexes[x];
2568 p++;
2569 }
2570 }
2571 else
2572 {
2573 /*
2574 Scale Y direction.
2575 */
2576 while (scale.y < span.y)
2577 {
2578 if ((next_row != MagickFalse) && (number_rows < (long) image->rows))
2579 {
2580 /*
2581 Read a new scanline.
2582 */
2583 p=GetVirtualPixels(image,0,i++,image->columns,1,exception);
2584 if (p == (const PixelPacket *) NULL)
2585 break;
2586 indexes=GetVirtualIndexQueue(image);
2587 for (x=0; x < (long) image->columns; x++)
2588 {
2589 x_vector[x].red=(MagickRealType) p->red;
2590 x_vector[x].green=(MagickRealType) p->green;
2591 x_vector[x].blue=(MagickRealType) p->blue;
2592 if (image->matte != MagickFalse)
2593 x_vector[x].opacity=(MagickRealType) p->opacity;
2594 if (indexes != (IndexPacket *) NULL)
2595 x_vector[x].index=(MagickRealType) indexes[x];
2596 p++;
2597 }
2598 number_rows++;
2599 }
2600 for (x=0; x < (long) image->columns; x++)
2601 {
2602 y_vector[x].red+=scale.y*x_vector[x].red;
2603 y_vector[x].green+=scale.y*x_vector[x].green;
2604 y_vector[x].blue+=scale.y*x_vector[x].blue;
2605 if (scale_image->matte != MagickFalse)
2606 y_vector[x].opacity+=scale.y*x_vector[x].opacity;
2607 if (scale_indexes != (IndexPacket *) NULL)
2608 y_vector[x].index+=scale.y*x_vector[x].index;
2609 }
2610 span.y-=scale.y;
2611 scale.y=(double) scale_image->rows/(double) image->rows;
2612 next_row=MagickTrue;
2613 }
2614 if ((next_row != MagickFalse) && (number_rows < (long) image->rows))
2615 {
2616 /*
2617 Read a new scanline.
2618 */
2619 p=GetVirtualPixels(image,0,i++,image->columns,1,exception);
2620 if (p == (const PixelPacket *) NULL)
2621 break;
2622 indexes=GetVirtualIndexQueue(image);
2623 for (x=0; x < (long) image->columns; x++)
2624 {
2625 x_vector[x].red=(MagickRealType) p->red;
2626 x_vector[x].green=(MagickRealType) p->green;
2627 x_vector[x].blue=(MagickRealType) p->blue;
2628 if (image->matte != MagickFalse)
2629 x_vector[x].opacity=(MagickRealType) p->opacity;
2630 if (indexes != (IndexPacket *) NULL)
2631 x_vector[x].index=(MagickRealType) indexes[x];
2632 p++;
2633 }
2634 number_rows++;
2635 next_row=MagickFalse;
2636 }
2637 s=scanline;
2638 for (x=0; x < (long) image->columns; x++)
2639 {
2640 pixel.red=y_vector[x].red+span.y*x_vector[x].red;
2641 pixel.green=y_vector[x].green+span.y*x_vector[x].green;
2642 pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
2643 if (image->matte != MagickFalse)
2644 pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
2645 if (scale_indexes != (IndexPacket *) NULL)
2646 pixel.index=y_vector[x].index+span.y*x_vector[x].index;
2647 s->red=pixel.red;
2648 s->green=pixel.green;
2649 s->blue=pixel.blue;
2650 if (scale_image->matte != MagickFalse)
2651 s->opacity=pixel.opacity;
2652 if (scale_indexes != (IndexPacket *) NULL)
2653 s->index=pixel.index;
2654 s++;
2655 y_vector[x]=zero;
2656 }
2657 scale.y-=span.y;
2658 if (scale.y <= 0)
2659 {
2660 scale.y=(double) scale_image->rows/(double) image->rows;
2661 next_row=MagickTrue;
2662 }
2663 span.y=1.0;
2664 }
2665 if (scale_image->columns == image->columns)
2666 {
2667 /*
2668 Transfer scanline to scaled image.
2669 */
2670 s=scanline;
2671 for (x=0; x < (long) scale_image->columns; x++)
2672 {
2673 q->red=RoundToQuantum(s->red);
2674 q->green=RoundToQuantum(s->green);
2675 q->blue=RoundToQuantum(s->blue);
2676 if (scale_image->matte != MagickFalse)
2677 q->opacity=RoundToQuantum(s->opacity);
2678 if (scale_indexes != (IndexPacket *) NULL)
2679 scale_indexes[x]=(IndexPacket) RoundToQuantum(s->index);
2680 q++;
2681 s++;
2682 }
2683 }
2684 else
2685 {
2686 /*
2687 Scale X direction.
2688 */
2689 pixel=zero;
2690 next_column=MagickFalse;
2691 span.x=1.0;
2692 s=scanline;
2693 t=scale_scanline;
2694 for (x=0; x < (long) image->columns; x++)
2695 {
2696 scale.x=(double) scale_image->columns/(double) image->columns;
2697 while (scale.x >= span.x)
2698 {
2699 if (next_column != MagickFalse)
2700 {
2701 pixel=zero;
2702 t++;
2703 }
2704 pixel.red+=span.x*s->red;
2705 pixel.green+=span.x*s->green;
2706 pixel.blue+=span.x*s->blue;
2707 if (image->matte != MagickFalse)
2708 pixel.opacity+=span.x*s->opacity;
2709 if (scale_indexes != (IndexPacket *) NULL)
2710 pixel.index+=span.x*s->index;
2711 t->red=pixel.red;
2712 t->green=pixel.green;
2713 t->blue=pixel.blue;
2714 if (scale_image->matte != MagickFalse)
2715 t->opacity=pixel.opacity;
2716 if (scale_indexes != (IndexPacket *) NULL)
2717 t->index=pixel.index;
2718 scale.x-=span.x;
2719 span.x=1.0;
2720 next_column=MagickTrue;
2721 }
2722 if (scale.x > 0)
2723 {
2724 if (next_column != MagickFalse)
2725 {
2726 pixel=zero;
2727 next_column=MagickFalse;
2728 t++;
2729 }
2730 pixel.red+=scale.x*s->red;
2731 pixel.green+=scale.x*s->green;
2732 pixel.blue+=scale.x*s->blue;
2733 if (scale_image->matte != MagickFalse)
2734 pixel.opacity+=scale.x*s->opacity;
2735 if (scale_indexes != (IndexPacket *) NULL)
2736 pixel.index+=scale.x*s->index;
2737 span.x-=scale.x;
2738 }
2739 s++;
2740 }
2741 if (span.x > 0)
2742 {
2743 s--;
2744 pixel.red+=span.x*s->red;
2745 pixel.green+=span.x*s->green;
2746 pixel.blue+=span.x*s->blue;
2747 if (scale_image->matte != MagickFalse)
2748 pixel.opacity+=span.x*s->opacity;
2749 if (scale_indexes != (IndexPacket *) NULL)
2750 pixel.index+=span.x*s->index;
2751 }
2752 if ((next_column == MagickFalse) &&
2753 ((long) (t-scale_scanline) < (long) scale_image->columns))
2754 {
2755 t->red=pixel.red;
2756 t->green=pixel.green;
2757 t->blue=pixel.blue;
2758 if (scale_image->matte != MagickFalse)
2759 t->opacity=pixel.opacity;
2760 if (scale_indexes != (IndexPacket *) NULL)
2761 t->index=pixel.index;
2762 }
2763 /*
2764 Transfer scanline to scaled image.
2765 */
2766 t=scale_scanline;
2767 for (x=0; x < (long) scale_image->columns; x++)
2768 {
2769 alpha=1.0;
2770 if (image->matte != MagickFalse)
2771 alpha=(MagickRealType) (QuantumScale*(QuantumRange-t->opacity));
2772 gamma=1.0/(fabs((double) alpha) <= MagickEpsilon ? 1.0 : alpha);
2773 q->red=RoundToQuantum(gamma*t->red);
2774 q->green=RoundToQuantum(gamma*t->green);
2775 q->blue=RoundToQuantum(gamma*t->blue);
2776 if (scale_image->matte != MagickFalse)
2777 q->opacity=RoundToQuantum(t->opacity);
2778 if (scale_indexes != (IndexPacket *) NULL)
2779 scale_indexes[x]=(IndexPacket) RoundToQuantum(gamma*t->index);
2780 t++;
2781 q++;
2782 }
2783 }
2784 if (SyncAuthenticPixels(scale_image,exception) == MagickFalse)
2785 break;
2786 proceed=SetImageProgress(image,ScaleImageTag,y,image->rows);
2787 if (proceed == MagickFalse)
2788 break;
2789 }
2790 /*
2791 Free allocated memory.
2792 */
2793 y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
2794 scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
2795 if (scale_image->rows != image->rows)
2796 scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
2797 x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
2798 scale_image->type=image->type;
2799 return(scale_image);
2800}
2801
2802/*
2803%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2804% %
2805% %
2806% %
2807+ S e t R e s i z e F i l t e r S u p p o r t %
2808% %
2809% %
2810% %
2811%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2812%
2813% SetResizeFilterSupport() specifies which IR filter to use to window
2814%
2815% The format of the SetResizeFilterSupport method is:
2816%
2817% void SetResizeFilterSupport(ResizeFilter *resize_filter,
2818% const MagickRealType support)
2819%
2820% A description of each parameter follows:
2821%
2822% o resize_filter: the resize filter.
2823%
2824% o support: the filter spport radius.
2825%
2826*/
2827MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
2828 const MagickRealType support)
2829{
2830 assert(resize_filter != (ResizeFilter *) NULL);
2831 assert(resize_filter->signature == MagickSignature);
2832 resize_filter->support=support;
2833}
2834
2835/*
2836%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2837% %
2838% %
2839% %
2840% T h u m b n a i l I m a g e %
2841% %
2842% %
2843% %
2844%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2845%
2846% ThumbnailImage() changes the size of an image to the given dimensions and
2847% removes any associated profiles. The goal is to produce small low cost
2848% thumbnail images suited for display on the Web.
2849%
2850% The format of the ThumbnailImage method is:
2851%
2852% Image *ThumbnailImage(const Image *image,const unsigned long columns,
2853% const unsigned long rows,ExceptionInfo *exception)
2854%
2855% A description of each parameter follows:
2856%
2857% o image: the image.
2858%
2859% o columns: the number of columns in the scaled image.
2860%
2861% o rows: the number of rows in the scaled image.
2862%
2863% o exception: return any errors or warnings in this structure.
2864%
2865*/
2866MagickExport Image *ThumbnailImage(const Image *image,
2867 const unsigned long columns,const unsigned long rows,ExceptionInfo *exception)
2868{
2869#define SampleFactor 5
2870
2871 char
2872 value[MaxTextExtent];
2873
2874 const char
2875 *name;
2876
2877 Image
2878 *thumbnail_image;
2879
2880 MagickRealType
2881 x_factor,
2882 y_factor;
2883
2884 struct stat
2885 attributes;
2886
2887 unsigned long
2888 version;
2889
2890 assert(image != (Image *) NULL);
2891 assert(image->signature == MagickSignature);
2892 if (image->debug != MagickFalse)
2893 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2894 assert(exception != (ExceptionInfo *) NULL);
2895 assert(exception->signature == MagickSignature);
2896 x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
2897 y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
2898 if ((x_factor*y_factor) > 0.1)
2899 thumbnail_image=ZoomImage(image,columns,rows,exception);
2900 else
2901 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
2902 thumbnail_image=ZoomImage(image,columns,rows,exception);
2903 else
2904 {
2905 Image
2906 *sample_image;
2907
2908 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
2909 exception);
2910 if (sample_image == (Image *) NULL)
2911 return((Image *) NULL);
2912 thumbnail_image=ZoomImage(sample_image,columns,rows,exception);
2913 sample_image=DestroyImage(sample_image);
2914 }
2915 if (thumbnail_image == (Image *) NULL)
2916 return(thumbnail_image);
2917 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
2918 if (thumbnail_image->matte == MagickFalse)
2919 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
2920 thumbnail_image->depth=8;
2921 thumbnail_image->interlace=NoInterlace;
2922 /*
2923 Strip all profiles except color profiles.
2924 */
2925 ResetImageProfileIterator(thumbnail_image);
2926 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
2927 {
2928 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
2929 {
2930 DeleteImageProfile(thumbnail_image,name);
2931 ResetImageProfileIterator(thumbnail_image);
2932 }
2933 name=GetNextImageProfile(thumbnail_image);
2934 }
2935 (void) DeleteImageProperty(thumbnail_image,"comment");
2936 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
2937 if (strstr(image->magick_filename,"///") == (char *) NULL)
2938 (void) FormatMagickString(value,MaxTextExtent,"file:///%s",
2939 image->magick_filename);
2940 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
2941 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
2942 if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
2943 {
2944 (void) FormatMagickString(value,MaxTextExtent,"%ld",(long)
2945 attributes.st_mtime);
2946 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
2947 }
2948 (void) FormatMagickString(value,MaxTextExtent,"%ld",(long)
2949 attributes.st_mtime);
2950 (void) FormatMagickSize(GetBlobSize(image),value);
2951 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
2952 (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
2953 LocaleLower(value);
2954 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
2955 (void) SetImageProperty(thumbnail_image,"software",
2956 GetMagickVersion(&version));
2957 (void) FormatMagickString(value,MaxTextExtent,"%lu",image->magick_columns);
2958 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
2959 (void) FormatMagickString(value,MaxTextExtent,"%lu",image->magick_rows);
2960 (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
2961 (void) FormatMagickString(value,MaxTextExtent,"%lu",
2962 GetImageListLength(image));
2963 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
2964 return(thumbnail_image);
2965}
2966
2967/*
2968%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2969% %
2970% %
2971% %
2972% Z o o m I m a g e %
2973% %
2974% %
2975% %
2976%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2977%
2978% ZoomImage() creates a new image that is a scaled size of an existing one.
2979% It allocates the memory necessary for the new Image structure and returns a
2980% pointer to the new image. The Point filter gives fast pixel replication,
2981% Triangle is equivalent to bi-linear interpolation, and Mitchel giver slower,
2982% very high-quality results. See Graphic Gems III for details on this
2983% algorithm.
2984%
2985% The filter member of the Image structure specifies which image filter to
2986% use. Blur specifies the blur factor where > 1 is blurry, < 1 is sharp.
2987%
2988% The format of the ZoomImage method is:
2989%
2990% Image *ZoomImage(const Image *image,const unsigned long columns,
2991% const unsigned long rows,ExceptionInfo *exception)
2992%
2993% A description of each parameter follows:
2994%
2995% o image: the image.
2996%
2997% o columns: An integer that specifies the number of columns in the zoom
2998% image.
2999%
3000% o rows: An integer that specifies the number of rows in the scaled
3001% image.
3002%
3003% o exception: return any errors or warnings in this structure.
3004%
3005*/
3006MagickExport Image *ZoomImage(const Image *image,const unsigned long columns,
3007 const unsigned long rows,ExceptionInfo *exception)
3008{
3009 Image
3010 *zoom_image;
3011
3012 assert(image != (const Image *) NULL);
3013 assert(image->signature == MagickSignature);
3014 if (image->debug != MagickFalse)
3015 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3016 assert(exception != (ExceptionInfo *) NULL);
3017 assert(exception->signature == MagickSignature);
3018 zoom_image=ResizeImage(image,columns,rows,image->filter,image->blur,
3019 exception);
3020 return(zoom_image);
3021}