mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
add texture loader for more formats using image
crate
This adds support for PNG images only for now. More formats can be added relatively easily. Images with various pixel formats are supported (such as RGB-16bit or R-8bit).
This commit is contained in:
parent
7412b0ec25
commit
23149f1753
5 changed files with 160 additions and 33 deletions
|
@ -20,9 +20,7 @@ bevy_window = { path = "../bevy_window" }
|
|||
# rendering
|
||||
spirv-reflect = "0.2.3"
|
||||
glsl-to-spirv = { git = "https://github.com/cart/glsl-to-spirv" }
|
||||
# TODO: move this to its own crate
|
||||
png = "0.16.0"
|
||||
image = "0.23"
|
||||
image = { version = "0.23", default-features = false, features = ["png", "hdr"] }
|
||||
|
||||
# misc
|
||||
log = { version = "0.4", features = ["release_max_level_info"] }
|
||||
|
|
|
@ -38,7 +38,7 @@ use render_graph::{
|
|||
};
|
||||
use renderer::{AssetRenderResourceBindings, RenderResourceBindings};
|
||||
use std::ops::Range;
|
||||
use texture::{HdrTextureLoader, PngTextureLoader, TextureResourceSystemState};
|
||||
use texture::{HdrTextureLoader, ImageTextureLoader, TextureResourceSystemState};
|
||||
|
||||
pub mod stage {
|
||||
/// Stage where render resources are set up
|
||||
|
@ -76,7 +76,7 @@ impl AppPlugin for RenderPlugin {
|
|||
.add_asset::<Shader>()
|
||||
.add_asset::<PipelineDescriptor>()
|
||||
.add_asset_loader::<Texture, HdrTextureLoader>()
|
||||
.add_asset_loader::<Texture, PngTextureLoader>()
|
||||
.add_asset_loader::<Texture, ImageTextureLoader>()
|
||||
.register_component::<Camera>()
|
||||
.register_component::<Draw>()
|
||||
.register_component::<RenderPipelines>()
|
||||
|
|
155
crates/bevy_render/src/texture/image_texture_loader.rs
Normal file
155
crates/bevy_render/src/texture/image_texture_loader.rs
Normal file
|
@ -0,0 +1,155 @@
|
|||
use super::{Texture, TextureFormat};
|
||||
use anyhow::Result;
|
||||
use bevy_asset::AssetLoader;
|
||||
use bevy_math::Vec2;
|
||||
use std::path::Path;
|
||||
|
||||
/// Loader for images that can be read by the `image` crate.
|
||||
///
|
||||
/// Reads only PNG images for now.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct ImageTextureLoader;
|
||||
|
||||
impl AssetLoader<Texture> for ImageTextureLoader {
|
||||
fn from_bytes(&self, asset_path: &Path, bytes: Vec<u8>) -> Result<Texture> {
|
||||
use bevy_core::AsBytes;
|
||||
|
||||
// Find the image type we expect. A file with the extension "png" should
|
||||
// probably load as a PNG.
|
||||
|
||||
let ext = asset_path.extension().unwrap().to_str().unwrap();
|
||||
|
||||
// NOTE: If more formats are added they can be added here.
|
||||
let img_format = if ext.eq_ignore_ascii_case("png") {
|
||||
image::ImageFormat::Png
|
||||
} else {
|
||||
panic!(
|
||||
"Unexpected image format {:?} for file {}, this is an error in `bevy_render`.",
|
||||
ext,
|
||||
asset_path.display()
|
||||
)
|
||||
};
|
||||
|
||||
// Load the image in the expected format.
|
||||
// Some formats like PNG allow for R or RG textures too, so the texture
|
||||
// format needs to be determined. For RGB textures an alpha channel
|
||||
// needs to be added, so the image data needs to be converted in those
|
||||
// cases.
|
||||
|
||||
let dyn_img = image::load_from_memory_with_format(bytes.as_slice(), img_format)?;
|
||||
|
||||
let width;
|
||||
let height;
|
||||
|
||||
let data: Vec<u8>;
|
||||
let format: TextureFormat;
|
||||
|
||||
match dyn_img {
|
||||
image::DynamicImage::ImageLuma8(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::R8Unorm;
|
||||
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageLumaA8(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Rg8Unorm;
|
||||
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageRgb8(i) => {
|
||||
let i = image::DynamicImage::ImageRgb8(i).into_rgba();
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Rgba8UnormSrgb;
|
||||
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageRgba8(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Rgba8UnormSrgb;
|
||||
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageBgr8(i) => {
|
||||
let i = image::DynamicImage::ImageBgr8(i).into_bgra();
|
||||
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Bgra8UnormSrgb;
|
||||
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageBgra8(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Bgra8UnormSrgb;
|
||||
|
||||
data = i.into_raw();
|
||||
}
|
||||
image::DynamicImage::ImageLuma16(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::R16Uint;
|
||||
|
||||
let raw_data = i.into_raw();
|
||||
|
||||
data = raw_data.as_slice().as_bytes().to_owned();
|
||||
}
|
||||
image::DynamicImage::ImageLumaA16(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Rg16Uint;
|
||||
|
||||
let raw_data = i.into_raw();
|
||||
|
||||
data = raw_data.as_slice().as_bytes().to_owned();
|
||||
}
|
||||
image::DynamicImage::ImageRgb16(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Rgba16Uint;
|
||||
|
||||
let mut d =
|
||||
Vec::with_capacity(width as usize * height as usize * format.pixel_size());
|
||||
|
||||
for pixel in i.into_raw().chunks_exact(3) {
|
||||
// TODO unsafe_get in release builds?
|
||||
let r = pixel[0];
|
||||
let g = pixel[1];
|
||||
let b = pixel[2];
|
||||
let a = u16::max_value();
|
||||
|
||||
d.extend_from_slice(&r.to_ne_bytes());
|
||||
d.extend_from_slice(&g.to_ne_bytes());
|
||||
d.extend_from_slice(&b.to_ne_bytes());
|
||||
d.extend_from_slice(&a.to_ne_bytes());
|
||||
}
|
||||
|
||||
data = d;
|
||||
}
|
||||
image::DynamicImage::ImageRgba16(i) => {
|
||||
width = i.width();
|
||||
height = i.height();
|
||||
format = TextureFormat::Rgba16Uint;
|
||||
|
||||
let raw_data = i.into_raw();
|
||||
|
||||
data = raw_data.as_slice().as_bytes().to_owned();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Texture::new(
|
||||
Vec2::new(width as f32, height as f32),
|
||||
data,
|
||||
format,
|
||||
))
|
||||
}
|
||||
fn extensions(&self) -> &[&str] {
|
||||
static EXTENSIONS: &[&str] = &["png"];
|
||||
EXTENSIONS
|
||||
}
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
mod hdr_texture_loader;
|
||||
mod png_texture_loader;
|
||||
mod image_texture_loader;
|
||||
mod sampler_descriptor;
|
||||
mod texture;
|
||||
mod texture_descriptor;
|
||||
mod texture_dimension;
|
||||
|
||||
pub use hdr_texture_loader::*;
|
||||
pub use png_texture_loader::*;
|
||||
pub use image_texture_loader::*;
|
||||
pub use sampler_descriptor::*;
|
||||
pub use texture::*;
|
||||
pub use texture_descriptor::*;
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
use super::{Texture, TextureFormat};
|
||||
use anyhow::Result;
|
||||
use bevy_asset::AssetLoader;
|
||||
use bevy_math::Vec2;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PngTextureLoader;
|
||||
|
||||
impl AssetLoader<Texture> for PngTextureLoader {
|
||||
fn from_bytes(&self, _asset_path: &Path, bytes: Vec<u8>) -> Result<Texture> {
|
||||
let decoder = png::Decoder::new(bytes.as_slice());
|
||||
let (info, mut reader) = decoder.read_info()?;
|
||||
let mut data = vec![0; info.buffer_size()];
|
||||
reader.next_frame(&mut data)?;
|
||||
Ok(Texture::new(
|
||||
Vec2::new(info.width as f32, info.height as f32),
|
||||
data,
|
||||
TextureFormat::Rgba8UnormSrgb,
|
||||
))
|
||||
}
|
||||
fn extensions(&self) -> &[&str] {
|
||||
static EXTENSIONS: &[&str] = &["png"];
|
||||
EXTENSIONS
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue