Treat the miter limit as part of the canvas state, allowing it to be saved and

restored even if miter joins are not in use.

In my opinion, this behavior is less logical, but it matches the behavior of
the HTML canvas API, which we aim to remain close to.
This commit is contained in:
Patrick Walton 2019-05-30 21:39:08 -07:00
parent eeaf51ce72
commit 0b9a41c533
1 changed files with 44 additions and 8 deletions

View File

@ -19,7 +19,8 @@ use pathfinder_geometry::basic::rect::RectF;
use pathfinder_geometry::basic::transform2d::Transform2DF; use pathfinder_geometry::basic::transform2d::Transform2DF;
use pathfinder_geometry::color::ColorU; use pathfinder_geometry::color::ColorU;
use pathfinder_geometry::outline::{Contour, Outline}; use pathfinder_geometry::outline::{Contour, Outline};
use pathfinder_geometry::stroke::{LineCap, LineJoin, OutlineStrokeToFill, StrokeStyle}; use pathfinder_geometry::stroke::{LineCap, LineJoin as StrokeLineJoin};
use pathfinder_geometry::stroke::{OutlineStrokeToFill, StrokeStyle};
use pathfinder_renderer::paint::{Paint, PaintId}; use pathfinder_renderer::paint::{Paint, PaintId};
use pathfinder_renderer::scene::{PathObject, Scene}; use pathfinder_renderer::scene::{PathObject, Scene};
use pathfinder_text::{SceneExt, TextRenderMode}; use pathfinder_text::{SceneExt, TextRenderMode};
@ -87,7 +88,7 @@ impl CanvasRenderingContext2D {
pub fn stroke_text(&mut self, string: &str, position: Point2DF) { pub fn stroke_text(&mut self, string: &str, position: Point2DF) {
let paint_id = self.scene.push_paint(&self.current_state.stroke_paint); let paint_id = self.scene.push_paint(&self.current_state.stroke_paint);
let render_mode = TextRenderMode::Stroke(self.current_state.stroke_style); let render_mode = TextRenderMode::Stroke(self.current_state.resolve_stroke_style());
self.fill_or_stroke_text(string, position, paint_id, render_mode); self.fill_or_stroke_text(string, position, paint_id, render_mode);
} }
@ -130,17 +131,22 @@ impl CanvasRenderingContext2D {
#[inline] #[inline]
pub fn set_line_width(&mut self, new_line_width: f32) { pub fn set_line_width(&mut self, new_line_width: f32) {
self.current_state.stroke_style.line_width = new_line_width self.current_state.line_width = new_line_width
} }
#[inline] #[inline]
pub fn set_line_cap(&mut self, new_line_cap: LineCap) { pub fn set_line_cap(&mut self, new_line_cap: LineCap) {
self.current_state.stroke_style.line_cap = new_line_cap self.current_state.line_cap = new_line_cap
} }
#[inline] #[inline]
pub fn set_line_join(&mut self, new_line_join: LineJoin) { pub fn set_line_join(&mut self, new_line_join: LineJoin) {
self.current_state.stroke_style.line_join = new_line_join self.current_state.line_join = new_line_join
}
#[inline]
pub fn set_miter_limit(&mut self, new_miter_limit: f32) {
self.current_state.miter_limit = new_miter_limit
} }
#[inline] #[inline]
@ -183,7 +189,7 @@ impl CanvasRenderingContext2D {
let paint = self.current_state.resolve_paint(self.current_state.stroke_paint); let paint = self.current_state.resolve_paint(self.current_state.stroke_paint);
let paint_id = self.scene.push_paint(&paint); let paint_id = self.scene.push_paint(&paint);
let mut stroke_style = self.current_state.stroke_style; let mut stroke_style = self.current_state.resolve_stroke_style();
stroke_style.line_width = f32::max(stroke_style.line_width, HAIRLINE_STROKE_WIDTH); stroke_style.line_width = f32::max(stroke_style.line_width, HAIRLINE_STROKE_WIDTH);
let outline = path.into_outline(); let outline = path.into_outline();
@ -246,7 +252,10 @@ pub struct State {
text_align: TextAlign, text_align: TextAlign,
fill_paint: Paint, fill_paint: Paint,
stroke_paint: Paint, stroke_paint: Paint,
stroke_style: StrokeStyle, line_width: f32,
line_cap: LineCap,
line_join: LineJoin,
miter_limit: f32,
global_alpha: f32, global_alpha: f32,
} }
@ -259,7 +268,10 @@ impl State {
text_align: TextAlign::Left, text_align: TextAlign::Left,
fill_paint: Paint { color: ColorU::black() }, fill_paint: Paint { color: ColorU::black() },
stroke_paint: Paint { color: ColorU::black() }, stroke_paint: Paint { color: ColorU::black() },
stroke_style: StrokeStyle::default(), line_width: 1.0,
line_cap: LineCap::Butt,
line_join: LineJoin::Miter,
miter_limit: 10.0,
global_alpha: 1.0, global_alpha: 1.0,
} }
} }
@ -268,6 +280,18 @@ impl State {
paint.color.a = (paint.color.a as f32 * self.global_alpha).round() as u8; paint.color.a = (paint.color.a as f32 * self.global_alpha).round() as u8;
paint paint
} }
fn resolve_stroke_style(&self) -> StrokeStyle {
StrokeStyle {
line_width: self.line_width,
line_cap: self.line_cap,
line_join: match self.line_join {
LineJoin::Miter => StrokeLineJoin::Miter(self.miter_limit),
LineJoin::Bevel => StrokeLineJoin::Bevel,
LineJoin::Round => StrokeLineJoin::Round,
},
}
}
} }
#[derive(Clone)] #[derive(Clone)]
@ -376,6 +400,18 @@ pub enum TextAlign {
Center, Center,
} }
// We duplicate `pathfinder_geometry::stroke::LineJoin` here because the HTML canvas API treats the
// miter limit as part of the canvas state, while the native Pathfinder API treats the miter limit
// as part of the line join. Pathfinder's choice is more logical, because the miter limit is
// specific to miter joins. In this API, however, for compatibility we go with the HTML canvas
// semantics.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum LineJoin {
Miter,
Bevel,
Round,
}
// TODO(pcwalton): Support other fields. // TODO(pcwalton): Support other fields.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct TextMetrics { pub struct TextMetrics {