Looking at the C++ code
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.
Figuring it out ignoring C++
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.
Demonstration that the results are equivalent
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.