Skip to content

Commit

Permalink
expose texture/image conversions as From/TryFrom (#2175)
Browse files Browse the repository at this point in the history
fixes #2169 

Instead of having custom methods with reduced visibility, implement `From<image::DynamicImage> for Texture` and `TryFrom<Texture> for image::DynamicImage`
  • Loading branch information
mockersf committed Jun 8, 2021
1 parent cebb553 commit c2722f7
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 135 deletions.
259 changes: 134 additions & 125 deletions crates/bevy_render/src/texture/image_texture_conversion.rs
Original file line number Diff line number Diff line change
@@ -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<image::DynamicImage> for Texture {
fn from(dyn_img: image::DynamicImage) -> Self {
use bevy_core::cast_slice;
let width;
let height;

let data: Vec<u8>;
let format: TextureFormat;
let data: Vec<u8>;
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<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,
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<Texture> for image::DynamicImage {
type Error = TextureConversionError;

fn try_from(texture: Texture) -> Result<Self, Self::Error> {
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),
}
}
}
18 changes: 9 additions & 9 deletions crates/bevy_render/src/texture/texture.rs
Original file line number Diff line number Diff line change
@@ -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,
};
Expand Down Expand Up @@ -135,9 +134,10 @@ impl Texture {
/// - `TextureFormat::Rg8Unorm`
/// - `TextureFormat::Rgba8UnormSrgb`
/// - `TextureFormat::Bgra8UnormSrgb`
pub fn convert(&self, new_format: TextureFormat) -> Option<Self> {
super::image_texture_conversion::texture_to_image(self)
.and_then(|img| match new_format {
pub fn convert(self, new_format: TextureFormat) -> Option<Self> {
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()))
Expand All @@ -150,7 +150,7 @@ impl Texture {
}
_ => None,
})
.map(super::image_texture_conversion::image_to_texture)
.map(|image| image.into())
}

pub fn texture_resource_system(
Expand Down Expand Up @@ -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())
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_sprite/src/texture_atlas_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit c2722f7

Please sign in to comment.