summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--README.md2
-rw-r--r--alacritty/res/gles2/text.f.glsl57
-rw-r--r--alacritty/res/gles2/text.v.glsl46
-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.glsl118
-rw-r--r--alacritty/res/rect.v.glsl11
-rw-r--r--alacritty/src/display/mod.rs55
-rw-r--r--alacritty/src/renderer/mod.rs1221
-rw-r--r--alacritty/src/renderer/rects.rs10
-rw-r--r--alacritty/src/renderer/shader.rs49
-rw-r--r--alacritty/src/renderer/text/atlas.rs273
-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.rs478
-rw-r--r--alacritty/src/renderer/text/glsl3.rs460
-rw-r--r--alacritty/src/renderer/text/glyph_cache.rs322
-rw-r--r--alacritty/src/renderer/text/mod.rs202
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
diff --git a/README.md b/README.md
index e4af703b..9a8546ba 100644
--- a/README.md
+++ b/README.md
@@ -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, &regular_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