Fitting an image to its element frame

mortoray

edA‑qa mort‑ora‑y

Posted on April 11, 2018

Fitting an image to its element frame

Images require various sizing modes to fit in a responsive layout. Here we'll look at the relationship between the element and visual and the basic sizing options. I'll also look at some additional helpful features, not all of which I've implemented before.

The element and visual content

When considering the box model, it's common to confuse the context-box with the visual content itself. If the element is collapsing to the size of the visual content, there will not be a difference in size. However, if the element is expanding to fill space, the visuals may not fill the available area.

Before we get into images, let's consider this concept with a circle element first. The circle will always be a circle, as opposed to being an ellipse.

A circle fitted to the element size

The circle element can be larger than the visual; the circle centers itself within the available element area, expanding to either the width or height.

An image element must deal with the same situations: the element size is not necessarily the same size as the image. The correct way to size and position the visual depends on the design; we must provide properties to specify what happens. In Fuse I called this the StretchMode; In CSS this is the background-size or object-fit property.

Pixel and Point precise

Perhaps the most apparent sizing mode is just using the natural size of the image; regardless of the element size, we'll draw the image as-is and either clip the edges or leave empty spaces.

A 320x212 image clipped into a 250x300 element

This sizing mode is most frequently used in layout where the element ends up the same size as the image. For example, you may use icons in a title bar and let them define its size.

We need to resolve the question of what "natural size" we're talking about though. We could use either pixels or points. A pixel precise sizing mode is needed when you want an image to be crisp on the display, with no resizing. A point precise mode is used when you want the image to define the size of the element and be consistent across devices.

Pixel precise mode is hard to work with on mobile as the devices have wildly different pixel-to-point densities. If desired it must be used in conjunction with multiple density images: an image element that uses different source files depending on the target density. In Fuse, I added a PointPrefer mode to get the best of both worlds: it uses a pixel-precise mode if one of the pixel sizes is close enough to the point size; otherwise, it stretches the closest image to the desired point size.

Fill and Scale9

Images are often used to fill an area, providing a background, border, or perhaps divider. This requires the image to stretch, filling the entire element area, even if the image itself is distorted.

A stretched image, with aspect distortion.

The above photo helps show the distortion, but the fill feature is not likely suitable for such images.

A helpful feature of stretching is a 9-grid stretching: the image is divided into nine areas and stretched on their own. This allows the creation of borders for a dynamically sized element. CSS provides a rather robust feature with border-image.

A 9-grid image, showing preserved border/corner sizes, but stretched centres.

Uniform and UniformToFill

If the entire image must fit within the element, and the aspect ratio must be preserved, we use the Uniform mode (called contain in CSS). This scales up the size of the image so that it touches two sides of the available area, leaving the remainder of the element empty.

Preserve ratio, full image inside element.

If instead, you wish to fill the entire element and still retain the aspect ratio, we use a UniformToFill mode (called cover in CSS). This scales the image until it reaches all the edges of the element, clipping those parts outside of the element.

Stretch to fill preserving aspect, clipping image

Content Alignment

Other than the Fill or Scale9 mode, the image doesn't fit exactly into the available space. This leaves open the question of where to position the image? Should a Uniform image be positioned to the left, or right? Should a UniformToFill image prefer to show the bottom or top of the image?

Adding a ContentAlignment to an Image allows the user to specify this option. A good default is inheriting the alignment of the image element. If you align an image to the bottom right, you'd expect the visual to also be aligned to the bottom right.

Uniform fill with Left and Right content alignment

One option I never got to implement myself is anchoring; basic alignment doesn't cover the nuances of design well enough. An anchor allows us to choose a spot of interest on a photo and ensure it's centered, such as the face for a profile image.

Extending this concept is an anchoring area: instead of just a point we mark a rectangle of interest in the source image. This would open up the possibility of a Uniform mode that ensures the area of interest is fully shown, and the rest of the image can be used to fill up the empty areas.

I suspect most services require square profile photos as they don't have anchoring available. With anchoring, you can accept photos of arbitrary sizes, let the user mark the face area, or detect it, and then use the whole image in a responsive design.

Options

For Fuse, we also had a StretchDirection option. It could be one of Both (the default), UpOnly, or DownOnly. An image would only stretch in the allowed directions. For example, if we have an Image {StretchMode=Uniform StretchDirection=DownOnly}, the visual would shrink to fit an element, but would never expand beyond its natural size. In practice, there are limited use-cases for this.

An optional Fill flag provides an alternative way to fill in the empty area of the element. After positioning the element with the stretching mode, it will be repeated to cover the remaining space. This makes the most sense for a background image, or when used as a brush.

In addition to anchoring, an Offset property would allow for creating simple animations. Position and size the visual normally, then apply an offset to the resulting position. This would most likely be combined with the Fill flag on tiled images.

Other elements

If you are writing a UI engine you might be interested in knowing how to do all these calculations. Let me know if you'd like such an article -- it'd be rather math dominant, though not complicated. In Fuse, you can lookup the SizingContainer class which encapsulates most of the logic -- albeit the layout calculation code is also part of it.

An interesting point about these sizing modes is that they could equally well apply to any number of element types, not just images. The generic CSS object-fit property goes in that direction. I even implemented a Viewbox once which scaled arbitrary elements, which supported StretchDirection but forced a Uniform fill mode.

The concept repeats; merging all the diverse uses into a single set of properties would be a good goal for a new UI-engine. Even if some combinations don't make sense for certain elements, having a common set of properties reduces the learning curve and ensures a rich set of features.

Read more articles about writing a UI Engine. Follow me on Twitter, and to keep me writing, consider becoming a patron.

💖 💪 🙅 🚩
mortoray
edA‑qa mort‑ora‑y

Posted on April 11, 2018

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related