From a8f7438cd9e02f20c5bb806be5fb13761ba08892 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 4 Mar 2020 16:18:32 -0800 Subject: [PATCH] Remove transforms from paths. Paints aren't scale-invariant when rendered into the texture atlas, so they actually do need individual transforms. --- canvas/src/lib.rs | 18 ++++++++------ content/src/gradient.rs | 28 ++++++--------------- content/src/lib.rs | 1 + content/src/pattern.rs | 20 ++++++++++++--- content/src/util.rs | 47 ++++++++++++++++++++++++++++++++++++ renderer/src/builder.rs | 10 ++------ renderer/src/gpu/renderer.rs | 1 - renderer/src/paint.rs | 25 ++++++++++--------- renderer/src/scene.rs | 16 +----------- renderer/src/tiles.rs | 5 ---- renderer/src/z_buffer.rs | 21 +++++----------- svg/src/lib.rs | 23 ++++++++++++------ 12 files changed, 121 insertions(+), 94 deletions(-) create mode 100644 content/src/util.rs diff --git a/canvas/src/lib.rs b/canvas/src/lib.rs index 42480d7b..ec300657 100644 --- a/canvas/src/lib.rs +++ b/canvas/src/lib.rs @@ -101,9 +101,11 @@ impl CanvasRenderingContext2D { let paint = self.current_state.resolve_paint(&paint); let paint_id = self.scene.push_paint(&paint); - let mut path = DrawPath::new(path.into_outline(), paint_id); + let mut outline = path.into_outline(); + outline.transform(&self.current_state.transform); + + let mut path = DrawPath::new(outline, paint_id); path.set_blend_mode(BlendMode::Clear); - path.set_transform(self.current_state.transform); self.scene.push_path(path); } @@ -245,7 +247,7 @@ impl CanvasRenderingContext2D { self.current_state.clip_path = Some(clip_path_id); } - fn push_path(&mut self, outline: Outline, paint_id: PaintId, fill_rule: FillRule) { + fn push_path(&mut self, mut outline: Outline, paint_id: PaintId, fill_rule: FillRule) { let transform = self.current_state.transform; let clip_path = self.current_state.clip_path; let blend_mode = self.current_state.global_composite_operation.to_blend_mode(); @@ -259,13 +261,15 @@ impl CanvasRenderingContext2D { let paint = self.current_state.resolve_paint(&self.current_state.shadow_paint); let paint_id = self.scene.push_paint(&paint); - let mut path = DrawPath::new(outline.clone(), paint_id); + let mut outline = outline.clone(); + outline.transform(&(Transform2F::from_translation(self.current_state.shadow_offset) * + transform)); + + let mut path = DrawPath::new(outline, paint_id); path.set_clip_path(clip_path); path.set_fill_rule(fill_rule); path.set_blend_mode(blend_mode); path.set_opacity(opacity); - path.set_transform(Transform2F::from_translation(self.current_state.shadow_offset) * - transform); self.scene.push_path(path); self.composite_shadow_blur_render_targets_if_needed(shadow_blur_render_target_ids); @@ -273,13 +277,13 @@ impl CanvasRenderingContext2D { } let render_target_id = self.push_render_target_if_needed(composite_op); + outline.transform(&transform); let mut path = DrawPath::new(outline, paint_id); path.set_clip_path(clip_path); path.set_fill_rule(fill_rule); path.set_blend_mode(blend_mode); path.set_opacity(opacity); - path.set_transform(transform); self.scene.push_path(path); self.composite_render_target_if_needed(composite_op, render_target_id); diff --git a/content/src/gradient.rs b/content/src/gradient.rs index d9a16e21..0eea60fe 100644 --- a/content/src/gradient.rs +++ b/content/src/gradient.rs @@ -9,10 +9,10 @@ // except according to those terms. use crate::sorted_vector::SortedVector; +use crate::util; use pathfinder_color::ColorU; use pathfinder_geometry::line_segment::LineSegment2F; -use pathfinder_geometry::util; -use pathfinder_simd::default::F32x4; +use pathfinder_geometry::util as geometry_util; use std::cmp::{Ordering, PartialOrd}; use std::convert; use std::hash::{Hash, Hasher}; @@ -47,31 +47,17 @@ impl Hash for Gradient { match self.geometry { GradientGeometry::Linear(line) => { (0).hash(state); - hash_line_segment(line, state); + util::hash_line_segment(line, state); } GradientGeometry::Radial { line, start_radius, end_radius } => { (1).hash(state); - hash_line_segment(line, state); - hash_f32(start_radius, state); - hash_f32(end_radius, state); + util::hash_line_segment(line, state); + util::hash_f32(start_radius, state); + util::hash_f32(end_radius, state); } } self.stops.hash(state); - - fn hash_line_segment(line_segment: LineSegment2F, state: &mut H) where H: Hasher { - unsafe { - let data: [u32; 4] = mem::transmute::(line_segment.0); - data.hash(state); - } - } - - fn hash_f32(value: f32, state: &mut H) where H: Hasher { - unsafe { - let data: u32 = mem::transmute::(value); - data.hash(state); - } - } } } @@ -128,7 +114,7 @@ impl Gradient { return ColorU::transparent_black(); } - t = util::clamp(t, 0.0, 1.0); + t = geometry_util::clamp(t, 0.0, 1.0); let last_index = self.stops.len() - 1; let upper_index = self.stops.binary_search_by(|stop| { stop.offset.partial_cmp(&t).unwrap_or(Ordering::Less) diff --git a/content/src/lib.rs b/content/src/lib.rs index dbc30bb3..86747602 100644 --- a/content/src/lib.rs +++ b/content/src/lib.rs @@ -32,3 +32,4 @@ pub mod stroke; pub mod transform; mod dilation; +mod util; diff --git a/content/src/pattern.rs b/content/src/pattern.rs index 0e820d10..a444e8aa 100644 --- a/content/src/pattern.rs +++ b/content/src/pattern.rs @@ -11,17 +11,21 @@ //! Raster image patterns. use crate::render_target::RenderTargetId; +use crate::util; use pathfinder_color::{self as color, ColorU}; +use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::vector::Vector2I; use std::fmt::{self, Debug, Formatter}; +use std::hash::{Hash, Hasher}; #[cfg(feature = "pf-image")] use image::RgbaImage; /// A raster image pattern. -#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, PartialEq, Debug)] pub struct Pattern { pub source: PatternSource, + pub transform: Transform2F, pub flags: PatternFlags, } @@ -51,8 +55,8 @@ bitflags! { impl Pattern { #[inline] - pub fn new(source: PatternSource, flags: PatternFlags) -> Pattern { - Pattern { source, flags } + pub fn new(source: PatternSource, transform: Transform2F, flags: PatternFlags) -> Pattern { + Pattern { source, transform, flags } } } @@ -106,3 +110,13 @@ impl Debug for Image { write!(formatter, "(image {}×{} px)", self.size.x(), self.size.y()) } } + +impl Eq for Pattern {} + +impl Hash for Pattern { + fn hash(&self, state: &mut H) where H: Hasher { + self.source.hash(state); + util::hash_transform2f(self.transform, state); + self.flags.hash(state); + } +} diff --git a/content/src/util.rs b/content/src/util.rs new file mode 100644 index 00000000..8c0c186b --- /dev/null +++ b/content/src/util.rs @@ -0,0 +1,47 @@ +// pathfinder/content/src/util.rs +// +// Copyright © 2020 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Miscellaneous utilities. + +use pathfinder_geometry::line_segment::LineSegment2F; +use pathfinder_geometry::transform2d::Transform2F; +use pathfinder_simd::default::{F32x2, F32x4}; +use std::hash::{Hash, Hasher}; +use std::mem; + +pub(crate) fn hash_line_segment(line_segment: LineSegment2F, state: &mut H) where H: Hasher { + hash_f32x4(line_segment.0, state); +} + +pub(crate) fn hash_transform2f(transform: Transform2F, state: &mut H) where H: Hasher { + hash_f32x4(transform.matrix.0, state); + hash_f32x2(transform.vector.0, state); +} + +pub(crate) fn hash_f32(value: f32, state: &mut H) where H: Hasher { + unsafe { + let data: u32 = mem::transmute::(value); + data.hash(state); + } +} + +pub(crate) fn hash_f32x2(vector: F32x2, state: &mut H) where H: Hasher { + unsafe { + let data: [u32; 2] = mem::transmute::(vector); + data.hash(state); + } +} + +pub(crate) fn hash_f32x4(vector: F32x4, state: &mut H) where H: Hasher { + unsafe { + let data: [u32; 4] = mem::transmute::(vector); + data.hash(state); + } +} diff --git a/renderer/src/builder.rs b/renderer/src/builder.rs index 653ccc0b..d06a238e 100644 --- a/renderer/src/builder.rs +++ b/renderer/src/builder.rs @@ -188,7 +188,6 @@ impl<'a> SceneBuilder<'a> { blend_mode: path_object.blend_mode(), opacity: path_object.opacity(), built_clip_path, - transform_inv: path_object.transform().inverse(), })); tiler.generate_tiles(); @@ -359,10 +358,7 @@ impl<'a> SceneBuilder<'a> { let solid_tiles = &built_draw_path.path.solid_tiles; let path_index = (path_subindex + start_index) as u32; let path = &self.scene.paths[path_index as usize]; - let metadata = DepthMetadata { - paint_id: path.paint(), - transform: path.transform(), - }; + let metadata = DepthMetadata { paint_id: path.paint() }; z_buffer.update(solid_tiles, current_depth, metadata); current_depth += 1; } @@ -762,9 +758,7 @@ impl AlphaTileVertex { draw_tiling_path_info: &DrawTilingPathInfo) -> AlphaTileVertex { let tile_position = tile_origin + tile_offset; - let transform_inv = draw_tiling_path_info.transform_inv; - let color_uv = draw_tiling_path_info.paint_metadata.calculate_tex_coords(tile_position, - transform_inv); + let color_uv = draw_tiling_path_info.paint_metadata.calculate_tex_coords(tile_position); let mask_uv = calculate_mask_uv(tile_index, tile_offset); AlphaTileVertex { tile_x: tile_position.x() as i16, diff --git a/renderer/src/gpu/renderer.rs b/renderer/src/gpu/renderer.rs index 294a589c..0ed9e28c 100644 --- a/renderer/src/gpu/renderer.rs +++ b/renderer/src/gpu/renderer.rs @@ -423,7 +423,6 @@ where } pub fn render_command(&mut self, command: &RenderCommand) { - println!("{:?}", command); match *command { RenderCommand::Start { bounding_quad, path_count, needs_readable_framebuffer } => { self.start_rendering(bounding_quad, path_count, needs_readable_framebuffer); diff --git a/renderer/src/paint.rs b/renderer/src/paint.rs index 1e942472..798402b7 100644 --- a/renderer/src/paint.rs +++ b/renderer/src/paint.rs @@ -146,9 +146,7 @@ impl Paint { } } } - Paint::Pattern(_) => { - // TODO(pcwalton): Implement this. - } + Paint::Pattern(ref mut pattern) => pattern.transform = *transform * pattern.transform, } } } @@ -283,18 +281,24 @@ impl Palette { Transform2F::from_translation(texture_origin_uv) * Transform2F::from_scale(gradient_tile_scale / view_box_size.to_f32()) } - Paint::Pattern(Pattern { source: PatternSource::Image(_), .. }) => { + Paint::Pattern(Pattern { source: PatternSource::Image(_), transform, .. }) => { let texture_origin_uv = rect_to_uv(metadata.texture_rect, texture_scale).origin(); Transform2F::from_translation(texture_origin_uv) * - Transform2F::from_scale(texture_scale) + Transform2F::from_scale(texture_scale) * + transform.inverse() } - Paint::Pattern(Pattern { source: PatternSource::RenderTarget(_), .. }) => { + Paint::Pattern(Pattern { + source: PatternSource::RenderTarget(_), + transform, + .. + }) => { // FIXME(pcwalton): Only do this in GL, not Metal! let texture_origin_uv = rect_to_uv(metadata.texture_rect, texture_scale).lower_left(); Transform2F::from_translation(texture_origin_uv) * - Transform2F::from_scale(texture_scale.scale_xy(Vector2F::new(1.0, -1.0))) + Transform2F::from_scale(texture_scale.scale_xy(Vector2F::new(1.0, -1.0))) * + transform.inverse() } } } @@ -553,13 +557,10 @@ impl Palette { impl PaintMetadata { // TODO(pcwalton): Apply clamp/repeat to tile rect. - pub(crate) fn calculate_tex_coords(&self, - tile_position: Vector2I, - path_transform_inv: Transform2F) - -> Vector2F { + pub(crate) fn calculate_tex_coords(&self, tile_position: Vector2I) -> Vector2F { let tile_size = Vector2I::new(TILE_WIDTH as i32, TILE_HEIGHT as i32); let position = tile_position.scale_xy(tile_size).to_f32(); - let tex_coords = self.texture_transform * path_transform_inv * position; + let tex_coords = self.texture_transform * position; tex_coords } } diff --git a/renderer/src/scene.rs b/renderer/src/scene.rs index 92025f35..e8aacd28 100644 --- a/renderer/src/scene.rs +++ b/renderer/src/scene.rs @@ -46,9 +46,7 @@ impl Scene { } } - pub fn push_path(&mut self, mut path: DrawPath) { - path.outline.transform(&path.transform()); - + pub fn push_path(&mut self, path: DrawPath) { self.bounds = self.bounds.union_rect(path.outline.bounds()); self.paths.push(path); @@ -225,7 +223,6 @@ impl<'a> Iterator for PathIter<'a> { pub struct DrawPath { outline: Outline, paint: PaintId, - transform: Transform2F, clip_path: Option, fill_rule: FillRule, blend_mode: BlendMode, @@ -275,7 +272,6 @@ impl DrawPath { DrawPath { outline, paint, - transform: Transform2F::default(), clip_path: None, fill_rule: FillRule::Winding, blend_mode: BlendMode::SrcOver, @@ -289,16 +285,6 @@ impl DrawPath { &self.outline } - #[inline] - pub(crate) fn transform(&self) -> Transform2F { - self.transform - } - - #[inline] - pub fn set_transform(&mut self, new_transform: Transform2F) { - self.transform = new_transform - } - #[inline] pub(crate) fn clip_path(&self) -> Option { self.clip_path diff --git a/renderer/src/tiles.rs b/renderer/src/tiles.rs index 4fdf48af..f718e081 100644 --- a/renderer/src/tiles.rs +++ b/renderer/src/tiles.rs @@ -18,7 +18,6 @@ use pathfinder_content::segment::Segment; use pathfinder_content::sorted_vector::SortedVector; use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::rect::{RectF, RectI}; -use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::vector::{Vector2F, Vector2I}; use std::cmp::Ordering; use std::mem; @@ -52,10 +51,6 @@ pub(crate) struct DrawTilingPathInfo<'a> { pub(crate) paint_metadata: &'a PaintMetadata, pub(crate) blend_mode: BlendMode, pub(crate) opacity: u8, - // The inverse of the path transform. - // - // We calculate texture transforms with this. - pub(crate) transform_inv: Transform2F, pub(crate) built_clip_path: Option<&'a BuiltPath>, } diff --git a/renderer/src/z_buffer.rs b/renderer/src/z_buffer.rs index f7dac74b..3625f11e 100644 --- a/renderer/src/z_buffer.rs +++ b/renderer/src/z_buffer.rs @@ -16,7 +16,6 @@ use crate::paint::{PaintId, PaintMetadata}; use crate::tile_map::DenseTileMap; use crate::tiles; use pathfinder_geometry::rect::RectF; -use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::vector::Vector2I; use vec_map::VecMap; @@ -32,7 +31,6 @@ pub(crate) struct SolidTiles { #[derive(Clone, Copy)] pub(crate) struct DepthMetadata { pub(crate) paint_id: PaintId, - pub(crate) transform: Transform2F, } impl ZBuffer { pub(crate) fn new(view_box: RectF) -> ZBuffer { @@ -73,7 +71,6 @@ impl ZBuffer { let depth_metadata = self.depth_metadata[depth as usize]; let paint_metadata = &paint_metadata[depth_metadata.paint_id.0 as usize]; - let transform = depth_metadata.transform; let tile_position = tile_coords + self.buffer.rect.origin(); @@ -93,16 +90,10 @@ impl ZBuffer { let batch = solid_tiles.batches.last_mut().unwrap(); batch.vertices.extend_from_slice(&[ - SolidTileVertex::new(tile_position, transform, paint_metadata), - SolidTileVertex::new(tile_position + Vector2I::new(1, 0), - transform, - paint_metadata), - SolidTileVertex::new(tile_position + Vector2I::new(0, 1), - transform, - paint_metadata), - SolidTileVertex::new(tile_position + Vector2I::new(1, 1), - transform, - paint_metadata), + SolidTileVertex::new(tile_position, paint_metadata), + SolidTileVertex::new(tile_position + Vector2I::new(1, 0), paint_metadata), + SolidTileVertex::new(tile_position + Vector2I::new(0, 1), paint_metadata), + SolidTileVertex::new(tile_position + Vector2I::new(1, 1), paint_metadata), ]); } @@ -111,9 +102,9 @@ impl ZBuffer { } impl SolidTileVertex { - fn new(tile_position: Vector2I, transform: Transform2F, paint_metadata: &PaintMetadata) + fn new(tile_position: Vector2I, paint_metadata: &PaintMetadata) -> SolidTileVertex { - let color_uv = paint_metadata.calculate_tex_coords(tile_position, transform); + let color_uv = paint_metadata.calculate_tex_coords(tile_position); SolidTileVertex { tile_x: tile_position.x() as i16, tile_y: tile_position.y() as i16, diff --git a/svg/src/lib.rs b/svg/src/lib.rs index 990cfd6e..262f091f 100644 --- a/svg/src/lib.rs +++ b/svg/src/lib.rs @@ -223,20 +223,21 @@ impl BuiltSVG { } fn push_draw_path(&mut self, - outline: Outline, + mut outline: Outline, name: String, state: &State, paint: &UsvgPaint, opacity: Opacity, fill_rule: UsvgFillRule) { - let style = self.scene.push_paint(&Paint::from_svg_paint(paint, &mut self.result_flags)); + outline.transform(&state.transform); + let paint = Paint::from_svg_paint(paint, &state.transform, &mut self.result_flags); + let style = self.scene.push_paint(&paint); let fill_rule = FillRule::from_usvg_fill_rule(fill_rule); let mut path = DrawPath::new(outline, style); path.set_clip_path(state.clip_path); path.set_fill_rule(fill_rule); path.set_name(name); path.set_opacity((opacity.value() * 255.0) as u8); - path.set_transform(state.transform); self.scene.push_path(path); } } @@ -284,21 +285,29 @@ impl Display for BuildResultFlags { } trait PaintExt { - fn from_svg_paint(svg_paint: &UsvgPaint, result_flags: &mut BuildResultFlags) -> Self; + fn from_svg_paint(svg_paint: &UsvgPaint, + transform: &Transform2F, + result_flags: &mut BuildResultFlags) + -> Self; } impl PaintExt for Paint { #[inline] - fn from_svg_paint(svg_paint: &UsvgPaint, result_flags: &mut BuildResultFlags) -> Paint { + fn from_svg_paint(svg_paint: &UsvgPaint, + transform: &Transform2F, + result_flags: &mut BuildResultFlags) + -> Paint { // TODO(pcwalton): Support gradients. - Paint::Color(match *svg_paint { + let mut paint = Paint::Color(match *svg_paint { UsvgPaint::Color(color) => ColorU::from_svg_color(color), UsvgPaint::Link(_) => { // TODO(pcwalton) result_flags.insert(BuildResultFlags::UNSUPPORTED_LINK_PAINT); ColorU::black() } - }) + }); + paint.apply_transform(transform); + paint } }