From 9aeb8b3e4e497ebfc9e53c0f4e7e612433bacc62 Mon Sep 17 00:00:00 2001 From: Alexis Osorio <113266346+The-Nice-One@users.noreply.github.com> Date: Fri, 8 May 2026 22:00:54 +0000 Subject: [PATCH 1/4] setup work --- .devcontainer/devcontainer.json | 30 +++ Cargo.lock | 10 + Cargo.toml | 4 +- backends/spfc-target-p8/Cargo.toml | 13 ++ backends/spfc-target-p8/src/builders/mod.rs | 12 ++ backends/spfc-target-p8/src/lib.rs | 40 ++++ .../src/utilities/characters.rs | 184 ++++++++++++++++++ backends/spfc-target-p8/src/utilities/mod.rs | 6 + .../spfc-target-p8/src/utilities/pixmap.rs | 93 +++++++++ backends/spfc-target-ttf/Cargo.toml | 4 +- 10 files changed, 393 insertions(+), 3 deletions(-) create mode 100644 .devcontainer/devcontainer.json create mode 100644 backends/spfc-target-p8/Cargo.toml create mode 100644 backends/spfc-target-p8/src/builders/mod.rs create mode 100644 backends/spfc-target-p8/src/lib.rs create mode 100644 backends/spfc-target-p8/src/utilities/characters.rs create mode 100644 backends/spfc-target-p8/src/utilities/mod.rs create mode 100644 backends/spfc-target-p8/src/utilities/pixmap.rs diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..6bb5c98 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,30 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/rust +{ + "name": "Rust", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/rust:1-1-bullseye", + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + "settings": {}, + "extensions": [ + "streetsidesoftware.code-spell-checker" + ] + } + } + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "rustc --version", + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 802d433..9efc56c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1822,6 +1822,16 @@ dependencies = [ "syn", ] +[[package]] +name = "spfc-target-p8" +version = "0.1.0" +dependencies = [ + "bitvec", + "render_spf", + "spf", + "spfc-abi", +] + [[package]] name = "spfc-target-ttf" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index f0dba6d..3232844 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ members = [ "spfc-abi", "spfc-cli", "backends/spfc-target-ttf" -, "spfc-macros"] +, "spfc-macros", "backends/spfc-target-p8"] [workspace.package] version = "0.0.1" @@ -15,3 +15,5 @@ license = "Apache-2.0" # Share dependencies across the workspace to compile faster [workspace.dependencies] anyhow = "1.0" +spf = { version = "0.8.0-alpha.0", default-features = false, features = ["std"] } +render_spf = { git = "https://github.com/SimplePixelFont/render-spf.git" } \ No newline at end of file diff --git a/backends/spfc-target-p8/Cargo.toml b/backends/spfc-target-p8/Cargo.toml new file mode 100644 index 0000000..50b63fa --- /dev/null +++ b/backends/spfc-target-p8/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "spfc-target-p8" +version = "0.1.0" +edition.workspace = true + +[lib] +crate-type = ["cdylib"] + +[dependencies] +spfc-abi = { path = "../../spfc-abi" } +spf.workspace = true +bitvec = "1.0.1" +render_spf.workspace = true \ No newline at end of file diff --git a/backends/spfc-target-p8/src/builders/mod.rs b/backends/spfc-target-p8/src/builders/mod.rs new file mode 100644 index 0000000..a6eb8df --- /dev/null +++ b/backends/spfc-target-p8/src/builders/mod.rs @@ -0,0 +1,12 @@ +use std::collections::BTreeMap; + +use crate::utilities::PixmapGlyph; + + +#[derive(Default, Debug)] +pub struct Process { + pub family_name: String, + pub family_version: f64, + pub manufacturer: String, + pub pixmap_pairs: BTreeMap, +} \ No newline at end of file diff --git a/backends/spfc-target-p8/src/lib.rs b/backends/spfc-target-p8/src/lib.rs new file mode 100644 index 0000000..20e22de --- /dev/null +++ b/backends/spfc-target-p8/src/lib.rs @@ -0,0 +1,40 @@ +use spf::core::layout_from_data; +use spfc_abi::{BackendInfo, CURRENT_ABI_VERSION, CompileOptions, CompileResult, PluginOption}; + +mod builders; +use builders::*; +mod utilities; +use utilities::*; + +#[spfc_abi::export] +fn get_backend_info() -> BackendInfo { + BackendInfo { + name: "P8 PICO8 Backend", + version: 1, + abi_version: CURRENT_ABI_VERSION, + } +} + +#[spfc_abi::export] +fn get_plugin_options() -> Vec { + vec![] +} + +#[spfc_abi::export] +fn compile(options: CompileOptions) -> CompileResult { + let data = std::fs::read(&options.input).unwrap(); + let layout = layout_from_data(&data).unwrap(); + let font_table = layout.font_tables.first().unwrap(); + let font = font_table.fonts.first().unwrap(); + + let mut process = Process::default(); + process.family_name = font.name.clone(); + process.family_version = font.version as f64; + process.manufacturer = font.author.clone(); + process.pixmap_pairs = create_pixmap_pairs(&layout); + + + + + CompileResult::Success +} \ No newline at end of file diff --git a/backends/spfc-target-p8/src/utilities/characters.rs b/backends/spfc-target-p8/src/utilities/characters.rs new file mode 100644 index 0000000..13182bf --- /dev/null +++ b/backends/spfc-target-p8/src/utilities/characters.rs @@ -0,0 +1,184 @@ +// Adapted from https://github.com/dansanderson/picotool/blob/main/pico8/lua/lua.py#L106-L289 + +struct P8Char(u8, String, String); + +const P8SCII_CHARSET: [P8Char; 255] = [ + // Control codes + P8Char(0, "\x00", "Terminate printing"), + P8Char(1, "\x01", "Repeat next character P0 times"), + P8Char(2, "\x02", "Draw solid background with color P0"), + P8Char(3, "\x03", "Move cursor horizontally by P0-16 pixels"), + P8Char(4, "\x04", "Move cursor vertically by P0-16 pixels"), + P8Char(5, "\x05", "Move cursor by P0-16, P1-16 pixels"), + P8Char(6, "\x06", "Special command"), + P8Char(7, "\x07", "Audio command"), + P8Char(8, "\x08", "Backspace"), + P8Char(9, "\x09", "Tab"), + P8Char(10, "\x0a", "Newline"), + P8Char(11, "\x0b", "Decorate previous character command"), + P8Char(12, "\x0c", "Set foreground to color P0"), + P8Char(13, "\x0d", "Carriage return"), + P8Char(14, "\x0e", "Switch font defined at 0x5600"), + P8Char(15, "\x0f", "Switch font to default"), + + // Japanese punctuation + P8Char(16, "▮", "Vertical rectangle"), + P8Char(17, "■", "Filled square"), + P8Char(18, "□", "Hollow square"), + P8Char(19, "⁙", "Five dot"), + P8Char(20, "⁘", "Four dot"), + P8Char(21, "‖", "Pause"), + P8Char(22, "◀", "Back"), + P8Char(23, "▶", "Forward"), + P8Char(24, "「", "Japanese starting quote"), + P8Char(25, "」", "Japanese ending quote"), + P8Char(26, "¥", "Yen sign"), + P8Char(27, "•", "Interpunct"), + P8Char(28, "、", "Japanese comma"), + P8Char(29, "。", "Japanese full stop"), + P8Char(30, "゛", "Japanese dakuten"), + P8Char(31, "゜", "Japanese handakuten"), + + // ASCII + P8Char(32, " ", "space"), +] + [ + P8Char(x, chr(x), chr(x)) for x in range(33, 127) +] + [ + P8Char(127, "○", "Hollow circle"), + + // Symbols + P8Char(128, "█", "Rectangle"), + P8Char(129, "▒", "Checkerboard"), + P8Char(130, "🐱", "Jelpi"), + P8Char(131, "⬇️", "Down key"), + P8Char(132, "░", "Dot pattern"), + P8Char(133, "✽", "Throwing star"), + P8Char(134, "●", "Ball"), + P8Char(135, "♥", "Heart"), + P8Char(136, "☉", "Eye"), + P8Char(137, "웃", "Man"), + P8Char(138, "⌂", "House"), + P8Char(139, "⬅️", "Left key"), + P8Char(140, "😐", "Face"), + P8Char(141, "♪", "Musical note"), + P8Char(142, "🅾️", "O key"), + P8Char(143, "◆", "Diamond"), + P8Char(144, "…", "Ellipsis"), + P8Char(145, "➡️", "Right key"), + P8Char(146, "★", "Five-pointed star"), + P8Char(147, "⧗", "Hourglass"), + P8Char(148, "⬆️", "Up key"), + P8Char(149, "ˇ", "Birds"), + P8Char(150, "∧", "Sawtooth"), + P8Char(151, "❎", "X key"), + P8Char(152, "▤", "Horiz lines"), + P8Char(153, "▥", "Vert lines"), + + // Hiragana + P8Char(154, "あ", "Hiragana: a"), + P8Char(155, "い", "i"), + P8Char(156, "う", "u"), + P8Char(157, "え", "e"), + P8Char(158, "お", "o"), + P8Char(159, "か", "ka"), + P8Char(160, "き", "ki"), + P8Char(161, "く", "ku"), + P8Char(162, "け", "ke"), + P8Char(163, "こ", "ko"), + P8Char(164, "さ", "sa"), + P8Char(165, "し", "si"), + P8Char(166, "す", "su"), + P8Char(167, "せ", "se"), + P8Char(168, "そ", "so"), + P8Char(169, "た", "ta"), + P8Char(170, "ち", "chi"), + P8Char(171, "つ", "tsu"), + P8Char(172, "て", "te"), + P8Char(173, "と", "to"), + P8Char(174, "な", "na"), + P8Char(175, "に", "ni"), + P8Char(176, "ぬ", "nu"), + P8Char(177, "ね", "ne"), + P8Char(178, "の", "no"), + P8Char(179, "は", "ha"), + P8Char(180, "ひ", "hi"), + P8Char(181, "ふ", "phu"), + P8Char(182, "へ", "he"), + P8Char(183, "ほ", "ho"), + P8Char(184, "ま", "ma"), + P8Char(185, "み", "mi"), + P8Char(186, "む", "mu"), + P8Char(187, "め", "me"), + P8Char(188, "も", "mo"), + P8Char(189, "や", "ya"), + P8Char(190, "ゆ", "yu"), + P8Char(191, "よ", "yo"), + P8Char(192, "ら", "ra"), + P8Char(193, "り", "ri"), + P8Char(194, "る", "ru"), + P8Char(195, "れ", "re"), + P8Char(196, "ろ", "ro"), + P8Char(197, "わ", "wa"), + P8Char(198, "を", "wo"), + P8Char(199, "ん", "n"), + P8Char(200, "っ", "Hiragana sokuon"), + P8Char(201, "ゃ", "Hiragana digraphs: ya"), + P8Char(202, "ゅ", "yu"), + P8Char(203, "ょ", "yo"), + + // Katakana + P8Char(204, "ア", "Katakana: a"), + P8Char(205, "イ", "i"), + P8Char(206, "ウ", "u"), + P8Char(207, "エ", "e"), + P8Char(208, "オ", "o"), + P8Char(209, "カ", "ka"), + P8Char(210, "キ", "ki"), + P8Char(211, "ク", "ku"), + P8Char(212, "ケ", "ke"), + P8Char(213, "コ", "ko"), + P8Char(214, "サ", "sa"), + P8Char(215, "シ", "si"), + P8Char(216, "ス", "su"), + P8Char(217, "セ", "se"), + P8Char(218, "ソ", "so"), + P8Char(219, "タ", "ta"), + P8Char(220, "チ", "chi"), + P8Char(221, "ツ", "tsu"), + P8Char(222, "テ", "te"), + P8Char(223, "ト", "to"), + P8Char(224, "ナ", "na"), + P8Char(225, "ニ", "ni"), + P8Char(226, "ヌ", "nu"), + P8Char(227, "ネ", "ne"), + P8Char(228, "ノ", "no"), + P8Char(229, "ハ", "ha"), + P8Char(230, "ヒ", "hi"), + P8Char(231, "フ", "phu"), + P8Char(232, "ヘ", "he"), + P8Char(233, "ホ", "ho"), + P8Char(234, "マ", "ma"), + P8Char(235, "ミ", "mi"), + P8Char(236, "ム", "mu"), + P8Char(237, "メ", "me"), + P8Char(238, "モ", "mo"), + P8Char(239, "ヤ", "ya"), + P8Char(240, "ユ", "yu"), + P8Char(241, "ヨ", "yo"), + P8Char(242, "ラ", "ra"), + P8Char(243, "リ", "ri"), + P8Char(244, "ル", "ru"), + P8Char(245, "レ", "re"), + P8Char(246, "ロ", "ro"), + P8Char(247, "ワ", "wa"), + P8Char(248, "ヲ", "wo"), + P8Char(249, "ン", "n"), + P8Char(250, "ッ", "Katakana sokuon"), + P8Char(251, "ャ", "Katakana digraphs: ya"), + P8Char(252, "ュ", "yu"), + P8Char(253, "ョ", "yo"), + + // Remaining symbols + P8Char(254, "◜", "Left arc"), + P8Char(255, "◝", "Right arc") +] \ No newline at end of file diff --git a/backends/spfc-target-p8/src/utilities/mod.rs b/backends/spfc-target-p8/src/utilities/mod.rs new file mode 100644 index 0000000..f3b8e34 --- /dev/null +++ b/backends/spfc-target-p8/src/utilities/mod.rs @@ -0,0 +1,6 @@ + +pub mod pixmap; +pub use pixmap::*; + +pub mod characters; +pub use characters::*; \ No newline at end of file diff --git a/backends/spfc-target-p8/src/utilities/pixmap.rs b/backends/spfc-target-p8/src/utilities/pixmap.rs new file mode 100644 index 0000000..fe33342 --- /dev/null +++ b/backends/spfc-target-p8/src/utilities/pixmap.rs @@ -0,0 +1,93 @@ +use std::collections::BTreeMap; + +use bitvec::{field::BitField, order::Lsb0, view::BitView}; +use render_spf::{ + ColorControl, RenderableTexture, cache::{TextureBuilder, generic_update_cache} +}; +use spf::core::{Character, Layout, Pixmap, PixmapTable}; + +pub(crate) struct PixmapGlyphTextureBuilder; + +impl TextureBuilder for PixmapGlyphTextureBuilder { + fn build_texture( + &self, + character: &Character, + pixmap: &Pixmap, + pixmap_table: &PixmapTable, + _layout: &Layout, + ) -> PixmapGlyph { + let width = pixmap_table + .constant_width + .or(pixmap.custom_width) + .expect("no width defined in pixmap or pixmap table"); + let height = pixmap_table + .constant_height + .or(pixmap.custom_height) + .expect("no height defined in pixmap or pixmap table"); + + let bits_per_pixel = pixmap_table + .constant_bits_per_pixel + .or(pixmap.custom_bits_per_pixel) + .unwrap(); + + let advance_x = character.advance_x.unwrap_or(width); + + let bits = pixmap.data.view_bits::(); + let pixels: Vec = bits + .chunks(bits_per_pixel as usize) + .map(|chunk| chunk.load_be::()) + .take(width as usize * height as usize) + .collect(); + + let mut pixel_bools = Vec::with_capacity(pixels.len()); + for byte in pixels { + pixel_bools.push(byte != 0); + } + + PixmapGlyph { + advance_x, + width, + height, + pixmap: pixel_bools, + } + } +} + +impl RenderableTexture for PixmapGlyph { + fn width(&self) -> u32 { + self.width as u32 + } + fn height(&self) -> u32 { + self.height as u32 + } + fn advance_x(&self) -> u32 { + self.advance_x as u32 + } +} + +#[derive(Debug, Clone, Default)] +pub struct PixmapGlyph { + pub advance_x: u8, + pub width: u8, + pub height: u8, + pub pixmap: Vec, +} + +pub fn create_pixmap_pairs(layout: &Layout) -> BTreeMap { + let mut pixmap_pairs = BTreeMap::new(); + let mut color_control = ColorControl::with_capacity(layout.color_tables.len()); + + generic_update_cache( + &layout.font_tables[0], + &layout.font_tables[0].fonts[0], + layout, + &PixmapGlyphTextureBuilder, + &mut color_control, + |grapheme| grapheme.to_owned().chars().next().unwrap_or('\0'), + |key, glyph: PixmapGlyph| { + pixmap_pairs.insert(key, glyph.clone()); + }, + ); + + pixmap_pairs +} \ No newline at end of file diff --git a/backends/spfc-target-ttf/Cargo.toml b/backends/spfc-target-ttf/Cargo.toml index 8327e05..fef2ebf 100644 --- a/backends/spfc-target-ttf/Cargo.toml +++ b/backends/spfc-target-ttf/Cargo.toml @@ -11,8 +11,8 @@ spfc-abi = { path = "../../spfc-abi" } write-fonts = "=0.45.0" kurbo = "0.13.0" bitvec = "1.0.1" -spf = { version = "0.8.0-alpha.0", default-features = false, features = ["std"] } -render_spf = { git = "https://github.com/SimplePixelFont/render-spf.git" } +spf.workspace = true +render_spf.workspace = true anyhow.workspace = true chrono = "0.4.43" unicode-segmentation = "1.12.0" \ No newline at end of file From a9601f96de8fe69354d92af4fb250bd22f2d267a Mon Sep 17 00:00:00 2001 From: Alexis Osorio <113266346+The-Nice-One@users.noreply.github.com> Date: Fri, 8 May 2026 22:13:58 +0000 Subject: [PATCH 2/4] add git-lfs to devcontainer --- .devcontainer/devcontainer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 6bb5c98..3c68db6 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -17,13 +17,14 @@ "streetsidesoftware.code-spell-checker" ] } - } + }, // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // Use 'postCreateCommand' to run commands after the container is created. // "postCreateCommand": "rustc --version", + "postCreateCommand": "sudo apt update && sudo apt install -y git-lfs && git lfs install" // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. // "remoteUser": "root" From 880da18471ac759b6542047e5e2e63b425ba7e6d Mon Sep 17 00:00:00 2001 From: The-Nice-One Date: Fri, 8 May 2026 19:00:26 -0400 Subject: [PATCH 3/4] Fix pico-8 character set --- .../src/utilities/characters.rs | 360 ++++++++++-------- 1 file changed, 192 insertions(+), 168 deletions(-) diff --git a/backends/spfc-target-p8/src/utilities/characters.rs b/backends/spfc-target-p8/src/utilities/characters.rs index 13182bf..aca2a27 100644 --- a/backends/spfc-target-p8/src/utilities/characters.rs +++ b/backends/spfc-target-p8/src/utilities/characters.rs @@ -1,184 +1,208 @@ // Adapted from https://github.com/dansanderson/picotool/blob/main/pico8/lua/lua.py#L106-L289 -struct P8Char(u8, String, String); +#[derive(Copy, Clone, Debug)] +pub struct P8Char { + pub id: u8, + pub symbol: &'static str, + pub description: &'static str, +} -const P8SCII_CHARSET: [P8Char; 255] = [ +const ASCII_POOL: &str = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + +const fn generate_character_set() -> [P8Char; 256] { + let mut character_set = [P8Char { id: 0, symbol: "", description: "" }; 256]; // Control codes - P8Char(0, "\x00", "Terminate printing"), - P8Char(1, "\x01", "Repeat next character P0 times"), - P8Char(2, "\x02", "Draw solid background with color P0"), - P8Char(3, "\x03", "Move cursor horizontally by P0-16 pixels"), - P8Char(4, "\x04", "Move cursor vertically by P0-16 pixels"), - P8Char(5, "\x05", "Move cursor by P0-16, P1-16 pixels"), - P8Char(6, "\x06", "Special command"), - P8Char(7, "\x07", "Audio command"), - P8Char(8, "\x08", "Backspace"), - P8Char(9, "\x09", "Tab"), - P8Char(10, "\x0a", "Newline"), - P8Char(11, "\x0b", "Decorate previous character command"), - P8Char(12, "\x0c", "Set foreground to color P0"), - P8Char(13, "\x0d", "Carriage return"), - P8Char(14, "\x0e", "Switch font defined at 0x5600"), - P8Char(15, "\x0f", "Switch font to default"), + character_set[0] = P8Char { id: 0, symbol: "\x00", description: "Terminate printing" }; + character_set[1] = P8Char { id: 1, symbol: "\x01", description: "Repeat next character P0 times" }; + character_set[2] = P8Char { id: 2, symbol: "\x02", description: "Draw solid background with color P0" }; + character_set[3] = P8Char { id: 3, symbol: "\x03", description: "Move cursor horizontally by P0-16 pixels" }; + character_set[4] = P8Char { id: 4, symbol: "\x04", description: "Move cursor vertically by P0-16 pixels" }; + character_set[5] = P8Char { id: 5, symbol: "\x05", description: "Move cursor by P0-16, P1-16 pixels" }; + character_set[6] = P8Char { id: 6, symbol: "\x06", description: "Special command" }; + character_set[7] = P8Char { id: 7, symbol: "\x07", description: "Audio command" }; + character_set[8] = P8Char { id: 8, symbol: "\x08", description: "Backspace" }; + character_set[9] = P8Char { id: 9, symbol: "\x09", description: "Tab" }; + character_set[10] = P8Char { id: 10, symbol: "\x0a", description: "Newline" }; + character_set[11] = P8Char { id: 11, symbol: "\x0b", description: "Decorate previous character command" }; + character_set[12] = P8Char { id: 12, symbol: "\x0c", description: "Set foreground to color P0" }; + character_set[13] = P8Char { id: 13, symbol: "\x0d", description: "Ccharacter_setiage return" }; + character_set[14] = P8Char { id: 14, symbol: "\x0e", description: "Switch font defined at 0x5600" }; + character_set[15] = P8Char { id: 15, symbol: "\x0f", description: "Switch font to default" }; // Japanese punctuation - P8Char(16, "▮", "Vertical rectangle"), - P8Char(17, "■", "Filled square"), - P8Char(18, "□", "Hollow square"), - P8Char(19, "⁙", "Five dot"), - P8Char(20, "⁘", "Four dot"), - P8Char(21, "‖", "Pause"), - P8Char(22, "◀", "Back"), - P8Char(23, "▶", "Forward"), - P8Char(24, "「", "Japanese starting quote"), - P8Char(25, "」", "Japanese ending quote"), - P8Char(26, "¥", "Yen sign"), - P8Char(27, "•", "Interpunct"), - P8Char(28, "、", "Japanese comma"), - P8Char(29, "。", "Japanese full stop"), - P8Char(30, "゛", "Japanese dakuten"), - P8Char(31, "゜", "Japanese handakuten"), + character_set[16] = P8Char { id: 16, symbol: "▮", description: "Vertical rectangle" }; + character_set[17] = P8Char { id: 17, symbol: "■", description: "Filled square" }; + character_set[18] = P8Char { id: 18, symbol: "□", description: "Hollow square" }; + character_set[19] = P8Char { id: 19, symbol: "⁙", description: "Five dot" }; + character_set[20] = P8Char { id: 20, symbol: "⁘", description: "Four dot" }; + character_set[21] = P8Char { id: 21, symbol: "‖", description: "Pause" }; + character_set[22] = P8Char { id: 22, symbol: "◀", description: "Back" }; + character_set[23] = P8Char { id: 23, symbol: "▶", description: "Forward" }; + character_set[24] = P8Char { id: 24, symbol: "「", description: "Japanese starting quote" }; + character_set[25] = P8Char { id: 25, symbol: "」", description: "Japanese ending quote" }; + character_set[26] = P8Char { id: 26, symbol: "¥", description: "Yen sign" }; + character_set[27] = P8Char { id: 27, symbol: "•", description: "Interpunct" }; + character_set[28] = P8Char { id: 28, symbol: "、", description: "Japanese comma" }; + character_set[29] = P8Char { id: 29, symbol: "。", description: "Japanese full stop" }; + character_set[30] = P8Char { id: 30, symbol: "゛", description: "Japanese dakuten" }; + character_set[31] = P8Char { id: 31, symbol: "゜", description: "Japanese handakuten" }; // ASCII - P8Char(32, " ", "space"), -] + [ - P8Char(x, chr(x), chr(x)) for x in range(33, 127) -] + [ - P8Char(127, "○", "Hollow circle"), + character_set[32] = P8Char { id: 32, symbol: " ", description: "space" }; + + let mut i: usize = 33; + while i <= 126 { + let pool_index = i - 33; + // Unsafe is "safe" since we are using a predefined string that will always work :) + let symbol = unsafe { + std::str::from_utf8_unchecked(std::slice::from_raw_parts( + ASCII_POOL.as_ptr().add(pool_index), + 1 + )) + }; + character_set[i] = P8Char { id: i as u8, symbol: symbol, description: symbol }; + i += 1; + } + + character_set[127] = P8Char { id: 127, symbol: "○", description: "Hollow circle" }; // Symbols - P8Char(128, "█", "Rectangle"), - P8Char(129, "▒", "Checkerboard"), - P8Char(130, "🐱", "Jelpi"), - P8Char(131, "⬇️", "Down key"), - P8Char(132, "░", "Dot pattern"), - P8Char(133, "✽", "Throwing star"), - P8Char(134, "●", "Ball"), - P8Char(135, "♥", "Heart"), - P8Char(136, "☉", "Eye"), - P8Char(137, "웃", "Man"), - P8Char(138, "⌂", "House"), - P8Char(139, "⬅️", "Left key"), - P8Char(140, "😐", "Face"), - P8Char(141, "♪", "Musical note"), - P8Char(142, "🅾️", "O key"), - P8Char(143, "◆", "Diamond"), - P8Char(144, "…", "Ellipsis"), - P8Char(145, "➡️", "Right key"), - P8Char(146, "★", "Five-pointed star"), - P8Char(147, "⧗", "Hourglass"), - P8Char(148, "⬆️", "Up key"), - P8Char(149, "ˇ", "Birds"), - P8Char(150, "∧", "Sawtooth"), - P8Char(151, "❎", "X key"), - P8Char(152, "▤", "Horiz lines"), - P8Char(153, "▥", "Vert lines"), + character_set[128] = P8Char { id: 128, symbol: "█", description: "Rectangle" }; + character_set[129] = P8Char { id: 129, symbol: "▒", description: "Checkerboard" }; + character_set[130] = P8Char { id: 130, symbol: "🐱", description: "Jelpi" }; + character_set[131] = P8Char { id: 131, symbol: "⬇️", description: "Down key" }; + character_set[132] = P8Char { id: 132, symbol: "░", description: "Dot pattern" }; + character_set[133] = P8Char { id: 133, symbol: "✽", description: "Throwing star" }; + character_set[134] = P8Char { id: 134, symbol: "●", description: "Ball" }; + character_set[135] = P8Char { id: 135, symbol: "♥", description: "Heart" }; + character_set[136] = P8Char { id: 136, symbol: "☉", description: "Eye" }; + character_set[137] = P8Char { id: 137, symbol: "웃", description: "Man" }; + character_set[138] = P8Char { id: 138, symbol: "⌂", description: "House" }; + character_set[139] = P8Char { id: 139, symbol: "⬅️", description: "Left key" }; + character_set[140] = P8Char { id: 140, symbol: "😐", description: "Face" }; + character_set[141] = P8Char { id: 141, symbol: "♪", description: "Musical note" }; + character_set[142] = P8Char { id: 142, symbol: "🅾️", description: "O key" }; + character_set[143] = P8Char { id: 143, symbol: "◆", description: "Diamond" }; + character_set[144] = P8Char { id: 144, symbol: "…", description: "Ellipsis" }; + character_set[145] = P8Char { id: 145, symbol: "➡️", description: "Right key" }; + character_set[146] = P8Char { id: 146, symbol: "★", description: "Five-pointed star" }; + character_set[147] = P8Char { id: 147, symbol: "⧗", description: "Hourglass" }; + character_set[148] = P8Char { id: 148, symbol: "⬆️", description: "Up key" }; + character_set[149] = P8Char { id: 149, symbol: "ˇ", description: "Birds" }; + character_set[150] = P8Char { id: 150, symbol: "∧", description: "Sawtooth" }; + character_set[151] = P8Char { id: 151, symbol: "❎", description: "X key" }; + character_set[152] = P8Char { id: 152, symbol: "▤", description: "Horiz lines" }; + character_set[153] = P8Char { id: 153, symbol: "▥", description: "Vert lines" }; // Hiragana - P8Char(154, "あ", "Hiragana: a"), - P8Char(155, "い", "i"), - P8Char(156, "う", "u"), - P8Char(157, "え", "e"), - P8Char(158, "お", "o"), - P8Char(159, "か", "ka"), - P8Char(160, "き", "ki"), - P8Char(161, "く", "ku"), - P8Char(162, "け", "ke"), - P8Char(163, "こ", "ko"), - P8Char(164, "さ", "sa"), - P8Char(165, "し", "si"), - P8Char(166, "す", "su"), - P8Char(167, "せ", "se"), - P8Char(168, "そ", "so"), - P8Char(169, "た", "ta"), - P8Char(170, "ち", "chi"), - P8Char(171, "つ", "tsu"), - P8Char(172, "て", "te"), - P8Char(173, "と", "to"), - P8Char(174, "な", "na"), - P8Char(175, "に", "ni"), - P8Char(176, "ぬ", "nu"), - P8Char(177, "ね", "ne"), - P8Char(178, "の", "no"), - P8Char(179, "は", "ha"), - P8Char(180, "ひ", "hi"), - P8Char(181, "ふ", "phu"), - P8Char(182, "へ", "he"), - P8Char(183, "ほ", "ho"), - P8Char(184, "ま", "ma"), - P8Char(185, "み", "mi"), - P8Char(186, "む", "mu"), - P8Char(187, "め", "me"), - P8Char(188, "も", "mo"), - P8Char(189, "や", "ya"), - P8Char(190, "ゆ", "yu"), - P8Char(191, "よ", "yo"), - P8Char(192, "ら", "ra"), - P8Char(193, "り", "ri"), - P8Char(194, "る", "ru"), - P8Char(195, "れ", "re"), - P8Char(196, "ろ", "ro"), - P8Char(197, "わ", "wa"), - P8Char(198, "を", "wo"), - P8Char(199, "ん", "n"), - P8Char(200, "っ", "Hiragana sokuon"), - P8Char(201, "ゃ", "Hiragana digraphs: ya"), - P8Char(202, "ゅ", "yu"), - P8Char(203, "ょ", "yo"), + character_set[154] = P8Char { id: 154, symbol: "あ", description: "Hiragana: a" }; + character_set[155] = P8Char { id: 155, symbol: "い", description: "i" }; + character_set[156] = P8Char { id: 156, symbol: "う", description: "u" }; + character_set[157] = P8Char { id: 157, symbol: "え", description: "e" }; + character_set[158] = P8Char { id: 158, symbol: "お", description: "o" }; + character_set[159] = P8Char { id: 159, symbol: "か", description: "ka" }; + character_set[160] = P8Char { id: 160, symbol: "き", description: "ki" }; + character_set[161] = P8Char { id: 161, symbol: "く", description: "ku" }; + character_set[162] = P8Char { id: 162, symbol: "け", description: "ke" }; + character_set[163] = P8Char { id: 163, symbol: "こ", description: "ko" }; + character_set[164] = P8Char { id: 164, symbol: "さ", description: "sa" }; + character_set[165] = P8Char { id: 165, symbol: "し", description: "si" }; + character_set[166] = P8Char { id: 166, symbol: "す", description: "su" }; + character_set[167] = P8Char { id: 167, symbol: "せ", description: "se" }; + character_set[168] = P8Char { id: 168, symbol: "そ", description: "so" }; + character_set[169] = P8Char { id: 169, symbol: "た", description: "ta" }; + character_set[170] = P8Char { id: 170, symbol: "ち", description: "chi" }; + character_set[171] = P8Char { id: 171, symbol: "つ", description: "tsu" }; + character_set[172] = P8Char { id: 172, symbol: "て", description: "te" }; + character_set[173] = P8Char { id: 173, symbol: "と", description: "to" }; + character_set[174] = P8Char { id: 174, symbol: "な", description: "na" }; + character_set[175] = P8Char { id: 175, symbol: "に", description: "ni" }; + character_set[176] = P8Char { id: 176, symbol: "ぬ", description: "nu" }; + character_set[177] = P8Char { id: 177, symbol: "ね", description: "ne" }; + character_set[178] = P8Char { id: 178, symbol: "の", description: "no" }; + character_set[179] = P8Char { id: 179, symbol: "は", description: "ha" }; + character_set[180] = P8Char { id: 180, symbol: "ひ", description: "hi" }; + character_set[181] = P8Char { id: 181, symbol: "ふ", description: "phu" }; + character_set[182] = P8Char { id: 182, symbol: "へ", description: "he" }; + character_set[183] = P8Char { id: 183, symbol: "ほ", description: "ho" }; + character_set[184] = P8Char { id: 184, symbol: "ま", description: "ma" }; + character_set[185] = P8Char { id: 185, symbol: "み", description: "mi" }; + character_set[186] = P8Char { id: 186, symbol: "む", description: "mu" }; + character_set[187] = P8Char { id: 187, symbol: "め", description: "me" }; + character_set[188] = P8Char { id: 188, symbol: "も", description: "mo" }; + character_set[189] = P8Char { id: 189, symbol: "や", description: "ya" }; + character_set[190] = P8Char { id: 190, symbol: "ゆ", description: "yu" }; + character_set[191] = P8Char { id: 191, symbol: "よ", description: "yo" }; + character_set[192] = P8Char { id: 192, symbol: "ら", description: "ra" }; + character_set[193] = P8Char { id: 193, symbol: "り", description: "ri" }; + character_set[194] = P8Char { id: 194, symbol: "る", description: "ru" }; + character_set[195] = P8Char { id: 195, symbol: "れ", description: "re" }; + character_set[196] = P8Char { id: 196, symbol: "ろ", description: "ro" }; + character_set[197] = P8Char { id: 197, symbol: "わ", description: "wa" }; + character_set[198] = P8Char { id: 198, symbol: "を", description: "wo" }; + character_set[199] = P8Char { id: 199, symbol: "ん", description: "n" }; + character_set[200] = P8Char { id: 200, symbol: "っ", description: "Hiragana sokuon" }; + character_set[201] = P8Char { id: 201, symbol: "ゃ", description: "Hiragana digraphs: ya" }; + character_set[202] = P8Char { id: 202, symbol: "ゅ", description: "yu" }; + character_set[203] = P8Char { id: 203, symbol: "ょ", description: "yo" }; // Katakana - P8Char(204, "ア", "Katakana: a"), - P8Char(205, "イ", "i"), - P8Char(206, "ウ", "u"), - P8Char(207, "エ", "e"), - P8Char(208, "オ", "o"), - P8Char(209, "カ", "ka"), - P8Char(210, "キ", "ki"), - P8Char(211, "ク", "ku"), - P8Char(212, "ケ", "ke"), - P8Char(213, "コ", "ko"), - P8Char(214, "サ", "sa"), - P8Char(215, "シ", "si"), - P8Char(216, "ス", "su"), - P8Char(217, "セ", "se"), - P8Char(218, "ソ", "so"), - P8Char(219, "タ", "ta"), - P8Char(220, "チ", "chi"), - P8Char(221, "ツ", "tsu"), - P8Char(222, "テ", "te"), - P8Char(223, "ト", "to"), - P8Char(224, "ナ", "na"), - P8Char(225, "ニ", "ni"), - P8Char(226, "ヌ", "nu"), - P8Char(227, "ネ", "ne"), - P8Char(228, "ノ", "no"), - P8Char(229, "ハ", "ha"), - P8Char(230, "ヒ", "hi"), - P8Char(231, "フ", "phu"), - P8Char(232, "ヘ", "he"), - P8Char(233, "ホ", "ho"), - P8Char(234, "マ", "ma"), - P8Char(235, "ミ", "mi"), - P8Char(236, "ム", "mu"), - P8Char(237, "メ", "me"), - P8Char(238, "モ", "mo"), - P8Char(239, "ヤ", "ya"), - P8Char(240, "ユ", "yu"), - P8Char(241, "ヨ", "yo"), - P8Char(242, "ラ", "ra"), - P8Char(243, "リ", "ri"), - P8Char(244, "ル", "ru"), - P8Char(245, "レ", "re"), - P8Char(246, "ロ", "ro"), - P8Char(247, "ワ", "wa"), - P8Char(248, "ヲ", "wo"), - P8Char(249, "ン", "n"), - P8Char(250, "ッ", "Katakana sokuon"), - P8Char(251, "ャ", "Katakana digraphs: ya"), - P8Char(252, "ュ", "yu"), - P8Char(253, "ョ", "yo"), + character_set[204] = P8Char { id: 204, symbol: "ア", description: "Katakana: a" }; + character_set[205] = P8Char { id: 205, symbol: "イ", description: "i" }; + character_set[206] = P8Char { id: 206, symbol: "ウ", description: "u" }; + character_set[207] = P8Char { id: 207, symbol: "エ", description: "e" }; + character_set[208] = P8Char { id: 208, symbol: "オ", description: "o" }; + character_set[209] = P8Char { id: 209, symbol: "カ", description: "ka" }; + character_set[210] = P8Char { id: 210, symbol: "キ", description: "ki" }; + character_set[211] = P8Char { id: 211, symbol: "ク", description: "ku" }; + character_set[212] = P8Char { id: 212, symbol: "ケ", description: "ke" }; + character_set[213] = P8Char { id: 213, symbol: "コ", description: "ko" }; + character_set[214] = P8Char { id: 214, symbol: "サ", description: "sa" }; + character_set[215] = P8Char { id: 215, symbol: "シ", description: "si" }; + character_set[216] = P8Char { id: 216, symbol: "ス", description: "su" }; + character_set[217] = P8Char { id: 217, symbol: "セ", description: "se" }; + character_set[218] = P8Char { id: 218, symbol: "ソ", description: "so" }; + character_set[219] = P8Char { id: 219, symbol: "タ", description: "ta" }; + character_set[220] = P8Char { id: 220, symbol: "チ", description: "chi" }; + character_set[221] = P8Char { id: 221, symbol: "ツ", description: "tsu" }; + character_set[222] = P8Char { id: 222, symbol: "テ", description: "te" }; + character_set[223] = P8Char { id: 223, symbol: "ト", description: "to" }; + character_set[224] = P8Char { id: 224, symbol: "ナ", description: "na" }; + character_set[225] = P8Char { id: 225, symbol: "ニ", description: "ni" }; + character_set[226] = P8Char { id: 226, symbol: "ヌ", description: "nu" }; + character_set[227] = P8Char { id: 227, symbol: "ネ", description: "ne" }; + character_set[228] = P8Char { id: 228, symbol: "ノ", description: "no" }; + character_set[229] = P8Char { id: 229, symbol: "ハ", description: "ha" }; + character_set[230] = P8Char { id: 230, symbol: "ヒ", description: "hi" }; + character_set[231] = P8Char { id: 231, symbol: "フ", description: "phu" }; + character_set[232] = P8Char { id: 232, symbol: "ヘ", description: "he" }; + character_set[233] = P8Char { id: 233, symbol: "ホ", description: "ho" }; + character_set[234] = P8Char { id: 234, symbol: "マ", description: "ma" }; + character_set[235] = P8Char { id: 235, symbol: "ミ", description: "mi" }; + character_set[236] = P8Char { id: 236, symbol: "ム", description: "mu" }; + character_set[237] = P8Char { id: 237, symbol: "メ", description: "me" }; + character_set[238] = P8Char { id: 238, symbol: "モ", description: "mo" }; + character_set[239] = P8Char { id: 239, symbol: "ヤ", description: "ya" }; + character_set[240] = P8Char { id: 240, symbol: "ユ", description: "yu" }; + character_set[241] = P8Char { id: 241, symbol: "ヨ", description: "yo" }; + character_set[242] = P8Char { id: 242, symbol: "ラ", description: "ra" }; + character_set[243] = P8Char { id: 243, symbol: "リ", description: "ri" }; + character_set[244] = P8Char { id: 244, symbol: "ル", description: "ru" }; + character_set[245] = P8Char { id: 245, symbol: "レ", description: "re" }; + character_set[246] = P8Char { id: 246, symbol: "ロ", description: "ro" }; + character_set[247] = P8Char { id: 247, symbol: "ワ", description: "wa" }; + character_set[248] = P8Char { id: 248, symbol: "ヲ", description: "wo" }; + character_set[249] = P8Char { id: 249, symbol: "ン", description: "n" }; + character_set[250] = P8Char { id: 250, symbol: "ッ", description: "Katakana sokuon" }; + character_set[251] = P8Char { id: 251, symbol: "ャ", description: "Katakana digraphs: ya" }; + character_set[252] = P8Char { id: 252, symbol: "ュ", description: "yu" }; + character_set[253] = P8Char { id: 253, symbol: "ョ", description: "yo" }; // Remaining symbols - P8Char(254, "◜", "Left arc"), - P8Char(255, "◝", "Right arc") -] \ No newline at end of file + character_set[254] = P8Char { id: 254, symbol: "◜", description: "Left arc" }; + character_set[255] = P8Char { id: 255, symbol: "◝", description: "Right arc" }; + + character_set +} + +pub const P8SCII_CHARSET: [P8Char; 256] = generate_character_set(); \ No newline at end of file From eec6f4cc4061c31c68ac7a1ed2bef183fc4d6616 Mon Sep 17 00:00:00 2001 From: The-Nice-One Date: Fri, 8 May 2026 21:16:27 -0400 Subject: [PATCH 4/4] Add pico-8 custom font target --- Cargo.lock | 1 + backends/spfc-target-p8/Cargo.toml | 3 +- backends/spfc-target-p8/src/builders/mod.rs | 5 ++- .../spfc-target-p8/src/builders/writer.rs | 38 ++++++++++++++++ backends/spfc-target-p8/src/lib.rs | 10 ++++- .../src/utilities/characters.rs | 7 ++- backends/spfc-target-p8/src/utilities/mod.rs | 34 ++++++++++++++- .../spfc-target-p8/src/utilities/pixmap.rs | 43 ++++++++++++++----- 8 files changed, 124 insertions(+), 17 deletions(-) create mode 100644 backends/spfc-target-p8/src/builders/writer.rs diff --git a/Cargo.lock b/Cargo.lock index 9efc56c..357e84c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1826,6 +1826,7 @@ dependencies = [ name = "spfc-target-p8" version = "0.1.0" dependencies = [ + "anyhow", "bitvec", "render_spf", "spf", diff --git a/backends/spfc-target-p8/Cargo.toml b/backends/spfc-target-p8/Cargo.toml index 50b63fa..01ff7e9 100644 --- a/backends/spfc-target-p8/Cargo.toml +++ b/backends/spfc-target-p8/Cargo.toml @@ -10,4 +10,5 @@ crate-type = ["cdylib"] spfc-abi = { path = "../../spfc-abi" } spf.workspace = true bitvec = "1.0.1" -render_spf.workspace = true \ No newline at end of file +render_spf.workspace = true +anyhow.workspace = true \ No newline at end of file diff --git a/backends/spfc-target-p8/src/builders/mod.rs b/backends/spfc-target-p8/src/builders/mod.rs index a6eb8df..3648afc 100644 --- a/backends/spfc-target-p8/src/builders/mod.rs +++ b/backends/spfc-target-p8/src/builders/mod.rs @@ -2,11 +2,14 @@ use std::collections::BTreeMap; use crate::utilities::PixmapGlyph; +pub mod writer; +pub use writer::*; + #[derive(Default, Debug)] pub struct Process { pub family_name: String, pub family_version: f64, pub manufacturer: String, - pub pixmap_pairs: BTreeMap, + pub pixmap_pairs: BTreeMap, } \ No newline at end of file diff --git a/backends/spfc-target-p8/src/builders/writer.rs b/backends/spfc-target-p8/src/builders/writer.rs new file mode 100644 index 0000000..483c538 --- /dev/null +++ b/backends/spfc-target-p8/src/builders/writer.rs @@ -0,0 +1,38 @@ +use crate::{builders::Process, utilities::{last_character_index, max_height, max_width}}; +use anyhow::Result; + +pub fn create_program_string(process: &Process) -> Result { + let mut program_string = String::new(); + + program_string += "pico-8 cartridge // http://www.pico-8.com\nversion 41\n__lua__\n"; + + program_string += format!("-- {} v{}\n-- {}\n-- Generated by spfc\n-- https://github.com/SimplePixelFont/spfc\n\n", process.family_name, process.family_version, process.manufacturer).as_str(); + + let font_function = format!("load_{}", process.family_name.to_ascii_lowercase().replace(" ", "_")); + program_string += format!("-- Copy the function below\n-- into your own cart\nfunction {}()\n\t-- enable custom fonts\n\tpoke(0x5f58,0x81)\n\n\t-- add font to memory\n", font_function).as_str(); + + + let mut buffer = vec![0; (last_character_index(&process.pixmap_pairs) as usize + 1) * 8]; + buffer[0] = max_width(&process.pixmap_pairs) + 1; //1 added for letter spacing + buffer[1] = max_width(&process.pixmap_pairs); // This will be added as a custom flag + buffer[2] = max_height(&process.pixmap_pairs) + 1; //1 added for line spacing + buffer[3] = 0; // This will be added as a custom flag offset_x + buffer[4] = 0; // This will be added as a custom flag offset_y + + for (key, glyph) in &process.pixmap_pairs { + if let Some(p8_char) = crate::utilities::get_character_by_symbol(key) { + let char_index = p8_char.id as usize; + let bitmap_data = &glyph.bitmap; + for (i, byte) in bitmap_data.iter().enumerate() { + buffer[char_index * 8 + i] = *byte; + } + } + } + + program_string += format!("\tpoke(0x5600,unpack(split\"{}\"))\nend\n\n", buffer.iter().map(|byte| byte.to_string()).collect::>().join(",")).as_str(); + + program_string += format!("-- Sample usage below\nfunction _init()\n\t{}()\nend\n\n", font_function).as_str(); + program_string += format!("function _draw()\n\tcls()\n\tprint(\"Hello from {}!\")\nend ", process.family_name).as_str(); + + Ok(program_string) +} \ No newline at end of file diff --git a/backends/spfc-target-p8/src/lib.rs b/backends/spfc-target-p8/src/lib.rs index 20e22de..248ca0a 100644 --- a/backends/spfc-target-p8/src/lib.rs +++ b/backends/spfc-target-p8/src/lib.rs @@ -33,8 +33,14 @@ fn compile(options: CompileOptions) -> CompileResult { process.manufacturer = font.author.clone(); process.pixmap_pairs = create_pixmap_pairs(&layout); - - + let font_data = create_program_string(&process).unwrap(); + std::fs::write(&options.output, &font_data).unwrap(); + + println!( + "Finished writing {} bytes to {}", + font_data.len(), + options.output + ); CompileResult::Success } \ No newline at end of file diff --git a/backends/spfc-target-p8/src/utilities/characters.rs b/backends/spfc-target-p8/src/utilities/characters.rs index aca2a27..c0a19a3 100644 --- a/backends/spfc-target-p8/src/utilities/characters.rs +++ b/backends/spfc-target-p8/src/utilities/characters.rs @@ -4,6 +4,7 @@ pub struct P8Char { pub id: u8, pub symbol: &'static str, + #[allow(dead_code)] pub description: &'static str, } @@ -205,4 +206,8 @@ const fn generate_character_set() -> [P8Char; 256] { character_set } -pub const P8SCII_CHARSET: [P8Char; 256] = generate_character_set(); \ No newline at end of file +pub const P8SCII_CHARSET: [P8Char; 256] = generate_character_set(); + +pub fn get_character_by_symbol(symbol: &str) -> Option<&P8Char> { + P8SCII_CHARSET.iter().find(|c| c.symbol == symbol) +} \ No newline at end of file diff --git a/backends/spfc-target-p8/src/utilities/mod.rs b/backends/spfc-target-p8/src/utilities/mod.rs index f3b8e34..989be82 100644 --- a/backends/spfc-target-p8/src/utilities/mod.rs +++ b/backends/spfc-target-p8/src/utilities/mod.rs @@ -1,6 +1,38 @@ pub mod pixmap; +use std::collections::BTreeMap; + pub use pixmap::*; pub mod characters; -pub use characters::*; \ No newline at end of file +pub use characters::*; + +pub fn max_width(bitmaps: &BTreeMap) -> u8 { + bitmaps + .iter() + .map(|pair| pair.1.width as usize) + .max() + .unwrap_or(0) as u8 +} + +pub fn max_height(bitmaps: &BTreeMap) -> u8 { + bitmaps + .iter() + .map(|pair| pair.1.height as usize) + .max() + .unwrap_or(0) as u8 +} + +pub fn last_character_index(bitmaps: &BTreeMap) -> u8 { + bitmaps + .iter() + .map(|pair| { + if let Some(p8_char) = get_character_by_symbol(pair.0) { + p8_char.id as usize + } else { + 0 + } + }) + .max() + .unwrap_or(0) as u8 +} \ No newline at end of file diff --git a/backends/spfc-target-p8/src/utilities/pixmap.rs b/backends/spfc-target-p8/src/utilities/pixmap.rs index fe33342..fe40fea 100644 --- a/backends/spfc-target-p8/src/utilities/pixmap.rs +++ b/backends/spfc-target-p8/src/utilities/pixmap.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use bitvec::{field::BitField, order::Lsb0, view::BitView}; +use bitvec::{bitvec, field::BitField, order::Lsb0, view::BitView}; use render_spf::{ ColorControl, RenderableTexture, cache::{TextureBuilder, generic_update_cache} }; @@ -31,6 +31,15 @@ impl TextureBuilder for PixmapGlyphTextureBuilder { .unwrap(); let advance_x = character.advance_x.unwrap_or(width); + if advance_x != width { + todo!("ABI 0.2.0: will add logging; On p8 target the advance_x is ignored because pico-8 doesn't support it.") + } + if width > 8 || height > 8 { + todo!("ABI 0.2.0: will add logging; On p8 target only 8x8 pixels are supported, Anything larger than that will be treated as 8x8.") + } + if bits_per_pixel != 1 { + todo!("ABI 0.2.0: will add logging; On p8 target only 1 bit per pixel is supported, Anything other than 0 will be treated as opaque.") + } let bits = pixmap.data.view_bits::(); let pixels: Vec = bits @@ -39,16 +48,29 @@ impl TextureBuilder for PixmapGlyphTextureBuilder { .take(width as usize * height as usize) .collect(); - let mut pixel_bools = Vec::with_capacity(pixels.len()); - for byte in pixels { - pixel_bools.push(byte != 0); + let mut processed_pixels = Vec::with_capacity(height as usize); + + let mut current_x = 0; + let mut current_y = 0; + let mut pixel_row = bitvec![u8, Lsb0; 0; 8]; + for pixel in pixels { + pixel_row.set(current_x as usize, pixel != 0); + current_x += 1; + if current_x == width { + processed_pixels.push(pixel_row.load_le::()); + current_x = 0; + current_y += 1; + if current_y == height { + break; + } + pixel_row = bitvec![u8, Lsb0; 0; 8]; + } } PixmapGlyph { - advance_x, width, height, - pixmap: pixel_bools, + bitmap: processed_pixels, } } } @@ -61,19 +83,18 @@ impl RenderableTexture for PixmapGlyph { self.height as u32 } fn advance_x(&self) -> u32 { - self.advance_x as u32 + self.width as u32 } } #[derive(Debug, Clone, Default)] pub struct PixmapGlyph { - pub advance_x: u8, pub width: u8, pub height: u8, - pub pixmap: Vec, + pub bitmap: Vec, } -pub fn create_pixmap_pairs(layout: &Layout) -> BTreeMap { +pub fn create_pixmap_pairs(layout: &Layout) -> BTreeMap { let mut pixmap_pairs = BTreeMap::new(); let mut color_control = ColorControl::with_capacity(layout.color_tables.len()); @@ -83,7 +104,7 @@ pub fn create_pixmap_pairs(layout: &Layout) -> BTreeMap { layout, &PixmapGlyphTextureBuilder, &mut color_control, - |grapheme| grapheme.to_owned().chars().next().unwrap_or('\0'), + |grapheme| grapheme.to_string(), |key, glyph: PixmapGlyph| { pixmap_pairs.insert(key, glyph.clone()); },