Auto merge of #387 - pcwalton:web-canvas, r=pcwalton
Flesh out the `web_canvas` crate a bit more
This commit is contained in:
commit
b97c92ca8d
|
@ -89,6 +89,12 @@ impl Canvas {
|
|||
Canvas { scene }
|
||||
}
|
||||
|
||||
/// Returns the inner scene.
|
||||
#[inline]
|
||||
pub fn scene(&self) -> &Scene {
|
||||
&self.scene
|
||||
}
|
||||
|
||||
/// Returns the inner scene, replacing it with a blank scene.
|
||||
#[inline]
|
||||
pub fn take_scene(&mut self) -> Scene {
|
||||
|
@ -98,6 +104,7 @@ impl Canvas {
|
|||
mem::replace(&mut self.scene, new_scene)
|
||||
}
|
||||
|
||||
/// Destroys this canvas and returns the inner scene.
|
||||
#[inline]
|
||||
pub fn into_scene(self) -> Scene {
|
||||
self.scene
|
||||
|
@ -122,6 +129,12 @@ impl Canvas {
|
|||
pub fn size(&self) -> Vector2I {
|
||||
self.scene.view_box().size().ceil().to_i32()
|
||||
}
|
||||
|
||||
pub fn set_size(&mut self, new_size: Vector2I) {
|
||||
let new_view_box = RectI::new(Vector2I::default(), new_size).to_f32();
|
||||
self.scene.set_bounds(new_view_box);
|
||||
self.scene.set_view_box(new_view_box);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CanvasRenderingContext2D {
|
||||
|
@ -150,6 +163,13 @@ impl CanvasRenderingContext2D {
|
|||
self.canvas
|
||||
}
|
||||
|
||||
// Extensions
|
||||
|
||||
/// Clears the current canvas.
|
||||
pub fn clear(&mut self) {
|
||||
drop(self.canvas.take_scene())
|
||||
}
|
||||
|
||||
// Drawing rectangles
|
||||
|
||||
#[inline]
|
||||
|
@ -184,26 +204,51 @@ impl CanvasRenderingContext2D {
|
|||
|
||||
// Line styles
|
||||
|
||||
#[inline]
|
||||
pub fn line_width(&self) -> f32 {
|
||||
self.current_state.line_width
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_line_width(&mut self, new_line_width: f32) {
|
||||
self.current_state.line_width = new_line_width
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn line_cap(&self) -> LineCap {
|
||||
self.current_state.line_cap
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_line_cap(&mut self, new_line_cap: LineCap) {
|
||||
self.current_state.line_cap = new_line_cap
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn line_join(&self) -> LineJoin {
|
||||
self.current_state.line_join
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_line_join(&mut self, new_line_join: LineJoin) {
|
||||
self.current_state.line_join = new_line_join
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn miter_limit(&self) -> f32 {
|
||||
self.current_state.miter_limit
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_miter_limit(&mut self, new_miter_limit: f32) {
|
||||
self.current_state.miter_limit = new_miter_limit
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn line_dash(&mut self) -> &[f32] {
|
||||
&self.current_state.line_dash
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_line_dash(&mut self, mut new_line_dash: Vec<f32>) {
|
||||
// Duplicate and concatenate if an odd number of dashes are present.
|
||||
|
@ -216,6 +261,11 @@ impl CanvasRenderingContext2D {
|
|||
self.current_state.line_dash = new_line_dash
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn line_dash_offset(&self) -> f32 {
|
||||
self.current_state.line_dash_offset
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_line_dash_offset(&mut self, new_line_dash_offset: f32) {
|
||||
self.current_state.line_dash_offset = new_line_dash_offset
|
||||
|
@ -671,7 +721,6 @@ impl Path2D {
|
|||
|
||||
#[inline]
|
||||
pub fn move_to(&mut self, to: Vector2F) {
|
||||
// TODO(pcwalton): Cull degenerate contours.
|
||||
self.flush_current_contour();
|
||||
self.current_contour.push_endpoint(to);
|
||||
}
|
||||
|
|
|
@ -292,7 +292,9 @@ impl<W> DemoApp<W> where W: Window {
|
|||
let viewport = self.window.viewport(self.ui_model.mode.view(0));
|
||||
self.scene_proxy.set_view_box(RectF::new(Vector2F::zero(),
|
||||
viewport.size().to_f32()));
|
||||
self.renderer.set_main_framebuffer_size(self.window_size.device_size());
|
||||
self.renderer.options_mut().dest =
|
||||
DestFramebuffer::full_window(self.window_size.device_size());
|
||||
self.renderer.dest_framebuffer_size_changed();
|
||||
self.dirty = true;
|
||||
}
|
||||
Event::MouseDown(new_position) => {
|
||||
|
|
|
@ -489,7 +489,8 @@ impl<D> Renderer<D> where D: Device {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_main_framebuffer_size(&mut self, new_framebuffer_size: Vector2I) {
|
||||
pub fn dest_framebuffer_size_changed(&mut self) {
|
||||
let new_framebuffer_size = self.core.main_viewport().size();
|
||||
if let Some(ref mut debug_ui_presenter) = self.debug_ui_presenter {
|
||||
debug_ui_presenter.ui_presenter.set_framebuffer_size(new_framebuffer_size);
|
||||
}
|
||||
|
|
|
@ -47,4 +47,14 @@ wasm-bindgen-test = "0.3"
|
|||
|
||||
[profile.release]
|
||||
# Tell `rustc` to optimize for small code size.
|
||||
opt-level = "s"
|
||||
# opt-level = "s"
|
||||
debug = 1
|
||||
|
||||
[package.metadata.wasm-pack.profile.profiling]
|
||||
wasm-opt = false
|
||||
dwarf-debug-info = true
|
||||
|
||||
[package.metadata.wasm-pack.profile.profiling.wasm-bindgen]
|
||||
debug-js-glue = false
|
||||
demangle-name-section = true
|
||||
dwarf-debug-info = true
|
||||
|
|
|
@ -9,32 +9,38 @@
|
|||
// except according to those terms.
|
||||
|
||||
use css_color_parser::Color;
|
||||
use pathfinder_canvas::{Canvas, CanvasFontContext, CanvasRenderingContext2D, FillRule, Path2D};
|
||||
use pathfinder_color::{ColorF, ColorU};
|
||||
use pathfinder_canvas::{Canvas, CanvasFontContext, CanvasRenderingContext2D, FillRule, FillStyle};
|
||||
use pathfinder_canvas::{LineCap, Path2D};
|
||||
use pathfinder_color::ColorU;
|
||||
use pathfinder_geometry::rect::RectF;
|
||||
use pathfinder_geometry::transform2d::Transform2F;
|
||||
use pathfinder_geometry::vector::{Vector2I, vec2f, vec2i};
|
||||
use pathfinder_geometry::vector::{vec2f, vec2i};
|
||||
use pathfinder_renderer::concurrent::executor::SequentialExecutor;
|
||||
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererMode, RendererOptions};
|
||||
use pathfinder_renderer::gpu::renderer::Renderer;
|
||||
use pathfinder_renderer::options::BuildOptions;
|
||||
use pathfinder_resources::embedded::EmbeddedResourceLoader;
|
||||
use pathfinder_webgl::WebGlDevice;
|
||||
use std::mem;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::{self, console, HtmlCanvasElement, WebGl2RenderingContext};
|
||||
use web_sys::{self, HtmlCanvasElement, WebGl2RenderingContext};
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct PFCanvasRenderingContext2D {
|
||||
html_canvas: HtmlCanvasElement,
|
||||
context: CanvasRenderingContext2D,
|
||||
resource_loader: EmbeddedResourceLoader,
|
||||
renderer: Option<Renderer<WebGlDevice>>,
|
||||
renderer: Renderer<WebGlDevice>,
|
||||
default_path: Path2D,
|
||||
// FIXME(pcwalton): Remove this once renderers are resizable.
|
||||
prev_framebuffer_size: Vector2I,
|
||||
current_state: WebCanvasState,
|
||||
saved_states: Vec<WebCanvasState>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct WebCanvasState {
|
||||
fill_style_string: Arc<String>,
|
||||
stroke_style_string: Arc<String>,
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "createContext")]
|
||||
|
@ -55,7 +61,7 @@ pub fn create_context(html_canvas: HtmlCanvasElement) -> PFCanvasRenderingContex
|
|||
let mode = RendererMode::default_for_device(&pathfinder_device);
|
||||
let options = RendererOptions {
|
||||
dest: DestFramebuffer::full_window(framebuffer_size),
|
||||
background_color: Some(ColorF::white()),
|
||||
background_color: None,
|
||||
..RendererOptions::default()
|
||||
};
|
||||
let resource_loader = EmbeddedResourceLoader::new();
|
||||
|
@ -68,41 +74,37 @@ pub fn create_context(html_canvas: HtmlCanvasElement) -> PFCanvasRenderingContex
|
|||
PFCanvasRenderingContext2D {
|
||||
html_canvas,
|
||||
context,
|
||||
resource_loader,
|
||||
renderer: Some(renderer),
|
||||
renderer,
|
||||
default_path: Path2D::new(),
|
||||
prev_framebuffer_size: framebuffer_size,
|
||||
current_state: WebCanvasState {
|
||||
fill_style_string: Arc::new("black".to_owned()),
|
||||
stroke_style_string: Arc::new("black".to_owned()),
|
||||
},
|
||||
saved_states: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl PFCanvasRenderingContext2D {
|
||||
pub fn flush(&mut self) {
|
||||
#[wasm_bindgen(js_name = "pfFlush")]
|
||||
pub fn pf_flush(&mut self) {
|
||||
// Update framebuffer size.
|
||||
let framebuffer_size = vec2i(self.html_canvas.width() as i32,
|
||||
self.html_canvas.height() as i32);
|
||||
if self.prev_framebuffer_size != framebuffer_size {
|
||||
// Recreate the Pathfinder renderer.
|
||||
//
|
||||
// FIXME(pcwalton): This shouldn't be necessary!
|
||||
let pathfinder_device = self.renderer.take().unwrap().destroy();
|
||||
let mode = RendererMode::default_for_device(&pathfinder_device);
|
||||
let options = RendererOptions {
|
||||
dest: DestFramebuffer::full_window(framebuffer_size),
|
||||
background_color: Some(ColorF::white()),
|
||||
..RendererOptions::default()
|
||||
};
|
||||
self.renderer = Some(Renderer::new(pathfinder_device,
|
||||
&self.resource_loader,
|
||||
mode,
|
||||
options));
|
||||
self.prev_framebuffer_size = framebuffer_size;
|
||||
self.renderer.options_mut().dest = DestFramebuffer::full_window(framebuffer_size);
|
||||
self.renderer.options_mut().background_color = None;
|
||||
self.renderer.dest_framebuffer_size_changed();
|
||||
|
||||
// TODO(pcwalton): This is inefficient!
|
||||
let mut scene = (*self.context.canvas_mut().scene()).clone();
|
||||
scene.build_and_render(&mut self.renderer, BuildOptions::default(), SequentialExecutor);
|
||||
|
||||
self.context.canvas_mut().set_size(framebuffer_size);
|
||||
}
|
||||
|
||||
let mut scene = self.context.canvas_mut().take_scene();
|
||||
scene.build_and_render(self.renderer.as_mut().unwrap(),
|
||||
BuildOptions::default(),
|
||||
SequentialExecutor);
|
||||
#[wasm_bindgen(js_name = "pfClear")]
|
||||
pub fn pf_clear(&mut self) {
|
||||
self.context.clear();
|
||||
}
|
||||
|
||||
#[wasm_bindgen(getter)]
|
||||
|
@ -110,26 +112,11 @@ impl PFCanvasRenderingContext2D {
|
|||
self.html_canvas.clone()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "fillStyle")]
|
||||
#[wasm_bindgen(setter)]
|
||||
pub fn set_fill_style(&mut self, new_style: &str) {
|
||||
let css_color = match Color::from_str(new_style) {
|
||||
Err(_) => return,
|
||||
Ok(css_color) => css_color,
|
||||
};
|
||||
let color = ColorU::new(css_color.r,
|
||||
css_color.g,
|
||||
css_color.b,
|
||||
(css_color.a * 255.0).round() as u8);
|
||||
self.context.set_fill_style(color);
|
||||
}
|
||||
// Drawing rectangles
|
||||
|
||||
pub fn save(&mut self) {
|
||||
self.context.save();
|
||||
}
|
||||
|
||||
pub fn restore(&mut self) {
|
||||
self.context.restore();
|
||||
#[wasm_bindgen(js_name = "clearRect")]
|
||||
pub fn clear_rect(&mut self, x: f32, y: f32, width: f32, height: f32) {
|
||||
self.context.clear_rect(RectF::new(vec2f(x, y), vec2f(width, height)));
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "fillRect")]
|
||||
|
@ -137,6 +124,67 @@ impl PFCanvasRenderingContext2D {
|
|||
self.context.fill_rect(RectF::new(vec2f(x, y), vec2f(width, height)));
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "strokeRect")]
|
||||
pub fn stroke_rect(&mut self, x: f32, y: f32, width: f32, height: f32) {
|
||||
self.context.stroke_rect(RectF::new(vec2f(x, y), vec2f(width, height)));
|
||||
}
|
||||
|
||||
// TODO(pcwalton): Drawing text
|
||||
|
||||
// Line styles
|
||||
|
||||
#[wasm_bindgen(js_name = "lineWidth")]
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn line_width(&self) -> f32 {
|
||||
self.context.line_width()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "lineWidth")]
|
||||
#[wasm_bindgen(setter)]
|
||||
pub fn set_line_width(&mut self, new_line_width: f32) {
|
||||
self.context.set_line_width(new_line_width);
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "lineCap")]
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn line_cap(&self) -> String {
|
||||
match self.context.line_cap() {
|
||||
LineCap::Butt => "butt".to_owned(),
|
||||
LineCap::Round => "round".to_owned(),
|
||||
LineCap::Square => "square".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "lineCap")]
|
||||
#[wasm_bindgen(setter)]
|
||||
pub fn set_line_cap(&mut self, new_line_cap: &str) {
|
||||
if new_line_cap == "butt" {
|
||||
self.context.set_line_cap(LineCap::Butt)
|
||||
} else if new_line_cap == "round" {
|
||||
self.context.set_line_cap(LineCap::Round)
|
||||
} else if new_line_cap == "square" {
|
||||
self.context.set_line_cap(LineCap::Square)
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "fillStyle")]
|
||||
#[wasm_bindgen(setter)]
|
||||
pub fn set_fill_style(&mut self, new_style_string: &str) {
|
||||
if let Some(new_style) = parse_fill_or_stroke_style(new_style_string) {
|
||||
self.context.set_fill_style(new_style);
|
||||
self.current_state.fill_style_string = Arc::new(new_style_string.to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "strokeStyle")]
|
||||
#[wasm_bindgen(setter)]
|
||||
pub fn set_stroke_style(&mut self, new_style_string: &str) {
|
||||
if let Some(new_style) = parse_fill_or_stroke_style(new_style_string) {
|
||||
self.context.set_stroke_style(new_style);
|
||||
self.current_state.stroke_style_string = Arc::new(new_style_string.to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transform(&mut self, a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) {
|
||||
let new_transform = self.context.transform() * Transform2F::row_major(a, c, e, b, d, f);
|
||||
self.context.set_transform(&new_transform)
|
||||
|
@ -193,4 +241,28 @@ impl PFCanvasRenderingContext2D {
|
|||
let path = self.default_path.clone();
|
||||
self.context.stroke_path(path);
|
||||
}
|
||||
|
||||
pub fn save(&mut self) {
|
||||
self.context.save();
|
||||
self.saved_states.push(self.current_state.clone());
|
||||
}
|
||||
|
||||
pub fn restore(&mut self) {
|
||||
if let Some(saved_state) = self.saved_states.pop() {
|
||||
self.current_state = saved_state;
|
||||
}
|
||||
self.context.restore();
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_fill_or_stroke_style(string: &str) -> Option<FillStyle> {
|
||||
let css_color = match Color::from_str(string) {
|
||||
Err(_) => return None,
|
||||
Ok(css_color) => css_color,
|
||||
};
|
||||
let color = ColorU::new(css_color.r,
|
||||
css_color.g,
|
||||
css_color.b,
|
||||
(css_color.a * 255.0).round() as u8);
|
||||
Some(FillStyle::Color(color))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue