The crime that is Windows DPI handling

I was just looking into the whole topic of drawing something on screen in a .NET WinForms application, so that it appears in the “correct” size. I found a number of issues around the handling of screen resolution and DPI, and in order to remember what’s what, and perhaps help somebody with the same problems, I thought I’d write it all down.

Some definitions

For a start, what does DPI really mean? It is often confused with “resolution”. That is of course not wrong, but still very confusing. The term “resolution” is usually associated with displays — or perhaps that’s just my understanding. But when I hear somebody talking about resolution, I expect to hear things like 1024×768, and I’m sure I’m not alone. This kind of resolution information defines the total number of pixels that can be displayed.

Resolution alone doesn’t tell me everything there is to know about how a picture is going to look when displayed. The reason is that there’s a second factor that’s also important: the size of the display area. On a 15 inch screen, 1024×768 used to look okay. On your 21 inch flat screen it’s probably not so great.

That’s where DPI comes into play. It means “dots per inch”, and that’s what it defines: the number of pixels an output device can render per inch of physical real estate.

For example, my old Dell 2001 FP is a 20 inch screen. That’s of course its diagonal, the size of the visual area is 16×12 inches (I’m surprised to find that this means exactly a 20 inch diagonal – well done, Dell!). It has a native resolution of 1600×1200 pixels. Easy maths – that’s 100 DPI!

On the other hand, I’m now using two 28 inch screens at a resolution of 1920×1200. Their size is 23.39×14.57 inches. DPI in horizontal direction is 82.1, in vertical direction it’s 82.4. With a large screen like this, DPI tends to be much lower because resolution isn’t keeping up well these days with the growth of screen sizes. It is also interesting that the two values I calculated aren’t identical – the difference seems large enough to think that it’s not down to measuring errors. Of course this is perfectly well possible, because DPI values in X and Y directions would only be identical if the pixels on the device were perfectly square.

Why is DPI relevant?

So why bother knowing these precise values for DPI? Well, it’s simple: because unless you know these values, you can’t predict how large something is going to appear when displayed. On printers, for example, knowing the DPI “resolution” of the output is very important. If you print a line that’s defined to be an inch long in your DTP application, you expect it to be an inch long when it comes out of the printer.

Although it surprises some people, the same is true for displaying something on screen. An application should be able to display a document on screen in such a way that an element which is going to be an inch long on printed paper is also an inch long on screen. This is easily possible when taking DPI into account, and entirely impossible when ignoring it.

Just in case you still think that’s a weird idea — think about it. When displaying a simple UI element, like a button, or perhaps a piece of text. Is it more useful to configure the button to be 80 pixels wide or 2cm? Does a text height of 10 pixels (I’m talking about labels or the like) give a better result or rather 4mm? In conjunction with text there are other measurements like pt and similar, which confuse things further. The point is, without taking the actual screen DPI values into account, you just don’t know what size something is going to have when the user sees it.

Of course some people realized the importance of DPI a long long time ago. In conjunction with computer displays, the standards DDC and EDID allow your computer to receive information about its size directly from your screen, which of course allows the computer to calculate the DPI values depending on the current resolution.

So what’s wrong in Windows?

There are several things that go wrong in Windows. For a start, it doesn’t seem that display drivers typically evaluate the EDID information to find out about DPI values automatically. I’m not an expert on this level, but I guess it would theoretically possible to do this on the driver level, and maybe there’s even some influence over the actual Windows DPI configuration that the driver has, but which is not passed on to the end user of the system. Maybe some graphics drivers do something in this regard, I don’t know.

It doesn’t matter all that much though, because Windows has always (largely) ignored or misinterpreted DPI for its own rendering of its UI. Very recently things are starting to change a bit with the introduction of vector based graphics through WPF and Silverlight, but of course it’s going to take ages until things change for the majority of applications that make up the OS, as well as 3rd party apps.

There are a few places where Windows takes DPI into account to some extent. For one thing, there’s some sort of scaling of UI elements going on, which usually results in rather crazy looking layouts. I don’t know anybody who really understands exactly what’s going on there, and I’ve never found it relevant enough to look into this and understand the details. There’s one other thing though, which most people have observed at one point or another: the “large fonts” setting in Windows.

I seem to remember that traditionally you could only choose between “normal” and “large” fonts. These days, i.e. in Windows 7, there’s a dialog that lets you select a “percentage of normal size” for your font display. So far, so good — this is of course a valuable feature to have.

If only they did it right…. let’s think about it. As I said above, knowing about DPI allows me to render a piece of text in such a way that it appears exactly 8mm high on screen. For example. If I want that piece of text scaled by 25%, I’ll render it to appear at 1cm height on screen. Again, knowing my DPI makes it possible, and if I can see 1cm high text better, that’s great.

On the other hand, what MS does defies any logic I can come up with (having accepted long ago that there are always several different kinds): they go and change the DPI value for the display depending on the “font size” setting. They just claim that my display has a DPI value different from what it really has. Wow.

As a side note

They don’t even get it right! Check out these two screen shots (from Windows 7, in spite of the appearance):

Now, as you can see I’m asked to enter the “percentage of normal size”. I haven’t measured precisely, but I can believe that in the second shot, the height of the text is roughly twice that of the first shot, so the scaling of the font itself may be correct. But, tell me: how does that relate to the information that says “… at 192 pixels per inch”?

Logically, I can see two different scenarios. Number one, I have an element that is, say, 100 pixels wide. When displaying it at a DPI of 96, it would be just over an inch wide. At a DPI of 192, it would be just over half an inch wide. The higher the DPI, the smaller the object, if it has a fixed width in pixels.

Number two, I have an element whose size is not described in device pixels, but rather in a “real world” measurement, like inches or centimeters. If I have an element that is one inch wide, then I’d have to draw it over 96 pixels at 96 DPI and over 192 pixels at 192 DPI. Right? Right.

How MS manage to claim that the size of 9pt Segoe UI grows while the “fake DPI” grows as well, is beyond me.

Oh, and the “point” they are referring to? That’s a real-world measurement, called Point. According to its modern definition it measures 0.3527mm. Go figure.

Moving on — so why might they choose an approach of scaling by DPI? At a glance it seems like this might be a good idea if the objective is to scale your entire UI at once. By claiming to have a DPI that’s only half as large as the one I really have I would effectively double the size of everything on screen.

I think this would look crazy, rather like one of those magnifying glass implementations in pixel based drawing programs. I also think it also doesn’t make all that much sense for a computer UI — after all, there are many elements of varying importance on screen, and while I may want some of them to be larger so I can see them better, that doesn’t apply to every separator line and similar graphical gimmick. I’m almost sure MS don’t do this either.

… and there’s more

Okay, so it appears that Windows has at least some semblance of an understanding of DPI internally, regardless of the funny ideas with font sizes and large vs small DPI values. One minor issue, comparatively speaking, is the fact that you can only configure one DPI value, while it seems obvious that there should be two, one for the horizontal and one for the vertical direction. Well, I’m calling it minor… I’m sure it can be a rather big issue when carefully designing documents for large print output on your high res display while using a compromise DPI value that’s the same for X and Y.

More important is something else though: Windows enforces a particular range of DPI values. The 100% item in the dialogs shown above is the minimum value you can enter. Remember my 28 inch screens I described above? They have a DPI of 82. It’s impossible to configure Windows to this value. Why? Brilliant, just brilliant.

and some unknowns

I honestly don’t have a clue what the “Use Windows XP style DPI scaling” checkbox is for in the dialogs. I can’t use it either, it just goes on and off on its own.

Something for developers

If you’re a developer, there’s not too much you can do about the OS’s non-grasp of DPI information, but unfortunately you’re the one who gets criticized by customers if your display looks funny. Whatever use it is, there is some at least some functionality in Windows that lets you query DPI information to the extent that the OS is aware of it.

Generally this is quite simple and it’s been supported by Windows API calls forever. There’s a helpful function called GetDeviceCaps that you can call with a device context handle and a constant value called either LOGPIXELSX or LOGPIXELSY (hey, finally separate values! where should they come from at this point…?) in order to retrieve DPI info. Of course there’s a lot to do wrong when using Windows API functions — here’s an example, with the caveat that I’m not checking for errors from the API functions like I should. Oh, and I’m abusing Size as a container for the value pair. Well, if you just copy&paste, that’s your own fault 🙂

[DllImport("user32.dll", SetLastError = true)]
public static extern bool SetProcessDPIAware( );

[DllImport("gdi32.dll")]
public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetDC(IntPtr hWnd);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);

public static Size GetScreenDPI( ) {
  // no error checking here - being lazy
  var dc = GetDC(IntPtr.Zero);
  try {
    return new Size(
      GetDeviceCaps(dc, (int) DeviceCap.LOGPIXELSX),
      GetDeviceCaps(dc, (int) DeviceCap.LOGPIXELSY));
  }
  finally {
    ReleaseDC(IntPtr.Zero, dc);
  }
}

The pinvoke definitions are courtesy pinvoke.net.

Oh, and I found one thing that a lot of blog posts showed “wrong”: they call CreateDC and DeleteDC instead of GetDC and ReleaseDC. My example simply retrieves the device context that already exists for the desktop, instead of creating a new one. I believe that should be much more efficient. Some other examples I found also leave out the bit with LOGPIXELSY — not something I would do. Can’t be that lazy.

Now, if you try this code, you may be a bit surprised to find that you don’t seem to get actual DPI information back. There’s a reason for that: MS think you’re going to do no good with it, and therefore they don’t give it to you unless you prove that you’re in fact clever enough. That means you need to call that function at the top of the example, SetProcessDPIAware, during your application initialization — otherwise GetDeviceCaps will just not tell you what the real values are. Great, eh?

Note: MSDN suggests you don’t use SetProcessDPIAware directly, but instead add an entry to your application manifest. See here.

Graphics != Graphics

Finally, I found a lot of blog posts that suggest you could retrieve DPI information from instances of the .NET Graphics class. This is true, and while I haven’t tried it now to confirm, I expect it will return the same values as the Windows API functions above. But there’s one important thing that the blog posts I’m talking about don’t usually stress: make sure you know where that Graphics instance comes from!

Think about it: why would the Graphics object have DPI information if it was always the same? No, the reason it has that information is because it depends on the graphics context the object represents. If you haven’t created a Graphics instance yourself, don’t assume what its resolution is. It might represent an image with whatever resolution, or perhaps a printer object with a very high DPI.

If you want to use Graphics for the purpose of retrieving DPI info, make sure you hold an instance that refers to an on-screen drawing context. And perhaps consider not using it at all — yes, it’s convenient, but it’s got a lot of overhead compared to simple API calls like the above.

As a final note

I’m not an expert in typography, and I’m describing everything in this post as logic seems to dictate, and according to my knowledge. I’m sure I’ve got at least one thing wrong, and if you know what it is, please let me know and I’ll fix it.

16 Comments on The crime that is Windows DPI handling

  1. Peter Ibbotson // September 2, 2010 at 5:51 pm // Reply

    It’s a horrid problem. GDI+ makes it worse and fonts get rounded size wise as well.
    The best example of the problems is to draw some text in a box and try zooming it in a print preview its a horrid mess as the text changes position relative to the box.

    http://www.ibbotson.co.uk/vbprint/vbprinting.html

    shows some of the hoops I went through in the VB6 days to get print preview looking nice (In particular lines relative to text). Thankfully these days I don’t care as videos are hard to print.

    Like

  2. Ultimately this is an artifact of the deep desire for backwards compatibility built into the OS. Way back in the day it was common to scale applications based on DLU which was dynamically calculated based on the current font – in other words it had no fixed relationship to a pixel. At the same time, for performance reasons much of the UI worked in pixels (e.g. graphics were bitmaps of a fixed pixel size, borders were 1 pixel wide, etc.) This created an implicit coupling in the UI between the size of a pixel and the DLU of the current system font. This all was pretty reasonable back in 1995 when the idea of dynamically scaling on this was a performance pipe dream (remember how slow NeXT stations were?)

    Now of course it’s a different issue – modern hardware is perfectly capable of calculating all of this on the fly (at least if you have hardware 3d enough to run Aero, which even 4 years ago was relatively rare in the commodity PC world).

    But, we’re stuck with this old dichotomy. WinForms attempted to fix it somewhat, as did both Windows XP and Windows Vista (but in different ways – that’s where that font setting comes from; do you want to disable the new handling method and revert to XP’s way).

    In many ways, WinForms is uniquely bad at this vs. C++ because it tries to do the right thing which works MUCH of the time but not ALL of the time (it rescales widths and heights independently by comparing the DLU value of the design time font and the runtime font, at least by default. You can override this on a control-by-control basis, which is why each gets its own graphics).

    The real solution is to not use a GDI-based window, meaning WPF or Silverlight (or, for that matter, Direct X directly). The second challenge is to convince manufacturers to make truly high resolution displays that make it WORTH having a UI that can scale correctly. As long as nearly every UI is running at a paltry 96 dpi there isn’t a lot of incentive because you can’t create a lot of additional richness to the display and you have a high risk of display artifacts (like the tendency for WPF to produce gray-ish text).

    So let’s create compelling applications to convince people to buy compelling hardware. Otherwise, all of this will be for naught and the only place to get a truly high-resolution experience will be a Mac (since they don’t really give you a choice as a developer. Kudos.)

    Like

  3. A desktop screen is not viewed at the same distance or angle as print on paper so there is no logical reason why something specified as one inch long should be physically one inch long on a screen. There is no real relationship between screen dpi setting and physical screen pixels per inch. Screen dpi setting was there only for translating physical inches into a number of pixels and the reason why Microsoft chose a default value of 96 are obscure – but it was not because it matched the physical pixel density of screens. Petzold in “Programming Windows” relates it to a magnifying effect, given the pixel density of screens at the time, such that 8pt text could be easily read on screen. Windows neither ignored or misinterpreted screen dpi but used it for it’s intended purpose of translating physical inches (font size) into pixels – but weren’t too bothered that when scaling up text the rest of the UI didn’t properly scale up as well.

    Windows, as far back I think as Win3.1, allowed screen dpi adjustment. One can adjust it in XP between 20% and 500% (19dpi – 480dpi) although I wouldn’t care to try it :-). In Win7 it’s 100% to 500%. Drag the on-screen ruler with the left mouse button in “Custom DPI Setting”. There is also nothing really magical about 120dpi.

    In “As a side note” they do actually get it right. The screens demonstrate the translation of font size in points, which are inches, into font size in pixels. That’s the whole purpose of screen dpi:
    pixels = (inches) x (screen dpi).
    Your first screen-shot shows 9 point Segoe, at 96 pixels per inch, being 100%. They actually mean 96dpi not 96 physical pixels per inch. At 200% it’s 192 simply because 192 = 2 * 96.
    At 100%, 96dpi:
    font size = 9/72 * 96 = 12 pixels
    At 200% 192dpi:
    font size = 9/72 * 192 = 24 pixels

    You say:
    ” I have an element that is, say, 100 pixels wide. When displaying it at a DPI of 96, it would be just over an inch wide……..”
    It would be just over one logical inch wide! You have to differentiate between screen dpi which pertains to logical inches and physical dpi (pixels per inch) which pertains to physical (ruler) inches. The DPI values in the screen shots you show have no relationship to the physical pixels per inch you get from measuring the screen with a ruler.

    Like

  4. Well, Richard, you’ve lost me there… you seem to have given this a lot of thought, perhaps more than I have, but I’m afraid I can’t follow your explanations. Or maybe it’s because I don’t agree with your premises: “There is no real relationship between screen dpi setting and physical screen pixels per inch” — you might be right that this is the case, but my point is that this is wrong.

    Regarding your comment “there is no logical reason why something specified as one inch long should be physically one inch long on a screen” — There is no logical reason why something specified as one inch long should be anything else but one inch long on screen. If I want it larger, I can scale it, but why somebody would choose a default that is different from the obvious precise relationship is, again, something I can’t understand.

    Like

  5. “why somebody would choose a default that is different from the obvious precise relationship..”

    Where does 96 DPI come from in Windows?
    http://blogs.msdn.com/b/fontblog/archive/2005/11/08/490490.aspx
    “The Windows solution was somewhat controversial. The decision was made to report the resolution of displays on Windows as about 1/3 greater than actual resolution. This roughly corresponds to the increased reading distance. So, for displays around 72 PPI, Windows would indicate 96 PPI. ”

    The 96 dpi screen value is purely for translating font points (inches) into pixels at a readable size for that time, but it somehow became universally believed that 96 was chosen because it was actually the number of pixels per physical inch (PPI) of screens. When something is drawn 96 pixels wide and it is obvious that it isn’t one ruler inch wide then there are complaints that Microsoft got it wrong 🙂

    In order to draw something on screen that was actually one inch wide one would have to know, via software, the physical width of the screen. Windows, at least up to XP, does not _accurately_ know the physical width/height of the screen drawing surface. Here’s a quote from that author buried in the comments for that article:
    “I should have mentioned this in the original article, but Windows did not know the monitor size and resolution. It has only be recently that EDID information from the display could be used in Windows.”

    Some people used to recommend adjusting the screen dpi to get 96 pixels measure one inch but anyone who has ever tried to measure lengths on a highly curved CRT screen, with parallax due to 1/16 inch thickness of glass, with 10 different adjustments to get rid of black bands and image curvature, would doubt their sanity. 🙂

    Like

  6. And don’t forget you can change the DPI in Windows, you can’t change the DPI setting of your display. You should read the Windows DPI setting as ‘the DPI of your monitor’, so setting the DPI to 192 will result in an one inch wide line if and only if your screen will be 192 DPI. But because your screen DPI size is fixed, it will increase the size of the text, instead of giving a same size, more detailed result.

    Like

  7. I was wondering why everybody in the US seems to be switching to Apple products. Microsoft got really lazy with Vista and 7. I won’t bother wondering if Phone 7 is worse, because I’m sure it is.

    BTW, I’m typing from a Win 7 system, where I manually forced the DPI to 90 by setting all values in the registry named LogPixels or LogPixel to 90 (decimal). It seems to work, but the Segoe UI 8pt font used in some system dialogs is replaced with the system font. Why? I mean – it is an open-type font, for crying out loud! WYSIWYG!? Microsoft invented that term, IIRC, and they were advertising it when they “invented” (or stole from Apple?) the TrueType fonts. Now they break all that?

    Like

  8. John Engström // December 1, 2010 at 11:07 am // Reply

    I agree with most of what Richard Mason said.

    The size in pixels of user interface elements can not generally depend upon neither physical display DPI nor size of the display surface.

    A projector can not report neither display size nor physical DPI (e.g. via EDID,) since those values are determined by the throw-distance of the beam.

    Also, the user is seated much further away from the display than any monitor, which alters the “experienced” DPI and physical size.

    DPI settings might be auto-selected by the methods mentioned here, but must always be configurable by the user anyway.

    Like

  9. Some strange situation with drawing must be in multimonitor systems, when some monitors have different real DPI.
    How is implemented drawing on a window, that is placed on more then one of this monitors in same time, across monitor’s border?

    Today function GetDeviceCaps for LOGPIXELSX returns one value for all monitors.

    Like

  10. At http://social.msdn.microsoft.com/Forums/en-US/windowsgeneraldevelopmentissues/thread/fdc97ab7-caae-4c5d-9f1a-45088ebdb48f there are report of some bug in Vista and Windows 7, that is ignore real size of display, while Windows XP allow draw graphics correctly sized.

    Like

  11. Misunderstanding often leads to frustration. I thought I will turn back and share one useful link I have found after reading this post, as I think it might clarify many of the questions raised here and prevent crapification of unripe minds that might occur.

    DPI and Device-Independent Pixels
    msdn.microsoft.com/en-us/library/windows/desktop/ff684173

    One might blame Microsoft for using fool-proof methods way too often, I say they never stop proving their usefullness.

    Like

  12. I agree with Oliver.
    Ok, MS has its reasons to make things working in this way but:
    1) some people WANT have objects of 1 inch that ARE 1 inch long on the screen;
    2) there is no way (better: I haven’t found it searching the net) to know the real size of the screen from windows in order to perform the calculation (on an application basis rather than at machine level through Custom DPI settings)

    Like

  13. Oliver Sturm // October 16, 2012 at 12:07 pm // Reply

    Thanks, tiz – exactly my thoughts.

    Oliver

    Like

  14. ConceptJunkie // December 11, 2012 at 8:00 pm // Reply

    I appreciate the research (and suffering) undertaken in order to produce this information which, as usual, is nowhere to be found in the “official” documentation.

    I’ve been tasked with maintaining and enhancing a very old MFC app (which I enjoy… MFC was never more than a half-hearted first draft of incorporating Win32 into C++, but using minimizes the levels of stupid between your code and the OS, unlike most newer frameworks).

    Needless to say, this app, which has innumerable complex dialogs packed with controls, looks horrible on Windows 7. Fortunately, I’ve been able to create base classes that override functionality in CDialog and CFormView to tame some of these arbitrary and capricious changes between the Windows 5 and Windows 6 generations. Your article is definitely helping.

    Like

  15. “How MS manage to claim that the size of 9pt Segoe UI grows while the “fake DPI” grows as well, is beyond me.”

    I believe that the logic is, if you set your DPI there to your monitor’s DPI, the font will be the correct size (1 point = 1/72nd of an inch).

    I agree that this is terribly confusing. Really, it shouldn’t say “This is what your font looks like at 192 DPI,” it should say “This is how big the font needs to be to be the correct size, if your monitor were 192 DPI.”

    Like

  16. I think Microsoft is incorrect in changing the meaning of DPI. DPI has meant dots per inch, and it seems like a fabrication to call it dots per logical inch. Here is the problem, I want to write .NET Framework WPF software which knows its relative portion on the screen, but with this Microsoft DPI scheme, the software cannot know its portion on the screen based on the screen size because the screen size, # pixels horiz and vert don’t change regardless of the local DPI. For example, a 1024×768 screen has a pixel in the middle at 512,384, but, with 200% magnification, the screen size is still reported as 1024×768, but now this is point at the lower right of the screen. To work-around, I wrote code which obtained the DPI for the desktop and scaled in my program accordingly. It works under Win7 with .NET Framework 4, but on Windows XP it did not work even with the scaling. … I think the user should have never been given a control to globally change the screens. Ideally, the screen & video card manufacturers should have the means to accurately indicate real DPI to apps, let application developers deal with reality, and let users deal with app developers if they want things bigger or smaller.

    Like

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s