GUI: Surface alpha issues
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.
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
Text subject to the alpha bug looks like this:

Properly rendered text looks like this:

This might also apply to atlas.render_into
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_functo accept 2 and 4 enums. The last one would callglBlendFuncSeparate
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_DEFAULTunless 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
#1158 is part of this issue and probably needs to be resolved first
Remaining work: Check all locations were render to texture is used
- Render into atlas
- Background texture
- TextureRenderTarget
UIManager now have a properties to configure this + a more sane default.