Click here to Skip to main content
15,892,674 members
Articles / Desktop Programming / MFC

Really cool visual FX

Rate me:
Please Sign up or sign in to vote.
4.92/5 (116 votes)
20 Aug 20012 min read 554.7K   12.2K   210  
A set of classes for doing stunning visual effects, including water, plasma and fire.
// CDIBSectionLite.cpp : implementation file
//
// General purpose DIBsection wrapper class for Win9x, NT 4.0
//
// Author      : Chris Maunder (cmaunder@dundas.com)
// Date        : 17 May 1999
//
// Copyright � Dundas Software Ltd. 1999, All Rights Reserved                      
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed unmodified by any means PROVIDING it is 
// not sold for profit without the authors written consent, and 
// providing that this notice and the authors name is included. If 
// the source code in this file is used in any commercial application 
// then a simple email would be nice.
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability for any damage, in any form, caused
// by this code. Use it at your own risk and as with all code expect bugs!
// It's been tested but I'm not perfect.
// 
// Please use and enjoy. Please let me know of any bugs/mods/improvements 
// that you have found/implemented and I will fix/incorporate them into this
// file.
//
// History :  25 May 1999 - First release
//             4 Jun 1999 - Fixed SetBitmap bug


#include "DIBSectionLite.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// Standard colours
PALETTEENTRY CDIBSectionLite::ms_StdColours[] = {
    { 0x00, 0x00, 0x00, 0 },    // First 2 will be palette for monochrome bitmaps
    { 0xFF, 0xFF, 0xFF, 0 },

    { 0x00, 0x00, 0xFF, 0 },    // First 16 will be palette for 16 colour bitmaps
    { 0x00, 0xFF, 0x00, 0 },
    { 0x00, 0xFF, 0xFF, 0 },
    { 0xFF, 0x00, 0x00, 0 },
    { 0xFF, 0x00, 0xFF, 0 },
    { 0xFF, 0xFF, 0x00, 0 },

    { 0x00, 0x00, 0x80, 0 },
    { 0x00, 0x80, 0x00, 0 },
    { 0x00, 0x80, 0x80, 0 },
    { 0x80, 0x00, 0x00, 0 },
    { 0x80, 0x00, 0x80, 0 },
    { 0x80, 0x80, 0x00, 0 },
    { 0x80, 0x80, 0x80, 0 },

    { 0xC0, 0xC0, 0xC0, 0 },
};

/////////////////////////////////////////////////////////////////////////////
// CDIBSectionLite static functions

// --- In� : nBitsPerPixel - bits per pixel
// --- Out : 
// --- Returns :The number of colours for this colour depth
// --- Effect : Returns the number of color table entries given the number
//              of bits per pixel of a bitmap
/*static*/ int CDIBSectionLite::NumColorEntries(int nBitsPerPixel) 
{
    int nColors = 0;

    switch (nBitsPerPixel) 
    {
	    case 1:  nColors = 2;   break;
        case 4:  nColors = 16;  break;
        case 8:  nColors = 256; break;
        case 16:
        case 24:
        case 32: nColors = 0;   break; // 16,24 or 32 bpp have no color table

        default:
           ASSERT(FALSE);
    }

    return nColors;
}

// --- In� : nWidth - image width in pixels
//           nBitsPerPixel - bits per pixel
// --- Out :
// --- Returns : Returns the number of storage bytes needed for each scanline 
//               in the bitmap
// --- Effect : 
/*static*/ int CDIBSectionLite::BytesPerLine(int nWidth, int nBitsPerPixel)
{
    int nBytesPerLine = nWidth * nBitsPerPixel;
    nBytesPerLine = ( (nBytesPerLine + 31) & (~31) ) / 8;

    return nBytesPerLine;
}

// --- In� : palette - reference to a palette object which will be filled
//           nNumColours - number of colour entries to fill
// --- Out :
// --- Returns : TRUE on success, false otherwise
// --- Effect : Creates a halftone color palette independant of screen color depth.
//              palette will be filled with the colors, and nNumColours is the No.
//              of colors to file. If nNumColoursis 0 or > 256, then 256 colors are used.
/*static*/ BOOL CDIBSectionLite::CreateHalftonePalette(CPalette& palette, int nNumColours)
{
    palette.DeleteObject();

    int nNumStandardColours = sizeof(ms_StdColours) / sizeof(ms_StdColours[0]);
    int nIndex = 0;
    int nNumEntries = nNumColours;
    if (nNumEntries <= 0 || nNumEntries > 256)
        nNumEntries = 256;

    PALETTEINFO PalInfo;                   
    PalInfo.palNumEntries = (WORD) nNumEntries;

    // The standard colours (16)
    for (int i = 0; i < nNumStandardColours; i++)
    {
        if (nIndex >= nNumEntries) break;
        memcpy( &(PalInfo.palPalEntry[nIndex]), &(ms_StdColours[i]), sizeof(PALETTEENTRY) );
        nIndex++;
    }

    // A colour cube (6 divisions = 216)
    for (int blue = 0; blue <= 5; blue++)
        for (int green = 0; green <= 5; green++)
            for (int red = 0; red <= 5; red++)
            {
                if (nIndex >= nNumEntries)
                    break;

                PalInfo.palPalEntry[nIndex].peRed   = (BYTE) ((red*255)/5);
                PalInfo.palPalEntry[nIndex].peGreen = (BYTE) ((green*255)/5);
                PalInfo.palPalEntry[nIndex].peBlue  = (BYTE) ((blue*255)/5);
                PalInfo.palPalEntry[nIndex].peFlags = 0;
                nIndex++;
            }

    // A grey scale (24 divisions)
    for (int grey = 0; grey <= 23; grey++)
    {
        if (nIndex >= nNumEntries) 
            break;

        PalInfo.palPalEntry[nIndex].peRed   = (BYTE) (grey*255/23);
        PalInfo.palPalEntry[nIndex].peGreen = (BYTE) (grey*255/23);
        PalInfo.palPalEntry[nIndex].peBlue  = (BYTE) (grey*255/23);
        PalInfo.palPalEntry[nIndex].peFlags = 0;
        nIndex++;
    }

    return palette.CreatePalette((LPLOGPALETTE) PalInfo);
}


/////////////////////////////////////////////////////////////////////////////
// CDIBSectionLite

CDIBSectionLite::CDIBSectionLite()
{
    m_hBitmap = NULL;
    m_bDither = FALSE;
    m_hDrawDib = NULL;

    DeleteObject(); // This will initialise to a known state - ie. empty
}

CDIBSectionLite::~CDIBSectionLite()
{
    DeleteObject();
}

// --- In� :
// --- Out :
// --- Returns :
// --- Effect : Resets the object to an empty state, and frees all memory used.
void CDIBSectionLite::DeleteObject()
{
    if (m_hBitmap)
        ::DeleteObject(m_hBitmap);
    m_hBitmap = NULL;
    m_ppvBits = NULL;

    memset(&m_DIBinfo, 0, sizeof(m_DIBinfo));

    if (m_hDrawDib)
        DrawDibClose(m_hDrawDib);
    m_hDrawDib = NULL;

    m_iColorDataType = DIB_RGB_COLORS;
    m_iColorTableSize = 0;
}

/////////////////////////////////////////////////////////////////////////////
// CDIBSectionLite diagnostics

#ifdef _DEBUG
void CDIBSectionLite::AssertValid() const
{
    ASSERT(m_hBitmap);

    DIBSECTION ds;
    DWORD dwSize = GetObject( m_hBitmap, sizeof(DIBSECTION), &ds );
    ASSERT(dwSize == sizeof(DIBSECTION));

    ASSERT(0 <= m_iColorTableSize && m_iColorTableSize <= 256);

	CObject::AssertValid();
}

void CDIBSectionLite::Dump(CDumpContext& dc) const
{
	CObject::Dump(dc);
}

#endif //_DEBUG

// --- In� : bDither - whether or not dithering should be enabled
// --- Out :
// --- Returns : TRUE on success
// --- Effect : Turns dithering on by using the DrawDib functions instead of 
//              the GDI functions
BOOL CDIBSectionLite::SetDither(BOOL bDither)
{
    BOOL bResult = TRUE;

    // Check for a no-change op.
    if ( (m_bDither == bDither) &&
         ((m_hDrawDib && bDither) || (!m_hDrawDib && !bDither)) )
        return bResult;

    if (m_hDrawDib != NULL)
    {
        DrawDibClose(m_hDrawDib);
        m_hDrawDib = NULL;
    }

    if (bDither)
    {
        if ( (m_hDrawDib = DrawDibOpen()) == NULL )
            bResult = FALSE;
    }

    m_bDither = (m_hDrawDib != NULL);

    return bResult;
}

// --- In� : 
// --- Out :
// --- Returns : TRUE if dithering is used
// --- Effect : Returns whether or not the DrawDib functions (and hence dithering)
//              is being used.
BOOL CDIBSectionLite::GetDither()
{
    return (m_bDither && (m_hDrawDib != NULL));
}

/////////////////////////////////////////////////////////////////////////////
// CDIBSectionLite operations

// --- In� : pDC - Pointer to a device context
//           ptDest - point at which the topleft corner of the image is drawn
//           bForceBackground - Specifies whether the palette is forced to be 
//                              a background palette
// --- Out :
// --- Returns : TRUE on success
// --- Effect : Draws the image 1:1 on the device context
BOOL CDIBSectionLite::Draw(CDC* pDC, CPoint ptDest, BOOL bForceBackground /*=FALSE*/ ) 
{ 
    if (!m_hBitmap)
        return FALSE;

    CSize size = GetSize();
    CPoint SrcOrigin = CPoint(0,0);

    BOOL bResult = FALSE;

    if (GetDither() && GetBitCount() >= 8)
    {
        DrawDibSetPalette( m_hDrawDib, (HPALETTE)m_Palette);
        DrawDibRealize( m_hDrawDib,  pDC->GetSafeHdc(),  bForceBackground);
        
        bResult = DrawDibDraw(m_hDrawDib, pDC->GetSafeHdc(), 
                              ptDest.x, ptDest.y, size.cx, size.cy, 
                              GetBitmapInfoHeader(), GetDIBits(), 
                              SrcOrigin.x, SrcOrigin.y, size.cx, size.cy, 
                              0/*DDF_HALFTONE*/);
    }
    else
    {
        CPalette* pOldPalette = NULL;
        if (m_Palette.m_hObject && UsesPalette(pDC))
        {
            pOldPalette = pDC->SelectPalette(&m_Palette, bForceBackground);
            pDC->RealizePalette();
        }

        bResult = SetDIBitsToDevice(pDC->GetSafeHdc(), 
                                    ptDest.x, ptDest.y, 
                                    size.cx, size.cy,
                                    SrcOrigin.x, SrcOrigin.y,
                                    SrcOrigin.y, size.cy - SrcOrigin.y, 
                                    GetDIBits(), GetBitmapInfo(), 
                                    m_iColorDataType); 

        if (pOldPalette)
            pDC->SelectPalette(pOldPalette, FALSE);
    }
    
    return bResult;
}

// --- In� : pDC - Pointer to a device context
//           ptDest - point at which the topleft corner of the image is drawn
//           size - size to stretch the image
//           bForceBackground - Specifies whether the palette is forced to be 
//                              a background palette
// --- Out :
// --- Returns : TRUE on success
// --- Effect : Stretch draws the image to the desired size on the device context
BOOL CDIBSectionLite::Stretch(CDC* pDC, CPoint ptDest, CSize size, 
                              BOOL bForceBackground /*=FALSE*/) 
{ 
    if (!m_hBitmap)
        return FALSE;

    CSize imagesize = GetSize();
    CPoint SrcOrigin = CPoint(0,0);

    BOOL bResult = FALSE;

    if (GetDither() && GetBitCount() >= 8)
    {
        DrawDibSetPalette( m_hDrawDib, (HPALETTE)m_Palette);
        DrawDibRealize( m_hDrawDib,  pDC->GetSafeHdc(),  bForceBackground);
        
        bResult = DrawDibDraw(m_hDrawDib, pDC->GetSafeHdc(), 
                              ptDest.x, ptDest.y, size.cx, size.cy, 
                              GetBitmapInfoHeader(), GetDIBits(), 
                              SrcOrigin.x, SrcOrigin.y, imagesize.cx, imagesize.cy, 
                              0/*DDF_HALFTONE*/);
    }
    else
    {
        CPalette* pOldPalette = NULL;
        if (m_Palette.m_hObject && UsesPalette(pDC))
        {
            pOldPalette = pDC->SelectPalette(&m_Palette, bForceBackground);
            pDC->RealizePalette();
        }

        pDC->SetStretchBltMode(COLORONCOLOR);

        bResult = StretchDIBits(pDC->GetSafeHdc(), 
                                ptDest.x, ptDest.y, 
                                size.cx, size.cy,
                                SrcOrigin.x, SrcOrigin.y,
                                imagesize.cx, imagesize.cy, 
                                GetDIBits(), GetBitmapInfo(), 
                                m_iColorDataType, SRCCOPY); 

        if (pOldPalette)
            pDC->SelectPalette(pOldPalette, FALSE);
    }

    return bResult;
}


//////////////////////////////////////////////////////////////////////////////
// Setting the bitmap...


// --- In� : nIDResource - resource ID
// --- Out :
// --- Returns : Returns TRUE on success, FALSE otherwise
// --- Effect : Initialises the bitmap from a resource. If failure, then object is
//              initialised back to an empty bitmap.
BOOL CDIBSectionLite::SetBitmap(UINT nIDResource)
{   
    return SetBitmap(MAKEINTRESOURCE(nIDResource));
}

// --- In� : lpszResourceName - resource name
// --- Out :
// --- Returns : Returns TRUE on success, FALSE otherwise
// --- Effect : Initialises the bitmap from a resource. If failure, then object is
//              initialised back to an empty bitmap.
BOOL CDIBSectionLite::SetBitmap(LPCTSTR lpszResourceName)
{
    HBITMAP hBmp = (HBITMAP)::LoadImage(AfxGetResourceHandle(), 
                                        lpszResourceName,
                                        IMAGE_BITMAP, 
                                        0,0, 
                                        LR_CREATEDIBSECTION);

    if (!hBmp) return FALSE;

    BOOL bResult = SetBitmap(hBmp);
    ::DeleteObject(hBmp);
    return bResult;
}

// --- In� : lpBitmapInfo - pointer to a BITMAPINFO structure
//           lpBits - pointer to image bits
// --- Out :
// --- Returns : Returns TRUE on success, FALSE otherwise
// --- Effect : Initialises the bitmap using the information in lpBitmapInfo to determine
//              the dimensions and colours, and the then sets the bits from the bits in
//              lpBits. If failure, then object is initialised back to an empty bitmap.
BOOL CDIBSectionLite::SetBitmap(LPBITMAPINFO lpBitmapInfo, LPVOID lpBits)
{
    DeleteObject();

    if (!lpBitmapInfo || !lpBits)
        return FALSE;

    HDC hDC = NULL;
    try {
        BITMAPINFOHEADER& bmih = lpBitmapInfo->bmiHeader;

        // Compute the number of colours in the colour table
        m_iColorTableSize = NumColorEntries(bmih.biBitCount);

        DWORD dwBitmapInfoSize = sizeof(BITMAPINFO) + m_iColorTableSize*sizeof(RGBQUAD);

        // Copy over BITMAPINFO contents
        memcpy(&m_DIBinfo, lpBitmapInfo, dwBitmapInfoSize);

        // Should now have all the info we need to create the sucker.
        //TRACE(_T("Width %d, Height %d, Bits/pixel %d, Image Size %d\n"),
        //      bmih.biWidth, bmih.biHeight, bmih.biBitCount, bmih.biSizeImage);

        // Create a DC which will be used to get DIB, then create DIBsection
        hDC = ::GetDC(NULL);
        if (!hDC) 
        {
            TRACE0("Unable to get DC\n");
            AfxThrowResourceException();
        }

        m_hBitmap = CreateDIBSection(hDC, (const BITMAPINFO*) m_DIBinfo,
                                     m_iColorDataType, &m_ppvBits, NULL, 0);
        ::ReleaseDC(NULL, hDC);
        if (!m_hBitmap)
        {
            TRACE0("CreateDIBSection failed\n");
            AfxThrowResourceException();
        }

        DWORD dwImageSize = m_DIBinfo.bmiHeader.biSizeImage;
        if (dwImageSize == 0)
        {
            int nBytesPerLine = BytesPerLine(lpBitmapInfo->bmiHeader.biWidth, 
                                             lpBitmapInfo->bmiHeader.biBitCount);
            dwImageSize = nBytesPerLine * lpBitmapInfo->bmiHeader.biHeight;
        }

        // Flush the GDI batch queue 
        GdiFlush();

        memcpy(m_ppvBits, lpBits, dwImageSize);

        if (!CreatePalette())
        {
            TRACE0("Unable to create palette\n");
            AfxThrowResourceException();
        }
    }
    catch (CException *e)
    {
        e->Delete();
        _ShowLastError();
        if (hDC) 
            ::ReleaseDC(NULL, hDC);
        DeleteObject();
        return FALSE;
    }

    return TRUE;
}

// --- In� : hBitmap - handle to image
//           pPalette - optional palette to use when setting image
// --- Out :
// --- Returns : Returns TRUE on success, FALSE otherwise
// --- Effect : Initialises the bitmap from the HBITMAP supplied. If failure, then
//              object is initialised back to an empty bitmap.
BOOL CDIBSectionLite::SetBitmap(HBITMAP hBitmap, CPalette* pPalette /*= NULL*/)
{
    DeleteObject();

    if (!hBitmap)
        return FALSE;

    // Get dimensions of bitmap
    BITMAP bm;
    if (!::GetObject(hBitmap, sizeof(bm),(LPVOID)&bm))
        return FALSE;
    bm.bmHeight = abs(bm.bmHeight);
   
    CWindowDC dc(NULL);
    CPalette* pOldPalette = NULL;

    try {
        m_iColorTableSize = NumColorEntries(bm.bmBitsPixel);

        // Initialize the BITMAPINFOHEADER in m_DIBinfo
        BITMAPINFOHEADER& bih = m_DIBinfo.bmiHeader;
        bih.biSize          = sizeof(BITMAPINFOHEADER);
        bih.biWidth         = bm.bmWidth;
        bih.biHeight        = bm.bmHeight;
        bih.biPlanes        = 1;                // Must always be 1 according to docs
        bih.biBitCount      = bm.bmBitsPixel;
        bih.biCompression   = BI_RGB;
        bih.biSizeImage     = BytesPerLine(bm.bmWidth, bm.bmBitsPixel) * bm.bmHeight;
        bih.biXPelsPerMeter = 0;
        bih.biYPelsPerMeter = 0;
        bih.biClrUsed       = 0;
        bih.biClrImportant  = 0;

        // calls GetDIBits with NULL bits pointer to fill in the BITMAPINFOHEADER data
        if (!::GetDIBits(dc.m_hDC, hBitmap, 0, bm.bmHeight, NULL, m_DIBinfo, m_iColorDataType))
        {
            TRACE0("Unable to GetDIBits\n");
            AfxThrowResourceException();
        }

        // If the driver did not fill in the biSizeImage field, then compute it
        // Each scan line of the image is aligned on a DWORD (32bit) boundary
        if (bih.biSizeImage == 0)
            bih.biSizeImage = BytesPerLine(bih.biWidth, bih.biBitCount) * bih.biHeight;

        if (pPalette)
            SetPalette(pPalette);
        else
            CreateHalftonePalette(m_Palette, m_iColorTableSize);

        if (m_Palette.GetSafeHandle())
        {
            pOldPalette = dc.SelectPalette(&m_Palette, FALSE);
            dc.RealizePalette();
        }

        // Create it!
        m_hBitmap = CreateDIBSection(dc.m_hDC, 
                                     (const BITMAPINFO*) m_DIBinfo,
                                     m_iColorDataType,
                                     &m_ppvBits, 
                                     NULL, 0);
        if (pOldPalette)
            dc.SelectPalette(pOldPalette, FALSE);
        pOldPalette = NULL; 

        if (!m_hBitmap)
        {
            TRACE0("Unable to CreateDIBSection\n");
            AfxThrowResourceException();
        }
    
        // Need to copy the supplied bitmap onto the newly created DIBsection
        CDC memDC, CopyDC;
        if (!CopyDC.CreateCompatibleDC(&dc) || !memDC.CreateCompatibleDC(&dc)) 
        {
            TRACE0("Unable to create compatible DC's\n");
            AfxThrowResourceException();
        }

        if (m_Palette.GetSafeHandle())
        {
            memDC.SelectPalette(&m_Palette, FALSE);  memDC.RealizePalette();
            CopyDC.SelectPalette(&m_Palette, FALSE); CopyDC.RealizePalette();
        }

        GdiFlush();

        HBITMAP hOldMemBitmap  = (HBITMAP) SelectObject(memDC.m_hDC,  hBitmap);
        HBITMAP hOldCopyBitmap = (HBITMAP) SelectObject(CopyDC.m_hDC, m_hBitmap);

        CopyDC.BitBlt(0,0, bm.bmWidth, bm.bmHeight, &memDC, 0,0, SRCCOPY);

        SelectObject(memDC.m_hDC, hOldMemBitmap);
        SelectObject(CopyDC.m_hDC, hOldCopyBitmap);

        if (m_Palette.GetSafeHandle())
        {
            memDC.SelectStockObject(DEFAULT_PALETTE);
            CopyDC.SelectStockObject(DEFAULT_PALETTE);
        }
    }
    catch (CException *e)
    {
        e->Delete();
        _ShowLastError();
        if (pOldPalette)
            dc.SelectPalette(pOldPalette, FALSE);
        DeleteObject();
        return FALSE;
    }

    return TRUE;
}


//////////////////////////////////////////////////////////////////////////////
// Persistance...


// --- In� : lpszFileName - image filename
// --- Out :
// --- Returns : Returns TRUE on success, FALSE otherwise
// --- Effect : Loads the bitmap from a bitmap file with the name lpszFileName. 
//              If failure, then object is initialised back to an empty bitmap.
BOOL CDIBSectionLite::Load(LPCTSTR lpszFileName)
{
    CFile file;
    if (!file.Open(lpszFileName, CFile::modeRead))
        return FALSE;

    // Get the current file position.  
    DWORD dwFileStart = file.GetPosition();

    // The first part of the file contains the file header.
	// This will tell us if it is a bitmap, how big the header is, and how big 
    // the file is. The header size in the file header includes the color table.
	BITMAPFILEHEADER BmpFileHdr;
    int nBytes;
    nBytes = file.Read(&BmpFileHdr, sizeof(BmpFileHdr));
    if (nBytes != sizeof(BmpFileHdr)) 
    {
        TRACE0("Failed to read file header\n");
        return FALSE;
    }

    // Check that we have the magic 'BM' at the start.
    if (BmpFileHdr.bfType != DS_BITMAP_FILEMARKER)
    {
        TRACE0("Not a bitmap file\n");
        return FALSE;
    }

    // Read the header (assuming it's a DIB). 
	DIBINFO	BmpInfo;
    nBytes = file.Read(&BmpInfo, sizeof(BITMAPINFOHEADER)); 
    if (nBytes != sizeof(BITMAPINFOHEADER)) 
    {
        TRACE0("Failed to read BITMAPINFOHEADER\n");
        return FALSE;
    }

    // Check that we have a real Windows DIB file.
    if (BmpInfo.bmiHeader.biSize != sizeof(BITMAPINFOHEADER))
    {
        TRACE0(" File is not a Windows DIB\n");
        return FALSE;
    }

    // See how big the color table is in the file (if there is one).  
    int nColors = NumColorEntries(BmpInfo.bmiHeader.biBitCount);
	if (nColors > 0) 
    {
        // Read the color table from the file.
        int nColorTableSize = nColors * sizeof(RGBQUAD);
		nBytes = file.Read(BmpInfo.ColorTable(), nColorTableSize);
		if (nBytes != nColorTableSize) 
        {
			TRACE0("Failed to read color table\n");
			return FALSE;
        }
	}

	// So how big the bitmap surface is.
    int nBitsSize = BmpFileHdr.bfSize - BmpFileHdr.bfOffBits;

    // Allocate the memory for the bits and read the bits from the file.
    BYTE* pBits = (BYTE*) malloc(nBitsSize);
    if (!pBits) 
    {
        TRACE0("Out of memory for DIB bits\n");
        return FALSE;
    }

    // Seek to the bits in the file.
    file.Seek(dwFileStart + BmpFileHdr.bfOffBits, CFile::begin);

    // read the bits
    nBytes = file.Read(pBits, nBitsSize);
    if (nBytes != nBitsSize) 
    {
        TRACE0("Failed to read bits\n");
        free(pBits);
		return FALSE;
    }

	// Everything went OK.
	BmpInfo.bmiHeader.biSizeImage = nBitsSize;

    if (!SetBitmap((LPBITMAPINFO) BmpInfo, pBits))
    {
        TRACE0("Failed to set bitmap info\n");
        free(pBits);
		return FALSE;
    }

    free(pBits);

    return TRUE;
}

// --- In� : lpszFileName - image filename
// --- Out :
// --- Returns : Returns TRUE on success, FALSE otherwise
// --- Effect : Saves the image to file.
BOOL CDIBSectionLite::Save(LPCTSTR lpszFileName)
{
    BITMAPFILEHEADER   hdr;
    LPBITMAPINFOHEADER lpbi = GetBitmapInfoHeader();

    if (!lpbi || !lpszFileName)
        return FALSE;

    CFile file;
    if (!file.Open(lpszFileName, CFile::modeWrite|CFile::modeCreate))
        return FALSE;

    DWORD dwBitmapInfoSize = sizeof(BITMAPINFO) + m_iColorTableSize*sizeof(RGBQUAD);
    DWORD dwFileHeaderSize = dwBitmapInfoSize + sizeof(hdr);

    // Fill in the fields of the file header 
    hdr.bfType       = DS_BITMAP_FILEMARKER;
    hdr.bfSize       = dwFileHeaderSize + lpbi->biSizeImage;
    hdr.bfReserved1  = 0;
    hdr.bfReserved2  = 0;
    hdr.bfOffBits    = dwFileHeaderSize;

    // Write the file header 
    file.Write(&hdr, sizeof(hdr));

    // Write the DIB header
    file.Write(lpbi, dwBitmapInfoSize);
    
    // Write DIB bits
    file.Write(GetDIBits(), lpbi->biSizeImage);

    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CDIBSectionLite palette stuff


// --- In� :
// --- Out :
// --- Returns : TRUE on success
// --- Effect : Creates the palette from the DIBSection's color table. Assumes 
//              m_iColorTableSize has been set and the DIBsection m_hBitmap created
BOOL CDIBSectionLite::CreatePalette()
{
    m_Palette.DeleteObject();

    if (!m_hBitmap)
        return FALSE;

    // Create a 256 color halftone palette if there is no color table in the DIBSection
    if (m_iColorTableSize == 0)
        return CreateHalftonePalette(m_Palette, 256);


    HDC hDC = ::GetDC(NULL);
    if (!hDC)
        return FALSE;

    // Get space for the colour entries
    RGBQUAD *pRGB = new RGBQUAD[m_iColorTableSize];
    if (!pRGB)
    {
        ReleaseDC(NULL, hDC);
        return CreateHalftonePalette(m_Palette, m_iColorTableSize);
    }

    // Create a memory DC compatible with the current DC
    CDC MemDC;
    MemDC.CreateCompatibleDC(CDC::FromHandle(hDC));
    if (!MemDC.GetSafeHdc())
    {
        delete [] pRGB;
        ::ReleaseDC(NULL, hDC);
        return CreateHalftonePalette(m_Palette, m_iColorTableSize);
    }
    ::ReleaseDC(NULL, hDC);
    
    HBITMAP hOldBitmap = (HBITMAP) ::SelectObject(MemDC.GetSafeHdc(), m_hBitmap);
    if (!hOldBitmap)
    {
        delete [] pRGB;
        return CreateHalftonePalette(m_Palette, m_iColorTableSize);
    }

    // Get the colours used. 
    int nColours = ::GetDIBColorTable(MemDC.GetSafeHdc(), 0, m_iColorTableSize, pRGB);

    // Clean up
    ::SelectObject(MemDC.GetSafeHdc(), hOldBitmap);

    if (!nColours)   // No colours retrieved => the bitmap in the DC is not a DIB section
    {
        delete [] pRGB;
        return CreateHalftonePalette(m_Palette, m_iColorTableSize);
    }   
    
    // Create and fill a LOGPALETTE structure with the colours used.
    PALETTEINFO PaletteInfo;
    PaletteInfo.palNumEntries = (WORD) m_iColorTableSize;
                        
    for (int i = 0; i < nColours; i++)
    {
        PaletteInfo.palPalEntry[i].peRed   = pRGB[i].rgbRed;
        PaletteInfo.palPalEntry[i].peGreen = pRGB[i].rgbGreen;
        PaletteInfo.palPalEntry[i].peBlue  = pRGB[i].rgbBlue;
        PaletteInfo.palPalEntry[i].peFlags = 0;
    }
    for (; i < (int) m_iColorTableSize; i++)
    {
        PaletteInfo.palPalEntry[i].peRed   = 0;
        PaletteInfo.palPalEntry[i].peGreen = 0;
        PaletteInfo.palPalEntry[i].peBlue  = 0;
        PaletteInfo.palPalEntry[i].peFlags = 0;
    }

    delete [] pRGB;

    // Create Palette!
    return m_Palette.CreatePalette(&PaletteInfo);
}

// --- In� : pPalette - new palette to use
// --- Out :
// --- Returns : TRUE on success
// --- Effect : Sets the current palette used by the image from the supplied CPalette,
//              and sets the color table in the DIBSection
BOOL CDIBSectionLite::SetPalette(CPalette* pPalette)
{
    m_Palette.DeleteObject();

    if (!pPalette)
        return FALSE;

    UINT nColours = pPalette->GetEntryCount();
    if (nColours <= 0 || nColours > 256)
        return FALSE;

    // Get palette entries
    PALETTEINFO pi;
    pi.palNumEntries = (WORD) pPalette->GetPaletteEntries(0, nColours, (LPPALETTEENTRY) pi);
                          
    // TODO: If pi.palNumEntries < m_iColorTableSize, then fill in blanks with 0's

    return SetLogPalette(&pi);
}

// --- In� : pLogPalette - new palette to use
// --- Out :
// --- Returns : TRUE on success
// --- Effect : Sets the current palette used by the image from the supplied LOGPALETTE
BOOL CDIBSectionLite::SetLogPalette(LOGPALETTE* pLogPalette)
{
    ASSERT(pLogPalette->palVersion == (WORD) 0x300);
    UINT nColours = pLogPalette->palNumEntries;
    if (nColours <= 0 || nColours > 256)
    {
        CreatePalette();
        return FALSE;
    }

    // Create new palette
    m_Palette.DeleteObject();
    if (!m_Palette.CreatePalette(pLogPalette))
    {
        CreatePalette();
        return FALSE;
    }

    if (m_iColorTableSize == 0)
        return TRUE;

    // Set the DIB colours
    RGBQUAD RGBquads[256]; 
    for (UINT i = 0; i < nColours; i++)
    {
        RGBquads[i].rgbRed   = pLogPalette->palPalEntry[i].peRed;
        RGBquads[i].rgbGreen = pLogPalette->palPalEntry[i].peGreen;
        RGBquads[i].rgbBlue  = pLogPalette->palPalEntry[i].peBlue;
        RGBquads[i].rgbReserved = 0;
    }
    
    return FillDIBColorTable(nColours, RGBquads);
}

void CDIBSectionLite::CreateGradientBmp	(COLORREF clrBack,
								 COLORREF clrStart,
								 COLORREF clrEnd,
								 int iWidth,
								 int iHeight,
								 int iDirection)
{
	RECT rectFill;			   // Rectangle for filling band
	float fStep;              // How wide is each band?
	CBrush brush;			// Brush to fill in the bar	

	CBitmap ourNewBitmap;
	CWindowDC dc(NULL);
	CDC tempDC;
	CDC tempDC1;

	tempDC.CreateCompatibleDC(&dc);
	tempDC1.CreateCompatibleDC(&dc);

	ourNewBitmap.CreateDiscardableBitmap(&dc,iWidth,iHeight);
	//ourNewBitmap.CreateBitmap(iWidth,iHeight,3,32,NULL);

	CBitmap* pOldBitmap = tempDC.SelectObject(&ourNewBitmap);
	// First find out the largest color distance between the start and end colors.  This distance
	// will determine how many steps we use to carve up the client region and the size of each
	// gradient rect.
	int r, g, b;							// First distance, then starting value
	float rStep, gStep, bStep;		// Step size for each color

	// Get the color differences
	r = (GetRValue(clrEnd) - GetRValue(clrStart));
	g = (GetGValue(clrEnd) - GetGValue(clrStart));
	b =  (GetBValue(clrEnd) - GetBValue(clrStart));

	// Make the number of steps equal to the greatest distance
	int nSteps = max(abs(r), max(abs(g), abs(b)));

	// Determine how large each band should be in order to cover the
	// client with nSteps bands (one for every color intensity level)
	if(iDirection == BMGRADIENT_DIRECTION_HORZ)
		fStep = (float)iWidth / (float)nSteps;
	else if(iDirection == BMGRADIENT_DIRECTION_VERT)
		fStep = (float)iHeight / (float)nSteps;

	// Calculate the step size for each color
	rStep = r/(float)nSteps;
	gStep = g/(float)nSteps;
	bStep = b/(float)nSteps;

	// Reset the colors to the starting position
	r = GetRValue(clrStart);
	g = GetGValue(clrStart);
	b = GetBValue(clrStart);

	// Start filling bands

	for (int iOnBand = 0; iOnBand < nSteps; iOnBand++) 
	{
		// Set the rect we will fill with the gradient
		if(iDirection == BMGRADIENT_DIRECTION_HORZ)
		{
			::SetRect(&rectFill,
						(int)(iOnBand * fStep),       // Upper left X
						 0,									 // Upper left Y
						(int)((iOnBand+1) * fStep),          // Lower right X
						iHeight+1);			// Lower right Y
		}
		else if(iDirection == BMGRADIENT_DIRECTION_VERT)
		{
			::SetRect(&rectFill,
						0,       // Upper left X
						 (iOnBand * fStep),					 // Upper left Y
						iWidth,          // Lower right X
						(int)((iOnBand+1) * fStep));			// Lower right Y
		}
	
		// CDC::FillSolidRect is faster, but it does not handle 8-bit color depth
		VERIFY(brush.CreateSolidBrush(RGB(r+rStep*iOnBand, g + gStep*iOnBand, b + bStep *iOnBand)));
		tempDC.FillRect(&rectFill,&brush);
		VERIFY(brush.DeleteObject());


		// If we are past the maximum for the current position we need to get out of the loop.
		// Before we leave, we repaint the remainder of the client area with the background color.
		if(iDirection == BMGRADIENT_DIRECTION_HORZ)
		{
			if (rectFill.right > iWidth)
			{
				::SetRect(&rectFill, rectFill.right, 0, iWidth, iHeight);
				VERIFY(brush.CreateSolidBrush(clrBack));
				tempDC.FillRect(&rectFill, &brush);
				VERIFY(brush.DeleteObject());
				return;
			}
		}
		else if(iDirection == BMGRADIENT_DIRECTION_VERT)
		{
			if (rectFill.right > iHeight)
			{
				::SetRect(&rectFill, rectFill.right, 0, iWidth, iHeight);
				VERIFY(brush.CreateSolidBrush(clrBack));
				tempDC.FillRect(&rectFill, &brush);
				VERIFY(brush.DeleteObject());
				return;
			}
		}
		
	}


	// Create a 32 bit bitmap
	m_ppvBits = new DWORD[iWidth*iHeight];
	BITMAPINFO bih;

    // create DIB Section
    bih.bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
    bih.bmiHeader.biWidth         = iWidth; 
    bih.bmiHeader.biHeight        = iHeight; 
    bih.bmiHeader.biPlanes        = 1; 
    bih.bmiHeader.biBitCount      = 32; 
    bih.bmiHeader.biCompression   = BI_RGB; 
    bih.bmiHeader.biSizeImage     = 0; 
    bih.bmiHeader.biXPelsPerMeter = 0; 
    bih.bmiHeader.biYPelsPerMeter = 0; 
    bih.bmiHeader.biClrUsed       = 0; 
    bih.bmiHeader.biClrImportant  = 0; 
	
	
	SetBitmap(&bih,m_ppvBits);

	DWORD* pLine = (DWORD*)GetDIBits();


	// Copy the bits into our 32 bit dib..
	for(int i=0;i<iHeight;i++)
	{	
		for(int j=0;j<iWidth;j++)
		{
			pLine[(i*iWidth)+j] = FixColorRef(tempDC.GetPixel(j,i));
		}
	}

	// Set the dc back to normal
	tempDC.SelectObject(pOldBitmap);


	
}
void CDIBSectionLite::Create32BitFromPicture (CPictureHolder* pPicture,
											 int iWidth,
											 int iHeight)
{
	CBitmap ourNewSizedBMP;
	CWindowDC dc(NULL);
	CDC tempDC;
	CRect rcRender;

	tempDC.CreateCompatibleDC(&dc);

	ourNewSizedBMP.CreateDiscardableBitmap(&dc,iWidth,iHeight);

	// Now we need to select it into our temp DC so we can render the picture onto it
	CBitmap* pOldBitmap = tempDC.SelectObject(&ourNewSizedBMP);

	// Render the picture onto our bmp
	rcRender.SetRect(0,0,iWidth,iHeight);
	pPicture->Render(&tempDC,rcRender,rcRender);

	// Create a 32 bit bitmap
	m_ppvBits = new DWORD[iWidth*iHeight];
	BITMAPINFO bih;

    // create DIB Section
    bih.bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
    bih.bmiHeader.biWidth         = iWidth; 
    bih.bmiHeader.biHeight        = iHeight; 
    bih.bmiHeader.biPlanes        = 1; 
    bih.bmiHeader.biBitCount      = 32; 
    bih.bmiHeader.biCompression   = BI_RGB; 
    bih.bmiHeader.biSizeImage     = 0; 
    bih.bmiHeader.biXPelsPerMeter = 0; 
    bih.bmiHeader.biYPelsPerMeter = 0; 
    bih.bmiHeader.biClrUsed       = 0; 
    bih.bmiHeader.biClrImportant  = 0; 
	
	
	SetBitmap(&bih,m_ppvBits);

	DWORD* pLine = (DWORD*)GetDIBits();


	// Copy the bits into our 32 bit dib..
	for(int i=0;i<iHeight;i++)
	{	
		for(int j=0;j<iWidth;j++)
		{
			pLine[(i*iWidth)+j] = FixColorRef(tempDC.GetPixel(j,i));
		}
	}

	// Set the dc back to normal
	tempDC.SelectObject(pOldBitmap);

}
inline COLORREF CDIBSectionLite::FixColorRef(COLORREF clr)
{
	int r = GetRValue(clr);
	int g = GetGValue(clr);
	int b =  GetBValue(clr);

	return RGB(b,g,r);
}

// --- In� : nNumColours - number of colours to set
//           pRGB - colours to fill
// --- Out :
// --- Returns : Returns TRUE on success
// --- Effect : Sets the colours used by the image. Only works if # colours <= 256
BOOL CDIBSectionLite::FillDIBColorTable(UINT nNumColours, RGBQUAD *pRGB)
{
    if (!m_hBitmap || !pRGB || !nNumColours || !m_iColorTableSize
        || nNumColours > 256)
        return FALSE;

    // Create a memory DC compatible with the screen
    HDC hDC = ::GetDC(NULL);
    if (!hDC) return FALSE;

    CDC MemDC;
    MemDC.CreateCompatibleDC(CDC::FromHandle(hDC));
    ::ReleaseDC(NULL, hDC);
    if (!MemDC.GetSafeHdc())
        return FALSE;

    HBITMAP hOldBitmap = (HBITMAP) ::SelectObject(MemDC.GetSafeHdc(), m_hBitmap);

    // Set the bitmap colours.
    int nColours = ::SetDIBColorTable(MemDC.GetSafeHdc(), 0, nNumColours, pRGB);

    // Clean up
    if (hOldBitmap)
        ::SelectObject(MemDC.GetSafeHdc(), hOldBitmap);
    MemDC.DeleteDC();

    return (nColours > 0);
}

#ifdef _DEBUG
// Makes trace windows a little bit more informative...
void CDIBSectionLite::_ShowLastError()
{
    LPVOID lpMsgBuf;
    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,    
                  NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
                  (LPTSTR) &lpMsgBuf, 0, NULL);
    TRACE1("Last error: %s\n", lpMsgBuf);
    LocalFree(lpMsgBuf);
}
#else
void CDIBSectionLite::_ShowLastError() {}
#endif

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions