Content Scaling on iPhone 7+

Hi,

I borrowed an iPhone 7+ for testing purposes, and was surprised to see the “scale factor” was set at 1.  Low res images look quite bad on the device.

I checked display.pixelWidth / display.actualContentWidth (is this the correct formula for scale factor) and can see it 1.85125, so it should be “@2x” as I have the threshhold set at 1.1?.  

I am using Sergy’s Pixel Perfect config.lua (although I made the erroneous decision to set the screen at 800 x 1200, too late in the project to change this now) which sets the contentWidth and contentHeight at 1480 x 800 respectively.

I think I am missing something here,  can anyone shed some light on this for me now?  Is there anyway to tweak my setting to get the correct images loading without having to redo everything (been working on this game for over a year and about 2 weeks from publishing)?  Currently, I am being rather old school and including an ‘if statement’ in my config.lua to compensate (not shown because I am hoping it is not needed…)

local w, h = display.pixelWidth, display.pixelHeight local modes = {1, 1.5, 2, 3, 4, 6, 8} -- Scaling factors to try local contentW, contentH = 800, 1200 -- Minimal size your content can fit in -- Try each mode and find the best one local \_w, \_h, \_m = w, h, 1 for i = 1, #modes do local m = modes[i] local lw, lh = w / m, h / m if lw \< contentW or lh \< contentH then break else \_w, \_h, \_m = lw, lh, m end end -- If scaling is not pixel perfect (between 1 and 2) - use letterbox if \_m \< 2 then local scale = math.max(contentW / w, contentH / h) \_w, \_h = w \* scale, h \* scale end application = { license = { google = { key = "" }, }, showRuntimeErrors = true, content = { width = \_w, height = \_h, scale = 'letterbox', fps = 60, imageSuffix = { ['@2x'] = 1.1, } } }

Thanks,

Craig

Craig,

  1. This is not really an answer.  I’m merely giving you a way to debug what is going on.

  2. If you have SSK2 PRO, don’t forget you can use EAT Free to generate a variety of config files, including a derivative of Sergey’s file.

  3. I personally use a fixed resolution config file, so I’m less than familiar with the above code.

Having said all that, if you have SSK (lite or PRO) you can run this code and both tweak the algorithm as well as test what it will do on different devices and with different settings (just load SSK, then put this in main.lua and play around with values and simulated devices):

local function testSmart( pixelWidth, pixelHeight, contentW, contentH ) --local w, h = display.pixelWidth, display.pixelHeight local w, h = pixelWidth, pixelHeight local modes = {1, 1.5, 2, 3, 4, 6, 8} -- Scaling factors to try --local contentW, contentH = 800, 1200 -- Minimal size your content can fit in -- Try each mode and find the best one local \_w, \_h, \_m = w, h, 1 for i = 1, #modes do local m = modes[i] local lw, lh = w / m, h / m if lw \< contentW or lh \< contentH then break else \_w, \_h, \_m = lw, lh, m end end -- If scaling is not pixel perfect (between 1 and 2) - use letterbox if \_m \< 2 then local scale = math.max(contentW / w, contentH / h) \_w, \_h = w \* scale, h \* scale end local application = { content = { width = \_w, height = \_h, scale = 'letterbox', fps = 60, imageSuffix = { ['@2x'] = 1.1, } } } print( "testSmart() ", pixelWidth, pixelHeight, contentW, contentH ) table.print\_r( application ) end

testSmart( display.pixelWidth, display.pixelHeight, 800, 1200 )

Thanks Ed,  

Ill have a play around.  Is this the correct formula for scale factor “display.pixelWidth / display.actualContentWidth”.

Cheers,

Craig

Craig,

I’m afraid I don’t know.  I simply lifted the code you posted and put it into a function that let you tweak values and print_r  the results.

Here is another piece of code to help you debug what the algorithm will produce:

local function testSmart2( contentW, contentH, target ) local w, h = target.pixelWidth, target.pixelHeight local modes = {1, 1.5, 2, 3, 4, 6, 8} -- Scaling factors to try -- From Sergey's code local \_w, \_h, \_m = w, h, 1 for i = 1, #modes do local m = modes[i] local lw, lh = w / m, h / m if lw \< contentW or lh \< contentH then break else \_w, \_h, \_m = lw, lh, m end end -- If scaling is not pixel perfect (between 1 and 2) - use letterbox if \_m \< 2 then local scale = math.max(contentW / w, contentH / h) \_w, \_h = w \* scale, h \* scale end print( "testSmart2() " .. target.name .. " produces config.width/height == " .. \_w .. "/" .. \_h .. " from content w/h " .. contentW .. "/" .. contentH ) end local targets = {} targets[1] = { name = "iPad", pixelWidth = 768, pixelHeight = 1024 } targets[2] = { name = "iPad Air", pixelWidth = 768\*2, pixelHeight = 1024\*2 } targets[3] = { name = "iPad Pro", pixelWidth = 2048, pixelHeight = 2732 } targets[4] = { name = "iPhone 4", pixelWidth = 640, pixelHeight = 960 } targets[5] = { name = "iPhone 5", pixelWidth = 640, pixelHeight = 1136 } for i = 1, #targets do testSmart2( 800, 1200, targets[i] ) end

The second piece of code, let’s you use Sergey’s algorithm and test it in a loop against any device dimensions you want, then prints out the results:

testSmart2() iPad produces config.width/height == 900/1200 from content w/h 800/1200 testSmart2() iPad Air produces config.width/height == 900/1200 from content w/h 800/1200 testSmart2() iPad Pro produces config.width/height == 1024/1366 from content w/h 800/1200 testSmart2() iPhone 4 produces config.width/height == 800/1200 from content w/h 800/1200 testSmart2() iPhone 5 produces config.width/height == 800/1420 from content w/h 800/1200

I don’t want to step on Sergey’s toes here, but as I understand this method of calculating config w/h settings, the PRIMARY concern is that scaling be pixel perfect and NOT cause scaling artifacts.  The SECONDARY concern is that the right texture be used.

i.e. I think this may cause you to use a 1X image when it is better to select the @2x image.  However this could be just be me, missing some detail in Sergey’s algorithm.

Again, I use a fixed config.lua file because I am not a fan of ambiguity with regards to such factors. 

Cool,  thanks for your input Ed.

I went back to Sergy’s original acticle and put his code in my project (code below).  It looks like it is a previous issue coming to the surface again.  If I run the EXACT same code without live build then it loads @2x, with live builds I get nothing.  The scale factor is actually different in both cases as well.  Ill put together a simple test case and submit a bug.  I have attached images that show this (excuse the blatant plug for soon to be release game).

Cheers,

Craig

local \_W, \_H = display.contentWidth, display.contentHeight local \_CX, \_CY = \_W \* 0.5, \_H \* 0.5 -- x = 0, y = 0 - top left corner -- x = \_W, y = \_H - bottom right corner display.setStatusBar(display.HiddenStatusBar) display.setDefault('background', 0.5, 0, 0) local s = 70 local frame = display.newRect(\_CX, \_CY, \_W - 2, \_H - 2) frame:setFillColor(0) frame.strokeWidth = 1 local topLeftRect = display.newRect(0, 0, s, s) topLeftRect.anchorX, topLeftRect.anchorY = 0, 0 local bottomRightRect = display.newRect(\_W, \_H, s, s) bottomRightRect.anchorX, bottomRightRect.anchorY = 1, 1 local contentSizeText = display.newText{ x = \_CX, y = \_CY - 100, fontSize = 14, text = 'Content: ' .. display.actualContentWidth .. ' x ' .. display.actualContentHeight } local suffix = display.imageSuffix or 'none' local suffixText = display.newText{ x = \_CX, y = \_CY - 50, fontSize = 14, text = 'Suffix: ' .. suffix } local scale = display.pixelHeight / display.actualContentHeight local scaleText = display.newText{ x = \_CX, y = \_CY, fontSize = 14, text = 'Scale: ' .. scale } local pixelPerfect = 'no' if scale == 3 then pixelPerfect = 'kinda' elseif scale == 1 or scale % 2 == 0 then pixelPerfect = 'yes' end local pixelText = display.newText{ x = \_CX, y = \_CY + 50, fontSize = 14, text = 'Pixel perfect: ' .. pixelPerfect } print('Content', display.actualContentWidth .. ' x ' .. display.actualContentHeight) print('Suffix', suffix) print('Scale', scale) print('Pixel perfect', pixelPerfect)

Craig,

  1. This is not really an answer.  I’m merely giving you a way to debug what is going on.

  2. If you have SSK2 PRO, don’t forget you can use EAT Free to generate a variety of config files, including a derivative of Sergey’s file.

  3. I personally use a fixed resolution config file, so I’m less than familiar with the above code.

Having said all that, if you have SSK (lite or PRO) you can run this code and both tweak the algorithm as well as test what it will do on different devices and with different settings (just load SSK, then put this in main.lua and play around with values and simulated devices):

local function testSmart( pixelWidth, pixelHeight, contentW, contentH ) --local w, h = display.pixelWidth, display.pixelHeight local w, h = pixelWidth, pixelHeight local modes = {1, 1.5, 2, 3, 4, 6, 8} -- Scaling factors to try --local contentW, contentH = 800, 1200 -- Minimal size your content can fit in -- Try each mode and find the best one local \_w, \_h, \_m = w, h, 1 for i = 1, #modes do local m = modes[i] local lw, lh = w / m, h / m if lw \< contentW or lh \< contentH then break else \_w, \_h, \_m = lw, lh, m end end -- If scaling is not pixel perfect (between 1 and 2) - use letterbox if \_m \< 2 then local scale = math.max(contentW / w, contentH / h) \_w, \_h = w \* scale, h \* scale end local application = { content = { width = \_w, height = \_h, scale = 'letterbox', fps = 60, imageSuffix = { ['@2x'] = 1.1, } } } print( "testSmart() ", pixelWidth, pixelHeight, contentW, contentH ) table.print\_r( application ) end

testSmart( display.pixelWidth, display.pixelHeight, 800, 1200 )

Thanks Ed,  

Ill have a play around.  Is this the correct formula for scale factor “display.pixelWidth / display.actualContentWidth”.

Cheers,

Craig

Craig,

I’m afraid I don’t know.  I simply lifted the code you posted and put it into a function that let you tweak values and print_r  the results.

Here is another piece of code to help you debug what the algorithm will produce:

local function testSmart2( contentW, contentH, target ) local w, h = target.pixelWidth, target.pixelHeight local modes = {1, 1.5, 2, 3, 4, 6, 8} -- Scaling factors to try -- From Sergey's code local \_w, \_h, \_m = w, h, 1 for i = 1, #modes do local m = modes[i] local lw, lh = w / m, h / m if lw \< contentW or lh \< contentH then break else \_w, \_h, \_m = lw, lh, m end end -- If scaling is not pixel perfect (between 1 and 2) - use letterbox if \_m \< 2 then local scale = math.max(contentW / w, contentH / h) \_w, \_h = w \* scale, h \* scale end print( "testSmart2() " .. target.name .. " produces config.width/height == " .. \_w .. "/" .. \_h .. " from content w/h " .. contentW .. "/" .. contentH ) end local targets = {} targets[1] = { name = "iPad", pixelWidth = 768, pixelHeight = 1024 } targets[2] = { name = "iPad Air", pixelWidth = 768\*2, pixelHeight = 1024\*2 } targets[3] = { name = "iPad Pro", pixelWidth = 2048, pixelHeight = 2732 } targets[4] = { name = "iPhone 4", pixelWidth = 640, pixelHeight = 960 } targets[5] = { name = "iPhone 5", pixelWidth = 640, pixelHeight = 1136 } for i = 1, #targets do testSmart2( 800, 1200, targets[i] ) end

The second piece of code, let’s you use Sergey’s algorithm and test it in a loop against any device dimensions you want, then prints out the results:

testSmart2() iPad produces config.width/height == 900/1200 from content w/h 800/1200 testSmart2() iPad Air produces config.width/height == 900/1200 from content w/h 800/1200 testSmart2() iPad Pro produces config.width/height == 1024/1366 from content w/h 800/1200 testSmart2() iPhone 4 produces config.width/height == 800/1200 from content w/h 800/1200 testSmart2() iPhone 5 produces config.width/height == 800/1420 from content w/h 800/1200

I don’t want to step on Sergey’s toes here, but as I understand this method of calculating config w/h settings, the PRIMARY concern is that scaling be pixel perfect and NOT cause scaling artifacts.  The SECONDARY concern is that the right texture be used.

i.e. I think this may cause you to use a 1X image when it is better to select the @2x image.  However this could be just be me, missing some detail in Sergey’s algorithm.

Again, I use a fixed config.lua file because I am not a fan of ambiguity with regards to such factors. 

Cool,  thanks for your input Ed.

I went back to Sergy’s original acticle and put his code in my project (code below).  It looks like it is a previous issue coming to the surface again.  If I run the EXACT same code without live build then it loads @2x, with live builds I get nothing.  The scale factor is actually different in both cases as well.  Ill put together a simple test case and submit a bug.  I have attached images that show this (excuse the blatant plug for soon to be release game).

Cheers,

Craig

local \_W, \_H = display.contentWidth, display.contentHeight local \_CX, \_CY = \_W \* 0.5, \_H \* 0.5 -- x = 0, y = 0 - top left corner -- x = \_W, y = \_H - bottom right corner display.setStatusBar(display.HiddenStatusBar) display.setDefault('background', 0.5, 0, 0) local s = 70 local frame = display.newRect(\_CX, \_CY, \_W - 2, \_H - 2) frame:setFillColor(0) frame.strokeWidth = 1 local topLeftRect = display.newRect(0, 0, s, s) topLeftRect.anchorX, topLeftRect.anchorY = 0, 0 local bottomRightRect = display.newRect(\_W, \_H, s, s) bottomRightRect.anchorX, bottomRightRect.anchorY = 1, 1 local contentSizeText = display.newText{ x = \_CX, y = \_CY - 100, fontSize = 14, text = 'Content: ' .. display.actualContentWidth .. ' x ' .. display.actualContentHeight } local suffix = display.imageSuffix or 'none' local suffixText = display.newText{ x = \_CX, y = \_CY - 50, fontSize = 14, text = 'Suffix: ' .. suffix } local scale = display.pixelHeight / display.actualContentHeight local scaleText = display.newText{ x = \_CX, y = \_CY, fontSize = 14, text = 'Scale: ' .. scale } local pixelPerfect = 'no' if scale == 3 then pixelPerfect = 'kinda' elseif scale == 1 or scale % 2 == 0 then pixelPerfect = 'yes' end local pixelText = display.newText{ x = \_CX, y = \_CY + 50, fontSize = 14, text = 'Pixel perfect: ' .. pixelPerfect } print('Content', display.actualContentWidth .. ' x ' .. display.actualContentHeight) print('Suffix', suffix) print('Scale', scale) print('Pixel perfect', pixelPerfect)