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