GUI blend layering artifacts
We seem to get some blending artifacts when drawing the framebuffer to the screen. Especially when doing text rendering.
Text rendered on top of an opaque brown surface. It should not cause the blended pixels to make transparent holes in the surface making the window background visible through the surface once rendered to the screen.
This is the raw pixel dump from the surface itself: shown in gimp to identify the transparency
Possibly we move this to 3.0.x if there is no time.
The only reasonable way to solve this is actually doing the math. I have a blending simulator we can use for this.
- Simulate blending opaque, semi-transparent and fully transparent objects to the surface (no top of each other)
- Simulate drawing this to the window with blending
Is this somehow going into 3.0?
I started doing some math on the blending, but we'll try to attack this properly as soon as possible in 3.0.x
Just created some example code to reproduce:
import arcade
from arcade import LBWH, open_window
from arcade.gui import (
Surface,
UIAnchorLayout,
UILabel,
UIView,
)
class CustomWidget(UILabel):
def __init__(self, *, text: str, **kwargs):
super().__init__(text=text, font_size=200, **kwargs)
def do_render_base(self, surface: Surface):
"""prevent the widget from clearing the surface with background color"""
surface.limit(self.rect)
arcade.draw_rect_filled(LBWH(0, 0, *self.rect.size), color=self._bg_color)
class MyView(UIView):
def __init__(self):
super().__init__()
self.background_color = arcade.color.BLUE
root = self.ui.add(UIAnchorLayout())
root.with_padding(all=30)
nested = root.add(UIAnchorLayout())
nested.with_padding(all=30)
nested.with_background(color=arcade.color.RED)
custom = nested.add(CustomWidget(text="O"))
custom.with_background(color=arcade.color.WHITE.replace(a=100))
def on_mouse_scroll(self, x: int, y: int, scroll_x: int, scroll_y: int):
self.ui.camera.zoom += scroll_y / 10
self.ui.camera.zoom = min(max(self.ui.camera.zoom, 0.1), 10)
if __name__ == "__main__":
open_window(window_title="Minimal example", width=800, height=800, resizable=True).show_view(
MyView()
)
arcade.run()
From my point of view, it looks like drawing the text just uses another blending.
https://pyglet.readthedocs.io/en/latest/programming_guide/text.html#blend-state
But maybe I am also completely wrong 😅
Changing blend values fixes the issue:
https://github.com/pyglet/pyglet/blob/4da7e7457051870cfd4c539dfa7c0352db9aaa1c/pyglet/text/layout/base.py#L771
glBlendFuncSeparate(
GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
GL_ONE, GL_ONE,
)
Fixed