Click here to Skip to main content
Click here to Skip to main content

Glow and Shadow Effects using Windows GDI

, 27 Oct 2007
Rate this:
Please Sign up or sign in to vote.
An article on creating glow and shadow effects using plain Windows GDI
Screenshot - Original2.jpg

Screenshot - Shadow2.jpg

Screenshot - Glowing2.jpg

Introduction

This article is about a simple but effective method of rendering soft-shadows and shape-glowing using plain Windows GDI. The goal here is to show the reader that not only GDI+ (from .NET) and different commercial 3rd-party graphical libraries are capable of doing this type of work, but it can also be done by means of applying simple image processing techniques on the selected image. I have found some resources here on The Code Project considering this topic but most of them are done using the new Microsoft GDI+ graphics library that is far away from the traditional Windows GDI API.

Background

Rendering glowing and soft-shadows probably looks like a hard thing to do, but it really isn't. We can start from the original bitmap and build the separate layers (new bitmaps) for, say shadow or glow. The point is that the original data (original bitmap) is drawn two times, and a simple blur technique is applied as an intermediate operation.

Shadows - Where Do They Come From?

Basically, from some source of light. They are the projection of an object on different surfaces. The original bitmap is copied (excluding the transparent pixels) to the new temporary bitmap. Usually some horizontal and vertical offsets are used to simulate shadow distance. This temporary bitmap is then low-pass filtered (usually blur kernel is applied) and copied to the final destination bitmap (excluding the transparent pixels). The original image is then again copied to the destination bitmap this time (excluding the transparent pixels). Here are the steps:

  • Copy original bitmap to a new one of the same size but with the small offset (the shadow offset) and make all non-transparent pixels of the same color (the shadow color)
  • Apply blurring on the shadow bitmap
  • Render this new bitmap to the destination bitmap
  • Render original bitmap to the destination bitmap (skipping all transparent pixels)

This method is explained through the CreateShadow(COLORREF transparentColor, COLORREF shadowColor) function, as shown below:

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);
}

Glowing - Where Am I Then?

A similar story goes for object glowing effect. It appears as if there is a shining aura around the object. The steps are almost the same:

  • Copy original bitmap to a new one of the same size but with small offsets in all four directions (the glowing directions) and make all non-transparent pixels of the same color (the glowing color)
  • Apply blurring on the glowing bitmap
  • Render this new bitmap to the destination bitmap
  • Render original bitmap to the destination bitmap (skipping all transparent pixels)

This method is explained through the CreateGlow(COLORREF transparentColor, COLORREF glowColor) function, as shown below:

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);
}

So, Where is the Difference Then?

The algorithms are almost the same except for one thing: you make just one/two offset[s] (horizontal and/or vertical) when you create the shadow effect, but four offsets or more (in all possible directions) when you create the glowing effect. Applying the blur kernel is very important since it makes shadows and glowing appear more realistic on the final bitmap.

The Blurring Kernel

The blurring kernel in this example is the following one (also called the lowpass-filter):

{[1 1 1][1 1 1][1 1 1]} * 1/9

So, the sum of the 9 surrounding source pixels is averaged (divided with the number of pixels) and the result represents the color of the destination pixel.

Points of Interest

I am very interested in simple image processing techniques like the ones shown in this article, by using basic graphical APIs, like Windows GDI.

History

  • 28th October, 2007: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

darkoman
Software Developer (Senior) Elektromehanika d.o.o. Nis
Serbia Serbia
He has a master degree in Computer Science at Faculty of Electronics in Nis (Serbia), and works as a C++/C# application developer for Windows platforms since 2001. He likes traveling, reading and meeting new people and cultures.

Comments and Discussions

 
QuestionAlpha on non-solid backgrounds? PinmemberJohann Gerell29-Oct-07 3:35 
AnswerRe: Alpha on non-solid backgrounds? Pinmemberdarkoman29-Oct-07 7:35 
GeneralRe: Alpha on non-solid backgrounds? PinmemberJohann Gerell29-Oct-07 20:13 
GeneralRe: Alpha on non-solid backgrounds? Pinmemberdarkoman30-Oct-07 7:45 
GeneralRe: Alpha on non-solid backgrounds? PinmemberJohann Gerell30-Oct-07 8:44 
GeneralArticle text update Pinmemberdarkoman28-Oct-07 22:19 
GeneralSome thoughts... PinmemberScope28-Oct-07 11:25 
GeneralRe: Some thoughts... PinmemberDavid Howe28-Oct-07 17:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 28 Oct 2007
Article Copyright 2007 by darkoman
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid