From 21f65b3b7c53d394900ef62531366c3b21911bcd Mon Sep 17 00:00:00 2001 From: cyrgani Date: Mon, 24 Jun 2024 13:16:05 +0200 Subject: [PATCH 01/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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/14] `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/14] 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/14] 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/14] 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/14] 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/14] 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/14] 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