From 9138e1e0bb0c0e45abb93a2d113415adaefb37ec Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 29 May 2019 21:15:03 -0700 Subject: [PATCH] Pass in the font context to the canvas rendering context constructor so that it can be reused. Creating a system font source can do I/O on some platforms, so obviously we don't want to do it every frame. --- c/include/pathfinder/pathfinder.h | 6 ++- c/src/lib.rs | 20 +++++-- canvas/src/lib.rs | 57 ++++++++++++-------- examples/c_canvas_minimal/c_canvas_minimal.c | 3 +- examples/canvas_minimal/src/main.rs | 5 +- examples/canvas_moire/src/main.rs | 7 ++- examples/canvas_text/src/main.rs | 4 +- 7 files changed, 67 insertions(+), 35 deletions(-) diff --git a/c/include/pathfinder/pathfinder.h b/c/include/pathfinder/pathfinder.h index fd82dbb3..a879a05f 100644 --- a/c/include/pathfinder/pathfinder.h +++ b/c/include/pathfinder/pathfinder.h @@ -39,6 +39,8 @@ struct PFCanvas; typedef struct PFCanvas *PFCanvasRef; struct PFPath; typedef struct PFPath *PFPathRef; +struct PFCanvasFontContext; +typedef struct PFCanvasFontContext *PFCanvasFontContextRef; // `geometry` @@ -103,8 +105,10 @@ typedef struct PFSceneProxy *PFSceneProxyRef; // `canvas` -PFCanvasRef PFCanvasCreate(const PFPoint2DF *size); +PFCanvasRef PFCanvasCreate(PFCanvasFontContextRef font_context, const PFPoint2DF *size); void PFCanvasDestroy(PFCanvasRef canvas); +PFCanvasFontContextRef PFCanvasFontContextCreate(); +void PFCanvasFontContextDestroy(PFCanvasFontContextRef font_context); PFSceneRef PFCanvasCreateScene(PFCanvasRef canvas); void PFCanvasFillRect(PFCanvasRef canvas, const PFRectF *rect); void PFCanvasStrokeRect(PFCanvasRef canvas, const PFRectF *rect); diff --git a/c/src/lib.rs b/c/src/lib.rs index c529ba58..5cfeedc1 100644 --- a/c/src/lib.rs +++ b/c/src/lib.rs @@ -11,7 +11,7 @@ //! C bindings to Pathfinder. use gl; -use pathfinder_canvas::{CanvasRenderingContext2D, Path2D}; +use pathfinder_canvas::{CanvasFontContext, CanvasRenderingContext2D, Path2D}; use pathfinder_geometry::basic::point::{Point2DF, Point2DI}; use pathfinder_geometry::basic::rect::{RectF, RectI}; use pathfinder_geometry::color::ColorF; @@ -39,6 +39,7 @@ pub const PF_CLEAR_FLAGS_HAS_RECT: u8 = 0x8; // `canvas` pub type PFCanvasRef = *mut CanvasRenderingContext2D; pub type PFPathRef = *mut Path2D; +pub type PFCanvasFontContextRef = *mut CanvasFontContext; // `geometry` #[repr(C)] @@ -102,8 +103,11 @@ pub struct PFRenderOptions { // `canvas` #[no_mangle] -pub unsafe extern "C" fn PFCanvasCreate(size: *const PFPoint2DF) -> PFCanvasRef { - Box::into_raw(Box::new(CanvasRenderingContext2D::new((*size).to_rust()))) +pub unsafe extern "C" fn PFCanvasCreate(font_context: PFCanvasFontContextRef, + size: *const PFPoint2DF) + -> PFCanvasRef { + Box::into_raw(Box::new(CanvasRenderingContext2D::new(*Box::from_raw(font_context), + (*size).to_rust()))) } #[no_mangle] @@ -111,6 +115,16 @@ pub unsafe extern "C" fn PFCanvasDestroy(canvas: PFCanvasRef) { drop(Box::from_raw(canvas)) } +#[no_mangle] +pub unsafe extern "C" fn PFCanvasFontContextCreate() -> PFCanvasFontContextRef { + Box::into_raw(Box::new(CanvasFontContext::new())) +} + +#[no_mangle] +pub unsafe extern "C" fn PFCanvasFontContextDestroy(font_context: PFCanvasFontContextRef) { + drop(Box::from_raw(font_context)) +} + /// Consumes the canvas. #[no_mangle] pub unsafe extern "C" fn PFCanvasCreateScene(canvas: PFCanvasRef) -> PFSceneRef { diff --git a/canvas/src/lib.rs b/canvas/src/lib.rs index 2c0e25a0..35c1b37e 100644 --- a/canvas/src/lib.rs +++ b/canvas/src/lib.rs @@ -35,41 +35,24 @@ pub struct CanvasRenderingContext2D { scene: Scene, current_state: State, saved_states: Vec, - #[allow(dead_code)] - font_source: SystemSource, - #[allow(dead_code)] - default_font_collection: Arc, + font_context: CanvasFontContext, } impl CanvasRenderingContext2D { #[inline] - pub fn new(size: Point2DF) -> CanvasRenderingContext2D { + pub fn new(font_context: CanvasFontContext, size: Point2DF) -> CanvasRenderingContext2D { let mut scene = Scene::new(); scene.set_view_box(RectF::new(Point2DF::default(), size)); - CanvasRenderingContext2D::from_scene(scene) + CanvasRenderingContext2D::from_scene(font_context, scene) } - pub fn from_scene(scene: Scene) -> CanvasRenderingContext2D { - // TODO(pcwalton): Allow the user to cache this? - let font_source = SystemSource::new(); - - let mut default_font_collection = FontCollection::new(); - let default_font = - font_source.select_best_match(&[FamilyName::SansSerif], &Properties::new()) - .expect("Failed to select the default font!") - .load() - .expect("Failed to load the default font!"); - default_font_collection.add_family(FontFamily::new_from_font(default_font)); - let default_font_collection = Arc::new(default_font_collection); - + pub fn from_scene(font_context: CanvasFontContext, scene: Scene) -> CanvasRenderingContext2D { CanvasRenderingContext2D { scene, - current_state: State::default(default_font_collection.clone()), + current_state: State::default(font_context.default_font_collection.clone()), saved_states: vec![], - - font_source, - default_font_collection, + font_context, } } @@ -335,3 +318,31 @@ impl FillStyle { match *self { FillStyle::Color(color) => Paint { color } } } } + +#[derive(Clone)] +pub struct CanvasFontContext { + #[allow(dead_code)] + font_source: Arc, + #[allow(dead_code)] + default_font_collection: Arc, +} + +impl CanvasFontContext { + pub fn new() -> CanvasFontContext { + let font_source = Arc::new(SystemSource::new()); + + let mut default_font_collection = FontCollection::new(); + let default_font = + font_source.select_best_match(&[FamilyName::SansSerif], &Properties::new()) + .expect("Failed to select the default font!") + .load() + .expect("Failed to load the default font!"); + default_font_collection.add_family(FontFamily::new_from_font(default_font)); + let default_font_collection = Arc::new(default_font_collection); + + CanvasFontContext { + font_source, + default_font_collection, + } + } +} diff --git a/examples/c_canvas_minimal/c_canvas_minimal.c b/examples/c_canvas_minimal/c_canvas_minimal.c index 9b5f3777..9cdbe0a4 100644 --- a/examples/c_canvas_minimal/c_canvas_minimal.c +++ b/examples/c_canvas_minimal/c_canvas_minimal.c @@ -66,7 +66,8 @@ int main(int argc, const char **argv) { }); // Make a canvas. We're going to draw a house. - PFCanvasRef canvas = PFCanvasCreate(&(PFPoint2DF){640.0f, 480.0f}); + PFCanvasRef canvas = PFCanvasCreate(PFCanvasFontContextCreate(), + &(PFPoint2DF){640.0f, 480.0f}); // Set line width. PFCanvasSetLineWidth(canvas, 10.0f); diff --git a/examples/canvas_minimal/src/main.rs b/examples/canvas_minimal/src/main.rs index 786151b3..bf340d46 100644 --- a/examples/canvas_minimal/src/main.rs +++ b/examples/canvas_minimal/src/main.rs @@ -8,11 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use pathfinder_canvas::{CanvasRenderingContext2D, Path2D}; +use pathfinder_canvas::{CanvasFontContext, CanvasRenderingContext2D, Path2D}; use pathfinder_geometry::basic::point::{Point2DF, Point2DI}; use pathfinder_geometry::basic::rect::RectF; use pathfinder_geometry::color::ColorF; -use pathfinder_geometry::stroke::LineJoin; use pathfinder_gl::{GLDevice, GLVersion}; use pathfinder_gpu::resources::FilesystemResourceLoader; use pathfinder_gpu::{ClearParams, Device}; @@ -55,7 +54,7 @@ fn main() { renderer.device.clear(&ClearParams { color: Some(ColorF::white()), ..ClearParams::default() }); // Make a canvas. We're going to draw a house. - let mut canvas = CanvasRenderingContext2D::new(window_size.to_f32()); + let mut canvas = CanvasRenderingContext2D::new(CanvasFontContext::new(), window_size.to_f32()); // Set line width. canvas.set_line_width(10.0); diff --git a/examples/canvas_moire/src/main.rs b/examples/canvas_moire/src/main.rs index bd72423a..c231aec6 100644 --- a/examples/canvas_moire/src/main.rs +++ b/examples/canvas_moire/src/main.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use pathfinder_canvas::{CanvasRenderingContext2D, FillStyle, Path2D}; +use pathfinder_canvas::{CanvasFontContext, CanvasRenderingContext2D, FillStyle, Path2D}; use pathfinder_geometry::basic::point::{Point2DF, Point2DI}; use pathfinder_geometry::color::{ColorF, ColorU}; use pathfinder_gl::{GLDevice, GLVersion}; @@ -85,6 +85,7 @@ fn main() { struct MoireRenderer { renderer: Renderer, + font_context: CanvasFontContext, scene: SceneProxy, frame: i32, window_size: Point2DI, @@ -98,6 +99,7 @@ impl MoireRenderer { -> MoireRenderer { MoireRenderer { renderer, + font_context: CanvasFontContext::new(), scene: SceneProxy::new(RayonExecutor), frame: 0, window_size, @@ -128,7 +130,8 @@ impl MoireRenderer { }); // Make a canvas. - let mut canvas = CanvasRenderingContext2D::new(self.drawable_size.to_f32()); + let mut canvas = CanvasRenderingContext2D::new(self.font_context.clone(), + self.drawable_size.to_f32()); canvas.set_line_width(CIRCLE_THICKNESS * self.device_pixel_ratio); canvas.set_stroke_style(FillStyle::Color(foreground_color.to_u8())); canvas.set_global_alpha(0.75); diff --git a/examples/canvas_text/src/main.rs b/examples/canvas_text/src/main.rs index 93871097..5306138f 100644 --- a/examples/canvas_text/src/main.rs +++ b/examples/canvas_text/src/main.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use pathfinder_canvas::CanvasRenderingContext2D; +use pathfinder_canvas::{CanvasFontContext, CanvasRenderingContext2D}; use pathfinder_geometry::basic::point::{Point2DF, Point2DI}; use pathfinder_geometry::color::ColorF; use pathfinder_gl::{GLDevice, GLVersion}; @@ -53,7 +53,7 @@ fn main() { renderer.device.clear(&ClearParams { color: Some(ColorF::white()), ..ClearParams::default() }); // Make a canvas. We're going to draw some text. - let mut canvas = CanvasRenderingContext2D::new(window_size.to_f32()); + let mut canvas = CanvasRenderingContext2D::new(CanvasFontContext::new(), window_size.to_f32()); // Draw the text. canvas.set_font_size(32.0);