In Godot (V4), I have a TextureRect containing a 1000x500 Bitmap. I have put the TextureRect inside several different nodes (AspectContainer, MarginContainer, Control Node, etc), so I can resize the Window, and have the Bitmap resize, while retaining its aspect-ratio. That part works well - but I also want to get the current displayed size of the Bitmap, after the Window's been resized - and that's been trickier. It almost appears impossible, but I can't believe that.
Some posts on the subject:
get-the-displayed-size-of-a-stretched-texture
get-size-and-position-of-a-scaled-texture
Getting the actual texture size of TextureRect had this gem:
If you want the final texture size, you can check how size calculation is implemented and copy some parts:
case STRETCH_SCALE: {
size = get_size();
} break;
with about a dozen more cases of the stretch-parameter. I'd actually try this, but it's Godot3, and I'm hoping there's a better answer for Godot 4
I also really liked this post: how-to-automatically-scale-a-texture-rect
the TextureRect is a bit weird, like beyond just eating crayons weird. Especially if you try to house it inside a Container node.
Uh, great... I tried it both inside and not inside a Container node - its behavior is fine, but trying to get information about it?
Even the game-engine doesn't seem to know the actual size - when I shrink the window vertically, the actual bitmap will shrink and retain its proportions. But the size of the TextureRect is reported weirdly:
global rect = [P: (0, 27), S: (1300, 421)]
global rect.size = (1300, 421)
viewport.rect = [P: (0, 0), S: (1300, 421)]
viewport.rect.size = (1300, 421)
The size has to be somewhere, it's displayed correctly - do I have to drop into C++ to get it? I hope not.
The size you want is not exposed. Godot only needs to compute it to render the texture every time it changes. You actually quoted a part of the code (which you found on a github comment), although that is an old version, it is not hard to find a newer version of the code if you are familiar with github.
The code looks like this:
switch (stretch_mode) {
case STRETCH_SCALE: {
size = get_size();
} break;
case STRETCH_TILE: {
size = get_size();
tile = true;
} break;
case STRETCH_KEEP: {
size = texture->get_size();
} break;
case STRETCH_KEEP_CENTERED: {
offset = (get_size() - texture->get_size()) / 2;
size = texture->get_size();
} break;
case STRETCH_KEEP_ASPECT_CENTERED:
case STRETCH_KEEP_ASPECT: {
size = get_size();
int tex_width = texture->get_width() * size.height / texture->get_height();
int tex_height = size.height;
if (tex_width > size.width) {
tex_width = size.width;
tex_height = texture->get_height() * tex_width / texture->get_width();
}
if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) {
offset.x += (size.width - tex_width) / 2;
offset.y += (size.height - tex_height) / 2;
}
size.width = tex_width;
size.height = tex_height;
} break;
case STRETCH_KEEP_ASPECT_COVERED: {
size = get_size();
Size2 tex_size = texture->get_size();
Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height);
float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height;
Size2 scaled_tex_size = tex_size * scale;
region.position = ((scaled_tex_size - size) / scale).abs() / 2.0f;
region.size = size / scale;
} break;
}
So, if you want to get it... The options are basically translating that to GDScript (or other language you can use), or modifying Godot source code to expose it.
There is a proposal you can go upvote if you want this feature to be added on future versions of Godot (it haven't got much attention so far): Add actual texture size and offset to TextureRect .
Since you don't want to get into C++, I'll assume that translating it to GDScript is fine.
I'll do keep aspect, which is the harder case any way:
var tex_width := texture.get_width() * size.height / texture.get_height()
var tex_height := size.height
if tex_width > size.width:
tex_width = size.width
tex_height = texture.get_height() * tex_width / texture.get_width()
var result := Vector2(tex_width, tex_height)
Note: compare this with the STRETCH_KEEP_ASPECT
case above. I skipped the STRETCH_KEEP_ASPECT_CENTERED
check, which only changes offsets. Aside from that you should be able to see that this matches the C++ code logic.
Just for exercise, let us come up with code for this without using the C++ code as reference.
We know the texture has size texture.get_width()
by texture.get_height()
. We are going to scale that up or down uniformly by some factor f
, such that texture.get_width() * f <= size.width
and texture.get_height() * f <= size.height
. A trivial solution is making f = 0.0
so, I also need to add that we want the largest possible f
.
So, there are two candidates, one we figure out from the width, and one from the height, let us do that.
From this:
texture.get_width() * fw == size.get_width()
We solve for fw
:
fw == size.width / texture.get_width()
And do the same for fh
:
fh == size.height / texture.get_height()
Now we can pick the smaller of the two and scale by that:
var fw := size.width / texture.get_width()
var fh := size.height / texture.get_height()
var result := texture.get_size() * minf(fw, fh)
For complete mathematical rigor, I'll mention that I know all these values are positive because they are sizes, and there are no negative sizes.
And for kicks and giggles, let us see what we would have to do to make that code look like the one in Godot.
What happens in Godot code is that it computes one possible result first, and then check if that is the wrong one:
var f := size.height / texture.get_height()
var result := texture.get_size() * f
if result.width > size.width:
f = size.width / texture.get_width()
result = texture.get_size() * f
Instead of using a vector, have two variables tex_width
and tex_height
:
var f := size.height / texture.get_height()
var tex_width := texture.get_width() * f
var tex_height := texture.get_height() * f
if result.width > size.width:
f = size.width / texture.get_width()
tex_width = texture.get_width() * f
tex_height = texture.get_height() * f
var result := Vector2(tex_width, tex_height)
Inline f
:
var tex_width := texture.get_width() * size.height / texture.get_height()
var tex_height := texture.get_height() * size.height / texture.get_height()
if result.width > size.width:
tex_width = texture.get_width() * size.width / texture.get_width()
tex_height = texture.get_height() * size.width / texture.get_width()
var result := Vector2(tex_width, tex_height)
And simplify the fractions:
var tex_width := texture.get_width() * size.height / texture.get_height()
var tex_height := size.height
if result.width > size.width:
tex_width = size.width
tex_height = texture.get_height() * size.width / texture.get_width()
var result := Vector2(tex_width, tex_height)
The advantage I can see from using this approach is that it avoids introducing some precision errors from the division.
Hopefully that shows that you can figure out how get the size at which the texture rendered and that Godot's C++ code is not too hard.