Skip to content

Commit

Permalink
removed #[duplicate_item] in favor of generics true
Browse files Browse the repository at this point in the history
moved all structs and windows/mac backends to new generics
added format conversion system, only 2 formats currently to test out.
exposed a few more pixels_ functions
added a winit_tiny_skia example script using new format
  • Loading branch information
jaysonmaw committed Sep 11, 2024
1 parent d616099 commit b8f4fda
Show file tree
Hide file tree
Showing 9 changed files with 496 additions and 347 deletions.
9 changes: 5 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,22 @@ harness = false

[features]
default = ["kms", "x11", "x11-dlopen", "wayland", "wayland-dlopen","compatibility"]
kms = ["bytemuck", "drm", "rustix"]
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"
duplicate = "1.0.0"
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 Down Expand Up @@ -91,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.pixels_mut();
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);
}
132 changes: 53 additions & 79 deletions src/backend_dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
use crate::{backend_interface::*, backends, InitError, Rect, SoftBufferError, BufferReturn, WithAlpha, WithoutAlpha};

use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
use duplicate::duplicate_item;
use std::num::NonZeroU32;
#[cfg(any(wayland_platform, x11_platform, kms_platform))]
use std::sync::Arc;

/// A macro for creating the enum used to statically dispatch to the platform-specific implementation.
macro_rules! make_enum {
macro_rules! make_dispatch {
(
<$dgen: ident, $wgen: ident, $alpha: ident> =>
$(
Expand All @@ -33,58 +32,9 @@ macro_rules! make_enum {
)*
}

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

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))
}
}
};
}

macro_rules! make_dispatch {
(
<$dgen: ident, $wgen: ident, $alpha: ident> =>
$(
$(#[$attr:meta])*
$name: ident
($context_inner: ty, $surface_inner: ty, $buffer_inner: ty),
)*
) => {
impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceInterface<D, W, $alpha> for SurfaceDispatch<D, W, $alpha>{
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, $alpha> 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 Down Expand Up @@ -147,8 +97,14 @@ macro_rules! make_dispatch {
}
}


impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface<$alpha> for BufferDispatch<'a, D, W, $alpha> {
pub(crate) enum BufferDispatch<'a, $dgen, $wgen, $alpha> {
$(
$(#[$attr])*
$name($buffer_inner),
)*
}

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 @@ -169,6 +125,15 @@ 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 {
$(
Expand Down Expand Up @@ -205,12 +170,44 @@ 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_enum!{
make_dispatch! {
<D, W, A> =>
#[cfg(x11_platform)]
X11(Arc<backends::x11::X11DisplayImpl<D>>, backends::x11::X11Impl<D, W>, backends::x11::BufferImpl<'a, D, W>),
Expand All @@ -226,27 +223,4 @@ make_enum!{
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>),
}

#[duplicate_item(
TY;
[ WithAlpha ];
[ WithoutAlpha ];
)]
make_dispatch! {
<D, W, TY> =>
#[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, TY>, backends::win32::BufferImpl<'a, D, W, TY>),
#[cfg(target_vendor = "apple")]
CoreGraphics(D, backends::cg::CGImpl<D, W, TY>, backends::cg::BufferImpl<'a, D, W, TY>),
#[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

0 comments on commit b8f4fda

Please sign in to comment.