www.pudn.com > DiskInfo.tgz > DIBitmap.cpp


#include "stdafx.h" 
#include "DIBitmap.h" 
 
CBmpPalette::CBmpPalette( CDIBitmap* pBmp ) 
{ 
    ASSERT( pBmp ); 
    int cPaletteEntries = pBmp->GetPalEntries(); 
    int cPalette = sizeof(LOGPALETTE) + 
                   sizeof(PALETTEENTRY) * cPaletteEntries; 
    // Since the LOGPALETTE structure is open-ended, you 
    // must dynamically allocate it, rather than using one 
    // off the stack. 
    LOGPALETTE* pPal = (LOGPALETTE*)new BYTE[cPalette]; 
    RGBQUAD*    pColorTab = pBmp->GetColorTablePtr(); 
    pPal->palVersion = 0x300; 
    pPal->palNumEntries = cPaletteEntries; 
    // Roll through the color table, and add each color to 
    // the logical palette. 
    for( int ndx = 0; ndx < cPaletteEntries; ndx++ ) 
    { 
        pPal->palPalEntry[ndx].peRed   = pColorTab[ndx].rgbRed; 
        pPal->palPalEntry[ndx].peGreen = pColorTab[ndx].rgbGreen; 
        pPal->palPalEntry[ndx].peBlue  = pColorTab[ndx].rgbBlue; 
        pPal->palPalEntry[ndx].peFlags = NULL; 
    } 
    VERIFY( CreatePalette( pPal ) ); 
    delete [] (BYTE*)pPal; 
} 
 
#define PADWIDTH(x)	(((x)*8 + 31)  & (~31))/8 
 
 
CDIBitmap :: CDIBitmap() 
	: m_pInfo(0) 
	, m_pPixels(0) 
	, m_pPal(0) 
	, m_bIsPadded(FALSE) 
{ 
} 
 
CDIBitmap :: ~CDIBitmap() { 
    delete [] (BYTE*)m_pInfo; 
    delete [] m_pPixels; 
	delete m_pPal; 
} 
 
void CDIBitmap :: DestroyBitmap() { 
    delete [] (BYTE*)m_pInfo; 
    delete [] m_pPixels; 
	delete m_pPal; 
	m_pInfo = 0; 
	m_pPixels = 0; 
	m_pPal = 0; 
} 
 
BOOL CDIBitmap :: CreateFromBitmap( CDC * pDC, CBitmap * pSrcBitmap ) { 
	ASSERT_VALID(pSrcBitmap); 
	ASSERT_VALID(pDC); 
 
	try { 
		BITMAP bmHdr; 
 
		// Get the pSrcBitmap info 
		pSrcBitmap->GetObject(sizeof(BITMAP), &bmHdr); 
 
		// Reallocate space for the image data 
		if( m_pPixels ) { 
			delete [] m_pPixels; 
			m_pPixels = 0; 
		} 
 
		DWORD dwWidth; 
		if (bmHdr.bmBitsPixel > 8) 
			dwWidth = PADWIDTH(bmHdr.bmWidth * 3); 
		else 
			dwWidth = PADWIDTH(bmHdr.bmWidth); 
 
		m_pPixels = new BYTE[dwWidth*bmHdr.bmHeight]; 
		if( !m_pPixels ) 
			throw TEXT("could not allocate data storage\n"); 
 
		// Set the appropriate number of colors base on BITMAP structure info 
		WORD wColors; 
		switch( bmHdr.bmBitsPixel ) { 
			case 1 :  
				wColors = 2; 
				break; 
			case 4 : 
				wColors = 16; 
				break; 
			case 8 : 
				wColors = 256; 
				break; 
			default : 
				wColors = 0; 
				break; 
		} 
 
		// Re-allocate and populate BITMAPINFO structure 
		if( m_pInfo ) { 
			delete [] (BYTE*)m_pInfo; 
			m_pInfo = 0; 
		} 
 
		m_pInfo = (BITMAPINFO*)new BYTE[sizeof(BITMAPINFOHEADER) + wColors*sizeof(RGBQUAD)]; 
		if( !m_pInfo ) 
			throw TEXT("could not allocate BITMAPINFO struct\n"); 
 
		// Populate BITMAPINFO header info 
		m_pInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
		m_pInfo->bmiHeader.biWidth = bmHdr.bmWidth; 
		m_pInfo->bmiHeader.biHeight = bmHdr.bmHeight; 
		m_pInfo->bmiHeader.biPlanes = bmHdr.bmPlanes; 
		 
		 
		if( bmHdr.bmBitsPixel > 8 ) 
			m_pInfo->bmiHeader.biBitCount = 24; 
		else 
			m_pInfo->bmiHeader.biBitCount = bmHdr.bmBitsPixel; 
 
		m_pInfo->bmiHeader.biCompression = BI_RGB; 
		m_pInfo->bmiHeader.biSizeImage = ((((bmHdr.bmWidth * bmHdr.bmBitsPixel) + 31) & ~31) >> 3) * bmHdr.bmHeight; 
		m_pInfo->bmiHeader.biXPelsPerMeter = 0; 
		m_pInfo->bmiHeader.biYPelsPerMeter = 0; 
		m_pInfo->bmiHeader.biClrUsed = 0; 
		m_pInfo->bmiHeader.biClrImportant = 0; 
 
		// Now actually get the bits 
		int test = ::GetDIBits(pDC->GetSafeHdc(), (HBITMAP)pSrcBitmap->GetSafeHandle(), 
	 		0, (WORD)bmHdr.bmHeight, m_pPixels, m_pInfo, DIB_RGB_COLORS); 
 
		// check that we scanned in the correct number of bitmap lines 
		if( test != (int)bmHdr.bmHeight ) 
			throw TEXT("call to GetDIBits did not return full number of requested scan lines\n"); 
		 
		CreatePalette(); 
		m_bIsPadded = FALSE; 
#ifdef _DEBUG 
	} catch( TCHAR * psz ) { 
		TRACE1("CDIBitmap::CreateFromBitmap(): %s\n", psz); 
#else 
	} catch( TCHAR * ) { 
#endif 
		if( m_pPixels ) { 
			delete [] m_pPixels; 
			m_pPixels = 0; 
		} 
		if( m_pInfo ) { 
			delete [] (BYTE*) m_pInfo; 
			m_pInfo = 0; 
		} 
		return FALSE; 
	} 
 
	return TRUE; 
} 
 
 
BOOL CDIBitmap :: LoadResource(LPCTSTR pszID) { 
	HBITMAP hBmp = (HBITMAP)::LoadImage( 
						AfxGetInstanceHandle(),  
						pszID, IMAGE_BITMAP, 
						0,0, LR_CREATEDIBSECTION 
					); 
 
	if( hBmp == 0 )  
		return FALSE; 
 
	CBitmap bmp; 
	bmp.Attach(hBmp); 
	CClientDC cdc( CWnd::GetDesktopWindow() ); 
	BOOL bRet = CreateFromBitmap( &cdc, &bmp ); 
	bmp.DeleteObject(); 
 
	CreatePalette(); 
	return bRet; 
} 
 
 
BOOL CDIBitmap :: Load( CFile* pFile ) { 
    ASSERT( pFile ); 
    BOOL fReturn = TRUE; 
    try { 
        delete [] (BYTE*)m_pInfo; 
        delete [] m_pPixels; 
        m_pInfo = 0; 
        m_pPixels = 0; 
        DWORD       dwStart = pFile->GetPosition(); 
        // 
        // Check to make sure we have a bitmap. The first two bytes must 
        // be 'B' and 'M'. 
        BITMAPFILEHEADER fileHeader; 
        pFile->Read(&fileHeader, sizeof(fileHeader)); 
        if( fileHeader.bfType != 0x4D42 ) 
            throw TEXT("Error:Unexpected file type, not a DIB\n"); 
 
        BITMAPINFOHEADER infoHeader; 
        pFile->Read( &infoHeader, sizeof(infoHeader) ); 
        if( infoHeader.biSize != sizeof(infoHeader) ) 
            throw TEXT("Error:OS2 PM BMP Format not supported\n"); 
 
        // Store the sizes of the DIB structures 
        int cPaletteEntries = GetPalEntries( infoHeader ); 
        int cColorTable = 256 * sizeof(RGBQUAD); 
        int cInfo = sizeof(BITMAPINFOHEADER) + cColorTable; 
        int cPixels = fileHeader.bfSize - fileHeader.bfOffBits; 
        // 
        // Allocate space for a new bitmap info header, and copy 
        // the info header that was loaded from the file. Read the 
        // the file and store the results in the color table. 
        m_pInfo = (BITMAPINFO*)new BYTE[cInfo]; 
        memcpy( m_pInfo, &infoHeader, sizeof(BITMAPINFOHEADER) ); 
        pFile->Read( ((BYTE*)m_pInfo) + sizeof(BITMAPINFOHEADER), 
                     cColorTable ); 
        // 
        // Allocate space for the pixel area, and load the pixel 
        // info from the file. 
        m_pPixels = new BYTE[cPixels]; 
        pFile->Seek(dwStart + fileHeader.bfOffBits, CFile::begin); 
        pFile->Read( m_pPixels, cPixels ); 
		CreatePalette(); 
		m_bIsPadded = TRUE; 
#ifdef _DEBUG 
    } catch( TCHAR * psz ) { 
		TRACE( psz ); 
#else 
    } catch( TCHAR * ) { 
#endif 
        fReturn = FALSE; 
    } 
    return fReturn; 
} 
 
BOOL CDIBitmap :: Load( const CString & strFilename ) { 
	CFile file; 
	if( file.Open( strFilename, CFile::modeRead ) ) 
		return Load( &file ); 
	return FALSE; 
} 
 
BOOL CDIBitmap :: Save( const CString & strFileName ) { 
    ASSERT(! strFileName.IsEmpty()); 
 
    CFile File; 
 
	if( !File.Open(strFileName, CFile::modeCreate|CFile::modeWrite) ) { 
	    TRACE1("CDIBitmap::Save(): Failed to open file %s for writing.\n", LPCSTR(strFileName)); 
	    return FALSE; 
	} 
 
    return Save( &File ); 
}       
 
 
// Does not open or close pFile.  Assumes 
// caller will do it. 
BOOL CDIBitmap :: Save( CFile * pFile ) { 
	ASSERT_VALID( pFile ); 
    ASSERT( m_pInfo ); 
    ASSERT( m_pPixels ); 
     
    BITMAPFILEHEADER bmfHdr; 
     
	DWORD dwPadWidth = PADWIDTH(GetWidth()); 
 
	// Make sure bitmap data is in padded format 
	PadBits(); 
     
    bmfHdr.bfType = 0x4D42; 
    // initialize to BitmapInfo size 
    DWORD dwImageSize= m_pInfo->bmiHeader.biSize; 
	// Add in palette size 
    WORD wColors = GetColorCount(); 
    WORD wPaletteSize = (WORD)(wColors*sizeof(RGBQUAD)); 
    dwImageSize += wPaletteSize; 
     
    // Add in size of actual bit array 
    dwImageSize += PADWIDTH((GetWidth()) * DWORD(m_pInfo->bmiHeader.biBitCount)/8) * GetHeight(); 
    m_pInfo->bmiHeader.biSizeImage = 0; 
    bmfHdr.bfSize = dwImageSize + sizeof(BITMAPFILEHEADER); 
    bmfHdr.bfReserved1 = 0; 
    bmfHdr.bfReserved2 = 0; 
    bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + m_pInfo->bmiHeader.biSize + wPaletteSize; 
    pFile->Write(&bmfHdr, sizeof(BITMAPFILEHEADER)); 
     
    pFile->Write(m_pInfo, sizeof(BITMAPINFO) + (wColors-1)*sizeof(RGBQUAD)); 
    pFile->WriteHuge(m_pPixels, 
		DWORD((dwPadWidth*(DWORD)m_pInfo->bmiHeader.biBitCount*GetHeight())/8) ); 
   
	return TRUE; 
} 
 
BOOL CDIBitmap :: CreatePalette() { 
	if( m_pPal ) 
		delete m_pPal; 
	m_pPal = 0; 
	ASSERT( m_pInfo ); 
	// We only need a palette, if there are <= 256 colors. 
	// otherwise we would bomb the memory. 
	if( m_pInfo->bmiHeader.biBitCount <= 8 ) 
		m_pPal = new CBmpPalette(this); 
	return m_pPal ? TRUE : FALSE; 
} 
 
void CDIBitmap :: ClearPalette() { 
	if( m_pPal ) 
		delete m_pPal; 
	m_pPal = 0; 
} 
 
void CDIBitmap :: DrawDIB( CDC* pDC, int x, int y ) { 
	DrawDIB( pDC, x, y, GetWidth(), GetHeight() ); 
} 
 
// 
// DrawDib uses StretchDIBits to display the bitmap. 
void CDIBitmap :: DrawDIB( CDC* pDC, int x, int y, int width, int height ) { 
    ASSERT( pDC ); 
    HDC     hdc = pDC->GetSafeHdc(); 
 
	CPalette * pOldPal = 0; 
 
	if( m_pPal ) { 
		pOldPal = pDC->SelectPalette( m_pPal, FALSE ); 
		pDC->RealizePalette(); 
		// Make sure to use the stretching mode best for color pictures 
		pDC->SetStretchBltMode(COLORONCOLOR); 
	} 
 
    if( m_pInfo ) 
        StretchDIBits( hdc, 
                       x, 
                       y, 
                       width, 
                       height, 
                       0, 
                       0, 
                       GetWidth(), 
                       GetHeight(), 
                       GetPixelPtr(), 
                       GetHeaderPtr(), 
                       DIB_RGB_COLORS, 
                       SRCCOPY ); 
	 
	if( m_pPal ) 
		pDC->SelectPalette( pOldPal, FALSE ); 
} 
 
int CDIBitmap :: DrawDIB( CDC * pDC, CRect & rectDC, CRect & rectDIB ) { 
    ASSERT( pDC ); 
    HDC     hdc = pDC->GetSafeHdc(); 
 
	CPalette * pOldPal = 0; 
 
	if( m_pPal ) { 
		pOldPal = pDC->SelectPalette( m_pPal, FALSE ); 
		pDC->RealizePalette(); 
		// Make sure to use the stretching mode best for color pictures 
		pDC->SetStretchBltMode(COLORONCOLOR); 
	} 
 
	int nRet = 0; 
 
    if( m_pInfo ) 
		nRet =	SetDIBitsToDevice( 
					hdc,					// device 
					rectDC.left,			// DestX 
					rectDC.top,				// DestY 
					rectDC.Width(),			// DestWidth 
					rectDC.Height(),		// DestHeight 
					rectDIB.left,			// SrcX 
					rectDIB.top,			// SrcY 
					0,						// StartScan 
					GetHeight(),			// NumScans 
					GetPixelPtr(),			// color data 
					GetHeaderPtr(),			// header data 
					DIB_RGB_COLORS			// color usage 
				); 
	 
	if( m_pPal ) 
		pDC->SelectPalette( pOldPal, FALSE ); 
 
	return nRet; 
} 
 
void	CDIBitmap :: FillDIB( CDC * pDC, CRect rc) 
{ 
	ASSERT( pDC ); 
    HDC     hdc = pDC->GetSafeHdc(); 
 
	CPalette * pOldPal = 0; 
 
	if( m_pPal ) { 
		pOldPal = pDC->SelectPalette( m_pPal, FALSE ); 
		pDC->RealizePalette(); 
		// Make sure to use the stretching mode best for color pictures 
		//pDC->SetStretchBltMode(COLORONCOLOR); 
	} 
 
	/*CBrush br; 
	br.CreateDIBPatternBrush(GetHeaderPtr(), DIB_RGB_COLORS); 
	pDC->FillRect(&rc, &br);*/ 
 
	if( m_pInfo ) 
	{ 
		// tile the bitmap 
		int x=rc.left, y=rc.top; 
		int h = rc.bottom; 
		int w = rc.right; 
		int wBmp = GetWidth(); 
		int hBmp = GetHeight(); 
		int wPart = (rc.Width()+wBmp) % wBmp; 
		int hPart = (rc.Height()+hBmp) % hBmp; 
		int wCur, hCur; 
 
		BYTE* pPtr = GetPixelPtr(); 
		BITMAPINFO* pHeader = GetHeaderPtr(); 
	 
		while(y < h)  
		{ 
			hCur = (y + hBmp <= h)? hBmp : hPart; 
 
			x = rc.left; 
				 
			while(x < w)  
			{ 
				wCur = (x + wBmp <= w)? wBmp : wPart; 
 
				StretchDIBits( hdc, 
					x, y, wCur, hCur, 
					0, 0, wCur, hCur, 
					pPtr, pHeader, DIB_RGB_COLORS, SRCCOPY ); 
				x += wCur; 
				 
			} 
			 
			y += hCur; 
		} 
	} 
     
	if( m_pPal ) 
		pDC->SelectPalette( pOldPal, FALSE ); 
} 
 
BITMAPINFO * CDIBitmap :: GetHeaderPtr() const { 
    ASSERT( m_pInfo ); 
    ASSERT( m_pPixels ); 
    return m_pInfo; 
} 
 
RGBQUAD * CDIBitmap :: GetColorTablePtr() const { 
    ASSERT( m_pInfo ); 
    ASSERT( m_pPixels ); 
    RGBQUAD* pColorTable = 0; 
    if( m_pInfo != 0 ) { 
        int cOffset = sizeof(BITMAPINFOHEADER); 
        pColorTable = (RGBQUAD*)(((BYTE*)(m_pInfo)) + cOffset); 
    } 
    return pColorTable; 
} 
 
BYTE * CDIBitmap :: GetPixelPtr() const { 
    ASSERT( m_pInfo ); 
    ASSERT( m_pPixels ); 
    return m_pPixels; 
} 
 
int CDIBitmap :: GetWidth() const { 
    ASSERT( m_pInfo ); 
    return m_pInfo->bmiHeader.biWidth; 
} 
 
int CDIBitmap :: GetHeight() const { 
    ASSERT( m_pInfo ); 
    return m_pInfo->bmiHeader.biHeight; 
} 
 
WORD CDIBitmap :: GetColorCount() const { 
	ASSERT( m_pInfo ); 
 
	switch( m_pInfo->bmiHeader.biBitCount )	{ 
		case 1:		return 2; 
		case 4:		return 16; 
		case 8:		return 256; 
		default:	return 0; 
	} 
} 
 
int CDIBitmap :: GetPalEntries() const { 
    ASSERT( m_pInfo ); 
    return GetPalEntries( *(BITMAPINFOHEADER*)m_pInfo ); 
} 
 
int CDIBitmap :: GetPalEntries( BITMAPINFOHEADER& infoHeader ) const { 
	int nReturn; 
	if( infoHeader.biClrUsed == 0 ) 
		nReturn = ( 1 << infoHeader.biBitCount ); 
	else 
		nReturn = infoHeader.biClrUsed; 
 
	return nReturn; 
} 
 
DWORD CDIBitmap :: GetBitsPerPixel() const { 
	ASSERT( m_pInfo ); 
	return m_pInfo->bmiHeader.biBitCount; 
} 
 
DWORD CDIBitmap :: LastByte( DWORD dwBitsPerPixel, DWORD dwPixels ) const { 
	register DWORD dwBits = dwBitsPerPixel * dwPixels; 
	register DWORD numBytes  = dwBits / 8; 
	register DWORD extraBits = dwBits - numBytes * 8; 
    return (extraBits % 8) ? numBytes+1 : numBytes; 
} 
 
 
DWORD CDIBitmap :: GetBytesPerLine( DWORD dwBitsPerPixel, DWORD dwWidth ) const { 
	DWORD dwBits = dwBitsPerPixel * dwWidth; 
 
	if( (dwBits % 32) == 0 ) 
		return (dwBits/8);  // already DWORD aligned, no padding needed 
	 
	DWORD dwPadBits = 32 - (dwBits % 32); 
	return (dwBits/8 + dwPadBits/8 + (((dwPadBits % 8) > 0) ? 1 : 0)); 
} 
 
BOOL CDIBitmap :: PadBits() { 
	if( m_bIsPadded ) 
		return TRUE; 
 
    // dwAdjust used when bits per pixel spreads over more than 1 byte 
    DWORD dwAdjust = 1, dwOffset = 0, dwPadOffset=0; 
	BOOL bIsOdd = FALSE; 
     
	dwPadOffset = GetBytesPerLine(GetBitsPerPixel(), GetWidth()); 
	dwOffset = LastByte(GetBitsPerPixel(), GetWidth()); 
 
	if( dwPadOffset == dwOffset ) 
		return TRUE; 
 
    BYTE * pTemp = new BYTE [GetWidth()*dwAdjust]; 
    if( !pTemp ) { 
		TRACE1("CDIBitmap::PadBits(): could not allocate row of width %d.\n", GetWidth()); 
		return FALSE; 
	} 
     
    // enough space has already been allocated for the bit array to 
    // include the padding, so we just need to shift rows around. 
    // This will pad each "row" on a DWORD alignment. 
 
    for( DWORD row = GetHeight()-1 ; row>0 ; --row ) { 
	    CopyMemory((void *)pTemp, (const void *)(m_pPixels + (row*dwOffset)), dwOffset ); 
	    CopyMemory((void *)(m_pPixels + (row*dwPadOffset)), (const void *)pTemp, dwOffset); 
	} 
    delete [] pTemp; 
     
    return TRUE; 
} 
 
BOOL CDIBitmap::UnPadBits() { 
	if( ! m_bIsPadded ) 
		return TRUE; 
 
	DWORD dwAdjust = 1; 
	BOOL bIsOdd = FALSE; 
 
	DWORD dwPadOffset = GetBytesPerLine(GetBitsPerPixel(), GetWidth()); 
	DWORD dwOffset = LastByte(GetBitsPerPixel(), GetWidth()); 
 
    BYTE * pTemp = new BYTE [dwOffset]; 
    if( !pTemp ) { 
		TRACE1("CDIBitmap::UnPadBits() could not allocate row of width %d.\n", GetWidth()); 
		return FALSE; 
	} 
 
    // enough space has already been allocated for the bit array to 
    // include the padding, so we just need to shift rows around. 
    for( DWORD row=1 ; row < DWORD(GetHeight()); ++row ) { 
		CopyMemory((void *)pTemp, (const void *)(m_pPixels + row*(dwPadOffset)), dwOffset); 
	    CopyMemory((void *)(m_pPixels + (row*dwOffset)), (const void *)pTemp, dwOffset); 
	} 
 
    delete [] pTemp; 
     
    return TRUE; 
}