/********************************************************************** Filename : GResizeImage.h Content : Bitmap Image resizing with filtering Created : 2007 Authors : Maxim Shemanarev Copyright : (c) 2001-2006 Scaleform Corp. All Rights Reserved. Notes : Licensees may use this file in accordance with the valid Scaleform Commercial License Agreement provided with the software. This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE. ---------------------------------------------------------------------- The code of these classes was taken from the Anti-Grain Geometry Project and modified for the use by Scaleform. Permission to use without restrictions is hereby granted to Scaleform Corporation by the author of Anti-Grain Geometry Project. See http://antigtain.com for details. **********************************************************************/ #ifndef INC_GResizeImage_H #define INC_GResizeImage_H #include "GArrayUnsafe.h" #include "GMath.h" //------------------------------------------------------------------------ class GLinearInterpolator { public: //-------------------------------------------------------------------- GLinearInterpolator() {} GLinearInterpolator(int y1, int y2, int count) : Cnt(count), Lft((y2 - y1) / count), Rem((y2 - y1) % count), m_y(y1) { Mod = Rem; if(Mod <= 0) { Mod += count; Rem += count; Lft--; } Mod -= count; } //-------------------------------------------------------------------- void operator++() { Mod += Rem; m_y += Lft; if(Mod > 0) { Mod -= Cnt; m_y++; } } //-------------------------------------------------------------------- int y() const { return m_y; } private: int Cnt; int Lft; int Rem; int Mod; int m_y; }; //------------------------------------------------------------------------ enum GImageFilterConstants { GImgSubpixelShift = 8, GImgSubpixelShift2 = GImgSubpixelShift * 2, GImgSubpixelScale = 1 << GImgSubpixelShift, GImgSubpixelMask = GImgSubpixelScale - 1, GImgSubpixelOffset = GImgSubpixelScale / 2, GImgSubpixelInitial = GImgSubpixelScale * GImgSubpixelScale / 2, GImgFilterShift = 14, GImgFilterScale = 1 << GImgFilterShift, GImgFilterMask = GImgFilterScale - 1, GImgMaxFilterRadius = 8 }; //------------------------------------------------------------------------ class GImageFilterLut { public: template void Calculate(const FilterF& filter, bool normalization=true) { Float r = filter.GetRadius(); reallocLut(r); unsigned i; unsigned pivot = GetDiameter() << (GImgSubpixelShift - 1); for (i = 0; i < pivot; i++) { Float x = Float(i) / Float(GImgSubpixelScale); Float y = filter.GetWeight(x); WeightArray[pivot + i] = WeightArray[pivot - i] = SInt16(gfrnd(y * GImgFilterScale)); } unsigned end = (GetDiameter() << GImgSubpixelShift) - 1; WeightArray[0] = WeightArray[end]; if (normalization) Normalize(); } GImageFilterLut() : Radius(0), Diameter(0), Start(0) {} template GImageFilterLut(const FilterF& filter, bool normalization=true) { Calculate(filter, normalization); } Float GetRadius() const { return Radius; } unsigned GetDiameter() const { return Diameter; } int GetStart() const { return Start; } const SInt16* GetWeightArray() const { return &WeightArray[0]; } void Normalize(); private: void reallocLut(Float radius); GImageFilterLut(const GImageFilterLut&); const GImageFilterLut& operator = (const GImageFilterLut&); Float Radius; unsigned Diameter; int Start; GArrayUnsafe WeightArray; }; //------------------------------------------------------------------------ struct GImageFilterBilinear { Float GetRadius() const { return 1.0f; } Float GetWeight(Float x) const { return 1.0f - x; } }; //------------------------------------------------------------------------ struct GImageFilterHanning { Float GetRadius() const { return 1.0f; } Float GetWeight(Float x) const { return 0.5f + 0.5f * cosf((Float)GFC_MATH_PI * x); } }; //------------------------------------------------------------------------ struct GImageFilterHamming { Float GetRadius() const { return 1.0f; } Float GetWeight(Float x) const { return 0.54f + 0.46f * cosf((Float)GFC_MATH_PI * x); } }; //------------------------------------------------------------------------ struct GImageFilterHermite { Float GetRadius() const { return 1.0f; } Float GetWeight(Float x) const { return (2.0f * x - 3.0f) * x * x + 1.0f; } }; //------------------------------------------------------------------------ struct GImageFilterQuadric { Float GetRadius() const { return 1.5f; } Float GetWeight(Float x) const { Float t; if(x < 0.5f) return 0.75f - x * x; if(x < 1.5f) {t = x - 1.5f; return 0.5f * t * t;} return 0.0f; } }; //------------------------------------------------------------------------ // The piecewise cubic interpolation function can be controlled by two parameters // (B and C) as shown in the function below. The effects of changing these // parameters is generalized in the graph of B and C on the following page. This // graph is the result of a subjective test in which nine expert observers were // shown images reconstructed with random values of B and C. The observers were // asked to classify the appearance of the images into one of these categories: // blurring, ringing, anisotropy, and satisfactory. The graph shows the results of // the test over the range of 0.0 to 1.0. Of course B and C may be outside this // range, the effects will be more noticeable. // 1.0 +-------------+--------------- // | | | // | | | // B 0.8 + _~ \ II | Regions: // | I / \ | ------- // P | < ~-_ _ // a 0.6 + > \ _-~ | I - Blurring // r | __-~ |~-_-~ | II - Anisotropy // a +__--~~ / | III - Ringing // m 0.4 + \ | | IV - Anisotropy // e | \ V / | V - Satisfactory // t | \ / III | // e 0.2 + ~\ | | // r | IV \ \ | // | ~~~\ | | // 0.0 +-----+-----+----++-----+----- // 0.0 0.2 0.4 0.6 0.8 1.0 // C Parameter // // // The default values for (B, C) are (1/3, 1/3) which is recommended by // Mitchell and Netravali. Other interesting values are: // // (1, 0) - Equivalent to the Cubic B-Spline, // (0, 0.5) - Equivalent to the Catmull-Rom Spline, // (0, C) - The family of Cardinal Cubic Splines, // (B, 0) - Duff's tensioned B-Splines ("Splines in Animation and Modeling"). //------------------------------------------------------------------------ struct GImageFilterBicubic { Float B, C; GImageFilterBicubic(Float b=0.3333f, Float c=0.3333f) : B(b), C(c) {} Float GetRadius() const { return 2.0f; } Float GetWeight(Float x) const { if (x < 1.0f) { return ((12.0f - 9.0f*B - 6.0f*C)*x*x*x + (-18.0f + 12.0f*B + 6.0f*C)*x*x + 6.0f - 2.0f*B) / 6.0f; } else if (x < 2.0f) { return ((-B - 6.0f*C)*x*x*x + (6.0f*B + 30.0f*C)*x*x + (-12.0f*B - 48.0f*C)*x + (8.0f*B + 24.0f*C)) / 6.0f; } return 0; } }; //------------------------------------------------------------------------ class GImageFilterKaiser { Float a; Float i0a; Float epsilon; public: GImageFilterKaiser(Float b = 6.33f) : a(b), epsilon(1e-6f) { i0a = 1.0f / bessel_i0(b); } Float GetRadius() const { return 1.0f; } Float GetWeight(Float x) const { return bessel_i0(a * sqrtf(1.0f - x * x)) * i0a; } private: Float bessel_i0(Float x) const { int i; Float sum, y, t; sum = 1.0f; y = x * x / 4.0f; t = y; for(i = 2; t > epsilon; i++) { sum += t; t *= (Float)y / (i * i); } return sum; } }; //------------------------------------------------------------------------ struct GImageFilterCatrom { Float GetRadius() const { return 2.0f; } Float GetWeight(Float x) const { if(x < 1.0f) return 0.5f * (2.0f + x * x * (-5.0f + x * 3.0f)); if(x < 2.0f) return 0.5f * (4.0f + x * (-8.0f + x * (5.0f - x))); return 0.0f; } }; //------------------------------------------------------------------------ class GImageFilterMitchell { Float p0, p2, p3; Float q0, q1, q2, q3; public: GImageFilterMitchell(Float b = 1.0f/3.0f, Float c = 1.0f/3.0f) : p0((6.0f - 2.0f * b) / 6.0f), p2((-18.0f + 12.0f * b + 6.0f * c) / 6.0f), p3((12.0f - 9.0f * b - 6.0f * c) / 6.0f), q0((8.0f * b + 24.0f * c) / 6.0f), q1((-12.0f * b - 48.0f * c) / 6.0f), q2((6.0f * b + 30.0f * c) / 6.0f), q3((-b - 6.0f * c) / 6.0f) {} Float GetRadius() const { return 2.0f; } Float GetWeight(Float x) const { if(x < 1.0f) return p0 + x * x * (p2 + x * p3); if(x < 2.0f) return q0 + x * (q1 + x * (q2 + x * q3)); return 0.0f; } }; //------------------------------------------------------------------------ struct GImageFilterSpline16 { Float GetRadius() const { return 2.0f; } Float GetWeight(Float x) const { if(x < 1.0f) { return ((x - 9.0f/5.0f ) * x - 1.0f/5.0f ) * x + 1.0f; } return ((-1.0f/3.0f * (x-1.0f) + 4.0f/5.0f) * (x-1.0f) - 7.0f/15.0f ) * (x-1.0f); } }; //------------------------------------------------------------------------ struct GImageFilterSpline36 { Float GetRadius() const { return 3.0f; } Float GetWeight(Float x) const { if(x < 1.0f) { return ((13.0f/11.0f * x - 453.0f/209.0f) * x - 3.0f/209.0f) * x + 1.0f; } if(x < 2.0f) { return ((-6.0f/11.0f * (x-1.0f) + 270.0f/209.0f) * (x-1.0f) - 156.0f / 209.0f) * (x-1.0f); } return ((1.0f/11.0f * (x-2.0f) - 45.0f/209.0f) * (x-2.0f) + 26.0f/209.0f) * (x-2.0f); } }; //------------------------------------------------------------------------ struct GImageFilterGaussian { Float GetRadius() const { return 2.0f; } Float GetWeight(Float x) const { return expf(-2.0f * x * x) * sqrtf(2.0f / (Float)GFC_MATH_PI); } }; //------------------------------------------------------------------------ // Function GBessel calculates Bessel function of first kind of order n // Arguments: // n - an integer (>=0), the order // x - value at which the Bessel function is required //-------------------- inline Float GBessel(Float x, int n) { if(n < 0) { return 0; } Float d = 1E-6f; Float b = 0; if(fabsf(x) <= d) { if(n != 0) return 0; return 1; } Float b1 = 0; // b1 is the value from the previous iteration // Set up a starting order for recurrence int m1 = (int)fabsf(x) + 6; if(fabsf(x) > 5) { m1 = (int)(fabsf(1.4f * x + 60 / x)); } int m2 = (int)(n + 2 + fabsf(x) / 4); if (m1 > m2) { m2 = m1; } // Apply recurrence down from current max order for(;;) { Float c3 = 0; Float c2 = 1E-30f; Float c4 = 0; int m8 = 1; if (m2 / 2 * 2 == m2) { m8 = -1; } int imax = m2 - 2; for (int i = 1; i <= imax; i++) { Float c6 = 2 * (m2 - i) * c2 / x - c3; c3 = c2; c2 = c6; if(m2 - i - 1 == n) b = c6; m8 = -1 * m8; if (m8 > 0) c4 = c4 + 2 * c6; } Float c6 = 2 * c2 / x - c3; if(n == 0) b = c6; c4 += c6; b /= c4; if(fabsf(b - b1) < d) break; b1 = b; m2 += 3; } return b; } //------------------------------------------------------------------------ struct GImageFilterBessel { Float GetRadius() const { return 3.2383f; } Float GetWeight(Float x) const { return (x == 0.0f) ? (Float)GFC_MATH_PI / 4.0f : GBessel((Float)GFC_MATH_PI * x, 1) / (2.0f * x); } }; //------------------------------------------------------------------------ class GImageFilterSinc { public: GImageFilterSinc(Float r=4.0f) : Radius(G_Clamp(r, 2.0f, Float(GImgMaxFilterRadius))) {} Float GetRadius() const { return Radius; } Float GetWeight(Float x) const { if(x == 0.0f) return 1.0f; x *= (Float)GFC_MATH_PI; return sinf(x) / x; } private: Float Radius; }; //------------------------------------------------------------------------ class GImageFilterLanczos { public: GImageFilterLanczos(Float r=4.0f) : Radius(G_Clamp(r, 2.0f, Float(GImgMaxFilterRadius))) {} Float GetRadius() const { return Radius; } Float GetWeight(Float x) const { if(x == 0.0f) return 1.0f; if(x > Radius) return 0.0f; x *= (Float)GFC_MATH_PI; Float xr = x / Radius; return (sinf(x) / x) * (sinf(xr) / xr); } private: Float Radius; }; //------------------------------------------------------------------------ class GImageFilterBlackman { public: GImageFilterBlackman(Float r=4.0f) : Radius(G_Clamp(r, 2.0f, Float(GImgMaxFilterRadius))) {} Float GetRadius() const { return Radius; } Float GetWeight(Float x) const { if(x == 0.0f) return 1.0f; if(x > Radius) return 0.0f; x *= (Float)GFC_MATH_PI; Float xr = x / Radius; return (sinf(x) / x) * (0.42f + 0.5f*cosf(xr) + 0.08f*cosf(2.0f*xr)); } private: Float Radius; }; // Operations for the image resizer enum GResizeImageType { GResizeRgbToRgb, // 24-bit RGB GResizeRgbaToRgba, // 32-bit RGBA GResizeRgbToRgba, // Add Alpha and form RGBA GResizeGray // 8-bit gray scale }; void GSTDCALL GResizeImageBox(UByte* pDst, int dstWidth, int dstHeight, int dstPitch, const UByte* pSrc, int srcWidth, int srcHeight, int srcPitch, GResizeImageType type); void GSTDCALL GResizeImageBilinear(UByte* pDst, int dstWidth, int dstHeight, int dstPitch, const UByte* pSrc, int srcWidth, int srcHeight, int srcPitch, GResizeImageType type); void GSTDCALL GResizeImage(UByte* pDst, int dstWidth, int dstHeight, int dstPitch, const UByte* pSrc, int srcWidth, int srcHeight, int srcPitch, GResizeImageType type, const GImageFilterLut& filter); #endif