// d is the effective diameter in pixels; all weight factors outside d are assumed
// to be zero. The factor 5.0 is somewhat arbitrary; a value between 4.0 and 6.0
// is generally recommended.
UINT d = Int(5.0f * radius + 1.0f);
if (d > m_MaxDim) return false; // radius to great
d |= 1; // must be odd
m_Dim = d;
if (m_Dim == 0) return true; // radius 0 is acceptable; effectively no convolution
m_FilterVector = new int[m_Dim];
d /= 2;
REAL num = 2 * radius * radius;
REAL f = expf(d * d / num);
m_Denominator = Int(f);
m_FilterVector[d] = m_Denominator;
for (UINT i = 1; i <= d; i++)
{
int i2 = - (int)(i * i);
int v = Int(f * expf(i2 / num));
m_FilterVector[d - i] = v;
m_FilterVector[d + i] = v;
m_Denominator += 2 * v;
}
return true;
}
// This is the workhorse of QGaussFilter. It calculates the convolution either in
// horizontal direction, or in vertical direction (depending on bHorizontal).
Bitmap * QGaussFilter::ConvoluteDimension(Bitmap * pSrc, Rect * pRect, UINT flags,
bool bHorizontal, bool bCopy)
{
Rect rc(0, 0, pSrc->GetWidth(), pSrc->GetHeight());
if (pRect) rc.Intersect(* pRect);
if (m_Dim <= 1) return pSrc->Clone(rc, PixelFormat24bppRGB);
// m_Dim == 0 or 1: effectively no convolution, just return a copy
// LockBits on source
BitmapData dataSrc;
Status s = pSrc->LockBits(& rc, ImageLockModeRead, PixelFormat24bppRGB, & dataSrc);
if (s != Ok) return NULL;
UINT d = m_Dim / 2;
const int nPlanes = 3;
Bitmap * pDest = new Bitmap(rc.Width, rc.Height, PixelFormat24bppRGB);
if (! pDest)
{
pSrc->UnlockBits(& dataSrc);
return NULL;
}
UINT nLines; // number of lines (horizontal or vertical)
UINT nPixels; // number of pixels per line
UINT dPixelSrc; // pixel step in source
UINT dPixelDest; // pixel step in destination
UINT dLineSrc; // line step in source
UINT dLineDest; // line step in destination
for (UINT x = xBegin; x <= pxl + d; x++)
{
sum += *pFactors++ * *pPixelSrc;
pPixelSrc += dPixelSrc;
}
sum /= m_Denominator;
* pPixelDest = (BYTE) sum;
pPixelDest += dPixelDest;
}
for (pxl = nPixels - d; pxl < nPixels; pxl++)
// loop through pixels in right/bottom margin
{
int * pFactors = m_FilterVector;
int denom = 0;
int sum = 0;
for (UINT x = xBegin; x < nPixels; x++)
{
denom += *pFactors;
sum += *pFactors++ * *pPixelSrc;
pPixelSrc += dPixelSrc;
}
if (denom) sum /= denom;
* pPixelDest = (BYTE) sum;
pPixelDest += dPixelDest;
}
if (m_bStop) break;
pLineSrc += dLineSrc;
pLineDest += dLineDest;
} // next line
if (m_bStop) break;
}
else if (bCopy) // no convolution, just copy
{
for (UINT line = 0; line < nLines; line++) // loop through lines
{
BYTE * pPixelSrc = pLineSrc;
BYTE * pPixelDest = pLineDest;
// Although undocumented, it seems perfectly legal to draw a bitmap on itself.
Graphics g(pBitmap);
g.DrawImage(pBitmap,
rc,
0, 0, rc.Width, rc.Height,
UnitPixel,
& attr);
}
// QGaussFilter.cpp
//
// MFC class to apply Unsharp Mask or Blur to a GDI+ Bitmap.
// Use at your own risk. Comments welcome.
//
// Version 1.1, 02/02/2004:
// Bug resolved in handling of small bitmaps. Thanks to Maik Wiege.
//
// Version 1.0 (c) 2003, Sjaak Priester, Amsterdam.
// mailto:sjaak@sjaakpriester.nl
// Stop calculation of Unsharp Mask or Blur in seperate thread
void QGaussFilter::Stop(void)
{
if (m_pThread)
{
m_bStop = true;
::WaitForSingleObject(m_pThread->m_hThread, INFINITE);
}
}
// Calculate and return Unsharp Mask bitmap
Bitmap * QGaussFilter::GetUnsharpMask(Bitmap * pSrc, REAL radius, REAL depth, Rect * pRect, UINT flags)
{
// Start with blur
Bitmap * pResult = GetBlur(pSrc, radius, pRect, flags, false);
if (pResult)
{
// Subtract blurred bitmap from original to get Unsharp Mask
Rect rcSrc(0, 0, pSrc->GetWidth(), pSrc->GetHeight());
if (pRect) rcSrc.Intersect(* pRect);
BitmapData dataSrc;
Status s = pSrc->LockBits(& rcSrc, ImageLockModeRead,
PixelFormat24bppRGB, & dataSrc);
if (s != Ok)
{
delete pResult;
return NULL;
}
Rect rcResult(0, 0, pResult->GetWidth(), pResult->GetHeight());
BitmapData dataResult;
s = pResult->LockBits(& rcResult, ImageLockModeRead | ImageLockModeWrite,
PixelFormat24bppRGB, & dataResult);
if (s != Ok)
{
pSrc->UnlockBits(& dataSrc);
delete pResult;
return NULL;
}
const int nPlanes = 3;
// On modern systems, the difference is not big, but real math is still somewhat
// slower than integer math. But if this ever changes, you may define REAL_MATH.
#ifdef REAL_MATH
REAL depthPlus = depth + 1.0f;
#else
int denom = 10000; // use an arbitrary denominator, not too small
int dpt = Int((REAL) denom * depth);
int dptplus = dpt + denom;
#endif
if (bThisPlane)
{
for (UINT line = 0; line < dataResult.Height; line++) // loop through lines
{
BYTE * pPixelSrc = pLineSrc;
BYTE * pPixelResult = pLineResult;
for (UINT pxl = 0; pxl < dataResult.Width; pxl++) // loop through pixels
{
#ifdef REAL_MATH
REAL v = depthPlus * *pPixelSrc - depth * *pPixelResult;
if (v > 255.0f) v = 255.0f;
if (v < 0.0f) v = 0.0f;
#else
int v = dptplus * *pPixelSrc - dpt * *pPixelResult;
v /= denom;
// Clipping is very essential here. for large values of depth
// (> 5.0f) more than half of the pixel values are clipped.
if (v > 255) v = 255;
if (v < 0) v = 0;
#endif
* pPixelResult = (BYTE) v;
pPixelSrc += nPlanes;
pPixelResult += nPlanes;
}
if (m_bStop) break;
pLineSrc += dataSrc.Stride;
pLineResult += dataResult.Stride;
}
}
else // no subtraction, just copy
{
for (UINT line = 0; line < dataResult.Height; line++) // loop through lines
{
BYTE * pPixelSrc = pLineSrc;
BYTE * pPixelResult = pLineResult;