use bevy::asset::{AssetLoader, RenderAssetUsages};
use bevy::image::TextureFormatPixelInfo;
use bevy::log;
use bevy::prelude::*;
use bevy::render::render_resource::{Extent3d, TextureDimension, TextureFormat};
use bevy::tasks::ConditionalSendFuture;
use bevy::tasks::futures_lite::io;
use crate::error::AsepriteLoaderError;
use crate::{AsepriteAsset, AsepriteInfo};
#[derive(Debug, Default)]
pub struct AsepriteLoader;
impl AssetLoader for AsepriteLoader {
type Asset = AsepriteAsset;
type Settings = ();
type Error = AsepriteLoaderError;
fn load(
&self,
reader: &mut dyn bevy::asset::io::Reader,
_settings: &Self::Settings,
load_context: &mut bevy::asset::LoadContext,
) -> impl ConditionalSendFuture<Output = Result<Self::Asset, Self::Error>> {
Box::pin(async move {
log::debug!("Loading aseprite at {:?}", load_context.path());
let mut buffer = vec![];
reader.read_to_end(&mut buffer).await?;
let ase_data = bevy_aseprite_reader::Aseprite::from_bytes(buffer)?;
let frames = ase_data.frames();
let ase_images = frames.get_for(&(0..frames.count() as u16)).get_images()?;
let rects: Vec<URect> = ase_images
.iter()
.enumerate()
.map(|(i, texture)| {
let min_x = i as u32 * texture.width();
let min_y = 0;
let max_x = (i + 1) as u32 * texture.width();
let max_y = texture.height();
URect::new(min_x, min_y, max_x, max_y)
})
.collect();
let format = TextureFormat::bevy_default();
let textures = ase_images
.into_iter()
.map(|texture| {
Image::new(
Extent3d {
width: texture.width(),
height: texture.height(),
depth_or_array_layers: 1,
},
TextureDimension::D2,
texture.into_raw(),
format,
RenderAssetUsages::default(),
)
})
.collect::<Vec<_>>();
let info: AsepriteInfo = ase_data.into();
let (frame_width, frame_height) = info.dimensions;
let atlas_width = frame_width as u32 * info.frame_count as u32;
let atlas_height = frame_height as u32;
let mut atlas_texture = Image::new(
Extent3d {
width: atlas_width,
height: atlas_height,
depth_or_array_layers: 1,
},
TextureDimension::D2,
vec![0; format.pixel_size()? * (atlas_width * atlas_height) as usize],
format,
RenderAssetUsages::default(),
);
for (i, texture) in textures.into_iter().enumerate() {
let rect_width = frame_width as usize;
let rect_height = frame_height as usize;
let rect_x = i * frame_width as usize;
let rect_y = 0;
let atlas_width = atlas_width as usize;
let format_size = format.pixel_size()?;
for (texture_y, bound_y) in (rect_y..rect_y + rect_height).enumerate() {
let begin = (bound_y * atlas_width + rect_x) * format_size;
let end = begin + rect_width * format_size;
let texture_begin = texture_y * rect_width * format_size;
let texture_end = texture_begin + rect_width * format_size;
let atlas_data = atlas_texture.data.as_mut().ok_or_else(|| {
AsepriteLoaderError::Io(io::Error::other("No atlas data"))
})?;
let image_data = texture.data.as_ref().ok_or_else(|| {
AsepriteLoaderError::Io(io::Error::other("No image data"))
})?;
atlas_data[begin..end].copy_from_slice(&image_data[texture_begin..texture_end]);
}
}
let atlas_texture = load_context.add_labeled_asset("image".into(), atlas_texture);
let mut atlas_layout =
TextureAtlasLayout::new_empty(UVec2::new(atlas_width, atlas_height));
atlas_layout.textures = rects;
let atlas_layout = load_context.add_labeled_asset("atlas".into(), atlas_layout);
Ok(AsepriteAsset {
info,
atlas_texture,
atlas_layout,
})
})
}
fn extensions(&self) -> &[&str] {
&["ase", "aseprite"]
}
}