From d61609947ef53ea9d4f627cb101606288bd8b067 Mon Sep 17 00:00:00 2001 From: Jaysonmaw <55055180+jaysonmaw@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:19:31 -0500 Subject: [PATCH 1/5] Added ability to tell backend that you want to use transparency/alpha channel Created RGBX/RGBA structs that can be used on every platform without needing to think about how the platform stores the u32 as the conversion to each of the platforms happens automatically --- Cargo.toml | 7 +- benches/buffer_mut.rs | 2 +- src/backend_dispatch.rs | 109 ++++++++++++++++++------ src/backend_interface.rs | 152 +++++++++++++++++++++++++++++++-- src/backends/cg.rs | 162 ++++++++++++++++++++++++++++++++--- src/backends/win32.rs | 180 ++++++++++++++++++++++++++++++++++++--- src/error.rs | 4 + src/lib.rs | 149 +++++++++++++++++++++++++++++--- 8 files changed, 693 insertions(+), 72 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fdce0873..e8783dda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,16 +17,19 @@ name = "buffer_mut" harness = false [features] -default = ["kms", "x11", "x11-dlopen", "wayland", "wayland-dlopen"] +default = ["kms", "x11", "x11-dlopen", "wayland", "wayland-dlopen","compatibility"] kms = ["bytemuck", "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-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" [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 } @@ -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"] } diff --git a/benches/buffer_mut.rs b/benches/buffer_mut.rs index beaea990..f69f906f 100644 --- a/benches/buffer_mut.rs +++ b/benches/buffer_mut.rs @@ -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_mut(); black_box(x); } }); diff --git a/src/backend_dispatch.rs b/src/backend_dispatch.rs index 208f82cd..7a1de501 100644 --- a/src/backend_dispatch.rs +++ b/src/backend_dispatch.rs @@ -1,16 +1,17 @@ //! 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 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_dispatch { +macro_rules! make_enum { ( - <$dgen: ident, $wgen: ident> => + <$dgen: ident, $wgen: ident, $alpha: ident> => $( $(#[$attr:meta])* $name: ident @@ -24,6 +25,21 @@ macro_rules! make_dispatch { )* } + #[allow(clippy::large_enum_variant)] // it's boxed anyways + pub(crate) enum SurfaceDispatch<$dgen, $wgen, $alpha> { + $( + $(#[$attr])* + $name($surface_inner), + )* + } + + pub(crate) enum BufferDispatch<'a, $dgen, $wgen, $alpha> { + $( + $(#[$attr])* + $name($buffer_inner), + )* + } + impl ContextDispatch { pub fn variant_name(&self) -> &'static str { match self { @@ -54,18 +70,21 @@ macro_rules! make_dispatch { Err(InitError::Unsupported(display)) } } + }; +} - #[allow(clippy::large_enum_variant)] // it's boxed anyways - pub(crate) enum SurfaceDispatch<$dgen, $wgen> { - $( - $(#[$attr])* - $name($surface_inner), - )* - } - - impl SurfaceInterface for SurfaceDispatch { +macro_rules! make_dispatch { + ( + <$dgen: ident, $wgen: ident, $alpha: ident> => + $( + $(#[$attr:meta])* + $name: ident + ($context_inner: ty, $surface_inner: ty, $buffer_inner: ty), + )* + ) => { + impl SurfaceInterface for SurfaceDispatch{ type Context = ContextDispatch; - type Buffer<'a> = BufferDispatch<'a, D, W> where Self: 'a; + type Buffer<'a> = BufferDispatch<'a, D, W, $alpha> where Self: 'a; fn new(window: W, display: &Self::Context) -> Result> where @@ -79,6 +98,18 @@ macro_rules! make_dispatch { } } + fn new_with_alpha(window: W, display: &Self::Context) -> Result> + 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 { $( @@ -97,7 +128,7 @@ macro_rules! make_dispatch { } } - fn buffer_mut(&mut self) -> Result, SoftBufferError> { + fn buffer_mut(&mut self) -> Result, SoftBufferError> { match self { $( $(#[$attr])* @@ -116,14 +147,8 @@ macro_rules! make_dispatch { } } - pub(crate) enum BufferDispatch<'a, $dgen, $wgen> { - $( - $(#[$attr])* - $name($buffer_inner), - )* - } - - impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferDispatch<'a, D, W> { + + impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface<$alpha> for BufferDispatch<'a, D, W, $alpha> { #[inline] fn pixels(&self) -> &[u32] { match self { @@ -144,6 +169,15 @@ macro_rules! make_dispatch { } } + 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 { $( @@ -176,8 +210,8 @@ macro_rules! make_dispatch { // XXX empty enum with generic bound is invalid? -make_dispatch! { - => +make_enum!{ + => #[cfg(x11_platform)] X11(Arc>, backends::x11::X11Impl, backends::x11::BufferImpl<'a, D, W>), #[cfg(wayland_platform)] @@ -185,11 +219,34 @@ make_dispatch! { #[cfg(kms_platform)] Kms(Arc>, backends::kms::KmsImpl, backends::kms::BufferImpl<'a, D, W>), #[cfg(target_os = "windows")] - Win32(D, backends::win32::Win32Impl, backends::win32::BufferImpl<'a, D, W>), + Win32(D, backends::win32::Win32Impl, backends::win32::BufferImpl<'a, D, W, A>), #[cfg(target_vendor = "apple")] - CoreGraphics(D, backends::cg::CGImpl, backends::cg::BufferImpl<'a, D, W>), + CoreGraphics(D, backends::cg::CGImpl, backends::cg::BufferImpl<'a, D, W, A>), #[cfg(target_arch = "wasm32")] Web(backends::web::WebDisplayImpl, backends::web::WebImpl, backends::web::BufferImpl<'a, D, W>), #[cfg(target_os = "redox")] Orbital(D, backends::orbital::OrbitalImpl, backends::orbital::BufferImpl<'a, D, W>), } + +#[duplicate_item( + TY; + [ WithAlpha ]; + [ WithoutAlpha ]; + )] +make_dispatch! { + => + #[cfg(x11_platform)] + X11(Arc>, backends::x11::X11Impl, backends::x11::BufferImpl<'a, D, W>), + #[cfg(wayland_platform)] + Wayland(Arc>, backends::wayland::WaylandImpl, backends::wayland::BufferImpl<'a, D, W>), + #[cfg(kms_platform)] + Kms(Arc>, backends::kms::KmsImpl, backends::kms::BufferImpl<'a, D, W>), + #[cfg(target_os = "windows")] + Win32(D, backends::win32::Win32Impl, backends::win32::BufferImpl<'a, D, W, TY>), + #[cfg(target_vendor = "apple")] + CoreGraphics(D, backends::cg::CGImpl, backends::cg::BufferImpl<'a, D, W, TY>), + #[cfg(target_arch = "wasm32")] + Web(backends::web::WebDisplayImpl, backends::web::WebImpl, backends::web::BufferImpl<'a, D, W>), + #[cfg(target_os = "redox")] + Orbital(D, backends::orbital::OrbitalImpl, backends::orbital::BufferImpl<'a, D, W>), +} \ No newline at end of file diff --git a/src/backend_interface.rs b/src/backend_interface.rs index 13e3555c..3bdd70eb 100644 --- a/src/backend_interface.rs +++ b/src/backend_interface.rs @@ -1,9 +1,10 @@ //! Interface implemented by backends -use crate::{InitError, Rect, SoftBufferError}; +use crate::{BufferReturn, InitError, Rect, SoftBufferError}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; -use std::num::NonZeroU32; +use std::{fmt::Debug, num::NonZeroU32}; +use num::cast::AsPrimitive; pub(crate) trait ContextInterface { fn new(display: D) -> Result> @@ -12,13 +13,17 @@ pub(crate) trait ContextInterface { Self: Sized; } -pub(crate) trait SurfaceInterface { +pub(crate) trait SurfaceInterface { type Context: ContextInterface; - type Buffer<'a>: BufferInterface + type Buffer<'a>: BufferInterface where Self: 'a; fn new(window: W, context: &Self::Context) -> Result> + where + W: Sized, + Self: Sized; + fn new_with_alpha(window: W, context: &Self::Context) -> Result> where W: Sized, Self: Sized; @@ -34,10 +39,147 @@ pub(crate) trait SurfaceInterface { + #[deprecated = "Left for backwards compatibility. Will panic in the future. Switch to using the pixels_rgb or pixels_rgba methods for better cross platform portability"] fn pixels(&self) -> &[u32]; + #[deprecated = "Left for backwards compatibility. Will panic in the future. Switch to using the pixels_rgb_mut or pixels_rgba_mut methods for better cross platform portability"] fn pixels_mut(&mut self) -> &mut [u32]; + fn pixels_rgb_mut(&mut self) -> &mut[::Output]; fn age(&self) -> u8; fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError>; fn present(self) -> Result<(), SoftBufferError>; } + +macro_rules! define_rgbx_little_endian { + ( + $( + $(#[$attr:meta])* + $first_vis:vis $first:ident,$second_vis:vis $second:ident,$third_vis:vis $third:ident,$forth_vis:vis $forth:ident + )* + ) => { + $( + $(#[$attr])* + #[repr(C)] + pub struct RGBX{ + $forth_vis $forth: u8, + $third_vis $third: u8, + $second_vis $second: u8, + $first_vis $first: u8, + } + )* + }; +} + +macro_rules! define_rgba_little_endian { + ( + $( + $(#[$attr:meta])* + $first_vis:vis $first:ident,$second_vis:vis $second:ident,$third_vis:vis $third:ident,$forth_vis:vis $forth:ident + )* + ) => { + $( + $(#[$attr])* + #[repr(C)] + pub struct RGBA{ + $forth_vis $forth: u8, + $third_vis $third: u8, + $second_vis $second: u8, + $first_vis $first: u8, + } + )* + }; +} + +define_rgbx_little_endian!{ + #[cfg(x11_platform)] + x,pub r,pub g,pub b + #[cfg(wayland_platform)] + x,pub r,pub g,pub b + #[cfg(kms_platform)] + x,pub r,pub g,pub b + #[cfg(target_os = "windows")] + x,pub r,pub g,pub b + #[cfg(target_vendor = "apple")] + x,pub r,pub g,pub b + #[cfg(target_arch = "wasm32")] + x,pub r,pub g,pub b + #[cfg(target_os = "redox")] + x,pub r,pub g,pub b +} + +define_rgba_little_endian!{ + #[cfg(x11_platform)] + pub a,pub r,pub g,pub b + #[cfg(wayland_platform)] + pub a,pub r,pub g,pub b + #[cfg(kms_platform)] + pub a,pub r,pub g,pub b + #[cfg(target_os = "windows")] + pub a,pub r,pub g,pub b + #[cfg(target_vendor = "apple")] + pub a,pub r,pub g,pub b + #[cfg(target_arch = "wasm32")] + pub a,pub r,pub g,pub b + #[cfg(target_os = "redox")] + pub a,pub r,pub g,pub b +} + +impl RGBX{ + #[inline] + /// Creates new RGBX from r,g,b values. + /// Takes any primitive value that can be converted to a u8 using the ```as``` keyword + /// If the value is greater than the u8::MAX the function will return an error + pub fn new(r: T,g: T,b: T) -> Result + where + T: AsPrimitive + std::cmp::PartialOrd, + u8: AsPrimitive + { + let MAX_U8 = 255.as_(); + if r > MAX_U8 || g > MAX_U8 || b > MAX_U8{ + Err(SoftBufferError::PrimitiveOutsideOfU8Range) + }else{ + Ok(Self { r: r.as_(), g: g.as_(), b: b.as_(), x: 255 }) + } + } + + /// Creates new RGBX from r,g,b values. + /// Takes any primitive value that can be converted to a u8 using the ```as``` keyword + /// Unlike ```RGBX::new``` this function does not care if the value you provide is greater than u8. It will silently ignore any higher bits, taking only the last 8 bits. + #[inline] + pub fn new_unchecked(r: T,g: T,b: T) -> Self + where + T: AsPrimitive + { + Self { r: r.as_(), g: g.as_(), b: b.as_(), x: 255 } + } +} + +impl RGBA{ + #[inline] + /// Creates new RGBX from r,g,b values. + /// Takes any primitive value that can be converted to a u8 using the ```as``` keyword + /// If the value is greater than the u8::MAX the function will return an error + pub fn new(r: T,g: T,b: T,a: T) -> Result + where + T: AsPrimitive + std::cmp::PartialOrd, + u8: AsPrimitive + { + let max_u8 = 255.as_(); + if r > max_u8 || g > max_u8 || b > max_u8 || a > max_u8{ + Err(SoftBufferError::PrimitiveOutsideOfU8Range) + }else{ + Ok(Self { r: r.as_(), g: g.as_(), b: b.as_(), a: a.as_() }) + } + } + + /// Creates new RGBX from r,g,b values. + /// Takes any primitive value that can be converted to a u8 using the ```as``` keyword + /// Unlike ```RGBX::new``` this function does not care if the value you provide is greater than u8. It will silently ignore any higher bits, taking only the last 8 bits. + #[inline] + pub fn new_unchecked(r: T,g: T,b: T, a: T) -> Self + where + T: AsPrimitive + { + Self { r: r.as_(), g: g.as_(), b: b.as_(), a: a.as_() } + } +} diff --git a/src/backends/cg.rs b/src/backends/cg.rs index ba957d85..36bfea41 100644 --- a/src/backends/cg.rs +++ b/src/backends/cg.rs @@ -1,12 +1,13 @@ -use crate::backend_interface::*; +use crate::{backend_interface::*, BufferReturn, WithAlpha, WithoutAlpha}; use crate::error::InitError; use crate::{Rect, SoftBufferError}; use core_graphics::base::{ - kCGBitmapByteOrder32Little, kCGImageAlphaNoneSkipFirst, kCGRenderingIntentDefault, + kCGBitmapByteOrder32Little, kCGImageAlphaNoneSkipFirst, kCGRenderingIntentDefault, kCGImageAlphaFirst }; use core_graphics::color_space::CGColorSpace; use core_graphics::data_provider::CGDataProvider; use core_graphics::image::CGImage; +use duplicate::duplicate_item; use foreign_types::ForeignType; use objc2::rc::Retained; use objc2::runtime::{AnyObject, Bool}; @@ -19,6 +20,7 @@ use objc2_foundation::{ use objc2_quartz_core::{kCAGravityTopLeft, CALayer, CATransaction}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawWindowHandle}; +// use core::slice::SlicePattern; use std::ffi::c_void; use std::marker::PhantomData; use std::num::NonZeroU32; @@ -113,7 +115,7 @@ impl Observer { } } -pub struct CGImpl { +pub struct CGImpl { /// Our layer. layer: SendCALayer, /// The layer that our layer was created from. @@ -127,10 +129,10 @@ pub struct CGImpl { /// The height of the underlying buffer. height: usize, window_handle: W, - _display: PhantomData, + _display: PhantomData<(D,A)>, } -impl Drop for CGImpl { +impl Drop for CGImpl { fn drop(&mut self) { // SAFETY: Registered in `new`, must be removed before the observer is deallocated. unsafe { @@ -142,9 +144,15 @@ impl Drop for CGImpl { } } -impl SurfaceInterface for CGImpl { +#[duplicate_item( + TY; + [ WithAlpha ]; + [ WithoutAlpha ]; + )] +impl SurfaceInterface for CGImpl +{ type Context = D; - type Buffer<'a> = BufferImpl<'a, D, W> where Self: 'a; + type Buffer<'a> = BufferImpl<'a, D, W, TY> where Self: 'a; fn new(window_src: W, _display: &D) -> Result> { // `NSView`/`UIView` can only be accessed from the main thread. @@ -264,6 +272,124 @@ impl SurfaceInterface for CGImpl< }) } + fn new_with_alpha(window_src: W, _display: &D) -> Result> { + // `NSView`/`UIView` can only be accessed from the main thread. + let _mtm = MainThreadMarker::new().ok_or(SoftBufferError::PlatformError( + Some("can only access Core Graphics handles from the main thread".to_string()), + None, + ))?; + + let root_layer = match window_src.window_handle()?.as_raw() { + RawWindowHandle::AppKit(handle) => { + // SAFETY: The pointer came from `WindowHandle`, which ensures that the + // `AppKitWindowHandle` contains a valid pointer to an `NSView`. + // + // We use `NSObject` here to avoid importing `objc2-app-kit`. + let view: &NSObject = unsafe { handle.ns_view.cast().as_ref() }; + + // Force the view to become layer backed + let _: () = unsafe { msg_send![view, setWantsLayer: Bool::YES] }; + + // SAFETY: `-[NSView layer]` returns an optional `CALayer` + let layer: Option> = unsafe { msg_send_id![view, layer] }; + layer.expect("failed making the view layer-backed") + } + RawWindowHandle::UiKit(handle) => { + // SAFETY: The pointer came from `WindowHandle`, which ensures that the + // `UiKitWindowHandle` contains a valid pointer to an `UIView`. + // + // We use `NSObject` here to avoid importing `objc2-ui-kit`. + let view: &NSObject = unsafe { handle.ui_view.cast().as_ref() }; + + // SAFETY: `-[UIView layer]` returns `CALayer` + let layer: Retained = unsafe { msg_send_id![view, layer] }; + layer + } + _ => return Err(InitError::Unsupported(window_src)), + }; + + // Add a sublayer, to avoid interfering with the root layer, since setting the contents of + // e.g. a view-controlled layer is brittle. + let layer = CALayer::new(); + root_layer.addSublayer(&layer); + + // Set the anchor point and geometry. Softbuffer's uses a coordinate system with the origin + // in the top-left corner. + // + // NOTE: This doesn't really matter unless we start modifying the `position` of our layer + // ourselves, but it's nice to have in place. + layer.setAnchorPoint(CGPoint::new(0.0, 0.0)); + layer.setGeometryFlipped(true); + + // Do not use auto-resizing mask. + // + // This is done to work around a bug in macOS 14 and above, where views using auto layout + // may end up setting fractional values as the bounds, and that in turn doesn't propagate + // properly through the auto-resizing mask and with contents gravity. + // + // Instead, we keep the bounds of the layer in sync with the root layer using an observer, + // see below. + // + // layer.setAutoresizingMask(kCALayerHeightSizable | kCALayerWidthSizable); + + let observer = Observer::new(&layer); + // Observe changes to the root layer's bounds and scale factor, and apply them to our layer. + // + // The previous implementation updated the scale factor inside `resize`, but this works + // poorly with transactions, and is generally inefficient. Instead, we update the scale + // factor only when needed because the super layer's scale factor changed. + // + // Note that inherent in this is an explicit design decision: We control the `bounds` and + // `contentsScale` of the layer directly, and instead let the `resize` call that the user + // controls only be the size of the underlying buffer. + // + // SAFETY: Observer deregistered in `Drop` before the observer object is deallocated. + unsafe { + root_layer.addObserver_forKeyPath_options_context( + &observer, + ns_string!("contentsScale"), + NSKeyValueObservingOptions::NSKeyValueObservingOptionNew + | NSKeyValueObservingOptions::NSKeyValueObservingOptionInitial, + ptr::null_mut(), + ); + root_layer.addObserver_forKeyPath_options_context( + &observer, + ns_string!("bounds"), + NSKeyValueObservingOptions::NSKeyValueObservingOptionNew + | NSKeyValueObservingOptions::NSKeyValueObservingOptionInitial, + ptr::null_mut(), + ); + } + + // Set the content so that it is placed in the top-left corner if it does not have the same + // size as the surface itself. + // + // TODO(madsmtm): Consider changing this to `kCAGravityResize` to stretch the content if + // resized to something that doesn't fit, see #177. + layer.setContentsGravity(unsafe { kCAGravityTopLeft }); + + // Initialize color space here, to reduce work later on. + let color_space = CGColorSpace::create_device_rgb(); + + // Grab initial width and height from the layer (whose properties have just been initialized + // by the observer using `NSKeyValueObservingOptionInitial`). + let size = layer.bounds().size; + let scale_factor = layer.contentsScale(); + let width = (size.width * scale_factor) as usize; + let height = (size.height * scale_factor) as usize; + + Ok(Self { + layer: SendCALayer(layer), + root_layer: SendCALayer(root_layer), + observer, + color_space: SendCGColorSpace(color_space), + width, + height, + _display: PhantomData, + window_handle: window_src, + }) + } + #[inline] fn window(&self) -> &W { &self.window_handle @@ -275,20 +401,26 @@ impl SurfaceInterface for CGImpl< Ok(()) } - fn buffer_mut(&mut self) -> Result, SoftBufferError> { + fn buffer_mut(&mut self) -> Result, SoftBufferError> { Ok(BufferImpl { buffer: vec![0; self.width * self.height], imp: self, + _marker: PhantomData }) } } -pub struct BufferImpl<'a, D, W> { - imp: &'a mut CGImpl, +pub struct BufferImpl<'a, D, W, A> { + imp: &'a mut CGImpl, buffer: Vec, + _marker: PhantomData, } - -impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl<'a, D, W> { +#[duplicate_item( + TY platform_alpha_mode; + [ WithAlpha ] [kCGImageAlphaFirst]; + [ WithoutAlpha ] [kCGImageAlphaNoneSkipFirst]; + )] +impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl<'a, D, W, TY> { #[inline] fn pixels(&self) -> &[u32] { &self.buffer @@ -299,6 +431,10 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl &mut self.buffer } + fn pixels_rgb_mut(&mut self) -> &mut[::Output] { + unsafe{std::mem::transmute::<& mut [u32],&mut [::Output]>(&mut self.buffer[..])} + } + fn age(&self) -> u8 { 0 } @@ -313,7 +449,7 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl 32, self.imp.width * 4, &self.imp.color_space.0, - kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst, + kCGBitmapByteOrder32Little | platform_alpha_mode, &data_provider, false, kCGRenderingIntentDefault, diff --git a/src/backends/win32.rs b/src/backends/win32.rs index 084ad47b..0ff1ca80 100644 --- a/src/backends/win32.rs +++ b/src/backends/win32.rs @@ -2,8 +2,9 @@ //! //! This module converts the input buffer into a bitmap and then stretches it to the window. -use crate::backend_interface::*; +use crate::{backend_interface::*, BufferReturn, WithAlpha, WithoutAlpha}; use crate::{Rect, SoftBufferError}; +use duplicate::duplicate_item; use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawWindowHandle}; use std::io; @@ -16,7 +17,8 @@ use std::sync::{mpsc, Mutex, OnceLock}; use std::thread; use windows_sys::Win32::Foundation::HWND; -use windows_sys::Win32::Graphics::Gdi; +use windows_sys::Win32::Graphics::Gdi::{self, BITMAPINFO, CIEXYZTRIPLE, CIEXYZ}; +use windows_sys::Win32::UI::ColorSystem::LCS_WINDOWS_COLOR_SPACE; const ZERO_QUAD: Gdi::RGBQUAD = Gdi::RGBQUAD { rgbBlue: 0, @@ -113,6 +115,97 @@ impl Buffer { } } + fn new_with_alpha(window_dc: Gdi::HDC, width: NonZeroI32, height: NonZeroI32) -> Self { + let dc = Allocator::get().allocate(window_dc); + assert!(!dc.is_null()); + + // Create a new bitmap info struct. + let bitmap_info = BitmapInfoV4 { + bmi_header: Gdi::BITMAPV4HEADER { + bV4Size: mem::size_of::() as u32, + bV4Width: width.get(), + bV4Height: -height.get(), + bV4Planes: 1, + bV4BitCount: 32, + bV4V4Compression: Gdi::BI_BITFIELDS, + bV4SizeImage: 0, + bV4XPelsPerMeter: 0, + bV4YPelsPerMeter: 0, + bV4ClrUsed: 0, + bV4ClrImportant: 0, + bV4RedMask: 0x00ff0000, + bV4GreenMask: 0x0000ff00, + bV4BlueMask: 0xff0000ff, + bV4AlphaMask: 0xff000000, + bV4CSType: LCS_WINDOWS_COLOR_SPACE as u32, + bV4Endpoints: CIEXYZTRIPLE{ + ciexyzRed: CIEXYZ{ + ciexyzX: 0, + ciexyzY: 0, + ciexyzZ: 0, + }, + ciexyzGreen: CIEXYZ{ + ciexyzX: 0, + ciexyzY: 0, + ciexyzZ: 0, + }, + ciexyzBlue: CIEXYZ{ + ciexyzX: 0, + ciexyzY: 0, + ciexyzZ: 0, + }, + }, + bV4GammaRed: 0, + bV4GammaGreen: 0, + bV4GammaBlue: 0, + }, + bmi_colors: [ + Gdi::RGBQUAD { + rgbRed: 0xff, + ..ZERO_QUAD + }, + Gdi::RGBQUAD { + rgbGreen: 0xff, + ..ZERO_QUAD + }, + Gdi::RGBQUAD { + rgbBlue: 0xff, + ..ZERO_QUAD + }, + ], + }; + + // XXX alignment? + // XXX better to use CreateFileMapping, and pass hSection? + // XXX test return value? + let mut pixels: *mut u32 = ptr::null_mut(); + let bitmap = unsafe { + Gdi::CreateDIBSection( + dc, + &bitmap_info as *const BitmapInfoV4 as *const _, + Gdi::DIB_RGB_COLORS, + &mut pixels as *mut *mut u32 as _, + ptr::null_mut(), + 0, + ) + }; + assert!(!bitmap.is_null()); + let pixels = NonNull::new(pixels).unwrap(); + + unsafe { + Gdi::SelectObject(dc, bitmap); + } + + Self { + dc, + bitmap, + width, + height, + pixels, + presented: false, + } + } + #[inline] fn pixels(&self) -> &[u32] { unsafe { @@ -135,7 +228,7 @@ impl Buffer { } /// The handle to a window for software buffering. -pub struct Win32Impl { +pub struct Win32Impl { /// The window handle. window: OnlyUsedFromOrigin, @@ -154,9 +247,10 @@ pub struct Win32Impl { /// /// We don't use this, but other code might. _display: PhantomData, + _marker: PhantomData, } -impl Drop for Win32Impl { +impl Drop for Win32Impl { fn drop(&mut self) { // Release our resources. Allocator::get().release(self.window.0, self.dc.0); @@ -170,7 +264,14 @@ struct BitmapInfo { bmi_colors: [Gdi::RGBQUAD; 3], } -impl Win32Impl { +/// The Win32-compatible bitmap information. +#[repr(C)] +struct BitmapInfoV4 { + bmi_header: Gdi::BITMAPV4HEADER, + bmi_colors: [Gdi::RGBQUAD; 3], +} + +impl Win32Impl { fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> { let buffer = self.buffer.as_mut().unwrap(); unsafe { @@ -206,9 +307,14 @@ impl Win32Impl { } } -impl SurfaceInterface for Win32Impl { +#[duplicate_item( + TY internal_buffer_function; + [ WithAlpha ] [new_with_alpha]; + [ WithoutAlpha ] [new]; + )] +impl SurfaceInterface for Win32Impl { type Context = D; - type Buffer<'a> = BufferImpl<'a, D, W> where Self: 'a; + type Buffer<'a> = BufferImpl<'a, D, W, TY> where Self: 'a; /// Create a new `Win32Impl` from a `Win32WindowHandle`. fn new(window: W, _display: &D) -> Result> { @@ -238,6 +344,45 @@ impl SurfaceInterface for Win32Im buffer: None, handle: window, _display: PhantomData, + _marker: PhantomData, + }) + } + + fn new_with_alpha( + window: W, + context: &Self::Context, + ) -> Result> + where + W: Sized, + Self: Sized, + { + let raw = window.window_handle()?.as_raw(); + let handle = match raw { + RawWindowHandle::Win32(handle) => handle, + _ => return Err(crate::InitError::Unsupported(window)), + }; + + // Get the handle to the device context. + // SAFETY: We have confirmed that the window handle is valid. + let hwnd = handle.hwnd.get() as HWND; + let dc = Allocator::get().get_dc(hwnd); + + // GetDC returns null if there is a platform error. + if dc.is_null() { + return Err(SoftBufferError::PlatformError( + Some("Device Context is null".into()), + Some(Box::new(io::Error::last_os_error())), + ) + .into()); + } + + Ok(Self { + dc: dc.into(), + window: hwnd.into(), + buffer: None, + handle: window, + _display: PhantomData, + _marker: PhantomData, }) } @@ -260,12 +405,12 @@ impl SurfaceInterface for Win32Im } } - self.buffer = Some(Buffer::new(self.dc.0, width, height)); + self.buffer = Some(Buffer::internal_buffer_function(self.dc.0, width, height)); Ok(()) } - fn buffer_mut(&mut self) -> Result, SoftBufferError> { + fn buffer_mut(&mut self) -> Result, SoftBufferError> { if self.buffer.is_none() { panic!("Must set size of surface before calling `buffer_mut()`"); } @@ -279,9 +424,14 @@ impl SurfaceInterface for Win32Im } } -pub struct BufferImpl<'a, D, W>(&'a mut Win32Impl); +pub struct BufferImpl<'a, D, W, A>(&'a mut Win32Impl); -impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl<'a, D, W> { +#[duplicate_item( + TY; + [ WithAlpha ]; + [ WithoutAlpha ]; + )] +impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl<'a, D, W, TY> { #[inline] fn pixels(&self) -> &[u32] { self.0.buffer.as_ref().unwrap().pixels() @@ -292,6 +442,14 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl self.0.buffer.as_mut().unwrap().pixels_mut() } + fn pixels_rgb_mut(&mut self) -> &mut [::Output] { + unsafe { + std::mem::transmute::<&mut [u32], &mut [::Output]>( + self.0.buffer.as_mut().unwrap().pixels_mut(), + ) + } + } + fn age(&self) -> u8 { match self.0.buffer.as_ref() { Some(buffer) if buffer.presented => 1, diff --git a/src/error.rs b/src/error.rs index eaec8563..4aee4ccf 100644 --- a/src/error.rs +++ b/src/error.rs @@ -100,6 +100,9 @@ pub enum SoftBufferError { /// actual error type. PlatformError(Option, Option>), + /// An Error returned from RGBX/RGBA::new() if called with numbers that are higher than u8::MAX + PrimitiveOutsideOfU8Range, + /// This function is unimplemented on this platform. Unimplemented, } @@ -138,6 +141,7 @@ impl fmt::Display for SoftBufferError { "Damage rect {}x{} at ({}, {}) out of range for backend.", rect.width, rect.height, rect.x, rect.y ), + Self::PrimitiveOutsideOfU8Range => write!(f, "The passed in primitive value is greater than what can fit in a u8"), Self::Unimplemented => write!(f, "This function is unimplemented on this platform."), } } diff --git a/src/lib.rs b/src/lib.rs index d19eafa7..3631f453 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,8 +22,10 @@ use std::sync::Arc; use error::InitError; pub use error::SoftBufferError; +pub use backend_interface::{RGBX,RGBA}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle}; +use duplicate::duplicate_item; #[cfg(target_arch = "wasm32")] pub use backends::web::SurfaceExtWeb; @@ -71,20 +73,35 @@ pub struct Rect { pub height: NonZeroU32, } +pub trait BufferReturn { + type Output: Sized; +} +pub enum WithoutAlpha{} + +impl BufferReturn for WithoutAlpha{ + type Output = RGBX; +} +pub enum WithAlpha{} + + +impl BufferReturn for WithAlpha{ + type Output = RGBA; +} + /// A surface for drawing to a window with software buffers. -pub struct Surface { +pub struct Surface { /// This is boxed so that `Surface` is the same size on every platform. - surface_impl: Box>, + surface_impl: Box>, _marker: PhantomData>, } -impl Surface { +impl Surface { /// Creates a new surface for the context for the provided window. - pub fn new(context: &Context, window: W) -> Result { + pub fn new(context: &Context, window: W) -> Result, SoftBufferError> { match SurfaceDispatch::new(window, &context.context_impl) { Ok(surface_dispatch) => Ok(Self { surface_impl: Box::new(surface_dispatch), - _marker: PhantomData, + _marker: PhantomData }), Err(InitError::Unsupported(window)) => { let raw = window.window_handle()?.as_raw(); @@ -97,6 +114,14 @@ impl Surface { Err(InitError::Failure(f)) => Err(f), } } +} + +#[duplicate_item( + TY; + [ WithAlpha ]; + [ WithoutAlpha ]; + )] +impl Surface { /// Get a reference to the underlying window handle. pub fn window(&self) -> &W { @@ -134,7 +159,7 @@ impl Surface { /// - On DRM/KMS, there is no reliable and sound way to wait for the page flip to happen from within /// `softbuffer`. Therefore it is the responsibility of the user to wait for the page flip before /// sending another frame. - pub fn buffer_mut(&mut self) -> Result, SoftBufferError> { + pub fn buffer_mut(&mut self) -> Result, SoftBufferError> { Ok(Buffer { buffer_impl: self.surface_impl.buffer_mut()?, _marker: PhantomData, @@ -142,14 +167,45 @@ impl Surface { } } -impl AsRef for Surface { +impl Surface { + /// Creates a new surface for the context for the provided window. + pub fn new_with_alpha(context: &Context, window: W) -> Result, SoftBufferError> { + match SurfaceDispatch::new_with_alpha(window, &context.context_impl) { + Ok(surface_dispatch) => Ok(Self { + surface_impl: Box::new(surface_dispatch), + _marker: PhantomData + }), + Err(InitError::Unsupported(window)) => { + let raw = window.window_handle()?.as_raw(); + Err(SoftBufferError::UnsupportedWindowPlatform { + human_readable_window_platform_name: window_handle_type_name(&raw), + human_readable_display_platform_name: context.context_impl.variant_name(), + window_handle: raw, + }) + } + Err(InitError::Failure(f)) => Err(f), + } + } +} + +#[duplicate_item( + TY; + [ WithAlpha ]; + [ WithoutAlpha ]; + )] +impl AsRef for Surface { #[inline] fn as_ref(&self) -> &W { self.window() } } -impl HasWindowHandle for Surface { +#[duplicate_item( + TY; + [ WithAlpha ]; + [ WithoutAlpha ]; + )] +impl HasWindowHandle for Surface { #[inline] fn window_handle( &self, @@ -196,12 +252,17 @@ impl HasWindowHandle for Surface /// - Web /// - AppKit /// - UIKit -pub struct Buffer<'a, D, W> { - buffer_impl: BufferDispatch<'a, D, W>, +pub struct Buffer<'a, D, W, A> { + buffer_impl: BufferDispatch<'a, D, W, A>, _marker: PhantomData<(Arc, Cell<()>)>, } -impl<'a, D: HasDisplayHandle, W: HasWindowHandle> Buffer<'a, D, W> { +#[duplicate_item( + TY; + [ WithAlpha ]; + [ WithoutAlpha ]; + )] +impl<'a, D: HasDisplayHandle, W: HasWindowHandle> Buffer<'a, D, W, TY> { /// Is age is the number of frames ago this buffer was last presented. So if the value is /// `1`, it is the same as the last frame, and if it is `2`, it is the same as the frame /// before that (for backends using double buffering). If the value is `0`, it is a new @@ -244,7 +305,29 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> Buffer<'a, D, W> { } } -impl<'a, D: HasDisplayHandle, W: HasWindowHandle> ops::Deref for Buffer<'a, D, W> { +#[duplicate_item( + TY; + [ WithAlpha ]; + [ WithoutAlpha ]; + )] +impl<'a, D: HasDisplayHandle, W: HasWindowHandle> Buffer<'a, D, W, TY>{ + #[deprecated = "Left for backwards compatibility. Will panic in the future. Switch to using the pixels_rgb or pixels_rgba methods for better cross platform portability"] + pub fn pixels(&self)-> &[u32] { + self.buffer_impl.pixels() + } + #[deprecated = "Left for backwards compatibility. Will panic in the future. Switch to using the pixels_rgb_mut or pixels_rgba_mut methods for better cross platform portability"] + pub fn pixels_mut(&mut self)-> &mut [u32] { + self.buffer_impl.pixels_mut() + } +} + +#[duplicate_item( + TY; + [ WithAlpha ]; + [ WithoutAlpha ]; + )] +#[cfg(feature = "compatibility")] +impl<'a, D: HasDisplayHandle, W: HasWindowHandle> ops::Deref for Buffer<'a, D, W, TY> { type Target = [u32]; #[inline] @@ -253,13 +336,51 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> ops::Deref for Buffer<'a, D, W } } -impl<'a, D: HasDisplayHandle, W: HasWindowHandle> ops::DerefMut for Buffer<'a, D, W> { +#[duplicate_item( + TY; + [ WithAlpha ]; + [ WithoutAlpha ]; + )] +#[cfg(feature = "compatibility")] +impl<'a, D: HasDisplayHandle, W: HasWindowHandle> ops::DerefMut for Buffer<'a, D, W, TY> { #[inline] fn deref_mut(&mut self) -> &mut [u32] { self.buffer_impl.pixels_mut() } } +#[duplicate_item( + TY; + [ WithAlpha ]; + [ WithoutAlpha ]; + )] +#[cfg(not(feature = "compatibility"))] +impl<'a, D: HasDisplayHandle, W: HasWindowHandle> ops::Deref for Buffer<'a, D, W, TY> { + type Target = [::Output]; + + #[inline] + fn deref(&self) -> &[::Output] { + // self.buffer_impl.pixels() + todo!() + } +} + +#[duplicate_item( + TY; + [ WithAlpha ]; + [ WithoutAlpha ]; + )] +#[cfg(not(feature = "compatibility"))] +impl<'a, D: HasDisplayHandle, W: HasWindowHandle> ops::DerefMut for Buffer<'a, D, W,TY> { + // type Target = [crate::RGBX]; + #[inline] + fn deref_mut(&mut self) -> &mut [::Output] { + // self.buffer_impl.pixels_mut() + self.buffer_impl.pixels_rgb_mut() + } +} + + /// There is no display handle. #[derive(Debug)] #[allow(dead_code)] @@ -330,7 +451,7 @@ fn __assert_send() { is_send::>(); is_sync::>(); is_send::>(); - is_send::>(); + is_send::>(); /// ```compile_fail /// use softbuffer::Surface; From b8f4fdaf3b670b883741b1fb38c6dc2c41401477 Mon Sep 17 00:00:00 2001 From: Jaysonmaw <55055180+jaysonmaw@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:25:13 -0500 Subject: [PATCH 2/5] removed #[duplicate_item] in favor of generics true 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 --- Cargo.toml | 9 +- benches/buffer_mut.rs | 2 +- examples/winit_tiny_skia.rs | 121 ++++++++++++++++++++ src/backend_dispatch.rs | 132 +++++++++------------- src/backend_interface.rs | 99 ++++++++++++++++- src/backends/cg.rs | 153 ++++---------------------- src/backends/win32.rs | 58 ++-------- src/formats.rs | 56 ++++++++++ src/lib.rs | 213 ++++++++++++++++++++++-------------- 9 files changed, 496 insertions(+), 347 deletions(-) create mode 100644 examples/winit_tiny_skia.rs create mode 100644 src/formats.rs diff --git a/Cargo.toml b/Cargo.toml index e8783dda..3f241d63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,10 +18,10 @@ 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 = [] @@ -29,11 +29,11 @@ compatibility = [] 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 } @@ -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" diff --git a/benches/buffer_mut.rs b/benches/buffer_mut.rs index f69f906f..fb3c92fa 100644 --- a/benches/buffer_mut.rs +++ b/benches/buffer_mut.rs @@ -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); } }); diff --git a/examples/winit_tiny_skia.rs b/examples/winit_tiny_skia.rs new file mode 100644 index 00000000..916c8b94 --- /dev/null +++ b/examples/winit_tiny_skia.rs @@ -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); +} diff --git a/src/backend_dispatch.rs b/src/backend_dispatch.rs index 7a1de501..53fd8770 100644 --- a/src/backend_dispatch.rs +++ b/src/backend_dispatch.rs @@ -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> => $( @@ -33,58 +32,9 @@ macro_rules! make_enum { )* } - pub(crate) enum BufferDispatch<'a, $dgen, $wgen, $alpha> { - $( - $(#[$attr])* - $name($buffer_inner), - )* - } - - impl ContextDispatch { - pub fn variant_name(&self) -> &'static str { - match self { - $( - $(#[$attr])* - Self::$name(_) => stringify!($name), - )* - } - } - } - - impl ContextInterface for ContextDispatch { - fn new(mut display: D) -> Result> - where - D: Sized, - { - $( - $(#[$attr])* - match <$context_inner as ContextInterface>::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 SurfaceInterface for SurfaceDispatch{ + impl SurfaceInterface for SurfaceDispatch{ type Context = ContextDispatch; - 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> where @@ -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 for BufferDispatch<'a, D, W, A> { #[inline] fn pixels(&self) -> &[u32] { match self { @@ -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 { $( @@ -205,12 +170,44 @@ macro_rules! make_dispatch { } } } + + impl ContextDispatch { + pub fn variant_name(&self) -> &'static str { + match self { + $( + $(#[$attr])* + Self::$name(_) => stringify!($name), + )* + } + } + } + + impl ContextInterface for ContextDispatch { + fn new(mut display: D) -> Result> + where + D: Sized, + { + $( + $(#[$attr])* + match <$context_inner as ContextInterface>::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! { => #[cfg(x11_platform)] X11(Arc>, backends::x11::X11Impl, backends::x11::BufferImpl<'a, D, W>), @@ -226,27 +223,4 @@ make_enum!{ Web(backends::web::WebDisplayImpl, backends::web::WebImpl, backends::web::BufferImpl<'a, D, W>), #[cfg(target_os = "redox")] Orbital(D, backends::orbital::OrbitalImpl, backends::orbital::BufferImpl<'a, D, W>), -} - -#[duplicate_item( - TY; - [ WithAlpha ]; - [ WithoutAlpha ]; - )] -make_dispatch! { - => - #[cfg(x11_platform)] - X11(Arc>, backends::x11::X11Impl, backends::x11::BufferImpl<'a, D, W>), - #[cfg(wayland_platform)] - Wayland(Arc>, backends::wayland::WaylandImpl, backends::wayland::BufferImpl<'a, D, W>), - #[cfg(kms_platform)] - Kms(Arc>, backends::kms::KmsImpl, backends::kms::BufferImpl<'a, D, W>), - #[cfg(target_os = "windows")] - Win32(D, backends::win32::Win32Impl, backends::win32::BufferImpl<'a, D, W, TY>), - #[cfg(target_vendor = "apple")] - CoreGraphics(D, backends::cg::CGImpl, backends::cg::BufferImpl<'a, D, W, TY>), - #[cfg(target_arch = "wasm32")] - Web(backends::web::WebDisplayImpl, backends::web::WebImpl, backends::web::BufferImpl<'a, D, W>), - #[cfg(target_os = "redox")] - Orbital(D, backends::orbital::OrbitalImpl, backends::orbital::BufferImpl<'a, D, W>), } \ No newline at end of file diff --git a/src/backend_interface.rs b/src/backend_interface.rs index 3bdd70eb..de6b5a1e 100644 --- a/src/backend_interface.rs +++ b/src/backend_interface.rs @@ -1,6 +1,6 @@ //! Interface implemented by backends -use crate::{BufferReturn, InitError, Rect, SoftBufferError}; +use crate::{formats::RGBFormat, BufferReturn, InitError, Rect, SoftBufferError}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use std::{fmt::Debug, num::NonZeroU32}; @@ -40,16 +40,19 @@ pub(crate) trait SurfaceInterface { - #[deprecated = "Left for backwards compatibility. Will panic in the future. Switch to using the pixels_rgb or pixels_rgba methods for better cross platform portability"] + // #[deprecated = "Left for backwards compatibility. Will panic in the future. Switch to using the pixels_rgb or pixels_rgba methods for better cross platform portability"] fn pixels(&self) -> &[u32]; - #[deprecated = "Left for backwards compatibility. Will panic in the future. Switch to using the pixels_rgb_mut or pixels_rgba_mut methods for better cross platform portability"] + // #[deprecated = "Left for backwards compatibility. Will panic in the future. Switch to using the pixels_rgb_mut or pixels_rgba_mut methods for better cross platform portability"] fn pixels_mut(&mut self) -> &mut [u32]; + fn pixels_rgb(&self) -> &[::Output]; fn pixels_rgb_mut(&mut self) -> &mut[::Output]; fn age(&self) -> u8; fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError>; fn present(self) -> Result<(), SoftBufferError>; } + + macro_rules! define_rgbx_little_endian { ( $( @@ -60,6 +63,7 @@ macro_rules! define_rgbx_little_endian { $( $(#[$attr])* #[repr(C)] + #[derive(Copy,Clone)] pub struct RGBX{ $forth_vis $forth: u8, $third_vis $third: u8, @@ -80,6 +84,7 @@ macro_rules! define_rgba_little_endian { $( $(#[$attr])* #[repr(C)] + #[derive(Copy,Clone)] pub struct RGBA{ $forth_vis $forth: u8, $third_vis $third: u8, @@ -138,7 +143,7 @@ impl RGBX{ if r > MAX_U8 || g > MAX_U8 || b > MAX_U8{ Err(SoftBufferError::PrimitiveOutsideOfU8Range) }else{ - Ok(Self { r: r.as_(), g: g.as_(), b: b.as_(), x: 255 }) + Ok(Self { r: r.as_(), g: g.as_(), b: b.as_(), x: 0 }) } } @@ -152,6 +157,14 @@ impl RGBX{ { Self { r: r.as_(), g: g.as_(), b: b.as_(), x: 255 } } + + // pub fn new_from_u32(u32: u32) -> Self{ + // todo!() + // } + + // pub fn as_u32(&self) -> &u32{ + // unsafe{std::mem::transmute(self)} + // } } impl RGBA{ @@ -183,3 +196,81 @@ impl RGBA{ Self { r: r.as_(), g: g.as_(), b: b.as_(), a: a.as_() } } } + +//TODO, change this to be a different impl based on platform +impl RGBFormat for RGBA{ + fn to_rgba_format(self) -> crate::formats::RGBA { + crate::formats::RGBA{ + a: self.a, + b: self.b, + g: self.g, + r: self.r, + } + } + + fn from_rgba_format(rgba: crate::formats::RGBA) -> Self { + Self{ + b: rgba.b, + g: rgba.g, + r: rgba.r, + a: rgba.a, + } + } + + fn to_rgba_u8_format(self) -> crate::formats::RGBAu8 { + crate::formats::RGBAu8{ + a: self.a, + b: self.b, + g: self.g, + r: self.r, + } + } + + fn from_rgba_u8_format(rgba: crate::formats::RGBAu8) -> Self { + Self{ + b: rgba.b, + g: rgba.g, + r: rgba.r, + a: rgba.a, + } + } + +} + +impl RGBFormat for RGBX{ + fn to_rgba_format(self) -> crate::formats::RGBA { + crate::formats::RGBA{ + a: self.x, + b: self.b, + g: self.g, + r: self.r, + } + } + + fn from_rgba_format(rgba: crate::formats::RGBA) -> Self { + Self{ + b: rgba.b, + g: rgba.g, + r: rgba.r, + x: rgba.a, + } + } + + fn to_rgba_u8_format(self) -> crate::formats::RGBAu8 { + crate::formats::RGBAu8{ + a: self.x, + b: self.b, + g: self.g, + r: self.r, + } + } + + fn from_rgba_u8_format(rgba: crate::formats::RGBAu8) -> Self { + Self{ + b: rgba.b, + g: rgba.g, + r: rgba.r, + x: rgba.a, + } + } +} \ No newline at end of file diff --git a/src/backends/cg.rs b/src/backends/cg.rs index 36bfea41..e393d8c2 100644 --- a/src/backends/cg.rs +++ b/src/backends/cg.rs @@ -7,7 +7,6 @@ use core_graphics::base::{ use core_graphics::color_space::CGColorSpace; use core_graphics::data_provider::CGDataProvider; use core_graphics::image::CGImage; -use duplicate::duplicate_item; use foreign_types::ForeignType; use objc2::rc::Retained; use objc2::runtime::{AnyObject, Bool}; @@ -144,15 +143,10 @@ impl Drop for CGImpl { } } -#[duplicate_item( - TY; - [ WithAlpha ]; - [ WithoutAlpha ]; - )] -impl SurfaceInterface for CGImpl +impl SurfaceInterface for CGImpl { type Context = D; - type Buffer<'a> = BufferImpl<'a, D, W, TY> where Self: 'a; + type Buffer<'a> = BufferImpl<'a, D, W, A> where Self: 'a; fn new(window_src: W, _display: &D) -> Result> { // `NSView`/`UIView` can only be accessed from the main thread. @@ -273,121 +267,7 @@ impl SurfaceInterface for CGI } fn new_with_alpha(window_src: W, _display: &D) -> Result> { - // `NSView`/`UIView` can only be accessed from the main thread. - let _mtm = MainThreadMarker::new().ok_or(SoftBufferError::PlatformError( - Some("can only access Core Graphics handles from the main thread".to_string()), - None, - ))?; - - let root_layer = match window_src.window_handle()?.as_raw() { - RawWindowHandle::AppKit(handle) => { - // SAFETY: The pointer came from `WindowHandle`, which ensures that the - // `AppKitWindowHandle` contains a valid pointer to an `NSView`. - // - // We use `NSObject` here to avoid importing `objc2-app-kit`. - let view: &NSObject = unsafe { handle.ns_view.cast().as_ref() }; - - // Force the view to become layer backed - let _: () = unsafe { msg_send![view, setWantsLayer: Bool::YES] }; - - // SAFETY: `-[NSView layer]` returns an optional `CALayer` - let layer: Option> = unsafe { msg_send_id![view, layer] }; - layer.expect("failed making the view layer-backed") - } - RawWindowHandle::UiKit(handle) => { - // SAFETY: The pointer came from `WindowHandle`, which ensures that the - // `UiKitWindowHandle` contains a valid pointer to an `UIView`. - // - // We use `NSObject` here to avoid importing `objc2-ui-kit`. - let view: &NSObject = unsafe { handle.ui_view.cast().as_ref() }; - - // SAFETY: `-[UIView layer]` returns `CALayer` - let layer: Retained = unsafe { msg_send_id![view, layer] }; - layer - } - _ => return Err(InitError::Unsupported(window_src)), - }; - - // Add a sublayer, to avoid interfering with the root layer, since setting the contents of - // e.g. a view-controlled layer is brittle. - let layer = CALayer::new(); - root_layer.addSublayer(&layer); - - // Set the anchor point and geometry. Softbuffer's uses a coordinate system with the origin - // in the top-left corner. - // - // NOTE: This doesn't really matter unless we start modifying the `position` of our layer - // ourselves, but it's nice to have in place. - layer.setAnchorPoint(CGPoint::new(0.0, 0.0)); - layer.setGeometryFlipped(true); - - // Do not use auto-resizing mask. - // - // This is done to work around a bug in macOS 14 and above, where views using auto layout - // may end up setting fractional values as the bounds, and that in turn doesn't propagate - // properly through the auto-resizing mask and with contents gravity. - // - // Instead, we keep the bounds of the layer in sync with the root layer using an observer, - // see below. - // - // layer.setAutoresizingMask(kCALayerHeightSizable | kCALayerWidthSizable); - - let observer = Observer::new(&layer); - // Observe changes to the root layer's bounds and scale factor, and apply them to our layer. - // - // The previous implementation updated the scale factor inside `resize`, but this works - // poorly with transactions, and is generally inefficient. Instead, we update the scale - // factor only when needed because the super layer's scale factor changed. - // - // Note that inherent in this is an explicit design decision: We control the `bounds` and - // `contentsScale` of the layer directly, and instead let the `resize` call that the user - // controls only be the size of the underlying buffer. - // - // SAFETY: Observer deregistered in `Drop` before the observer object is deallocated. - unsafe { - root_layer.addObserver_forKeyPath_options_context( - &observer, - ns_string!("contentsScale"), - NSKeyValueObservingOptions::NSKeyValueObservingOptionNew - | NSKeyValueObservingOptions::NSKeyValueObservingOptionInitial, - ptr::null_mut(), - ); - root_layer.addObserver_forKeyPath_options_context( - &observer, - ns_string!("bounds"), - NSKeyValueObservingOptions::NSKeyValueObservingOptionNew - | NSKeyValueObservingOptions::NSKeyValueObservingOptionInitial, - ptr::null_mut(), - ); - } - - // Set the content so that it is placed in the top-left corner if it does not have the same - // size as the surface itself. - // - // TODO(madsmtm): Consider changing this to `kCAGravityResize` to stretch the content if - // resized to something that doesn't fit, see #177. - layer.setContentsGravity(unsafe { kCAGravityTopLeft }); - - // Initialize color space here, to reduce work later on. - let color_space = CGColorSpace::create_device_rgb(); - - // Grab initial width and height from the layer (whose properties have just been initialized - // by the observer using `NSKeyValueObservingOptionInitial`). - let size = layer.bounds().size; - let scale_factor = layer.contentsScale(); - let width = (size.width * scale_factor) as usize; - let height = (size.height * scale_factor) as usize; - - Ok(Self { - layer: SendCALayer(layer), - root_layer: SendCALayer(root_layer), - observer, - color_space: SendCGColorSpace(color_space), - width, - height, - _display: PhantomData, - window_handle: window_src, - }) + Self::new(window_src, _display) } #[inline] @@ -401,7 +281,7 @@ impl SurfaceInterface for CGI Ok(()) } - fn buffer_mut(&mut self) -> Result, SoftBufferError> { + fn buffer_mut(&mut self) -> Result, SoftBufferError> { Ok(BufferImpl { buffer: vec![0; self.width * self.height], imp: self, @@ -415,12 +295,8 @@ pub struct BufferImpl<'a, D, W, A> { buffer: Vec, _marker: PhantomData, } -#[duplicate_item( - TY platform_alpha_mode; - [ WithAlpha ] [kCGImageAlphaFirst]; - [ WithoutAlpha ] [kCGImageAlphaNoneSkipFirst]; - )] -impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl<'a, D, W, TY> { + +impl<'a, D: HasDisplayHandle, W: HasWindowHandle, A: BufferReturn> BufferInterface for BufferImpl<'a, D, W, A> { #[inline] fn pixels(&self) -> &[u32] { &self.buffer @@ -431,8 +307,12 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for Buffer &mut self.buffer } - fn pixels_rgb_mut(&mut self) -> &mut[::Output] { - unsafe{std::mem::transmute::<& mut [u32],&mut [::Output]>(&mut self.buffer[..])} + fn pixels_rgb(&self) -> &[::Output] { + unsafe{std::mem::transmute::<&[u32],& [::Output]>(&self.buffer[..])} + } + + fn pixels_rgb_mut(&mut self) -> &mut[::Output] { + unsafe{std::mem::transmute::<& mut [u32],&mut [::Output]>(&mut self.buffer[..])} } fn age(&self) -> u8 { @@ -442,6 +322,13 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for Buffer fn present(self) -> Result<(), SoftBufferError> { let data_provider = CGDataProvider::from_buffer(Arc::new(Buffer(self.buffer))); + + let bitmap_mode = if A::ALPHA_MODE{ + kCGImageAlphaFirst + }else{ + kCGImageAlphaNoneSkipFirst + }; + let image = CGImage::new( self.imp.width, self.imp.height, @@ -449,7 +336,7 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for Buffer 32, self.imp.width * 4, &self.imp.color_space.0, - kCGBitmapByteOrder32Little | platform_alpha_mode, + kCGBitmapByteOrder32Little | bitmap_mode, &data_provider, false, kCGRenderingIntentDefault, diff --git a/src/backends/win32.rs b/src/backends/win32.rs index 0ff1ca80..b9c9f787 100644 --- a/src/backends/win32.rs +++ b/src/backends/win32.rs @@ -4,7 +4,6 @@ use crate::{backend_interface::*, BufferReturn, WithAlpha, WithoutAlpha}; use crate::{Rect, SoftBufferError}; -use duplicate::duplicate_item; use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawWindowHandle}; use std::io; @@ -307,14 +306,9 @@ impl Win32Impl { } } -#[duplicate_item( - TY internal_buffer_function; - [ WithAlpha ] [new_with_alpha]; - [ WithoutAlpha ] [new]; - )] -impl SurfaceInterface for Win32Impl { +impl SurfaceInterface for Win32Impl { type Context = D; - type Buffer<'a> = BufferImpl<'a, D, W, TY> where Self: 'a; + type Buffer<'a> = BufferImpl<'a, D, W, A> where Self: 'a; /// Create a new `Win32Impl` from a `Win32WindowHandle`. fn new(window: W, _display: &D) -> Result> { @@ -356,34 +350,7 @@ impl SurfaceInterface for Win W: Sized, Self: Sized, { - let raw = window.window_handle()?.as_raw(); - let handle = match raw { - RawWindowHandle::Win32(handle) => handle, - _ => return Err(crate::InitError::Unsupported(window)), - }; - - // Get the handle to the device context. - // SAFETY: We have confirmed that the window handle is valid. - let hwnd = handle.hwnd.get() as HWND; - let dc = Allocator::get().get_dc(hwnd); - - // GetDC returns null if there is a platform error. - if dc.is_null() { - return Err(SoftBufferError::PlatformError( - Some("Device Context is null".into()), - Some(Box::new(io::Error::last_os_error())), - ) - .into()); - } - - Ok(Self { - dc: dc.into(), - window: hwnd.into(), - buffer: None, - handle: window, - _display: PhantomData, - _marker: PhantomData, - }) + Self::new(window,context) } #[inline] @@ -405,12 +372,16 @@ impl SurfaceInterface for Win } } - self.buffer = Some(Buffer::internal_buffer_function(self.dc.0, width, height)); + if A::ALPHA_MODE{ + self.buffer = Some(Buffer::new_with_alpha(self.dc.0, width, height)); + }else{ + self.buffer = Some(Buffer::new(self.dc.0, width, height)); + }; Ok(()) } - fn buffer_mut(&mut self) -> Result, SoftBufferError> { + fn buffer_mut(&mut self) -> Result, SoftBufferError> { if self.buffer.is_none() { panic!("Must set size of surface before calling `buffer_mut()`"); } @@ -426,12 +397,7 @@ impl SurfaceInterface for Win pub struct BufferImpl<'a, D, W, A>(&'a mut Win32Impl); -#[duplicate_item( - TY; - [ WithAlpha ]; - [ WithoutAlpha ]; - )] -impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl<'a, D, W, TY> { +impl<'a, D: HasDisplayHandle, W: HasWindowHandle, A: BufferReturn> BufferInterface for BufferImpl<'a, D, W, A> { #[inline] fn pixels(&self) -> &[u32] { self.0.buffer.as_ref().unwrap().pixels() @@ -442,9 +408,9 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for Buffer self.0.buffer.as_mut().unwrap().pixels_mut() } - fn pixels_rgb_mut(&mut self) -> &mut [::Output] { + fn pixels_rgb_mut(&mut self) -> &mut [::Output] { unsafe { - std::mem::transmute::<&mut [u32], &mut [::Output]>( + std::mem::transmute::<&mut [u32], &mut [::Output]>( self.0.buffer.as_mut().unwrap().pixels_mut(), ) } diff --git a/src/formats.rs b/src/formats.rs new file mode 100644 index 00000000..b279d386 --- /dev/null +++ b/src/formats.rs @@ -0,0 +1,56 @@ +pub trait RGBFormat{ + fn to_rgba_format(self) -> crate::formats::RGBA; + fn from_rgba_format(rgba: crate::formats::RGBA) -> Self; + fn to_rgba_u8_format(self) -> crate::formats::RGBAu8; + fn from_rgba_u8_format(rgba: crate::formats::RGBAu8) -> Self; +} + +//When wanting the bytes in a specific order by u8, you no longer care about endianness +#[repr(C)] +#[derive(Copy, Clone)] +pub struct RGBAu8{ + pub r: u8, + pub g: u8, + pub b: u8, + pub a: u8, +} + +#[cfg(target_endian = "little")] +#[repr(C)] +#[derive(Copy, Clone)] +pub struct RGBA{ + pub a: u8, + pub b: u8, + pub g: u8, + pub r: u8, +} + +#[cfg(target_endian = "big")] +#[repr(C)] +#[derive(Copy, Clone)] +pub struct RGBA{ + pub r: u8, + pub g: u8, + pub b: u8, + pub a: u8, +} + +#[cfg(target_endian = "little")] +#[repr(C)] +#[derive(Copy, Clone)] +pub struct ARGB{ + pub b: u8, + pub g: u8, + pub r: u8, + pub a: u8, +} + +#[cfg(target_endian = "big")] +#[repr(C)] +#[derive(Copy, Clone)] +pub struct ARGB{ + pub a: u8, + pub r: u8, + pub g: u8, + pub b: u8, +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 3631f453..689b114b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,8 +10,10 @@ mod backend_dispatch; use backend_dispatch::*; mod backend_interface; use backend_interface::*; +use formats::RGBFormat; mod backends; mod error; +mod formats; mod util; use std::cell::Cell; @@ -20,12 +22,11 @@ use std::num::NonZeroU32; use std::ops; use std::sync::Arc; +pub use backend_interface::{RGBA, RGBX}; use error::InitError; pub use error::SoftBufferError; -pub use backend_interface::{RGBX,RGBA}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle}; -use duplicate::duplicate_item; #[cfg(target_arch = "wasm32")] pub use backends::web::SurfaceExtWeb; @@ -74,18 +75,21 @@ pub struct Rect { } pub trait BufferReturn { - type Output: Sized; + type Output: RGBFormat + Copy; + const ALPHA_MODE: bool; } -pub enum WithoutAlpha{} +pub enum WithoutAlpha {} -impl BufferReturn for WithoutAlpha{ +impl BufferReturn for WithoutAlpha { type Output = RGBX; + const ALPHA_MODE: bool = false; } -pub enum WithAlpha{} +pub enum WithAlpha {} - -impl BufferReturn for WithAlpha{ +impl BufferReturn for WithAlpha { type Output = RGBA; + + const ALPHA_MODE: bool = true; } /// A surface for drawing to a window with software buffers. @@ -97,11 +101,14 @@ pub struct Surface { impl Surface { /// Creates a new surface for the context for the provided window. - pub fn new(context: &Context, window: W) -> Result, SoftBufferError> { + pub fn new( + context: &Context, + window: W, + ) -> Result, SoftBufferError> { match SurfaceDispatch::new(window, &context.context_impl) { Ok(surface_dispatch) => Ok(Self { surface_impl: Box::new(surface_dispatch), - _marker: PhantomData + _marker: PhantomData, }), Err(InitError::Unsupported(window)) => { let raw = window.window_handle()?.as_raw(); @@ -116,13 +123,7 @@ impl Surface { } } -#[duplicate_item( - TY; - [ WithAlpha ]; - [ WithoutAlpha ]; - )] -impl Surface { - +impl Surface { /// Get a reference to the underlying window handle. pub fn window(&self) -> &W { self.surface_impl.window() @@ -159,7 +160,7 @@ impl Surface { /// - On DRM/KMS, there is no reliable and sound way to wait for the page flip to happen from within /// `softbuffer`. Therefore it is the responsibility of the user to wait for the page flip before /// sending another frame. - pub fn buffer_mut(&mut self) -> Result, SoftBufferError> { + pub fn buffer_mut(&mut self) -> Result, SoftBufferError> { Ok(Buffer { buffer_impl: self.surface_impl.buffer_mut()?, _marker: PhantomData, @@ -169,11 +170,14 @@ impl Surface { impl Surface { /// Creates a new surface for the context for the provided window. - pub fn new_with_alpha(context: &Context, window: W) -> Result, SoftBufferError> { + pub fn new_with_alpha( + context: &Context, + window: W, + ) -> Result, SoftBufferError> { match SurfaceDispatch::new_with_alpha(window, &context.context_impl) { Ok(surface_dispatch) => Ok(Self { surface_impl: Box::new(surface_dispatch), - _marker: PhantomData + _marker: PhantomData, }), Err(InitError::Unsupported(window)) => { let raw = window.window_handle()?.as_raw(); @@ -188,24 +192,16 @@ impl Surface { } } -#[duplicate_item( - TY; - [ WithAlpha ]; - [ WithoutAlpha ]; - )] -impl AsRef for Surface { +impl AsRef for Surface { #[inline] fn as_ref(&self) -> &W { self.window() } } -#[duplicate_item( - TY; - [ WithAlpha ]; - [ WithoutAlpha ]; - )] -impl HasWindowHandle for Surface { +impl HasWindowHandle + for Surface +{ #[inline] fn window_handle( &self, @@ -257,12 +253,7 @@ pub struct Buffer<'a, D, W, A> { _marker: PhantomData<(Arc, Cell<()>)>, } -#[duplicate_item( - TY; - [ WithAlpha ]; - [ WithoutAlpha ]; - )] -impl<'a, D: HasDisplayHandle, W: HasWindowHandle> Buffer<'a, D, W, TY> { +impl<'a, D: HasDisplayHandle, W: HasWindowHandle, A: BufferReturn> Buffer<'a, D, W, A> { /// Is age is the number of frames ago this buffer was last presented. So if the value is /// `1`, it is the same as the last frame, and if it is `2`, it is the same as the frame /// before that (for backends using double buffering). If the value is `0`, it is a new @@ -305,29 +296,103 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> Buffer<'a, D, W, TY> { } } -#[duplicate_item( - TY; - [ WithAlpha ]; - [ WithoutAlpha ]; - )] -impl<'a, D: HasDisplayHandle, W: HasWindowHandle> Buffer<'a, D, W, TY>{ - #[deprecated = "Left for backwards compatibility. Will panic in the future. Switch to using the pixels_rgb or pixels_rgba methods for better cross platform portability"] - pub fn pixels(&self)-> &[u32] { +macro_rules! cast_to_format_helper { + ($self:ident , $src:ident , $dst:ty, $func:ident , $to_func:ident , $from_func:ident) => { + { + let temp = $self.buffer_impl.pixels_mut(); + for element in temp.iter_mut() { + unsafe { + let temp_as_concrete_type = + std::mem::transmute::<&mut u32, &mut <$src as BufferReturn>::Output>(element); + let temp_as_destination_type = temp_as_concrete_type.$to_func(); + *element = std::mem::transmute(temp_as_destination_type); + } + } + $func(temp); + for element in temp.iter_mut() { + unsafe { + let temp_as_concrete_type = + std::mem::transmute::<&mut u32, &mut $dst>(element); + let temp_as_destination_type = + &mut <$src as BufferReturn>::Output::$from_func(*temp_as_concrete_type); + *element = *std::mem::transmute::<&mut <$src as BufferReturn>::Output, &mut u32>( + temp_as_destination_type, + ); + } + } + } + }; +} + +impl<'a, D: HasDisplayHandle, W: HasWindowHandle, A: BufferReturn> Buffer<'a, D, W, A> { + /// Gets a ```&[u32]``` of the buffer of pixels + /// The layout of the pixels is dependent on the platform that you are on + /// It is recommended to deref pixels to a ```&[RGBA]``` ```&[RGBX]``` struct as that will automatically handle the differences across platforms for free + /// If you need a ```&[u32]``` of a specific format, there are helper functions to get those, and conversions are automatic based on platform. + /// If using the format for your native platform, there is no cost. + pub fn pixels_platform_dependent(&self) -> &[u32] { self.buffer_impl.pixels() } - #[deprecated = "Left for backwards compatibility. Will panic in the future. Switch to using the pixels_rgb_mut or pixels_rgba_mut methods for better cross platform portability"] - pub fn pixels_mut(&mut self)-> &mut [u32] { + /// Gets a ```&mut [u32]``` of the buffer of pixels + /// The layout of the pixels is dependent on the platform that you are on + /// It is recommended to deref pixels to a ```&mut [RGBA]``` ```&mut [RGBX]``` struct as that will automatically handle the differences across platforms for free + /// If you need a ```&mut [u32]``` of a specific format, there are helper functions to get those, and conversions are automatic based on platform. + /// If using the format for your native platform, there is no cost. + pub fn pixels_platform_dependent_mut(&mut self) -> &mut [u32] { self.buffer_impl.pixels_mut() } + + /// Access the platform dependent representation of the pixel buffer. + /// Will return either ```&[RGBX]``` or ```&[RGBA]``` depending on if called on a surface with alpha enabled or not. + /// This is the generally recommended method of accessing the pixel buffer as it is a zero cost abstraction, that + /// automatically handles any platform dependent ordering of the r,g,b,a fields for you. + /// + /// Alternative to using Deref on buffer. + pub fn pixels_rgb(&mut self) -> &[::Output]{ + self.buffer_impl.pixels_rgb() + } + + /// Access the platform dependent representation of the pixel buffer. + /// Will return either ```&[RGBX]``` or ```&[RGBA]``` depending on if called on a surface with alpha enabled or not. + /// This is the generally recommended method of accessing the pixel buffer as it is a zero cost abstraction, that + /// automatically handles any platform dependent ordering of the r,g,b,a fields for you. + /// + /// Alternative to using Deref on buffer. + pub fn pixels_rgb_mut(&mut self) -> &mut[::Output]{ + self.buffer_impl.pixels_rgb_mut() + } + + /// Gives a ```&mut [u32]``` slice in the RGBA u32 format. + /// Endianness is adjusted based on platform automatically. + /// If using the format for your native platform, there is no cost. + /// Useful when using other crates that require a specific format. + /// + /// This takes a closure that gives you the required ```&mut [u32]```. + /// The closure is necessary because if conversion is required for your platform, we need to convert back to the platform native format before presenting to the buffer. + pub fn pixel_u32slice_rgba(&mut self, f: F) { + cast_to_format_helper!(self,A,formats::RGBA,f,to_rgba_format,from_rgba_format) + } + + /// Gives a ```&mut [u32]``` slice in the RGBA u8 format. + /// If using the format for your native platform, there is no cost. + /// Useful when using other crates that require a specific format. + /// + /// This takes a closure that gives you the required ```&mut [u32]```. + /// The closure is necessary because if conversion is required for your platform, we need to convert back to the platform native format before presenting to the buffer + pub fn pixel_u8_slice_rgba(&mut self, f: F) { + let wrapper = |x: &mut [u32]|{ + f(bytemuck::cast_slice_mut(x)) + }; + cast_to_format_helper!(self,A,formats::RGBAu8,wrapper,to_rgba_u8_format,from_rgba_u8_format) + } } -#[duplicate_item( - TY; - [ WithAlpha ]; - [ WithoutAlpha ]; - )] + + #[cfg(feature = "compatibility")] -impl<'a, D: HasDisplayHandle, W: HasWindowHandle> ops::Deref for Buffer<'a, D, W, TY> { +impl<'a, D: HasDisplayHandle, W: HasWindowHandle, A: BufferReturn> ops::Deref + for Buffer<'a, D, W, A> +{ type Target = [u32]; #[inline] @@ -336,51 +401,39 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> ops::Deref for Buffer<'a, D, W } } -#[duplicate_item( - TY; - [ WithAlpha ]; - [ WithoutAlpha ]; - )] #[cfg(feature = "compatibility")] -impl<'a, D: HasDisplayHandle, W: HasWindowHandle> ops::DerefMut for Buffer<'a, D, W, TY> { +impl<'a, D: HasDisplayHandle, W: HasWindowHandle, A: BufferReturn> ops::DerefMut + for Buffer<'a, D, W, A> +{ #[inline] fn deref_mut(&mut self) -> &mut [u32] { self.buffer_impl.pixels_mut() } } -#[duplicate_item( - TY; - [ WithAlpha ]; - [ WithoutAlpha ]; - )] #[cfg(not(feature = "compatibility"))] -impl<'a, D: HasDisplayHandle, W: HasWindowHandle> ops::Deref for Buffer<'a, D, W, TY> { - type Target = [::Output]; +impl<'a, D: HasDisplayHandle, W: HasWindowHandle, A: BufferReturn> ops::Deref + for Buffer<'a, D, W, A> +{ + type Target = [::Output]; #[inline] - fn deref(&self) -> &[::Output] { - // self.buffer_impl.pixels() - todo!() + fn deref(&self) -> &[::Output] { + self.buffer_impl.pixels_rgb() } } -#[duplicate_item( - TY; - [ WithAlpha ]; - [ WithoutAlpha ]; - )] #[cfg(not(feature = "compatibility"))] -impl<'a, D: HasDisplayHandle, W: HasWindowHandle> ops::DerefMut for Buffer<'a, D, W,TY> { +impl<'a, D: HasDisplayHandle, W: HasWindowHandle, A: BufferReturn> ops::DerefMut + for Buffer<'a, D, W, A> +{ // type Target = [crate::RGBX]; #[inline] - fn deref_mut(&mut self) -> &mut [::Output] { - // self.buffer_impl.pixels_mut() + fn deref_mut(&mut self) -> &mut [::Output] { self.buffer_impl.pixels_rgb_mut() } } - /// There is no display handle. #[derive(Debug)] #[allow(dead_code)] @@ -451,7 +504,7 @@ fn __assert_send() { is_send::>(); is_sync::>(); is_send::>(); - is_send::>(); + is_send::>(); /// ```compile_fail /// use softbuffer::Surface; From 459297f97b943b01a453f2144f40e479e42c9a2f Mon Sep 17 00:00:00 2001 From: jaysonmaw <55055180+jaysonmaw@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:57:47 -0500 Subject: [PATCH 3/5] forgot to update windows impl --- src/backends/win32.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/backends/win32.rs b/src/backends/win32.rs index b9c9f787..83d2ae68 100644 --- a/src/backends/win32.rs +++ b/src/backends/win32.rs @@ -408,6 +408,14 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle, A: BufferReturn> BufferInterfa self.0.buffer.as_mut().unwrap().pixels_mut() } + fn pixels_rgb(&self) -> &[::Output] { + unsafe { + std::mem::transmute::<&[u32], &[::Output]>( + self.0.buffer.as_ref().unwrap().pixels(), + ) + } + } + fn pixels_rgb_mut(&mut self) -> &mut [::Output] { unsafe { std::mem::transmute::<&mut [u32], &mut [::Output]>( From e15f9ad5ac24278fc5dc78baba82023a1e0955b3 Mon Sep 17 00:00:00 2001 From: Jaysonmaw <55055180+jaysonmaw@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:32:58 -0500 Subject: [PATCH 4/5] remove old TODO that no longer makes sense --- src/backend_interface.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backend_interface.rs b/src/backend_interface.rs index de6b5a1e..8d7573be 100644 --- a/src/backend_interface.rs +++ b/src/backend_interface.rs @@ -197,7 +197,6 @@ impl RGBA{ } } -//TODO, change this to be a different impl based on platform impl RGBFormat for RGBA{ fn to_rgba_format(self) -> crate::formats::RGBA { crate::formats::RGBA{ From 710d249674e261520608b95fb0ac816c5416d0b7 Mon Sep 17 00:00:00 2001 From: Jaysonmaw <55055180+jaysonmaw@users.noreply.github.com> Date: Wed, 11 Sep 2024 20:51:37 -0500 Subject: [PATCH 5/5] Adding an additional format that mirrors the windows/mac os format Added some documentation with examples to better show how different parts work together Added u32/8 conversions for formats and main RGBX/RGBA structs Refactored RGBFormat impl's into macro as it was a bunch of repeated code. --- src/backend_interface.rs | 196 ++++++++++++++++++++++++--------------- src/formats.rs | 77 +++++++++++++++ src/lib.rs | 2 +- 3 files changed, 198 insertions(+), 77 deletions(-) diff --git a/src/backend_interface.rs b/src/backend_interface.rs index 8d7573be..e954e106 100644 --- a/src/backend_interface.rs +++ b/src/backend_interface.rs @@ -64,6 +64,14 @@ macro_rules! define_rgbx_little_endian { $(#[$attr])* #[repr(C)] #[derive(Copy,Clone)] + /// If you want to modify an RGBX returned from a ```buffer[index]``` you can modify the r,g,b fields directly, as doing that is completely platform independent + /// # Example: + /// ```rust + /// let pixel = &mut buffer[index]; + /// pixel.r = 255; + /// pixel.g = 255; + /// pixel.b = 255; + /// ``` pub struct RGBX{ $forth_vis $forth: u8, $third_vis $third: u8, @@ -85,6 +93,15 @@ macro_rules! define_rgba_little_endian { $(#[$attr])* #[repr(C)] #[derive(Copy,Clone)] + /// If you want to modify an RGBA returned from a ```buffer[index]``` you can modify the r,g,b,a fields directly, as doing that is completely platform independent + /// # Example: + /// ```rust + /// let pixel = &mut buffer[index]; + /// pixel.r = 255; + /// pixel.g = 255; + /// pixel.b = 255; + /// pixel.a = 255; + /// ``` pub struct RGBA{ $forth_vis $forth: u8, $third_vis $third: u8, @@ -158,13 +175,32 @@ impl RGBX{ Self { r: r.as_(), g: g.as_(), b: b.as_(), x: 255 } } - // pub fn new_from_u32(u32: u32) -> Self{ - // todo!() - // } + /// Creates a RGBX from a u32 + /// It is not recommended to use this function as you need to ensure that the u32 matches the format expected by your target platform + /// + /// Instead it is better if you must create an RGBX from a u32, to instead use the ```softbuffer::formats::RGBFormat```, that way you can use a + /// specific format that is not platform dependent, and if using the correct format for your platform, this is a Zero Cost abstraction. + /// ```rust + /// RGBA::from_rgba_format(softbuffer::formats::RGBA::new_from_u32(u32_rgba)) + /// ``` + pub fn new_from_u32_platform_dependent(u32: u32) -> Self{ + unsafe{std::mem::transmute(u32)} + } - // pub fn as_u32(&self) -> &u32{ - // unsafe{std::mem::transmute(self)} - // } + /// Creates a u32 from a RGBX + /// It is not recommended to use this function as is will be in whatever format your platform uses + /// + /// If you want to modify an RGBX returned from a ```buffer[index]``` you can modify the r,g,b fields directly, as doing that is completely platform independent + /// # Example: + /// ```rust + /// let pixel = &mut buffer[index]; + /// pixel.r = 255; + /// pixel.g = 255; + /// pixel.b = 255; + /// ``` + pub fn as_u32(&self) -> &u32{ + unsafe{std::mem::transmute(self)} + } } impl RGBA{ @@ -195,81 +231,89 @@ impl RGBA{ { Self { r: r.as_(), g: g.as_(), b: b.as_(), a: a.as_() } } -} -impl RGBFormat for RGBA{ - fn to_rgba_format(self) -> crate::formats::RGBA { - crate::formats::RGBA{ - a: self.a, - b: self.b, - g: self.g, - r: self.r, - } - } - - fn from_rgba_format(rgba: crate::formats::RGBA) -> Self { - Self{ - b: rgba.b, - g: rgba.g, - r: rgba.r, - a: rgba.a, - } - } - - fn to_rgba_u8_format(self) -> crate::formats::RGBAu8 { - crate::formats::RGBAu8{ - a: self.a, - b: self.b, - g: self.g, - r: self.r, - } + /// Creates a RGBA from a u32 + /// It is not recommended to use this function as you need to ensure that the u32 matches the format expected by your target platform + /// + /// Instead it is better if you must create an RGBA from a u32, to instead use the ```softbuffer::formats::RGBFormat```, that way you can use a + /// specific format that is not platform dependent, and if using the correct format for your platform, this is a Zero Cost abstraction. + /// ```rust + /// RGBA::from_rgba_format(softbuffer::formats::RGBA::new_from_u32(u32_rgba)) + /// ``` + #[inline] + pub fn new_from_u32_platform_dependent(u32: u32) -> Self{ + unsafe{std::mem::transmute(u32)} } - - fn from_rgba_u8_format(rgba: crate::formats::RGBAu8) -> Self { - Self{ - b: rgba.b, - g: rgba.g, - r: rgba.r, - a: rgba.a, - } + + /// Creates a u32 from a RGBA + /// It is not recommended to use this function as is will be in whatever format your platform uses + /// + /// If you want to modify an RGBA returned from a ```buffer[index]``` you can modify the r,g,b fields directly, as doing that is completely platform independent + /// # Example: + /// ```rust + /// let pixel = &mut buffer[index]; + /// pixel.r = 255; + /// pixel.g = 255; + /// pixel.b = 255; + /// pixel.a = 255; + /// ``` + #[inline] + pub fn as_u32(&self) -> &u32{ + unsafe{std::mem::transmute(self)} } - } -impl RGBFormat for RGBX{ - fn to_rgba_format(self) -> crate::formats::RGBA { - crate::formats::RGBA{ - a: self.x, - b: self.b, - g: self.g, - r: self.r, - } - } - - fn from_rgba_format(rgba: crate::formats::RGBA) -> Self { - Self{ - b: rgba.b, - g: rgba.g, - r: rgba.r, - x: rgba.a, - } - } - - fn to_rgba_u8_format(self) -> crate::formats::RGBAu8 { - crate::formats::RGBAu8{ - a: self.x, - b: self.b, - g: self.g, - r: self.r, +macro_rules! impl_conversions { + ( + $( + $to_func: ident => $from_func: ident => $to_type:ident; + )* + ) => { + impl RGBFormat for RGBA{ + $( + fn $to_func(self) -> crate::formats::$to_type { + crate::formats::$to_type{ + a: self.a, + b: self.b, + g: self.g, + r: self.r, + } + } + fn $from_func(rgba: crate::formats::$to_type) -> Self { + Self{ + b: rgba.b, + g: rgba.g, + r: rgba.r, + a: rgba.a, + } + } + )* } - } - - fn from_rgba_u8_format(rgba: crate::formats::RGBAu8) -> Self { - Self{ - b: rgba.b, - g: rgba.g, - r: rgba.r, - x: rgba.a, + impl RGBFormat for RGBX{ + $( + fn $to_func(self) -> crate::formats::$to_type { + crate::formats::$to_type{ + a: self.x, + b: self.b, + g: self.g, + r: self.r, + } + } + fn $from_func(rgba: crate::formats::$to_type) -> Self { + Self{ + b: rgba.b, + g: rgba.g, + r: rgba.r, + x: rgba.a, + } + } + )* } - } + }; +} + +impl_conversions!{ + to_rgba_format => from_rgba_format => RGBA; + to_rgba_u8_format => from_rgba_u8_format => RGBAu8; + to_argb_format => from_argb_format => ARGB; } \ No newline at end of file diff --git a/src/formats.rs b/src/formats.rs index b279d386..4069b4c0 100644 --- a/src/formats.rs +++ b/src/formats.rs @@ -1,8 +1,27 @@ +/// Used for converting to and from the ```softbuffer::RGBX``` or ```softbuffer::RGBA``` +/// in their platform specific formats, to a specific format when needed +/// +/// Keep in mind that platform endianness still maters when creating these format values +/// as they are backed by a u32 where endianness matters. A workaround for this is to use the u32::from_be_bytes as shown in the example. +/// +/// If wanting a completely non endian format, use one of the u8 based formats. +/// +/// # Example: +/// ```rust +/// let red = 255; +/// let green = 0; +/// let blue = 255; +/// let alpha = 255; +/// let purple_u32_rgba = u32::from_be_bytes([red, green, blue, alpha]); //ensures is platform independent +/// RGBA::from_rgba_format(softbuffer::formats::RGBA::new_from_u32(purple_u32_rgba)); +/// ``` pub trait RGBFormat{ fn to_rgba_format(self) -> crate::formats::RGBA; fn from_rgba_format(rgba: crate::formats::RGBA) -> Self; fn to_rgba_u8_format(self) -> crate::formats::RGBAu8; fn from_rgba_u8_format(rgba: crate::formats::RGBAu8) -> Self; + fn to_argb_format(self) -> crate::formats::ARGB; + fn from_argb_format(rgba: crate::formats::ARGB) -> Self; } //When wanting the bytes in a specific order by u8, you no longer care about endianness @@ -15,6 +34,26 @@ pub struct RGBAu8{ pub a: u8, } +impl RGBAu8{ + /// Creates a ```softbuffer::formats::RGBAu8``` from a ```[u8;4]```. + /// + /// To get a useable softbuffer::RGBA import the ```softbuffer::formats::RGBFormat``` trait, and then you can call + /// softbuffer::RGBA::from_rgba_u8_format() + pub fn new_from_u8_array(slice: [u8;4])->Self{ + unsafe{ + std::mem::transmute(slice) + } + } + + /// Converts a ```softbuffer::formats::RGBAu8``` into a ```[u8;4]``` + pub fn as_u8_array(self) -> [u8;4]{ + unsafe{ + std::mem::transmute(self) + } + } +} + + #[cfg(target_endian = "little")] #[repr(C)] #[derive(Copy, Clone)] @@ -35,6 +74,25 @@ pub struct RGBA{ pub a: u8, } +impl RGBA { + /// Creates a ```softbuffer::formats::RGBA``` from a u32. + /// + /// To get a useable softbuffer::RGBA import the ```softbuffer::formats::RGBFormat``` trait, and then you can call + /// softbuffer::RGBA::from_rgba_format() + pub fn new_from_u32(u32: u32)->Self{ + unsafe{ + std::mem::transmute(u32) + } + } + + /// Converts a ```softbuffer::formats::RGBA``` into a u32 + pub fn as_u32(self) -> u32{ + unsafe{ + std::mem::transmute(self) + } + } +} + #[cfg(target_endian = "little")] #[repr(C)] #[derive(Copy, Clone)] @@ -53,4 +111,23 @@ pub struct ARGB{ pub r: u8, pub g: u8, pub b: u8, +} + +impl ARGB { + /// Creates a ```softbuffer::formats::ARGB``` from a u32. + /// + /// To get a useable softbuffer::RGBA import the ```softbuffer::formats::RGBFormat``` trait, and then you can call + /// softbuffer::RGBA::from_argb_format() + pub fn new_from_u32(u32: u32)->Self{ + unsafe{ + std::mem::transmute(u32) + } + } + + /// Converts a ```softbuffer::formats::ARGB``` into a u32 + pub fn as_u32(self) -> u32{ + unsafe{ + std::mem::transmute(self) + } + } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 689b114b..57170dfc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ use backend_interface::*; use formats::RGBFormat; mod backends; mod error; -mod formats; +pub mod formats; mod util; use std::cell::Cell;