Start a simple HTML canvas-like API, and add a minimal example to show how to
use it.
This commit is contained in:
parent
4a4011d303
commit
9de7d95d33
|
@ -156,6 +156,20 @@ name = "byteorder"
|
|||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "canvas_minimal"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pathfinder_canvas 0.1.0",
|
||||
"pathfinder_geometry 0.3.0",
|
||||
"pathfinder_gl 0.1.0",
|
||||
"pathfinder_gpu 0.1.0",
|
||||
"pathfinder_renderer 0.1.0",
|
||||
"sdl2 0.32.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sdl2-sys 0.32.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.29"
|
||||
|
@ -1013,6 +1027,14 @@ dependencies = [
|
|||
"pathfinder_gpu 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathfinder_canvas"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"pathfinder_geometry 0.3.0",
|
||||
"pathfinder_renderer 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathfinder_demo"
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"canvas",
|
||||
"demo/android/rust",
|
||||
"demo/common",
|
||||
"demo/magicleap",
|
||||
"demo/native",
|
||||
"examples/canvas_minimal",
|
||||
"geometry",
|
||||
"gl",
|
||||
"gpu",
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "pathfinder_canvas"
|
||||
version = "0.1.0"
|
||||
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
|
||||
[dependencies.pathfinder_geometry]
|
||||
path = "../geometry"
|
||||
|
||||
[dependencies.pathfinder_renderer]
|
||||
path = "../renderer"
|
|
@ -0,0 +1,145 @@
|
|||
// pathfinder/canvas/src/lib.rs
|
||||
//
|
||||
// Copyright © 2019 The Pathfinder Project Developers.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! A simple API for Pathfinder that mirrors a subset of HTML canvas.
|
||||
|
||||
use pathfinder_geometry::basic::point::Point2DF32;
|
||||
use pathfinder_geometry::basic::rect::RectF32;
|
||||
use pathfinder_geometry::color::ColorU;
|
||||
use pathfinder_geometry::outline::{Contour, Outline};
|
||||
use pathfinder_geometry::stroke::OutlineStrokeToFill;
|
||||
use pathfinder_renderer::scene::{Paint, PathObject, Scene};
|
||||
use std::mem;
|
||||
|
||||
const HAIRLINE_STROKE_WIDTH: f32 = 0.0333;
|
||||
|
||||
pub struct CanvasRenderingContext2D {
|
||||
scene: Scene,
|
||||
current_paint: Paint,
|
||||
current_line_width: f32,
|
||||
}
|
||||
|
||||
impl CanvasRenderingContext2D {
|
||||
#[inline]
|
||||
pub fn new(size: Point2DF32) -> CanvasRenderingContext2D {
|
||||
let mut scene = Scene::new();
|
||||
scene.set_view_box(RectF32::new(Point2DF32::default(), size));
|
||||
CanvasRenderingContext2D::from_scene(scene)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_scene(scene: Scene) -> CanvasRenderingContext2D {
|
||||
CanvasRenderingContext2D {
|
||||
scene,
|
||||
current_paint: Paint { color: ColorU::black() },
|
||||
current_line_width: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_scene(self) -> Scene {
|
||||
self.scene
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fill_rect(&mut self, rect: RectF32) {
|
||||
let mut path = Path2D::new();
|
||||
path.rect(rect);
|
||||
self.fill_path(path);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn stroke_rect(&mut self, rect: RectF32) {
|
||||
let mut path = Path2D::new();
|
||||
path.rect(rect);
|
||||
self.stroke_path(path);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_line_width(&mut self, new_line_width: f32) {
|
||||
self.current_line_width = new_line_width
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fill_path(&mut self, path: Path2D) {
|
||||
let paint_id = self.scene.push_paint(&self.current_paint);
|
||||
self.scene.push_object(PathObject::new(path.into_outline(), paint_id, String::new()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn stroke_path(&mut self, path: Path2D) {
|
||||
let paint_id = self.scene.push_paint(&self.current_paint);
|
||||
let stroke_width = f32::max(self.current_line_width, HAIRLINE_STROKE_WIDTH);
|
||||
let mut stroke_to_fill = OutlineStrokeToFill::new(path.into_outline(), stroke_width);
|
||||
stroke_to_fill.offset();
|
||||
self.scene.push_object(PathObject::new(stroke_to_fill.outline, paint_id, String::new()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Path2D {
|
||||
outline: Outline,
|
||||
current_contour: Contour,
|
||||
}
|
||||
|
||||
// TODO(pcwalton): `arc`, `ellipse`
|
||||
impl Path2D {
|
||||
#[inline]
|
||||
pub fn new() -> Path2D {
|
||||
Path2D { outline: Outline::new(), current_contour: Contour::new() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn close_path(&mut self) {
|
||||
self.current_contour.close();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn move_to(&mut self, to: Point2DF32) {
|
||||
// TODO(pcwalton): Cull degenerate contours.
|
||||
self.flush_current_contour();
|
||||
self.current_contour.push_endpoint(to);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn line_to(&mut self, to: Point2DF32) {
|
||||
self.current_contour.push_endpoint(to);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn quadratic_curve_to(&mut self, ctrl: Point2DF32, to: Point2DF32) {
|
||||
self.current_contour.push_quadratic(ctrl, to);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bezier_curve_to(&mut self, ctrl0: Point2DF32, ctrl1: Point2DF32, to: Point2DF32) {
|
||||
self.current_contour.push_cubic(ctrl0, ctrl1, to);
|
||||
}
|
||||
|
||||
pub fn rect(&mut self, rect: RectF32) {
|
||||
self.flush_current_contour();
|
||||
self.current_contour.push_endpoint(rect.origin());
|
||||
self.current_contour.push_endpoint(rect.upper_right());
|
||||
self.current_contour.push_endpoint(rect.lower_right());
|
||||
self.current_contour.push_endpoint(rect.lower_left());
|
||||
self.current_contour.close();
|
||||
}
|
||||
|
||||
fn into_outline(mut self) -> Outline {
|
||||
self.flush_current_contour();
|
||||
self.outline
|
||||
}
|
||||
|
||||
fn flush_current_contour(&mut self) {
|
||||
if !self.current_contour.is_empty() {
|
||||
self.outline.push_contour(mem::replace(&mut self.current_contour, Contour::new()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -242,8 +242,7 @@ impl<W> DemoApp<W> where W: Window {
|
|||
subpixel_aa_enabled: self.ui.subpixel_aa_effect_enabled,
|
||||
};
|
||||
|
||||
let built_options = render_options.prepare(self.scene_metadata.bounds);
|
||||
self.render_command_stream = Some(self.scene_proxy.build_with_stream(built_options));
|
||||
self.render_command_stream = Some(self.scene_proxy.build_with_stream(render_options));
|
||||
}
|
||||
|
||||
fn handle_events(&mut self, events: Vec<Event>) -> Vec<UIEvent> {
|
||||
|
@ -769,7 +768,6 @@ impl BackgroundColor {
|
|||
|
||||
struct SceneMetadata {
|
||||
view_box: RectF32,
|
||||
bounds: RectF32,
|
||||
monochrome_color: Option<ColorU>,
|
||||
}
|
||||
|
||||
|
@ -780,6 +778,6 @@ impl SceneMetadata {
|
|||
let view_box = scene.view_box();
|
||||
let monochrome_color = scene.monochrome_color();
|
||||
scene.set_view_box(RectF32::new(Point2DF32::default(), viewport_size.to_f32()));
|
||||
SceneMetadata { view_box, monochrome_color, bounds: scene.bounds() }
|
||||
SceneMetadata { view_box, monochrome_color }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
[package]
|
||||
name = "canvas_minimal"
|
||||
version = "0.1.0"
|
||||
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
gl = "0.6"
|
||||
sdl2 = "0.32"
|
||||
sdl2-sys = "0.32"
|
||||
|
||||
[dependencies.pathfinder_canvas]
|
||||
path = "../../canvas"
|
||||
|
||||
[dependencies.pathfinder_geometry]
|
||||
path = "../../geometry"
|
||||
|
||||
[dependencies.pathfinder_gl]
|
||||
path = "../../gl"
|
||||
|
||||
[dependencies.pathfinder_gpu]
|
||||
path = "../../gpu"
|
||||
|
||||
[dependencies.pathfinder_renderer]
|
||||
path = "../../renderer"
|
|
@ -0,0 +1,89 @@
|
|||
// pathfinder/canvas_minimal/src/main.rs
|
||||
//
|
||||
// Copyright © 2019 The Pathfinder Project Developers.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use pathfinder_canvas::{CanvasRenderingContext2D, Path2D};
|
||||
use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32};
|
||||
use pathfinder_geometry::basic::rect::RectF32;
|
||||
use pathfinder_geometry::color::ColorF;
|
||||
use pathfinder_gl::{GLDevice, GLVersion};
|
||||
use pathfinder_gpu::resources::FilesystemResourceLoader;
|
||||
use pathfinder_gpu::{ClearParams, Device};
|
||||
use pathfinder_renderer::concurrent::rayon::RayonExecutor;
|
||||
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
|
||||
use pathfinder_renderer::gpu::renderer::{DestFramebuffer, Renderer};
|
||||
use pathfinder_renderer::options::RenderOptions;
|
||||
use sdl2::event::Event;
|
||||
use sdl2::keyboard::Keycode;
|
||||
use sdl2::video::GLProfile;
|
||||
|
||||
fn main() {
|
||||
// Set up SDL2.
|
||||
let sdl_context = sdl2::init().unwrap();
|
||||
let video = sdl_context.video().unwrap();
|
||||
|
||||
// Make sure we have at least a GL 3.0 context. Pathfinder requires this.
|
||||
let gl_attributes = video.gl_attr();
|
||||
gl_attributes.set_context_profile(GLProfile::Core);
|
||||
gl_attributes.set_context_version(3, 3);
|
||||
|
||||
// Open a window.
|
||||
let window_size = Point2DI32::new(640, 480);
|
||||
let window = video.window("Minimal example", window_size.x() as u32, window_size.y() as u32)
|
||||
.opengl()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// Create the GL context, and make it current.
|
||||
let gl_context = window.gl_create_context().unwrap();
|
||||
gl::load_with(|name| video.gl_get_proc_address(name) as *const _);
|
||||
window.gl_make_current(&gl_context).unwrap();
|
||||
|
||||
// Create a Pathfinder renderer.
|
||||
let mut renderer = Renderer::new(GLDevice::new(GLVersion::GL3, 0),
|
||||
&FilesystemResourceLoader::locate(),
|
||||
DestFramebuffer::full_window(window_size));
|
||||
|
||||
// Clear to white.
|
||||
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());
|
||||
|
||||
// Set line width.
|
||||
canvas.set_line_width(10.0);
|
||||
|
||||
// Draw walls.
|
||||
canvas.stroke_rect(RectF32::new(Point2DF32::new(75.0, 140.0), Point2DF32::new(150.0, 110.0)));
|
||||
|
||||
// Draw door.
|
||||
canvas.fill_rect(RectF32::new(Point2DF32::new(130.0, 190.0), Point2DF32::new(40.0, 60.0)));
|
||||
|
||||
// Draw roof.
|
||||
let mut path = Path2D::new();
|
||||
path.move_to(Point2DF32::new(50.0, 140.0));
|
||||
path.line_to(Point2DF32::new(150.0, 60.0));
|
||||
path.line_to(Point2DF32::new(250.0, 140.0));
|
||||
path.close_path();
|
||||
canvas.stroke_path(path);
|
||||
|
||||
// Render the canvas to screen.
|
||||
let scene = SceneProxy::new(canvas.into_scene(), RayonExecutor);
|
||||
scene.build_and_render(&mut renderer, RenderOptions::default());
|
||||
window.gl_swap_window();
|
||||
|
||||
// Wait for a keypress.
|
||||
let mut event_pump = sdl_context.event_pump().unwrap();
|
||||
loop {
|
||||
match event_pump.wait_event() {
|
||||
Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => return,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -63,6 +63,11 @@ impl ColorF {
|
|||
ColorF(F32x4::default())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn white() -> ColorF {
|
||||
ColorF(F32x4::splat(1.0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn r(&self) -> f32 {
|
||||
self.0[0]
|
||||
|
|
|
@ -24,7 +24,7 @@ use std::mem;
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct Outline {
|
||||
pub contours: Vec<Contour>,
|
||||
pub(crate) contours: Vec<Contour>,
|
||||
pub(crate) bounds: RectF32,
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,6 @@ impl Outline {
|
|||
{
|
||||
let mut outline = Outline::new();
|
||||
let mut current_contour = Contour::new();
|
||||
let mut bounds = None;
|
||||
|
||||
for segment in segments {
|
||||
if segment.flags.contains(SegmentFlags::FIRST_IN_SUBPATH) {
|
||||
|
@ -75,8 +74,7 @@ impl Outline {
|
|||
if !current_contour.is_empty() {
|
||||
current_contour.close();
|
||||
let contour = mem::replace(&mut current_contour, Contour::new());
|
||||
contour.update_bounds(&mut bounds);
|
||||
outline.contours.push(contour);
|
||||
outline.push_contour(contour);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -99,15 +97,7 @@ impl Outline {
|
|||
current_contour.push_point(segment.baseline.to(), PointFlags::empty(), true);
|
||||
}
|
||||
|
||||
if !current_contour.is_empty() {
|
||||
current_contour.update_bounds(&mut bounds);
|
||||
outline.contours.push(current_contour);
|
||||
}
|
||||
|
||||
if let Some(bounds) = bounds {
|
||||
outline.bounds = bounds;
|
||||
}
|
||||
|
||||
outline.push_contour(current_contour);
|
||||
outline
|
||||
}
|
||||
|
||||
|
@ -116,6 +106,25 @@ impl Outline {
|
|||
self.bounds
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contours(&self) -> &[Contour] {
|
||||
&self.contours
|
||||
}
|
||||
|
||||
pub fn push_contour(&mut self, contour: Contour) {
|
||||
if contour.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.contours.is_empty() {
|
||||
self.bounds = contour.bounds;
|
||||
} else {
|
||||
self.bounds = self.bounds.union_rect(contour.bounds);
|
||||
}
|
||||
|
||||
self.contours.push(contour);
|
||||
}
|
||||
|
||||
pub fn transform(&mut self, transform: &Transform2DF32) {
|
||||
let mut new_bounds = None;
|
||||
for contour in &mut self.contours {
|
||||
|
@ -166,15 +175,9 @@ impl Outline {
|
|||
return;
|
||||
}
|
||||
|
||||
let mut new_bounds = None;
|
||||
for contour in mem::replace(&mut self.contours, vec![]) {
|
||||
let contour = ContourPolygonClipper::new(clip_polygon, contour).clip();
|
||||
if !contour.is_empty() {
|
||||
contour.update_bounds(&mut new_bounds);
|
||||
self.contours.push(contour);
|
||||
}
|
||||
self.push_contour(ContourPolygonClipper::new(clip_polygon, contour).clip());
|
||||
}
|
||||
self.bounds = new_bounds.unwrap_or_else(|| RectF32::default());
|
||||
}
|
||||
|
||||
pub fn clip_against_rect(&mut self, clip_rect: RectF32) {
|
||||
|
@ -182,15 +185,9 @@ impl Outline {
|
|||
return;
|
||||
}
|
||||
|
||||
let mut new_bounds = None;
|
||||
for contour in mem::replace(&mut self.contours, vec![]) {
|
||||
let contour = ContourRectClipper::new(clip_rect, contour).clip();
|
||||
if !contour.is_empty() {
|
||||
contour.update_bounds(&mut new_bounds);
|
||||
self.contours.push(contour);
|
||||
}
|
||||
self.push_contour(ContourRectClipper::new(clip_rect, contour).clip());
|
||||
}
|
||||
self.bounds = new_bounds.unwrap_or_else(|| RectF32::default());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -271,7 +268,25 @@ impl Contour {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn close(&mut self) {
|
||||
pub fn push_endpoint(&mut self, point: Point2DF32) {
|
||||
self.push_point(point, PointFlags::empty(), true);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn push_quadratic(&mut self, ctrl: Point2DF32, point: Point2DF32) {
|
||||
self.push_point(ctrl, PointFlags::CONTROL_POINT_0, true);
|
||||
self.push_point(point, PointFlags::empty(), true);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn push_cubic(&mut self, ctrl0: Point2DF32, ctrl1: Point2DF32, point: Point2DF32) {
|
||||
self.push_point(ctrl0, PointFlags::CONTROL_POINT_0, true);
|
||||
self.push_point(ctrl1, PointFlags::CONTROL_POINT_1, true);
|
||||
self.push_point(point, PointFlags::empty(), true);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn close(&mut self) {
|
||||
self.closed = true;
|
||||
}
|
||||
|
||||
|
@ -596,6 +611,8 @@ impl Contour {
|
|||
true
|
||||
}
|
||||
|
||||
// Use this function to keep bounds up to date when mutating paths. See `Outline::transform()`
|
||||
// for an example of use.
|
||||
pub(crate) fn update_bounds(&self, bounds: &mut Option<RectF32>) {
|
||||
*bounds = Some(match *bounds {
|
||||
None => self.bounds,
|
||||
|
|
|
@ -21,7 +21,7 @@ use std::sync::atomic::AtomicUsize;
|
|||
use std::time::Instant;
|
||||
use std::u16;
|
||||
|
||||
pub struct SceneBuilder<'a> {
|
||||
pub(crate) struct SceneBuilder<'a> {
|
||||
scene: &'a Scene,
|
||||
built_options: &'a PreparedRenderOptions,
|
||||
|
||||
|
@ -31,7 +31,7 @@ pub struct SceneBuilder<'a> {
|
|||
}
|
||||
|
||||
impl<'a> SceneBuilder<'a> {
|
||||
pub fn new(
|
||||
pub(crate) fn new(
|
||||
scene: &'a Scene,
|
||||
built_options: &'a PreparedRenderOptions,
|
||||
listener: Box<dyn RenderCommandListener>,
|
||||
|
|
|
@ -20,10 +20,12 @@
|
|||
//! You don't need to use this API to use Pathfinder; it's only a convenience.
|
||||
|
||||
use crate::concurrent::executor::Executor;
|
||||
use crate::gpu::renderer::Renderer;
|
||||
use crate::gpu_data::RenderCommand;
|
||||
use crate::options::{PreparedRenderOptions, RenderCommandListener};
|
||||
use crate::options::{RenderCommandListener, RenderOptions};
|
||||
use crate::scene::Scene;
|
||||
use pathfinder_geometry::basic::rect::RectF32;
|
||||
use pathfinder_gpu::Device;
|
||||
use std::sync::mpsc::{self, Receiver, Sender};
|
||||
use std::thread;
|
||||
|
||||
|
@ -52,18 +54,34 @@ impl SceneProxy {
|
|||
|
||||
#[inline]
|
||||
pub fn build_with_listener(&self,
|
||||
built_options: PreparedRenderOptions,
|
||||
options: RenderOptions,
|
||||
listener: Box<dyn RenderCommandListener>) {
|
||||
self.sender.send(MainToWorkerMsg::Build(built_options, listener)).unwrap();
|
||||
self.sender.send(MainToWorkerMsg::Build(options, listener)).unwrap();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn build_with_stream(&self, built_options: PreparedRenderOptions) -> RenderCommandStream {
|
||||
pub fn build_with_stream(&self, options: RenderOptions) -> RenderCommandStream {
|
||||
let (sender, receiver) = mpsc::sync_channel(MAX_MESSAGES_IN_FLIGHT);
|
||||
let listener = Box::new(move |command| sender.send(command).unwrap());
|
||||
self.build_with_listener(built_options, listener);
|
||||
self.build_with_listener(options, listener);
|
||||
RenderCommandStream::new(receiver)
|
||||
}
|
||||
|
||||
/// A convenience method to build a scene and send the resulting commands
|
||||
/// to the given renderer.
|
||||
///
|
||||
/// Exactly equivalent to:
|
||||
///
|
||||
/// for command in scene_proxy.build_with_stream(options) {
|
||||
/// renderer.render_command(&command)
|
||||
/// }
|
||||
#[inline]
|
||||
pub fn build_and_render<D>(&self, renderer: &mut Renderer<D>, options: RenderOptions)
|
||||
where D: Device {
|
||||
for command in self.build_with_stream(options) {
|
||||
renderer.render_command(&command)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn scene_thread<E>(mut scene: Scene,
|
||||
|
@ -74,9 +92,7 @@ fn scene_thread<E>(mut scene: Scene,
|
|||
match msg {
|
||||
MainToWorkerMsg::ReplaceScene(new_scene) => scene = new_scene,
|
||||
MainToWorkerMsg::SetViewBox(new_view_box) => scene.set_view_box(new_view_box),
|
||||
MainToWorkerMsg::Build(options, listener) => {
|
||||
scene.build(&options, listener, &executor);
|
||||
}
|
||||
MainToWorkerMsg::Build(options, listener) => scene.build(options, listener, &executor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +100,7 @@ fn scene_thread<E>(mut scene: Scene,
|
|||
enum MainToWorkerMsg {
|
||||
ReplaceScene(Scene),
|
||||
SetViewBox(RectF32),
|
||||
Build(PreparedRenderOptions, Box<dyn RenderCommandListener>),
|
||||
Build(RenderOptions, Box<dyn RenderCommandListener>),
|
||||
}
|
||||
|
||||
pub struct RenderCommandStream {
|
||||
|
|
|
@ -177,7 +177,7 @@ where
|
|||
|
||||
let debug_ui = DebugUI::new(&device, resources, dest_framebuffer.window_size(&device));
|
||||
|
||||
Renderer {
|
||||
let renderer = Renderer {
|
||||
device,
|
||||
|
||||
dest_framebuffer,
|
||||
|
@ -218,7 +218,12 @@ where
|
|||
|
||||
render_mode: RenderMode::default(),
|
||||
use_depth: false,
|
||||
}
|
||||
};
|
||||
|
||||
// As a convenience, bind the destination framebuffer.
|
||||
renderer.bind_dest_framebuffer();
|
||||
|
||||
renderer
|
||||
}
|
||||
|
||||
pub fn begin_scene(&mut self) {
|
||||
|
@ -1466,6 +1471,12 @@ impl<D> DestFramebuffer<D>
|
|||
where
|
||||
D: Device,
|
||||
{
|
||||
#[inline]
|
||||
pub fn full_window(window_size: Point2DI32) -> DestFramebuffer<D> {
|
||||
let viewport = RectI32::new(Point2DI32::default(), window_size);
|
||||
DestFramebuffer::Default { viewport, window_size }
|
||||
}
|
||||
|
||||
fn window_size(&self, device: &D) -> Point2DI32 {
|
||||
match *self {
|
||||
DestFramebuffer::Default { window_size, .. } => window_size,
|
||||
|
|
|
@ -39,7 +39,7 @@ pub struct RenderOptions {
|
|||
}
|
||||
|
||||
impl RenderOptions {
|
||||
pub fn prepare(self, bounds: RectF32) -> PreparedRenderOptions {
|
||||
pub(crate) fn prepare(self, bounds: RectF32) -> PreparedRenderOptions {
|
||||
PreparedRenderOptions {
|
||||
transform: self.transform.prepare(bounds),
|
||||
dilation: self.dilation,
|
||||
|
@ -119,15 +119,15 @@ impl RenderTransform {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct PreparedRenderOptions {
|
||||
pub transform: PreparedRenderTransform,
|
||||
pub dilation: Point2DF32,
|
||||
pub subpixel_aa_enabled: bool,
|
||||
pub(crate) struct PreparedRenderOptions {
|
||||
pub(crate) transform: PreparedRenderTransform,
|
||||
pub(crate) dilation: Point2DF32,
|
||||
pub(crate) subpixel_aa_enabled: bool,
|
||||
}
|
||||
|
||||
impl PreparedRenderOptions {
|
||||
#[inline]
|
||||
pub fn bounding_quad(&self) -> BoundingQuad {
|
||||
pub(crate) fn bounding_quad(&self) -> BoundingQuad {
|
||||
match self.transform {
|
||||
PreparedRenderTransform::Perspective { quad, .. } => quad,
|
||||
_ => [Point3DF32::default(); 4],
|
||||
|
@ -135,9 +135,9 @@ impl PreparedRenderOptions {
|
|||
}
|
||||
}
|
||||
|
||||
pub type BoundingQuad = [Point3DF32; 4];
|
||||
pub(crate) type BoundingQuad = [Point3DF32; 4];
|
||||
|
||||
pub enum PreparedRenderTransform {
|
||||
pub(crate) enum PreparedRenderTransform {
|
||||
None,
|
||||
Transform2D(Transform2DF32),
|
||||
Perspective {
|
||||
|
@ -149,7 +149,7 @@ pub enum PreparedRenderTransform {
|
|||
|
||||
impl PreparedRenderTransform {
|
||||
#[inline]
|
||||
pub fn is_2d(&self) -> bool {
|
||||
pub(crate) fn is_2d(&self) -> bool {
|
||||
match *self {
|
||||
PreparedRenderTransform::Transform2D(_) => true,
|
||||
_ => false,
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
|
||||
use crate::builder::SceneBuilder;
|
||||
use crate::concurrent::executor::Executor;
|
||||
use crate::options::{PreparedRenderOptions, PreparedRenderTransform, RenderCommandListener};
|
||||
use crate::options::{PreparedRenderOptions, PreparedRenderTransform};
|
||||
use crate::options::{RenderCommandListener, RenderOptions};
|
||||
use hashbrown::HashMap;
|
||||
use pathfinder_geometry::basic::point::Point2DF32;
|
||||
use pathfinder_geometry::basic::rect::RectF32;
|
||||
|
@ -43,6 +44,7 @@ impl Scene {
|
|||
}
|
||||
|
||||
pub fn push_object(&mut self, object: PathObject) {
|
||||
self.bounds = self.bounds.union_rect(object.outline.bounds());
|
||||
self.objects.push(object);
|
||||
}
|
||||
|
||||
|
@ -165,7 +167,7 @@ impl Scene {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn effective_view_box(&self, render_options: &PreparedRenderOptions) -> RectF32 {
|
||||
pub(crate) fn effective_view_box(&self, render_options: &PreparedRenderOptions) -> RectF32 {
|
||||
if render_options.subpixel_aa_enabled {
|
||||
self.view_box.scale_xy(Point2DF32::new(3.0, 1.0))
|
||||
} else {
|
||||
|
@ -175,11 +177,12 @@ impl Scene {
|
|||
|
||||
#[inline]
|
||||
pub fn build<E>(&self,
|
||||
built_options: &PreparedRenderOptions,
|
||||
options: RenderOptions,
|
||||
listener: Box<dyn RenderCommandListener>,
|
||||
executor: &E)
|
||||
where E: Executor {
|
||||
SceneBuilder::new(self, built_options, listener).build(executor)
|
||||
let prepared_options = options.prepare(self.bounds);
|
||||
SceneBuilder::new(self, &prepared_options, listener).build(executor)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,25 +218,12 @@ pub struct PathObject {
|
|||
outline: Outline,
|
||||
paint: PaintId,
|
||||
name: String,
|
||||
kind: PathObjectKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum PathObjectKind {
|
||||
Fill,
|
||||
Stroke,
|
||||
}
|
||||
|
||||
impl PathObject {
|
||||
#[inline]
|
||||
pub fn new(outline: Outline, paint: PaintId, name: String, kind: PathObjectKind)
|
||||
-> PathObject {
|
||||
PathObject {
|
||||
outline,
|
||||
paint,
|
||||
name,
|
||||
kind,
|
||||
}
|
||||
pub fn new(outline: Outline, paint: PaintId, name: String) -> PathObject {
|
||||
PathObject { outline, paint, name }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -245,7 +245,7 @@ impl<'a> Tiler<'a> {
|
|||
let outline = &self.outline;
|
||||
let point_index = self.point_queue.pop().unwrap().point_index;
|
||||
|
||||
let contour = &outline.contours[point_index.contour() as usize];
|
||||
let contour = &outline.contours()[point_index.contour() as usize];
|
||||
|
||||
// TODO(pcwalton): Could use a bitset of processed edges…
|
||||
let prev_endpoint_index = contour.prev_endpoint_index_of(point_index.point());
|
||||
|
@ -311,7 +311,7 @@ impl<'a> Tiler<'a> {
|
|||
fn init_point_queue(&mut self) {
|
||||
// Find MIN points.
|
||||
self.point_queue.clear();
|
||||
for (contour_index, contour) in self.outline.contours.iter().enumerate() {
|
||||
for (contour_index, contour) in self.outline.contours().iter().enumerate() {
|
||||
let contour_index = contour_index as u32;
|
||||
let mut cur_endpoint_index = 0;
|
||||
let mut prev_endpoint_index = contour.prev_endpoint_index_of(cur_endpoint_index);
|
||||
|
|
|
@ -21,7 +21,7 @@ use pathfinder_geometry::color::ColorU;
|
|||
use pathfinder_geometry::outline::Outline;
|
||||
use pathfinder_geometry::segment::{Segment, SegmentFlags};
|
||||
use pathfinder_geometry::stroke::OutlineStrokeToFill;
|
||||
use pathfinder_renderer::scene::{Paint, PathObject, PathObjectKind, Scene};
|
||||
use pathfinder_renderer::scene::{Paint, PathObject, Scene};
|
||||
use std::fmt::{Display, Formatter, Result as FormatResult};
|
||||
use std::mem;
|
||||
use usvg::{Color as SvgColor, Node, NodeExt, NodeKind, Paint as UsvgPaint};
|
||||
|
@ -122,12 +122,10 @@ impl BuiltSVG {
|
|||
let path = Transform2DF32PathIter::new(path, &transform);
|
||||
let outline = Outline::from_segments(path);
|
||||
|
||||
self.scene.set_bounds(self.scene.bounds().union_rect(outline.bounds()));
|
||||
self.scene.push_object(PathObject::new(
|
||||
outline,
|
||||
style,
|
||||
node.id().to_string(),
|
||||
PathObjectKind::Fill,
|
||||
format!("Fill({})", node.id()),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -146,12 +144,10 @@ impl BuiltSVG {
|
|||
let mut outline = stroke_to_fill.outline;
|
||||
outline.transform(&transform);
|
||||
|
||||
self.scene.set_bounds(self.scene.bounds().union_rect(outline.bounds()));
|
||||
self.scene.push_object(PathObject::new(
|
||||
outline,
|
||||
style,
|
||||
node.id().to_string(),
|
||||
PathObjectKind::Stroke,
|
||||
format!("Stroke({})", node.id()),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue