2016-06-30 03:56:12 +00:00
|
|
|
// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
2016-06-06 21:31:12 +00:00
|
|
|
use std::collections::HashMap;
|
2016-06-04 17:54:33 +00:00
|
|
|
use std::fs::File;
|
2017-10-15 01:58:19 +00:00
|
|
|
use std::hash::BuildHasherDefault;
|
2016-06-04 17:54:33 +00:00
|
|
|
use std::io::{self, Read};
|
2016-02-26 04:59:21 +00:00
|
|
|
use std::mem::size_of;
|
2018-10-16 17:02:52 +00:00
|
|
|
use std::path::PathBuf;
|
2016-02-26 04:59:21 +00:00
|
|
|
use std::ptr;
|
2016-10-23 22:37:06 +00:00
|
|
|
use std::sync::mpsc;
|
2017-08-30 19:34:23 +00:00
|
|
|
use std::time::Duration;
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2017-01-13 18:03:51 +00:00
|
|
|
use fnv::FnvHasher;
|
2018-10-16 17:02:52 +00:00
|
|
|
use font::{self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer};
|
2019-03-30 16:48:36 +00:00
|
|
|
use glutin::dpi::PhysicalSize;
|
2018-12-22 17:16:54 +00:00
|
|
|
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
|
|
|
|
|
2019-04-25 20:01:23 +00:00
|
|
|
use crate::ansi::CursorStyle;
|
2019-03-30 16:48:36 +00:00
|
|
|
use crate::config::{self, Config, Delta};
|
2018-12-10 17:53:56 +00:00
|
|
|
use crate::gl;
|
2019-03-30 16:48:36 +00:00
|
|
|
use crate::gl::types::*;
|
2019-03-17 21:09:27 +00:00
|
|
|
use crate::index::{Column, Line};
|
2019-03-30 16:48:36 +00:00
|
|
|
use crate::renderer::rects::{Rect, Rects};
|
2019-02-07 22:36:45 +00:00
|
|
|
use crate::term::color::Rgb;
|
2019-04-19 18:00:24 +00:00
|
|
|
use crate::term::{self, cell, RenderableCell, RenderableCellContent};
|
2018-12-22 17:16:54 +00:00
|
|
|
|
2019-02-07 22:36:45 +00:00
|
|
|
pub mod rects;
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2016-10-27 15:43:23 +00:00
|
|
|
// Shader paths for live reload
|
2019-04-28 13:24:58 +00:00
|
|
|
static TEXT_SHADER_F_PATH: &'static str =
|
|
|
|
concat!(env!("CARGO_MANIFEST_DIR"), "/../res/text.f.glsl");
|
|
|
|
static TEXT_SHADER_V_PATH: &'static str =
|
|
|
|
concat!(env!("CARGO_MANIFEST_DIR"), "/../res/text.v.glsl");
|
|
|
|
static RECT_SHADER_F_PATH: &'static str =
|
|
|
|
concat!(env!("CARGO_MANIFEST_DIR"), "/../res/rect.f.glsl");
|
|
|
|
static RECT_SHADER_V_PATH: &'static str =
|
|
|
|
concat!(env!("CARGO_MANIFEST_DIR"), "/../res/rect.v.glsl");
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2016-10-27 15:43:23 +00:00
|
|
|
// Shader source which is used when live-shader-reload feature is disable
|
2018-10-16 17:02:52 +00:00
|
|
|
static TEXT_SHADER_F: &'static str =
|
2019-04-28 13:24:58 +00:00
|
|
|
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../res/text.f.glsl"));
|
2018-10-16 17:02:52 +00:00
|
|
|
static TEXT_SHADER_V: &'static str =
|
2019-04-28 13:24:58 +00:00
|
|
|
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../res/text.v.glsl"));
|
2018-12-17 19:06:07 +00:00
|
|
|
static RECT_SHADER_F: &'static str =
|
2019-04-28 13:24:58 +00:00
|
|
|
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../res/rect.f.glsl"));
|
2018-12-17 19:06:07 +00:00
|
|
|
static RECT_SHADER_V: &'static str =
|
2019-04-28 13:24:58 +00:00
|
|
|
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../res/rect.v.glsl"));
|
2016-10-27 15:43:23 +00:00
|
|
|
|
2016-12-17 06:13:51 +00:00
|
|
|
/// `LoadGlyph` allows for copying a rasterized glyph into graphics memory
|
2016-06-06 21:31:12 +00:00
|
|
|
pub trait LoadGlyph {
|
|
|
|
/// Load the rasterized glyph into GPU memory
|
|
|
|
fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph;
|
2017-10-15 01:58:19 +00:00
|
|
|
|
|
|
|
/// Clear any state accumulated from previous loaded glyphs
|
|
|
|
///
|
|
|
|
/// This can, for instance, be used to reset the texture Atlas.
|
|
|
|
fn clear(&mut self);
|
2016-06-06 21:31:12 +00:00
|
|
|
}
|
|
|
|
|
2016-10-23 22:37:06 +00:00
|
|
|
enum Msg {
|
|
|
|
ShaderReload,
|
|
|
|
}
|
|
|
|
|
2017-01-02 03:19:15 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum Error {
|
|
|
|
ShaderCreation(ShaderCreationError),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ::std::error::Error for Error {
|
2018-12-10 17:53:56 +00:00
|
|
|
fn cause(&self) -> Option<&dyn (::std::error::Error)> {
|
2017-01-02 03:19:15 +00:00
|
|
|
match *self {
|
|
|
|
Error::ShaderCreation(ref err) => Some(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn description(&self) -> &str {
|
|
|
|
match *self {
|
|
|
|
Error::ShaderCreation(ref err) => err.description(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ::std::fmt::Display for Error {
|
2018-12-10 17:53:56 +00:00
|
|
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
2017-01-02 03:19:15 +00:00
|
|
|
match *self {
|
|
|
|
Error::ShaderCreation(ref err) => {
|
|
|
|
write!(f, "There was an error initializing the shaders: {}", err)
|
2019-03-30 16:48:36 +00:00
|
|
|
},
|
2017-01-02 03:19:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ShaderCreationError> for Error {
|
|
|
|
fn from(val: ShaderCreationError) -> Error {
|
|
|
|
Error::ShaderCreation(val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
/// Text drawing program
|
|
|
|
///
|
|
|
|
/// Uniforms are prefixed with "u", and vertex attributes are prefixed with "a".
|
2016-06-05 04:26:28 +00:00
|
|
|
#[derive(Debug)]
|
2018-12-17 19:06:07 +00:00
|
|
|
pub struct TextShaderProgram {
|
2016-06-06 20:20:35 +00:00
|
|
|
// Program id
|
|
|
|
id: GLuint,
|
|
|
|
|
2019-02-04 19:03:25 +00:00
|
|
|
/// projection scale and offset uniform
|
2016-06-06 20:20:35 +00:00
|
|
|
u_projection: GLint,
|
|
|
|
|
|
|
|
/// Cell dimensions (pixels)
|
|
|
|
u_cell_dim: GLint,
|
|
|
|
|
2016-06-06 23:54:15 +00:00
|
|
|
/// Background pass flag
|
|
|
|
///
|
|
|
|
/// Rendering is split into two passes; 1 for backgrounds, and one for text
|
2016-10-24 17:59:25 +00:00
|
|
|
u_background: GLint,
|
2016-06-06 20:20:35 +00:00
|
|
|
}
|
|
|
|
|
2018-12-17 19:06:07 +00:00
|
|
|
/// Rectangle drawing program
|
|
|
|
///
|
|
|
|
/// Uniforms are prefixed with "u"
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct RectShaderProgram {
|
|
|
|
// Program id
|
|
|
|
id: GLuint,
|
|
|
|
/// Rectangle color
|
2019-02-03 16:45:09 +00:00
|
|
|
u_color: GLint,
|
2018-12-17 19:06:07 +00:00
|
|
|
}
|
|
|
|
|
2018-12-09 15:28:22 +00:00
|
|
|
#[derive(Copy, Debug, Clone)]
|
2016-06-05 04:26:28 +00:00
|
|
|
pub struct Glyph {
|
|
|
|
tex_id: GLuint,
|
2017-07-04 02:43:44 +00:00
|
|
|
top: f32,
|
|
|
|
left: f32,
|
|
|
|
width: f32,
|
|
|
|
height: f32,
|
|
|
|
uv_bot: f32,
|
|
|
|
uv_left: f32,
|
|
|
|
uv_width: f32,
|
|
|
|
uv_height: f32,
|
2016-06-05 04:26:28 +00:00
|
|
|
}
|
|
|
|
|
2016-06-06 21:31:12 +00:00
|
|
|
/// Naïve glyph cache
|
|
|
|
///
|
2016-07-04 00:00:00 +00:00
|
|
|
/// Currently only keyed by `char`, and thus not possible to hold different
|
|
|
|
/// representations of the same code point.
|
2016-06-06 21:31:12 +00:00
|
|
|
pub struct GlyphCache {
|
|
|
|
/// Cache of buffered glyphs
|
2017-01-13 18:03:51 +00:00
|
|
|
cache: HashMap<GlyphKey, Glyph, BuildHasherDefault<FnvHasher>>,
|
2016-06-06 21:31:12 +00:00
|
|
|
|
2019-04-25 20:01:23 +00:00
|
|
|
/// Cache of buffered cursor glyphs
|
|
|
|
cursor_cache: HashMap<CursorStyle, Glyph, BuildHasherDefault<FnvHasher>>,
|
|
|
|
|
2016-06-06 21:31:12 +00:00
|
|
|
/// Rasterizer for loading new glyphs
|
|
|
|
rasterizer: Rasterizer,
|
|
|
|
|
2016-08-12 18:38:41 +00:00
|
|
|
/// regular font
|
|
|
|
font_key: FontKey,
|
2016-06-06 21:31:12 +00:00
|
|
|
|
2016-08-12 18:38:41 +00:00
|
|
|
/// italic font
|
|
|
|
italic_key: FontKey,
|
|
|
|
|
|
|
|
/// bold font
|
|
|
|
bold_key: FontKey,
|
|
|
|
|
|
|
|
/// font size
|
|
|
|
font_size: font::Size,
|
2017-01-15 17:38:04 +00:00
|
|
|
|
|
|
|
/// glyph offset
|
2018-03-13 06:07:40 +00:00
|
|
|
glyph_offset: Delta<i8>,
|
2017-05-03 22:12:23 +00:00
|
|
|
|
|
|
|
metrics: ::font::Metrics,
|
2016-06-06 21:31:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl GlyphCache {
|
2017-01-01 03:49:51 +00:00
|
|
|
pub fn new<L>(
|
|
|
|
mut rasterizer: Rasterizer,
|
2017-10-15 01:58:19 +00:00
|
|
|
font: &config::Font,
|
2018-10-16 17:02:52 +00:00
|
|
|
loader: &mut L,
|
2017-01-01 03:49:51 +00:00
|
|
|
) -> Result<GlyphCache, font::Error>
|
2018-10-16 17:02:52 +00:00
|
|
|
where
|
|
|
|
L: LoadGlyph,
|
2016-08-12 18:38:41 +00:00
|
|
|
{
|
2017-10-15 01:58:19 +00:00
|
|
|
let (regular, bold, italic) = Self::compute_font_keys(font, &mut rasterizer)?;
|
2016-08-12 18:38:41 +00:00
|
|
|
|
2017-10-15 01:58:19 +00:00
|
|
|
// 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.
|
2018-07-21 17:17:41 +00:00
|
|
|
rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size() })?;
|
2018-10-16 17:02:52 +00:00
|
|
|
|
|
|
|
let metrics = rasterizer.metrics(regular, font.size())?;
|
2017-10-15 01:58:19 +00:00
|
|
|
|
|
|
|
let mut cache = GlyphCache {
|
|
|
|
cache: HashMap::default(),
|
2019-04-25 20:01:23 +00:00
|
|
|
cursor_cache: HashMap::default(),
|
2018-03-04 22:40:15 +00:00
|
|
|
rasterizer,
|
2017-10-15 01:58:19 +00:00
|
|
|
font_size: font.size(),
|
|
|
|
font_key: regular,
|
|
|
|
bold_key: bold,
|
|
|
|
italic_key: italic,
|
|
|
|
glyph_offset: *font.glyph_offset(),
|
2018-03-04 22:40:15 +00:00
|
|
|
metrics,
|
2017-10-15 01:58:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
cache.load_glyphs_for_font(regular, loader);
|
|
|
|
cache.load_glyphs_for_font(bold, loader);
|
|
|
|
cache.load_glyphs_for_font(italic, loader);
|
|
|
|
|
|
|
|
Ok(cache)
|
|
|
|
}
|
|
|
|
|
2018-10-16 17:02:52 +00:00
|
|
|
fn load_glyphs_for_font<L: LoadGlyph>(&mut self, font: FontKey, loader: &mut L) {
|
2017-10-15 01:58:19 +00:00
|
|
|
let size = self.font_size;
|
2019-03-17 21:09:27 +00:00
|
|
|
for i in 32u8..=128u8 {
|
2019-03-30 16:48:36 +00:00
|
|
|
self.get(GlyphKey { font_key: font, c: i as char, size }, loader);
|
2017-05-13 09:46:31 +00:00
|
|
|
}
|
2017-10-15 01:58:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Computes font keys for (Regular, Bold, Italic)
|
|
|
|
fn compute_font_keys(
|
|
|
|
font: &config::Font,
|
2018-10-16 17:02:52 +00:00
|
|
|
rasterizer: &mut Rasterizer,
|
2017-10-15 01:58:19 +00:00
|
|
|
) -> Result<(FontKey, FontKey, FontKey), font::Error> {
|
|
|
|
let size = font.size();
|
2017-05-13 09:46:31 +00:00
|
|
|
|
|
|
|
// Load regular font
|
2019-03-30 16:48:36 +00:00
|
|
|
let regular_desc =
|
|
|
|
Self::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal);
|
2017-01-03 02:52:41 +00:00
|
|
|
|
2018-10-16 17:02:52 +00:00
|
|
|
let regular = rasterizer.load_font(®ular_desc, size)?;
|
2016-08-12 18:38:41 +00:00
|
|
|
|
2017-05-13 09:46:31 +00:00
|
|
|
// helper to load a description if it is not the regular_desc
|
2018-10-16 17:02:52 +00:00
|
|
|
let mut load_or_regular = |desc: FontDesc| {
|
2017-05-13 09:46:31 +00:00
|
|
|
if desc == regular_desc {
|
|
|
|
regular
|
|
|
|
} else {
|
2019-03-30 16:48:36 +00:00
|
|
|
rasterizer.load_font(&desc, size).unwrap_or_else(|_| regular)
|
2017-05-13 09:46:31 +00:00
|
|
|
}
|
2017-01-03 02:52:41 +00:00
|
|
|
};
|
2016-08-12 18:38:41 +00:00
|
|
|
|
2017-05-13 09:46:31 +00:00
|
|
|
// Load bold font
|
2019-01-17 09:17:26 +00:00
|
|
|
let bold_desc = Self::make_desc(&font.bold(), font::Slant::Normal, font::Weight::Bold);
|
2017-05-13 09:46:31 +00:00
|
|
|
|
2017-10-15 01:58:19 +00:00
|
|
|
let bold = load_or_regular(bold_desc);
|
2016-08-12 18:38:41 +00:00
|
|
|
|
|
|
|
// Load italic font
|
2019-03-30 16:48:36 +00:00
|
|
|
let italic_desc =
|
|
|
|
Self::make_desc(&font.italic(), font::Slant::Italic, font::Weight::Normal);
|
2016-08-12 18:38:41 +00:00
|
|
|
|
2017-10-15 01:58:19 +00:00
|
|
|
let italic = load_or_regular(italic_desc);
|
2016-08-12 18:38:41 +00:00
|
|
|
|
2017-10-15 01:58:19 +00:00
|
|
|
Ok((regular, bold, italic))
|
|
|
|
}
|
2017-05-03 22:12:23 +00:00
|
|
|
|
2017-10-15 01:58:19 +00:00
|
|
|
fn make_desc(
|
|
|
|
desc: &config::FontDescription,
|
|
|
|
slant: font::Slant,
|
|
|
|
weight: font::Weight,
|
|
|
|
) -> FontDesc {
|
|
|
|
let style = if let Some(ref spec) = desc.style {
|
|
|
|
font::Style::Specific(spec.to_owned())
|
|
|
|
} else {
|
2018-03-04 22:40:15 +00:00
|
|
|
font::Style::Description { slant, weight }
|
2016-08-12 18:38:41 +00:00
|
|
|
};
|
2019-01-17 09:17:26 +00:00
|
|
|
FontDesc::new(desc.family.clone(), style)
|
2016-06-06 21:31:12 +00:00
|
|
|
}
|
|
|
|
|
2016-08-12 18:38:41 +00:00
|
|
|
pub fn font_metrics(&self) -> font::Metrics {
|
2017-01-01 03:49:51 +00:00
|
|
|
self.rasterizer
|
2018-10-16 17:02:52 +00:00
|
|
|
.metrics(self.font_key, self.font_size)
|
2017-01-01 03:49:51 +00:00
|
|
|
.expect("metrics load since font is loaded at glyph cache creation")
|
2016-06-06 21:31:12 +00:00
|
|
|
}
|
|
|
|
|
2018-07-21 17:17:41 +00:00
|
|
|
pub fn get<'a, L>(&'a mut self, glyph_key: GlyphKey, loader: &mut L) -> &'a Glyph
|
2019-03-30 16:48:36 +00:00
|
|
|
where
|
|
|
|
L: LoadGlyph,
|
2016-06-06 21:31:12 +00:00
|
|
|
{
|
2017-01-15 17:38:04 +00:00
|
|
|
let glyph_offset = self.glyph_offset;
|
2017-01-13 18:03:51 +00:00
|
|
|
let rasterizer = &mut self.rasterizer;
|
2017-05-03 22:12:23 +00:00
|
|
|
let metrics = &self.metrics;
|
2019-03-30 16:48:36 +00:00
|
|
|
self.cache.entry(glyph_key).or_insert_with(|| {
|
|
|
|
let mut rasterized =
|
|
|
|
rasterizer.get_glyph(glyph_key).unwrap_or_else(|_| Default::default());
|
2017-01-15 17:38:04 +00:00
|
|
|
|
2019-03-30 16:48:36 +00:00
|
|
|
rasterized.left += i32::from(glyph_offset.x);
|
|
|
|
rasterized.top += i32::from(glyph_offset.y);
|
|
|
|
rasterized.top -= metrics.descent as i32;
|
2017-01-15 17:38:04 +00:00
|
|
|
|
2019-03-30 16:48:36 +00:00
|
|
|
loader.load_glyph(&rasterized)
|
2018-10-16 17:02:52 +00:00
|
|
|
})
|
2016-06-06 21:31:12 +00:00
|
|
|
}
|
2019-03-30 16:48:36 +00:00
|
|
|
|
2017-10-15 01:58:19 +00:00
|
|
|
pub fn update_font_size<L: LoadGlyph>(
|
|
|
|
&mut self,
|
|
|
|
font: &config::Font,
|
2018-01-05 03:22:58 +00:00
|
|
|
size: font::Size,
|
2018-11-10 16:08:48 +00:00
|
|
|
dpr: f64,
|
2019-03-30 16:48:36 +00:00
|
|
|
loader: &mut L,
|
2017-10-15 01:58:19 +00:00
|
|
|
) -> Result<(), font::Error> {
|
|
|
|
// Clear currently cached data in both GL and the registry
|
|
|
|
loader.clear();
|
|
|
|
self.cache = HashMap::default();
|
2019-04-25 20:01:23 +00:00
|
|
|
self.cursor_cache = HashMap::default();
|
2017-10-15 01:58:19 +00:00
|
|
|
|
2018-11-10 16:08:48 +00:00
|
|
|
// Update dpi scaling
|
|
|
|
self.rasterizer.update_dpr(dpr as f32);
|
|
|
|
|
2017-10-15 01:58:19 +00:00
|
|
|
// Recompute font keys
|
2018-01-05 03:22:58 +00:00
|
|
|
let font = font.to_owned().with_size(size);
|
2017-10-15 01:58:19 +00:00
|
|
|
let (regular, bold, italic) = Self::compute_font_keys(&font, &mut self.rasterizer)?;
|
2018-11-11 20:24:41 +00:00
|
|
|
|
2018-07-21 17:17:41 +00:00
|
|
|
self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size() })?;
|
2018-10-16 17:02:52 +00:00
|
|
|
let metrics = self.rasterizer.metrics(regular, size)?;
|
2017-10-15 01:58:19 +00:00
|
|
|
|
2019-01-07 00:06:57 +00:00
|
|
|
info!("Font size changed to {:?} with DPR of {}", font.size, dpr);
|
2018-11-10 16:08:48 +00:00
|
|
|
|
2017-10-15 01:58:19 +00:00
|
|
|
self.font_size = font.size;
|
|
|
|
self.font_key = regular;
|
|
|
|
self.bold_key = bold;
|
|
|
|
self.italic_key = italic;
|
|
|
|
self.metrics = metrics;
|
|
|
|
|
|
|
|
self.load_glyphs_for_font(regular, loader);
|
|
|
|
self.load_glyphs_for_font(bold, loader);
|
|
|
|
self.load_glyphs_for_font(italic, loader);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2019-04-14 15:37:58 +00:00
|
|
|
|
|
|
|
// Calculate font metrics without access to a glyph cache
|
|
|
|
//
|
|
|
|
// This should only be used *before* OpenGL is initialized and the glyph cache can be filled.
|
|
|
|
pub fn static_metrics(config: &Config, dpr: f32) -> Result<font::Metrics, font::Error> {
|
|
|
|
let font = config.font().clone();
|
|
|
|
|
|
|
|
let mut rasterizer = font::Rasterizer::new(dpr, config.use_thin_strokes())?;
|
|
|
|
let regular_desc =
|
|
|
|
GlyphCache::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal);
|
|
|
|
let regular = rasterizer.load_font(®ular_desc, font.size())?;
|
|
|
|
rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size() })?;
|
|
|
|
|
|
|
|
rasterizer.metrics(regular, font.size())
|
|
|
|
}
|
2016-06-06 21:31:12 +00:00
|
|
|
}
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
#[derive(Debug)]
|
2016-10-24 17:59:25 +00:00
|
|
|
#[repr(C)]
|
2016-06-06 20:20:35 +00:00
|
|
|
struct InstanceData {
|
2017-07-04 02:43:44 +00:00
|
|
|
// coords
|
|
|
|
col: f32,
|
|
|
|
row: f32,
|
2016-06-06 20:20:35 +00:00
|
|
|
// glyph offset
|
2017-07-04 02:43:44 +00:00
|
|
|
left: f32,
|
|
|
|
top: f32,
|
2016-06-06 20:20:35 +00:00
|
|
|
// glyph scale
|
2017-07-04 02:43:44 +00:00
|
|
|
width: f32,
|
|
|
|
height: f32,
|
2016-06-06 20:20:35 +00:00
|
|
|
// uv offset
|
2017-07-04 02:43:44 +00:00
|
|
|
uv_left: f32,
|
|
|
|
uv_bot: f32,
|
2016-06-06 20:20:35 +00:00
|
|
|
// uv scale
|
2017-07-04 02:43:44 +00:00
|
|
|
uv_width: f32,
|
|
|
|
uv_height: f32,
|
2016-06-06 20:20:35 +00:00
|
|
|
// color
|
2017-07-04 02:43:44 +00:00
|
|
|
r: f32,
|
|
|
|
g: f32,
|
|
|
|
b: f32,
|
2016-06-06 23:54:15 +00:00
|
|
|
// background color
|
2017-07-04 02:43:44 +00:00
|
|
|
bg_r: f32,
|
|
|
|
bg_g: f32,
|
|
|
|
bg_b: f32,
|
2017-10-21 22:26:42 +00:00
|
|
|
bg_a: f32,
|
2016-06-06 20:20:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2016-02-26 04:59:21 +00:00
|
|
|
pub struct QuadRenderer {
|
2018-12-17 19:06:07 +00:00
|
|
|
program: TextShaderProgram,
|
|
|
|
rect_program: RectShaderProgram,
|
2016-02-26 04:59:21 +00:00
|
|
|
vao: GLuint,
|
|
|
|
ebo: GLuint,
|
2016-06-06 20:20:35 +00:00
|
|
|
vbo_instance: GLuint,
|
2018-12-17 19:06:07 +00:00
|
|
|
rect_vao: GLuint,
|
|
|
|
rect_vbo: GLuint,
|
2016-06-03 05:14:55 +00:00
|
|
|
atlas: Vec<Atlas>,
|
2017-10-15 01:58:19 +00:00
|
|
|
current_atlas: usize,
|
2016-06-03 05:14:55 +00:00
|
|
|
active_tex: GLuint,
|
2016-06-06 20:20:35 +00:00
|
|
|
batch: Batch,
|
2016-10-23 22:37:06 +00:00
|
|
|
rx: mpsc::Receiver<Msg>,
|
2016-06-06 20:20:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct RenderApi<'a> {
|
|
|
|
active_tex: &'a mut GLuint,
|
|
|
|
batch: &'a mut Batch,
|
2016-06-06 21:31:12 +00:00
|
|
|
atlas: &'a mut Vec<Atlas>,
|
2017-10-15 01:58:19 +00:00
|
|
|
current_atlas: &'a mut usize,
|
2018-12-17 19:06:07 +00:00
|
|
|
program: &'a mut TextShaderProgram,
|
2017-02-11 20:49:40 +00:00
|
|
|
config: &'a Config,
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
|
|
|
|
2016-08-12 18:38:41 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct LoaderApi<'a> {
|
|
|
|
active_tex: &'a mut GLuint,
|
|
|
|
atlas: &'a mut Vec<Atlas>,
|
2017-10-15 01:58:19 +00:00
|
|
|
current_atlas: &'a mut usize,
|
2016-08-12 18:38:41 +00:00
|
|
|
}
|
|
|
|
|
2016-02-27 21:08:39 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct PackedVertex {
|
|
|
|
x: f32,
|
|
|
|
y: f32,
|
2016-02-27 06:30:42 +00:00
|
|
|
}
|
|
|
|
|
2018-01-06 01:42:55 +00:00
|
|
|
#[derive(Debug, Default)]
|
2016-06-06 20:20:35 +00:00
|
|
|
pub struct Batch {
|
2016-06-05 04:26:28 +00:00
|
|
|
tex: GLuint,
|
2016-06-06 20:20:35 +00:00
|
|
|
instances: Vec<InstanceData>,
|
2016-06-05 04:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Batch {
|
2016-06-06 20:20:35 +00:00
|
|
|
#[inline]
|
2017-02-11 20:49:40 +00:00
|
|
|
pub fn new() -> Batch {
|
2019-03-30 16:48:36 +00:00
|
|
|
Batch { tex: 0, instances: Vec::with_capacity(BATCH_MAX) }
|
2016-06-05 04:26:28 +00:00
|
|
|
}
|
|
|
|
|
2018-10-16 17:02:52 +00:00
|
|
|
pub fn add_item(&mut self, cell: &RenderableCell, glyph: &Glyph) {
|
2016-06-05 04:26:28 +00:00
|
|
|
if self.is_empty() {
|
|
|
|
self.tex = glyph.tex_id;
|
|
|
|
}
|
|
|
|
|
2016-11-28 22:13:11 +00:00
|
|
|
self.instances.push(InstanceData {
|
2017-07-04 02:43:44 +00:00
|
|
|
col: cell.column.0 as f32,
|
|
|
|
row: cell.line.0 as f32,
|
2016-06-06 20:20:35 +00:00
|
|
|
|
|
|
|
top: glyph.top,
|
|
|
|
left: glyph.left,
|
|
|
|
width: glyph.width,
|
|
|
|
height: glyph.height,
|
|
|
|
|
|
|
|
uv_bot: glyph.uv_bot,
|
|
|
|
uv_left: glyph.uv_left,
|
|
|
|
uv_width: glyph.uv_width,
|
|
|
|
uv_height: glyph.uv_height,
|
|
|
|
|
2018-01-06 01:42:55 +00:00
|
|
|
r: f32::from(cell.fg.r),
|
|
|
|
g: f32::from(cell.fg.g),
|
|
|
|
b: f32::from(cell.fg.b),
|
2016-06-06 23:54:15 +00:00
|
|
|
|
2018-01-06 01:42:55 +00:00
|
|
|
bg_r: f32::from(cell.bg.r),
|
|
|
|
bg_g: f32::from(cell.bg.g),
|
|
|
|
bg_b: f32::from(cell.bg.b),
|
2017-10-21 22:26:42 +00:00
|
|
|
bg_a: cell.bg_alpha,
|
2016-11-28 22:13:11 +00:00
|
|
|
});
|
2016-06-05 04:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn full(&self) -> bool {
|
|
|
|
self.capacity() == self.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn len(&self) -> usize {
|
2016-06-06 20:20:35 +00:00
|
|
|
self.instances.len()
|
2016-06-05 04:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn capacity(&self) -> usize {
|
|
|
|
BATCH_MAX
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.len() == 0
|
|
|
|
}
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn size(&self) -> usize {
|
|
|
|
self.len() * size_of::<InstanceData>()
|
2016-06-05 04:26:28 +00:00
|
|
|
}
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.tex = 0;
|
|
|
|
self.instances.clear();
|
2016-06-05 04:26:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Maximum items to be drawn in a batch.
|
2018-01-26 20:20:42 +00:00
|
|
|
const BATCH_MAX: usize = 0x1_0000;
|
2016-06-06 20:29:05 +00:00
|
|
|
const ATLAS_SIZE: i32 = 1024;
|
2016-05-21 18:08:50 +00:00
|
|
|
|
2016-02-26 04:59:21 +00:00
|
|
|
impl QuadRenderer {
|
2019-04-14 15:37:58 +00:00
|
|
|
pub fn new() -> Result<QuadRenderer, Error> {
|
|
|
|
let program = TextShaderProgram::new()?;
|
2018-12-17 19:06:07 +00:00
|
|
|
let rect_program = RectShaderProgram::new()?;
|
2016-02-26 04:59:21 +00:00
|
|
|
|
|
|
|
let mut vao: GLuint = 0;
|
|
|
|
let mut ebo: GLuint = 0;
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
let mut vbo_instance: GLuint = 0;
|
|
|
|
|
2018-12-17 19:06:07 +00:00
|
|
|
let mut rect_vao: GLuint = 0;
|
|
|
|
let mut rect_vbo: GLuint = 0;
|
|
|
|
let mut rect_ebo: GLuint = 0;
|
|
|
|
|
2016-02-26 04:59:21 +00:00
|
|
|
unsafe {
|
2016-12-10 05:10:49 +00:00
|
|
|
gl::Enable(gl::BLEND);
|
|
|
|
gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR);
|
|
|
|
gl::Enable(gl::MULTISAMPLE);
|
|
|
|
|
2019-02-04 19:03:25 +00:00
|
|
|
// Disable depth mask, as the renderer never uses depth tests
|
|
|
|
gl::DepthMask(gl::FALSE);
|
|
|
|
|
2016-02-26 04:59:21 +00:00
|
|
|
gl::GenVertexArrays(1, &mut vao);
|
|
|
|
gl::GenBuffers(1, &mut ebo);
|
2016-06-06 20:20:35 +00:00
|
|
|
gl::GenBuffers(1, &mut vbo_instance);
|
2016-02-26 04:59:21 +00:00
|
|
|
gl::BindVertexArray(vao);
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
// ---------------------
|
|
|
|
// Set up element buffer
|
|
|
|
// ---------------------
|
2018-10-16 17:02:52 +00:00
|
|
|
let indices: [u32; 6] = [0, 1, 3, 1, 2, 3];
|
2016-02-26 04:59:21 +00:00
|
|
|
|
|
|
|
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
|
2018-10-16 17:02:52 +00:00
|
|
|
gl::BufferData(
|
|
|
|
gl::ELEMENT_ARRAY_BUFFER,
|
|
|
|
(6 * size_of::<u32>()) as isize,
|
|
|
|
indices.as_ptr() as *const _,
|
|
|
|
gl::STATIC_DRAW,
|
|
|
|
);
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
// ----------------------------
|
|
|
|
// Setup vertex instance buffer
|
|
|
|
// ----------------------------
|
|
|
|
gl::BindBuffer(gl::ARRAY_BUFFER, vbo_instance);
|
2018-10-16 17:02:52 +00:00
|
|
|
gl::BufferData(
|
|
|
|
gl::ARRAY_BUFFER,
|
|
|
|
(BATCH_MAX * size_of::<InstanceData>()) as isize,
|
|
|
|
ptr::null(),
|
|
|
|
gl::STREAM_DRAW,
|
|
|
|
);
|
2016-06-06 20:20:35 +00:00
|
|
|
// coords
|
2018-10-16 17:02:52 +00:00
|
|
|
gl::VertexAttribPointer(
|
2019-02-04 19:03:25 +00:00
|
|
|
0,
|
2018-10-16 17:02:52 +00:00
|
|
|
2,
|
|
|
|
gl::FLOAT,
|
|
|
|
gl::FALSE,
|
|
|
|
size_of::<InstanceData>() as i32,
|
|
|
|
ptr::null(),
|
|
|
|
);
|
2019-02-04 19:03:25 +00:00
|
|
|
gl::EnableVertexAttribArray(0);
|
|
|
|
gl::VertexAttribDivisor(0, 1);
|
2016-06-06 20:20:35 +00:00
|
|
|
// glyphoffset
|
2018-10-16 17:02:52 +00:00
|
|
|
gl::VertexAttribPointer(
|
2019-02-04 19:03:25 +00:00
|
|
|
1,
|
2018-10-16 17:02:52 +00:00
|
|
|
4,
|
|
|
|
gl::FLOAT,
|
|
|
|
gl::FALSE,
|
|
|
|
size_of::<InstanceData>() as i32,
|
|
|
|
(2 * size_of::<f32>()) as *const _,
|
|
|
|
);
|
2019-02-04 19:03:25 +00:00
|
|
|
gl::EnableVertexAttribArray(1);
|
|
|
|
gl::VertexAttribDivisor(1, 1);
|
2016-06-06 20:20:35 +00:00
|
|
|
// uv
|
2018-10-16 17:02:52 +00:00
|
|
|
gl::VertexAttribPointer(
|
2019-02-04 19:03:25 +00:00
|
|
|
2,
|
2018-10-16 17:02:52 +00:00
|
|
|
4,
|
|
|
|
gl::FLOAT,
|
|
|
|
gl::FALSE,
|
|
|
|
size_of::<InstanceData>() as i32,
|
|
|
|
(6 * size_of::<f32>()) as *const _,
|
|
|
|
);
|
2019-02-04 19:03:25 +00:00
|
|
|
gl::EnableVertexAttribArray(2);
|
|
|
|
gl::VertexAttribDivisor(2, 1);
|
2016-06-06 20:20:35 +00:00
|
|
|
// color
|
2018-10-16 17:02:52 +00:00
|
|
|
gl::VertexAttribPointer(
|
2019-02-04 19:03:25 +00:00
|
|
|
3,
|
2018-10-16 17:02:52 +00:00
|
|
|
3,
|
|
|
|
gl::FLOAT,
|
|
|
|
gl::FALSE,
|
|
|
|
size_of::<InstanceData>() as i32,
|
|
|
|
(10 * size_of::<f32>()) as *const _,
|
|
|
|
);
|
2019-02-04 19:03:25 +00:00
|
|
|
gl::EnableVertexAttribArray(3);
|
|
|
|
gl::VertexAttribDivisor(3, 1);
|
2016-06-06 23:54:15 +00:00
|
|
|
// color
|
2018-10-16 17:02:52 +00:00
|
|
|
gl::VertexAttribPointer(
|
2019-02-04 19:03:25 +00:00
|
|
|
4,
|
2018-10-16 17:02:52 +00:00
|
|
|
4,
|
|
|
|
gl::FLOAT,
|
|
|
|
gl::FALSE,
|
|
|
|
size_of::<InstanceData>() as i32,
|
|
|
|
(13 * size_of::<f32>()) as *const _,
|
|
|
|
);
|
2019-02-04 19:03:25 +00:00
|
|
|
gl::EnableVertexAttribArray(4);
|
|
|
|
gl::VertexAttribDivisor(4, 1);
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2018-12-17 19:06:07 +00:00
|
|
|
// Rectangle setup
|
|
|
|
gl::GenVertexArrays(1, &mut rect_vao);
|
|
|
|
gl::GenBuffers(1, &mut rect_vbo);
|
|
|
|
gl::GenBuffers(1, &mut rect_ebo);
|
|
|
|
gl::BindVertexArray(rect_vao);
|
2019-03-30 16:48:36 +00:00
|
|
|
let indices: [i32; 6] = [0, 1, 3, 1, 2, 3];
|
2018-12-17 19:06:07 +00:00
|
|
|
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, rect_ebo);
|
|
|
|
gl::BufferData(
|
|
|
|
gl::ELEMENT_ARRAY_BUFFER,
|
|
|
|
(size_of::<i32>() * indices.len()) as _,
|
|
|
|
indices.as_ptr() as *const _,
|
2019-03-30 16:48:36 +00:00
|
|
|
gl::STATIC_DRAW,
|
2018-12-17 19:06:07 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Cleanup
|
2016-02-26 04:59:21 +00:00
|
|
|
gl::BindVertexArray(0);
|
2016-02-27 21:08:39 +00:00
|
|
|
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
|
2018-12-17 19:06:07 +00:00
|
|
|
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0);
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
|
|
|
|
2016-10-23 22:37:06 +00:00
|
|
|
let (msg_tx, msg_rx) = mpsc::channel();
|
2016-06-04 17:54:33 +00:00
|
|
|
|
2016-10-27 15:43:23 +00:00
|
|
|
if cfg!(feature = "live-shader-reload") {
|
|
|
|
::std::thread::spawn(move || {
|
|
|
|
let (tx, rx) = ::std::sync::mpsc::channel();
|
2017-09-05 16:03:30 +00:00
|
|
|
// The Duration argument is a debouncing period.
|
2018-10-16 17:02:52 +00:00
|
|
|
let mut watcher =
|
|
|
|
watcher(tx, Duration::from_millis(10)).expect("create file watcher");
|
|
|
|
watcher
|
|
|
|
.watch(TEXT_SHADER_F_PATH, RecursiveMode::NonRecursive)
|
|
|
|
.expect("watch fragment shader");
|
|
|
|
watcher
|
|
|
|
.watch(TEXT_SHADER_V_PATH, RecursiveMode::NonRecursive)
|
|
|
|
.expect("watch vertex shader");
|
2016-06-04 17:54:33 +00:00
|
|
|
|
2016-10-27 15:43:23 +00:00
|
|
|
loop {
|
|
|
|
let event = rx.recv().expect("watcher event");
|
2016-06-04 17:54:33 +00:00
|
|
|
|
2017-08-30 19:34:23 +00:00
|
|
|
match event {
|
2019-03-30 16:48:36 +00:00
|
|
|
DebouncedEvent::Rename(..) => continue,
|
2018-10-16 17:02:52 +00:00
|
|
|
DebouncedEvent::Create(_)
|
|
|
|
| DebouncedEvent::Write(_)
|
|
|
|
| DebouncedEvent::Chmod(_) => {
|
2017-08-30 19:34:23 +00:00
|
|
|
msg_tx.send(Msg::ShaderReload).expect("msg send ok");
|
2019-03-30 16:48:36 +00:00
|
|
|
},
|
|
|
|
_ => {},
|
2016-06-04 17:54:33 +00:00
|
|
|
}
|
|
|
|
}
|
2016-10-27 15:43:23 +00:00
|
|
|
});
|
|
|
|
}
|
2016-06-04 17:54:33 +00:00
|
|
|
|
2016-06-03 16:26:58 +00:00
|
|
|
let mut renderer = QuadRenderer {
|
2018-03-04 22:40:15 +00:00
|
|
|
program,
|
2018-12-17 19:06:07 +00:00
|
|
|
rect_program,
|
2018-03-04 22:40:15 +00:00
|
|
|
vao,
|
|
|
|
ebo,
|
|
|
|
vbo_instance,
|
2018-12-17 19:06:07 +00:00
|
|
|
rect_vao,
|
|
|
|
rect_vbo,
|
2016-06-03 16:26:58 +00:00
|
|
|
atlas: Vec::new(),
|
2017-10-15 01:58:19 +00:00
|
|
|
current_atlas: 0,
|
2016-06-03 05:14:55 +00:00
|
|
|
active_tex: 0,
|
2017-02-11 20:49:40 +00:00
|
|
|
batch: Batch::new(),
|
2016-10-23 22:37:06 +00:00
|
|
|
rx: msg_rx,
|
2016-06-03 16:26:58 +00:00
|
|
|
};
|
|
|
|
|
2016-06-06 21:31:12 +00:00
|
|
|
let atlas = Atlas::new(ATLAS_SIZE);
|
2016-06-03 16:26:58 +00:00
|
|
|
renderer.atlas.push(atlas);
|
|
|
|
|
2017-01-02 03:19:15 +00:00
|
|
|
Ok(renderer)
|
2016-06-03 03:27:07 +00:00
|
|
|
}
|
|
|
|
|
2018-12-17 19:06:07 +00:00
|
|
|
// Draw all rectangles simultaneously to prevent excessive program swaps
|
|
|
|
pub fn draw_rects(
|
2016-12-04 21:13:44 +00:00
|
|
|
&mut self,
|
|
|
|
config: &Config,
|
|
|
|
props: &term::SizeInfo,
|
2017-02-11 20:49:40 +00:00
|
|
|
visual_bell_intensity: f64,
|
2019-02-07 22:36:45 +00:00
|
|
|
cell_line_rects: Rects,
|
2018-12-17 19:06:07 +00:00
|
|
|
) {
|
|
|
|
// Swap to rectangle rendering program
|
|
|
|
unsafe {
|
|
|
|
// Swap program
|
|
|
|
gl::UseProgram(self.rect_program.id);
|
|
|
|
|
|
|
|
// Remove padding from viewport
|
|
|
|
gl::Viewport(0, 0, props.width as i32, props.height as i32);
|
|
|
|
|
|
|
|
// Change blending strategy
|
|
|
|
gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
|
|
|
|
|
|
|
|
// Setup data and buffers
|
|
|
|
gl::BindVertexArray(self.rect_vao);
|
|
|
|
gl::BindBuffer(gl::ARRAY_BUFFER, self.rect_vbo);
|
|
|
|
|
|
|
|
// Position
|
2019-03-30 16:48:36 +00:00
|
|
|
gl::VertexAttribPointer(
|
|
|
|
0,
|
|
|
|
2,
|
|
|
|
gl::FLOAT,
|
|
|
|
gl::FALSE,
|
|
|
|
(size_of::<f32>() * 2) as _,
|
|
|
|
ptr::null(),
|
|
|
|
);
|
2018-12-17 19:06:07 +00:00
|
|
|
gl::EnableVertexAttribArray(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw visual bell
|
|
|
|
let color = config.visual_bell().color();
|
|
|
|
let rect = Rect::new(0., 0., props.width, props.height);
|
|
|
|
self.render_rect(&rect, color, visual_bell_intensity as f32, props);
|
|
|
|
|
2018-12-22 17:16:54 +00:00
|
|
|
// Draw underlines and strikeouts
|
|
|
|
for cell_line_rect in cell_line_rects.rects() {
|
|
|
|
self.render_rect(&cell_line_rect.0, cell_line_rect.1, 255., props);
|
|
|
|
}
|
|
|
|
|
2018-12-17 19:06:07 +00:00
|
|
|
// Deactivate rectangle program again
|
|
|
|
unsafe {
|
|
|
|
// Reset blending strategy
|
|
|
|
gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR);
|
|
|
|
|
|
|
|
// Reset data and buffers
|
|
|
|
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
|
|
|
|
gl::BindVertexArray(0);
|
|
|
|
|
|
|
|
let padding_x = props.padding_x as i32;
|
|
|
|
let padding_y = props.padding_y as i32;
|
|
|
|
let width = props.width as i32;
|
|
|
|
let height = props.height as i32;
|
|
|
|
gl::Viewport(padding_x, padding_y, width - 2 * padding_x, height - 2 * padding_y);
|
|
|
|
|
|
|
|
// Disable program
|
|
|
|
gl::UseProgram(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-30 16:48:36 +00:00
|
|
|
pub fn with_api<F, T>(&mut self, config: &Config, props: &term::SizeInfo, func: F) -> T
|
2018-10-16 17:02:52 +00:00
|
|
|
where
|
2018-12-10 17:53:56 +00:00
|
|
|
F: FnOnce(RenderApi<'_>) -> T,
|
2016-06-03 03:27:07 +00:00
|
|
|
{
|
2018-12-17 19:06:07 +00:00
|
|
|
// Flush message queue
|
|
|
|
if let Ok(Msg::ShaderReload) = self.rx.try_recv() {
|
2019-04-14 15:37:58 +00:00
|
|
|
self.reload_shaders(props);
|
2016-06-05 04:26:28 +00:00
|
|
|
}
|
2018-12-17 19:06:07 +00:00
|
|
|
while let Ok(_) = self.rx.try_recv() {}
|
2016-06-05 04:26:28 +00:00
|
|
|
|
2016-02-26 04:59:21 +00:00
|
|
|
unsafe {
|
2018-12-17 19:06:07 +00:00
|
|
|
gl::UseProgram(self.program.id);
|
2016-06-04 22:30:17 +00:00
|
|
|
self.program.set_term_uniforms(props);
|
2016-06-03 03:27:07 +00:00
|
|
|
|
|
|
|
gl::BindVertexArray(self.vao);
|
|
|
|
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
|
2016-06-06 20:20:35 +00:00
|
|
|
gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo_instance);
|
2016-06-03 05:14:55 +00:00
|
|
|
gl::ActiveTexture(gl::TEXTURE0);
|
2016-06-03 03:27:07 +00:00
|
|
|
}
|
2016-06-04 17:54:33 +00:00
|
|
|
|
2016-08-12 18:38:41 +00:00
|
|
|
let res = func(RenderApi {
|
2016-06-06 20:20:35 +00:00
|
|
|
active_tex: &mut self.active_tex,
|
|
|
|
batch: &mut self.batch,
|
2016-06-06 21:31:12 +00:00
|
|
|
atlas: &mut self.atlas,
|
2017-10-15 01:58:19 +00:00
|
|
|
current_atlas: &mut self.current_atlas,
|
2016-06-06 23:54:15 +00:00
|
|
|
program: &mut self.program,
|
2018-03-04 22:40:15 +00:00
|
|
|
config,
|
2016-06-06 20:20:35 +00:00
|
|
|
});
|
2016-06-03 03:27:07 +00:00
|
|
|
|
|
|
|
unsafe {
|
|
|
|
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0);
|
|
|
|
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
|
|
|
|
gl::BindVertexArray(0);
|
|
|
|
|
2018-12-17 19:06:07 +00:00
|
|
|
gl::UseProgram(0);
|
2016-06-03 03:27:07 +00:00
|
|
|
}
|
2016-08-12 18:38:41 +00:00
|
|
|
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_loader<F, T>(&mut self, func: F) -> T
|
2018-10-16 17:02:52 +00:00
|
|
|
where
|
2018-12-10 17:53:56 +00:00
|
|
|
F: FnOnce(LoaderApi<'_>) -> T,
|
2016-08-12 18:38:41 +00:00
|
|
|
{
|
|
|
|
unsafe {
|
|
|
|
gl::ActiveTexture(gl::TEXTURE0);
|
|
|
|
}
|
|
|
|
|
|
|
|
func(LoaderApi {
|
|
|
|
active_tex: &mut self.active_tex,
|
|
|
|
atlas: &mut self.atlas,
|
2017-10-15 01:58:19 +00:00
|
|
|
current_atlas: &mut self.current_atlas,
|
2016-08-12 18:38:41 +00:00
|
|
|
})
|
2016-06-03 03:27:07 +00:00
|
|
|
}
|
|
|
|
|
2019-04-14 15:37:58 +00:00
|
|
|
pub fn reload_shaders(&mut self, props: &term::SizeInfo) {
|
|
|
|
info!("Reloading shaders...");
|
|
|
|
let result = (TextShaderProgram::new(), RectShaderProgram::new());
|
2018-12-17 19:06:07 +00:00
|
|
|
let (program, rect_program) = match result {
|
|
|
|
(Ok(program), Ok(rect_program)) => {
|
2019-04-14 15:37:58 +00:00
|
|
|
unsafe {
|
|
|
|
gl::UseProgram(program.id);
|
|
|
|
program.update_projection(
|
|
|
|
props.width,
|
|
|
|
props.height,
|
|
|
|
props.padding_x,
|
|
|
|
props.padding_y,
|
|
|
|
);
|
|
|
|
gl::UseProgram(0);
|
|
|
|
}
|
|
|
|
|
2019-01-07 00:06:57 +00:00
|
|
|
info!("... successfully reloaded shaders");
|
2018-12-17 19:06:07 +00:00
|
|
|
(program, rect_program)
|
2019-03-30 16:48:36 +00:00
|
|
|
},
|
2018-12-17 19:06:07 +00:00
|
|
|
(Err(err), _) | (_, Err(err)) => {
|
2019-01-07 00:06:57 +00:00
|
|
|
error!("{}", err);
|
2016-06-04 17:54:33 +00:00
|
|
|
return;
|
2019-03-30 16:48:36 +00:00
|
|
|
},
|
2016-06-04 17:54:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
self.active_tex = 0;
|
|
|
|
self.program = program;
|
2018-12-17 19:06:07 +00:00
|
|
|
self.rect_program = rect_program;
|
2016-06-04 17:54:33 +00:00
|
|
|
}
|
2016-06-29 17:21:02 +00:00
|
|
|
|
2018-11-12 18:23:23 +00:00
|
|
|
pub fn resize(&mut self, size: PhysicalSize, padding_x: f32, padding_y: f32) {
|
2018-11-11 20:24:41 +00:00
|
|
|
let (width, height): (u32, u32) = size.into();
|
2017-05-06 15:45:23 +00:00
|
|
|
|
2016-06-29 17:21:02 +00:00
|
|
|
// viewport
|
|
|
|
unsafe {
|
2018-11-12 18:23:23 +00:00
|
|
|
let width = width as i32;
|
|
|
|
let height = height as i32;
|
|
|
|
let padding_x = padding_x as i32;
|
|
|
|
let padding_y = padding_y as i32;
|
2018-11-11 20:24:41 +00:00
|
|
|
gl::Viewport(padding_x, padding_y, width - 2 * padding_x, height - 2 * padding_y);
|
2018-12-17 19:06:07 +00:00
|
|
|
|
|
|
|
// update projection
|
|
|
|
gl::UseProgram(self.program.id);
|
|
|
|
self.program.update_projection(
|
|
|
|
width as f32,
|
|
|
|
height as f32,
|
|
|
|
padding_x as f32,
|
|
|
|
padding_y as f32,
|
|
|
|
);
|
|
|
|
gl::UseProgram(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Render a rectangle
|
|
|
|
//
|
|
|
|
// This requires the rectangle program to be activated
|
|
|
|
fn render_rect(&mut self, rect: &Rect<f32>, color: Rgb, alpha: f32, size: &term::SizeInfo) {
|
|
|
|
// Do nothing when alpha is fully transparent
|
|
|
|
if alpha == 0. {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate rectangle position
|
|
|
|
let center_x = size.width / 2.;
|
|
|
|
let center_y = size.height / 2.;
|
|
|
|
let x = (rect.x - center_x) / center_x;
|
|
|
|
let y = -(rect.y - center_y) / center_y;
|
|
|
|
let width = rect.width / center_x;
|
|
|
|
let height = rect.height / center_y;
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
// Setup vertices
|
2019-03-30 16:48:36 +00:00
|
|
|
let vertices: [f32; 8] = [x + width, y, x + width, y - height, x, y - height, x, y];
|
2018-12-17 19:06:07 +00:00
|
|
|
|
|
|
|
// Load vertex data into array buffer
|
|
|
|
gl::BufferData(
|
|
|
|
gl::ARRAY_BUFFER,
|
|
|
|
(size_of::<f32>() * vertices.len()) as _,
|
|
|
|
vertices.as_ptr() as *const _,
|
2019-03-30 16:48:36 +00:00
|
|
|
gl::STATIC_DRAW,
|
2018-12-17 19:06:07 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// Color
|
|
|
|
self.rect_program.set_color(color, alpha);
|
|
|
|
|
|
|
|
// Draw the rectangle
|
|
|
|
gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, ptr::null());
|
2016-06-29 17:21:02 +00:00
|
|
|
}
|
2018-12-17 19:06:07 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-29 17:21:02 +00:00
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
impl<'a> RenderApi<'a> {
|
2017-07-28 22:14:18 +00:00
|
|
|
pub fn clear(&self, color: Rgb) {
|
2017-08-20 16:55:45 +00:00
|
|
|
let alpha = self.config.background_opacity().get();
|
2016-11-28 22:13:11 +00:00
|
|
|
unsafe {
|
|
|
|
gl::ClearColor(
|
2018-12-17 19:06:07 +00:00
|
|
|
(f32::from(color.r) / 255.0).min(1.0) * alpha,
|
|
|
|
(f32::from(color.g) / 255.0).min(1.0) * alpha,
|
|
|
|
(f32::from(color.b) / 255.0).min(1.0) * alpha,
|
|
|
|
alpha,
|
|
|
|
);
|
2016-11-28 22:13:11 +00:00
|
|
|
gl::Clear(gl::COLOR_BUFFER_BIT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
fn render_batch(&mut self) {
|
|
|
|
unsafe {
|
2018-10-16 17:02:52 +00:00
|
|
|
gl::BufferSubData(
|
|
|
|
gl::ARRAY_BUFFER,
|
|
|
|
0,
|
|
|
|
self.batch.size() as isize,
|
|
|
|
self.batch.instances.as_ptr() as *const _,
|
|
|
|
);
|
2016-06-06 20:20:35 +00:00
|
|
|
}
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
// Bind texture if necessary
|
|
|
|
if *self.active_tex != self.batch.tex {
|
|
|
|
unsafe {
|
|
|
|
gl::BindTexture(gl::TEXTURE_2D, self.batch.tex);
|
|
|
|
}
|
|
|
|
*self.active_tex = self.batch.tex;
|
|
|
|
}
|
2016-06-04 22:30:17 +00:00
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
unsafe {
|
2016-06-06 23:54:15 +00:00
|
|
|
self.program.set_background_pass(true);
|
2018-10-16 17:02:52 +00:00
|
|
|
gl::DrawElementsInstanced(
|
|
|
|
gl::TRIANGLES,
|
|
|
|
6,
|
|
|
|
gl::UNSIGNED_INT,
|
|
|
|
ptr::null(),
|
|
|
|
self.batch.len() as GLsizei,
|
|
|
|
);
|
2016-06-06 23:54:15 +00:00
|
|
|
self.program.set_background_pass(false);
|
2018-10-16 17:02:52 +00:00
|
|
|
gl::DrawElementsInstanced(
|
|
|
|
gl::TRIANGLES,
|
|
|
|
6,
|
|
|
|
gl::UNSIGNED_INT,
|
|
|
|
ptr::null(),
|
|
|
|
self.batch.len() as GLsizei,
|
|
|
|
);
|
2016-06-06 20:20:35 +00:00
|
|
|
}
|
2016-06-04 22:30:17 +00:00
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
self.batch.clear();
|
|
|
|
}
|
Display errors and warnings
To make sure that all error and information reporting to the user is
unified, all instances of `print!`, `eprint!`, `println!` and
`eprintln!` have been removed and replaced by logging.
When `RUST_LOG` is not specified, the default Alacritty logger now also
prints to both the stderr and a log file. The log file is only created
when a message is written to it and its name is printed to stdout the
first time it is used.
Whenever a warning or an error has been written to the log file/stderr,
a message is now displayed in Alacritty which points to the log file
where the full error is documented.
The message is cleared whenever the screen is cleared using either the
`clear` command or the `Ctrl+L` key binding.
To make sure that log files created by root don't prevent normal users
from interacting with them, the Alacritty log file is `/tmp/Alacritty-$PID.log`.
Since it's still possible that the log file can't be created, the UI
error/warning message now informs the user if the message was only
written to stderr. The reason why it couldn't be created is then printed
to stderr.
To make sure the deletion of the log file at runtime doesn't create any
issues, the file is re-created if a write is attempted without the file
being present.
To help with debugging Alacritty issues, a timestamp and the error
level are printed in all log messages.
All log messages now follow this format:
[YYYY-MM-DD HH:MM] [LEVEL] Message
Since it's not unusual to spawn a lot of different terminal emulators
without restarting, Alacritty can create a ton of different log files.
To combat this problem, logfiles are removed by default after
Alacritty has been closed. If the user wants to persist the log of a
single session, the `--persistent_logging` option can be used. For
persisting all log files, the `persistent_logging` option can be set in
the configuration file
2018-11-17 14:39:13 +00:00
|
|
|
|
|
|
|
/// Render a string in a variable location. Used for printing the render timer, warnings and
|
|
|
|
/// errors.
|
|
|
|
pub fn render_string(
|
|
|
|
&mut self,
|
|
|
|
string: &str,
|
|
|
|
line: Line,
|
|
|
|
glyph_cache: &mut GlyphCache,
|
2019-03-30 16:48:36 +00:00
|
|
|
color: Option<Rgb>,
|
Display errors and warnings
To make sure that all error and information reporting to the user is
unified, all instances of `print!`, `eprint!`, `println!` and
`eprintln!` have been removed and replaced by logging.
When `RUST_LOG` is not specified, the default Alacritty logger now also
prints to both the stderr and a log file. The log file is only created
when a message is written to it and its name is printed to stdout the
first time it is used.
Whenever a warning or an error has been written to the log file/stderr,
a message is now displayed in Alacritty which points to the log file
where the full error is documented.
The message is cleared whenever the screen is cleared using either the
`clear` command or the `Ctrl+L` key binding.
To make sure that log files created by root don't prevent normal users
from interacting with them, the Alacritty log file is `/tmp/Alacritty-$PID.log`.
Since it's still possible that the log file can't be created, the UI
error/warning message now informs the user if the message was only
written to stderr. The reason why it couldn't be created is then printed
to stderr.
To make sure the deletion of the log file at runtime doesn't create any
issues, the file is re-created if a write is attempted without the file
being present.
To help with debugging Alacritty issues, a timestamp and the error
level are printed in all log messages.
All log messages now follow this format:
[YYYY-MM-DD HH:MM] [LEVEL] Message
Since it's not unusual to spawn a lot of different terminal emulators
without restarting, Alacritty can create a ton of different log files.
To combat this problem, logfiles are removed by default after
Alacritty has been closed. If the user wants to persist the log of a
single session, the `--persistent_logging` option can be used. For
persisting all log files, the `persistent_logging` option can be set in
the configuration file
2018-11-17 14:39:13 +00:00
|
|
|
) {
|
2019-02-07 22:36:45 +00:00
|
|
|
let bg_alpha = color.map(|_| 1.0).unwrap_or(0.0);
|
2016-11-28 22:13:11 +00:00
|
|
|
let col = Column(0);
|
|
|
|
|
2018-10-16 17:02:52 +00:00
|
|
|
let cells = string
|
|
|
|
.chars()
|
2016-11-28 22:13:11 +00:00
|
|
|
.enumerate()
|
2017-02-11 20:49:40 +00:00
|
|
|
.map(|(i, c)| RenderableCell {
|
2018-03-04 22:40:15 +00:00
|
|
|
line,
|
2016-11-28 22:13:11 +00:00
|
|
|
column: col + i,
|
2019-04-19 18:00:24 +00:00
|
|
|
inner: RenderableCellContent::Chars({
|
2018-12-09 15:28:22 +00:00
|
|
|
let mut chars = [' '; cell::MAX_ZEROWIDTH_CHARS + 1];
|
|
|
|
chars[0] = c;
|
|
|
|
chars
|
2019-04-19 18:00:24 +00:00
|
|
|
}),
|
2019-03-30 16:48:36 +00:00
|
|
|
bg: color.unwrap_or(Rgb { r: 0, g: 0, b: 0 }),
|
2017-02-11 20:49:40 +00:00
|
|
|
fg: Rgb { r: 0, g: 0, b: 0 },
|
|
|
|
flags: cell::Flags::empty(),
|
2019-02-07 22:36:45 +00:00
|
|
|
bg_alpha,
|
2016-11-28 22:13:11 +00:00
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
2016-06-04 22:30:17 +00:00
|
|
|
|
2018-12-22 17:16:54 +00:00
|
|
|
for cell in cells {
|
|
|
|
self.render_cell(cell, glyph_cache);
|
|
|
|
}
|
2016-06-06 20:20:35 +00:00
|
|
|
}
|
2016-06-04 22:30:17 +00:00
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
#[inline]
|
2017-02-11 20:49:40 +00:00
|
|
|
fn add_render_item(&mut self, cell: &RenderableCell, glyph: &Glyph) {
|
2016-06-06 20:29:05 +00:00
|
|
|
// Flush batch if tex changing
|
2016-12-17 06:13:51 +00:00
|
|
|
if !self.batch.is_empty() && self.batch.tex != glyph.tex_id {
|
|
|
|
self.render_batch();
|
2016-06-06 20:29:05 +00:00
|
|
|
}
|
|
|
|
|
2017-02-11 20:49:40 +00:00
|
|
|
self.batch.add_item(cell, glyph);
|
2016-06-04 22:30:17 +00:00
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
// Render batch and clear if it's full
|
|
|
|
if self.batch.full() {
|
|
|
|
self.render_batch();
|
|
|
|
}
|
|
|
|
}
|
2016-06-04 22:30:17 +00:00
|
|
|
|
2018-12-22 17:16:54 +00:00
|
|
|
pub fn render_cell(&mut self, cell: RenderableCell, glyph_cache: &mut GlyphCache) {
|
2019-04-19 20:56:11 +00:00
|
|
|
let chars = match cell.inner {
|
2019-04-25 20:01:23 +00:00
|
|
|
RenderableCellContent::Cursor((cursor_style, ref raw)) => {
|
2019-04-19 20:56:11 +00:00
|
|
|
// Raw cell pixel buffers like cursors don't need to go through font lookup
|
2019-04-25 20:01:23 +00:00
|
|
|
let glyph = glyph_cache
|
|
|
|
.cursor_cache
|
|
|
|
.entry(cursor_style)
|
|
|
|
.or_insert_with(|| self.load_glyph(raw));
|
2019-04-19 20:56:11 +00:00
|
|
|
self.add_render_item(&cell, &glyph);
|
|
|
|
return;
|
|
|
|
},
|
|
|
|
RenderableCellContent::Chars(chars) => chars,
|
|
|
|
};
|
2019-04-19 18:00:24 +00:00
|
|
|
|
2018-12-22 17:16:54 +00:00
|
|
|
// Get font key for cell
|
|
|
|
// FIXME this is super inefficient.
|
|
|
|
let font_key = if cell.flags.contains(cell::Flags::BOLD) {
|
|
|
|
glyph_cache.bold_key
|
|
|
|
} else if cell.flags.contains(cell::Flags::ITALIC) {
|
|
|
|
glyph_cache.italic_key
|
|
|
|
} else {
|
|
|
|
glyph_cache.font_key
|
|
|
|
};
|
2016-08-12 18:38:41 +00:00
|
|
|
|
2018-12-22 17:16:54 +00:00
|
|
|
// Don't render text of HIDDEN cells
|
|
|
|
let mut chars = if cell.flags.contains(cell::Flags::HIDDEN) {
|
|
|
|
[' '; cell::MAX_ZEROWIDTH_CHARS + 1]
|
|
|
|
} else {
|
2019-04-19 20:56:11 +00:00
|
|
|
chars
|
2018-12-22 17:16:54 +00:00
|
|
|
};
|
2018-12-09 15:28:22 +00:00
|
|
|
|
2018-12-22 17:16:54 +00:00
|
|
|
// Render tabs as spaces in case the font doesn't support it
|
|
|
|
if chars[0] == '\t' {
|
|
|
|
chars[0] = ' ';
|
|
|
|
}
|
2018-12-15 21:33:33 +00:00
|
|
|
|
2019-03-30 16:48:36 +00:00
|
|
|
let mut glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c: chars[0] };
|
2018-12-09 15:28:22 +00:00
|
|
|
|
2018-12-22 17:16:54 +00:00
|
|
|
// Add cell to batch
|
|
|
|
let glyph = glyph_cache.get(glyph_key, self);
|
|
|
|
self.add_render_item(&cell, glyph);
|
|
|
|
|
|
|
|
// Render zero-width characters
|
|
|
|
for c in (&chars[1..]).iter().filter(|c| **c != ' ') {
|
|
|
|
glyph_key.c = *c;
|
|
|
|
let mut glyph = *glyph_cache.get(glyph_key, self);
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
glyph.left += glyph_cache.metrics.average_advance as f32;
|
|
|
|
|
|
|
|
self.add_render_item(&cell, &glyph);
|
2016-06-06 20:20:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-06-04 22:30:17 +00:00
|
|
|
|
2017-12-20 02:59:45 +00:00
|
|
|
/// Load a glyph into a texture atlas
|
|
|
|
///
|
|
|
|
/// If the current atlas is full, a new one will be created.
|
|
|
|
#[inline]
|
|
|
|
fn load_glyph(
|
|
|
|
active_tex: &mut GLuint,
|
|
|
|
atlas: &mut Vec<Atlas>,
|
|
|
|
current_atlas: &mut usize,
|
2019-03-30 16:48:36 +00:00
|
|
|
rasterized: &RasterizedGlyph,
|
2017-12-20 02:59:45 +00:00
|
|
|
) -> Glyph {
|
|
|
|
// At least one atlas is guaranteed to be in the `self.atlas` list; thus
|
|
|
|
// the unwrap.
|
|
|
|
match atlas[*current_atlas].insert(rasterized, active_tex) {
|
|
|
|
Ok(glyph) => glyph,
|
2017-12-24 18:00:12 +00:00
|
|
|
Err(AtlasInsertError::Full) => {
|
2017-12-20 02:59:45 +00:00
|
|
|
*current_atlas += 1;
|
|
|
|
if *current_atlas == atlas.len() {
|
|
|
|
let new = Atlas::new(ATLAS_SIZE);
|
|
|
|
*active_tex = 0; // Atlas::new binds a texture. Ugh this is sloppy.
|
|
|
|
atlas.push(new);
|
2016-08-12 18:38:41 +00:00
|
|
|
}
|
2017-12-20 02:59:45 +00:00
|
|
|
load_glyph(active_tex, atlas, current_atlas, rasterized)
|
2019-03-30 16:48:36 +00:00
|
|
|
},
|
|
|
|
Err(AtlasInsertError::GlyphTooLarge) => Glyph {
|
|
|
|
tex_id: atlas[*current_atlas].id,
|
|
|
|
top: 0.0,
|
|
|
|
left: 0.0,
|
|
|
|
width: 0.0,
|
|
|
|
height: 0.0,
|
|
|
|
uv_bot: 0.0,
|
|
|
|
uv_left: 0.0,
|
|
|
|
uv_width: 0.0,
|
|
|
|
uv_height: 0.0,
|
|
|
|
},
|
2016-08-12 18:38:41 +00:00
|
|
|
}
|
2017-12-20 02:59:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn clear_atlas(atlas: &mut Vec<Atlas>, current_atlas: &mut usize) {
|
|
|
|
for atlas in atlas.iter_mut() {
|
|
|
|
atlas.clear();
|
|
|
|
}
|
|
|
|
*current_atlas = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> LoadGlyph for LoaderApi<'a> {
|
|
|
|
fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph {
|
|
|
|
load_glyph(self.active_tex, self.atlas, self.current_atlas, rasterized)
|
|
|
|
}
|
2017-10-15 01:58:19 +00:00
|
|
|
|
|
|
|
fn clear(&mut self) {
|
2017-12-20 02:59:45 +00:00
|
|
|
clear_atlas(self.atlas, self.current_atlas)
|
2017-10-15 01:58:19 +00:00
|
|
|
}
|
2016-08-12 18:38:41 +00:00
|
|
|
}
|
|
|
|
|
2016-06-06 21:31:12 +00:00
|
|
|
impl<'a> LoadGlyph for RenderApi<'a> {
|
|
|
|
fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph {
|
2017-12-20 02:59:45 +00:00
|
|
|
load_glyph(self.active_tex, self.atlas, self.current_atlas, rasterized)
|
2016-06-06 21:31:12 +00:00
|
|
|
}
|
2017-10-15 01:58:19 +00:00
|
|
|
|
|
|
|
fn clear(&mut self) {
|
2017-12-20 02:59:45 +00:00
|
|
|
clear_atlas(self.atlas, self.current_atlas)
|
2017-10-15 01:58:19 +00:00
|
|
|
}
|
2016-06-06 21:31:12 +00:00
|
|
|
}
|
|
|
|
|
2016-06-06 20:20:35 +00:00
|
|
|
impl<'a> Drop for RenderApi<'a> {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
if !self.batch.is_empty() {
|
|
|
|
self.render_batch();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-17 19:06:07 +00:00
|
|
|
impl TextShaderProgram {
|
2019-04-14 15:37:58 +00:00
|
|
|
pub fn new() -> Result<TextShaderProgram, ShaderCreationError> {
|
2018-12-17 19:06:07 +00:00
|
|
|
let (vertex_src, fragment_src) = if cfg!(feature = "live-shader-reload") {
|
|
|
|
(None, None)
|
2016-10-27 15:43:23 +00:00
|
|
|
} else {
|
2018-12-17 19:06:07 +00:00
|
|
|
(Some(TEXT_SHADER_V), Some(TEXT_SHADER_F))
|
2016-10-27 15:43:23 +00:00
|
|
|
};
|
2018-12-17 19:06:07 +00:00
|
|
|
let vertex_shader = create_shader(TEXT_SHADER_V_PATH, gl::VERTEX_SHADER, vertex_src)?;
|
|
|
|
let fragment_shader = create_shader(TEXT_SHADER_F_PATH, gl::FRAGMENT_SHADER, fragment_src)?;
|
|
|
|
let program = create_program(vertex_shader, fragment_shader)?;
|
2016-02-26 04:59:21 +00:00
|
|
|
|
|
|
|
unsafe {
|
|
|
|
gl::DeleteShader(fragment_shader);
|
2018-12-17 19:06:07 +00:00
|
|
|
gl::DeleteShader(vertex_shader);
|
2016-06-04 22:30:17 +00:00
|
|
|
gl::UseProgram(program);
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! cptr {
|
2019-03-30 16:48:36 +00:00
|
|
|
($thing:expr) => {
|
|
|
|
$thing.as_ptr() as *const _
|
|
|
|
};
|
2016-06-04 22:30:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! assert_uniform_valid {
|
|
|
|
($uniform:expr) => {
|
|
|
|
assert!($uniform != gl::INVALID_VALUE as i32);
|
|
|
|
assert!($uniform != gl::INVALID_OPERATION as i32);
|
|
|
|
};
|
|
|
|
( $( $uniform:expr ),* ) => {
|
2016-08-12 18:38:41 +00:00
|
|
|
$( assert_uniform_valid!($uniform); )*
|
2016-06-04 22:30:17 +00:00
|
|
|
};
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// get uniform locations
|
2019-02-04 19:03:25 +00:00
|
|
|
let (projection, cell_dim, background) = unsafe {
|
2016-06-04 22:30:17 +00:00
|
|
|
(
|
|
|
|
gl::GetUniformLocation(program, cptr!(b"projection\0")),
|
|
|
|
gl::GetUniformLocation(program, cptr!(b"cellDim\0")),
|
2016-06-06 23:54:15 +00:00
|
|
|
gl::GetUniformLocation(program, cptr!(b"backgroundPass\0")),
|
2016-06-04 22:30:17 +00:00
|
|
|
)
|
|
|
|
};
|
|
|
|
|
2019-02-04 19:03:25 +00:00
|
|
|
assert_uniform_valid!(projection, cell_dim, background);
|
2016-06-03 03:27:07 +00:00
|
|
|
|
2018-12-17 19:06:07 +00:00
|
|
|
let shader = TextShaderProgram {
|
2016-02-26 04:59:21 +00:00
|
|
|
id: program,
|
|
|
|
u_projection: projection,
|
2016-06-04 22:30:17 +00:00
|
|
|
u_cell_dim: cell_dim,
|
2016-06-06 23:54:15 +00:00
|
|
|
u_background: background,
|
2016-02-26 04:59:21 +00:00
|
|
|
};
|
|
|
|
|
2019-03-30 16:48:36 +00:00
|
|
|
unsafe {
|
|
|
|
gl::UseProgram(0);
|
|
|
|
}
|
2016-06-29 17:21:02 +00:00
|
|
|
|
|
|
|
Ok(shader)
|
|
|
|
}
|
|
|
|
|
2018-11-11 20:24:41 +00:00
|
|
|
fn update_projection(&self, width: f32, height: f32, padding_x: f32, padding_y: f32) {
|
2017-05-06 15:45:23 +00:00
|
|
|
// Bounds check
|
2019-03-30 16:48:36 +00:00
|
|
|
if (width as u32) < (2 * padding_x as u32) || (height as u32) < (2 * padding_y as u32) {
|
2017-05-06 15:45:23 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-02-04 19:03:25 +00:00
|
|
|
// 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.;
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2019-01-07 00:06:57 +00:00
|
|
|
info!("Width: {}, Height: {}", width, height);
|
2016-02-26 04:59:21 +00:00
|
|
|
|
|
|
|
unsafe {
|
2019-02-04 19:03:25 +00:00
|
|
|
gl::Uniform4f(self.u_projection, offset_x, offset_y, scale_x, scale_y);
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-28 16:18:54 +00:00
|
|
|
fn set_term_uniforms(&self, props: &term::SizeInfo) {
|
2016-06-04 22:30:17 +00:00
|
|
|
unsafe {
|
|
|
|
gl::Uniform2f(self.u_cell_dim, props.cell_width, props.cell_height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-06 23:54:15 +00:00
|
|
|
fn set_background_pass(&self, background_pass: bool) {
|
2018-10-16 17:02:52 +00:00
|
|
|
let value = if background_pass { 1 } else { 0 };
|
2016-06-06 23:54:15 +00:00
|
|
|
|
|
|
|
unsafe {
|
|
|
|
gl::Uniform1i(self.u_background, value);
|
|
|
|
}
|
|
|
|
}
|
2018-12-17 19:06:07 +00:00
|
|
|
}
|
2016-06-06 23:54:15 +00:00
|
|
|
|
2018-12-17 19:06:07 +00:00
|
|
|
impl Drop for TextShaderProgram {
|
|
|
|
fn drop(&mut self) {
|
2016-02-26 04:59:21 +00:00
|
|
|
unsafe {
|
2018-12-17 19:06:07 +00:00
|
|
|
gl::DeleteProgram(self.id);
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
|
|
|
}
|
2018-12-17 19:06:07 +00:00
|
|
|
}
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2018-12-17 19:06:07 +00:00
|
|
|
impl RectShaderProgram {
|
|
|
|
pub fn new() -> Result<Self, ShaderCreationError> {
|
|
|
|
let (vertex_src, fragment_src) = if cfg!(feature = "live-shader-reload") {
|
|
|
|
(None, None)
|
2016-10-27 15:43:23 +00:00
|
|
|
} else {
|
2018-12-17 19:06:07 +00:00
|
|
|
(Some(RECT_SHADER_V), Some(RECT_SHADER_F))
|
2016-10-27 15:43:23 +00:00
|
|
|
};
|
2019-03-30 16:48:36 +00:00
|
|
|
let vertex_shader = create_shader(RECT_SHADER_V_PATH, gl::VERTEX_SHADER, vertex_src)?;
|
|
|
|
let fragment_shader = create_shader(RECT_SHADER_F_PATH, gl::FRAGMENT_SHADER, fragment_src)?;
|
2018-12-17 19:06:07 +00:00
|
|
|
let program = create_program(vertex_shader, fragment_shader)?;
|
2016-10-27 15:43:23 +00:00
|
|
|
|
2018-12-17 19:06:07 +00:00
|
|
|
unsafe {
|
|
|
|
gl::DeleteShader(fragment_shader);
|
|
|
|
gl::DeleteShader(vertex_shader);
|
|
|
|
gl::UseProgram(program);
|
|
|
|
}
|
2016-10-27 15:43:23 +00:00
|
|
|
|
2018-12-17 19:06:07 +00:00
|
|
|
// get uniform locations
|
2019-03-30 16:48:36 +00:00
|
|
|
let u_color = unsafe { gl::GetUniformLocation(program, b"color\0".as_ptr() as *const _) };
|
2016-06-04 17:54:33 +00:00
|
|
|
|
2019-03-30 16:48:36 +00:00
|
|
|
let shader = RectShaderProgram { id: program, u_color };
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2018-12-17 19:06:07 +00:00
|
|
|
unsafe { gl::UseProgram(0) }
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2018-12-17 19:06:07 +00:00
|
|
|
Ok(shader)
|
|
|
|
}
|
2016-06-04 17:54:33 +00:00
|
|
|
|
2018-12-17 19:06:07 +00:00
|
|
|
fn set_color(&self, color: Rgb, alpha: f32) {
|
|
|
|
unsafe {
|
|
|
|
gl::Uniform4f(
|
2019-02-03 16:45:09 +00:00
|
|
|
self.u_color,
|
2018-12-17 19:06:07 +00:00
|
|
|
f32::from(color.r) / 255.,
|
|
|
|
f32::from(color.g) / 255.,
|
|
|
|
f32::from(color.b) / 255.,
|
|
|
|
alpha,
|
|
|
|
);
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-04 17:54:33 +00:00
|
|
|
}
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2018-12-17 19:06:07 +00:00
|
|
|
impl Drop for RectShaderProgram {
|
2016-06-06 20:20:35 +00:00
|
|
|
fn drop(&mut self) {
|
|
|
|
unsafe {
|
|
|
|
gl::DeleteProgram(self.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-17 19:06:07 +00:00
|
|
|
fn create_program(vertex: GLuint, fragment: GLuint) -> Result<GLuint, ShaderCreationError> {
|
|
|
|
unsafe {
|
|
|
|
let program = gl::CreateProgram();
|
|
|
|
gl::AttachShader(program, vertex);
|
|
|
|
gl::AttachShader(program, fragment);
|
|
|
|
gl::LinkProgram(program);
|
|
|
|
|
|
|
|
let mut success: GLint = 0;
|
|
|
|
gl::GetProgramiv(program, gl::LINK_STATUS, &mut success);
|
|
|
|
|
|
|
|
if success == i32::from(gl::TRUE) {
|
|
|
|
Ok(program)
|
|
|
|
} else {
|
|
|
|
Err(ShaderCreationError::Link(get_program_info_log(program)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-30 16:48:36 +00:00
|
|
|
fn create_shader(
|
|
|
|
path: &str,
|
|
|
|
kind: GLenum,
|
|
|
|
source: Option<&'static str>,
|
|
|
|
) -> Result<GLuint, ShaderCreationError> {
|
2018-12-17 19:06:07 +00:00
|
|
|
let from_disk;
|
|
|
|
let source = if let Some(src) = source {
|
|
|
|
src
|
|
|
|
} else {
|
|
|
|
from_disk = read_file(path)?;
|
|
|
|
&from_disk[..]
|
|
|
|
};
|
|
|
|
|
|
|
|
let len: [GLint; 1] = [source.len() as GLint];
|
|
|
|
|
|
|
|
let shader = unsafe {
|
|
|
|
let shader = gl::CreateShader(kind);
|
|
|
|
gl::ShaderSource(shader, 1, &(source.as_ptr() as *const _), len.as_ptr());
|
|
|
|
gl::CompileShader(shader);
|
|
|
|
shader
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut success: GLint = 0;
|
|
|
|
unsafe {
|
|
|
|
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success);
|
|
|
|
}
|
|
|
|
|
|
|
|
if success == GLint::from(gl::TRUE) {
|
|
|
|
Ok(shader)
|
|
|
|
} else {
|
|
|
|
// Read log
|
|
|
|
let log = get_shader_info_log(shader);
|
|
|
|
|
|
|
|
// Cleanup
|
2019-03-30 16:48:36 +00:00
|
|
|
unsafe {
|
|
|
|
gl::DeleteShader(shader);
|
|
|
|
}
|
2018-12-17 19:06:07 +00:00
|
|
|
|
|
|
|
Err(ShaderCreationError::Compile(PathBuf::from(path), log))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-05 04:26:28 +00:00
|
|
|
fn get_program_info_log(program: GLuint) -> String {
|
|
|
|
// Get expected log length
|
|
|
|
let mut max_length: GLint = 0;
|
|
|
|
unsafe {
|
|
|
|
gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut max_length);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the info log
|
|
|
|
let mut actual_length: GLint = 0;
|
|
|
|
let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize);
|
|
|
|
unsafe {
|
2019-03-30 16:48:36 +00:00
|
|
|
gl::GetProgramInfoLog(program, max_length, &mut actual_length, buf.as_mut_ptr() as *mut _);
|
2016-06-05 04:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Build a string
|
|
|
|
unsafe {
|
|
|
|
buf.set_len(actual_length as usize);
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX should we expect opengl to return garbage?
|
|
|
|
String::from_utf8(buf).unwrap()
|
|
|
|
}
|
|
|
|
|
2016-06-04 17:54:33 +00:00
|
|
|
fn get_shader_info_log(shader: GLuint) -> String {
|
|
|
|
// Get expected log length
|
|
|
|
let mut max_length: GLint = 0;
|
|
|
|
unsafe {
|
|
|
|
gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut max_length);
|
|
|
|
}
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2016-06-04 17:54:33 +00:00
|
|
|
// Read the info log
|
|
|
|
let mut actual_length: GLint = 0;
|
|
|
|
let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize);
|
|
|
|
unsafe {
|
2019-03-30 16:48:36 +00:00
|
|
|
gl::GetShaderInfoLog(shader, max_length, &mut actual_length, buf.as_mut_ptr() as *mut _);
|
2016-06-04 17:54:33 +00:00
|
|
|
}
|
2016-02-26 04:59:21 +00:00
|
|
|
|
2016-06-04 17:54:33 +00:00
|
|
|
// Build a string
|
|
|
|
unsafe {
|
|
|
|
buf.set_len(actual_length as usize);
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX should we expect opengl to return garbage?
|
|
|
|
String::from_utf8(buf).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read_file(path: &str) -> Result<String, io::Error> {
|
|
|
|
let mut f = File::open(path)?;
|
|
|
|
let mut buf = String::new();
|
|
|
|
f.read_to_string(&mut buf)?;
|
|
|
|
|
|
|
|
Ok(buf)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum ShaderCreationError {
|
|
|
|
/// Error reading file
|
|
|
|
Io(io::Error),
|
|
|
|
|
|
|
|
/// Error compiling shader
|
|
|
|
Compile(PathBuf, String),
|
2017-10-21 22:26:42 +00:00
|
|
|
|
|
|
|
/// Problem linking
|
|
|
|
Link(String),
|
2016-06-04 17:54:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ::std::error::Error for ShaderCreationError {
|
2018-12-10 17:53:56 +00:00
|
|
|
fn cause(&self) -> Option<&dyn (::std::error::Error)> {
|
2016-06-04 17:54:33 +00:00
|
|
|
match *self {
|
|
|
|
ShaderCreationError::Io(ref err) => Some(err),
|
2017-10-21 22:26:42 +00:00
|
|
|
_ => None,
|
2016-06-04 17:54:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn description(&self) -> &str {
|
|
|
|
match *self {
|
|
|
|
ShaderCreationError::Io(ref err) => err.description(),
|
|
|
|
ShaderCreationError::Compile(ref _path, ref s) => s.as_str(),
|
2017-10-21 22:26:42 +00:00
|
|
|
ShaderCreationError::Link(ref s) => s.as_str(),
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-04 17:54:33 +00:00
|
|
|
impl ::std::fmt::Display for ShaderCreationError {
|
2018-12-10 17:53:56 +00:00
|
|
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
2016-06-04 17:54:33 +00:00
|
|
|
match *self {
|
2019-03-30 16:48:36 +00:00
|
|
|
ShaderCreationError::Io(ref err) => write!(f, "Couldn't read shader: {}", err),
|
2019-01-07 00:06:57 +00:00
|
|
|
ShaderCreationError::Compile(ref path, ref log) => {
|
|
|
|
write!(f, "Failed compiling shader at {}: {}", path.display(), log)
|
|
|
|
},
|
2019-03-30 16:48:36 +00:00
|
|
|
ShaderCreationError::Link(ref log) => write!(f, "Failed linking shader: {}", log),
|
2016-06-04 17:54:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<io::Error> for ShaderCreationError {
|
|
|
|
fn from(val: io::Error) -> ShaderCreationError {
|
|
|
|
ShaderCreationError::Io(val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-03 05:14:55 +00:00
|
|
|
/// Manages a single texture atlas
|
|
|
|
///
|
|
|
|
/// The strategy for filling an atlas looks roughly like this:
|
|
|
|
///
|
2016-11-20 00:16:20 +00:00
|
|
|
/// ```ignore
|
2016-06-03 05:14:55 +00:00
|
|
|
/// (width, height)
|
|
|
|
/// ┌─────┬─────┬─────┬─────┬─────┐
|
|
|
|
/// │ 10 │ │ │ │ │ <- Empty spaces; can be filled while
|
|
|
|
/// │ │ │ │ │ │ glyph_height < height - row_baseline
|
|
|
|
/// ├⎼⎼⎼⎼⎼┼⎼⎼⎼⎼⎼┼⎼⎼⎼⎼⎼┼⎼⎼⎼⎼⎼┼⎼⎼⎼⎼⎼┤
|
|
|
|
/// │ 5 │ 6 │ 7 │ 8 │ 9 │
|
|
|
|
/// │ │ │ │ │ │
|
|
|
|
/// ├⎼⎼⎼⎼⎼┼⎼⎼⎼⎼⎼┼⎼⎼⎼⎼⎼┼⎼⎼⎼⎼⎼┴⎼⎼⎼⎼⎼┤ <- Row height is tallest glyph in row; this is
|
|
|
|
/// │ 1 │ 2 │ 3 │ 4 │ used as the baseline for the following row.
|
|
|
|
/// │ │ │ │ │ <- Row considered full when next glyph doesn't
|
|
|
|
/// └─────┴─────┴─────┴───────────┘ fit in the row.
|
|
|
|
/// (0, 0) x->
|
2016-11-20 00:16:20 +00:00
|
|
|
/// ```
|
2016-06-06 20:20:35 +00:00
|
|
|
#[derive(Debug)]
|
2016-06-03 05:14:55 +00:00
|
|
|
struct Atlas {
|
|
|
|
/// Texture id for this atlas
|
|
|
|
id: GLuint,
|
|
|
|
|
|
|
|
/// Width of atlas
|
2016-02-26 04:59:21 +00:00
|
|
|
width: i32,
|
2016-06-03 05:14:55 +00:00
|
|
|
|
|
|
|
/// Height of atlas
|
2016-02-26 04:59:21 +00:00
|
|
|
height: i32,
|
2016-06-03 05:14:55 +00:00
|
|
|
|
|
|
|
/// Left-most free pixel in a row.
|
|
|
|
///
|
|
|
|
/// This is called the extent because it is the upper bound of used pixels
|
|
|
|
/// in a row.
|
|
|
|
row_extent: i32,
|
|
|
|
|
|
|
|
/// Baseline for glyphs in the current row
|
|
|
|
row_baseline: i32,
|
|
|
|
|
|
|
|
/// Tallest glyph in current row
|
|
|
|
///
|
|
|
|
/// This is used as the advance when end of row is reached
|
|
|
|
row_tallest: i32,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Error that can happen when inserting a texture to the Atlas
|
|
|
|
enum AtlasInsertError {
|
|
|
|
/// Texture atlas is full
|
|
|
|
Full,
|
2017-12-24 18:00:12 +00:00
|
|
|
|
|
|
|
/// The glyph cannot fit within a single texture
|
|
|
|
GlyphTooLarge,
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
|
|
|
|
2016-06-03 05:14:55 +00:00
|
|
|
impl Atlas {
|
2016-06-06 21:31:12 +00:00
|
|
|
fn new(size: i32) -> Atlas {
|
|
|
|
let mut id: GLuint = 0;
|
|
|
|
unsafe {
|
|
|
|
gl::PixelStorei(gl::UNPACK_ALIGNMENT, 1);
|
|
|
|
gl::GenTextures(1, &mut id);
|
|
|
|
gl::BindTexture(gl::TEXTURE_2D, id);
|
|
|
|
gl::TexImage2D(
|
|
|
|
gl::TEXTURE_2D,
|
|
|
|
0,
|
|
|
|
gl::RGB as i32,
|
|
|
|
size,
|
|
|
|
size,
|
|
|
|
0,
|
|
|
|
gl::RGB,
|
|
|
|
gl::UNSIGNED_BYTE,
|
2018-10-16 17:02:52 +00:00
|
|
|
ptr::null(),
|
2016-06-06 21:31:12 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
|
|
|
|
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
|
|
|
|
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
|
|
|
|
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
|
|
|
|
|
|
|
|
gl::BindTexture(gl::TEXTURE_2D, 0);
|
|
|
|
}
|
|
|
|
|
2019-03-30 16:48:36 +00:00
|
|
|
Atlas { id, width: size, height: size, row_extent: 0, row_baseline: 0, row_tallest: 0 }
|
2016-06-06 21:31:12 +00:00
|
|
|
}
|
2016-06-03 05:14:55 +00:00
|
|
|
|
2017-10-15 01:58:19 +00:00
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.row_extent = 0;
|
|
|
|
self.row_baseline = 0;
|
|
|
|
self.row_tallest = 0;
|
|
|
|
}
|
|
|
|
|
2016-06-03 05:14:55 +00:00
|
|
|
/// Insert a RasterizedGlyph into the texture atlas
|
2017-11-26 19:48:28 +00:00
|
|
|
pub fn insert(
|
|
|
|
&mut self,
|
|
|
|
glyph: &RasterizedGlyph,
|
2019-03-30 16:48:36 +00:00
|
|
|
active_tex: &mut u32,
|
2017-11-26 19:48:28 +00:00
|
|
|
) -> Result<Glyph, AtlasInsertError> {
|
2017-12-24 18:00:12 +00:00
|
|
|
if glyph.width > self.width || glyph.height > self.height {
|
|
|
|
return Err(AtlasInsertError::GlyphTooLarge);
|
|
|
|
}
|
|
|
|
|
2016-06-03 05:14:55 +00:00
|
|
|
// If there's not enough room in current row, go onto next one
|
|
|
|
if !self.room_in_row(glyph) {
|
|
|
|
self.advance_row()?;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there's still not room, there's nothing that can be done here.
|
|
|
|
if !self.room_in_row(glyph) {
|
|
|
|
return Err(AtlasInsertError::Full);
|
|
|
|
}
|
|
|
|
|
|
|
|
// There appears to be room; load the glyph.
|
2016-06-03 16:26:58 +00:00
|
|
|
Ok(self.insert_inner(glyph, active_tex))
|
2016-06-03 05:14:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Insert the glyph without checking for room
|
|
|
|
///
|
2016-12-17 06:48:04 +00:00
|
|
|
/// Internal function for use once atlas has been checked for space. GL
|
|
|
|
/// errors could still occur at this point if we were checking for them;
|
|
|
|
/// hence, the Result.
|
2017-11-26 19:48:28 +00:00
|
|
|
fn insert_inner(&mut self, glyph: &RasterizedGlyph, active_tex: &mut u32) -> Glyph {
|
2016-06-03 05:14:55 +00:00
|
|
|
let offset_y = self.row_baseline;
|
|
|
|
let offset_x = self.row_extent;
|
|
|
|
let height = glyph.height as i32;
|
|
|
|
let width = glyph.width as i32;
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
gl::BindTexture(gl::TEXTURE_2D, self.id);
|
|
|
|
|
|
|
|
// Load data into OpenGL
|
|
|
|
gl::TexSubImage2D(
|
|
|
|
gl::TEXTURE_2D,
|
|
|
|
0,
|
|
|
|
offset_x,
|
|
|
|
offset_y,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
gl::RGB,
|
|
|
|
gl::UNSIGNED_BYTE,
|
2018-10-16 17:02:52 +00:00
|
|
|
glyph.buf.as_ptr() as *const _,
|
2016-06-03 05:14:55 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
gl::BindTexture(gl::TEXTURE_2D, 0);
|
2016-06-03 16:26:58 +00:00
|
|
|
*active_tex = 0;
|
2016-06-03 05:14:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Update Atlas state
|
|
|
|
self.row_extent = offset_x + width;
|
|
|
|
if height > self.row_tallest {
|
|
|
|
self.row_tallest = height;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate UV coordinates
|
|
|
|
let uv_bot = offset_y as f32 / self.height as f32;
|
|
|
|
let uv_left = offset_x as f32 / self.width as f32;
|
|
|
|
let uv_height = height as f32 / self.height as f32;
|
|
|
|
let uv_width = width as f32 / self.width as f32;
|
|
|
|
|
2016-12-17 06:13:51 +00:00
|
|
|
Glyph {
|
2016-06-04 22:30:17 +00:00
|
|
|
tex_id: self.id,
|
2017-07-04 02:43:44 +00:00
|
|
|
top: glyph.top as f32,
|
|
|
|
width: width as f32,
|
|
|
|
height: height as f32,
|
|
|
|
left: glyph.left as f32,
|
2018-03-04 22:40:15 +00:00
|
|
|
uv_bot,
|
|
|
|
uv_left,
|
|
|
|
uv_width,
|
|
|
|
uv_height,
|
2016-12-17 06:13:51 +00:00
|
|
|
}
|
2016-02-26 04:59:21 +00:00
|
|
|
}
|
2016-06-03 05:14:55 +00:00
|
|
|
|
|
|
|
/// Check if there's room in the current row for given glyph
|
|
|
|
fn room_in_row(&self, raw: &RasterizedGlyph) -> bool {
|
|
|
|
let next_extent = self.row_extent + raw.width as i32;
|
|
|
|
let enough_width = next_extent <= self.width;
|
|
|
|
let enough_height = (raw.height as i32) < (self.height - self.row_baseline);
|
|
|
|
|
|
|
|
enough_width && enough_height
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Mark current row as finished and prepare to insert into the next row
|
|
|
|
fn advance_row(&mut self) -> Result<(), AtlasInsertError> {
|
|
|
|
let advance_to = self.row_baseline + self.row_tallest;
|
|
|
|
if self.height - advance_to <= 0 {
|
|
|
|
return Err(AtlasInsertError::Full);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.row_baseline = advance_to;
|
|
|
|
self.row_extent = 0;
|
|
|
|
self.row_tallest = 0;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|