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:
parent
27357a23d3
commit
a8f7438cd9
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -32,3 +32,4 @@ pub mod stroke;
|
|||
pub mod transform;
|
||||
|
||||
mod dilation;
|
||||
mod util;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>,
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue