Skip to content

Commit c2722f7

Browse files
committed
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`
1 parent cebb553 commit c2722f7

File tree

3 files changed

+144
-135
lines changed

3 files changed

+144
-135
lines changed
Lines changed: 134 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,151 +1,160 @@
1+
use std::convert::TryFrom;
2+
use thiserror::Error;
3+
14
use super::{Extent3d, Texture, TextureDimension, TextureFormat};
25

3-
/// Helper method to convert a `DynamicImage` to a `Texture`
4-
pub(crate) fn image_to_texture(dyn_img: image::DynamicImage) -> Texture {
5-
use bevy_core::cast_slice;
6-
let width;
7-
let height;
6+
impl From<image::DynamicImage> for Texture {
7+
fn from(dyn_img: image::DynamicImage) -> Self {
8+
use bevy_core::cast_slice;
9+
let width;
10+
let height;
811

9-
let data: Vec<u8>;
10-
let format: TextureFormat;
12+
let data: Vec<u8>;
13+
let format: TextureFormat;
1114

12-
match dyn_img {
13-
image::DynamicImage::ImageLuma8(i) => {
14-
let i = image::DynamicImage::ImageLuma8(i).into_rgba8();
15-
width = i.width();
16-
height = i.height();
17-
format = TextureFormat::Rgba8UnormSrgb;
15+
match dyn_img {
16+
image::DynamicImage::ImageLuma8(i) => {
17+
let i = image::DynamicImage::ImageLuma8(i).into_rgba8();
18+
width = i.width();
19+
height = i.height();
20+
format = TextureFormat::Rgba8UnormSrgb;
1821

19-
data = i.into_raw();
20-
}
21-
image::DynamicImage::ImageLumaA8(i) => {
22-
let i = image::DynamicImage::ImageLumaA8(i).into_rgba8();
23-
width = i.width();
24-
height = i.height();
25-
format = TextureFormat::Rgba8UnormSrgb;
22+
data = i.into_raw();
23+
}
24+
image::DynamicImage::ImageLumaA8(i) => {
25+
let i = image::DynamicImage::ImageLumaA8(i).into_rgba8();
26+
width = i.width();
27+
height = i.height();
28+
format = TextureFormat::Rgba8UnormSrgb;
2629

27-
data = i.into_raw();
28-
}
29-
image::DynamicImage::ImageRgb8(i) => {
30-
let i = image::DynamicImage::ImageRgb8(i).into_rgba8();
31-
width = i.width();
32-
height = i.height();
33-
format = TextureFormat::Rgba8UnormSrgb;
30+
data = i.into_raw();
31+
}
32+
image::DynamicImage::ImageRgb8(i) => {
33+
let i = image::DynamicImage::ImageRgb8(i).into_rgba8();
34+
width = i.width();
35+
height = i.height();
36+
format = TextureFormat::Rgba8UnormSrgb;
3437

35-
data = i.into_raw();
36-
}
37-
image::DynamicImage::ImageRgba8(i) => {
38-
width = i.width();
39-
height = i.height();
40-
format = TextureFormat::Rgba8UnormSrgb;
38+
data = i.into_raw();
39+
}
40+
image::DynamicImage::ImageRgba8(i) => {
41+
width = i.width();
42+
height = i.height();
43+
format = TextureFormat::Rgba8UnormSrgb;
4144

42-
data = i.into_raw();
43-
}
44-
image::DynamicImage::ImageBgr8(i) => {
45-
let i = image::DynamicImage::ImageBgr8(i).into_bgra8();
45+
data = i.into_raw();
46+
}
47+
image::DynamicImage::ImageBgr8(i) => {
48+
let i = image::DynamicImage::ImageBgr8(i).into_bgra8();
4649

47-
width = i.width();
48-
height = i.height();
49-
format = TextureFormat::Bgra8UnormSrgb;
50+
width = i.width();
51+
height = i.height();
52+
format = TextureFormat::Bgra8UnormSrgb;
5053

51-
data = i.into_raw();
52-
}
53-
image::DynamicImage::ImageBgra8(i) => {
54-
width = i.width();
55-
height = i.height();
56-
format = TextureFormat::Bgra8UnormSrgb;
54+
data = i.into_raw();
55+
}
56+
image::DynamicImage::ImageBgra8(i) => {
57+
width = i.width();
58+
height = i.height();
59+
format = TextureFormat::Bgra8UnormSrgb;
5760

58-
data = i.into_raw();
59-
}
60-
image::DynamicImage::ImageLuma16(i) => {
61-
width = i.width();
62-
height = i.height();
63-
format = TextureFormat::R16Uint;
61+
data = i.into_raw();
62+
}
63+
image::DynamicImage::ImageLuma16(i) => {
64+
width = i.width();
65+
height = i.height();
66+
format = TextureFormat::R16Uint;
67+
let raw_data = i.into_raw();
6468

65-
let raw_data = i.into_raw();
69+
data = cast_slice(&raw_data).to_owned();
70+
}
71+
image::DynamicImage::ImageLumaA16(i) => {
72+
width = i.width();
73+
height = i.height();
74+
format = TextureFormat::Rg16Uint;
6675

67-
data = cast_slice(&raw_data).to_owned();
68-
}
69-
image::DynamicImage::ImageLumaA16(i) => {
70-
width = i.width();
71-
height = i.height();
72-
format = TextureFormat::Rg16Uint;
76+
let raw_data = i.into_raw();
7377

74-
let raw_data = i.into_raw();
78+
data = cast_slice(&raw_data).to_owned();
79+
}
7580

76-
data = cast_slice(&raw_data).to_owned();
77-
}
81+
image::DynamicImage::ImageRgb16(image) => {
82+
width = image.width();
83+
height = image.height();
84+
format = TextureFormat::Rgba16Uint;
7885

79-
image::DynamicImage::ImageRgb16(image) => {
80-
width = image.width();
81-
height = image.height();
82-
format = TextureFormat::Rgba16Uint;
83-
84-
let mut local_data =
85-
Vec::with_capacity(width as usize * height as usize * format.pixel_size());
86-
87-
for pixel in image.into_raw().chunks_exact(3) {
88-
// TODO unsafe_get in release builds?
89-
let r = pixel[0];
90-
let g = pixel[1];
91-
let b = pixel[2];
92-
let a = u16::max_value();
93-
94-
local_data.extend_from_slice(&r.to_ne_bytes());
95-
local_data.extend_from_slice(&g.to_ne_bytes());
96-
local_data.extend_from_slice(&b.to_ne_bytes());
97-
local_data.extend_from_slice(&a.to_ne_bytes());
98-
}
86+
let mut local_data =
87+
Vec::with_capacity(width as usize * height as usize * format.pixel_size());
9988

100-
data = local_data;
101-
}
102-
image::DynamicImage::ImageRgba16(i) => {
103-
width = i.width();
104-
height = i.height();
105-
format = TextureFormat::Rgba16Uint;
89+
for pixel in image.into_raw().chunks_exact(3) {
90+
// TODO unsafe_get in release builds?
91+
let r = pixel[0];
92+
let g = pixel[1];
93+
let b = pixel[2];
94+
let a = u16::MAX;
95+
96+
local_data.extend_from_slice(&r.to_ne_bytes());
97+
local_data.extend_from_slice(&g.to_ne_bytes());
98+
local_data.extend_from_slice(&b.to_ne_bytes());
99+
local_data.extend_from_slice(&a.to_ne_bytes());
100+
}
106101

107-
let raw_data = i.into_raw();
102+
data = local_data;
103+
}
104+
image::DynamicImage::ImageRgba16(i) => {
105+
width = i.width();
106+
height = i.height();
107+
format = TextureFormat::Rgba16Uint;
108108

109-
data = cast_slice(&raw_data).to_owned();
109+
let raw_data = i.into_raw();
110+
111+
data = cast_slice(&raw_data).to_owned();
112+
}
110113
}
114+
115+
Texture::new(
116+
Extent3d::new(width, height, 1),
117+
TextureDimension::D2,
118+
data,
119+
format,
120+
)
111121
}
122+
}
112123

113-
Texture::new(
114-
Extent3d::new(width, height, 1),
115-
TextureDimension::D2,
116-
data,
117-
format,
118-
)
124+
#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
125+
pub enum TextureConversionError {
126+
#[error("Unsupported texture format")]
127+
UnsupportedFormat,
128+
#[error("Invalid texture size")]
129+
InvalidSize,
119130
}
120131

121-
/// Helper method to convert a `Texture` to a `DynamicImage`. Not all `Texture` formats are
122-
/// covered, it will return `None` if the format is not supported
123-
pub(crate) fn texture_to_image(texture: &Texture) -> Option<image::DynamicImage> {
124-
match texture.format {
125-
TextureFormat::R8Unorm => image::ImageBuffer::from_raw(
126-
texture.size.width,
127-
texture.size.height,
128-
texture.data.clone(),
129-
)
130-
.map(image::DynamicImage::ImageLuma8),
131-
TextureFormat::Rg8Unorm => image::ImageBuffer::from_raw(
132-
texture.size.width,
133-
texture.size.height,
134-
texture.data.clone(),
135-
)
136-
.map(image::DynamicImage::ImageLumaA8),
137-
TextureFormat::Rgba8UnormSrgb => image::ImageBuffer::from_raw(
138-
texture.size.width,
139-
texture.size.height,
140-
texture.data.clone(),
141-
)
142-
.map(image::DynamicImage::ImageRgba8),
143-
TextureFormat::Bgra8UnormSrgb => image::ImageBuffer::from_raw(
144-
texture.size.width,
145-
texture.size.height,
146-
texture.data.clone(),
147-
)
148-
.map(image::DynamicImage::ImageBgra8),
149-
_ => None,
132+
impl TryFrom<Texture> for image::DynamicImage {
133+
type Error = TextureConversionError;
134+
135+
fn try_from(texture: Texture) -> Result<Self, Self::Error> {
136+
match texture.format {
137+
TextureFormat::R8Unorm => {
138+
image::ImageBuffer::from_raw(texture.size.width, texture.size.height, texture.data)
139+
.map(image::DynamicImage::ImageLuma8)
140+
.ok_or(TextureConversionError::InvalidSize)
141+
}
142+
TextureFormat::Rg8Unorm => {
143+
image::ImageBuffer::from_raw(texture.size.width, texture.size.height, texture.data)
144+
.map(image::DynamicImage::ImageLumaA8)
145+
.ok_or(TextureConversionError::InvalidSize)
146+
}
147+
TextureFormat::Rgba8UnormSrgb => {
148+
image::ImageBuffer::from_raw(texture.size.width, texture.size.height, texture.data)
149+
.map(image::DynamicImage::ImageRgba8)
150+
.ok_or(TextureConversionError::InvalidSize)
151+
}
152+
TextureFormat::Bgra8UnormSrgb => {
153+
image::ImageBuffer::from_raw(texture.size.width, texture.size.height, texture.data)
154+
.map(image::DynamicImage::ImageBgra8)
155+
.ok_or(TextureConversionError::InvalidSize)
156+
}
157+
_ => Err(TextureConversionError::UnsupportedFormat),
158+
}
150159
}
151160
}

crates/bevy_render/src/texture/texture.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
use super::{
2-
image_texture_conversion::image_to_texture, Extent3d, SamplerDescriptor, TextureDescriptor,
3-
TextureDimension, TextureFormat,
4-
};
1+
use std::convert::TryInto;
2+
3+
use super::{Extent3d, SamplerDescriptor, TextureDescriptor, TextureDimension, TextureFormat};
54
use crate::renderer::{
65
RenderResource, RenderResourceContext, RenderResourceId, RenderResourceType,
76
};
@@ -135,9 +134,10 @@ impl Texture {
135134
/// - `TextureFormat::Rg8Unorm`
136135
/// - `TextureFormat::Rgba8UnormSrgb`
137136
/// - `TextureFormat::Bgra8UnormSrgb`
138-
pub fn convert(&self, new_format: TextureFormat) -> Option<Self> {
139-
super::image_texture_conversion::texture_to_image(self)
140-
.and_then(|img| match new_format {
137+
pub fn convert(self, new_format: TextureFormat) -> Option<Self> {
138+
self.try_into()
139+
.ok()
140+
.and_then(|img: image::DynamicImage| match new_format {
141141
TextureFormat::R8Unorm => Some(image::DynamicImage::ImageLuma8(img.into_luma8())),
142142
TextureFormat::Rg8Unorm => {
143143
Some(image::DynamicImage::ImageLumaA8(img.into_luma_alpha8()))
@@ -150,7 +150,7 @@ impl Texture {
150150
}
151151
_ => None,
152152
})
153-
.map(super::image_texture_conversion::image_to_texture)
153+
.map(|image| image.into())
154154
}
155155

156156
pub fn texture_resource_system(
@@ -243,7 +243,7 @@ impl Texture {
243243
// cases.
244244

245245
let dyn_img = image::load_from_memory_with_format(buffer, format)?;
246-
Ok(image_to_texture(dyn_img))
246+
Ok(dyn_img.into())
247247
}
248248
}
249249

crates/bevy_sprite/src/texture_atlas_builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ impl TextureAtlasBuilder {
113113
) {
114114
if self.format == texture.format {
115115
Self::copy_texture_to_atlas(atlas_texture, texture, packed_location);
116-
} else if let Some(converted_texture) = texture.convert(self.format) {
116+
} else if let Some(converted_texture) = texture.clone().convert(self.format) {
117117
debug!(
118118
"Converting texture from '{:?}' to '{:?}'",
119119
texture.format, self.format

0 commit comments

Comments
 (0)