diff --git a/src/font.rs b/src/font.rs index b276729..7a019cc 100644 --- a/src/font.rs +++ b/src/font.rs @@ -1,6 +1,7 @@ #![allow(dead_code)] use std::path::Path; +use std::collections::HashMap; use freetype; use sdl2; @@ -8,10 +9,22 @@ use sdl2; use sdl2::surface::Surface; use sdl2::rect::Rect; +use string_utils::{is_line_ending}; + +struct CachedGlyph { + texture: Option, + height: i32, + width: i32, + advance: i32, + bitmap_top: i32, + bitmap_left: i32, +} + pub struct Font { ftl: freetype::Library, face: freetype::Face, + glyph_cache: HashMap, } impl Font { @@ -23,52 +36,86 @@ impl Font { Font { ftl: lib, 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 y = cy; + let y = cy; for grapheme in text.graphemes(true) { - for ch in grapheme.chars() { - let _ = self.face.load_char(ch as u64, freetype::face::RENDER); - let g = self.face.glyph(); - - match (g.bitmap().width(), g.bitmap().rows()) { - (0, _) | (_, 0) => { - }, + 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, + }; - _ => { - // Get the char's glyph bitmap as an sdl surface + + let _ = self.face.load_char(ch as u64, freetype::face::RENDER); + let g = self.face.glyph(); + + match (g.bitmap().width(), g.bitmap().rows()) { + (0, _) | (_, 0) => { + cg.advance = (g.advance().x >> 6) as i32; + }, - let bitmap = g.bitmap(); - let width = g.bitmap().width() as isize; - let height = g.bitmap().rows() as isize; - let mut buf = Vec::with_capacity(bitmap.buffer().len() * 4); - for b in bitmap.buffer().iter() { - buf.push(*b); - buf.push(color.2); - buf.push(color.1); - buf.push(color.0); + _ => { + // Get the char's glyph bitmap as an sdl surface + let bitmap = g.bitmap(); + cg.width = g.bitmap().width(); + 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); + for b in bitmap.buffer().iter() { + buf.push(*b); + buf.push(color.2); + buf.push(color.1); + buf.push(color.0); + } + 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 + cg.texture = Some(renderer.create_texture_from_surface(&gs).unwrap()); } - let gs = Surface::from_data(buf.as_mut_slice(), width, height, 32, width*4, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF).unwrap(); - - // Get glyph surface as a texture - let gt = renderer.create_texture_from_surface(&gs).unwrap(); - - // 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})); - - } + + self.glyph_cache.insert(ch, cg); + } + + // Draw the glyph + 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 += (g.advance().x >> 6) as i32; - y += (g.advance().y >> 6) as i32; - break; + x += cg.advance; } } + + return (x - cx) as usize; } } diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 206d963..327c6b6 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + use sdl2; use font::Font; @@ -12,7 +14,7 @@ pub struct GUI { impl 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 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 { - 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 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, _, _) => { let _ = self.renderer.set_draw_color(sdl2::pixels::Color::RGB(240, 240, 240)); 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(); } _ => {} @@ -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; + } + } } \ No newline at end of file