bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
36.06k stars 3.56k forks source link

Building and manipulating `Image` objects pixel-by-pixel is very tedious and error prone #11887

Open alice-i-cecile opened 8 months ago

alice-i-cecile commented 8 months ago

What problem does this solve or what need does it fill?

Building images from programmatically controlled data can be a useful tool for getting reasonably high performance without having to swap to shaders. Staying in pure Rust is great: I've previously used this to quickly hack together a minimap.

However, the APIs for constructing and accessing an Image is painful: offering only access to the raw data.

What solution would you like?

I would like:

  1. Image::from_colors, which takes a 2 dimensional vector of Color types, converted into the format of the user's choice.
  2. Image::get_pixel, which returns the color at the provided pixel.
  3. Image::set_pixel, which sets the value of the data at the provided pixel.

What alternative(s) have you considered?

We could construct a higher level abstraction for this, which then gets converted into an image?

We could also say that this is an abuse of the API, and point users to a more appropriate solution.

shanecelis commented 2 months ago

I decided to take this on. I have a preliminary implementation here: pixel.rs.

I'm curious, which of the TextureFormats do we want to support? I've implemented support for Rgba8Unorm(Srgb) and the R8*s for get_pixel() just to get a feeling for what that would look like.

The Unorm and Snorm formats seem useful for pixel operations. I'm less convinced the Uint and Sint would be practical.

I implemented the requested methods above. However, I see now I've deviated on the Image::from_colors specification. I accept a 1D array and don't accept a format. I can amend that but I do think two dimensional arrays are awkward to work with. Any suggestions on the actual type? Maybe Vec<[C; N]> would at least ensure it's a valid form.

I also implemented Image::pixels which returns an iterator of the color. It's not necessary. I can drop it but might be a nice way to feed into from_colors however.

For the get and set pixel, I made it work with linear or cartesian indices. Sometimes one form is immensely more helpful than the other, so let's do both if it doesn't hurt the ergonomics was my thought.

shanecelis commented 2 months ago

Looks like there is a more mature implementation here.

mintlu8 commented 2 months ago

I think I'd rather use a rusttyle like API

Image::construct(usize, usize, impl FnMut(usize, usize) -> LinearRgba))
rparrett commented 2 months ago

Related: #1502

shanecelis commented 2 months ago

In the discord someone mentioned that DynamicImage actually has a lot of this functionality already. See RgbImage. But it doesn't use bevy's Color. Perhaps that would be the right implementation avenue though.