bevy_image/
compressed_image_saver.rs1use crate::{Image, ImageFormat, ImageFormatSetting, ImageLoader, ImageLoaderSettings};
2
3use bevy_asset::{
4 saver::{AssetSaver, SavedAsset},
5 AssetPath,
6};
7use bevy_reflect::TypePath;
8use futures_lite::AsyncWriteExt;
9use thiserror::Error;
10
11#[derive(const _: () =
{
#[allow(deprecated, reason =
"derives on a deprecated type shouldn't be considered a usage")]
impl bevy_reflect::TypePath for CompressedImageSaver where {
fn type_path() -> &'static str {
"bevy_image::compressed_image_saver::CompressedImageSaver"
}
fn short_type_path() -> &'static str { "CompressedImageSaver" }
fn type_ident() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("CompressedImageSaver")
}
fn crate_name() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("bevy_image::compressed_image_saver".split(':').next().unwrap())
}
fn module_path() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("bevy_image::compressed_image_saver")
}
}
};TypePath)]
13pub struct CompressedImageSaver;
14
15#[non_exhaustive]
17#[derive(#[automatically_derived]
impl ::core::fmt::Debug for CompressedImageSaverError {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
CompressedImageSaverError::Io(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Io",
&__self_0),
CompressedImageSaverError::UninitializedImage =>
::core::fmt::Formatter::write_str(f, "UninitializedImage"),
}
}
}Debug, fn from(source: std::io::Error) -> Self {
CompressedImageSaverError::Io { 0: source }
}Error, const _: () =
{
#[allow(deprecated, reason =
"derives on a deprecated type shouldn't be considered a usage")]
impl bevy_reflect::TypePath for CompressedImageSaverError where {
fn type_path() -> &'static str {
"bevy_image::compressed_image_saver::CompressedImageSaverError"
}
fn short_type_path() -> &'static str {
"CompressedImageSaverError"
}
fn type_ident() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("CompressedImageSaverError")
}
fn crate_name() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("bevy_image::compressed_image_saver".split(':').next().unwrap())
}
fn module_path() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("bevy_image::compressed_image_saver")
}
}
};TypePath)]
18pub enum CompressedImageSaverError {
19 #[error(transparent)]
21 Io(#[from] std::io::Error),
22 #[error("Cannot compress an uninitialized image")]
24 UninitializedImage,
25}
26
27impl AssetSaver for CompressedImageSaver {
28 type Asset = Image;
29
30 type Settings = ();
31 type OutputLoader = ImageLoader;
32 type Error = CompressedImageSaverError;
33
34 async fn save(
35 &self,
36 writer: &mut bevy_asset::io::Writer,
37 image: SavedAsset<'_, '_, Self::Asset>,
38 _settings: &Self::Settings,
39 _asset_path: AssetPath<'_>,
40 ) -> Result<ImageLoaderSettings, Self::Error> {
41 let is_srgb = image.texture_descriptor.format.is_srgb();
42
43 let compressed_basis_data = {
44 let mut compressor_params = basis_universal::CompressorParams::new();
45 compressor_params.set_basis_format(basis_universal::BasisTextureFormat::UASTC4x4);
46 compressor_params.set_generate_mipmaps(true);
47 let color_space = if is_srgb {
48 basis_universal::ColorSpace::Srgb
49 } else {
50 basis_universal::ColorSpace::Linear
51 };
52 compressor_params.set_color_space(color_space);
53 compressor_params.set_uastc_quality_level(basis_universal::UASTC_QUALITY_DEFAULT);
54
55 let mut source_image = compressor_params.source_image_mut(0);
56 let size = image.size();
57 let Some(ref data) = image.data else {
58 return Err(CompressedImageSaverError::UninitializedImage);
59 };
60 source_image.init(data, size.x, size.y, 4);
61
62 let mut compressor = basis_universal::Compressor::new(4);
63 #[expect(
64 unsafe_code,
65 reason = "The basis-universal compressor cannot be interacted with except through unsafe functions"
66 )]
67 unsafe {
70 compressor.init(&compressor_params);
71 compressor.process().unwrap();
72 }
73 compressor.basis_file().to_vec()
74 };
75
76 writer.write_all(&compressed_basis_data).await?;
77 Ok(ImageLoaderSettings {
78 format: ImageFormatSetting::Format(ImageFormat::Basis),
79 is_srgb,
80 sampler: image.sampler.clone(),
81 asset_usage: image.asset_usage,
82 texture_format: None,
83 array_layout: None,
84 })
85 }
86}