render: Implement drawing lines using canvas

This patch provides an efficient implementation of draw_line
and draw_line_rect for canvas using strokes.
This commit is contained in:
Kamil Jarosz 2024-06-09 13:02:08 +02:00
parent 2ba5e47b59
commit d86d69f791
1 changed files with 61 additions and 4 deletions

View File

@ -35,6 +35,8 @@ pub struct WebCanvasRenderBackend {
viewport_width: u32,
viewport_height: u32,
rect: Path2d,
line: Path2d,
line_rect: Path2d,
mask_state: MaskState,
blend_modes: Vec<RenderBlendMode>,
@ -294,6 +296,17 @@ impl WebCanvasRenderBackend {
let rect = Path2d::new().into_js_result()?;
rect.rect(0.0, 0.0, 1.0, 1.0);
let line = Path2d::new().into_js_result()?;
line.move_to(0.0, 0.0);
line.line_to(1.0, 0.0);
let line_rect = Path2d::new().into_js_result()?;
line_rect.move_to(0.0, 0.0);
line_rect.line_to(1.0, 0.0);
line_rect.line_to(1.0, 1.0);
line_rect.line_to(0.0, 1.0);
line_rect.close_path();
let renderer = Self {
canvas: canvas.clone(),
color_matrix,
@ -302,6 +315,8 @@ impl WebCanvasRenderBackend {
viewport_height: 0,
viewport_scale_factor: 1.0,
rect,
line,
line_rect,
mask_state: MaskState::DrawContent,
blend_modes: vec![RenderBlendMode::Builtin(BlendMode::Normal)],
};
@ -361,6 +376,23 @@ impl WebCanvasRenderBackend {
self.context.set_global_alpha(1.0);
}
#[inline]
fn set_line_style(&self, color: Color) {
self.context.set_line_cap("butt");
self.context.set_line_width(1.0);
self.context.set_line_join("miter");
self.context.set_stroke_style(
&format!(
"rgba({},{},{},{})",
color.r,
color.g,
color.b,
f32::from(color.a) / 255.0,
)
.into(),
);
}
fn apply_blend_mode(&mut self, blend: RenderBlendMode) {
// TODO: Objects with a blend mode need to be rendered to an intermediate buffer first,
// but for now we render each child directly to the canvas. This should look reasonable for most
@ -428,6 +460,31 @@ impl WebCanvasRenderBackend {
self.apply_blend_mode(current.clone());
}
}
fn draw_lines(&mut self, color: Color, mut matrix: Matrix, rect: bool) {
matrix.tx += Twips::HALF;
matrix.ty += Twips::HALF;
let dom_matrix = matrix.to_dom_matrix();
let stroke = if rect { &self.line_rect } else { &self.line };
match &self.mask_state {
MaskState::DrawContent => {
self.clear_color_filter();
// The transform needs to be applied to the path directly,
// otherwise its thickness will also be transformed.
let _ = self.context.reset_transform();
let transformed_stroke = Path2d::new().expect("new Path2d");
transformed_stroke.add_path_with_transformation(stroke, dom_matrix.unchecked_ref());
self.set_line_style(color);
self.context.stroke_with_path(&transformed_stroke);
}
MaskState::DrawMask(mask_path) => {
mask_path.add_path_with_transformation(stroke, dom_matrix.unchecked_ref());
}
MaskState::ClearMask => (),
}
}
}
impl RenderBackend for WebCanvasRenderBackend {
@ -772,12 +829,12 @@ impl CommandHandler for WebCanvasRenderBackend {
}
}
fn draw_line(&mut self, _color: Color, _matrix: Matrix) {
// TODO implement
fn draw_line(&mut self, color: Color, matrix: Matrix) {
self.draw_lines(color, matrix, false);
}
fn draw_line_rect(&mut self, _color: Color, _matrix: Matrix) {
// TODO implement
fn draw_line_rect(&mut self, color: Color, matrix: Matrix) {
self.draw_lines(color, matrix, true)
}
fn push_mask(&mut self) {