diff --git a/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoActivity.java b/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoActivity.java index bb2406f4..c7686551 100644 --- a/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoActivity.java +++ b/demo/android/app/src/main/java/graphics/pathfinder/pathfinderdemo/PathfinderDemoActivity.java @@ -63,6 +63,8 @@ public class PathfinderDemoActivity extends Activity { void setVRMode(boolean enabled) { try { setVrModeEnabled(false, mVRListenerComponentName); + mContentView.setStereoModeEnabled(enabled); + mContentView.setDistortionCorrectionEnabled(false); } catch (PackageManager.NameNotFoundException exception) { startActivity(new Intent(Settings.ACTION_VR_LISTENER_SETTINGS)); } @@ -77,7 +79,6 @@ public class PathfinderDemoActivity extends Activity { setContentView(R.layout.activity_pathfinder); mContentView = findViewById(R.id.fullscreen_content); - mContentView.setStereoModeEnabled(false); setVRMode(false); mContentView.setEGLContextClientVersion(3); diff --git a/demo/common/src/lib.rs b/demo/common/src/lib.rs index 88c43f80..1ea24add 100644 --- a/demo/common/src/lib.rs +++ b/demo/common/src/lib.rs @@ -21,6 +21,7 @@ use pathfinder_geometry::basic::rect::{RectF32, RectI32}; use pathfinder_geometry::basic::transform2d::Transform2DF32; use pathfinder_geometry::basic::transform3d::{Perspective, Transform3DF32}; use pathfinder_geometry::color::ColorU; +use pathfinder_geometry::distortion::BarrelDistortionCoefficients; use pathfinder_gl::GLDevice; use pathfinder_gpu::resources::ResourceLoader; use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderState, StencilFunc}; @@ -213,6 +214,11 @@ impl DemoApp where W: Window { let is_first_frame = self.frame_counter == 0; let frame_count = if is_first_frame { 2 } else { 1 }; + let barrel_distortion = match self.ui.mode { + Mode::VR => Some(self.window.barrel_distortion_coefficients()), + _ => None, + }; + for _ in 0..frame_count { let viewport_count = self.ui.mode.viewport_count(); let render_transforms = iter::repeat(render_transform.clone()).take(viewport_count) @@ -224,6 +230,7 @@ impl DemoApp where W: Window { } else { None }, + barrel_distortion, })).unwrap(); } @@ -686,6 +693,7 @@ enum MainToSceneMsg { struct BuildOptions { render_transforms: Vec, stem_darkening_font_size: Option, + barrel_distortion: Option, } struct SceneToMainMsg { @@ -798,6 +806,7 @@ fn build_scene(scene: &Scene, Point2DF32::new(x, y).scale(font_size) } }, + barrel_distortion: build_options.barrel_distortion, }; let built_options = render_options.prepare(scene.bounds); diff --git a/demo/common/src/window.rs b/demo/common/src/window.rs index 212f8b9f..8679b630 100644 --- a/demo/common/src/window.rs +++ b/demo/common/src/window.rs @@ -11,6 +11,7 @@ //! A minimal cross-platform windowing layer. use pathfinder_geometry::basic::point::Point2DI32; +use pathfinder_geometry::distortion::BarrelDistortionCoefficients; use pathfinder_gl::GLVersion; use pathfinder_gpu::resources::ResourceLoader; use std::path::PathBuf; @@ -24,6 +25,11 @@ pub trait Window { fn push_user_event(message_type: u32, message_data: u32); fn present_open_svg_dialog(&mut self); fn run_save_dialog(&self, extension: &str) -> Result; + + #[inline] + fn barrel_distortion_coefficients(&self) -> BarrelDistortionCoefficients { + BarrelDistortionCoefficients::default() + } } pub enum Event { diff --git a/geometry/src/distortion.rs b/geometry/src/distortion.rs new file mode 100644 index 00000000..63e8313c --- /dev/null +++ b/geometry/src/distortion.rs @@ -0,0 +1,67 @@ +// pathfinder/geometry/src/distortion.rs +// +// Copyright © 2019 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. + +use crate::basic::point::{Point2DF32, Point2DI32}; +use crate::outline::{self, Contour}; + +#[derive(Clone, Copy, Debug)] +pub struct BarrelDistortionCoefficients { + pub k0: f32, + pub k1: f32, +} + +impl Default for BarrelDistortionCoefficients { + // Matches Google Daydream (Cardboard v2.2). + #[inline] + fn default() -> BarrelDistortionCoefficients { + BarrelDistortionCoefficients { k0: 0.34, k1: 0.55 } + } +} + +pub struct ContourBarrelDistorter<'a> { + contour: &'a mut Contour, + window_size: Point2DI32, + coefficients: BarrelDistortionCoefficients, +} + +impl<'a> ContourBarrelDistorter<'a> { + pub fn new(contour: &'a mut Contour, + coefficients: BarrelDistortionCoefficients, + window_size: Point2DI32) + -> ContourBarrelDistorter<'a> { + ContourBarrelDistorter { contour, window_size, coefficients } + } + + pub fn distort(&mut self) { + let one = Point2DF32::splat(1.0); + let window_size = self.window_size.to_f32(); + let inv_window_size = Point2DF32(window_size.0.approx_recip()); + let BarrelDistortionCoefficients { k0, k1 } = self.coefficients; + + let point_count = self.contour.len(); + for point_index in 0..point_count { + // Convert from window coordinates to NDC. + let mut position = self.contour.position_of(point_index); + position = position.scale_xy(inv_window_size).scale(2.0) - one; + + // Apply distortion. + let r2 = position.square_length(); + let scaling = 1.0 + k0 * r2 + k1 * r2 * r2; + position = position.scale(1.0 / scaling); + + // Convert back to window coordinates. + position = (position + one).scale(0.5).scale_xy(window_size); + + // Store resulting point. + self.contour.points[point_index as usize] = position; + outline::union_rect(&mut self.contour.bounds, position, point_index == 0); + } + } +} diff --git a/geometry/src/lib.rs b/geometry/src/lib.rs index e3cab18e..a869a518 100644 --- a/geometry/src/lib.rs +++ b/geometry/src/lib.rs @@ -18,6 +18,7 @@ extern crate bitflags; pub mod basic; pub mod clip; pub mod color; +pub mod distortion; pub mod monotonic; pub mod orientation; pub mod outline; diff --git a/geometry/src/outline.rs b/geometry/src/outline.rs index d3c9fa1a..3f2ee59c 100644 --- a/geometry/src/outline.rs +++ b/geometry/src/outline.rs @@ -11,12 +11,13 @@ //! A compressed in-memory representation of paths. use crate::basic::line_segment::LineSegmentF32; -use crate::basic::point::Point2DF32; +use crate::basic::point::{Point2DF32, Point2DI32}; use crate::basic::rect::RectF32; use crate::basic::transform2d::Transform2DF32; use crate::basic::transform3d::Perspective; use crate::clip::{self, ContourPolygonClipper, ContourRectClipper}; use crate::dilation::ContourDilator; +use crate::distortion::{BarrelDistortionCoefficients, ContourBarrelDistorter}; use crate::orientation::Orientation; use crate::segment::{Segment, SegmentFlags, SegmentKind}; use std::fmt::{self, Debug, Formatter}; @@ -135,6 +136,17 @@ impl Outline { self.bounds = self.bounds.dilate(amount); } + pub fn barrel_distort(&mut self, + coefficients: BarrelDistortionCoefficients, + window_size: Point2DI32) { + let mut new_bounds = None; + for contour in &mut self.contours { + contour.barrel_distort(coefficients, window_size); + contour.update_bounds(&mut new_bounds); + } + self.bounds = new_bounds.unwrap_or_else(|| RectF32::default()); + } + pub fn prepare_for_tiling(&mut self, view_box: RectF32) { self.contours.iter_mut().for_each(|contour| contour.prepare_for_tiling(view_box)); self.bounds = self.bounds.intersection(view_box).unwrap_or_else(|| RectF32::default()); @@ -419,6 +431,12 @@ impl Contour { self.bounds = self.bounds.dilate(amount); } + pub fn barrel_distort(&mut self, + coefficients: BarrelDistortionCoefficients, + window_size: Point2DI32) { + ContourBarrelDistorter::new(self, coefficients, window_size).distort(); + } + fn prepare_for_tiling(&mut self, view_box: RectF32) { // Snap points to the view box bounds. This mops up floating point error from the clipping // process. @@ -667,7 +685,7 @@ impl<'a> Iterator for ContourIter<'a> { } #[inline] -fn union_rect(bounds: &mut RectF32, new_point: Point2DF32, first: bool) { +pub(crate) fn union_rect(bounds: &mut RectF32, new_point: Point2DF32, first: bool) { if first { *bounds = RectF32::from_points(new_point, new_point); } else { diff --git a/renderer/src/builder.rs b/renderer/src/builder.rs index 3fa7bdcf..1165195f 100644 --- a/renderer/src/builder.rs +++ b/renderer/src/builder.rs @@ -20,6 +20,7 @@ use pathfinder_geometry::basic::rect::{RectF32, RectI32}; use pathfinder_geometry::basic::transform2d::Transform2DF32; use pathfinder_geometry::basic::transform3d::Perspective; use pathfinder_geometry::clip::PolygonClipper3D; +use pathfinder_geometry::distortion::BarrelDistortionCoefficients; use std::iter; use std::u16; @@ -126,6 +127,7 @@ impl SceneBuilder { pub struct RenderOptions { pub transform: RenderTransform, pub dilation: Point2DF32, + pub barrel_distortion: Option, } impl RenderOptions { @@ -133,6 +135,7 @@ impl RenderOptions { PreparedRenderOptions { transform: self.transform.prepare(bounds), dilation: self.dilation, + barrel_distortion: self.barrel_distortion, } } } @@ -201,6 +204,7 @@ impl RenderTransform { pub struct PreparedRenderOptions { pub transform: PreparedRenderTransform, pub dilation: Point2DF32, + pub barrel_distortion: Option, } impl PreparedRenderOptions { @@ -218,4 +222,3 @@ pub enum PreparedRenderTransform { Transform2D(Transform2DF32), Perspective { perspective: Perspective, clip_polygon: Vec, quad: [Point3DF32; 4] } } - diff --git a/renderer/src/scene.rs b/renderer/src/scene.rs index 3deff388..24225e40 100644 --- a/renderer/src/scene.rs +++ b/renderer/src/scene.rs @@ -116,6 +116,11 @@ impl Scene { outline = (*original_outline).clone(); outline.clip_against_polygon(clip_polygon); outline.apply_perspective(perspective); + + // TODO(pcwalton): Support this in 2D too. + if let Some(barrel_distortion) = options.barrel_distortion { + outline.barrel_distort(barrel_distortion, perspective.window_size); + } } } PreparedRenderTransform::Transform2D(ref transform) => { diff --git a/resources/svg/julius-caesar.svg b/resources/svg/julius-caesar.svg new file mode 100644 index 00000000..89ece6ce --- /dev/null +++ b/resources/svg/julius-caesar.svg @@ -0,0 +1,1478 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/simd/src/scalar.rs b/simd/src/scalar.rs index 94ade16e..c227de7b 100644 --- a/simd/src/scalar.rs +++ b/simd/src/scalar.rs @@ -31,6 +31,11 @@ impl F32x4 { // Basic operations + #[inline] + pub fn approx_recip(self) -> F32x4 { + F32x4([1.0 / self[0], 1.0 / self[1], 1.0 / self[2], 1.0 / self[3]]) + } + #[inline] pub fn min(self, other: F32x4) -> F32x4 { F32x4([ diff --git a/simd/src/x86.rs b/simd/src/x86.rs index f3d07a72..12e859f6 100644 --- a/simd/src/x86.rs +++ b/simd/src/x86.rs @@ -37,6 +37,11 @@ impl F32x4 { // Basic operations + #[inline] + pub fn approx_recip(self) -> F32x4 { + unsafe { F32x4(x86_64::_mm_rcp_ps(self.0)) } + } + #[inline] pub fn min(self, other: F32x4) -> F32x4 { unsafe { F32x4(x86_64::_mm_min_ps(self.0, other.0)) }