|
MagickCore
6.7.5
|
00001 /* 00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00003 % % 00004 % % 00005 % % 00006 % RRRR EEEEE SSSSS IIIII ZZZZZ EEEEE % 00007 % R R E SS I ZZ E % 00008 % RRRR EEE SSS I ZZZ EEE % 00009 % R R E SS I ZZ E % 00010 % R R EEEEE SSSSS IIIII ZZZZZ EEEEE % 00011 % % 00012 % % 00013 % MagickCore Image Resize Methods % 00014 % % 00015 % Software Design % 00016 % John Cristy % 00017 % July 1992 % 00018 % % 00019 % % 00020 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization % 00021 % dedicated to making software imaging solutions freely available. % 00022 % % 00023 % You may not use this file except in compliance with the License. You may % 00024 % obtain a copy of the License at % 00025 % % 00026 % http://www.imagemagick.org/script/license.php % 00027 % % 00028 % Unless required by applicable law or agreed to in writing, software % 00029 % distributed under the License is distributed on an "AS IS" BASIS, % 00030 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 00031 % See the License for the specific language governing permissions and % 00032 % limitations under the License. % 00033 % % 00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00035 % 00036 % 00037 */ 00038 00039 /* 00040 Include declarations. 00041 */ 00042 #include "MagickCore/studio.h" 00043 #include "MagickCore/artifact.h" 00044 #include "MagickCore/blob.h" 00045 #include "MagickCore/cache.h" 00046 #include "MagickCore/cache-view.h" 00047 #include "MagickCore/color.h" 00048 #include "MagickCore/color-private.h" 00049 #include "MagickCore/draw.h" 00050 #include "MagickCore/exception.h" 00051 #include "MagickCore/exception-private.h" 00052 #include "MagickCore/gem.h" 00053 #include "MagickCore/image.h" 00054 #include "MagickCore/image-private.h" 00055 #include "MagickCore/list.h" 00056 #include "MagickCore/memory_.h" 00057 #include "MagickCore/magick.h" 00058 #include "MagickCore/pixel-accessor.h" 00059 #include "MagickCore/property.h" 00060 #include "MagickCore/monitor.h" 00061 #include "MagickCore/monitor-private.h" 00062 #include "MagickCore/option.h" 00063 #include "MagickCore/pixel.h" 00064 #include "MagickCore/quantum-private.h" 00065 #include "MagickCore/resample.h" 00066 #include "MagickCore/resample-private.h" 00067 #include "MagickCore/resize.h" 00068 #include "MagickCore/resize-private.h" 00069 #include "MagickCore/string_.h" 00070 #include "MagickCore/string-private.h" 00071 #include "MagickCore/thread-private.h" 00072 #include "MagickCore/utility.h" 00073 #include "MagickCore/utility-private.h" 00074 #include "MagickCore/version.h" 00075 #if defined(MAGICKCORE_LQR_DELEGATE) 00076 #include <lqr.h> 00077 #endif 00078 00079 /* 00080 Typedef declarations. 00081 */ 00082 struct _ResizeFilter 00083 { 00084 MagickRealType 00085 (*filter)(const MagickRealType,const ResizeFilter *), 00086 (*window)(const MagickRealType,const ResizeFilter *), 00087 support, /* filter region of support - the filter support limit */ 00088 window_support, /* window support, usally equal to support (expert only) */ 00089 scale, /* dimension scaling to fit window support (usally 1.0) */ 00090 blur, /* x-scale (blur-sharpen) */ 00091 coefficient[7]; /* cubic coefficents for BC-cubic spline filters */ 00092 00093 size_t 00094 signature; 00095 }; 00096 00097 /* 00098 Forward declaractions. 00099 */ 00100 static MagickRealType 00101 I0(MagickRealType x), 00102 BesselOrderOne(MagickRealType), 00103 Sinc(const MagickRealType, const ResizeFilter *), 00104 SincFast(const MagickRealType, const ResizeFilter *); 00105 00106 /* 00107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00108 % % 00109 % % 00110 % % 00111 + F i l t e r F u n c t i o n s % 00112 % % 00113 % % 00114 % % 00115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00116 % 00117 % These are the various filter and windowing functions that are provided. 00118 % 00119 % They are internal to this module only. See AcquireResizeFilterInfo() for 00120 % details of the access to these functions, via the GetResizeFilterSupport() 00121 % and GetResizeFilterWeight() API interface. 00122 % 00123 % The individual filter functions have this format... 00124 % 00125 % static MagickRealtype *FilterName(const MagickRealType x, 00126 % const MagickRealType support) 00127 % 00128 % A description of each parameter follows: 00129 % 00130 % o x: the distance from the sampling point generally in the range of 0 to 00131 % support. The GetResizeFilterWeight() ensures this a positive value. 00132 % 00133 % o resize_filter: current filter information. This allows function to 00134 % access support, and possibly other pre-calculated information defining 00135 % the functions. 00136 % 00137 */ 00138 00139 #define MagickPIL ((MagickRealType) 3.14159265358979323846264338327950288420L) 00140 00141 static MagickRealType Blackman(const MagickRealType x, 00142 const ResizeFilter *magick_unused(resize_filter)) 00143 { 00144 /* 00145 Blackman: 2nd order cosine windowing function: 00146 0.42 + 0.5 cos(pi x) + 0.08 cos(2pi x) 00147 00148 Refactored by Chantal Racette and Nicolas Robidoux to one trig call and 00149 five flops. 00150 */ 00151 const MagickRealType cosine=cos((double) (MagickPIL*x)); 00152 return(0.34+cosine*(0.5+cosine*0.16)); 00153 } 00154 00155 static MagickRealType Bohman(const MagickRealType x, 00156 const ResizeFilter *magick_unused(resize_filter)) 00157 { 00158 /* 00159 Bohman: 2rd Order cosine windowing function: 00160 (1-x) cos(pi x) + sin(pi x) / pi. 00161 00162 Refactored by Nicolas Robidoux to one trig call, one sqrt call, and 7 flops, 00163 taking advantage of the fact that the support of Bohman is 1.0 (so that we 00164 know that sin(pi x) >= 0). 00165 */ 00166 const MagickRealType cosine=cos((double) (MagickPIL*x)); 00167 const MagickRealType sine=sqrt(1.0-cosine*cosine); 00168 return((1.0-x)*cosine+(1.0/MagickPIL)*sine); 00169 } 00170 00171 static MagickRealType Box(const MagickRealType magick_unused(x), 00172 const ResizeFilter *magick_unused(resize_filter)) 00173 { 00174 /* 00175 A Box filter is a equal weighting function (all weights equal). DO NOT 00176 LIMIT results by support or resize point sampling will work as it requests 00177 points beyond its normal 0.0 support size. 00178 */ 00179 return(1.0); 00180 } 00181 00182 static MagickRealType CubicBC(const MagickRealType x, 00183 const ResizeFilter *resize_filter) 00184 { 00185 /* 00186 Cubic Filters using B,C determined values: 00187 Mitchell-Netravali B= 1/3 C= 1/3 "Balanced" cubic spline filter 00188 Catmull-Rom B= 0 C= 1/2 Interpolatory and exact on linears 00189 Cubic B-Spline B= 1 C= 0 Spline approximation of Gaussian 00190 Hermite B= 0 C= 0 Spline with small support (= 1) 00191 00192 See paper by Mitchell and Netravali, Reconstruction Filters in Computer 00193 Graphics Computer Graphics, Volume 22, Number 4, August 1988 00194 http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/ 00195 Mitchell.pdf. 00196 00197 Coefficents are determined from B,C values: 00198 P0 = ( 6 - 2*B )/6 = coeff[0] 00199 P1 = 0 00200 P2 = (-18 +12*B + 6*C )/6 = coeff[1] 00201 P3 = ( 12 - 9*B - 6*C )/6 = coeff[2] 00202 Q0 = ( 8*B +24*C )/6 = coeff[3] 00203 Q1 = ( -12*B -48*C )/6 = coeff[4] 00204 Q2 = ( 6*B +30*C )/6 = coeff[5] 00205 Q3 = ( - 1*B - 6*C )/6 = coeff[6] 00206 00207 which are used to define the filter: 00208 00209 P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1 00210 Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x < 2 00211 00212 which ensures function is continuous in value and derivative (slope). 00213 */ 00214 if (x < 1.0) 00215 return(resize_filter->coefficient[0]+x*(x*(resize_filter->coefficient[1]+x* 00216 resize_filter->coefficient[2]))); 00217 if (x < 2.0) 00218 return(resize_filter->coefficient[3]+x*(resize_filter->coefficient[4]+x* 00219 (resize_filter->coefficient[5]+x*resize_filter->coefficient[6]))); 00220 return(0.0); 00221 } 00222 00223 static MagickRealType Gaussian(const MagickRealType x, 00224 const ResizeFilter *resize_filter) 00225 { 00226 /* 00227 Gaussian with a fixed sigma = 1/2 00228 00229 Gaussian Formula (1D) ... 00230 exp( -(x^2)/((2.0*sigma^2) ) / sqrt(2*PI)sigma^2)) 00231 00232 The constants are pre-calculated... 00233 exp( -coeff[0]*(x^2)) ) * coeff[1] 00234 00235 However the multiplier coefficent (1) is not needed and not used. 00236 00237 Gaussian Formula (2D) ... 00238 exp( -(x^2)/((2.0*sigma^2) ) / (PI*sigma^2) ) 00239 00240 Note that it is only a change in the normalization multiplier which is 00241 not needed or used when gausian is used as a filter. 00242 00243 This separates the gaussian 'sigma' value from the 'blur/support' 00244 settings allowing for its use in special 'small sigma' gaussians, 00245 without the filter 'missing' pixels because the support becomes too small. 00246 */ 00247 return(exp((double)(-resize_filter->coefficient[0]*x*x))); 00248 } 00249 00250 static MagickRealType Hanning(const MagickRealType x, 00251 const ResizeFilter *magick_unused(resize_filter)) 00252 { 00253 /* 00254 Cosine window function: 00255 0.5+0.5*cos(pi*x). 00256 */ 00257 const MagickRealType cosine=cos((double) (MagickPIL*x)); 00258 return(0.5+0.5*cosine); 00259 } 00260 00261 static MagickRealType Hamming(const MagickRealType x, 00262 const ResizeFilter *magick_unused(resize_filter)) 00263 { 00264 /* 00265 Offset cosine window function: 00266 .54 + .46 cos(pi x). 00267 */ 00268 const MagickRealType cosine=cos((double) (MagickPIL*x)); 00269 return(0.54+0.46*cosine); 00270 } 00271 00272 static MagickRealType Jinc(const MagickRealType x, 00273 const ResizeFilter *magick_unused(resize_filter)) 00274 { 00275 /* 00276 See Pratt "Digital Image Processing" p.97 for Jinc/Bessel functions. 00277 http://mathworld.wolfram.com/JincFunction.html and page 11 of 00278 http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf 00279 00280 The original "zoom" program by Paul Heckbert called this "Bessel". But 00281 really it is more accurately named "Jinc". 00282 */ 00283 if (x == 0.0) 00284 return(0.5*MagickPIL); 00285 return(BesselOrderOne(MagickPIL*x)/x); 00286 } 00287 00288 static MagickRealType Kaiser(const MagickRealType x, 00289 const ResizeFilter *resize_filter) 00290 { 00291 /* 00292 Kaiser Windowing Function (bessel windowing) 00293 Alpha (c[0]) is a free value from 5 to 8 (defaults to 6.5). 00294 A scaling factor (c[1]) is not needed as filter is normalized 00295 */ 00296 return(resize_filter->coefficient[1]* 00297 I0(resize_filter->coefficient[0]*sqrt((double) (1.0-x*x)))); 00298 } 00299 00300 static MagickRealType Lagrange(const MagickRealType x, 00301 const ResizeFilter *resize_filter) 00302 { 00303 MagickRealType 00304 value; 00305 00306 register ssize_t 00307 i; 00308 00309 ssize_t 00310 n, 00311 order; 00312 00313 /* 00314 Lagrange piecewise polynomial fit of sinc: N is the 'order' of the lagrange 00315 function and depends on the overall support window size of the filter. That 00316 is: for a support of 2, it gives a lagrange-4 (piecewise cubic function). 00317 00318 "n" identifies the piece of the piecewise polynomial. 00319 00320 See Survey: Interpolation Methods, IEEE Transactions on Medical Imaging, 00321 Vol 18, No 11, November 1999, p1049-1075, -- Equation 27 on p1064. 00322 */ 00323 if (x > resize_filter->support) 00324 return(0.0); 00325 order=(ssize_t) (2.0*resize_filter->window_support); /* number of pieces */ 00326 n=(ssize_t) (resize_filter->window_support+x); 00327 value=1.0f; 00328 for (i=0; i < order; i++) 00329 if (i != n) 00330 value*=(n-i-x)/(n-i); 00331 return(value); 00332 } 00333 00334 static MagickRealType Quadratic(const MagickRealType x, 00335 const ResizeFilter *magick_unused(resize_filter)) 00336 { 00337 /* 00338 2rd order (quadratic) B-Spline approximation of Gaussian. 00339 */ 00340 if (x < 0.5) 00341 return(0.75-x*x); 00342 if (x < 1.5) 00343 return(0.5*(x-1.5)*(x-1.5)); 00344 return(0.0); 00345 } 00346 00347 static MagickRealType Sinc(const MagickRealType x, 00348 const ResizeFilter *magick_unused(resize_filter)) 00349 { 00350 /* 00351 Scaled sinc(x) function using a trig call: 00352 sinc(x) == sin(pi x)/(pi x). 00353 */ 00354 if (x != 0.0) 00355 { 00356 const MagickRealType alpha=(MagickRealType) (MagickPIL*x); 00357 return(sin((double) alpha)/alpha); 00358 } 00359 return((MagickRealType) 1.0); 00360 } 00361 00362 static MagickRealType SincFast(const MagickRealType x, 00363 const ResizeFilter *magick_unused(resize_filter)) 00364 { 00365 /* 00366 Approximations of the sinc function sin(pi x)/(pi x) over the interval 00367 [-4,4] constructed by Nicolas Robidoux and Chantal Racette with funding 00368 from the Natural Sciences and Engineering Research Council of Canada. 00369 00370 Although the approximations are polynomials (for low order of 00371 approximation) and quotients of polynomials (for higher order of 00372 approximation) and consequently are similar in form to Taylor polynomials / 00373 Pade approximants, the approximations are computed with a completely 00374 different technique. 00375 00376 Summary: These approximations are "the best" in terms of bang (accuracy) 00377 for the buck (flops). More specifically: Among the polynomial quotients 00378 that can be computed using a fixed number of flops (with a given "+ - * / 00379 budget"), the chosen polynomial quotient is the one closest to the 00380 approximated function with respect to maximum absolute relative error over 00381 the given interval. 00382 00383 The Remez algorithm, as implemented in the boost library's minimax package, 00384 is the key to the construction: http://www.boost.org/doc/libs/1_36_0/libs/ 00385 math/doc/sf_and_dist/html/math_toolkit/backgrounders/remez.html 00386 00387 If outside of the interval of approximation, use the standard trig formula. 00388 */ 00389 if (x > 4.0) 00390 { 00391 const MagickRealType alpha=(MagickRealType) (MagickPIL*x); 00392 return(sin((double) alpha)/alpha); 00393 } 00394 { 00395 /* 00396 The approximations only depend on x^2 (sinc is an even function). 00397 */ 00398 const MagickRealType xx = x*x; 00399 #if MAGICKCORE_QUANTUM_DEPTH <= 8 00400 /* 00401 Maximum absolute relative error 6.3e-6 < 1/2^17. 00402 */ 00403 const MagickRealType c0 = 0.173610016489197553621906385078711564924e-2L; 00404 const MagickRealType c1 = -0.384186115075660162081071290162149315834e-3L; 00405 const MagickRealType c2 = 0.393684603287860108352720146121813443561e-4L; 00406 const MagickRealType c3 = -0.248947210682259168029030370205389323899e-5L; 00407 const MagickRealType c4 = 0.107791837839662283066379987646635416692e-6L; 00408 const MagickRealType c5 = -0.324874073895735800961260474028013982211e-8L; 00409 const MagickRealType c6 = 0.628155216606695311524920882748052490116e-10L; 00410 const MagickRealType c7 = -0.586110644039348333520104379959307242711e-12L; 00411 const MagickRealType p = 00412 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7)))))); 00413 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p); 00414 #elif MAGICKCORE_QUANTUM_DEPTH <= 16 00415 /* 00416 Max. abs. rel. error 2.2e-8 < 1/2^25. 00417 */ 00418 const MagickRealType c0 = 0.173611107357320220183368594093166520811e-2L; 00419 const MagickRealType c1 = -0.384240921114946632192116762889211361285e-3L; 00420 const MagickRealType c2 = 0.394201182359318128221229891724947048771e-4L; 00421 const MagickRealType c3 = -0.250963301609117217660068889165550534856e-5L; 00422 const MagickRealType c4 = 0.111902032818095784414237782071368805120e-6L; 00423 const MagickRealType c5 = -0.372895101408779549368465614321137048875e-8L; 00424 const MagickRealType c6 = 0.957694196677572570319816780188718518330e-10L; 00425 const MagickRealType c7 = -0.187208577776590710853865174371617338991e-11L; 00426 const MagickRealType c8 = 0.253524321426864752676094495396308636823e-13L; 00427 const MagickRealType c9 = -0.177084805010701112639035485248501049364e-15L; 00428 const MagickRealType p = 00429 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*(c7+xx*(c8+xx*c9)))))))); 00430 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)*p); 00431 #else 00432 /* 00433 Max. abs. rel. error 1.2e-12 < 1/2^39. 00434 */ 00435 const MagickRealType c0 = 0.173611111110910715186413700076827593074e-2L; 00436 const MagickRealType c1 = -0.289105544717893415815859968653611245425e-3L; 00437 const MagickRealType c2 = 0.206952161241815727624413291940849294025e-4L; 00438 const MagickRealType c3 = -0.834446180169727178193268528095341741698e-6L; 00439 const MagickRealType c4 = 0.207010104171026718629622453275917944941e-7L; 00440 const MagickRealType c5 = -0.319724784938507108101517564300855542655e-9L; 00441 const MagickRealType c6 = 0.288101675249103266147006509214934493930e-11L; 00442 const MagickRealType c7 = -0.118218971804934245819960233886876537953e-13L; 00443 const MagickRealType p = 00444 c0+xx*(c1+xx*(c2+xx*(c3+xx*(c4+xx*(c5+xx*(c6+xx*c7)))))); 00445 const MagickRealType d0 = 1.0L; 00446 const MagickRealType d1 = 0.547981619622284827495856984100563583948e-1L; 00447 const MagickRealType d2 = 0.134226268835357312626304688047086921806e-2L; 00448 const MagickRealType d3 = 0.178994697503371051002463656833597608689e-4L; 00449 const MagickRealType d4 = 0.114633394140438168641246022557689759090e-6L; 00450 const MagickRealType q = d0+xx*(d1+xx*(d2+xx*(d3+xx*d4))); 00451 return((xx-1.0)*(xx-4.0)*(xx-9.0)*(xx-16.0)/q*p); 00452 #endif 00453 } 00454 } 00455 00456 static MagickRealType Triangle(const MagickRealType x, 00457 const ResizeFilter *magick_unused(resize_filter)) 00458 { 00459 /* 00460 1st order (linear) B-Spline, bilinear interpolation, Tent 1D filter, or 00461 a Bartlett 2D Cone filter. Also used as a Bartlett Windowing function 00462 for Sinc(). 00463 */ 00464 if (x < 1.0) 00465 return(1.0-x); 00466 return(0.0); 00467 } 00468 00469 static MagickRealType Welsh(const MagickRealType x, 00470 const ResizeFilter *magick_unused(resize_filter)) 00471 { 00472 /* 00473 Welsh parabolic windowing filter. 00474 */ 00475 if (x < 1.0) 00476 return(1.0-x*x); 00477 return(0.0); 00478 } 00479 00480 /* 00481 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00482 % % 00483 % % 00484 % % 00485 + A c q u i r e R e s i z e F i l t e r % 00486 % % 00487 % % 00488 % % 00489 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00490 % 00491 % AcquireResizeFilter() allocates the ResizeFilter structure. Choose from 00492 % these filters: 00493 % 00494 % FIR (Finite impulse Response) Filters 00495 % Box Triangle Quadratic Cubic Hermite Catrom Mitchell 00496 % 00497 % IIR (Infinite impulse Response) Filters 00498 % Gaussian Sinc Jinc (Bessel) 00499 % 00500 % Windowed Sinc/Jinc Filters 00501 % Blackman Hanning Hamming Kaiser Lanczos 00502 % 00503 % Special purpose Filters 00504 % SincFast LanczosSharp Lanczos2D Lanczos2DSharp Robidoux 00505 % 00506 % The users "-filter" selection is used to lookup the default 'expert' 00507 % settings for that filter from a internal table. However any provided 00508 % 'expert' settings (see below) may override this selection. 00509 % 00510 % FIR filters are used as is, and are limited to that filters support window 00511 % (unless over-ridden). 'Gaussian' while classed as an IIR filter, is also 00512 % simply clipped by its support size (currently 1.5 or approximatally 3*sigma 00513 % as recommended by many references) 00514 % 00515 % The special a 'cylindrical' filter flag will promote the default 4-lobed 00516 % Windowed Sinc filter to a 3-lobed Windowed Jinc equivalent, which is better 00517 % suited to this style of image resampling. This typically happens when using 00518 % such a filter for images distortions. 00519 % 00520 % Directly requesting 'Sinc', 'Jinc' function as a filter will force the use 00521 % of function without any windowing, or promotion for cylindrical usage. This 00522 % is not recommended, except by image processing experts, especially as part 00523 % of expert option filter function selection. 00524 % 00525 % Two forms of the 'Sinc' function are available: Sinc and SincFast. Sinc is 00526 % computed using the traditional sin(pi*x)/(pi*x); it is selected if the user 00527 % specifically specifies the use of a Sinc filter. SincFast uses highly 00528 % accurate (and fast) polynomial (low Q) and rational (high Q) approximations, 00529 % and will be used by default in most cases. 00530 % 00531 % The Lanczos filter is a special 3-lobed Sinc-windowed Sinc filter (promoted 00532 % to Jinc-windowed Jinc for cylindrical (Elliptical Weighted Average) use). 00533 % The Sinc version is the most popular windowed filter. 00534 % 00535 % LanczosSharp is a slightly sharpened (blur=0.9812505644269356 < 1) form of 00536 % the Lanczos filter, specifically designed for EWA distortion (as a 00537 % Jinc-Jinc); it can also be used as a slightly sharper orthogonal Lanczos 00538 % (Sinc-Sinc) filter. The chosen blur value comes as close as possible to 00539 % satisfying the following condition without changing the character of the 00540 % corresponding EWA filter: 00541 % 00542 % 'No-Op' Vertical and Horizontal Line Preservation Condition: Images with 00543 % only vertical or horizontal features are preserved when performing 'no-op" 00544 % with EWA distortion. 00545 % 00546 % The Lanczos2 and Lanczos2Sharp filters are 2-lobe versions of the Lanczos 00547 % filters. The 'sharp' version uses a blur factor of 0.9549963639785485, 00548 % again chosen because the resulting EWA filter comes as close as possible to 00549 % satisfying the above condition. 00550 % 00551 % Robidoux is another filter tuned for EWA. It is the Keys cubic filter 00552 % defined by B=(228 - 108 sqrt(2))/199. Robidoux satisfies the "'No-Op' 00553 % Vertical and Horizontal Line Preservation Condition" exactly, and it 00554 % moderately blurs high frequency 'pixel-hash' patterns under no-op. It turns 00555 % out to be close to both Mitchell and Lanczos2Sharp. For example, its first 00556 % crossing is at (36 sqrt(2) + 123)/(72 sqrt(2) + 47), almost the same as the 00557 % first crossing of Mitchell and Lanczos2Sharp. 00558 % 00559 % 'EXPERT' OPTIONS: 00560 % 00561 % These artifact "defines" are not recommended for production use without 00562 % expert knowledge of resampling, filtering, and the effects they have on the 00563 % resulting resampled (resize ro distorted) image. 00564 % 00565 % They can be used to override any and all filter default, and it is 00566 % recommended you make good use of "filter:verbose" to make sure that the 00567 % overall effect of your selection (before and after) is as expected. 00568 % 00569 % "filter:verbose" controls whether to output the exact results of the 00570 % filter selections made, as well as plotting data for graphing the 00571 % resulting filter over the filters support range. 00572 % 00573 % "filter:filter" select the main function associated with this filter 00574 % name, as the weighting function of the filter. This can be used to 00575 % set a windowing function as a weighting function, for special 00576 % purposes, such as graphing. 00577 % 00578 % If a "filter:window" operation has not been provided, a 'Box' 00579 % windowing function will be set to denote that no windowing function is 00580 % being used. 00581 % 00582 % "filter:window" Select this windowing function for the filter. While any 00583 % filter could be used as a windowing function, using the 'first lobe' of 00584 % that filter over the whole support window, using a non-windowing 00585 % function is not advisible. If no weighting filter function is specifed 00586 % a 'SincFast' filter is used. 00587 % 00588 % "filter:lobes" Number of lobes to use for the Sinc/Jinc filter. This a 00589 % simpler method of setting filter support size that will correctly 00590 % handle the Sinc/Jinc switch for an operators filtering requirements. 00591 % Only integers should be given. 00592 % 00593 % "filter:support" Set the support size for filtering to the size given. 00594 % This not recommended for Sinc/Jinc windowed filters (lobes should be 00595 % used instead). This will override any 'filter:lobes' option. 00596 % 00597 % "filter:win-support" Scale windowing function to this size instead. This 00598 % causes the windowing (or self-windowing Lagrange filter) to act is if 00599 % the support window it much much larger than what is actually supplied 00600 % to the calling operator. The filter however is still clipped to the 00601 % real support size given, by the support range suppiled to the caller. 00602 % If unset this will equal the normal filter support size. 00603 % 00604 % "filter:blur" Scale the filter and support window by this amount. A value 00605 % > 1 will generally result in a more burred image with more ringing 00606 % effects, while a value <1 will sharpen the resulting image with more 00607 % aliasing effects. 00608 % 00609 % "filter:sigma" The sigma value to use for the Gaussian filter only. 00610 % Defaults to '1/2'. Using a different sigma effectively provides a 00611 % method of using the filter as a 'blur' convolution. Particularly when 00612 % using it for Distort. 00613 % 00614 % "filter:b" 00615 % "filter:c" Override the preset B,C values for a Cubic type of filter. 00616 % If only one of these are given it is assumes to be a 'Keys' type of 00617 % filter such that B+2C=1, where Keys 'alpha' value = C. 00618 % 00619 % Examples: 00620 % 00621 % Set a true un-windowed Sinc filter with 10 lobes (very slow): 00622 % -define filter:filter=Sinc 00623 % -define filter:lobes=8 00624 % 00625 % Set an 8 lobe Lanczos (Sinc or Jinc) filter: 00626 % -filter Lanczos 00627 % -define filter:lobes=8 00628 % 00629 % The format of the AcquireResizeFilter method is: 00630 % 00631 % ResizeFilter *AcquireResizeFilter(const Image *image, 00632 % const FilterTypes filter_type, const MagickBooleanType radial, 00633 % ExceptionInfo *exception) 00634 % 00635 % A description of each parameter follows: 00636 % 00637 % o image: the image. 00638 % 00639 % o filter: the filter type, defining a preset filter, window and support. 00640 % The artifact settings listed above will override those selections. 00641 % 00642 % o blur: blur the filter by this amount, use 1.0 if unknown. Image 00643 % artifact "filter:blur" will override this API call usage, including any 00644 % internal change (such as for cylindrical usage). 00645 % 00646 % o radial: use a 1D orthogonal filter (Sinc) or 2D cylindrical (radial) 00647 % filter (Jinc). 00648 % 00649 % o exception: return any errors or warnings in this structure. 00650 % 00651 */ 00652 MagickPrivate ResizeFilter *AcquireResizeFilter(const Image *image, 00653 const FilterTypes filter,const MagickRealType blur, 00654 const MagickBooleanType cylindrical,ExceptionInfo *exception) 00655 { 00656 const char 00657 *artifact; 00658 00659 FilterTypes 00660 filter_type, 00661 window_type; 00662 00663 MagickRealType 00664 B, 00665 C, 00666 value; 00667 00668 register ResizeFilter 00669 *resize_filter; 00670 00671 /* 00672 Table Mapping given Filter, into Weighting and Windowing functions. A 00673 'Box' windowing function means its a simble non-windowed filter. An 00674 'SincFast' filter function could be upgraded to a 'Jinc' filter if a 00675 "cylindrical", unless a 'Sinc' or 'SincFast' filter was specifically 00676 requested. 00677 00678 WARNING: The order of this tabel must match the order of the FilterTypes 00679 enumeration specified in "resample.h", or the filter names will not match 00680 the filter being setup. 00681 00682 You can check filter setups with the "filter:verbose" setting. 00683 */ 00684 static struct 00685 { 00686 FilterTypes 00687 filter, 00688 window; 00689 } const mapping[SentinelFilter] = 00690 { 00691 { UndefinedFilter, BoxFilter }, /* Undefined (default to Box) */ 00692 { PointFilter, BoxFilter }, /* SPECIAL: Nearest neighbour */ 00693 { BoxFilter, BoxFilter }, /* Box averaging filter */ 00694 { TriangleFilter, BoxFilter }, /* Linear interpolation filter */ 00695 { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */ 00696 { SincFastFilter, HanningFilter }, /* Hanning -- cosine-sinc */ 00697 { SincFastFilter, HammingFilter }, /* Hamming -- '' variation */ 00698 { SincFastFilter, BlackmanFilter }, /* Blackman -- 2*cosine-sinc */ 00699 { GaussianFilter, BoxFilter }, /* Gaussian blur filter */ 00700 { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approx */ 00701 { CubicFilter, BoxFilter }, /* Cubic B-Spline */ 00702 { CatromFilter, BoxFilter }, /* Cubic-Keys interpolator */ 00703 { MitchellFilter, BoxFilter }, /* 'Ideal' Cubic-Keys filter */ 00704 { JincFilter, BoxFilter }, /* Raw 3-lobed Jinc function */ 00705 { SincFilter, BoxFilter }, /* Raw 4-lobed Sinc function */ 00706 { SincFastFilter, BoxFilter }, /* Raw fast sinc ("Pade"-type) */ 00707 { SincFastFilter, KaiserFilter }, /* Kaiser -- square root-sinc */ 00708 { SincFastFilter, WelshFilter }, /* Welsh -- parabolic-sinc */ 00709 { SincFastFilter, CubicFilter }, /* Parzen -- cubic-sinc */ 00710 { SincFastFilter, BohmanFilter }, /* Bohman -- 2*cosine-sinc */ 00711 { SincFastFilter, TriangleFilter }, /* Bartlett -- triangle-sinc */ 00712 { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing */ 00713 { LanczosFilter, LanczosFilter }, /* Lanczos Sinc-Sinc filters */ 00714 { LanczosSharpFilter, LanczosSharpFilter }, /* | these require */ 00715 { Lanczos2Filter, Lanczos2Filter }, /* | special handling */ 00716 { Lanczos2SharpFilter, Lanczos2SharpFilter }, 00717 { RobidouxFilter, BoxFilter }, /* Cubic Keys tuned for EWA */ 00718 }; 00719 /* 00720 Table mapping the filter/window from the above table to an actual function. 00721 The default support size for that filter as a weighting function, the range 00722 to scale with to use that function as a sinc windowing function, (typ 1.0). 00723 00724 Note that the filter_type -> function is 1 to 1 except for Sinc(), 00725 SincFast(), and CubicBC() functions, which may have multiple filter to 00726 function associations. 00727 00728 See "filter:verbose" handling below for the function -> filter mapping. 00729 */ 00730 static struct 00731 { 00732 MagickRealType 00733 (*function)(const MagickRealType,const ResizeFilter*), 00734 lobes, /* Default lobes/support size of the weighting filter. */ 00735 scale, /* Support when function used as a windowing function 00736 Typically equal to the location of the first zero crossing. */ 00737 B,C; /* BC-spline coefficients, ignored if not a CubicBC filter. */ 00738 } const filters[SentinelFilter] = 00739 { 00740 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Undefined (default to Box) */ 00741 { Box, 0.0, 0.5, 0.0, 0.0 }, /* Point (special handling) */ 00742 { Box, 0.5, 0.5, 0.0, 0.0 }, /* Box */ 00743 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Triangle */ 00744 { CubicBC, 1.0, 1.0, 0.0, 0.0 }, /* Hermite (cubic B=C=0) */ 00745 { Hanning, 1.0, 1.0, 0.0, 0.0 }, /* Hanning, cosine window */ 00746 { Hamming, 1.0, 1.0, 0.0, 0.0 }, /* Hamming, '' variation */ 00747 { Blackman, 1.0, 1.0, 0.0, 0.0 }, /* Blackman, 2*cosine window */ 00748 { Gaussian, 2.0, 1.5, 0.0, 0.0 }, /* Gaussian */ 00749 { Quadratic, 1.5, 1.5, 0.0, 0.0 }, /* Quadratic gaussian */ 00750 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Cubic B-Spline (B=1,C=0) */ 00751 { CubicBC, 2.0, 1.0, 0.0, 0.5 }, /* Catmull-Rom (B=0,C=1/2) */ 00752 { CubicBC, 2.0, 8.0/7.0, 1./3., 1./3. }, /* Mitchell (B=C=1/3) */ 00753 { Jinc, 3.0, 1.2196698912665045, 0.0, 0.0 }, /* Raw 3-lobed Jinc */ 00754 { Sinc, 4.0, 1.0, 0.0, 0.0 }, /* Raw 4-lobed Sinc */ 00755 { SincFast, 4.0, 1.0, 0.0, 0.0 }, /* Raw fast sinc ("Pade"-type) */ 00756 { Kaiser, 1.0, 1.0, 0.0, 0.0 }, /* Kaiser (square root window) */ 00757 { Welsh, 1.0, 1.0, 0.0, 0.0 }, /* Welsh (parabolic window) */ 00758 { CubicBC, 2.0, 2.0, 1.0, 0.0 }, /* Parzen (B-Spline window) */ 00759 { Bohman, 1.0, 1.0, 0.0, 0.0 }, /* Bohman, 2*Cosine window */ 00760 { Triangle, 1.0, 1.0, 0.0, 0.0 }, /* Bartlett (triangle window) */ 00761 { Lagrange, 2.0, 1.0, 0.0, 0.0 }, /* Lagrange sinc approximation */ 00762 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* Lanczos, 3-lobed Sinc-Sinc */ 00763 { SincFast, 3.0, 1.0, 0.0, 0.0 }, /* lanczos, Sharpened */ 00764 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos, 2-lobed */ 00765 { SincFast, 2.0, 1.0, 0.0, 0.0 }, /* Lanczos2, sharpened */ 00766 { CubicBC, 2.0, 1.1685777620836932, 0.37821575509399867, 00767 0.31089212245300067 } 00768 /* Robidoux: Keys cubic close to Lanczos2D sharpened */ 00769 }; 00770 /* 00771 The known zero crossings of the Jinc() or more accurately the Jinc(x*PI) 00772 function being used as a filter. It is used by the "filter:lobes" expert 00773 setting and for 'lobes' for Jinc functions in the previous table. This way 00774 users do not have to deal with the highly irrational lobe sizes of the Jinc 00775 filter. 00776 00777 Values taken from http://cose.math.bas.bg/webMathematica/webComputing/ 00778 BesselZeros.jsp using Jv-function with v=1, then dividing by PI. 00779 */ 00780 static MagickRealType 00781 jinc_zeros[16] = 00782 { 00783 1.2196698912665045, 00784 2.2331305943815286, 00785 3.2383154841662362, 00786 4.2410628637960699, 00787 5.2427643768701817, 00788 6.2439216898644877, 00789 7.2447598687199570, 00790 8.2453949139520427, 00791 9.2458926849494673, 00792 10.246293348754916, 00793 11.246622794877883, 00794 12.246898461138105, 00795 13.247132522181061, 00796 14.247333735806849, 00797 15.247508563037300, 00798 16.247661874700962 00799 }; 00800 00801 /* 00802 Allocate resize filter. 00803 */ 00804 assert(image != (const Image *) NULL); 00805 assert(image->signature == MagickSignature); 00806 if (image->debug != MagickFalse) 00807 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 00808 assert(UndefinedFilter < filter && filter < SentinelFilter); 00809 assert(exception != (ExceptionInfo *) NULL); 00810 assert(exception->signature == MagickSignature); 00811 resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter)); 00812 if (resize_filter == (ResizeFilter *) NULL) 00813 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); 00814 (void) ResetMagickMemory(resize_filter,0,sizeof(*resize_filter)); 00815 /* 00816 Defaults for the requested filter. 00817 */ 00818 filter_type=mapping[filter].filter; 00819 window_type=mapping[filter].window; 00820 resize_filter->blur = blur; /* function argument blur factor */ 00821 /* Promote 1D Windowed Sinc Filters to a 2D Windowed Jinc filters */ 00822 if ((cylindrical != MagickFalse) && (filter_type == SincFastFilter) && 00823 (filter != SincFastFilter)) 00824 filter_type=JincFilter; /* 1D Windowed Sinc => 2D Windowed Jinc filters */ 00825 /* Expert filter setting override */ 00826 artifact=GetImageArtifact(image,"filter:filter"); 00827 if (artifact != (const char *) NULL) 00828 { 00829 ssize_t 00830 option; 00831 00832 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact); 00833 if ((UndefinedFilter < option) && (option < SentinelFilter)) 00834 { 00835 /* 00836 Raw filter request - no window function. 00837 */ 00838 filter_type=(FilterTypes) option; 00839 window_type=BoxFilter; 00840 } 00841 /* 00842 Filter override with a specific window function. 00843 */ 00844 artifact=GetImageArtifact(image,"filter:window"); 00845 if (artifact != (const char *) NULL) 00846 { 00847 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact); 00848 if ((UndefinedFilter < option) && (option < SentinelFilter)) 00849 window_type=(FilterTypes) option; 00850 } 00851 } 00852 else 00853 { 00854 /* 00855 Window specified, but no filter function? Assume Sinc/Jinc. 00856 */ 00857 artifact=GetImageArtifact(image,"filter:window"); 00858 if (artifact != (const char *) NULL) 00859 { 00860 ssize_t 00861 option; 00862 00863 option=ParseCommandOption(MagickFilterOptions,MagickFalse,artifact); 00864 if ((UndefinedFilter < option) && (option < SentinelFilter)) 00865 { 00866 filter_type=cylindrical != MagickFalse ? JincFilter : 00867 SincFastFilter; 00868 window_type=(FilterTypes) option; 00869 } 00870 } 00871 } 00872 /* 00873 Assign the real functions to use for the filters selected. 00874 */ 00875 resize_filter->filter=filters[filter_type].function; 00876 resize_filter->support=filters[filter_type].lobes; 00877 resize_filter->window=filters[window_type].function; 00878 resize_filter->scale=filters[window_type].scale; 00879 resize_filter->signature=MagickSignature; 00880 /* 00881 Filter Modifications for orthogonal/cylindrical usage. 00882 */ 00883 if (cylindrical != MagickFalse) 00884 switch (filter_type) 00885 { 00886 case BoxFilter: 00887 { 00888 /* 00889 Support for Cylindrical Box should be sqrt(2)/2. 00890 */ 00891 resize_filter->support=(MagickRealType) MagickSQ1_2; 00892 break; 00893 } 00894 case LanczosFilter: 00895 case LanczosSharpFilter: 00896 case Lanczos2Filter: 00897 case Lanczos2SharpFilter: 00898 { 00899 /* 00900 Number of lobes (support window size) remain unchanged. 00901 */ 00902 resize_filter->filter=filters[JincFilter].function; 00903 resize_filter->window=filters[JincFilter].function; 00904 resize_filter->scale=filters[JincFilter].scale; 00905 break; 00906 } 00907 default: 00908 break; 00909 } 00910 /* 00911 Global Sharpening (regardless of orthoginal/cylindrical). 00912 */ 00913 switch (filter_type) 00914 { 00915 case LanczosSharpFilter: 00916 { 00917 resize_filter->blur*=0.9812505644269356; 00918 break; 00919 } 00920 case Lanczos2SharpFilter: 00921 { 00922 resize_filter->blur*=0.9549963639785485; 00923 break; 00924 } 00925 default: 00926 break; 00927 } 00928 /* 00929 Expert Option Modifications. 00930 */ 00931 /* User Gaussian Sigma Override - no support change */ 00932 value=0.5; /* guassian sigma default, half pixel */ 00933 if ( GaussianFilter ) { 00934 artifact=GetImageArtifact(image,"filter:sigma"); 00935 if (artifact != (const char *) NULL) 00936 value=StringToDouble(artifact,(char **) NULL); 00937 /* Define coefficents for Gaussian */ 00938 resize_filter->coefficient[0]=1.0/(2.0*value*value); /* X scaling */ 00939 resize_filter->coefficient[1]=(MagickRealType) (1.0/(Magick2PI*value* 00940 value)); /* normalization */ 00941 } 00942 /* User Kaiser Alpha Override - no support change */ 00943 if ( KaiserFilter ) { 00944 value=6.5; /* default alpha value for Kaiser bessel windowing function */ 00945 artifact=GetImageArtifact(image,"filter:alpha"); 00946 if (artifact != (const char *) NULL) 00947 value=StringToDouble(artifact,(char **) NULL); 00948 /* Define coefficents for Kaiser Windowing Function */ 00949 resize_filter->coefficient[0]=value; /* X scaling */ 00950 resize_filter->coefficient[1]=1.0/I0(value); /* normalization */ 00951 } 00952 00953 /* Blur Override */ 00954 artifact=GetImageArtifact(image,"filter:blur"); 00955 if (artifact != (const char *) NULL) 00956 resize_filter->blur*=StringToDouble(artifact,(char **) NULL); 00957 if (resize_filter->blur < MagickEpsilon) 00958 resize_filter->blur=(MagickRealType) MagickEpsilon; 00959 00960 /* Support Overrides */ 00961 artifact=GetImageArtifact(image,"filter:lobes"); 00962 if (artifact != (const char *) NULL) 00963 { 00964 ssize_t 00965 lobes; 00966 00967 lobes=(ssize_t) StringToLong(artifact); 00968 if (lobes < 1) 00969 lobes=1; 00970 resize_filter->support=(MagickRealType) lobes; 00971 } 00972 /* Convert a Jinc function lobes value to a real support value */ 00973 if (resize_filter->filter == Jinc) 00974 { 00975 if (resize_filter->support > 16) 00976 resize_filter->support=jinc_zeros[15]; /* largest entry in table */ 00977 else 00978 resize_filter->support=jinc_zeros[((long)resize_filter->support)-1]; 00979 } 00980 /* expert override of the support setting */ 00981 artifact=GetImageArtifact(image,"filter:support"); 00982 if (artifact != (const char *) NULL) 00983 resize_filter->support=fabs(StringToDouble(artifact,(char **) NULL)); 00984 /* 00985 Scale windowing function separately to the support 'clipping' 00986 window that calling operator is planning to actually use. (Expert 00987 override) 00988 */ 00989 resize_filter->window_support=resize_filter->support; /* default */ 00990 artifact=GetImageArtifact(image,"filter:win-support"); 00991 if (artifact != (const char *) NULL) 00992 resize_filter->window_support=fabs(StringToDouble(artifact,(char **) NULL)); 00993 /* 00994 Adjust window function scaling to match windowing support for 00995 weighting function. This avoids a division on every filter call. 00996 */ 00997 resize_filter->scale/=resize_filter->window_support; 00998 00999 /* 01000 * Set Cubic Spline B,C values, calculate Cubic coefficients. 01001 */ 01002 B=0.0; 01003 C=0.0; 01004 if ((filters[filter_type].function == CubicBC) || 01005 (filters[window_type].function == CubicBC)) 01006 { 01007 B=filters[filter_type].B; 01008 C=filters[filter_type].C; 01009 if (filters[window_type].function == CubicBC) 01010 { 01011 B=filters[window_type].B; 01012 C=filters[window_type].C; 01013 } 01014 artifact=GetImageArtifact(image,"filter:b"); 01015 if (artifact != (const char *) NULL) 01016 { 01017 B=StringToDouble(artifact,(char **) NULL); 01018 C=(1.0-B)/2.0; /* Calculate C to get a Keys cubic filter. */ 01019 artifact=GetImageArtifact(image,"filter:c"); /* user C override */ 01020 if (artifact != (const char *) NULL) 01021 C=StringToDouble(artifact,(char **) NULL); 01022 } 01023 else 01024 { 01025 artifact=GetImageArtifact(image,"filter:c"); 01026 if (artifact != (const char *) NULL) 01027 { 01028 C=StringToDouble(artifact,(char **) NULL); 01029 B=1.0-2.0*C; /* Calculate B to get a Keys cubic filter. */ 01030 } 01031 } 01032 /* Convert B,C values into Cubic Coefficents. See CubicBC(). */ 01033 { 01034 double 01035 B_squared; 01036 01037 B_squared=B+B; 01038 resize_filter->coefficient[0]=1.0-(1.0/3.0)*B; 01039 resize_filter->coefficient[1]=-3.0+B_squared+C; 01040 resize_filter->coefficient[2]=2.0-1.5*B-C; 01041 resize_filter->coefficient[3]=(4.0/3.0)*B+4.0*C; 01042 resize_filter->coefficient[4]=-8.0*C-B_squared; 01043 resize_filter->coefficient[5]=B+5.0*C; 01044 resize_filter->coefficient[6]=(-1.0/6.0)*B-C; 01045 } 01046 } 01047 01048 /* 01049 Expert Option Request for verbose details of the resulting filter. 01050 */ 01051 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01052 #pragma omp master 01053 { 01054 #endif 01055 artifact=GetImageArtifact(image,"filter:verbose"); 01056 if (IsMagickTrue(artifact) != MagickFalse) 01057 { 01058 double 01059 support, 01060 x; 01061 01062 /* 01063 Set the weighting function properly when the weighting 01064 function may not exactly match the filter of the same name. 01065 EG: a Point filter is really uses a Box weighting function 01066 with a different support than is typically used. 01067 */ 01068 if (resize_filter->filter == Box) filter_type=BoxFilter; 01069 if (resize_filter->filter == Sinc) filter_type=SincFilter; 01070 if (resize_filter->filter == SincFast) filter_type=SincFastFilter; 01071 if (resize_filter->filter == Jinc) filter_type=JincFilter; 01072 if (resize_filter->filter == CubicBC) filter_type=CubicFilter; 01073 if (resize_filter->window == Box) window_type=BoxFilter; 01074 if (resize_filter->window == Sinc) window_type=SincFilter; 01075 if (resize_filter->window == SincFast) window_type=SincFastFilter; 01076 if (resize_filter->window == Jinc) window_type=JincFilter; 01077 if (resize_filter->window == CubicBC) window_type=CubicFilter; 01078 /* 01079 Report Filter Details. 01080 */ 01081 support=GetResizeFilterSupport(resize_filter); /* practical_support */ 01082 (void) FormatLocaleFile(stdout,"# Resize Filter (for graphing)\n#\n"); 01083 (void) FormatLocaleFile(stdout,"# filter = %s\n", 01084 CommandOptionToMnemonic(MagickFilterOptions,filter_type)); 01085 (void) FormatLocaleFile(stdout,"# window = %s\n", 01086 CommandOptionToMnemonic(MagickFilterOptions, window_type)); 01087 (void) FormatLocaleFile(stdout,"# support = %.*g\n", 01088 GetMagickPrecision(),(double) resize_filter->support); 01089 (void) FormatLocaleFile(stdout,"# win-support = %.*g\n", 01090 GetMagickPrecision(),(double) resize_filter->window_support); 01091 (void) FormatLocaleFile(stdout,"# scale_blur = %.*g\n", 01092 GetMagickPrecision(), (double)resize_filter->blur); 01093 if (filter_type == GaussianFilter) 01094 (void) FormatLocaleFile(stdout,"# gaussian_sigma = %.*g\n", 01095 GetMagickPrecision(), (double)value); 01096 if ( filter_type == KaiserFilter ) 01097 (void) FormatLocaleFile(stdout,"# kaiser_alpha = %.*g\n", 01098 GetMagickPrecision(), (double)value); 01099 (void) FormatLocaleFile(stdout,"# practical_support = %.*g\n", 01100 GetMagickPrecision(), (double)support); 01101 if ( filter_type == CubicFilter || window_type == CubicFilter ) 01102 (void) FormatLocaleFile(stdout,"# B,C = %.*g,%.*g\n", 01103 GetMagickPrecision(),(double)B, GetMagickPrecision(),(double)C); 01104 (void) FormatLocaleFile(stdout,"\n"); 01105 /* 01106 Output values of resulting filter graph -- for graphing 01107 filter result. 01108 */ 01109 for (x=0.0; x <= support; x+=0.01f) 01110 (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",x, 01111 GetMagickPrecision(),(double) GetResizeFilterWeight(resize_filter, 01112 x)); 01113 /* A final value so gnuplot can graph the 'stop' properly. */ 01114 (void) FormatLocaleFile(stdout,"%5.2lf\t%.*g\n",support, 01115 GetMagickPrecision(),0.0); 01116 } 01117 /* Output the above once only for each image - remove setting */ 01118 (void) DeleteImageArtifact((Image *) image,"filter:verbose"); 01119 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01120 } 01121 #endif 01122 return(resize_filter); 01123 } 01124 01125 /* 01126 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01127 % % 01128 % % 01129 % % 01130 % A d a p t i v e R e s i z e I m a g e % 01131 % % 01132 % % 01133 % % 01134 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01135 % 01136 % AdaptiveResizeImage() adaptively resize image with pixel resampling. 01137 % 01138 % This is shortcut function for a fast interpolative resize using mesh 01139 % interpolation. It works well for small resizes of less than +/- 50% 01140 % of the original image size. For larger resizing on images a full 01141 % filtered and slower resize function should be used instead. 01142 % 01143 % The format of the AdaptiveResizeImage method is: 01144 % 01145 % Image *AdaptiveResizeImage(const Image *image,const size_t columns, 01146 % const size_t rows, ExceptionInfo *exception) 01147 % 01148 % A description of each parameter follows: 01149 % 01150 % o image: the image. 01151 % 01152 % o columns: the number of columns in the resized image. 01153 % 01154 % o rows: the number of rows in the resized image. 01155 % 01156 % o exception: return any errors or warnings in this structure. 01157 % 01158 */ 01159 MagickExport Image *AdaptiveResizeImage(const Image *image, 01160 const size_t columns,const size_t rows,ExceptionInfo *exception) 01161 { 01162 Image 01163 *resize_image; 01164 01165 resize_image=InterpolativeResizeImage(image,columns,rows,MeshInterpolatePixel, 01166 exception); 01167 return(resize_image); 01168 } 01169 01170 /* 01171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01172 % % 01173 % % 01174 % % 01175 + B e s s e l O r d e r O n e % 01176 % % 01177 % % 01178 % % 01179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01180 % 01181 % BesselOrderOne() computes the Bessel function of x of the first kind of 01182 % order 0. This is used to create the Jinc() filter function below. 01183 % 01184 % Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8] 01185 % 01186 % j1(x) = x*j1(x); 01187 % 01188 % For x in (8,inf) 01189 % 01190 % j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1)) 01191 % 01192 % where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow: 01193 % 01194 % cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4) 01195 % = 1/sqrt(2) * (sin(x) - cos(x)) 01196 % sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) 01197 % = -1/sqrt(2) * (sin(x) + cos(x)) 01198 % 01199 % The format of the BesselOrderOne method is: 01200 % 01201 % MagickRealType BesselOrderOne(MagickRealType x) 01202 % 01203 % A description of each parameter follows: 01204 % 01205 % o x: MagickRealType value. 01206 % 01207 */ 01208 01209 #undef I0 01210 static MagickRealType I0(MagickRealType x) 01211 { 01212 MagickRealType 01213 sum, 01214 t, 01215 y; 01216 01217 register ssize_t 01218 i; 01219 01220 /* 01221 Zeroth order Bessel function of the first kind. 01222 */ 01223 sum=1.0; 01224 y=x*x/4.0; 01225 t=y; 01226 for (i=2; t > MagickEpsilon; i++) 01227 { 01228 sum+=t; 01229 t*=y/((MagickRealType) i*i); 01230 } 01231 return(sum); 01232 } 01233 01234 #undef J1 01235 static MagickRealType J1(MagickRealType x) 01236 { 01237 MagickRealType 01238 p, 01239 q; 01240 01241 register ssize_t 01242 i; 01243 01244 static const double 01245 Pone[] = 01246 { 01247 0.581199354001606143928050809e+21, 01248 -0.6672106568924916298020941484e+20, 01249 0.2316433580634002297931815435e+19, 01250 -0.3588817569910106050743641413e+17, 01251 0.2908795263834775409737601689e+15, 01252 -0.1322983480332126453125473247e+13, 01253 0.3413234182301700539091292655e+10, 01254 -0.4695753530642995859767162166e+7, 01255 0.270112271089232341485679099e+4 01256 }, 01257 Qone[] = 01258 { 01259 0.11623987080032122878585294e+22, 01260 0.1185770712190320999837113348e+20, 01261 0.6092061398917521746105196863e+17, 01262 0.2081661221307607351240184229e+15, 01263 0.5243710262167649715406728642e+12, 01264 0.1013863514358673989967045588e+10, 01265 0.1501793594998585505921097578e+7, 01266 0.1606931573481487801970916749e+4, 01267 0.1e+1 01268 }; 01269 01270 p=Pone[8]; 01271 q=Qone[8]; 01272 for (i=7; i >= 0; i--) 01273 { 01274 p=p*x*x+Pone[i]; 01275 q=q*x*x+Qone[i]; 01276 } 01277 return(p/q); 01278 } 01279 01280 #undef P1 01281 static MagickRealType P1(MagickRealType x) 01282 { 01283 MagickRealType 01284 p, 01285 q; 01286 01287 register ssize_t 01288 i; 01289 01290 static const double 01291 Pone[] = 01292 { 01293 0.352246649133679798341724373e+5, 01294 0.62758845247161281269005675e+5, 01295 0.313539631109159574238669888e+5, 01296 0.49854832060594338434500455e+4, 01297 0.2111529182853962382105718e+3, 01298 0.12571716929145341558495e+1 01299 }, 01300 Qone[] = 01301 { 01302 0.352246649133679798068390431e+5, 01303 0.626943469593560511888833731e+5, 01304 0.312404063819041039923015703e+5, 01305 0.4930396490181088979386097e+4, 01306 0.2030775189134759322293574e+3, 01307 0.1e+1 01308 }; 01309 01310 p=Pone[5]; 01311 q=Qone[5]; 01312 for (i=4; i >= 0; i--) 01313 { 01314 p=p*(8.0/x)*(8.0/x)+Pone[i]; 01315 q=q*(8.0/x)*(8.0/x)+Qone[i]; 01316 } 01317 return(p/q); 01318 } 01319 01320 #undef Q1 01321 static MagickRealType Q1(MagickRealType x) 01322 { 01323 MagickRealType 01324 p, 01325 q; 01326 01327 register ssize_t 01328 i; 01329 01330 static const double 01331 Pone[] = 01332 { 01333 0.3511751914303552822533318e+3, 01334 0.7210391804904475039280863e+3, 01335 0.4259873011654442389886993e+3, 01336 0.831898957673850827325226e+2, 01337 0.45681716295512267064405e+1, 01338 0.3532840052740123642735e-1 01339 }, 01340 Qone[] = 01341 { 01342 0.74917374171809127714519505e+4, 01343 0.154141773392650970499848051e+5, 01344 0.91522317015169922705904727e+4, 01345 0.18111867005523513506724158e+4, 01346 0.1038187585462133728776636e+3, 01347 0.1e+1 01348 }; 01349 01350 p=Pone[5]; 01351 q=Qone[5]; 01352 for (i=4; i >= 0; i--) 01353 { 01354 p=p*(8.0/x)*(8.0/x)+Pone[i]; 01355 q=q*(8.0/x)*(8.0/x)+Qone[i]; 01356 } 01357 return(p/q); 01358 } 01359 01360 static MagickRealType BesselOrderOne(MagickRealType x) 01361 { 01362 MagickRealType 01363 p, 01364 q; 01365 01366 if (x == 0.0) 01367 return(0.0); 01368 p=x; 01369 if (x < 0.0) 01370 x=(-x); 01371 if (x < 8.0) 01372 return(p*J1(x)); 01373 q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)- 01374 cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+ 01375 cos((double) x)))); 01376 if (p < 0.0) 01377 q=(-q); 01378 return(q); 01379 } 01380 01381 /* 01382 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01383 % % 01384 % % 01385 % % 01386 + D e s t r o y R e s i z e F i l t e r % 01387 % % 01388 % % 01389 % % 01390 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01391 % 01392 % DestroyResizeFilter() destroy the resize filter. 01393 % 01394 % The format of the DestroyResizeFilter method is: 01395 % 01396 % ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter) 01397 % 01398 % A description of each parameter follows: 01399 % 01400 % o resize_filter: the resize filter. 01401 % 01402 */ 01403 MagickPrivate ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter) 01404 { 01405 assert(resize_filter != (ResizeFilter *) NULL); 01406 assert(resize_filter->signature == MagickSignature); 01407 resize_filter->signature=(~MagickSignature); 01408 resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter); 01409 return(resize_filter); 01410 } 01411 01412 /* 01413 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01414 % % 01415 % % 01416 % % 01417 + G e t R e s i z e F i l t e r S u p p o r t % 01418 % % 01419 % % 01420 % % 01421 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01422 % 01423 % GetResizeFilterSupport() return the current support window size for this 01424 % filter. Note that this may have been enlarged by filter:blur factor. 01425 % 01426 % The format of the GetResizeFilterSupport method is: 01427 % 01428 % MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter) 01429 % 01430 % A description of each parameter follows: 01431 % 01432 % o filter: Image filter to use. 01433 % 01434 */ 01435 MagickPrivate MagickRealType GetResizeFilterSupport( 01436 const ResizeFilter *resize_filter) 01437 { 01438 assert(resize_filter != (ResizeFilter *) NULL); 01439 assert(resize_filter->signature == MagickSignature); 01440 return(resize_filter->support*resize_filter->blur); 01441 } 01442 01443 /* 01444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01445 % % 01446 % % 01447 % % 01448 + G e t R e s i z e F i l t e r W e i g h t % 01449 % % 01450 % % 01451 % % 01452 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01453 % 01454 % GetResizeFilterWeight evaluates the specified resize filter at the point x 01455 % which usally lies between zero and the filters current 'support' and 01456 % returns the weight of the filter function at that point. 01457 % 01458 % The format of the GetResizeFilterWeight method is: 01459 % 01460 % MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter, 01461 % const MagickRealType x) 01462 % 01463 % A description of each parameter follows: 01464 % 01465 % o filter: the filter type. 01466 % 01467 % o x: the point. 01468 % 01469 */ 01470 MagickPrivate MagickRealType GetResizeFilterWeight( 01471 const ResizeFilter *resize_filter,const MagickRealType x) 01472 { 01473 MagickRealType 01474 scale, 01475 weight, 01476 x_blur; 01477 01478 /* 01479 Windowing function - scale the weighting filter by this amount. 01480 */ 01481 assert(resize_filter != (ResizeFilter *) NULL); 01482 assert(resize_filter->signature == MagickSignature); 01483 x_blur=fabs((double) x)/resize_filter->blur; /* X offset with blur scaling */ 01484 if ((resize_filter->window_support < MagickEpsilon) || 01485 (resize_filter->window == Box)) 01486 scale=1.0; /* Point or Box Filter -- avoid division by zero */ 01487 else 01488 { 01489 scale=resize_filter->scale; 01490 scale=resize_filter->window(x_blur*scale,resize_filter); 01491 } 01492 weight=scale*resize_filter->filter(x_blur,resize_filter); 01493 return(weight); 01494 } 01495 01496 /* 01497 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01498 % % 01499 % % 01500 % % 01501 % I n t e r p o l a t i v e R e s i z e I m a g e % 01502 % % 01503 % % 01504 % % 01505 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01506 % 01507 % InterpolativeResizeImage() resizes an image using the specified 01508 % interpolation method. 01509 % 01510 % The format of the InterpolativeResizeImage method is: 01511 % 01512 % Image *InterpolativeResizeImage(const Image *image,const size_t columns, 01513 % const size_t rows,const PixelInterpolateMethod method, 01514 % ExceptionInfo *exception) 01515 % 01516 % A description of each parameter follows: 01517 % 01518 % o image: the image. 01519 % 01520 % o columns: the number of columns in the resized image. 01521 % 01522 % o rows: the number of rows in the resized image. 01523 % 01524 % o method: the pixel interpolation method. 01525 % 01526 % o exception: return any errors or warnings in this structure. 01527 % 01528 */ 01529 MagickExport Image *InterpolativeResizeImage(const Image *image, 01530 const size_t columns,const size_t rows,const PixelInterpolateMethod method, 01531 ExceptionInfo *exception) 01532 { 01533 #define InterpolativeResizeImageTag "Resize/Image" 01534 01535 CacheView 01536 *image_view, 01537 *resize_view; 01538 01539 Image 01540 *resize_image; 01541 01542 MagickBooleanType 01543 status; 01544 01545 MagickOffsetType 01546 progress; 01547 01548 PointInfo 01549 scale; 01550 01551 ssize_t 01552 y; 01553 01554 /* 01555 Interpolatively resize image. 01556 */ 01557 assert(image != (const Image *) NULL); 01558 assert(image->signature == MagickSignature); 01559 if (image->debug != MagickFalse) 01560 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 01561 assert(exception != (ExceptionInfo *) NULL); 01562 assert(exception->signature == MagickSignature); 01563 if ((columns == 0) || (rows == 0)) 01564 return((Image *) NULL); 01565 if ((columns == image->columns) && (rows == image->rows)) 01566 return(CloneImage(image,0,0,MagickTrue,exception)); 01567 resize_image=CloneImage(image,columns,rows,MagickTrue,exception); 01568 if (resize_image == (Image *) NULL) 01569 return((Image *) NULL); 01570 if (SetImageStorageClass(resize_image,DirectClass,exception) == MagickFalse) 01571 { 01572 resize_image=DestroyImage(resize_image); 01573 return((Image *) NULL); 01574 } 01575 status=MagickTrue; 01576 progress=0; 01577 image_view=AcquireCacheView(image); 01578 resize_view=AcquireCacheView(resize_image); 01579 scale.x=(double) image->columns/resize_image->columns; 01580 scale.y=(double) image->rows/resize_image->rows; 01581 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01582 #pragma omp parallel for schedule(static) shared(progress,status) 01583 #endif 01584 for (y=0; y < (ssize_t) resize_image->rows; y++) 01585 { 01586 PointInfo 01587 offset; 01588 01589 register Quantum 01590 *restrict q; 01591 01592 register ssize_t 01593 x; 01594 01595 if (status == MagickFalse) 01596 continue; 01597 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1, 01598 exception); 01599 if (q == (Quantum *) NULL) 01600 continue; 01601 offset.y=((MagickRealType) y+0.5)*scale.y-0.5; 01602 for (x=0; x < (ssize_t) resize_image->columns; x++) 01603 { 01604 register ssize_t 01605 i; 01606 01607 if (GetPixelMask(resize_image,q) != 0) 01608 { 01609 q+=GetPixelChannels(resize_image); 01610 continue; 01611 } 01612 for (i=0; i < (ssize_t) GetPixelChannels(resize_image); i++) 01613 { 01614 PixelChannel 01615 channel; 01616 01617 PixelTrait 01618 resize_traits, 01619 traits; 01620 01621 channel=GetPixelChannelMapChannel(image,i); 01622 traits=GetPixelChannelMapTraits(image,channel); 01623 resize_traits=GetPixelChannelMapTraits(resize_image,channel); 01624 if ((traits == UndefinedPixelTrait) || 01625 (resize_traits == UndefinedPixelTrait)) 01626 continue; 01627 offset.x=((MagickRealType) x+0.5)*scale.x-0.5; 01628 status=InterpolatePixelChannels(image,image_view,resize_image,method, 01629 offset.x,offset.y,q,exception); 01630 } 01631 q+=GetPixelChannels(resize_image); 01632 } 01633 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse) 01634 continue; 01635 if (image->progress_monitor != (MagickProgressMonitor) NULL) 01636 { 01637 MagickBooleanType 01638 proceed; 01639 01640 #if defined(MAGICKCORE_OPENMP_SUPPORT) 01641 #pragma omp critical (MagickCore_InterpolativeResizeImage) 01642 #endif 01643 proceed=SetImageProgress(image,InterpolativeResizeImageTag,progress++, 01644 image->rows); 01645 if (proceed == MagickFalse) 01646 status=MagickFalse; 01647 } 01648 } 01649 resize_view=DestroyCacheView(resize_view); 01650 image_view=DestroyCacheView(image_view); 01651 if (status == MagickFalse) 01652 resize_image=DestroyImage(resize_image); 01653 return(resize_image); 01654 } 01655 #if defined(MAGICKCORE_LQR_DELEGATE) 01656 01657 /* 01658 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01659 % % 01660 % % 01661 % % 01662 % L i q u i d R e s c a l e I m a g e % 01663 % % 01664 % % 01665 % % 01666 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01667 % 01668 % LiquidRescaleImage() rescales image with seam carving. 01669 % 01670 % The format of the LiquidRescaleImage method is: 01671 % 01672 % Image *LiquidRescaleImage(const Image *image,const size_t columns, 01673 % const size_t rows,const double delta_x,const double rigidity, 01674 % ExceptionInfo *exception) 01675 % 01676 % A description of each parameter follows: 01677 % 01678 % o image: the image. 01679 % 01680 % o columns: the number of columns in the rescaled image. 01681 % 01682 % o rows: the number of rows in the rescaled image. 01683 % 01684 % o delta_x: maximum seam transversal step (0 means straight seams). 01685 % 01686 % o rigidity: introduce a bias for non-straight seams (typically 0). 01687 % 01688 % o exception: return any errors or warnings in this structure. 01689 % 01690 */ 01691 MagickExport Image *LiquidRescaleImage(const Image *image,const size_t columns, 01692 const size_t rows,const double delta_x,const double rigidity, 01693 ExceptionInfo *exception) 01694 { 01695 #define LiquidRescaleImageTag "Rescale/Image" 01696 01697 CacheView 01698 *image_view, 01699 *rescale_view; 01700 01701 gfloat 01702 *packet, 01703 *pixels; 01704 01705 Image 01706 *rescale_image; 01707 01708 int 01709 x_offset, 01710 y_offset; 01711 01712 LqrCarver 01713 *carver; 01714 01715 LqrRetVal 01716 lqr_status; 01717 01718 MagickBooleanType 01719 status; 01720 01721 register gfloat 01722 *q; 01723 01724 ssize_t 01725 y; 01726 01727 /* 01728 Liquid rescale image. 01729 */ 01730 assert(image != (const Image *) NULL); 01731 assert(image->signature == MagickSignature); 01732 if (image->debug != MagickFalse) 01733 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 01734 assert(exception != (ExceptionInfo *) NULL); 01735 assert(exception->signature == MagickSignature); 01736 if ((columns == 0) || (rows == 0)) 01737 return((Image *) NULL); 01738 if ((columns == image->columns) && (rows == image->rows)) 01739 return(CloneImage(image,0,0,MagickTrue,exception)); 01740 if ((columns <= 2) || (rows <= 2)) 01741 return(ResizeImage(image,columns,rows,image->filter,image->blur,exception)); 01742 if ((columns >= (2*image->columns)) || (rows >= (2*image->rows))) 01743 { 01744 Image 01745 *resize_image; 01746 01747 size_t 01748 height, 01749 width; 01750 01751 /* 01752 Honor liquid resize size limitations. 01753 */ 01754 for (width=image->columns; columns >= (2*width-1); width*=2); 01755 for (height=image->rows; rows >= (2*height-1); height*=2); 01756 resize_image=ResizeImage(image,width,height,image->filter,image->blur, 01757 exception); 01758 if (resize_image == (Image *) NULL) 01759 return((Image *) NULL); 01760 rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x, 01761 rigidity,exception); 01762 resize_image=DestroyImage(resize_image); 01763 return(rescale_image); 01764 } 01765 pixels=(gfloat *) AcquireQuantumMemory(image->columns,image->rows* 01766 GetPixelChannels(image)*sizeof(*pixels)); 01767 if (pixels == (gfloat *) NULL) 01768 return((Image *) NULL); 01769 status=MagickTrue; 01770 q=pixels; 01771 image_view=AcquireCacheView(image); 01772 for (y=0; y < (ssize_t) image->rows; y++) 01773 { 01774 register const Quantum 01775 *restrict p; 01776 01777 register ssize_t 01778 x; 01779 01780 if (status == MagickFalse) 01781 continue; 01782 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 01783 if (p == (const Quantum *) NULL) 01784 { 01785 status=MagickFalse; 01786 continue; 01787 } 01788 for (x=0; x < (ssize_t) image->columns; x++) 01789 { 01790 register ssize_t 01791 i; 01792 01793 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 01794 *q++=QuantumScale*p[i]; 01795 p+=GetPixelChannels(image); 01796 } 01797 } 01798 image_view=DestroyCacheView(image_view); 01799 carver=lqr_carver_new_ext(pixels,image->columns,image->rows, 01800 GetPixelChannels(image),LQR_COLDEPTH_32F); 01801 if (carver == (LqrCarver *) NULL) 01802 { 01803 pixels=(gfloat *) RelinquishMagickMemory(pixels); 01804 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); 01805 } 01806 lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity); 01807 lqr_status=lqr_carver_resize(carver,columns,rows); 01808 (void) lqr_status; 01809 rescale_image=CloneImage(image,lqr_carver_get_width(carver), 01810 lqr_carver_get_height(carver),MagickTrue,exception); 01811 if (rescale_image == (Image *) NULL) 01812 { 01813 pixels=(gfloat *) RelinquishMagickMemory(pixels); 01814 return((Image *) NULL); 01815 } 01816 if (SetImageStorageClass(rescale_image,DirectClass,exception) == MagickFalse) 01817 { 01818 pixels=(gfloat *) RelinquishMagickMemory(pixels); 01819 rescale_image=DestroyImage(rescale_image); 01820 return((Image *) NULL); 01821 } 01822 rescale_view=AcquireCacheView(rescale_image); 01823 (void) lqr_carver_scan_reset(carver); 01824 while (lqr_carver_scan_ext(carver,&x_offset,&y_offset,(void **) &packet) != 0) 01825 { 01826 register Quantum 01827 *restrict q; 01828 01829 register ssize_t 01830 i; 01831 01832 q=QueueCacheViewAuthenticPixels(rescale_view,x_offset,y_offset,1,1, 01833 exception); 01834 if (q == (Quantum *) NULL) 01835 break; 01836 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 01837 { 01838 PixelChannel 01839 channel; 01840 01841 PixelTrait 01842 rescale_traits, 01843 traits; 01844 01845 channel=GetPixelChannelMapChannel(image,i); 01846 traits=GetPixelChannelMapTraits(image,channel); 01847 rescale_traits=GetPixelChannelMapTraits(rescale_image,channel); 01848 if ((traits == UndefinedPixelTrait) || 01849 (rescale_traits == UndefinedPixelTrait)) 01850 continue; 01851 SetPixelChannel(rescale_image,channel,ClampToQuantum(QuantumRange* 01852 packet[i]),q); 01853 } 01854 if (SyncCacheViewAuthenticPixels(rescale_view,exception) == MagickFalse) 01855 break; 01856 } 01857 rescale_view=DestroyCacheView(rescale_view); 01858 lqr_carver_destroy(carver); 01859 return(rescale_image); 01860 } 01861 #else 01862 MagickExport Image *LiquidRescaleImage(const Image *image, 01863 const size_t magick_unused(columns),const size_t magick_unused(rows), 01864 const double magick_unused(delta_x),const double magick_unused(rigidity), 01865 ExceptionInfo *exception) 01866 { 01867 assert(image != (const Image *) NULL); 01868 assert(image->signature == MagickSignature); 01869 if (image->debug != MagickFalse) 01870 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 01871 assert(exception != (ExceptionInfo *) NULL); 01872 assert(exception->signature == MagickSignature); 01873 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError, 01874 "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename); 01875 return((Image *) NULL); 01876 } 01877 #endif 01878 01879 /* 01880 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01881 % % 01882 % % 01883 % % 01884 % M a g n i f y I m a g e % 01885 % % 01886 % % 01887 % % 01888 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01889 % 01890 % MagnifyImage() is a convenience method that scales an image proportionally 01891 % to twice its size. 01892 % 01893 % The format of the MagnifyImage method is: 01894 % 01895 % Image *MagnifyImage(const Image *image,ExceptionInfo *exception) 01896 % 01897 % A description of each parameter follows: 01898 % 01899 % o image: the image. 01900 % 01901 % o exception: return any errors or warnings in this structure. 01902 % 01903 */ 01904 MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception) 01905 { 01906 Image 01907 *magnify_image; 01908 01909 assert(image != (Image *) NULL); 01910 assert(image->signature == MagickSignature); 01911 if (image->debug != MagickFalse) 01912 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 01913 assert(exception != (ExceptionInfo *) NULL); 01914 assert(exception->signature == MagickSignature); 01915 magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter, 01916 1.0,exception); 01917 return(magnify_image); 01918 } 01919 01920 /* 01921 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01922 % % 01923 % % 01924 % % 01925 % M i n i f y I m a g e % 01926 % % 01927 % % 01928 % % 01929 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01930 % 01931 % MinifyImage() is a convenience method that scales an image proportionally to 01932 % half its size. 01933 % 01934 % The format of the MinifyImage method is: 01935 % 01936 % Image *MinifyImage(const Image *image,ExceptionInfo *exception) 01937 % 01938 % A description of each parameter follows: 01939 % 01940 % o image: the image. 01941 % 01942 % o exception: return any errors or warnings in this structure. 01943 % 01944 */ 01945 MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception) 01946 { 01947 Image 01948 *minify_image; 01949 01950 assert(image != (Image *) NULL); 01951 assert(image->signature == MagickSignature); 01952 if (image->debug != MagickFalse) 01953 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 01954 assert(exception != (ExceptionInfo *) NULL); 01955 assert(exception->signature == MagickSignature); 01956 minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,1.0, 01957 exception); 01958 return(minify_image); 01959 } 01960 01961 /* 01962 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01963 % % 01964 % % 01965 % % 01966 % R e s a m p l e I m a g e % 01967 % % 01968 % % 01969 % % 01970 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 01971 % 01972 % ResampleImage() resize image in terms of its pixel size, so that when 01973 % displayed at the given resolution it will be the same size in terms of 01974 % real world units as the original image at the original resolution. 01975 % 01976 % The format of the ResampleImage method is: 01977 % 01978 % Image *ResampleImage(Image *image,const double x_resolution, 01979 % const double y_resolution,const FilterTypes filter,const double blur, 01980 % ExceptionInfo *exception) 01981 % 01982 % A description of each parameter follows: 01983 % 01984 % o image: the image to be resized to fit the given resolution. 01985 % 01986 % o x_resolution: the new image x resolution. 01987 % 01988 % o y_resolution: the new image y resolution. 01989 % 01990 % o filter: Image filter to use. 01991 % 01992 % o blur: the blur factor where > 1 is blurry, < 1 is sharp. 01993 % 01994 */ 01995 MagickExport Image *ResampleImage(const Image *image,const double x_resolution, 01996 const double y_resolution,const FilterTypes filter,const double blur, 01997 ExceptionInfo *exception) 01998 { 01999 #define ResampleImageTag "Resample/Image" 02000 02001 Image 02002 *resample_image; 02003 02004 size_t 02005 height, 02006 width; 02007 02008 /* 02009 Initialize sampled image attributes. 02010 */ 02011 assert(image != (const Image *) NULL); 02012 assert(image->signature == MagickSignature); 02013 if (image->debug != MagickFalse) 02014 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 02015 assert(exception != (ExceptionInfo *) NULL); 02016 assert(exception->signature == MagickSignature); 02017 width=(size_t) (x_resolution*image->columns/(image->resolution.x == 0.0 ? 02018 72.0 : image->resolution.x)+0.5); 02019 height=(size_t) (y_resolution*image->rows/(image->resolution.y == 0.0 ? 02020 72.0 : image->resolution.y)+0.5); 02021 resample_image=ResizeImage(image,width,height,filter,blur,exception); 02022 if (resample_image != (Image *) NULL) 02023 { 02024 resample_image->resolution.x=x_resolution; 02025 resample_image->resolution.y=y_resolution; 02026 } 02027 return(resample_image); 02028 } 02029 02030 /* 02031 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02032 % % 02033 % % 02034 % % 02035 % R e s i z e I m a g e % 02036 % % 02037 % % 02038 % % 02039 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02040 % 02041 % ResizeImage() scales an image to the desired dimensions, using the given 02042 % filter (see AcquireFilterInfo()). 02043 % 02044 % If an undefined filter is given the filter defaults to Mitchell for a 02045 % colormapped image, a image with a matte channel, or if the image is 02046 % enlarged. Otherwise the filter defaults to a Lanczos. 02047 % 02048 % ResizeImage() was inspired by Paul Heckbert's "zoom" program. 02049 % 02050 % The format of the ResizeImage method is: 02051 % 02052 % Image *ResizeImage(Image *image,const size_t columns, 02053 % const size_t rows,const FilterTypes filter,const double blur, 02054 % ExceptionInfo *exception) 02055 % 02056 % A description of each parameter follows: 02057 % 02058 % o image: the image. 02059 % 02060 % o columns: the number of columns in the scaled image. 02061 % 02062 % o rows: the number of rows in the scaled image. 02063 % 02064 % o filter: Image filter to use. 02065 % 02066 % o blur: the blur factor where > 1 is blurry, < 1 is sharp. Typically set 02067 % this to 1.0. 02068 % 02069 % o exception: return any errors or warnings in this structure. 02070 % 02071 */ 02072 02073 typedef struct _ContributionInfo 02074 { 02075 MagickRealType 02076 weight; 02077 02078 ssize_t 02079 pixel; 02080 } ContributionInfo; 02081 02082 static ContributionInfo **DestroyContributionThreadSet( 02083 ContributionInfo **contribution) 02084 { 02085 register ssize_t 02086 i; 02087 02088 assert(contribution != (ContributionInfo **) NULL); 02089 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++) 02090 if (contribution[i] != (ContributionInfo *) NULL) 02091 contribution[i]=(ContributionInfo *) RelinquishMagickMemory( 02092 contribution[i]); 02093 contribution=(ContributionInfo **) RelinquishMagickMemory(contribution); 02094 return(contribution); 02095 } 02096 02097 static ContributionInfo **AcquireContributionThreadSet(const size_t count) 02098 { 02099 register ssize_t 02100 i; 02101 02102 ContributionInfo 02103 **contribution; 02104 02105 size_t 02106 number_threads; 02107 02108 number_threads=GetOpenMPMaximumThreads(); 02109 contribution=(ContributionInfo **) AcquireQuantumMemory(number_threads, 02110 sizeof(*contribution)); 02111 if (contribution == (ContributionInfo **) NULL) 02112 return((ContributionInfo **) NULL); 02113 (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution)); 02114 for (i=0; i < (ssize_t) number_threads; i++) 02115 { 02116 contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count, 02117 sizeof(**contribution)); 02118 if (contribution[i] == (ContributionInfo *) NULL) 02119 return(DestroyContributionThreadSet(contribution)); 02120 } 02121 return(contribution); 02122 } 02123 02124 static inline double MagickMax(const double x,const double y) 02125 { 02126 if (x > y) 02127 return(x); 02128 return(y); 02129 } 02130 02131 static inline double MagickMin(const double x,const double y) 02132 { 02133 if (x < y) 02134 return(x); 02135 return(y); 02136 } 02137 02138 static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter, 02139 const Image *image,Image *resize_image,const MagickRealType x_factor, 02140 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception) 02141 { 02142 #define ResizeImageTag "Resize/Image" 02143 02144 CacheView 02145 *image_view, 02146 *resize_view; 02147 02148 ClassType 02149 storage_class; 02150 02151 ContributionInfo 02152 **restrict contributions; 02153 02154 MagickBooleanType 02155 status; 02156 02157 MagickRealType 02158 scale, 02159 support; 02160 02161 ssize_t 02162 x; 02163 02164 /* 02165 Apply filter to resize horizontally from image to resize image. 02166 */ 02167 scale=MagickMax(1.0/x_factor+MagickEpsilon,1.0); 02168 support=scale*GetResizeFilterSupport(resize_filter); 02169 storage_class=support > 0.5 ? DirectClass : image->storage_class; 02170 if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse) 02171 return(MagickFalse); 02172 if (support < 0.5) 02173 { 02174 /* 02175 Support too small even for nearest neighbour: Reduce to point sampling. 02176 */ 02177 support=(MagickRealType) 0.5; 02178 scale=1.0; 02179 } 02180 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0)); 02181 if (contributions == (ContributionInfo **) NULL) 02182 { 02183 (void) ThrowMagickException(exception,GetMagickModule(), 02184 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); 02185 return(MagickFalse); 02186 } 02187 status=MagickTrue; 02188 scale=1.0/scale; 02189 image_view=AcquireCacheView(image); 02190 resize_view=AcquireCacheView(resize_image); 02191 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02192 #pragma omp parallel for schedule(static,4) shared(status) 02193 #endif 02194 for (x=0; x < (ssize_t) resize_image->columns; x++) 02195 { 02196 MagickRealType 02197 bisect, 02198 density; 02199 02200 register const Quantum 02201 *restrict p; 02202 02203 register ContributionInfo 02204 *restrict contribution; 02205 02206 register Quantum 02207 *restrict q; 02208 02209 register ssize_t 02210 y; 02211 02212 ssize_t 02213 n, 02214 start, 02215 stop; 02216 02217 if (status == MagickFalse) 02218 continue; 02219 bisect=(MagickRealType) (x+0.5)/x_factor; 02220 start=(ssize_t) MagickMax(bisect-support+0.5,0.0); 02221 stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->columns); 02222 density=0.0; 02223 contribution=contributions[GetOpenMPThreadId()]; 02224 for (n=0; n < (stop-start); n++) 02225 { 02226 contribution[n].pixel=start+n; 02227 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale* 02228 ((MagickRealType) (start+n)-bisect+0.5)); 02229 density+=contribution[n].weight; 02230 } 02231 if ((density != 0.0) && (density != 1.0)) 02232 { 02233 register ssize_t 02234 i; 02235 02236 /* 02237 Normalize. 02238 */ 02239 density=1.0/density; 02240 for (i=0; i < n; i++) 02241 contribution[i].weight*=density; 02242 } 02243 p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,(size_t) 02244 (contribution[n-1].pixel-contribution[0].pixel+1),image->rows,exception); 02245 q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows, 02246 exception); 02247 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 02248 { 02249 status=MagickFalse; 02250 continue; 02251 } 02252 for (y=0; y < (ssize_t) resize_image->rows; y++) 02253 { 02254 register ssize_t 02255 i; 02256 02257 for (i=0; i < (ssize_t) GetPixelChannels(resize_image); i++) 02258 { 02259 MagickRealType 02260 alpha, 02261 gamma, 02262 pixel; 02263 02264 PixelChannel 02265 channel; 02266 02267 PixelTrait 02268 resize_traits, 02269 traits; 02270 02271 register ssize_t 02272 j; 02273 02274 ssize_t 02275 k; 02276 02277 channel=GetPixelChannelMapChannel(image,i); 02278 traits=GetPixelChannelMapTraits(image,channel); 02279 resize_traits=GetPixelChannelMapTraits(resize_image,channel); 02280 if ((traits == UndefinedPixelTrait) || 02281 (resize_traits == UndefinedPixelTrait)) 02282 continue; 02283 if (((resize_traits & CopyPixelTrait) != 0) || 02284 (GetPixelMask(resize_image,q) != 0)) 02285 { 02286 j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double) 02287 stop-1.0)+0.5); 02288 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+ 02289 (contribution[j-start].pixel-contribution[0].pixel); 02290 SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i], 02291 q); 02292 continue; 02293 } 02294 pixel=0.0; 02295 if ((resize_traits & BlendPixelTrait) == 0) 02296 { 02297 /* 02298 No alpha blending. 02299 */ 02300 for (j=0; j < n; j++) 02301 { 02302 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+ 02303 (contribution[j].pixel-contribution[0].pixel); 02304 alpha=contribution[j].weight; 02305 pixel+=alpha*p[k*GetPixelChannels(image)+i]; 02306 } 02307 SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q); 02308 continue; 02309 } 02310 /* 02311 Alpha blending. 02312 */ 02313 gamma=0.0; 02314 for (j=0; j < n; j++) 02315 { 02316 k=y*(contribution[n-1].pixel-contribution[0].pixel+1)+ 02317 (contribution[j].pixel-contribution[0].pixel); 02318 alpha=contribution[j].weight*QuantumScale* 02319 GetPixelAlpha(image,p+k*GetPixelChannels(image)); 02320 pixel+=alpha*p[k*GetPixelChannels(image)+i]; 02321 gamma+=alpha; 02322 } 02323 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma); 02324 SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q); 02325 } 02326 q+=GetPixelChannels(resize_image); 02327 } 02328 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse) 02329 status=MagickFalse; 02330 if (image->progress_monitor != (MagickProgressMonitor) NULL) 02331 { 02332 MagickBooleanType 02333 proceed; 02334 02335 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02336 #pragma omp critical (MagickCore_HorizontalFilter) 02337 #endif 02338 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span); 02339 if (proceed == MagickFalse) 02340 status=MagickFalse; 02341 } 02342 } 02343 resize_view=DestroyCacheView(resize_view); 02344 image_view=DestroyCacheView(image_view); 02345 contributions=DestroyContributionThreadSet(contributions); 02346 return(status); 02347 } 02348 02349 static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter, 02350 const Image *image,Image *resize_image,const MagickRealType y_factor, 02351 const MagickSizeType span,MagickOffsetType *offset,ExceptionInfo *exception) 02352 { 02353 CacheView 02354 *image_view, 02355 *resize_view; 02356 02357 ClassType 02358 storage_class; 02359 02360 ContributionInfo 02361 **restrict contributions; 02362 02363 MagickBooleanType 02364 status; 02365 02366 PixelInfo 02367 zero; 02368 02369 MagickRealType 02370 scale, 02371 support; 02372 02373 ssize_t 02374 y; 02375 02376 /* 02377 Apply filter to resize vertically from image to resize image. 02378 */ 02379 scale=MagickMax(1.0/y_factor+MagickEpsilon,1.0); 02380 support=scale*GetResizeFilterSupport(resize_filter); 02381 storage_class=support > 0.5 ? DirectClass : image->storage_class; 02382 if (SetImageStorageClass(resize_image,storage_class,exception) == MagickFalse) 02383 return(MagickFalse); 02384 if (support < 0.5) 02385 { 02386 /* 02387 Support too small even for nearest neighbour: Reduce to point sampling. 02388 */ 02389 support=(MagickRealType) 0.5; 02390 scale=1.0; 02391 } 02392 contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0)); 02393 if (contributions == (ContributionInfo **) NULL) 02394 { 02395 (void) ThrowMagickException(exception,GetMagickModule(), 02396 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename); 02397 return(MagickFalse); 02398 } 02399 status=MagickTrue; 02400 scale=1.0/scale; 02401 (void) ResetMagickMemory(&zero,0,sizeof(zero)); 02402 image_view=AcquireCacheView(image); 02403 resize_view=AcquireCacheView(resize_image); 02404 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02405 #pragma omp parallel for schedule(static,4) shared(status) 02406 #endif 02407 for (y=0; y < (ssize_t) resize_image->rows; y++) 02408 { 02409 MagickRealType 02410 bisect, 02411 density; 02412 02413 register const Quantum 02414 *restrict p; 02415 02416 register ContributionInfo 02417 *restrict contribution; 02418 02419 register Quantum 02420 *restrict q; 02421 02422 register ssize_t 02423 x; 02424 02425 ssize_t 02426 n, 02427 start, 02428 stop; 02429 02430 if (status == MagickFalse) 02431 continue; 02432 bisect=(MagickRealType) (y+0.5)/y_factor; 02433 start=(ssize_t) MagickMax(bisect-support+0.5,0.0); 02434 stop=(ssize_t) MagickMin(bisect+support+0.5,(double) image->rows); 02435 density=0.0; 02436 contribution=contributions[GetOpenMPThreadId()]; 02437 for (n=0; n < (stop-start); n++) 02438 { 02439 contribution[n].pixel=start+n; 02440 contribution[n].weight=GetResizeFilterWeight(resize_filter,scale* 02441 ((MagickRealType) (start+n)-bisect+0.5)); 02442 density+=contribution[n].weight; 02443 } 02444 if ((density != 0.0) && (density != 1.0)) 02445 { 02446 register ssize_t 02447 i; 02448 02449 /* 02450 Normalize. 02451 */ 02452 density=1.0/density; 02453 for (i=0; i < n; i++) 02454 contribution[i].weight*=density; 02455 } 02456 p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel, 02457 image->columns,(size_t) (contribution[n-1].pixel-contribution[0].pixel+1), 02458 exception); 02459 q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1, 02460 exception); 02461 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 02462 { 02463 status=MagickFalse; 02464 continue; 02465 } 02466 for (x=0; x < (ssize_t) resize_image->columns; x++) 02467 { 02468 register ssize_t 02469 i; 02470 02471 for (i=0; i < (ssize_t) GetPixelChannels(resize_image); i++) 02472 { 02473 MagickRealType 02474 alpha, 02475 gamma, 02476 pixel; 02477 02478 PixelChannel 02479 channel; 02480 02481 PixelTrait 02482 resize_traits, 02483 traits; 02484 02485 register ssize_t 02486 j; 02487 02488 ssize_t 02489 k; 02490 02491 channel=GetPixelChannelMapChannel(image,i); 02492 traits=GetPixelChannelMapTraits(image,channel); 02493 resize_traits=GetPixelChannelMapTraits(resize_image,channel); 02494 if ((traits == UndefinedPixelTrait) || 02495 (resize_traits == UndefinedPixelTrait)) 02496 continue; 02497 if (((resize_traits & CopyPixelTrait) != 0) || 02498 (GetPixelMask(resize_image,q) != 0)) 02499 { 02500 j=(ssize_t) (MagickMin(MagickMax(bisect,(double) start),(double) 02501 stop-1.0)+0.5); 02502 k=(ssize_t) ((contribution[j-start].pixel-contribution[0].pixel)* 02503 image->columns+x); 02504 SetPixelChannel(resize_image,channel,p[k*GetPixelChannels(image)+i], 02505 q); 02506 continue; 02507 } 02508 pixel=0.0; 02509 if ((resize_traits & BlendPixelTrait) == 0) 02510 { 02511 /* 02512 No alpha blending. 02513 */ 02514 for (j=0; j < n; j++) 02515 { 02516 k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)* 02517 image->columns+x); 02518 alpha=contribution[j].weight; 02519 pixel+=alpha*p[k*GetPixelChannels(image)+i]; 02520 } 02521 SetPixelChannel(resize_image,channel,ClampToQuantum(pixel),q); 02522 continue; 02523 } 02524 gamma=0.0; 02525 for (j=0; j < n; j++) 02526 { 02527 k=(ssize_t) ((contribution[j].pixel-contribution[0].pixel)* 02528 image->columns+x); 02529 alpha=contribution[j].weight*QuantumScale* 02530 GetPixelAlpha(image,p+k*GetPixelChannels(image)); 02531 pixel+=alpha*p[k*GetPixelChannels(image)+i]; 02532 gamma+=alpha; 02533 } 02534 gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma); 02535 SetPixelChannel(resize_image,channel,ClampToQuantum(gamma*pixel),q); 02536 } 02537 q+=GetPixelChannels(resize_image); 02538 } 02539 if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse) 02540 status=MagickFalse; 02541 if (image->progress_monitor != (MagickProgressMonitor) NULL) 02542 { 02543 MagickBooleanType 02544 proceed; 02545 02546 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02547 #pragma omp critical (MagickCore_VerticalFilter) 02548 #endif 02549 proceed=SetImageProgress(image,ResizeImageTag,(*offset)++,span); 02550 if (proceed == MagickFalse) 02551 status=MagickFalse; 02552 } 02553 } 02554 resize_view=DestroyCacheView(resize_view); 02555 image_view=DestroyCacheView(image_view); 02556 contributions=DestroyContributionThreadSet(contributions); 02557 return(status); 02558 } 02559 02560 MagickExport Image *ResizeImage(const Image *image,const size_t columns, 02561 const size_t rows,const FilterTypes filter,const double blur, 02562 ExceptionInfo *exception) 02563 { 02564 #define WorkLoadFactor 0.265 02565 02566 FilterTypes 02567 filter_type; 02568 02569 Image 02570 *filter_image, 02571 *resize_image; 02572 02573 MagickOffsetType 02574 offset; 02575 02576 MagickRealType 02577 x_factor, 02578 y_factor; 02579 02580 MagickSizeType 02581 span; 02582 02583 MagickStatusType 02584 status; 02585 02586 ResizeFilter 02587 *resize_filter; 02588 02589 /* 02590 Acquire resize image. 02591 */ 02592 assert(image != (Image *) NULL); 02593 assert(image->signature == MagickSignature); 02594 if (image->debug != MagickFalse) 02595 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 02596 assert(exception != (ExceptionInfo *) NULL); 02597 assert(exception->signature == MagickSignature); 02598 if ((columns == 0) || (rows == 0)) 02599 ThrowImageException(ImageError,"NegativeOrZeroImageSize"); 02600 if ((columns == image->columns) && (rows == image->rows) && 02601 (filter == UndefinedFilter) && (blur == 1.0)) 02602 return(CloneImage(image,0,0,MagickTrue,exception)); 02603 resize_image=CloneImage(image,columns,rows,MagickTrue,exception); 02604 if (resize_image == (Image *) NULL) 02605 return(resize_image); 02606 /* 02607 Acquire resize filter. 02608 */ 02609 x_factor=(MagickRealType) columns/(MagickRealType) image->columns; 02610 y_factor=(MagickRealType) rows/(MagickRealType) image->rows; 02611 if ((x_factor*y_factor) > WorkLoadFactor) 02612 filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception); 02613 else 02614 filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception); 02615 if (filter_image == (Image *) NULL) 02616 return(DestroyImage(resize_image)); 02617 filter_type=LanczosFilter; 02618 if (filter != UndefinedFilter) 02619 filter_type=filter; 02620 else 02621 if ((x_factor == 1.0) && (y_factor == 1.0)) 02622 filter_type=PointFilter; 02623 else 02624 if ((image->storage_class == PseudoClass) || 02625 (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0)) 02626 filter_type=MitchellFilter; 02627 resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse, 02628 exception); 02629 /* 02630 Resize image. 02631 */ 02632 offset=0; 02633 if ((x_factor*y_factor) > WorkLoadFactor) 02634 { 02635 span=(MagickSizeType) (filter_image->columns+rows); 02636 status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span, 02637 &offset,exception); 02638 status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor, 02639 span,&offset,exception); 02640 } 02641 else 02642 { 02643 span=(MagickSizeType) (filter_image->rows+columns); 02644 status=VerticalFilter(resize_filter,image,filter_image,y_factor,span, 02645 &offset,exception); 02646 status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor, 02647 span,&offset,exception); 02648 } 02649 /* 02650 Free resources. 02651 */ 02652 filter_image=DestroyImage(filter_image); 02653 resize_filter=DestroyResizeFilter(resize_filter); 02654 if (status == MagickFalse) 02655 { 02656 resize_image=DestroyImage(resize_image); 02657 return((Image *) NULL); 02658 } 02659 resize_image->type=image->type; 02660 return(resize_image); 02661 } 02662 02663 /* 02664 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02665 % % 02666 % % 02667 % % 02668 % S a m p l e I m a g e % 02669 % % 02670 % % 02671 % % 02672 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02673 % 02674 % SampleImage() scales an image to the desired dimensions with pixel 02675 % sampling. Unlike other scaling methods, this method does not introduce 02676 % any additional color into the scaled image. 02677 % 02678 % The format of the SampleImage method is: 02679 % 02680 % Image *SampleImage(const Image *image,const size_t columns, 02681 % const size_t rows,ExceptionInfo *exception) 02682 % 02683 % A description of each parameter follows: 02684 % 02685 % o image: the image. 02686 % 02687 % o columns: the number of columns in the sampled image. 02688 % 02689 % o rows: the number of rows in the sampled image. 02690 % 02691 % o exception: return any errors or warnings in this structure. 02692 % 02693 */ 02694 MagickExport Image *SampleImage(const Image *image,const size_t columns, 02695 const size_t rows,ExceptionInfo *exception) 02696 { 02697 #define SampleImageTag "Sample/Image" 02698 02699 CacheView 02700 *image_view, 02701 *sample_view; 02702 02703 Image 02704 *sample_image; 02705 02706 MagickBooleanType 02707 status; 02708 02709 MagickOffsetType 02710 progress; 02711 02712 register ssize_t 02713 x; 02714 02715 ssize_t 02716 *x_offset, 02717 y; 02718 02719 /* 02720 Initialize sampled image attributes. 02721 */ 02722 assert(image != (const Image *) NULL); 02723 assert(image->signature == MagickSignature); 02724 if (image->debug != MagickFalse) 02725 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 02726 assert(exception != (ExceptionInfo *) NULL); 02727 assert(exception->signature == MagickSignature); 02728 if ((columns == 0) || (rows == 0)) 02729 ThrowImageException(ImageError,"NegativeOrZeroImageSize"); 02730 if ((columns == image->columns) && (rows == image->rows)) 02731 return(CloneImage(image,0,0,MagickTrue,exception)); 02732 sample_image=CloneImage(image,columns,rows,MagickTrue,exception); 02733 if (sample_image == (Image *) NULL) 02734 return((Image *) NULL); 02735 /* 02736 Allocate scan line buffer and column offset buffers. 02737 */ 02738 x_offset=(ssize_t *) AcquireQuantumMemory((size_t) sample_image->columns, 02739 sizeof(*x_offset)); 02740 if (x_offset == (ssize_t *) NULL) 02741 { 02742 sample_image=DestroyImage(sample_image); 02743 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); 02744 } 02745 for (x=0; x < (ssize_t) sample_image->columns; x++) 02746 x_offset[x]=(ssize_t) (((MagickRealType) x+0.5)*image->columns/ 02747 sample_image->columns); 02748 /* 02749 Sample each row. 02750 */ 02751 status=MagickTrue; 02752 progress=0; 02753 image_view=AcquireCacheView(image); 02754 sample_view=AcquireCacheView(sample_image); 02755 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02756 #pragma omp parallel for schedule(static) shared(progress,status) 02757 #endif 02758 for (y=0; y < (ssize_t) sample_image->rows; y++) 02759 { 02760 register const Quantum 02761 *restrict p; 02762 02763 register Quantum 02764 *restrict q; 02765 02766 register ssize_t 02767 x; 02768 02769 ssize_t 02770 y_offset; 02771 02772 if (status == MagickFalse) 02773 continue; 02774 y_offset=(ssize_t) (((MagickRealType) y+0.5)*image->rows/ 02775 sample_image->rows); 02776 p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1, 02777 exception); 02778 q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1, 02779 exception); 02780 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 02781 { 02782 status=MagickFalse; 02783 continue; 02784 } 02785 /* 02786 Sample each column. 02787 */ 02788 for (x=0; x < (ssize_t) sample_image->columns; x++) 02789 { 02790 register ssize_t 02791 i; 02792 02793 if (GetPixelMask(sample_image,q) != 0) 02794 { 02795 q+=GetPixelChannels(sample_image); 02796 continue; 02797 } 02798 for (i=0; i < (ssize_t) GetPixelChannels(sample_image); i++) 02799 { 02800 PixelChannel 02801 channel; 02802 02803 PixelTrait 02804 sample_traits, 02805 traits; 02806 02807 channel=GetPixelChannelMapChannel(image,i); 02808 traits=GetPixelChannelMapTraits(image,channel); 02809 sample_traits=GetPixelChannelMapTraits(sample_image,channel); 02810 if ((traits == UndefinedPixelTrait) || 02811 (sample_traits == UndefinedPixelTrait)) 02812 continue; 02813 SetPixelChannel(sample_image,channel,p[x_offset[x]*GetPixelChannels( 02814 image)+i],q); 02815 } 02816 q+=GetPixelChannels(sample_image); 02817 } 02818 if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse) 02819 status=MagickFalse; 02820 if (image->progress_monitor != (MagickProgressMonitor) NULL) 02821 { 02822 MagickBooleanType 02823 proceed; 02824 02825 #if defined(MAGICKCORE_OPENMP_SUPPORT) 02826 #pragma omp critical (MagickCore_SampleImage) 02827 #endif 02828 proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows); 02829 if (proceed == MagickFalse) 02830 status=MagickFalse; 02831 } 02832 } 02833 image_view=DestroyCacheView(image_view); 02834 sample_view=DestroyCacheView(sample_view); 02835 x_offset=(ssize_t *) RelinquishMagickMemory(x_offset); 02836 sample_image->type=image->type; 02837 return(sample_image); 02838 } 02839 02840 /* 02841 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02842 % % 02843 % % 02844 % % 02845 % S c a l e I m a g e % 02846 % % 02847 % % 02848 % % 02849 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 02850 % 02851 % ScaleImage() changes the size of an image to the given dimensions. 02852 % 02853 % The format of the ScaleImage method is: 02854 % 02855 % Image *ScaleImage(const Image *image,const size_t columns, 02856 % const size_t rows,ExceptionInfo *exception) 02857 % 02858 % A description of each parameter follows: 02859 % 02860 % o image: the image. 02861 % 02862 % o columns: the number of columns in the scaled image. 02863 % 02864 % o rows: the number of rows in the scaled image. 02865 % 02866 % o exception: return any errors or warnings in this structure. 02867 % 02868 */ 02869 MagickExport Image *ScaleImage(const Image *image,const size_t columns, 02870 const size_t rows,ExceptionInfo *exception) 02871 { 02872 #define ScaleImageTag "Scale/Image" 02873 02874 CacheView 02875 *image_view, 02876 *scale_view; 02877 02878 Image 02879 *scale_image; 02880 02881 MagickBooleanType 02882 next_column, 02883 next_row, 02884 proceed; 02885 02886 MagickRealType 02887 alpha, 02888 gamma, 02889 pixel[CompositePixelChannel], 02890 *scale_scanline, 02891 *scanline, 02892 *x_vector, 02893 *y_vector; 02894 02895 PixelChannel 02896 channel; 02897 02898 PixelTrait 02899 scale_traits, 02900 traits; 02901 02902 PointInfo 02903 scale, 02904 span; 02905 02906 register ssize_t 02907 i; 02908 02909 ssize_t 02910 n, 02911 number_rows, 02912 y; 02913 02914 /* 02915 Initialize scaled image attributes. 02916 */ 02917 assert(image != (const Image *) NULL); 02918 assert(image->signature == MagickSignature); 02919 if (image->debug != MagickFalse) 02920 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 02921 assert(exception != (ExceptionInfo *) NULL); 02922 assert(exception->signature == MagickSignature); 02923 if ((columns == 0) || (rows == 0)) 02924 return((Image *) NULL); 02925 if ((columns == image->columns) && (rows == image->rows)) 02926 return(CloneImage(image,0,0,MagickTrue,exception)); 02927 scale_image=CloneImage(image,columns,rows,MagickTrue,exception); 02928 if (scale_image == (Image *) NULL) 02929 return((Image *) NULL); 02930 if (SetImageStorageClass(scale_image,DirectClass,exception) == MagickFalse) 02931 { 02932 scale_image=DestroyImage(scale_image); 02933 return((Image *) NULL); 02934 } 02935 /* 02936 Allocate memory. 02937 */ 02938 x_vector=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns, 02939 GetPixelChannels(image)*sizeof(*x_vector)); 02940 scanline=x_vector; 02941 if (image->rows != scale_image->rows) 02942 scanline=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns, 02943 GetPixelChannels(image)*sizeof(*scanline)); 02944 scale_scanline=(MagickRealType *) AcquireQuantumMemory((size_t) 02945 scale_image->columns,MaxPixelChannels*sizeof(*scale_scanline)); 02946 y_vector=(MagickRealType *) AcquireQuantumMemory((size_t) image->columns, 02947 GetPixelChannels(image)*sizeof(*y_vector)); 02948 if ((scanline == (MagickRealType *) NULL) || 02949 (scale_scanline == (MagickRealType *) NULL) || 02950 (x_vector == (MagickRealType *) NULL) || 02951 (y_vector == (MagickRealType *) NULL)) 02952 { 02953 scale_image=DestroyImage(scale_image); 02954 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); 02955 } 02956 /* 02957 Scale image. 02958 */ 02959 number_rows=0; 02960 next_row=MagickTrue; 02961 span.y=1.0; 02962 scale.y=(double) scale_image->rows/(double) image->rows; 02963 for (i=0; i < (ssize_t) (GetPixelChannels(image)*image->columns); i++) 02964 y_vector[i]=0.0; 02965 n=0; 02966 image_view=AcquireCacheView(image); 02967 scale_view=AcquireCacheView(scale_image); 02968 for (y=0; y < (ssize_t) scale_image->rows; y++) 02969 { 02970 register const Quantum 02971 *restrict p; 02972 02973 register Quantum 02974 *restrict q; 02975 02976 register ssize_t 02977 x; 02978 02979 q=QueueCacheViewAuthenticPixels(scale_view,0,y,scale_image->columns,1, 02980 exception); 02981 if (q == (Quantum *) NULL) 02982 break; 02983 alpha=1.0; 02984 if (scale_image->rows == image->rows) 02985 { 02986 /* 02987 Read a new scanline. 02988 */ 02989 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1, 02990 exception); 02991 if (p == (const Quantum *) NULL) 02992 break; 02993 for (x=0; x < (ssize_t) image->columns; x++) 02994 { 02995 if (GetPixelMask(image,p) != 0) 02996 { 02997 p+=GetPixelChannels(image); 02998 continue; 02999 } 03000 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 03001 { 03002 PixelChannel 03003 channel; 03004 03005 PixelTrait 03006 traits; 03007 03008 channel=GetPixelChannelMapChannel(image,i); 03009 traits=GetPixelChannelMapTraits(image,channel); 03010 if ((traits & BlendPixelTrait) == 0) 03011 { 03012 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType) p[i]; 03013 continue; 03014 } 03015 alpha=QuantumScale*GetPixelAlpha(image,p); 03016 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i]; 03017 } 03018 p+=GetPixelChannels(image); 03019 } 03020 } 03021 else 03022 { 03023 /* 03024 Scale Y direction. 03025 */ 03026 while (scale.y < span.y) 03027 { 03028 if ((next_row != MagickFalse) && 03029 (number_rows < (ssize_t) image->rows)) 03030 { 03031 /* 03032 Read a new scanline. 03033 */ 03034 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1, 03035 exception); 03036 if (p == (const Quantum *) NULL) 03037 break; 03038 for (x=0; x < (ssize_t) image->columns; x++) 03039 { 03040 if (GetPixelMask(image,p) != 0) 03041 { 03042 p+=GetPixelChannels(image); 03043 continue; 03044 } 03045 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 03046 { 03047 PixelChannel 03048 channel; 03049 03050 PixelTrait 03051 traits; 03052 03053 channel=GetPixelChannelMapChannel(image,i); 03054 traits=GetPixelChannelMapTraits(image,channel); 03055 if ((traits & BlendPixelTrait) == 0) 03056 { 03057 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType) 03058 p[i]; 03059 continue; 03060 } 03061 alpha=QuantumScale*GetPixelAlpha(image,p); 03062 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i]; 03063 } 03064 p+=GetPixelChannels(image); 03065 } 03066 number_rows++; 03067 } 03068 for (x=0; x < (ssize_t) image->columns; x++) 03069 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 03070 y_vector[x*GetPixelChannels(image)+i]+=scale.y* 03071 x_vector[x*GetPixelChannels(image)+i]; 03072 span.y-=scale.y; 03073 scale.y=(double) scale_image->rows/(double) image->rows; 03074 next_row=MagickTrue; 03075 } 03076 if ((next_row != MagickFalse) && (number_rows < (ssize_t) image->rows)) 03077 { 03078 /* 03079 Read a new scanline. 03080 */ 03081 p=GetCacheViewVirtualPixels(image_view,0,n++,image->columns,1, 03082 exception); 03083 if (p == (const Quantum *) NULL) 03084 break; 03085 for (x=0; x < (ssize_t) image->columns; x++) 03086 { 03087 if (GetPixelMask(image,p) != 0) 03088 { 03089 p+=GetPixelChannels(image); 03090 continue; 03091 } 03092 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 03093 { 03094 PixelChannel 03095 channel; 03096 03097 PixelTrait 03098 traits; 03099 03100 channel=GetPixelChannelMapChannel(image,i); 03101 traits=GetPixelChannelMapTraits(image,channel); 03102 if ((traits & BlendPixelTrait) == 0) 03103 { 03104 x_vector[x*GetPixelChannels(image)+i]=(MagickRealType) 03105 p[i]; 03106 continue; 03107 } 03108 alpha=QuantumScale*GetPixelAlpha(image,p); 03109 x_vector[x*GetPixelChannels(image)+i]=alpha*p[i]; 03110 } 03111 p+=GetPixelChannels(image); 03112 } 03113 number_rows++; 03114 next_row=MagickFalse; 03115 } 03116 for (x=0; x < (ssize_t) image->columns; x++) 03117 { 03118 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 03119 { 03120 pixel[i]=y_vector[x*GetPixelChannels(image)+i]+span.y* 03121 x_vector[x*GetPixelChannels(image)+i]; 03122 scanline[x*GetPixelChannels(image)+i]=pixel[i]; 03123 y_vector[x*GetPixelChannels(image)+i]=0.0; 03124 } 03125 } 03126 scale.y-=span.y; 03127 if (scale.y <= 0) 03128 { 03129 scale.y=(double) scale_image->rows/(double) image->rows; 03130 next_row=MagickTrue; 03131 } 03132 span.y=1.0; 03133 } 03134 if (scale_image->columns == image->columns) 03135 { 03136 /* 03137 Transfer scanline to scaled image. 03138 */ 03139 for (x=0; x < (ssize_t) scale_image->columns; x++) 03140 { 03141 if (GetPixelMask(scale_image,q) != 0) 03142 { 03143 q+=GetPixelChannels(scale_image); 03144 continue; 03145 } 03146 for (i=0; i < (ssize_t) GetPixelChannels(scale_image); i++) 03147 { 03148 ssize_t 03149 offset; 03150 03151 channel=GetPixelChannelMapChannel(scale_image,i); 03152 traits=GetPixelChannelMapTraits(image,channel); 03153 scale_traits=GetPixelChannelMapTraits(scale_image,channel); 03154 if ((traits == UndefinedPixelTrait) || 03155 (scale_traits == UndefinedPixelTrait)) 03156 continue; 03157 offset=GetPixelChannelMapOffset(image,channel); 03158 if ((traits & BlendPixelTrait) == 0) 03159 { 03160 SetPixelChannel(scale_image,channel,ClampToQuantum( 03161 scanline[x*GetPixelChannels(image)+offset]),q); 03162 continue; 03163 } 03164 alpha=QuantumScale*scanline[x*GetPixelChannels(image)+ 03165 GetPixelChannelMapChannel(image,AlphaPixelChannel)]; 03166 gamma=1.0/(fabs((double) alpha) <= MagickEpsilon ? 1.0 : alpha); 03167 SetPixelChannel(scale_image,channel,ClampToQuantum(gamma*scanline[ 03168 x*GetPixelChannels(image)+offset]),q); 03169 } 03170 q+=GetPixelChannels(scale_image); 03171 } 03172 } 03173 else 03174 { 03175 ssize_t 03176 n; 03177 03178 /* 03179 Scale X direction. 03180 */ 03181 next_column=MagickFalse; 03182 n=0; 03183 span.x=1.0; 03184 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 03185 pixel[i]=0.0; 03186 for (x=0; x < (ssize_t) image->columns; x++) 03187 { 03188 scale.x=(double) scale_image->columns/(double) image->columns; 03189 while (scale.x >= span.x) 03190 { 03191 if (next_column != MagickFalse) 03192 { 03193 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 03194 pixel[i]=0.0; 03195 n++; 03196 } 03197 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 03198 { 03199 PixelChannel 03200 channel; 03201 03202 PixelTrait 03203 traits; 03204 03205 channel=GetPixelChannelMapChannel(image,i); 03206 traits=GetPixelChannelMapTraits(image,channel); 03207 if (traits == UndefinedPixelTrait) 03208 continue; 03209 pixel[i]+=span.x*scanline[x*GetPixelChannels(image)+i]; 03210 scale_scanline[n*MaxPixelChannels+channel]=pixel[i]; 03211 } 03212 scale.x-=span.x; 03213 span.x=1.0; 03214 next_column=MagickTrue; 03215 } 03216 if (scale.x > 0) 03217 { 03218 if (next_column != MagickFalse) 03219 { 03220 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 03221 pixel[i]=0.0; 03222 n++; 03223 next_column=MagickFalse; 03224 } 03225 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 03226 pixel[i]+=scale.x*scanline[x*GetPixelChannels(image)+i]; 03227 span.x-=scale.x; 03228 } 03229 } 03230 if (span.x > 0) 03231 { 03232 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 03233 pixel[i]+=span.x*scanline[(x-1)*GetPixelChannels(image)+i]; 03234 } 03235 if ((next_column == MagickFalse) && 03236 ((ssize_t) n < (ssize_t) scale_image->columns)) 03237 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 03238 { 03239 channel=GetPixelChannelMapChannel(image,i); 03240 scale_scanline[n*MaxPixelChannels+channel]=pixel[i]; 03241 } 03242 /* 03243 Transfer scanline to scaled image. 03244 */ 03245 for (x=0; x < (ssize_t) scale_image->columns; x++) 03246 { 03247 if (GetPixelMask(scale_image,q) != 0) 03248 { 03249 q+=GetPixelChannels(scale_image); 03250 continue; 03251 } 03252 for (i=0; i < (ssize_t) GetPixelChannels(scale_image); i++) 03253 { 03254 channel=GetPixelChannelMapChannel(scale_image,i); 03255 traits=GetPixelChannelMapTraits(image,channel); 03256 scale_traits=GetPixelChannelMapTraits(scale_image,channel); 03257 if ((traits == UndefinedPixelTrait) || 03258 (scale_traits == UndefinedPixelTrait)) 03259 continue; 03260 if ((traits & BlendPixelTrait) == 0) 03261 { 03262 SetPixelChannel(scale_image,channel,ClampToQuantum( 03263 scale_scanline[x*MaxPixelChannels+channel]),q); 03264 continue; 03265 } 03266 alpha=QuantumScale*scanline[x*GetPixelChannels(image)+ 03267 GetPixelChannelMapChannel(image,AlphaPixelChannel)]; 03268 gamma=1.0/(fabs((double) alpha) <= MagickEpsilon ? 1.0 : alpha); 03269 SetPixelChannel(scale_image,channel,ClampToQuantum(gamma* 03270 scale_scanline[x*MaxPixelChannels+channel]),q); 03271 } 03272 q+=GetPixelChannels(scale_image); 03273 } 03274 } 03275 if (SyncCacheViewAuthenticPixels(scale_view,exception) == MagickFalse) 03276 break; 03277 proceed=SetImageProgress(image,ScaleImageTag,(MagickOffsetType) y, 03278 image->rows); 03279 if (proceed == MagickFalse) 03280 break; 03281 } 03282 scale_view=DestroyCacheView(scale_view); 03283 image_view=DestroyCacheView(image_view); 03284 /* 03285 Free allocated memory. 03286 */ 03287 y_vector=(MagickRealType *) RelinquishMagickMemory(y_vector); 03288 scale_scanline=(MagickRealType *) RelinquishMagickMemory(scale_scanline); 03289 if (scale_image->rows != image->rows) 03290 scanline=(MagickRealType *) RelinquishMagickMemory(scanline); 03291 x_vector=(MagickRealType *) RelinquishMagickMemory(x_vector); 03292 scale_image->type=image->type; 03293 return(scale_image); 03294 } 03295 03296 /* 03297 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03298 % % 03299 % % 03300 % % 03301 % T h u m b n a i l I m a g e % 03302 % % 03303 % % 03304 % % 03305 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 03306 % 03307 % ThumbnailImage() changes the size of an image to the given dimensions and 03308 % removes any associated profiles. The goal is to produce small low cost 03309 % thumbnail images suited for display on the Web. 03310 % 03311 % The format of the ThumbnailImage method is: 03312 % 03313 % Image *ThumbnailImage(const Image *image,const size_t columns, 03314 % const size_t rows,ExceptionInfo *exception) 03315 % 03316 % A description of each parameter follows: 03317 % 03318 % o image: the image. 03319 % 03320 % o columns: the number of columns in the scaled image. 03321 % 03322 % o rows: the number of rows in the scaled image. 03323 % 03324 % o exception: return any errors or warnings in this structure. 03325 % 03326 */ 03327 MagickExport Image *ThumbnailImage(const Image *image,const size_t columns, 03328 const size_t rows,ExceptionInfo *exception) 03329 { 03330 #define SampleFactor 5 03331 03332 char 03333 value[MaxTextExtent]; 03334 03335 const char 03336 *name; 03337 03338 Image 03339 *thumbnail_image; 03340 03341 MagickRealType 03342 x_factor, 03343 y_factor; 03344 03345 size_t 03346 version; 03347 03348 struct stat 03349 attributes; 03350 03351 assert(image != (Image *) NULL); 03352 assert(image->signature == MagickSignature); 03353 if (image->debug != MagickFalse) 03354 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 03355 assert(exception != (ExceptionInfo *) NULL); 03356 assert(exception->signature == MagickSignature); 03357 x_factor=(MagickRealType) columns/(MagickRealType) image->columns; 03358 y_factor=(MagickRealType) rows/(MagickRealType) image->rows; 03359 if ((x_factor*y_factor) > 0.1) 03360 thumbnail_image=ResizeImage(image,columns,rows,image->filter,image->blur, 03361 exception); 03362 else 03363 if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128)) 03364 thumbnail_image=ResizeImage(image,columns,rows,image->filter, 03365 image->blur,exception); 03366 else 03367 { 03368 Image 03369 *sample_image; 03370 03371 sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows, 03372 exception); 03373 if (sample_image == (Image *) NULL) 03374 return((Image *) NULL); 03375 thumbnail_image=ResizeImage(sample_image,columns,rows,image->filter, 03376 image->blur,exception); 03377 sample_image=DestroyImage(sample_image); 03378 } 03379 if (thumbnail_image == (Image *) NULL) 03380 return(thumbnail_image); 03381 (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page); 03382 if (thumbnail_image->matte == MagickFalse) 03383 (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel,exception); 03384 thumbnail_image->depth=8; 03385 thumbnail_image->interlace=NoInterlace; 03386 /* 03387 Strip all profiles except color profiles. 03388 */ 03389 ResetImageProfileIterator(thumbnail_image); 03390 for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; ) 03391 { 03392 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0)) 03393 { 03394 (void) DeleteImageProfile(thumbnail_image,name); 03395 ResetImageProfileIterator(thumbnail_image); 03396 } 03397 name=GetNextImageProfile(thumbnail_image); 03398 } 03399 (void) DeleteImageProperty(thumbnail_image,"comment"); 03400 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent); 03401 if (strstr(image->magick_filename,"//") == (char *) NULL) 03402 (void) FormatLocaleString(value,MaxTextExtent,"file://%s", 03403 image->magick_filename); 03404 (void) SetImageProperty(thumbnail_image,"Thumb::URI",value,exception); 03405 (void) CopyMagickString(value,image->magick_filename,MaxTextExtent); 03406 if (GetPathAttributes(image->filename,&attributes) != MagickFalse) 03407 { 03408 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 03409 attributes.st_mtime); 03410 (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value,exception); 03411 } 03412 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 03413 attributes.st_mtime); 03414 (void) FormatMagickSize(GetBlobSize(image),MagickFalse,value); 03415 (void) ConcatenateMagickString(value,"B",MaxTextExtent); 03416 (void) SetImageProperty(thumbnail_image,"Thumb::Size",value,exception); 03417 (void) FormatLocaleString(value,MaxTextExtent,"image/%s",image->magick); 03418 LocaleLower(value); 03419 (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value,exception); 03420 (void) SetImageProperty(thumbnail_image,"software",GetMagickVersion(&version), 03421 exception); 03422 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 03423 image->magick_columns); 03424 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value, 03425 exception); 03426 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 03427 image->magick_rows); 03428 (void) SetImageProperty(thumbnail_image,"Thumb::Image::Height",value, 03429 exception); 03430 (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double) 03431 GetImageListLength(image)); 03432 (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value, 03433 exception); 03434 return(thumbnail_image); 03435 }