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:
parent
cebb553bff
commit
c2722f713a
@ -1,151 +1,160 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
let data: Vec<u8>;
|
let data: Vec<u8>;
|
||||||
let format: TextureFormat;
|
let format: TextureFormat;
|
||||||
|
|
||||||
match dyn_img {
|
match dyn_img {
|
||||||
image::DynamicImage::ImageLuma8(i) => {
|
image::DynamicImage::ImageLuma8(i) => {
|
||||||
let i = image::DynamicImage::ImageLuma8(i).into_rgba8();
|
let i = image::DynamicImage::ImageLuma8(i).into_rgba8();
|
||||||
width = i.width();
|
width = i.width();
|
||||||
height = i.height();
|
height = i.height();
|
||||||
format = TextureFormat::Rgba8UnormSrgb;
|
format = TextureFormat::Rgba8UnormSrgb;
|
||||||
|
|
||||||
data = i.into_raw();
|
data = i.into_raw();
|
||||||
}
|
}
|
||||||
image::DynamicImage::ImageLumaA8(i) => {
|
image::DynamicImage::ImageLumaA8(i) => {
|
||||||
let i = image::DynamicImage::ImageLumaA8(i).into_rgba8();
|
let i = image::DynamicImage::ImageLumaA8(i).into_rgba8();
|
||||||
width = i.width();
|
width = i.width();
|
||||||
height = i.height();
|
height = i.height();
|
||||||
format = TextureFormat::Rgba8UnormSrgb;
|
format = TextureFormat::Rgba8UnormSrgb;
|
||||||
|
|
||||||
data = i.into_raw();
|
data = i.into_raw();
|
||||||
}
|
}
|
||||||
image::DynamicImage::ImageRgb8(i) => {
|
image::DynamicImage::ImageRgb8(i) => {
|
||||||
let i = image::DynamicImage::ImageRgb8(i).into_rgba8();
|
let i = image::DynamicImage::ImageRgb8(i).into_rgba8();
|
||||||
width = i.width();
|
width = i.width();
|
||||||
height = i.height();
|
height = i.height();
|
||||||
format = TextureFormat::Rgba8UnormSrgb;
|
format = TextureFormat::Rgba8UnormSrgb;
|
||||||
|
|
||||||
data = i.into_raw();
|
data = i.into_raw();
|
||||||
}
|
}
|
||||||
image::DynamicImage::ImageRgba8(i) => {
|
image::DynamicImage::ImageRgba8(i) => {
|
||||||
width = i.width();
|
width = i.width();
|
||||||
height = i.height();
|
height = i.height();
|
||||||
format = TextureFormat::Rgba8UnormSrgb;
|
format = TextureFormat::Rgba8UnormSrgb;
|
||||||
|
|
||||||
data = i.into_raw();
|
data = i.into_raw();
|
||||||
}
|
}
|
||||||
image::DynamicImage::ImageBgr8(i) => {
|
image::DynamicImage::ImageBgr8(i) => {
|
||||||
let i = image::DynamicImage::ImageBgr8(i).into_bgra8();
|
let i = image::DynamicImage::ImageBgr8(i).into_bgra8();
|
||||||
|
|
||||||
width = i.width();
|
width = i.width();
|
||||||
height = i.height();
|
height = i.height();
|
||||||
format = TextureFormat::Bgra8UnormSrgb;
|
format = TextureFormat::Bgra8UnormSrgb;
|
||||||
|
|
||||||
data = i.into_raw();
|
data = i.into_raw();
|
||||||
}
|
}
|
||||||
image::DynamicImage::ImageBgra8(i) => {
|
image::DynamicImage::ImageBgra8(i) => {
|
||||||
width = i.width();
|
width = i.width();
|
||||||
height = i.height();
|
height = i.height();
|
||||||
format = TextureFormat::Bgra8UnormSrgb;
|
format = TextureFormat::Bgra8UnormSrgb;
|
||||||
|
|
||||||
data = i.into_raw();
|
data = i.into_raw();
|
||||||
}
|
}
|
||||||
image::DynamicImage::ImageLuma16(i) => {
|
image::DynamicImage::ImageLuma16(i) => {
|
||||||
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();
|
||||||
|
}
|
||||||
|
image::DynamicImage::ImageLumaA16(i) => {
|
||||||
|
width = i.width();
|
||||||
|
height = i.height();
|
||||||
|
format = TextureFormat::Rg16Uint;
|
||||||
|
|
||||||
data = cast_slice(&raw_data).to_owned();
|
let raw_data = i.into_raw();
|
||||||
}
|
|
||||||
image::DynamicImage::ImageLumaA16(i) => {
|
|
||||||
width = i.width();
|
|
||||||
height = i.height();
|
|
||||||
format = TextureFormat::Rg16Uint;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data = local_data;
|
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;
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
data = local_data;
|
||||||
|
}
|
||||||
|
image::DynamicImage::ImageRgba16(i) => {
|
||||||
|
width = i.width();
|
||||||
|
height = i.height();
|
||||||
|
format = TextureFormat::Rgba16Uint;
|
||||||
|
|
||||||
|
let raw_data = i.into_raw();
|
||||||
|
|
||||||
|
data = cast_slice(&raw_data).to_owned();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
image::DynamicImage::ImageRgba16(i) => {
|
|
||||||
width = i.width();
|
|
||||||
height = i.height();
|
|
||||||
format = TextureFormat::Rgba16Uint;
|
|
||||||
|
|
||||||
let raw_data = i.into_raw();
|
Texture::new(
|
||||||
|
Extent3d::new(width, height, 1),
|
||||||
data = cast_slice(&raw_data).to_owned();
|
TextureDimension::D2,
|
||||||
}
|
data,
|
||||||
}
|
format,
|
||||||
|
|
||||||
Texture::new(
|
|
||||||
Extent3d::new(width, height, 1),
|
|
||||||
TextureDimension::D2,
|
|
||||||
data,
|
|
||||||
format,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<image::DynamicImage> {
|
|
||||||
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,
|
#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
|
||||||
texture.data.clone(),
|
pub enum TextureConversionError {
|
||||||
)
|
#[error("Unsupported texture format")]
|
||||||
.map(image::DynamicImage::ImageLumaA8),
|
UnsupportedFormat,
|
||||||
TextureFormat::Rgba8UnormSrgb => image::ImageBuffer::from_raw(
|
#[error("Invalid texture size")]
|
||||||
texture.size.width,
|
InvalidSize,
|
||||||
texture.size.height,
|
}
|
||||||
texture.data.clone(),
|
|
||||||
)
|
impl TryFrom<Texture> for image::DynamicImage {
|
||||||
.map(image::DynamicImage::ImageRgba8),
|
type Error = TextureConversionError;
|
||||||
TextureFormat::Bgra8UnormSrgb => image::ImageBuffer::from_raw(
|
|
||||||
texture.size.width,
|
fn try_from(texture: Texture) -> Result<Self, Self::Error> {
|
||||||
texture.size.height,
|
match texture.format {
|
||||||
texture.data.clone(),
|
TextureFormat::R8Unorm => {
|
||||||
)
|
image::ImageBuffer::from_raw(texture.size.width, texture.size.height, texture.data)
|
||||||
.map(image::DynamicImage::ImageBgra8),
|
.map(image::DynamicImage::ImageLuma8)
|
||||||
_ => None,
|
.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),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user