How to create memory DC with 24 bits per pixel? - c++

I need it to work with RGB24 data using GDI functions (specifically StretchBlt() which is pretty fast) and I can't use CreateCompatibleDC() since it can create memory DC only with color depth of other DC. Usually it's used with screen DC (by transmitting NULL pointer to function) and usually screen has color depth of value 32. In addition I can't rely on it, 'coz if screen settings are changed my application probably won't work.
So I need some way to create memory DC with specific certain color depth. So far I've found only one way with using CreateDC() function but it requires many device specific parameters and seems somewhat unreliable for me. Moreover there are too many fields to be filled with appropriate values to call CreateDC().
Is there some easier way to create specific memory DC and not rely on some devices? Or even if to create memory DC with 24 bpp?
P.S. I need it for some fast graphics. I've tried manual adding alpha channel to bitmap for using it with compatible to screen 32bpp memory DC and it worked out, but was too slow. And as I said above, I can't rely on screen settings which can be changed.

Bits-per-pixel does not really depend on a DC, but on the bitmap selected into it. Create a 24bpp bitmap with CreateDIBSection then select it into a memory DC.

Related

Real time drawing in GDI

I'm currently writing a 3D renderer (for fun and research), so I need a way to draw my framebuffer to a window. Since I'm doing all of my calculations on CPU, the drawing needs to be as fast as possible.
One of my goals is to use no existing graphics library (OpenGL/DirectX) so the drawing to the screen is pure Win32. In my research I've found a couple of ways to create and draw bitmaps and now I'm looking for the best one.
My current implementation uses a bitmap created with CreateDIBSection(), which is drawn to my window DC using BitBlt().
CreateDIBSection() give me a pointer to my bitmap bytes so I can manipulate it without copying. Using this method I achieve an update rate of about 260 FPS (without any rendering done).
This seems a bit slow, so I'm looking for optimizations.
I've read something about that if you don't create a bitmap with the same palette as the system palette, some slow color conversions are done.
How can I make sure my DIB bitmap and window are compatible?
Are there methods of drawing an bitmap which are faster than my current implementation?
I've also read something about DrawDibDraw(), can anyone confirm that this is faster?
I've read something about that if you don't create a bitmap with the same palette as the system palette, some slow color conversions are done.
Very few systems run in a palette mode any more, so it seems unlikely this is an issue for you.
Aside from palettes, some GDI functions also cause a color matching conversion to be applied if the source bitmap and the destination have different gamuts. BitBlt, however, does not do this type of color matching, so you're not paying a price for that.
How can I make sure my DIB bitmap and window are compatible?
You don't. You can use DIBs (which are Device-Independent Bitmaps) or compatible (device-dependent) bitmaps. It's possible that your DIB bitmap matches the current mode of your device. For example, if you're using a 32 bpp DIB, and your display is in that same mode, then no conversion is necessary. If you want a bitmap that's guaranteed to be in the same mode as your device, then you can't use a DIB and all the nice properties it provides for predictable pixel layout and format.
Are there methods of drawing an bitmap which are faster than my current implementation?
The limitation is most likely in getting the data from system memory to graphics adapter memory. To get around that limitation, you need a faster graphics bus, or you need to render directly into graphic memory, which means you'd need to do your computation on the GPU rather than the CPU.
If you're rendering a 1920 x 1080 pixel image at 24 bits per pixel, that's close to 6 MB for your frame buffer. That's an awful lot of data. If you're doing that 260 times per second, that's actually pretty impressive.
I've also read something about DrawDibDraw(), can anyone confirm that this is faster?
It's conceivable, but the only way to know would be to measure it. And the results might vary from machine to machine because of differences in the graphics adapter (and which bus they use).

Alpha channel in DeviceContext (HDC)

Please help me with alpha channel in HDC. I make HDC dc throw CreateCompatibleDC. Than call CreateDIBSection and can find bytes of image in memory. Than call DrawFrameControl to this dc. All works, but in memory there are 4 bytes per pixel and alpha channel fills by 00. Even if there were FF before. But I need alpha channel. How can I make DrawFrameControl set real alpha values or just don't touch them. Thank you. And sorry for bad english :(
You can't make GDI not write to the alpha / reserved byte of a four-byte-per-pixel bitmap. GDI is not really alpha-aware, with the exception of a couple of functions like AlphaBlend. However, you can use the knowledge that it writes to and resets the alpha to 0 to know which pixels it wrote to, and manually fix the alpha afterwards.
For more information, read these three articles:
Transparent Graphics with GDI, Part 1
Transparent Graphics with GDI, Part 1 1/2
Transparent Graphics with GDI, Part 2
The first two probably give you enough information to achieve what you want.
These articles take a generic approach to handling alpha with GDI functions, by scanning for pixels where the alpha was clobbered and fixing it (and goes into more advanced techniques to draw several things on top of each other, with correct alpha.) FrameRect draws a rectangle where the lines are one unit wide and high. You might find it more efficient to draw using lines, or even directly editing the pixel bitmap in memory, to draw straight lines in memory. That avoids having to scan the whole bitmap for GDI-drawn pixels - after all, since it's a rectangle with one-unit-wide-edges you know exactly which pixels will be drawn to already, and can edit them yourself.

Draw to HDC with user allocated memory

I have an windowless IViewObject object.
I want to call its Draw function to render into Opengl PBO memory (alternatively OleDraw is simpler).
Right now i first create an HBITMAP using CreateDIBSection (which allocates its own memory) and then copy from this into my PBO memory. However, I'd like to avoid this extra copy. I believe this should be possible in theory as mapped PBOs live in page-locked memory.
This is what I do today simplified:
hdc = CreateCompatibleDC(nullptr); // Get current DC
HBITMAP bitmap = CreateDIBSection(... hdc, &bitmap_data_...); // Create Bitmap
SelectObject(hdc, bitmap);
m_spViewObject->Draw(... hdc ...); // IViewObject->Draw to bitmap
memcpy(pbo_ptr, bitmap_data_, size);
My question is if its possible to have the Draw function to somehow draw to memory which i have specified (in this case the PBO) instead of memory allocated by the win32 api (CreateDIBSection)?
One way might be if it is possible to create a device independent bitmap from user specified memory? However, I have found no function that does this.
What you're trying to do is very logical IMHO. However unfortunately AFAIK there's no conventional way to do this.
According to WinAPI DIB sections either allocate their own memory or work with the file mapping. OTOH OpenGL manages its memory in its own way.
There exists an option to create a DIB from any memory block, but it's for video drivers only, not accessible to the user-mode applications.
There's actually a very complex possibility to avoid this extra-copy. You may create a virtual video driver (not to be confused with mirror video driver), obtain its HDC. Then in you 'custom' way tell it where it should draw (via Escape). And then you may pass this HDC to whatever you want to do the drawing, your virtual video driver will draw directly on the memory you want.
Moreover, to do the actual drawing you will be able to use the OS functions. Wrap your memory by GDI-recognizable bitmap (EngCreateBitmap), and draw on it using OS (EngXXXX), so that you'll have to implement only the minimum functionality in your driver.
However this is a driver development anyway. I believe that copying a memory block is fast enough (comparison to the actual drawing), so that you can leave your code as-is.

Understanding Device Contexts

As a relative newcomer to MFC, I see Device Contexts (DCs) a lot. I vaguely understand that it's something to do with drawing, but the specifics are not very well explained anywhere that I can find. What does creating a "compatible Device Context" mean, and why is it important? What does SelectObject do, and how must I make a DC compatible first?
A Device Context is just a place that drawing occurs, so if you have two different DC's, you're drawing in two different places. Kind of like a file handle.
Device Contexts can refer to real-estate on screen, or to bitmaps that just reside in memory, and probably other places, too, those are just the two I can think of at the moment.
Compatible contexts are ones that have the same underlying pixel organization, by which is meant number of bits per pixel, bytes per pixel, color organization and so forth. Memory bitmap device contexts can have any organization you want, but your screen contexts are going to be related (eventually) to buffers on your graphics card, which will (depending on mode, etc) have a very specific pixel organization.
Having compatible contexts means its efficient to transfer image data between them, because little or no translation of the data is required. At the other extreme, if you have a 256 color palette, 8 bit map and you try to blit it to a screen that has 8 bits each of RGBA per pixel, every last pixel will require significant massaging as it is copied and so copying incompatible bitmaps is very much slower. According to the Win32 SDK documentation, at least BitBlt() and StretchBlt() "convert the source color format to match the destination format", so it can be done.
Investigate CreateCompatibleDC() and CreateCompatibleBitmap() as starting points for how to create drawing contexts that are compatible with already existing ones.
SelectObject() controls which resources are currently active within the device context. A context has a current pen, brush, font, and bitmap. These make a lot of the other GDI calls simpler by allowing you to specifiy fewer parameters. For instance, you don't have to specify the font when you use TextOut(), but if you want to change the font, that's where SelectObject() comes in. If you feed SelectObject() a handle to a font, the return value is a handle to the font that was in effect, and subsequent operations use the new font. Behavior is the same for the other kinds of resources, pens, brushes, etc.
(Old question but this is shown when googling...)
I'm afraid that, for beginners, the selected answer can be a bit misleading.
Please keep in mind that MFC wraps the Win32 API, so we need to see from the Win32 level, to better understand what's going on.
To understand why there is Device Context, we should understand GDI(Graphics Device Interface).
Then, why GDI exist? - For device independence. To achieve this, Microsoft made Graphic Objects (Brush, Pen, ...), each of which wraps and abstracts all the device dependency issues.
Now we don't have to care about different devices, and that's the whole point of GDI.
So we need to hold Graphic Objects(Brush, Pen, Bitmap,...) in some data structure, and that's the Device Context.
Then what is a SelectObject function?
Literally, it enables DC to "select" a Graphic Object. I.e, we use SelectObject to change a graphic object handle stored in the DC to another graphic object handle that we want to use.
Then what is a compatible device context?
compatible device context(=memory device context) uses memory, rather than devices.
From MSDN (emphasis mine):
To enable applications to place output in memory rather than sending
it to an actual device, use a special device context for bitmap
operations called a memory device context. A memory DC enables the
system to treat a portion of memory as a virtual device. It is an
array of bits in memory that an application can use temporarily to
store the color data for bitmaps created on a normal drawing surface.
Because the bitmap is compatible with the device, a memory DC is also
sometimes referred to as a compatible device context.
The memory DC stores bitmap images for a particular device. An
application can create a memory DC by calling the CreateCompatibleDC
function.
Compatible DC can be used, for example, to reduce flickering because we can save the bitmap in the memory and show it once, not showing every time the image changes.
Following MSDN docs would be helpful to newbies (including me).
Device Contexts, from the MFC viewpoint.
Device Contexts, from the Win32 viewpoint, and this following section.

Draw scaled images using CImageList

If you have images stored in a CImageList, is there an easy way to render them (with proper transparency) scaled to fit a given target rectangle? CImageList::DrawEx takes size information but I don't believe it does scaling, only cropping?
I guess you could render them to an offscreen bitmap, then StretchBlt() them to either your device or another offscreen bitmap, letting StretchBlt() do the scaling... Getting the transparency to carry over correctly will require some fiddling though, depending on your circumstances you may need to use AlphaBlend() instead.
My opinion is that most of the Win32 image handling code, and therefore by extension their MFC equivalents, like CImageList, CIcon, CImage, CBitmap, ... are inadequate for today's graphics needs. Especially handling per-pixel transparency hardly ever works consistently. I usually store my images in a CImage and use ::AlphaBlend() everywhere to get them to DC, or I use GetDIBits()/SetDIBits() and directly manipulate the RGBA entries (not very practical for doing scaling and similar operations, I admit). On the other hand I understand what it's like having to maintain code that uses these things already and wanting to update them to give them a bit of a modern look...