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. 
/*
* 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