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