bevy_render/texture/
image_loader.rs1use bevy_asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext};
2use bevy_ecs::prelude::{FromWorld, World};
3use thiserror::Error;
4
5use crate::{
6 render_asset::RenderAssetUsages,
7 renderer::RenderDevice,
8 texture::{Image, ImageFormat, ImageType, TextureError},
9};
10
11use super::{CompressedImageFormats, ImageSampler};
12use serde::{Deserialize, Serialize};
13
14#[derive(Clone)]
16pub struct ImageLoader {
17 supported_compressed_formats: CompressedImageFormats,
18}
19
20pub(crate) const IMG_FILE_EXTENSIONS: &[&str] = &[
21 #[cfg(feature = "basis-universal")]
22 "basis",
23 #[cfg(feature = "bmp")]
24 "bmp",
25 #[cfg(feature = "png")]
26 "png",
27 #[cfg(feature = "dds")]
28 "dds",
29 #[cfg(feature = "tga")]
30 "tga",
31 #[cfg(feature = "jpeg")]
32 "jpg",
33 #[cfg(feature = "jpeg")]
34 "jpeg",
35 #[cfg(feature = "ktx2")]
36 "ktx2",
37 #[cfg(feature = "webp")]
38 "webp",
39 #[cfg(feature = "pnm")]
40 "pam",
41 #[cfg(feature = "pnm")]
42 "pbm",
43 #[cfg(feature = "pnm")]
44 "pgm",
45 #[cfg(feature = "pnm")]
46 "ppm",
47];
48
49#[derive(Serialize, Deserialize, Default, Debug)]
50pub enum ImageFormatSetting {
51 #[default]
52 FromExtension,
53 Format(ImageFormat),
54 Guess,
55}
56
57#[derive(Serialize, Deserialize, Debug)]
58pub struct ImageLoaderSettings {
59 pub format: ImageFormatSetting,
60 pub is_srgb: bool,
61 pub sampler: ImageSampler,
62 pub asset_usage: RenderAssetUsages,
63}
64
65impl Default for ImageLoaderSettings {
66 fn default() -> Self {
67 Self {
68 format: ImageFormatSetting::default(),
69 is_srgb: true,
70 sampler: ImageSampler::Default,
71 asset_usage: RenderAssetUsages::default(),
72 }
73 }
74}
75
76#[non_exhaustive]
77#[derive(Debug, Error)]
78pub enum ImageLoaderError {
79 #[error("Could load shader: {0}")]
80 Io(#[from] std::io::Error),
81 #[error("Could not load texture file: {0}")]
82 FileTexture(#[from] FileTextureError),
83}
84
85impl AssetLoader for ImageLoader {
86 type Asset = Image;
87 type Settings = ImageLoaderSettings;
88 type Error = ImageLoaderError;
89 async fn load<'a>(
90 &'a self,
91 reader: &'a mut Reader<'_>,
92 settings: &'a ImageLoaderSettings,
93 load_context: &'a mut LoadContext<'_>,
94 ) -> Result<Image, Self::Error> {
95 let mut bytes = Vec::new();
96 reader.read_to_end(&mut bytes).await?;
97 let image_type = match settings.format {
98 ImageFormatSetting::FromExtension => {
99 let ext = load_context.path().extension().unwrap().to_str().unwrap();
101 ImageType::Extension(ext)
102 }
103 ImageFormatSetting::Format(format) => ImageType::Format(format),
104 ImageFormatSetting::Guess => {
105 let format = image::guess_format(&bytes).map_err(|err| FileTextureError {
106 error: err.into(),
107 path: format!("{}", load_context.path().display()),
108 })?;
109 ImageType::Format(ImageFormat::from_image_crate_format(format).ok_or_else(
110 || FileTextureError {
111 error: TextureError::UnsupportedTextureFormat(format!("{format:?}")),
112 path: format!("{}", load_context.path().display()),
113 },
114 )?)
115 }
116 };
117 Ok(Image::from_buffer(
118 #[cfg(all(debug_assertions, feature = "dds"))]
119 load_context.path().display().to_string(),
120 &bytes,
121 image_type,
122 self.supported_compressed_formats,
123 settings.is_srgb,
124 settings.sampler.clone(),
125 settings.asset_usage,
126 )
127 .map_err(|err| FileTextureError {
128 error: err,
129 path: format!("{}", load_context.path().display()),
130 })?)
131 }
132
133 fn extensions(&self) -> &[&str] {
134 IMG_FILE_EXTENSIONS
135 }
136}
137
138impl FromWorld for ImageLoader {
139 fn from_world(world: &mut World) -> Self {
140 let supported_compressed_formats = match world.get_resource::<RenderDevice>() {
141 Some(render_device) => CompressedImageFormats::from_features(render_device.features()),
142
143 None => CompressedImageFormats::NONE,
144 };
145 Self {
146 supported_compressed_formats,
147 }
148 }
149}
150
151#[derive(Error, Debug)]
153pub struct FileTextureError {
154 error: TextureError,
155 path: String,
156}
157impl std::fmt::Display for FileTextureError {
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
159 write!(
160 f,
161 "Error reading image file {}: {}, this is an error in `bevy_render`.",
162 self.path, self.error
163 )
164 }
165}