use bitflags::bitflags; use crossfont::{GlyphKey, RasterizedGlyph}; use alacritty_terminal::term::cell::Flags; use crate::display::content::RenderableCell; use crate::display::SizeInfo; 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; /// Get loader API for the renderer. fn loader_api(&mut self) -> LoaderApi<'_>; /// Draw cells. fn draw_cells<'b: 'a, I: Iterator>( &'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) -> 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: 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, 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); } }