2021-03-18 19:13:31 +00:00
|
|
|
use crate::{
|
|
|
|
color::ColorU,
|
2021-03-25 09:42:46 +00:00
|
|
|
fonts::{FontId, GlyphId},
|
2021-03-25 16:21:26 +00:00
|
|
|
geometry::{
|
|
|
|
rect::RectF,
|
|
|
|
vector::{vec2f, Vector2F},
|
|
|
|
},
|
2021-03-24 15:51:28 +00:00
|
|
|
platform, scene, PaintContext,
|
2021-03-18 19:13:31 +00:00
|
|
|
};
|
|
|
|
use ordered_float::OrderedFloat;
|
|
|
|
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
|
|
|
|
use smallvec::SmallVec;
|
|
|
|
use std::{
|
|
|
|
borrow::Borrow,
|
|
|
|
collections::HashMap,
|
|
|
|
hash::{Hash, Hasher},
|
|
|
|
ops::Range,
|
|
|
|
sync::Arc,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub struct TextLayoutCache {
|
|
|
|
prev_frame: Mutex<HashMap<CacheKeyValue, Arc<Line>>>,
|
|
|
|
curr_frame: RwLock<HashMap<CacheKeyValue, Arc<Line>>>,
|
2021-03-24 15:51:28 +00:00
|
|
|
fonts: Arc<dyn platform::FontSystem>,
|
2021-03-18 19:13:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl TextLayoutCache {
|
2021-03-24 15:51:28 +00:00
|
|
|
pub fn new(fonts: Arc<dyn platform::FontSystem>) -> Self {
|
2021-03-18 19:13:31 +00:00
|
|
|
Self {
|
|
|
|
prev_frame: Mutex::new(HashMap::new()),
|
|
|
|
curr_frame: RwLock::new(HashMap::new()),
|
2021-03-24 15:51:28 +00:00
|
|
|
fonts,
|
2021-03-18 19:13:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn finish_frame(&self) {
|
|
|
|
let mut prev_frame = self.prev_frame.lock();
|
|
|
|
let mut curr_frame = self.curr_frame.write();
|
|
|
|
std::mem::swap(&mut *prev_frame, &mut *curr_frame);
|
|
|
|
curr_frame.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn layout_str<'a>(
|
|
|
|
&'a self,
|
|
|
|
text: &'a str,
|
|
|
|
font_size: f32,
|
|
|
|
runs: &'a [(Range<usize>, FontId)],
|
|
|
|
) -> Arc<Line> {
|
|
|
|
let key = &CacheKeyRef {
|
|
|
|
text,
|
|
|
|
font_size: OrderedFloat(font_size),
|
|
|
|
runs,
|
|
|
|
} as &dyn CacheKey;
|
|
|
|
let curr_frame = self.curr_frame.upgradable_read();
|
|
|
|
if let Some(line) = curr_frame.get(key) {
|
|
|
|
return line.clone();
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
|
|
|
|
if let Some((key, line)) = self.prev_frame.lock().remove_entry(key) {
|
|
|
|
curr_frame.insert(key, line.clone());
|
|
|
|
line.clone()
|
|
|
|
} else {
|
2021-03-24 15:51:28 +00:00
|
|
|
let line = Arc::new(self.fonts.layout_str(text, font_size, runs));
|
2021-03-18 19:13:31 +00:00
|
|
|
let key = CacheKeyValue {
|
|
|
|
text: text.into(),
|
|
|
|
font_size: OrderedFloat(font_size),
|
|
|
|
runs: SmallVec::from(runs),
|
|
|
|
};
|
|
|
|
curr_frame.insert(key, line.clone());
|
|
|
|
line
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
trait CacheKey {
|
|
|
|
fn key<'a>(&'a self) -> CacheKeyRef<'a>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> PartialEq for (dyn CacheKey + 'a) {
|
|
|
|
fn eq(&self, other: &dyn CacheKey) -> bool {
|
|
|
|
self.key() == other.key()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Eq for (dyn CacheKey + 'a) {}
|
|
|
|
|
|
|
|
impl<'a> Hash for (dyn CacheKey + 'a) {
|
|
|
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
|
|
self.key().hash(state)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Eq, PartialEq)]
|
|
|
|
struct CacheKeyValue {
|
|
|
|
text: String,
|
|
|
|
font_size: OrderedFloat<f32>,
|
|
|
|
runs: SmallVec<[(Range<usize>, FontId); 1]>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CacheKey for CacheKeyValue {
|
|
|
|
fn key<'a>(&'a self) -> CacheKeyRef<'a> {
|
|
|
|
CacheKeyRef {
|
|
|
|
text: &self.text.as_str(),
|
|
|
|
font_size: self.font_size,
|
|
|
|
runs: self.runs.as_slice(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Hash for CacheKeyValue {
|
|
|
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
|
|
self.key().hash(state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Borrow<dyn CacheKey + 'a> for CacheKeyValue {
|
|
|
|
fn borrow(&self) -> &(dyn CacheKey + 'a) {
|
|
|
|
self as &dyn CacheKey
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
|
|
|
struct CacheKeyRef<'a> {
|
|
|
|
text: &'a str,
|
|
|
|
font_size: OrderedFloat<f32>,
|
|
|
|
runs: &'a [(Range<usize>, FontId)],
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> CacheKey for CacheKeyRef<'a> {
|
|
|
|
fn key<'b>(&'b self) -> CacheKeyRef<'b> {
|
|
|
|
*self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-24 15:51:28 +00:00
|
|
|
#[derive(Default, Debug)]
|
2021-03-18 19:13:31 +00:00
|
|
|
pub struct Line {
|
|
|
|
pub width: f32,
|
2021-04-06 15:14:55 +00:00
|
|
|
pub ascent: f32,
|
|
|
|
pub descent: f32,
|
2021-03-18 19:13:31 +00:00
|
|
|
pub runs: Vec<Run>,
|
|
|
|
pub len: usize,
|
2021-03-24 15:51:28 +00:00
|
|
|
pub font_size: f32,
|
2021-03-18 19:13:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Run {
|
|
|
|
pub font_id: FontId,
|
|
|
|
pub glyphs: Vec<Glyph>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Glyph {
|
|
|
|
pub id: GlyphId,
|
|
|
|
pub position: Vector2F,
|
|
|
|
pub index: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Line {
|
|
|
|
pub fn x_for_index(&self, index: usize) -> f32 {
|
|
|
|
for run in &self.runs {
|
|
|
|
for glyph in &run.glyphs {
|
|
|
|
if glyph.index == index {
|
|
|
|
return glyph.position.x();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.width
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn index_for_x(&self, x: f32) -> Option<usize> {
|
|
|
|
if x >= self.width {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
for run in self.runs.iter().rev() {
|
|
|
|
for glyph in run.glyphs.iter().rev() {
|
|
|
|
if glyph.position.x() <= x {
|
|
|
|
return Some(glyph.index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Some(0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-01 09:19:50 +00:00
|
|
|
pub fn paint(
|
|
|
|
&self,
|
|
|
|
origin: Vector2F,
|
|
|
|
bounds: RectF,
|
|
|
|
colors: &[(Range<usize>, ColorU)],
|
|
|
|
ctx: &mut PaintContext,
|
|
|
|
) {
|
2021-03-23 14:15:41 +00:00
|
|
|
let mut colors = colors.iter().peekable();
|
|
|
|
let mut color = ColorU::black();
|
2021-03-18 19:13:31 +00:00
|
|
|
|
2021-04-06 15:14:55 +00:00
|
|
|
let padding_top = (bounds.height() - self.ascent - self.descent) / 2.;
|
|
|
|
let baseline_origin = vec2f(0., padding_top + self.ascent);
|
|
|
|
|
2021-03-23 14:15:41 +00:00
|
|
|
for run in &self.runs {
|
2021-04-06 15:14:55 +00:00
|
|
|
let max_glyph_width = ctx.font_cache.bounding_box(run.font_id, self.font_size).x();
|
|
|
|
|
2021-03-23 14:15:41 +00:00
|
|
|
for glyph in &run.glyphs {
|
2021-04-06 15:14:55 +00:00
|
|
|
let glyph_origin = baseline_origin + glyph.position;
|
|
|
|
|
2021-03-23 14:15:41 +00:00
|
|
|
if glyph_origin.x() + max_glyph_width < bounds.origin().x() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if glyph_origin.x() > bounds.upper_right().x() {
|
|
|
|
break;
|
|
|
|
}
|
2021-03-18 19:13:31 +00:00
|
|
|
|
2021-03-23 14:15:41 +00:00
|
|
|
while let Some((range, next_color)) = colors.peek() {
|
|
|
|
if glyph.index >= range.end {
|
|
|
|
colors.next();
|
|
|
|
} else {
|
|
|
|
color = *next_color;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-03-18 19:13:31 +00:00
|
|
|
|
2021-03-23 14:15:41 +00:00
|
|
|
ctx.scene.push_glyph(scene::Glyph {
|
|
|
|
font_id: run.font_id,
|
|
|
|
font_size: self.font_size,
|
2021-03-23 18:11:56 +00:00
|
|
|
id: glyph.id,
|
2021-04-06 15:14:55 +00:00
|
|
|
origin: origin + glyph_origin,
|
2021-03-23 14:15:41 +00:00
|
|
|
color,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2021-03-18 19:13:31 +00:00
|
|
|
}
|
|
|
|
}
|