Skip to content

Commit

Permalink
Texture atlas format and conversion (#1365)
Browse files Browse the repository at this point in the history
* can specify texture format for a texture atlas
* add automatic conversion
  • Loading branch information
mockersf authored Feb 1, 2021
1 parent cf5f3b5 commit 6b8f8a7
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 119 deletions.
264 changes: 150 additions & 114 deletions crates/bevy_render/src/texture/image_texture_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ impl AssetLoader for ImageTextureLoader {
load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, Result<()>> {
Box::pin(async move {
use bevy_core::AsBytes;

// Find the image type we expect. A file with the extension "png" should
// probably load as a PNG.

Expand All @@ -41,118 +39,7 @@ impl AssetLoader for ImageTextureLoader {

let dyn_img = image::load_from_memory_with_format(bytes, img_format)?;

let width;
let height;

let data: Vec<u8>;
let format: TextureFormat;

match dyn_img {
image::DynamicImage::ImageLuma8(i) => {
width = i.width();
height = i.height();
format = TextureFormat::R8Unorm;

data = i.into_raw();
}
image::DynamicImage::ImageLumaA8(i) => {
width = i.width();
height = i.height();
format = TextureFormat::Rg8Unorm;

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::ImageBgr8(i) => {
let i = image::DynamicImage::ImageBgr8(i).into_bgra8();

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;

let raw_data = i.into_raw();

data = raw_data.as_slice().as_bytes().to_owned();
}
image::DynamicImage::ImageLumaA16(i) => {
width = i.width();
height = i.height();
format = TextureFormat::Rg16Uint;

let raw_data = i.into_raw();

data = raw_data.as_slice().as_bytes().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::ImageRgba16(i) => {
width = i.width();
height = i.height();
format = TextureFormat::Rgba16Uint;

let raw_data = i.into_raw();

data = raw_data.as_slice().as_bytes().to_owned();
}
}

let texture = Texture::new(
Extent3d::new(width, height, 1),
TextureDimension::D2,
data,
format,
);
load_context.set_default_asset(LoadedAsset::new(texture));
load_context.set_default_asset(LoadedAsset::new(image_to_texture(dyn_img)));
Ok(())
})
}
Expand All @@ -173,3 +60,152 @@ mod tests {
}
}
}

/// Helper method to convert a `DynamicImage` to a `Texture`
pub(crate) fn image_to_texture(dyn_img: image::DynamicImage) -> Texture {
use bevy_core::AsBytes;

let width;
let height;

let data: Vec<u8>;
let format: TextureFormat;

match dyn_img {
image::DynamicImage::ImageLuma8(i) => {
width = i.width();
height = i.height();
format = TextureFormat::R8Unorm;

data = i.into_raw();
}
image::DynamicImage::ImageLumaA8(i) => {
width = i.width();
height = i.height();
format = TextureFormat::Rg8Unorm;

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::ImageBgr8(i) => {
let i = image::DynamicImage::ImageBgr8(i).into_bgra8();

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;

let raw_data = i.into_raw();

data = raw_data.as_slice().as_bytes().to_owned();
}
image::DynamicImage::ImageLumaA16(i) => {
width = i.width();
height = i.height();
format = TextureFormat::Rg16Uint;

let raw_data = i.into_raw();

data = raw_data.as_slice().as_bytes().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::ImageRgba16(i) => {
width = i.width();
height = i.height();
format = TextureFormat::Rgba16Uint;

let raw_data = i.into_raw();

data = raw_data.as_slice().as_bytes().to_owned();
}
}

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,
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,
}
}
31 changes: 31 additions & 0 deletions crates/bevy_render/src/texture/texture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,37 @@ impl Texture {
});
}

/// Convert a texture from a format to another
/// Only a few formats are supported as input and output:
/// - `TextureFormat::R8Unorm`
/// - `TextureFormat::Rg8Unorm`
/// - `TextureFormat::Rgba8UnormSrgb`
/// - `TextureFormat::Bgra8UnormSrgb`
#[cfg(any(
feature = "png",
feature = "dds",
feature = "tga",
feature = "jpeg",
feature = "bmp"
))]
pub fn convert(&self, new_format: TextureFormat) -> Option<Self> {
super::texture_to_image(self)
.and_then(|img| match new_format {
TextureFormat::R8Unorm => Some(image::DynamicImage::ImageLuma8(img.into_luma8())),
TextureFormat::Rg8Unorm => {
Some(image::DynamicImage::ImageLumaA8(img.into_luma_alpha8()))
}
TextureFormat::Rgba8UnormSrgb => {
Some(image::DynamicImage::ImageRgba8(img.into_rgba8()))
}
TextureFormat::Bgra8UnormSrgb => {
Some(image::DynamicImage::ImageBgra8(img.into_bgra8()))
}
_ => None,
})
.map(super::image_to_texture)
}

pub fn texture_resource_system(
render_resource_context: Res<Box<dyn RenderResourceContext>>,
textures: Res<Assets<Texture>>,
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_sprite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ bevy_app = { path = "../bevy_app", version = "0.4.0" }
bevy_asset = { path = "../bevy_asset", version = "0.4.0" }
bevy_core = { path = "../bevy_core", version = "0.4.0" }
bevy_ecs = { path = "../bevy_ecs", version = "0.4.0" }
bevy_log = { path = "../bevy_log", version = "0.4.0" }
bevy_math = { path = "../bevy_math", version = "0.4.0" }
bevy_reflect = { path = "../bevy_reflect", version = "0.4.0", features = ["bevy"] }
bevy_render = { path = "../bevy_render", version = "0.4.0" }
Expand Down
Loading

0 comments on commit 6b8f8a7

Please sign in to comment.