use api::{FontInstanceData, FontInstanceFlags, FontInstanceKey};
use api::{FontInstanceOptions, FontInstancePlatformOptions};
use api::{FontKey, FontRenderMode, FontSize, FontTemplate, FontVariation};
use api::{ColorU, GlyphIndex, GlyphDimensions, SyntheticItalics};
use api::{IdNamespace, BlobImageResources};
use api::channel::crossbeam::{unbounded, Receiver, Sender};
use api::units::*;
use api::ImageFormat;
use crate::platform::font::FontContext;
use crate::profiler::GlyphRasterizeProfiler;
use crate::types::{FastHashMap, FastHashSet};
use crate::telemetry::Telemetry;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use rayon::ThreadPool;
use rayon::prelude::*;
use euclid::approxeq::ApproxEq;
use smallvec::SmallVec;
use std::cmp;
use std::cell::Cell;
use std::hash::{Hash, Hasher};
use std::mem;
use std::ops::Deref;
use std::sync::{Arc, Condvar, Mutex, MutexGuard, Weak};
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use std::sync::atomic::{AtomicBool, Ordering};
pub static GLYPH_FLASHING: AtomicBool = AtomicBool::new(false);
const GLYPH_BATCH_SIZE: usize = 32;
impl FontContexts {
pub fn lock_current_context(&self) -> MutexGuard<FontContext> {
match self.current_worker_id() {
Some(id) => self.lock_context(id),
None => self.lock_any_context(),
}
}
pub(in super) fn current_worker_id(&self) -> Option<usize> {
self.workers.current_thread_index()
}
}
thread_local! {
pub static SEED: Cell<u32> = Cell::new(0);
}
fn random() -> u32 {
SEED.with(|seed| {
seed.set(seed.get().wrapping_mul(22695477).wrapping_add(1));
seed.get()
})
}
impl GlyphRasterizer {
pub fn request_glyphs<F>(
&mut self,
font: FontInstance,
glyph_keys: &[GlyphKey],
mut handle: F,
)
where F: FnMut(&GlyphKey) -> bool
{
assert!(self.has_font(font.font_key));
let mut batch_size = 0;
for key in glyph_keys {
if !handle(key) {
continue;
}
self.pending_glyph_count += 1;
self.glyph_request_count += 1;
match self.pending_glyph_requests.get_mut(&font) {
Some(container) => {
container.push(*key);
batch_size = container.len();
}
None => {
self.pending_glyph_requests.insert(
font.clone(),
smallvec![*key],
);
}
}
}
if batch_size >= GLYPH_BATCH_SIZE {
let container = self.pending_glyph_requests.get_mut(&font).unwrap();
let glyphs = mem::replace(container, SmallVec::new());
self.flush_glyph_requests(font, glyphs, true);
}
}
pub fn enable_multithreading(&mut self, enable: bool) {
self.enable_multithreading = enable;
}
fn flush_glyph_requests(
&mut self,
font: FontInstance,
glyphs: SmallVec<[GlyphKey; 16]>,
use_workers: bool,
) {
let font = Arc::new(font);
let font_contexts = Arc::clone(&self.font_contexts);
self.pending_glyph_jobs += glyphs.len();
self.pending_glyph_count -= glyphs.len();
let can_use_r8_format = self.can_use_r8_format;
if let Some(thread) = &self.dedicated_thread {
let tx = self.glyph_tx.clone();
let _ = thread.tx.send(GlyphRasterMsg::Rasterize { font, glyphs, can_use_r8_format, tx });
} else if self.enable_multithreading && use_workers {
profile_scope!("spawning process_glyph jobs");
let tx = self.glyph_tx.clone();
self.workers.spawn(move || {
FontContext::begin_rasterize(&font);
if FontContext::distribute_across_threads() {
glyphs.par_iter().for_each(|key| {
let mut context = font_contexts.lock_current_context();
let job_font = font.clone();
let job = process_glyph(&mut context, can_use_r8_format, job_font, *key);
tx.send(job).unwrap();
});
} else {
for key in glyphs {
let mut context = font_contexts.lock_current_context();
let job_font = font.clone();
let job = process_glyph(&mut context, can_use_r8_format, job_font, key);
tx.send(job).unwrap();
}
}
FontContext::end_rasterize(&font);
});
} else {
FontContext::begin_rasterize(&font);
for key in glyphs {
let mut context = font_contexts.lock_current_context();
let job_font = font.clone();
let job = process_glyph(&mut context, can_use_r8_format, job_font, key);
self.glyph_tx.send(job).unwrap();
}
FontContext::end_rasterize(&font);
}
}
pub fn resolve_glyphs<F, G>(
&mut self,
mut handle: F,
profile: &mut G,
)
where
F: FnMut(GlyphRasterJob, bool),
G: GlyphRasterizeProfiler,
{
profile.start_time();
let timer_id = Telemetry::start_rasterize_glyphs_time();
let mut pending_glyph_requests = mem::replace(
&mut self.pending_glyph_requests,
FastHashMap::default(),
);
let use_workers = self.pending_glyph_count >= 8;
for (font, pending_glyphs) in pending_glyph_requests.drain() {
self.flush_glyph_requests(
font,
pending_glyphs,
use_workers,
);
}
self.pending_glyph_requests = pending_glyph_requests;
debug_assert_eq!(self.pending_glyph_count, 0);
debug_assert!(self.pending_glyph_requests.is_empty());
if self.glyph_request_count > 0 {
profile.set(self.glyph_request_count as f64);
self.glyph_request_count = 0;
}
profile_scope!("resolve_glyphs");
let mut jobs = {
profile_scope!("blocking wait on glyph_rx");
self.glyph_rx.iter().take(self.pending_glyph_jobs).collect::<Vec<_>>()
};
assert_eq!(jobs.len(), self.pending_glyph_jobs, "BUG: Didn't receive all pending glyphs!");
self.pending_glyph_jobs = 0;
jobs.sort_by(|a, b| (*a.font).cmp(&*b.font).then(a.key.cmp(&b.key)));
for job in jobs {
handle(job, self.can_use_r8_format);
}
self.remove_dead_fonts();
Telemetry::stop_and_accumulate_rasterize_glyphs_time(timer_id);
profile.end_time();
}
}
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct FontTransform {
pub scale_x: f32,
pub skew_x: f32,
pub skew_y: f32,
pub scale_y: f32,
}
impl Eq for FontTransform {}
impl Ord for FontTransform {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.partial_cmp(other).unwrap_or(cmp::Ordering::Equal)
}
}
impl Hash for FontTransform {
fn hash<H: Hasher>(&self, state: &mut H) {
self.scale_x.to_bits().hash(state);
self.skew_x.to_bits().hash(state);
self.skew_y.to_bits().hash(state);
self.scale_y.to_bits().hash(state);
}
}
impl FontTransform {
const QUANTIZE_SCALE: f32 = 1024.0;
pub fn new(scale_x: f32, skew_x: f32, skew_y: f32, scale_y: f32) -> Self {
FontTransform { scale_x, skew_x, skew_y, scale_y }
}
pub fn identity() -> Self {
FontTransform::new(1.0, 0.0, 0.0, 1.0)
}
#[allow(dead_code)]
pub fn is_identity(&self) -> bool {
*self == FontTransform::identity()
}
pub fn quantize(&self) -> Self {
FontTransform::new(
(self.scale_x * Self::QUANTIZE_SCALE).round() / Self::QUANTIZE_SCALE,
(self.skew_x * Self::QUANTIZE_SCALE).round() / Self::QUANTIZE_SCALE,
(self.skew_y * Self::QUANTIZE_SCALE).round() / Self::QUANTIZE_SCALE,
(self.scale_y * Self::QUANTIZE_SCALE).round() / Self::QUANTIZE_SCALE,
)
}
#[allow(dead_code)]
pub fn determinant(&self) -> f64 {
self.scale_x as f64 * self.scale_y as f64 - self.skew_y as f64 * self.skew_x as f64
}
#[allow(dead_code)]
pub fn compute_scale(&self) -> Option<(f64, f64)> {
let det = self.determinant();
if det != 0.0 {
let x_scale = (self.scale_x as f64).hypot(self.skew_y as f64);
let y_scale = det.abs() / x_scale;
Some((x_scale, y_scale))
} else {
None
}
}
#[allow(dead_code)]
pub fn pre_scale(&self, scale_x: f32, scale_y: f32) -> Self {
FontTransform::new(
self.scale_x * scale_x,
self.skew_x * scale_y,
self.skew_y * scale_x,
self.scale_y * scale_y,
)
}
#[allow(dead_code)]
pub fn scale(&self, scale: f32) -> Self { self.pre_scale(scale, scale) }
#[allow(dead_code)]
pub fn invert_scale(&self, x_scale: f64, y_scale: f64) -> Self {
self.pre_scale(x_scale.recip() as f32, y_scale.recip() as f32)
}
pub fn synthesize_italics(&self, angle: SyntheticItalics, size: f64, vertical: bool) -> (Self, (f64, f64)) {
let skew_factor = angle.to_skew();
if vertical {
let (tx, ty) = (0.0, -size * 0.5 * skew_factor as f64);
(FontTransform::new(
self.scale_x + self.skew_x * skew_factor,
self.skew_x,
self.skew_y + self.scale_y * skew_factor,
self.scale_y,
), (self.scale_x as f64 * tx + self.skew_x as f64 * ty,
self.skew_y as f64 * tx + self.scale_y as f64 * ty))
} else {
(FontTransform::new(
self.scale_x,
self.skew_x - self.scale_x * skew_factor,
self.skew_y,
self.scale_y - self.skew_y * skew_factor,
), (0.0, 0.0))
}
}
pub fn swap_xy(&self) -> Self {
FontTransform::new(self.skew_x, self.scale_x, self.scale_y, self.skew_y)
}
pub fn flip_x(&self) -> Self {
FontTransform::new(-self.scale_x, self.skew_x, -self.skew_y, self.scale_y)
}
pub fn flip_y(&self) -> Self {
FontTransform::new(self.scale_x, -self.skew_x, self.skew_y, -self.scale_y)
}
pub fn transform(&self, point: &LayoutPoint) -> DevicePoint {
DevicePoint::new(
self.scale_x * point.x + self.skew_x * point.y,
self.skew_y * point.x + self.scale_y * point.y,
)
}
pub fn get_subpx_dir(&self) -> SubpixelDirection {
if self.skew_y.approx_eq(&0.0) {
SubpixelDirection::Horizontal
} else if self.scale_x.approx_eq(&0.0) {
SubpixelDirection::Vertical
} else {
SubpixelDirection::Mixed
}
}
}
impl<'a> From<&'a LayoutToWorldTransform> for FontTransform {
fn from(xform: &'a LayoutToWorldTransform) -> Self {
FontTransform::new(xform.m11, xform.m21, xform.m12, xform.m22)
}
}
pub const FONT_SIZE_LIMIT: f32 = 320.0;
#[derive(Clone, Debug, Ord, PartialOrd, MallocSizeOf)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BaseFontInstance {
pub instance_key: FontInstanceKey,
pub font_key: FontKey,
pub size: FontSize,
pub options: FontInstanceOptions,
#[cfg_attr(any(feature = "capture", feature = "replay"), serde(skip))]
pub platform_options: Option<FontInstancePlatformOptions>,
pub variations: Vec<FontVariation>,
}
impl BaseFontInstance {
pub fn new(
instance_key: FontInstanceKey,
font_key: FontKey,
size: f32,
options: Option<FontInstanceOptions>,
platform_options: Option<FontInstancePlatformOptions>,
variations: Vec<FontVariation>,
) -> Self {
BaseFontInstance {
instance_key,
font_key,
size: size.into(),
options: options.unwrap_or_default(),
platform_options,
variations,
}
}
}
impl Deref for BaseFontInstance {
type Target = FontInstanceOptions;
fn deref(&self) -> &FontInstanceOptions {
&self.options
}
}
impl Hash for BaseFontInstance {
fn hash<H: Hasher>(&self, state: &mut H) {
self.font_key.hash(state);
self.size.hash(state);
self.options.hash(state);
self.platform_options.hash(state);
self.variations.hash(state);
}
}
impl PartialEq for BaseFontInstance {
fn eq(&self, other: &BaseFontInstance) -> bool {
self.font_key == other.font_key &&
self.size == other.size &&
self.options == other.options &&
self.platform_options == other.platform_options &&
self.variations == other.variations
}
}
impl Eq for BaseFontInstance {}
struct MappedFontKey {
font_key: FontKey,
template: FontTemplate,
}
struct FontKeyMapLocked {
namespace: IdNamespace,
next_id: u32,
template_map: FastHashMap<FontTemplate, Arc<MappedFontKey>>,
key_map: FastHashMap<FontKey, Arc<MappedFontKey>>,
}
#[derive(Clone)]
pub struct FontKeyMap(Arc<RwLock<FontKeyMapLocked>>);
impl FontKeyMap {
pub fn new(namespace: IdNamespace) -> Self {
FontKeyMap(Arc::new(RwLock::new(FontKeyMapLocked {
namespace,
next_id: 1,
template_map: FastHashMap::default(),
key_map: FastHashMap::default(),
})))
}
fn lock(&self) -> RwLockReadGuard<FontKeyMapLocked> {
self.0.read().unwrap()
}
fn lock_mut(&mut self) -> RwLockWriteGuard<FontKeyMapLocked> {
self.0.write().unwrap()
}
pub fn keys(&self) -> Vec<FontKey> {
self.lock().key_map.keys().cloned().collect()
}
pub fn map_key(&self, font_key: &FontKey) -> FontKey {
match self.lock().key_map.get(font_key) {
Some(mapped) => mapped.font_key,
None => *font_key,
}
}
pub fn add_key(&mut self, font_key: &FontKey, template: &FontTemplate) -> Option<FontKey> {
let mut locked = self.lock_mut();
if locked.key_map.contains_key(font_key) {
return None;
}
if let Some(mapped) = locked.template_map.get(template).cloned() {
locked.key_map.insert(*font_key, mapped);
return None;
}
let shared_key = FontKey::new(locked.namespace, locked.next_id);
locked.next_id += 1;
let mapped = Arc::new(MappedFontKey {
font_key: shared_key,
template: template.clone(),
});
locked.template_map.insert(template.clone(), mapped.clone());
locked.key_map.insert(*font_key, mapped);
Some(shared_key)
}
pub fn delete_key(&mut self, font_key: &FontKey) -> Option<FontKey> {
let mut locked = self.lock_mut();
let mapped = match locked.key_map.remove(font_key) {
Some(mapped) => mapped,
None => return Some(*font_key),
};
if Arc::strong_count(&mapped) <= 2 {
locked.template_map.remove(&mapped.template);
Some(mapped.font_key)
} else {
None
}
}
pub fn clear_namespace(&mut self, namespace: IdNamespace) -> Vec<FontKey> {
let mut locked = self.lock_mut();
locked.key_map.retain(|key, _| {
if key.0 == namespace {
false
} else {
true
}
});
let mut deleted_keys = Vec::new();
locked.template_map.retain(|_, mapped| {
if Arc::strong_count(mapped) <= 1 {
deleted_keys.push(mapped.font_key);
false
} else {
true
}
});
deleted_keys
}
}
type FontTemplateMapLocked = FastHashMap<FontKey, FontTemplate>;
#[derive(Clone)]
pub struct FontTemplateMap(Arc<RwLock<FontTemplateMapLocked>>);
impl FontTemplateMap {
pub fn new() -> Self {
FontTemplateMap(Arc::new(RwLock::new(FastHashMap::default())))
}
pub fn lock(&self) -> RwLockReadGuard<FontTemplateMapLocked> {
self.0.read().unwrap()
}
fn lock_mut(&mut self) -> RwLockWriteGuard<FontTemplateMapLocked> {
self.0.write().unwrap()
}
pub fn clear(&mut self) {
self.lock_mut().clear();
}
pub fn len(&self) -> usize {
self.lock().len()
}
pub fn has_font(&self, key: &FontKey) -> bool {
self.lock().contains_key(key)
}
pub fn get_font(&self, key: &FontKey) -> Option<FontTemplate> {
self.lock().get(key).cloned()
}
pub fn add_font(&mut self, key: FontKey, template: FontTemplate) -> bool {
self.lock_mut().insert(key, template).is_none()
}
pub fn delete_font(&mut self, key: &FontKey) -> Option<FontTemplate> {
self.lock_mut().remove(key)
}
pub fn delete_fonts(&mut self, keys: &[FontKey]) {
if !keys.is_empty() {
let mut map = self.lock_mut();
for key in keys {
map.remove(key);
}
}
}
pub fn clear_namespace(&mut self, namespace: IdNamespace) -> Vec<FontKey> {
let mut deleted_keys = Vec::new();
self.lock_mut().retain(|key, _| {
if key.0 == namespace {
deleted_keys.push(*key);
false
} else {
true
}
});
deleted_keys
}
}
struct FontInstanceKeyMapLocked {
namespace: IdNamespace,
next_id: u32,
instances: FastHashSet<Arc<BaseFontInstance>>,
key_map: FastHashMap<FontInstanceKey, Weak<BaseFontInstance>>,
}
#[derive(Clone)]
pub struct FontInstanceKeyMap(Arc<RwLock<FontInstanceKeyMapLocked>>);
impl FontInstanceKeyMap {
pub fn new(namespace: IdNamespace) -> Self {
FontInstanceKeyMap(Arc::new(RwLock::new(FontInstanceKeyMapLocked {
namespace,
next_id: 1,
instances: FastHashSet::default(),
key_map: FastHashMap::default(),
})))
}
fn lock(&self) -> RwLockReadGuard<FontInstanceKeyMapLocked> {
self.0.read().unwrap()
}
fn lock_mut(&mut self) -> RwLockWriteGuard<FontInstanceKeyMapLocked> {
self.0.write().unwrap()
}
pub fn keys(&self) -> Vec<FontInstanceKey> {
self.lock().key_map.keys().cloned().collect()
}
pub fn map_key(&self, key: &FontInstanceKey) -> FontInstanceKey {
match self.lock().key_map.get(key).and_then(|weak| weak.upgrade()) {
Some(mapped) => mapped.instance_key,
None => *key,
}
}
pub fn add_key(&mut self, mut instance: BaseFontInstance) -> Option<Arc<BaseFontInstance>> {
let mut locked = self.lock_mut();
if locked.key_map.contains_key(&instance.instance_key) {
return None;
}
if let Some(weak) = locked.instances.get(&instance).map(|mapped| Arc::downgrade(mapped)) {
locked.key_map.insert(instance.instance_key, weak);
return None;
}
let unmapped_key = instance.instance_key;
instance.instance_key = FontInstanceKey::new(locked.namespace, locked.next_id);
locked.next_id += 1;
let shared_instance = Arc::new(instance);
locked.instances.insert(shared_instance.clone());
locked.key_map.insert(unmapped_key, Arc::downgrade(&shared_instance));
Some(shared_instance)
}
pub fn delete_key(&mut self, key: &FontInstanceKey) -> Option<FontInstanceKey> {
let mut locked = self.lock_mut();
let mapped = match locked.key_map.remove(key).and_then(|weak| weak.upgrade()) {
Some(mapped) => mapped,
None => return Some(*key),
};
if Arc::weak_count(&mapped) == 0 {
locked.instances.remove(&mapped);
Some(mapped.instance_key)
} else {
None
}
}
pub fn clear_namespace(&mut self, namespace: IdNamespace) -> Vec<FontInstanceKey> {
let mut locked = self.lock_mut();
locked.key_map.retain(|key, _| {
if key.0 == namespace {
false
} else {
true
}
});
let mut deleted_keys = Vec::new();
locked.instances.retain(|mapped| {
if Arc::weak_count(mapped) == 0 {
deleted_keys.push(mapped.instance_key);
false
} else {
true
}
});
deleted_keys
}
}
type FontInstanceMapLocked = FastHashMap<FontInstanceKey, Arc<BaseFontInstance>>;
#[derive(Clone)]
pub struct FontInstanceMap(Arc<RwLock<FontInstanceMapLocked>>);
impl FontInstanceMap {
pub fn new() -> Self {
FontInstanceMap(Arc::new(RwLock::new(FastHashMap::default())))
}
pub fn lock(&self) -> RwLockReadGuard<FontInstanceMapLocked> {
self.0.read().unwrap()
}
fn lock_mut(&mut self) -> RwLockWriteGuard<FontInstanceMapLocked> {
self.0.write().unwrap()
}
pub fn clear(&mut self) {
self.lock_mut().clear();
}
pub fn get_font_instance_data(&self, key: FontInstanceKey) -> Option<FontInstanceData> {
match self.lock().get(&key) {
Some(instance) => Some(FontInstanceData {
font_key: instance.font_key,
size: instance.size.into(),
options: Some(FontInstanceOptions {
render_mode: instance.render_mode,
flags: instance.flags,
synthetic_italics: instance.synthetic_italics,
_padding: 0,
}),
platform_options: instance.platform_options,
variations: instance.variations.clone(),
}),
None => None,
}
}
pub fn get_font_instance(&self, instance_key: FontInstanceKey) -> Option<Arc<BaseFontInstance>> {
let instance_map = self.lock();
instance_map.get(&instance_key).cloned()
}
pub fn add_font_instance(&mut self, instance: Arc<BaseFontInstance>) {
self.lock_mut().insert(instance.instance_key, instance);
}
pub fn delete_font_instance(&mut self, instance_key: FontInstanceKey) {
self.lock_mut().remove(&instance_key);
}
pub fn delete_font_instances(&mut self, keys: &[FontInstanceKey]) {
if !keys.is_empty() {
let mut map = self.lock_mut();
for key in keys {
map.remove(key);
}
}
}
pub fn clear_namespace(&mut self, namespace: IdNamespace) {
self.lock_mut().retain(|key, _| key.0 != namespace);
}
}
#[derive(Clone)]
pub struct SharedFontResources {
pub templates: FontTemplateMap,
pub instances: FontInstanceMap,
pub font_keys: FontKeyMap,
pub instance_keys: FontInstanceKeyMap,
}
impl SharedFontResources {
pub fn new(namespace: IdNamespace) -> Self {
SharedFontResources {
templates: FontTemplateMap::new(),
instances: FontInstanceMap::new(),
font_keys: FontKeyMap::new(namespace),
instance_keys: FontInstanceKeyMap::new(namespace),
}
}
}
impl BlobImageResources for SharedFontResources {
fn get_font_data(&self, key: FontKey) -> Option<FontTemplate> {
let shared_key = self.font_keys.map_key(&key);
self.templates.get_font(&shared_key)
}
fn get_font_instance_data(&self, key: FontInstanceKey) -> Option<FontInstanceData> {
let shared_key = self.instance_keys.map_key(&key);
self.instances.get_font_instance_data(shared_key)
}
}
#[derive(Clone, Debug, Ord, PartialOrd)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct FontInstance {
pub base: Arc<BaseFontInstance>,
pub transform: FontTransform,
pub render_mode: FontRenderMode,
pub flags: FontInstanceFlags,
pub color: ColorU,
pub size: FontSize,
}
impl Hash for FontInstance {
fn hash<H: Hasher>(&self, state: &mut H) {
self.base.instance_key.hash(state);
self.transform.hash(state);
self.render_mode.hash(state);
self.flags.hash(state);
self.color.hash(state);
self.size.hash(state);
}
}
impl PartialEq for FontInstance {
fn eq(&self, other: &FontInstance) -> bool {
self.base.instance_key == other.base.instance_key &&
self.transform == other.transform &&
self.render_mode == other.render_mode &&
self.flags == other.flags &&
self.color == other.color &&
self.size == other.size
}
}
impl Eq for FontInstance {}
impl Deref for FontInstance {
type Target = BaseFontInstance;
fn deref(&self) -> &BaseFontInstance {
self.base.as_ref()
}
}
impl MallocSizeOf for FontInstance {
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { 0 }
}
impl FontInstance {
pub fn new(
base: Arc<BaseFontInstance>,
color: ColorU,
render_mode: FontRenderMode,
flags: FontInstanceFlags,
) -> Self {
FontInstance {
transform: FontTransform::identity(),
color,
size: base.size,
base,
render_mode,
flags,
}
}
pub fn from_base(
base: Arc<BaseFontInstance>,
) -> Self {
let color = ColorU::new(0, 0, 0, 255);
let render_mode = base.render_mode;
let flags = base.flags;
Self::new(base, color, render_mode, flags)
}
pub fn use_texture_padding(&self) -> bool {
self.flags.contains(FontInstanceFlags::TEXTURE_PADDING)
}
pub fn use_transform_glyphs(&self) -> bool {
self.flags.contains(FontInstanceFlags::TRANSFORM_GLYPHS)
}
pub fn get_alpha_glyph_format(&self) -> GlyphFormat {
if self.use_transform_glyphs() { GlyphFormat::TransformedAlpha } else { GlyphFormat::Alpha }
}
pub fn get_subpixel_glyph_format(&self) -> GlyphFormat {
if self.use_transform_glyphs() { GlyphFormat::TransformedSubpixel } else { GlyphFormat::Subpixel }
}
pub fn disable_subpixel_aa(&mut self) {
self.render_mode = self.render_mode.limit_by(FontRenderMode::Alpha);
}
pub fn disable_subpixel_position(&mut self) {
self.flags.remove(FontInstanceFlags::SUBPIXEL_POSITION);
}
pub fn use_subpixel_position(&self) -> bool {
self.flags.contains(FontInstanceFlags::SUBPIXEL_POSITION) &&
self.render_mode != FontRenderMode::Mono
}
pub fn get_subpx_dir(&self) -> SubpixelDirection {
if self.use_subpixel_position() {
let mut subpx_dir = self.transform.get_subpx_dir();
if self.flags.contains(FontInstanceFlags::TRANSPOSE) {
subpx_dir = subpx_dir.swap_xy();
}
subpx_dir
} else {
SubpixelDirection::None
}
}
#[allow(dead_code)]
pub fn get_subpx_offset(&self, glyph: &GlyphKey) -> (f64, f64) {
if self.use_subpixel_position() {
let (dx, dy) = glyph.subpixel_offset();
(dx.into(), dy.into())
} else {
(0.0, 0.0)
}
}
#[allow(dead_code)]
pub fn get_glyph_format(&self) -> GlyphFormat {
match self.render_mode {
FontRenderMode::Mono | FontRenderMode::Alpha => self.get_alpha_glyph_format(),
FontRenderMode::Subpixel => self.get_subpixel_glyph_format(),
}
}
#[allow(dead_code)]
pub fn get_extra_strikes(&self, flags: FontInstanceFlags, x_scale: f64) -> usize {
if self.flags.intersects(flags) {
let mut bold_offset = self.size.to_f64_px() / 48.0;
if bold_offset < 1.0 {
bold_offset = 0.25 + 0.75 * bold_offset;
}
(bold_offset * x_scale).max(1.0).round() as usize
} else {
0
}
}
pub fn synthesize_italics(&self, transform: FontTransform, size: f64) -> (FontTransform, (f64, f64)) {
transform.synthesize_italics(self.synthetic_italics, size, self.flags.contains(FontInstanceFlags::VERTICAL))
}
#[allow(dead_code)]
pub fn get_transformed_size(&self) -> f64 {
let (_, y_scale) = self.transform.compute_scale().unwrap_or((1.0, 1.0));
self.size.to_f64_px() * y_scale
}
}
#[repr(u32)]
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
pub enum SubpixelDirection {
None = 0,
Horizontal,
Vertical,
Mixed,
}
impl SubpixelDirection {
pub fn limit_by(self, glyph_format: GlyphFormat) -> Self {
match glyph_format {
GlyphFormat::Bitmap |
GlyphFormat::ColorBitmap => SubpixelDirection::None,
_ => self,
}
}
pub fn swap_xy(self) -> Self {
match self {
SubpixelDirection::None | SubpixelDirection::Mixed => self,
SubpixelDirection::Horizontal => SubpixelDirection::Vertical,
SubpixelDirection::Vertical => SubpixelDirection::Horizontal,
}
}
}
#[repr(u8)]
#[derive(Hash, Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum SubpixelOffset {
Zero = 0,
Quarter = 1,
Half = 2,
ThreeQuarters = 3,
}
impl SubpixelOffset {
fn quantize(pos: f32) -> Self {
let apos = ((pos - pos.floor()) * 8.0) as i32;
match apos {
1..=2 => SubpixelOffset::Quarter,
3..=4 => SubpixelOffset::Half,
5..=6 => SubpixelOffset::ThreeQuarters,
_ => SubpixelOffset::Zero,
}
}
}
impl Into<f64> for SubpixelOffset {
fn into(self) -> f64 {
match self {
SubpixelOffset::Zero => 0.0,
SubpixelOffset::Quarter => 0.25,
SubpixelOffset::Half => 0.5,
SubpixelOffset::ThreeQuarters => 0.75,
}
}
}
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct GlyphKey(u32);
impl GlyphKey {
pub fn new(
index: u32,
point: DevicePoint,
subpx_dir: SubpixelDirection,
) -> Self {
let (dx, dy) = match subpx_dir {
SubpixelDirection::None => (0.0, 0.0),
SubpixelDirection::Horizontal => (point.x, 0.0),
SubpixelDirection::Vertical => (0.0, point.y),
SubpixelDirection::Mixed => (point.x, point.y),
};
let sox = SubpixelOffset::quantize(dx);
let soy = SubpixelOffset::quantize(dy);
assert_eq!(0, index & 0xF0000000);
GlyphKey(index | (sox as u32) << 28 | (soy as u32) << 30)
}
pub fn index(&self) -> GlyphIndex {
self.0 & 0x0FFFFFFF
}
fn subpixel_offset(&self) -> (SubpixelOffset, SubpixelOffset) {
let x = (self.0 >> 28) as u8 & 3;
let y = (self.0 >> 30) as u8 & 3;
unsafe {
(mem::transmute(x), mem::transmute(y))
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[allow(dead_code)]
pub enum GlyphFormat {
Alpha,
TransformedAlpha,
Subpixel,
TransformedSubpixel,
Bitmap,
ColorBitmap,
}
impl GlyphFormat {
pub fn image_format(&self, can_use_r8_format: bool) -> ImageFormat {
match *self {
GlyphFormat::Alpha |
GlyphFormat::TransformedAlpha |
GlyphFormat::Bitmap => {
if can_use_r8_format {
ImageFormat::R8
} else {
ImageFormat::BGRA8
}
}
GlyphFormat::Subpixel |
GlyphFormat::TransformedSubpixel |
GlyphFormat::ColorBitmap => ImageFormat::BGRA8,
}
}
}
#[allow(dead_code)]
#[inline]
fn blend_strike_pixel(dest: u8, src: u32, src_alpha: u32) -> u8 {
let x = src * 255 + dest as u32 * (255 - src_alpha) + 128;
((x + (x >> 8)) >> 8) as u8
}
#[allow(dead_code)]
fn blend_strike(
dest_bitmap: &mut [u8],
src_bitmap: &[u8],
width: usize,
height: usize,
subpixel_mask: bool,
offset: f64,
) {
let dest_stride = dest_bitmap.len() / height;
let src_stride = width * 4;
let offset_integer = offset.floor() as usize * 4;
let offset_fract = (offset.fract() * 256.0) as u32;
for (src_row, dest_row) in src_bitmap.chunks(src_stride).zip(dest_bitmap.chunks_mut(dest_stride)) {
let mut prev_px = [0u32; 4];
let dest_row_offset = &mut dest_row[offset_integer .. offset_integer + src_stride];
for (src, dest) in src_row.chunks(4).zip(dest_row_offset.chunks_mut(4)) {
let px = [src[0] as u32, src[1] as u32, src[2] as u32, src[3] as u32];
let next_px = [px[0] * offset_fract,
px[1] * offset_fract,
px[2] * offset_fract,
px[3] * offset_fract];
let offset_px = [(((px[0] << 8) - next_px[0]) + prev_px[0] + 128) >> 8,
(((px[1] << 8) - next_px[1]) + prev_px[1] + 128) >> 8,
(((px[2] << 8) - next_px[2]) + prev_px[2] + 128) >> 8,
(((px[3] << 8) - next_px[3]) + prev_px[3] + 128) >> 8];
if subpixel_mask {
dest[0] = blend_strike_pixel(dest[0], offset_px[0], offset_px[0]);
dest[1] = blend_strike_pixel(dest[1], offset_px[1], offset_px[1]);
dest[2] = blend_strike_pixel(dest[2], offset_px[2], offset_px[2]);
dest[3] = blend_strike_pixel(dest[3], offset_px[3], offset_px[3]);
} else {
dest[0] = blend_strike_pixel(dest[0], offset_px[0], offset_px[3]);
dest[1] = blend_strike_pixel(dest[1], offset_px[1], offset_px[3]);
dest[2] = blend_strike_pixel(dest[2], offset_px[2], offset_px[3]);
dest[3] = blend_strike_pixel(dest[3], offset_px[3], offset_px[3]);
}
prev_px = next_px;
}
if offset_fract > 0 {
let dest = &mut dest_row[offset_integer + src_stride .. ];
let offset_px = [(prev_px[0] + 128) >> 8,
(prev_px[1] + 128) >> 8,
(prev_px[2] + 128) >> 8,
(prev_px[3] + 128) >> 8];
if subpixel_mask {
dest[0] = blend_strike_pixel(dest[0], offset_px[0], offset_px[0]);
dest[1] = blend_strike_pixel(dest[1], offset_px[1], offset_px[1]);
dest[2] = blend_strike_pixel(dest[2], offset_px[2], offset_px[2]);
dest[3] = blend_strike_pixel(dest[3], offset_px[3], offset_px[3]);
} else {
dest[0] = blend_strike_pixel(dest[0], offset_px[0], offset_px[3]);
dest[1] = blend_strike_pixel(dest[1], offset_px[1], offset_px[3]);
dest[2] = blend_strike_pixel(dest[2], offset_px[2], offset_px[3]);
dest[3] = blend_strike_pixel(dest[3], offset_px[3], offset_px[3]);
}
}
}
}
#[allow(dead_code)]
pub fn apply_multistrike_bold(
src_bitmap: &[u8],
width: usize,
height: usize,
subpixel_mask: bool,
extra_strikes: usize,
pixel_step: f64,
) -> (Vec<u8>, usize) {
let src_stride = width * 4;
let extra_width = (extra_strikes as f64 * pixel_step).ceil() as usize;
let dest_width = width + extra_width;
let dest_stride = dest_width * 4;
let mut dest_bitmap = vec![0u8; dest_stride * height];
for (src_row, dest_row) in src_bitmap.chunks(src_stride).zip(dest_bitmap.chunks_mut(dest_stride)) {
dest_row[0 .. src_stride].copy_from_slice(src_row);
}
for i in 1 ..= extra_strikes {
let offset = i as f64 * pixel_step;
blend_strike(&mut dest_bitmap, src_bitmap, width, height, subpixel_mask, offset);
}
(dest_bitmap, dest_width)
}
pub struct RasterizedGlyph {
pub top: f32,
pub left: f32,
pub width: i32,
pub height: i32,
pub scale: f32,
pub format: GlyphFormat,
pub bytes: Vec<u8>,
}
impl RasterizedGlyph {
#[allow(dead_code)]
pub fn downscale_bitmap_if_required(&mut self, font: &FontInstance) {
match self.format {
GlyphFormat::Bitmap | GlyphFormat::ColorBitmap => {},
_ => return,
}
let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
let upscaled = x_scale.max(y_scale) as f32;
let mut new_scale = self.scale;
if new_scale * upscaled <= 0.0 {
return;
}
let mut steps = 0;
while new_scale * upscaled <= 0.5 {
new_scale *= 2.0;
steps += 1;
}
if steps == 0 {
return;
}
let new_width = (self.width as usize + (1 << steps) - 1) >> steps;
let new_height = (self.height as usize + (1 << steps) - 1) >> steps;
let mut new_bytes: Vec<u8> = Vec::with_capacity(new_width * new_height * 4);
for y in 0 .. new_height {
for x in 0 .. new_width {
let src_y = y << steps;
let src_x = x << steps;
let y_samples = (1 << steps).min(self.height as usize - src_y);
let x_samples = (1 << steps).min(self.width as usize - src_x);
let num_samples = (x_samples * y_samples) as u32;
let mut src_idx = (src_y * self.width as usize + src_x) * 4;
let mut accum = [num_samples / 2; 4];
for _ in 0 .. y_samples {
for _ in 0 .. x_samples {
accum[0] += self.bytes[src_idx + 0] as u32;
accum[1] += self.bytes[src_idx + 1] as u32;
accum[2] += self.bytes[src_idx + 2] as u32;
accum[3] += self.bytes[src_idx + 3] as u32;
src_idx += 4;
}
src_idx += (self.width as usize - x_samples) * 4;
}
new_bytes.extend_from_slice(&[
(accum[0] / num_samples) as u8,
(accum[1] / num_samples) as u8,
(accum[2] / num_samples) as u8,
(accum[3] / num_samples) as u8,
]);
}
}
self.top /= (1 << steps) as f32;
self.left /= (1 << steps) as f32;
self.width = new_width as i32;
self.height = new_height as i32;
self.scale = new_scale;
self.bytes = new_bytes;
}
}
pub struct FontContexts {
worker_contexts: Vec<Mutex<FontContext>>,
#[allow(dead_code)]
workers: Arc<ThreadPool>,
locked_mutex: Mutex<bool>,
locked_cond: Condvar,
}
impl FontContexts {
pub fn lock_context(&self, id: usize) -> MutexGuard<FontContext> {
self.worker_contexts[id].lock().unwrap()
}
pub fn lock_any_context(&self) -> MutexGuard<FontContext> {
for context in &self.worker_contexts {
if let Ok(mutex) = context.try_lock() {
return mutex;
}
}
self.lock_context(0)
}
pub fn num_worker_contexts(&self) -> usize {
self.worker_contexts.len()
}
}
pub trait AsyncForEach<T> {
fn async_for_each<F: Fn(MutexGuard<T>) + Send + 'static>(&self, f: F);
}
impl AsyncForEach<FontContext> for Arc<FontContexts> {
fn async_for_each<F: Fn(MutexGuard<FontContext>) + Send + 'static>(&self, f: F) {
let mut locked = self.locked_mutex.lock().unwrap();
*locked = false;
let font_contexts = self.clone();
self.workers.spawn(move || {
let mut locks = Vec::with_capacity(font_contexts.num_worker_contexts());
for i in 0 .. font_contexts.num_worker_contexts() {
locks.push(font_contexts.lock_context(i));
}
*font_contexts.locked_mutex.lock().unwrap() = true;
font_contexts.locked_cond.notify_all();
for context in locks {
f(context);
}
});
while !*locked {
locked = self.locked_cond.wait(locked).unwrap();
}
}
}
pub struct GlyphRasterizer {
#[allow(dead_code)]
workers: Arc<ThreadPool>,
font_contexts: Arc<FontContexts>,
dedicated_thread: Option<GlyphRasterThread>,
fonts: FastHashSet<FontKey>,
pending_glyph_count: usize,
pending_glyph_jobs: usize,
glyph_request_count: usize,
pending_glyph_requests: FastHashMap<FontInstance, SmallVec<[GlyphKey; 16]>>,
glyph_rx: Receiver<GlyphRasterJob>,
glyph_tx: Sender<GlyphRasterJob>,
fonts_to_remove: Vec<FontKey>,
font_instances_to_remove: Vec<FontInstance>,
enable_multithreading: bool,
can_use_r8_format: bool,
}
impl GlyphRasterizer {
pub fn new(workers: Arc<ThreadPool>, dedicated_thread: Option<GlyphRasterThread>, can_use_r8_format: bool) -> Self {
let (glyph_tx, glyph_rx) = unbounded();
let num_workers = workers.current_num_threads();
let mut contexts = Vec::with_capacity(num_workers);
for _ in 0 .. num_workers {
contexts.push(Mutex::new(FontContext::new()));
}
let font_context = FontContexts {
worker_contexts: contexts,
workers: Arc::clone(&workers),
locked_mutex: Mutex::new(false),
locked_cond: Condvar::new(),
};
GlyphRasterizer {
font_contexts: Arc::new(font_context),
fonts: FastHashSet::default(),
dedicated_thread,
pending_glyph_jobs: 0,
pending_glyph_count: 0,
glyph_request_count: 0,
glyph_rx,
glyph_tx,
workers,
fonts_to_remove: Vec::new(),
font_instances_to_remove: Vec::new(),
enable_multithreading: true,
pending_glyph_requests: FastHashMap::default(),
can_use_r8_format,
}
}
pub fn add_font(&mut self, font_key: FontKey, template: FontTemplate) {
if self.fonts.insert(font_key.clone()) {
if let Some(thread) = &self.dedicated_thread {
let _ = thread.tx.send(GlyphRasterMsg::AddFont { font_key, template });
} else {
self.font_contexts.async_for_each(move |mut context| {
context.add_font(&font_key, &template);
});
}
}
}
pub fn delete_font(&mut self, font_key: FontKey) {
self.fonts_to_remove.push(font_key);
}
pub fn delete_fonts(&mut self, font_keys: &[FontKey]) {
self.fonts_to_remove.extend_from_slice(font_keys);
}
pub fn delete_font_instance(&mut self, instance: &FontInstance) {
self.font_instances_to_remove.push(instance.clone());
}
pub fn prepare_font(&self, font: &mut FontInstance) {
FontContext::prepare_font(font);
font.transform = font.transform.quantize();
}
pub fn has_font(&self, font_key: FontKey) -> bool {
self.fonts.contains(&font_key)
}
pub fn get_glyph_dimensions(
&mut self,
font: &FontInstance,
glyph_index: GlyphIndex,
) -> Option<GlyphDimensions> {
let glyph_key = GlyphKey::new(
glyph_index,
DevicePoint::zero(),
SubpixelDirection::None,
);
self.font_contexts
.lock_any_context()
.get_glyph_dimensions(font, &glyph_key)
}
pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
self.font_contexts
.lock_any_context()
.get_glyph_index(font_key, ch)
}
fn remove_dead_fonts(&mut self) {
if self.fonts_to_remove.is_empty() && self.font_instances_to_remove.is_empty() {
return
}
profile_scope!("remove_dead_fonts");
let mut fonts_to_remove = mem::replace(& mut self.fonts_to_remove, Vec::new());
fonts_to_remove.retain(|font| self.fonts.remove(font));
let font_instances_to_remove = mem::replace(& mut self.font_instances_to_remove, Vec::new());
if let Some(thread) = &self.dedicated_thread {
for font_key in fonts_to_remove {
let _ = thread.tx.send(GlyphRasterMsg::DeleteFont { font_key });
}
for instance in font_instances_to_remove {
let _ = thread.tx.send(GlyphRasterMsg::DeleteFontInstance { instance });
}
} else {
self.font_contexts.async_for_each(move |mut context| {
for font_key in &fonts_to_remove {
context.delete_font(font_key);
}
for instance in &font_instances_to_remove {
context.delete_font_instance(instance);
}
});
}
}
#[cfg(feature = "replay")]
pub fn reset(&mut self) {
self.pending_glyph_jobs = 0;
self.pending_glyph_count = 0;
self.glyph_request_count = 0;
self.fonts_to_remove.clear();
self.font_instances_to_remove.clear();
}
}
trait AddFont {
fn add_font(&mut self, font_key: &FontKey, template: &FontTemplate);
}
impl AddFont for FontContext {
fn add_font(&mut self, font_key: &FontKey, template: &FontTemplate) {
match *template {
FontTemplate::Raw(ref bytes, index) => {
self.add_raw_font(font_key, bytes.clone(), index);
}
FontTemplate::Native(ref native_font_handle) => {
self.add_native_font(font_key, (*native_font_handle).clone());
}
}
}
}
#[allow(dead_code)]
pub struct GlyphRasterJob {
pub font: Arc<FontInstance>,
pub key: GlyphKey,
pub result: GlyphRasterResult,
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum GlyphRasterError {
LoadFailed,
}
#[allow(dead_code)]
pub type GlyphRasterResult = Result<RasterizedGlyph, GlyphRasterError>;
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct GpuGlyphCacheKey(pub u32);
fn process_glyph(
context: &mut FontContext,
can_use_r8_format: bool,
font: Arc<FontInstance>,
key: GlyphKey,
) -> GlyphRasterJob {
profile_scope!("glyph-raster");
let result = context.rasterize_glyph(&font, &key);
let mut job = GlyphRasterJob {
font: font,
key: key.clone(),
result,
};
if let Ok(ref mut glyph) = job.result {
let bpp = 4; assert_eq!(
glyph.bytes.len(),
bpp * (glyph.width * glyph.height) as usize
);
fn over(dst: u8, src: u8) -> u8 {
let a = src as u32;
let a = 256 - a;
let dst = ((dst as u32 * a) >> 8) as u8;
src + dst
}
if GLYPH_FLASHING.load(Ordering::Relaxed) {
let color = (random() & 0xff) as u8;
for i in &mut glyph.bytes {
*i = over(*i, color);
}
}
assert_eq!((glyph.left.fract(), glyph.top.fract()), (0.0, 0.0));
glyph.downscale_bitmap_if_required(&job.font);
if glyph.format.image_format(can_use_r8_format).bytes_per_pixel() == 1 {
glyph.bytes = glyph.bytes
.chunks_mut(4)
.map(|pixel| pixel[3])
.collect::<Vec<_>>();
}
}
job
}
pub enum GlyphRasterMsg {
Rasterize {
font: Arc<FontInstance>,
glyphs: SmallVec<[GlyphKey; 16]>,
can_use_r8_format: bool,
tx: Sender<GlyphRasterJob>,
},
AddFont { font_key: FontKey, template: FontTemplate },
DeleteFont { font_key: FontKey },
DeleteFontInstance { instance: FontInstance },
ShutDown,
}
#[derive(Clone)]
pub struct GlyphRasterThread {
tx: Sender<GlyphRasterMsg>,
}
impl GlyphRasterThread {
pub fn new(
on_start: impl FnOnce() + Send + 'static,
on_end: impl FnOnce() + Send+ 'static,
) -> std::io::Result<Self> {
let (tx, rx) = unbounded();
std::thread::Builder::new().name("Glyph rasterizer".to_string()).spawn(move || {
on_start();
let mut context = FontContext::new();
loop {
match rx.recv() {
Ok(GlyphRasterMsg::Rasterize { font, glyphs, can_use_r8_format, tx }) => {
for glyph in &glyphs {
let job = process_glyph(&mut context, can_use_r8_format, font.clone(), *glyph);
let _ = tx.send(job);
}
}
Ok(GlyphRasterMsg::AddFont { font_key, template }) => {
context.add_font(&font_key, &template)
}
Ok(GlyphRasterMsg::DeleteFont { font_key }) => {
context.delete_font(&font_key)
}
Ok(GlyphRasterMsg::DeleteFontInstance { instance }) => {
context.delete_font_instance(&instance)
}
Ok(GlyphRasterMsg::ShutDown) => {
break;
}
Err(..) => {
break;
}
}
}
on_end();
})?;
Ok(GlyphRasterThread {
tx,
})
}
pub fn shut_down(&self) {
let _ = self.tx.send(GlyphRasterMsg::ShutDown);
}
}
#[cfg(test)]
mod test_glyph_rasterizer {
use crate::profiler::GlyphRasterizeProfiler;
struct Profiler;
impl GlyphRasterizeProfiler for Profiler {
fn start_time(&mut self) {}
fn end_time(&mut self) -> f64 {
0.
}
fn set(&mut self, _value: f64) {}
}
#[test]
fn rasterize_200_glyphs() {
use rayon::ThreadPoolBuilder;
use std::fs::File;
use std::io::Read;
use api::{FontKey, FontInstanceKey, FontTemplate, IdNamespace};
use api::units::DevicePoint;
use std::sync::Arc;
use crate::rasterizer::{FontInstance, BaseFontInstance, GlyphKey, GlyphRasterizer};
let worker = ThreadPoolBuilder::new()
.thread_name(|idx|{ format!("WRWorker#{}", idx) })
.build();
let workers = Arc::new(worker.unwrap());
let mut glyph_rasterizer = GlyphRasterizer::new(workers, None, true);
let mut font_file =
File::open("../wrench/reftests/text/VeraBd.ttf").expect("Couldn't open font file");
let mut font_data = vec![];
font_file
.read_to_end(&mut font_data)
.expect("failed to read font file");
let font_key = FontKey::new(IdNamespace(0), 0);
glyph_rasterizer.add_font(font_key, FontTemplate::Raw(Arc::new(font_data), 0));
let font = FontInstance::from_base(Arc::new(BaseFontInstance::new(
FontInstanceKey::new(IdNamespace(0), 0),
font_key,
32.0,
None,
None,
Vec::new(),
)));
let subpx_dir = font.get_subpx_dir();
let mut glyph_keys = Vec::with_capacity(200);
for i in 0 .. 200 {
glyph_keys.push(GlyphKey::new(
i,
DevicePoint::zero(),
subpx_dir,
));
}
for i in 0 .. 4 {
glyph_rasterizer.request_glyphs(
font.clone(),
&glyph_keys[(50 * i) .. (50 * (i + 1))],
|_| true,
);
}
glyph_rasterizer.delete_font(font_key);
glyph_rasterizer.resolve_glyphs(
|_, _| {},
&mut Profiler,
);
}
#[test]
fn rasterize_large_glyphs() {
use rayon::ThreadPoolBuilder;
use std::fs::File;
use std::io::Read;
use api::{FontKey, FontInstanceKey, FontTemplate, IdNamespace};
use api::units::DevicePoint;
use std::sync::Arc;
use crate::rasterizer::{FontInstance, BaseFontInstance, GlyphKey, GlyphRasterizer};
let worker = ThreadPoolBuilder::new()
.thread_name(|idx|{ format!("WRWorker#{}", idx) })
.build();
let workers = Arc::new(worker.unwrap());
let mut glyph_rasterizer = GlyphRasterizer::new(workers, None, true);
let mut font_file =
File::open("../wrench/reftests/text/VeraBd.ttf").expect("Couldn't open font file");
let mut font_data = vec![];
font_file
.read_to_end(&mut font_data)
.expect("failed to read font file");
let font_key = FontKey::new(IdNamespace(0), 0);
glyph_rasterizer.add_font(font_key, FontTemplate::Raw(Arc::new(font_data), 0));
let font = FontInstance::from_base(Arc::new(BaseFontInstance::new(
FontInstanceKey::new(IdNamespace(0), 0),
font_key,
200.0,
None,
None,
Vec::new(),
)));
let subpx_dir = font.get_subpx_dir();
let mut glyph_keys = Vec::with_capacity(10);
for i in 0 .. 10 {
glyph_keys.push(GlyphKey::new(
i,
DevicePoint::zero(),
subpx_dir,
));
}
glyph_rasterizer.request_glyphs(
font.clone(),
&glyph_keys,
|_| true,
);
glyph_rasterizer.delete_font(font_key);
glyph_rasterizer.resolve_glyphs(
|_, _| {},
&mut Profiler,
);
}
#[test]
fn test_subpx_quantize() {
use crate::rasterizer::SubpixelOffset;
assert_eq!(SubpixelOffset::quantize(0.0), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(-0.0), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.1), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.01), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.05), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.12), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.124), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.125), SubpixelOffset::Quarter);
assert_eq!(SubpixelOffset::quantize(0.2), SubpixelOffset::Quarter);
assert_eq!(SubpixelOffset::quantize(0.25), SubpixelOffset::Quarter);
assert_eq!(SubpixelOffset::quantize(0.33), SubpixelOffset::Quarter);
assert_eq!(SubpixelOffset::quantize(0.374), SubpixelOffset::Quarter);
assert_eq!(SubpixelOffset::quantize(0.375), SubpixelOffset::Half);
assert_eq!(SubpixelOffset::quantize(0.4), SubpixelOffset::Half);
assert_eq!(SubpixelOffset::quantize(0.5), SubpixelOffset::Half);
assert_eq!(SubpixelOffset::quantize(0.58), SubpixelOffset::Half);
assert_eq!(SubpixelOffset::quantize(0.624), SubpixelOffset::Half);
assert_eq!(SubpixelOffset::quantize(0.625), SubpixelOffset::ThreeQuarters);
assert_eq!(SubpixelOffset::quantize(0.67), SubpixelOffset::ThreeQuarters);
assert_eq!(SubpixelOffset::quantize(0.7), SubpixelOffset::ThreeQuarters);
assert_eq!(SubpixelOffset::quantize(0.78), SubpixelOffset::ThreeQuarters);
assert_eq!(SubpixelOffset::quantize(0.874), SubpixelOffset::ThreeQuarters);
assert_eq!(SubpixelOffset::quantize(0.875), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.89), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.91), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.967), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(0.999), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(-1.0), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(1.0), SubpixelOffset::Zero);
assert_eq!(SubpixelOffset::quantize(1.5), SubpixelOffset::Half);
assert_eq!(SubpixelOffset::quantize(-1.625), SubpixelOffset::Half);
assert_eq!(SubpixelOffset::quantize(-4.33), SubpixelOffset::ThreeQuarters);
}
}