GUI actually displays the text of the loaded buffer now. Woo hoo!
To make this performant, the Font struct caches rendered glyphs as sdl textures. Without caching, performance is abysmal.
This commit is contained in:
parent
463911e5b3
commit
6c6e8896a1
77
src/font.rs
77
src/font.rs
|
@ -1,6 +1,7 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use freetype;
|
use freetype;
|
||||||
use sdl2;
|
use sdl2;
|
||||||
|
@ -8,10 +9,22 @@ use sdl2;
|
||||||
use sdl2::surface::Surface;
|
use sdl2::surface::Surface;
|
||||||
use sdl2::rect::Rect;
|
use sdl2::rect::Rect;
|
||||||
|
|
||||||
|
use string_utils::{is_line_ending};
|
||||||
|
|
||||||
|
struct CachedGlyph {
|
||||||
|
texture: Option<sdl2::render::Texture>,
|
||||||
|
height: i32,
|
||||||
|
width: i32,
|
||||||
|
advance: i32,
|
||||||
|
bitmap_top: i32,
|
||||||
|
bitmap_left: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct Font {
|
pub struct Font {
|
||||||
ftl: freetype::Library,
|
ftl: freetype::Library,
|
||||||
face: freetype::Face,
|
face: freetype::Face,
|
||||||
|
glyph_cache: HashMap<char, CachedGlyph>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Font {
|
impl Font {
|
||||||
|
@ -23,29 +36,59 @@ impl Font {
|
||||||
Font {
|
Font {
|
||||||
ftl: lib,
|
ftl: lib,
|
||||||
face: face,
|
face: face,
|
||||||
|
glyph_cache: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn draw_text(&mut self, text: &str, color: (u8, u8, u8), cx: i32, cy: i32, renderer: &sdl2::render::Renderer) {
|
pub fn line_height(&self) -> usize {
|
||||||
|
self.face.height() as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn draw_text(&mut self, text: &str, color: (u8, u8, u8), cx: i32, cy: i32, renderer: &sdl2::render::Renderer) -> usize {
|
||||||
let mut x = cx;
|
let mut x = cx;
|
||||||
let mut y = cy;
|
let y = cy;
|
||||||
|
|
||||||
for grapheme in text.graphemes(true) {
|
for grapheme in text.graphemes(true) {
|
||||||
for ch in grapheme.chars() {
|
if is_line_ending(grapheme) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if grapheme == "\t" {
|
||||||
|
// TODO: handle tab characters
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let ch = grapheme.chars().next().unwrap();
|
||||||
|
|
||||||
|
// Generate and cache glyph if we haven't already
|
||||||
|
if !self.glyph_cache.contains_key(&ch) {
|
||||||
|
let mut cg = CachedGlyph {
|
||||||
|
texture: None,
|
||||||
|
height: 0,
|
||||||
|
width: 0,
|
||||||
|
advance: 0,
|
||||||
|
bitmap_top: 0,
|
||||||
|
bitmap_left: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
let _ = self.face.load_char(ch as u64, freetype::face::RENDER);
|
let _ = self.face.load_char(ch as u64, freetype::face::RENDER);
|
||||||
let g = self.face.glyph();
|
let g = self.face.glyph();
|
||||||
|
|
||||||
match (g.bitmap().width(), g.bitmap().rows()) {
|
match (g.bitmap().width(), g.bitmap().rows()) {
|
||||||
(0, _) | (_, 0) => {
|
(0, _) | (_, 0) => {
|
||||||
|
cg.advance = (g.advance().x >> 6) as i32;
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
// Get the char's glyph bitmap as an sdl surface
|
// Get the char's glyph bitmap as an sdl surface
|
||||||
|
|
||||||
let bitmap = g.bitmap();
|
let bitmap = g.bitmap();
|
||||||
let width = g.bitmap().width() as isize;
|
cg.width = g.bitmap().width();
|
||||||
let height = g.bitmap().rows() as isize;
|
cg.height = g.bitmap().rows();
|
||||||
|
cg.advance = (g.advance().x >> 6) as i32;
|
||||||
|
cg.bitmap_left = g.bitmap_left();
|
||||||
|
cg.bitmap_top = g.bitmap_top();
|
||||||
|
|
||||||
let mut buf = Vec::with_capacity(bitmap.buffer().len() * 4);
|
let mut buf = Vec::with_capacity(bitmap.buffer().len() * 4);
|
||||||
for b in bitmap.buffer().iter() {
|
for b in bitmap.buffer().iter() {
|
||||||
buf.push(*b);
|
buf.push(*b);
|
||||||
|
@ -53,22 +96,26 @@ impl Font {
|
||||||
buf.push(color.1);
|
buf.push(color.1);
|
||||||
buf.push(color.0);
|
buf.push(color.0);
|
||||||
}
|
}
|
||||||
let gs = Surface::from_data(buf.as_mut_slice(), width, height, 32, width*4, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF).unwrap();
|
let gs = Surface::from_data(buf.as_mut_slice(), cg.width as isize, cg.height as isize, 32, (cg.width as isize) * 4, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF).unwrap();
|
||||||
|
|
||||||
// Get glyph surface as a texture
|
// Get glyph surface as a texture
|
||||||
let gt = renderer.create_texture_from_surface(&gs).unwrap();
|
cg.texture = Some(renderer.create_texture_from_surface(&gs).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.glyph_cache.insert(ch, cg);
|
||||||
|
}
|
||||||
|
|
||||||
// Draw the glyph
|
// Draw the glyph
|
||||||
let _ = renderer.copy(>, Some(Rect{x:0, y:0, h:height as i32, w:width as i32}), Some(Rect{x:x+g.bitmap_left(), y:y-g.bitmap_top(), h:height as i32, w:width as i32}));
|
let ref cg = self.glyph_cache[ch];
|
||||||
|
if let Some(ref tex) = cg.texture {
|
||||||
|
let _ = renderer.copy(tex, Some(Rect{x:0, y:0, h:cg.height, w:cg.width}), Some(Rect{x:x+cg.bitmap_left, y:y-cg.bitmap_top, h:cg.height, w:cg.width}));
|
||||||
|
}
|
||||||
|
|
||||||
|
x += cg.advance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
x += (g.advance().x >> 6) as i32;
|
return (x - cx) as usize;
|
||||||
y += (g.advance().y >> 6) as i32;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use sdl2;
|
use sdl2;
|
||||||
|
|
||||||
use font::Font;
|
use font::Font;
|
||||||
|
@ -12,7 +14,7 @@ pub struct GUI {
|
||||||
|
|
||||||
impl GUI {
|
impl GUI {
|
||||||
pub fn new() -> GUI {
|
pub fn new() -> GUI {
|
||||||
let mut font = Font::new_from_file(&Path::new("./fonts/source_code_pro/SourceCodePro-Regular.ttf"), 14);
|
let font = Font::new_from_file(&Path::new("./fonts/source_code_pro/SourceCodePro-Regular.ttf"), 14);
|
||||||
|
|
||||||
// Get the window and renderer for sdl
|
// Get the window and renderer for sdl
|
||||||
let window = sdl2::video::Window::new("Led Editor", sdl2::video::WindowPos::PosCentered, sdl2::video::WindowPos::PosCentered, 800, 600, sdl2::video::OPENGL | sdl2::video::RESIZABLE).unwrap();
|
let window = sdl2::video::Window::new("Led Editor", sdl2::video::WindowPos::PosCentered, sdl2::video::WindowPos::PosCentered, 800, 600, sdl2::video::OPENGL | sdl2::video::RESIZABLE).unwrap();
|
||||||
|
@ -30,7 +32,7 @@ impl GUI {
|
||||||
|
|
||||||
|
|
||||||
pub fn new_from_editor(ed: Editor) -> GUI {
|
pub fn new_from_editor(ed: Editor) -> GUI {
|
||||||
let mut font = Font::new_from_file(&Path::new("./fonts/source_code_pro/SourceCodePro-Regular.ttf"), 14);
|
let font = Font::new_from_file(&Path::new("./fonts/source_code_pro/SourceCodePro-Regular.ttf"), 14);
|
||||||
|
|
||||||
// Get the window and renderer for sdl
|
// Get the window and renderer for sdl
|
||||||
let window = sdl2::video::Window::new("Led Editor", sdl2::video::WindowPos::PosCentered, sdl2::video::WindowPos::PosCentered, 800, 600, sdl2::video::OPENGL | sdl2::video::RESIZABLE).unwrap();
|
let window = sdl2::video::Window::new("Led Editor", sdl2::video::WindowPos::PosCentered, sdl2::video::WindowPos::PosCentered, 800, 600, sdl2::video::OPENGL | sdl2::video::RESIZABLE).unwrap();
|
||||||
|
@ -59,7 +61,8 @@ impl GUI {
|
||||||
sdl2::event::Event::Window(_, _, sdl2::event::WindowEventId::Exposed, _, _) => {
|
sdl2::event::Event::Window(_, _, sdl2::event::WindowEventId::Exposed, _, _) => {
|
||||||
let _ = self.renderer.set_draw_color(sdl2::pixels::Color::RGB(240, 240, 240));
|
let _ = self.renderer.set_draw_color(sdl2::pixels::Color::RGB(240, 240, 240));
|
||||||
let _ = self.renderer.clear();
|
let _ = self.renderer.clear();
|
||||||
self.font.draw_text("Hi there! (How's it going???) { let b = 42; }", (0, 0, 0), 50, 50, &self.renderer);
|
//self.font.draw_text("Hi there! (How's it going???) { let b = 42; }", (0, 0, 0), 50, 50, &self.renderer);
|
||||||
|
self.draw_editor_text((50, 50), (300, 300));
|
||||||
self.renderer.present();
|
self.renderer.present();
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -67,4 +70,20 @@ impl GUI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn draw_editor_text(&mut self, c1: (usize, usize), c2: (usize, usize)) {
|
||||||
|
let mut line_iter = self.editor.buffer.line_iter();
|
||||||
|
|
||||||
|
let mut x = c1.1;
|
||||||
|
let mut y = c1.0;
|
||||||
|
|
||||||
|
for line in line_iter {
|
||||||
|
for g in line.grapheme_iter() {
|
||||||
|
x += self.font.draw_text(g, (0, 0, 0), x as i32, y as i32, &self.renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
x = c1.1;
|
||||||
|
y += self.font.line_height() >> 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user