Manual mode that keeps aspect ratio of a texture

Back with another question, sorry:

So I got my 200 animal images, all with different aspect ratios. The images are fairly large since there will be a close-up later in my app.
For my landing page, I clone a gui box (400 x 400 px) and assign the images as textures to my clones.

Setting the size mode to manual stretches the images as they fill the box, setting it to auto uses the input image size which is much larger.
To solve this, I made a second, smaller set of images to use with the box. This of course adds to the bundle size.
I can think of two other ways to achieve keeping the aspect: scale or size each clone individually. Cumbersome (since all images have a different aspect ratio) and, knowing me, prone to errors.

Would it be possible to have a manual size mode that keeps the aspect of textures?
I think this could be a useful feature. Or can this be done already and I missed something?

1 Like

I think you should be able to calculate the scale factor of the node, to make it the wanted final size. (while still using AUTO size mode)

E.g. something like:

local size = gui.get_size(n)
local max_size = math.max(size.x, size.y)
local wanted_size = 128 

-- if max_size == 256, then scale will become 0.5
local scale = wanted_size / max_size

gui.set_scale(n, vmath.vector3(scale, scale, scale))
2 Likes

Ah, of course @Mathias_Westerdahl, this would work! Thank you - you see, I am a fool sometimes (I would have used a huge array with the desired sizes). Off to changing my code.

Brigitte

1 Like

No worries!

I think your other solution is also perfectly valid. E.g. if you want to have custom thumbnails. The bundle size is something to monitor, but it’s not an absolute necessity to keep it as small as possible. It comes down to workflow, and maintenance as well as if the increase in bundle size is acceptable.

1 Like

True - I’ll keep an eye on the bundle size and see what works best.

1 Like

Good morning @Mathias_Westerdahl ,

back once more.
I now remember why I didn’t scale the nodes with a formula similar to yours.
Whenever I use atlases with my many images, bundle sizes go through the roof. Assuming an optimal atlas size of 2048 px, I’d need 50 of them for my images only - not counting the ones for buttons etc.
So I use sys.load_resource instead. And with this, I have not found a way to get at the image dimensions at init. Is there one?
Whenever a clone enters the window, I load the image and make the texture. It is only then that I can get at the image dimensions. When the clone leaves the window, I delete the texture to free up memory since I learned the other day that simply disabling them would not be enough.
So this means loading, making texture, deleting time and time again. If I add scaling to this as well, it leads to considerable lagging.
This is why I made a huge table with the desired dimensions and used it to size the clones once - in init.
For 200 images, making this table is very, very tedious and also error-prone, so I decided to use a smaller set of images with the right dimensions instead.
If there is anything wrong with my deliberations - could you let me know, please?

P.S Your formula was indeed very helpful - I hadn’t thought about using math.max when establishing the scale factor. Now I know - thank you!

1 Like

I now remember why I didn’t scale the nodes with a formula similar to yours.
Whenever I use atlases with my many images, bundle sizes go through the roof. Assuming an optimal atlas size of 2048 px, I’d need 50 of them for my images only - not counting the ones for buttons etc.

That is a lot of texture data. And it’s always dependent on your usage patterns. E.g are they all used “randomly”, or are they grouped logically, like “level 1” etc. Your use case has to dictate your workflow.
When you say “bundle size”, is that also with texture compression enabled?

So I use sys.load_resource instead. And with this, I have not found a way to get at the image dimensions at init. Is there one?

sys.load_resource() only returns the data. How do you parse the data into a texture?

So this means loading, making texture, deleting time and time again. If I add scaling to this as well, it leads to considerable lagging.

I’m not sure how the scaling would lead to lagging? The scaling factor should be set on the gui node, not applied to the texture itself.

For 200 images, making this table is very, very tedious and also error-prone, so I decided to use a smaller set of images with the right dimensions instead.
If there is anything wrong with my deliberations - could you let me know, please?

An option is of course for you to create an offline script (e.g. a python script) to parse all your images and output e.g an image_sizes.lua which you can require from your script.
Also, creating the smaller images is also very easy offline, using a script.

There are plenty of command line tools to find out the size of an image, or resize an image. I most often use ImageMagick

1 Like

Not sure either, but it does and I set the scale on the clone, not the texture. I can only assume because the images are larger.

Each image represents a level. Texture compression is enabled.

I do this (looping through the clones):

loadimage = sys.load_resource(/images/paintings/..index...jpg)
img = image.load(loadimage)
gui.new_texture(small_painting..index, img.width, img.height, img.type, img.buffer, false)
gui.set_texture(clones_table[index], small_painting..index)

Then I could store the dimensions in variables:
local width = img.width, local height = img.height

Now, this is a brilliant idea!

I never got used to ImageMagick and use either Phatch or Irfanview for these purposes.

Anyway - thank you very much indeed for your help and your patience!
Next, I am going to try and reduce my draw calls (I use a stencil) - should be no prob.

1 Like

Not sure either, but it does and I set the scale on the clone, not the texture. I can only assume because the images are larger.

The scale only affects the geometry of the gui node (ie. the vertices), not the texture itself.
So I guess something else is going on in your case.

Each image represents a level. Texture compression is enabled.

Then, only one atlas needs to be loaded at a time?
I may not understand you use case fully, but it seems it would suffice to have a single atlas loaded per level.
Are you also using one gui per level?

How much larger is the bundle compared to the bundle with only the .jpeg’s?
And you’re using BASIS compression?

And with this, I have not found a way to get at the image dimensions at init. Is there one?

Well, you have it there in your own code, to save the img.width/img.height?

1 Like

Aha, that is interesting - must have a look what it going on.

There will be one gui for the levels. Only the image will change, so I need one only.
At the moment, I am dealing with the landing page. There, the user scrolls though the images and picks the desired level. Max 12 images are fully or partially on-screen at a time.
Here is a quick prototype:

I ticked enable texture compression in the preference and use the built-in texture profiles.
As for the bundle size, I haven’t tried for this project yet. But I did with my last app. It used 168 images and using atlases, the bundle was about 120 Mb, without 22 Mb - quite a difference. This is why I stopped using atlases when dealing with many large images and switched to using sys.load_resource. I must do something wrong, I am sure.

True - but I would love to have been able to get at these data in init without having to load the images first.

I’ll follow your hints and make a version using atlases and see if I can optimize them and I’ll also look at texture profiles. Atlases sure would be a much more convenient way for dealing with my textures.

Thanks @Mathias_Westerdahl!

The default texture profile doesn’t compress the textures, you need to set it up to do so.
You need to use “Basis Uastc” in order to compress the textures.

They won’t become just as small as a compressed png/jpg.
The big benefit though is that they’re suitable for hardware acceleration on all platforms (i.e. they won’t take up the full RGBA size on the GPU)

1 Like

Cool - I missed that, thanks a lot! And I’ll have a stern word with my painter about using all these fancy formats: small and square is the way to go from now on :grinning:

1 Like

@Mathias_Westerdahl, I need to bother you again - sorry!
I made a test project with a huge number of uncompressed jpgs and try to see how much I can shrink the bundle.
My compression efforts lead to: no compression. I cannot spot my mistake and there must be one, maybe you can?

Settings:

output:

output

Could you please have a look if you have a little time?

The texture compression is applied on the image files that are referenced by the game.
Any custom resources (such as the ones you load with sys.load_resource() are left as-is.

I suggest you create one .atlas per level, and images to it, then use these .atlas files in your gui. That way they’ll be picked up by the build pipeline.

1 Like

Thank you very much @Mathias_Westerdahl!

This test project consist only of one gui scene with one box node. No script. So, if I understand correctly, images in custom resources will not be compressed?

If I use atlases and don’t load anything with sys.load_resources() , do I then need to have custom resources at all?

1 Like

So, if I understand correctly, images in custom resources will not be compressed?

Correct.

Correct. Then you don’t need the custom resources.

1 Like

Many, many thanks @Mathias_Westerdahl - textures in Defold puzzled me right from the start. But I will get a hang of them, thanks to all the help I get here. You see, I had atlases and custom resources, so now I know why my bundle sizes were so enormous. My atlas test is just in: 4000 kb - amazing! 50 atlases, same 200 large uncompressed jpgs, custom resources removed.
output

1 Like

Note that with Basis Universal compression, you’ll get lossy compression.
So we recommend using .png (lossless) as source images, as opposed to .jpg (can be lossless) if not special care is taken.
I.e. you don’t want two compressions.

When using lossy compression (as Basis Uastc), it’s also relevant to double check the output, and verify that it is acceptable.

2 Likes

That is very good to know. Now I just need to tell my camera to give up it’s jpg ways :grinning: No, I like it, I’ll just convert the images to pngs. Off to some serious testing.
So, thank you very much again, @Mathias_Westerdahl!

1 Like