expose texture/image conversions as From/TryFrom (#2175)

fixes #2169 

Instead of having custom methods with reduced visibility, implement `From<image::DynamicImage> for Texture` and `TryFrom<Texture> for image::DynamicImage`
This commit is contained in:
François 2021-06-08 02:26:51 +00:00
parent cebb553bff
commit c2722f713a
3 changed files with 149 additions and 140 deletions

View file

@ -1,7 +1,10 @@
use std::convert::TryFrom;
use thiserror::Error;
use super::{Extent3d, Texture, TextureDimension, TextureFormat}; use super::{Extent3d, Texture, TextureDimension, TextureFormat};
/// Helper method to convert a `DynamicImage` to a `Texture` impl From<image::DynamicImage> for Texture {
pub(crate) fn image_to_texture(dyn_img: image::DynamicImage) -> Texture { fn from(dyn_img: image::DynamicImage) -> Self {
use bevy_core::cast_slice; use bevy_core::cast_slice;
let width; let width;
let height; let height;
@ -61,7 +64,6 @@ pub(crate) fn image_to_texture(dyn_img: image::DynamicImage) -> Texture {
width = i.width(); width = i.width();
height = i.height(); height = i.height();
format = TextureFormat::R16Uint; format = TextureFormat::R16Uint;
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();
@ -89,7 +91,7 @@ pub(crate) fn image_to_texture(dyn_img: image::DynamicImage) -> Texture {
let r = pixel[0]; let r = pixel[0];
let g = pixel[1]; let g = pixel[1];
let b = pixel[2]; let b = pixel[2];
let a = u16::max_value(); let a = u16::MAX;
local_data.extend_from_slice(&r.to_ne_bytes()); local_data.extend_from_slice(&r.to_ne_bytes());
local_data.extend_from_slice(&g.to_ne_bytes()); local_data.extend_from_slice(&g.to_ne_bytes());
@ -117,35 +119,42 @@ pub(crate) fn image_to_texture(dyn_img: image::DynamicImage) -> Texture {
format, format,
) )
} }
}
/// Helper method to convert a `Texture` to a `DynamicImage`. Not all `Texture` formats are #[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
/// covered, it will return `None` if the format is not supported pub enum TextureConversionError {
pub(crate) fn texture_to_image(texture: &Texture) -> Option<image::DynamicImage> { #[error("Unsupported texture format")]
UnsupportedFormat,
#[error("Invalid texture size")]
InvalidSize,
}
impl TryFrom<Texture> for image::DynamicImage {
type Error = TextureConversionError;
fn try_from(texture: Texture) -> Result<Self, Self::Error> {
match texture.format { match texture.format {
TextureFormat::R8Unorm => image::ImageBuffer::from_raw( TextureFormat::R8Unorm => {
texture.size.width, image::ImageBuffer::from_raw(texture.size.width, texture.size.height, texture.data)
texture.size.height, .map(image::DynamicImage::ImageLuma8)
texture.data.clone(), .ok_or(TextureConversionError::InvalidSize)
) }
.map(image::DynamicImage::ImageLuma8), TextureFormat::Rg8Unorm => {
TextureFormat::Rg8Unorm => image::ImageBuffer::from_raw( image::ImageBuffer::from_raw(texture.size.width, texture.size.height, texture.data)
texture.size.width, .map(image::DynamicImage::ImageLumaA8)
texture.size.height, .ok_or(TextureConversionError::InvalidSize)
texture.data.clone(), }
) TextureFormat::Rgba8UnormSrgb => {
.map(image::DynamicImage::ImageLumaA8), image::ImageBuffer::from_raw(texture.size.width, texture.size.height, texture.data)
TextureFormat::Rgba8UnormSrgb => image::ImageBuffer::from_raw( .map(image::DynamicImage::ImageRgba8)
texture.size.width, .ok_or(TextureConversionError::InvalidSize)
texture.size.height, }
texture.data.clone(), TextureFormat::Bgra8UnormSrgb => {
) image::ImageBuffer::from_raw(texture.size.width, texture.size.height, texture.data)
.map(image::DynamicImage::ImageRgba8), .map(image::DynamicImage::ImageBgra8)
TextureFormat::Bgra8UnormSrgb => image::ImageBuffer::from_raw( .ok_or(TextureConversionError::InvalidSize)
texture.size.width, }
texture.size.height, _ => Err(TextureConversionError::UnsupportedFormat),
texture.data.clone(), }
)
.map(image::DynamicImage::ImageBgra8),
_ => None,
} }
} }

View file

@ -1,7 +1,6 @@
use super::{ use std::convert::TryInto;
image_texture_conversion::image_to_texture, Extent3d, SamplerDescriptor, TextureDescriptor,
TextureDimension, TextureFormat, use super::{Extent3d, SamplerDescriptor, TextureDescriptor, TextureDimension, TextureFormat};
};
use crate::renderer::{ use crate::renderer::{
RenderResource, RenderResourceContext, RenderResourceId, RenderResourceType, RenderResource, RenderResourceContext, RenderResourceId, RenderResourceType,
}; };
@ -135,9 +134,10 @@ impl Texture {
/// - `TextureFormat::Rg8Unorm` /// - `TextureFormat::Rg8Unorm`
/// - `TextureFormat::Rgba8UnormSrgb` /// - `TextureFormat::Rgba8UnormSrgb`
/// - `TextureFormat::Bgra8UnormSrgb` /// - `TextureFormat::Bgra8UnormSrgb`
pub fn convert(&self, new_format: TextureFormat) -> Option<Self> { pub fn convert(self, new_format: TextureFormat) -> Option<Self> {
super::image_texture_conversion::texture_to_image(self) self.try_into()
.and_then(|img| match new_format { .ok()
.and_then(|img: image::DynamicImage| match new_format {
TextureFormat::R8Unorm => Some(image::DynamicImage::ImageLuma8(img.into_luma8())), TextureFormat::R8Unorm => Some(image::DynamicImage::ImageLuma8(img.into_luma8())),
TextureFormat::Rg8Unorm => { TextureFormat::Rg8Unorm => {
Some(image::DynamicImage::ImageLumaA8(img.into_luma_alpha8())) Some(image::DynamicImage::ImageLumaA8(img.into_luma_alpha8()))
@ -150,7 +150,7 @@ impl Texture {
} }
_ => None, _ => None,
}) })
.map(super::image_texture_conversion::image_to_texture) .map(|image| image.into())
} }
pub fn texture_resource_system( pub fn texture_resource_system(
@ -243,7 +243,7 @@ impl Texture {
// cases. // cases.
let dyn_img = image::load_from_memory_with_format(buffer, format)?; let dyn_img = image::load_from_memory_with_format(buffer, format)?;
Ok(image_to_texture(dyn_img)) Ok(dyn_img.into())
} }
} }

View file

@ -113,7 +113,7 @@ impl TextureAtlasBuilder {
) { ) {
if self.format == texture.format { if self.format == texture.format {
Self::copy_texture_to_atlas(atlas_texture, texture, packed_location); 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!( debug!(
"Converting texture from '{:?}' to '{:?}'", "Converting texture from '{:?}' to '{:?}'",
texture.format, self.format texture.format, self.format