9Patch - Pixel distortion & UV coverage issues

Bugs will be moved here once resolved.

Post » Thu Dec 17, 2015 4:47 pm

Problem Description
I may have come across 2 graphical issues related to the 9Patch object. I hope it's okay to incorporate them both into a single bug report. They aren't quite related, but they can manifest simultaneously, and do in the example capx.

1. Pixel distortion:
Pixel rows and columns are rendered with varying thicknesses (plaid-like) rather than as a uniform pixel grid.
(see image below)

2: UV coverage issues:
If multiple instances of a 9Patch object have different margin settings, only instances with settings matching those of the instances at the backmost z-index will render correctly.
(see image below)


Attach a Capx
Here's an example capx.


Description of Capx
The capx has two layouts, one shows the effect in "Image 1", and the other layout shows the effect seen in "Image 2".
In the first layout there are also two extra 9Patch objects that should look okay even during runtime as they have their margins set so that the sliced "patches" of the source texture all have power-of-two dimensions.

Note: If the patch dimensions are powers-of-2, then the distortion will not occur in webGL, but any other dimensions cause the distortion. Possibly related to gl.REPEAT requiring power-of-2 textures. The power-of-two dimensions does not fix the other UV coverage issues though.


Steps to Reproduce Bug
  • 1. In the project settings, make sure webGL is enabled, and set sampling to "point ".
  • 2. Add a "start of layout" event to scale up the layout by 8x or so at runtime (as you might do for pixel/retro-looking games).
  • 3. Add a32x32 9Patch object with any detailed per-pixel pattern.
  • 4. In the 9Patch properties, set all margins to 4px.
  • 5. Set Fill and Edges to "Tile".
  • 6. Set Seems to "Exact".
  • 7. Resize the 9Patch so that corner, edge, and fill zones are all visible.
  • 8. Run the game. (Make sure the 9Patch is centered as you'll be zooming in 8x.)
Result: In the runtime window, you should see distortion artifacts in the 9Patch pixels, similar to those shown in "Image 1". Conversely, if you look at the same 9Patch in the editor, zoomed to 800% (use Ctrl + Shift + MouseWheel), it should look correct, with no distortion.

  • 9. Now, create another instance of the same 9Patch object but set all its margins to 8px. (You should now have an 8px-margin instance of the 9Patch object, in addition to the original 4px- margin instance.)
  • 10. Run the game.
Result: In the runtime window, you should see distortion, just as before, but you should also see that the new 8px-margin instance is tiling its edges and fill in what looks like a smeared and chopped up jumble.


Observed Result

1. Pixel distortion:
Pixel rows and columns are stretched and squashed, such that the pixels that make up the texture take on a plaid-like distribution, with some rows and columns being wider than others, rather than a uniform pixel grid.
R0J0hound tested and confirmed this, also noting that it only occurs when using the webGL renderer, but not the canvas2D renderer. (Though the "UV coverage issue" occurs regardless of renderer.)

Image 1 - Pixel distortion
Shown, several instances of a few test patterns, and a stone block from a game of mine, in which I first began investigating the occasional 9Patch runtime render issues. Both are zoomed to 8x scale. The Editor is at 800% zoom, and the "In game" runtime has the layout scaled by a factor of 8 via events.

(click to enlarge)
Image



2: UV coverage issues:
If multiple instances of a 9Patch object have different margin settings, each unique margin configuration will not get its own uniquely sliced image patch set. Instead all instances will use the image patch set created for the instance with the backmost z-index, and only instances with margin configurations matching that backmost instance will be rendered correctly.
R0J0hound checked the code in the plugin when I inquired about tracking down the UV math to see if that was the issue. He suggested that this issue is due to the sliced image patch set being stored in the object "type".
I think this may mean that only one sliced patch set is ever created, and all subsequent instances use that set regardless of margin settings.

Image 2 - UV coverage issues
Note: It's the Z-order at game start that matters. Once the game is running, rearranging the Z-order won't affect how this issue manifests. So in my example capx, if you want to see the effect of a different instance acting as the backmost boss-instance (not sure what to call it), you'll need to change the Z-order in the editor and relaunch the game.

(click to enlarge)
Image



Expected Result
1. Pixel distortion:
Expected pixels to be square, uniformly sized and rendered as a geometrically uniform grid. Much as you would expect to see if zooming in on pixel art in a graphics editor.

2: UV coverage issues:
Expected the per-instance customizability of the margin settings to result in corresponding UV coverage and tiling on a per-instance basis.


Affected Browsers
  • Chrome: YES
  • FireFox: YES
  • Internet Explorer: YES


Operating System and Service Pack
Win7 x64 - Pro
SP1


Construct 2 Version ID
Release 218 (64-bit) checked
Built at 14:06: 17 on Nov 25 2015
Release Notes
B
28
S
18
G
8
Posts: 333
Reputation: 6,193

Post » Thu Dec 17, 2015 5:28 pm

fisholith wrote:1. Pixel distortion:
Pixel rows and columns are rendered with varying thicknesses (plaid-like) rather than as a uniform pixel grid.
(see image below)

...Note: If the patch dimensions are powers-of-2, then the distortion will not occur in webGL, but any other dimensions cause the distortion. Possibly related to gl.REPEAT requiring power-of-2 textures. The power-of-two dimensions does not fix the other UV coverage issues though.

You hit the nail on the head with the power-of-2 issue. After cutting the image in to patches, if tile mode is enabled then each patch is put on a repeating texture. However WebGL 1 does not support non-power-of-two repeat textures, so the renderer stretches the image to a power-of-two size using the project's sampling mode (point/linear) when creating the texture. This stretching produces artefacts, and that's what you're seeing in the bug report. The good news is WebGL 2 has full support for non-power-of-two textures and so should fix this particular problem. WebGL 2 is in active development for all major browsers (except Edge, where its status is "development is likely for a future release"), so rather than come up with a fix for this case I think I'd rather wait for WebGL 2 support.

2: UV coverage issues:
If multiple instances of a 9Patch object have different margin settings, only instances with settings matching those of the instances at the backmost z-index will render correctly.
(see image below)

In Construct 2 the general model is that image data is stored in the ObjectType and instances all share the same set of images. This is to avoid wasting memory. For example if you created 100 9-patch objects and gave them all slightly different margins, it would have to load 100 sets of textures. I also assumed that for any given image, there would only be one correct set of margins, so why would it be necessary to support that feature? Right now it's by design... although I guess it's a problem it's possible to give different instances different settings when really they share images. So are there really some compelling cases where it's great to be able to set different margins on the same image?

BTW, kudos for writing an essentially perfect bug report, I wish everyone's reports were like this! :D Beautiful .capx and good writeup... if I gave awards for ideal bug reports, this is a winner ;)
Scirra Founder
B
403
S
238
G
89
Posts: 24,653
Reputation: 196,143

Post » Sat Dec 19, 2015 5:02 pm

Thanks for all the info Ashley. :)

Yeah, waiting on WebGL 2 sounds like a good call. It's cool that they're including non-power-of-two textures, as they are admittedly a less common sort of thing.


I was actually just talking with R0J0hound about the multiple margin configurations and vram issue.

My initial thought:
It might be best to store the split up images in the object type, but make a unique split-image-set for each unique margin configuration in the layout. That way if you have 100 9Patch instances, but only 2 different margin configurations among them, (e.g. 4,4,4,4 and 8,8,8,8) you'll only have to store a single "4,4,4,4" image set and a single "8,8,8,8" image set, and the 100 instances will just point to one of the two.


While it's certainly true that using 100 different margin configurations would require 100 different patch sets in vram, in most practical cases where I've been interested in different margin configs, it's only been 1 or 2 extra. Though, those are basically always cases where a 9-Patch object is narrower than the sum of its margins on the relevant axis.

e.g. If a 9-Patch has 8px top and 8px bottom margins, then you can't shrink the object's height below 16px without introducing margin patch overlap. For instance, if you set the object's height to 8px, then you would need to modify the margins to be 4px top and 4px bottom, to keep the meeting point of the margins centered.


Below is an image showing a block with 8px margins on all sides.
On the top in the reddish box all example sizes use 8px margins even as the object's width and height drop below 16px accross. On the bottom bluish box, the first 3 columns of example sizes use 8px, and the last 3 columns (narrower than 16px) use increasingly smaller custom margin settings to keep the meeting line centered.

This is partly why I was thinking that in practical cases there would often only be 1 or 2 extra sizes, because realistically even 3 special size gradations (as seen in the image) is more than usual. In the game I'm currently working on, I only have 1 variant of my "8,8,8,8" block, and that's a 4,4,4,4 block.

(click to enlarge)
Image



Another possible approach, (though I'm not sure how the graphics are placed on quads so I'm not sure if this is realistic), might be to have the quads automatically trim themselves as the object's size gets too narrow. and just let the UV's hang off the edge of the trimmed quad to mask them out.

Because the case shown in the image is the only case I really know of where special margin configs are regularly useful, the automatic quad cropping would produce the exact same result without requiring anything extra to be stored in vram.

Anyway, thanks again for the info. :)
B
28
S
18
G
8
Posts: 333
Reputation: 6,193


Return to Closed bugs

Who is online

Users browsing this forum: No registered users and 2 guests