Cache loaded fonts and glyph outlines.
Approximately a 70% CPU time improvement on the NanoVG demo.
This commit is contained in:
parent
582f025c91
commit
0b43f629cd
|
@ -285,6 +285,7 @@ dependencies = [
|
||||||
"gl 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gl 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"image 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"image 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jemallocator 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jemallocator 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pathfinder_canvas 0.1.0",
|
"pathfinder_canvas 0.1.0",
|
||||||
"pathfinder_color 0.1.0",
|
"pathfinder_color 0.1.0",
|
||||||
"pathfinder_content 0.1.0",
|
"pathfinder_content 0.1.0",
|
||||||
|
|
|
@ -94,16 +94,18 @@ impl Canvas {
|
||||||
self.scene
|
self.scene
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_context_2d(self, font_context: CanvasFontContext) -> CanvasRenderingContext2D {
|
pub fn get_context_2d(self, canvas_font_context: CanvasFontContext)
|
||||||
|
-> CanvasRenderingContext2D {
|
||||||
#[cfg(feature = "pf-text")]
|
#[cfg(feature = "pf-text")]
|
||||||
let default_font_collection = font_context.default_font_collection.clone();
|
let default_font_collection =
|
||||||
|
canvas_font_context.0.borrow().default_font_collection.clone();
|
||||||
#[cfg(not(feature = "pf-text"))]
|
#[cfg(not(feature = "pf-text"))]
|
||||||
let default_font_collection = Arc::new(FontCollection);
|
let default_font_collection = Arc::new(FontCollection);
|
||||||
CanvasRenderingContext2D {
|
CanvasRenderingContext2D {
|
||||||
canvas: self,
|
canvas: self,
|
||||||
current_state: State::default(default_font_collection),
|
current_state: State::default(default_font_collection),
|
||||||
saved_states: vec![],
|
saved_states: vec![],
|
||||||
font_context,
|
canvas_font_context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +120,7 @@ pub struct CanvasRenderingContext2D {
|
||||||
current_state: State,
|
current_state: State,
|
||||||
saved_states: Vec<State>,
|
saved_states: Vec<State>,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
font_context: CanvasFontContext,
|
canvas_font_context: CanvasFontContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CanvasRenderingContext2D {
|
impl CanvasRenderingContext2D {
|
||||||
|
@ -134,11 +136,6 @@ impl CanvasRenderingContext2D {
|
||||||
self.canvas
|
self.canvas
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn font_context(&self) -> CanvasFontContext {
|
|
||||||
self.font_context.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drawing rectangles
|
// Drawing rectangles
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -21,8 +21,10 @@ use pathfinder_geometry::transform2d::Transform2F;
|
||||||
use pathfinder_geometry::util;
|
use pathfinder_geometry::util;
|
||||||
use pathfinder_geometry::vector::{Vector2F, vec2f};
|
use pathfinder_geometry::vector::{Vector2F, vec2f};
|
||||||
use pathfinder_renderer::paint::PaintId;
|
use pathfinder_renderer::paint::PaintId;
|
||||||
use pathfinder_text::{SceneExt, TextRenderMode};
|
use pathfinder_text::{FontContext, FontRenderOptions, TextRenderMode};
|
||||||
use skribo::{FontCollection, FontFamily, FontRef, Layout, TextStyle};
|
use skribo::{FontCollection, FontFamily, FontRef, Layout, TextStyle};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
impl CanvasRenderingContext2D {
|
impl CanvasRenderingContext2D {
|
||||||
|
@ -51,14 +53,22 @@ impl CanvasRenderingContext2D {
|
||||||
let clip_path = self.current_state.clip_path;
|
let clip_path = self.current_state.clip_path;
|
||||||
let blend_mode = self.current_state.global_composite_operation.to_blend_mode();
|
let blend_mode = self.current_state.global_composite_operation.to_blend_mode();
|
||||||
|
|
||||||
drop(self.canvas.scene.push_layout(&layout,
|
// TODO(pcwalton): Report errors.
|
||||||
|
drop(self.canvas_font_context
|
||||||
|
.0
|
||||||
|
.borrow_mut()
|
||||||
|
.font_context
|
||||||
|
.push_layout(&mut self.canvas.scene,
|
||||||
|
&layout,
|
||||||
&TextStyle { size: self.current_state.font_size },
|
&TextStyle { size: self.current_state.font_size },
|
||||||
&(transform * self.current_state.transform),
|
&FontRenderOptions {
|
||||||
TextRenderMode::Fill,
|
transform: transform * self.current_state.transform,
|
||||||
HintingOptions::None,
|
render_mode: TextRenderMode::Fill,
|
||||||
|
hinting_options: HintingOptions::None,
|
||||||
clip_path,
|
clip_path,
|
||||||
blend_mode,
|
blend_mode,
|
||||||
paint_id));
|
paint_id,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_or_stroke_text(&mut self,
|
fn fill_or_stroke_text(&mut self,
|
||||||
|
@ -75,14 +85,21 @@ impl CanvasRenderingContext2D {
|
||||||
let transform = self.current_state.transform * Transform2F::from_translation(position);
|
let transform = self.current_state.transform * Transform2F::from_translation(position);
|
||||||
|
|
||||||
// TODO(pcwalton): Report errors.
|
// TODO(pcwalton): Report errors.
|
||||||
drop(self.canvas.scene.push_layout(&layout,
|
drop(self.canvas_font_context
|
||||||
|
.0
|
||||||
|
.borrow_mut()
|
||||||
|
.font_context
|
||||||
|
.push_layout(&mut self.canvas.scene,
|
||||||
|
&layout,
|
||||||
&TextStyle { size: self.current_state.font_size },
|
&TextStyle { size: self.current_state.font_size },
|
||||||
&transform,
|
&FontRenderOptions {
|
||||||
|
transform,
|
||||||
render_mode,
|
render_mode,
|
||||||
HintingOptions::None,
|
hinting_options: HintingOptions::None,
|
||||||
clip_path,
|
clip_path,
|
||||||
blend_mode,
|
blend_mode,
|
||||||
paint_id));
|
paint_id,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_text(&self, string: &str) -> Layout {
|
fn layout_text(&self, string: &str) -> Layout {
|
||||||
|
@ -100,7 +117,7 @@ impl CanvasRenderingContext2D {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_font<FC>(&mut self, font_collection: FC) where FC: IntoFontCollection {
|
pub fn set_font<FC>(&mut self, font_collection: FC) where FC: IntoFontCollection {
|
||||||
let font_collection = font_collection.into_font_collection(&self.font_context);
|
let font_collection = font_collection.into_font_collection(&self.canvas_font_context);
|
||||||
self.current_state.font_collection = font_collection;
|
self.current_state.font_collection = font_collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +196,10 @@ pub struct TextMetrics {
|
||||||
|
|
||||||
#[cfg(feature = "pf-text")]
|
#[cfg(feature = "pf-text")]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CanvasFontContext {
|
pub struct CanvasFontContext(pub(crate) Rc<RefCell<CanvasFontContextData>>);
|
||||||
|
|
||||||
|
pub(super) struct CanvasFontContextData {
|
||||||
|
pub(super) font_context: FontContext<Font>,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(super) font_source: Arc<dyn Source>,
|
pub(super) font_source: Arc<dyn Source>,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -196,10 +216,11 @@ impl CanvasFontContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CanvasFontContext {
|
CanvasFontContext(Rc::new(RefCell::new(CanvasFontContextData {
|
||||||
font_source,
|
font_source,
|
||||||
default_font_collection: Arc::new(default_font_collection),
|
default_font_collection: Arc::new(default_font_collection),
|
||||||
}
|
font_context: FontContext::new(),
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A convenience method to create a font context with the system source.
|
/// A convenience method to create a font context with the system source.
|
||||||
|
@ -212,6 +233,18 @@ impl CanvasFontContext {
|
||||||
pub fn from_fonts<I>(fonts: I) -> CanvasFontContext where I: Iterator<Item = Handle> {
|
pub fn from_fonts<I>(fonts: I) -> CanvasFontContext where I: Iterator<Item = Handle> {
|
||||||
CanvasFontContext::new(Arc::new(MemSource::from_fonts(fonts).unwrap()))
|
CanvasFontContext::new(Arc::new(MemSource::from_fonts(fonts).unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_font_by_postscript_name(&self, postscript_name: &str) -> Font {
|
||||||
|
let this = self.0.borrow();
|
||||||
|
if let Some(cached_font) = this.font_context.get_cached_font(postscript_name) {
|
||||||
|
return (*cached_font).clone();
|
||||||
|
}
|
||||||
|
this.font_source
|
||||||
|
.select_by_postscript_name(postscript_name)
|
||||||
|
.expect("Couldn't find a font with that PostScript name!")
|
||||||
|
.load()
|
||||||
|
.expect("Failed to load the font!")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Text layout utilities
|
// Text layout utilities
|
||||||
|
@ -413,6 +446,7 @@ impl IntoFontCollection for Vec<FontFamily> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
impl IntoFontCollection for Handle {
|
impl IntoFontCollection for Handle {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_font_collection(self, context: &CanvasFontContext) -> Arc<FontCollection> {
|
fn into_font_collection(self, context: &CanvasFontContext) -> Arc<FontCollection> {
|
||||||
|
@ -422,15 +456,18 @@ impl IntoFontCollection for Handle {
|
||||||
|
|
||||||
impl<'a> IntoFontCollection for &'a [Handle] {
|
impl<'a> IntoFontCollection for &'a [Handle] {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_font_collection(self, _: &CanvasFontContext) -> Arc<FontCollection> {
|
fn into_font_collection(self, context: &CanvasFontContext) -> Arc<FontCollection> {
|
||||||
let mut font_collection = FontCollection::new();
|
let mut font_collection = FontCollection::new();
|
||||||
for handle in self {
|
for handle in self {
|
||||||
|
let postscript_name = handle.postscript_name();
|
||||||
|
|
||||||
let font = handle.load().expect("Failed to load the font!");
|
let font = handle.load().expect("Failed to load the font!");
|
||||||
font_collection.add_family(FontFamily::new_from_font(font));
|
font_collection.add_family(FontFamily::new_from_font(font));
|
||||||
}
|
}
|
||||||
Arc::new(font_collection)
|
Arc::new(font_collection)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
impl IntoFontCollection for Font {
|
impl IntoFontCollection for Font {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -453,10 +490,7 @@ impl<'a> IntoFontCollection for &'a [Font] {
|
||||||
impl<'a> IntoFontCollection for &'a str {
|
impl<'a> IntoFontCollection for &'a str {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_font_collection(self, context: &CanvasFontContext) -> Arc<FontCollection> {
|
fn into_font_collection(self, context: &CanvasFontContext) -> Arc<FontCollection> {
|
||||||
context.font_source
|
context.get_font_by_postscript_name(self).into_font_collection(context)
|
||||||
.select_by_postscript_name(self)
|
|
||||||
.expect("Couldn't find a font with that PostScript name!")
|
|
||||||
.into_font_collection(context)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,11 +499,7 @@ impl<'a, 'b> IntoFontCollection for &'a [&'b str] {
|
||||||
fn into_font_collection(self, context: &CanvasFontContext) -> Arc<FontCollection> {
|
fn into_font_collection(self, context: &CanvasFontContext) -> Arc<FontCollection> {
|
||||||
let mut font_collection = FontCollection::new();
|
let mut font_collection = FontCollection::new();
|
||||||
for postscript_name in self {
|
for postscript_name in self {
|
||||||
let font = context.font_source
|
let font = context.get_font_by_postscript_name(postscript_name);
|
||||||
.select_by_postscript_name(postscript_name)
|
|
||||||
.expect("Failed to find a font with that PostScript name!")
|
|
||||||
.load()
|
|
||||||
.expect("Failed to load the font!");
|
|
||||||
font_collection.add_family(FontFamily::new_from_font(font));
|
font_collection.add_family(FontFamily::new_from_font(font));
|
||||||
}
|
}
|
||||||
Arc::new(font_collection)
|
Arc::new(font_collection)
|
||||||
|
|
|
@ -16,6 +16,10 @@ version = "0.23"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["png"]
|
features = ["png"]
|
||||||
|
|
||||||
|
[dependencies.log]
|
||||||
|
version = "0.4"
|
||||||
|
features = ["release_max_level_info"]
|
||||||
|
|
||||||
[dependencies.pathfinder_canvas]
|
[dependencies.pathfinder_canvas]
|
||||||
path = "../../canvas"
|
path = "../../canvas"
|
||||||
features = ["pf-text"]
|
features = ["pf-text"]
|
||||||
|
|
|
@ -1544,7 +1544,8 @@ fn main() {
|
||||||
gpu_graph.render(&mut context, vec2f(415.0, 5.0));
|
gpu_graph.render(&mut context, vec2f(415.0, 5.0));
|
||||||
|
|
||||||
// Render the canvas to screen.
|
// Render the canvas to screen.
|
||||||
let scene = SceneProxy::from_scene(context.into_canvas().into_scene(), RayonExecutor);
|
let canvas = context.into_canvas();
|
||||||
|
let scene = SceneProxy::from_scene(canvas.into_scene(), RayonExecutor);
|
||||||
scene.build_and_render(&mut renderer, BuildOptions::default());
|
scene.build_and_render(&mut renderer, BuildOptions::default());
|
||||||
window.gl_swap_window();
|
window.gl_swap_window();
|
||||||
|
|
||||||
|
|
226
text/src/lib.rs
226
text/src/lib.rs
|
@ -11,138 +11,182 @@
|
||||||
use font_kit::error::GlyphLoadingError;
|
use font_kit::error::GlyphLoadingError;
|
||||||
use font_kit::hinting::HintingOptions;
|
use font_kit::hinting::HintingOptions;
|
||||||
use font_kit::loader::Loader;
|
use font_kit::loader::Loader;
|
||||||
|
use font_kit::loaders::default::Font as DefaultLoader;
|
||||||
use font_kit::outline::OutlineSink;
|
use font_kit::outline::OutlineSink;
|
||||||
use pathfinder_content::effects::BlendMode;
|
use pathfinder_content::effects::BlendMode;
|
||||||
use pathfinder_content::outline::{Contour, Outline};
|
use pathfinder_content::outline::{Contour, Outline};
|
||||||
use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle};
|
use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle};
|
||||||
use pathfinder_geometry::line_segment::LineSegment2F;
|
use pathfinder_geometry::line_segment::LineSegment2F;
|
||||||
use pathfinder_geometry::transform2d::Transform2F;
|
use pathfinder_geometry::transform2d::Transform2F;
|
||||||
use pathfinder_geometry::vector::Vector2F;
|
use pathfinder_geometry::vector::{Vector2F, vec2f};
|
||||||
use pathfinder_renderer::paint::PaintId;
|
use pathfinder_renderer::paint::PaintId;
|
||||||
use pathfinder_renderer::scene::{ClipPathId, DrawPath, Scene};
|
use pathfinder_renderer::scene::{ClipPathId, DrawPath, Scene};
|
||||||
use skribo::{FontCollection, Layout, TextStyle};
|
use skribo::{FontCollection, Layout, TextStyle};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
// FIXME(pcwalton): Too many parameters!
|
#[derive(Clone)]
|
||||||
pub trait SceneExt {
|
pub struct FontContext<F> where F: Loader {
|
||||||
// TODO(pcwalton): Support stroked glyphs.
|
font_info: HashMap<String, FontInfo<F>>,
|
||||||
fn push_glyph<F>(&mut self,
|
|
||||||
font: &F,
|
|
||||||
glyph_id: u32,
|
|
||||||
transform: &Transform2F,
|
|
||||||
render_mode: TextRenderMode,
|
|
||||||
hinting_options: HintingOptions,
|
|
||||||
clip_path: Option<ClipPathId>,
|
|
||||||
blend_mode: BlendMode,
|
|
||||||
paint_id: PaintId)
|
|
||||||
-> Result<(), GlyphLoadingError>
|
|
||||||
where F: Loader;
|
|
||||||
|
|
||||||
fn push_layout(&mut self,
|
|
||||||
layout: &Layout,
|
|
||||||
style: &TextStyle,
|
|
||||||
transform: &Transform2F,
|
|
||||||
render_mode: TextRenderMode,
|
|
||||||
hinting_options: HintingOptions,
|
|
||||||
clip_path: Option<ClipPathId>,
|
|
||||||
blend_mode: BlendMode,
|
|
||||||
paint_id: PaintId)
|
|
||||||
-> Result<(), GlyphLoadingError>;
|
|
||||||
|
|
||||||
fn push_text(&mut self,
|
|
||||||
text: &str,
|
|
||||||
style: &TextStyle,
|
|
||||||
collection: &FontCollection,
|
|
||||||
transform: &Transform2F,
|
|
||||||
render_mode: TextRenderMode,
|
|
||||||
hinting_options: HintingOptions,
|
|
||||||
clip_path: Option<ClipPathId>,
|
|
||||||
blend_mode: BlendMode,
|
|
||||||
paint_id: PaintId)
|
|
||||||
-> Result<(), GlyphLoadingError>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SceneExt for Scene {
|
#[derive(Clone)]
|
||||||
#[inline]
|
struct FontInfo<F> where F: Loader {
|
||||||
fn push_glyph<F>(&mut self,
|
font: F,
|
||||||
font: &F,
|
outline_cache: HashMap<GlyphId, Outline>,
|
||||||
glyph_id: u32,
|
}
|
||||||
transform: &Transform2F,
|
|
||||||
render_mode: TextRenderMode,
|
|
||||||
hinting_options: HintingOptions,
|
|
||||||
clip_path: Option<ClipPathId>,
|
|
||||||
blend_mode: BlendMode,
|
|
||||||
paint_id: PaintId)
|
|
||||||
-> Result<(), GlyphLoadingError>
|
|
||||||
where F: Loader {
|
|
||||||
let mut outline_builder = OutlinePathBuilder::new(transform);
|
|
||||||
font.outline(glyph_id, hinting_options, &mut outline_builder)?;
|
|
||||||
let mut outline = outline_builder.build();
|
|
||||||
|
|
||||||
if let TextRenderMode::Stroke(stroke_style) = render_mode {
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct FontRenderOptions {
|
||||||
|
pub transform: Transform2F,
|
||||||
|
pub render_mode: TextRenderMode,
|
||||||
|
pub hinting_options: HintingOptions,
|
||||||
|
pub clip_path: Option<ClipPathId>,
|
||||||
|
pub blend_mode: BlendMode,
|
||||||
|
pub paint_id: PaintId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FontRenderOptions {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> FontRenderOptions {
|
||||||
|
FontRenderOptions {
|
||||||
|
transform: Transform2F::default(),
|
||||||
|
render_mode: TextRenderMode::Fill,
|
||||||
|
hinting_options: HintingOptions::None,
|
||||||
|
clip_path: None,
|
||||||
|
blend_mode: BlendMode::SrcOver,
|
||||||
|
paint_id: PaintId(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash)]
|
||||||
|
pub struct GlyphId(pub u32);
|
||||||
|
|
||||||
|
impl<F> FontContext<F> where F: Loader {
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> FontContext<F> {
|
||||||
|
FontContext { font_info: HashMap::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_glyph(&mut self,
|
||||||
|
scene: &mut Scene,
|
||||||
|
font: &F,
|
||||||
|
glyph_id: GlyphId,
|
||||||
|
render_options: &FontRenderOptions)
|
||||||
|
-> Result<(), GlyphLoadingError> {
|
||||||
|
let font_key = font.postscript_name();
|
||||||
|
let metrics = font.metrics();
|
||||||
|
|
||||||
|
// Insert the font into the cache if needed.
|
||||||
|
if let Some(ref font_key) = font_key {
|
||||||
|
if !self.font_info.contains_key(&*font_key) {
|
||||||
|
self.font_info.insert((*font_key).clone(), FontInfo::new((*font).clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if we have a cached outline.
|
||||||
|
//
|
||||||
|
// TODO(pcwalton): Cache hinted outlines too.
|
||||||
|
let mut cached_outline = None;
|
||||||
|
let can_cache_outline = font_key.is_some() &&
|
||||||
|
render_options.hinting_options == HintingOptions::None;
|
||||||
|
if can_cache_outline {
|
||||||
|
if let Some(ref font_info) = self.font_info.get(&*font_key.as_ref().unwrap()) {
|
||||||
|
if let Some(ref outline) = font_info.outline_cache.get(&glyph_id) {
|
||||||
|
cached_outline = Some((*outline).clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut outline = match cached_outline {
|
||||||
|
Some(mut cached_outline) => {
|
||||||
|
let scale = 1.0 / metrics.units_per_em as f32;
|
||||||
|
cached_outline.transform(&(render_options.transform *
|
||||||
|
Transform2F::from_scale(scale)));
|
||||||
|
cached_outline
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let transform = if can_cache_outline {
|
||||||
|
Transform2F::from_scale(metrics.units_per_em as f32)
|
||||||
|
} else {
|
||||||
|
render_options.transform
|
||||||
|
};
|
||||||
|
let mut outline_builder = OutlinePathBuilder::new(&transform);
|
||||||
|
font.outline(glyph_id.0, render_options.hinting_options, &mut outline_builder)?;
|
||||||
|
let mut outline = outline_builder.build();
|
||||||
|
if can_cache_outline {
|
||||||
|
let font_key = font_key.as_ref().unwrap();
|
||||||
|
let font_info = self.font_info.get_mut(&*font_key).unwrap();
|
||||||
|
font_info.outline_cache.insert(glyph_id, outline.clone());
|
||||||
|
let scale = 1.0 / metrics.units_per_em as f32;
|
||||||
|
outline.transform(&(render_options.transform *
|
||||||
|
Transform2F::from_scale(scale)));
|
||||||
|
}
|
||||||
|
outline
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let TextRenderMode::Stroke(stroke_style) = render_options.render_mode {
|
||||||
let mut stroke_to_fill = OutlineStrokeToFill::new(&outline, stroke_style);
|
let mut stroke_to_fill = OutlineStrokeToFill::new(&outline, stroke_style);
|
||||||
stroke_to_fill.offset();
|
stroke_to_fill.offset();
|
||||||
outline = stroke_to_fill.into_outline();
|
outline = stroke_to_fill.into_outline();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut path = DrawPath::new(outline, paint_id);
|
let mut path = DrawPath::new(outline, render_options.paint_id);
|
||||||
path.set_clip_path(clip_path);
|
path.set_clip_path(render_options.clip_path);
|
||||||
path.set_blend_mode(blend_mode);
|
path.set_blend_mode(render_options.blend_mode);
|
||||||
|
|
||||||
self.push_path(path);
|
scene.push_path(path);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_layout(&mut self,
|
/// Attempts to look up a font in the font cache.
|
||||||
|
#[inline]
|
||||||
|
pub fn get_cached_font(&self, postscript_name: &str) -> Option<&F> {
|
||||||
|
self.font_info.get(postscript_name).map(|font_info| &font_info.font)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontContext<DefaultLoader> {
|
||||||
|
pub fn push_layout(&mut self,
|
||||||
|
scene: &mut Scene,
|
||||||
layout: &Layout,
|
layout: &Layout,
|
||||||
style: &TextStyle,
|
style: &TextStyle,
|
||||||
transform: &Transform2F,
|
render_options: &FontRenderOptions)
|
||||||
render_mode: TextRenderMode,
|
|
||||||
hinting_options: HintingOptions,
|
|
||||||
clip_path: Option<ClipPathId>,
|
|
||||||
blend_mode: BlendMode,
|
|
||||||
paint_id: PaintId)
|
|
||||||
-> Result<(), GlyphLoadingError> {
|
-> Result<(), GlyphLoadingError> {
|
||||||
for glyph in &layout.glyphs {
|
for glyph in &layout.glyphs {
|
||||||
let offset = glyph.offset;
|
let offset = glyph.offset;
|
||||||
let font = &*glyph.font.font;
|
let font = &*glyph.font.font;
|
||||||
// FIXME(pcwalton): Cache this!
|
// FIXME(pcwalton): Cache this!
|
||||||
let scale = style.size / (font.metrics().units_per_em as f32);
|
let scale = style.size / (font.metrics().units_per_em as f32);
|
||||||
let scale = Vector2F::new(scale, -scale);
|
let scale = vec2f(scale, -scale);
|
||||||
let transform = *transform * Transform2F::from_scale(scale).translate(offset);
|
let render_options = FontRenderOptions {
|
||||||
self.push_glyph(font,
|
transform: render_options.transform *
|
||||||
glyph.glyph_id,
|
Transform2F::from_scale(scale).translate(offset),
|
||||||
&transform,
|
..*render_options
|
||||||
render_mode,
|
};
|
||||||
hinting_options,
|
self.push_glyph(scene, font, GlyphId(glyph.glyph_id), &render_options)?;
|
||||||
clip_path,
|
|
||||||
blend_mode,
|
|
||||||
paint_id)?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn push_text(&mut self,
|
pub fn push_text(&mut self,
|
||||||
|
scene: &mut Scene,
|
||||||
text: &str,
|
text: &str,
|
||||||
style: &TextStyle,
|
style: &TextStyle,
|
||||||
collection: &FontCollection,
|
collection: &FontCollection,
|
||||||
transform: &Transform2F,
|
render_options: &FontRenderOptions)
|
||||||
render_mode: TextRenderMode,
|
|
||||||
hinting_options: HintingOptions,
|
|
||||||
clip_path: Option<ClipPathId>,
|
|
||||||
blend_mode: BlendMode,
|
|
||||||
paint_id: PaintId)
|
|
||||||
-> Result<(), GlyphLoadingError> {
|
-> Result<(), GlyphLoadingError> {
|
||||||
let layout = skribo::layout(style, collection, text);
|
let layout = skribo::layout(style, collection, text);
|
||||||
self.push_layout(&layout,
|
self.push_layout(scene, &layout, style, render_options)
|
||||||
style,
|
}
|
||||||
&transform,
|
}
|
||||||
render_mode,
|
|
||||||
hinting_options,
|
impl<F> FontInfo<F> where F: Loader {
|
||||||
clip_path,
|
fn new(font: F) -> FontInfo<F> {
|
||||||
blend_mode,
|
FontInfo { font, outline_cache: HashMap::new() }
|
||||||
paint_id)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue