www.gusucode.com > 包含近30种图像特效的VC++小程序源码程序 > 包含近30种图像特效的VC++小程序源码程序/code/DIBSectionLite.cpp

    /////////////////////////////////////////////////////////////////////////
//类名:CDIBSectionLite
//功能:DIB位图文件操作类(载入、显示、保存)
//用法:
//      CDIBSectionLite dibsection;
//      dibsection.Load(_T("image.bmp"));
//      dibsection.Draw(pDC, CPoint(0,0));  // pDC的类型是CDC*
//
//      CDIBSectionLite dibsection;
//      dibsection.SetBitmap(IDB_BITMAP); 
//      dibsection.Draw(pDC, CPoint(0,0));  // pDC的类型是CDC*
//修改:徐景周(jingzhou_xu@163.net)
//组织:未来工作室(Future Studio)
//日期:2002.1.8
////////////////////////////////////////////////////////////////////////
#include "stdafx.h"				// 加入预编译头文件,jingzhou xu
#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
						(int)(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
	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; 
	
	// 加入bTemp临时变量便于删除,以防止内存泄漏,jingzhou xu.
	DWORD* bTemp = new DWORD[iWidth*iHeight];
	SetBitmap(&bih,bTemp);

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


	// 复制位到32位DIB图中,进行垂直映射复制(因为内存中位图为垂直反方向的),jingzhou xu.
	for(int i=0;i<iHeight;i++)
	{	
		int nVFlip = iHeight-i-1;
		for(int j=0;j<iWidth;j++)
		{
			pLine[(nVFlip*iWidth)+j] = FixColorRef(tempDC.GetPixel(j,i));
		}
	}

	delete bTemp;
	// 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
	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; 
	
	// 加入bTemp临时变量便于删除,以防止内存泄漏,jingzhou xu.
	DWORD* bTemp = new DWORD[iWidth*iHeight];
	SetBitmap(&bih,bTemp);

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


	// 复制位到32位DIB图中,进行垂直映射复制(因为内存中位图为垂直反方向的),jingzhou xu.
	for(int i=0;i<iHeight;i++)
	{	
		int nVFlip = iHeight-i-1;
		for(int j=0;j<iWidth;j++)
		{
			pLine[(nVFlip*iWidth)+j] = FixColorRef(tempDC.GetPixel(j,i));
		}
	}

	delete bTemp;
	// 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