Add FreeType face cache
This commit is contained in:
parent
14dc170caa
commit
84f57ac836
|
@ -113,7 +113,7 @@ impl crate::Rasterize for DirectWriteRasterizer {
|
||||||
let face = font.create_font_face();
|
let face = font.create_font_face();
|
||||||
self.fonts.push(face);
|
self.fonts.push(face);
|
||||||
|
|
||||||
Ok(FontKey { token: (self.fonts.len() - 1) as u16 })
|
Ok(FontKey { token: (self.fonts.len() - 1) as u32 })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_glyph(&mut self, glyph: GlyphKey) -> Result<RasterizedGlyph, Error> {
|
fn get_glyph(&mut self, glyph: GlyphKey) -> Result<RasterizedGlyph, Error> {
|
||||||
|
|
|
@ -16,10 +16,12 @@
|
||||||
use std::cmp::{min, Ordering};
|
use std::cmp::{min, Ordering};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use freetype::freetype_sys;
|
|
||||||
use freetype::tt_os2::TrueTypeOS2Table;
|
use freetype::tt_os2::TrueTypeOS2Table;
|
||||||
use freetype::{self, Library};
|
use freetype::{self, Library};
|
||||||
|
use freetype::{freetype_sys, Face as FTFace};
|
||||||
use libc::c_uint;
|
use libc::c_uint;
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
|
|
||||||
|
@ -32,28 +34,21 @@ use super::{
|
||||||
Style, Weight,
|
Style, Weight,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FixedSize {
|
|
||||||
pixelsize: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FallbackFont {
|
struct FallbackFont {
|
||||||
pattern: Pattern,
|
pattern: Pattern,
|
||||||
id: FontID,
|
key: FontKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FallbackFont {
|
impl FallbackFont {
|
||||||
fn new(pattern: Pattern, id: FontID) -> FallbackFont {
|
fn new(pattern: Pattern, key: FontKey) -> FallbackFont {
|
||||||
Self { pattern, id }
|
Self { pattern, key }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
|
impl FontKey {
|
||||||
struct FontID(u32);
|
fn from_pattern_hashes(lhs: PatternHash, rhs: PatternHash) -> Self {
|
||||||
|
|
||||||
impl FontID {
|
|
||||||
fn new(lhs: PatternHash, rhs: PatternHash) -> Self {
|
|
||||||
// XOR two hashes to get a font ID
|
// XOR two hashes to get a font ID
|
||||||
Self(lhs.0.rotate_left(1) ^ rhs.0)
|
Self { token: lhs.0.rotate_left(1) ^ rhs.0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,22 +58,20 @@ struct FallbackList {
|
||||||
coverage: CharSet,
|
coverage: CharSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Face {
|
struct FaceLoadingProperties {
|
||||||
ft_face: freetype::Face,
|
|
||||||
key: FontKey,
|
|
||||||
load_flags: freetype::face::LoadFlag,
|
load_flags: freetype::face::LoadFlag,
|
||||||
render_mode: freetype::RenderMode,
|
render_mode: freetype::RenderMode,
|
||||||
lcd_filter: c_uint,
|
lcd_filter: c_uint,
|
||||||
non_scalable: Option<FixedSize>,
|
pixelsize: f64,
|
||||||
has_color: bool,
|
colored: bool,
|
||||||
pixelsize_fixup_factor: Option<f64>,
|
pixelsize_fixup_factor: Option<f64>,
|
||||||
|
ft_face: Rc<FTFace>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Face {
|
impl fmt::Debug for FaceLoadingProperties {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
f.debug_struct("Face")
|
f.debug_struct("Face")
|
||||||
.field("ft_face", &self.ft_face)
|
.field("ft_face", &self.ft_face)
|
||||||
.field("key", &self.key)
|
|
||||||
.field("load_flags", &self.load_flags)
|
.field("load_flags", &self.load_flags)
|
||||||
.field("render_mode", &match self.render_mode {
|
.field("render_mode", &match self.render_mode {
|
||||||
freetype::RenderMode::Normal => "Normal",
|
freetype::RenderMode::Normal => "Normal",
|
||||||
|
@ -95,9 +88,9 @@ impl fmt::Debug for Face {
|
||||||
|
|
||||||
/// Rasterizes glyphs for a single font face.
|
/// Rasterizes glyphs for a single font face.
|
||||||
pub struct FreeTypeRasterizer {
|
pub struct FreeTypeRasterizer {
|
||||||
faces: HashMap<FontKey, Face>,
|
|
||||||
library: Library,
|
library: Library,
|
||||||
keys: HashMap<FontID, FontKey>,
|
faces: HashMap<FontKey, FaceLoadingProperties>,
|
||||||
|
ft_faces: HashMap<PathBuf, Rc<FTFace>>,
|
||||||
fallback_lists: HashMap<FontKey, FallbackList>,
|
fallback_lists: HashMap<FontKey, FallbackList>,
|
||||||
device_pixel_ratio: f32,
|
device_pixel_ratio: f32,
|
||||||
}
|
}
|
||||||
|
@ -115,7 +108,7 @@ impl Rasterize for FreeTypeRasterizer {
|
||||||
|
|
||||||
Ok(FreeTypeRasterizer {
|
Ok(FreeTypeRasterizer {
|
||||||
faces: HashMap::new(),
|
faces: HashMap::new(),
|
||||||
keys: HashMap::new(),
|
ft_faces: HashMap::new(),
|
||||||
fallback_lists: HashMap::new(),
|
fallback_lists: HashMap::new(),
|
||||||
library,
|
library,
|
||||||
device_pixel_ratio,
|
device_pixel_ratio,
|
||||||
|
@ -123,8 +116,8 @@ impl Rasterize for FreeTypeRasterizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn metrics(&self, key: FontKey, _size: Size) -> Result<Metrics, Error> {
|
fn metrics(&self, key: FontKey, _size: Size) -> Result<Metrics, Error> {
|
||||||
let face = self.faces.get(&key).ok_or(Error::FontNotLoaded)?;
|
let face = &mut self.faces.get(&key).ok_or(Error::FontNotLoaded)?;
|
||||||
let full = self.full_metrics(key)?;
|
let full = self.full_metrics(&face)?;
|
||||||
|
|
||||||
let height = (full.size_metrics.height / 64) as f64;
|
let height = (full.size_metrics.height / 64) as f64;
|
||||||
let descent = (full.size_metrics.descender / 64) as f32;
|
let descent = (full.size_metrics.descender / 64) as f32;
|
||||||
|
@ -142,7 +135,7 @@ impl Rasterize for FreeTypeRasterizer {
|
||||||
|
|
||||||
// Get strikeout position and thickness in device pixels
|
// Get strikeout position and thickness in device pixels
|
||||||
let (strikeout_position, strikeout_thickness) =
|
let (strikeout_position, strikeout_thickness) =
|
||||||
match TrueTypeOS2Table::from_face(&mut face.ft_face.clone()) {
|
match TrueTypeOS2Table::from_face(&mut (*face.ft_face).clone()) {
|
||||||
Some(os2) => {
|
Some(os2) => {
|
||||||
let strikeout_position = f32::from(os2.y_strikeout_position()) * x_scale / 64.;
|
let strikeout_position = f32::from(os2.y_strikeout_position()) * x_scale / 64.;
|
||||||
let strikeout_thickness = f32::from(os2.y_strikeout_size()) * x_scale / 64.;
|
let strikeout_thickness = f32::from(os2.y_strikeout_size()) * x_scale / 64.;
|
||||||
|
@ -253,123 +246,107 @@ impl FreeTypeRasterizer {
|
||||||
let primary_font = pattern.render_prepare(config, primary_font);
|
let primary_font = pattern.render_prepare(config, primary_font);
|
||||||
|
|
||||||
// Hash pattern together with request pattern to include requested font size in the hash
|
// Hash pattern together with request pattern to include requested font size in the hash
|
||||||
let primary_font_id = FontID::new(hash, primary_font.hash());
|
let primary_font_key = FontKey::from_pattern_hashes(hash, primary_font.hash());
|
||||||
|
|
||||||
// Reload already loaded faces and drop their fallback faces
|
// Return if we already have the same primary font
|
||||||
let font_key = if let Some(font_key) = self.keys.remove(&primary_font_id) {
|
if self.fallback_lists.contains_key(&primary_font_key) {
|
||||||
let fallback_list = self.fallback_lists.remove(&font_key).unwrap_or_default();
|
return Ok(primary_font_key);
|
||||||
|
|
||||||
for fallback_font in &fallback_list.list {
|
|
||||||
if let Some(ff_key) = self.keys.get(&fallback_font.id) {
|
|
||||||
// Skip primary fonts, since these are all reloaded later
|
|
||||||
if !self.fallback_lists.contains_key(&ff_key) {
|
|
||||||
self.faces.remove(ff_key);
|
|
||||||
self.keys.remove(&fallback_font.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = self.faces.remove(&font_key);
|
// Load font if we haven't loaded it yet
|
||||||
Some(font_key)
|
if !self.faces.contains_key(&primary_font_key) {
|
||||||
} else {
|
self.face_from_pattern(&primary_font, primary_font_key)
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reuse the font_key, since changing it can break library users
|
|
||||||
let font_key = self
|
|
||||||
.face_from_pattern(&primary_font, primary_font_id, font_key)
|
|
||||||
.and_then(|pattern| pattern.ok_or_else(|| Error::MissingFont(desc.to_owned())))?;
|
.and_then(|pattern| pattern.ok_or_else(|| Error::MissingFont(desc.to_owned())))?;
|
||||||
|
}
|
||||||
|
|
||||||
// Coverage for fallback fonts
|
// Coverage for fallback fonts
|
||||||
let coverage = CharSet::new();
|
let coverage = CharSet::new();
|
||||||
let empty_charset = CharSet::new();
|
let empty_charset = CharSet::new();
|
||||||
|
|
||||||
// Build fallback list
|
|
||||||
let list: Vec<FallbackFont> = matched_fonts
|
let list: Vec<FallbackFont> = matched_fonts
|
||||||
.map(|fallback_font| {
|
.map(|fallback_font| {
|
||||||
let charset = fallback_font.get_charset().unwrap_or(&empty_charset);
|
let charset = fallback_font.get_charset().unwrap_or(&empty_charset);
|
||||||
|
|
||||||
// Use original pattern to preserve loading flags
|
// Use original pattern to preserve loading flags
|
||||||
let fallback_font = pattern.render_prepare(config, fallback_font);
|
let fallback_font = pattern.render_prepare(config, fallback_font);
|
||||||
let fallback_font_id = FontID::new(hash, fallback_font.hash());
|
let fallback_font_key = FontKey::from_pattern_hashes(hash, fallback_font.hash());
|
||||||
|
|
||||||
let _ = coverage.merge(&charset);
|
let _ = coverage.merge(&charset);
|
||||||
|
|
||||||
FallbackFont::new(fallback_font, fallback_font_id)
|
FallbackFont::new(fallback_font, fallback_font_key)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
self.fallback_lists.insert(font_key, FallbackList { list, coverage });
|
self.fallback_lists.insert(primary_font_key, FallbackList { list, coverage });
|
||||||
|
|
||||||
Ok(font_key)
|
Ok(primary_font_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn full_metrics(&self, key: FontKey) -> Result<FullMetrics, Error> {
|
fn full_metrics(&self, face_load_props: &FaceLoadingProperties) -> Result<FullMetrics, Error> {
|
||||||
let face = self.faces.get(&key).ok_or(Error::FontNotLoaded)?;
|
let ft_face = &face_load_props.ft_face;
|
||||||
|
let size_metrics = ft_face.size_metrics().ok_or(Error::MissingSizeMetrics)?;
|
||||||
|
|
||||||
let size_metrics = face.ft_face.size_metrics().ok_or(Error::MissingSizeMetrics)?;
|
let width = match ft_face.load_char('0' as usize, face_load_props.load_flags) {
|
||||||
|
Ok(_) => ft_face.glyph().metrics().horiAdvance / 64,
|
||||||
let width = match face.ft_face.load_char('0' as usize, face.load_flags) {
|
|
||||||
Ok(_) => face.ft_face.glyph().metrics().horiAdvance / 64,
|
|
||||||
Err(_) => size_metrics.max_advance / 64,
|
Err(_) => size_metrics.max_advance / 64,
|
||||||
} as f64;
|
} as f64;
|
||||||
|
|
||||||
Ok(FullMetrics { size_metrics, cell_width: width })
|
Ok(FullMetrics { size_metrics, cell_width: width })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn face_from_pattern(
|
fn load_ft_face(&mut self, path: PathBuf, index: isize) -> Result<Rc<FTFace>, Error> {
|
||||||
&mut self,
|
|
||||||
pattern: &PatternRef,
|
|
||||||
font_id: FontID,
|
|
||||||
key: Option<FontKey>,
|
|
||||||
) -> Result<Option<FontKey>, Error> {
|
|
||||||
if let (Some(path), Some(index)) = (pattern.file(0), pattern.index().next()) {
|
|
||||||
if let Some(key) = self.keys.get(&font_id) {
|
|
||||||
return Ok(Some(*key));
|
|
||||||
}
|
|
||||||
|
|
||||||
trace!("Got font path={:?}", path);
|
|
||||||
let mut ft_face = self.library.new_face(&path, index)?;
|
let mut ft_face = self.library.new_face(&path, index)?;
|
||||||
|
if ft_face.has_color() {
|
||||||
// Get available pixel sizes if font isn't scalable.
|
|
||||||
let non_scalable = if pattern.scalable().next().unwrap_or(true) {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let mut pixelsize = pattern.pixelsize();
|
|
||||||
debug!("pixelsizes: {:?}", 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 {
|
unsafe {
|
||||||
// Select the colored bitmap size to use from the array of available sizes
|
// Select the colored bitmap size to use from the array of available sizes
|
||||||
freetype_sys::FT_Select_Size(ft_face.raw_mut(), 0);
|
freetype_sys::FT_Select_Size(ft_face.raw_mut(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reuse the original fontkey if you're reloading the font
|
let ft_face = Rc::new(ft_face);
|
||||||
let key = if let Some(key) = key { key } else { FontKey::next() };
|
self.ft_faces.insert(path, Rc::clone(&ft_face));
|
||||||
|
|
||||||
let face = Face {
|
Ok(ft_face)
|
||||||
ft_face,
|
}
|
||||||
key,
|
|
||||||
|
fn face_from_pattern(
|
||||||
|
&mut self,
|
||||||
|
pattern: &PatternRef,
|
||||||
|
font_key: FontKey,
|
||||||
|
) -> Result<Option<FontKey>, Error> {
|
||||||
|
if let (Some(path), Some(index)) = (pattern.file(0), pattern.index().next()) {
|
||||||
|
if self.faces.get(&font_key).is_some() {
|
||||||
|
return Ok(Some(font_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!("Got font path={:?}", path);
|
||||||
|
|
||||||
|
let ft_face = match self.ft_faces.get(&path) {
|
||||||
|
Some(ft_face) => Rc::clone(ft_face),
|
||||||
|
None => self.load_ft_face(path, index)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get available pixel sizes if font isn't scalable.
|
||||||
|
let pixelsize =
|
||||||
|
pattern.pixelsize().next().expect("Font is missing pixelsize information.");
|
||||||
|
|
||||||
|
let pixelsize_fixup_factor = pattern.pixelsizefixupfactor().next();
|
||||||
|
|
||||||
|
let face = FaceLoadingProperties {
|
||||||
load_flags: Self::ft_load_flags(pattern),
|
load_flags: Self::ft_load_flags(pattern),
|
||||||
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,
|
pixelsize,
|
||||||
has_color,
|
colored: ft_face.has_color(),
|
||||||
pixelsize_fixup_factor,
|
pixelsize_fixup_factor,
|
||||||
|
ft_face,
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("Loaded Face {:?}", face);
|
debug!("Loaded Face {:?}", face);
|
||||||
|
|
||||||
let key = face.key;
|
self.faces.insert(font_key, face);
|
||||||
self.faces.insert(key, face);
|
|
||||||
self.keys.insert(font_id, key);
|
Ok(Some(font_key))
|
||||||
Ok(Some(key))
|
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
@ -396,20 +373,15 @@ impl FreeTypeRasterizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
for fallback_font in &fallback_list.list {
|
for fallback_font in &fallback_list.list {
|
||||||
let font_id = fallback_font.id;
|
let font_key = fallback_font.key;
|
||||||
let font_pattern = &fallback_font.pattern;
|
let font_pattern = &fallback_font.pattern;
|
||||||
match self.keys.get(&font_id) {
|
match self.faces.get(&font_key) {
|
||||||
Some(&key) => {
|
Some(face) => {
|
||||||
let face = match self.faces.get(&key) {
|
|
||||||
Some(face) => face,
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
let index = face.ft_face.get_char_index(glyph.c as usize);
|
let index = face.ft_face.get_char_index(glyph.c as usize);
|
||||||
|
|
||||||
// We found something in a current face, so let's use it
|
// We found something in a current face, so let's use it
|
||||||
if index != 0 {
|
if index != 0 {
|
||||||
return Ok(key);
|
return Ok(font_key);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
|
@ -418,7 +390,8 @@ impl FreeTypeRasterizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
let pattern = font_pattern.clone();
|
let pattern = font_pattern.clone();
|
||||||
let key = self.face_from_pattern(&pattern, font_id, None)?.unwrap();
|
let key = self.face_from_pattern(&pattern, font_key)?.unwrap();
|
||||||
|
|
||||||
return Ok(key);
|
return Ok(key);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -434,13 +407,8 @@ impl FreeTypeRasterizer {
|
||||||
let face = &self.faces[&font_key];
|
let face = &self.faces[&font_key];
|
||||||
let index = face.ft_face.get_char_index(glyph_key.c as usize);
|
let index = face.ft_face.get_char_index(glyph_key.c as usize);
|
||||||
|
|
||||||
let size =
|
if !face.colored {
|
||||||
face.non_scalable.as_ref().map(|v| v.pixelsize as f32).unwrap_or_else(|| {
|
face.ft_face.set_char_size(to_freetype_26_6(face.pixelsize as f32), 0, 0, 0)?;
|
||||||
glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.
|
|
||||||
});
|
|
||||||
|
|
||||||
if !face.has_color {
|
|
||||||
face.ft_face.set_char_size(to_freetype_26_6(size), 0, 0, 0)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -464,13 +432,13 @@ impl FreeTypeRasterizer {
|
||||||
buf,
|
buf,
|
||||||
};
|
};
|
||||||
|
|
||||||
if face.has_color {
|
if face.colored {
|
||||||
let fixup_factor = if let Some(pixelsize_fixup_factor) = face.pixelsize_fixup_factor {
|
let fixup_factor = if let Some(pixelsize_fixup_factor) = face.pixelsize_fixup_factor {
|
||||||
pixelsize_fixup_factor
|
pixelsize_fixup_factor
|
||||||
} else {
|
} else {
|
||||||
// Fallback if user has bitmap scaling disabled
|
// Fallback if user has bitmap scaling disabled
|
||||||
let metrics = face.ft_face.size_metrics().ok_or(Error::MissingSizeMetrics)?;
|
let metrics = face.ft_face.size_metrics().ok_or(Error::MissingSizeMetrics)?;
|
||||||
size as f64 / metrics.y_ppem as f64
|
face.pixelsize / metrics.y_ppem as f64
|
||||||
};
|
};
|
||||||
Ok(downsample_bitmap(rasterized_glyph, fixup_factor))
|
Ok(downsample_bitmap(rasterized_glyph, fixup_factor))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)]
|
#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)]
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
use std::ops::{Add, Mul};
|
use std::ops::{Add, Mul};
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
@ -97,7 +96,7 @@ impl fmt::Display for FontDesc {
|
||||||
/// Identifier for a Font for use in maps/etc
|
/// Identifier for a Font for use in maps/etc
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
pub struct FontKey {
|
pub struct FontKey {
|
||||||
token: u16,
|
token: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontKey {
|
impl FontKey {
|
||||||
|
@ -111,39 +110,13 @@ impl FontKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct GlyphKey {
|
pub struct GlyphKey {
|
||||||
pub c: char,
|
pub c: char,
|
||||||
pub font_key: FontKey,
|
pub font_key: FontKey,
|
||||||
pub size: Size,
|
pub size: Size,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for GlyphKey {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
unsafe {
|
|
||||||
// This transmute is fine:
|
|
||||||
//
|
|
||||||
// - If GlyphKey ever becomes a different size, this will fail to compile
|
|
||||||
// - Result is being used for hashing and has no fields (it's a u64)
|
|
||||||
::std::mem::transmute::<GlyphKey, u64>(*self)
|
|
||||||
}
|
|
||||||
.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for GlyphKey {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
unsafe {
|
|
||||||
// This transmute is fine:
|
|
||||||
//
|
|
||||||
// - If GlyphKey ever becomes a different size, this will fail to compile
|
|
||||||
// - Result is being used for equality checking and has no fields (it's a u64)
|
|
||||||
let other = ::std::mem::transmute::<GlyphKey, u64>(*other);
|
|
||||||
::std::mem::transmute::<GlyphKey, u64>(*self).eq(&other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Font size stored as integer
|
/// Font size stored as integer
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Size(i16);
|
pub struct Size(i16);
|
||||||
|
|
Loading…
Reference in New Issue