Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Format API / Transparency #241

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,23 @@ name = "buffer_mut"
harness = false

[features]
default = ["kms", "x11", "x11-dlopen", "wayland", "wayland-dlopen"]
kms = ["bytemuck", "drm", "rustix"]
default = ["kms", "x11", "x11-dlopen", "wayland", "wayland-dlopen","compatibility"]
kms = ["drm", "rustix"]
wayland = ["wayland-backend", "wayland-client", "wayland-sys", "memmap2", "rustix", "fastrand"]
wayland-dlopen = ["wayland-sys/dlopen"]
x11 = ["as-raw-xcb-connection", "bytemuck", "fastrand", "rustix", "tiny-xlib", "x11rb"]
x11 = ["as-raw-xcb-connection", "fastrand", "rustix", "tiny-xlib", "x11rb"]
x11-dlopen = ["tiny-xlib/dlopen", "x11rb/dl-libxcb"]
compatibility = []

[dependencies]
log = "0.4.17"
raw_window_handle = { package = "raw-window-handle", version = "0.6", features = ["std"] }
num = "0.4.3"
bytemuck = "1.12.3"

[target.'cfg(all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox"))))'.dependencies]
as-raw-xcb-connection = { version = "1.0.0", optional = true }
bytemuck = { version = "1.12.3", optional = true }
# bytemuck = { version = "1.12.3", optional = true }
drm = { version = "0.12.0", default-features = false, optional = true }
fastrand = { version = "2.0.0", optional = true }
memmap2 = { version = "0.9.0", optional = true }
Expand All @@ -43,7 +46,7 @@ x11rb = { version = "0.13.0", features = ["allow-unsafe-code", "shm"], optional

[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
version = "0.59.0"
features = ["Win32_Graphics_Gdi", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging", "Win32_Foundation"]
features = ["Win32_Graphics_Gdi", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging", "Win32_Foundation", "Win32_UI_ColorSystem"]

[target.'cfg(target_vendor = "apple")'.dependencies]
bytemuck = { version = "1.12.3", features = ["extern_crate_alloc"] }
Expand Down Expand Up @@ -88,6 +91,7 @@ colorous = "1.0.12"
criterion = { version = "0.4.0", default-features = false, features = ["cargo_bench_support"] }
web-time = "1.0.0"
winit = "0.30.0"
tiny-skia = "0.11.4"

[dev-dependencies.image]
version = "0.25.0"
Expand Down
2 changes: 1 addition & 1 deletion benches/buffer_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ fn buffer_mut(c: &mut Criterion) {
let mut buffer = surface.buffer_mut().unwrap();
b.iter(|| {
for _ in 0..500 {
let x: &mut [u32] = &mut buffer;
let x: &mut [u32] = &mut buffer.pixels_platform_dependent_mut();
black_box(x);
}
});
Expand Down
121 changes: 121 additions & 0 deletions examples/winit_tiny_skia.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use softbuffer::RGBA;
use std::num::NonZeroU32;
use winit::event::{Event, KeyEvent, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::keyboard::{Key, NamedKey};

#[path = "utils/winit_app.rs"]
mod winit_app;

use tiny_skia::{BlendMode, LineCap, Paint, PathBuilder, PixmapMut, Stroke, StrokeDash, Transform};

fn main() {
let event_loop = EventLoop::new().unwrap();

let app = winit_app::WinitAppBuilder::with_init(|elwt| {
let window = winit_app::make_window(elwt, |w| w.with_transparent(true));

let context = softbuffer::Context::new(window.clone()).unwrap();
let surface = softbuffer::Surface::new_with_alpha(&context, window.clone()).unwrap();

(window, surface)
})
.with_event_handler(|state, event, elwt| {
let (window, surface) = state;
elwt.set_control_flow(ControlFlow::Wait);

match event {
Event::WindowEvent {
window_id,
event: WindowEvent::Resized(size),
} if window_id == window.id() => {
if let (Some(width), Some(height)) =
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
{
surface.resize(width, height).unwrap();
}
}
Event::WindowEvent {
window_id,
event: WindowEvent::RedrawRequested,
} if window_id == window.id() => {
let size = window.inner_size();
if let (Some(width), Some(height)) =
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
{
let mut buffer = surface.buffer_mut().unwrap();

//We draw the background of our window in softbuffer writing to individual pixels
for y in 0..height.get() {
for x in 0..width.get() {
const SCALE_FACTOR: u32 = 3;
let red = (x/SCALE_FACTOR) % 255;
let green = (y/SCALE_FACTOR) % 255;
let blue = ((x/SCALE_FACTOR) * (y/SCALE_FACTOR)) % 255;
let alpha = if blue > 255/2{
255
}else{
0
};
let index = y as usize * width.get() as usize + x as usize;
buffer.pixels_rgb_mut()[index] = softbuffer::RGBA::new_unchecked(red,green, blue, alpha);
}
}

// buffer.fill(RGBA::new_unchecked(50,0,50, 200)); // Alternatively we could fill with a solid color

//using tiny_skia that accepts the u8 rgba format, we draw a star on top of our background
buffer.pixel_u8_slice_rgba(|u8_buffer_rgba| {
let mut pixmap =
PixmapMut::from_bytes(u8_buffer_rgba, width.get(), height.get())
.unwrap();
let mut paint = Paint::default();
// paint.set_color_rgba8(255, 0, 255, 0); // <-- We could set the color, but because we are using BlendMode::Clear the color does not matter
paint.anti_alias = true;
paint.blend_mode = BlendMode::Clear; // <-- Set Blend mode so that we can draw transparent pixels

let path = {
let mut pb = PathBuilder::new();
let RADIUS: f32 = (width.get().min(height.get()) / 2) as f32;
let CENTER: f32 = (width.get().min(height.get()) / 2) as f32;
pb.move_to(CENTER + RADIUS, CENTER);
for i in 1..8 {
let a = 2.6927937 * i as f32;
pb.line_to(CENTER + RADIUS * a.cos(), CENTER + RADIUS * a.sin());
}
pb.finish().unwrap()
};

let mut stroke = Stroke::default();
stroke.width = 24.0;
stroke.line_cap = LineCap::Round;
stroke.dash = StrokeDash::new(vec![20.0, 40.0], 0.0);

pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), None);
});


buffer.present().unwrap();
}
}
Event::WindowEvent {
event:
WindowEvent::CloseRequested
| WindowEvent::KeyboardInput {
event:
KeyEvent {
logical_key: Key::Named(NamedKey::Escape),
..
},
..
},
window_id,
} if window_id == window.id() => {
elwt.exit();
}
_ => {}
}
});

winit_app::run_app(event_loop, app);
}
117 changes: 74 additions & 43 deletions src/backend_dispatch.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Implements `buffer_interface::*` traits for enums dispatching to backends

use crate::{backend_interface::*, backends, InitError, Rect, SoftBufferError};
use crate::{backend_interface::*, backends, InitError, Rect, SoftBufferError, BufferReturn, WithAlpha, WithoutAlpha};

use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
use std::num::NonZeroU32;
Expand All @@ -10,7 +10,7 @@ use std::sync::Arc;
/// A macro for creating the enum used to statically dispatch to the platform-specific implementation.
macro_rules! make_dispatch {
(
<$dgen: ident, $wgen: ident> =>
<$dgen: ident, $wgen: ident, $alpha: ident> =>
$(
$(#[$attr:meta])*
$name: ident
Expand All @@ -24,48 +24,17 @@ macro_rules! make_dispatch {
)*
}

impl<D: HasDisplayHandle> ContextDispatch<D> {
pub fn variant_name(&self) -> &'static str {
match self {
$(
$(#[$attr])*
Self::$name(_) => stringify!($name),
)*
}
}
}

impl<D: HasDisplayHandle> ContextInterface<D> for ContextDispatch<D> {
fn new(mut display: D) -> Result<Self, InitError<D>>
where
D: Sized,
{
$(
$(#[$attr])*
match <$context_inner as ContextInterface<D>>::new(display) {
Ok(x) => {
return Ok(Self::$name(x));
}
Err(InitError::Unsupported(d)) => display = d,
Err(InitError::Failure(f)) => return Err(InitError::Failure(f)),
}
)*

Err(InitError::Unsupported(display))
}
}

#[allow(clippy::large_enum_variant)] // it's boxed anyways
pub(crate) enum SurfaceDispatch<$dgen, $wgen> {
pub(crate) enum SurfaceDispatch<$dgen, $wgen, $alpha> {
$(
$(#[$attr])*
$name($surface_inner),
)*
}

impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceInterface<D, W> for SurfaceDispatch<D, W> {
impl<D: HasDisplayHandle, W: HasWindowHandle,A: BufferReturn> SurfaceInterface<D, W, A> for SurfaceDispatch<D, W, A>{
type Context = ContextDispatch<D>;
type Buffer<'a> = BufferDispatch<'a, D, W> where Self: 'a;
type Buffer<'a> = BufferDispatch<'a, D, W, A> where Self: 'a;

fn new(window: W, display: &Self::Context) -> Result<Self, InitError<W>>
where
Expand All @@ -79,6 +48,18 @@ macro_rules! make_dispatch {
}
}

fn new_with_alpha(window: W, display: &Self::Context) -> Result<Self, InitError<W>>
where
W: Sized,
Self: Sized {
match display {
$(
$(#[$attr])*
ContextDispatch::$name(inner) => Ok(Self::$name(<$surface_inner>::new_with_alpha(window, inner)?)),
)*
}
}

fn window(&self) -> &W {
match self {
$(
Expand All @@ -97,7 +78,7 @@ macro_rules! make_dispatch {
}
}

fn buffer_mut(&mut self) -> Result<BufferDispatch<'_, D, W>, SoftBufferError> {
fn buffer_mut(&mut self) -> Result<BufferDispatch<'_, D, W, $alpha>, SoftBufferError> {
match self {
$(
$(#[$attr])*
Expand All @@ -116,14 +97,14 @@ macro_rules! make_dispatch {
}
}

pub(crate) enum BufferDispatch<'a, $dgen, $wgen> {
pub(crate) enum BufferDispatch<'a, $dgen, $wgen, $alpha> {
$(
$(#[$attr])*
$name($buffer_inner),
)*
}

impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferDispatch<'a, D, W> {
impl<'a, D: HasDisplayHandle, W: HasWindowHandle,A: BufferReturn> BufferInterface<A> for BufferDispatch<'a, D, W, A> {
#[inline]
fn pixels(&self) -> &[u32] {
match self {
Expand All @@ -144,6 +125,24 @@ macro_rules! make_dispatch {
}
}

fn pixels_rgb(&self) -> &[<$alpha as BufferReturn>::Output]{
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.pixels_rgb(),
)*
}
}

fn pixels_rgb_mut(&mut self) -> &mut[<$alpha as BufferReturn>::Output]{
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.pixels_rgb_mut(),
)*
}
}

fn age(&self) -> u8 {
match self {
$(
Expand Down Expand Up @@ -171,25 +170,57 @@ macro_rules! make_dispatch {
}
}
}

impl<D: HasDisplayHandle> ContextDispatch<D> {
pub fn variant_name(&self) -> &'static str {
match self {
$(
$(#[$attr])*
Self::$name(_) => stringify!($name),
)*
}
}
}

impl<D: HasDisplayHandle> ContextInterface<D> for ContextDispatch<D> {
fn new(mut display: D) -> Result<Self, InitError<D>>
where
D: Sized,
{
$(
$(#[$attr])*
match <$context_inner as ContextInterface<D>>::new(display) {
Ok(x) => {
return Ok(Self::$name(x));
}
Err(InitError::Unsupported(d)) => display = d,
Err(InitError::Failure(f)) => return Err(InitError::Failure(f)),
}
)*

Err(InitError::Unsupported(display))
}
}
};
}


// XXX empty enum with generic bound is invalid?

make_dispatch! {
<D, W> =>
<D, W, A> =>
#[cfg(x11_platform)]
X11(Arc<backends::x11::X11DisplayImpl<D>>, backends::x11::X11Impl<D, W>, backends::x11::BufferImpl<'a, D, W>),
#[cfg(wayland_platform)]
Wayland(Arc<backends::wayland::WaylandDisplayImpl<D>>, backends::wayland::WaylandImpl<D, W>, backends::wayland::BufferImpl<'a, D, W>),
#[cfg(kms_platform)]
Kms(Arc<backends::kms::KmsDisplayImpl<D>>, backends::kms::KmsImpl<D, W>, backends::kms::BufferImpl<'a, D, W>),
#[cfg(target_os = "windows")]
Win32(D, backends::win32::Win32Impl<D, W>, backends::win32::BufferImpl<'a, D, W>),
Win32(D, backends::win32::Win32Impl<D, W, A>, backends::win32::BufferImpl<'a, D, W, A>),
#[cfg(target_vendor = "apple")]
CoreGraphics(D, backends::cg::CGImpl<D, W>, backends::cg::BufferImpl<'a, D, W>),
CoreGraphics(D, backends::cg::CGImpl<D, W, A>, backends::cg::BufferImpl<'a, D, W, A>),
#[cfg(target_arch = "wasm32")]
Web(backends::web::WebDisplayImpl<D>, backends::web::WebImpl<D, W>, backends::web::BufferImpl<'a, D, W>),
#[cfg(target_os = "redox")]
Orbital(D, backends::orbital::OrbitalImpl<D, W>, backends::orbital::BufferImpl<'a, D, W>),
}
}
Loading
Loading