Can't properly load bitmap from the memory - c++

Brief introduction to the problem:
I got the zip archive, where the set of files in the bmp format are placed.
I wan't to load bitmaps from that archive (create system objects) and pass their HBITMAP to a client method which will display it on the screen.
I tried 2 ways to solve my task:
1) using temporary file (this methods works fine, but I think that it is inefficient - as requires additional actions on file creation and requires that user posses some privileges that allows him to create files)
hBitmap = (HBITMAP)LoadImage(NULL, fullpath.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTCOLOR | LR_CREATEDIBSECTION );
2) unzipping the needed file to the memory and using the memory buffer for bitmap creation
(My solution is based on the information from the similar questions on SO and other sources)
BITMAPFILEHEADER* bmfh = (BITMAPFILEHEADER*)ibuf;
BITMAPINFOHEADER* bmih = (BITMAPINFOHEADER*)(ibuf + sizeof(BITMAPFILEHEADER));
BITMAPINFO* bmi = (BITMAPINFO*)bmih;
void* pBMPdata = (void*)(ibuf + bmfh->bfOffBits);
hBitmap = CreateDIBitmap(NULL, bmih, CBM_INIT, pBMPdata, bmi, DIB_RGB_COLORS);
I need help on the 2-nd way.
When image is loaded from the HBITMAP obtained on the second method - black square is displayed/
UPDATE:
I tried 3-rd method:
BITMAPFILEHEADER* pBmfh = (BITMAPFILEHEADER*)ibuf;
BITMAPINFOHEADER* pBmih = (BITMAPINFOHEADER*)(ibuf + sizeof(BITMAPFILEHEADER));
BITMAPINFO* pBmi = (BITMAPINFO*)pBmih;
void* pBMPdata = (void*)(ibuf + pBmfh->bfOffBits);
void* pToFill = 0;
hBitmap = CreateDIBSection(NULL, pBmi, DIB_RGB_COLORS, &pToFill, NULL, NULL);
Result: is black square, which has dimensions of the picture I am trying to load.
Any ideas that might help are appreciated!

It might be helpful for others how may encounter this issue(black square which has dimensions of the image you are trying to load).
3-rd method works.
BITMAPFILEHEADER* pBmfh = (BITMAPFILEHEADER*)ibuf;
BITMAPINFOHEADER* pBmih = (BITMAPINFOHEADER*)(ibuf + sizeof(BITMAPFILEHEADER));
BITMAPINFO* pBmi = (BITMAPINFO*)pBmih;
void* pBMPdata = (void*)(ibuf + pBmfh->bfOffBits);
void* pToFill = 0;
hBitmap = CreateDIBSection(NULL, pBmi, DIB_RGB_COLORS, &pToFill, NULL, NULL);
memcpy(pToFill, pBMPdata, pBmfh->bfSize - pBmfh->bfOffBits); // this line should be added!!
My mistake was, that I was trying to pass pointer to pointer to the databits, instead of it
I should after invokage copy databits to the void (which is pToFill).**

Related

glfwSetWindowIcon from an embedded resource with transparency

I'm trying to achieve what almost every application does, and that's to have an embedded icon which I can show in the title bar and taskbar.
The requirements are:
The icon must support transparency
The icon must be embedded, not a separate file on the disk
The icon file format doesn't matter; can be ico, bmp or anything else that works.
I tried to create a bmp in paint.net, but LoadImage kept returning NULL. I recreated it as a 32-bit png, then used
this tool to convert it to a 32-bit bmp, my reasoning for which being to get LoadImage working again, and this time with transparency. I'm using LoadImage because apparently LoadBitmap isn't so well-supported.
resources.rc:
100 BITMAP "icons\\icon.bmp"
code:
HBITMAP h_bmp = (HBITMAP)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(100), IMAGE_BITMAP, 0, 0, 0);
HDC dc = CreateCompatibleDC(NULL);
HBITMAP bmp_old = (HBITMAP)SelectObject(dc, h_bmp);
BITMAP bmp = {};
GetObject(h_bmp, sizeof(bmp), &bmp);
BITMAPINFO info;
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biWidth = bmp.bmWidth;
info.bmiHeader.biHeight = -bmp.bmHeight;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = bmp.bmBitsPixel;
info.bmiHeader.biCompression = BI_RGB;
info.bmiHeader.biSizeImage = ((bmp.bmWidth * bmp.bmBitsPixel + 31) / 32) * 4 * bmp.bmHeight;
std::vector<unsigned char> pixels;
pixels.resize(info.bmiHeader.biSizeImage);
GetDIBits(dc, h_bmp, 0, bmp.bmHeight, &pixels[0], &info, DIB_RGB_COLORS);
SelectObject(dc, bmp_old);
DeleteDC(dc);
GLFWimage image = { bmp.bmWidth, bmp.bmHeight, &pixels[0] };
glfwSetWindowIcon(window, 1, &image);
There were two issues with the image; firstly it was displaying upside-down, hence the negation of the height above. Secondly, the RGB channels are flipped and I have no idea how to fix it.
So, is there a simpler way to do this, perhaps without having to suffer bmp formats and needing 3rd-party tools, and if not, how can I get the colours displaying properly?
Windows (HWNDs) uses HICONs. A HICON is a handle to an icon loaded from a .ico file or an icon resource. A .ico file consists of one or more bmp and/or png files in various sizes. You should use a real icon editor to create your icon. You can find several free editors if you look around.
Once you have the ico file in your resources, load it with LoadImage(hInst, ..., IMAGE_ICON, ..., LR_SHARED) and apply it with SetClassLongPtr or WM_SETICON.
Solved as per #user253751's comment;
SetClassLongPtr(glfwGetWin32Window(window), GCLP_HICON, (LONG_PTR)LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(100)));
Much simpler than I expected!

WinAPI: Correctly copying a HBITMAP to the clipboard

I have some difficulties trying to copy a HBITMAP to the clipboard. My HBITMAP is created from a COLORREF array and I am able to display it correctly. Here is how it is created:
COLORREF* colors = new COLORREF[imageSize[0] * imageSize[1]];
for (int i = 0; i < imageSize[1]; i++) {
for (int j = 0; j < imageSize[0]; j++) {
colors[imageSize[0] * i + j] = RGB(/* ... */);
}
}
// Create bitmap
HBITMAP hBitmap = CreateBitmap(imageSize[0], imageSize[1], 1, 32, (void*)colors);
delete[] colors;
In order to copy my bitmap to the clipboard, I use this small piece of code:
OpenClipboard(hWnd);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBitmap);
CloseClipboard();
When I execute my app, I am able to copy the bitmap and paste it somewhere, for example in MS Paint. But if I try to copy it a second time, the clipboard content can't be pasted anymore unless the first piece of code above is executed again.
In the MSDN documentation, it is said that
If SetClipboardData succeeds, the system owns the object identified by the hMem parameter.
I don't understand exactly what this means, but I guess it is the source of my problem. I found an example of a function which does what I want here, but it does not seem to use the same kind of variables. Another example, using strings this time, can be found here.
I am not too sure how to translate this last example to my case. Could you point me in the right direction?
A comment that was deleted helped me find the answer. I actually have to copy my HBITMAP to another HBITMAP before calling SetClipboardData. This way, the copied bitmap can be sent to the clipboard, and the original bitmap is kept for later.
To copy the bitmap, I used the code that can be found in Copying a Bitmap to another Bitmap. In my code, it looks like this:
// Create a new bitmap
HBITMAP hBitmap_copy = CreateBitmap(imageSize[0], imageSize[1], 1, 32, NULL);
// Copy the source bitmap to the new one
HDC srcDC = CreateCompatibleDC(GetDC(NULL));
HDC newDC = CreateCompatibleDC(GetDC(NULL));
HBITMAP srcBitmap = (HBITMAP)SelectObject(srcDC, hBitmap);
HBITMAP newBitmap = (HBITMAP)SelectObject(newDC, hBitmap_copy);
BitBlt(newDC, 0, 0, imageSize[0], imageSize[1], srcDC, 0, 0, SRCCOPY);
SelectObject(srcDC, srcBitmap);
SelectObject(newDC, newBitmap);
DeleteDC(srcDC);
DeleteDC(newDC);
// hBitmap_copy can now be copied to the clipboard
OpenClipboard(hWnd);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBitmap_copy);
CloseClipboard();
I can now copy the displayed bitmap as many times as I want!
// You can't pass hBitmap to SetClipboardData directly
OpenClipboard(NULL)
HBITMAP hBitmap = getBit(); // From somewhere
DIBSECTION ds;
::GetObject(hBitmap, sizeof(DIBSECTION), &ds);
//make sure compression is BI_RGB
ds.dsBmih.biCompression = BI_RGB;
HDC hdc = ::GetDC(NULL);
HBITMAP hbitmap_ddb = ::CreateDIBitmap(
hdc, &ds.dsBmih, CBM_INIT, ds.dsBm.bmBits, (BITMAPINFO*)&ds.dsBmih, DIB_RGB_COLORS);
::ReleaseDC(NULL, hdc);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hbitmap_ddb);
CloseClipboard();

GDI+ DC in memory always monochrome

Working in mingw, having a terrible time creating a color DC in memory. For instance, in the following code snippet, as written, "foo_scratch.bmp" is a monochrome version of the
image (from an EMR_STRETCHDIBITS record). If instead aDC is omitted and srcDC uses the CreateDC directly, then that file has a color image.
Gdiplus::Bitmap *pbmp = NULL;
BITMAPINFO *pbitmapinfo = (BITMAPINFO *)((char *)lpEMFR + pEmr->offBmiSrc);
void *pBitsInMem = (char *)lpEMFR + pEmr->offBitsSrc;
HBITMAP hbmsrc;
HDC aDC = CreateDC("DISPLAY", "", NULL, NULL);
HDC srcDC = CreateCompatibleDC(aDC);
hbmsrc = CreateDIBitmap(
srcDC,
&(pbitmapinfo->bmiHeader),
CBM_INIT,
pBitsInMem,
pbitmapinfo,
DIB_RGB_COLORS);
if(hbmsrc){
CLSID pngClsid;
GetEncoderClsid(L"image/bmp", &pngClsid);
pbmp = Gdiplus::Bitmap::FromHBITMAP(hbmsrc,NULL);
pbmp->Save(L"C:\\Temp\\foo_scratch.bmp",&pngClsid, NULL);
This all comes to a head later when two images (hbmdst, hbmsrc) need to be put together with a bitblt operation. At present the best I have managed is monochrome. At worst the image is solid black. In this snippet the ROP has been hard coded to SRCCOPY, and I still have not succeeded in just copying the image from one HBITMAP to another. Very frustrating!
HDC dstDC = CreateCompatibleDC(aDC);
HBITMAP hbmdOld = (HBITMAP) SelectObject(dstDC, hbmdst);
HBITMAP hbmsOld = (HBITMAP) SelectObject(srcDC, hbmsrc);
GetObject(hbmsrc, sizeof(bm), &bm);
BitBlt(dstDC, 0, 0, bm.bmWidth, bm.bmHeight, srcDC, 0, 0, SRCCOPY);
SelectObject(srcDC, hbmsOld);
SelectObject(dstDC, hbmdOld);
(void) DeleteDC(dstDC);
pbmp = Gdiplus::Bitmap::FromHBITMAP(hbmdst,NULL);
pbmp->Save(L"C:\\Temp\\scratch.bmp",&pngClsid, NULL);
What am I doing wrong?
Thanks
Figured it out - wherever a bitmap is created have to use the DC directly associated with the display (or other device), not the "compatible" DC derived from the first DC.

Windows 7 and ScreenShot.cpp GDI+ PNG problemo

was using XP without issue for a long time. switched to 7 and trying to capture screenshots with my previously functioning code no longer works. simple concept and relatively generic code...just find the window that i call and save it as a .png. any ideas what might make this bad boy run again? can't debug with my current setup, but it makes it all the way and spits out the error message after bmp->save(...) ...couldn't save image file. edit: also a file does get created/saved, but it is blank and not written to. perhaps the bitmap encoding or GDI is screwed up?
bool CScreenShot::Snap(CString wintitle, CString file, CString& ermsg)
{
ermsg = ""; // no error message
// create screen shot bitmap
EnumWinProcStruct prm = {0, (LPSTR)(LPCTSTR)wintitle, 0};
// Find the descriptor of the window with the caption wintitle
EnumDesktopWindows(0, EnumWindowsProc, (LPARAM)&prm);
if(!prm.hwnd)
{
ermsg.Format("couldn't find window \"%s\"", wintitle);
return false;
}
// Make the window the topmost window
SetWindowPos(prm.hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
Sleep(300);
// Get device context for the top-level window and client rect
HDC hDC = GetDC(prm.hwnd);
RECT rc;
GetClientRect(prm.hwnd, &rc);
HDC memDC = CreateCompatibleDC(hDC);
// Set the size and color depth for the screen shot image
BITMAPINFO bmpInfo;
memset(&bmpInfo, 0, sizeof(bmpInfo));
bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader);
bmpInfo.bmiHeader.biWidth = rc.right - rc.left;
bmpInfo.bmiHeader.biHeight = rc.bottom - rc.top;
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biBitCount = 24;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biSizeImage = bmpInfo.bmiHeader.biWidth * bmpInfo.bmiHeader.biHeight * 3;
// Create memory buffer and perform a bit-block transfer of the color data from the window to the memory
LPVOID addr;
HBITMAP memBM = CreateDIBSection(memDC, &bmpInfo, DIB_RGB_COLORS, &addr, 0, 0);
HGDIOBJ stdBM = SelectObject(memDC, memBM);
BOOL OK = BitBlt(memDC, 0, 0, bmpInfo.bmiHeader.biWidth, bmpInfo.bmiHeader.biHeight, hDC, 0, 0, SRCCOPY);
ReleaseDC(prm.hwnd, hDC);
SetWindowPos(prm.hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
if(GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL) != Ok)
{
ermsg.Format("couldn't start GDI+");
return false;
}
// Create a Bitmap object for work with images defined by pixel data from the GDI HBitmap and the GDI HPalette.
Bitmap* bmp = ::new Bitmap(memBM, DIB_RGB_COLORS);
SelectObject(memDC, stdBM);
DeleteObject(memBM);
DeleteDC(memDC);
// Find the encoder for "image/png" mime type
CLSID encoderClsid;
EncoderParameters encoderParameters;
GetEncoderClsid(L"image/png", &encoderClsid);
encoderParameters.Count = 0;
// Convert file name to Unicode (wide-char) string.
WCHAR fn[_MAX_PATH];
MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, file, file.GetLength() + 1, fn, _MAX_PATH);
// Save the screen shot into the specified file using image encoder with the mime style "image/png"
if(bmp->Save(fn, &encoderClsid, &encoderParameters) != Ok)
{
ermsg.Format("couldn't save image file \"%s\"", file);
return false;
}
::delete bmp;
GdiplusShutdown(gdiplusToken);
return true;
}
The error message implies that you're trying to save the file to a folder that you don't have permission to write to. Many folders such as Program Files are now protected. Since you didn't include the path in your sample code I'm unable to determine if this is the actual problem.
Edit: Another possibility is that the Bitmap is improperly constructed which causes the Save to fail. The second parameter to the constructor is supposed to be a handle to a palette, I think DIB_RGB_COLORS would be invalid here and you should use NULL. Also there are a couple of caveats noted in the Microsoft documentation and perhaps the different OS versions react differently when you break the rules:
You are responsible for deleting the GDI bitmap and the GDI palette. However, you should not delete the GDI bitmap or the GDI palette until after the GDI+ Bitmap::Bitmap object is deleted or goes out of scope.
Do not pass to the GDI+ Bitmap::Bitmap constructor a GDI bitmap or a GDI palette that is currently (or was previously) selected into a device context.
win7 won't accept encoderParameters.Count == 0 for some reason. Set that == 1 and you should be all set.
you probably could also just remove that parameter from Save() (overloaded)

CreateDIBSection in MFC and rendering using picture control

I'm creating a DIB Section in MFC using the call CreateDIBSection. I get a HBITMAP from the call which I pass onto another dialog in my MFC Project. In the other dialog I'm using CStatic::SetBitmap(HBITMAP) call to render the bitmap. But for some reason I'm not able to see anything. This works perfectly fine if this is done in same dialog, but I want to create bitmap in one dialog and display in another.
The code for creating the DIBSection is
//-----------------BEGINNING OF FIRST DIALOG--------------------------------------------------
LPVOID pViewBitmapBits = NULL;
BITMAPINFOHEADER BMHeaderInfo;
memset(&BMHeaderInfo, 0, sizeof(BITMAPINFOHEADER));
BMHeaderInfo.biSize = sizeof(BITMAPINFOHEADER);
BMHeaderInfo.biWidth = 800;
BMHeaderInfo.biHeight = 400;
BMHeaderInfo.biPlanes = 1;
BMHeaderInfo.biBitCount = 8;
BMHeaderInfo.biCompression = BI_RGB;
BMHeaderInfo.biSizeImage = 0;
BMHeaderInfo.biClrUsed = 0;
BMHeaderInfo.biClrImportant= 0;
BITMAPINFO BMInfo;
memset(&BMInfo, 0, sizeof(BMInfo));
BMInfo.bmiHeader = BMHeaderInfo;
BMInfo.bmiColors[0].rgbBlue=255;
HBITMAP hGlobalBitMap = CreateDIBSection(m_pParentSheet->test.m_hDC, &BMInfo, DIB_RGB_COLORS, &pViewBitmapBits, NULL, NULL);
SelectObject(m_pParentSheet->test.m_hDC, hGlobalBitMap);
//--------------------------END OF FIRST DIALOG----------------------------------
//-----------------------------BEGINNING OF SECOND DIALOG----------------------------------------
void CreateViewDlg::OnBnClickedButton2()
{
m_pic.SetBitmap(hGlobalBitMap );
}
//------------------------------------END OF SECOND DIALOG---------------------------------
Please help me with this. Is there any limitation to the usage of HBITMAP Handles?
HBITMAP hGlobalBitMap = ...
Looks like local variable hiding the global variable.