The best way to add border/gap/margin to existing spritesheet

I was remembering this thread about visual artifacts on iPhone 6/6+ and iOS compatibility mode so I thought it might be worth to try to add the launchImages and see if it solves…

https://forums.coronalabs.com/topic/61654-tableview-line-width/?hl=%2Bdefault+%2B6%2B#entry331493

Are you sprite sheets JPG or PNG?  I’d go with PNG.  

Is your ‘tween’ color black or transparent?  I’d make it transparent.

Just some ideas, but I’m mostly shooting in the dark.

I do agree, you need to create a minimal test case that reproduces the issue, then when you resolve that, try it in your game.  Solving in situ is a strong temptation, but often a bad idea.

One little known option is this:

display.setDefault( “isImageSheetSampledInsideFrame”, true )

 

This offsets UVs by half a pixel within the sprite sheets.

I think this is going to the be something rather difficult to reproduce, but I am seeing something interesting:

  • when I change the layer opacity of my floor tiles, which affects the display group’s alpha  value for that layer, the black edge issues goes away on iOS, seriously, nothing else is touched.

So I am going to reproduce the issue in that direction…

But I still need to figure out the offset error on my roof layer, where the problem is not black edge, but off-by-1px sprite selection from image sheet, it’s also an interesting case: because it’s loaded the same way as every other layers, and only some of tiles are affected.

Basically, fun time with Corona :wink:

PS: I should change the post title at this point, but not sure how to do this…

I can see how that might affect my layer rendering, even though I think compatibility mode is explicitly UI related. But I will add that back in just in case. Is there any way to add launch images to a directory/sub-folder, or do we have to have every image at the root?

EDIT: added but it didn’t resolve my off-by-1px issues unfortunately.

I have found a semi-ultimate answer to this: just create a new tileset, a new layer, move the exact same tiles onto them, and do your layer again.

And BAM, no more off-1px or sub-pixel rendering problem on iOS.

Does it imply a bug somewhere in the Corona SDK? Yes.

Can I find an easy reproduction test for it? No, I tried, it only happens when you have a large number of tiles and layers.

So guys, if you are absolutely stuck, and tried all my previous solutions: try re-do your layer and tileset.

I did find a problem in the past with very large tile source images, due (I assume) to some kind of rounding error from integer pixels to float UV coordinates - if I recall it occurred when tile set images were larger than 1024x1024 pixels (causing tiles further into an image than 1024 pixels in any axis to be slightly offset). Does this correlate at all to the images you are using? Also, this was a long time ago, so maybe it isn’t an issue any more.

I thought I posted some other possibilities in the other thread, but apparently they never survived the dodgy internet from my phone while on the bus, so I’ll try to remember what I suggested elsewhere.

At least 2 affected tilesets are 256 x 1280 and 256 x 1728, so it’s possible.

I would like to know why this isn’t mentioned on documentation… Is it iOS only?

EDIT: Case 46684 is opened for this issue, if they couldn’t fix it, I am hoping at least we can ask them to update a warning in the newImageSheet documentation?

@rakoonic, slight tangent on precision (i.e. probably not relevant to bitinn’s issue), but for larger images, the UV’s default to mediump (https://docs.coronalabs.com/guide/graphics/customEffects.html#precision-qualifier-macros) which is device-dependent.

You can override them to higher precision as described here: https://docs.coronalabs.com/guide/basics/configSettings/index.html#shaderPrecision

In addition, if memory serves, you can also set different precision for each macro separately rather than setting them globally for all macros as currently documented.

This gives you more flexibility to selectively choose higher precision which generally increases the GPU cost in the shader (so only increase if you really have to):

application = {

content = {

shaderPrecision = { P_UV = “highp”, P_COLOR = “highp”, },

},

}

While I have not had to do this, you might consider using a spritesheet unpacker followed by re-packing with a tool like texture packer.  This will involve a bit of labor, but should get the job done.

@all - If anyone out there has other suggestions, please bring them as this is an interesting (and more common than one would think) problem.  i.e. The need to extract, optionally tweak, and re-pack sprite assets.

@bitinn,

are you a windows or os x user?  I ask because people may want to suggest tools, but knowing that you can use them would be helpful.  For example, there is a Windows tool called ‘Alferd Sprite Unpacker’ you may find useful, but if you’re and OS X user…

@roaminggamer I am on OS X unfortunately.

My tilesets are simple enough (rectangle layout, all 32px square tiles, no border or margin), most online spritesheet splitter SHOULD work, but DOESN’T due to size of my tileset (contains up to 200 tiles per tileset)

I would also prefer to repack them in the order them were packed, because it would save me a lot of time on fixing my Tiled map…

Anyway I am writing my node.js command line tool at this point, most texture packers I could find are primarily concerned with “memory usage reduction”, while for me, the primary concern is “maintaining the order of tiles and adding simple margins”.

Huh, I re-did my tileset with border, yet the same issue persists…

I am not able to reproduce it on simulator, only on iOS…

Does anyone has any idea on why else would it has a 1px black gap between tiles, only on iOS device?

To add:

  • I am using adaptive content scaling in my config.lua

  • The issue can be observed (though happens on different tiles) if I try to view as iPhone 6+ in Corona SDK simulator

  • But not with iPhone 6 simulator

  • But with my iPhone 6 (actual device), it does render with black gaps (or double pixel at the tile edge).

 I wonder if you’re seeing a gap between the tiles.  i.e. Sub-pixel alignment issues.

Is it possible for you to draw colored rectangles instead and then to see if the gaps exist still?

Is this a game or a business/other app?  

It is pretty rare to use adaptive scaling for games.  Can I suggest you try this just for an experiment:

http://spiralcodestudio.com/corona-sdk-pro-tip-of-the-day-36/

If you try it, remember to set this line to match your ‘target resolution’ (#1 device resolution you want to target)

contentW, contentH = 320, 480

> Is it possible for you to draw colored rectangles instead and then to see if the gaps exist still?

Did that, can’t see the black gap on actual device anymore.

> Is this a game or a business/other app?

A game

> It is pretty rare to use adaptive scaling for games

I didn’t know that, I use it mostly to make the unit (virtual pixel) easier to reason about, and it takes the pain out of supporting different resolution, since I already make my UI sort of responsive (as in responsive web design).

> If you try it, remember to set this line to match your ‘target resolution’

Did this too, seems promising on simulator, but 1px black gap still show up in actual device.

The problem looks like this on device (not all tiles are affected…)

BCKkw3V.png

Do the gaps always show or only when the level is moved.  That is, do they pop in and out as the ‘camera’ moves.

PS - re: Adaptive.  Don’t let me stop you. Do what works for you as that is the only valid choice at the end of the day.

> That is, do they pop in and out as the ‘camera’ moves.

Yes they do pop in and out as I pan.

My code looks something like this:

function Map:touch (event) if event.phase == 'began' then -- enter panning state self.camera\_panning = true self.ox = self.view.x self.oy = self.view.y elseif event.phase == 'moved' and self.camera\_panning == true then -- distance of movement from initial position self.view.x = math.floor(self.ox + event.x - event.xStart) self.view.y = math.floor(self.oy + event.y - event.yStart) elseif event.phase == 'ended' or event.phase == 'cancalled' then -- exit panning state self.camera\_panning = false end end

As you can see I tried to normalize the movement into full pixel…