use std::collections::{HashMap, HashSet};
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
Spec(&'static str),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Spec(info) => write!(f, "{}", info),
}
}
}
impl std::error::Error for Error {}
pub struct Prefs {
pub unit_size: u32,
pub padding: u32,
pub uv_inset: f32,
pub trim_transparent: bool,
pub atlas_size_limit: u32,
pub atlas_square: bool,
pub atlas_pot: bool,
pub ppu: f32,
pub pivot: Pivot,
pub on_progress: Option<ProgressCallback>,
}
impl Default for Prefs {
fn default() -> Self {
Self {
unit_size: 64,
padding: 2,
uv_inset: 0.0,
trim_transparent: true,
atlas_size_limit: 2048,
atlas_square: false,
atlas_pot: false,
ppu: 100.0,
pivot: Pivot { x: 0.5, y: 0.5 },
on_progress: None,
}
}
}
pub type ProgressCallback = Box<dyn Fn(Progress)>;
#[derive(Debug, Clone)]
pub struct Progress {
pub ratio: f32,
pub activity: String,
}
impl Progress {
pub fn report(prefs: &Prefs, stage: u8, idx: usize, len: usize, activity: &str) {
if let Some(cb) = &prefs.on_progress {
let num = idx + 1;
let ratio = (stage as f32 / 5.0) + 0.2 * (num as f32 / len as f32);
let activity = format!("{activity}... ({num} of {len})");
cb(Progress { ratio, activity });
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
pub struct Pixel([u8; 4]);
impl Pixel {
pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
Pixel([r, g, b, a])
}
pub const fn from_raw(raw: [u8; 4]) -> Self {
Pixel(raw)
}
pub fn r(&self) -> u8 {
self.0[0]
}
pub fn g(&self) -> u8 {
self.0[1]
}
pub fn b(&self) -> u8 {
self.0[2]
}
pub fn a(&self) -> u8 {
self.0[3]
}
pub fn to_raw(self) -> [u8; 4] {
self.0
}
}
#[derive(Debug, Clone)]
pub struct Texture {
pub width: u32,
pub height: u32,
pub pixels: Vec<Pixel>,
}
#[derive(Debug, Clone)]
pub struct SourceSprite {
pub id: String,
pub texture: Texture,
pub pivot: Option<Pivot>,
}
#[derive(Debug, Clone)]
pub struct Artifacts {
pub atlases: Vec<Texture>,
pub sprites: Vec<DicedSprite>,
}
#[derive(Debug, Clone)]
pub struct DicedSprite {
pub id: String,
pub atlas_index: usize,
pub vertices: Vec<Vertex>,
pub uvs: Vec<Uv>,
pub indices: Vec<usize>,
pub rect: Rect,
pub pivot: Pivot,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Rect {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
}
impl Rect {
pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
Rect {
x,
y,
width,
height,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Pivot {
pub x: f32,
pub y: f32,
}
impl Pivot {
pub fn new(x: f32, y: f32) -> Self {
Pivot { x, y }
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Vertex {
pub x: f32,
pub y: f32,
}
impl Vertex {
pub fn new(x: f32, y: f32) -> Self {
Vertex { x, y }
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Uv {
pub u: f32,
pub v: f32,
}
impl Uv {
pub fn new(u: f32, v: f32) -> Self {
Uv { u, v }
}
}
#[derive(Debug, Clone)]
pub(crate) struct DicedTexture {
pub id: String,
pub size: USize,
pub pivot: Option<Pivot>,
pub units: Vec<DicedUnit>,
pub unique: HashSet<u64>,
}
#[derive(Debug, Clone)]
pub(crate) struct DicedUnit {
pub rect: URect,
pub pixels: Vec<Pixel>,
pub hash: u64,
}
#[derive(Debug, Clone)]
pub(crate) struct Atlas {
pub texture: Texture,
pub rects: HashMap<u64, FRect>,
pub packed: Vec<DicedTexture>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct URect {
pub x: u32,
pub y: u32,
pub width: u32,
pub height: u32,
}
impl URect {
#[allow(dead_code)] pub fn new(x: u32, y: u32, width: u32, height: u32) -> Self {
URect {
x,
y,
width,
height,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct IRect {
pub x: i32,
pub y: i32,
pub width: u32,
pub height: u32,
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct FRect {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
}
impl FRect {
pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
FRect {
x,
y,
width,
height,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct USize {
pub width: u32,
pub height: u32,
}
impl USize {
pub fn new(width: u32, height: u32) -> Self {
USize { width, height }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_create_pixel_from_raw() {
let pixel = Pixel::from_raw([1, 2, 3, 4]);
assert_eq!(pixel.r(), 1);
assert_eq!(pixel.g(), 2);
assert_eq!(pixel.b(), 3);
assert_eq!(pixel.a(), 4);
}
}