How to save 24bit BMP - c++

I have a function to capture screen and save it to bitmap (32 bit). This function works great but I also need 24 bit bitmap.
I have no idea how to convert this function.
HWND okno=GetDesktopWindow();
HDC _dc = GetWindowDC(okno);
RECT re;
GetWindowRect( okno, & re );
unsigned int w = re.right, h = re.bottom;
HDC dc = CreateCompatibleDC( 0 );
HBITMAP bm = CreateCompatibleBitmap( _dc, w, h );
SelectObject( dc, bm );
StretchBlt( dc, 0, 0, w, h, _dc, 0, 0, w, h, SRCCOPY );
void * file = CreateFile(file_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0 ); //create file
void * buf = new char[ w * h * 3 ]; //buffor
GetObject( bm, 84, buf );
HDC ddd = GetDC( 0 );
HDC dc2 = CreateCompatibleDC( ddd );
tagBITMAPINFO bit_info; //bitmapinfo
bit_info.bmiHeader.biSize = sizeof( bit_info.bmiHeader );
bit_info.bmiHeader.biWidth = w;
bit_info.bmiHeader.biHeight = h;
bit_info.bmiHeader.biPlanes = 1;
bit_info.bmiHeader.biBitCount = 32;
bit_info.bmiHeader.biCompression = 0;
bit_info.bmiHeader.biSizeImage = 0;
CreateDIBSection( dc, & bit_info, DIB_RGB_COLORS, & buf, 0, 0 );
GetDIBits( dc, bm, 0, h, buf, & bit_info, DIB_RGB_COLORS );
BITMAPFILEHEADER bit_header;
bit_header.bfType = MAKEWORD( 'B', 'M' );
bit_header.bfSize = w * h * 4 + 54;
bit_header.bfOffBits = 54;
BITMAPINFOHEADER bit_info_header;
bit_info_header.biSize = 40;
bit_info_header.biWidth = w;
bit_info_header.biHeight = h;
bit_info_header.biPlanes = 0;
bit_info_header.biBitCount = 32;
bit_info_header.biCompression = 0;
bit_info_header.biSizeImage = w * h *4;
DWORD r;
WriteFile( file, & bit_header, sizeof( bit_header ), & r, NULL );
WriteFile( file, & bit_info_header, sizeof( bit_info_header ), & r, NULL );
WriteFile( file, buf, w * h * 4, & r, NULL );
Sorry for my english :-)

Study about 32 bit and 24 bit presentation of images. Every image file has two parts header and data. In normal images a upto a fixed size of bytes contains header (Usually 1024byte). Then the data starts. If the image size is WxH then you will get WxHx32 byte data. Each 32 bit contains a single pixel information so you will get R, G, B and alpha information (4x8). you write it in 24 format with only RGB data. That's all you need. I haven't find any build in function for it.

Related

Read pixels from a HDC

I'm trying to read all pixels on a given area of a HDC to find if a color is present, currently I came up with:
IDirect3DSurface9* pSurface = 0;
p1->CreateOffscreenPlainSurface(1280, 1024,D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &pSurface, NULL);
p1->GetFrontBufferData(0, pSurface);
//assert( pSurface );
if( pSurface && GetTickCount() > dwGAKS_Fix )
{
HDC dc;
pSurface->GetDC( &dc );
COLORREF dpurp = D3DCOLOR_ARGB (255,102,0 ,153);
for( DWORD h = 610; h <= 670; h++ )
{
for( DWORD w = 480; w<=530; w++ )
{
COLORREF dwPixel = GetPixel( dc, h, w );
// CString strPixel; strPixel.Format( "Pixel col: %u at: %u X %u", dwPixel, d, i );
//if( dx_Font )
if( dwPixel == dpurp )
{
dx_Font->DrawTextA(NULL, "Shoot", strlen("Shoot"), &pos, DT_NOCLIP, D3DCOLOR_XRGB(0, 255, 0));
}
else
dx_Font->DrawTextA(NULL, "NoShoot", strlen("NoShoot"), &pos, DT_NOCLIP, D3DCOLOR_XRGB(0, 255, 0));
}
}
dwGAKS_Fix = GetTickCount() + 15;
pSurface->ReleaseDC( dc );
pSurface->Release();
But this solution is slow, very slow, I need something somewhat more..uh professional
edit
D3DLOCKED_RECT d3dlocked;
if( D3D_OK == pSurface->LockRect( &d3dlocked, 0, 0 ) )
{
UINT *pixels=(UINT *)locked.pBits;
if(pixels[52+15*1024]&0xFFFFFF00==dpurp)
{
}
pSurface->UnlockRect();
}
GetPixel is always slow. You can get direct access to the bits in the off-screen surface using IDirect3DSurface9::LockRect and then scan through the bitmap yourself, which should be much quicker.
(Edit) Any given pixel (x,y) is the 32 bit value found at:
*(DWORD*)(((BYTE*)d3dlocked.pBits) + y * d3dlocked.Pitch + x * sizeof(DWORD));
You should AND the value with 0x00ffffff to ignore the alpha channel.

Getting HBITMAP from layered window - incorrect data

I created a layered window (with WS_EX_LAYERED), size of about 400X300 px.
When drawing the window (using UpdateLayeredWindow) everything works great.
The problem is that I'm unable to get the HBITMAP of the window after drawing it.
When trying to get the HBITMAP through the window's HDC, I get an empty (black) bitmap, the size of my entire desktop (1920X1080 px insted of 400X300 px).
Does anybody know if it is even possible to get the HDC\HBITMAP of a layered window?
Code samples
Here's the code of how I draw the layered window (again, works great):
void MyLayeredWindow::DrawLayered() const
{
RECT rcWindow;
GetWindowRect(rcWindow);
int nWidth = abs(rcWindow.right - rcWindow.left);
int nHeight = abs(rcWindow.bottom - rcWindow.top);
// Create 32Bit bitmap to apply transparency
// (have to set negative height because if not the (0,0) point would be the bottom left instead of top left)
VOID *ppvBits = NULL;
BITMAPINFO BitmapInfo = {0};
BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfo.bmiHeader.biWidth = nWidth;
BitmapInfo.bmiHeader.biHeight = -nHeight;
BitmapInfo.bmiHeader.biPlanes = 1;
BitmapInfo.bmiHeader.biBitCount = 32;
BitmapInfo.bmiHeader.biCompression = BI_RGB;
// Copy view buffer to a temp DC and bitmap
HDC hDcTemp = ::CreateCompatibleDC(NULL);
assert(hDcTemp);
HBITMAP hBitmapTemp = ::CreateDIBSection(hDcTemp, &BitmapInfo, DIB_RGB_COLORS, &ppvBits, NULL, 0);
assert(hBitmapTemp && hBitmapTemp!=(HBITMAP)ERROR_INVALID_PARAMETER)
::SelectObject(hDcTemp, hBitmapTemp);
// Darwing the window's conent here
// ....
// ....
// Call UpdateLayeredWindow
BLENDFUNCTION blend = {0};
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 190;
blend.AlphaFormat = AC_SRC_ALPHA;
SIZE sizeWnd = {0};
sizeWnd.cx = nWidth;
sizeWnd.cy = nHeight;
POINT ptPos = {0};
ptPos.x = rcWindow.left;
ptPos.y = rcWindow.top;
POINT ptSrc = {0,0};
::UpdateLayeredWindow(m_hWnd, NULL, &ptPos, &sizeWnd, hDcTemp, &ptSrc, 0, &blend, ULW_ALPHA);
if(hDcTemp)
::DeleteDC(hDcTemp);
if(hBitmapTemp)
::DeleteObject(hBitmapTemp);
}
Here's the code of how I capture the window's bitmap and save it to a file:
(NOTICE: It works of "normal" windows, such as the Calculator)
bool MyLayeredWindow::SaveBitmapFile(__in const HWND& hWnd, __in const wstring& sFileName)
{
HDC hDC = ::GetDC(hWnd);
// get bitmap of DC
HBITMAP hBmp = (HBITMAP)::GetCurrentObject( hDC, OBJ_BITMAP );
// get info of bitmap
BITMAPINFO stBmpInfo;
stBmpInfo.bmiHeader.biSize = sizeof( stBmpInfo.bmiHeader );
stBmpInfo.bmiHeader.biBitCount = 0;
GetDIBits( hDC, hBmp, 0, 0, NULL, &stBmpInfo, DIB_RGB_COLORS );
// init info size
ULONG iBmpInfoSize;
switch( stBmpInfo.bmiHeader.biBitCount )
{
case 24:
iBmpInfoSize = sizeof(BITMAPINFOHEADER);
break;
case 16:
case 32:
iBmpInfoSize = sizeof(BITMAPINFOHEADER)+sizeof(DWORD)*3;
break;
default:
iBmpInfoSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * ( 1 << stBmpInfo.bmiHeader.biBitCount );
break;
}
// copy header
PBITMAPINFO pstBmpInfo = NULL;
if( iBmpInfoSize != sizeof(BITMAPINFOHEADER) )
{
pstBmpInfo = (PBITMAPINFO)GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, iBmpInfoSize );
PBYTE pbtBmpInfoDest = (PBYTE)pstBmpInfo;
PBYTE pbtBmpInfoSrc = (PBYTE)&stBmpInfo;
ULONG iSizeTmp = sizeof( BITMAPINFOHEADER );
while( iSizeTmp-- )
*( ( pbtBmpInfoDest )++ ) = *( ( pbtBmpInfoSrc )++ );
}
// create file
HANDLE hFile = CreateFile( sFileName.c_str(), GENERIC_WRITE, 0 , NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL );
// init bmp file header
BITMAPFILEHEADER stBmpFileHder = {0};
stBmpFileHder.bfType = 0x4D42; // 'BM'
stBmpFileHder.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + iBmpInfoSize + pstBmpInfo->bmiHeader.biSizeImage;
stBmpFileHder.bfReserved1 = 0;
stBmpFileHder.bfReserved2 = 0;
stBmpFileHder.bfOffBits = sizeof(BITMAPFILEHEADER) + iBmpInfoSize;
// write header to file
DWORD dRet;
WriteFile( hFile, (LPCVOID)&stBmpFileHder, sizeof(BITMAPFILEHEADER), &dRet, NULL );
// allocate size for rest of bmp (body)
PBYTE pBits = (PBYTE)GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, stBmpInfo.bmiHeader.biSizeImage );
// get bmp bits
HBITMAP hBmpOld;
HBITMAP hTmpBmp = CreateCompatibleBitmap( hDC, pstBmpInfo->bmiHeader.biWidth, pstBmpInfo->bmiHeader.biHeight );
hBmpOld = (HBITMAP)SelectObject( hDC, hTmpBmp );
GetDIBits( hDC, hBmp, 0, pstBmpInfo->bmiHeader.biHeight, (LPSTR)pBits, pstBmpInfo, DIB_RGB_COLORS );
// write bmp info
WriteFile( hFile, (LPCVOID)pstBmpInfo, iBmpInfoSize, &dRet, NULL );
// write bmp bits
WriteFile( hFile, (LPCVOID)pBits, pstBmpInfo->bmiHeader.biSizeImage, &dRet, NULL );
// release handles and free memory
SelectObject( hDC, hBmpOld );
DeleteObject( hTmpBmp );
CloseHandle( hFile );
GlobalFree( pstBmpInfo );
GlobalFree( pBits );
ReleaseDC( hWnd, hDC );
return true;
}
Thanks!
Since I didn't get any better answer, I simply called a "Draw" function that paints my layered window, onto a temporary HDC.
Meaning I don't copy existing bitmap, but create an identical one, using the same drawing function.
I'd still love to get a better answer for this question.

Creating a HBITMAP from glReadPixels

I need to create a HBITMAP from data returned by a glReadPixels() call:
HDC hCompDC = CreateCompatibleDC(NULL);
HDC hDC = GetDC();
m_hClipboardBitmap = CreateCompatibleBitmap(hDC, size.cx, size.cy);
if ( m_hClipboardBitmap == NULL )
{
throw runtime_error( "Unable to create bitmap." );
}
HBITMAP hOldBm = (HBITMAP) SelectObject( hCompDC, m_hClipboardBitmap );
int numberOfBytes = 4 * size.cx * size.cy;
unsigned char *pPixelData = new unsigned char[numberOfBytes];
::glReadPixels(minimum.x, minimum.y, size.cx, size.cy, GL_BGRA, GL_UNSIGNED_BYTE, pPixelData);
I tried using:
BITMAPINFOHEADER header;
header.biWidth = size.cx;
header.biHeight = size.cy;
header.biSizeImage = numberOfBytes;
header.biSize = sizeof(BITMAPINFOHEADER);
header.biPlanes = 1;
header.biBitCount = 4 * 8; // RGBA
header.biCompression = 0;
header.biXPelsPerMeter = 0;
header.biYPelsPerMeter = 0;
header.biClrUsed = 0;
header.biClrImportant = 0;
HANDLE handle = (HANDLE)::GlobalAlloc (GHND, sizeof(BITMAPINFOHEADER) + numberOfBytes);
if(handle != NULL)
{
char *pData = (char *) ::GlobalLock((HGLOBAL)handle);
memcpy(pData,&header,sizeof(BITMAPINFOHEADER));
memcpy(pData + sizeof(BITMAPINFOHEADER), pPixelData, numberOfBytes);
::GlobalUnlock((HGLOBAL)handle);
OpenClipboard();
EmptyClipboard();
SetClipboardData(CF_DIB, handle);
CloseClipboard();
}
And that pastes into mspaint OK (so the data is good) but how on earth do I get it into a HBITMAP?!?!
Very old thread, but I wanted to give an answer, at least to keep it as a repository.
void WriteOpenGLPixelsToHBITMAP( HBITMAP dstHBITMAP, HDC dstDC, SIZE dims )
{
BITMAPINFO bitmapInfo;
{
::memset( &bitmapInfo, 0, sizeof( BITMAPINFO ) );
bitmapInfo.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biWidth = dims.cx;
bitmapInfo.bmiHeader.biHeight = dims.cy;
bitmapInfo.bmiHeader.biSizeImage = dims.cx * dims.cy * 4; // Size 4, assuming RGBA from OpenGL
}
void *bmBits = NULL;
HDC memDC = ::CreateCompatibleDC( dstDC );
HBITMAP memBM = ::CreateDIBSection( NULL, &bitmapInfo, DIB_RGB_COLORS, &bmBits, NULL, 0 );
::glReadPixels( 0,
0,
dims.cx,
dims.cy,
GL_BGRA_EXT,
GL_UNSIGNED_BYTE,
bmBits );
HGDIOBJ prevBitmap = ::SelectObject( memDC, memBM );
HGDIOBJ obj = ::SelectObject( dstDC, dstHBITMAP );
// Remember that OpenGL origin is at bottom, left, but bitmaps are top, left
if ( false == BitBlt( dstDC, 0 /*left*/, dims.cy /*top*/, dims.cx, dims.cy, memDC, 0, 0, SRCCOPY ) )
{
assert( false && "Failed to write pixels to HBitmap from OpenGL glReadPixels" );
}
::SelectObject( memDC, prevBitmap );
::DeleteObject( memBM );
::DeleteDC( memDC );
}
As mentioned, be aware of image being inverted. You can swap SRCCOPY for SRCINVERT. Also you might want to make sure you are copying regions. The code above assumes that the region matches the viewport.
Are you calling the function with the correct parameters. Check the documentation of the function: http://msdn.microsoft.com/en-us/library/dd183491(v=vs.85).aspx. Seems like you have swapped the parameter order and are passing a pointer to a pointer to the data.
-Timo

Display a image in a MFC/C++ application using OpenCV

I would like to display in a MFC application, frames that I capture from an avi file with OpenCV (cvCaptureFromAVI function).
I'm new to MFC but feel like I'm close to making it work. But instead of the frames being displayed in the picture box they are displayed in a new window.
cvGetWindowName returns always a null value.
There is my code:
CWnd* hPic = 0;
hPic = GetDlgItem(IDC_STATICPIC1);
const char* szWindName = cvGetWindowName(hPic->GetSafeHwnd());
cvShowImage(szWindName, frame_copy);
So I found something to make it work after long researches.
The solution is to create the window and then insert it inside the picture box. I'm not sure it's good practice but I haven't found anything better for now.
cvNamedWindow("IDC_STATIC_OUTPUT", 0);
cvResizeWindow("IDC_STATIC_OUTPUT", 420, 240);
HWND hWnd = (HWND) cvGetWindowHandle("IDC_STATIC_OUTPUT");
HWND hParent = ::GetParent(hWnd);
::SetParent(hWnd, GetDlgItem(IDC_PIC1)->m_hWnd);
::ShowWindow(hParent, SW_HIDE);
cvShowImage("IDC_STATIC_OUTPUT", frame_copy);
In this case the picture box is called IDC_PIC1 and frame_copy is a OpenCV IplImage.
Hope this helps somebody.
Using the following code you can convert Mat to CImage and then display CImage everywhere you want:
int Mat2CImage(Mat *mat, CImage &img){
if(!mat || mat->empty())
return -1;
int nBPP = mat->channels()*8;
img.Create(mat->cols, mat->rows, nBPP);
if(nBPP == 8)
{
static RGBQUAD pRGB[256];
for (int i = 0; i < 256; i++)
pRGB[i].rgbBlue = pRGB[i].rgbGreen = pRGB[i].rgbRed = i;
img.SetColorTable(0, 256, pRGB);
}
uchar* psrc = mat->data;
uchar* pdst = (uchar*) img.GetBits();
int imgPitch = img.GetPitch();
for(int y = 0; y < mat->rows; y++)
{
memcpy(pdst, psrc, mat->cols*mat->channels());//mat->step is incorrect for those images created by roi (sub-images!)
psrc += mat->step;
pdst += imgPitch;
}
return 0;
}
NOTE: If you use the StretchDIBits() method with the BITMAPINFO approach, you MUST be aware that StretchDIBits() expects the raw OpenCV Mat::data pointer to have row lengths in even multiples of 4 bytes! If not, you'll get freaky shearing when you try to copy the data to the DC via StretchDIBits() - where the image is not only sheered along an angle, but the colors are all be trashed as well.
Here is my completely working edition of the code, which also supports maintaining image aspect ratio in the target control's rectangle. It can probably be made a bit faster, but it works for now:
void AdjustAspectImageSize( const Size& imageSize,
const Size& destSize,
Size& newSize )
{
double destAspectRatio = float( destSize.width ) / float( destSize.height );
double imageAspectRatio = float( imageSize.width ) / float( imageSize.height );
if ( imageAspectRatio > destAspectRatio )
{
// Margins on top/bottom
newSize.width = destSize.width;
newSize.height = int( imageSize.height *
( double( destSize.width ) / double( imageSize.width ) ) );
}
else
{
// Margins on left/right
newSize.height = destSize.height;
newSize.width = int( imageSize.width *
( double( destSize.height ) / double( imageSize.height ) ) );
}
}
void DrawPicToHDC( Mat cvImg,
UINT nDlgID,
bool bMaintainAspectRatio /* =true*/ )
{
// Get the HDC handle information from the ID passed
CDC* pDC = GetDlgItem(nDlgID)->GetDC();
HDC hDC = pDC->GetSafeHdc();
CRect rect;
GetDlgItem(nDlgID)->GetClientRect(rect);
Size winSize( rect.right, rect.bottom );
// Calculate the size of the image that
// will fit in the control rectangle.
Size origImageSize( cvImg.cols, cvImg.rows );
Size imageSize;
int offsetX;
int offsetY;
if ( ! bMaintainAspectRatio )
{
// Image should be the same size as the control's rectangle
imageSize = winSize;
}
else
{
Size newSize;
_AdjustAspectImageSize( origImageSize,
winSize,
imageSize );
}
offsetX = ( winSize.width - imageSize.width ) / 2;
offsetY = ( winSize.height - imageSize.height ) / 2;
// Resize the source to the size of the destination image if necessary
Mat cvImgTmp;
resize( cvImg,
cvImgTmp,
imageSize,
0,
0,
INTER_AREA );
// To handle our Mat object of this width, the source rows must
// be even multiples of a DWORD in length to be compatible with
// SetDIBits(). Calculate what the correct byte width of the
// row should be to be compatible with SetDIBits() below.
int stride = ( ( ( ( imageSize.width * 24 ) + 31 ) & ~31 ) >> 3 );
// Allocate a buffer for our DIB bits
uchar* pcDibBits = (uchar*) malloc( imageSize.height * stride );
if ( pcDibBits != NULL )
{
// Copy the raw pixel data over to our dibBits buffer.
// NOTE: Can setup cvImgTmp to add the padding to skip this.
for ( int row = 0; row < cvImgTmp.rows; ++row )
{
// Get pointers to the beginning of the row on both buffers
uchar* pcSrcPixel = cvImgTmp.ptr<uchar>(row);
uchar* pcDstPixel = pcDibBits + ( row * stride );
// We can just use memcpy
memcpy( pcDstPixel,
pcSrcPixel,
stride );
}
// Initialize the BITMAPINFO structure
BITMAPINFO bitInfo;
bitInfo.bmiHeader.biBitCount = 24;
bitInfo.bmiHeader.biWidth = cvImgTmp.cols;
bitInfo.bmiHeader.biHeight = -cvImgTmp.rows;
bitInfo.bmiHeader.biPlanes = 1;
bitInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitInfo.bmiHeader.biCompression = BI_RGB;
bitInfo.bmiHeader.biClrImportant = 0;
bitInfo.bmiHeader.biClrUsed = 0;
bitInfo.bmiHeader.biSizeImage = 0; //winSize.height * winSize.width * * 3;
bitInfo.bmiHeader.biXPelsPerMeter = 0;
bitInfo.bmiHeader.biYPelsPerMeter = 0;
// Add header and OPENCV image's data to the HDC
StretchDIBits( hDC,
offsetX,
offsetY,
cvImgTmp.cols,
cvImgTmp.rows,
0,
0,
cvImgTmp.cols,
cvImgTmp.rows,
pcDibBits,
& bitInfo,
DIB_RGB_COLORS,
SRCCOPY );
free(pcDibBits);
}
ReleaseDC(pDC);
}
int DrawImageToHDC(IplImage* img, HDC hdc, int xDest, int yDest, UINT iUsage, DWORD rop)
char m_chBmpBuf[2048];
BITMAPINFO *m_pBmpInfo = 0;
m_pBmpInfo = (BITMAPINFO*)m_chBmpBuf;
m_pBmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
m_pBmpInfo->bmiHeader.biWidth = img->width;
m_pBmpInfo->bmiHeader.biHeight = -img->height;
m_pBmpInfo->bmiHeader.biBitCount = 24;
m_pBmpInfo->bmiHeader.biPlanes = 1;
m_pBmpInfo->bmiHeader.biCompression = BI_RGB;
m_pBmpInfo->bmiHeader.biSizeImage = 0;
m_pBmpInfo->bmiHeader.biXPelsPerMeter = 0;
m_pBmpInfo->bmiHeader.biYPelsPerMeter = 0;
m_pBmpInfo->bmiHeader.biClrUsed = 0;
m_pBmpInfo->bmiHeader.biClrImportant = 0;
return StretchDIBits(hdc, xDest, yDest, img->width, img->height, 0, 0,
img->width, img->height, img->imageData, m_pBmpInfo, DIB_RGB_COLORS, SRCCOPY);
Usage: DrawImageToHDC(img, pDC->m_hDC, Area.left, Area.top, DIB_RGB_COLORS, SRCCOPY);

array[byte] to HBITMAP or CBitmap

I have an array of bytes (which I read through a stream directly from a .bmp and then store as a BLOB in a database) which I want to display as icons in a CImageList. Therefore I want to somehow load my data into an HBITMAP or CBitmap. I have done it like this up to now, reading from a file:
hPic = (HBITMAP)LoadImage(NULL, strPath, IMAGE_BITMAP, dwWidth, dwHeight, LR_LOADFROMFILE | LR_VGACOLOR);
...
CBitmap bitmap;
bitmap.Attach(hPicRet);
But obviously, that only works for files, but not for byte-arrays. How can I get the same result, but reading from an array of byte?
Edit:
Note that my array does not contain just the colour information, but rather the complete file as it is written on disk, including all headers and meta-data. It seems to me that discarding all that information is a bad idea.
Assuming you have the information loaded into a BYTE array named bytes....
BITMAPFILEHEADER* bmfh;
bmfh = (BITMAPFILEHEADER*)bytes;
BITMAPINFOHEADER* bmih;
bmih = (BITMAPINFOHEADER*)(bytes + sizeof(BITMAPFILEHEADER));
BITMAPINFO* bmi;
bmi = (BITMAPINFO*)bmih;
void* bits;
bits = (void*)(bytes + bmfh->bfOffBits);
HDC hdc = ::GetDC(NULL);
HBITMAP hbmp = CreateDIBitmap(hdc, bmih, CBM_INIT, bits, bmi, DIB_RGB_COLORS) ;
::ReleaseDC(NULL, hdc);
It's a little messy and could use a hefty dose of error checking, but the basic idea is sound.
Following sample could help you.
BITMAPINFO bmInfo;
BITMAPINFOHEADER &bmInfohdr = (BITMAPINFOHEADER)bmInfo.bmiHeader;
bmInfohdr.biSize = 40 + 255; //I think it's not of use
bmInfohdr.biWidth = x;
bmInfohdr.biHeight = y;
bmInfohdr.biPlanes=1;
bmInfohdr.biBitCount=8;
bmInfohdr.biCompression=0;
bmInfohdr.biSizeImage=0;
bmInfohdr.biXPelsPerMeter = 0;
bmInfohdr.biYPelsPerMeter = 0;
bmInfohdr.biClrUsed = 0;
bmInfohdr.biClrImportant = 0;
// should I allocate memory further than the
// bmColors[1]?? anyway the compiler gives an
// error for type mismatch!
//bmInfo.bmiColors = (RGBQUAD *)
malloc(sizeof(RGBQUAD) * 256);
// here I define the 256 graylevel palette
for (int i=0; i<256; i++)
{
bmInfo.bmiColors[i].rgbRed = i;
bmInfo.bmiColors[i].rgbGreen = i;
bmInfo.bmiColors[i].rgbBlue = i;
}
BYTE *matrix;
matrix = (BYTE*)malloc(size*sizeof(BYTE));
// here I put the BYTE values of the pixels
CDC *pdcDest = this->GetDC();
HBITMAP hBmp = CreateDIBitmap( pdcDest->m_hDC,
&bmInfohdr,
CBM_INIT,
matrix,
&bmInfo,
DIB_RGB_COLORS);
m_bmpBitmap.Attach( hBmp );
Something like this worked for me:
int bitmap[WX*WY]; // truecolor bitmap data
BITMAPINFO bm = { sizeof(BITMAPINFOHEADER), WX, WY, 1, 32, BI_RGB, 0, 0, 0, 0, 0 };
HBITMAP bmp = CreateDIBSection( GetDC(win), &bm, DIB_RGB_COLORS, (void**)&bitmap, 0,0 );
(This is specifically configured for 32-bit colors, but you can specify any kind).
Ok, here's a complete example: http://nishi.dreamhosters.com/u/so_bmp_v0.zip
#include <stdio.h>
#include <windows.h>
#pragma comment(lib,"gdi32.lib")
#pragma comment(lib,"user32.lib")
char buf[1<<22];
int main( int argc, char **argv ) {
FILE* f = fopen( "winnt.bmp", "rb" ); if( f==0 ) return 1;
fread( buf, 1,sizeof(buf), f );
fclose(f);
BITMAPFILEHEADER& bfh = (BITMAPFILEHEADER&)buf[0];
BITMAPINFO& bi = (BITMAPINFO&)buf[sizeof(BITMAPFILEHEADER)];
BITMAPINFOHEADER& bih = bi.bmiHeader;
char* bitmap = &buf[bfh.bfOffBits];
int WX=1024, WY=512; // window's width/height
int SX=bih.biWidth, SY=bih.biHeight;
HWND win = CreateWindow( "STATIC", "Bitmap test", 0x90C0, 0,0, WX,WY, 0,0, GetModuleHandle(0), 0 );
MSG msg;
PAINTSTRUCT ps;
HDC DC = GetDC(win); // window's DC
HBITMAP dib = CreateDIBitmap( DC, &bih, CBM_INIT, bitmap, &bi, DIB_RGB_COLORS );
HDC dibDC = CreateCompatibleDC( DC ); SelectObject( dibDC, dib );
ShowWindow( win, SW_SHOWNOACTIVATE );
SetFocus( win );
while( GetMessage(&msg,win,0,0) ) {
int m = msg.message;
if( m==WM_PAINT ) {
DC = BeginPaint( win, &ps );
StretchBlt( DC, 0,0,WX,WY, dibDC,0,0,SX,SY, SRCCOPY );
EndPaint( win, &ps );
} else if( (m==WM_KEYDOWN) || (m==WM_SYSKEYDOWN) ) {
break;
} else {
DispatchMessage(&msg);
}
}
return 0;
}