563 lines
16 KiB
C++
563 lines
16 KiB
C++
/**********************************************************************
|
|
|
|
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<class FilterF> 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<class FilterF> 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<SInt16> 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
|
|
|