font rendering

This commit is contained in:
lifning 2021-10-21 03:17:52 -07:00
parent 8471ec85fa
commit b5cdf18eea
6 changed files with 1373 additions and 44 deletions

33
Cargo.lock generated
View File

@ -1,5 +1,14 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
@ -263,6 +272,12 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "num"
version = "0.1.42"
@ -547,6 +562,23 @@ dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "regex"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "sdl2"
version = "0.32.2"
@ -709,6 +741,7 @@ dependencies = [
"itertools",
"rand 0.8.4",
"rand_xoshiro",
"regex",
"sdl2",
"structopt",
]

View File

@ -19,3 +19,4 @@ structopt = "0.3"
itertools = "0.10"
rand = "0.8"
rand_xoshiro = "0.6"
regex = "1.5"

215
src/gui.rs Normal file
View File

@ -0,0 +1,215 @@
use super::Result;
use std::fs::File;
use std::io::Read;
use std::collections::HashMap;
use sdl2::pixels::{Color, PixelFormatEnum};
use sdl2::surface::{Surface, SurfaceRef};
use sdl2::image::ImageRWops;
use sdl2::rect::{Rect, Point};
use regex::Regex;
pub const BG_COLOR: Color = Color { r: 65, g: 44, b: 130, a: 255 };
pub const LIFONT_PNG: &'static [u8] = include_bytes!("lifont.png");
pub const MOUSE_PNG: &'static [u8] = include_bytes!("zmouse.png");
pub const MOUSE_SHADOW_PNG: &'static [u8] = include_bytes!("zmouse-shadow.png");
const FONT_ROWS: usize = 16;
const FONT_COLS: usize = 16;
const FONT_MAX_W: usize = 8;
const FONT_MAX_H: usize = 8;
pub struct SurfaceFont<'a> {
char_rects: HashMap<String, Rect>,
pub surf: Surface<'a>,
line_height: u32,
}
impl<'a> SurfaceFont<'a> {
pub fn load_lifont() -> Result<SurfaceFont<'a>> {
let surf = surface_asset(LIFONT_PNG)?;
let surf_rect = surf.rect();
let canvas = surf.into_canvas()?;
let mut char_rects = HashMap::new();
let mut src_rect = Rect::new(0, 0, 8, 8);
for name in include_str!("lifont.txt").lines() {
let pixel_bytes = canvas.read_pixels(src_rect, PixelFormatEnum::ARGB1555)?;
let pixel_shorts = unsafe { std::slice::from_raw_parts(
pixel_bytes.as_ptr() as *const u16,
pixel_bytes.len() / 2,
)};
let mut pixel_points = Vec::new();
for y in 0..8 {
for x in 0..8 {
if (pixel_shorts[y * 8 + x] & 0x8000) != 0 {
pixel_points.push(Point::new(x as i32, y as i32));
}
}
}
let crop = Rect::from_enclose_points(&pixel_points, None)
.unwrap_or(Rect::new(0, 0, 8, 8));
let mut rect = src_rect;
rect.set_x(crop.x() + src_rect.x());
rect.set_width(crop.width());
char_rects.insert(name.to_string(), rect);
// position for next char
src_rect.set_x(src_rect.x() + 8);
if !surf_rect.contains_rect(src_rect) {
src_rect.set_x(0);
src_rect.set_y(src_rect.y() + 8);
}
}
Ok(SurfaceFont {
char_rects,
surf: canvas.into_surface(),
line_height: 8,
})
}
pub fn load_zfont() -> Result<SurfaceFont<'a>> {
let mut zfont_txt = String::new();
// max 8x8 fonts, 16*16=256 characters
let mut canvas = Surface::new(
(FONT_MAX_W * FONT_COLS) as u32,
(FONT_MAX_H * FONT_ROWS) as u32,
PixelFormatEnum::ARGB1555,
)?.into_canvas()?;
canvas.set_draw_color(Color { r: 255, g: 255, b: 255, a: 255 });
File::open("zfont.txt")
.and_then(|mut f| {
f.read_to_string(&mut zfont_txt).map(|_| {})
})
.unwrap_or_else(|_| {
zfont_txt = String::from(include_str!("zfont.txt"));
});
let lines: Vec<_> = zfont_txt.lines().map(str::trim).collect();
let mut char_rects = HashMap::new();
let mut max_height = 0;
let mut line_num = 0;
while line_num < lines.len() {
let char_hdr = lines[line_num];
if char_hdr == "EOF" {
break;
}
let r = Regex::new(r"^; (.*) 0x([0123456789abcdefABCDEF]{2})( \^\^)?$").unwrap();
let groups = r.captures(char_hdr).ok_or_else(||
format!("zfont.txt:{} character descriptor invalid:\n\t{}", line_num, char_hdr)
)?;
let mut name = groups.get(1)
.ok_or_else(|| format!("zfont.txt:{} missing character name:\n\t{}", line_num, char_hdr))?
.as_str()
.to_string();
let hex_match = groups.get(2)
.ok_or_else(|| format!("zfont.txt:{} missing hex code:\n\t{}", line_num, char_hdr))?;
let index = usize::from_str_radix(hex_match.as_str(), 16)?;
if char_rects.len() != index {
return Err(format!(
"zfont.txt:{} code 0x{} out of order (expecting {:x})",
line_num, index, char_rects.len()
).into());
}
let x0 = ((index % FONT_COLS) * FONT_MAX_W) as i32;
let y0 = ((index / FONT_COLS) * FONT_MAX_H) as i32;
let mut y = y0;
let mut max_x = 0;
for row in &lines[line_num + 1..=(line_num + FONT_MAX_H).min(lines.len() - 1)] {
if let Some(';') = row.chars().next() {
break;
}
if *row == "EOF" {
break;
}
let mut x = x0;
if let Ok(mut row_bits) = u16::from_str_radix(row, 2) {
row_bits <<= 16 - FONT_MAX_W;
while row_bits != 0 {
if (row_bits & (1 << 15)) != 0 {
canvas.draw_point(Point::new(x, y))?;
if x > max_x {
max_x = x;
}
}
row_bits <<= 1;
x += 1;
}
} else {
return Err(format!("zfont.txt:{} invalid bit map in character {:?}", line_num, name).into())
}
y += 1;
}
let mut width = 1 + (max_x - x0) as u32;
let height = (y - y0) as u32;
if height > max_height {
max_height = height;
}
if width == 1 {
width = height;
name = " ".to_string();
}
char_rects.insert(name, Rect::new(x0, y0, width, height));
line_num += 1 + height as usize;
}
Ok(SurfaceFont {
char_rects,
surf: canvas.into_surface(),
line_height: max_height,
})
}
pub fn blit_text(&self, target: &mut impl AsMut<SurfaceRef>, text: impl AsRef<str>, dest_rect: Rect) -> Result<()> {
let target = target.as_mut();
let text = text.as_ref();
let mut cursor_x = dest_rect.x();
let mut cursor_y = dest_rect.y();
for i in 0..text.len() {
let c = &text[i..=i];
if c == "\n" {
cursor_x = dest_rect.x();
cursor_y += self.line_height as i32 + 1;
} else if let Some(src_char_rect) = self.char_rects.get(c) {
let mut dst_char_rect = src_char_rect.clone();
dst_char_rect.set_x(cursor_x);
dst_char_rect.set_y(cursor_y);
if dest_rect.contains_rect(dst_char_rect) {
cursor_x += src_char_rect.width() as i32 + 1;
self.surf.blit(*src_char_rect, target, dst_char_rect)?;
} else {
return Err("out of bounds text rendering".into());
}
}
}
Ok(())
}
}
pub fn surface_asset(asset: &'static [u8]) -> Result<Surface<'static>> {
let rwops = sdl2::rwops::RWops::from_bytes(asset)?;
let surf_shortlived = rwops.load_png()?;
// there's a nonsemantic inheritance of lifetime from ImageRWops::load_png. TODO: pr
unsafe {
let ptr = surf_shortlived.raw();
std::mem::forget(surf_shortlived);
Ok(Surface::from_ll(ptr))
}
}
pub enum GuiState {
Menus,
Game
}
impl GuiState {
pub fn toggle(&mut self) {
match self {
GuiState::Menus => *self = GuiState::Game,
GuiState::Game => *self = GuiState::Menus,
}
}
}

256
src/lifont.txt Normal file
View File

@ -0,0 +1,256 @@
frame-top-left
frame-top
frame-top-right
frame-left
frame-right
frame-bottom-left
frame-bottom
frame-bottom-right
cursor-right
cursor-left
cursor-down
cursor-up
cancel
ok
no
back
arrow-up
arrow-down
arrow-left
arrow-right
arrow-up-left
arrow-up-right
arrow-down-left
arrow-down-right
square-big
square-medium
square-small
circle-big
circle-medium
circle-small
diamond
dot
!
"
#
$
%
&
'
(
)
*
+
,
-
.
/
0
1
2
3
4
5
6
7
8
9
:
;
<
=
>
?
@
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
[
\
]
^
_
`
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
{
|
}
~
...
potion-empty
potion-red
potion-yellow
potion-green
potion-blue
potion-purple
coffee
black-tea
green-tea
coffee-to-go
shield-blue
pad-usb-button-1
pad-usb-button-2
pad-usb-button-3
pad-usb-button-4
pad-usb-button-5
pad-usb-button-6
pad-usb-button-7
pad-usb-button-8
pad-usb-button-9
pad-usb-button-10
pad-usb-button-11
pad-usb-button-12
pad-usb-hat
pad-usb-stick
pad-usb-lefthalf
pad-usb-righthalf
pad-xbox-button-1
pad-xbox-button-2
pad-xbox-button-3
pad-xbox-button-4
pad-xbox-button-5
pad-xbox-button-6
pad-xbox-button-7
pad-xbox-button-8
pad-xbox-button-9
pad-xbox-button-10
pad-xbox-button-11
pad-xbox-button-12
pad-xbox-hat
pad-xbox-stick
pad-xbox-lefthalf
pad-xbox-righthalf
pad-ps2-button-1
pad-ps2-button-2
pad-ps2-button-3
pad-ps2-button-4
pad-ps2-button-5
pad-ps2-button-6
pad-ps2-button-7
pad-ps2-button-8
pad-ps2-button-9
pad-ps2-button-10
pad-ps2-button-11
pad-ps2-button-12
pad-ps2-hat
pad-ps2-stick
pad-ps2-lefthalf
pad-ps2-righthalf
pad-wii-button-1
pad-wii-button-2
pad-wii-button-3
pad-wii-button-4
pad-wii-button-5
pad-wii-button-6
pad-wii-button-7
pad-wii-button-8
pad-wii-button-9
pad-wii-button-10
pad-wii-button-11
pad-wii-button-12
pad-wii-hat
pad-wii-stick
pad-wii-lefthalf
pad-wii-righthalf

View File

@ -7,11 +7,11 @@ use std::pin::Pin;
use sdl2::{
EventPump,
event::Event,
image::ImageRWops,
event::{Event, WindowEvent},
keyboard::Keycode,
mouse::{Cursor, MouseUtil},
pixels::{Color, PixelFormatEnum},
rect::Point,
rect::{Point, Rect},
render::{WindowCanvas, BlendMode},
surface::Surface,
};
@ -20,6 +20,7 @@ use rand::{SeedableRng, Rng};
use rand_xoshiro::Xoshiro128Plus;
use structopt::StructOpt;
use itertools::Itertools;
use ferretro_components::base::ControlFlow;
use ferretro_components::prelude::*;
@ -27,12 +28,12 @@ use ferretro_components::provided::{
sdl2::{Sdl2SurfaceComponent, SimpleSdl2AudioComponent},
stdlib::{SleepFramerateLimitComponent, PathBufComponent},
};
use sdl2::mouse::{Cursor, MouseUtil};
use sdl2::rect::Rect;
use itertools::Itertools;
use sdl2::event::WindowEvent;
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
use crate::gui::*;
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
mod gui;
#[derive(StructOpt)]
struct Opt {
@ -47,24 +48,10 @@ struct Opt {
system: Option<PathBuf>,
}
enum GuiState {
Menus,
Game
}
impl GuiState {
fn toggle(&mut self) {
match self {
GuiState::Menus => *self = GuiState::Game,
GuiState::Game => *self = GuiState::Menus,
}
}
}
struct Zretro {
emu: Pin<Box<RetroComponentBase>>,
canvas: WindowCanvas,
font_surf: Surface<'static>,
font: SurfaceFont<'static>,
mouse_util: MouseUtil,
mouse_cursor: Cursor,
mouse_shadow_surf: Surface<'static>,
@ -76,22 +63,6 @@ struct Zretro {
event_pump: EventPump,
}
const BG_COLOR: Color = Color { r: 65, g: 44, b: 130, a: 255 };
const FONT_PNG: &'static [u8] = include_bytes!("lifont.png");
const MOUSE_PNG: &'static [u8] = include_bytes!("zmouse.png");
const MOUSE_SHADOW_PNG: &'static [u8] = include_bytes!("zmouse-shadow.png");
fn surface_asset(asset: &'static [u8]) -> Result<Surface<'static>> {
let rwops = sdl2::rwops::RWops::from_bytes(asset)?;
let surf_shortlived = rwops.load_png()?;
// there's a nonsemantic inheritance of lifetime from ImageRWops::load_png. TODO: pr
unsafe {
let ptr = surf_shortlived.raw();
std::mem::forget(surf_shortlived);
Ok(Surface::from_ll(ptr))
}
}
impl Zretro {
fn new(opt: Opt) -> Result<Self> {
let mut emu = RetroComponentBase::new(&opt.core);
@ -133,7 +104,8 @@ impl Zretro {
emu.init().unwrap();
emu.load_game(&opt.rom).unwrap(); // TODO: optional via CLI positional arg, menu-browsable
let font_surf = surface_asset(FONT_PNG)?;
let font = SurfaceFont::load_lifont()?;
let mouse_surf = surface_asset(MOUSE_PNG)?;
let mouse_shadow_surf = surface_asset(MOUSE_SHADOW_PNG)?;
@ -155,7 +127,7 @@ impl Zretro {
Ok(Zretro {
emu,
canvas,
font_surf,
font,
mouse_util,
mouse_cursor,
mouse_shadow_surf,
@ -213,9 +185,8 @@ impl Zretro {
fn run(&mut self) -> Result<()> {
let tc = self.canvas.texture_creator();
let font_tx = tc.create_texture_from_surface(&self.font_surf)?;
let mut font_rect = self.font_surf.rect();
font_rect.set_height(64);
let font_tx = tc.create_texture_from_surface(&self.font.surf)?;
let font_rect = self.font.surf.rect();
'outer: loop {
let events = self.event_pump.poll_iter().collect_vec();
for event in events {
@ -263,6 +234,12 @@ impl Zretro {
let ui_bg_mut = self.ui_bg.as_mut().ok_or("no bg?")?;
let _ = self.mouse_shadow_surf.blit(None, ui_bg_mut, self.mouse_shadow_pos);
self.font.blit_text(
ui_bg_mut,
"Hello world\nfrom lifont",
Rect::new(144, 32, 100, 20)
)?;
let mut ui_tx = tc.create_texture_from_surface(ui_bg_mut)?;
ui_tx.set_blend_mode(BlendMode::Blend);
self.canvas.copy(&ui_tx, None, None)?;

847
src/zfont.txt Normal file
View File

@ -0,0 +1,847 @@
; empty space 0x00
00000000
00000000
00000000
00000000
00000000
; 0 0x01
01110000
10011000
10101000
11001000
01110000
; 1 0x02
00100000
01100000
00100000
00100000
01110000
; 2 0x03
01110000
10001000
00110000
01000000
11111000
; 3 0x04
01110000
10001000
00110000
10001000
01110000
; 4 0x05
01010000
10010000
11111000
00010000
00010000
; 5 0x06
11111000
10000000
11110000
00001000
11110000
; 6 0x07
01110000
10000000
11110000
10001000
01110000
; 7 0x08
11111000
00001000
00010000
00010000
00010000
; 8 0x09
01110000
10001000
01110000
10001000
01110000
; 9 0x0A
01110000
10001000
01111000
00001000
01110000
; A 0x0B
01110000
10001000
11111000
10001000
10001000
; B 0x0C
11110000
10001000
11110000
10001000
11110000
; C 0x0D
01110000
10001000
10000000
10001000
01110000
; D 0x0E
11110000
10001000
10001000
10001000
11110000
; E 0x0F
11111000
10000000
11110000
10000000
11111000
; F 0x10
11111000
10000000
11110000
10000000
10000000
; G 0x11
01111000
10000000
10011000
10001000
01110000
; H 0x12
10001000
10001000
11111000
10001000
10001000
; I 0x13
11111000
00100000
00100000
00100000
11111000
; J 0x14
01111000
00010000
00010000
10010000
01100000
; K 0x15
10010000
10100000
11100000
10010000
10001000
; L 0x16
10000000
10000000
10000000
10000000
11111000
; M 0x17
11011000
10101000
10101000
10101000
10001000
; N 0x18
11001000
10101000
10101000
10101000
10011000
; O 0x19
01110000
10001000
10001000
10001000
01110000
; P 0x1A
11110000
10001000
11110000
10000000
10000000
; Q 0x1B
01110000
10001000
10101000
10010000
01101000
; R 0x1C
11110000
10001000
11110000
10010000
10001000
; S 0x1D
01111000
10000000
01110000
00001000
11110000
; T 0x1E
11111000
00100000
00100000
00100000
00100000
; U 0x1F
10001000
10001000
10001000
10001000
01110000
; V 0x20
10001000
10001000
01010000
01010000
00100000
; W 0x21
10001000
10101000
10101000
10101000
01010000
; X 0x22
10001000
01010000
00100000
01010000
10001000
; Y 0x23
10001000
01010000
00100000
00100000
00100000
; Z 0x24
11111000
00010000
00100000
01000000
11111000
; - 0x25
00000000
00000000
11111000
00000000
00000000
; _ 0x26
00000000
00000000
00000000
00000000
11111000
; ~ 0x27
01101000
10010000
00000000
00000000
00000000
; . 0x28
00000000
00000000
00000000
00000000
00100000
; / 0x29
00001000
00010000
00100000
01000000
10000000
; < 0x2A
00010000
00100000
01000000
00100000
00010000
; > 0x2B
01000000
00100000
00010000
00100000
01000000
; [ 0x2C
01110000
01000000
01000000
01000000
01110000
; ] 0x2D
01110000
00010000
00010000
00010000
01110000
; : 0x2E
00000000
00100000
00000000
00100000
00000000
; & 0x2F
01100000
10011000
01110000
10011000
01101000
; arrow down 0x30
00100000
00100000
10101000
01110000
00100000
; # 0x31
01010000
11111000
01010000
11111000
01010000
; = 0x32
00000000
11111000
00000000
11111000
00000000
; " 0x33
01001000
10010000
00000000
00000000
00000000
; \ 0x34
10000000
01000000
00100000
00010000
00001000
; * 0x35
10101000
01110000
11111000
01110000
10101000
; ? 0x36
01110000
10001000
00110000
00000000
00100000
; % 0x37
10001000
00010000
00100000
01000000
10001000
; + 0x38
00100000
00100000
11111000
00100000
00100000
; , 0x39
00000000
00000000
00000000
00100000
01000000
; ( 0x3A
00110000
01000000
01000000
01000000
00110000
; ) 0x3B
01100000
00010000
00010000
00010000
01100000
; @ 0x3C
01110000
10011000
10111000
10000000
01110000
; ' 0x3D
00100000
01000000
00000000
00000000
00000000
; ! 0x3E
00100000
00100000
00100000
00000000
00100000
; $ 0x3F
01111000
10100000
01110000
00101000
11110000
; ; 0x40
00000000
00100000
00000000
00100000
01000000
; ` 0x41
01000000
00100000
00000000
00000000
00000000
; ^ 0x42
00100000
01010000
00000000
00000000
00000000
; { 0x43
00110000
01000000
11000000
01000000
00110000
; } 0x44
01100000
00010000
00011000
00010000
01100000
; up 0x45
00100000
00100000
01110000
01110000
11111000
; down 0x46
11111000
01110000
01110000
00100000
00100000
; left 0x47
00001000
00111000
11111000
00111000
00001000
; right 0x48
10000000
11100000
11111000
11100000
10000000
; arrow left 0x49
00100000
01100000
11111000
01100000
00100000
; .5 0x4A
00111000
00100000
00110000
00001000
10110000
; maximize (Win) 0x4B
11111100
10000100
11111100
00000000
00000000
; minimize (Win) 0x4C
00000000
11111100
00000000
00000000
00000000
; maximize (SDL) 0x4D
11111000
10001000
10001000
10001000
11111000
; shw fullstop 0x4E
00000000
00000000
00100000
01010000
00100000
; shw left bracket 0x4F
01110000
01000000
01000000
01000000
00000000
; shw right bracket 0x50
00000000
00010000
00010000
00010000
01110000
; shw comma 0x51
00000000
00000000
00000000
01000000
00100000
; shw mid-dot 0x52
00000000
00100000
01110000
00100000
00000000
; shw wo 0x53
11111000
00001000
11110000
00100000
11000000
; shw mini a 0x54
00000000
11111000
01010000
01100000
01000000
; shw mini i 0x55
00000000
00010000
00100000
11100000
00100000
; shw mini u 0x56
00000000
00100000
11111000
10001000
00110000
; shw mini e 0x57
00000000
00000000
11111000
00100000
11111000
; shw mini o 0x58
00000000
00010000
11111000
00110000
11010000
; shw mini ya 0x59
00000000
01000000
11111000
01010000
01000000
; shw mini yu 0x5A
00000000
00000000
11110000
00010000
11111000
; shw mini yo 0x5B
00000000
11111000
00001000
01111000
11111000
; shw mini tsu 0x5C
00000000
10101000
10101000
00010000
01100000
; shw prolong 0x5D
00000000
10000000
01111000
00000000
00000000
; shw a 0x5E
11111000
00101000
00110000
00100000
11000000
; shw i 0x5F
00001000
00110000
11100000
00100000
00100000
; shw u 0x60
00100000
11111000
10001000
00010000
01100000
; shw e 0x61
11111000
00100000
00100000
00100000
11111000
; shw o 0x62
00010000
11111000
00110000
01010000
10010000
; shw ka 0x63
01000000
11111000
01001000
01001000
10011000
; shw ki 0x64
00100000
11111000
00100000
11111000
00100000
; shw ku 0x65
01000000
01111000
10001000
00010000
01100000
; shw ke 0x66 ^^
01000000
01111000
10010000
00010000
01100000
; shw ko 0x67
11111000
00001000
00001000
00001000
11111000
; shw sa 0x68
01010000
11111000
01010000
00010000
01100000
; shw shi 0x69
01000000
10101000
01001000
00010000
11100000
; shw su 0x6A
11111000
00001000
00010000
00110000
11001000
; shw se 0x6B
01000000
11111000
01010000
01000000
00111000
; shw so 0x6C
10001000
01001000
00001000
00010000
01100000
; shw ta 0x6D
01000000
01111000
11001000
00110000
01100000
; shw chi 0x6E
11111000
00100000
11111000
00100000
01000000
; shw tsu 0x6F
10101000
10101000
00001000
00010000
01100000
; shw te 0x70
11111000
00000000
11111000
00100000
11000000
; shw to 0x71
01000000
01000000
01100000
01010000
01000000
; shw na 0x72
00100000
11111000
00100000
00100000
01000000
; shw ni 0x73
11110000
00000000
00000000
00000000
11111000
; shw nu 0x74
11111000
00001000
00101000
00010000
01101000
; shw ne 0x75
00100000
11111000
00001000
01110000
10101000
; shw no 0x76
00001000
00001000
00001000
00010000
01100000
; shw ha 0x77
01010000
01010000
01010000
10001000
10001000
; shw hi 0x78
10000000
10011000
11100000
10000000
01111000
; shw hu 0x79
11111000
00001000
00001000
00010000
01100000
; shw he 0x7A
01000000
10100000
10010000
00001000
00000000
; shw ho 0x7B
00100000
11111000
01110000
10101000
00100000
; shw ma 0x7C
11111000
00001000
10010000
01100000
00100000
; shw mi 0x7D
11111000
00000000
11111000
00000000
11111000
; shw mu 0x7E
00100000
01000000
01000000
10010000
11111000
; shw me 0x7F
00001000
01001000
00110000
00110000
11001000
; shw mo 0x80
11111000
00100000
11111000
00100000
00111000
; shw ya 0x81
01000000
11111100
01001000
00100000
00100000
; shw yu 0x82
11110000
00010000
00010000
00010000
11111000
; shw yo 0x83
11111000
00001000
11111000
00001000
11111000
; shw ra 0x84
11111000
00000000
11111000
00010000
01100000
; shw ri 0x85
10001000
10001000
10001000
00010000
01100000
; shw ru 0x86
01100000
01100000
01101000
01101000
10110000
; shw re 0x87
10000000
10000000
10001000
10001000
11110000
; shw ro 0x88
11111000
10001000
10001000
10001000
11111000
; shw wa 0x89
11111000
10001000
00001000
00010000
01100000
; shw n 0x8A
10000000
01001000
00001000
00010000
11100000
; shw voiced 0x8B
10100000
10100000
00000000
00000000
00000000
; shw halfvoiced 0x8C
01000000
10100000
01000000
00000000
00000000
EOF