use bitflags::bitflags;
use crossfont::{GlyphKey, RasterizedGlyph};
use alacritty_terminal::term::cell::Flags;
use crate::display::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};
bitflags! {
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct RenderingGlyphFlags: u8 {
const COLORED = 0b0000_0001;
const WIDE_CHAR = 0b0000_0010;
}
}
#[repr(u8)]
enum RenderingPass {
Background = 0,
SubpixelPass1 = 1,
SubpixelPass2 = 2,
SubpixelPass3 = 3,
}
pub trait TextRenderer<'a> {
type Shader: TextShader;
type RenderBatch: TextRenderBatch;
type RenderApi: TextRenderApi<Self::RenderBatch>;
fn loader_api(&mut self) -> LoaderApi<'_>;
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;
fn resize(&self, size: &SizeInfo) {
unsafe {
let program = self.program();
gl::UseProgram(program.id());
update_projection(program.projection_uniform(), size);
gl::UseProgram(0);
}
}
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 {
fn is_empty(&self) -> bool;
fn full(&self) -> bool;
fn tex(&self) -> GLuint;
fn add_item(&mut self, cell: &RenderableCell, glyph: &Glyph, size_info: &SizeInfo);
}
pub trait TextRenderApi<T: TextRenderBatch>: LoadGlyph {
fn batch(&mut self) -> &mut T;
fn render_batch(&mut self);
#[inline]
fn add_render_item(&mut self, cell: &RenderableCell, glyph: &Glyph, size_info: &SizeInfo) {
if !self.batch().is_empty() && self.batch().tex() != glyph.tex_id {
self.render_batch();
}
self.batch().add_item(cell, glyph, size_info);
if self.batch().full() {
self.render_batch();
}
}
fn draw_cell(
&mut self,
mut cell: RenderableCell,
glyph_cache: &mut GlyphCache,
size_info: &SizeInfo,
) {
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,
};
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 };
let glyph = glyph_cache.get(glyph_key, self, true);
self.add_render_item(&cell, &glyph, size_info);
if let Some(zerowidth) =
cell.extra.as_mut().and_then(|extra| extra.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;
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 LoadGlyph for LoaderApi<'_> {
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();
if (width as u32) < (2 * padding_x as u32) || (height as u32) < (2 * padding_y as u32) {
return;
}
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);
}
}