I just had to deal with something similar: let's call rectangle2 the image
, and rectangle1 the window
. So our task is to fit an image into the window.
Also in my scenario, both the image
and the window
have an "inner" coordinates system of [-1,1]X[-1,1]
. In fact, inside the window, there is a viewport
(rectangle3 in the question) where the image
will be projected, and so our task is to find the largest viewport
(a rectangle inside window
) that has the same aspect ratio (width/height) as image
.
The viewport
is determined by a fraction of the width/height of the image
:
viewport_width = scale_w * window_w
viewport_height = scale_h * window_h
So our goal is to find the right scaling of the width and height of window
, such that the viewport
will have the same aspect ratio (width/height) as the image
:
# goal:
scale_w * window_w == image_w
scale_h * window_h == image_h
Which means that the scale we are looking for satisfies:
(scale_w / scale_h) == ((image_w / image_h) / (window_w / window_h))
Let's denote the right hand side of this equation by s
for a moment.
Since we are looking for the maximal viewport
, surely at least one out of scale_w
and scale_h
will be equal to 1. So - if s<1
, meaning the window
is wider than the image
, we will have scale_w=s, scale_h=1
(this corresponds to the figure above, with scale_w=0.5
). And if s>1
, meaning the image
is wider than the window
, we will have scale_w=1, scale_h=1/s
.
In python code it looks like this:
def compute_viewport_scale(image_size, window_size):
image_w, image_h = image_size
window_w, window_h = window_size
im_ratio, win_ratio = image_w / image_h, window_w / window_h
scale = im_ratio / win_ratio
if scale > 1: # image is wider than screen
scale_w = 1
scale_h = 1 / scale
else: # window is wider then image
scale_w = scale
scale_h = 1
viewport_w, viewport_h = window_w * scale_w, window_h * scale_h
assert (round(viewport_w / viewport_h, 5) == round(image_w / image_h, 5))
return scale_w, scale_h
Since I wanted to be absolutely sure I have all the cases covered, I constructed a list of examples for sizes, to make sure indeed this formula is correct. And indeed, if you run all these lines, you will get no assert errors :)
# aspect ratio = 1
compute_viewport_scale((100, 100), (100, 100))
compute_viewport_scale((100, 100), (200, 200))
compute_viewport_scale((200, 200), (100, 100))
# image is wider than screen: (im_w / im_h) > (win_w / win_h) [ i.e. im_ratio > win_ratio ]
# (im_ratio < 1 and win_ratio > 1 cant happen)
# im_w > win_w and im_h > win_h
compute_viewport_scale((300, 200), (100, 90)) # im_ratio > 1 and win_ratio > 1
compute_viewport_scale((200, 300), (100, 200)) # im_ratio < 1 and win_ratio < 1
compute_viewport_scale((300, 200), (100, 150)) # im_ratio > 1 and win_ratio < 1
# im_w > win_w and im_h < win_h
compute_viewport_scale((150, 50), (110, 100)) # im_ratio > 1 and win_ratio > 1
compute_viewport_scale((200, 300), (100, 400)) # im_ratio < 1 and win_ratio < 1
compute_viewport_scale((300, 100), (100, 150)) # im_ratio > 1 and win_ratio < 1
# (im_w < win_w and im_h > win_h cant happen)
# im_w < win_w and im_h < win_h
compute_viewport_scale((150, 50), (200, 100)) # im_ratio > 1 and win_ratio > 1
compute_viewport_scale((90, 100), (100, 200)) # im_ratio < 1 and win_ratio < 1
compute_viewport_scale((110, 100), (100, 200)) # im_ratio > 1 and win_ratio < 1
# image is wider than screen: (im_w / im_h) < (win_w / win_h) [ i.e. im_ratio < win_ratio ]
# (im_ratio > 1 and win_ratio < 1 cant happen)
# im_w > win_w and im_h > win_h
compute_viewport_scale((300, 200), (100, 50)) # im_ratio > 1 and win_ratio > 1
compute_viewport_scale((200, 400), (100, 150)) # im_ratio < 1 and win_ratio < 1
compute_viewport_scale((200, 400), (150, 100)) # im_ratio < 1 and win_ratio > 1
# (im_w > win_w and im_h < win_h cant happen)
# im_w < win_w and im_h > win_h
compute_viewport_scale((150, 100), (200, 50)) # im_ratio > 1 and win_ratio > 1
compute_viewport_scale((200, 400), (380, 390)) # im_ratio < 1 and win_ratio < 1
compute_viewport_scale((200, 400), (390, 380)) # im_ratio < 1 and win_ratio > 1
# im_w < win_w and im_h < win_h
compute_viewport_scale((300, 200), (600, 300)) # im_ratio > 1 and win_ratio > 1
compute_viewport_scale((200, 300), (500, 600)) # im_ratio < 1 and win_ratio < 1
compute_viewport_scale((50, 100), (300, 200)) # im_ratio < 1 and win_ratio > 1