Skip to content

Instantly share code, notes, and snippets.

@mortoray
Last active August 29, 2015 14:16
Show Gist options
  • Save mortoray/e151ddfd1015643bb400 to your computer and use it in GitHub Desktop.
Save mortoray/e151ddfd1015643bb400 to your computer and use it in GitHub Desktop.
Handling density in images

The desire of this proposal is to make a system that works with a minimal number of assets and a minimal syntax. It retains pixel crispness when possible, but still presents a usably sized display on all devices. It also optionally allows for using multiple images to get better clarity on multiple devices when a project can afford to produce multiple assets.

Basic

A user creates a single image.png file and should be able to use this in the interface like this:

<Image ux:File="image.png"/>

This should work across all devices, that is ones with different screen densities. It won't be significantly larger or smaller if the target screen density differs. It produces a predictable UI layout.

There are two options for how to interpret the above to make that happen:

  • the file is assumed to be at 1:1 with points. So a 100x100 file maps to 100x100 points. This would only result in pixel-crispness if the target density is the same, or a multiple. The layout size of this image would always be 100x100 points on all devices.
  • the density is will be near 1:1 with points to keep pixel clarity on devices. For example, if the screen has a density of 0.9 it would not retain the 100x100 point size, instead we'd say the image is 111x111 points so it maps exactly to 100 screen pixels. The result point size would always be "near" 100x100 but vary slightly to try and match pixels.

It seems reasonable that a user may wish to choose one mode over the other, but for crispness reasons we may wish to make the second mode the default. Or we could use the SnapToPixels property to decide.

High density

In this scenario the user wants to produce a single high density image that will be used on all devices.

<Image ux:File="image@2x.png"/>

The filename includes the density of the image, or rather the screen density for which it was designed. In this case the same basic two modes could apply

  • the file maps 2:1 with points, so a 200x200 source file maps to 100x100 point size. This point size doesn't change between devices
  • the file maps to nearly 2:1 with points, so the point size will vary slightly to keep pixel accuracy, but the actual layout size will be "near" 100x100 points.

The reason the user would do this is so that they can create a single high resolution image that still looks okay when resampled to other resolutions -- as opposed to just a low-resolution image which doesn't upsample very nicely.

Multiple Density

A user wishes to create two different densities and have the system pick the one which is closest to the target screen density. There are a couple possible syntaxes here, and explicit list:

<Image ux:Files="image.png, image@2x.png"/>

In this case the system will just use the one closest to the screen density and then use one of the modes mentioned before.

The other option is that the system automatically finds the related files:

<Image ux:File="image.png"/>

This causes the system to look for all files with the pattern image@*x.png and create the list of alternate densities automatically. So if the user has image.png and image@2x.png and image@0.75x.png all three will end up in the list of available densities.

Backing data structures

Obviously the above syntax would just be a shortcut syntax which uses the name of the file. An extended form must also be available in case the user wants to specific explicitly, or needs to create this from code.

For explicit density the current image sources already support the required attribute:

<BundleFileIMageSource ux:Path="image.png" Density="1"/>

To combine multiple densities we probably need a combining image source of some kind:

<MultiDensityImageSource>
    <BundleFileIMageSource ux:Path="image.png" Density="1"/>
    <BundleFileIMageSource ux:Path="image@2x.png" Density="2"/>
</MultiDensityImageSource>

The UX processor would expand to the appropriate form based on what files it finds (or it could just always use MultiDensity with just a single item).

HTTP Images

Just like BundleFileIamgeSource, HttpImageSource also supports a Density parameter. We could optionally allow the image source to get the density from the name of the file.

This also works naturally within a MultieDensityImageSource:

<MultiDensityImageSource>
    <HttpImageSource ux:Path="http://domain.com/image.png?density=1" Density="1"/>
    <HttpImageSource ux:Path="http://domain.com/image.png?density=2" Density="2"/>
</MultiDensityImageSource>

This even allows that only the best matching density is actually loaded.

As an additonal feature we might create a special HttpMultiDensityImagESource which takes a single URL and adds a parameter indicating the density. This would allow the server to provdie the correct image in a dynamic fashion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment