nav-left cat-right
cat-right
Matthias Pospiech > Programming > C++ > MFC > Open, Save and Read Bitmaps

Open, Save and Read Bitmaps

The task to read a bitmap into a data array, show this bitmap and save an array or dc to a file sound like a simple task but is rather difficult in reality if one uses MFC. Maybe I am totaly wrong when stating this, but that is my result of one week searching for solutions and implementing my own ones.

What I have achieved can be as an example project.

  bitmapopenandsave.zip (61,0 KiB)

The idea of these classes is to open a bitmap into a dc and to save the content of a dc into a bitmap.

CBitmapOp

#pragma once
 
#include <vector>
using namespace std; 
 
class CBitmapOp
{
public:
	CBitmapOp(void);
	~CBitmapOp(void);
 
public:
	BOOL SaveDcToBitmapFile( LPCTSTR szFile, CPaintDC* pDC, CRect rect);
	BOOL WriteDIBToFile( LPCTSTR szFile, HANDLE hDIB);
	HANDLE DDBToDIB( CBitmap& bitmap, DWORD dwCompression, CPalette* pPal ) ;
	BOOL LoadBitmapFileToCBitmap( LPCTSTR sBMPFile, CBitmap& bitmap, CPalette *pPal );
	BOOL GetBitmapPixels(CBitmap& bitmap);
	void SetBitmapDataSize(const int x, const int y);
	vector</vector><vector <COLORREF> > GetBitmapArray();
private:
	int m_PixelNumberX;
	int m_PixelNumberY;
	vector</vector><vector <COLORREF> > BitmapData; 
};
</vector>
#include "StdAfx.h"
#include "BitmapOp.h"
 
 
CBitmapOp::CBitmapOp(void)
{
	m_PixelNumberX=0;
	m_PixelNumberY=0;	
}
 
CBitmapOp::~CBitmapOp(void)
{
}
 
////////////////////////////////////////////////////////////////////
// www.codeguru.com
// Writing a window image to a BMP file 
// Zafir Anjum
////////////////////////////////////////////////////////////////////
// To save the image of a window to a BMP file, you have to draw the image
// to a compatible memory device context, convert the bitmap associated with
// the memory DC to a device-independent bitmap and then write the DIB to a file.
//	In previous sections, we have already covered how to get a DIB from a 
// device-dependent bitmap and how to save a DIB to a file. The WriteWindowToDIB()
// function uses the DDBToDIB() and the WriteDIB() functions to accomplish its task.
//	  However, this is just half of the job; the other half is to get the logical
// palette that can be used with the bitmap. The bitmap won't display properly if
// it needs a logical palette and one is not available. Depending on the display 
// settings we don't always need a logical palette. We use GetDeviceCaps() to determine
// whether a display device supports palette operation. For instance, if the display is 
// set for true color then it doesn't support a palette. We allocate enough memory for a
// LOGPALETTE structure and then use the GetSystemPaletteEntries() to fill the color table.
// Now we can create the logical palette.
//	  The function then uses the DDBToDIB() and the WriteDIB() functions to complete the job.
// You should note that if the window is partially or completely covered by other windows then
// the saved bitmap will show parts of these other windows too. 
////////////////////////////////////////////////////////////////////
// changed by Matthias Pospiech (2007), so that it works directly with a DC.
////////////////////////////////////////////////////////////////////
BOOL CBitmapOp::SaveDcToBitmapFile( LPCTSTR szFile, CPaintDC* pDC, CRect rect)
{
	CBitmap 	bitmap;
	//CWindowDC	dc(pWnd);
	CDC 		memDC;
 
	memDC.CreateCompatibleDC(pDC);
 
	bitmap.CreateCompatibleBitmap(pDC, rect.Width(),rect.Height() );
 
	CBitmap* pOldBitmap = memDC.SelectObject(&bitmap);
	memDC.BitBlt(0, 0, rect.Width(),rect.Height(), pDC, 0, 0, SRCCOPY);
 
	// Create logical palette if device support a palette
	CPalette pal;
	if( pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE )
	{
		UINT nSize = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * 256);
		LOGPALETTE *pLP = (LOGPALETTE *) new BYTE[nSize];
		pLP->palVersion = 0x300;
 
		pLP->palNumEntries = GetSystemPaletteEntries(*pDC, 0, 255, pLP->palPalEntry );
 
		// Create the palette
		pal.CreatePalette( pLP );
 
		delete[] pLP;
	}
 
	memDC.SelectObject(pOldBitmap);
 
	// Convert the bitmap to a DIB
	HANDLE hDIB = DDBToDIB( bitmap, BI_RGB, &pal );
 
	if( hDIB == NULL )
		return FALSE;
 
	// Write it to file
	WriteDIBToFile( szFile, hDIB );
 
	// Free the memory allocated by DDBToDIB for the DIB
	GlobalFree( hDIB );
	return TRUE;
}
 
 
////////////////////////////////////////////////////////////////////
// www.codeguru.com
// Writing a bitmap to a BMP file 
// Zafir Anjum
////////////////////////////////////////////////////////////////////
 
// WriteDIB		- Writes a DIB to file
// Returns		- TRUE on success
// szFile		- Name of file to write to
// hDIB			- Handle of the DIB
BOOL CBitmapOp::WriteDIBToFile( LPCTSTR szFile, HANDLE hDIB)
{
	BITMAPFILEHEADER	hdr;
	LPBITMAPINFOHEADER	lpbi;
 
	if (!hDIB)
		return FALSE;
 
	CFile file;
	if( !file.Open( szFile, CFile::modeWrite|CFile::modeCreate) )
		return FALSE;
 
	lpbi = (LPBITMAPINFOHEADER)hDIB;
 
	int nColors = 1 < < lpbi->biBitCount;
 
	// Fill in the fields of the file header 
	hdr.bfType		= ((WORD) ('M' < < 8) | 'B');	// is always "BM"
	hdr.bfSize		= (DWORD)(GlobalSize (hDIB) + sizeof( hdr ));
	hdr.bfReserved1 = 0;
	hdr.bfReserved2 = 0;
	hdr.bfOffBits	= (DWORD) (sizeof( hdr ) + lpbi->biSize + nColors * sizeof(RGBQUAD));
 
	// Write the file header 
	file.Write( &hdr, sizeof(hdr) );
 
	// Write the DIB header and the bits 
	file.Write( lpbi, (UINT)GlobalSize(hDIB) );
 
	return TRUE;
}
 
////////////////////////////////////////////////////////////////////
// www.codeguru.com
// Writing a window image to a BMP file 
// Zafir Anjum
////////////////////////////////////////////////////////////////////
//
// A device-dependent bitmap (DDB) is structured in such a way that it matches 
// very closely the form in which the bitmap is finally displayed by the device driver.
// For this reason the DDB is unlikely to be compatible with other display devices.
// A device-independent bitmap (DIB) on the other hand has a logical layout and can be
// decifered by any device driver. The disadvantage with DIB though is that it is much 
// slower to render onto the device as compared to a DDB.
//
// One situation where we need to convert a DDB to a DIB is when we want to save the bitmap
// to a file. The BMP files are infact just composed of a short header followed by
// information in a DIB.
//
// The steps involved in creating the DIB are:
//   1. Initialize a BITMAPINFOHEADER data structure. We use information in the bitmap 
//	  to determine the widht, height and the bit count. The compression field is set 
//	  based on the argument passed into the function. It's best to use the BI_RGB 
//	  compression for greater portability and faster rendering.
//   2. Select and realize the logical palette associated with the bitmap. This 
//      is passed in as the last argument to the function. If a palette is not provided 
//	  use the default palette.
//   3. Determine the number of bytes required for the bitmap bits. First allocate 
//      enough memory for BITMAPINFOHEADER and the color table and then call GetDIBits() 
//	  to size. Supplying a NULL for the lpBits parameter instructs GetDIBits() to 
//	  biSizeImage field in the bitmapinfoheader. Sometimes, this does not work 
//	  (depending on the device driver) in which case we make our own estimate. 
//	  We can compute this since we already know the width, height, the number of bits 
//	  per pixel and the fact that each row of pixels will occupy a multiple 
//	  of 4 bytes (32 bits).
//   4. Given the final size of the bitmap bits, reallocate the memory block to hold
//      the bitmapinfoheader, the color table and the bitmap bits.
//   5. Finally call the GetDIBits() again to get the bitmap bits.
////////////////////////////////////////////////////////////////////
 
// DDBToDIB		- Creates a DIB from a DDB
// bitmap		- Device dependent bitmap
// dwCompression	- Type of compression - see BITMAPINFOHEADER
// pPal			- Logical palette
HANDLE CBitmapOp::DDBToDIB( CBitmap& bitmap, DWORD dwCompression, CPalette* pPal ) 
{
	BITMAP			bm;
	BITMAPINFOHEADER	bi;
	LPBITMAPINFOHEADER 	lpbi;
	DWORD			dwLen;
	HANDLE			hDIB;
	HANDLE			handle;
	HDC 			hDC;
	HPALETTE		hPal;
 
 
	ASSERT( bitmap.GetSafeHandle() );
 
	// The function has no arg for bitfields
	if( dwCompression == BI_BITFIELDS )
		return NULL;
 
	// If a palette has not been supplied use defaul palette
	hPal = (HPALETTE) pPal->GetSafeHandle();
	if (hPal==NULL)
		hPal = (HPALETTE) GetStockObject(DEFAULT_PALETTE);
 
	// Get bitmap information
	bitmap.GetObject(sizeof(bm),(LPSTR)&bm);
 
	// Initialize the bitmapinfoheader
	bi.biSize		= sizeof(BITMAPINFOHEADER);
	bi.biWidth		= bm.bmWidth;
	bi.biHeight 		= bm.bmHeight;
	bi.biPlanes 		= 1;
	bi.biBitCount		= bm.bmPlanes * bm.bmBitsPixel;
	bi.biCompression	= dwCompression;
	bi.biSizeImage		= 0;
	bi.biXPelsPerMeter	= 0;
	bi.biYPelsPerMeter	= 0;
	bi.biClrUsed		= 0;
	bi.biClrImportant	= 0;
 
	// Compute the size of the  infoheader and the color table
	int nColors = (1 < < bi.biBitCount);
	if( nColors > 256 ) 
		nColors = 0;
	dwLen  = bi.biSize + nColors * sizeof(RGBQUAD);
 
	// We need a device context to get the DIB from
	hDC = GetDC(NULL);
	hPal = SelectPalette(hDC,hPal,FALSE);
	RealizePalette(hDC);
 
	// Allocate enough memory to hold bitmapinfoheader and color table
	hDIB = GlobalAlloc(GMEM_FIXED,dwLen);
 
	if (!hDIB){
		SelectPalette(hDC,hPal,FALSE);
		ReleaseDC(NULL,hDC);
		return NULL;
	}
 
	lpbi = (LPBITMAPINFOHEADER)hDIB;
 
	*lpbi = bi;
 
	// Call GetDIBits with a NULL lpBits param, so the device driver 
	// will calculate the biSizeImage field 
	GetDIBits(hDC, (HBITMAP)bitmap.GetSafeHandle(), 0L, (DWORD)bi.biHeight,
			(LPBYTE)NULL, (LPBITMAPINFO)lpbi, (DWORD)DIB_RGB_COLORS);
 
	bi = *lpbi;
 
	// 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 (bi.biSizeImage == 0){
		bi.biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) 
						* bi.biHeight;
 
		// If a compression scheme is used the result may infact be larger
		// Increase the size to account for this.
		if (dwCompression != BI_RGB)
			bi.biSizeImage = (bi.biSizeImage * 3) / 2;
	}
 
	// Realloc the buffer so that it can hold all the bits
	dwLen += bi.biSizeImage;
	if (handle = GlobalReAlloc(hDIB, dwLen, GMEM_MOVEABLE))
		hDIB = handle;
	else{
		GlobalFree(hDIB);
 
		// Reselect the original palette
		SelectPalette(hDC,hPal,FALSE);
		ReleaseDC(NULL,hDC);
		return NULL;
	}
 
	// Get the bitmap bits
	lpbi = (LPBITMAPINFOHEADER)hDIB;
 
	// FINALLY get the DIB
	BOOL bGotBits = GetDIBits( hDC, (HBITMAP)bitmap.GetSafeHandle(),
				0L,				// Start scan line
				(DWORD)bi.biHeight,		// # of scan lines
				(LPBYTE)lpbi 			// address for bitmap bits
				+ (bi.biSize + nColors * sizeof(RGBQUAD)),
				(LPBITMAPINFO)lpbi,		// address of bitmapinfo
				(DWORD)DIB_RGB_COLORS);		// Use RGB for color table
 
	if( !bGotBits )
	{
		GlobalFree(hDIB);
 
		SelectPalette(hDC,hPal,FALSE);
		ReleaseDC(NULL,hDC);
		return NULL;
	}
 
	SelectPalette(hDC,hPal,FALSE);
	ReleaseDC(NULL,hDC);
	return hDIB;
}
 
////////////////////////////////////////////////////////////////////
// www.codeguru.com
// Creating a bitmap object from a BMP file
// Zafir Anjum
////////////////////////////////////////////////////////////////////
//  Create the bitmap object
// The LoadBMPImage() function given below will read in a BMP file,
// create a device dependent bitmap of it and attach it to the CBitmap 
// object passed in as an argument. It can also create a logical palette 
// if a pointer to a CPalette object is supplied.
//	This function basically reads in the BMP file, creates a logical palette
// and calls CreateDIBitmap() to create the GDI bitmap object. Note that when
// you supply a pointer to a CPalette object, the function creates a logical 
// palette and uses the palette when creating the bitmap object. 
////////////////////////////////////////////////////////////////////
 
// LoadBMPImage	- Loads a BMP file and creates a bitmap GDI object
//		  also creates logical palette for it.
// Returns	- TRUE for success
// sBMPFile	- Full path of the BMP file
// bitmap	- The bitmap object to initialize
// pPal		- Will hold the logical palette. Can be NULL
BOOL CBitmapOp::LoadBitmapFileToCBitmap( LPCTSTR sBMPFile, CBitmap& bitmap, CPalette *pPal )
{
	CFile file;
	if( !file.Open( sBMPFile, CFile::modeRead) )
		return FALSE;
 
	BITMAPFILEHEADER bmfHeader;
 
	// Read file header
	if (file.Read((LPSTR)&bmfHeader, sizeof(bmfHeader)) != sizeof(bmfHeader))
		return FALSE;
 
	// File type should be 'BM'
	if (bmfHeader.bfType != ((WORD) ('M' < < 8) | 'B'))
		return FALSE;
 
	// Get length of the remainder of the file and allocate memory
	DWORD nPackedDIBLen = (DWORD)(file.GetLength() - sizeof(BITMAPFILEHEADER));
	HGLOBAL hDIB = ::GlobalAlloc(GMEM_FIXED, nPackedDIBLen);
	if (hDIB == 0)
		return FALSE;
 
	// Read the remainder of the bitmap file.
	if (file.Read((LPSTR)hDIB, nPackedDIBLen) != nPackedDIBLen )
	{
		::GlobalFree(hDIB);
		return FALSE;
	}
 
 
	BITMAPINFOHEADER &bmiHeader = *(LPBITMAPINFOHEADER)hDIB ;
	BITMAPINFO &bmInfo = *(LPBITMAPINFO)hDIB ;
 
	// If bmiHeader.biClrUsed is zero we have to infer the number
	// of colors from the number of bits used to specify it.
	int nColors = bmiHeader.biClrUsed ? bmiHeader.biClrUsed :
						1 << bmiHeader.biBitCount;
 
	LPVOID lpDIBBits;
	if( bmInfo.bmiHeader.biBitCount > 8 )
		lpDIBBits = (LPVOID)((LPDWORD)(bmInfo.bmiColors + bmInfo.bmiHeader.biClrUsed) +
			((bmInfo.bmiHeader.biCompression == BI_BITFIELDS) ? 3 : 0));
	else
		lpDIBBits = (LPVOID)(bmInfo.bmiColors + nColors);
 
	// Create the logical palette
	if( pPal != NULL )
	{
		// Create the palette
		if( nColors < = 256 )
		{
			UINT nSize = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * nColors);
			LOGPALETTE *pLP = (LOGPALETTE *) new BYTE[nSize];
 
			pLP->palVersion = 0x300;
			pLP->palNumEntries = nColors;
 
			for( int i=0; i < nColors; i++)
			{
				pLP->palPalEntry[i].peRed = bmInfo.bmiColors[i].rgbRed;
				pLP->palPalEntry[i].peGreen = bmInfo.bmiColors[i].rgbGreen;
				pLP->palPalEntry[i].peBlue = bmInfo.bmiColors[i].rgbBlue;
				pLP->palPalEntry[i].peFlags = 0;
			}
 
			pPal->CreatePalette( pLP );
 
			delete[] pLP;
		}
	}
 
	CClientDC dc(NULL);
	CPalette* pOldPalette = NULL;
	if( pPal )
	{
		pOldPalette = dc.SelectPalette( pPal, FALSE );
		dc.RealizePalette();
	}
 
	HBITMAP hBmp = CreateDIBitmap( dc.m_hDC,		// handle to device context 
				&bmiHeader,	// pointer to bitmap size and format data 
				CBM_INIT,	// initialization flag 
				lpDIBBits,	// pointer to initialization data 
				&bmInfo,	// pointer to bitmap color-format data 
				DIB_RGB_COLORS);		// color-data usage 
	bitmap.Attach( hBmp );
 
	if( pOldPalette )
		dc.SelectPalette( pOldPalette, FALSE );
 
	::GlobalFree(hDIB);
	return TRUE;
}
 
////////////////////////////////////////////////////////////////////
// www.codeguru.com
// Original: Print transparent bitmaps via regions
// Wes Rogers
////////////////////////////////////////////////////////////////////
//Load pixels into BitmapData array 
//
//Returns true if OK, else false
//
//This code is based on the BitmapToRegion(
//function contributed by Mr. Lachand-Robert
//
//It is a modification of the original function
//which does the following
//
//1) Given a DC which is being drawn into
//   (such as a printer), the function
//   makes sure that the regions draws
//   will be centered on the X,Y offsets
//
//2) The function then loops thru the bits
//   in the bitmap. 
//
//The function assumes a mapping mode other
//than MM_TEXT for the passed-in Device
//Context. I used it successfully with
//MM_ANISOTROPIC, for example, and was able
//to "zoom" the bitmap correctly
///////////////////////////////////////////
BOOL CBitmapOp::GetBitmapPixels(CBitmap& bitmap)
{
	HBITMAP hBmp;
	hBmp = HBITMAP(bitmap);
 
	//Sanity..
	if (hBmp == NULL) return FALSE;
 
	// Create a memory DC inside which we will scan the bitmap content
	HDC hMemDC = CreateCompatibleDC(NULL);
	ASSERT(hMemDC);
 
	if (hMemDC)
	{
		// Get bitmap siz
		BITMAP bm;
		GetObject(hBmp, sizeof(bm), &bm);
 
		// Create a 32 bits depth bitmap and select it into the memory DC
		BITMAPINFOHEADER RGB32BITSBITMAPINFO =
		{
			sizeof(BITMAPINFOHEADER),// biSize
			bm.bmWidth,				 // biWidth;
           -bm.bmHeight,			 // biHeight
			1,						 // biPlanes;
			32,						 // biBitCount
			BI_RGB,					 // biCompression;
			0,						 // biSizeImage;
			0,						 // biXPelsPerMeter;
			0,						 // biYPelsPerMeter;
			0,						 // biClrUsed;
			0						 // biClrImportant;
		};
 
		VOID * pbits32;
 
		HBITMAP hbm32 = CreateDIBSection(
		                hMemDC,
		                (BITMAPINFO *)&RGB32BITSBITMAPINFO,
							 DIB_RGB_COLORS,
							 &pbits32,
							 NULL,
							 0);
 
		if (hbm32)
		{
			HBITMAP holdBmp = (HBITMAP)SelectObject(hMemDC, hbm32);
 
			// Create a DC just to copy the bitmap into the memory D
			HDC hDC = CreateCompatibleDC(hMemDC);
 
			if (hDC)
			{
				// Get how many bytes per row w
				//have for the bitmap bit
				//(rounded up to 32 bits
				BITMAP bm32;
				VERIFY(GetObject(hbm32, sizeof(bm32), &bm32));
 
				while (bm32.bmWidthBytes % 4)
					bm32.bmWidthBytes++;
 
				// Copy the bitmap into the memory DC
				HBITMAP holdBmp = (HBITMAP)SelectObject(hDC, hBmp);
				VERIFY(BitBlt(hMemDC,
								  0,
								  0,
								  bm.bmWidth,
								  bm.bmHeight,
								  hDC,
								  0,
								  0,
								  SRCCOPY));
 
				//Scan each bitmap row from bottom to to
				//(the bitmap is inverted vertically
				BYTE *p32 = (BYTE *)bm32.bmBits +
				            (bm32.bmHeight - 1) * bm32.bmWidthBytes;
 
				//Scan each bitmap pixel from left to right				
				SetBitmapDataSize(bm.bmWidth, bm.bmHeight);
				for (int y = 0; y < bm.bmHeight; y++)
				{
					for (int x = 0; x < bm.bmWidth; x++)
					{
						int  x0 = x; //Ptr to start of region to fil
						LONG *p = (LONG *)p32 + x;
 
						BitmapData[x][y] = RGB(GetBValue((DWORD)(*p)),
								   			  GetGValue((DWORD)(*p)),
											  GetRValue((DWORD)(*p)));
					}	//end for 
 
					// Go to next row
					//(remember, the bitmap is inverted vertically
					p32 -= bm32.bmWidthBytes;
				}	//end for 
 
				// Clean up
				SelectObject(hDC, holdBmp);
				DeleteDC(hDC);
			}
 
			DeleteObject(SelectObject(hMemDC, holdBmp));
		}
 
		DeleteDC(hMemDC);
	}
 
	return TRUE;
}
 
void CBitmapOp::SetBitmapDataSize(const int x, const int y)
{
	m_PixelNumberX = x;
	m_PixelNumberY = y;
	if ((x>=0) && (y>=0))
	{		
		BitmapData.resize(m_PixelNumberX); 
		for(int x = 0; x < m_PixelNumberX; x++) 
			BitmapData[x].resize(m_PixelNumberY); 
	}
}
 
vector<vector<COLORREF> > CBitmapOp::GetBitmapArray()
{
	return BitmapData;
}

CGraphCtrl

the part of the overloaded CStatic class responsible for loading and saving of the bitmap:
BOOL CGraphCtrl::SaveBitmap(CString csFileName)
{
	CPaintDC dc(this);
	PlotToDC(&dc);
	BOOL Result = BitmapOperations.SaveDcToBitmapFile((LPCTSTR)csFileName, &dc, GetSize());
	return Result;
}
 
BOOL CGraphCtrl::LoadBitmap(CString csFileName)
{
	BOOL Result = LoadBitmapData(csFileName);	
	if (Result==FALSE)
		return Result;
	UpdatePlot();
	return Result;
}
 
BOOL CGraphCtrl::LoadBitmapData(CString csFileName)
{
	CBitmap DrawMeBmp;
	BOOL Result = BitmapOperations.LoadBitmapFileToCBitmap((LPCTSTR)csFileName, DrawMeBmp, NULL );
	if (Result==FALSE)
		return Result;
	Result = BitmapOperations.GetBitmapPixels(DrawMeBmp);
	if (Result==FALSE)
		return Result;
 
	BITMAP bmpInfo;
    DrawMeBmp.GetBitmap(&bmpInfo);
 
	SetPlotDataSize(bmpInfo.bmWidth, bmpInfo.bmHeight);
	SetPlotData(BitmapOperations.GetBitmapArray());
	return Result;
}

Any ideas, correction or help concerning this classes and problems is appreciated.

Einen Kommentar schreiben

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *


*

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

WP-SpamFree by Pole Position Marketing