Remove transforms from paths.

Paints aren't scale-invariant when rendered into the texture atlas, so they
actually do need individual transforms.
This commit is contained in:
Patrick Walton 2020-03-04 16:18:32 -08:00
parent 27357a23d3
commit a8f7438cd9
12 changed files with 121 additions and 94 deletions

View File

@ -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);

View File

@ -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<H>(line_segment: LineSegment2F, state: &mut H) where H: Hasher {
unsafe {
let data: [u32; 4] = mem::transmute::<F32x4, [u32; 4]>(line_segment.0);
data.hash(state);
}
}
fn hash_f32<H>(value: f32, state: &mut H) where H: Hasher {
unsafe {
let data: u32 = mem::transmute::<f32, u32>(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)

View File

@ -32,3 +32,4 @@ pub mod stroke;
pub mod transform;
mod dilation;
mod util;

View File

@ -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<H>(&self, state: &mut H) where H: Hasher {
self.source.hash(state);
util::hash_transform2f(self.transform, state);
self.flags.hash(state);
}
}

47
content/src/util.rs Normal file
View File

@ -0,0 +1,47 @@
// pathfinder/content/src/util.rs
//
// Copyright © 2020 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<H>(line_segment: LineSegment2F, state: &mut H) where H: Hasher {
hash_f32x4(line_segment.0, state);
}
pub(crate) fn hash_transform2f<H>(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<H>(value: f32, state: &mut H) where H: Hasher {
unsafe {
let data: u32 = mem::transmute::<f32, u32>(value);
data.hash(state);
}
}
pub(crate) fn hash_f32x2<H>(vector: F32x2, state: &mut H) where H: Hasher {
unsafe {
let data: [u32; 2] = mem::transmute::<F32x2, [u32; 2]>(vector);
data.hash(state);
}
}
pub(crate) fn hash_f32x4<H>(vector: F32x4, state: &mut H) where H: Hasher {
unsafe {
let data: [u32; 4] = mem::transmute::<F32x4, [u32; 4]>(vector);
data.hash(state);
}
}

View File

@ -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,

View File

@ -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);

View File

@ -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
}
}

View File

@ -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<ClipPathId>,
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<ClipPathId> {
self.clip_path

View File

@ -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>,
}

View File

@ -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,

View File

@ -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
}
}