mirror of
https://github.com/alacritty/alacritty.git
synced 2024-11-25 14:05:41 -05:00
parent
88b4dbfc5a
commit
79b19176ee
9 changed files with 258 additions and 52 deletions
|
@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
- Terminal escape bindings with combined modifiers for Delete and Insert
|
- Terminal escape bindings with combined modifiers for Delete and Insert
|
||||||
- /Applications symlink into OS X DMG for easier installation
|
- /Applications symlink into OS X DMG for easier installation
|
||||||
|
- Colored emojis on Linux/BSD
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ use std::cmp;
|
||||||
|
|
||||||
use alacritty_terminal::ansi::CursorStyle;
|
use alacritty_terminal::ansi::CursorStyle;
|
||||||
|
|
||||||
use font::{Metrics, RasterizedGlyph};
|
use font::{BitmapBuffer, Metrics, RasterizedGlyph};
|
||||||
|
|
||||||
/// Width/Height of the cursor relative to the font width
|
/// Width/Height of the cursor relative to the font width
|
||||||
pub const CURSOR_WIDTH_PERCENTAGE: i32 = 15;
|
pub const CURSOR_WIDTH_PERCENTAGE: i32 = 15;
|
||||||
|
@ -55,7 +55,14 @@ pub fn get_underline_cursor_glyph(width: i32, line_width: i32) -> RasterizedGlyp
|
||||||
let buf = vec![255u8; (width * line_width * 3) as usize];
|
let buf = vec![255u8; (width * line_width * 3) as usize];
|
||||||
|
|
||||||
// Create a custom glyph with the rectangle data attached to it
|
// Create a custom glyph with the rectangle data attached to it
|
||||||
RasterizedGlyph { c: ' ', top: line_width, left: 0, height: line_width, width, buf }
|
RasterizedGlyph {
|
||||||
|
c: ' ',
|
||||||
|
top: line_width,
|
||||||
|
left: 0,
|
||||||
|
height: line_width,
|
||||||
|
width,
|
||||||
|
buf: BitmapBuffer::RGB(buf),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a custom beam cursor character
|
// Returns a custom beam cursor character
|
||||||
|
@ -64,7 +71,14 @@ pub fn get_beam_cursor_glyph(height: i32, line_width: i32) -> RasterizedGlyph {
|
||||||
let buf = vec![255u8; (line_width * height * 3) as usize];
|
let buf = vec![255u8; (line_width * height * 3) as usize];
|
||||||
|
|
||||||
// Create a custom glyph with the rectangle data attached to it
|
// Create a custom glyph with the rectangle data attached to it
|
||||||
RasterizedGlyph { c: ' ', top: height, left: 0, height, width: line_width, buf }
|
RasterizedGlyph {
|
||||||
|
c: ' ',
|
||||||
|
top: height,
|
||||||
|
left: 0,
|
||||||
|
height,
|
||||||
|
width: line_width,
|
||||||
|
buf: BitmapBuffer::RGB(buf),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a custom box cursor character
|
// Returns a custom box cursor character
|
||||||
|
@ -86,7 +100,7 @@ pub fn get_box_cursor_glyph(height: i32, width: i32, line_width: i32) -> Rasteri
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a custom glyph with the rectangle data attached to it
|
// Create a custom glyph with the rectangle data attached to it
|
||||||
RasterizedGlyph { c: ' ', top: height, left: 0, height, width, buf }
|
RasterizedGlyph { c: ' ', top: height, left: 0, height, width, buf: BitmapBuffer::RGB(buf) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a custom block cursor character
|
// Returns a custom block cursor character
|
||||||
|
@ -95,5 +109,5 @@ pub fn get_block_cursor_glyph(height: i32, width: i32) -> RasterizedGlyph {
|
||||||
let buf = vec![255u8; (width * height * 3) as usize];
|
let buf = vec![255u8; (width * height * 3) as usize];
|
||||||
|
|
||||||
// Create a custom glyph with the rectangle data attached to it
|
// Create a custom glyph with the rectangle data attached to it
|
||||||
RasterizedGlyph { c: ' ', top: height, left: 0, height, width, buf }
|
RasterizedGlyph { c: ' ', top: height, left: 0, height, width, buf: BitmapBuffer::RGB(buf) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,9 @@ use std::sync::mpsc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use fnv::FnvHasher;
|
use fnv::FnvHasher;
|
||||||
use font::{self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer};
|
use font::{
|
||||||
|
self, BitmapBuffer, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer,
|
||||||
|
};
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
|
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
|
||||||
|
|
||||||
|
@ -139,6 +141,7 @@ pub struct RectShaderProgram {
|
||||||
#[derive(Copy, Debug, Clone)]
|
#[derive(Copy, Debug, Clone)]
|
||||||
pub struct Glyph {
|
pub struct Glyph {
|
||||||
tex_id: GLuint,
|
tex_id: GLuint,
|
||||||
|
colored: bool,
|
||||||
top: f32,
|
top: f32,
|
||||||
left: f32,
|
left: f32,
|
||||||
width: f32,
|
width: f32,
|
||||||
|
@ -462,11 +465,19 @@ impl Batch {
|
||||||
Batch { tex: 0, instances: Vec::with_capacity(BATCH_MAX) }
|
Batch { tex: 0, instances: Vec::with_capacity(BATCH_MAX) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_item(&mut self, cell: RenderableCell, glyph: &Glyph) {
|
pub fn add_item(&mut self, mut cell: RenderableCell, glyph: &Glyph) {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
self.tex = glyph.tex_id;
|
self.tex = glyph.tex_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if glyph.colored {
|
||||||
|
// XXX Temporary workaround to prevent emojis being rendered with a wrong colors on, at
|
||||||
|
// least, dark backgrounds. For more info see #1864.
|
||||||
|
cell.fg.r = 255;
|
||||||
|
cell.fg.g = 255;
|
||||||
|
cell.fg.b = 255;
|
||||||
|
}
|
||||||
|
|
||||||
self.instances.push(InstanceData {
|
self.instances.push(InstanceData {
|
||||||
col: cell.column.0 as f32,
|
col: cell.column.0 as f32,
|
||||||
row: cell.line.0 as f32,
|
row: cell.line.0 as f32,
|
||||||
|
@ -1091,6 +1102,7 @@ fn load_glyph(
|
||||||
},
|
},
|
||||||
Err(AtlasInsertError::GlyphTooLarge) => Glyph {
|
Err(AtlasInsertError::GlyphTooLarge) => Glyph {
|
||||||
tex_id: atlas[*current_atlas].id,
|
tex_id: atlas[*current_atlas].id,
|
||||||
|
colored: false,
|
||||||
top: 0.0,
|
top: 0.0,
|
||||||
left: 0.0,
|
left: 0.0,
|
||||||
width: 0.0,
|
width: 0.0,
|
||||||
|
@ -1573,11 +1585,23 @@ impl Atlas {
|
||||||
let offset_x = self.row_extent;
|
let offset_x = self.row_extent;
|
||||||
let height = glyph.height as i32;
|
let height = glyph.height as i32;
|
||||||
let width = glyph.width as i32;
|
let width = glyph.width as i32;
|
||||||
|
let colored;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
gl::BindTexture(gl::TEXTURE_2D, self.id);
|
gl::BindTexture(gl::TEXTURE_2D, self.id);
|
||||||
|
|
||||||
// Load data into OpenGL
|
// Load data into OpenGL
|
||||||
|
let (format, buf) = match &glyph.buf {
|
||||||
|
BitmapBuffer::RGB(buf) => {
|
||||||
|
colored = false;
|
||||||
|
(gl::RGB, buf)
|
||||||
|
},
|
||||||
|
BitmapBuffer::RGBA(buf) => {
|
||||||
|
colored = true;
|
||||||
|
(gl::RGBA, buf)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
gl::TexSubImage2D(
|
gl::TexSubImage2D(
|
||||||
gl::TEXTURE_2D,
|
gl::TEXTURE_2D,
|
||||||
0,
|
0,
|
||||||
|
@ -1585,9 +1609,9 @@ impl Atlas {
|
||||||
offset_y,
|
offset_y,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
gl::RGB,
|
format,
|
||||||
gl::UNSIGNED_BYTE,
|
gl::UNSIGNED_BYTE,
|
||||||
glyph.buf.as_ptr() as *const _,
|
buf.as_ptr() as *const _,
|
||||||
);
|
);
|
||||||
|
|
||||||
gl::BindTexture(gl::TEXTURE_2D, 0);
|
gl::BindTexture(gl::TEXTURE_2D, 0);
|
||||||
|
@ -1608,6 +1632,7 @@ impl Atlas {
|
||||||
|
|
||||||
Glyph {
|
Glyph {
|
||||||
tex_id: self.id,
|
tex_id: self.id,
|
||||||
|
colored,
|
||||||
top: glyph.top as f32,
|
top: glyph.top as f32,
|
||||||
width: width as f32,
|
width: width as f32,
|
||||||
height: height as f32,
|
height: height as f32,
|
||||||
|
|
|
@ -23,6 +23,27 @@ pub const kCGBitmapByteOrder32Host: u32 = kCGBitmapByteOrder32Little;
|
||||||
#[cfg(target_endian = "big")]
|
#[cfg(target_endian = "big")]
|
||||||
pub const kCGBitmapByteOrder32Host: u32 = kCGBitmapByteOrder32Big;
|
pub const kCGBitmapByteOrder32Host: u32 = kCGBitmapByteOrder32Big;
|
||||||
|
|
||||||
|
#[cfg(target_endian = "little")]
|
||||||
|
pub fn extract_rgba(bytes: &[u8]) -> Vec<u8> {
|
||||||
|
let pixels = bytes.len() / 4;
|
||||||
|
let mut rgb = Vec::with_capacity(pixels * 4);
|
||||||
|
|
||||||
|
for i in 0..pixels {
|
||||||
|
let offset = i * 4;
|
||||||
|
rgb.push(bytes[offset + 2]);
|
||||||
|
rgb.push(bytes[offset + 1]);
|
||||||
|
rgb.push(bytes[offset]);
|
||||||
|
rgb.push(bytes[offset + 3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
rgb
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_endian = "big")]
|
||||||
|
pub fn extract_rgba(bytes: Vec<u8>) -> Vec<u8> {
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_endian = "little")]
|
#[cfg(target_endian = "little")]
|
||||||
pub fn extract_rgb(bytes: &[u8]) -> Vec<u8> {
|
pub fn extract_rgb(bytes: &[u8]) -> Vec<u8> {
|
||||||
let pixels = bytes.len() / 4;
|
let pixels = bytes.len() / 4;
|
||||||
|
|
|
@ -33,6 +33,7 @@ use core_text::font::{
|
||||||
};
|
};
|
||||||
use core_text::font_collection::create_for_family;
|
use core_text::font_collection::create_for_family;
|
||||||
use core_text::font_collection::get_family_names as ct_get_family_names;
|
use core_text::font_collection::get_family_names as ct_get_family_names;
|
||||||
|
use core_text::font_descriptor::kCTFontColorGlyphsTrait;
|
||||||
use core_text::font_descriptor::kCTFontDefaultOrientation;
|
use core_text::font_descriptor::kCTFontDefaultOrientation;
|
||||||
use core_text::font_descriptor::kCTFontHorizontalOrientation;
|
use core_text::font_descriptor::kCTFontHorizontalOrientation;
|
||||||
use core_text::font_descriptor::kCTFontVerticalOrientation;
|
use core_text::font_descriptor::kCTFontVerticalOrientation;
|
||||||
|
@ -41,10 +42,9 @@ use core_text::font_descriptor::{CTFontDescriptor, CTFontOrientation};
|
||||||
|
|
||||||
use euclid::{Point2D, Rect, Size2D};
|
use euclid::{Point2D, Rect, Size2D};
|
||||||
|
|
||||||
use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph};
|
use super::{BitmapBuffer, FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph};
|
||||||
|
|
||||||
pub mod byte_order;
|
pub mod byte_order;
|
||||||
use self::byte_order::extract_rgb;
|
|
||||||
use self::byte_order::kCGBitmapByteOrder32Host;
|
use self::byte_order::kCGBitmapByteOrder32Host;
|
||||||
|
|
||||||
use super::Size;
|
use super::Size;
|
||||||
|
@ -431,6 +431,10 @@ impl Font {
|
||||||
self.ct_font.symbolic_traits().is_italic()
|
self.ct_font.symbolic_traits().is_italic()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_colored(&self) -> bool {
|
||||||
|
(self.ct_font.symbolic_traits() & kCTFontColorGlyphsTrait) != 0
|
||||||
|
}
|
||||||
|
|
||||||
fn glyph_advance(&self, character: char) -> f64 {
|
fn glyph_advance(&self, character: char) -> f64 {
|
||||||
let index = self.glyph_index(character).unwrap();
|
let index = self.glyph_index(character).unwrap();
|
||||||
|
|
||||||
|
@ -471,7 +475,7 @@ impl Font {
|
||||||
height: 0,
|
height: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
buf: Vec::new(),
|
buf: BitmapBuffer::RGB(Vec::new()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,7 +524,11 @@ impl Font {
|
||||||
|
|
||||||
let rasterized_pixels = cg_context.data().to_vec();
|
let rasterized_pixels = cg_context.data().to_vec();
|
||||||
|
|
||||||
let buf = extract_rgb(&rasterized_pixels);
|
let buf = if self.is_colored() {
|
||||||
|
BitmapBuffer::RGBA(byte_order::extract_rgba(&rasterized_pixels))
|
||||||
|
} else {
|
||||||
|
BitmapBuffer::RGB(byte_order::extract_rgb(&rasterized_pixels))
|
||||||
|
};
|
||||||
|
|
||||||
Ok(RasterizedGlyph {
|
Ok(RasterizedGlyph {
|
||||||
c: character,
|
c: character,
|
||||||
|
@ -564,6 +572,8 @@ impl Font {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::BitmapBuffer;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_family_names() {
|
fn get_family_names() {
|
||||||
let names = super::get_family_names();
|
let names = super::get_family_names();
|
||||||
|
@ -585,11 +595,16 @@ mod tests {
|
||||||
for c in &['a', 'b', 'c', 'd'] {
|
for c in &['a', 'b', 'c', 'd'] {
|
||||||
let glyph = font.get_glyph(*c, 72., false).unwrap();
|
let glyph = font.get_glyph(*c, 72., false).unwrap();
|
||||||
|
|
||||||
|
let buf = match &glyph.buf {
|
||||||
|
BitmapBuffer::RGB(buf) => buf,
|
||||||
|
BitmapBuffer::RGBA(buf) => buf,
|
||||||
|
};
|
||||||
|
|
||||||
// Debug the glyph.. sigh
|
// Debug the glyph.. sigh
|
||||||
for row in 0..glyph.height {
|
for row in 0..glyph.height {
|
||||||
for col in 0..glyph.width {
|
for col in 0..glyph.width {
|
||||||
let index = ((glyph.width * 3 * row) + (col * 3)) as usize;
|
let index = ((glyph.width * 3 * row) + (col * 3)) as usize;
|
||||||
let value = glyph.buf[index];
|
let value = buf[index];
|
||||||
let c = match value {
|
let c = match value {
|
||||||
0..=50 => ' ',
|
0..=50 => ' ',
|
||||||
51..=100 => '.',
|
51..=100 => '.',
|
||||||
|
|
|
@ -18,7 +18,9 @@ use self::dwrote::{
|
||||||
FontCollection, FontStretch, FontStyle, FontWeight, GlyphOffset, GlyphRunAnalysis,
|
FontCollection, FontStretch, FontStyle, FontWeight, GlyphOffset, GlyphRunAnalysis,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight};
|
use super::{
|
||||||
|
BitmapBuffer, FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct DirectWriteRasterizer {
|
pub struct DirectWriteRasterizer {
|
||||||
fonts: Vec<dwrote::FontFace>,
|
fonts: Vec<dwrote::FontFace>,
|
||||||
|
@ -173,7 +175,7 @@ impl crate::Rasterize for DirectWriteRasterizer {
|
||||||
height: (bounds.bottom - bounds.top) as i32,
|
height: (bounds.bottom - bounds.top) as i32,
|
||||||
top: -bounds.top,
|
top: -bounds.top,
|
||||||
left: bounds.left,
|
left: bounds.left,
|
||||||
buf,
|
buf: BitmapBuffer::RGB(buf),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -420,6 +420,7 @@ impl PatternRef {
|
||||||
size() => b"size\0",
|
size() => b"size\0",
|
||||||
aspect() => b"aspect\0",
|
aspect() => b"aspect\0",
|
||||||
pixelsize() => b"pixelsize\0",
|
pixelsize() => b"pixelsize\0",
|
||||||
|
pixelsizefixupfactor() => b"pixelsizefixupfactor\0",
|
||||||
scale() => b"scale\0",
|
scale() => b"scale\0",
|
||||||
dpi() => b"dpi\0"
|
dpi() => b"dpi\0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,18 +13,22 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
//! Rasterization powered by FreeType and FontConfig
|
//! Rasterization powered by FreeType and FontConfig
|
||||||
use std::cmp::min;
|
use std::cmp::{min, Ordering};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use freetype::freetype_sys;
|
||||||
use freetype::tt_os2::TrueTypeOS2Table;
|
use freetype::tt_os2::TrueTypeOS2Table;
|
||||||
use freetype::{self, Library};
|
use freetype::{self, Library};
|
||||||
use libc::c_uint;
|
use libc::c_uint;
|
||||||
|
|
||||||
pub mod fc;
|
pub mod fc;
|
||||||
|
|
||||||
use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight};
|
use super::{
|
||||||
|
BitmapBuffer, FontDesc, FontKey, GlyphKey, Metrics, Rasterize, RasterizedGlyph, Size, Slant,
|
||||||
|
Style, Weight,
|
||||||
|
};
|
||||||
|
|
||||||
struct FixedSize {
|
struct FixedSize {
|
||||||
pixelsize: f64,
|
pixelsize: f64,
|
||||||
|
@ -37,6 +41,8 @@ struct Face {
|
||||||
render_mode: freetype::RenderMode,
|
render_mode: freetype::RenderMode,
|
||||||
lcd_filter: c_uint,
|
lcd_filter: c_uint,
|
||||||
non_scalable: Option<FixedSize>,
|
non_scalable: Option<FixedSize>,
|
||||||
|
has_color: bool,
|
||||||
|
pixelsize_fixup_factor: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Face {
|
impl fmt::Debug for Face {
|
||||||
|
@ -64,6 +70,7 @@ pub struct FreeTypeRasterizer {
|
||||||
library: Library,
|
library: Library,
|
||||||
keys: HashMap<PathBuf, FontKey>,
|
keys: HashMap<PathBuf, FontKey>,
|
||||||
device_pixel_ratio: f32,
|
device_pixel_ratio: f32,
|
||||||
|
pixel_size: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -71,7 +78,7 @@ fn to_freetype_26_6(f: f32) -> isize {
|
||||||
((1i32 << 6) as f32 * f) as isize
|
((1i32 << 6) as f32 * f) as isize
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::Rasterize for FreeTypeRasterizer {
|
impl Rasterize for FreeTypeRasterizer {
|
||||||
type Err = Error;
|
type Err = Error;
|
||||||
|
|
||||||
fn new(device_pixel_ratio: f32, _: bool) -> Result<FreeTypeRasterizer, Error> {
|
fn new(device_pixel_ratio: f32, _: bool) -> Result<FreeTypeRasterizer, Error> {
|
||||||
|
@ -82,6 +89,7 @@ impl ::Rasterize for FreeTypeRasterizer {
|
||||||
keys: HashMap::new(),
|
keys: HashMap::new(),
|
||||||
library,
|
library,
|
||||||
device_pixel_ratio,
|
device_pixel_ratio,
|
||||||
|
pixel_size: 0.0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,15 +189,16 @@ impl FreeTypeRasterizer {
|
||||||
fn get_face(&mut self, desc: &FontDesc, size: Size) -> Result<FontKey, Error> {
|
fn get_face(&mut self, desc: &FontDesc, size: Size) -> Result<FontKey, Error> {
|
||||||
// Adjust for DPI
|
// Adjust for DPI
|
||||||
let size = Size::new(size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.);
|
let size = Size::new(size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.);
|
||||||
|
self.pixel_size = f64::from(size.as_f32_pts());
|
||||||
|
|
||||||
match desc.style {
|
match desc.style {
|
||||||
Style::Description { slant, weight } => {
|
Style::Description { slant, weight } => {
|
||||||
// Match nearest font
|
// Match nearest font
|
||||||
self.get_matching_face(&desc, slant, weight, size)
|
self.get_matching_face(&desc, slant, weight)
|
||||||
},
|
},
|
||||||
Style::Specific(ref style) => {
|
Style::Specific(ref style) => {
|
||||||
// If a name was specified, try and load specifically that font.
|
// If a name was specified, try and load specifically that font.
|
||||||
self.get_specific_face(&desc, &style, size)
|
self.get_specific_face(&desc, &style)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,13 +221,12 @@ impl FreeTypeRasterizer {
|
||||||
desc: &FontDesc,
|
desc: &FontDesc,
|
||||||
slant: Slant,
|
slant: Slant,
|
||||||
weight: Weight,
|
weight: Weight,
|
||||||
size: Size,
|
|
||||||
) -> Result<FontKey, Error> {
|
) -> Result<FontKey, Error> {
|
||||||
let mut pattern = fc::Pattern::new();
|
let mut pattern = fc::Pattern::new();
|
||||||
pattern.add_family(&desc.name);
|
pattern.add_family(&desc.name);
|
||||||
pattern.set_weight(weight.into_fontconfig_type());
|
pattern.set_weight(weight.into_fontconfig_type());
|
||||||
pattern.set_slant(slant.into_fontconfig_type());
|
pattern.set_slant(slant.into_fontconfig_type());
|
||||||
pattern.add_pixelsize(f64::from(size.as_f32_pts()));
|
pattern.add_pixelsize(self.pixel_size);
|
||||||
|
|
||||||
let font = fc::font_match(fc::Config::get_current(), &mut pattern)
|
let font = fc::font_match(fc::Config::get_current(), &mut pattern)
|
||||||
.ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
|
.ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
|
||||||
|
@ -228,16 +236,11 @@ impl FreeTypeRasterizer {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_specific_face(
|
fn get_specific_face(&mut self, desc: &FontDesc, style: &str) -> Result<FontKey, Error> {
|
||||||
&mut self,
|
|
||||||
desc: &FontDesc,
|
|
||||||
style: &str,
|
|
||||||
size: Size,
|
|
||||||
) -> Result<FontKey, Error> {
|
|
||||||
let mut pattern = fc::Pattern::new();
|
let mut pattern = fc::Pattern::new();
|
||||||
pattern.add_family(&desc.name);
|
pattern.add_family(&desc.name);
|
||||||
pattern.add_style(style);
|
pattern.add_style(style);
|
||||||
pattern.add_pixelsize(f64::from(size.as_f32_pts()));
|
pattern.add_pixelsize(self.pixel_size);
|
||||||
|
|
||||||
let font = fc::font_match(fc::Config::get_current(), &mut pattern)
|
let font = fc::font_match(fc::Config::get_current(), &mut pattern)
|
||||||
.ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
|
.ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
|
||||||
|
@ -253,7 +256,7 @@ impl FreeTypeRasterizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!("Got font path={:?}", path);
|
trace!("Got font path={:?}", path);
|
||||||
let ft_face = self.library.new_face(&path, index)?;
|
let mut ft_face = self.library.new_face(&path, index)?;
|
||||||
|
|
||||||
// Get available pixel sizes if font isn't scalable.
|
// Get available pixel sizes if font isn't scalable.
|
||||||
let non_scalable = if pattern.scalable().next().unwrap_or(true) {
|
let non_scalable = if pattern.scalable().next().unwrap_or(true) {
|
||||||
|
@ -265,6 +268,16 @@ impl FreeTypeRasterizer {
|
||||||
Some(FixedSize { pixelsize: pixelsize.next().expect("has 1+ pixelsize") })
|
Some(FixedSize { pixelsize: pixelsize.next().expect("has 1+ pixelsize") })
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let pixelsize_fixup_factor = pattern.pixelsizefixupfactor().next();
|
||||||
|
|
||||||
|
let has_color = ft_face.has_color();
|
||||||
|
if has_color {
|
||||||
|
unsafe {
|
||||||
|
// Select the colored bitmap size to use from the array of available sizes
|
||||||
|
freetype_sys::FT_Select_Size(ft_face.raw_mut(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let face = Face {
|
let face = Face {
|
||||||
ft_face,
|
ft_face,
|
||||||
key: FontKey::next(),
|
key: FontKey::next(),
|
||||||
|
@ -272,6 +285,8 @@ impl FreeTypeRasterizer {
|
||||||
render_mode: Self::ft_render_mode(pattern),
|
render_mode: Self::ft_render_mode(pattern),
|
||||||
lcd_filter: Self::ft_lcd_filter(pattern),
|
lcd_filter: Self::ft_lcd_filter(pattern),
|
||||||
non_scalable,
|
non_scalable,
|
||||||
|
has_color,
|
||||||
|
pixelsize_fixup_factor,
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("Loaded Face {:?}", face);
|
debug!("Loaded Face {:?}", face);
|
||||||
|
@ -320,7 +335,9 @@ impl FreeTypeRasterizer {
|
||||||
glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.
|
glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.
|
||||||
});
|
});
|
||||||
|
|
||||||
face.ft_face.set_char_size(to_freetype_26_6(size), 0, 0, 0)?;
|
if !face.has_color {
|
||||||
|
face.ft_face.set_char_size(to_freetype_26_6(size), 0, 0, 0)?;
|
||||||
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let ft_lib = self.library.raw();
|
let ft_lib = self.library.raw();
|
||||||
|
@ -328,19 +345,33 @@ impl FreeTypeRasterizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
face.ft_face.load_glyph(index as u32, face.load_flags)?;
|
face.ft_face.load_glyph(index as u32, face.load_flags)?;
|
||||||
|
|
||||||
let glyph = face.ft_face.glyph();
|
let glyph = face.ft_face.glyph();
|
||||||
glyph.render_glyph(face.render_mode)?;
|
glyph.render_glyph(face.render_mode)?;
|
||||||
|
|
||||||
let (pixel_height, pixel_width, buf) = Self::normalize_buffer(&glyph.bitmap())?;
|
let (pixel_height, pixel_width, buf) = Self::normalize_buffer(&glyph.bitmap())?;
|
||||||
|
|
||||||
Ok(RasterizedGlyph {
|
let rasterized_glyph = RasterizedGlyph {
|
||||||
c: glyph_key.c,
|
c: glyph_key.c,
|
||||||
top: glyph.bitmap_top(),
|
top: glyph.bitmap_top(),
|
||||||
left: glyph.bitmap_left(),
|
left: glyph.bitmap_left(),
|
||||||
width: pixel_width,
|
width: pixel_width,
|
||||||
height: pixel_height,
|
height: pixel_height,
|
||||||
buf,
|
buf,
|
||||||
})
|
};
|
||||||
|
|
||||||
|
if face.has_color {
|
||||||
|
let fixup_factor = if let Some(pixelsize_fixup_factor) = face.pixelsize_fixup_factor {
|
||||||
|
pixelsize_fixup_factor
|
||||||
|
} else {
|
||||||
|
// Fallback if user has bitmap scaling disabled
|
||||||
|
let metrics = face.ft_face.size_metrics().ok_or(Error::MissingSizeMetrics)?;
|
||||||
|
self.pixel_size as f64 / metrics.y_ppem as f64
|
||||||
|
};
|
||||||
|
Ok(downsample_bitmap(rasterized_glyph, fixup_factor))
|
||||||
|
} else {
|
||||||
|
Ok(rasterized_glyph)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ft_load_flags(pat: &fc::Pattern) -> freetype::face::LoadFlag {
|
fn ft_load_flags(pat: &fc::Pattern) -> freetype::face::LoadFlag {
|
||||||
|
@ -348,6 +379,7 @@ impl FreeTypeRasterizer {
|
||||||
let hinting = pat.hintstyle().next().unwrap_or(fc::HintStyle::Slight);
|
let hinting = pat.hintstyle().next().unwrap_or(fc::HintStyle::Slight);
|
||||||
let rgba = pat.rgba().next().unwrap_or(fc::Rgba::Unknown);
|
let rgba = pat.rgba().next().unwrap_or(fc::Rgba::Unknown);
|
||||||
let embedded_bitmaps = pat.embeddedbitmap().next().unwrap_or(true);
|
let embedded_bitmaps = pat.embeddedbitmap().next().unwrap_or(true);
|
||||||
|
let color = pat.color().next().unwrap_or(false);
|
||||||
|
|
||||||
use freetype::face::LoadFlag;
|
use freetype::face::LoadFlag;
|
||||||
let mut flags = match (antialias, hinting, rgba) {
|
let mut flags = match (antialias, hinting, rgba) {
|
||||||
|
@ -385,6 +417,10 @@ impl FreeTypeRasterizer {
|
||||||
flags |= LoadFlag::NO_BITMAP;
|
flags |= LoadFlag::NO_BITMAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if color {
|
||||||
|
flags |= LoadFlag::COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
flags
|
flags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,7 +450,7 @@ impl FreeTypeRasterizer {
|
||||||
/// The i32 value in the return type is the number of pixels per row.
|
/// The i32 value in the return type is the number of pixels per row.
|
||||||
fn normalize_buffer(
|
fn normalize_buffer(
|
||||||
bitmap: &freetype::bitmap::Bitmap,
|
bitmap: &freetype::bitmap::Bitmap,
|
||||||
) -> freetype::FtResult<(i32, i32, Vec<u8>)> {
|
) -> freetype::FtResult<(i32, i32, BitmapBuffer)> {
|
||||||
use freetype::bitmap::PixelMode;
|
use freetype::bitmap::PixelMode;
|
||||||
|
|
||||||
let buf = bitmap.buffer();
|
let buf = bitmap.buffer();
|
||||||
|
@ -427,7 +463,7 @@ impl FreeTypeRasterizer {
|
||||||
let stop = start + bitmap.width() as usize;
|
let stop = start + bitmap.width() as usize;
|
||||||
packed.extend_from_slice(&buf[start..stop]);
|
packed.extend_from_slice(&buf[start..stop]);
|
||||||
}
|
}
|
||||||
Ok((bitmap.rows(), bitmap.width() / 3, packed))
|
Ok((bitmap.rows(), bitmap.width() / 3, BitmapBuffer::RGB(packed)))
|
||||||
},
|
},
|
||||||
PixelMode::LcdV => {
|
PixelMode::LcdV => {
|
||||||
for i in 0..bitmap.rows() / 3 {
|
for i in 0..bitmap.rows() / 3 {
|
||||||
|
@ -438,7 +474,7 @@ impl FreeTypeRasterizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok((bitmap.rows() / 3, bitmap.width(), packed))
|
Ok((bitmap.rows() / 3, bitmap.width(), BitmapBuffer::RGB(packed)))
|
||||||
},
|
},
|
||||||
// Mono data is stored in a packed format using 1 bit per pixel.
|
// Mono data is stored in a packed format using 1 bit per pixel.
|
||||||
PixelMode::Mono => {
|
PixelMode::Mono => {
|
||||||
|
@ -469,7 +505,7 @@ impl FreeTypeRasterizer {
|
||||||
byte += 1;
|
byte += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok((bitmap.rows(), bitmap.width(), packed))
|
Ok((bitmap.rows(), bitmap.width(), BitmapBuffer::RGB(packed)))
|
||||||
},
|
},
|
||||||
// Gray data is stored as a value between 0 and 255 using 1 byte per pixel.
|
// Gray data is stored as a value between 0 and 255 using 1 byte per pixel.
|
||||||
PixelMode::Gray => {
|
PixelMode::Gray => {
|
||||||
|
@ -482,7 +518,19 @@ impl FreeTypeRasterizer {
|
||||||
packed.push(*byte);
|
packed.push(*byte);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok((bitmap.rows(), bitmap.width(), packed))
|
Ok((bitmap.rows(), bitmap.width(), BitmapBuffer::RGB(packed)))
|
||||||
|
},
|
||||||
|
PixelMode::Bgra => {
|
||||||
|
let buf_size = (bitmap.rows() * bitmap.width() * 4) as usize;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < buf_size {
|
||||||
|
packed.push(buf[i + 2]);
|
||||||
|
packed.push(buf[i + 1]);
|
||||||
|
packed.push(buf[i]);
|
||||||
|
packed.push(buf[i + 3]);
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
Ok((bitmap.rows(), bitmap.width(), BitmapBuffer::RGBA(packed)))
|
||||||
},
|
},
|
||||||
mode => panic!("unhandled pixel mode: {:?}", mode),
|
mode => panic!("unhandled pixel mode: {:?}", mode),
|
||||||
}
|
}
|
||||||
|
@ -493,6 +541,7 @@ impl FreeTypeRasterizer {
|
||||||
charset.add(glyph);
|
charset.add(glyph);
|
||||||
let mut pattern = fc::Pattern::new();
|
let mut pattern = fc::Pattern::new();
|
||||||
pattern.add_charset(&charset);
|
pattern.add_charset(&charset);
|
||||||
|
pattern.add_pixelsize(self.pixel_size as f64);
|
||||||
|
|
||||||
let config = fc::Config::get_current();
|
let config = fc::Config::get_current();
|
||||||
match fc::font_match(config, &mut pattern) {
|
match fc::font_match(config, &mut pattern) {
|
||||||
|
@ -503,6 +552,9 @@ impl FreeTypeRasterizer {
|
||||||
// load it again.
|
// load it again.
|
||||||
Some(&key) => {
|
Some(&key) => {
|
||||||
debug!("Hit for font {:?}; no need to load", path);
|
debug!("Hit for font {:?}; no need to load", path);
|
||||||
|
// Update fixup factor
|
||||||
|
self.faces.get_mut(&key).unwrap().pixelsize_fixup_factor =
|
||||||
|
pattern.pixelsizefixupfactor().next();
|
||||||
Ok(key)
|
Ok(key)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -529,6 +581,76 @@ impl FreeTypeRasterizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Downscale a bitmap by a fixed factor.
|
||||||
|
///
|
||||||
|
/// This will take the `bitmap_glyph` as input and return the glyph's content downscaled by
|
||||||
|
/// `fixup_factor`.
|
||||||
|
fn downsample_bitmap(mut bitmap_glyph: RasterizedGlyph, fixup_factor: f64) -> RasterizedGlyph {
|
||||||
|
// Only scale colored buffers which are bigger than required
|
||||||
|
let bitmap_buffer = match (&bitmap_glyph.buf, fixup_factor.partial_cmp(&1.0)) {
|
||||||
|
(BitmapBuffer::RGBA(buffer), Some(Ordering::Less)) => buffer,
|
||||||
|
_ => return bitmap_glyph,
|
||||||
|
};
|
||||||
|
|
||||||
|
let bitmap_width = bitmap_glyph.width as usize;
|
||||||
|
let bitmap_height = bitmap_glyph.height as usize;
|
||||||
|
|
||||||
|
let target_width = (bitmap_width as f64 * fixup_factor) as usize;
|
||||||
|
let target_height = (bitmap_height as f64 * fixup_factor) as usize;
|
||||||
|
|
||||||
|
// Number of pixels in the input buffer, per pixel in the output buffer
|
||||||
|
let downsampling_step = 1.0 / fixup_factor;
|
||||||
|
|
||||||
|
let mut downsampled_buffer = Vec::<u8>::with_capacity(target_width * target_height * 4);
|
||||||
|
|
||||||
|
for line_index in 0..target_height {
|
||||||
|
// Get the first and last line which will be consolidated in the current output pixel
|
||||||
|
let line_index = line_index as f64;
|
||||||
|
let source_line_start = (line_index * downsampling_step).round() as usize;
|
||||||
|
let source_line_end = ((line_index + 1.) * downsampling_step).round() as usize;
|
||||||
|
|
||||||
|
for column_index in 0..target_width {
|
||||||
|
// Get the first and last column which will be consolidated in the current output pixel
|
||||||
|
let column_index = column_index as f64;
|
||||||
|
let source_column_start = (column_index * downsampling_step).round() as usize;
|
||||||
|
let source_column_end = ((column_index + 1.) * downsampling_step).round() as usize;
|
||||||
|
|
||||||
|
let (mut r, mut g, mut b, mut a) = (0u32, 0u32, 0u32, 0u32);
|
||||||
|
let mut pixels_picked: u32 = 0;
|
||||||
|
|
||||||
|
// Consolidate all pixels within the source rectangle into a single averaged pixel
|
||||||
|
for source_line in source_line_start..source_line_end {
|
||||||
|
let source_pixel_index = source_line * bitmap_width;
|
||||||
|
|
||||||
|
for source_column in source_column_start..source_column_end {
|
||||||
|
let offset = (source_pixel_index + source_column) * 4;
|
||||||
|
r += bitmap_buffer[offset] as u32;
|
||||||
|
g += bitmap_buffer[offset + 1] as u32;
|
||||||
|
b += bitmap_buffer[offset + 2] as u32;
|
||||||
|
a += bitmap_buffer[offset + 3] as u32;
|
||||||
|
pixels_picked += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a single pixel to the output buffer for the downscaled source rectangle
|
||||||
|
downsampled_buffer.push((r / pixels_picked) as u8);
|
||||||
|
downsampled_buffer.push((g / pixels_picked) as u8);
|
||||||
|
downsampled_buffer.push((b / pixels_picked) as u8);
|
||||||
|
downsampled_buffer.push((a / pixels_picked) as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmap_glyph.buf = BitmapBuffer::RGBA(downsampled_buffer);
|
||||||
|
|
||||||
|
// Downscale the metrics
|
||||||
|
bitmap_glyph.top = (bitmap_glyph.top as f64 * fixup_factor) as i32;
|
||||||
|
bitmap_glyph.left = (bitmap_glyph.left as f64 * fixup_factor) as i32;
|
||||||
|
bitmap_glyph.width = target_width as i32;
|
||||||
|
bitmap_glyph.height = target_height as i32;
|
||||||
|
|
||||||
|
bitmap_glyph
|
||||||
|
}
|
||||||
|
|
||||||
/// Errors occurring when using the freetype rasterizer
|
/// Errors occurring when using the freetype rasterizer
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -545,7 +667,7 @@ pub enum Error {
|
||||||
FontNotLoaded,
|
FontNotLoaded,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::error::Error for Error {
|
impl std::error::Error for Error {
|
||||||
fn cause(&self) -> Option<&dyn std::error::Error> {
|
fn cause(&self) -> Option<&dyn std::error::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
Error::FreeType(ref err) => Some(err),
|
Error::FreeType(ref err) => Some(err),
|
||||||
|
@ -563,7 +685,7 @@ impl ::std::error::Error for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::fmt::Display for Error {
|
impl std::fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Error::FreeType(ref err) => err.fmt(f),
|
Error::FreeType(ref err) => err.fmt(f),
|
||||||
|
|
|
@ -220,20 +220,25 @@ pub struct RasterizedGlyph {
|
||||||
pub height: i32,
|
pub height: i32,
|
||||||
pub top: i32,
|
pub top: i32,
|
||||||
pub left: i32,
|
pub left: i32,
|
||||||
pub buf: Vec<u8>,
|
pub buf: BitmapBuffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum BitmapBuffer {
|
||||||
|
RGB(Vec<u8>),
|
||||||
|
RGBA(Vec<u8>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RasterizedGlyph {
|
impl Default for RasterizedGlyph {
|
||||||
fn default() -> RasterizedGlyph {
|
fn default() -> RasterizedGlyph {
|
||||||
RasterizedGlyph { c: ' ', width: 0, height: 0, top: 0, left: 0, buf: Vec::new() }
|
RasterizedGlyph {
|
||||||
}
|
c: ' ',
|
||||||
}
|
width: 0,
|
||||||
|
height: 0,
|
||||||
struct BufDebugger<'a>(&'a [u8]);
|
top: 0,
|
||||||
|
left: 0,
|
||||||
impl<'a> fmt::Debug for BufDebugger<'a> {
|
buf: BitmapBuffer::RGB(Vec::new()),
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
}
|
||||||
f.debug_struct("GlyphBuffer").field("len", &self.0.len()).field("bytes", &self.0).finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,7 +250,7 @@ impl fmt::Debug for RasterizedGlyph {
|
||||||
.field("height", &self.height)
|
.field("height", &self.height)
|
||||||
.field("top", &self.top)
|
.field("top", &self.top)
|
||||||
.field("left", &self.left)
|
.field("left", &self.left)
|
||||||
.field("buf", &BufDebugger(&self.buf[..]))
|
.field("buf", &self.buf)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue