2026-05-23 19:19:28 +01:00

82 lines
2.9 KiB
Rust

use crate::color::Color;
use serde::{Deserialize, Serialize};
pub const GLYPH_BANK_PALETTE_COUNT_V1: usize = 64;
pub const GLYPH_BANK_COLORS_PER_PALETTE: usize = 16;
/// Standard sizes for square tiles.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub enum TileSize {
/// 8x8 pixels.
Size8 = 8,
/// 16x16 pixels.
Size16 = 16,
/// 32x32 pixels.
Size32 = 32,
}
/// A container for graphical assets.
///
/// A GlyphBank stores the decoded runtime representation of a glyph-bank asset.
///
/// Serialized `assets.pa` payloads keep pixel indices packed as `4bpp` nibbles.
/// After decode, the runtime expands them to one `u8` palette index per pixel
/// while preserving the same `0..15` logical range.
pub struct GlyphBank {
/// Dimension of each individual tile in the bank.
pub tile_size: TileSize,
/// Width of the full bank sheet in pixels.
pub width: usize,
/// Height of the full bank sheet in pixels.
pub height: usize,
/// Decoded pixel data stored as one palette index per pixel.
/// Serialized payloads are packed; runtime memory is expanded.
/// Palette indices are ordinary indices; transparency is resolved through
/// the RGBA alpha channel of the palette entry.
pub pixel_indices: Vec<u8>,
/// Runtime-facing v1 palette table: 64 palettes of 16 RGBA8888 colors each.
pub palettes: [[Color; GLYPH_BANK_COLORS_PER_PALETTE]; GLYPH_BANK_PALETTE_COUNT_V1],
}
impl GlyphBank {
/// Creates an empty glyph bank with the specified dimensions.
pub fn new(tile_size: TileSize, width: usize, height: usize) -> Self {
Self {
tile_size,
width,
height,
pixel_indices: vec![0; width * height],
palettes: [[Color::BLACK; GLYPH_BANK_COLORS_PER_PALETTE]; GLYPH_BANK_PALETTE_COUNT_V1],
}
}
/// Resolves a global tile ID and local pixel coordinates to a palette index.
/// tile_id: the tile index in the bank
/// local_x, local_y: the pixel position inside the tile (0 to tile_size-1)
pub fn get_pixel_index(&self, tile_id: u16, local_x: usize, local_y: usize) -> u8 {
let size = self.tile_size as usize;
let tiles_per_row = self.width / size;
let tile_x = (tile_id as usize % tiles_per_row) * size;
let tile_y = (tile_id as usize / tiles_per_row) * size;
let pixel_x = tile_x + local_x;
let pixel_y = tile_y + local_y;
if pixel_x < self.width && pixel_y < self.height {
self.pixel_indices[pixel_y * self.width + pixel_x]
} else {
0
}
}
/// Maps a 4-bit index to a real RGBA8888 Color using the specified palette.
pub fn resolve_color(&self, palette_id: u8, pixel_index: u8) -> Color {
self.palettes
.get(palette_id as usize)
.and_then(|palette| palette.get(pixel_index as usize))
.copied()
.unwrap_or(Color::TRANSPARENT)
}
}