From 21f65b3b7c53d394900ef62531366c3b21911bcd Mon Sep 17 00:00:00 2001 From: cyrgani Date: Mon, 24 Jun 2024 13:16:05 +0200 Subject: [PATCH 01/25] add unsafe functions for modifying raw image data --- src/texture.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/texture.rs b/src/texture.rs index bfa8c940..3cc97684 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -170,6 +170,30 @@ impl Image { self.height as usize } + /// Allows changing the width of this image unsafely. + /// + /// # Safety + /// Increasing the width without properly filling the new pixels can cause Undefined Behaviour. + pub unsafe fn width_mut(&mut self) -> &mut u16 { + &mut self.width + } + + /// Allows changing the height of this image unsafely. + /// + /// # Safety + /// Increasing the height without properly filling the new pixels can cause Undefined Behaviour. + pub unsafe fn height_mut(&mut self) -> &mut u16 { + &mut self.height + } + + /// Allows changing the bytes of this image unsafely. + /// + /// # Safety + /// Removing bytes and not changing width and height accordingly can cause Undefined Behaviour. + pub unsafe fn bytes_mut(&mut self) -> &mut Vec { + &mut self.bytes + } + /// Returns this image's data as a slice of 4-byte arrays. pub fn get_image_data(&self) -> &[[u8; 4]] { use std::slice; From 52aecab9645bd45db12f5e3417a592c71b71f702 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Mon, 24 Jun 2024 13:17:55 +0200 Subject: [PATCH 02/25] add the safe empty image constructor from #637 --- src/texture.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/texture.rs b/src/texture.rs index 3cc97684..acfaef2f 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -98,6 +98,20 @@ impl Image { } } + /// Creates an empty Image from a width and height. + /// + /// ``` + /// # use macroquad::prelude::*; + /// let image = Image::new(32, 32); + /// ``` + pub fn new(width: u16, height: u16) -> Image { + Image { + width, + height, + bytes: vec![0; width as usize * height as usize * 4] + } + } + /// Creates an Image from a slice of bytes that contains an encoded image. /// /// If `format` is None, it will make an educated guess on the From 12b5f0c25b51063388e2c7c34b4de3f1181a7dda Mon Sep 17 00:00:00 2001 From: cyrgani Date: Mon, 24 Jun 2024 13:18:57 +0200 Subject: [PATCH 03/25] change the width and height return type from usize to u16 --- examples/life.rs | 4 ++-- src/texture.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/life.rs b/examples/life.rs index be147eb1..713b678c 100644 --- a/examples/life.rs +++ b/examples/life.rs @@ -26,8 +26,8 @@ async fn main() { loop { clear_background(WHITE); - let w = image.width(); - let h = image.height(); + let w = image.width() as usize; + let h = image.height() as usize; for y in 0..h as i32 { for x in 0..w as i32 { diff --git a/src/texture.rs b/src/texture.rs index acfaef2f..13d360ab 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -175,13 +175,13 @@ impl Image { } /// Returns the width of this image. - pub fn width(&self) -> usize { - self.width as usize + pub fn width(&self) -> u16 { + self.width } /// Returns the height of this image. - pub fn height(&self) -> usize { - self.height as usize + pub fn height(&self) -> u16 { + self.height } /// Allows changing the width of this image unsafely. From efd68941d5d253266dc4812e7b1cb0a7436f90d0 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Mon, 24 Jun 2024 13:19:32 +0200 Subject: [PATCH 04/25] make image fields pub(crate) instead of pub --- src/texture.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/texture.rs b/src/texture.rs index 13d360ab..83a406f5 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -68,9 +68,9 @@ impl TexturesContext { /// Image, data stored in CPU memory #[derive(Clone)] pub struct Image { - pub bytes: Vec, - pub width: u16, - pub height: u16, + pub(crate) bytes: Vec, + pub(crate) width: u16, + pub(crate) height: u16, } impl std::fmt::Debug for Image { From fd82749f290c8225d69dd71747e39872f8806318 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Mon, 24 Jun 2024 23:31:53 +0200 Subject: [PATCH 05/25] make image fields private, add unsafe from_raw_parts method --- src/text.rs | 15 +++++++-------- src/text/atlas.rs | 28 +++++++++++++++------------- src/texture.rs | 27 +++++++++++++++++++++++---- src/ui/style.rs | 24 ++++++++++++------------ 4 files changed, 57 insertions(+), 37 deletions(-) diff --git a/src/text.rs b/src/text.rs index 3f25161b..63b2cb71 100644 --- a/src/text.rs +++ b/src/text.rs @@ -90,17 +90,16 @@ impl Font { let (width, height) = (metrics.width as u16, metrics.height as u16); let sprite = self.atlas.lock().unwrap().new_unique_id(); - self.atlas.lock().unwrap().cache_sprite( - sprite, - Image { - bytes: bitmap + self.atlas.lock().unwrap().cache_sprite(sprite, unsafe { + Image::from_raw_parts( + width, + height, + bitmap .iter() .flat_map(|coverage| vec![255, 255, 255, *coverage]) .collect(), - width, - height, - }, - ); + ) + }); let advance = metrics.advance_width; let (offset_x, offset_y) = (metrics.xmin, metrics.ymin); diff --git a/src/text/atlas.rs b/src/text/atlas.rs index a58805c8..176637d1 100644 --- a/src/text/atlas.rs +++ b/src/text/atlas.rs @@ -42,7 +42,7 @@ impl Atlas { pub fn new(ctx: &mut dyn miniquad::RenderingBackend, filter: miniquad::FilterMode) -> Atlas { let image = Image::gen_image_color(512, 512, Color::new(0.0, 0.0, 0.0, 0.0)); - let texture = ctx.new_texture_from_rgba8(image.width, image.height, &image.bytes); + let texture = ctx.new_texture_from_rgba8(image.width(), image.height(), &image.bytes()); ctx.texture_set_filter( texture, miniquad::FilterMode::Nearest, @@ -79,11 +79,11 @@ impl Atlas { } pub fn width(&self) -> u16 { - self.image.width + self.image.width() } pub fn height(&self) -> u16 { - self.image.height + self.image.height() } pub fn texture(&mut self) -> miniquad::TextureId { @@ -91,18 +91,20 @@ impl Atlas { if self.dirty { self.dirty = false; let (texture_width, texture_height) = ctx.texture_size(self.texture); - if texture_width != self.image.width as _ || texture_height != self.image.height as _ { + if texture_width != self.image.width() as _ + || texture_height != self.image.height() as _ + { ctx.delete_texture(self.texture); self.texture = ctx.new_texture_from_rgba8( - self.image.width, - self.image.height, - &self.image.bytes[..], + self.image.width(), + self.image.height(), + &self.image.bytes()[..], ); ctx.texture_set_filter(self.texture, self.filter, miniquad::MipmapFilterMode::None); } - ctx.texture_update(self.texture, &self.image.bytes); + ctx.texture_update(self.texture, self.image.bytes()); } self.texture @@ -123,9 +125,9 @@ impl Atlas { } pub fn cache_sprite(&mut self, key: SpriteKey, sprite: Image) { - let (width, height) = (sprite.width as usize, sprite.height as usize); + let (width, height) = (sprite.width() as usize, sprite.height() as usize); - let x = if self.cursor_x + (width as u16) < self.image.width { + let x = if self.cursor_x + (width as u16) < self.image.width() { if height as u16 > self.max_line_height { self.max_line_height = height as u16; } @@ -141,7 +143,7 @@ impl Atlas { let y = self.cursor_y; // texture bounds exceeded - if y + sprite.height > self.image.height || x + sprite.width > self.image.width { + if y + sprite.height() > self.image.height() || x + sprite.width() > self.image.width() { // reset glyph cache state let sprites = self.sprites.drain().collect::>(); self.cursor_x = 0; @@ -154,8 +156,8 @@ impl Atlas { // note: if we tried to fit gigantic texture into a small atlas, // new_width will still be not enough. But its fine, it will // be regenerated on the recursion call. - let new_width = self.image.width * 2; - let new_height = self.image.height * 2; + let new_width = self.image.width() * 2; + let new_height = self.image.height() * 2; self.image = Image::gen_image_color(new_width, new_height, Color::new(0.0, 0.0, 0.0, 0.0)); diff --git a/src/texture.rs b/src/texture.rs index 83a406f5..e5d9d518 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -68,9 +68,9 @@ impl TexturesContext { /// Image, data stored in CPU memory #[derive(Clone)] pub struct Image { - pub(crate) bytes: Vec, - pub(crate) width: u16, - pub(crate) height: u16, + bytes: Vec, + width: u16, + height: u16, } impl std::fmt::Debug for Image { @@ -108,7 +108,7 @@ impl Image { Image { width, height, - bytes: vec![0; width as usize * height as usize * 4] + bytes: vec![0; width as usize * height as usize * 4], } } @@ -146,6 +146,20 @@ impl Image { }) } + /// Creates an image from the provided parts without checking their validity. + /// + /// # Safety + /// If the amount of bytes is too low for the width and height, + /// reading from these missing pixels is Undefined Behavior. + pub unsafe fn from_raw_parts(width: u16, height: u16, bytes: Vec) -> Image { + Image { + width, + height, + bytes, + } + } + + /// Creates an Image filled with the provided [Color]. pub fn gen_image_color(width: u16, height: u16, color: Color) -> Image { let mut bytes = vec![0; width as usize * height as usize * 4]; @@ -184,6 +198,11 @@ impl Image { self.height } + /// Returns the bytes of this image. + pub fn bytes(&self) -> &[u8] { + &self.bytes + } + /// Allows changing the width of this image unsafely. /// /// # Safety diff --git a/src/ui/style.rs b/src/ui/style.rs index 9d0cf2ca..d11a3bcd 100644 --- a/src/ui/style.rs +++ b/src/ui/style.rs @@ -396,10 +396,8 @@ impl Skin { .color_inactive(Color::from_rgba(238, 238, 238, 128)) .text_color(Color::from_rgba(0, 0, 0, 255)) .color(Color::from_rgba(220, 220, 220, 255)) - .background(Image { - width: 16, - height: 30, - bytes: include_bytes!("combobox.img").to_vec(), + .background(unsafe { + Image::from_raw_parts(16, 30, include_bytes!("combobox.img").to_vec()) }) .build(), tabbar_style: Style { @@ -416,14 +414,16 @@ impl Skin { .background_margin(RectOffset::new(1., 1., 1., 1.)) .color_inactive(Color::from_rgba(238, 238, 238, 128)) .text_color(Color::from_rgba(0, 0, 0, 255)) - .background(Image { - width: 3, - height: 3, - bytes: vec![ - 68, 68, 68, 255, 68, 68, 68, 255, 68, 68, 68, 255, 68, 68, 68, 255, 238, - 238, 238, 255, 68, 68, 68, 255, 68, 68, 68, 255, 68, 68, 68, 255, 68, 68, - 68, 255, - ], + .background(unsafe { + Image::from_raw_parts( + 3, + 3, + vec![ + 68, 68, 68, 255, 68, 68, 68, 255, 68, 68, 68, 255, 68, 68, 68, 255, + 238, 238, 238, 255, 68, 68, 68, 255, 68, 68, 68, 255, 68, 68, 68, 255, + 68, 68, 68, 255, + ], + ) }) .build(), window_titlebar_style: Style { From c757a13886c9cd131e0c47587e2796d2cdd1648f Mon Sep 17 00:00:00 2001 From: cyrgani Date: Tue, 25 Jun 2024 10:45:42 +0200 Subject: [PATCH 06/25] speedup Image::gen_image_color by factor of 2-3 --- src/texture.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/texture.rs b/src/texture.rs index e5d9d518..13037a45 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -124,7 +124,7 @@ impl Image { /// let icon = Image::from_file_with_format( /// include_bytes!("../examples/rust.png"), /// Some(ImageFormat::Png), - /// ); + /// ); /// ``` pub fn from_file_with_format( bytes: &[u8], @@ -147,9 +147,9 @@ impl Image { } /// Creates an image from the provided parts without checking their validity. - /// + /// /// # Safety - /// If the amount of bytes is too low for the width and height, + /// If the amount of bytes is too low for the width and height, /// reading from these missing pixels is Undefined Behavior. pub unsafe fn from_raw_parts(width: u16, height: u16, bytes: Vec) -> Image { Image { @@ -159,20 +159,18 @@ impl Image { } } - /// Creates an Image filled with the provided [Color]. pub fn gen_image_color(width: u16, height: u16, color: Color) -> Image { - let mut bytes = vec![0; width as usize * height as usize * 4]; - for i in 0..width as usize * height as usize { - bytes[i * 4 + 0] = (color.r * 255.) as u8; - bytes[i * 4 + 1] = (color.g * 255.) as u8; - bytes[i * 4 + 2] = (color.b * 255.) as u8; - bytes[i * 4 + 3] = (color.a * 255.) as u8; - } Image { width, height, - bytes, + bytes: [ + (color.r * 255.) as u8, + (color.g * 255.) as u8, + (color.b * 255.) as u8, + (color.a * 255.) as u8, + ] + .repeat(width as usize * height as usize), } } From 37a101d1cdc47150bb78664b97fbc51a71019913 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Tue, 25 Jun 2024 10:55:08 +0200 Subject: [PATCH 07/25] remove unnecessary constructor again --- src/texture.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/texture.rs b/src/texture.rs index 13037a45..71d9a3d9 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -98,20 +98,6 @@ impl Image { } } - /// Creates an empty Image from a width and height. - /// - /// ``` - /// # use macroquad::prelude::*; - /// let image = Image::new(32, 32); - /// ``` - pub fn new(width: u16, height: u16) -> Image { - Image { - width, - height, - bytes: vec![0; width as usize * height as usize * 4], - } - } - /// Creates an Image from a slice of bytes that contains an encoded image. /// /// If `format` is None, it will make an educated guess on the From 554bbbd25a80bde564fcbe7c6c4d7288d38cb8c7 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Tue, 25 Jun 2024 14:22:32 +0200 Subject: [PATCH 08/25] `From` instead of `Into` for `Color` --- src/color.rs | 38 +++++++++++++++++++------------------- src/texture.rs | 14 +++++++------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/color.rs b/src/color.rs index c4f12ed9..b3286343 100644 --- a/src/color.rs +++ b/src/color.rs @@ -44,31 +44,31 @@ fn color_from_bytes() { ); } -impl Into<[u8; 4]> for Color { - fn into(self) -> [u8; 4] { - [ - (self.r * 255.) as u8, - (self.g * 255.) as u8, - (self.b * 255.) as u8, - (self.a * 255.) as u8, - ] +impl From<[u8; 4]> for Color { + fn from(value: [u8; 4]) -> Color { + Color::new( + value[0] as f32 / 255., + value[1] as f32 / 255., + value[2] as f32 / 255., + value[3] as f32 / 255., + ) } } -impl Into for [u8; 4] { - fn into(self) -> Color { - Color::new( - self[0] as f32 / 255., - self[1] as f32 / 255., - self[2] as f32 / 255., - self[3] as f32 / 255., - ) +impl From for [u8; 4] { + fn from(value: Color) -> Self { + [ + (value.r * 255.) as u8, + (value.g * 255.) as u8, + (value.b * 255.) as u8, + (value.a * 255.) as u8, + ] } } -impl Into<[f32; 4]> for Color { - fn into(self) -> [f32; 4] { - [self.r, self.g, self.b, self.a] +impl From for [f32; 4] { + fn from(value: Color) -> [f32; 4] { + [value.r, value.g, value.b, value.a] } } diff --git a/src/texture.rs b/src/texture.rs index 71d9a3d9..d4a18bf8 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -261,7 +261,7 @@ impl Image { let mut n = 0; for y in y..y + height { for x in x..x + width { - bytes[n] = self.bytes[y * self.width as usize * 4 + x * 4 + 0]; + bytes[n] = self.bytes[y * self.width as usize * 4 + x * 4]; bytes[n + 1] = self.bytes[y * self.width as usize * 4 + x * 4 + 1]; bytes[n + 2] = self.bytes[y * self.width as usize * 4 + x * 4 + 2]; bytes[n + 3] = self.bytes[y * self.width as usize * 4 + x * 4 + 3]; @@ -284,19 +284,19 @@ impl Image { ); for i in 0..self.bytes.len() / 4 { - let c1: Color = Color { + let c1 = Color { r: self.bytes[i * 4] as f32 / 255., g: self.bytes[i * 4 + 1] as f32 / 255., b: self.bytes[i * 4 + 2] as f32 / 255., a: self.bytes[i * 4 + 3] as f32 / 255., }; - let c2: Color = Color { + let c2 = Color { r: other.bytes[i * 4] as f32 / 255., g: other.bytes[i * 4 + 1] as f32 / 255., b: other.bytes[i * 4 + 2] as f32 / 255., a: other.bytes[i * 4 + 3] as f32 / 255., }; - let new_color: Color = Color { + let new_color = Color { r: f32::min(c1.r * c1.a + c2.r * c2.a, 1.), g: f32::min(c1.g * c1.a + c2.g * c2.a, 1.), b: f32::min(c1.b * c1.a + c2.b * c2.a, 1.), @@ -320,19 +320,19 @@ impl Image { ); for i in 0..self.bytes.len() / 4 { - let c1: Color = Color { + let c1 = Color { r: self.bytes[i * 4] as f32 / 255., g: self.bytes[i * 4 + 1] as f32 / 255., b: self.bytes[i * 4 + 2] as f32 / 255., a: self.bytes[i * 4 + 3] as f32 / 255., }; - let c2: Color = Color { + let c2 = Color { r: other.bytes[i * 4] as f32 / 255., g: other.bytes[i * 4 + 1] as f32 / 255., b: other.bytes[i * 4 + 2] as f32 / 255., a: other.bytes[i * 4 + 3] as f32 / 255., }; - let new_color: Color = Color { + let new_color = Color { r: f32::min(c1.r * (1. - c2.a) + c2.r * c2.a, 1.), g: f32::min(c1.g * (1. - c2.a) + c2.g * c2.a, 1.), b: f32::min(c1.b * (1. - c2.a) + c2.b * c2.a, 1.), From 9a816d962e28b2bfe5998c1b4e934388f1277ee9 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Wed, 26 Jun 2024 22:16:55 +0200 Subject: [PATCH 09/25] breaking change: make blend and overlay assertions stricter --- src/texture.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/texture.rs b/src/texture.rs index d4a18bf8..43bd7844 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -275,13 +275,17 @@ impl Image { } } + fn assert_same_size(&self, other: &Image) { + assert!( + self.width == other.width && self.height == other.height, + "Images have different sizes!" + ); + } + /// Blends this image with another image (of identical dimensions) /// Inspired by OpenCV saturated blending pub fn blend(&mut self, other: &Image) { - assert!( - self.width as usize * self.height as usize - == other.width as usize * other.height as usize - ); + self.assert_same_size(other); for i in 0..self.bytes.len() / 4 { let c1 = Color { @@ -314,10 +318,7 @@ impl Image { /// overlaying a completely transparent image has no effect /// on the original image, though blending them would. pub fn overlay(&mut self, other: &Image) { - assert!( - self.width as usize * self.height as usize - == other.width as usize * other.height as usize - ); + self.assert_same_size(other); for i in 0..self.bytes.len() / 4 { let c1 = Color { From 0ad80cf59f88a185b595986da0d407119c6869ac Mon Sep 17 00:00:00 2001 From: cyrgani Date: Wed, 26 Jun 2024 22:20:15 +0200 Subject: [PATCH 10/25] breaking change: make get_pixel correct, change get_pixel and set_pixel argument types from u32 to u16 --- src/text/atlas.rs | 6 +++--- src/texture.rs | 15 +++++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/text/atlas.rs b/src/text/atlas.rs index 176637d1..5dd48bf2 100644 --- a/src/text/atlas.rs +++ b/src/text/atlas.rs @@ -176,9 +176,9 @@ impl Atlas { for j in 0..height { for i in 0..width { self.image.set_pixel( - x as u32 + i as u32, - y as u32 + j as u32, - sprite.get_pixel(i as u32, j as u32), + x + i as u16, + y + j as u16, + sprite.get_pixel(i as u16, j as u16), ); } } diff --git a/src/texture.rs b/src/texture.rs index 43bd7844..00a28621 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -236,18 +236,21 @@ impl Image { } /// Modifies a pixel [Color] in this image. - pub fn set_pixel(&mut self, x: u32, y: u32, color: Color) { - assert!(x < self.width as u32); - assert!(y < self.height as u32); + pub fn set_pixel(&mut self, x: u16, y: u16, color: Color) { + assert!(x < self.width); + assert!(y < self.height); let width = self.width; - self.get_image_data_mut()[(y * width as u32 + x) as usize] = color.into(); + self.get_image_data_mut()[(y * width + x) as usize] = color.into(); } /// Returns a pixel [Color] from this image. - pub fn get_pixel(&self, x: u32, y: u32) -> Color { - self.get_image_data()[(y * self.width as u32 + x) as usize].into() + pub fn get_pixel(&self, x: u16, y: u16) -> Color { + assert!(x < self.width); + assert!(y < self.height); + + self.get_image_data()[(y * self.width + x) as usize].into() } /// Returns an Image from a rect inside this image. From 3f75df8b584509a4b093199b689c17e0dac14a3f Mon Sep 17 00:00:00 2001 From: cyrgani Date: Thu, 27 Jun 2024 13:15:37 +0200 Subject: [PATCH 11/25] make from_raw_parts safe with assertions --- src/text.rs | 7 ++++--- src/texture.rs | 11 ++++++----- src/ui/style.rs | 28 ++++++++++++++-------------- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/text.rs b/src/text.rs index 63b2cb71..1ded13ad 100644 --- a/src/text.rs +++ b/src/text.rs @@ -90,7 +90,8 @@ impl Font { let (width, height) = (metrics.width as u16, metrics.height as u16); let sprite = self.atlas.lock().unwrap().new_unique_id(); - self.atlas.lock().unwrap().cache_sprite(sprite, unsafe { + self.atlas.lock().unwrap().cache_sprite( + sprite, Image::from_raw_parts( width, height, @@ -98,8 +99,8 @@ impl Font { .iter() .flat_map(|coverage| vec![255, 255, 255, *coverage]) .collect(), - ) - }); + ), + ); let advance = metrics.advance_width; let (offset_x, offset_y) = (metrics.xmin, metrics.ymin); diff --git a/src/texture.rs b/src/texture.rs index 00a28621..1dbeb2e3 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -132,12 +132,13 @@ impl Image { }) } - /// Creates an image from the provided parts without checking their validity. + /// Creates an image from the provided parts. /// - /// # Safety - /// If the amount of bytes is too low for the width and height, - /// reading from these missing pixels is Undefined Behavior. - pub unsafe fn from_raw_parts(width: u16, height: u16, bytes: Vec) -> Image { + /// # Panics + /// Panics if the width and height do not match the amount of bytes given. + pub fn from_raw_parts(width: u16, height: u16, bytes: Vec) -> Image { + assert_eq!(width as usize * height as usize * 4, bytes.len()); + Image { width, height, diff --git a/src/ui/style.rs b/src/ui/style.rs index d11a3bcd..a0eaae9f 100644 --- a/src/ui/style.rs +++ b/src/ui/style.rs @@ -396,9 +396,11 @@ impl Skin { .color_inactive(Color::from_rgba(238, 238, 238, 128)) .text_color(Color::from_rgba(0, 0, 0, 255)) .color(Color::from_rgba(220, 220, 220, 255)) - .background(unsafe { - Image::from_raw_parts(16, 30, include_bytes!("combobox.img").to_vec()) - }) + .background(Image::from_raw_parts( + 16, + 30, + include_bytes!("combobox.img").to_vec(), + )) .build(), tabbar_style: Style { margin: Some(RectOffset::new(2., 2., 2., 2.)), @@ -414,17 +416,15 @@ impl Skin { .background_margin(RectOffset::new(1., 1., 1., 1.)) .color_inactive(Color::from_rgba(238, 238, 238, 128)) .text_color(Color::from_rgba(0, 0, 0, 255)) - .background(unsafe { - Image::from_raw_parts( - 3, - 3, - vec![ - 68, 68, 68, 255, 68, 68, 68, 255, 68, 68, 68, 255, 68, 68, 68, 255, - 238, 238, 238, 255, 68, 68, 68, 255, 68, 68, 68, 255, 68, 68, 68, 255, - 68, 68, 68, 255, - ], - ) - }) + .background(Image::from_raw_parts( + 3, + 3, + vec![ + 68, 68, 68, 255, 68, 68, 68, 255, 68, 68, 68, 255, 68, 68, 68, 255, 238, + 238, 238, 255, 68, 68, 68, 255, 68, 68, 68, 255, 68, 68, 68, 255, 68, 68, + 68, 255, + ], + )) .build(), window_titlebar_style: Style { color: Color::from_rgba(68, 68, 68, 255), From 5dbfc9953458479f102e9176203eade012c5bfac Mon Sep 17 00:00:00 2001 From: cyrgani Date: Thu, 27 Jun 2024 13:18:01 +0200 Subject: [PATCH 12/25] const for applicable image methods --- src/texture.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/texture.rs b/src/texture.rs index 1dbeb2e3..b6352532 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -90,7 +90,7 @@ impl Image { /// # use macroquad::prelude::*; /// let image = Image::empty(); /// ``` - pub fn empty() -> Image { + pub const fn empty() -> Image { Image { width: 0, height: 0, @@ -174,12 +174,12 @@ impl Image { } /// Returns the width of this image. - pub fn width(&self) -> u16 { + pub const fn width(&self) -> u16 { self.width } /// Returns the height of this image. - pub fn height(&self) -> u16 { + pub const fn height(&self) -> u16 { self.height } From 9908a0f44150a007516bb809b988e9f5842a98e6 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Thu, 27 Jun 2024 13:19:30 +0200 Subject: [PATCH 13/25] more documentation, add `Image::pixel_amount` --- src/texture.rs | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/texture.rs b/src/texture.rs index b6352532..b0bcd984 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -162,8 +162,11 @@ impl Image { } /// Updates this image from a slice of [Color]s. + /// + /// # Panics + /// Panics if the amount of colors and the amount of image pixels differ. pub fn update(&mut self, colors: &[Color]) { - assert!(self.width as usize * self.height as usize == colors.len()); + assert_eq!(self.pixel_amount(), colors.len()); for i in 0..colors.len() { self.bytes[i * 4] = (colors[i].r * 255.) as u8; @@ -212,16 +215,16 @@ impl Image { &mut self.bytes } + /// Returns the amount of pixels this image has according to its dimensions. + pub const fn pixel_amount(&self) -> usize { + self.width as usize * self.height as usize + } + /// Returns this image's data as a slice of 4-byte arrays. pub fn get_image_data(&self) -> &[[u8; 4]] { use std::slice; - unsafe { - slice::from_raw_parts( - self.bytes.as_ptr() as *const [u8; 4], - self.width as usize * self.height as usize, - ) - } + unsafe { slice::from_raw_parts(self.bytes.as_ptr() as *const [u8; 4], self.pixel_amount()) } } /// Returns this image's data as a mutable slice of 4-byte arrays. @@ -229,14 +232,14 @@ impl Image { use std::slice; unsafe { - slice::from_raw_parts_mut( - self.bytes.as_mut_ptr() as *mut [u8; 4], - self.width as usize * self.height as usize, - ) + slice::from_raw_parts_mut(self.bytes.as_mut_ptr() as *mut [u8; 4], self.pixel_amount()) } } /// Modifies a pixel [Color] in this image. + /// + /// # Panics + /// Panics if the given pixel coordinates are not inside the image. pub fn set_pixel(&mut self, x: u16, y: u16, color: Color) { assert!(x < self.width); assert!(y < self.height); @@ -247,6 +250,9 @@ impl Image { } /// Returns a pixel [Color] from this image. + /// + /// # Panics + /// Panics if the given pixel coordinates are not inside the image. pub fn get_pixel(&self, x: u16, y: u16) -> Color { assert!(x < self.width); assert!(y < self.height); @@ -287,7 +293,7 @@ impl Image { } /// Blends this image with another image (of identical dimensions) - /// Inspired by OpenCV saturated blending + /// Inspired by OpenCV saturated blending pub fn blend(&mut self, other: &Image) { self.assert_same_size(other); @@ -354,21 +360,22 @@ impl Image { /// Saves this image as a PNG file. /// This method is not supported on web and will panic. pub fn export_png(&self, path: &str) { - let mut bytes = vec![0; self.width as usize * self.height as usize * 4]; + let mut bytes = vec![0; self.pixel_amount() * 4]; + let height = self.height as usize; + let width = self.width as usize; // flip the image before saving - for y in 0..self.height as usize { - for x in 0..self.width as usize * 4 { - bytes[y * self.width as usize * 4 + x] = - self.bytes[(self.height as usize - y - 1) * self.width as usize * 4 + x]; + for y in 0..height { + for x in 0..width * 4 { + bytes[y * width * 4 + x] = self.bytes[(height - y - 1) * width * 4 + x]; } } image::save_buffer( path, &bytes[..], - self.width as _, - self.height as _, + self.width as u32, + self.height as u32, image::ColorType::Rgba8, ) .unwrap(); From 16385719150a16128f86c2c75b382d6271da4398 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Sun, 30 Jun 2024 11:14:21 +0200 Subject: [PATCH 14/25] rename `Image::gen_image_color` to `Image::from_color` --- src/texture.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/texture.rs b/src/texture.rs index b0bcd984..b34ee5be 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -147,20 +147,21 @@ impl Image { } /// Creates an Image filled with the provided [Color]. - pub fn gen_image_color(width: u16, height: u16, color: Color) -> Image { + pub fn from_color(width: u16, height: u16, color: Color) -> Image { + let bytes: [u8; 4] = color.into(); Image { width, height, - bytes: [ - (color.r * 255.) as u8, - (color.g * 255.) as u8, - (color.b * 255.) as u8, - (color.a * 255.) as u8, - ] - .repeat(width as usize * height as usize), + bytes: bytes.repeat(width as usize * height as usize), } } + /// Creates an Image filled with the provided [Color]. + #[deprecated(since = "0.4.11", note = "use `Image::from_color` instead")] + pub fn gen_image_color(width: u16, height: u16, color: Color) -> Image { + Image::from_color(width, height, color) + } + /// Updates this image from a slice of [Color]s. /// /// # Panics From dfe6535cf6caf53c40cc1b092c70ed5bbe00d4ff Mon Sep 17 00:00:00 2001 From: cyrgani Date: Sun, 7 Jul 2024 22:48:37 +0200 Subject: [PATCH 15/25] increase `Image::sub_image` speed significantly --- src/texture.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/texture.rs b/src/texture.rs index b34ee5be..8d027e5a 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -265,18 +265,15 @@ impl Image { pub fn sub_image(&self, rect: Rect) -> Image { let width = rect.w as usize; let height = rect.h as usize; - let mut bytes = vec![0; width * height * 4]; - - let x = rect.x as usize; - let y = rect.y as usize; - let mut n = 0; - for y in y..y + height { - for x in x..x + width { - bytes[n] = self.bytes[y * self.width as usize * 4 + x * 4]; - bytes[n + 1] = self.bytes[y * self.width as usize * 4 + x * 4 + 1]; - bytes[n + 2] = self.bytes[y * self.width as usize * 4 + x * 4 + 2]; - bytes[n + 3] = self.bytes[y * self.width as usize * 4 + x * 4 + 3]; - n += 4; + let mut bytes = Vec::with_capacity(width * height * 4); + + let rect_x = rect.x as usize; + let rect_y = rect.y as usize; + + for y in rect_y..rect_y + height { + for x in rect_x..rect_x + width { + let b = y * self.width as usize * 4 + x * 4; + bytes.extend_from_slice(&self.bytes[b..b + 4]); } } Image { From e7101ed5e0bf061ad915c2eb2eaa3772f3d95f35 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Sun, 7 Jul 2024 22:55:54 +0200 Subject: [PATCH 16/25] rename usages of `Image::gen_image_color` to `Image::from_color`, fix examples using images --- examples/life.rs | 8 ++++---- examples/shadertoy.rs | 4 ++-- src/text/atlas.rs | 5 ++--- src/ui.rs | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/examples/life.rs b/examples/life.rs index 713b678c..23282be6 100644 --- a/examples/life.rs +++ b/examples/life.rs @@ -14,7 +14,7 @@ async fn main() { let mut cells = vec![CellState::Dead; w * h]; let mut buffer = vec![CellState::Dead; w * h]; - let mut image = Image::gen_image_color(w as u16, h as u16, WHITE); + let mut image = Image::from_color(w as u16, h as u16, WHITE); for cell in cells.iter_mut() { if rand::gen_range(0, 5) == 0 { @@ -75,9 +75,9 @@ async fn main() { cells[i] = buffer[i]; image.set_pixel( - (i % w) as u32, - (i / w) as u32, - match buffer[i as usize] { + (i % w) as u16, + (i / w) as u16, + match buffer[i] { CellState::Alive => BLACK, CellState::Dead => WHITE, }, diff --git a/examples/shadertoy.rs b/examples/shadertoy.rs index ed19fb33..0f64caef 100644 --- a/examples/shadertoy.rs +++ b/examples/shadertoy.rs @@ -28,7 +28,7 @@ impl Uniform { fn color_picker_texture(w: usize, h: usize) -> (Texture2D, Image) { let ratio = 1.0 / h as f32; - let mut image = Image::gen_image_color(w as u16, h as u16, WHITE); + let mut image = Image::from_color(w as u16, h as u16, WHITE); let image_data = image.get_image_data_mut(); for j in 0..h { @@ -301,7 +301,7 @@ async fn main() { let y = mouse.1 as i32 - (cursor.y as i32 + 20); let color = color_picker_image - .get_pixel(x.max(0).min(199) as u32, y.max(0).min(199) as u32); + .get_pixel(x.max(0).min(199) as u16, y.max(0).min(199) as u16); canvas.rect( Rect::new(cursor.x, cursor.y, 200.0, 18.0), diff --git a/src/text/atlas.rs b/src/text/atlas.rs index 5dd48bf2..912823f2 100644 --- a/src/text/atlas.rs +++ b/src/text/atlas.rs @@ -41,7 +41,7 @@ impl Atlas { const UNIQUENESS_OFFSET: u64 = 100000; pub fn new(ctx: &mut dyn miniquad::RenderingBackend, filter: miniquad::FilterMode) -> Atlas { - let image = Image::gen_image_color(512, 512, Color::new(0.0, 0.0, 0.0, 0.0)); + let image = Image::from_color(512, 512, Color::new(0.0, 0.0, 0.0, 0.0)); let texture = ctx.new_texture_from_rgba8(image.width(), image.height(), &image.bytes()); ctx.texture_set_filter( texture, @@ -159,8 +159,7 @@ impl Atlas { let new_width = self.image.width() * 2; let new_height = self.image.height() * 2; - self.image = - Image::gen_image_color(new_width, new_height, Color::new(0.0, 0.0, 0.0, 0.0)); + self.image = Image::from_color(new_width, new_height, Color::new(0.0, 0.0, 0.0, 0.0)); // recache all previously cached symbols for (key, sprite) in sprites { diff --git a/src/ui.rs b/src/ui.rs index 54b5b030..df9bb93d 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -664,7 +664,7 @@ impl Ui { atlas .lock() .unwrap() - .cache_sprite(SpriteKey::Id(0), Image::gen_image_color(1, 1, crate::WHITE)); + .cache_sprite(SpriteKey::Id(0), Image::from_color(1, 1, crate::WHITE)); let font = Arc::new(Mutex::new(font)); Ui { From f0af2072b3929632b358605db22ad881f34de431 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Fri, 12 Jul 2024 10:55:13 +0200 Subject: [PATCH 17/25] replace most usages of the fields within the module with function calls --- src/texture.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/texture.rs b/src/texture.rs index 8d027e5a..60670957 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -705,7 +705,7 @@ impl Texture2D { /// Creates a Texture2D from an [Image]. pub fn from_image(image: &Image) -> Texture2D { - Texture2D::from_rgba8(image.width, image.height, &image.bytes) + Texture2D::from_rgba8(image.width(), image.height(), image.bytes()) } /// Creates a Texture2D from a miniquad @@ -746,10 +746,10 @@ impl Texture2D { let ctx = get_quad_context(); let (width, height) = ctx.texture_size(self.raw_miniquad_id()); - assert_eq!(width, image.width as u32); - assert_eq!(height, image.height as u32); + assert_eq!(width, image.width() as u32); + assert_eq!(height, image.height() as u32); - ctx.texture_update(self.raw_miniquad_id(), &image.bytes); + ctx.texture_update(self.raw_miniquad_id(), image.bytes()); } // Updates the texture from an array of bytes. @@ -780,7 +780,7 @@ impl Texture2D { y_offset, width, height, - &image.bytes, + image.bytes(), ) } @@ -880,11 +880,7 @@ impl Texture2D { pub fn get_texture_data(&self) -> Image { let ctx = get_quad_context(); let (width, height) = ctx.texture_size(self.raw_miniquad_id()); - let mut image = Image { - width: width as _, - height: height as _, - bytes: vec![0; width as usize * height as usize * 4], - }; + let mut image = Image::from_color(width as u16, height as u16, crate::color::BLANK); ctx.texture_read_pixels(self.raw_miniquad_id(), &mut image.bytes); image } From dde84b64febeae1f68612b317e40ef6cace8c459 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Sat, 13 Jul 2024 22:30:34 +0200 Subject: [PATCH 18/25] rename `Image::from_color` to `Image::filled_with_color` for clarity --- examples/life.rs | 2 +- examples/shadertoy.rs | 2 +- src/text/atlas.rs | 5 +++-- src/texture.rs | 8 ++++---- src/ui.rs | 8 ++++---- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/examples/life.rs b/examples/life.rs index 23282be6..7b93bb29 100644 --- a/examples/life.rs +++ b/examples/life.rs @@ -14,7 +14,7 @@ async fn main() { let mut cells = vec![CellState::Dead; w * h]; let mut buffer = vec![CellState::Dead; w * h]; - let mut image = Image::from_color(w as u16, h as u16, WHITE); + let mut image = Image::filled_with_color(w as u16, h as u16, WHITE); for cell in cells.iter_mut() { if rand::gen_range(0, 5) == 0 { diff --git a/examples/shadertoy.rs b/examples/shadertoy.rs index 0f64caef..d3161c72 100644 --- a/examples/shadertoy.rs +++ b/examples/shadertoy.rs @@ -28,7 +28,7 @@ impl Uniform { fn color_picker_texture(w: usize, h: usize) -> (Texture2D, Image) { let ratio = 1.0 / h as f32; - let mut image = Image::from_color(w as u16, h as u16, WHITE); + let mut image = Image::filled_with_color(w as u16, h as u16, WHITE); let image_data = image.get_image_data_mut(); for j in 0..h { diff --git a/src/text/atlas.rs b/src/text/atlas.rs index 912823f2..bc6bb58d 100644 --- a/src/text/atlas.rs +++ b/src/text/atlas.rs @@ -41,7 +41,7 @@ impl Atlas { const UNIQUENESS_OFFSET: u64 = 100000; pub fn new(ctx: &mut dyn miniquad::RenderingBackend, filter: miniquad::FilterMode) -> Atlas { - let image = Image::from_color(512, 512, Color::new(0.0, 0.0, 0.0, 0.0)); + let image = Image::filled_with_color(512, 512, Color::new(0.0, 0.0, 0.0, 0.0)); let texture = ctx.new_texture_from_rgba8(image.width(), image.height(), &image.bytes()); ctx.texture_set_filter( texture, @@ -159,7 +159,8 @@ impl Atlas { let new_width = self.image.width() * 2; let new_height = self.image.height() * 2; - self.image = Image::from_color(new_width, new_height, Color::new(0.0, 0.0, 0.0, 0.0)); + self.image = + Image::filled_with_color(new_width, new_height, Color::new(0.0, 0.0, 0.0, 0.0)); // recache all previously cached symbols for (key, sprite) in sprites { diff --git a/src/texture.rs b/src/texture.rs index 60670957..d718a9d4 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -147,7 +147,7 @@ impl Image { } /// Creates an Image filled with the provided [Color]. - pub fn from_color(width: u16, height: u16, color: Color) -> Image { + pub fn filled_with_color(width: u16, height: u16, color: Color) -> Image { let bytes: [u8; 4] = color.into(); Image { width, @@ -157,9 +157,9 @@ impl Image { } /// Creates an Image filled with the provided [Color]. - #[deprecated(since = "0.4.11", note = "use `Image::from_color` instead")] + #[deprecated(since = "0.4.12", note = "use `Image::filled_with_color` instead")] pub fn gen_image_color(width: u16, height: u16, color: Color) -> Image { - Image::from_color(width, height, color) + Image::filled_with_color(width, height, color) } /// Updates this image from a slice of [Color]s. @@ -880,7 +880,7 @@ impl Texture2D { pub fn get_texture_data(&self) -> Image { let ctx = get_quad_context(); let (width, height) = ctx.texture_size(self.raw_miniquad_id()); - let mut image = Image::from_color(width as u16, height as u16, crate::color::BLANK); + let mut image = Image::filled_with_color(width as u16, height as u16, crate::color::BLANK); ctx.texture_read_pixels(self.raw_miniquad_id(), &mut image.bytes); image } diff --git a/src/ui.rs b/src/ui.rs index df9bb93d..bcb64d64 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -661,10 +661,10 @@ impl Ui { font.cache_glyph(character, 13); } - atlas - .lock() - .unwrap() - .cache_sprite(SpriteKey::Id(0), Image::from_color(1, 1, crate::WHITE)); + atlas.lock().unwrap().cache_sprite( + SpriteKey::Id(0), + Image::filled_with_color(1, 1, crate::WHITE), + ); let font = Arc::new(Mutex::new(font)); Ui { From 1b63ee795395d19243cbf9c5acaf17c18a26cde2 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Sat, 13 Jul 2024 22:34:15 +0200 Subject: [PATCH 19/25] add `Image::bytes_vec_mut`, make `Image::bytes_mut` safe --- src/texture.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/texture.rs b/src/texture.rs index d718a9d4..bf5a6277 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -187,11 +187,16 @@ impl Image { self.height } - /// Returns the bytes of this image. + /// Returns the bytes of this image as an immutable slice. pub fn bytes(&self) -> &[u8] { &self.bytes } + /// Returns the bytes of this image as a mutable slice. + pub fn bytes_mut(&mut self) -> &mut [u8] { + &mut self.bytes + } + /// Allows changing the width of this image unsafely. /// /// # Safety @@ -210,9 +215,12 @@ impl Image { /// Allows changing the bytes of this image unsafely. /// + /// If you do not intend to change the amount of the bytes, + /// use `Image::bytes_mut` instead, which is safe. + /// /// # Safety /// Removing bytes and not changing width and height accordingly can cause Undefined Behaviour. - pub unsafe fn bytes_mut(&mut self) -> &mut Vec { + pub unsafe fn bytes_vec_mut(&mut self) -> &mut Vec { &mut self.bytes } From 7c6726f0b4708a53d23a1a07dd36d2191cae34a4 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Sat, 13 Jul 2024 23:07:59 +0200 Subject: [PATCH 20/25] make image fields public again to restore compatibility, add deprecation warnings to fields --- src/texture.rs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/texture.rs b/src/texture.rs index bf5a6277..d61b8ec2 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -68,13 +68,28 @@ impl TexturesContext { /// Image, data stored in CPU memory #[derive(Clone)] pub struct Image { - bytes: Vec, - width: u16, - height: u16, + // FIXME: remove all the deprecation notes once the `Image` fields are private + #[deprecated( + since = "0.4.12", + note = "this will be made private, use `Image::bytes`, `Image::bytes_mut` or `Image::bytes_vec_mut` for reading and writing instead" + )] + pub bytes: Vec, + #[deprecated( + since = "0.4.12", + note = "this will be made private, use `Image::width` or `Image::width_mut` for reading and writing instead" + )] + pub width: u16, + #[deprecated( + since = "0.4.12", + note = "this will be made private, use `Image::height` or `Image::height_mut` for reading and writing instead" + )] + pub height: u16, } impl std::fmt::Debug for Image { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // FIXME: remove this once the `Image` fields are private + #[allow(deprecated)] f.debug_struct("Image") .field("width", &self.width) .field("height", &self.height) @@ -83,6 +98,8 @@ impl std::fmt::Debug for Image { } } +// FIXME: remove this once the `Image` fields are private +#[allow(deprecated)] impl Image { /// Creates an empty Image. /// @@ -889,7 +906,7 @@ impl Texture2D { let ctx = get_quad_context(); let (width, height) = ctx.texture_size(self.raw_miniquad_id()); let mut image = Image::filled_with_color(width as u16, height as u16, crate::color::BLANK); - ctx.texture_read_pixels(self.raw_miniquad_id(), &mut image.bytes); + ctx.texture_read_pixels(self.raw_miniquad_id(), image.bytes_mut()); image } } From c47c4119e0c077e68557e779dad9c11241e3e98d Mon Sep 17 00:00:00 2001 From: cyrgani Date: Sun, 14 Jul 2024 11:43:22 +0200 Subject: [PATCH 21/25] revert other breaking changes for now --- src/text/atlas.rs | 30 ++++++++++++++++++------------ src/texture.rs | 24 ++++++++++++++++-------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/text/atlas.rs b/src/text/atlas.rs index bc6bb58d..8fb7e72d 100644 --- a/src/text/atlas.rs +++ b/src/text/atlas.rs @@ -42,7 +42,8 @@ impl Atlas { pub fn new(ctx: &mut dyn miniquad::RenderingBackend, filter: miniquad::FilterMode) -> Atlas { let image = Image::filled_with_color(512, 512, Color::new(0.0, 0.0, 0.0, 0.0)); - let texture = ctx.new_texture_from_rgba8(image.width(), image.height(), &image.bytes()); + let texture = + ctx.new_texture_from_rgba8(image.width() as u16, image.height() as u16, &image.bytes()); ctx.texture_set_filter( texture, miniquad::FilterMode::Nearest, @@ -79,11 +80,11 @@ impl Atlas { } pub fn width(&self) -> u16 { - self.image.width() + self.image.width() as u16 } pub fn height(&self) -> u16 { - self.image.height() + self.image.height() as u16 } pub fn texture(&mut self) -> miniquad::TextureId { @@ -97,8 +98,8 @@ impl Atlas { ctx.delete_texture(self.texture); self.texture = ctx.new_texture_from_rgba8( - self.image.width(), - self.image.height(), + self.image.width() as u16, + self.image.height() as u16, &self.image.bytes()[..], ); ctx.texture_set_filter(self.texture, self.filter, miniquad::MipmapFilterMode::None); @@ -127,7 +128,7 @@ impl Atlas { pub fn cache_sprite(&mut self, key: SpriteKey, sprite: Image) { let (width, height) = (sprite.width() as usize, sprite.height() as usize); - let x = if self.cursor_x + (width as u16) < self.image.width() { + let x = if self.cursor_x + (width as u16) < self.image.width() as u16 { if height as u16 > self.max_line_height { self.max_line_height = height as u16; } @@ -143,7 +144,9 @@ impl Atlas { let y = self.cursor_y; // texture bounds exceeded - if y + sprite.height() > self.image.height() || x + sprite.width() > self.image.width() { + if y + sprite.height() as u16 > self.image.height() as u16 + || x + sprite.width() as u16 > self.image.width() as u16 + { // reset glyph cache state let sprites = self.sprites.drain().collect::>(); self.cursor_x = 0; @@ -159,8 +162,11 @@ impl Atlas { let new_width = self.image.width() * 2; let new_height = self.image.height() * 2; - self.image = - Image::filled_with_color(new_width, new_height, Color::new(0.0, 0.0, 0.0, 0.0)); + self.image = Image::filled_with_color( + new_width as u16, + new_height as u16, + Color::new(0.0, 0.0, 0.0, 0.0), + ); // recache all previously cached symbols for (key, sprite) in sprites { @@ -176,9 +182,9 @@ impl Atlas { for j in 0..height { for i in 0..width { self.image.set_pixel( - x + i as u16, - y + j as u16, - sprite.get_pixel(i as u16, j as u16), + x as u32 + i as u32, + y as u32 + j as u32, + sprite.get_pixel(i as u32, j as u32), ); } } diff --git a/src/texture.rs b/src/texture.rs index d61b8ec2..00ad4fd3 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -195,13 +195,15 @@ impl Image { } /// Returns the width of this image. - pub const fn width(&self) -> u16 { - self.width + // FIXME: change the argument type to u16 + pub const fn width(&self) -> usize { + self.width as usize } /// Returns the height of this image. - pub const fn height(&self) -> u16 { - self.height + // FIXME: change the argument type to u16 + pub const fn height(&self) -> usize { + self.height as usize } /// Returns the bytes of this image as an immutable slice. @@ -266,7 +268,10 @@ impl Image { /// /// # Panics /// Panics if the given pixel coordinates are not inside the image. - pub fn set_pixel(&mut self, x: u16, y: u16, color: Color) { + // FIXME: change the argument type to u16 + pub fn set_pixel(&mut self, x: u32, y: u32, color: Color) { + let x = x as u16; + let y = y as u16; assert!(x < self.width); assert!(y < self.height); @@ -279,7 +284,10 @@ impl Image { /// /// # Panics /// Panics if the given pixel coordinates are not inside the image. - pub fn get_pixel(&self, x: u16, y: u16) -> Color { + // FIXME: change the argument type to u16 + pub fn get_pixel(&self, x: u32, y: u32) -> Color { + let x = x as u16; + let y = y as u16; assert!(x < self.width); assert!(y < self.height); @@ -311,7 +319,7 @@ impl Image { fn assert_same_size(&self, other: &Image) { assert!( self.width == other.width && self.height == other.height, - "Images have different sizes!" + "images have different sizes" ); } @@ -730,7 +738,7 @@ impl Texture2D { /// Creates a Texture2D from an [Image]. pub fn from_image(image: &Image) -> Texture2D { - Texture2D::from_rgba8(image.width(), image.height(), image.bytes()) + Texture2D::from_rgba8(image.width() as u16, image.height() as u16, image.bytes()) } /// Creates a Texture2D from a miniquad From 326a025b9eca7bd14039adaceef24c89689bc2c7 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Sat, 27 Jul 2024 23:37:53 +0200 Subject: [PATCH 22/25] add dots in docstring --- src/texture.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/texture.rs b/src/texture.rs index 00ad4fd3..ae434342 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -323,8 +323,8 @@ impl Image { ); } - /// Blends this image with another image (of identical dimensions) - /// Inspired by OpenCV saturated blending + /// Blends this image with another image (of identical dimensions). + /// Inspired by OpenCV saturated blending. pub fn blend(&mut self, other: &Image) { self.assert_same_size(other); @@ -404,7 +404,7 @@ impl Image { image::save_buffer( path, - &bytes[..], + &bytes, self.width as u32, self.height as u32, image::ColorType::Rgba8, From d72eda5bd01499bac533c0cf6b188c3ffec402d2 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Sat, 27 Jul 2024 23:41:08 +0200 Subject: [PATCH 23/25] fix type mismatch --- examples/life.rs | 4 ++-- examples/shadertoy.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/life.rs b/examples/life.rs index 7b93bb29..3217b97c 100644 --- a/examples/life.rs +++ b/examples/life.rs @@ -75,8 +75,8 @@ async fn main() { cells[i] = buffer[i]; image.set_pixel( - (i % w) as u16, - (i / w) as u16, + (i % w) as u32, + (i / w) as u32, match buffer[i] { CellState::Alive => BLACK, CellState::Dead => WHITE, diff --git a/examples/shadertoy.rs b/examples/shadertoy.rs index d3161c72..16c58d66 100644 --- a/examples/shadertoy.rs +++ b/examples/shadertoy.rs @@ -301,7 +301,7 @@ async fn main() { let y = mouse.1 as i32 - (cursor.y as i32 + 20); let color = color_picker_image - .get_pixel(x.max(0).min(199) as u16, y.max(0).min(199) as u16); + .get_pixel(x.max(0).min(199) as u32, y.max(0).min(199) as u32); canvas.rect( Rect::new(cursor.x, cursor.y, 200.0, 18.0), From 3755461c86d6f216c72e0d088273ddef11a752c3 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Wed, 11 Sep 2024 12:23:21 +0200 Subject: [PATCH 24/25] fix #544 and update deprecation version numbers --- src/texture.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/texture.rs b/src/texture.rs index ae434342..a6a94b80 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -70,17 +70,17 @@ impl TexturesContext { pub struct Image { // FIXME: remove all the deprecation notes once the `Image` fields are private #[deprecated( - since = "0.4.12", + since = "0.4.14", note = "this will be made private, use `Image::bytes`, `Image::bytes_mut` or `Image::bytes_vec_mut` for reading and writing instead" )] pub bytes: Vec, #[deprecated( - since = "0.4.12", + since = "0.4.14", note = "this will be made private, use `Image::width` or `Image::width_mut` for reading and writing instead" )] pub width: u16, #[deprecated( - since = "0.4.12", + since = "0.4.14", note = "this will be made private, use `Image::height` or `Image::height_mut` for reading and writing instead" )] pub height: u16, @@ -174,7 +174,7 @@ impl Image { } /// Creates an Image filled with the provided [Color]. - #[deprecated(since = "0.4.12", note = "use `Image::filled_with_color` instead")] + #[deprecated(since = "0.4.14", note = "use `Image::filled_with_color` instead")] pub fn gen_image_color(width: u16, height: u16, color: Color) -> Image { Image::filled_with_color(width, height, color) } @@ -264,6 +264,21 @@ impl Image { } } + /// Replace the bytes of this image with the bytes from `data`. + /// + /// # Panics + /// Panics if `data` has a different length than `self.bytes`. + pub fn set_image_data(&mut self, data: &[[u8; 4]]) { + assert_eq!(self.pixel_amount(), data.len()); + + for i in 0..data.len() { + self.bytes[i * 4] = data[i][0]; + self.bytes[i * 4 + 1] = data[i][1]; + self.bytes[i * 4 + 2] = data[i][2]; + self.bytes[i * 4 + 3] = data[i][3]; + } + } + /// Modifies a pixel [Color] in this image. /// /// # Panics From d4f1587ebb192603b5160ef6a6c275132db1af66 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Fri, 10 Jan 2025 00:14:27 +0100 Subject: [PATCH 25/25] fix ooverflow bug --- src/texture.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/texture.rs b/src/texture.rs index 4d56e86f..16a5c281 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -281,12 +281,10 @@ impl Image { /// Panics if the given pixel coordinates are not inside the image. // FIXME: change the argument type to u16 pub fn set_pixel(&mut self, x: u32, y: u32, color: Color) { - let x = x as u16; - let y = y as u16; - assert!(x < self.width); - assert!(y < self.height); + assert!(x < self.width as u32); + assert!(y < self.height as u32); - let width = self.width; + let width = self.width as u32; self.get_image_data_mut()[(y * width + x) as usize] = color.into(); }