arcade icon indicating copy to clipboard operation
arcade copied to clipboard

GUI: Surface alpha issues

Open einarf opened this issue 3 years ago • 7 comments

We render content to one or more Surface framebuffers for lazy updates of widgets. This is done with alpha blending enabled (default blend mode). When we then also alpha blend this surface to the screen using default blend mode, pixels with alpha lower than 1.0 will appear darker than in the surface.

einarf avatar Mar 26 '22 15:03 einarf

import arcade
from arcade import gui
class MyWindow(arcade.Window):
    def setup_ui(self):
        self.gui_manager = gui.UIManager(auto_enable=True)
        self.text_area = gui.UITextArea(
            x=20,
            width=self.window.width - 50,
            height=self.window.height,
            text_color=color.ASH_GREY,
            font_size=15,
            scroll_speed=2,
            text="Hi There!",
        )
        self.next_button = gui.UIFlatButton(
            x=375,
            y=10,
            width=100,
            height=30,
            text="Start",
        )
        self.gui_manager.add(self.text_area)
        self.gui_manager.add(self.next_button)

    def on_draw(self):
        self.clear()
        self.gui_manager.draw()

MyWindow().run()

Minimum Reproducible Example

Hunter2809 avatar Mar 26 '22 15:03 Hunter2809

Text subject to the alpha bug looks like this:

bilde

Properly rendered text looks like this:

bilde

einarf avatar Mar 26 '22 15:03 einarf

This might also apply to atlas.render_into

einarf avatar Mar 31 '22 21:03 einarf

The solutuon is the following:

When drawing into the framebuffer we need to set a separate blend function for the alpha component.

ctx.blend_func = (
    SRC_ALPHA, ONE_MINUS_SRC_ALPHA,  # RGB blend func (default)
    ONE, ONE_MINUS_SRC_ALPHA         # Alpha blend func
)

This should cause the framebuffer texture to have the correct alpha values for blending the texture to the screen when using the following blend function (Surface):

ctx.blend_func = ONE, ONE_MINUS_SRC_ALPHA

This means the following in practice:

  • The first blend function needs to be set when when drawing the widgets
  • The second blend function is set when we render the Surface to the screen
  • Change Context.blend_func to accept 2 and 4 enums. The last one would call glBlendFuncSeparate

einarf avatar Apr 01 '22 04:04 einarf

There are a few more things to take into consideration here. We need to take control over the global blend state in arcade. UIManager.draw() should be able to set the global blend state when drawing all the widgets. If the users is drawing sprites in their widget they will get the wrong blend mode. atlas.render_into will also have this problem.

  • SpriteList enables blending and enforces BLEND_DEFAULT unless something else is specified. This doesn't work well.
  • TextureAtlas disables the global blending when the atlas is resizing
  • A few examples are showing bad practices when it comes to setting blend mode

einarf avatar Apr 02 '22 01:04 einarf

#1158 is part of this issue and probably needs to be resolved first

einarf avatar Apr 03 '22 00:04 einarf

Remaining work: Check all locations were render to texture is used

  • Render into atlas
  • Background texture
  • TextureRenderTarget

einarf avatar Apr 22 '22 20:04 einarf

UIManager now have a properties to configure this + a more sane default.

einarf avatar Feb 28 '23 15:02 einarf