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 | ## Assets | ||||||
| 
 | 
 | ||||||
| * Generic RPG Pack (CC0 license) by [Bakudas](https://twitter.com/bakudas) and [Gabe Fern](https://twitter.com/_Gabrielfer) | * 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 | # rendering | ||||||
| spirv-reflect = "0.2.3" | spirv-reflect = "0.2.3" | ||||||
| glsl-to-spirv = { git = "https://github.com/cart/glsl-to-spirv" } | glsl-to-spirv = { git = "https://github.com/cart/glsl-to-spirv" } | ||||||
| # TODO: move this to its own crate | image = { version = "0.23", default-features = false, features = ["png", "hdr"] } | ||||||
| png = "0.16.0" |  | ||||||
| 
 | 
 | ||||||
| # misc | # misc | ||||||
| log = { version = "0.4", features = ["release_max_level_info"] } | log = { version = "0.4", features = ["release_max_level_info"] } | ||||||
|  | |||||||
| @ -38,7 +38,7 @@ use render_graph::{ | |||||||
| }; | }; | ||||||
| use renderer::{AssetRenderResourceBindings, RenderResourceBindings}; | use renderer::{AssetRenderResourceBindings, RenderResourceBindings}; | ||||||
| use std::ops::Range; | use std::ops::Range; | ||||||
| use texture::{PngTextureLoader, TextureResourceSystemState}; | use texture::{HdrTextureLoader, ImageTextureLoader, TextureResourceSystemState}; | ||||||
| 
 | 
 | ||||||
| pub mod stage { | pub mod stage { | ||||||
|     /// Stage where render resources are set up
 |     /// Stage where render resources are set up
 | ||||||
| @ -75,7 +75,8 @@ impl AppPlugin for RenderPlugin { | |||||||
|             .add_asset::<Texture>() |             .add_asset::<Texture>() | ||||||
|             .add_asset::<Shader>() |             .add_asset::<Shader>() | ||||||
|             .add_asset::<PipelineDescriptor>() |             .add_asset::<PipelineDescriptor>() | ||||||
|             .add_asset_loader::<Texture, PngTextureLoader>() |             .add_asset_loader::<Texture, HdrTextureLoader>() | ||||||
|  |             .add_asset_loader::<Texture, ImageTextureLoader>() | ||||||
|             .register_component::<Camera>() |             .register_component::<Camera>() | ||||||
|             .register_component::<Draw>() |             .register_component::<Draw>() | ||||||
|             .register_component::<RenderPipelines>() |             .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 sampler_descriptor; | ||||||
| mod texture; | mod texture; | ||||||
| mod texture_descriptor; | mod texture_descriptor; | ||||||
| mod texture_dimension; | mod texture_dimension; | ||||||
| 
 | 
 | ||||||
| pub use png_texture_loader::*; | pub use hdr_texture_loader::*; | ||||||
|  | pub use image_texture_loader::*; | ||||||
| pub use sampler_descriptor::*; | pub use sampler_descriptor::*; | ||||||
| pub use texture::*; | pub use texture::*; | ||||||
| pub use texture_descriptor::*; | 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; |         self.size = size; | ||||||
|         let width = size.x() as usize; |         let width = size.x() as usize; | ||||||
|         let height = size.y() 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( |     pub fn texture_resource_system( | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Carter Anderson
						Carter Anderson