diff --git a/Cargo.toml b/Cargo.toml index fdce0873..3f241d63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,20 +17,23 @@ name = "buffer_mut" harness = false [features] -default = ["kms", "x11", "x11-dlopen", "wayland", "wayland-dlopen"] -kms = ["bytemuck", "drm", "rustix"] +default = ["kms", "x11", "x11-dlopen", "wayland", "wayland-dlopen","compatibility"] +kms = ["drm", "rustix"] wayland = ["wayland-backend", "wayland-client", "wayland-sys", "memmap2", "rustix", "fastrand"] wayland-dlopen = ["wayland-sys/dlopen"] -x11 = ["as-raw-xcb-connection", "bytemuck", "fastrand", "rustix", "tiny-xlib", "x11rb"] +x11 = ["as-raw-xcb-connection", "fastrand", "rustix", "tiny-xlib", "x11rb"] x11-dlopen = ["tiny-xlib/dlopen", "x11rb/dl-libxcb"] +compatibility = [] [dependencies] log = "0.4.17" raw_window_handle = { package = "raw-window-handle", version = "0.6", features = ["std"] } +num = "0.4.3" +bytemuck = "1.12.3" [target.'cfg(all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox"))))'.dependencies] as-raw-xcb-connection = { version = "1.0.0", optional = true } -bytemuck = { version = "1.12.3", optional = true } +# bytemuck = { version = "1.12.3", optional = true } drm = { version = "0.12.0", default-features = false, optional = true } fastrand = { version = "2.0.0", optional = true } memmap2 = { version = "0.9.0", optional = true } @@ -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"] } @@ -88,6 +91,7 @@ colorous = "1.0.12" criterion = { version = "0.4.0", default-features = false, features = ["cargo_bench_support"] } web-time = "1.0.0" winit = "0.30.0" +tiny-skia = "0.11.4" [dev-dependencies.image] version = "0.25.0" diff --git a/benches/buffer_mut.rs b/benches/buffer_mut.rs index beaea990..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; + 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 208f82cd..53fd8770 100644 --- a/src/backend_dispatch.rs +++ b/src/backend_dispatch.rs @@ -1,6 +1,6 @@ //! Implements `buffer_interface::*` traits for enums dispatching to backends -use crate::{backend_interface::*, backends, InitError, Rect, SoftBufferError}; +use crate::{backend_interface::*, backends, InitError, Rect, SoftBufferError, BufferReturn, WithAlpha, WithoutAlpha}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use std::num::NonZeroU32; @@ -10,7 +10,7 @@ use std::sync::Arc; /// A macro for creating the enum used to statically dispatch to the platform-specific implementation. macro_rules! make_dispatch { ( - <$dgen: ident, $wgen: ident> => + <$dgen: ident, $wgen: ident, $alpha: ident> => $( $(#[$attr:meta])* $name: ident @@ -24,48 +24,17 @@ 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)) - } - } - #[allow(clippy::large_enum_variant)] // it's boxed anyways - pub(crate) enum SurfaceDispatch<$dgen, $wgen> { + pub(crate) enum SurfaceDispatch<$dgen, $wgen, $alpha> { $( $(#[$attr])* $name($surface_inner), )* } - impl SurfaceInterface for SurfaceDispatch { + impl SurfaceInterface for SurfaceDispatch{ type Context = ContextDispatch; - type Buffer<'a> = BufferDispatch<'a, D, W> where Self: 'a; + type Buffer<'a> = BufferDispatch<'a, D, W, A> where Self: 'a; fn new(window: W, display: &Self::Context) -> Result> where @@ -79,6 +48,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 +78,7 @@ macro_rules! make_dispatch { } } - fn buffer_mut(&mut self) -> Result, SoftBufferError> { + fn buffer_mut(&mut self) -> Result, SoftBufferError> { match self { $( $(#[$attr])* @@ -116,14 +97,14 @@ macro_rules! make_dispatch { } } - pub(crate) enum BufferDispatch<'a, $dgen, $wgen> { + pub(crate) enum BufferDispatch<'a, $dgen, $wgen, $alpha> { $( $(#[$attr])* $name($buffer_inner), )* } - impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferDispatch<'a, D, W> { + impl<'a, D: HasDisplayHandle, W: HasWindowHandle,A: BufferReturn> BufferInterface for BufferDispatch<'a, D, W, A> { #[inline] fn pixels(&self) -> &[u32] { match self { @@ -144,6 +125,24 @@ macro_rules! make_dispatch { } } + fn pixels_rgb(&self) -> &[<$alpha as BufferReturn>::Output]{ + match self { + $( + $(#[$attr])* + Self::$name(inner) => inner.pixels_rgb(), + )* + } + } + + fn pixels_rgb_mut(&mut self) -> &mut[<$alpha as BufferReturn>::Output]{ + match self { + $( + $(#[$attr])* + Self::$name(inner) => inner.pixels_rgb_mut(), + )* + } + } + fn age(&self) -> u8 { match self { $( @@ -171,13 +170,45 @@ 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_dispatch! { - => + => #[cfg(x11_platform)] X11(Arc>, backends::x11::X11Impl, backends::x11::BufferImpl<'a, D, W>), #[cfg(wayland_platform)] @@ -185,11 +216,11 @@ 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>), -} +} \ No newline at end of file diff --git a/src/backend_interface.rs b/src/backend_interface.rs index 13e3555c..e954e106 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::{formats::RGBFormat, 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,281 @@ 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(&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 { + ( + $( + $(#[$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)] + #[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, + $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)] + #[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, + $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: 0 }) + } + } + + /// 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 } + } + + /// 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)} + } + + /// 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{ + #[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_() } + } + + /// 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)} + } + + /// 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)} + } +} + +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, + } + } + )* + } + 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/backends/cg.rs b/src/backends/cg.rs index ba957d85..e393d8c2 100644 --- a/src/backends/cg.rs +++ b/src/backends/cg.rs @@ -1,8 +1,8 @@ -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; @@ -19,6 +19,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 +114,7 @@ impl Observer { } } -pub struct CGImpl { +pub struct CGImpl { /// Our layer. layer: SendCALayer, /// The layer that our layer was created from. @@ -127,10 +128,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 +143,10 @@ impl Drop for CGImpl { } } -impl SurfaceInterface for CGImpl { +impl SurfaceInterface for CGImpl +{ type Context = D; - type Buffer<'a> = BufferImpl<'a, D, W> 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. @@ -264,6 +266,10 @@ impl SurfaceInterface for CGImpl< }) } + fn new_with_alpha(window_src: W, _display: &D) -> Result> { + Self::new(window_src, _display) + } + #[inline] fn window(&self) -> &W { &self.window_handle @@ -275,20 +281,22 @@ 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> { +impl<'a, D: HasDisplayHandle, W: HasWindowHandle, A: BufferReturn> BufferInterface for BufferImpl<'a, D, W, A> { #[inline] fn pixels(&self) -> &[u32] { &self.buffer @@ -299,6 +307,14 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl &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 { 0 } @@ -306,6 +322,13 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl 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, @@ -313,7 +336,7 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl 32, self.imp.width * 4, &self.imp.color_space.0, - kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst, + kCGBitmapByteOrder32Little | bitmap_mode, &data_provider, false, kCGRenderingIntentDefault, diff --git a/src/backends/win32.rs b/src/backends/win32.rs index 084ad47b..83d2ae68 100644 --- a/src/backends/win32.rs +++ b/src/backends/win32.rs @@ -2,7 +2,7 @@ //! //! 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 raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawWindowHandle}; @@ -16,7 +16,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 +114,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 +227,7 @@ impl Buffer { } /// The handle to a window for software buffering. -pub struct Win32Impl { +pub struct Win32Impl { /// The window handle. window: OnlyUsedFromOrigin, @@ -154,9 +246,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 +263,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 +306,9 @@ impl Win32Impl { } } -impl SurfaceInterface for Win32Impl { +impl SurfaceInterface for Win32Impl { type Context = D; - type Buffer<'a> = BufferImpl<'a, D, W> 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> { @@ -238,9 +338,21 @@ 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, + { + Self::new(window,context) + } + #[inline] fn window(&self) -> &W { &self.handle @@ -260,12 +372,16 @@ impl SurfaceInterface for Win32Im } } - self.buffer = Some(Buffer::new(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()`"); } @@ -279,9 +395,9 @@ 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> { +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() @@ -292,6 +408,22 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl 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]>( + 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/formats.rs b/src/formats.rs new file mode 100644 index 00000000..4069b4c0 --- /dev/null +++ b/src/formats.rs @@ -0,0 +1,133 @@ +/// 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 +#[repr(C)] +#[derive(Copy, Clone)] +pub struct RGBAu8{ + pub r: u8, + pub g: u8, + pub b: u8, + 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)] +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, +} + +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)] +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, +} + +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 d19eafa7..57170dfc 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; +pub mod formats; mod util; use std::cell::Cell; @@ -20,6 +22,7 @@ use std::num::NonZeroU32; use std::ops; use std::sync::Arc; +pub use backend_interface::{RGBA, RGBX}; use error::InitError; pub use error::SoftBufferError; @@ -71,16 +74,37 @@ pub struct Rect { pub height: NonZeroU32, } +pub trait BufferReturn { + type Output: RGBFormat + Copy; + const ALPHA_MODE: bool; +} +pub enum WithoutAlpha {} + +impl BufferReturn for WithoutAlpha { + type Output = RGBX; + const ALPHA_MODE: bool = false; +} +pub enum WithAlpha {} + +impl BufferReturn for WithAlpha { + type Output = RGBA; + + const ALPHA_MODE: bool = true; +} + /// 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), @@ -97,7 +121,9 @@ impl Surface { Err(InitError::Failure(f)) => Err(f), } } +} +impl Surface { /// Get a reference to the underlying window handle. pub fn window(&self) -> &W { self.surface_impl.window() @@ -134,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, @@ -142,14 +168,40 @@ 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), + } + } +} + +impl AsRef for Surface { #[inline] fn as_ref(&self) -> &W { self.window() } } -impl HasWindowHandle for Surface { +impl HasWindowHandle + for Surface +{ #[inline] fn window_handle( &self, @@ -196,12 +248,12 @@ 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> { +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 @@ -244,7 +296,103 @@ impl<'a, D: HasDisplayHandle, W: HasWindowHandle> Buffer<'a, D, W> { } } -impl<'a, D: HasDisplayHandle, W: HasWindowHandle> ops::Deref for Buffer<'a, D, W> { +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() + } + /// 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) + } +} + + + +#[cfg(feature = "compatibility")] +impl<'a, D: HasDisplayHandle, W: HasWindowHandle, A: BufferReturn> ops::Deref + for Buffer<'a, D, W, A> +{ type Target = [u32]; #[inline] @@ -253,13 +401,39 @@ 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> { +#[cfg(feature = "compatibility")] +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() } } +#[cfg(not(feature = "compatibility"))] +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_rgb() + } +} + +#[cfg(not(feature = "compatibility"))] +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_rgb_mut() + } +} + /// There is no display handle. #[derive(Debug)] #[allow(dead_code)] @@ -330,7 +504,7 @@ fn __assert_send() { is_send::>(); is_sync::>(); is_send::>(); - is_send::>(); + is_send::>(); /// ```compile_fail /// use softbuffer::Surface;