diff options
Diffstat (limited to 'alacritty/src/renderer/text/mod.rs')
-rw-r--r-- | alacritty/src/renderer/text/mod.rs | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/alacritty/src/renderer/text/mod.rs b/alacritty/src/renderer/text/mod.rs new file mode 100644 index 00000000..05ac59e8 --- /dev/null +++ b/alacritty/src/renderer/text/mod.rs @@ -0,0 +1,202 @@ +use bitflags::bitflags; +use crossfont::{GlyphKey, RasterizedGlyph}; + +use alacritty_terminal::term::cell::Flags; +use alacritty_terminal::term::SizeInfo; + +use crate::display::content::RenderableCell; +use crate::gl; +use crate::gl::types::*; + +mod atlas; +mod builtin_font; +mod gles2; +mod glsl3; +pub mod glyph_cache; + +use atlas::Atlas; +pub use gles2::Gles2Renderer; +pub use glsl3::Glsl3Renderer; +pub use glyph_cache::GlyphCache; +use glyph_cache::{Glyph, LoadGlyph}; + +// NOTE: These flags must be in sync with their usage in the text.*.glsl shaders. +bitflags! { + #[repr(C)] + struct RenderingGlyphFlags: u8 { + const WIDE_CHAR = 0b0000_0001; + const COLORED = 0b0000_0010; + } +} + +pub trait TextRenderer<'a> { + type Shader: TextShader; + type RenderBatch: TextRenderBatch; + type RenderApi: TextRenderApi<Self::RenderBatch>; + + /// Get loader API for the renderer. + fn loader_api(&mut self) -> LoaderApi<'_>; + + /// Draw cells. + fn draw_cells<'b: 'a, I: Iterator<Item = RenderableCell>>( + &'b mut self, + size_info: &'b SizeInfo, + glyph_cache: &'a mut GlyphCache, + cells: I, + ) { + self.with_api(size_info, |mut api| { + for cell in cells { + api.draw_cell(cell, glyph_cache, size_info); + } + }) + } + + fn with_api<'b: 'a, F, T>(&'b mut self, size_info: &'b SizeInfo, func: F) -> T + where + F: FnOnce(Self::RenderApi) -> T; + + fn program(&self) -> &Self::Shader; + + /// Resize the text rendering. + fn resize(&self, size: &SizeInfo) { + unsafe { + let program = self.program(); + gl::UseProgram(program.id()); + update_projection(program.projection_uniform(), size); + gl::UseProgram(0); + } + } + + /// Invoke renderer with the loader. + fn with_loader<F: FnOnce(LoaderApi<'_>) -> T, T>(&mut self, func: F) -> T { + unsafe { + gl::ActiveTexture(gl::TEXTURE0); + } + + func(self.loader_api()) + } +} + +pub trait TextRenderBatch { + /// Check if `Batch` is empty. + fn is_empty(&self) -> bool; + + /// Check whether the `Batch` is full. + fn full(&self) -> bool; + + /// Get texture `Batch` is using. + fn tex(&self) -> GLuint; + + /// Add item to the batch. + fn add_item(&mut self, cell: &RenderableCell, glyph: &Glyph, size_info: &SizeInfo); +} + +pub trait TextRenderApi<T: TextRenderBatch>: LoadGlyph { + /// Get `Batch` the api is using. + fn batch(&mut self) -> &mut T; + + /// Render the underlying data. + fn render_batch(&mut self); + + /// Add item to the rendering queue. + #[inline] + fn add_render_item(&mut self, cell: &RenderableCell, glyph: &Glyph, size_info: &SizeInfo) { + // Flush batch if tex changing. + if !self.batch().is_empty() && self.batch().tex() != glyph.tex_id { + self.render_batch(); + } + + self.batch().add_item(cell, glyph, size_info); + + // Render batch and clear if it's full. + if self.batch().full() { + self.render_batch(); + } + } + + /// Draw cell. + fn draw_cell( + &mut self, + mut cell: RenderableCell, + glyph_cache: &mut GlyphCache, + size_info: &SizeInfo, + ) { + // Get font key for cell. + let font_key = match cell.flags & Flags::BOLD_ITALIC { + Flags::BOLD_ITALIC => glyph_cache.bold_italic_key, + Flags::ITALIC => glyph_cache.italic_key, + Flags::BOLD => glyph_cache.bold_key, + _ => glyph_cache.font_key, + }; + + // Ignore hidden cells and render tabs as spaces to prevent font issues. + let hidden = cell.flags.contains(Flags::HIDDEN); + if cell.character == '\t' || hidden { + cell.character = ' '; + } + + let mut glyph_key = + GlyphKey { font_key, size: glyph_cache.font_size, character: cell.character }; + + // Add cell to batch. + let glyph = glyph_cache.get(glyph_key, self, true); + self.add_render_item(&cell, &glyph, size_info); + + // Render visible zero-width characters. + if let Some(zerowidth) = cell.zerowidth.take().filter(|_| !hidden) { + for character in zerowidth { + glyph_key.character = character; + let glyph = glyph_cache.get(glyph_key, self, false); + self.add_render_item(&cell, &glyph, size_info); + } + } + } +} + +pub trait TextShader { + fn id(&self) -> GLuint; + + /// Id of the projection uniform. + fn projection_uniform(&self) -> GLint; +} + +#[derive(Debug)] +pub struct LoaderApi<'a> { + active_tex: &'a mut GLuint, + atlas: &'a mut Vec<Atlas>, + current_atlas: &'a mut usize, +} + +impl<'a> LoadGlyph for LoaderApi<'a> { + fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph { + Atlas::load_glyph(self.active_tex, self.atlas, self.current_atlas, rasterized) + } + + fn clear(&mut self) { + Atlas::clear_atlas(self.atlas, self.current_atlas) + } +} + +fn update_projection(u_projection: GLint, size: &SizeInfo) { + let width = size.width(); + let height = size.height(); + let padding_x = size.padding_x(); + let padding_y = size.padding_y(); + + // Bounds check. + if (width as u32) < (2 * padding_x as u32) || (height as u32) < (2 * padding_y as u32) { + return; + } + + // Compute scale and offset factors, from pixel to ndc space. Y is inverted. + // [0, width - 2 * padding_x] to [-1, 1] + // [height - 2 * padding_y, 0] to [-1, 1] + let scale_x = 2. / (width - 2. * padding_x); + let scale_y = -2. / (height - 2. * padding_y); + let offset_x = -1.; + let offset_y = 1.; + + unsafe { + gl::Uniform4f(u_projection, offset_x, offset_y, scale_x, scale_y); + } +} |