65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Windows GDI 实现发光和阴影效果

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (25投票s)

2007 年 10 月 28 日

CPOL

3分钟阅读

viewsIcon

102562

downloadIcon

4896

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

Screenshot - Original2.jpg

Screenshot - Shadow2.jpg

Screenshot - Glowing2.jpg

引言

本文介绍了一种使用普通 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 日:初步发布
© . All rights reserved.