I need to capture the screen of a windows given its HWND handle and store the capture in a ID2D1Bitmap object in order to draw this bitmap by means of my render target.
How can I achive this result?
Direct2D doesn't provide such functionality.
A possible way to go is if you first capture the screen via GDI (1) and then create a ID2D1Bitmap from the returned bitmap handle (2).
Getting a HBITMAP - Check this answer: https://stackoverflow.com/a/5164267/3962893. You need the part till the HBITMAP hbDesktop = CreateCompatibleBitmap( hdc, width, height); The hbDesktop variable will contain a handle to the screen captured bitmap.
Creating an ID2D1Bitmap from a HBITMAP - check this answer: https://stackoverflow.com/a/27500938/3962893. It copies an icon to a ID2D1Bitmap, but the workflow is identical. Except:
hIcon := SendMessage(Handle, WM_GETICON, ICON_BIG, 0);
....
wicFactory.CreateBitmapFromHICON(hIcon, wicBitmap);
that you have to change to:
wicFactory.CreateBitmapFromHBITMAP(hbDesktop, wicBitmap);
Related
My intention is to create a toolbar in Win32 containing a transparent icon. I have tried the following code to create a simple toolbar with one button having a custom image:
// Create the toolbar
HWND hToolbar = CreateWindow(TOOLBARCLASSNAME,
NULL,
WS_CHILD | TBSTYLE_FLAT | TBSTYLE_AUTOSIZE | TBSTYLE_LIST | CCS_BOTTOM,
0, 0, 0, 0,
hwnd,
NULL,
ghInstance, // <-this is the HINSTANCE of the application
NULL);
// Set the font (this cannot be the problem)
SendMessage(hToolbar,
WM_SETFONT,
(WPARAM)hFontBold,
static_cast<LPARAM>(MAKELONG(TRUE, 0)));
auto hImagelist = ImageList_Create(32, 32,ILC_COLOR24 | ILC_MASK, 1, 0);
HBITMAP bitmap = static_cast<HBITMAP>(LoadImage(ghInstance,
/* ID_IMG_SPAWN is my custom resource -> */ MAKEINTRESOURCE(ID_IMG_SPAWN),
IMAGE_BITMAP,
32, 32,
NULL));
ImageList_AddMasked(hImagelist,
bitmap,
RGB(255,255,255) /* white is the transparent color */);
SendMessage(hToolbar,
TB_SETIMAGELIST,
static_cast<WPARAM>(0),
(LPARAM)hImagelist);
ImageList_Create only supports 24-bit bitmaps, which means there is no alpha channel for transparency. However, I can simulate a transparency effect by using a mask color via ImageList_AddMasked. (Here, I am setting white (RGB(255, 255, 255)) to the mask color.)
This worked fine, but the image displayed this way is extremely sharp/jagged because of the lack of granularity in the alpha channel (each pixel is either transparent or fully opaque).
I understand that the PNG format can solve this, since it provides a true alpha channel. I know that the PNG format is supported by Win32 ImageLists, but I don't know how to use it properly. (PNG resources can be added to Visual Studio resources, but I don't know how to use them from code.)
I couldn't find any way to make LoadImage load a PNG. The only supported types are IMAGE_BITMAP IMAGE_CURSOR and IMAGE_ICON. I changed the resource (ID_IMG_SPAWN) to a PNG file and tried each of those three types one by one, but all resulted in merely a blank display like this:
Can anyone help me out? How can I use LoadImage to load a transparent PNG and use it as a toolbar image?
ImageList_Create only supports 24-bit bitmaps, which means there is no alpha channel for transparency.
No, that's wrong. ImageList_Create supports 32-bit bitmaps as well.
Since you intend to create a toolbar in Win32 containing a transparent icon, you do NOT need to load a PNG at all. If you desire PNG you may have to work around with GdiPlus as #barmak says.
32-bit bitmap has 8 bits for ALPHA. Using 32-bit bitmaps can make the same effect as PNG does.
You say your button image was showing blank when you did these:
changed ILC_COLOR24 to ILC_COLOR32
changed the resource of ID_IMG_SPAWN to a 32-bit bitmap
IN FACT To show a 32-bit bitmap properly, you have to:
change ILC_COLOR24 to ILC_COLOR32
change the resource of ID_IMG_SPAWN to a 32-bit bitmap with premultiplied alpha.
create a DIB section to your bitmap when loading
(Win32's format requirement is very strict)
Q: How to create a DIB section to the bitmap?
A: Specify LR_CREATEDIBSECTION in the last parameter of LoadImage.
Explanation:
LoadImage((HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE),MAKEINTRESOURCE(ID_IMG_SPAWN), IMAGE_BITMAP,32, 32,NULL)
This is your code of the LoadImage function. See the MSDN document of LoadImage, to create the DIB section, all you need is to specify LR_CREATEDIBSECTION in the last parameter of LoadImage.
Q: How to get a BMP with premultiplied alpha?
A: Pixelformer can help you convert your alpha-channeled file to a premultiplied-alpha BMP.
The steps are
Open your image (any format) in Pixelformer and choose Export from the menu
Select A8:R8:G8: B8 (32bpp) and Premultiplied Alpha, then click Ok.
Then you can save your BMP file! Import this BMP file to Visual Studio resources, replacing your previous 24-bit BMP.
Then, you don't need to use the ImageList_AddMasked (which makes the image sharp) anymore, because you already have a recognizable ALPHA in your 32-bit BMP. So, straight use ImageList_Add.
Okay, after the manipulations explained above your code should be this:
// Create the toolbar
HWND hToolbar = CreateWindow(TOOLBARCLASSNAME,NULL,
WS_CHILD | TBSTYLE_FLAT | TBSTYLE_AUTOSIZE | TBSTYLE_LIST | CCS_BOTTOM,
0, 0, 0, 0, hwnd, NULL, ghInstance, NULL);
// Set the font (this cannot be the problem)
SendMessage(hToolbar, WM_SETFONT, (WPARAM)hFontBold,
static_cast<LPARAM>(MAKELONG(TRUE, 0)));
auto hImagelist =
ImageList_Create(32, 32,ILC_COLOR32 /*DON'T NEED THE MASK. CHANGED TO ILC_COLOR32.*/, 1, 0);
HBITMAP bitmap = static_cast<HBITMAP>(LoadImage((HINSTANCE)GetWindowLong(hwnd,
GWL_HINSTANCE), MAKEINTRESOURCE(ID_IMG_SPAWN), IMAGE_BITMAP,
32, 32, LR_CREATEDIBSECTION /*THIS IS IMPORTANT*/ ));
ImageList_Add(hImagelist, bitmap, NULL);
SendMessage(hToolbar, TB_SETIMAGELIST, static_cast<WPARAM>(0), (LPARAM)hImagelist);
This worked fine as below.
These I answered above is well enough to solve this problem.
For more information about DIB bitmaps and Premultiplied Alpha, see the links.
LoadImage will return NULL when trying to load PNG resource.
You can add your PNG resource as ICON. Otherwise use Windows Imaging Component, or Gdiplus+ to load the png resource.
Read PNG resource as follows:
HBITMAP loadimage(HINSTANCE hinst, const wchar_t* name)
{
HBITMAP hbitmap = NULL;
ULONG_PTR token;
Gdiplus::GdiplusStartupInput tmp;
Gdiplus::GdiplusStartup(&token, &tmp, NULL);
if(auto hres = FindResource(hinst, name, RT_RCDATA))
if(auto size = SizeofResource(hinst, hres))
if(auto data = LockResource(LoadResource(hinst, hres)))
if(auto stream = SHCreateMemStream((BYTE*)data, size))
{
Gdiplus::Bitmap bmp(stream);
stream->Release();
bmp.GetHBITMAP(Gdiplus::Color::Transparent, &hbitmap);
}
Gdiplus::GdiplusShutdown(token);
return hbitmap;
}
...
auto hbitmap = loadimage(ghinst, MAKEINTRESOURCE(ID_PNG1 ));
if(hbitmap)
{
ImageList_AddMasked(himage, hbitmap, 0);
DeleteObject(hbitmap);
}
Resource definition should look like this:
ID_PNG1 RCDATA "file.png"
For a past few days I was looking for an option to use DirectX 2D in a pattern where I can render to 'something' and then this 'something' - use as input to other drawing.
All of RenderTargets like Hwnd, Dc, Wic bitmap - does not allow to do it (Wic target does not use HW acceleration).
Only one way I found is D2D1DeviceContext
M$ document
There I can create ID2D1Bitmap1 (which also, when created with CPU_READ flag - can be mapped and read) which can be set as target.
So far - so good, all is working fine.
However, at the end, we all want to display the result of drawing onto a user screen and for this purpose - we need to pass it to some WinAPI DC.
I've done it this way.
Create ID2D1Bitmap1 with GDI compatibility
D2D1_BITMAP_PROPERTIES1 bitmapProperties =
D2D1::BitmapProperties1(
D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_GDI_COMPATIBLE,
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)
);
Get from ID2D1DeviceContext - ID2D1GdiInteropRenderTarget
_d2d1context->QueryInterface(IID_PPV_ARGS(&_d2d1GDIinterface))
And finally - using this interface, I get DC and use GDI function BitBlt
void paste_bitmap_into_dc(HDC dc, ID2D1Bitmap& bitmap) {
_d2d1context->SetTarget(&bitmap);
_d2d1context->BeginDraw();
HDC new_dc;
_d2d1GDIinterface->GetDC(D2D1_DC_INITIALIZE_MODE_COPY, &new_dc);
BitBlt(dc, 0, 0, bitmap.size().x, bitmap.size().y, new_dc, 0, 0, SRCCOPY);
RECT update_rect;
SetRect(&update_rect, 0, 0, 0, 0); // i don't want to actually update bitmap on ID2D1DeviceContext
_d2d1GDIinterface->ReleaseDC(&update_rect);
_d2d1context->EndDraw();
}
That works but I'm not sure that this is a proper (fastest) way, since there is DC created and its coppied twice (hopefuly - on HW side): from target ID2D1Bitmap to this new DC and then from new DC to input DC.
And actually this method is more for drawing with GDI on D2D1 content (ReleaceDC have argument which part of bitmap to update).
Someone could help/confirm this is the way?
Thanks in advance!
I am developing an MFC application, where I need to capture the desktop and save as a vector image.
I can capture screen shot as .BMP and write to clipboard with following code.
int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
HDC hDesktopDC = GetDC(NULL);
HDC hCaptureDC = CreateCompatibleDC(hDesktopDC);
HBITMAP hCaptureBitmap =CreateCompatibleBitmap(hDesktopDC,
nScreenWidth, nScreenHeight);
HGDIOBJ old_obj = SelectObject(hCaptureDC,hCaptureBitmap);
BitBlt(hCaptureDC,0,0,nScreenWidth,nScreenHeight,
hDesktopDC,0,0,SRCCOPY|CAPTUREBLT);
// save bitmap to clipboard
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hCaptureBitmap);
CloseClipboard();
// clean up
SelectObject(hCaptureDC, old_obj);
DeleteDC(hCaptureDC);
ReleaseDC(NULL, hDesktopDC);
DeleteObject(hCaptureBitmap);
But I am looking something similar code which would allow me to capture screenshot as vector image and place into clipboard; so, I can read as EMF from clipboard.
I tried searching for MFC functions to convert raster images to vector images from clipboard, but I have not found anything useful.
You can't. A screenshot gathers pixel data only. All information on how those pixels were generated (which is essentially what an EMF would store) is lost. It's not possible to recover the GDI calls issued to construct the final image.
Even if that were possible, EMF wouldn't be capable of representing rendering information for image data that is not produced by the GDI (e.g. Qt applications with an OpenGL/Direct2D rasterizer, WPF applications, or UWP applications).
i have an issue with a fix i made to allow a flood filled object be printed...
so, the full story is we were using the windows GDI FloodFill function, which we noticed doesnt work on printers, so what i found on the inet, was to create a memory DC, compatible with the printer DC, and make all my drawing operations on the memory DC, and then BitBlt it all at once to the printer DC (i had to change to use a recursive, color replacment flood fill function too, since the memory DC only allows what the main DC did)
the problem is the memory DC seems to be a pixel or two bigger on the x and y, but i dont know what to do, when i get the selected bitmap from the memory DC, it shows it to be the correct size, i would like to use StretchBlt, but the values i have access to use as params for StretchBlt, make it no different than calling BitBlt
let me know if you need more info...
thanks in advance!!!
heres my code:
HDC hMemPrnDC = CreateCompatibleDC (hPrnDC);
HBITMAP hBitmap = CreateCompatibleBitmap (hPrnDC, iWidthLP, iHeightLP);
HBITMAP hOldBitmap = SelectBitmap (hMemPrnDC, hBitmap);
// paint the whole memory DC with the window color
HBRUSH hBrush = CreateSolidBrush (GetSysColor (COLOR_WINDOW));
RECT rect;
// add one to right and bottom, FillRect doesnt include the right and bottom edges
SetRect (&rect, 0, 0, iWidthLP + 1, iHeightLP + 1);
// NOTE: im doing this cause it starts out as all black
FillRect (hMemPrnDC, &rect, hBrush);
// delete object
DeleteBrush (hBrush);
//
// do all my MoveToEx, LineTo, Ellipse, Arc, TextOut,
// SetPixel, etc calls on hMemPrnDC here
//
// copy all the memory DC drawing data to the printer DC
BitBlt (hPrnDC, 0, 0, iWidthLP, iHeightLP, hMemPrnDC, 0, 0, SRCCOPY);
// select old bitmap, and clean up objects
SelectBitmap (hMemPrnDC, hOldBitmap);
DeleteBitmap (hBitmap);
DeleteDC (hMemPrnDC);
hMemPrnDC = NULL;
UPDATE (Sept 5):
here is a link to a PDF print where I draw straight to the printer DC:
hPrnDC.pdf
and here is the same but I draw to the memory DC then BitBlt it to the printer DC:
hMemPrnDC.pdf
now, I did enable my recursive flood fill function on the second, to show an example of what we are trying to achieve, it does the same without it, so that is not an issue
as you can see, the bottom and right edge are cut off, I'm also concerned about the differences in font & line weight between the two, but not as much as the sizing mismatch
NOTE: the filename printed at the top doesn't go through the memory DC, that is always drawn straight to the printer DC
I found a solution to my problem, more of a work-around, but it achieved the desired results...
I only used the memory DC as a go between on the items that needed the recursive flood fill (GetPixel and SetPixel), so I draw them first to the memory DC, copy it all over to the printer DC and then draw everything else straight to the printer DC, seems to work just fine
thanks for the help!
well i have a question about transparency in a bitmap file which i want to save as png file, at the moment i take a screenshot of the cursor in the system and save it as png, this work fine whit arrow, hand and other cursors but in the moment of use cursors like I-beam or "not" cursor this have a problem, the explanation is that each cursor have a mask and a color bitmap which combine and result in a transparent cursor but I-beam and others no have color bitmap, them have only mask that contains the color and mask in the same bitmap, i refer this post: C# - Capturing the Mouse cursor image well they use C# but the idea is the same.
in my code i use C++ and i manage to create a cursor but with white background color, i don't know how to convert it in transparent color, in the post i refer use a function MakeTransparent, any idea? thanks for the help :D
CURSORINFO cursor;
ICONINFO cursorIconInfo;
HICON cursorIcon;
cursor.cbSize=sizeof(CURSORINFO);
GetCursorInfo(&cursor);
GetIconInfo(cursor.hCursor,&cursorIconInfo);
//cursorIcon=CopyIcon(cursor.hCursor);
//GetIconInfo(LoadCursor(NULL,IDC_ARROW),&cursorIconInfo);
//cursorIcon=CreateIconIndirect(&cursorIconInfo);
CxImage* imag=new CxImage();
/*imag->CreateFromHICON(cursorIcon);
imag->Save("cursor.png",CXIMAGE_FORMAT_PNG);*/
BITMAP bm;
//CImage* imag=new CImage();
GetObject(cursorIconInfo.hbmMask,sizeof(BITMAP),&bm);
if(bm.bmHeight == bm.bmWidth*2){
HDC screendc=CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
HDC cursormaskDC=CreateCompatibleDC(screendc);
HDC cursorfinalDC=CreateCompatibleDC(screendc);
HBITMAP cursormask=CreateCompatibleBitmap(screendc,bm.bmWidth,bm.bmWidth);
HBITMAP cursorfinal=CreateCompatibleBitmap(screendc,bm.bmWidth,bm.bmWidth);
SelectObject(cursormaskDC,cursorIconInfo.hbmMask);
SelectObject(cursorfinalDC,cursorfinal);
BitBlt(cursorfinalDC,0,0,bm.bmWidth,bm.bmWidth,cursormaskDC,0,bm.bmWidth,SRCCOPY);
BitBlt(cursorfinalDC,0,0,bm.bmWidth,bm.bmWidth,cursormaskDC,0,0,SRCINVERT);
/*cursorIconInfo.hbmColor=cursorcolor;
cursorIconInfo.hbmMask=cursormask;
cursorIcon=CreateIconIndirect(&cursorIconInfo);
imag->CreateFromHICON(cursorIcon);
imag->Save("cursorPrub.png",CXIMAGE_FORMAT_PNG);*/
imag->CreateFromHBITMAP(cursorfinal);
imag->Save("cursor.png",CXIMAGE_FORMAT_PNG);
DeleteObject(cursorIconInfo.hbmMask);
DeleteObject(cursorIconInfo.hbmColor);
DestroyIcon(cursorIcon);
imag->Destroy();
return;
}
Use GDIPlus. It work correct with alpha. Gdi works incorrect with alpha sometime.