Any easy way to make an Image fill its space without stretching?

This seems like it should be simple, but I can’t see it:

I just want my background image to fill the screen (or in general, for any image to fill its bounds) while preserving aspect ratio. This means the top/bottom or left/right sides may be cut off.

The “Preserve Aspect” checkbox does not do this; instead it letterboxes your image, so the whole thing is visible, but you have big ugly gaps on the sides (or top/bottom).

In Cocoa View terms, Unity seems to provide UIViewContentModeScaleAspectFit, but I need UIViewContentModeScaleAspectFill. (Man, I do not miss Cocoa’s wordy names for everything!)

If I have to write it myself… I guess I can probably do it with the RawImage control. But it seems like such a common need, surely there’s an out-of-the-box solution I’m missing?

Got impatient. :wink:

/*
* Add this component to a RawImage, to automatically adjust the
* uvRect to make the image completely fill its bounds while
* preserving its aspect ratio (by clipping the top/bottom or sides).
*/
using UnityEngine;
using UnityEngine.UI;

[ExecuteInEditMode]

public class RawImageAspectFill : MonoBehaviour {
    RectTransform rt;
    RawImage img;
    Rect lastBounds;
    Texture lastTexture;

    void Update() {
        if (rt == null) rt = transform as RectTransform;
        if (img == null) img = GetComponent<RawImage>();

        if ((rt != null && rt.rect != lastBounds) ||
            (img != null && img.mainTexture != lastTexture)) UpdateUV();
    }

    public void UpdateUV() {
        if (rt == null || img == null) return;
        lastBounds = rt.rect;
        float frameAspect = lastBounds.width/lastBounds.height;

        lastTexture = img.mainTexture;
        float imageAspect = (float)lastTexture.width / (float)lastTexture.height;

        if (frameAspect == imageAspect) {
            img.uvRect = new Rect(0,0,1,1);
        } else if (frameAspect < imageAspect) {
            float w = frameAspect / imageAspect;
            img.uvRect = new Rect(0.5f - w*0.5f, 0, w, 1);
        } else {
            float h = imageAspect / frameAspect;
            img.uvRect = new Rect(0, 0.5f - h*0.5f, 1, h);
        }
    }
}

Seems to do what I need… but comments are welcome, of course!

2 Likes