goud_engine/assets/loaders/texture/
loader.rs1use image::{DynamicImage, ImageError};
4
5use crate::assets::{Asset, AssetLoadError, AssetLoader, LoadContext};
6
7use super::{asset::TextureAsset, format::TextureFormat, settings::TextureSettings};
8
9#[derive(Debug, Clone, Default)]
31pub struct TextureLoader;
32
33impl TextureLoader {
34 pub fn new() -> Self {
36 Self
37 }
38
39 pub(super) fn load_from_bytes(
41 bytes: &[u8],
42 settings: &TextureSettings,
43 format_hint: Option<TextureFormat>,
44 ) -> Result<TextureAsset, AssetLoadError> {
45 let img = if let Some(format) = format_hint.and_then(|f| f.to_image_format()) {
47 image::load_from_memory_with_format(bytes, format)
49 .or_else(|_| image::load_from_memory(bytes))
50 } else {
51 image::load_from_memory(bytes)
52 }
53 .map_err(Self::convert_image_error)?;
54
55 let img = if settings.flip_vertical {
57 img.flipv()
58 } else {
59 img
60 };
61
62 let rgba = img.to_rgba8();
63 let width = rgba.width();
64 let height = rgba.height();
65 let data = rgba.into_raw();
66
67 let format = format_hint
69 .unwrap_or_else(|| Self::detect_format(&img).unwrap_or(TextureFormat::Unknown));
70
71 Ok(TextureAsset {
72 data,
73 width,
74 height,
75 format,
76 })
77 }
78
79 fn detect_format(_img: &DynamicImage) -> Option<TextureFormat> {
84 None
85 }
86
87 fn convert_image_error(error: ImageError) -> AssetLoadError {
89 match error {
90 ImageError::IoError(e) => AssetLoadError::io_error("", e),
91 ImageError::Decoding(e) => AssetLoadError::decode_failed(e.to_string()),
92 ImageError::Encoding(e) => AssetLoadError::decode_failed(e.to_string()),
93 ImageError::Parameter(e) => AssetLoadError::decode_failed(e.to_string()),
94 ImageError::Limits(e) => AssetLoadError::decode_failed(e.to_string()),
95 ImageError::Unsupported(e) => AssetLoadError::decode_failed(e.to_string()),
96 }
97 }
98}
99
100impl AssetLoader for TextureLoader {
101 type Asset = TextureAsset;
102 type Settings = TextureSettings;
103
104 fn extensions(&self) -> &[&str] {
105 TextureAsset::extensions()
106 }
107
108 fn load<'a>(
109 &'a self,
110 bytes: &'a [u8],
111 settings: &'a Self::Settings,
112 context: &'a mut LoadContext,
113 ) -> Result<Self::Asset, AssetLoadError> {
114 let format_hint = context
116 .extension()
117 .map(TextureFormat::from_extension)
118 .filter(|f| *f != TextureFormat::Unknown);
119
120 Self::load_from_bytes(bytes, settings, format_hint)
121 }
122}