diff options
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | alacritty/res/gles2/text.f.glsl | 57 | ||||
-rw-r--r-- | alacritty/res/gles2/text.v.glsl | 46 | ||||
-rw-r--r-- | alacritty/res/glsl3/text.f.glsl (renamed from alacritty/res/text.f.glsl) | 1 | ||||
-rw-r--r-- | alacritty/res/glsl3/text.v.glsl (renamed from alacritty/res/text.v.glsl) | 1 | ||||
-rw-r--r-- | alacritty/res/rect.f.glsl | 118 | ||||
-rw-r--r-- | alacritty/res/rect.v.glsl | 11 | ||||
-rw-r--r-- | alacritty/src/display/mod.rs | 55 | ||||
-rw-r--r-- | alacritty/src/renderer/mod.rs | 1221 | ||||
-rw-r--r-- | alacritty/src/renderer/rects.rs | 10 | ||||
-rw-r--r-- | alacritty/src/renderer/shader.rs | 49 | ||||
-rw-r--r-- | alacritty/src/renderer/text/atlas.rs | 273 | ||||
-rw-r--r-- | alacritty/src/renderer/text/builtin_font.rs (renamed from alacritty/src/renderer/builtin_font.rs) | 0 | ||||
-rw-r--r-- | alacritty/src/renderer/text/gles2.rs | 478 | ||||
-rw-r--r-- | alacritty/src/renderer/text/glsl3.rs | 460 | ||||
-rw-r--r-- | alacritty/src/renderer/text/glyph_cache.rs | 322 | ||||
-rw-r--r-- | alacritty/src/renderer/text/mod.rs | 202 |
18 files changed, 2066 insertions, 1241 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 15b9a41c..188276c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Track and report surface damage information to Wayland compositors - Escape sequence for undercurl, dotted and dashed underlines (`CSI 4 : [3-5] m`) - `ToggleMaximized` key binding action to (un-)maximize the active window, not bound by default +- Support for OpenGL ES 2.0 ### Changed @@ -47,7 +47,7 @@ For everyone else, the detailed instructions to install Alacritty can be found ### Requirements -- OpenGL 3.3 or higher +- At least OpenGL ES 2.0 - [Windows] ConPTY support (Windows 10 version 1809 or higher) ## Configuration diff --git a/alacritty/res/gles2/text.f.glsl b/alacritty/res/gles2/text.f.glsl new file mode 100644 index 00000000..cc5943c9 --- /dev/null +++ b/alacritty/res/gles2/text.f.glsl @@ -0,0 +1,57 @@ +varying mediump vec2 TexCoords; +varying mediump vec3 fg; +varying highp float colored; +varying mediump vec4 bg; + +uniform highp int renderingPass; +uniform sampler2D mask; + +#define COLORED 1 + +mediump float max_rgb(mediump vec3 mask) { + return max(max(mask.r, mask.g), mask.b); +} + +void render_text() { + mediump vec4 mask = texture2D(mask, TexCoords); + mediump float m_rgb = max_rgb(mask.rgb); + + if (renderingPass == 1) { + gl_FragColor = vec4(mask.rgb, m_rgb); + } else if (renderingPass == 2) { + gl_FragColor = bg * (vec4(m_rgb) - vec4(mask.rgb, m_rgb)); + } else { + gl_FragColor = vec4(fg, 1.) * vec4(mask.rgb, m_rgb); + } +} + +// Render colored bitmaps. +void render_bitmap() { + if (renderingPass == 2) { + discard; + } + mediump vec4 mask = texture2D(mask, TexCoords); + if (renderingPass == 1) { + gl_FragColor = mask.aaaa; + } else { + gl_FragColor = mask; + } +} + +void main() { + // Handle background pass drawing before anything else. + if (renderingPass == 0) { + if (bg.a == 0.0) { + discard; + } + + gl_FragColor = vec4(bg.rgb * bg.a, bg.a); + return; + } + + if (int(colored) == COLORED) { + render_bitmap(); + } else { + render_text(); + } +} diff --git a/alacritty/res/gles2/text.v.glsl b/alacritty/res/gles2/text.v.glsl new file mode 100644 index 00000000..009094cc --- /dev/null +++ b/alacritty/res/gles2/text.v.glsl @@ -0,0 +1,46 @@ +// Cell coords. +attribute vec2 cellCoords; + +// Glyph coords. +attribute vec2 glyphCoords; + +// uv mapping. +attribute vec2 uv; + +// Text foreground rgb packed together with cell flags. textColor.a +// are the bitflags; consult RenderingGlyphFlags in renderer/mod.rs +// for the possible values. +attribute vec4 textColor; + +// Background color. +attribute vec4 backgroundColor; + +varying vec2 TexCoords; +varying vec3 fg; +varying float colored; +varying vec4 bg; + +uniform highp int renderingPass; +uniform vec4 projection; + +void main() { + vec2 projectionOffset = projection.xy; + vec2 projectionScale = projection.zw; + + vec2 position; + if (renderingPass == 0) { + TexCoords = vec2(0, 0); + position = cellCoords; + } else { + TexCoords = uv; + position = glyphCoords; + } + + fg = vec3(float(textColor.r), float(textColor.g), float(textColor.b)) / 255.; + colored = float(textColor.a); + bg = vec4(float(backgroundColor.r), float(backgroundColor.g), float(backgroundColor.b), + float(backgroundColor.a)) / 255.; + + vec2 finalPosition = projectionOffset + position * projectionScale; + gl_Position = vec4(finalPosition, 0., 1.); +} diff --git a/alacritty/res/text.f.glsl b/alacritty/res/glsl3/text.f.glsl index 3b8cd67c..eddc1734 100644 --- a/alacritty/res/text.f.glsl +++ b/alacritty/res/glsl3/text.f.glsl @@ -1,4 +1,3 @@ -#version 330 core in vec2 TexCoords; flat in vec4 fg; flat in vec4 bg; diff --git a/alacritty/res/text.v.glsl b/alacritty/res/glsl3/text.v.glsl index a4a31382..5c22e0e6 100644 --- a/alacritty/res/text.v.glsl +++ b/alacritty/res/glsl3/text.v.glsl @@ -1,4 +1,3 @@ -#version 330 core // Cell properties. layout(location = 0) in vec2 gridCoords; diff --git a/alacritty/res/rect.f.glsl b/alacritty/res/rect.f.glsl index aad8b418..10c7983a 100644 --- a/alacritty/res/rect.f.glsl +++ b/alacritty/res/rect.f.glsl @@ -1,18 +1,32 @@ -#version 330 core +#if defined(GLES2_RENDERER) +#define float_t mediump float +#define color_t mediump vec4 +#define FRAG_COLOR gl_FragColor + +varying color_t color; + +#else +#define float_t float +#define int_t int +#define color_t vec4 -flat in vec4 color; out vec4 FragColor; -uniform int rectKind; +#define FRAG_COLOR FragColor -uniform float cellWidth; -uniform float cellHeight; -uniform float paddingY; -uniform float paddingX; +flat in color_t color; -uniform float underlinePosition; -uniform float underlineThickness; +#endif -uniform float undercurlPosition; +uniform int rectKind; +uniform float_t cellWidth; +uniform float_t cellHeight; +uniform float_t paddingY; +uniform float_t paddingX; + +uniform float_t underlinePosition; +uniform float_t underlineThickness; + +uniform float_t undercurlPosition; #define UNDERCURL 1 #define DOTTED 2 @@ -20,19 +34,18 @@ uniform float undercurlPosition; #define PI 3.1415926538 -vec4 draw_undercurl(int x, int y) { +color_t draw_undercurl(float_t x, float_t y) { // We use `undercurlPosition` as an amplitude, since it's half of the descent // value. - float undercurl = - undercurlPosition / 2. * cos(float(x) * 2 * PI / cellWidth) + - + undercurlPosition - 1.; + float_t undercurl = undercurlPosition / 2. * cos(x * 2. * PI / cellWidth) + + undercurlPosition - 1.; - float undercurlTop = undercurl + max((underlineThickness - 1), 0); - float undercurlBottom = undercurl - max((underlineThickness - 1), 0); + float_t undercurlTop = undercurl + max((underlineThickness - 1.), 0.); + float_t undercurlBottom = undercurl - max((underlineThickness - 1.), 0.); // Compute resulted alpha based on distance from `gl_FragCoord.y` to the // cosine curve. - float alpha = 1.; + float_t alpha = 1.; if (y > undercurlTop || y < undercurlBottom) { alpha = 1. - min(abs(undercurlTop - y), abs(undercurlBottom - y)); } @@ -43,54 +56,54 @@ vec4 draw_undercurl(int x, int y) { // When the dot size increases we can use AA to make spacing look even and the // dots rounded. -vec4 draw_dotted_aliased(float x, float y) { - int dotNumber = int(x / underlineThickness); +color_t draw_dotted_aliased(float_t x, float_t y) { + float_t dotNumber = floor(x / underlineThickness); - float radius = underlineThickness / 2.; - float centerY = underlinePosition - 1.; + float_t radius = underlineThickness / 2.; + float_t centerY = underlinePosition - 1.; - float leftCenter = (dotNumber - dotNumber % 2) * underlineThickness + radius; - float rightCenter = leftCenter + 2 * underlineThickness; + float_t leftCenter = (dotNumber - mod(dotNumber, 2.)) * underlineThickness + radius; + float_t rightCenter = leftCenter + 2. * underlineThickness; - float distanceLeft = sqrt(pow(x - leftCenter, 2) + pow(y - centerY, 2)); - float distanceRight = sqrt(pow(x - rightCenter, 2) + pow(y - centerY, 2)); + float_t distanceLeft = sqrt(pow(x - leftCenter, 2.) + pow(y - centerY, 2.)); + float_t distanceRight = sqrt(pow(x - rightCenter, 2.) + pow(y - centerY, 2.)); - float alpha = max(1 - (min(distanceLeft, distanceRight) - radius), 0); + float_t alpha = max(1. - (min(distanceLeft, distanceRight) - radius), 0.); return vec4(color.rgb, alpha); } /// Draw dotted line when dot is just a single pixel. -vec4 draw_dotted(int x, int y) { - int cellEven = 0; +color_t draw_dotted(float_t x, float_t y) { + float_t cellEven = 0.; // Since the size of the dot and its gap combined is 2px we should ensure that // spacing will be even. If the cellWidth is even it'll work since we start // with dot and end with gap. However if cellWidth is odd, the cell will start // and end with a dot, creating a dash. To resolve this issue, we invert the // pattern every two cells. - if (int(cellWidth) % 2 != 0) { - cellEven = int((gl_FragCoord.x - paddingX) / cellWidth) % 2; + if (int(mod(cellWidth, 2.)) != 0) { + cellEven = mod((gl_FragCoord.x - paddingX) / cellWidth, 2.); } // Since we use the entire descent area for dotted underlines, we limit its // height to a single pixel so we don't draw bars instead of dots. - float alpha = 1. - abs(round(underlinePosition - 1.) - y); - if (x % 2 != cellEven) { - alpha = 0; + float_t alpha = 1. - abs(floor(underlinePosition - 0.5) - y); + if (int(mod(x, 2.)) != int(cellEven)) { + alpha = 0.; } return vec4(color.rgb, alpha); } -vec4 draw_dashed(int x) { +color_t draw_dashed(float_t x) { // Since dashes of adjacent cells connect with each other our dash length is // half of the desired total length. - int halfDashLen = int(cellWidth) / 4; + float_t halfDashLen = floor(cellWidth / 4.); - float alpha = 1.; + float_t alpha = 1.; // Check if `x` coordinate is where we should draw gap. - if (x > halfDashLen && x < cellWidth - halfDashLen - 1) { + if (x > halfDashLen && x < cellWidth - halfDashLen - 1.) { alpha = 0.; } @@ -98,25 +111,20 @@ vec4 draw_dashed(int x) { } void main() { - int x = int(gl_FragCoord.x - paddingX) % int(cellWidth); - int y = int(gl_FragCoord.y - paddingY) % int(cellHeight); - - switch (rectKind) { - case UNDERCURL: - FragColor = draw_undercurl(x, y); - break; - case DOTTED: - if (underlineThickness < 2) { - FragColor = draw_dotted(x, y); + float_t x = floor(mod(gl_FragCoord.x - paddingX, cellWidth)); + float_t y = floor(mod(gl_FragCoord.y - paddingY, cellHeight)); + + if (rectKind == UNDERCURL) { + FRAG_COLOR = draw_undercurl(x, y); + } else if (rectKind == DOTTED) { + if (underlineThickness < 2.) { + FRAG_COLOR = draw_dotted(x, y); } else { - FragColor = draw_dotted_aliased(x, y); + FRAG_COLOR = draw_dotted_aliased(x, y); } - break; - case DASHED: - FragColor = draw_dashed(x); - break; - default: - FragColor = color; - break; + } else if (rectKind == DASHED) { + FRAG_COLOR = draw_dashed(x); + } else { + FRAG_COLOR = color; } } diff --git a/alacritty/res/rect.v.glsl b/alacritty/res/rect.v.glsl index bf9a97d3..746eab74 100644 --- a/alacritty/res/rect.v.glsl +++ b/alacritty/res/rect.v.glsl @@ -1,11 +1,16 @@ -#version 330 core +#if defined(GLES2_RENDERER) +attribute vec2 aPos; +attribute vec4 aColor; + +varying mediump vec4 color; +#else layout (location = 0) in vec2 aPos; layout (location = 1) in vec4 aColor; flat out vec4 color; +#endif -void main() -{ +void main() { color = aColor; gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0); } diff --git a/alacritty/src/display/mod.rs b/alacritty/src/display/mod.rs index dfd43d04..7e8cfc86 100644 --- a/alacritty/src/display/mod.rs +++ b/alacritty/src/display/mod.rs @@ -50,7 +50,7 @@ use crate::display::window::Window; use crate::event::{Mouse, SearchState}; use crate::message_bar::{MessageBuffer, MessageType}; use crate::renderer::rects::{RenderLines, RenderRect}; -use crate::renderer::{self, GlyphCache, QuadRenderer}; +use crate::renderer::{self, GlyphCache, Renderer}; pub mod content; pub mod cursor; @@ -204,7 +204,7 @@ pub struct Display { is_damage_supported: bool, debug_damage: bool, damage_rects: Vec<DamageRect>, - renderer: QuadRenderer, + renderer: Renderer, glyph_cache: GlyphCache, meter: Meter, } @@ -259,7 +259,7 @@ impl Display { )?; // Create renderer. - let mut renderer = QuadRenderer::new()?; + let mut renderer = Renderer::new()?; let scale_factor = window.scale_factor; info!("Display scale factor: {}", scale_factor); @@ -308,9 +308,7 @@ impl Display { // Clear screen. let background_color = config.colors.primary.background; - renderer.with_api(config, &size_info, |api| { - api.clear(background_color); - }); + renderer.clear(background_color, config.window_opacity()); // Set subpixel anti-aliasing. #[cfg(target_os = "macos")] @@ -325,9 +323,7 @@ impl Display { #[cfg(not(any(target_os = "macos", windows)))] if is_x11 { window.swap_buffers(); - renderer.with_api(config, &size_info, |api| { - api.finish(); - }); + renderer.finish(); } window.set_visible(true); @@ -376,7 +372,7 @@ impl Display { /// /// This will return a tuple of the cell width and height. fn update_glyph_cache( - renderer: &mut QuadRenderer, + renderer: &mut Renderer, glyph_cache: &mut GlyphCache, scale_factor: f64, config: &UiConfig, @@ -573,10 +569,7 @@ impl Display { // Make sure this window's OpenGL context is active. self.window.make_current(); - self.renderer.with_api(config, &size_info, |api| { - api.clear(background_color); - }); - + self.renderer.clear(background_color, config.window_opacity()); let mut lines = RenderLines::new(); // Draw grid. @@ -590,9 +583,10 @@ impl Display { let glyph_cache = &mut self.glyph_cache; let highlighted_hint = &self.highlighted_hint; let vi_highlighted_hint = &self.vi_highlighted_hint; - self.renderer.with_api(config, &size_info, |mut api| { - // Iterate over all non-empty cells in the grid. - for mut cell in grid_cells { + self.renderer.draw_cells( + &size_info, + glyph_cache, + grid_cells.into_iter().map(|mut cell| { // Underline hints hovered by mouse or vi mode cursor. let point = viewport_to_point(display_offset, cell.point); if highlighted_hint.as_ref().map_or(false, |h| h.bounds.contains(&point)) @@ -604,10 +598,9 @@ impl Display { // Update underline/strikeout. lines.update(&cell); - // Draw the cell. - api.draw_cell(cell, glyph_cache); - } - }); + cell + }), + ); } let mut rects = lines.rects(&metrics, &size_info); @@ -675,9 +668,7 @@ impl Display { let fg = config.colors.primary.background; for (i, message_text) in text.iter().enumerate() { let point = Point::new(start_line + i, Column(0)); - self.renderer.with_api(config, &size_info, |mut api| { - api.draw_string(glyph_cache, point, fg, bg, message_text); - }); + self.renderer.draw_string(point, fg, bg, message_text, &size_info, glyph_cache); } } else { // Draw rectangles. @@ -726,9 +717,7 @@ impl Display { // On X11 `swap_buffers` does not block for vsync. However the next OpenGl command // will block to synchronize (this is `glClear` in Alacritty), which causes a // permanent one frame delay. - self.renderer.with_api(config, &size_info, |api| { - api.finish(); - }); + self.renderer.finish(); } self.damage_rects.clear(); @@ -833,9 +822,7 @@ impl Display { let fg = config.colors.search_bar_foreground(); let bg = config.colors.search_bar_background(); - self.renderer.with_api(config, size_info, |mut api| { - api.draw_string(glyph_cache, point, fg, bg, &text); - }); + self.renderer.draw_string(point, fg, bg, &text, size_info, glyph_cache); } /// Draw render timer. @@ -853,9 +840,7 @@ impl Display { self.damage_from_point(point, self.size_info.columns() as u32); let glyph_cache = &mut self.glyph_cache; - self.renderer.with_api(config, size_info, |mut api| { - api.draw_string(glyph_cache, point, fg, bg, &timing); - }); + self.renderer.draw_string(point, fg, bg, &timing, size_info, glyph_cache); } /// Draw an indicator for the position of a line in history. @@ -894,9 +879,7 @@ impl Display { // Do not render anything if it would obscure the vi mode cursor. if obstructed_column.map_or(true, |obstructed_column| obstructed_column < column) { let glyph_cache = &mut self.glyph_cache; - self.renderer.with_api(config, size_info, |mut api| { - api.draw_string(glyph_cache, point, fg, bg, &text); - }); + self.renderer.draw_string(point, fg, bg, &text, size_info, glyph_cache); } } diff --git a/alacritty/src/renderer/mod.rs b/alacritty/src/renderer/mod.rs index 4aa562ad..cf9ee149 100644 --- a/alacritty/src/renderer/mod.rs +++ b/alacritty/src/renderer/mod.rs @@ -1,33 +1,27 @@ -use std::collections::HashMap; -use std::hash::BuildHasherDefault; -use std::mem::size_of; -use std::{fmt, ptr}; +use std::ffi::CStr; +use std::fmt; -use bitflags::bitflags; -use crossfont::{ - BitmapBuffer, Error as RasterizerError, FontDesc, FontKey, GlyphKey, Metrics, Rasterize, - RasterizedGlyph, Rasterizer, Size, Slant, Style, Weight, -}; -use fnv::FnvHasher; -use log::{error, info}; -use unicode_width::UnicodeWidthChar; +use crossfont::Metrics; +use log::info; use alacritty_terminal::index::Point; use alacritty_terminal::term::cell::Flags; use alacritty_terminal::term::color::Rgb; use alacritty_terminal::term::SizeInfo; -use crate::config::font::{Font, FontDescription}; -use crate::config::ui_config::{Delta, UiConfig}; use crate::display::content::RenderableCell; use crate::gl; -use crate::gl::types::*; use crate::renderer::rects::{RectRenderer, RenderRect}; -use crate::renderer::shader::{ShaderError, ShaderProgram}; +use crate::renderer::shader::ShaderError; -pub mod builtin_font; pub mod rects; mod shader; +mod text; + +pub use text::{GlyphCache, LoaderApi}; + +use shader::ShaderVersion; +use text::{Gles2Renderer, Glsl3Renderer, TextRenderer}; macro_rules! cstr { ($s:literal) => { @@ -37,21 +31,6 @@ macro_rules! cstr { } pub(crate) use cstr; -// Shader source. -static TEXT_SHADER_F: &str = include_str!("../../res/text.f.glsl"); -static TEXT_SHADER_V: &str = include_str!("../../res/text.v.glsl"); - -/// `LoadGlyph` allows for copying a rasterized glyph into graphics memory. -pub trait LoadGlyph { - /// Load the rasterized glyph into GPU memory. - fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph; - - /// Clear any state accumulated from previous loaded glyphs. - /// - /// This can, for instance, be used to reset the texture Atlas. - fn clear(&mut self); -} - #[derive(Debug)] pub enum Error { /// Shader error. @@ -82,588 +61,93 @@ impl From<ShaderError> for Error { } } -/// Text drawing program. -/// -/// Uniforms are prefixed with "u", and vertex attributes are prefixed with "a". #[derive(Debug)] -pub struct TextShaderProgram { - /// Shader program. - program: ShaderProgram, - - /// Projection scale and offset uniform. - u_projection: GLint, - - /// Cell dimensions (pixels). - u_cell_dim: GLint, - - /// Background pass flag. - /// - /// Rendering is split into two passes; 1 for backgrounds, and one for text. - u_background: GLint, -} - -#[derive(Copy, Clone, Debug)] -pub struct Glyph { - tex_id: GLuint, - multicolor: bool, - top: i16, - left: i16, - width: i16, - height: i16, - uv_bot: f32, - uv_left: f32, - uv_width: f32, - uv_height: f32, +enum TextRendererProvider { + Gles2(Gles2Renderer), + Glsl3(Glsl3Renderer), } -/// Naïve glyph cache. -/// -/// Currently only keyed by `char`, and thus not possible to hold different -/// representations of the same code point. -pub struct GlyphCache { - /// Cache of buffered glyphs. - cache: HashMap<GlyphKey, Glyph, BuildHasherDefault<FnvHasher>>, - - /// Rasterizer for loading new glyphs. - rasterizer: Rasterizer, - - /// Regular font. - font_key: FontKey, - - /// Bold font. - bold_key: FontKey, - - /// Italic font. - italic_key: FontKey, - - /// Bold italic font. - bold_italic_key: FontKey, - - /// Font size. - font_size: crossfont::Size, - - /// Font offset. - font_offset: Delta<i8>, - - /// Glyph offset. - glyph_offset: Delta<i8>, - - /// Font metrics. - metrics: Metrics, - - /// Whether to use the built-in font for box drawing characters. - builtin_box_drawing: bool, +#[derive(Debug)] +pub struct Renderer { + text_renderer: TextRendererProvider, + rect_renderer: RectRenderer, } -impl GlyphCache { - pub fn new(mut rasterizer: Rasterizer, font: &Font) -> Result<GlyphCache, crossfont::Error> { - let (regular, bold, italic, bold_italic) = Self::compute_font_keys(font, &mut rasterizer)?; - - // Need to load at least one glyph for the face before calling metrics. - // The glyph requested here ('m' at the time of writing) has no special - // meaning. - rasterizer.get_glyph(GlyphKey { font_key: regular, character: 'm', size: font.size() })?; - - let metrics = rasterizer.metrics(regular, font.size())?; - - Ok(Self { - cache: HashMap::default(), - rasterizer, - font_size: font.size(), - font_key: regular, - bold_key: bold, - italic_key: italic, - bold_italic_key: bold_italic, - font_offset: font.offset, - glyph_offset: font.glyph_offset, - metrics, - builtin_box_drawing: font.builtin_box_drawing, - }) - } - - fn load_glyphs_for_font<L: LoadGlyph>(&mut self, font: FontKey, loader: &mut L) { - let size = self.font_size; - - // Cache all ascii characters. - for i in 32u8..=126u8 { - self.get(GlyphKey { font_key: font, character: i as char, size }, loader, true); - } - } - - /// Computes font keys for (Regular, Bold, Italic, Bold Italic). - fn compute_font_keys( - font: &Font, - rasterizer: &mut Rasterizer, - ) -> Result<(FontKey, FontKey, FontKey, FontKey), crossfont::Error> { - let size = font.size(); - - // Load regular font. - let regular_desc = Self::make_desc(font.normal(), Slant::Normal, Weight::Normal); - - let regular = Self::load_regular_font(rasterizer, ®ular_desc, size)?; - - // Helper to load a description if it is not the `regular_desc`. - let mut load_or_regular = |desc: FontDesc| { - if desc == regular_desc { - regular - } else { - rasterizer.load_font(&desc, size).unwrap_or(regular) - } - }; - - // Load bold font. - let bold_desc = Self::make_desc(&font.bold(), Slant::Normal, Weight::Bold); - - let bold = load_or_regular(bold_desc); - - // Load italic font. - let italic_desc = Self::make_desc(&font.italic(), Slant::Italic, Weight::Normal); - - let italic = load_or_regular(italic_desc); - - // Load bold italic font. - let bold_italic_desc = Self::make_desc(&font.bold_italic(), Slant::Italic, Weight::Bold); - - let bold_italic = load_or_regular(bold_italic_desc); - - Ok((regular, bold, italic, bold_italic)) - } - - fn load_regular_font( - rasterizer: &mut Rasterizer, - description: &FontDesc, - size: Size, - ) -> Result<FontKey, crossfont::Error> { - match rasterizer.load_font(description, size) { - Ok(font) => Ok(font), - Err(err) => { - error!("{}", err); - - let fallback_desc = - Self::make_desc(Font::default().normal(), Slant::Normal, Weight::Normal); - rasterizer.load_font(&fallback_desc, size) - }, - } - } - - fn make_desc(desc: &FontDescription, slant: Slant, weight: Weight) -> FontDesc { - let style = if let Some(ref spec) = desc.style { - Style::Specific(spec.to_owned()) - } else { - Style::Description { slant, weight } - }; - FontDesc::new(desc.family.clone(), style) - } - - /// Get a glyph from the font. +impl Renderer { + /// Create a new renderer. /// - /// If the glyph has never been loaded before, it will be rasterized and inserted into the - /// cache. - /// - /// # Errors - /// - /// This will fail when the glyph could not be rasterized. Usually this is due to the glyph - /// not being present in any font. - fn get<L>(&mut self, glyph_key: GlyphKey, loader: &mut L, show_missing: bool) -> Glyph - where - L: LoadGlyph, - { - // Try to load glyph from cache. - if let Some(glyph) = self.cache.get(&glyph_key) { - return *glyph; + /// This will automatically pick between the GLES2 and GLSL3 renderer based on the GPU's + /// supported OpenGL version. + pub fn new() -> Result<Self, Error> { + let (version, renderer) = unsafe { + let renderer = CStr::from_ptr(gl::GetString(gl::RENDERER) as *mut _); + let version = CStr::from_ptr(gl::GetString(gl::SHADING_LANGUAGE_VERSION) as *mut _); + (version.to_string_lossy(), renderer.to_string_lossy()) }; - // Rasterize the glyph using the built-in font for special characters or the user's font - // for everything else. - let rasterized = self - .builtin_box_drawing - .then(|| { - builtin_font::builtin_glyph( - glyph_key.character, - &self.metrics, - &self.font_offset, - &self.glyph_offset, - ) - }) - .flatten() - .map_or_else(|| self.rasterizer.get_glyph(glyph_key), Ok); - - let glyph = match rasterized { - Ok(rasterized) => self.load_glyph(loader, rasterized), - // Load fallback glyph. - Err(RasterizerError::MissingGlyph(rasterized)) if show_missing => { - // Use `\0` as "missing" glyph to cache it only once. - let missing_key = GlyphKey { character: '\0', ..glyph_key }; - if let Some(glyph) = self.cache.get(&missing_key) { - *glyph - } else { - // If no missing glyph was loaded yet, insert it as `\0`. - let glyph = self.load_glyph(loader, rasterized); - self.cache.insert(missing_key, glyph); + info!("Running on {}", renderer); - glyph - } - }, - Err(_) => self.load_glyph(loader, Default::default()), + let (text_renderer, rect_renderer) = if version.as_ref() >= "3.3" { + let text_renderer = TextRendererProvider::Glsl3(Glsl3Renderer::new()?); + let rect_renderer = RectRenderer::new(ShaderVersion::Glsl3)?; + (text_renderer, rect_renderer) + } else { + let text_renderer = TextRendererProvider::Gles2(Gles2Renderer::new()?); + let rect_renderer = RectRenderer::new(ShaderVersion::Gles2)?; + (text_renderer, rect_renderer) }; - // Cache rasterized glyph. - *self.cache.entry(glyph_key).or_insert(glyph) + Ok(Self { text_renderer, rect_renderer }) } - /// Load glyph into the atlas. - /// - /// This will apply all transforms defined for the glyph cache to the rasterized glyph before - /// insertion. - fn load_glyph<L>(&self, loader: &mut L, mut glyph: RasterizedGlyph) -> Glyph - where - L: LoadGlyph, - { - glyph.left += i32::from(self.glyph_offset.x); - glyph.top += i32::from(self.glyph_offset.y); - glyph.top -= self.metrics.descent as i32; - - // The metrics of zero-width characters are based on rendering - // the character after the current cell, with the anchor at the - // right side of the preceding character. Since we render the - // zero-width characters inside the preceding character, the - // anchor has been moved to the right by one cell. - if glyph.character.width() == Some(0) { - glyph.left += self.metrics.average_advance as i32; + pub fn draw_cells<I: Iterator<Item = RenderableCell>>( + &mut self, + size_info: &SizeInfo, + glyph_cache: &mut GlyphCache, + cells: I, + ) { + match &mut self.text_renderer { + TextRendererProvider::Gles2(renderer) => { + renderer.draw_cells(size_info, glyph_cache, cells) + }, + TextRenderer |