bevy/crates/bevy_image/src/compressed_image_saver.rs
andriyDev 73f7fd0c12
Move ImageLoader and CompressedImageSaver to bevy_image. (#15812)
# Objective

This is a follow-up to #15650. While the core `Image` stuff moved from
`bevy_render` to `bevy_image`, the `ImageLoader` and the
`CompressedImageSaver` remained in `bevy_render`.

## Solution

I moved `ImageLoader` and `CompressedImageSaver` to `bevy_image` and
re-exported everything out from `bevy_render`. The second step isn't
strictly necessary, but `bevy_render` is already doing this for all the
other `bevy_image` types, so I kept it the same for consistency.

Unfortunately I had to give `ImageLoader` a constructor so I can keep
the `RenderDevice` stuff in `bevy_render`.

## Testing

It compiles!

## Migration Guide

- `ImageLoader` can no longer be initialized directly through
`init_asset_loader`. Now you must use
`app.register_asset_loader(ImageLoader::new(supported_compressed_formats))`
(check out the implementation of `bevy_render::ImagePlugin`). This only
affects you if you are initializing the loader manually and does not
affect users of `bevy_render::ImagePlugin`.

## Followup work

- We should be able to move most of the `ImagePlugin` to `bevy_image`.
This would likely require an `ImagePlugin` and a `RenderImagePlugin` or
something though.
2024-10-15 02:18:10 +00:00

64 lines
2.3 KiB
Rust

use crate::{Image, ImageFormat, ImageFormatSetting, ImageLoader, ImageLoaderSettings};
use bevy_asset::saver::{AssetSaver, SavedAsset};
use derive_more::derive::{Display, Error, From};
use futures_lite::AsyncWriteExt;
pub struct CompressedImageSaver;
#[non_exhaustive]
#[derive(Debug, Error, Display, From)]
pub enum CompressedImageSaverError {
Io(std::io::Error),
}
impl AssetSaver for CompressedImageSaver {
type Asset = Image;
type Settings = ();
type OutputLoader = ImageLoader;
type Error = CompressedImageSaverError;
async fn save(
&self,
writer: &mut bevy_asset::io::Writer,
image: SavedAsset<'_, Self::Asset>,
_settings: &Self::Settings,
) -> Result<ImageLoaderSettings, Self::Error> {
let is_srgb = image.texture_descriptor.format.is_srgb();
let compressed_basis_data = {
let mut compressor_params = basis_universal::CompressorParams::new();
compressor_params.set_basis_format(basis_universal::BasisTextureFormat::UASTC4x4);
compressor_params.set_generate_mipmaps(true);
let color_space = if is_srgb {
basis_universal::ColorSpace::Srgb
} else {
basis_universal::ColorSpace::Linear
};
compressor_params.set_color_space(color_space);
compressor_params.set_uastc_quality_level(basis_universal::UASTC_QUALITY_DEFAULT);
let mut source_image = compressor_params.source_image_mut(0);
let size = image.size();
source_image.init(&image.data, size.x, size.y, 4);
let mut compressor = basis_universal::Compressor::new(4);
// SAFETY: the CompressorParams are "valid" to the best of our knowledge. The basis-universal
// library bindings note that invalid params might produce undefined behavior.
unsafe {
compressor.init(&compressor_params);
compressor.process().unwrap();
}
compressor.basis_file().to_vec()
};
writer.write_all(&compressed_basis_data).await?;
Ok(ImageLoaderSettings {
format: ImageFormatSetting::Format(ImageFormat::Basis),
is_srgb,
sampler: image.sampler.clone(),
asset_usage: image.asset_usage,
})
}
}