Trying to get a device context (dc) in C to write to an image in memory where dc ignores the screen scaling.

technoway 241 Reputation points

I have an MFC MDI application written in C and C++. A C DLL creates a 24-bit DIB image that contains graphics and text. The size of the text matters a lot so the text fits in tight spaces in the image.

I cannot change the application from using MFC or change the image format.

Everything works fine in most situations, however, my 17-inch workstation laptop screen is 3840 pixels by 2160 pixels, and is set to use 250% scaling, which is recommended by the computer manufacturer. Because of that, the DrawText method creates text that is way too large.

If I right click on the executable, I can change the application DPI scaling, however, then it looks terrible. It looks much worse than when I set my screen resolution to be 1920 by 1080. I don't want to do either of those things.

I don't want to use Windows controls or APIs to change the monitor scaling for the application, because that makes the application look terrible, and the nature of the application requires as much screen precision as possible. Mouse movements can track image locations closely, and anything that hurts the precision makes the application worse. It will work at relatively low screen resolutions, but the higher the screen resolution, the better.

In my application, I create a memory device context for a DIB image.

CClientDC dc(this);

I create a DIB section that is selected into the memory device context, and I copy the image bits into the DIB before using BitBlt to send the image to the screen. I use the MFC CDC class BitBlt method for that using the client windows device context obtained by using.

CClientDC dc(this);

All of that works. I don't expect to have to change any of that. That just copies the 24-bit DIB image that is created. The problem is that the text is too large in the DIB image.

If I use CreateFontIndirect to create the font I select, the font is always 2.5 (250%) larger than I want. I understand why it's too large. I want to make it smaller. I also can't just choose a smaller font point size either. That would be too small to work.

Oddly, if I use GetStockObject(SYSTEM_FONT) to create the font, then the font is still too large, unless I change the size of the window, which causes an OnSize method, which uses MoveWindow to resize my contained image instance. Then the font becomes the size I want, as if the device context was rendering at the normal 96 DPI. However, if anything refreshes the client area of my view that doesn't resize it, then the text is always too large.

I've tried to change the device context scaling using some Windows APIs, but that didn't change the text size. I really don't want to change the resolution of my application, and I must use the 24-bit DIB, for other reasons I can't go into here.

Essentially I want to get a device context to that renders text to the image at 96 DPI. After that, I'm all set.

I've tried creating my own window using GetModuleName, GetModuleHandle, RegisterClassEx, and CreateWindowEx to create a window the size of the image, and used GetDC with the window handle to create a device context, and I can selected the bitmap handle created by CreateDIBSection into that, but DrawText fails when using that device context. The error is zero, and the error code indicated a bad argument, which I suspect means a bad device context.

So, how do I get a device context that render to the image at 96 DPI.

The code is extremely large, even the pared-down example, and that example runs as a console application in a command window, and that always creates small text, i.e. unscaled text that's the size I want. I haven't yet tried to make my Window class name by "ConsoleWindowClass", although I'm not sure if that's a reserved Microsoft class or not, or whether the will work.

I've worked on this issue for about two weeks. I'm sure there's a way to solve this, particularly since it's solved temporarily when I use a stock font and resize the window. I haven't yet tried calling MoveWindow twice to resize the window before rendering the image, however, that seems like a wrong solution even if it works. Something in the OnSize method is causing this to work.

Thank you for reading this. The issue is driving me crazy as I'm so close to having this work, and this is stopping me. I really don't want the delay of having to obtain an open-source font and write a function to draw text for this special case. That would be a lot of work, and is probably the wrong thing to do.

Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,469 questions
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,584 questions
{count} votes

Accepted answer
  1. technoway 241 Reputation points

    This turned out to be my error in code unrelated to the device context. I had messed up initializing some code that affected my font creation. The code created one font of the desired sizes, and then freed that and allocated a different font when a different size was requested. That code was correct, but I had set an initial value to a valid value, and that messed up the font code.

    This had the inexplicable result of changing the font size radically, as if screen scaling was being turned on. It wasn't that at all.

    I fixed this font code, and changed the device context code to just getting the device context once with CreateCompatibleDC(NULL) and no other calls to save, restore, or modify the device context, and now the font size is always the same, and always the size I want.

    Explaining the bug would take posting code, and I frankly don't understand how the bug caused the font size to be correct, and then to jump to a much larger font, when all the image sizes and bitmaps were the same size. That definitely was happening. Fixing that bug, which must have caused some type of memory corruption, made the problem vanish.

    I had upgraded from Visual Studio 17 to 19, installed a new device driver, and a new BIOS! I wondered if one of those has caused the problem, but of course, it was me!

    The bug was so far removed from where I thought it was, it took a while to find.

    Sorry for the noise. Thanks for running this forum.

0 additional answers

Sort by: Most helpful