WinAPI: Correctly copying a HBITMAP to the clipboard - c++

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();

Related

MFC: How to copy bitmap using OLE clipboard

Now I have a CImage img, and I wanted to copy the bitmap to the OLE clipboard.
What I did is the following
HBITMAP hImage = img.Detach();
CBitmap *pBitmap = CBitmap::FromHandle(hImage);
BITMAP bm;
pBitmap->GetObject(sizeof(bm), &bm);
HBITMAP hImage = Detach();
// the bitmap src
CBitmap *pBitmap = CBitmap::FromHandle(hImage);
BITMAP bm;
pBitmap->GetObject(sizeof(bm), &bm);
// the copy (dest)
CBitmap bitmap;
bitmap.CreateBitmapIndirect(&bm);
// create mem dc and select src and dest bitmaps
CDC dcMemSrc, dcMemDest;
dcMemSrc.CreateCompatibleDC(NULL);
CBitmap* pOldBitmapSrc = dcMemSrc.SelectObject(pBitmap);
dcMemDest.CreateCompatibleDC(NULL);
CBitmap* pOldBitmapDest = dcMemDest.SelectObject(&bitmap);
// bit-blt
dcMemDest.BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &dcMemSrc,
0, 0, SRCCOPY);
HBITMAP hBitmap = (HBITMAP)bitmap.Detach();
// restore mem dc
dcMemDest.SelectObject(pOldBitmapDest);
dcMemSrc.SelectObject(pOldBitmapSrc);
// Place the copy on the clipboard.
AfxOleInit();
CoInitialize(NULL);
COleDataSource* pods = new COleDataSource;
pods->CacheGlobalData(CF_BITMAP, hBitmap);
pods->SetClipboard();
CoUninitialize();
I tested the program, it just doesn't work, I tried to CTRL+V to the Windows Paint program, but it complained the data cannot be pasted in it. If I use the Legacy Clipboard(just replace the last 6 lines with ::OpenClipboard(hWnd), ::EmptyClipboard(), ::SetClipboardData(CF_BITMAP, hBitmap), ::CloseClipboard() ), it works fine. I have no experience working with COM and OLE, can anyone help? Many thanks.

c++ read pixels with GetDIBits()

I'm trying to create a function which is equivalent to the windows API GetPixel() function, but I want to create a bitmap of my screen and then read that buffer.
This is what I've got (Mostly copy pasted from google searches), when I run it it only prints out 0's. I think I've got most of it right, and that my issue is that I don't know how to read the BYTE variable.
So my question is, what do I need to do in order to get it to print out some random colors (R,G or B) with my for loop?
#include <Windows.h>
#include <iostream>
#include <math.h>
#include <stdio.h>
using namespace std;
int main() {
HDC hdc,hdcMem;
hdc = GetDC(NULL);
hdcMem = CreateCompatibleDC(hdc);
HBITMAP hBitmap = CreateCompatibleBitmap(hdc, 1680, 1050);
BITMAPINFO MyBMInfo = {0};
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);
// Get the BITMAPINFO structure from the bitmap
if(0 == GetDIBits(hdcMem, hBitmap, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS)) {
cout << "error" << endl;
}
// create the bitmap buffer
BYTE* lpPixels = new BYTE[MyBMInfo.bmiHeader.biSizeImage];
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);
MyBMInfo.bmiHeader.biBitCount = 32;
MyBMInfo.bmiHeader.biCompression = BI_RGB;
MyBMInfo.bmiHeader.biHeight = abs(MyBMInfo.bmiHeader.biHeight);
// get the actual bitmap buffer
if(0 == GetDIBits(hdc, hBitmap, 0, MyBMInfo.bmiHeader.biHeight, (LPVOID)lpPixels, &MyBMInfo, DIB_RGB_COLORS)) {
cout << "error2" << endl;
}
for(int i = 0; i < 100; i++) {
cout << (int)lpPixels[i] << endl;
}
return 0;
}
Windows 7
C::B 13.12 (Console Application)
Compiler: mingw32-gcc
Library gdi32 linked
As agreed, I'm adding a new answer with the working code snippet (I added the missing cleanup of lpPixels). See the discussions in my previous answer and the one made by #enhzflep.
#include <Windows.h>
#include <iostream>
#include <math.h>
#include <stdio.h>
using namespace std;
HBITMAP GetScreenBmp( HDC hdc) {
// Get screen dimensions
int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
// Create compatible DC, create a compatible bitmap and copy the screen using BitBlt()
HDC hCaptureDC = CreateCompatibleDC(hdc);
HBITMAP hBitmap = CreateCompatibleBitmap(hdc, nScreenWidth, nScreenHeight);
HGDIOBJ hOld = SelectObject(hCaptureDC, hBitmap);
BOOL bOK = BitBlt(hCaptureDC,0,0,nScreenWidth, nScreenHeight, hdc,0,0,SRCCOPY|CAPTUREBLT);
SelectObject(hCaptureDC, hOld); // always select the previously selected object once done
DeleteDC(hCaptureDC);
return hBitmap;
}
int main() {
HDC hdc = GetDC(0);
HBITMAP hBitmap = GetScreenBmp(hdc);
BITMAPINFO MyBMInfo = {0};
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);
// Get the BITMAPINFO structure from the bitmap
if(0 == GetDIBits(hdc, hBitmap, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS)) {
cout << "error" << endl;
}
// create the bitmap buffer
BYTE* lpPixels = new BYTE[MyBMInfo.bmiHeader.biSizeImage];
// Better do this here - the original bitmap might have BI_BITFILEDS, which makes it
// necessary to read the color table - you might not want this.
MyBMInfo.bmiHeader.biCompression = BI_RGB;
// get the actual bitmap buffer
if(0 == GetDIBits(hdc, hBitmap, 0, MyBMInfo.bmiHeader.biHeight, (LPVOID)lpPixels, &MyBMInfo, DIB_RGB_COLORS)) {
cout << "error2" << endl;
}
for(int i = 0; i < 100; i++) {
cout << (int)lpPixels[i];
}
DeleteObject(hBitmap);
ReleaseDC(NULL, hdc);
delete[] lpPixels;
return 0;
}
Basically, you need to have drawn some pixels in order to get back a result other than 0.
At present, the 4th line of code in your main creates an empty (blank, 0-initialized) image. You then get information about the size of this image with your first call to GetDIBits. You then get the actual (blank) pixels with your second call to GetDIBits.
To fix, just load a bitmap file from disk into your hBitmap and select this bitmap into your hdcMem.
I.e, change
HBITMAP hBitmap = CreateCompatibleBitmap(hdc, 1680, 1050);
to something like this.
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, "xpButton.bmp", IMAGE_BITMAP, 0,0, LR_LOADFROMFILE);
HBITMAP old = (HBITMAP) SelectObject(hdcMem, hBitmap);
(make sure you use a valid bmp file name. Mine exists in the same folder as the .cpp file, since this is the 'current' directory when you run via the IDE. If you wish to run via explorer, place another copy of the bmp in the same folder as your exe)
Here's the bmp I've used (which has been converted to a png after upload to SO):
And here's the first 10 iterations through the loop.
255
5
253
0
255
5
253
0
255
5
Note that the pixel at 0,0 has the colour of: rgb(253,5,255) and it's an 8bit image, so there's no alpha channel, hence it has the value 0. The pixels are stored as [BGRA], [BGRA], [BGRA], etc, etc.
I'll leave it to you to fix the (non-existant) clean-up section of your program. Windows will de-allocate the memory you've used here, but you absolutely should not get into the habit of not freeing any memory you've allocated. :)
Your code seems a bit confused. Too many snippets I guess :).
Still, you're quite close:
The first GetDIBits() call is in order to get the properties of the bitmap filled in, as the comment in your code suggests.
You are using an unnecessary MemDC for this - which is probably from a snippet that wants to do a BitBlt with the screen.
You then can use the filled in structure to get the actual bitmap pixels with the second GetDIBits() call, but what you're doing is replacing the properties with hard coded values again, making the first GetDIBits() call useless.
So: Drop the MemDC - you don't need it - and replace hdcMem with hdc in the first call to GetDIBits(), then remove all the statements that overwrite bmiHeader members after the first GetDIBits call and you should get your pixels.
Oh, and of course don't forget to call ReleaseDC()/DeleteObject() on the dc and bitmap and delete[] the buffer :)

Why does GetObject return an BITMAP with null bmBits?

Context: I'm trying to take a screenshot of another window to feed it into OpenCV. I found some code on the web that should be able to convert a BITMAP to something OpenCV can work with. Unfortunately I ran into some trouble.
Question: Why is the bmBits attribute/member always null? (I also tried with PrintWindow instead of BitBlt the result was the same)
#include <iostream>
#include <string>
#include <Windows.h>
int main(int argc, char* argv[])
{
std::wstring windowName = L"Calculator";
RECT rect;
HWND hwnd = FindWindow(NULL, windowName.c_str());
if (hwnd == NULL)
{
return 0;
}
GetClientRect(hwnd, &rect);
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen,
rect.right - rect.left, rect.bottom - rect.top);
SelectObject(hdc, hbmp);
PrintWindow(hwnd, hdc, PW_CLIENTONLY);
BITMAP bmp;
GetObject(hbmp, sizeof(BITMAP), &bmp);
return 0;
}
The bmBits member is non-null for DIB sections. For device-dependent bitmaps (such as the one you're creating), the bmBits is not set because the pixels are on the video card, not in main memory.
In your example, you need to change CreateCompatibleBitmap to CreateDIBSection if you want direct access to the bits.
Just for information.
When loading bitmap from file and want to use BITMAP .bmBits (for glTexImage2D, glDrawPixels):
LoadImage(NULL, "path_to.bmp", IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE);
u must specify flag LR_CREATEDIBSECTION
HBITMAP hBmp = NULL;
BITMAP BMp;
hBmp = (HBITMAP) LoadImage(NULL, "bitmap.bmp", IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);
GetObject(hBmp, sizeof(BMp), &BMp);
//BMp.bmBits now points to data
From GetObject documentation on MSDN. Please note the second paragraph.
If hgdiobj is a handle to a bitmap created by calling CreateDIBSection, and the specified buffer is large enough, the GetObject function returns a DIBSECTION structure. In addition, the bmBits member of the BITMAP structure contained within the DIBSECTION will contain a pointer to the bitmap's bit values.
If hgdiobj is a handle to a bitmap created by any other means, GetObject returns only the width, height, and color format information of the bitmap. You can obtain the bitmap's bit values by calling the GetDIBits or GetBitmapBits function.
One thing which you could do is to look at the return value of GetObject. If 0 you know something has gone wrong. Something wrong with the parameters of the call.

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.

How to convert HICON to HBITMAP in VC++?

How to convert HICON to HBITMAP in VC++?
I know this is an FAQ but all the solutions I've found on Google don't work. What I need is a function which takes a parameter HICON and returns HBITMAP.
Greatest if possible to make conversion to 32-bit bitmap even the icon is 24-bit, 16-bit or 8-bit.
This is the code, I don't know where it goes wrong:
HBITMAP icon_to_bitmap(HICON Icon_Handle) {
HDC Screen_Handle = GetDC(NULL);
HDC Device_Handle = CreateCompatibleDC(Screen_Handle);
HBITMAP Bitmap_Handle =
CreateCompatibleBitmap(Device_Handle,GetSystemMetrics(SM_CXICON),
GetSystemMetrics(SM_CYICON));
HBITMAP Old_Bitmap = (HBITMAP)SelectObject(Device_Handle,Bitmap_Handle);
DrawIcon(Device_Handle, 0,0, Icon_Handle);
SelectObject(Device_Handle,Old_Bitmap);
DeleteDC(Device_Handle);
ReleaseDC(NULL,Screen_Handle);
return Bitmap_Handle;
}
this code do it:
HICON hIcon = (HICON)LoadImage(instance, MAKEINTRESOURCEW(IDI_ICON), IMAGE_ICON, width, height, 0);
ICONINFO iconinfo;
GetIconInfo(hIcon, &iconinfo);
HBITMAP hBitmap = iconinfo.hbmColor;
and this is the code in the *.rc file:
IDI_ICON ICON "example.ico"
and this is the code in the *.h file:
#define IDI_ICON 4000
HDC hDC = GetDC(NULL);
HDC hMemDC = CreateCompatibleDC(hDC);
HBITMAP hMemBmp = CreateCompatibleBitmap(hDC, x, y);
HBITMAP hResultBmp = NULL;
HGDIOBJ hOrgBMP = SelectObject(hMemDC, hMemBmp);
DrawIconEx(hMemDC, 0, 0, hIcon, x, y, 0, NULL, DI_NORMAL);
hResultBmp = hMemBmp;
hMemBmp = NULL;
SelectObject(hMemDC, hOrgBMP);
DeleteDC(hMemDC);
ReleaseDC(NULL, hDC);
DestroyIcon(hIcon);
return hResultBmp;
I don't have code readily available to share, but I think this is pretty easy. You have to create the HBITMAP, create a device context, select the bitmap into the DC (this will make the bitmap the drawing area for this DC). Finally call the DrawIcon() function to draw your icon on this DC. After that detach the bitmap from the DC and destroy the DC. Your bitmap now should be ready to go.
Update after looking at your code:
I believe the problem is in the createCompatibleBitmap call. You are asking for a bitmap compatible with the memory DC, but memory DCs start with a 1 bit/pixel bitmap selected into them. Try asking for a bitmap compatible with the screen DC instead.
Update 2: you may want to look at this question as it seems related to your problem.
I found this(similar code works for me - 32x32 icons with or without alpha data):
used CopyImage (msdn link)
HICON hICON = /*your code here*/
HBITMAP hBITMAPcopy;
ICONINFOEX IconInfo;
BITMAP BM_32_bit_color;
BITMAP BM_1_bit_mask;
// 1. From HICON to HBITMAP for color and mask separately
//.cbSize required
//memset((void*)&IconInfo, 0, sizeof(ICONINFOEX));
IconInfo.cbSize = sizeof(ICONINFOEX);
GetIconInfoEx( hICON , &IconInfo);
//HBITMAP IconInfo.hbmColor is 32bit per pxl, however alpha bytes can be zeroed or can be not.
//HBITMAP IconInfo.hbmMask is 1bit per pxl
// 2. From HBITMAP to BITMAP for color
// (HBITMAP without raw data -> HBITMAP with raw data)
// LR_CREATEDIBSECTION - DIB section will be created,
// so .bmBits pointer will not be null
hBITMAPcopy = (HBITMAP)CopyImage(IconInfo.hbmColor, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
// (HBITMAP to BITMAP)
GetObject(hBITMAPcopy, sizeof(BITMAP), &BM_32_bit_color);
//Now: BM_32_bit_color.bmBits pointing to BGRA data.(.bmWidth * .bmHeight * (.bmBitsPixel/8))
// 3. From HBITMAP to BITMAP for mask
hBITMAPcopy = (HBITMAP)CopyImage(IconInfo.hbmMask, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
GetObject(hBITMAPcopy, sizeof(BITMAP), &BM_1_bit_mask);
//Now: BM_1_bit_mask.bmBits pointing to mask data (.bmWidth * .bmHeight Bits!)
BM_32_bit_color bitmap may be have Alpha *channel*(each 4th byte) already set! So - check for it before u add mask bit to color data.