Does "oversizing" images give better image quality?

yeh, i didn’t even toss in the orientation issue – and you’re right, it’s really THREE topics that need to be considered simultaneously

trying to close the loop and tie this back to my earlier assertion that calculated content resolutions can be MORE deterministic than letterbox wrt dynamic image scaling…

back to portrait orientation for the moment, if width is set to a fixed 480 and height were calc’d as 480*deviceAspectRatio, then there’d be no letterbox bars anywhere, and the 1.6 @2x images would have been used on that portrait iPad (as my ficticious OP imagined) – the resulting width-scale-ratio seems more “intuitive” with the calculated dimensions than with the letterbox extended-width.

personally I find zoomEven works best for my app.  iPad gets more vertical and iPhone gets more horizontal but then again my playing area is MANY times larger that the screen can display.  the only real pain is locking UI top and bottom.

to address the OP, this is a trick commonly used in websites… on non-retina displays (1 point = 1 pixel) the browser downscales the large image to the smaller image dimensions. but on retina displays (where 1 point is actually 4 pixels) then the higher resolution raw image is used instead.

so a 100x100 image fit into a non-retina display will be downscaled to 50x50.  On a retina display the 100x100 will be used but it will only take up 50x50 points on the display - if that makes sense

The ultimate config.lua and the following “Modernizing” was done to solve a problem, that was simply to make 0, 0 the top, left corner, and display.contentHeight, display.contentWidth the bottom right corner. This gives the app a content area that is guaranteed in letterbox mode to fill the screen exactly but it has a bad drawback in that is the distance between objects cannot be guaranteed. It requires things that should be a fixed distance apart harder to accomplish.  Considering these two scenarios:

A Card game with 7 stacks of cards

Angry Birds

In the card game scenario, you are likely going to have the 7 card stacks evenly spread across the screen. If your on an landscape iPad, you only have 480 content area points to draw the 7 stacks. On an HDTV shared phone, you have 570 pixels to show things, so it’s a bit easier to position the cards since the number of points between cards in irrelevant. They can spread out, they can compress together as long as they don’t over lap.  The variable config.lua can work for this scenario.

But this would totally stink for Angry Birds because the distance between the slingshot and the pigs would be different on every device and the game would be constantly be playing differently for each different screen shape.

The standard fixed config.lua guarantees that your 0-319, 0-479 will represent the same distance between points regardless of the device. However 0, 0 isn’t necessarily the top, right and display.contentWidth and display.contentHeight is the right, bottom of your content area. Because screens are a different shape, you pretty much have to use display.actualContentHeight and display.actualContentWidth to get the real size of the screen and then add display.screenOriginX, display.screenOriginY to anything that needs to be edge aligned.  

Some UI elements can be moved around without impacting the game. Things like the score, a lifebar, a fire button etc. need to be near edges. The distance between the score and the lifebar don’t have to be constant. You want your buttons in the corners for easy reach.

If you look at an iPhone 5 with a 320x480 content area (I’m going to do this example totally as a landscape example, but you still have to consider config.lua in portrait orientation). The actualContentWidth of the iPhone 5 with a fixed 320x480 config.lua will be 568px. The content area is typically centered on the screen.  568 - 480 = 88 content points that are now evenly divided on either side of the content area.  This means the left edge is actually at a .x of -44.  The right edge is either display.contentWidth + 44 or display.actualContentWidth - 44. They work out to be the same.

There are different strategies at tackling this, but they all involve using display.contentOriginX, display.contentOriginY. In this example .contentOriginX will be -44, .contentOriginY will be 0.

To position the health bar at the top-left corner 50 points from each edge you would do:

healthBar.x = 50 + display.contentOriginX

healthBar.y = 50 + display.contentOriginY

Corona will have calculated the value for contentOrigin*  and you know for certain the edge is those pixels away from your content area.

The trick on the other side is that since 0, 0 is already 44 pixels away from the leftEdge, if you want to position your fireButton at the bottom, right, if you use display.actualContentWidth, that part of your app will already be 44 pixels off screen, so you still have to add the contentOrigin* contents  to adjust things:

fireButton.x = display.actualContentWidth  - 50 + display.screenOriginX   – position the button 50 points from the right edge of the screen.

Of course you could take the math.abs(display.contentOriginX) and add that to display.contentWidth to get the same place on the screen.

For your core game elements, keep them within the 0-480, 0-320 range and call it a day. At that point you’re not trying to edge position but content area position.

Let’s switch to an iPad which gives you an effective 360x480 screen. Now display.screenOriginY will hold the value of -20 (360-320=40/2=20) and as long as you are always adding these screenOrigin* constants to edge positioned objects, they will move to the right place on your iPad, and your game elements stay in their 320x480 box.

We recommend that you use a fixed config.lua and write the extra code to add the screenOrigin* constants to your edge positioned items. It’s extra typing but unless you have a game that benefits from variable distance from game objects, you’re better off with the fixed config.lua.

Rob

@Rob:

Thanks for your thorough explanation.

If I understand you correctly, doing it the way you describe it above would in fact also guarantee that the screen would be filled with “content” and that there would be no “black edges” neither on top or to the sides. Is that correct?

Now that the “ultimate config.lua” seems to have fallen out of grace, what would you recommend that a good config.lua looked like (given that I do not want to optimize for any single device)?

Good config.lua?  

That depends on your app really.  If it is a simple match 3 style game at 320x480 with @1x, @2x and @4x assets will be just fine.  If you want something with more depth and more touch accuracy I would suggest 480x800 with @1x and @2x assets.

If you are only targeting high end devices you could go for 800x1200 but Corona will be downsizing on devices like iPhone 5.

IMHO, 480x800 covers most devices and scales well.  iPad might cause a few headaches as it is almost square but you could just over size your backgrounds and keep the main game content in the centre of the screen.

640x960 is also a “convenient” resolution these days, as often a single set of images can suffice.  (because it’s “@1x” is effectively @2x, so you’d only add @2x this THIS config only if you’d have added @4x to a 320x480 config)  caveat: if still need iPhone3 support with lots of images / memory concerns.

it can help to think about if your app has a “natural” single axis that it extends best along.  if so, then it’s often “convenient” to fix the other axis.  fe, if making a horizontal runner then fix the vertical axis, and let the horizontal axis be whatever the device aspect dictates - your “camera” should handle everything else.  (and vice versa for a vertical jumper/shooter)

if you free-scroll on both axes then it really doesn’t matter if either is fixed, your “camera” should handle everything.

if your app doesn’t “naturally” scroll in EITHER axis, fe if it’s mostly ui elements with a fairly rigid structure, then you can just letterbox, occupy the content area as designed, and maybe just fill the bars with background.  if you could compensate your ui a bit to better use those bars, instead of just background-filling them, then that’s pretty much the “standard config” way of doing things.

The config.lua that we recommend is:

application = { content = { width = 320, height = 480, scale = "letterbox", fps = 60, imageSuffix = { ["@2x"] = 1.5, ["@4x"] = 3.0, }, }, }

Make your backgrounds 360x570 for the 1x version. Don’t put anything important in the background image that’s in the 45 pixels near the long edge of the image and in the 20 pixels along the short side of the image.

Position game objects with the understanding that the only area guaranteed to be on screen is 0, 0 - 320, 480. Anything that needs positioned near an edge and can move add display.contentOrignX to the .x of the object and add display.contentOriginY to the .y. 

Rob

I’m surprised the whole mess of config.lua hasn’t been revisited as I’ve *always* found these extended configs to be more trouble than they are worth.

Personally I think it is time to move on from the width and height of 320x480 (way outdated) and use a better default resolution.
My own configs use something like:

width and height of 960x480 (all modern devices support at least this), letterbox scaling and alignX and alignY of left and top, so you will always get a *minimum* of 960 x 480, but with extra width or height as needed to fill the device screen.

@rakoonic, that’s reasonable too. However, it breaks display.contentCenterX, display.contentCenterX since you’re no longer centering the content area.  Now you do get 0, 0 being top, left and display.actualyContentWidth, display.actualContentRight being the right and bottom edges.  They all have their pros and cons.  I like this for business type apps where you’re going to fill things from the top down and perhaps have a scroll view that is sized to fit the screen. Typically in these apps you’re not centering things.

As far as sizes go, at one point Brent and I looked at modern devices and came up with 800x1200 as a good new size to standardize on. Though I was thinking about @2x versions which would be 2400 px which could cause some problems with maxTextureSize on some devices.  I think 640x960 is reasonable too but keep in mind, several of our widgets are designed based on a 320x480 content area and they don’t scale. You could of course skin them yourself but…

Another idea that someone suggested was to go to a 16:9 on all devices like 640x1140. On tablets  you would have more extra area at the top and bottom for you to place your UI elements, but the game/content itself would stay within the boundary of the content area.

Rob

(perhaps beating a dead horse…)

480xAspectCalc is what I have used for a long time, because it solves all my “problems” (display.contentCenterX/Y still work, display.screenOriginX/Y are both 0, all of the display.actual/viewable/content-Width/Height are equivalent, scale mode no longer matters so pick your favorite they’re all equivalent - no crop/bleed/anamorphic stretch, display.contentScaleX/Y are uniform, etc)

it’s more-or-less equivalent to the 480x800’s or 480x960’s listed above when running on devices of same aspect ratio, just avoiding any crop/scale/extend from having a fixed height with the various scale modes when device aspect DOESN’T match content.  (which could result in a potentially variable width OR height)

at design-time I “think” about my content dimensions as if they were 480x640 at 4:3 aspect, the most restrictive, then just plan to grow vertically (using portrait orientation terms) from there - since i know width will remain fixed at 480 no matter the device.  (it’s also nice that fullscreen @1x images fit well into 512x1024 - not that power-of-two is strictly necessary, just a “bonus”)

it also completely demystifies the dynamic image selection @2x ratio math (that can get muddled if your width becomes letterboxed).

the only thing that would remain wonky is widgets built for 320x, but i don’t use the widget lib, so don’t care.  an equivalent setup would be to use 320xAspectCalc if you depend upon widget.  i’ve also used 640xAspectCalc (a more recent project, where it didn’t seem excessive, given target devices) tho i’m not yet ready to move to 800xAspectCalc due to memory concerns.

This is actually a great discussion. People want one config.lua to rule them all, or one that they don’t have to ever think about. The point is there is a reason why we give you control over that file. You find what works for you. 

Rob

I will chime in here based on real stats…

480x800 is a great starting point and is my 3rd most popular layout… this also scales to the top 2 layouts nice

btw, same top 3 (android) for me too (tho different order/ratios)

also should mention that 320xAspectCalc (or any other W in WxAspectCalc) is really no different than if you set both fixed IFF they’re at a 4:3 aspect.  (i’m not aware of any extant “more-square” devices)

ie, using a fixed 480x640 with letterbox accomplishes nearly the same thing as calcing aspect height - as it’ll only grow vertically (again using portrait terms) keeping 480 width.  only downside vs calcing height is that you must then trade-off between keeping origin y=0 or keeping display.contentCenterY - you can align y to top (if want y=0) or to center (if want display.contentCenterY) but not both.  (tho you could recalc and override at runtime i guess)

@Divergent Monkey: the 50 that you set in your code are virtual pixels, the actual screen resolution on whatever mobile device you run it will be 2x or 3x higher. (Well I think you know this, just answering OP as written.)

You can use the @2x and @3x options in config.lua to force the program to use images specifically generated for these factors, but my experience is that a single image that is “significantly larger up to 3 times the resolution” will look good in 50x50 virtual pixels on all devices. I think 2x is a perfect compromise if you want to be reasonably economical about app size/memory requirements. That’s what I use all of the time and it looks good even on my QHD Nexus 6P.

 

For me it’s “work spent on the wrong things” to supply anything other than the launch images in specific resolutions. The device OSes handle image scaling effortlessly today, or at least they should and you should take advantage of that. Other things like usability, layout, design of the graphics is more important to me.

 

As for adapting to aspect ratios, I prefer to fix one axis (typically the width of the device in portrait mode) and stretch layouts dynamically using a variable for the height, making sure never to stretch images.

As a general rule with digital imagery (vector graphics aside), it’s always better to scale an image down than to scale it up. While some digital graphics (think a square on a solid color background) and with the “magTextureFilter” set to “linear” (good for line art, terrible for photos) will scale upwards fine, most won’t. Scaling up an image involves creating new pixels where there were none.  “linear” makes not attempt to smooth out the scaling, but for line art that’s usually okay. Most of the time you want “nearest” for anything more photo like. These new pixels are an approximation based some complex math that basically tries to figure out what color pixel to insert. The result is the more you scale an image up, the more blurry it becomes.

Downsizing throws away pixels and for the most part, the math that does that maintains a pretty sharp, usable image.  Based on this, you could load your large size image and let Corona scale it down and it would look just fine… however…

Smaller physical screens usually means a device with less memory, less storage, less CPU power and less GPU power. Resizing images on the fly takes computational power. It takes more time to read the image from storage and convert it to a GPU texture. A 4x image uses 16X the memory over a specifically designed 1x image (50x50x4 = 10,000 bytes of memory. 200x200x4 = 160,000 bytes of memory). Large images will quickly overwhelm the memory and processing power of older devices creating performance problems. Of course having three versions of every image takes up more storage, but like everything in life, it’s about tradeoff’s.

Rob

@Rob, It is not really the physical size of the screen but the screen resolution that matters most.  More pixels need more texture memory and GPU just to power the screen.  

e.g. A modern phone will have more texture memory and faster GPU then a tablet of a few years ago but with a much smaller screen.  iPad 2 is the worst case scenario of large screen but tiny memory!

In fact the new Galaxy s8 has Quad HD+ screen. By default, though, it’s not set to run at its full, native resolution of 2960x1440, instead it’s downsampled to “Full HD+,” or 2220x1080. And if you want, you can set it to go even lower, down to “HD+” or 1480x720.

2960x1440 is crazy resolution on a phone!

Yeah Rob, but DPI goes up, never down so that upscaled 2x still looks good to the eye. Contour stuff like typography, diagrams etc should be drawn not shown as pictures though.

Obviously if we’re talking about a game, then if you have beautiful graphics maybe even in 10x resolution, then you want to showcase that. I’m mostly talking about functional graphics like UI graphics, photos and thumbnails, often images that are lazy loaded from a server. It’s just no use doubling the dev cost since nobody pays for apps anyway. And all that. For games and fixed images in the app package it’s less extra work of course.