Keep aspect ratio when loading image with display.newImageRect

Hi,

I know similar topics have been asked dozen times, but I can’t find a satisfying answer, so I start a new one.

What is the best way to keep the original image ratio when I use display.newImageRect to load an image?

I want to use display.newImageRect to benefit of the “dynamic image selection” feature (@2x), but since I must specify both width and height when calling display.newImageRect, I loose the aspect ratio of my image.

So the most obvious answer would be to access the image’s original size before using newImageRect, by calling newImage (see this post: http://www.jeromehollon.com/2012/05/12/setting-max-width-and-height-on-images-for-corona-sdk/), but it seems to me extremely overkill to do that.

Is there a way to access the original size of an image without actually loading it in memory?

If not, do you plan to provide a similar feature?

Or something like modifying display.newImageRect so we would call it without specifying both width and height parameter, and it would keep the aspect ratio in this case (this feature would be very convenient)?

Thanks

 but it seems to me extremely overkill to do that.

(am assuming this must be a “dynamic” image that you can’t know it’s size in advance, otherwise just type it in)

textures are cached, so the first newImage would cause a load, but a subsequent newImageRect of the same file wouldn’t (it just creates a quad of the specified size and maps the cached texture across it), then calc aspect from original newImage, figure out aspect-corrected dimensions for your newImageRect, then dispose of the original newImage once the newImageRect has the reference to the cache.

Thanks Dave.

If I run the game on a device where the @2x (or more) assets are needed, for each image, I will load the standard version just to compute the aspect ratio, and then load the proper @2x version.

This doesn’t seem really efficient :frowning:

Plus I have no way to know which version has been loaded with newImageRect…

  1. you’d have to provide an example of your usage.  99 times out of 100, all you need do is specify the @1x dimensions to newImageRect (which you’d know if the images were bundled in your build).  only if you’re doing something rather “special” would you possibly need to complicate it any further than that.

  2.  display.imageSuffix

Thank you Dave.

My mistake, I didn’t know the imageSuffix chosen by Corona was the same for every imageRect. I thought Corona applied a imageSuffix dependant to the image size, that Corona computed how big the imageRect is scaled and chose an imageSuffix per imageRect.

In this case, I can simply rely on the display.imageSuffix and use display.newImage.

Thanks

If you have an image that’s 100 x 100  and is image.png and  you  have 200x200 and is image@2x.png, you just:

local image = display.newImageRect(“image.png”, 100, 100)

If we think you need the @2x image, we load image@2x.png and scale it to 100x100 in your content space (which on a device that needs the @2x image, it will be loaded in at 200x200, but its still 100x100 in content space). Corona handles all of the content scaling for you.

I don’t see why there would be a condition where you’re providing both 1x and @2x images and you would not know the dimensions in advance and you would need to have to deal with an unknown image size. But if you want to do this:

local image = display.newImage(“image.png”)

local width = image.width

local height = image.height

local aspectRatio = width / height

image:removeSelf()

image = nil

image = display.newImageRect(“image.png”, 50, 50 * aspectRatio)

But again, if you’re providing @2x images, they typically would not come from an unknown source.

Rob

I should precise what I was thinking about image size:

Of course I could provide the correct image size when loading it. But it’s really not convenient. Most of my images have various sizes, they are not all squared or with a “standard” size. So when I load them, I want to only specify the name of the image, and not its size. Because otherwise I would need to hardcode the size of each images in the code: if I want to resize or crop my image later, I would have to change it also in the code, which is not convenient at all.

Then when I resize my image, I want to make it fit in a specific “box”. For example 100x100. But my image size is not necessarily 100x100, or 200x200. Maybe it’s 210x190. And the most important is that I don’t want to know it and specify its size in the code because it can change in the future.

So in a perfect world, I would love to load my image using display.newImageRect(“image.png”, 100, 100), but with a extra parameter asking to not necessarily resize it to the exact 100x100 size, but resize it to make it fit into a 100x100 box. So if my image if 210x190, the imageRect size would be 100x90.

So yes, I could do that by first loading the image with display.newImage, computing the size/ratio, and then load with display.newImageRect with the proper size (like your piece of code in the last message).

The problem with that, is if I’m running of a @2x phone, each time I will load the first image (“image.png”) just to compute its size. The image.png will never be used. It’s not really efficient.

>> And the most important is that I don’t want to know it and specify its size in the code because it can change in the future.

Catch-22.  This is the source of your “problem”, which will persist in one form or another for as long as you force yourself to wear blinders.

In other words (as i said, as rob said) you (seem to) have access to all the information you need, but just refuse to use it, instead preferring to load images at run-time to rediscover that info, then ‘wishing’ there were some way you didn’t have to do so.

So why not build an “asset catalog” (you could even write a bit of lua as a dev-time utility to scan your files and produce it for you if you wanted), something like (just for ideas):

local assetCatalog = { ["image1.png"] = { w=210, h=190 }, ["image2.png"] = { w=100, h=100 }, -- etc } local cat = assetCatalog["image1.png"] print(cat.w, cat.h)

Then you could “look up” image dimensions as you wish.  Sure, you’d have to regenerate your catalog if/when your assets change, but that’s a small price to pay at development-time to avoid a bigger price at run-time.

The same reflexion can be applied to a lot of features, like the “Dynamic Image Selection” feature itself:

I could live without it. I could write a piece of code which compute the scale factor, choose an imageSuffix like “@3x”, and then when loading each image, add some code to append “@3x” and the end of the filename, try to load the @3x image, and if it doesn’t exist try to load @2x

I could totally do that. But fortunately Corona has this great feature and handles this for me, which is very convenient for a lot of people.

For me this is the same reflexion than my “problem”. Of course I could do like you said, make a tool which compute the size of each of my images…

But it would so more convenient to be able to retrieve the image size at runtime (without having to actually load it) or to have a parameter in display.newImageRect to keep the aspect ratio.

It doesn’t seem to be like a “crazy” or a “stupid” feature to me. Personally, it would be very useful and convenient to me.

So the whole purpose of my post was to ask if I have missed any feature like that and/or if you planned to add it.

And I have my answer now.

Thanks.

>>retrieve the image size at runtime (without having to actually load it) 

Just pretend you’re a Corona engineer for a moment and ask yourself how you’d do that.

Didn’t mean to imply your request was “crazy/stupid”, just that there are real ways around your problem right now, without having to wait for a feature request that may never come

>> Just pretend you’re a Corona engineer for a moment and ask yourself how you’d do that.

By just loading the header of the image file and accessing only this info. It wouldn’t be as heavy as loading the whole texture into video memory like display.newImage does I suppose.

Btw, I found a post with Lua implementation to access png and jpg info (size and some other useful metadata) without using display.newImage. Could be an interesting alternative:

http://developer.coronalabs.com/forum/2012/05/16/pnglib-extract-data-png-files-width-height-color-depth-etc

Thanks

no worries, we just “think differently” if you think that approach is “easier”, and that’s fine

if you DO feature request something, i’d separate it out into a “please provide a ‘get image header info’ method” request, that you could then use to calc/pass values to newImageRect, rather than an internal modification to how/what newImageRect does ‘automagically’ – guessing that it’d be more likely to get the proper consideration if worded that way

Thanks for the tip :wink:

In fairness, I have an app that displays photos. The photos can vary in size and I use display.newImage() at run time to load the image and then I fit them to a box. But there are not @2x and @4x versions. As I said above, if you have @2x images then you probably made the the two images and therefore you have the image size and can program them in.

But if you’re needing images you can’t control, you can use this scaling code.

local image = display.newImage("Image.png") local s if image.width \> 100 then -- 100 is the box width     s = 100 / image.width     image:scale( s, s ) end if image.contentHeight \> 100 then -- check to see if we are still too high. Need to use the scaled height though.    s = 100 / image.height    image:scale( s, s ) end

or something like that.

Rob

 but it seems to me extremely overkill to do that.

(am assuming this must be a “dynamic” image that you can’t know it’s size in advance, otherwise just type it in)

textures are cached, so the first newImage would cause a load, but a subsequent newImageRect of the same file wouldn’t (it just creates a quad of the specified size and maps the cached texture across it), then calc aspect from original newImage, figure out aspect-corrected dimensions for your newImageRect, then dispose of the original newImage once the newImageRect has the reference to the cache.

Thanks Dave.

If I run the game on a device where the @2x (or more) assets are needed, for each image, I will load the standard version just to compute the aspect ratio, and then load the proper @2x version.

This doesn’t seem really efficient :frowning:

Plus I have no way to know which version has been loaded with newImageRect…

  1. you’d have to provide an example of your usage.  99 times out of 100, all you need do is specify the @1x dimensions to newImageRect (which you’d know if the images were bundled in your build).  only if you’re doing something rather “special” would you possibly need to complicate it any further than that.

  2.  display.imageSuffix

Thank you Dave.

My mistake, I didn’t know the imageSuffix chosen by Corona was the same for every imageRect. I thought Corona applied a imageSuffix dependant to the image size, that Corona computed how big the imageRect is scaled and chose an imageSuffix per imageRect.

In this case, I can simply rely on the display.imageSuffix and use display.newImage.

Thanks

If you have an image that’s 100 x 100  and is image.png and  you  have 200x200 and is image@2x.png, you just:

local image = display.newImageRect(“image.png”, 100, 100)

If we think you need the @2x image, we load image@2x.png and scale it to 100x100 in your content space (which on a device that needs the @2x image, it will be loaded in at 200x200, but its still 100x100 in content space). Corona handles all of the content scaling for you.

I don’t see why there would be a condition where you’re providing both 1x and @2x images and you would not know the dimensions in advance and you would need to have to deal with an unknown image size. But if you want to do this:

local image = display.newImage(“image.png”)

local width = image.width

local height = image.height

local aspectRatio = width / height

image:removeSelf()

image = nil

image = display.newImageRect(“image.png”, 50, 50 * aspectRatio)

But again, if you’re providing @2x images, they typically would not come from an unknown source.

Rob

I should precise what I was thinking about image size:

Of course I could provide the correct image size when loading it. But it’s really not convenient. Most of my images have various sizes, they are not all squared or with a “standard” size. So when I load them, I want to only specify the name of the image, and not its size. Because otherwise I would need to hardcode the size of each images in the code: if I want to resize or crop my image later, I would have to change it also in the code, which is not convenient at all.

Then when I resize my image, I want to make it fit in a specific “box”. For example 100x100. But my image size is not necessarily 100x100, or 200x200. Maybe it’s 210x190. And the most important is that I don’t want to know it and specify its size in the code because it can change in the future.

So in a perfect world, I would love to load my image using display.newImageRect(“image.png”, 100, 100), but with a extra parameter asking to not necessarily resize it to the exact 100x100 size, but resize it to make it fit into a 100x100 box. So if my image if 210x190, the imageRect size would be 100x90.

So yes, I could do that by first loading the image with display.newImage, computing the size/ratio, and then load with display.newImageRect with the proper size (like your piece of code in the last message).

The problem with that, is if I’m running of a @2x phone, each time I will load the first image (“image.png”) just to compute its size. The image.png will never be used. It’s not really efficient.