Fix incorrect definition of 2D transform multiplication

This commit is contained in:
Patrick Walton 2019-07-12 11:26:09 -07:00
parent 96758dfc17
commit 1eb28a5539
11 changed files with 83 additions and 71 deletions

View File

@ -115,8 +115,7 @@ impl CanvasRenderingContext2D {
TextAlign::Center => position.set_x(position.x() - layout.width() * 0.5), TextAlign::Center => position.set_x(position.x() - layout.width() * 0.5),
} }
let transform = Transform2F::from_translation(position).post_mul(&self.current_state let transform = self.current_state.transform * Transform2F::from_translation(position);
.transform);
// TODO(pcwalton): Report errors. // TODO(pcwalton): Report errors.
drop(self.scene.push_layout(&layout, drop(self.scene.push_layout(&layout,
@ -443,7 +442,7 @@ impl Path2D {
end_angle: f32, end_angle: f32,
direction: ArcDirection) { direction: ArcDirection) {
let mut transform = Transform2F::from_scale(Vector2F::splat(radius)); let mut transform = Transform2F::from_scale(Vector2F::splat(radius));
transform = transform.post_mul(&Transform2F::from_translation(center)); transform = Transform2F::from_translation(center) * transform;
self.current_contour.push_arc(&transform, start_angle, end_angle, direction); self.current_contour.push_arc(&transform, start_angle, end_angle, direction);
} }
@ -458,7 +457,7 @@ impl Path2D {
let center = ctrl + bisector.scale(hypot / bisector.length()); let center = ctrl + bisector.scale(hypot / bisector.length());
let mut transform = Transform2F::from_scale(Vector2F::splat(radius)); let mut transform = Transform2F::from_scale(Vector2F::splat(radius));
transform = transform.post_mul(&Transform2F::from_translation(center)); transform = Transform2F::from_translation(center) * transform;
let chord = LineSegment2F::new(vu0.yx().scale_xy(Vector2F::new(-1.0, 1.0)), let chord = LineSegment2F::new(vu0.yx().scale_xy(Vector2F::new(-1.0, 1.0)),
vu1.yx().scale_xy(Vector2F::new(1.0, -1.0))); vu1.yx().scale_xy(Vector2F::new(1.0, -1.0)));
@ -484,9 +483,9 @@ impl Path2D {
end_angle: f32) { end_angle: f32) {
self.flush_current_contour(); self.flush_current_contour();
let mut transform = Transform2F::from_rotation(rotation); let mut transform = Transform2F::from_translation(center);
transform = transform.post_mul(&Transform2F::from_scale(axes)); transform *= Transform2F::from_rotation(rotation);
transform = transform.post_mul(&Transform2F::from_translation(center)); transform *= Transform2F::from_scale(axes);
self.current_contour.push_arc(&transform, start_angle, end_angle, ArcDirection::CW); self.current_contour.push_arc(&transform, start_angle, end_angle, ArcDirection::CW);
if end_angle - start_angle >= 2.0 * PI { if end_angle - start_angle >= 2.0 * PI {

View File

@ -393,8 +393,7 @@ impl Contour {
let half_sweep_vector = sweep_vector.halve_angle(); let half_sweep_vector = sweep_vector.halve_angle();
let rotation = Transform2F::from_rotation_vector(half_sweep_vector.rotate_by(vector)); let rotation = Transform2F::from_rotation_vector(half_sweep_vector.rotate_by(vector));
segment = segment.transform(&direction_transform.post_mul(&rotation) segment = segment.transform(&(*transform * rotation * direction_transform));
.post_mul(&transform));
let mut push_segment_flags = PushSegmentFlags::UPDATE_BOUNDS; let mut push_segment_flags = PushSegmentFlags::UPDATE_BOUNDS;
if first_segment { if first_segment {
@ -419,13 +418,13 @@ impl Contour {
self.push_segment(&segment.transform(transform), self.push_segment(&segment.transform(transform),
PushSegmentFlags::UPDATE_BOUNDS | PushSegmentFlags::INCLUDE_FROM_POINT); PushSegmentFlags::UPDATE_BOUNDS | PushSegmentFlags::INCLUDE_FROM_POINT);
rotation = Transform2F::from_rotation_vector(UnitVector(Vector2F::new( 0.0, 1.0))); rotation = Transform2F::from_rotation_vector(UnitVector(Vector2F::new( 0.0, 1.0)));
self.push_segment(&segment.transform(&rotation.post_mul(&transform)), self.push_segment(&segment.transform(&(*transform * rotation)),
PushSegmentFlags::UPDATE_BOUNDS); PushSegmentFlags::UPDATE_BOUNDS);
rotation = Transform2F::from_rotation_vector(UnitVector(Vector2F::new(-1.0, 0.0))); rotation = Transform2F::from_rotation_vector(UnitVector(Vector2F::new(-1.0, 0.0)));
self.push_segment(&segment.transform(&rotation.post_mul(&transform)), self.push_segment(&segment.transform(&(*transform * rotation)),
PushSegmentFlags::UPDATE_BOUNDS); PushSegmentFlags::UPDATE_BOUNDS);
rotation = Transform2F::from_rotation_vector(UnitVector(Vector2F::new( 0.0, -1.0))); rotation = Transform2F::from_rotation_vector(UnitVector(Vector2F::new( 0.0, -1.0)));
self.push_segment(&segment.transform(&rotation.post_mul(&transform)), self.push_segment(&segment.transform(&(*transform * rotation)),
PushSegmentFlags::UPDATE_BOUNDS); PushSegmentFlags::UPDATE_BOUNDS);
} }

View File

@ -140,7 +140,7 @@ impl<'a> OutlineStrokeToFill<'a> {
let offset = gradient.yx().scale_xy(Vector2F::new(-1.0, 1.0)); let offset = gradient.yx().scale_xy(Vector2F::new(-1.0, 1.0));
let mut transform = Transform2F::from_scale(scale); let mut transform = Transform2F::from_scale(scale);
let translation = p1 + offset.scale(width * 0.5); let translation = p1 + offset.scale(width * 0.5);
transform = transform.post_mul(&Transform2F::from_translation(translation)); transform = Transform2F::from_translation(translation) * transform;
let chord = LineSegment2F::new(-offset, offset); let chord = LineSegment2F::new(-offset, offset);
contour.push_arc_from_unit_chord(&transform, chord, ArcDirection::CW); contour.push_arc_from_unit_chord(&transform, chord, ArcDirection::CW);
} }
@ -374,8 +374,8 @@ impl Contour {
} }
LineJoin::Round => { LineJoin::Round => {
let scale = Vector2F::splat(distance.abs()); let scale = Vector2F::splat(distance.abs());
let mut transform = Transform2F::from_scale(scale); let mut transform = Transform2F::from_translation(join_point);
transform = transform.post_mul(&Transform2F::from_translation(join_point)); transform *= Transform2F::from_scale(scale);
let chord_from = (prev_tangent.to() - join_point).normalize(); let chord_from = (prev_tangent.to() - join_point).normalize();
let chord_to = (next_tangent.to() - join_point).normalize(); let chord_to = (next_tangent.to() - join_point).normalize();
let chord = LineSegment2F::new(chord_from, chord_to); let chord = LineSegment2F::new(chord_from, chord_to);

View File

@ -56,7 +56,8 @@ impl Camera {
let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32 let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32
* scale_factor_for_view_box(view_box); * scale_factor_for_view_box(view_box);
let origin = viewport_size.to_f32().scale(0.5) - view_box.size().scale(scale * 0.5); let origin = viewport_size.to_f32().scale(0.5) - view_box.size().scale(scale * 0.5);
Camera::TwoD(Transform2F::from_scale(Vector2F::splat(scale)).post_translate(origin)) Camera::TwoD(Transform2F::from_translation(origin) *
Transform2F::from_uniform_scale(scale))
} }
fn new_3d(mode: Mode, view_box: RectF, viewport_size: Vector2I) -> Camera { fn new_3d(mode: Mode, view_box: RectF, viewport_size: Vector2I) -> Camera {

View File

@ -327,10 +327,10 @@ impl<W> DemoApp<W> where W: Window {
if let Camera::TwoD(ref mut transform) = self.camera { if let Camera::TwoD(ref mut transform) = self.camera {
let backing_scale_factor = self.window_size.backing_scale_factor; let backing_scale_factor = self.window_size.backing_scale_factor;
let position = position.to_f32().scale(backing_scale_factor); let position = position.to_f32().scale(backing_scale_factor);
*transform = transform.post_translate(-position); *transform = Transform2F::from_translation(-position) * *transform;
let scale_delta = 1.0 + d_dist * CAMERA_SCALE_SPEED_2D; let scale_delta = 1.0 + d_dist * CAMERA_SCALE_SPEED_2D;
*transform = transform.post_scale(Vector2F::splat(scale_delta)); *transform = Transform2F::from_uniform_scale(scale_delta) * *transform;
*transform = transform.post_translate(position); *transform = Transform2F::from_translation(position) * *transform;
} }
} }
Event::Look { pitch, yaw } => { Event::Look { pitch, yaw } => {
@ -587,7 +587,8 @@ impl<W> DemoApp<W> where W: Window {
} }
UIEvent::MouseDragged(position) => { UIEvent::MouseDragged(position) => {
if let Camera::TwoD(ref mut transform) = self.camera { if let Camera::TwoD(ref mut transform) = self.camera {
*transform = transform.post_translate(position.relative.to_f32()); *transform = Transform2F::from_translation(position.relative.to_f32()) *
*transform;
} }
} }
_ => {} _ => {}
@ -607,10 +608,10 @@ impl<W> DemoApp<W> where W: Window {
if let Camera::TwoD(ref mut transform) = self.camera { if let Camera::TwoD(ref mut transform) = self.camera {
let scale = Vector2F::splat(1.0 + CAMERA_ZOOM_AMOUNT_2D); let scale = Vector2F::splat(1.0 + CAMERA_ZOOM_AMOUNT_2D);
let center = center_of_window(&self.window_size); let center = center_of_window(&self.window_size);
*transform = transform *transform = Transform2F::from_translation(center) *
.post_translate(-center) Transform2F::from_scale(scale) *
.post_scale(scale) Transform2F::from_translation(-center) *
.post_translate(center); *transform;
self.dirty = true; self.dirty = true;
} }
} }
@ -618,10 +619,10 @@ impl<W> DemoApp<W> where W: Window {
if let Camera::TwoD(ref mut transform) = self.camera { if let Camera::TwoD(ref mut transform) = self.camera {
let scale = Vector2F::splat(1.0 - CAMERA_ZOOM_AMOUNT_2D); let scale = Vector2F::splat(1.0 - CAMERA_ZOOM_AMOUNT_2D);
let center = center_of_window(&self.window_size); let center = center_of_window(&self.window_size);
*transform = transform *transform = Transform2F::from_translation(center) *
.post_translate(-center) Transform2F::from_scale(scale) *
.post_scale(scale) Transform2F::from_translation(-center) *
.post_translate(center); *transform;
self.dirty = true; self.dirty = true;
} }
} }
@ -635,10 +636,10 @@ impl<W> DemoApp<W> where W: Window {
if let Camera::TwoD(ref mut transform) = self.camera { if let Camera::TwoD(ref mut transform) = self.camera {
let old_rotation = transform.rotation(); let old_rotation = transform.rotation();
let center = center_of_window(&self.window_size); let center = center_of_window(&self.window_size);
*transform = transform *transform = Transform2F::from_translation(-center) *
.post_translate(-center) Transform2F::from_rotation(*theta - old_rotation) *
.post_rotate(*theta - old_rotation) Transform2F::from_translation(center) *
.post_translate(center); *transform;
} }
} }
} }

View File

@ -8,10 +8,10 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use pathfinder_canvas::{CanvasFontContext, CanvasRenderingContext2D, Path2D}; use pathfinder_canvas::{CanvasFontContext, CanvasRenderingContext2D, LineJoin, Path2D};
use pathfinder_geometry::vector::{Vector2F, Vector2I};
use pathfinder_geometry::rect::RectF;
use pathfinder_content::color::ColorF; use pathfinder_content::color::ColorF;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::{Vector2F, Vector2I};
use pathfinder_gl::{GLDevice, GLVersion}; use pathfinder_gl::{GLDevice, GLVersion};
use pathfinder_gpu::resources::FilesystemResourceLoader; use pathfinder_gpu::resources::FilesystemResourceLoader;
use pathfinder_renderer::concurrent::rayon::RayonExecutor; use pathfinder_renderer::concurrent::rayon::RayonExecutor;
@ -57,6 +57,7 @@ fn main() {
// Set line width. // Set line width.
canvas.set_line_width(10.0); canvas.set_line_width(10.0);
canvas.set_line_join(LineJoin::Round);
// Draw walls. // Draw walls.
canvas.stroke_rect(RectF::new(Vector2F::new(75.0, 140.0), Vector2F::new(150.0, 110.0))); canvas.stroke_rect(RectF::new(Vector2F::new(75.0, 140.0), Vector2F::new(150.0, 110.0)));

View File

@ -16,7 +16,7 @@ use crate::rect::RectF;
use crate::transform3d::Transform4F; use crate::transform3d::Transform4F;
use crate::unit_vector::UnitVector; use crate::unit_vector::UnitVector;
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::F32x4;
use std::ops::Sub; use std::ops::{Mul, MulAssign, Sub};
/// A 2x2 matrix, optimized with SIMD, in column-major order. /// A 2x2 matrix, optimized with SIMD, in column-major order.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
@ -46,18 +46,8 @@ impl Matrix2x2F {
} }
#[inline] #[inline]
pub fn row_major(m11: f32, m12: f32, m21: f32, m22: f32) -> Matrix2x2F { pub fn row_major(m00: f32, m01: f32, m10: f32, m11: f32) -> Matrix2x2F {
Matrix2x2F(F32x4::new(m11, m21, m12, m22)) Matrix2x2F(F32x4::new(m00, m10, m01, m11))
}
#[inline]
pub fn post_mul(&self, other: &Matrix2x2F) -> Matrix2x2F {
Matrix2x2F(self.0.xyxy() * other.0.xxzz() + self.0.zwzw() * other.0.yyww())
}
#[inline]
pub fn pre_mul(&self, other: &Matrix2x2F) -> Matrix2x2F {
other.post_mul(self)
} }
#[inline] #[inline]
@ -112,6 +102,14 @@ impl Sub<Matrix2x2F> for Matrix2x2F {
} }
} }
impl Mul<Matrix2x2F> for Matrix2x2F {
type Output = Matrix2x2F;
#[inline]
fn mul(self, other: Matrix2x2F) -> Matrix2x2F {
Matrix2x2F(self.0.xyxy() * other.0.xxzz() + self.0.zwzw() * other.0.yyww())
}
}
/// An affine transform, optimized with SIMD. /// An affine transform, optimized with SIMD.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct Transform2F { pub struct Transform2F {
@ -136,6 +134,11 @@ impl Transform2F {
} }
} }
#[inline]
pub fn from_uniform_scale(scale: f32) -> Transform2F {
Transform2F::from_scale(Vector2F::splat(scale))
}
#[inline] #[inline]
pub fn from_rotation(theta: f32) -> Transform2F { pub fn from_rotation(theta: f32) -> Transform2F {
Transform2F { Transform2F {
@ -165,7 +168,7 @@ impl Transform2F {
) -> Transform2F { ) -> Transform2F {
let rotation = Transform2F::from_rotation(theta); let rotation = Transform2F::from_rotation(theta);
let translation = Transform2F::from_translation(translation); let translation = Transform2F::from_translation(translation);
Transform2F::from_scale(scale).post_mul(&rotation).post_mul(&translation) Transform2F::from_scale(scale) * rotation * translation
} }
#[inline] #[inline]
@ -198,18 +201,6 @@ impl Transform2F {
RectF::from_points(min_point, max_point) RectF::from_points(min_point, max_point)
} }
#[inline]
pub fn post_mul(&self, other: &Transform2F) -> Transform2F {
let matrix = self.matrix.post_mul(&other.matrix);
let vector = other.transform_point(self.vector);
Transform2F { matrix, vector }
}
#[inline]
pub fn pre_mul(&self, other: &Transform2F) -> Transform2F {
other.post_mul(self)
}
// TODO(pcwalton): Optimize better with SIMD. // TODO(pcwalton): Optimize better with SIMD.
#[inline] #[inline]
pub fn to_3d(&self) -> Transform4F { pub fn to_3d(&self) -> Transform4F {
@ -255,6 +246,7 @@ impl Transform2F {
self.matrix.m22() self.matrix.m22()
} }
/*
#[inline] #[inline]
pub fn post_translate(&self, vector: Vector2F) -> Transform2F { pub fn post_translate(&self, vector: Vector2F) -> Transform2F {
self.post_mul(&Transform2F::from_translation(vector)) self.post_mul(&Transform2F::from_translation(vector))
@ -269,6 +261,7 @@ impl Transform2F {
pub fn post_scale(&self, scale: Vector2F) -> Transform2F { pub fn post_scale(&self, scale: Vector2F) -> Transform2F {
self.post_mul(&Transform2F::from_scale(scale)) self.post_mul(&Transform2F::from_scale(scale))
} }
*/
/// Returns the translation part of this matrix. /// Returns the translation part of this matrix.
/// ///
@ -294,3 +287,21 @@ impl Transform2F {
Vector2F(self.matrix.0.zw()).length() Vector2F(self.matrix.0.zw()).length()
} }
} }
impl Mul<Transform2F> for Transform2F {
type Output = Transform2F;
#[inline]
fn mul(self, other: Transform2F) -> Transform2F {
Transform2F {
matrix: self.matrix * other.matrix,
vector: self.transform_point(other.vector),
}
}
}
impl MulAssign for Transform2F {
#[inline]
fn mul_assign(&mut self, other: Transform2F) {
*self = *self * other
}
}

View File

@ -280,13 +280,13 @@ impl Transform4F {
// Compute temporary matrices. // Compute temporary matrices.
let a_inv = a.inverse(); let a_inv = a.inverse();
let x = c.post_mul(&a_inv); let x = c * a_inv;
let y = (d - x.post_mul(&b)).inverse(); let y = (d - x * b).inverse();
let z = a_inv.post_mul(&b); let z = a_inv * b;
// Compute new submatrices. // Compute new submatrices.
let (a_new, b_new) = (a_inv + z.post_mul(&y).post_mul(&x), (-z).post_mul(&y)); let (a_new, b_new) = (a_inv + z * y * x, -z * y);
let (c_new, d_new) = ((-y).post_mul(&x), y); let (c_new, d_new) = (-y * x, y);
// Construct inverse. // Construct inverse.
Transform4F::from_submatrices(a_new, b_new, c_new, d_new) Transform4F::from_submatrices(a_new, b_new, c_new, d_new)

View File

@ -119,8 +119,7 @@ impl Scene {
PreparedRenderTransform::Perspective { .. } => unreachable!(), PreparedRenderTransform::Perspective { .. } => unreachable!(),
}; };
if options.subpixel_aa_enabled { if options.subpixel_aa_enabled {
transform = transform transform *= Transform2F::from_scale(Vector2F::new(3.0, 1.0))
.post_mul(&Transform2F::from_scale(Vector2F::new(3.0, 1.0)))
} }
outline.transform(&transform); outline.transform(&transform);
} }

View File

@ -89,7 +89,7 @@ impl BuiltSVG {
fn process_node(&mut self, node: &Node, transform: &Transform2F) { fn process_node(&mut self, node: &Node, transform: &Transform2F) {
let node_transform = usvg_transform_to_transform_2d(&node.transform()); let node_transform = usvg_transform_to_transform_2d(&node.transform());
let transform = transform.pre_mul(&node_transform); let transform = node_transform * *transform;
match *node.borrow() { match *node.borrow() {
NodeKind::Group(ref group) => { NodeKind::Group(ref group) => {

View File

@ -93,8 +93,9 @@ impl SceneExt for Scene {
// FIXME(pcwalton): Cache this! // FIXME(pcwalton): Cache this!
let scale = style.size / (font.metrics().units_per_em as f32); let scale = style.size / (font.metrics().units_per_em as f32);
let scale = Vector2F::new(scale, -scale); let scale = Vector2F::new(scale, -scale);
let transform = let transform = *transform *
Transform2F::from_scale(scale).post_translate(offset).post_mul(transform); Transform2F::from_translation(offset) *
Transform2F::from_scale(scale);
self.push_glyph(font, self.push_glyph(font,
glyph.glyph_id, glyph.glyph_id,
&transform, &transform,