Skip to content

Commit 36c222a

Browse files
committed
TMP alpha mode
1 parent 688a216 commit 36c222a

File tree

5 files changed

+127
-21
lines changed

5 files changed

+127
-21
lines changed

src/backend_interface.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Interface implemented by backends
22
3-
use crate::{InitError, Rect, SoftBufferError};
3+
use crate::{AlphaMode, InitError, Rect, SoftBufferError};
44

55
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
66
use std::num::NonZeroU32;
@@ -22,12 +22,23 @@ pub(crate) trait SurfaceInterface<D: HasDisplayHandle + ?Sized, W: HasWindowHand
2222
where
2323
W: Sized,
2424
Self: Sized;
25+
2526
/// Get the inner window handle.
2627
fn window(&self) -> &W;
27-
/// Resize the internal buffer to the given width and height.
28-
fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError>;
28+
29+
fn alpha_mode(&self) -> AlphaMode;
30+
31+
/// Reconfigure the internal buffer(s).
32+
fn configure(
33+
&mut self,
34+
width: NonZeroU32,
35+
height: NonZeroU32,
36+
alpha_mode: AlphaMode,
37+
) -> Result<(), SoftBufferError>;
38+
2939
/// Get a mutable reference to the buffer.
3040
fn buffer_mut(&mut self) -> Result<Self::Buffer<'_>, SoftBufferError>;
41+
3142
/// Fetch the buffer from the window.
3243
fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
3344
Err(SoftBufferError::Unimplemented)

src/backends/cg.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceInterface<D, W> for CGImpl<
252252
&self.window_handle
253253
}
254254

255-
fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
255+
fn configure(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
256256
self.width = width.get() as usize;
257257
self.height = height.get() as usize;
258258
Ok(())

src/backends/wayland/buffer.rs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,13 @@ pub(super) struct WaylandBuffer {
8282
}
8383

8484
impl WaylandBuffer {
85-
pub fn new(shm: &wl_shm::WlShm, width: i32, height: i32, qh: &QueueHandle<State>) -> Self {
85+
pub fn new(
86+
shm: &wl_shm::WlShm,
87+
width: i32,
88+
height: i32,
89+
format: wl_shm::Format,
90+
qh: &QueueHandle<State>,
91+
) -> Self {
8692
// Calculate size to use for shm pool
8793
let pool_size = get_pool_size(width, height);
8894

@@ -94,15 +100,7 @@ impl WaylandBuffer {
94100
// Create wayland shm pool and buffer
95101
let pool = shm.create_pool(tempfile.as_fd(), pool_size, qh, ());
96102
let released = Arc::new(AtomicBool::new(true));
97-
let buffer = pool.create_buffer(
98-
0,
99-
width,
100-
height,
101-
width * 4,
102-
wl_shm::Format::Xrgb8888,
103-
qh,
104-
released.clone(),
105-
);
103+
let buffer = pool.create_buffer(0, width, height, width * 4, format, qh, released.clone());
106104

107105
Self {
108106
qh: qh.clone(),
@@ -118,7 +116,7 @@ impl WaylandBuffer {
118116
}
119117
}
120118

121-
pub fn resize(&mut self, width: i32, height: i32) {
119+
pub fn configure(&mut self, width: i32, height: i32, format: wl_shm::Format) {
122120
// If size is the same, there's nothing to do
123121
if self.width != width || self.height != height {
124122
// Destroy old buffer
@@ -139,7 +137,7 @@ impl WaylandBuffer {
139137
width,
140138
height,
141139
width * 4,
142-
wl_shm::Format::Xrgb8888,
140+
format,
143141
&self.qh,
144142
self.released.clone(),
145143
);

src/backends/wayland/mod.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{
22
backend_interface::*,
33
error::{InitError, SwResultExt},
4-
Rect, SoftBufferError,
4+
AlphaMode, Rect, SoftBufferError,
55
};
66
use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle};
77
use std::{
@@ -81,6 +81,7 @@ pub struct WaylandImpl<D: ?Sized, W: ?Sized> {
8181
surface: Option<wl_surface::WlSurface>,
8282
buffers: Option<(WaylandBuffer, WaylandBuffer)>,
8383
size: Option<(NonZeroI32, NonZeroI32)>,
84+
format: wl_shm::Format,
8485

8586
/// The pointer to the window object.
8687
///
@@ -128,7 +129,12 @@ impl<D: HasDisplayHandle + ?Sized, W: HasWindowHandle> SurfaceInterface<D, W>
128129
&self.window_handle
129130
}
130131

131-
fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
132+
fn configure(
133+
&mut self,
134+
width: NonZeroU32,
135+
height: NonZeroU32,
136+
alpha_mode: AlphaMode,
137+
) -> Result<AlphaMode, SoftBufferError> {
132138
self.size = Some(
133139
(|| {
134140
let width = NonZeroI32::try_from(width).ok()?;
@@ -137,7 +143,13 @@ impl<D: HasDisplayHandle + ?Sized, W: HasWindowHandle> SurfaceInterface<D, W>
137143
})()
138144
.ok_or(SoftBufferError::SizeOutOfRange { width, height })?,
139145
);
140-
Ok(())
146+
let (format, chosen_alpha_mode) = match alpha_mode {
147+
AlphaMode::Opaque => (wl_shm::Format::Xrgb8888, AlphaMode::Opaque),
148+
AlphaMode::PreMultiplied => (wl_shm::Format::Argb8888, AlphaMode::PreMultiplied),
149+
AlphaMode::PostMultiplied => (wl_shm::Format::Argb8888, AlphaMode::PostMultiplied),
150+
};
151+
self.format = format;
152+
Ok(chosen_alpha_mode)
141153
}
142154

143155
fn buffer_mut(&mut self) -> Result<BufferImpl<'_>, SoftBufferError> {
@@ -164,20 +176,22 @@ impl<D: HasDisplayHandle + ?Sized, W: HasWindowHandle> SurfaceInterface<D, W>
164176
}
165177

166178
// Resize, if buffer isn't large enough
167-
back.resize(width.get(), height.get());
179+
back.configure(width.get(), height.get(), self.format);
168180
} else {
169181
// Allocate front and back buffer
170182
self.buffers = Some((
171183
WaylandBuffer::new(
172184
&self.display.shm,
173185
width.get(),
174186
height.get(),
187+
self.format,
175188
&self.display.qh,
176189
),
177190
WaylandBuffer::new(
178191
&self.display.shm,
179192
width.get(),
180193
height.get(),
194+
self.format,
181195
&self.display.qh,
182196
),
183197
));

src/lib.rs

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ pub struct Rect {
7474
/// A surface for drawing to a window with software buffers.
7575
#[derive(Debug)]
7676
pub struct Surface<D, W> {
77+
alpha_mode: AlphaMode,
78+
/// A buffer that is used when `!supported_alpha_mode.contains(alpha_mode)`.
79+
fallback_buffer: Option<Vec<u8>>,
7780
/// This is boxed so that `Surface` is the same size on every platform.
7881
surface_impl: Box<SurfaceDispatch<D, W>>,
7982
_marker: PhantomData<Cell<()>>,
@@ -104,14 +107,28 @@ impl<D: HasDisplayHandle, W: HasWindowHandle> Surface<D, W> {
104107
self.surface_impl.window()
105108
}
106109

110+
/// The current alpha mode of the surface.
111+
pub fn alpha_mode(&self) -> AlphaMode {
112+
self.surface_impl.alpha_mode()
113+
}
114+
107115
/// Set the size of the buffer that will be returned by [`Surface::buffer_mut`].
108116
///
109117
/// If the size of the buffer does not match the size of the window, the buffer is drawn
110118
/// in the upper-left corner of the window. It is recommended in most production use cases
111119
/// to have the buffer fill the entire window. Use your windowing library to find the size
112120
/// of the window.
113121
pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
114-
self.surface_impl.resize(width, height)
122+
self.configure(width, height, self.alpha_mode())
123+
}
124+
125+
pub fn configure(
126+
&mut self,
127+
width: NonZeroU32,
128+
height: NonZeroU32,
129+
alpha_mode: AlphaMode,
130+
) -> Result<(), SoftBufferError> {
131+
self.surface_impl.configure(width, height, alpha_mode)
115132
}
116133

117134
/// Copies the window contents into a buffer.
@@ -424,6 +441,72 @@ impl Buffer<'_> {
424441
}
425442
}
426443

444+
/// Specifies how the alpha channel of the surface should be handled by the compositor.
445+
///
446+
/// Whether this has an effect is dependent on whether [the pixel format](crate::Format) has an
447+
/// alpha component.
448+
///
449+
/// See [the WhatWG spec][whatwg-premultiplied] for a good description of the difference between
450+
/// premultiplied and postmultiplied alpha.
451+
///
452+
/// [whatwg-premultiplied]: https://html.spec.whatwg.org/multipage/canvas.html#premultiplied-alpha-and-the-2d-rendering-context
453+
#[doc(alias = "Transparency")]
454+
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
455+
pub enum AlphaMode {
456+
/// The alpha channel is ignored.
457+
///
458+
/// # Platform-specific
459+
///
460+
/// This requires a copy on Web.
461+
#[default]
462+
Opaque,
463+
/// The non-alpha channels are expected to already have been multiplied by the alpha channel.
464+
///
465+
/// # Platform-specific
466+
///
467+
/// This requires a copy on Web.
468+
PreMultiplied,
469+
/// The non-alpha channels are not expected to already be multiplied by the alpha channel;
470+
/// instead, the compositor will multiply the non-alpha channels by the alpha channel during
471+
/// compositing.
472+
///
473+
/// Also known as "straight alpha".
474+
///
475+
/// # Platform-specific
476+
///
477+
/// This is unsupported on Wayland (TODO: And others?), so there Softbuffer creates a separate
478+
/// buffer that is used later on for the conversion.
479+
///
480+
/// Web: Works great.
481+
#[doc(alias = "Straight")]
482+
PostMultiplied,
483+
}
484+
485+
/// Convenience helpers.
486+
impl AlphaMode {
487+
/// Check if this is [`AlphaMode::Opaque`].
488+
pub fn is_opaque(self) -> bool {
489+
matches!(self, Self::Opaque)
490+
}
491+
492+
/// Check if this is [`AlphaMode::PreMultiplied`].
493+
pub fn is_pre_multiplied(self) -> bool {
494+
matches!(self, Self::PreMultiplied)
495+
}
496+
497+
/// Check if this is [`AlphaMode::PostMultiplied`].
498+
pub fn is_post_multiplied(self) -> bool {
499+
matches!(self, Self::PostMultiplied)
500+
}
501+
502+
/// Whether the alpha mode allows the surface to be transparent.
503+
///
504+
/// This is true for pre-multiplied and post-multiplied alpha modes.
505+
pub fn has_transparency(self) -> bool {
506+
!self.is_opaque()
507+
}
508+
}
509+
427510
/// There is no display handle.
428511
#[derive(Debug)]
429512
#[allow(dead_code)]

0 commit comments

Comments
 (0)