From cd37791d720e305b8813868603baabc9b14054db Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 2 Apr 2020 16:30:49 -0700 Subject: [PATCH] Implement the `drawImage()` method on canvas. Closes #223. --- canvas/src/lib.rs | 94 ++++++++++++++++++++++++++++++++++++++++-- content/src/pattern.rs | 20 ++++++++- renderer/src/paint.rs | 4 +- renderer/src/scene.rs | 65 +++++++++++++++++++++++++---- 4 files changed, 167 insertions(+), 16 deletions(-) diff --git a/canvas/src/lib.rs b/canvas/src/lib.rs index 996bbd3d..09bb4d67 100644 --- a/canvas/src/lib.rs +++ b/canvas/src/lib.rs @@ -112,13 +112,23 @@ pub struct CanvasRenderingContext2D { } impl CanvasRenderingContext2D { - // Finalization + // Canvas accessors + + #[inline] + pub fn canvas(&self) -> &Canvas { + &self.canvas + } #[inline] pub fn into_canvas(self) -> Canvas { self.canvas } + #[inline] + pub fn font_context(&self) -> CanvasFontContext { + self.font_context.clone() + } + // Drawing rectangles #[inline] @@ -413,6 +423,30 @@ impl CanvasRenderingContext2D { self.current_state.global_composite_operation = new_composite_operation; } + // Drawing images + + #[inline] + pub fn draw_image(&mut self, image: I, dest_location: L) + where I: CanvasImageSource, L: CanvasImageDestLocation { + let pattern = image.to_pattern(self, Transform2F::default()); + let src_rect = RectF::new(vec2f(0.0, 0.0), pattern.size().to_f32()); + self.draw_subimage(pattern, src_rect, dest_location) + } + + pub fn draw_subimage(&mut self, image: I, src_location: RectF, dest_location: L) + where I: CanvasImageSource, L: CanvasImageDestLocation { + let dest_size = dest_location.size().unwrap_or(src_location.size()); + let scale = dest_size / src_location.size(); + let offset = dest_location.origin() - src_location.origin(); + let transform = Transform2F::from_scale(scale).translate(offset); + + let pattern = image.to_pattern(self, transform); + let old_fill_paint = self.current_state.fill_paint.clone(); + self.set_fill_style(pattern); + self.fill_rect(RectF::new(dest_location.origin(), dest_size)); + self.current_state.fill_paint = old_fill_paint; + } + // Image smoothing #[inline] @@ -451,15 +485,19 @@ impl CanvasRenderingContext2D { // Extensions - pub fn create_pattern_from_canvas(&mut self, canvas: Canvas) -> Pattern { + pub fn create_pattern_from_canvas(&mut self, canvas: Canvas, transform: Transform2F) + -> Pattern { let subscene = canvas.into_scene(); let subscene_size = subscene.view_box().size().ceil().to_i32(); let render_target = RenderTarget::new(subscene_size, String::new()); let render_target_id = self.canvas.scene.push_render_target(render_target); self.canvas.scene.append_scene(subscene); self.canvas.scene.pop_render_target(); - let pattern_source = PatternSource::RenderTarget(render_target_id); - Pattern::new(pattern_source, Transform2F::default(), PatternFlags::empty()) + let pattern_source = PatternSource::RenderTarget { + id: render_target_id, + size: subscene_size, + }; + Pattern::new(pattern_source, transform, PatternFlags::empty()) } } @@ -782,6 +820,54 @@ pub enum ImageSmoothingQuality { High, } +pub trait CanvasImageSource { + fn to_pattern(self, dest_context: &mut CanvasRenderingContext2D, transform: Transform2F) + -> Pattern; +} + +pub trait CanvasImageDestLocation { + fn origin(&self) -> Vector2F; + fn size(&self) -> Option; +} + +impl CanvasImageSource for Pattern { + #[inline] + fn to_pattern(mut self, _: &mut CanvasRenderingContext2D, transform: Transform2F) -> Pattern { + self.transform(transform); + self + } +} + +impl CanvasImageSource for Canvas { + #[inline] + fn to_pattern(self, dest_context: &mut CanvasRenderingContext2D, transform: Transform2F) + -> Pattern { + dest_context.create_pattern_from_canvas(self, transform) + } +} + +impl CanvasImageDestLocation for RectF { + #[inline] + fn origin(&self) -> Vector2F { + RectF::origin(*self) + } + #[inline] + fn size(&self) -> Option { + Some(RectF::size(*self)) + } +} + +impl CanvasImageDestLocation for Vector2F { + #[inline] + fn origin(&self) -> Vector2F { + *self + } + #[inline] + fn size(&self) -> Option { + None + } +} + impl Debug for Path2D { fn fmt(&self, formatter: &mut Formatter) -> Result<(), FmtError> { self.clone().into_outline().fmt(formatter) diff --git a/content/src/pattern.rs b/content/src/pattern.rs index b97b9fb2..2faa4444 100644 --- a/content/src/pattern.rs +++ b/content/src/pattern.rs @@ -34,7 +34,10 @@ pub struct Pattern { #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum PatternSource { Image(Image), - RenderTarget(RenderTargetId), + RenderTarget { + id: RenderTargetId, + size: Vector2I, + } } /// RGBA, non-premultiplied. @@ -61,6 +64,19 @@ impl Pattern { pub fn new(source: PatternSource, transform: Transform2F, flags: PatternFlags) -> Pattern { Pattern { source, transform, flags } } + + #[inline] + pub fn transform(&mut self, transform: Transform2F) { + self.transform *= transform + } + + #[inline] + pub fn size(&self) -> Vector2I { + match self.source { + PatternSource::Image(ref image) => image.size(), + PatternSource::RenderTarget { size, .. } => size, + } + } } impl Image { @@ -104,7 +120,7 @@ impl PatternSource { pub fn is_opaque(&self) -> bool { match *self { PatternSource::Image(ref image) => image.is_opaque(), - PatternSource::RenderTarget(_) => { + PatternSource::RenderTarget { .. } => { // TODO(pcwalton): Maybe do something smarter here? false } diff --git a/renderer/src/paint.rs b/renderer/src/paint.rs index f70f7c24..d895d7fb 100644 --- a/renderer/src/paint.rs +++ b/renderer/src/paint.rs @@ -236,7 +236,7 @@ impl Palette { } Paint::Pattern(ref pattern) => { match pattern.source { - PatternSource::RenderTarget(render_target_id) => { + PatternSource::RenderTarget { id: render_target_id, .. } => { texture_location = render_target_metadata[render_target_id.0 as usize].location; } @@ -311,7 +311,7 @@ impl Palette { transform.inverse() * render_transform } Paint::Pattern(Pattern { - source: PatternSource::RenderTarget(_), + source: PatternSource::RenderTarget { .. }, transform, .. }) => { diff --git a/renderer/src/scene.rs b/renderer/src/scene.rs index 93028a9b..e2a551c2 100644 --- a/renderer/src/scene.rs +++ b/renderer/src/scene.rs @@ -18,6 +18,7 @@ use crate::paint::{Paint, PaintId, PaintInfo, Palette}; use pathfinder_content::effects::{BlendMode, Effects}; use pathfinder_content::fill::FillRule; use pathfinder_content::outline::Outline; +use pathfinder_content::pattern::{Pattern, PatternSource}; use pathfinder_content::render_target::RenderTargetId; use pathfinder_geometry::rect::RectF; use pathfinder_geometry::transform2d::Transform2F; @@ -48,19 +49,23 @@ impl Scene { } pub fn push_path(&mut self, path: DrawPath) { - self.bounds = self.bounds.union_rect(path.outline.bounds()); + let path_index = self.paths.len() as u32; self.paths.push(path); + self.push_path_with_index(path_index); + } + + fn push_path_with_index(&mut self, path_index: u32) { + self.bounds = self.bounds.union_rect(self.paths[path_index as usize].outline.bounds()); - let new_path_count = self.paths.len() as u32; if let Some(DisplayItem::DrawPaths { start_index: _, ref mut end_index }) = self.display_list.last_mut() { - *end_index = new_path_count; + *end_index = path_index + 1; } else { self.display_list.push(DisplayItem::DrawPaths { - start_index: new_path_count - 1, - end_index: new_path_count, + start_index: path_index, + end_index: path_index + 1, }); } } @@ -100,9 +105,25 @@ impl Scene { // Merge paints. let mut paint_mapping = HashMap::new(); - for (old_paint_index, paint) in scene.palette.paints.iter().enumerate() { + for (old_paint_index, old_paint) in scene.palette.paints.iter().enumerate() { let old_paint_id = PaintId(old_paint_index as u16); - let new_paint_id = self.palette.push_paint(&paint); + let new_paint_id = match old_paint { + Paint::Pattern(Pattern { + source: PatternSource::RenderTarget { id: old_render_target_id, size }, + transform, + flags + }) => { + self.palette.push_paint(&Paint::Pattern(Pattern { + source: PatternSource::RenderTarget { + id: render_target_mapping[old_render_target_id], + size: *size, + }, + transform: *transform, + flags: *flags, + })) + } + paint => self.palette.push_paint(paint), + }; paint_mapping.insert(old_paint_id, new_paint_id); } @@ -116,7 +137,7 @@ impl Scene { // Merge draw paths. let mut draw_path_mapping = Vec::with_capacity(scene.paths.len()); for draw_path in scene.paths { - draw_path_mapping.push(self.paths.len()); + draw_path_mapping.push(self.paths.len() as u32); self.paths.push(DrawPath { outline: draw_path.outline, paint: paint_mapping[&draw_path.paint], @@ -129,6 +150,34 @@ impl Scene { name: draw_path.name, }); } + + // Merge display items. + for display_item in scene.display_list { + match display_item { + DisplayItem::DrawRenderTarget { + render_target: old_render_target_id, + effects, + } => { + let new_render_target_id = render_target_mapping[&old_render_target_id]; + self.draw_render_target(new_render_target_id, effects) + } + DisplayItem::PushRenderTarget(old_render_target_id) => { + let new_render_target_id = render_target_mapping[&old_render_target_id]; + self.display_list.push(DisplayItem::PushRenderTarget(new_render_target_id)); + } + DisplayItem::PopRenderTarget => { + self.display_list.push(DisplayItem::PopRenderTarget); + } + DisplayItem::DrawPaths { + start_index: old_start_path_index, + end_index: old_end_path_index, + } => { + for old_path_index in old_start_path_index..old_end_path_index { + self.push_path_with_index(draw_path_mapping[old_path_index as usize]) + } + } + } + } } #[inline]