Skip to content

Conversation

@madsmtm
Copy link
Member

@madsmtm madsmtm commented Jan 23, 2026

Add:

impl<D, W> Surface<D, W> {
    pub fn alpha_mode(&self) -> AlphaMode { ... }
    pub fn supports_alpha_mode(&self, alpha_mode: AlphaMode) -> bool { ... }

    // `resize` now calls `configure`:
    pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
        self.configure(width, height, self.alpha_mode())
    }

    pub fn configure(
        &mut self,
        width: NonZeroU32,
        height: NonZeroU32,
        alpha_mode: AlphaMode,
    ) -> Result<(), SoftBufferError> { ... }
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub enum AlphaMode {
    #[default]
    Opaque,
    Ignored,
    Premultiplied,
    Postmultiplied,
}

This fixes #17 and prepares for #98 / #317.

As noted in the transparency issue, it is important to make a distinction between straight and premultiplied alpha. The former can be easier to work with, but the latter is often what's actually supported by compositors.

One mode that is a bit odd here is Opaque, but it's necessary for the Web backend, because that platform doesn't support zero-copy RGBX, the alpha channel is always read (at least from what I could figure out). Adding this mode (and thus requiring that alpha channel to be 255) fixes #207.

The implementation of these modes for each platform is as follows (I have tested all these):

  • Android: Transparency doesn't seem to be supported by Winit, so I haven't enabled transparency in Softbuffer yet either (since I couldn't test it).
  • CoreGraphics: Opaque, Ignored, Premultiplied and Postmultiplied enabled. Premultiplied clamps the additive variants, but this might change with the IOSurface impl.
  • Wayland: Opaque, Ignored, Premultiplied.
  • Web: Opaque and Postmultiplied. Premultiplied can be supported in the future with ImageBitmap IIUC.
  • Win32: I think it could support Premultiplied, but I couldn't get it to work properly, so only did part of it, I'm not too familiar with Windows. I suspect it might also need something like fixed window transparency winit#2503.
  • X11: Transparency not yet implemented, but pretty sure that Premultiplied could be supported.

We could fairly easily implement a conversion step to support postmultiplied alpha when premultiplied is supported by the backend, but I'd like to migrate towards a more efficient design where each backend always do zero-copying, so I haven't done that.

Expected behaviour

I've created an example transparency.rs, which renders a few different shades of orange and yellow. The expected result are as follows:

Opaque / Ignored

opaque

Premultiplied

premultiplied

Postmultiplied

postmultiplied

@madsmtm madsmtm added this to the Softbuffer v0.5 milestone Jan 23, 2026
@madsmtm madsmtm added enhancement New feature or request DS - CoreGraphics macOS/iOS/tvOS/watchOS/visionOS backend DS - Wayland DS - Web WebAssembly / WASM backend labels Jan 23, 2026
Comment on lines +547 to +548
/// - macOS/iOS: Supported, but currently doesn't work with additive values (maybe only as the
/// root layer?). Make sure that components are `<= alpha` if you want to be cross-platform.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this currently:

macos-premultiplied

I suspect that this may be an intentional limitation of transparency in the "root" / that goes through the top-level layer, perhaps as a form of security measure to avoid users reading contents of contents below the window in a shader or smth? But I'll need to investigate this further, might be possible to fix with IOSurface.

Comment on lines +552 to +564
/// The non-alpha channels are not expected to already be multiplied by the alpha channel;
/// instead, the compositor will multiply the non-alpha channels by the alpha channel during
/// compositing.
///
/// Also known as "straight alpha".
///
/// ## Platform Dependent Behavior
///
/// - Web and macOS/iOS: Supported.
/// - Android, KMS/DRM, Orbital, Windows, X11: Not yet supported.
#[doc(alias = "Straight")]
#[doc(alias = "Unassociated")]
Postmultiplied,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostMultiplied seems a bit clearer, and if in doubt I'm inclined to make softbuffer APIs match wgpu.

/// root layer?). Make sure that components are `<= alpha` if you want to be cross-platform.
/// - Android, Orbital, Web, Windows and X11: Not yet supported.
#[doc(alias = "Associated")]
Premultiplied,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I elected to call this Premultiplied instead of PreMultiplied (not camel casing), because most of the references I could find on the interwebs seemed to prefer to write it in one word instead of hyphenating as "pre-multiplied".

@madsmtm madsmtm force-pushed the madsmtm/alpha-mode branch from 4dd27ed to 304f431 Compare January 24, 2026 00:11
This was linked to issues Jan 24, 2026
Comment on lines +156 to +160
// TODO: Once we get pixel formats, replace this with something like:
// fn supported_pixel_formats(&self, alpha_mode: AlphaMode) -> &[PixelFormat];
//
// And return an empty list from that if the alpha mode isn't supported.
self.surface_impl.supports_alpha_mode(alpha_mode)
Copy link
Member Author

@madsmtm madsmtm Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least I think this makes sense? A Wayland compositor is allowed to say that it only supports the required Argb8888 and Xrgb8888, and then also Abgr8888, while not supporting the corresponding Xbgr8888, right?

In that case, we'd have:

assert_eq!(surface.supports_alpha_mode(AlphaMode::Opaque), [PixelFormat::Bgra8, PixelFormat::Rgba8]); // Opaque `Rgba8` can use either `Abgr8888` or `Xbgr8888`.
assert_eq!(surface.supports_alpha_mode(AlphaMode::Ignored), [PixelFormat::Bgra8]); // Needs `Xbgr8888`.
assert_eq!(surface.supports_alpha_mode(AlphaMode::Premultiplied), [PixelFormat::Bgra8, PixelFormat::Rgba8]);
assert_eq!(surface.supports_alpha_mode(AlphaMode::Postmultiplied), []);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. We'd probably expect a compositor that supports an A format to also support the corresponding X format, and vice versa, but there is no requirement for this in the core Wayland protocol, other than for the two required formats.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

DS - CoreGraphics macOS/iOS/tvOS/watchOS/visionOS backend DS - Wayland DS - Web WebAssembly / WASM backend enhancement New feature or request

Development

Successfully merging this pull request may close these issues.

Drawing on the web is very slow Transparency

3 participants