Pixel-perfect Multi-DPI Images in WPF (Part 2)

View source on GitHub.

See also: Part 1 and Part 3.

In Part 1 of this series, I explored the issue of displaying pixel-perfect bitmap images in the Windows Presentation Foundation. In this article, I’ll describe a method of displaying different images depending on the system DPI setting using a custom Markup Extension and multi-image TIFF files.

Tagged Image File Format (TIFF) files may contain multiple images, and WPF contains support for this format out of the box. You can use the TiffBitmapEncoder class to combine multiple images into one TIFF – I made a tool called PNGToMultiDPITIFF that does just this, but I’ll leave that to Part 3.

To pick the best-matching image from a multi-frame TIFF, I created two Markup Extensions – one for creating an ImageSource, and one for setting the image’s BitmapScalingMode. If the TIFF contains an exact match for the current DPI, the BitmapScalingMode can be set to NearestNeighbour (as there should be no scaling). If not, it will be set to ‘Unspecified’ (which means ‘Linear’ in WPF 4 or newer) so it looks better.

MultiDPIImage.7z
1,229 bytes; SHA-1: BB0B8867C48ECEADD7655E792DAA780A30299747

You can download the code for the Markup Extensions above.

As discussed in the previous post, remember to set UseLayoutRounding and SnapToDevicePixels to true on your Windows.

Code Discussion

The code for the markup extension is quite simple. To get the image frames, we use the TiffBitmapDecoder class:

We loop through the frames to see if any match the system DPI. If there is no exact match, the first frame with a DPI above the system DPI is selected. If there is no such frame, we just pick the frame with the highest DPI.

The code for choosing the BitmapScalingMode is similar – instead of returning an image, we return ‘NearestNeighbour’ if there is an exact DPI match or ‘Unspecified’ otherwise. If you want to use a different BitmapScalingMode fallback, you can specify an optional second paramater in MultiDPIImageScalingMode:

Drawbacks

  • Images set with the markup extension will not be visible in the WPF designer (Visual Studio or Blend). I’d welcome any suggestions on how to fix this.
  • Avoid using PNGOUT or PNGGauntlet on PNG images before putting them into multi-frame TIFF files. The Windows TIFF decoder has some issues with compressed PNGs.

Pixel-perfect Multi-DPI Images in WPF (Part 1)

View source on GitHub.

See also: Part 2 and Part 3.

I’ve written previously about DPI-awareness in the Windows Presentation Foundation and how to specify measurements in pixels rather than Device Independent Units (DIUs). Something else to consider is image scaling – unlike the Windows Ribbon control or WinRT, WPF has no in-built mechanism for displaying different images according to the system’s DPI setting. This is a nuisance.

To illustrate the problem, I created 5 images. Left-to-right, the image DPIs (vertical and horizontal) are: 72, 96, 120, 144 and 192. The image dimensions are: 32x32px, 32x32px, 40x40px, 48x48px and 64x64px. I purposely used single-pixel-wide lines to make any stretching obvious.

Original Images

Read on to see what WPF does with these images.

Continue reading “Pixel-perfect Multi-DPI Images in WPF (Part 1)”