// Dib.cpp: implementation of the CDib class. ////////////////////////////////////////////////////////////////////// // //用途:IconSnap所需调用的类 //功能:DIB设备无关位图基础操作类 //作者:徐景周 //日期:2001年9月 // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "Dib.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CDib::CDib() { } CDib::~CDib() { } /**************************************************************************** * * FUNCTION: FindDIBits * * PURPOSE: Locate the image bits in a CF_DIB format DIB. * * PARAMS: LPSTR lpbi - pointer to the CF_DIB memory block * * RETURNS: LPSTR - pointer to the image bits * * \****************************************************************************/ LPSTR CDib::FindDIBBits( LPSTR lpbi ) { return ( lpbi + *(LPDWORD)lpbi + PaletteSize( lpbi ) ); } /* End FindDIBits() *********************************************************/ /**************************************************************************** * * FUNCTION: DIBNumColors * * PURPOSE: Calculates the number of entries in the color table. * * PARAMS: LPSTR lpbi - pointer to the CF_DIB memory block * * RETURNS: WORD - Number of entries in the color table. * * \****************************************************************************/ WORD CDib::DIBNumColors( LPSTR lpbi ) { WORD wBitCount; DWORD dwClrUsed; dwClrUsed = ((LPBITMAPINFOHEADER) lpbi)->biClrUsed; if (dwClrUsed) return (WORD) dwClrUsed; wBitCount = ((LPBITMAPINFOHEADER) lpbi)->biBitCount; switch (wBitCount) { case 1: return 2; case 4: return 16; case 8: return 256; default:return 0; } return 0; } /* End DIBNumColors() ******************************************************/ /**************************************************************************** * * FUNCTION: PaletteSize * * PURPOSE: Calculates the number of bytes in the color table. * * PARAMS: LPSTR lpbi - pointer to the CF_DIB memory block * * RETURNS: WORD - number of bytes in the color table * * \****************************************************************************/ WORD CDib::PaletteSize( LPSTR lpbi ) { return ( DIBNumColors( lpbi ) * sizeof( RGBQUAD ) ); } /* End PaletteSize() ********************************************************/ /**************************************************************************** * * FUNCTION: BytesPerLine * * PURPOSE: Calculates the number of bytes in one scan line. * * PARAMS: LPBITMAPINFOHEADER lpBMIH - pointer to the BITMAPINFOHEADER * that begins the CF_DIB block * * RETURNS: DWORD - number of bytes in one scan line (DWORD aligned) * * \****************************************************************************/ DWORD CDib::BytesPerLine( LPBITMAPINFOHEADER lpBMIH ) { return WIDTHBYTES(lpBMIH->biWidth * lpBMIH->biPlanes * lpBMIH->biBitCount); } /* End BytesPerLine() ********************************************************/ /**************************************************************************** * * FUNCTION: ConvertDIBFormat * * PURPOSE: Creates a new DIB of the requested format, copies the source * image to the new DIB. * * PARAMS: LPBITMAPINFO lpSrcDIB - the source CF_DIB * UINT nWidth - width for new DIB * UINT nHeight - height for new DIB * UINT nbpp - bpp for new DIB * BOOL bStretch - TRUE to stretch source to dest * FALSE to take upper left of image * * RETURNS: LPBYTE - pointer to new CF_DIB memory block with new image * NULL on failure * * \****************************************************************************/ LPBYTE CDib::ConvertDIBFormat( LPBITMAPINFO lpSrcDIB, UINT nWidth, UINT nHeight, UINT nbpp, BOOL bStretch ) { LPBITMAPINFO lpbmi = NULL; LPBYTE lpSourceBits, lpTargetBits, lpResult; HDC hDC = NULL, hSourceDC, hTargetDC; HBITMAP hSourceBitmap, hTargetBitmap, hOldTargetBitmap, hOldSourceBitmap; DWORD dwSourceBitsSize, dwTargetBitsSize, dwTargetHeaderSize; // Allocate and fill out a BITMAPINFO struct for the new DIB // Allow enough room for a 256-entry color table, just in case dwTargetHeaderSize = sizeof( BITMAPINFO ) + ( 256 * sizeof( RGBQUAD ) ); lpbmi = (LPBITMAPINFO)malloc( dwTargetHeaderSize ); lpbmi->bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); lpbmi->bmiHeader.biWidth = nWidth; lpbmi->bmiHeader.biHeight = nHeight; lpbmi->bmiHeader.biPlanes = 1; lpbmi->bmiHeader.biBitCount = nbpp; lpbmi->bmiHeader.biCompression = BI_RGB; lpbmi->bmiHeader.biSizeImage = 0; lpbmi->bmiHeader.biXPelsPerMeter = 0; lpbmi->bmiHeader.biYPelsPerMeter = 0; lpbmi->bmiHeader.biClrUsed = 0; lpbmi->bmiHeader.biClrImportant = 0; // Fill in the color table if( ! CopyColorTable( lpbmi, (LPBITMAPINFO)lpSrcDIB ) ) { free( lpbmi ); return NULL; } // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em hDC = GetDC( NULL ); hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, (void ** )&lpTargetBits, NULL, 0 ); hSourceBitmap = CreateDIBSection( hDC, lpSrcDIB, DIB_RGB_COLORS,(void ** ) &lpSourceBits, NULL, 0 ); hSourceDC = CreateCompatibleDC( hDC ); hTargetDC = CreateCompatibleDC( hDC ); // Flip the bits on the source DIBSection to match the source DIB dwSourceBitsSize = lpSrcDIB->bmiHeader.biHeight * BytesPerLine(&(lpSrcDIB->bmiHeader)); dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine(&(lpbmi->bmiHeader)); memcpy( lpSourceBits, FindDIBBits((LPSTR)lpSrcDIB), dwSourceBitsSize ); // Select DIBSections into DCs hOldSourceBitmap =(HBITMAP ) SelectObject( hSourceDC, hSourceBitmap ); hOldTargetBitmap = (HBITMAP )SelectObject( hTargetDC, hTargetBitmap ); // Set the color tables for the DIBSections if( lpSrcDIB->bmiHeader.biBitCount <= 8 ) SetDIBColorTable( hSourceDC, 0, 1 << lpSrcDIB->bmiHeader.biBitCount, lpSrcDIB->bmiColors ); if( lpbmi->bmiHeader.biBitCount <= 8 ) SetDIBColorTable( hTargetDC, 0, 1 << lpbmi->bmiHeader.biBitCount, lpbmi->bmiColors ); // If we are asking for a straight copy, do it if( (lpSrcDIB->bmiHeader.biWidth==lpbmi->bmiHeader.biWidth) && (lpSrcDIB->bmiHeader.biHeight==lpbmi->bmiHeader.biHeight) ) { BitBlt( hTargetDC, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight, hSourceDC, 0, 0, SRCCOPY ); } else { // else, should we stretch it? if( bStretch ) { SetStretchBltMode( hTargetDC, COLORONCOLOR ); StretchBlt( hTargetDC, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight, hSourceDC, 0, 0, lpSrcDIB->bmiHeader.biWidth, lpSrcDIB->bmiHeader.biHeight, SRCCOPY ); } else { // or just take the upper left corner of the source BitBlt( hTargetDC, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight, hSourceDC, 0, 0, SRCCOPY ); } } // Clean up and delete the DCs SelectObject( hSourceDC, hOldSourceBitmap ); SelectObject( hSourceDC, hOldTargetBitmap ); DeleteDC( hSourceDC ); DeleteDC( hTargetDC ); ReleaseDC( NULL, hDC ); // Flush the GDI batch, so we can play with the bits GdiFlush(); // Allocate enough memory for the new CF_DIB, and copy bits lpResult =(LPBYTE ) malloc( dwTargetHeaderSize + dwTargetBitsSize ); memcpy( lpResult, lpbmi, dwTargetHeaderSize ); memcpy( FindDIBBits( (LPSTR)lpResult ), lpTargetBits, dwTargetBitsSize ); // final cleanup DeleteObject( hTargetBitmap ); DeleteObject( hSourceBitmap ); free( lpbmi ); return lpResult; } /* End ConvertDIBFormat() ***************************************************/ /**************************************************************************** * * FUNCTION: CopyColorTable * * PURPOSE: Copies the color table from one CF_DIB to another. * * PARAMS: LPBITMAPINFO lpTarget - pointer to target DIB * LPBITMAPINFO lpSource - pointer to source DIB * * RETURNS: BOOL - TRUE for success, FALSE for failure * * \****************************************************************************/ BOOL CDib::CopyColorTable( LPBITMAPINFO lpTarget, LPBITMAPINFO lpSource ) { // What we do depends on the target's color depth switch( lpTarget->bmiHeader.biBitCount ) { // 8bpp - need 256 entry color table case 8: if( lpSource->bmiHeader.biBitCount == 8 ) { // Source is 8bpp too, copy color table memcpy( lpTarget->bmiColors, lpSource->bmiColors, 256*sizeof(RGBQUAD) ); return TRUE; } else { // Source is != 8bpp, use halftone palette HPALETTE hPal; HDC hDC = GetDC( NULL ); PALETTEENTRY pe[256]; UINT i; hPal = CreateHalftonePalette( hDC ); ReleaseDC( NULL, hDC ); GetPaletteEntries( hPal, 0, 256, pe ); DeleteObject( hPal ); for(i=0;i<256;i++) { lpTarget->bmiColors[i].rgbRed = pe[i].peRed; lpTarget->bmiColors[i].rgbGreen = pe[i].peGreen; lpTarget->bmiColors[i].rgbBlue = pe[i].peBlue; lpTarget->bmiColors[i].rgbReserved = pe[i].peFlags; } return TRUE; } break; // end 8bpp // 4bpp - need 16 entry color table case 4: if( lpSource->bmiHeader.biBitCount == 4 ) { // Source is 4bpp too, copy color table memcpy( lpTarget->bmiColors, lpSource->bmiColors, 16*sizeof(RGBQUAD) ); return TRUE; } else { // Source is != 4bpp, use system palette HPALETTE hPal; PALETTEENTRY pe[256]; UINT i; hPal = (HPALETTE)GetStockObject( DEFAULT_PALETTE ); GetPaletteEntries( hPal, 0, 16, pe ); for(i=0;i<16;i++) { lpTarget->bmiColors[i].rgbRed = pe[i].peRed; lpTarget->bmiColors[i].rgbGreen = pe[i].peGreen; lpTarget->bmiColors[i].rgbBlue = pe[i].peBlue; lpTarget->bmiColors[i].rgbReserved = pe[i].peFlags; } return TRUE; } break; // end 4bpp // 1bpp - need 2 entry mono color table case 1: lpTarget->bmiColors[0].rgbRed = 0; lpTarget->bmiColors[0].rgbGreen = 0; lpTarget->bmiColors[0].rgbBlue = 0; lpTarget->bmiColors[0].rgbReserved = 0; lpTarget->bmiColors[1].rgbRed = 255; lpTarget->bmiColors[1].rgbGreen = 255; lpTarget->bmiColors[1].rgbBlue = 255; lpTarget->bmiColors[1].rgbReserved = 0; break; // end 1bpp // no color table for the > 8bpp modes case 32: case 24: case 16: default: return TRUE; break; } return TRUE; } /* End CopyColorTable() *****************************************************/ /**************************************************************************** * * FUNCTION: SetMonoDIBPixel * * PURPOSE: Sets/Clears a pixel in a 1bpp DIB by directly poking the bits. * * PARAMS: LPBYTE pANDBits - pointer to the 1bpp image bits * DWORD dwWidth - width of the DIB * DWORD dwHeight - height of the DIB * DWORD x - x location of pixel to set/clear * DWORD y - y location of pixel to set/clear * BOOL bWhite - TRUE to set pixel, FALSE to clear it * * RETURNS: void * * \****************************************************************************/ void CDib::SetMonoDIBPixel( LPBYTE pANDBits, DWORD dwWidth, DWORD dwHeight, DWORD x, DWORD y, BOOL bWhite ) { DWORD ByteIndex; BYTE BitNumber; // Find the byte on which this scanline begins ByteIndex = (dwHeight - y - 1) * WIDTHBYTES(dwWidth); // Find the byte containing this pixel ByteIndex += (x >> 3); // Which bit is it? BitNumber = (BYTE)( 7 - (x % 8) ); if( bWhite ) // Turn it on pANDBits[ByteIndex] |= (1<biSize != sizeof( BITMAPINFOHEADER ) ) { CloseHandle( hFile ); free( lpDIB ); MessageBox( NULL, "OS/2风格的位图不支持!", szFileName, MB_OK ); return NULL; } // How big are the elements? wPaletteSize = PaletteSize((LPSTR)lpDIB); dwBitsSize = ((LPBITMAPINFOHEADER)lpDIB)->biHeight * BytesPerLine((LPBITMAPINFOHEADER)lpDIB); // realloc to account for the total size of the DIB if( (lpTemp = (LPBYTE)realloc( lpDIB, sizeof( BITMAPINFOHEADER ) + wPaletteSize + dwBitsSize )) == NULL ) { CloseHandle( hFile ); MessageBox( NULL, "重新分配位图信息头所需内存失败!", szFileName, MB_OK ); free( lpDIB ); return NULL; } lpDIB = lpTemp; // If there is a color table, read it if( wPaletteSize != 0 ) { if( (!ReadFile( hFile, ((LPBITMAPINFO)lpDIB)->bmiColors, wPaletteSize, &dwBytes, NULL )) || (dwBytes!=wPaletteSize) ) { CloseHandle( hFile ); free( lpDIB ); MessageBox( NULL, "读位图颜色表出错!", szFileName, MB_OK ); return NULL; } } // Seek to the bits // checking against 0 in case some bogus app didn't set this element if( bfh.bfOffBits != 0 ) { if( SetFilePointer( hFile, bfh.bfOffBits, NULL, FILE_BEGIN ) == 0xffffffff ) { CloseHandle( hFile ); free( lpDIB ); MessageBox( NULL, "位图文件大小定位出错!", szFileName, MB_OK ); return NULL; } } // Read the image bits if( (!ReadFile( hFile, FindDIBBits((LPSTR)lpDIB), dwBitsSize, &dwBytes, NULL )) || (dwBytes!=dwBitsSize) ) { CloseHandle( hFile ); free( lpDIB ); MessageBox( NULL, "读位图文件出错!", szFileName, MB_OK ); return NULL; } // clean up CloseHandle( hFile ); return lpDIB; } /* End ReadBMPFile() ********************************************************/ /**************************************************************************** * * FUNCTION: WriteBMPFile * * PURPOSE: Writes a BMP file from CF_DIB format * * PARAMS: LPCTSTR szFileName - the name of the file to read * LPBYTE - pointer to the CF_DIB, NULL for failure * * RETURNS: BOOL - TRUE for success, FALSE for Failure * * \****************************************************************************/ BOOL CDib::WriteBMPFile( LPCTSTR szFileName, LPBYTE lpDIB ) { HANDLE hFile; BITMAPFILEHEADER bfh; DWORD dwBytes, dwBytesToWrite; LPBITMAPINFOHEADER lpbmih; // Open the file if( (hFile=CreateFile( szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE ) { MessageBox( NULL, "位图文件建立失败!", szFileName, MB_OK ); return FALSE; } bfh.bfType = 0x4d42; bfh.bfReserved1 = 0; bfh.bfReserved2 = 0; bfh.bfOffBits = sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER ) + PaletteSize((LPSTR) lpDIB ); bfh.bfSize = (bfh.bfOffBits + ((LPBITMAPINFOHEADER)lpDIB)->biHeight * BytesPerLine((LPBITMAPINFOHEADER)lpDIB))/4; // Write the header if( ( ! WriteFile( hFile, &bfh, sizeof(BITMAPFILEHEADER), &dwBytes, NULL ) ) || ( dwBytes != sizeof( BITMAPFILEHEADER ) ) ) { CloseHandle( hFile ); MessageBox( NULL, "写位图文件头出错!", szFileName, MB_OK ); return FALSE; } lpbmih = (LPBITMAPINFOHEADER)lpDIB; // lpbmih->biHeight /= 2; dwBytesToWrite = bfh.bfOffBits + (lpbmih->biHeight * BytesPerLine(lpbmih)); if( ( ! WriteFile( hFile, lpDIB, dwBytesToWrite, &dwBytes, NULL ) ) || ( dwBytes != dwBytesToWrite ) ) { CloseHandle( hFile ); MessageBox( NULL, "写位图文件出错!", szFileName, MB_OK ); return FALSE; } lpbmih->biHeight *= 2; CloseHandle( hFile ); return TRUE; } /* End WriteBMPFile() *******************************************************/