From c8d7879f4daa41a26c534f42ce2b7050e062db8c Mon Sep 17 00:00:00 2001 From: memdmp Date: Sun, 12 Jan 2025 02:41:00 +0100 Subject: fix: make font rendering not wonky --- build | 1 + build.rs | 66 ++++++++++++++++++++++++++++++++++++++++------------- src/font.rs | 76 +++++++++++++++++++++++++++++++++++++++++++++---------------- src/main.rs | 2 +- 4 files changed, 109 insertions(+), 36 deletions(-) diff --git a/build b/build index 6fc9458..3c2d560 100755 --- a/build +++ b/build @@ -1,3 +1,4 @@ #!/bin/sh set -eax RUSTFLAGS="-Zfmt-debug=none -Zlocation-detail=none" cargo +nightly build -Z build-std=std,panic_abort -Z build-std-features="optimize_for_size" -Z build-std-features=panic_immediate_abort -r +upx --brute target/release/cosin-2025-invite-deck diff --git a/build.rs b/build.rs index f26f98e..97179a0 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,4 @@ -use rusttype::{Font, Point, Scale}; +use rusttype::{Font, Point, Rect, Scale}; use std::fs::{self, File}; use std::io::{self, Write}; @@ -8,12 +8,41 @@ use std::io::{self, Write}; struct FontMetadata { pub charset: &'static str, pub name: &'static str, + pub scale: Scale, pub font: Font<'static>, } impl FontMetadata { pub fn render_character(&self, scale: Scale, character: char) -> Vec { let glyph = self.font.glyph(character).scaled(scale); - let bounding_box = glyph.exact_bounding_box().unwrap(); + let positioned_glyph = glyph.clone().positioned(Point { x: 0.0, y: 0.0 }); + let pixel_bounding_box = { + let bounding_box = positioned_glyph.pixel_bounding_box(); + if bounding_box.is_none() { + Rect { + min: Point { x: 0, y: 0 }, + max: Point { + x: glyph.h_metrics().advance_width as i32, + y: 0, + }, + } + } else { + bounding_box.unwrap() + } + }; + let bounding_box = { + let bounding_box = glyph.exact_bounding_box(); + if bounding_box.is_none() { + Rect { + min: Point { x: 0.0, y: 0.0 }, + max: Point { + x: pixel_bounding_box.width() as f32, + y: 0.0, + }, + } + } else { + bounding_box.unwrap() + } + }; let width = bounding_box.width() as u32; let height = bounding_box.height() as u32; @@ -29,22 +58,27 @@ impl FontMetadata { a[i] = v; } } - define_item(&mut image, 2 + ((width as usize) * (height as usize)), 0x00); + define_item( + &mut image, + 10 + ((width as usize) * (height as usize)), + 0x00, + ); image[0] = (width & 0b0000_0000_1111_1111) as u8; image[1] = (width & 0b1111_1111_0000_0000 << 8) as u8; if (image[0] as u16) | (image[1] as u16 >> 8) != width as u16 { panic!("Width missmatch!"); } + // TODO: do we *really* need i32 values here? wouldn't i16 be sufficient? + image[2..6].copy_from_slice(&pixel_bounding_box.min.x.to_le_bytes()); + image[6..10].copy_from_slice(&(pixel_bounding_box.min.y + (scale.y / 2.0) as i32).to_le_bytes()); - glyph - .positioned(Point { x: 0.0, y: 0.0 }) - .draw(|gx: u32, gy: u32, v| { - let bit = (v * 255.0) as u8; - if gx < width { - define_item(&mut image, (2 + (gy * width) + gx) as usize, bit); - } - }); + positioned_glyph.draw(|gx: u32, gy: u32, v| { + let bit = (v * 255.0) as u8; + if gx < width { + define_item(&mut image, (10 + (gy * width) + gx) as usize, bit); + } + }); image } pub fn img_to_hex(image: Vec) -> Vec { @@ -81,10 +115,8 @@ impl FontMetadata { } fn exec(font: FontMetadata) -> io::Result<()> { - let scale = Scale::uniform(32.0); // Set the font size - for c in font.unique_chars() { - let image = font.render_character(scale, c); + let image = font.render_character(font.scale, c); save_bits_to_file(&font, c as u8, &image)?; } @@ -164,12 +196,14 @@ use crate::font::BakedFont; FontMetadata { name: "Galmuri", font: { Font::try_from_vec(fs::read("assets/fonts/Galmuri11.ttf")?).unwrap() }, - charset: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz2053:", + charset: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz2053: ", + scale: Scale::uniform(64.0), }, FontMetadata { name: "CherryBombOne", font: { Font::try_from_vec(fs::read("assets/fonts/CherryBombOne.ttf")?).unwrap() }, - charset: "UwUSpace", + charset: "UwUSpace ", + scale: Scale::uniform(64.0), }, ]; for font in fonts { diff --git a/src/font.rs b/src/font.rs index 0baa862..fc5217e 100644 --- a/src/font.rs +++ b/src/font.rs @@ -1,6 +1,12 @@ use std::sync::LazyLock; -use sdl2::{pixels::{Color, PixelFormatEnum}, rect::Rect, render::{Texture, TextureCreator, TextureValueError}, surface::Surface, video::WindowContext}; +use sdl2::{ + pixels::{Color, PixelFormatEnum}, + rect::{Point, Rect}, + render::{Texture, TextureCreator, TextureValueError}, + surface::Surface, + video::WindowContext, +}; pub struct RenderableCharacter { /** The width of the character, indicating where to break into a newline */ @@ -9,6 +15,8 @@ pub struct RenderableCharacter { pub height: u16, /** The raw alpha layer of the character */ pub data: &'static [u8], + /** The offset to draw the character at */ + pub offset: Point, } /** A trait describing a generated font. @@ -19,11 +27,19 @@ pub trait BakedFont { fn get_char_bytes(&self, character: char) -> &'static [u8]; fn get_char(&self, character: char) -> RenderableCharacter { let bytes = self.get_char_bytes(character); - let width = (bytes[0] as u16) | (bytes[1] as u16 >> 8); - let data = &bytes[2..]; + let width = u16::from_le_bytes(bytes[0..2].try_into().unwrap()); + let offset_x = i32::from_le_bytes(bytes[2..6].try_into().unwrap()); + let offset_y = i32::from_le_bytes(bytes[6..10].try_into().unwrap()); + let data = &bytes[10..]; + let height = if data.len() == 0 { + 0 + } else { + data.len() as u16 / width + }; RenderableCharacter { width, - height: data.len() as u16 / width, + height, + offset: Point::new(offset_x, offset_y), data, } } @@ -32,23 +48,39 @@ impl RenderableCharacter { /** Alpha value of colour is currently ignored! */ pub fn to_surface(&self, colour: Color) -> Surface<'static> { let mut surface = Surface::new( - self.width.into(), - self.height.into(), + if self.width == 0 { + 1 + } else { + self.width.into() + }, + if self.height == 0 { + 1 + } else { + self.height.into() + }, PixelFormatEnum::RGBA32, ) .unwrap(); - surface.with_lock_mut(|buffer: &mut [u8]| { - let mut idx: usize = 0; - print!("{} ({}x{})", self.data.len() * 4, self.width, self.height); - for pixel in self.data { - let index = idx * 4; - buffer[index] = colour.r; // Red - buffer[index + 1] = colour.g; // Green - buffer[index + 2] = colour.b; // Blue - buffer[index + 3] = *pixel; // Alpha - idx += 1; - } - }); + if self.width != 0 || self.height != 0 { + surface.with_lock_mut(|buffer: &mut [u8]| { + let mut idx: usize = 0; + for pixel in self.data { + let index = idx * 4; + buffer[index] = colour.r; // Red + buffer[index + 1] = colour.g; // Green + buffer[index + 2] = colour.b; // Blue + buffer[index + 3] = *pixel; // Alpha + idx += 1; + } + }); + } else { + // Blank + surface.with_lock_mut(|buffer: &mut [u8]| { + for index in 0..buffer.len() { + buffer[index] = 0; + } + }) + } surface } /** Colour Alpha Channel is ignored */ @@ -61,6 +93,12 @@ impl RenderableCharacter { surface.as_texture(texture_creator) } pub fn to_rect(&self, x: i32, y: i32) -> Rect { - Rect::new(x, y, self.width.into(), self.height.into()) + println!("{:#?}",self.offset); + Rect::new( + x + self.offset.x, + y + self.offset.y, + i32::from(self.width).try_into().unwrap(), + i32::from(self.height).try_into().unwrap(), + ) } } diff --git a/src/main.rs b/src/main.rs index 287847d..ebe73d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,7 +27,7 @@ pub fn main() { // let uwu_font = font::UwUFont::new(&texture_creator); canvas.clear(); let mut offset: u16 = 0; - for c in "UwUSpace".chars() { + for c in "UwU Space".chars() { let char = FONT_CHERRY_BOMB_ONE.get_char(c); canvas .copy( -- cgit v1.2.3