Home > other >  Windows System DPI: GetDeviceCaps(hdc, LOGPIXELSX/Y) vs GetDpiForSystem()
Windows System DPI: GetDeviceCaps(hdc, LOGPIXELSX/Y) vs GetDpiForSystem()

Time:01-04

Recently, I read this excellent page about DPI on Win32:

DPI and device-independent pixels

However, I am confused about GetDeviceCaps(hdc, LOGPIXELSX/Y) vs GetDpiForSystem(). On systems where I tested, all three values always match.

Questions:

  1. Is it possible for GetDeviceCaps(hdc, LOGPIXELSX) and GetDeviceCaps(hdc, LOGPIXELSY) to return different values? I assume that LOGPIXELS means DPI. (Please correct me if wrong!)
  2. If the previous answer is yes, then is GetDeviceCaps(GetDC(NULL), LOGPIXELSX/Y) the same as GetDpiForSystem()?

When possible, I should be using GetDpiForWindow(hWnd), but I want to clarify my understanding about "system" DPI in my questions above.

CodePudding user response:

As far as I'm concerned, GetDeviceCaps(hdc, LOGPIXELSX/Y) is not a match with GetDpiForSystem().

LOGPIXELSX:Number of pixels per logical inch along the screen width. In a system with multiple display monitors, this value is the same for all monitors.

LOGPIXELSY:Number of pixels per logical inch along the screen height. In a system with multiple display monitors, this value is the same for all monitors.

This function is not able to return a per-monitor DPI. For that, you should use GetDpiForWindow().

GetDpiForWindow() also returns a different value based on the DPI_AWARENESS value.

GetDpiForSystem() returns the system DPI. The return value will be dependent based upon the calling context. If the current thread has a DPI_AWARENESS value of DPI_AWARENESS_UNAWARE, the return value will be 96.

CodePudding user response:

Is it possible for GetDeviceCaps(hdc, LOGPIXELSX) and GetDeviceCaps(hdc, LOGPIXELSY) to return different values? I assume that LOGPIXELS means DPI. (Please correct me if wrong!)

For monitors, I believe LOGPIXELSX == LOGPIXELSY even if your display has non-square pixels (which is extremely rare nowadays). There are still many printers out there that have different horizontal and vertical dot densities. In some cases, the printer driver may hide that, giving the illusion of square pixels. Among ones that don't, you not only have to be careful to use the right value for the context, but you should be aware the some drivers forget to swap the horizontal and vertical values when you change the page orientation to landscape.

LOGPIXELSX and LOGPIXELSY refer to the number of pixels per logical inch, an idea that's been buried in recent versions of Windows. You used to be able to tell Windows that, when a program wants to display something that's 1-inch long, use my logical inch value rather than the actual DPI (In the olden days of CRT monitors, it was usually impossible for Windows to discover the actual DPI anyway).

Common values for logical inches were 96 and 120. If you had a really high-end monitor, you might've used 144. If you were into WYSIWYG applications or desktop publishing, you'd usually choose a value that was about 20% larger than an actual inch on your screen. I've heard various rationales for this, but I usually chose a higher value for easier reading.

When possible, I should be using GetDpiForWindow(hWnd)

That's fine. I use LOGPIXELSX and LOGPIXELSY because I'm old school and have always written high-DPI aware code.

but I want to clarify my understanding about "system" DPI in my questions above.

I believe the system DPI is the scaled DPI of the primary monitor. The scaling gives the programmer the same functionality as a logical inch, but it's presented to the user in a different way conceptually.

On systems where I tested, all three values always match.

Yes, it's very common for all of the methods to report the same DPI.

  • If your program is not high-DPI aware, you'll get 96 regardless of how you ask.
  • If it is aware, the system DPI is the DPI of the primary monitor (well, it's the possibly scaled native DPI of the monitor, but that's the same value you'll be told for the DPI of monitor).

That covers a lot of common cases. To truly and thoroughly test, you'd need a second monitor with a different native DPI than the primary.

A couple points to keep in mind.

  1. The GetDeviceCaps() approach is specific to the device context you reference with the HDC parameter. Remember that you can have printer DCs, enhanced metafile DCs, and memory DCs in addition to screen DCs. For screens, the return value will depend on the DPI awareness.

  2. DPI awareness comes into play for screens (not printers). Your program's UI thread can be:

    DPI unaware, in which all methods will return 96 dots per inch and actual differences will/might be handled by the system scaling things on the backend.

    DPI aware, in which case most will return the system DPI. I believe the system DPI is the (scaled?) DPI of the primary monitor, but I'm not sure about that.

    per-monitor DPI aware, in which case the GetDeviceCaps() and GetDpiForWindow() functions will return the actual DPI of the monitor that's referenced by the HDC. I don't recall what it returns if the HDC spans multiple monitors. It might by the actual DPI if the monitors spanned have the same DPI, or it might be the system DPI, or it might be the DPI of one of the spanned monitors.

  3. GetDpiForMonitor() ignores DPI awareness altogether. I assume it remains for backward compatibility.

  • Related