BitBlit/Copy from D2D1DeviceContext target D2D1Bitmap1 to HDC - c++

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!

Related

Draw semitransparently in invisible layered window

My goal is to have a fullscreen overlaying invisible "canvas" on which I can draw using win32's various drawing functions.
The way I am currently attempting it is this:
WNDCLASSA myclass = { 0 };
myclass.lpfnWndProc = WindowProc3;
myclass.hInstance = GetModuleHandle(0);
myclass.lpszClassName = "MyCanvas";
myclass.hbrBackground = CreateSolidBrush(0xFEEDBEEF);
myclass.hCursor = LoadCursor(0, IDC_ARROW);
RegisterClassA(&myclass);
...
HWND wnd = CreateWindowExA(WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT, "MyCanvas", 0, WS_POPUP | WS_VISIBLE, 0, 0, screen_width, screen_height, 0, 0, GetModuleHandle(0), 0);
SetLayeredWindowAttributes(wnd, 0xFEEDBEEF, 0, LWA_COLORKEY);
Although this serves as a canvas, hours of googling later, I am still unable to draw on it semitransparently.
I have added a screenshot of what my program is currently displaying as I am writing this. What I would like to be able to do is, for example, make the black box in the top right corner (drawn with Rectangle) semitransparent so as to reveal the stackoverflow page content below it.
This is a question I found that I was hopeful about, but the resulting text is just a blended combination of the background color ((COLORREF)0xFEEDBEEF) and text color. Other things I have found have either just made the element fully invisible, done nothing at all, or required some library like MFC. I want to only use win32 functions if at all possible, as I would like to be able to achieve the highest FPS possible.
I do not care if this doesn't work on all Windows versions as long as it does on 7 up to 10.
If you only need transparency for a rectangular area where all pixels either have the same transparency (aka alpha) value or are completely transparent, you can use SetLayeredWindowAttributes() with a combination of alpha value and/or color key.
UpdateLayeredWindow() is the way to go if you need to be able to define transparency per-pixel.
For that you have to create memory DC and select a 32bpp bitmap into it. You may use the buffered paint API to ease the task. Raymond Chen has a blog post with a code sample about that.
You can draw into the memory DC, but you can't use most of GDI API for that, because GDI ignores the alpha channel (transparency). I suggest using GDI+ which allows you to specify the alpha values.
After you have completed drawing into the memory DC, you would call UpdateLayeredWindow() and pass that memory DC as the argument for the hdcSrc parameter to make the result visible on screen.
Illustration of possible effects:
SetLayeredWindowAttributes( hwnd, 0, 176, LWA_ALPHA );
SetLayeredWindowAttributes( hwnd, colorkey, 0, LWA_COLORKEY );
SetLayeredWindowAttributes( hwnd, colorkey, 176, LWA_ALPHA|LWA_COLORKEY );
UpdateLayeredWindow( ... )
Note the antialiased edge of the shape and the transparency gradient in the last example. Things like that are only possible with UpdateLayeredWindow().

Screen capture to Direct2D compatible bitmap

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

BitBlt issues with Memory DC created from printer DC

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!

GDI+ only draws monochrome on memory DC

I'm trying to do some double buffering in an MFC application and trying to draw on the memory DC with GDI+. However, although I called CreateCompatibleDC(), I'm only getting a monochrome image. Here is the code:
CDC bufferDC;
CBitmap bufferBitmap;
bufferDC.CreateCompatibleDC(&dc);
bufferBitmap.CreateCompatibleBitmap(&bufferDC, 300, 300);
bufferDC.SelectObject(bufferBitmap);
Graphics g(bufferDC);
g.Clear(Color::Green);
dc.BitBlt(0, 0, 300, 300, &bufferDC, 0, 0, SRCCOPY);
Instead of a green patch, I see a rectangle of dithered black and white dots. I even tried to save the bitmap to disk after the g.Clear() call. It is indeed a 1-bit depth file.
Any ideas what went wrong? Thanks.
A common mistake. A memory DC takes on the properties of the bitmap selected into it, no matter what compatibility it was created with. The default bitmap selected into a DC is monochrome. If you create a bitmap compatible with that DC, it will be monochrome too.
Create the bitmap to be compatible with the original DC, not the memory DC.
Both the bitnmap and the bufferDC should be compatible with dc (whatever device it refers to), not the bitmap compatible ... with its own DC.
Try to give &dc to CreateCopmpatibleBitmap.
Your code snippet does not show where the dc variable comes from. ThIs guy probably contains a monochrome bitmap, the default. You dont need it anyway. Instead, pass NULL to CreateCompatibleDC and it will be the same format as your display, which is probably color.

Unit testing of control drawing in GDI

I have a control written in C++ using WinAPI and I would like to automatically test that it is being drawn properly. I can either compare the drawn image with saved reference images or simply test that specific pixels have specific color. I have both types implemented.
The problem is that the tests now also run every night on a virtual machine which for some reason has only 16bit color depth. This causes the colors to be slightly off. I have tried to come up with colors that wouldn't be changed when drawn in 16bit color depth, but the rounding scheme seems to be rather complicated and I need the tests to be functional in both 32b an 16b color depths.
Another idea was to create an offscreen bitmap that would always have 32b color depth. It would have the benefit that the tests would use the same environment every time,but I couldn't get that to work. How can I create a 32b HBITMAP and HDC regardless of the screen color depth? Or do you have any other tips how to solve the general problem?
Thanks
How can I create a 32b HBITMAP and HDC
regardless of the screen color depth?
it's simple:
BITMAPINFO bmp_info = {};
bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmp_info.bmiHeader.biWidth = width;
bmp_info.bmiHeader.biHeight = height;
bmp_info.bmiHeader.biPlanes = 1;
bmp_info.bmiHeader.biBitCount = 32;
bmp_info.bmiHeader.biCompression = BI_RGB;
HDC mem_dc = CreateCompatibleDC(0);
void *dummy;
HBITMAP bitmap_handle = CreateDIBSection(mem_dc, &bmp_info, DIB_RGB_COLORS, &dummy, NULL, 0);
SelectObject(mem_dc, bitmap_handle));
now draw your button over this DC. Remember to check for errors and to release resources.
Or alternatively, automatically convert your button reference view to actual desktop mode:
HWND desktop = GetDesktopWindow();
HDC desktop_dc = GetDC(desktop);
HDC mem_dc = CreateCompatibleDC(desktop_dc);
RECT rect;
GetClientRect(desktop, &rect);
HBITMAP bitmap_handle = CreateCompatibleBitmap(desktop_dc, rect.right - rect.left, rect.bottom - rect.top);
SelectObject(mem_dc, bitmap_handle);
and now BitBlt your preloaded image over mem_dc. It will be automatically converted to current desktop color mode
I've done GDI unit testing by drawing into a WMF (now EMF) file. It did replicate the resolution and DPI of a source (and the later destination) device, but I don't recall if color depth was a "sticky" attribute. Even if it was, since the fileformat allows you to capture/replay the GDI sequence, you might have a more accurate unit test in the end anyways. We'd interpret the WMF file to make sure we generated what we thought we should.
CreateEnhMetaFile is a starting point.
What about using GDI+ to create the offscreen bitmap, and then drawing to it with GDI - something like this:
int width=64; // or whatever you need
int height=100;
int stride = width*4;
BYTE buffer[stride*height];
Gdiplus::Bitmap bitmap(width, height, stride, PixelFormat32bppARGB, buffer);
Gdiplus::Graphics g (&bitmap);
HDC dc = g.GetHDC();
// drawing code, using WinAPI, to draw to dc
g.ReleaseHDC();
// Now compare the contents of your buffer
More about gdi / gdi+ interop here: http://support.microsoft.com/kb/311221
Change your test to pass if comparison against the 32 bit image or against the 16 bit image is OK. Capture both 32 bit versions and 16 bit versions (running virtual). This is really quick and easy to implement.
You should already have an automatic way to capture the reference images for a known good version of the code. If you don't, do that now, as it will save you time later when you make small changes to the appearance of the control. You now have the reference for regression testing.
Either create a 32bpp offscreen surface like you said, or perform the exact same operation with your comparison reference image so you are testing both at the same bpp. In other words don't do your own rounding; let the GDI system do the same rounding to both surfaces.