From c2722f713aaa349669bc99085d8031b3253e7d3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Tue, 8 Jun 2021 02:26:51 +0000 Subject: [PATCH] expose texture/image conversions as From/TryFrom (#2175) fixes #2169 Instead of having custom methods with reduced visibility, implement `From for Texture` and `TryFrom for image::DynamicImage` --- .../src/texture/image_texture_conversion.rs | 259 +++++++++--------- crates/bevy_render/src/texture/texture.rs | 18 +- .../bevy_sprite/src/texture_atlas_builder.rs | 2 +- 3 files changed, 144 insertions(+), 135 deletions(-) diff --git a/crates/bevy_render/src/texture/image_texture_conversion.rs b/crates/bevy_render/src/texture/image_texture_conversion.rs index dbf12a0ead735..dc31be26d5e6b 100644 --- a/crates/bevy_render/src/texture/image_texture_conversion.rs +++ b/crates/bevy_render/src/texture/image_texture_conversion.rs @@ -1,151 +1,160 @@ +use std::convert::TryFrom; +use thiserror::Error; + use super::{Extent3d, Texture, TextureDimension, TextureFormat}; -/// Helper method to convert a `DynamicImage` to a `Texture` -pub(crate) fn image_to_texture(dyn_img: image::DynamicImage) -> Texture { - use bevy_core::cast_slice; - let width; - let height; +impl From for Texture { + fn from(dyn_img: image::DynamicImage) -> Self { + use bevy_core::cast_slice; + let width; + let height; - let data: Vec; - let format: TextureFormat; + let data: Vec; + let format: TextureFormat; - match dyn_img { - image::DynamicImage::ImageLuma8(i) => { - let i = image::DynamicImage::ImageLuma8(i).into_rgba8(); - width = i.width(); - height = i.height(); - format = TextureFormat::Rgba8UnormSrgb; + match dyn_img { + image::DynamicImage::ImageLuma8(i) => { + let i = image::DynamicImage::ImageLuma8(i).into_rgba8(); + width = i.width(); + height = i.height(); + format = TextureFormat::Rgba8UnormSrgb; - data = i.into_raw(); - } - image::DynamicImage::ImageLumaA8(i) => { - let i = image::DynamicImage::ImageLumaA8(i).into_rgba8(); - width = i.width(); - height = i.height(); - format = TextureFormat::Rgba8UnormSrgb; + data = i.into_raw(); + } + image::DynamicImage::ImageLumaA8(i) => { + let i = image::DynamicImage::ImageLumaA8(i).into_rgba8(); + width = i.width(); + height = i.height(); + format = TextureFormat::Rgba8UnormSrgb; - data = i.into_raw(); - } - image::DynamicImage::ImageRgb8(i) => { - let i = image::DynamicImage::ImageRgb8(i).into_rgba8(); - width = i.width(); - height = i.height(); - format = TextureFormat::Rgba8UnormSrgb; + data = i.into_raw(); + } + image::DynamicImage::ImageRgb8(i) => { + let i = image::DynamicImage::ImageRgb8(i).into_rgba8(); + 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::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_bgra8(); + data = i.into_raw(); + } + image::DynamicImage::ImageBgr8(i) => { + let i = image::DynamicImage::ImageBgr8(i).into_bgra8(); - width = i.width(); - height = i.height(); - format = TextureFormat::Bgra8UnormSrgb; + 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::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; + data = i.into_raw(); + } + image::DynamicImage::ImageLuma16(i) => { + width = i.width(); + height = i.height(); + format = TextureFormat::R16Uint; + let raw_data = i.into_raw(); - let raw_data = i.into_raw(); + data = cast_slice(&raw_data).to_owned(); + } + image::DynamicImage::ImageLumaA16(i) => { + width = i.width(); + height = i.height(); + format = TextureFormat::Rg16Uint; - data = cast_slice(&raw_data).to_owned(); - } - image::DynamicImage::ImageLumaA16(i) => { - width = i.width(); - height = i.height(); - format = TextureFormat::Rg16Uint; + let raw_data = i.into_raw(); - let raw_data = i.into_raw(); + data = cast_slice(&raw_data).to_owned(); + } - data = cast_slice(&raw_data).to_owned(); - } + image::DynamicImage::ImageRgb16(image) => { + width = image.width(); + height = image.height(); + format = TextureFormat::Rgba16Uint; - image::DynamicImage::ImageRgb16(image) => { - width = image.width(); - height = image.height(); - format = TextureFormat::Rgba16Uint; - - let mut local_data = - Vec::with_capacity(width as usize * height as usize * format.pixel_size()); - - for pixel in image.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(); - - local_data.extend_from_slice(&r.to_ne_bytes()); - local_data.extend_from_slice(&g.to_ne_bytes()); - local_data.extend_from_slice(&b.to_ne_bytes()); - local_data.extend_from_slice(&a.to_ne_bytes()); - } + let mut local_data = + Vec::with_capacity(width as usize * height as usize * format.pixel_size()); - data = local_data; - } - image::DynamicImage::ImageRgba16(i) => { - width = i.width(); - height = i.height(); - format = TextureFormat::Rgba16Uint; + for pixel in image.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; + + local_data.extend_from_slice(&r.to_ne_bytes()); + local_data.extend_from_slice(&g.to_ne_bytes()); + local_data.extend_from_slice(&b.to_ne_bytes()); + local_data.extend_from_slice(&a.to_ne_bytes()); + } - let raw_data = i.into_raw(); + data = local_data; + } + image::DynamicImage::ImageRgba16(i) => { + width = i.width(); + height = i.height(); + format = TextureFormat::Rgba16Uint; - data = cast_slice(&raw_data).to_owned(); + let raw_data = i.into_raw(); + + data = cast_slice(&raw_data).to_owned(); + } } + + Texture::new( + Extent3d::new(width, height, 1), + TextureDimension::D2, + data, + format, + ) } +} - Texture::new( - Extent3d::new(width, height, 1), - TextureDimension::D2, - data, - format, - ) +#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)] +pub enum TextureConversionError { + #[error("Unsupported texture format")] + UnsupportedFormat, + #[error("Invalid texture size")] + InvalidSize, } -/// Helper method to convert a `Texture` to a `DynamicImage`. Not all `Texture` formats are -/// covered, it will return `None` if the format is not supported -pub(crate) fn texture_to_image(texture: &Texture) -> Option { - match texture.format { - TextureFormat::R8Unorm => image::ImageBuffer::from_raw( - texture.size.width, - texture.size.height, - texture.data.clone(), - ) - .map(image::DynamicImage::ImageLuma8), - TextureFormat::Rg8Unorm => image::ImageBuffer::from_raw( - texture.size.width, - texture.size.height, - texture.data.clone(), - ) - .map(image::DynamicImage::ImageLumaA8), - TextureFormat::Rgba8UnormSrgb => image::ImageBuffer::from_raw( - texture.size.width, - texture.size.height, - texture.data.clone(), - ) - .map(image::DynamicImage::ImageRgba8), - TextureFormat::Bgra8UnormSrgb => image::ImageBuffer::from_raw( - texture.size.width, - texture.size.height, - texture.data.clone(), - ) - .map(image::DynamicImage::ImageBgra8), - _ => None, +impl TryFrom for image::DynamicImage { + type Error = TextureConversionError; + + fn try_from(texture: Texture) -> Result { + match texture.format { + TextureFormat::R8Unorm => { + image::ImageBuffer::from_raw(texture.size.width, texture.size.height, texture.data) + .map(image::DynamicImage::ImageLuma8) + .ok_or(TextureConversionError::InvalidSize) + } + TextureFormat::Rg8Unorm => { + image::ImageBuffer::from_raw(texture.size.width, texture.size.height, texture.data) + .map(image::DynamicImage::ImageLumaA8) + .ok_or(TextureConversionError::InvalidSize) + } + TextureFormat::Rgba8UnormSrgb => { + image::ImageBuffer::from_raw(texture.size.width, texture.size.height, texture.data) + .map(image::DynamicImage::ImageRgba8) + .ok_or(TextureConversionError::InvalidSize) + } + TextureFormat::Bgra8UnormSrgb => { + image::ImageBuffer::from_raw(texture.size.width, texture.size.height, texture.data) + .map(image::DynamicImage::ImageBgra8) + .ok_or(TextureConversionError::InvalidSize) + } + _ => Err(TextureConversionError::UnsupportedFormat), + } } } diff --git a/crates/bevy_render/src/texture/texture.rs b/crates/bevy_render/src/texture/texture.rs index 449314656a8de..980441852295f 100644 --- a/crates/bevy_render/src/texture/texture.rs +++ b/crates/bevy_render/src/texture/texture.rs @@ -1,7 +1,6 @@ -use super::{ - image_texture_conversion::image_to_texture, Extent3d, SamplerDescriptor, TextureDescriptor, - TextureDimension, TextureFormat, -}; +use std::convert::TryInto; + +use super::{Extent3d, SamplerDescriptor, TextureDescriptor, TextureDimension, TextureFormat}; use crate::renderer::{ RenderResource, RenderResourceContext, RenderResourceId, RenderResourceType, }; @@ -135,9 +134,10 @@ impl Texture { /// - `TextureFormat::Rg8Unorm` /// - `TextureFormat::Rgba8UnormSrgb` /// - `TextureFormat::Bgra8UnormSrgb` - pub fn convert(&self, new_format: TextureFormat) -> Option { - super::image_texture_conversion::texture_to_image(self) - .and_then(|img| match new_format { + pub fn convert(self, new_format: TextureFormat) -> Option { + self.try_into() + .ok() + .and_then(|img: image::DynamicImage| match new_format { TextureFormat::R8Unorm => Some(image::DynamicImage::ImageLuma8(img.into_luma8())), TextureFormat::Rg8Unorm => { Some(image::DynamicImage::ImageLumaA8(img.into_luma_alpha8())) @@ -150,7 +150,7 @@ impl Texture { } _ => None, }) - .map(super::image_texture_conversion::image_to_texture) + .map(|image| image.into()) } pub fn texture_resource_system( @@ -243,7 +243,7 @@ impl Texture { // cases. let dyn_img = image::load_from_memory_with_format(buffer, format)?; - Ok(image_to_texture(dyn_img)) + Ok(dyn_img.into()) } } diff --git a/crates/bevy_sprite/src/texture_atlas_builder.rs b/crates/bevy_sprite/src/texture_atlas_builder.rs index 37e3f62a9bf74..c09b819037272 100644 --- a/crates/bevy_sprite/src/texture_atlas_builder.rs +++ b/crates/bevy_sprite/src/texture_atlas_builder.rs @@ -113,7 +113,7 @@ impl TextureAtlasBuilder { ) { if self.format == texture.format { Self::copy_texture_to_atlas(atlas_texture, texture, packed_location); - } else if let Some(converted_texture) = texture.convert(self.format) { + } else if let Some(converted_texture) = texture.clone().convert(self.format) { debug!( "Converting texture from '{:?}' to '{:?}'", texture.format, self.format