From 0fa3b5446cbbaeb93744a122d70eef3f152ed98c Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 10 Apr 2023 15:12:56 -0700 Subject: [PATCH 1/5] WIP: Attempt to use `IOSurface` on macOS It looks like `BytesPerRow` (the stride) has some kind of alignment requirement (testing on M1), that doesn't let us just use the width. So for this to work, softbuffer needs to expose the stride, and the user needs to deal with that... This doesn't seem to be showing the right thing even when width matches stride. Not sure why. --- Cargo.toml | 2 + src/cg/buffer.rs | 93 ++++++++++++++++++++++++++++++++++++++++ src/{cg.rs => cg/mod.rs} | 43 ++++++------------- 3 files changed, 107 insertions(+), 31 deletions(-) create mode 100644 src/cg/buffer.rs rename src/{cg.rs => cg/mod.rs} (75%) diff --git a/Cargo.toml b/Cargo.toml index c6da900a..38a77a0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,8 +47,10 @@ features = ["Win32_Graphics_Gdi", "Win32_UI_WindowsAndMessaging", "Win32_Foundat [target.'cfg(target_os = "macos")'.dependencies] bytemuck = { version = "1.12.3", features = ["extern_crate_alloc"] } cocoa = "0.24.0" +core-foundation = "0.9.3" core-graphics = "0.22.3" foreign-types = "0.3.0" +io-surface = "0.15.1" objc = "0.2.7" [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/src/cg/buffer.rs b/src/cg/buffer.rs new file mode 100644 index 00000000..4949bdd3 --- /dev/null +++ b/src/cg/buffer.rs @@ -0,0 +1,93 @@ +use core_foundation::{ + base::TCFType, dictionary::CFDictionary, number::CFNumber, string::CFString, +}; +use io_surface::{ + kIOSurfaceBytesPerElement, kIOSurfaceBytesPerRow, kIOSurfaceHeight, kIOSurfacePixelFormat, + kIOSurfaceWidth, IOSurface, IOSurfaceRef, +}; +use std::{ffi::c_int, slice}; + +#[link(name = "IOSurface", kind = "framework")] +extern "C" { + fn IOSurfaceGetBaseAddress(buffer: IOSurfaceRef) -> *mut u8; + fn IOSurfaceGetBytesPerRow(buffer: IOSurfaceRef) -> usize; + fn IOSurfaceLock(buffer: IOSurfaceRef, options: u32, seed: *mut u32) -> c_int; + fn IOSurfaceUnlock(buffer: IOSurfaceRef, options: u32, seed: *mut u32) -> c_int; +} + +pub struct Buffer { + io_surface: IOSurface, + ptr: *mut u8, + pixels: usize, +} + +impl Buffer { + pub fn new(width: i32, height: i32) -> Self { + let properties = unsafe { + CFDictionary::from_CFType_pairs(&[ + ( + CFString::wrap_under_get_rule(kIOSurfaceWidth), + CFNumber::from(width).as_CFType(), + ), + ( + CFString::wrap_under_get_rule(kIOSurfaceHeight), + CFNumber::from(height).as_CFType(), + ), + ( + CFString::wrap_under_get_rule(kIOSurfaceBytesPerElement), + CFNumber::from(4).as_CFType(), + ), + // TODO: Can we always use stride = width? Is it efficient? + /* + ( + CFString::wrap_under_get_rule(kIOSurfaceBytesPerRow), + CFNumber::from(width).as_CFType(), + ), + */ + ( + CFString::wrap_under_get_rule(kIOSurfacePixelFormat), + CFNumber::from(i32::from_be_bytes([b'B', b'G', b'R', b'A'])).as_CFType(), + ), + ]) + }; + let io_surface = io_surface::new(&properties); + let ptr = unsafe { IOSurfaceGetBaseAddress(io_surface.obj) }; + dbg!(width); + dbg!(unsafe { IOSurfaceGetBytesPerRow(io_surface.obj) } / 4); + let pixels = width as usize * height as usize; + Self { + io_surface, + ptr, + pixels, + } + } + + pub fn as_ptr(&self) -> IOSurfaceRef { + self.io_surface.obj + } + + pub unsafe fn lock(&mut self) { + let mut seed = 0; + unsafe { + IOSurfaceLock(self.io_surface.obj, 0, &mut seed); + } + } + + pub unsafe fn unlock(&mut self) { + let mut seed = 0; + unsafe { + IOSurfaceUnlock(self.io_surface.obj, 0, &mut seed); + } + } + + // TODO: We can assume alignment, right? + #[inline] + pub unsafe fn pixels_ref(&self) -> &[u32] { + unsafe { slice::from_raw_parts(self.ptr as *mut u32, self.pixels) } + } + + #[inline] + pub unsafe fn pixels_mut(&self) -> &mut [u32] { + unsafe { slice::from_raw_parts_mut(self.ptr as *mut u32, self.pixels) } + } +} diff --git a/src/cg.rs b/src/cg/mod.rs similarity index 75% rename from src/cg.rs rename to src/cg/mod.rs index fce628d6..2e455090 100644 --- a/src/cg.rs +++ b/src/cg/mod.rs @@ -13,15 +13,9 @@ use cocoa::quartzcore::{transaction, CALayer, ContentsGravity}; use foreign_types::ForeignType; use std::num::NonZeroU32; -use std::sync::Arc; -struct Buffer(Vec); - -impl AsRef<[u8]> for Buffer { - fn as_ref(&self) -> &[u8] { - bytemuck::cast_slice(&self.0) - } -} +mod buffer; +use buffer::Buffer; pub struct CGImpl { layer: CALayer, @@ -64,44 +58,30 @@ impl CGImpl { } pub fn buffer_mut(&mut self) -> Result { - Ok(BufferImpl { - buffer: vec![0; self.width as usize * self.height as usize], - imp: self, - }) + // TODO conversion + let mut buffer = Buffer::new(self.width as i32, self.height as i32); + unsafe { buffer.lock() }; + Ok(BufferImpl { buffer, imp: self }) } } pub struct BufferImpl<'a> { imp: &'a mut CGImpl, - buffer: Vec, + buffer: Buffer, } impl<'a> BufferImpl<'a> { #[inline] pub fn pixels(&self) -> &[u32] { - &self.buffer + unsafe { self.buffer.pixels_ref() } } #[inline] pub fn pixels_mut(&mut self) -> &mut [u32] { - &mut self.buffer + unsafe { self.buffer.pixels_mut() } } - pub fn present(self) -> Result<(), SoftBufferError> { - let data_provider = CGDataProvider::from_buffer(Arc::new(Buffer(self.buffer))); - let image = CGImage::new( - self.imp.width as usize, - self.imp.height as usize, - 8, - 32, - (self.imp.width * 4) as usize, - &self.imp.color_space, - kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst, - &data_provider, - false, - kCGRenderingIntentDefault, - ); - + pub fn present(mut self) -> Result<(), SoftBufferError> { // The CALayer has a default action associated with a change in the layer contents, causing // a quarter second fade transition to happen every time a new buffer is applied. This can // be mitigated by wrapping the operation in a transaction and disabling all actions. @@ -109,10 +89,11 @@ impl<'a> BufferImpl<'a> { transaction::set_disable_actions(true); unsafe { + self.buffer.unlock(); self.imp .layer .set_contents_scale(self.imp.window.backingScaleFactor()); - self.imp.layer.set_contents(image.as_ptr() as id); + self.imp.layer.set_contents(self.buffer.as_ptr() as id); }; transaction::commit(); From 8c1ee39210278325d78a1518f02d0b408c8c4822 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 10 Apr 2023 17:18:17 -0700 Subject: [PATCH 2/5] Make winit example work with IOSurface, using alpha 255, stride --- examples/animation.rs | 3 ++- examples/winit.rs | 17 +++++++++-------- src/cg/buffer.rs | 35 +++++++++++++++++------------------ src/cg/mod.rs | 5 +++++ src/lib.rs | 15 +++++++++++++++ 5 files changed, 48 insertions(+), 27 deletions(-) diff --git a/examples/animation.rs b/examples/animation.rs index f729a395..20746ab6 100644 --- a/examples/animation.rs +++ b/examples/animation.rs @@ -29,6 +29,7 @@ fn main() { let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap(); let mut old_size = (0, 0); + // TODO: need to pre-render with right stride? let mut frames = pre_render_frames(0, 0); let start = Instant::now(); @@ -89,7 +90,7 @@ fn pre_render_frames(width: usize, height: usize) -> Vec> { let blue = ((((y - elapsed).cos() * 0.5 + 0.5) * 255.0).round() as u32).clamp(0, 255); - blue | (green << 8) | (red << 16) + blue | (green << 8) | (red << 16) | (255 << 24) }) .collect::>() }; diff --git a/examples/winit.rs b/examples/winit.rs index 935ef3f8..d145de49 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -42,14 +42,15 @@ fn main() { .unwrap(); let mut buffer = surface.buffer_mut().unwrap(); - for index in 0..(width * height) { - let y = index / width; - let x = index % width; - let red = x % 255; - let green = y % 255; - let blue = (x * y) % 255; - - buffer[index as usize] = blue | (green << 8) | (red << 16); + let stride = buffer.stride(); + for y in 0..height { + for x in 0..width { + let red = x % 255; + let green = y % 255; + let blue = (x * y) % 255; + let index = y as usize * stride + x as usize; + buffer[index] = blue | (green << 8) | (red << 16) | (255 << 24); + } } buffer.present().unwrap(); diff --git a/src/cg/buffer.rs b/src/cg/buffer.rs index 4949bdd3..85427ccb 100644 --- a/src/cg/buffer.rs +++ b/src/cg/buffer.rs @@ -1,5 +1,5 @@ use core_foundation::{ - base::TCFType, dictionary::CFDictionary, number::CFNumber, string::CFString, + base::TCFType, boolean::CFBoolean, dictionary::CFDictionary, number::CFNumber, string::CFString, }; use io_surface::{ kIOSurfaceBytesPerElement, kIOSurfaceBytesPerRow, kIOSurfaceHeight, kIOSurfacePixelFormat, @@ -17,8 +17,9 @@ extern "C" { pub struct Buffer { io_surface: IOSurface, - ptr: *mut u8, - pixels: usize, + ptr: *mut u32, + stride: usize, + len: usize, } impl Buffer { @@ -37,28 +38,21 @@ impl Buffer { CFString::wrap_under_get_rule(kIOSurfaceBytesPerElement), CFNumber::from(4).as_CFType(), ), - // TODO: Can we always use stride = width? Is it efficient? - /* - ( - CFString::wrap_under_get_rule(kIOSurfaceBytesPerRow), - CFNumber::from(width).as_CFType(), - ), - */ ( CFString::wrap_under_get_rule(kIOSurfacePixelFormat), - CFNumber::from(i32::from_be_bytes([b'B', b'G', b'R', b'A'])).as_CFType(), + CFNumber::from(i32::from_be_bytes(*b"BGRA")).as_CFType(), ), ]) }; let io_surface = io_surface::new(&properties); - let ptr = unsafe { IOSurfaceGetBaseAddress(io_surface.obj) }; - dbg!(width); - dbg!(unsafe { IOSurfaceGetBytesPerRow(io_surface.obj) } / 4); - let pixels = width as usize * height as usize; + let ptr = unsafe { IOSurfaceGetBaseAddress(io_surface.obj) } as *mut u32; + let stride = unsafe { IOSurfaceGetBytesPerRow(io_surface.obj) } / 4; + let len = stride * height as usize; Self { io_surface, ptr, - pixels, + stride, + len, } } @@ -66,6 +60,11 @@ impl Buffer { self.io_surface.obj } + #[inline] + pub fn stride(&self) -> usize { + self.stride + } + pub unsafe fn lock(&mut self) { let mut seed = 0; unsafe { @@ -83,11 +82,11 @@ impl Buffer { // TODO: We can assume alignment, right? #[inline] pub unsafe fn pixels_ref(&self) -> &[u32] { - unsafe { slice::from_raw_parts(self.ptr as *mut u32, self.pixels) } + unsafe { slice::from_raw_parts(self.ptr, self.len) } } #[inline] pub unsafe fn pixels_mut(&self) -> &mut [u32] { - unsafe { slice::from_raw_parts_mut(self.ptr as *mut u32, self.pixels) } + unsafe { slice::from_raw_parts_mut(self.ptr, self.len) } } } diff --git a/src/cg/mod.rs b/src/cg/mod.rs index 2e455090..9cc4afbb 100644 --- a/src/cg/mod.rs +++ b/src/cg/mod.rs @@ -81,6 +81,11 @@ impl<'a> BufferImpl<'a> { unsafe { self.buffer.pixels_mut() } } + #[inline] + pub fn stride(&self) -> usize { + self.buffer.stride() + } + pub fn present(mut self) -> Result<(), SoftBufferError> { // The CALayer has a default action associated with a change in the layer contents, causing // a quarter second fade transition to happen every time a new buffer is applied. This can diff --git a/src/lib.rs b/src/lib.rs index f7dadf63..5387ba9d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,6 +124,16 @@ macro_rules! make_dispatch { } } + #[inline] + pub fn stride(&self) -> usize { + match self { + $( + $(#[$attr])* + Self::$name(inner) => inner.stride(), + )* + } + } + pub fn present(self) -> Result<(), SoftBufferError> { match self { $( @@ -355,6 +365,11 @@ pub struct Buffer<'a> { } impl<'a> Buffer<'a> { + #[inline] + pub fn stride(&self) -> usize { + self.buffer_impl.stride() + } + /// Presents buffer to the window. /// /// # Platform dependent behavior From 095dba801ce81c4ae880483de9f3367b727b292b Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 11 Apr 2023 09:58:24 -0700 Subject: [PATCH 3/5] Copy to IOSurface --- Cargo.toml | 1 - examples/animation.rs | 3 +- examples/winit.rs | 17 ++++++------ src/cg/buffer.rs | 14 +++++----- src/cg/mod.rs | 64 +++++++++++++++++++++++++------------------ src/lib.rs | 15 ---------- 6 files changed, 53 insertions(+), 61 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 38a77a0b..73e596b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,6 @@ bytemuck = { version = "1.12.3", features = ["extern_crate_alloc"] } cocoa = "0.24.0" core-foundation = "0.9.3" core-graphics = "0.22.3" -foreign-types = "0.3.0" io-surface = "0.15.1" objc = "0.2.7" diff --git a/examples/animation.rs b/examples/animation.rs index 20746ab6..f729a395 100644 --- a/examples/animation.rs +++ b/examples/animation.rs @@ -29,7 +29,6 @@ fn main() { let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap(); let mut old_size = (0, 0); - // TODO: need to pre-render with right stride? let mut frames = pre_render_frames(0, 0); let start = Instant::now(); @@ -90,7 +89,7 @@ fn pre_render_frames(width: usize, height: usize) -> Vec> { let blue = ((((y - elapsed).cos() * 0.5 + 0.5) * 255.0).round() as u32).clamp(0, 255); - blue | (green << 8) | (red << 16) | (255 << 24) + blue | (green << 8) | (red << 16) }) .collect::>() }; diff --git a/examples/winit.rs b/examples/winit.rs index d145de49..935ef3f8 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -42,15 +42,14 @@ fn main() { .unwrap(); let mut buffer = surface.buffer_mut().unwrap(); - let stride = buffer.stride(); - for y in 0..height { - for x in 0..width { - let red = x % 255; - let green = y % 255; - let blue = (x * y) % 255; - let index = y as usize * stride + x as usize; - buffer[index] = blue | (green << 8) | (red << 16) | (255 << 24); - } + for index in 0..(width * height) { + let y = index / width; + let x = index % width; + let red = x % 255; + let green = y % 255; + let blue = (x * y) % 255; + + buffer[index as usize] = blue | (green << 8) | (red << 16); } buffer.present().unwrap(); diff --git a/src/cg/buffer.rs b/src/cg/buffer.rs index 85427ccb..da83408e 100644 --- a/src/cg/buffer.rs +++ b/src/cg/buffer.rs @@ -1,9 +1,9 @@ use core_foundation::{ - base::TCFType, boolean::CFBoolean, dictionary::CFDictionary, number::CFNumber, string::CFString, + base::TCFType, dictionary::CFDictionary, number::CFNumber, string::CFString, }; use io_surface::{ - kIOSurfaceBytesPerElement, kIOSurfaceBytesPerRow, kIOSurfaceHeight, kIOSurfacePixelFormat, - kIOSurfaceWidth, IOSurface, IOSurfaceRef, + kIOSurfaceBytesPerElement, kIOSurfaceHeight, kIOSurfacePixelFormat, kIOSurfaceWidth, IOSurface, + IOSurfaceRef, }; use std::{ffi::c_int, slice}; @@ -23,16 +23,16 @@ pub struct Buffer { } impl Buffer { - pub fn new(width: i32, height: i32) -> Self { + pub fn new(width: u32, height: u32) -> Self { let properties = unsafe { CFDictionary::from_CFType_pairs(&[ ( CFString::wrap_under_get_rule(kIOSurfaceWidth), - CFNumber::from(width).as_CFType(), + CFNumber::from(i64::from(width)).as_CFType(), ), ( CFString::wrap_under_get_rule(kIOSurfaceHeight), - CFNumber::from(height).as_CFType(), + CFNumber::from(i64::from(height)).as_CFType(), ), ( CFString::wrap_under_get_rule(kIOSurfaceBytesPerElement), @@ -79,7 +79,7 @@ impl Buffer { } } - // TODO: We can assume alignment, right? + #[allow(dead_code)] #[inline] pub unsafe fn pixels_ref(&self) -> &[u32] { unsafe { slice::from_raw_parts(self.ptr, self.len) } diff --git a/src/cg/mod.rs b/src/cg/mod.rs index 9cc4afbb..4dc4ea07 100644 --- a/src/cg/mod.rs +++ b/src/cg/mod.rs @@ -1,16 +1,9 @@ use crate::SoftBufferError; -use core_graphics::base::{ - kCGBitmapByteOrder32Little, kCGImageAlphaNoneSkipFirst, kCGRenderingIntentDefault, -}; -use core_graphics::color_space::CGColorSpace; -use core_graphics::data_provider::CGDataProvider; -use core_graphics::image::CGImage; use raw_window_handle::AppKitWindowHandle; use cocoa::appkit::{NSView, NSViewHeightSizable, NSViewWidthSizable, NSWindow}; use cocoa::base::{id, nil}; use cocoa::quartzcore::{transaction, CALayer, ContentsGravity}; -use foreign_types::ForeignType; use std::num::NonZeroU32; @@ -20,9 +13,10 @@ use buffer::Buffer; pub struct CGImpl { layer: CALayer, window: id, - color_space: CGColorSpace, width: u32, height: u32, + data: Vec, + buffer: Option, } impl CGImpl { @@ -41,64 +35,80 @@ impl CGImpl { view.addSubview_(subview); // retains subview (+1) = 2 let _: () = msg_send![subview, release]; // releases subview (-1) = 1 } - let color_space = CGColorSpace::create_device_rgb(); Ok(Self { layer, window, - color_space, width: 0, height: 0, + data: Vec::new(), + buffer: None, }) } pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { - self.width = width.get(); - self.height = height.get(); + let width = width.get(); + let height = height.get(); + if width != self.width || height != self.height { + self.width = width; + self.height = height; + self.data.resize(width as usize * height as usize, 0); + self.buffer = Some(Buffer::new(width, height)); + } Ok(()) } pub fn buffer_mut(&mut self) -> Result { - // TODO conversion - let mut buffer = Buffer::new(self.width as i32, self.height as i32); - unsafe { buffer.lock() }; - Ok(BufferImpl { buffer, imp: self }) + if self.buffer.is_none() { + panic!("Must set size of surface before calling `buffer_mut()`"); + } + + Ok(BufferImpl { imp: self }) } } pub struct BufferImpl<'a> { imp: &'a mut CGImpl, - buffer: Buffer, } impl<'a> BufferImpl<'a> { #[inline] pub fn pixels(&self) -> &[u32] { - unsafe { self.buffer.pixels_ref() } + &self.imp.data } #[inline] pub fn pixels_mut(&mut self) -> &mut [u32] { - unsafe { self.buffer.pixels_mut() } + &mut self.imp.data } - #[inline] - pub fn stride(&self) -> usize { - self.buffer.stride() - } - - pub fn present(mut self) -> Result<(), SoftBufferError> { + pub fn present(self) -> Result<(), SoftBufferError> { // The CALayer has a default action associated with a change in the layer contents, causing // a quarter second fade transition to happen every time a new buffer is applied. This can // be mitigated by wrapping the operation in a transaction and disabling all actions. transaction::begin(); transaction::set_disable_actions(true); + let buffer = self.imp.buffer.as_mut().unwrap(); unsafe { - self.buffer.unlock(); + // Copy pixels into `IOSurface` buffer, with right stride and + // alpha + buffer.lock(); + let stride = buffer.stride(); + let pixels = buffer.pixels_mut(); + let width = self.imp.width as usize; + for y in 0..self.imp.height as usize { + for x in 0..width { + // Set alpha to 255 + let value = self.imp.data[y * width + x] | (255 << 24); + pixels[y * stride + x] = value; + } + } + buffer.unlock(); + self.imp .layer .set_contents_scale(self.imp.window.backingScaleFactor()); - self.imp.layer.set_contents(self.buffer.as_ptr() as id); + self.imp.layer.set_contents(buffer.as_ptr() as id); }; transaction::commit(); diff --git a/src/lib.rs b/src/lib.rs index 5387ba9d..f7dadf63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,16 +124,6 @@ macro_rules! make_dispatch { } } - #[inline] - pub fn stride(&self) -> usize { - match self { - $( - $(#[$attr])* - Self::$name(inner) => inner.stride(), - )* - } - } - pub fn present(self) -> Result<(), SoftBufferError> { match self { $( @@ -365,11 +355,6 @@ pub struct Buffer<'a> { } impl<'a> Buffer<'a> { - #[inline] - pub fn stride(&self) -> usize { - self.buffer_impl.stride() - } - /// Presents buffer to the window. /// /// # Platform dependent behavior From 446e8ec2ddc93b92591eade91b6aa7bcc269f724 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 11 Apr 2023 10:00:14 -0700 Subject: [PATCH 4/5] fix --- src/cg/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cg/mod.rs b/src/cg/mod.rs index 4dc4ea07..9f832044 100644 --- a/src/cg/mod.rs +++ b/src/cg/mod.rs @@ -6,6 +6,7 @@ use cocoa::base::{id, nil}; use cocoa::quartzcore::{transaction, CALayer, ContentsGravity}; use std::num::NonZeroU32; +use std::ptr; mod buffer; use buffer::Buffer; @@ -108,6 +109,7 @@ impl<'a> BufferImpl<'a> { self.imp .layer .set_contents_scale(self.imp.window.backingScaleFactor()); + self.imp.layer.set_contents(ptr::null_mut()); self.imp.layer.set_contents(buffer.as_ptr() as id); }; From edc5c76180fd307f1225e66fb957cea5233f66e2 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 11 Apr 2023 12:20:48 -0700 Subject: [PATCH 5/5] examples: Avoid dividing index to get x, y; calculate index from x, y Division is more expensive than multiplication, and this doesn't seem to be optimize away, so iterating over `(0..height)` and `(0..width)` provides better performance. It's good to have the example show the best way to write code using softbuffer, and this helps when using examples as a benchmark. This will also be needed if Softbuffer supports formats where `stride` isn't just `width * pixel_size`, and needs alignment. --- examples/animation.rs | 9 +++++---- examples/rectangle.rs | 22 +++++++++++----------- examples/winit.rs | 16 ++++++++-------- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/examples/animation.rs b/examples/animation.rs index f729a395..933f3329 100644 --- a/examples/animation.rs +++ b/examples/animation.rs @@ -78,10 +78,11 @@ fn pre_render_frames(width: usize, height: usize) -> Vec> { let render = |frame_id| { let elapsed = ((frame_id as f64) / (60.0)) * 2.0 * PI; - (0..(width * height)) - .map(|index| { - let y = ((index / width) as f64) / (height as f64); - let x = ((index % width) as f64) / (width as f64); + let coords = (0..height).flat_map(|x| (0..width).map(move |y| (x, y))); + coords + .map(|(x, y)| { + let y = (y as f64) / (height as f64); + let x = (x as f64) / (width as f64); let red = ((((y + elapsed).sin() * 0.5 + 0.5) * 255.0).round() as u32).clamp(0, 255); let green = diff --git a/examples/rectangle.rs b/examples/rectangle.rs index 5443d3b9..4940ed3b 100644 --- a/examples/rectangle.rs +++ b/examples/rectangle.rs @@ -4,17 +4,17 @@ use winit::event_loop::{ControlFlow, EventLoop}; use winit::window::WindowBuilder; fn redraw(buffer: &mut [u32], width: usize, height: usize, flag: bool) { - for (index, color) in buffer.iter_mut().enumerate() { - let y = index / width; - let x = index % width; - - if flag && x >= 100 && x < width - 100 && y >= 100 && y < height - 100 { - *color = 0x00ffffff; - } else { - let red = (x & 0xff) ^ (y & 0xff); - let green = (x & 0x7f) ^ (y & 0x7f); - let blue = (x & 0x3f) ^ (y & 0x3f); - *color = (blue | (green << 8) | (red << 16)) as u32; + for y in 0..height { + for x in 0..width { + let value = if flag && x >= 100 && x < width - 100 && y >= 100 && y < height - 100 { + 0x00ffffff + } else { + let red = (x & 0xff) ^ (y & 0xff); + let green = (x & 0x7f) ^ (y & 0x7f); + let blue = (x & 0x3f) ^ (y & 0x3f); + (blue | (green << 8) | (red << 16)) as u32 + }; + buffer[y * width + x] = value; } } } diff --git a/examples/winit.rs b/examples/winit.rs index 935ef3f8..fe5a82b8 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -42,14 +42,14 @@ fn main() { .unwrap(); let mut buffer = surface.buffer_mut().unwrap(); - for index in 0..(width * height) { - let y = index / width; - let x = index % width; - let red = x % 255; - let green = y % 255; - let blue = (x * y) % 255; - - buffer[index as usize] = blue | (green << 8) | (red << 16); + for y in 0..height { + for x in 0..width { + let red = x % 255; + let green = y % 255; + let blue = (x * y) % 255; + let index = y as usize * width as usize + x as usize; + buffer[index] = blue | (green << 8) | (red << 16); + } } buffer.present().unwrap();