Merge pull request #75 from karroffel/hdr-loading
add more image loaders (HDR, JPEG, BMP) via `image` crate
This commit is contained in:
		
						commit
						6a0c2d5673
					
				| @ -15,3 +15,4 @@ | ||||
| ## Assets | ||||
| 
 | ||||
| * Generic RPG Pack (CC0 license) by [Bakudas](https://twitter.com/bakudas) and [Gabe Fern](https://twitter.com/_Gabrielfer) | ||||
| * Environment maps (`.hdr` files) from [HDRIHaven](https://hdrihaven.com) (CC0 license) | ||||
							
								
								
									
										
											BIN
										
									
								
								assets/textures/spiaggia_di_mondello_1k.hdr
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/textures/spiaggia_di_mondello_1k.hdr
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -20,8 +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 = { 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::{PngTextureLoader, TextureResourceSystemState}; | ||||
| use texture::{HdrTextureLoader, ImageTextureLoader, TextureResourceSystemState}; | ||||
| 
 | ||||
| pub mod stage { | ||||
|     /// Stage where render resources are set up
 | ||||
| @ -75,7 +75,8 @@ impl AppPlugin for RenderPlugin { | ||||
|             .add_asset::<Texture>() | ||||
|             .add_asset::<Shader>() | ||||
|             .add_asset::<PipelineDescriptor>() | ||||
|             .add_asset_loader::<Texture, PngTextureLoader>() | ||||
|             .add_asset_loader::<Texture, HdrTextureLoader>() | ||||
|             .add_asset_loader::<Texture, ImageTextureLoader>() | ||||
|             .register_component::<Camera>() | ||||
|             .register_component::<Draw>() | ||||
|             .register_component::<RenderPipelines>() | ||||
|  | ||||
							
								
								
									
										43
									
								
								crates/bevy_render/src/texture/hdr_texture_loader.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								crates/bevy_render/src/texture/hdr_texture_loader.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| use super::{Texture, TextureFormat}; | ||||
| use anyhow::Result; | ||||
| use bevy_asset::AssetLoader; | ||||
| use bevy_math::Vec2; | ||||
| use std::path::Path; | ||||
| 
 | ||||
| #[derive(Clone, Default)] | ||||
| pub struct HdrTextureLoader; | ||||
| 
 | ||||
| impl AssetLoader<Texture> for HdrTextureLoader { | ||||
|     fn from_bytes(&self, _asset_path: &Path, bytes: Vec<u8>) -> Result<Texture> { | ||||
|         let format = TextureFormat::Rgba32Float; | ||||
|         debug_assert_eq!( | ||||
|             format.pixel_size(), | ||||
|             4 * 4 * 1, | ||||
|             "Format should have 32bit x 4 size" | ||||
|         ); | ||||
| 
 | ||||
|         let decoder = image::hdr::HdrDecoder::new(bytes.as_slice())?; | ||||
|         let info = decoder.metadata(); | ||||
|         let rgb_data = decoder.read_image_hdr()?; | ||||
|         let mut rgba_data = Vec::with_capacity(rgb_data.len() * format.pixel_size()); | ||||
| 
 | ||||
|         for rgb in rgb_data { | ||||
|             let alpha = 1.0f32; | ||||
| 
 | ||||
|             rgba_data.extend_from_slice(&rgb.0[0].to_ne_bytes()); | ||||
|             rgba_data.extend_from_slice(&rgb.0[1].to_ne_bytes()); | ||||
|             rgba_data.extend_from_slice(&rgb.0[2].to_ne_bytes()); | ||||
|             rgba_data.extend_from_slice(&alpha.to_ne_bytes()); | ||||
|         } | ||||
| 
 | ||||
|         Ok(Texture::new( | ||||
|             Vec2::new(info.width as f32, info.height as f32), | ||||
|             rgba_data, | ||||
|             format, | ||||
|         )) | ||||
|     } | ||||
|     fn extensions(&self) -> &[&str] { | ||||
|         static EXTENSIONS: &[&str] = &["hdr"]; | ||||
|         EXTENSIONS | ||||
|     } | ||||
| } | ||||
							
								
								
									
										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,10 +1,12 @@ | ||||
| mod png_texture_loader; | ||||
| mod hdr_texture_loader; | ||||
| mod image_texture_loader; | ||||
| mod sampler_descriptor; | ||||
| mod texture; | ||||
| mod texture_descriptor; | ||||
| mod texture_dimension; | ||||
| 
 | ||||
| pub use png_texture_loader::*; | ||||
| pub use hdr_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 | ||||
|     } | ||||
| } | ||||
| @ -67,7 +67,8 @@ impl Texture { | ||||
|         self.size = size; | ||||
|         let width = size.x() as usize; | ||||
|         let height = size.y() as usize; | ||||
|         self.data.resize(width * height * self.format.pixel_size(), 0); | ||||
|         self.data | ||||
|             .resize(width * height * self.format.pixel_size(), 0); | ||||
|     } | ||||
| 
 | ||||
|     pub fn texture_resource_system( | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Carter Anderson
						Carter Anderson