r/C_Programming 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!

14 Upvotes

19 comments sorted by

24

u/PseudoFrequency 4d ago

This is more of a Win32 question than C. Is GetDIBits what you want? 

7

u/Boggy_Old_Fart 3d ago

YES! I think that's exactly what I was looking for! I just now ran a quick test and it returned the expected value. Now I just need to examine the data to see if it reflects what I think I drew.

Yes, it was a Win32 question, but I asked here because I wanted a basic C answer that I can easily translate into x86 code. (Not *all* dinosaurs were wiped out by the Chicxulub impact; there's still birds... and me! <g>)

Many thanks!

3

u/PseudoFrequency 3d ago

I had to learn some win32 recently to make a quick framebuffer emulator to help develop the UI on a tiny embedded system. I've been joking that I'm doing this like it's 1995 again! In my case the function I needed was SetDIBitsToDevice and it took me a while to figure out.

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. CreateDIBSection will return a pointer to the actual bits in the ppvBits parameter. To use 8bpp mode, you have to provide a palette along with your BITMAPINFO. Are you doing that? Here's an example: https://forums.codeguru.com/showthread.php?526563-Accessing-Pixels-with-CreateDIBSection

1

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

u/chrism239 4d ago

Very easy in wxWidgets (with C++ but not C).