Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image rework for soundness and usability #748

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
21f65b3
add unsafe functions for modifying raw image data
cyrgani Jun 24, 2024
52aecab
add the safe empty image constructor from #637
cyrgani Jun 24, 2024
12b5f0c
change the width and height return type from usize to u16
cyrgani Jun 24, 2024
efd6894
make image fields pub(crate) instead of pub
cyrgani Jun 24, 2024
fd82749
make image fields private, add unsafe from_raw_parts method
cyrgani Jun 24, 2024
c757a13
speedup Image::gen_image_color by factor of 2-3
cyrgani Jun 25, 2024
37a101d
remove unnecessary constructor again
cyrgani Jun 25, 2024
554bbbd
`From` instead of `Into` for `Color`
cyrgani Jun 25, 2024
9a816d9
breaking change: make blend and overlay assertions stricter
cyrgani Jun 26, 2024
0ad80cf
breaking change: make get_pixel correct, change get_pixel and set_pix…
cyrgani Jun 26, 2024
3f75df8
make from_raw_parts safe with assertions
cyrgani Jun 27, 2024
5dbfc99
const for applicable image methods
cyrgani Jun 27, 2024
9908a0f
more documentation, add `Image::pixel_amount`
cyrgani Jun 27, 2024
1638571
rename `Image::gen_image_color` to `Image::from_color`
cyrgani Jun 30, 2024
dfe6535
increase `Image::sub_image` speed significantly
cyrgani Jul 7, 2024
e7101ed
rename usages of `Image::gen_image_color` to `Image::from_color`, fix…
cyrgani Jul 7, 2024
f0af207
replace most usages of the fields within the module with function calls
cyrgani Jul 12, 2024
dde84b6
rename `Image::from_color` to `Image::filled_with_color` for clarity
cyrgani Jul 13, 2024
1b63ee7
add `Image::bytes_vec_mut`, make `Image::bytes_mut` safe
cyrgani Jul 13, 2024
7c6726f
make image fields public again to restore compatibility, add deprecat…
cyrgani Jul 13, 2024
c47c411
revert other breaking changes for now
cyrgani Jul 14, 2024
326a025
add dots in docstring
cyrgani Jul 27, 2024
d72eda5
fix type mismatch
cyrgani Jul 27, 2024
3755461
fix #544 and update deprecation version numbers
cyrgani Sep 11, 2024
2ff39dd
Merge branch 'master' into image-rework
cyrgani Oct 13, 2024
1a079a0
Merge branch 'master' into image-rework
cyrgani Oct 21, 2024
d4f1587
fix ooverflow bug
cyrgani Jan 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions examples/life.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::filled_with_color(w as u16, h as u16, WHITE);

for cell in cells.iter_mut() {
if rand::gen_range(0, 5) == 0 {
Expand All @@ -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 {
Expand Down Expand Up @@ -77,7 +77,7 @@ async fn main() {
image.set_pixel(
(i % w) as u32,
(i / w) as u32,
match buffer[i as usize] {
match buffer[i] {
CellState::Alive => BLACK,
CellState::Dead => WHITE,
},
Expand Down
2 changes: 1 addition & 1 deletion examples/shadertoy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::filled_with_color(w as u16, h as u16, WHITE);
let image_data = image.get_image_data_mut();

for j in 0..h {
Expand Down
38 changes: 19 additions & 19 deletions src/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Color> 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<Color> 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<Color> for [f32; 4] {
fn from(value: Color) -> [f32; 4] {
[value.r, value.g, value.b, value.a]
}
}

Expand Down
10 changes: 5 additions & 5 deletions src/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,14 @@ impl Font {
let sprite = self.atlas.lock().unwrap().new_unique_id();
self.atlas.lock().unwrap().cache_sprite(
sprite,
Image {
bytes: bitmap
Image::from_raw_parts(
width,
height,
bitmap
.iter()
.flat_map(|coverage| vec![255, 255, 255, *coverage])
.collect(),
width,
height,
},
),
);
let advance = metrics.advance_width;

Expand Down
40 changes: 24 additions & 16 deletions src/text/atlas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ 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 texture = ctx.new_texture_from_rgba8(image.width, image.height, &image.bytes);
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() as u16, image.height() as u16, &image.bytes());
ctx.texture_set_filter(
texture,
miniquad::FilterMode::Nearest,
Expand Down Expand Up @@ -79,30 +80,32 @@ impl Atlas {
}

pub const fn width(&self) -> u16 {
self.image.width
self.image.width() as u16
}

pub const fn height(&self) -> u16 {
self.image.height
self.image.height() as u16
}

pub fn texture(&mut self) -> miniquad::TextureId {
let ctx = get_quad_context();
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() as u16,
self.image.height() as u16,
&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
Expand All @@ -123,9 +126,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() as u16 {
if height as u16 > self.max_line_height {
self.max_line_height = height as u16;
}
Expand All @@ -141,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::<Vec<_>>();
self.cursor_x = 0;
Expand All @@ -154,11 +159,14 @@ 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));
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 {
Expand Down
Loading
Loading