treeform / boxy

2D GPU rendering with a tiling atlas.
MIT License
102 stars 7 forks source link

Draw UV rect rounding issue #64

Open elcritch opened 10 months ago

elcritch commented 10 months ago

I ported some GUI code to Boxy and see some artifacts when aligning rectangles and images next to each other. In particular it's quite noticeable with 9-patch boxes.

Screenshot 2023-11-15 at 10 53 50 PM Screenshot 2023-11-15 at 10 58 11 PM

It looks to be similar to Fidget, which I previously tracked down to floating point rounding after doing the uv position transformations in proc drawUvRect.

I ported the fix I did in Fidget and it appears to resolve the issue in Boxy as well:

proc drawUvRect(boxy: Boxy, at, to, uvAt, uvTo: Vec2, tint: Color) =
  ## Adds an image rect with a path to an ctx
  ## at, to, uvAt, uvTo are all in pixels
  let
    posQuad = [
      ceil(boxy.mat * vec2(at.x, to.y)),
      ceil(boxy.mat * vec2(to.x, to.y)),
      ceil(boxy.mat * vec2(to.x, at.y)),
      ceil(boxy.mat * vec2(at.x, at.y)),
    ]
    uvAt = uvAt / boxy.atlasSize.float32
    uvTo = uvTo / boxy.atlasSize.float32
    uvQuad = [
      vec2(uvAt.x, uvTo.y),
      vec2(uvTo.x, uvTo.y),
      vec2(uvTo.x, uvAt.y),
      vec2(uvAt.x, uvAt.y),
    ]
    tints = [tint, tint, tint, tint]

  boxy.drawQuad(posQuad, uvQuad, tints)
treeform commented 10 months ago

The problem comes if you don't want to draw pixel aligned rect. Boxy is not just for UI stuff. If your mat is pixel aligned this should not be an issue? Maybe a better fix, if you know you are doing UI, is to make sure that boxy.mat is aligned to pixels or half pixels? Thoughts?

elcritch commented 10 months ago

The problem comes if you don't want to draw pixel aligned rect. Boxy is not just for UI stuff.

Ah, that makes sense. Hmmm, that's tricky.

If your mat is pixel aligned this should not be an issue? Maybe a better fix, if you know you are doing UI, is to make sure that boxy.mat is aligned to pixels or half pixels? Thoughts?

That might work. When I tried previously I could only try to ensure the at and to were truncated or rounded. I'm not sure how one would align the boxy.mat.

The issue always seemed to come down to ensuring that two rect boundaries wouldn't overlap or leave a gap. The ceil(boxy.mat * vec2(at.x, to.y)) was the only way I figured how to get that to work with rounding errors and all.

Perhaps a compile time flag could work if nothing else?

I'm also not sure how to port the Fidget clipping model to the Boxy layering method.