Scott Hanselman

Be Aware of DPI with Image PNGs in WPF - Images Scale Weird or are Blurry

January 6, '09 Comments [28] Posted in Windows Client | WPF
Sponsored By

Is that enough TLAs (Three Letter Acronyms) in the title there? I continue to mentally deny the existence of DPI (dots per inch) as a concept. It's my own fault. I have been living on PCs at 96dpi for so many years, I just stopped caring. This problem we ran into is SO obvious after the fact, but it flummoxed us for a half-hour.

I've been helping a buddy on a super-cool super secret WPF application and the window has multiple states - regular, compact, and minimized. We've got three transparent PNGs for these three buttons. The designer created them and sent them along.

However, with one of them, it kept showing up at the wrong size, and was blurry. Even if we set the width and height ourselves, it looked totally wrong.

Here's wrong:


Unrelated to the scaling issue, I saw that the file was 3099 bytes, which I thought was a little large. I opened it up in the Visual Studio binary hex editor and noticed that I could see strings like "Photoshop ICC profile" in the header. PNGs are lossless (they are like Bitmaps, not JPEGs) and while they compress, there are may ways they can compress, like removing crap from headers.

I like to run PNGOUT on all my PNGs as it'll try different techniques to make the PNG as small as possible without losing any data (without changing the way the PNG looks). I ran PNGOUT on the wrong file and it went from 3099 bytes to 292 bytes. It also just happened it look right afterwards.

Here's right:


So why did it look right suddenly? Turns out that PNGOUT also changes the DPI, without asking, to 96dpi. Here's a little C# program I wrote to test:

static void Main(string[] args)
Image i = Image.FromFile(@"c:\users\scott\desktop\collapse-wrong.png");
Console.WriteLine(i.HorizontalResolution + " " + i.VerticalResolution);
Console.WriteLine(i.Width + " " + i.Height);

Image i2 = Image.FromFile(@"c:\users\scott\desktop\collapse.png");
Console.WriteLine(i2.HorizontalResolution + " " + i2.VerticalResolution);
Console.WriteLine(i2.Width + " " + i2.Height);

The output of this is:

72.009 72.009
56 10
96 96
56 10

Both files are 56x10 in dimension, but the first is 72.009 dpi and the second is 96 dpi. WPF defaults to 96 dpi so when it encounters the 72 dpi image it scales it up. If you can't change the image, there are funky ways around it. Personally, I believe if you are running into this because of a designer/developer mismatch, then just coordinate between each other and decide on a DPI, probably 96. In our case, DPI didn't really matter as the designer was developing pixel-accurate images, so we just run PNGOUT on all the images.

I usually run PNGOUT from Powershell, but there is a nice free .NET app called PNGGaunlet from Ben Hollis that provides a nice GUI frontend.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Tuesday, January 06, 2009 9:23:35 AM UTC
Why would you use raster images for controls in WPF? They even have text in them so that means an image would need to be compostited every time a new language was added, the font was changed, etc. Add in the accessibility concerns....

PNGOUT should warn about possible data loss, sure the data was in an optional tag; but just because the information does not have to be present doesn't mean it's not important. It mangles [badly] APNG's for example.

PS. That super special awesome secret software looks very Zune client-like, but of course Zune is DirectUI - not WPF....
Tuesday, January 06, 2009 9:24:28 AM UTC
Why not use 300 dpi? :P

Seriously though, changing the DPI without warning is messed up...

Like they expect people not to care.
Tuesday, January 06, 2009 9:37:48 AM UTC
I didn't feel like embedding a font, and sometimes when you want to do some REALLY custom UI, you're going to want to use graphics, textures, compositing, etc. Not everything can be vectorized.
Tuesday, January 06, 2009 11:32:01 AM UTC
So is this super-cool super secret project something you will be talking about again in the future (like what it is)?
Tuesday, January 06, 2009 12:02:59 PM UTC
I didn't see any Three Letter Acronyms in the title?

I saw a bunch of Three Letter Abbreviations though ;-)

Tuesday, January 06, 2009 2:39:53 PM UTC
DPI isn't the only way you can get blurry images, if you continue to have a fuzzy image after messing with the DPI, try setting "SnapsToDevicePixels" to true on your image. It looks fuzzy because the element above or to the left of your image has a height or width that isn't a whole number (27.4000394).

This typically a bigger problem when dealing with smaller icon size images (16x16).
Tuesday, January 06, 2009 4:17:27 PM UTC
Just for the record although the dpi is changed by PNGOUT it is lossless. The image is exactly the same it is only the way it will be displayed/printed. You can have a 200 dpi image 1 inch by 1 inch or a 100 dpi image 2 inch by 2 inch.

Since virtually no one has a monitor capable of more than 96 dpi (maybe capable of a little more but not much) I think it is good PNGOUT changes it, this makes it obvious when an image is wasting file size.
Tuesday, January 06, 2009 4:18:09 PM UTC
Just for the record although the dpi is changed by PNGOUT it is lossless. The image is exactly the same it is only the way it will be displayed/printed. You can have a 200 dpi image 1 inch by 1 inch or a 100 dpi image 2 inch by 2 inch.

Since virtually no one has a monitor capable of more than 96 dpi (maybe capable of a little more but not much) I think it is good PNGOUT changes it, this makes it obvious when an image is wasting file size.
Tuesday, January 06, 2009 4:53:43 PM UTC
That's a good piece of info to have. I do know people who run their desktop at 120 DPI for larger font scaling, and it definitely messes with images.

So then, wouldn't the best choice be to work with your designer to get your images as .svg or .wmf files such that you have the vector information on hand? XamlTune does a great job of converting svg files to xaml code. No more DPI problems!
Tuesday, January 06, 2009 5:46:43 PM UTC
PNGOUT looks like the bomb.

You can meet accessibility concerns with css tricks. My favorite is Leahy/Langridge image replacement (LIR), explained at Stuart Langridge's site.
Tuesday, January 06, 2009 6:38:55 PM UTC
I've used PNGOUT to great effect as well :) Before I moved the Paint.NET help content online, it helped me to shave off several hundred KB from the installer. When you're only a few megabytes, that really helps! To this day I still keep a release criteria bug around for re-running these tools before I release (along with things like "run a virus scan", etc.).

Another positive vote for PNGGauntlet -- I really like it. I also use optipng on the command-line, which uses a different algorithm. Occasionally running both PNGOUT and optipng can squeeze a few more bytes or KB out of images.

Using these compression tools can also benefit your application's performance. If you're loading a lot of small PNG's, like Paint.NET does in order to populate its toolbars and such, then you can save a good amount of disk I/O. It won't show up on most "warm" benchmarking, but it *really* helps in the "cold" and low-memory states (think of a freshly booted 512MB box, where every page fault is terribly evil).
Tuesday, January 06, 2009 9:49:10 PM UTC
"WPF defaults to 96 dpi" is a bit misleading. WPF bases its own measurement system on 96 device-independent-pixels per inch, but that is unrelated to the issue you're seeing.

The 'default' of 96dpi that matters here is actually your operating system, not WPF. Windows XP defaults to 96dpi out of the box, but my XP (for example) is set to 120dpi. So on my system your 96dpi image will still get scaled. In other words you haven't eliminated scaling by marking your image as 96dpi - you've just eliminated it on Windows systems where the OS is set to 96dpi (many but not all).

But WPF is doing the right thing here. If you have an image that is 96 pixels by 96 pixels, and is marked as 96 dpi, that means the image should be displayed as 1 inch by 1 inch, regardless of the hardware dpi. So if your hardware dpi (which WPF gets from the OS) is 120, then the image should be displayed as 120 pixels by 120 pixels, not 96 pixels by 96 pixels.

BTW It's actually pretty awesome when you first run a complex WPF app on a 120 dpi system and notice that everything scales up to the same size as it would be on a 96 dpi system. So everything is the right size, just rendered more 'finely'. The only 'disappointment' is bitmap icons that become blurry when they get scaled - but that's a minor downside in my view, and as others have suggested the best way to overcome that is to use vector icons (though unfortunately WPF has quite poor support for that).

Andy McMullan
Tuesday, January 06, 2009 11:50:16 PM UTC
Actually, on re-reading your post I just realised that your bitmap contains text - ugh, that's really going to look terrible when scaled, i.e. on any non-96dpi system. You might want to reconsider that approach.
Andy McMullan
Wednesday, January 07, 2009 12:07:47 AM UTC
Thanks for the link to PNGGauntlet, it's a fantastic program! Pff, and to think that I was actually gonna write a GUI myself... :D
Wednesday, January 07, 2009 8:28:31 AM UTC
Yeah WPF makes bitmaps blurry, I have discovered that when my 16x16 icons doesn't seem good on my buttons as in Windows Forms apps. If you are absolutely positioning your images and the images' pixels match to the device pixels' then it's not shown blurry. This can be achieved using the Canvas element for example. But I have found another solution at the following URL by Dwayne Need:

Blurry Bitmaps

Wednesday, January 07, 2009 4:00:25 PM UTC
You know, Scott

TLA is, itself, NOT a Three Letter Acronym.

Not the difference between an Acronym and an Abbreviation.
Wednesday, January 07, 2009 4:01:35 PM UTC
Oops ;)
Didn't read Andy's post.
Wednesday, January 07, 2009 6:42:14 PM UTC
Actually, Scott did use the word acronym properly, unlike the commentators providing helpful corrections in the name of pedantry.

Each of the TLAs used is indeed an acronym because it expands to three separate words. Unlike, for example, APT, which is an three letter abbreviation for a single word, apartment.
Ben Voigt
Wednesday, January 07, 2009 8:46:23 PM UTC
It seems a little off topic to discuss acronym vs. abbr, though knowing the actual difference may allow you to win friends and impress your loved ones.

I would not consider TLA, PNG nor WFP acronyms. They are more specifically initialisms, which is a form of abbreviation. I read out each letter when I speak "T","L","A" so it is an initialism. If you personally pronounce it "tlaaaa" then it's an acronym.

The Merriam-Webster site says if you read it carefully, though this page explains it much more clearly:
Wednesday, January 07, 2009 9:46:26 PM UTC
Wow, comments get nuked over in the Great Oxite Controversy, yet the discussion goes on here...

</BA, English>
Wednesday, January 07, 2009 10:57:32 PM UTC
What comments get nuked? I don't moderate or delete comments. I haven't deleted any on the Oxite post.
Thursday, January 08, 2009 12:11:34 AM UTC
My bad - I meant the post just before this about the MVC Gallery. I seemed to remember there were a few.
Thursday, January 08, 2009 4:54:45 AM UTC
Thanks for the PNGGauntlet mention - I'm glad people like it!
Thursday, January 08, 2009 10:50:59 AM UTC
Actually, according to wikipedia you can find different definitions of acronym in different dictionaries, some specify it must be a pronounceable word while others say it can be an initialism. You can state a preference for a particular definition, but you can't pronounce Scott plain wrong :)

But with regards to the actual post, I would side with those suggesting the raster text be replaced, lest you end up doing so later down the line when someone with a 120dpi display complains about a blurry UI.
Thursday, January 08, 2009 1:48:26 PM UTC
Ugh, don't get me started on bitmaps graphics in WPF. I thought I'd use some of the cool famfamfam icons ( in an app. forget about it! There is no way you can get them to look good. The only advice I got around the net was to use vector images, but I am not capable of creating good looking vector icons like that (hey, I'm a programmer).

That was seriously disappointing first WPF experience. That and the fact that Microsoft thinks it's no problem for the text on buttons to blur when the button is pressed (since all text blurs during 'animation'). I never touched WPF again... :-(
Tuesday, January 13, 2009 7:39:30 AM UTC
I'm finding PNGOUT pretty great actually, thanks for the tip
Tuesday, January 27, 2009 12:44:34 AM UTC
The idea of having vector graphics in WPF is good, but why does VS2008 only provide bitmap-based images/icons (%Program Files%\Microsoft Visual Studio 9.0\Common7\VS2008ImageLibrary\1033)?

if Microsoft really wants to promote WPF, then why doesn't it at least provide the same icons/images in svg ( format as well?

Device independence is a lofty goal, but the truth is that many (or dare i say most?) managed (.NET) Windows Apps are written for enterprise users who have standard dpi settings (i.e 96) on their desktop PCs. So in this case the developer of these apps can't care less about dpi. I personally know people who want to move to WPF but gave up after getting stuck by stupid issues like this (and a few others).

On a side note, can anyone point me to a few commercial products with a UI written in Windows Forms or WPF that are being sold at a decent price (well, let's just say over $99) instead of being given away for free?
Thursday, February 05, 2009 7:10:27 PM UTC
Scott: Actually, pngout doesn’t set the DPI (“PPI”, to be technical…). It just removes it as part of the ancillary pHYs chunk. Any PPI you’re seeing from a PNG that’s been through pngout with no paramaters is just the default in whatever software you’re using. For example, GIMP assumes 72 PPI for files that have been through pngout (or were simply saved without a PPI in the first place). You can keep the resolution data by running it with the /kpHYs switch (use /v for verbose output to see it in action).

Pete: I haven’t seen a new 96 PPI display in a long time.  :P

From the Pixel density article at Wikipedia:

Typical circa-2000 cathode ray tube or LCD computer displays range from 67 to 130 PPI. The IBM T220/T221 LCD monitors marketed from 2001 to 2005 reached 204 PPI. The Toshiba Portégé G900 Windows Mobile 6 Professional phone, launched in mid 2007, came with a 3" WVGA LCD having "print-quality" pixel density of 313 PPI…
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.