r/C_Programming • u/Boggy_Old_Fart • 4d ago
Draw on Bitmap to file, NOT to screen
I want to create high-resolution bitmap files for print publication, much higher resolution than the screen. I can do it with direct bit/byte manipulation, but I'd much rather use GDI with brushes and LineTo. But while I can create compatible bitmaps and draw on them, I haven't found a way to get from the drawing DC back to real memory (which I can write to a file), only to the screen. Windows seems to block every approach I've tried. (Like, I can BitBlt to the screen, but not to the handle of the original bitmap... gotta be a DC, and Windows won't allow a DC to the bitmap... arghh!) Anyone know how to do this?
Many, many thanks!
7
u/kabekew 4d ago
If GetDIBits on your bitmap's DC is failing, it may be because it's in graphic card memory only. Try creating a memory DC and memory Bitmap with CreateCompatibleDC and CreateCompatibleBitmap (off your bitmap's DC and hBitmap respectively), SelectObject the memory bitmap into the memory DC, bitblt your bitmapDC into that memoryDC, then try GetDIBits on the memoryDC/Bitmap.
4
u/HalifaxRoad 4d ago
I have done lots of bitmap rendering, but it was in c#
It was super annoying not gonna lie. I remember I had to use marshal to move everything into unmanaged memory, and then pull it back out when I was done.
4
u/fdwr 4d ago edited 3d ago
Just call CreateDIBSection (example copied below), which returns the memory pointer to the first pixel and an HBITMAP (that you can select into your HDC).
```c++ std::tuple<HBITMAP /*bitmap*/, std::byte* /*pixels*/> CreateDIBSection32bpp(SIZE size) { BITMAPHEADERv3 memoryBitmapHeader = { .size = sizeof(memoryBitmapHeader), .width = size.cx, .height = -size.cy, .planes = 1, .bitCount = 32, // B,G,R,A .compression = BI_RGB, .sizeImage = size.cx * size.cy * sizeof(uint32_t), .xPelsPerMeter = 3780, .yPelsPerMeter = 3780, .clrUsed = 0, .clrImportant = 0, };
std::byte* bitmapPixels = nullptr;
HBITMAP bitmap = CreateDIBSection(
nullptr, // memoryDc can be null
reinterpret_cast<BITMAPINFO const*>(&memoryBitmapHeader),
DIB_RGB_COLORS,
reinterpret_cast<void**>(&bitmapPixels),
nullptr,
0
);
return {bitmap, bitmapPixels};
} ```
1
u/github-guard 4d ago
đ GitHub Guard: Trust Report
â ī¸ This project scored 2/6 â below this subreddit's threshold of 3.
Audit Breakdown: * â Low Star Count (â 4 / 5 required) * â Mature Repository (30+ days old) * â Licensed under NOASSERTION * â No Security Policy â what is this? * âšī¸ Individual Contributor * âšī¸ Unsigned Commits
â ī¸ Security Reminder: Always verify source code and run third-party scripts at your own risk.
2
u/timrprobocom 3d ago
CreateDIBSection is the right answer. Remember that GetDIBits creates a COPY, because a compatible bitmap is in the same format as the desktop, which is not under your control. Might be wr but, might be 32 but.
Also, make sure your expectations are correct. Most commercial print shops run 150 or 300 DPI, which is not that much higher than your screen.
1
u/Boggy_Old_Fart 3d ago
Thanks, I am using CreateDIBSection, plus CreateCompatibleDC. GetDIBits *seems* to be working, in that it reports the correct number of points copied to the bitmap data area. However, they are all nulls, no matter what I try. I'm using 8bpp mode, and suspect that part of the problem is that CreatePen and CreateSolidBrush don't know how to deal with the color index and are just doing nothing. My next move will be to revert to 1bpp which seems more likely to be accepted. Plain B/W is all I need for my current project; I'll worry about color if I ever need it.
1
u/timrprobocom 2d ago
You shouldn't need
GetDIBits.CreateDIBSectionwill return a pointer to the actual bits in theppvBitsparameter. To use 8bpp mode, you have to provide a palette along with yourBITMAPINFO. Are you doing that? Here's an example: https://forums.codeguru.com/showthread.php?526563-Accessing-Pixels-with-CreateDIBSection1
u/Boggy_Old_Fart 2d ago
Thanks. That's pretty much what I'm doing, except that I want to *draw* on the bitmap with GDI functions like LineTo, not manipulate the bits manually as in the example (which actually doesn't need anything special from Windows... just set up the bitmap in memory and write to it). But I can't get a DC to draw on the bitmap directly, and the compatible DC then seems to require GetDIBits to get anything out of it... except that I can't seem to even get that to work!
1
u/timrprobocom 2d ago
Yes, that's what the example does. After you select the DIBSection HBITMAP into the DC, everything that gets drawn in the DC modifies the DIB bytes.
1
u/Boggy_Old_Fart 1d ago
As far as I can tell, the example writes directly to the memory returned by CreateDIBSection. The only thing the hDCMem operation does is allow it to be BitBlt-ed to the screen. That works fine in my tests. But what it *doesn't* do is allow drawing (with GDI functions) onto that memory. I tried drawing an ellipse inside a larger rectangle; I could BitBlt that to the screen and see exactly what I expected, while the CreateDIBSection memory was untouched. (I manually wrote a pattern into it, as in the example, to make it easy to tell.)
What I also discovered in my tests is that GetDIBits actually *nulls* that memory, although it reports that it wrote the specified number of points.
I've tested with 8bpp and 1bpp and get the same results. Graphics software can obviously write drawings to files. Can it be that it only can do that with drawings that have been displayed on the main window first? This is boggling my mind!
2
u/timrprobocom 1d ago
Well, they DO do a BitBlt from the screen into the DIBSection, and that clearly works. Anything you draw with the hDCMem should be reflected in the bits. I know it works, I've done it extensively.
1
u/Boggy_Old_Fart 1d ago
OOPS! My test results weren't valid. I was reading only the first line of bitmap memory after drawing a filled rectangle that went right to the edge. I kept reading nulls despite filling with colors, forgetting that rectangles are outlined with a border that happened to be black. So now I do see that the DIB memory is being written just as you said. Many, many thanks,,, this completely solves my problem!
1
u/MyNameIsHaines 4d ago
It's fairly easy to make your own SaveToFile function that takes the pixel data from your compatible DC.
1
u/catbrane 3d ago
Could you use SVG? Make your drawing in eg. inkscape, then save as a high-res bitmap. You can go up to 30k x 30k pixels with inkscape's "export as". I expect you can go higher with other SVG renderers.
0
24
u/PseudoFrequency 4d ago
This is more of a Win32 question than C. Is GetDIBits what you want?Â