aboutsummaryrefslogtreecommitdiff
path: root/alacritty/src/renderer/text/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'alacritty/src/renderer/text/mod.rs')
-rw-r--r--alacritty/src/renderer/text/mod.rs202
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);
+ }
+}