203 lines
5.6 KiB
Rust
203 lines
5.6 KiB
Rust
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);
|
|
}
|
|
}
|