使用 Windows GDI 实现发光和阴影效果
一篇关于使用纯 Windows GDI 创建发光和阴影效果的文章

引言
本文介绍了一种使用普通 Windows GDI 渲染柔和阴影和形状发光的简单但有效的方法。 这里的目标是向读者展示,不仅 GDI+(来自 .NET)和不同的商业第三方图形库能够完成这种类型的工作,而且还可以通过对选定的图像应用简单的图像处理技术来完成。 我在 Code Project 上找到了一些关于这个主题的资源,但其中大多数是使用新的 Microsoft GDI+ 图形库完成的,该库与传统的 Windows GDI API 相去甚远。
背景
渲染发光和柔和阴影可能看起来很难,但实际上并非如此。 我们可以从原始位图开始,构建单独的层(新的位图),例如阴影或发光。 关键是原始数据(原始位图)被绘制了两次,并且应用了一种简单的模糊技术作为中间操作。
阴影 - 它们来自哪里?
基本上,来自某个光源。 它们是物体在不同表面上的投影。 原始位图被复制(不包括透明像素)到新的临时位图中。 通常使用一些水平和垂直偏移来模拟阴影距离。 然后对这个临时位图进行低通滤波(通常应用模糊内核)并复制到最终目标位图(不包括透明像素)。 然后原始图像再次复制到目标位图(这次不包括透明像素)。 以下是步骤
- 将原始位图复制到一个新的、相同大小的位图,但带有小的偏移量(阴影偏移),并将所有非透明像素设为相同的颜色(阴影颜色)
- 对阴影位图应用模糊
- 将这个新的位图渲染到目标位图
- 将原始位图渲染到目标位图(跳过所有透明像素)
此方法通过 CreateShadow(COLORREF transparentColor, COLORREF shadowColor)
函数进行解释,如下所示
void CreateShadow(COLORREF transparentColor, COLORREF shadowColor)
{
int i,j, k, l;
RECT rect = {0, 0, 300, 200};
// Create temporary DC and bitmap
HDC hDC = ::GetDC(NULL);
HDC hTempDC = ::CreateCompatibleDC(hDC);
HBITMAP hTempBitmap = CreateCompatibleBitmap(hDC, 300, 200);
HBITMAP hOldTempBitmap = (HBITMAP)::SelectObject(hTempDC, hTempBitmap);
HDC hTempDC2 = ::CreateCompatibleDC(hDC);
HBITMAP hTempBitmap2 = CreateCompatibleBitmap(hDC, 300, 200);
HBITMAP hOldTempBitmap2 = (HBITMAP)::SelectObject(hTempDC2, hTempBitmap2);
HDC hTempDC3 = ::CreateCompatibleDC(hDC);
HBITMAP hTempBitmap3 = CreateCompatibleBitmap(hDC, 300, 200);
HBITMAP hOldTempBitmap3 = (HBITMAP)::SelectObject(hTempDC3, hTempBitmap3);
::ReleaseDC(NULL, hDC);
// Clear background
HBRUSH hBgBrush = ::CreateSolidBrush(transparentColor);
::FillRect(hTempDC, &rect, hBgBrush);
::FillRect(hTempDC2, &rect, hBgBrush);
::BitBlt(hTempDC3, 0, 0, 300, 200, m_hBgDC, 0, 0, SRCCOPY);
::DeleteObject(hBgBrush);
// Create drawing on shadow DC
CreateDrawing(hTempDC2);
// Draw memory DC on temporary DC
int shadowOffset = 5;
::TransparentBlt(hTempDC, shadowOffset, shadowOffset, 300, 200,
hTempDC2, 0, 0, 300, 200, transparentColor);
// Get original bitmap
BITMAP bmpOrig;
GetObject(m_hMemBitmap, sizeof(BITMAP), &bmpOrig);
int sizeOrig = bmpOrig.bmWidthBytes * bmpOrig.bmHeight;
BYTE* pDataOrig = new BYTE[sizeOrig];
GetBitmapBits(m_hMemBitmap, sizeOrig, pDataOrig);
int bppOrig = bmpOrig.bmBitsPixel >> 3;
// Get source bitmap
BITMAP bmpSrc;
GetObject(hTempBitmap, sizeof(BITMAP), &bmpSrc);
int sizeSrc = bmpSrc.bmWidthBytes * bmpSrc.bmHeight;
BYTE* pDataSrc = new BYTE[sizeSrc];
GetBitmapBits(hTempBitmap, sizeSrc, pDataSrc);
int bppSrc = bmpSrc.bmBitsPixel >> 3;
// Get source2 bitmap
BITMAP bmpSrc2;
GetObject(hTempBitmap2, sizeof(BITMAP), &bmpSrc2);
int sizeSrc2 = bmpSrc2.bmWidthBytes * bmpSrc2.bmHeight;
BYTE* pDataSrc2 = new BYTE[sizeSrc2];
GetBitmapBits(hTempBitmap2, sizeSrc2, pDataSrc2);
int bppSrc2 = bmpSrc2.bmBitsPixel >> 3;
// Get source3 bitmap
BITMAP bmpSrc3;
GetObject(hTempBitmap3, sizeof(BITMAP), &bmpSrc3);
int sizeSrc3 = bmpSrc3.bmWidthBytes * bmpSrc3.bmHeight;
BYTE* pDataSrc3 = new BYTE[sizeSrc3];
GetBitmapBits(hTempBitmap3, sizeSrc3, pDataSrc3);
int bppSrc3 = bmpSrc3.bmBitsPixel >> 3;
// Get destination bitmap
BITMAP bmpDst;
GetObject(m_hShadowBitmap, sizeof(BITMAP), &bmpDst);
int sizeDst = bmpDst.bmWidthBytes * bmpDst.bmHeight;
BYTE* pDataDst = new BYTE[sizeDst];
GetBitmapBits(m_hShadowBitmap, sizeDst, pDataDst);
int bppDst = bmpDst.bmBitsPixel >> 3;
// Get transparent color
BYTE redTransparent = GetRValue(transparentColor);
BYTE greenTransparent = GetGValue(transparentColor);
BYTE blueTransparent = GetBValue(transparentColor);
// Get shadow color
BYTE redShadow = GetRValue(shadowColor);
BYTE greenShadow = GetGValue(shadowColor);
BYTE blueShadow = GetBValue(shadowColor);
// Copy source bitmap to destination bitmap using transparent color
int verticalOffset = 0;
int horizontalOffset;
int totalOffset;
BYTE red, green, blue;
for (i=0; i<bmpSrc.bmHeight; i++)
{
horizontalOffset = 0;
for (j=0; j<bmpSrc.bmWidth; j++)
{
// Calculate total offset
totalOffset = verticalOffset + horizontalOffset;
// Get source pixel
blue = pDataSrc[totalOffset];
green = pDataSrc[totalOffset+1];
red = pDataSrc[totalOffset+2];
// Check for transparent color
if ((red != redTransparent) || (green != greenTransparent) ||
(blue != blueTransparent))
{
// Set destination pixel
pDataSrc3[totalOffset] = blueShadow;
pDataSrc3[totalOffset+1] = greenShadow;
pDataSrc3[totalOffset+2] = redShadow;
}
// Increment horizontal offset
horizontalOffset += bppSrc;
}
// Increment vertical offset
verticalOffset += bmpSrc.bmWidthBytes;
}
// Create temporary bitmap
BYTE* pDataTemp = new BYTE[sizeDst];
memcpy(pDataTemp, pDataSrc3, sizeDst);
BYTE* pDataTemp2 = new BYTE[sizeDst];
memcpy(pDataTemp2, pDataSrc, sizeDst);
// Apply blur effect
int filterSize = 5;
int filterHalfSize = filterSize >> 1;
int filterX, filterY, filterOffset;
int resultRed, resultGreen, resultBlue;
int resultRed2, resultGreen2, resultBlue2;
verticalOffset = 0;
for (i=filterHalfSize; i<bmpDst.bmHeight-filterHalfSize; i++)
{
horizontalOffset = 0;
for (j=filterHalfSize; j<bmpDst.bmWidth-filterHalfSize; j++)
{
// Calculate total offset
totalOffset = verticalOffset + horizontalOffset;
if ((i>=filterHalfSize) && (i<bmpDst.bmHeight-filterHalfSize) &&
(j>=filterHalfSize) && (j<bmpDst.bmWidth-filterHalfSize))
{
// Clear result pixel
resultRed = resultGreen = resultBlue = 0;
resultRed2 = resultGreen2 = resultBlue2 = 0;
// Set vertical filter offset
filterY = verticalOffset;
// Apply filter
for (k=-filterHalfSize; k<=filterHalfSize; k++)
{
// Set horizontal filter offset
filterX = horizontalOffset;
for (l=-filterHalfSize; l<=filterHalfSize; l++)
{
// Calculate total filter offset
filterOffset = filterY + filterX;
// Calculate result pixel
resultBlue += pDataSrc3[filterOffset];
resultGreen += pDataSrc3[filterOffset+1];
resultRed += pDataSrc3[filterOffset+2];
resultBlue2 += pDataSrc[filterOffset];
resultGreen2 += pDataSrc[filterOffset+1];
resultRed2 += pDataSrc[filterOffset+2];
// Increment horizontal filter offset
filterX += bppDst;
}
// Increment vertical filter offset
filterY += bmpDst.bmWidthBytes;
}
// Set destination pixel
pDataTemp[totalOffset] = resultBlue / (filterSize*filterSize);
pDataTemp[totalOffset+1] = resultGreen / (filterSize*filterSize);
pDataTemp[totalOffset+2] = resultRed / (filterSize*filterSize);
pDataTemp2[totalOffset] = resultBlue2 / (filterSize*filterSize);
pDataTemp2[totalOffset+1] = resultGreen2 / (filterSize*filterSize);
pDataTemp2[totalOffset+2] = resultRed2 / (filterSize*filterSize);
}
// Increment horizontal offset
horizontalOffset += bppDst;
}
// Increment vertical offset
verticalOffset += bmpDst.bmWidthBytes;
}
// Copy shadow bitmap to destination bitmap
verticalOffset = 0;
double alpha=1.0, alpha_koef;
double shadow_default_intensity = (redShadow + greenShadow + blueShadow) / 3;
double shadow_intenzity, shadow_koef;
for (i=0; i<bmpDst.bmHeight; i++)
{
horizontalOffset = 0;
for (j=0; j<bmpDst.bmWidth; j++)
{
// Calculate total offset
totalOffset = verticalOffset + horizontalOffset;
// Check for transparent color
if ((pDataTemp2[totalOffset+2] != redTransparent) ||
(pDataTemp2[totalOffset+1] != greenTransparent) ||
(pDataTemp2[totalOffset] != blueTransparent))
{
// Calculate shadow transparency
shadow_intenzity = (pDataTemp2[totalOffset] +
pDataTemp2[totalOffset+1] + pDataTemp2[totalOffset+2]) / 3;
shadow_koef =
(shadow_intenzity - 255) / (shadow_default_intensity - 255);
if (fabs(shadow_koef) > 0.5)
shadow_koef = 0.5 * (fabs(shadow_koef) / shadow_koef);
if (shadow_default_intensity == 255)
alpha_koef = 0.0;
else
alpha_koef = alpha * shadow_koef;
// Calculate destination pixel
blue = (BYTE)(alpha_koef*pDataTemp[totalOffset] +
(1.0-alpha_koef)*pDataDst[totalOffset]);
green = (BYTE)(alpha_koef*pDataTemp[totalOffset+1] +
(1.0-alpha_koef)*pDataDst[totalOffset+1]);
red = (BYTE)(alpha_koef*pDataTemp[totalOffset+2] +
(1.0-alpha_koef)*pDataDst[totalOffset+2]);
// Set destination pixel
pDataSrc3[totalOffset] = blue;
pDataSrc3[totalOffset+1] = green;
pDataSrc3[totalOffset+2] = red;
}
else
{
// Set destination pixel
pDataSrc3[totalOffset] = pDataDst[totalOffset];
pDataSrc3[totalOffset+1] = pDataDst[totalOffset+1];
pDataSrc3[totalOffset+2] = pDataDst[totalOffset+2];
}
// Increment horizontal offset
horizontalOffset += bppDst;
}
// Increment vertical offset
verticalOffset += bmpDst.bmWidthBytes;
}
// Set destination bitmap
::SetBitmapBits(m_hShadowBitmap, sizeDst, pDataSrc3);
// Destroy buffers
delete pDataOrig;
delete pDataTemp;
delete pDataTemp2;
delete pDataSrc;
delete pDataSrc2;
delete pDataSrc3;
delete pDataDst;
// Destroy temporary DC and bitmap
if (hTempDC)
{
::SelectObject(hTempDC, hOldTempBitmap);
::DeleteDC(hTempDC);
::DeleteObject(hTempBitmap);
}
if (hTempDC2)
{
::SelectObject(hTempDC2, hOldTempBitmap2);
::DeleteDC(hTempDC2);
::DeleteObject(hTempBitmap2);
}
if (hTempDC3)
{
::SelectObject(hTempDC3, hOldTempBitmap3);
::DeleteDC(hTempDC3);
::DeleteObject(hTempBitmap3);
}
// Create drawing on shadow DC
CreateDrawing(m_hShadowDC);
}
发光 - 我在哪里?
对于物体发光效果,也有类似的情况。 看起来物体周围有一个闪耀的光环。 步骤几乎相同
- 将原始位图复制到一个新的、相同大小的位图,但在所有四个方向上都有小的偏移量(发光方向),并将所有非透明像素设为相同的颜色(发光颜色)
- 对发光位图应用模糊
- 将这个新的位图渲染到目标位图
- 将原始位图渲染到目标位图(跳过所有透明像素)
此方法通过 CreateGlow(COLORREF transparentColor, COLORREF glowColor)
函数进行解释,如下所示
void CreateGlow(COLORREF transparentColor, COLORREF glowColor)
{
int i,j, k, l;
RECT rect = {0, 0, 300, 200};
// Create temporary DC and bitmap
HDC hDC = ::GetDC(NULL);
HDC hTempDC = ::CreateCompatibleDC(hDC);
HBITMAP hTempBitmap = CreateCompatibleBitmap(hDC, 300, 200);
HBITMAP hOldTempBitmap = (HBITMAP)::SelectObject(hTempDC, hTempBitmap);
HDC hTempDC2 = ::CreateCompatibleDC(hDC);
HBITMAP hTempBitmap2 = CreateCompatibleBitmap(hDC, 300, 200);
HBITMAP hOldTempBitmap2 = (HBITMAP)::SelectObject(hTempDC2, hTempBitmap2);
HDC hTempDC3 = ::CreateCompatibleDC(hDC);
HBITMAP hTempBitmap3 = CreateCompatibleBitmap(hDC, 300, 200);
HBITMAP hOldTempBitmap3 = (HBITMAP)::SelectObject(hTempDC3, hTempBitmap3);
::ReleaseDC(NULL, hDC);
// Clear background
HBRUSH hBgBrush = ::CreateSolidBrush(transparentColor);
::FillRect(hTempDC, &rect, hBgBrush);
::FillRect(hTempDC2, &rect, hBgBrush);
::BitBlt(hTempDC3, 0, 0, 300, 200, m_hBgDC, 0, 0, SRCCOPY);
::DeleteObject(hBgBrush);
// Create drawing on glow DC
CreateDrawing(hTempDC2);
// Draw memory DC on temporary DC
int glowingOffset = 4;
::TransparentBlt(hTempDC, -glowingOffset, 0, 300, 200, hTempDC2,
0, 0, 300, 200, transparentColor);
::TransparentBlt(hTempDC, glowingOffset, 0, 300, 200, hTempDC2,
0, 0, 300, 200, transparentColor);
::TransparentBlt(hTempDC, 0, -glowingOffset, 300, 200, hTempDC2,
0, 0, 300, 200, transparentColor);
::TransparentBlt(hTempDC, 0, glowingOffset, 300, 200, hTempDC2,
0, 0, 300, 200, transparentColor);
// Get original bitmap
BITMAP bmpOrig;
GetObject(m_hMemBitmap, sizeof(BITMAP), &bmpOrig);
int sizeOrig = bmpOrig.bmWidthBytes * bmpOrig.bmHeight;
BYTE* pDataOrig = new BYTE[sizeOrig];
GetBitmapBits(m_hMemBitmap, sizeOrig, pDataOrig);
int bppOrig = bmpOrig.bmBitsPixel >> 3;
// Get source bitmap
BITMAP bmpSrc;
GetObject(m_hMemBitmap, sizeof(BITMAP), &bmpSrc);
int sizeSrc = bmpSrc.bmWidthBytes * bmpSrc.bmHeight;
BYTE* pDataSrc = new BYTE[sizeSrc];
GetBitmapBits(hTempBitmap, sizeSrc, pDataSrc);
int bppSrc = bmpSrc.bmBitsPixel >> 3;
// Get source2 bitmap
BITMAP bmpSrc2;
GetObject(hTempBitmap2, sizeof(BITMAP), &bmpSrc2);
int sizeSrc2 = bmpSrc2.bmWidthBytes * bmpSrc2.bmHeight;
BYTE* pDataSrc2 = new BYTE[sizeSrc2];
GetBitmapBits(hTempBitmap2, sizeSrc2, pDataSrc2);
int bppSrc2 = bmpSrc2.bmBitsPixel >> 3;
// Get source3 bitmap
BITMAP bmpSrc3;
GetObject(hTempBitmap3, sizeof(BITMAP), &bmpSrc3);
int sizeSrc3 = bmpSrc3.bmWidthBytes * bmpSrc3.bmHeight;
BYTE* pDataSrc3 = new BYTE[sizeSrc3];
GetBitmapBits(hTempBitmap3, sizeSrc3, pDataSrc3);
int bppSrc3 = bmpSrc3.bmBitsPixel >> 3;
// Get destination bitmap
BITMAP bmpDst;
GetObject(m_hGlowBitmap, sizeof(BITMAP), &bmpDst);
int sizeDst = bmpDst.bmWidthBytes * bmpDst.bmHeight;
BYTE* pDataDst = new BYTE[sizeDst];
GetBitmapBits(m_hGlowBitmap, sizeDst, pDataDst);
int bppDst = bmpDst.bmBitsPixel >> 3;
// Get transparent color
BYTE redTransparent = GetRValue(transparentColor);
BYTE greenTransparent = GetGValue(transparentColor);
BYTE blueTransparent = GetBValue(transparentColor);
// Get glow color
BYTE redGlow = GetRValue(glowColor);
BYTE greenGlow = GetGValue(glowColor);
BYTE blueGlow = GetBValue(glowColor);
// Copy source bitmap to destination bitmap using transparent color
int verticalOffset = 0;
int horizontalOffset;
int totalOffset;
BYTE red, green, blue;
for (i=0; i<bmpSrc.bmHeight; i++)
{
horizontalOffset = 0;
for (j=0; j<bmpSrc.bmWidth; j++)
{
// Calculate total offset
totalOffset = verticalOffset + horizontalOffset;
// Get source pixel
blue = pDataSrc[totalOffset];
green = pDataSrc[totalOffset+1];
red = pDataSrc[totalOffset+2];
// Check for transparent color
if ((red != redTransparent) || (green != greenTransparent) ||
(blue != blueTransparent))
{
// Set destination pixel
pDataSrc3[totalOffset] = blueGlow;
pDataSrc3[totalOffset+1] = greenGlow;
pDataSrc3[totalOffset+2] = redGlow;
}
// Increment horizontal offset
horizontalOffset += bppSrc;
}
// Increment vertical offset
verticalOffset += bmpSrc.bmWidthBytes;
}
// Create temporary bitmap
BYTE* pDataTemp = new BYTE[sizeDst];
memcpy(pDataTemp, pDataSrc3, sizeDst);
BYTE* pDataTemp2 = new BYTE[sizeDst];
memcpy(pDataTemp2, pDataSrc, sizeDst);
// Apply blur effect
int filterSize = 11;
int filterHalfSize = filterSize >> 1;
int filterHorizontalOffset = filterHalfSize * bppDst;
int filterVerticalOffset = filterHalfSize * bmpSrc.bmWidthBytes;
int filterTotalOffset = filterVerticalOffset + filterHorizontalOffset;
int filterX, filterY, filterOffset;
int resultRed, resultGreen, resultBlue;
int resultRed2, resultGreen2, resultBlue2;
verticalOffset = 0;
for (i=filterHalfSize; i<bmpDst.bmHeight-filterHalfSize; i++)
{
horizontalOffset = 0;
for (j=filterHalfSize; j<bmpDst.bmWidth-filterHalfSize; j++)
{
// Calculate total offset
totalOffset = verticalOffset + horizontalOffset;
if ((i>=filterHalfSize) && (i<bmpDst.bmHeight-filterHalfSize) &&
(j>=filterHalfSize) && (j<bmpDst.bmWidth-filterHalfSize))
{
// Clear result pixel
resultRed = resultGreen = resultBlue = 0;
resultRed2 = resultGreen2 = resultBlue2 = 0;
// Set vertical filter offset
filterY = verticalOffset;
// Apply filter
for (k=-filterHalfSize; k<=filterHalfSize; k++)
{
// Set horizontal filter offset
filterX = horizontalOffset;
for (l=-filterHalfSize; l<=filterHalfSize; l++)
{
// Calculate total filter offset
filterOffset = filterY + filterX;
// Calculate result pixel
resultBlue += pDataSrc3[filterOffset];
resultGreen += pDataSrc3[filterOffset+1];
resultRed += pDataSrc3[filterOffset+2];
resultBlue2 += pDataSrc[filterOffset];
resultGreen2 += pDataSrc[filterOffset+1];
resultRed2 += pDataSrc[filterOffset+2];
// Increment horizontal filter offset
filterX += bppDst;
}
// Increment vertical filter offset
filterY += bmpDst.bmWidthBytes;
}
// Set destination pixel
pDataTemp[totalOffset+filterTotalOffset] =
resultBlue / (filterSize*filterSize);
pDataTemp[totalOffset+1+filterTotalOffset] =
resultGreen / (filterSize*filterSize);
pDataTemp[totalOffset+2+filterTotalOffset] =
resultRed / (filterSize*filterSize);
pDataTemp2[totalOffset+filterTotalOffset] =
resultBlue2 / (filterSize*filterSize);
pDataTemp2[totalOffset+1+filterTotalOffset] =
resultGreen2 / (filterSize*filterSize);
pDataTemp2[totalOffset+2+filterTotalOffset] =
resultRed2 / (filterSize*filterSize);
}
// Increment horizontal offset
horizontalOffset += bppDst;
}
// Increment vertical offset
verticalOffset += bmpDst.bmWidthBytes;
}
// Copy glow bitmap to destination bitmap
verticalOffset = 0;
double alpha=1.0, alpha_koef;
double glow_default_intensity = (redGlow + greenGlow + blueGlow) / 3;
double glow_intenzity, glow_koef;
for (i=0; i<bmpDst.bmHeight; i++)
{
horizontalOffset = 0;
for (j=0; j<bmpDst.bmWidth; j++)
{
// Calculate total offset
totalOffset = verticalOffset + horizontalOffset;
// Check for transparent color
if ((pDataTemp2[totalOffset+2] !=
redTransparent) || (pDataTemp2[totalOffset+1]
!= greenTransparent) || (pDataTemp2[totalOffset] != blueTransparent))
{
// Calculate glow transparency
glow_intenzity = (pDataTemp2[totalOffset] + pDataTemp2[totalOffset+1] +
pDataTemp2[totalOffset+2]) / 3;
glow_koef = (glow_intenzity - 255) / (glow_default_intensity - 255);
if (fabs(glow_koef) > 0.5)
glow_koef = 0.5 * (fabs(glow_koef) / glow_koef);
if (glow_default_intensity == 255)
alpha_koef = 0.0;
else
alpha_koef = alpha * glow_koef;
// Calculate destination pixel
blue = (BYTE)(alpha_koef*pDataTemp[totalOffset] +
(1.0-alpha_koef)*pDataDst[totalOffset]);
green = (BYTE)(alpha_koef*pDataTemp[totalOffset+1] +
(1.0-alpha_koef)*pDataDst[totalOffset+1]);
red = (BYTE)(alpha_koef*pDataTemp[totalOffset+2] +
(1.0-alpha_koef)*pDataDst[totalOffset+2]);
// Set destination pixel
pDataSrc3[totalOffset] = blue;
pDataSrc3[totalOffset+1] = green;
pDataSrc3[totalOffset+2] = red;
}
else
{
// Set destination pixel
pDataSrc3[totalOffset] = pDataDst[totalOffset];
pDataSrc3[totalOffset+1] = pDataDst[totalOffset+1];
pDataSrc3[totalOffset+2] = pDataDst[totalOffset+2];
}
// Increment horizontal offset
horizontalOffset += bppDst;
}
// Increment vertical offset
verticalOffset += bmpDst.bmWidthBytes;
}
// Set destination bitmap
::SetBitmapBits(m_hGlowBitmap, sizeDst, pDataSrc3);
// Destroy buffers
delete pDataOrig;
delete pDataTemp;
delete pDataTemp2;
delete pDataSrc;
delete pDataSrc2;
delete pDataSrc3;
delete pDataDst;
// Destroy temporary DC and bitmap
if (hTempDC)
{
::SelectObject(hTempDC, hOldTempBitmap);
::DeleteDC(hTempDC);
::DeleteObject(hTempBitmap);
}
if (hTempDC2)
{
::SelectObject(hTempDC2, hOldTempBitmap2);
::DeleteDC(hTempDC2);
::DeleteObject(hTempBitmap2);
}
if (hTempDC3)
{
::SelectObject(hTempDC3, hOldTempBitmap3);
::DeleteDC(hTempDC3);
::DeleteObject(hTempBitmap3);
}
// Create drawing on glow DC
CreateDrawing(m_hGlowDC);
}
那么,区别在哪里呢?
这些算法几乎相同,除了一个关键点:当你创建阴影效果时,你只需要创建一个/两个偏移[量](水平和/或垂直),但当你创建发光效果时,需要四个或更多偏移量(在所有可能的方向上)。 应用模糊内核非常重要,因为它使阴影和发光在最终位图上看起来更逼真。
模糊内核
在本例中,模糊内核是下面这个(也称为低通滤波器)
{[1 1 1][1 1 1][1 1 1]} * 1/9
因此,将 9 个周围源像素的总和取平均值(除以像素数),结果代表目标像素的颜色。
关注点
我对本文中显示的简单图像处理技术非常感兴趣,通过使用基本的图形 API,如 Windows GDI。
历史
- 2007 年 10 月 28 日:初步发布