GDI+ only draws monochrome on memory DC - c++

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.

Related

GDI+ Graphics Obj - antialias lost when drawing straight to DC with GDI

We're currently shifting an older application to draw through GDI+, instead of using GDI directly. As we are progressively translating teh system as we go, sometimes we need to grab a HDC from a Gdiplus::Graphics object in order to allow code which hasn't been translated yet to draw straight to it using GDI.
The drawing happens fine, except we seem to be losing antialias on images drawn directly to the DC using GDI. If, after grabbing the DC from the Graphics object, we draw a filled rectangle over the entire area, and then proceed with the drawing, it comes out fine. If we just get straight to drawing everything comes in without antialias.
void Draw(Gdiplus::Graphics& renderContext)
{
auto hdc = renderContext.GetHDC();
auto dc = HDC::FromHandle(nativeDC);
//Required to antialias drawing below
dc->FillSolidRect(GetClientRect(), RGB(255, 255, 255));
/* Do Drawing */
dc ->Detach();
renderContext.ReleaseHDC(hdc );
}
To be more precise, it seems like the antialias alpha get's flattened, which explains why filling a rectangle in GDI before drawing removes the artifacts.
It's a similar effect you might get if you draw with antialias on onto one Bitmap, and then try to alphablend it on top of another Bitmap - the alpha information on the top gets flattened and then the new alpha value specified in a ColorMatrix is applied to the whole image.
If anyone could provide some insight into what exactly is going on when you grab/release a HDC from a graphics object, and why drawing with GDI directly looses the alpha, I'd be appreciated.
The bitmap backing the HDC is a copy, not the original, as described here.
Using GDI on a GDI+ Graphics Object Backed by a Bitmap
When Graphics::GetHDC() is called for a Graphics object that is backed
by a bitmap rather than the screen, a memory HDC is created and a new
HBITMAP is created and selected into the memory HDC. This new memory
bitmap is not initialized with the original bitmap's image but rather
with a sentinel pattern, which allows GDI+ to track changes to the
bitmap. Any changes that are made to the memory bitmap through the use
of GDI code become apparent in changes to the sentinel pattern. When
Graphics::ReleaseHDC() is called, those changes are copied back to the
original bitmap... Also, there is a performance cost to this approach because
GDI+ must copy the changes back to the original bitmap.
Obviously when you draw on an HDC with GDI you won't get any antialiasing. GDI doesn't support it. That's what GDI+ is for.

How to create Gdiplus::Bitmap from CDC

I am trying to create a Gdiplus::Bitmap from a device context. The function I use is:
Bitmap bitmap((HBITMAP)myDC.GetCurrentBitmap(), (HPALETTE)myDC.GetCurrentPalette());
...but when I draw the bitmap on the screen, all I see is a black rectangle. I think I'm using the Bitmap constructor wrong, because in docs it's written:
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.
But I have no idea how to go around this. Another approach I tried is using:
To capture the preexisting image from a window, a Windows Graphics Device Interface (GDI) function such as BitBlt() or StretchBlt() would have to be used to copy the image from the screen to a memory bitmap. This memory bitmap could then be used in the overloaded Bitmap constructor, which takes an HBITMAP as a parameter.
But I couldn't achieve this either.

What's the best way to store, strech and display a bitmap using GDI?

I'm not exactly a windows programming expert ;) so please excuse me if this question is trivial.
I have a window on which I want to display a bitmap. The bitmap can be either stretched to span over the whole area or it will be displayed with original aspect ratio and empty space will be filled with a solid brush.
I load the bitmap with LoadImage and display it using BitBlt. I do it on WM_ERASEBKGND, I use the DC from wparam which is a mem DC.
I was thinking of using StretchBlt instead of BitBlt when the bitmap needs to be stretched.
So my questions are:
is it okay to store a bitmap as HBITMAP or should I use DIBSection? Which one is better performance-wise, what's the memory footprint of both solutions?
should I always stretch the bitmap when drawing to a DC on WM_ERASEBKGND, or should I create a compatible DC (or bitmap) with a precomputed stretched bitmap and then just BitBlt it to the target DC?
if I create a precomputed bitmap, should I immediately unload the original bitmap to save on memory?
should I use BitBlt/StretchBlt or CopyImage?
I read that I can also use LoadImage with desired width and height parameter, so that the loaded image is already stretched, is this solution better?
I'd be really grateful for some code examples.
Edit:
GDI+ solutions are welcome too. I'm interested in the best, easiest and best-performing solution for this problem.

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!

Bitmap not drawing in CFrameWnd's OnPaint

I can't seem to get the CBitmap to actually apear on the screen... Here's the code in OnPaint:
CRect frm;
GetClientRect(frm);
CClientDC dc(this);
//dc.FillSolidRect(CRect(0, 0, 1000, 1000), RGB(255, 255, 255));
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
CBitmap* cache = dcMem.SelectObject(&components.icon.bmp);
dc.BitBlt(0, 0, 55, 55, &dcMem, 0, 0, SRCCOPY);
dc.SelectObject(cache);
The definition of components.icon.bmp is...
components.icon.bmp.LoadBitmap(BMP_BOARDER);
BITMAP icon;
components.icon.bmp.GetBitmap(&icon);
There shouldn't be problem here. The Bitmap is a 32bit alpha bitmap and LoadBitmap returned TRUE.
It doesn't work with 24 bit either.
---- Got it! Problem with my resources
This code seems good to me but there are still many things that can be wrong. Test your code to know where the problem is:
- If you draw anything else (a rectangle for instance) does it appears on the frame window?
- Try to save the bmp to a file and see if it's ok.
- Try to convert the bmp to 24bits using a tool and see if it works.
Once you know where the problem is you'll be able to focus on the problem.
Probably you won't be able to draw a 32bits bmp using GDI but you should get something (a black square at least). Anyway, if you need to draw 32bits alpha-channel bmps using GDI you have to pre-multiply the image and use AlphaBlend method instead of BitBlt.