bevy_image/
image_loader.rs1use crate::image::{Image, ImageFormat, ImageType, TextureError};
2use bevy_asset::{io::Reader, AssetLoader, LoadContext, RenderAssetUsages};
3use thiserror::Error;
4
5use super::{CompressedImageFormats, ImageSampler};
6use serde::{Deserialize, Serialize};
7
8#[derive(Clone)]
10pub struct ImageLoader {
11 supported_compressed_formats: CompressedImageFormats,
12}
13
14impl ImageLoader {
15 pub const SUPPORTED_FORMATS: &'static [ImageFormat] = &[
17 #[cfg(feature = "basis-universal")]
18 ImageFormat::Basis,
19 #[cfg(feature = "bmp")]
20 ImageFormat::Bmp,
21 #[cfg(feature = "dds")]
22 ImageFormat::Dds,
23 #[cfg(feature = "ff")]
24 ImageFormat::Farbfeld,
25 #[cfg(feature = "gif")]
26 ImageFormat::Gif,
27 #[cfg(feature = "ico")]
28 ImageFormat::Ico,
29 #[cfg(feature = "jpeg")]
30 ImageFormat::Jpeg,
31 #[cfg(feature = "ktx2")]
32 ImageFormat::Ktx2,
33 #[cfg(feature = "png")]
34 ImageFormat::Png,
35 #[cfg(feature = "pnm")]
36 ImageFormat::Pnm,
37 #[cfg(feature = "qoi")]
38 ImageFormat::Qoi,
39 #[cfg(feature = "tga")]
40 ImageFormat::Tga,
41 #[cfg(feature = "tiff")]
42 ImageFormat::Tiff,
43 #[cfg(feature = "webp")]
44 ImageFormat::WebP,
45 ];
46
47 const COUNT_FILE_EXTENSIONS: usize = {
49 let mut count = 0;
50 let mut idx = 0;
51 while idx < Self::SUPPORTED_FORMATS.len() {
52 count += Self::SUPPORTED_FORMATS[idx].to_file_extensions().len();
53 idx += 1;
54 }
55 count
56 };
57
58 pub const SUPPORTED_FILE_EXTENSIONS: &'static [&'static str] = &{
60 let mut exts = [""; Self::COUNT_FILE_EXTENSIONS];
61 let mut ext_idx = 0;
62 let mut fmt_idx = 0;
63 while fmt_idx < Self::SUPPORTED_FORMATS.len() {
64 let mut off = 0;
65 let fmt_exts = Self::SUPPORTED_FORMATS[fmt_idx].to_file_extensions();
66 while off < fmt_exts.len() {
67 exts[ext_idx] = fmt_exts[off];
68 off += 1;
69 ext_idx += 1;
70 }
71 fmt_idx += 1;
72 }
73 exts
74 };
75
76 pub fn new(supported_compressed_formats: CompressedImageFormats) -> Self {
78 Self {
79 supported_compressed_formats,
80 }
81 }
82}
83
84#[derive(Serialize, Deserialize, Default, Debug, Clone)]
86pub enum ImageFormatSetting {
87 #[default]
91 FromExtension,
92 Format(ImageFormat),
94 Guess,
97}
98
99#[derive(Serialize, Deserialize, Debug, Clone)]
101pub struct ImageLoaderSettings {
102 pub format: ImageFormatSetting,
104 #[serde(skip)]
110 pub texture_format: Option<wgpu_types::TextureFormat>,
111 pub is_srgb: bool,
115 pub sampler: ImageSampler,
118 pub asset_usage: RenderAssetUsages,
121}
122
123impl Default for ImageLoaderSettings {
124 fn default() -> Self {
125 Self {
126 format: ImageFormatSetting::default(),
127 texture_format: None,
128 is_srgb: true,
129 sampler: ImageSampler::Default,
130 asset_usage: RenderAssetUsages::default(),
131 }
132 }
133}
134
135#[non_exhaustive]
137#[derive(Debug, Error)]
138pub enum ImageLoaderError {
139 #[error("Failed to load image bytes: {0}")]
141 Io(#[from] std::io::Error),
142 #[error("Could not load texture file: {0}")]
144 FileTexture(#[from] FileTextureError),
145}
146
147impl AssetLoader for ImageLoader {
148 type Asset = Image;
149 type Settings = ImageLoaderSettings;
150 type Error = ImageLoaderError;
151 async fn load(
152 &self,
153 reader: &mut dyn Reader,
154 settings: &ImageLoaderSettings,
155 load_context: &mut LoadContext<'_>,
156 ) -> Result<Image, Self::Error> {
157 let mut bytes = Vec::new();
158 reader.read_to_end(&mut bytes).await?;
159 let image_type = match settings.format {
160 ImageFormatSetting::FromExtension => {
161 let ext = load_context.path().extension().unwrap().to_str().unwrap();
163 ImageType::Extension(ext)
164 }
165 ImageFormatSetting::Format(format) => ImageType::Format(format),
166 ImageFormatSetting::Guess => {
167 let format = image::guess_format(&bytes).map_err(|err| FileTextureError {
168 error: err.into(),
169 path: format!("{}", load_context.path().display()),
170 })?;
171 ImageType::Format(ImageFormat::from_image_crate_format(format).ok_or_else(
172 || FileTextureError {
173 error: TextureError::UnsupportedTextureFormat(format!("{format:?}")),
174 path: format!("{}", load_context.path().display()),
175 },
176 )?)
177 }
178 };
179 Ok(Image::from_buffer(
180 &bytes,
181 image_type,
182 self.supported_compressed_formats,
183 settings.is_srgb,
184 settings.sampler.clone(),
185 settings.asset_usage,
186 )
187 .map(|mut image| {
188 if let Some(format) = settings.texture_format {
189 image.texture_descriptor.format = format;
190 }
191 image
192 })
193 .map_err(|err| FileTextureError {
194 error: err,
195 path: format!("{}", load_context.path().display()),
196 })?)
197 }
198
199 fn extensions(&self) -> &[&str] {
200 Self::SUPPORTED_FILE_EXTENSIONS
201 }
202}
203
204#[derive(Error, Debug)]
206#[error("Error reading image file {path}: {error}.")]
207pub struct FileTextureError {
208 error: TextureError,
209 path: String,
210}