diff --git a/CHANGELOG.md b/CHANGELOG.md index 05a043f8..ff6a048a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Initial unicode support for font rendering. + ### Fixed - Export the `dma` module correctly so you can write the types from it and use it in more complex cases. diff --git a/agb-image-converter/src/font_loader.rs b/agb-image-converter/src/font_loader.rs index d8f8177c..c63e09eb 100644 --- a/agb-image-converter/src/font_loader.rs +++ b/agb-image-converter/src/font_loader.rs @@ -4,6 +4,7 @@ use quote::quote; use proc_macro2::TokenStream; struct LetterData { + character: char, width: usize, height: usize, xmin: i32, @@ -27,9 +28,11 @@ pub fn load_font(font_data: &[u8], pixels_per_em: f32) -> TokenStream { let line_height = line_metrics.new_line_size as i32; let mut ascent = line_metrics.ascent as i32; - let letters: Vec<_> = (0..128) - .map(|i| font.rasterize(char::from_u32(i).unwrap(), pixels_per_em)) - .map(|(metrics, bitmap)| { + let mut letters: Vec<_> = font + .chars() + .iter() + .map(|(&c, _)| (c, font.rasterize(c, pixels_per_em))) + .map(|(c, (metrics, bitmap))| { let width = metrics.width; let height = metrics.height; @@ -48,6 +51,7 @@ pub fn load_font(font_data: &[u8], pixels_per_em: f32) -> TokenStream { .collect(); LetterData { + character: c, width, height, rendered, @@ -58,6 +62,8 @@ pub fn load_font(font_data: &[u8], pixels_per_em: f32) -> TokenStream { }) .collect(); + letters.sort_unstable_by_key(|letter| letter.character); + let maximum_above_line = letters .iter() .map(|x| (x.height as i32 + x.ymin)) @@ -69,6 +75,7 @@ pub fn load_font(font_data: &[u8], pixels_per_em: f32) -> TokenStream { } let font = letters.iter().map(|letter_data| { + let character = letter_data.character; let data_raw = ByteString(&letter_data.rendered); let height = letter_data.height as u8; let width = letter_data.width as u8; @@ -78,6 +85,7 @@ pub fn load_font(font_data: &[u8], pixels_per_em: f32) -> TokenStream { quote!( display::FontLetter::new( + #character, #width, #height, #data_raw, diff --git a/agb/examples/font/OFL.txt b/agb/examples/font/OFL.txt new file mode 100644 index 00000000..be1dc1f3 --- /dev/null +++ b/agb/examples/font/OFL.txt @@ -0,0 +1,94 @@ +Copyright (c) 2021, TakWolf (https://takwolf.com), +with Reserved Font Name 'Ark Pixel'. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/agb/examples/font/ark-pixel-10px-proportional-ja.ttf b/agb/examples/font/ark-pixel-10px-proportional-ja.ttf new file mode 100644 index 00000000..f4dc13c0 Binary files /dev/null and b/agb/examples/font/ark-pixel-10px-proportional-ja.ttf differ diff --git a/agb/examples/object_text_render.rs b/agb/examples/object_text_render.rs index 22bb20a9..fda39a89 100644 --- a/agb/examples/object_text_render.rs +++ b/agb/examples/object_text_render.rs @@ -15,7 +15,8 @@ extern crate alloc; use core::fmt::Write; -const FONT: Font = include_font!("examples/font/yoster.ttf", 12); +static FONT: Font = include_font!("examples/font/ark-pixel-10px-proportional-ja.ttf", 10); + #[agb::entry] fn entry(gba: agb::Gba) -> ! { main(gba); @@ -42,7 +43,7 @@ fn main(mut gba: agb::Gba) -> ! { let player_name = "You"; let _ = writeln!( wr, - "Woah!{change2} {player_name}! {change1}Hey there! I have a bunch of text I want to show you. However, you will find that the amount of text I can display is limited. Who'd have thought! Good thing that my text system supports scrolling! It only took around 20 jank versions to get here!", + "Woah!{change2} {player_name}! {change1}こんにちは! I have a bunch of text I want to show you. However, you will find that the amount of text I can display is limited. Who'd have thought! Good thing that my text system supports scrolling! It only took around 20 jank versions to get here!", change2 = ChangeColour::new(2), change1 = ChangeColour::new(1), ); diff --git a/agb/examples/text_render.rs b/agb/examples/text_render.rs index 350b80a5..741e3d95 100644 --- a/agb/examples/text_render.rs +++ b/agb/examples/text_render.rs @@ -11,7 +11,7 @@ use agb::{ use core::fmt::Write; -const FONT: Font = include_font!("examples/font/yoster.ttf", 12); +static FONT: Font = include_font!("examples/font/ark-pixel-10px-proportional-ja.ttf", 10); #[agb::entry] fn main(mut gba: agb::Gba) -> ! { @@ -47,7 +47,7 @@ fn main(mut gba: agb::Gba) -> ! { let mut renderer = FONT.render_text((0u16, 3u16)); let mut writer = renderer.writer(1, 2, &mut bg, &mut vram); - writeln!(&mut writer, "Hello, World!").unwrap(); + writeln!(&mut writer, "Hello, World! こんにちは 世界").unwrap(); writeln!(&mut writer, "This is a font rendering example").unwrap(); writer.commit(); diff --git a/agb/src/display/font.rs b/agb/src/display/font.rs index 54b3078f..d725e4b1 100644 --- a/agb/src/display/font.rs +++ b/agb/src/display/font.rs @@ -10,6 +10,7 @@ use super::tiled::{DynamicTile, RegularMap, VRamManager}; /// Does not support any unicode features. /// For usage see the `text_render.rs` example pub struct FontLetter { + pub(crate) character: char, pub(crate) width: u8, pub(crate) height: u8, pub(crate) data: &'static [u8], @@ -21,6 +22,7 @@ pub struct FontLetter { impl FontLetter { #[must_use] pub const fn new( + character: char, width: u8, height: u8, data: &'static [u8], @@ -29,6 +31,7 @@ impl FontLetter { advance_width: u8, ) -> Self { Self { + character, width, height, data, @@ -47,14 +50,14 @@ impl FontLetter { } pub struct Font { - letters: &'static [FontLetter; 128], + letters: &'static [FontLetter], line_height: i32, ascent: i32, } impl Font { #[must_use] - pub const fn new(letters: &'static [FontLetter; 128], line_height: i32, ascent: i32) -> Self { + pub const fn new(letters: &'static [FontLetter], line_height: i32, ascent: i32) -> Self { Self { letters, line_height, @@ -63,7 +66,14 @@ impl Font { } pub(crate) fn letter(&self, letter: char) -> &'static FontLetter { - &self.letters[letter as usize & (128 - 1)] + let letter = self + .letters + .binary_search_by_key(&letter, |letter| letter.character); + + match letter { + Ok(index) => &self.letters[index], + Err(_) => &self.letters[0], + } } pub(crate) fn ascent(&self) -> i32 { @@ -269,7 +279,7 @@ impl<'a, 'b> TextRenderer<'b> { mod tests { use super::*; use crate::display::tiled::{TileFormat, TiledMap}; - const FONT: Font = crate::include_font!("examples/font/yoster.ttf", 12); + static FONT: Font = crate::include_font!("examples/font/yoster.ttf", 12); #[test_case] fn font_display(gba: &mut crate::Gba) {