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

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);
m_memory_device_context.CreateCompatibleDC(&dc);
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.
Thank you for your reply. I don't want to change the DPI scaling for the entire monitor or for the entire application. That makes the application look bad, and it loses a lot of the resolution.
I did try that before I wrote the question above.
I want my C DLL that only writes to a 24-bit DIB to not scale the text.
Can I ask you about what you said, "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." I can't understand, you emphasized that text is too big, I wonder if the DIB image has not become bigger?
If possible, could you please give me a screenshot of the program without any personal privacy.
The image and the graphics are the same size and in the same place. I only use a device context to write text.
I made some progress. I add a call to SaveDC after calling CreateCompatibleDC.
Then, before using the device context, I call two functions.
With the change above that now makes the text the right since, the first call to DrawText using DT_CALC_RECT produces an unscaled rectangle the first time, and then if done again, the rectangle is scaled up, and too large. After that, if the application is resized, the rectangle is unscaled.
So, I'm seeing the same behavior with the device context changes above that I was seeing before with the text font size.
I think I can work around this. I'm not sure, since the test positioning is important.
Have you tried drawing your image and the graphics in OnPaint, or have you redrawn it by using Invalidate.
I have a class for the image that derives from CWnd. An instance of that class is contained in the view class - as opposed to being derived from it.
I call MFC MDI view's InvalidateRect method and then the UpdateWindow method.
Then the contained window's method to render the image to a memory device contexts is called outside of the OnPaint handler. Then Invalidate and UpdateWindow methods are called for the contained window, and then the OnPaint method uses the device context's BitBlt method to send the image to the screen.
Could this be an issue because I use a contained window, instead of using inheritance?
Is it a WND of the Child property,Dialog or FromView ?
I am not using CFormView in my MFC MDI application.
I have:
and
both views are displayed in a splitter window.
The C-DLL function calls are made in the SpecialImage class.
The C DLL creates it's own device context at startup, using CreateCompatibleDC, and then immediately calls SaveDC. The handle is stored in a structure that forms a handle.
When the C DLL used the device context, it gets it by calling the following C-static function.
HDC sanitized_get_dc(sanitized_handle_t sanitized_ptr)
{
// Restore the original device context settings
RestoreDC(sanitized_ptr->hdc, -1);
}
Adding the SetGraphicsMode and SetWorldTransform functions to have unity scaling didn't fix the problem.
When the application is shut down, RestoreDC and DeleteDC are called.
I hope that answers your question.
I drew images and text on the wnd, and all controls were proportionally larger when the resolution changed.
Perhaps I wasn't clear in the first post.
If the window is made larger, then the image in the window is larger. That's not the bug.
The original issue is that the graphics I drew in an in-memory image in a C-Dll, which were drawn without a device context (dc), always are the same size if the window is the same size.
However, the text would increase to be larger in some images, increased by the 250% screen scaling, and in other times the image is rendered, the text is not scaled.
So, the text increased in size. Absolutely everything else in the image, all the graphics, and the image size, were exactly the same. ONLY the text increased in size.
The in-memory image was literally an image I allocated in memory with no dc. I didn't use CreateBitmap or CreateDIBSection to create that image.
In the MFC application, I did use CreateDIBSection. That sets a pointer to the image data, and I copied the raw image data from the image created by the C DLL into the memory pointed to by the pointer that is set by CreateDIBSection.
So, there's a scaling issue with text in the C DLL. The only Windows functions called were related to drawing text in the image.
Now, restoring the dc results in the text always being unscaled - or scaled by unity, however, when the DrawText function is called DT_CALCRECT, the first image the size is small for all text written to the image, but the next time I render the image, the rectangle is scaled up and significantly larger. When OnSize is called, the rectangles are small again, and the texts snaps back to the right position.
Is it possible to write text output into your C-DLL function(the class SpecialImage)?
I can save the image to a file, however, I don't want to post the image here.
In my C-DLL, the sanitized_draw_text function uses CreateDIBSection (not the CreateDIBSection in the MFC code) to create a bitmap image to write black text on a white background. CreateDIBSection creates a tiny image the size of the image rectangle created by DrawText with DT_CALCRECT.
I then copy the bitmap image into the corresponding rectangle in the in-memory image. If the pixel is not the foreground color, I copy the three bytes of the pixel to the in-memory image in the rectangle offset by the values passed to the sanitized_draw_text function. (There's also an option to copy the text vertically)
Since they're both 24-bit DIB images, I copy three bytes for every pixel in the image.
This worked perfectly for a long time. I don't know if it was a Visual Studio Update, a Windows Update, or a driver update, that lead to this inconsistent behavior.
I'm using OutputDebugString, and I can see the rectangles produced by DrawText with DT_CALCRECT increase by about the screen scaling. If I resize the image, the rectangles have unity scaling again - until I refresh the image. Then the rectangles are large again.
It must be how I update the window. I wish I knew what WM_SIZE (OnSize) does to the device context. All I call in the view's OnSize method is MoveWindow to resize the contained SpecialImage instance, and in the SpecialImage::OnSize method, I call resize method in my C-DLL to resize the in-memory image.
(
JunjieZhu-MSFT JunjieZhu-MSFT technoway-4857 · 43 minutes ago
Is it possible to write text output into your C-DLL function(the class SpecialImage)?
I just understood the question.
No, this must be done in the C-DLL. That DLL requires drawing text, and later I'll want to port this to another platform, and that platform will need to write text some other way. It belongs in the C-DLL.
By the way, I also tried adding this code to the code above with no success.
]
I might need to write some smaller example to try to reproduce the problem.
If and when I do that, I'll post that example here.
Sign in to comment
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.
I'm glad it's been resolved. It's a skill development for you. Good luck!
Sign in to comment
0 additional answers
Sort by: Most helpful
Activity