diff --git a/.gitignore b/.gitignore index ac289464..d27c2533 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ node_modules /examples/c_canvas_minimal/build /shaders/build /c/build +/examples/macos_app/Pathfinder\ Example.xcodeproj/project.xcworkspace/xcuserdata/* # Editors *.swp diff --git a/Cargo.lock b/Cargo.lock index 8fe5c64a..2db21e83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -357,6 +357,15 @@ name = "constant_time_eq" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "convert" +version = "0.1.0" +dependencies = [ + "pathfinder_export 0.1.0", + "pathfinder_svg 0.1.0", + "usvg 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "core-foundation" version = "0.6.4" @@ -1446,6 +1455,7 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "metal 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "pathfinder_content 0.1.0", + "pathfinder_export 0.1.0", "pathfinder_geometry 0.3.0", "pathfinder_gl 0.1.0", "pathfinder_gpu 0.1.0", @@ -1458,6 +1468,16 @@ dependencies = [ "usvg 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pathfinder_export" +version = "0.1.0" +dependencies = [ + "deflate 0.7.19 (registry+https://github.com/rust-lang/crates.io-index)", + "pathfinder_content 0.1.0", + "pathfinder_geometry 0.3.0", + "pathfinder_renderer 0.1.0", +] + [[package]] name = "pathfinder_geometry" version = "0.3.0" @@ -1537,16 +1557,6 @@ dependencies = [ "pathfinder_simd 0.3.0", ] -[[package]] -name = "pathfinder_pdf" -version = "0.1.0" -dependencies = [ - "deflate 0.7.19 (registry+https://github.com/rust-lang/crates.io-index)", - "pathfinder_content 0.1.0", - "pathfinder_geometry 0.3.0", - "pathfinder_renderer 0.1.0", -] - [[package]] name = "pathfinder_renderer" version = "0.1.0" @@ -2127,15 +2137,6 @@ dependencies = [ "usvg 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "svg2pdf" -version = "0.1.0" -dependencies = [ - "pathfinder_pdf 0.1.0", - "pathfinder_svg 0.1.0", - "usvg 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "svgdom" version = "0.17.0" diff --git a/Cargo.toml b/Cargo.toml index 174b1bcf..aefdaa99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,13 +13,12 @@ members = [ "examples/canvas_moire", "examples/canvas_text", "examples/lottie_basic", - "examples/svg2pdf", "examples/swf_basic", "geometry", "gl", "gpu", "lottie", - "pdf", + "export", "metal", "renderer", "simd", @@ -29,5 +28,6 @@ members = [ "ui", "utils/area-lut", "utils/gamma-lut", - "utils/svg-to-skia" + "utils/svg-to-skia", + "utils/convert", ] diff --git a/README.md b/README.md index 5e946dd6..0c3042b7 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # Pathfinder 3 Pathfinder 3 is a fast, practical, GPU-based rasterizer for fonts and vector graphics using OpenGL -and OpenGL ES 3.0+. +3.0+, OpenGL ES 3.0+, or Metal. Please note that Pathfinder is under heavy development and is incomplete in various areas. @@ -34,7 +34,7 @@ The project features: * Fast GPU rendering, even at small pixel sizes. Even on lower-end GPUs, Pathfinder typically matches or exceeds the performance of the best CPU rasterizers. The difference is particularly - pronouced at large sizes, where Pathfinder regularly achieves multi-factor speedups. All shaders + pronounced at large sizes, where Pathfinder regularly achieves multi-factor speedups. All shaders have no loops and minimal branching. * Advanced font rendering. Pathfinder can render fonts with slight hinting and can perform subpixel @@ -75,12 +75,6 @@ The SDL 2 library requires some additional manual installation steps. Follow the [`rust-sdl2` installation instructions](https://github.com/Rust-SDL2/rust-sdl2#sdl20-development-libraries) to make sure the libraries are installed. Note that SDL2 is only required to run the demo; Pathfinder itself has no dependency on the library. -On macOS, it is recommended that you force the use of the integrated GPU, as issues with Apple's -OpenGL drivers may limit performance on discrete GPUs. You can use -[gfxCardStatus.app](https://gfx.io/) for this. See the -[wiki](https://github.com/pcwalton/pathfinder/wiki/GPU-driver-compatibility) for more information -on GPU compatibility issues. - ## Authors The primary author is Patrick Walton (@pcwalton), with contributions from the Servo development diff --git a/c/cbindgen.toml b/c/cbindgen.toml index 4d096e5d..3d2de1f8 100644 --- a/c/cbindgen.toml +++ b/c/cbindgen.toml @@ -2,18 +2,53 @@ language = "C" header = """\ /* Generated code. Do not edit; instead run `cargo build` in `pathfinder_c`. */ +#ifndef PF_PATHFINDER_H +#define PF_PATHFINDER_H + +#ifdef __APPLE__ +#include +#endif + +#ifdef __cplusplus extern \"C\" { +#endif +""" +trailer = """\ +#ifdef __cplusplus +} +#endif + +#endif """ -trailer = "}" -include_guard = "PF_PATHFINDER_H" include_version = true [parse] parse_deps = true include = [ + "font-kit", "pathfinder_canvas", "pathfinder_content", "pathfinder_geometry", "pathfinder_gl", + "pathfinder_gpu", + "pathfinder_metal", "pathfinder_renderer", ] + +[export.rename] +"BuildOptions" = "PFBuildOptionsPrivate" +"CanvasFontContext" = "PFCanvasFontContextPrivate" +"CanvasRenderingContext2D" = "PFCanvasRenderingContext2DPrivate" +"DestFramebuffer_GLDevice" = "PFDestFramebufferGLDevicePrivate" +"DestFramebuffer_MetalDevice" = "PFDestFramebufferMetalDevicePrivate" +"FillStyle" = "PFFillStylePrivate" +"GLDevice" = "PFGLDevicePrivate" +"Handle" = "FKHandlePrivate" +"MetalDevice" = "PFMetalDevicePrivate" +"Path2D" = "PFPath2DPrivate" +"RenderTransform" = "PFRenderTransformPrivate" +"Renderer_GLDevice" = "PFRendererGLDevicePrivate" +"Renderer_MetalDevice" = "PFRendererMetalDevicePrivate" +"ResourceLoaderWrapper" = "PFResourceLoaderWrapperPrivate" +"Scene" = "PFScenePrivate" +"SceneProxy" = "PFSceneProxyPrivate" diff --git a/c/src/lib.rs b/c/src/lib.rs index 7b2e2ef9..f80768e4 100644 --- a/c/src/lib.rs +++ b/c/src/lib.rs @@ -19,6 +19,8 @@ use pathfinder_content::color::{ColorF, ColorU}; use pathfinder_content::outline::ArcDirection; use pathfinder_content::stroke::LineCap; use pathfinder_geometry::rect::{RectF, RectI}; +use pathfinder_geometry::transform2d::{Matrix2x2F, Transform2F}; +use pathfinder_geometry::transform3d::{Perspective, Transform4F}; use pathfinder_geometry::vector::{Vector2F, Vector2I}; use pathfinder_gl::{GLDevice, GLVersion}; use pathfinder_gpu::resources::{FilesystemResourceLoader, ResourceLoader}; @@ -26,7 +28,7 @@ use pathfinder_renderer::concurrent::rayon::RayonExecutor; use pathfinder_renderer::concurrent::scene_proxy::SceneProxy; use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions}; use pathfinder_renderer::gpu::renderer::Renderer; -use pathfinder_renderer::options::BuildOptions; +use pathfinder_renderer::options::{BuildOptions, RenderTransform}; use pathfinder_renderer::scene::Scene; use pathfinder_simd::default::F32x4; use std::ffi::CString; @@ -60,6 +62,11 @@ pub const PF_TEXT_ALIGN_RIGHT: u8 = 2; pub const PF_ARC_DIRECTION_CW: u8 = 0; pub const PF_ARC_DIRECTION_CCW: u8 = 1; +// `gl` + +pub const PF_GL_VERSION_GL3: u8 = 0; +pub const PF_GL_VERSION_GLES3: u8 = 1; + // `renderer` pub const PF_RENDERER_OPTIONS_FLAGS_HAS_BACKGROUND_COLOR: u8 = 0x1; @@ -120,10 +127,35 @@ pub struct PFRectI { pub origin: PFVector2I, pub lower_right: PFVector2I, } +/// Row-major order. +#[repr(C)] +pub struct PFMatrix2x2F { + pub m00: f32, pub m01: f32, + pub m10: f32, pub m11: f32, +} +/// Row-major order. +#[repr(C)] +pub struct PFTransform2F { + pub matrix: PFMatrix2x2F, + pub vector: PFVector2F, +} +/// Row-major order. +#[repr(C)] +pub struct PFTransform4F { + pub m00: f32, pub m01: f32, pub m02: f32, pub m03: f32, + pub m10: f32, pub m11: f32, pub m12: f32, pub m13: f32, + pub m20: f32, pub m21: f32, pub m22: f32, pub m23: f32, + pub m30: f32, pub m31: f32, pub m32: f32, pub m33: f32, +} +#[repr(C)] +pub struct PFPerspective { + pub transform: PFTransform4F, + pub window_size: PFVector2I, +} // `gl` pub type PFGLDeviceRef = *mut GLDevice; -pub type PFGLVersion = GLVersion; +pub type PFGLVersion = u8; pub type PFGLFunctionLoader = extern "C" fn(name: *const c_char, userdata: *mut c_void) -> *const c_void; // `gpu` @@ -135,7 +167,8 @@ pub type PFMetalDestFramebufferRef = *mut DestFramebuffer; pub type PFMetalRendererRef = *mut Renderer; // FIXME(pcwalton): Double-boxing is unfortunate. Remove this when `std::raw::TraitObject` is // stable? -pub type PFResourceLoaderRef = *mut Box; +pub type PFResourceLoaderRef = *mut ResourceLoaderWrapper; +pub struct ResourceLoaderWrapper(Box); // `metal` #[cfg(all(target_os = "macos", not(feature = "pf-gl")))] @@ -150,20 +183,18 @@ pub struct PFRendererOptions { pub flags: PFRendererOptionsFlags, } pub type PFRendererOptionsFlags = u8; -// TODO(pcwalton) -#[repr(C)] -pub struct PFBuildOptions { - pub placeholder: u32, -} +pub type PFBuildOptionsRef = *mut BuildOptions; +pub type PFRenderTransformRef = *mut RenderTransform; // `canvas` -/// Consumes the font context. +/// This function internally adds a reference to the font context. Therefore, if you created the +/// font context, you must release it yourself to avoid a leak. #[no_mangle] pub unsafe extern "C" fn PFCanvasCreate(font_context: PFCanvasFontContextRef, size: *const PFVector2F) -> PFCanvasRef { - Box::into_raw(Box::new(CanvasRenderingContext2D::new(*Box::from_raw(font_context), + Box::into_raw(Box::new(CanvasRenderingContext2D::new((*font_context).clone(), (*size).to_rust()))) } @@ -188,17 +219,18 @@ pub unsafe extern "C" fn PFCanvasFontContextCreateWithFonts(fonts: *const FKHand } #[no_mangle] -pub unsafe extern "C" fn PFCanvasFontContextDestroy(font_context: PFCanvasFontContextRef) { - drop(Box::from_raw(font_context)) -} - -#[no_mangle] -pub unsafe extern "C" fn PFCanvasFontContextClone(font_context: PFCanvasFontContextRef) - -> PFCanvasFontContextRef { +pub unsafe extern "C" fn PFCanvasFontContextAddRef(font_context: PFCanvasFontContextRef) + -> PFCanvasFontContextRef { Box::into_raw(Box::new((*font_context).clone())) } -/// Consumes the canvas. +#[no_mangle] +pub unsafe extern "C" fn PFCanvasFontContextRelease(font_context: PFCanvasFontContextRef) { + drop(Box::from_raw(font_context)) +} + +/// This function takes ownership of the supplied canvas and will automatically destroy it when +/// the scene is destroyed. #[no_mangle] pub unsafe extern "C" fn PFCanvasCreateScene(canvas: PFCanvasRef) -> PFSceneRef { Box::into_raw(Box::new(Box::from_raw(canvas).into_scene())) @@ -315,13 +347,15 @@ pub unsafe extern "C" fn PFCanvasSetStrokeStyle(canvas: PFCanvasRef, (*canvas).set_stroke_style(*stroke_style) } -/// Consumes the path. +/// This function automatically destroys the path. If you wish to use the path again, clone it +/// first. #[no_mangle] pub unsafe extern "C" fn PFCanvasFillPath(canvas: PFCanvasRef, path: PFPathRef) { (*canvas).fill_path(*Box::from_raw(path)) } -/// Consumes the path. +/// This function automatically destroys the path. If you wish to use the path again, clone it +/// first. #[no_mangle] pub unsafe extern "C" fn PFCanvasStrokePath(canvas: PFCanvasRef, path: PFPathRef) { (*canvas).stroke_path(*Box::from_raw(path)) @@ -421,7 +455,7 @@ pub unsafe extern "C" fn PFFillStyleDestroy(fill_style: PFFillStyleRef) { #[no_mangle] pub unsafe extern "C" fn PFFilesystemResourceLoaderLocate() -> PFResourceLoaderRef { let loader = Box::new(FilesystemResourceLoader::locate()); - Box::into_raw(Box::new(loader as Box)) + Box::into_raw(Box::new(ResourceLoaderWrapper(loader as Box))) } #[no_mangle] @@ -435,6 +469,7 @@ pub unsafe extern "C" fn PFGLLoadWith(loader: PFGLFunctionLoader, userdata: *mut #[no_mangle] pub unsafe extern "C" fn PFGLDeviceCreate(version: PFGLVersion, default_framebuffer: u32) -> PFGLDeviceRef { + let version = match version { PF_GL_VERSION_GLES3 => GLVersion::GLES3, _ => GLVersion::GL3 }; Box::into_raw(Box::new(GLDevice::new(version, default_framebuffer))) } @@ -461,7 +496,9 @@ pub unsafe extern "C" fn PFGLDestFramebufferDestroy(dest_framebuffer: PFGLDestFr drop(Box::from_raw(dest_framebuffer)) } -/// Takes ownership of `device` and `dest_framebuffer`, but not `resources`. +/// This function takes ownership of and automatically takes responsibility for destroying `device` +/// and `dest_framebuffer`. However, it does not take ownership of `resources`; therefore, if you +/// created the resource loader, you must destroy it yourself to avoid a memory leak. #[no_mangle] pub unsafe extern "C" fn PFGLRendererCreate(device: PFGLDeviceRef, resources: PFResourceLoaderRef, @@ -469,7 +506,7 @@ pub unsafe extern "C" fn PFGLRendererCreate(device: PFGLDeviceRef, options: *const PFRendererOptions) -> PFGLRendererRef { Box::into_raw(Box::new(Renderer::new(*Box::from_raw(device), - &**resources, + &*((*resources).0), *Box::from_raw(dest_framebuffer), (*options).to_rust()))) } @@ -498,7 +535,9 @@ pub unsafe extern "C" fn PFMetalDestFramebufferDestroy(dest_framebuffer: drop(Box::from_raw(dest_framebuffer)) } -/// Takes ownership of `device` and `dest_framebuffer`, but not `resources`. +/// This function takes ownership of and automatically takes responsibility for destroying `device` +/// and `dest_framebuffer`. However, it does not take ownership of `resources`; therefore, if you +/// created the resource loader, you must destroy it yourself to avoid a memory leak. #[cfg(all(target_os = "macos", not(feature = "pf-gl")))] #[no_mangle] pub unsafe extern "C" fn PFMetalRendererCreate(device: PFMetalDeviceRef, @@ -507,7 +546,7 @@ pub unsafe extern "C" fn PFMetalRendererCreate(device: PFMetalDeviceRef, options: *const PFRendererOptions) -> PFMetalRendererRef { Box::into_raw(Box::new(Renderer::new(*Box::from_raw(device), - &**resources, + &*((*resources).0), *Box::from_raw(dest_framebuffer), (*options).to_rust()))) } @@ -518,25 +557,33 @@ pub unsafe extern "C" fn PFMetalRendererDestroy(renderer: PFMetalRendererRef) { drop(Box::from_raw(renderer)) } +/// Returns a reference to the Metal device in the renderer. +/// +/// This reference remains valid as long as the device is alive. #[cfg(all(target_os = "macos", not(feature = "pf-gl")))] #[no_mangle] -pub unsafe extern "C" fn PFMetalRendererGetDevice(renderer: PFMetalRendererRef) -> PFMetalDeviceRef { +pub unsafe extern "C" fn PFMetalRendererGetDevice(renderer: PFMetalRendererRef) + -> PFMetalDeviceRef { &mut (*renderer).device } +/// This function does not take ownership of `renderer` or `build_options`. Therefore, if you +/// created the renderer and/or options, you must destroy them yourself to avoid a leak. #[no_mangle] pub unsafe extern "C" fn PFSceneProxyBuildAndRenderGL(scene_proxy: PFSceneProxyRef, renderer: PFGLRendererRef, - build_options: *const PFBuildOptions) { - (*scene_proxy).build_and_render(&mut *renderer, (*build_options).to_rust()) + build_options: PFBuildOptionsRef) { + (*scene_proxy).build_and_render(&mut *renderer, (*build_options).clone()) } +/// This function does not take ownership of `renderer` or `build_options`. Therefore, if you +/// created the renderer and/or options, you must destroy them yourself to avoid a leak. #[cfg(all(target_os = "macos", not(feature = "pf-gl")))] #[no_mangle] pub unsafe extern "C" fn PFSceneProxyBuildAndRenderMetal(scene_proxy: PFSceneProxyRef, renderer: PFMetalRendererRef, - build_options: *const PFBuildOptions) { - (*scene_proxy).build_and_render(&mut *renderer, (*build_options).to_rust()) + build_options: PFBuildOptionsRef) { + (*scene_proxy).build_and_render(&mut *renderer, (*build_options).clone()) } // `metal` @@ -554,8 +601,60 @@ pub unsafe extern "C" fn PFMetalDeviceDestroy(device: PFMetalDeviceRef) { drop(Box::from_raw(device)) } +#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +#[no_mangle] +pub unsafe extern "C" fn PFMetalDevicePresentDrawable(device: PFMetalDeviceRef) { + (*device).present_drawable() +} + // `renderer` +#[no_mangle] +pub unsafe extern "C" fn PFRenderTransformCreate2D(transform: *const PFTransform2F) + -> PFRenderTransformRef { + Box::into_raw(Box::new(RenderTransform::Transform2D((*transform).to_rust()))) +} + +#[no_mangle] +pub unsafe extern "C" fn PFRenderTransformCreatePerspective(perspective: *const PFPerspective) + -> PFRenderTransformRef { + Box::into_raw(Box::new(RenderTransform::Perspective((*perspective).to_rust()))) +} + +#[no_mangle] +pub unsafe extern "C" fn PFRenderTransformDestroy(transform: PFRenderTransformRef) { + drop(Box::from_raw(transform)) +} + +#[no_mangle] +pub unsafe extern "C" fn PFBuildOptionsCreate() -> PFBuildOptionsRef { + Box::into_raw(Box::new(BuildOptions::default())) +} + +#[no_mangle] +pub unsafe extern "C" fn PFBuildOptionsDestroy(options: PFBuildOptionsRef) { + drop(Box::from_raw(options)) +} + +/// Consumes the transform. +#[no_mangle] +pub unsafe extern "C" fn PFBuildOptionsSetTransform(options: PFBuildOptionsRef, + transform: PFRenderTransformRef) { + (*options).transform = *Box::from_raw(transform) +} + +#[no_mangle] +pub unsafe extern "C" fn PFBuildOptionsSetDilation(options: PFBuildOptionsRef, + dilation: *const PFVector2F) { + (*options).dilation = (*dilation).to_rust() +} + +#[no_mangle] +pub unsafe extern "C" fn PFBuildOptionsSetSubpixelAAEnabled(options: PFBuildOptionsRef, + subpixel_aa_enabled: bool) { + (*options).subpixel_aa_enabled = subpixel_aa_enabled +} + #[no_mangle] pub unsafe extern "C" fn PFSceneDestroy(scene: PFSceneRef) { drop(Box::from_raw(scene)) @@ -637,6 +736,40 @@ impl PFVector2I { } } +impl PFMatrix2x2F { + #[inline] + pub fn to_rust(&self) -> Matrix2x2F { + Matrix2x2F::row_major(self.m00, self.m01, self.m10, self.m11) + } +} + +impl PFTransform2F { + #[inline] + pub fn to_rust(&self) -> Transform2F { + Transform2F { matrix: self.matrix.to_rust(), vector: self.vector.to_rust() } + } +} + +impl PFTransform4F { + #[inline] + pub fn to_rust(&self) -> Transform4F { + Transform4F::row_major(self.m00, self.m01, self.m02, self.m03, + self.m10, self.m11, self.m12, self.m13, + self.m20, self.m21, self.m22, self.m23, + self.m30, self.m31, self.m32, self.m33) + } +} + +impl PFPerspective { + #[inline] + pub fn to_rust(&self) -> Perspective { + Perspective { + transform: self.transform.to_rust(), + window_size: self.window_size.to_rust(), + } + } +} + // Helpers for `renderer` impl PFRendererOptions { @@ -651,9 +784,3 @@ impl PFRendererOptions { } } } - -impl PFBuildOptions { - pub fn to_rust(&self) -> BuildOptions { - BuildOptions::default() - } -} diff --git a/canvas/src/lib.rs b/canvas/src/lib.rs index 58279c39..d048c34b 100644 --- a/canvas/src/lib.rs +++ b/canvas/src/lib.rs @@ -25,7 +25,7 @@ use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle}; use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::vector::Vector2F; use pathfinder_geometry::rect::RectF; -use pathfinder_geometry::transform2d::Transform2DF; +use pathfinder_geometry::transform2d::Transform2F; use pathfinder_renderer::paint::{Paint, PaintId}; use pathfinder_renderer::scene::{PathObject, Scene}; use pathfinder_text::{SceneExt, TextRenderMode}; @@ -125,8 +125,7 @@ impl CanvasRenderingContext2D { TextAlign::Center => position.set_x(position.x() - layout.width() * 0.5), } - let transform = Transform2DF::from_translation(position).post_mul(&self.current_state - .transform); + let transform = self.current_state.transform * Transform2F::from_translation(position); // TODO(pcwalton): Report errors. drop(self.scene.push_layout(&layout, @@ -296,7 +295,7 @@ impl CanvasRenderingContext2D { let paint_id = self.scene.push_paint(&paint); let mut outline = outline.clone(); - outline.transform(&Transform2DF::from_translation(self.current_state.shadow_offset)); + outline.transform(&Transform2F::from_translation(self.current_state.shadow_offset)); self.scene.push_path(PathObject::new(outline, paint_id, String::new())) } @@ -306,18 +305,18 @@ impl CanvasRenderingContext2D { // Transformations #[inline] - pub fn current_transform(&self) -> Transform2DF { + pub fn current_transform(&self) -> Transform2F { self.current_state.transform } #[inline] - pub fn set_current_transform(&mut self, new_transform: &Transform2DF) { + pub fn set_current_transform(&mut self, new_transform: &Transform2F) { self.current_state.transform = *new_transform; } #[inline] pub fn reset_transform(&mut self) { - self.current_state.transform = Transform2DF::default(); + self.current_state.transform = Transform2F::default(); } // Compositing @@ -349,7 +348,7 @@ impl CanvasRenderingContext2D { #[derive(Clone)] struct State { - transform: Transform2DF, + transform: Transform2F, font_collection: Arc, font_size: f32, line_width: f32, @@ -369,7 +368,7 @@ struct State { impl State { fn default(default_font_collection: Arc) -> State { State { - transform: Transform2DF::default(), + transform: Transform2F::default(), font_collection: default_font_collection, font_size: DEFAULT_FONT_SIZE, line_width: 1.0, @@ -452,8 +451,7 @@ impl Path2D { start_angle: f32, end_angle: f32, direction: ArcDirection) { - let mut transform = Transform2DF::from_scale(Vector2F::splat(radius)); - transform = transform.post_mul(&Transform2DF::from_translation(center)); + let transform = Transform2F::from_scale(Vector2F::splat(radius)).translate(center); self.current_contour.push_arc(&transform, start_angle, end_angle, direction); } @@ -467,8 +465,7 @@ impl Path2D { let bisector = vu0 + vu1; let center = ctrl + bisector.scale(hypot / bisector.length()); - let mut transform = Transform2DF::from_scale(Vector2F::splat(radius)); - transform = transform.post_mul(&Transform2DF::from_translation(center)); + let transform = Transform2F::from_scale(Vector2F::splat(radius)).translate(center); let chord = LineSegment2F::new(vu0.yx().scale_xy(Vector2F::new(-1.0, 1.0)), vu1.yx().scale_xy(Vector2F::new(1.0, -1.0))); @@ -494,9 +491,7 @@ impl Path2D { end_angle: f32) { self.flush_current_contour(); - let mut transform = Transform2DF::from_rotation(rotation); - transform = transform.post_mul(&Transform2DF::from_scale(axes)); - transform = transform.post_mul(&Transform2DF::from_translation(center)); + let transform = Transform2F::from_scale(axes).rotate(rotation).translate(center); self.current_contour.push_arc(&transform, start_angle, end_angle, ArcDirection::CW); if end_angle - start_angle >= 2.0 * PI { diff --git a/content/src/clip.rs b/content/src/clip.rs index b4620ba5..fc4a0146 100644 --- a/content/src/clip.rs +++ b/content/src/clip.rs @@ -24,15 +24,15 @@ struct Edge(LineSegment2F); impl TEdge for Edge { #[inline] - fn point_is_inside(&self, point: &Vector2F) -> bool { - let area = (self.0.to() - self.0.from()).det(*point - self.0.from()); + fn point_is_inside(&self, point: Vector2F) -> bool { + let area = (self.0.to() - self.0.from()).det(point - self.0.from()); debug!("point_is_inside({:?}, {:?}), area={}", self, point, area); area >= 0.0 } - fn intersect_line_segment(&self, segment: &LineSegment2F) -> ArrayVec<[f32; 3]> { + fn intersect_line_segment(&self, segment: LineSegment2F) -> ArrayVec<[f32; 3]> { let mut results = ArrayVec::new(); - if let Some(t) = segment.intersection_t(&self.0) { + if let Some(t) = segment.intersection_t(self.0) { if t >= 0.0 && t <= 1.0 { results.push(t); } @@ -51,7 +51,7 @@ enum AxisAlignedEdge { impl TEdge for AxisAlignedEdge { #[inline] - fn point_is_inside(&self, point: &Vector2F) -> bool { + fn point_is_inside(&self, point: Vector2F) -> bool { match *self { AxisAlignedEdge::Left(x) => point.x() >= x, AxisAlignedEdge::Top(y) => point.y() >= y, @@ -60,7 +60,7 @@ impl TEdge for AxisAlignedEdge { } } - fn intersect_line_segment(&self, segment: &LineSegment2F) -> ArrayVec<[f32; 3]> { + fn intersect_line_segment(&self, segment: LineSegment2F) -> ArrayVec<[f32; 3]> { let mut results = ArrayVec::new(); let t = match *self { AxisAlignedEdge::Left(x) | AxisAlignedEdge::Right(x) => segment.solve_t_for_x(x), @@ -74,26 +74,26 @@ impl TEdge for AxisAlignedEdge { } trait TEdge: Debug { - fn point_is_inside(&self, point: &Vector2F) -> bool; - fn intersect_line_segment(&self, segment: &LineSegment2F) -> ArrayVec<[f32; 3]>; + fn point_is_inside(&self, point: Vector2F) -> bool; + fn intersect_line_segment(&self, segment: LineSegment2F) -> ArrayVec<[f32; 3]>; fn trivially_test_segment(&self, segment: &Segment) -> EdgeRelativeLocation { - let from_inside = self.point_is_inside(&segment.baseline.from()); + let from_inside = self.point_is_inside(segment.baseline.from()); debug!( "point {:?} inside {:?}: {:?}", segment.baseline.from(), self, from_inside ); - if from_inside != self.point_is_inside(&segment.baseline.to()) { + if from_inside != self.point_is_inside(segment.baseline.to()) { return EdgeRelativeLocation::Intersecting; } if !segment.is_line() { - if from_inside != self.point_is_inside(&segment.ctrl.from()) { + if from_inside != self.point_is_inside(segment.ctrl.from()) { return EdgeRelativeLocation::Intersecting; } if !segment.is_quadratic() { - if from_inside != self.point_is_inside(&segment.ctrl.to()) { + if from_inside != self.point_is_inside(segment.ctrl.to()) { return EdgeRelativeLocation::Intersecting; } } @@ -107,7 +107,7 @@ trait TEdge: Debug { fn intersect_segment(&self, segment: &Segment) -> ArrayVec<[f32; 3]> { if segment.is_line() { - return self.intersect_line_segment(&segment.baseline); + return self.intersect_line_segment(segment.baseline); } let mut segment = *segment; @@ -173,10 +173,10 @@ trait TEdge: Debug { } fn intersects_cubic_segment_hull(&self, cubic_segment: CubicSegment) -> bool { - let inside = self.point_is_inside(&cubic_segment.0.baseline.from()); - inside != self.point_is_inside(&cubic_segment.0.ctrl.from()) - || inside != self.point_is_inside(&cubic_segment.0.ctrl.to()) - || inside != self.point_is_inside(&cubic_segment.0.baseline.to()) + let inside = self.point_is_inside(cubic_segment.0.baseline.from()); + inside != self.point_is_inside(cubic_segment.0.ctrl.from()) + || inside != self.point_is_inside(cubic_segment.0.ctrl.to()) + || inside != self.point_is_inside(cubic_segment.0.baseline.to()) } } @@ -222,7 +222,7 @@ where // We have a potential intersection. debug!("potential intersection: {:?} edge: {:?}", segment, edge); - let mut starts_inside = edge.point_is_inside(&segment.baseline.from()); + let mut starts_inside = edge.point_is_inside(segment.baseline.from()); let intersection_ts = edge.intersect_segment(&segment); let mut last_t = 0.0; debug!("... intersections: {:?}", intersection_ts); diff --git a/content/src/outline.rs b/content/src/outline.rs index b09cc668..e2e5dec2 100644 --- a/content/src/outline.rs +++ b/content/src/outline.rs @@ -16,7 +16,7 @@ use crate::orientation::Orientation; use crate::segment::{Segment, SegmentFlags, SegmentKind}; use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::rect::RectF; -use pathfinder_geometry::transform2d::Transform2DF; +use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform3d::Perspective; use pathfinder_geometry::unit_vector::UnitVector; use pathfinder_geometry::vector::Vector2F; @@ -134,7 +134,7 @@ impl Outline { self.contours.push(contour); } - pub fn transform(&mut self, transform: &Transform2DF) { + pub fn transform(&mut self, transform: &Transform2F) { if transform.is_identity() { return; } @@ -353,7 +353,7 @@ impl Contour { } pub fn push_arc(&mut self, - transform: &Transform2DF, + transform: &Transform2F, start_angle: f32, end_angle: f32, direction: ArcDirection) { @@ -367,13 +367,13 @@ impl Contour { } pub fn push_arc_from_unit_chord(&mut self, - transform: &Transform2DF, + transform: &Transform2F, mut chord: LineSegment2F, direction: ArcDirection) { - let mut direction_transform = Transform2DF::default(); + let mut direction_transform = Transform2F::default(); if direction == ArcDirection::CCW { chord = chord.scale_xy(Vector2F::new(-1.0, 1.0)); - direction_transform = Transform2DF::from_scale(Vector2F::new(-1.0, 1.0)); + direction_transform = Transform2F::from_scale(Vector2F::new(-1.0, 1.0)); } let (mut vector, end_vector) = (UnitVector(chord.from()), UnitVector(chord.to())); @@ -392,9 +392,8 @@ impl Contour { } let half_sweep_vector = sweep_vector.halve_angle(); - let rotation = Transform2DF::from_rotation_vector(half_sweep_vector.rotate_by(vector)); - segment = segment.transform(&direction_transform.post_mul(&rotation) - .post_mul(&transform)); + let rotation = Transform2F::from_rotation_vector(half_sweep_vector.rotate_by(vector)); + segment = segment.transform(&(*transform * rotation * direction_transform)); let mut push_segment_flags = PushSegmentFlags::UPDATE_BOUNDS; if first_segment { @@ -413,19 +412,19 @@ impl Contour { const EPSILON: f32 = 0.001; } - pub fn push_ellipse(&mut self, transform: &Transform2DF) { + pub fn push_ellipse(&mut self, transform: &Transform2F) { let segment = Segment::quarter_circle_arc(); let mut rotation; self.push_segment(&segment.transform(transform), PushSegmentFlags::UPDATE_BOUNDS | PushSegmentFlags::INCLUDE_FROM_POINT); - rotation = Transform2DF::from_rotation_vector(UnitVector(Vector2F::new( 0.0, 1.0))); - self.push_segment(&segment.transform(&rotation.post_mul(&transform)), + rotation = Transform2F::from_rotation_vector(UnitVector(Vector2F::new( 0.0, 1.0))); + self.push_segment(&segment.transform(&(*transform * rotation)), PushSegmentFlags::UPDATE_BOUNDS); - rotation = Transform2DF::from_rotation_vector(UnitVector(Vector2F::new(-1.0, 0.0))); - self.push_segment(&segment.transform(&rotation.post_mul(&transform)), + rotation = Transform2F::from_rotation_vector(UnitVector(Vector2F::new(-1.0, 0.0))); + self.push_segment(&segment.transform(&(*transform * rotation)), PushSegmentFlags::UPDATE_BOUNDS); - rotation = Transform2DF::from_rotation_vector(UnitVector(Vector2F::new( 0.0, -1.0))); - self.push_segment(&segment.transform(&rotation.post_mul(&transform)), + rotation = Transform2F::from_rotation_vector(UnitVector(Vector2F::new( 0.0, -1.0))); + self.push_segment(&segment.transform(&(*transform * rotation)), PushSegmentFlags::UPDATE_BOUNDS); } @@ -434,25 +433,25 @@ impl Contour { debug_assert!(self.point_is_endpoint(point_index)); let mut segment = Segment::none(); - segment.baseline.set_from(&self.position_of(point_index)); + segment.baseline.set_from(self.position_of(point_index)); let point1_index = self.add_to_point_index(point_index, 1); if self.point_is_endpoint(point1_index) { - segment.baseline.set_to(&self.position_of(point1_index)); + segment.baseline.set_to(self.position_of(point1_index)); segment.kind = SegmentKind::Line; } else { - segment.ctrl.set_from(&self.position_of(point1_index)); + segment.ctrl.set_from(self.position_of(point1_index)); let point2_index = self.add_to_point_index(point_index, 2); if self.point_is_endpoint(point2_index) { - segment.baseline.set_to(&self.position_of(point2_index)); + segment.baseline.set_to(self.position_of(point2_index)); segment.kind = SegmentKind::Quadratic; } else { - segment.ctrl.set_to(&self.position_of(point2_index)); + segment.ctrl.set_to(self.position_of(point2_index)); segment.kind = SegmentKind::Cubic; let point3_index = self.add_to_point_index(point_index, 3); - segment.baseline.set_to(&self.position_of(point3_index)); + segment.baseline.set_to(self.position_of(point3_index)); } } @@ -528,20 +527,20 @@ impl Contour { } } - pub fn transform(&mut self, transform: &Transform2DF) { + pub fn transform(&mut self, transform: &Transform2F) { if transform.is_identity() { return; } for (point_index, point) in self.points.iter_mut().enumerate() { - *point = transform.transform_point(*point); + *point = *transform * *point; union_rect(&mut self.bounds, *point, point_index == 0); } } pub fn apply_perspective(&mut self, perspective: &Perspective) { for (point_index, point) in self.points.iter_mut().enumerate() { - *point = perspective.transform_point_2d(point); + *point = *perspective * *point; union_rect(&mut self.bounds, *point, point_index == 0); } } @@ -610,14 +609,14 @@ impl Contour { let ctrl_position = &contour.points[ctrl_point_index]; handle_cubic( self, - &Segment::quadratic(&baseline, *ctrl_position).to_cubic(), + &Segment::quadratic(baseline, *ctrl_position).to_cubic(), ); } else if point_count == 4 { let first_ctrl_point_index = last_endpoint_index as usize + 1; let ctrl_position_0 = &contour.points[first_ctrl_point_index + 0]; let ctrl_position_1 = &contour.points[first_ctrl_point_index + 1]; let ctrl = LineSegment2F::new(*ctrl_position_0, *ctrl_position_1); - handle_cubic(self, &Segment::cubic(&baseline, &ctrl)); + handle_cubic(self, &Segment::cubic(baseline, ctrl)); } self.push_point( @@ -802,21 +801,21 @@ impl<'a> Iterator for ContourIter<'a> { if self.index == contour.len() { let point1 = contour.position_of(0); self.index += 1; - return Some(Segment::line(&LineSegment2F::new(point0, point1))); + return Some(Segment::line(LineSegment2F::new(point0, point1))); } let point1_index = self.index; self.index += 1; let point1 = contour.position_of(point1_index); if contour.point_is_endpoint(point1_index) { - return Some(Segment::line(&LineSegment2F::new(point0, point1))); + return Some(Segment::line(LineSegment2F::new(point0, point1))); } let point2_index = self.index; let point2 = contour.position_of(point2_index); self.index += 1; if contour.point_is_endpoint(point2_index) { - return Some(Segment::quadratic(&LineSegment2F::new(point0, point2), point1)); + return Some(Segment::quadratic(LineSegment2F::new(point0, point2), point1)); } let point3_index = self.index; @@ -824,8 +823,8 @@ impl<'a> Iterator for ContourIter<'a> { self.index += 1; debug_assert!(contour.point_is_endpoint(point3_index)); return Some(Segment::cubic( - &LineSegment2F::new(point0, point3), - &LineSegment2F::new(point1, point2), + LineSegment2F::new(point0, point3), + LineSegment2F::new(point1, point2), )); } } diff --git a/content/src/segment.rs b/content/src/segment.rs index 4a9d6a76..8b0d101e 100644 --- a/content/src/segment.rs +++ b/content/src/segment.rs @@ -11,7 +11,7 @@ //! Line or curve segments, optimized with SIMD. use pathfinder_geometry::line_segment::LineSegment2F; -use pathfinder_geometry::transform2d::Transform2DF; +use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::util::{self, EPSILON}; use pathfinder_geometry::vector::Vector2F; use pathfinder_simd::default::F32x4; @@ -39,9 +39,9 @@ impl Segment { } #[inline] - pub fn line(line: &LineSegment2F) -> Segment { + pub fn line(line: LineSegment2F) -> Segment { Segment { - baseline: *line, + baseline: line, ctrl: LineSegment2F::default(), kind: SegmentKind::Line, flags: SegmentFlags::empty(), @@ -49,9 +49,9 @@ impl Segment { } #[inline] - pub fn quadratic(baseline: &LineSegment2F, ctrl: Vector2F) -> Segment { + pub fn quadratic(baseline: LineSegment2F, ctrl: Vector2F) -> Segment { Segment { - baseline: *baseline, + baseline, ctrl: LineSegment2F::new(ctrl, Vector2F::default()), kind: SegmentKind::Quadratic, flags: SegmentFlags::empty(), @@ -59,10 +59,10 @@ impl Segment { } #[inline] - pub fn cubic(baseline: &LineSegment2F, ctrl: &LineSegment2F) -> Segment { + pub fn cubic(baseline: LineSegment2F, ctrl: LineSegment2F) -> Segment { Segment { - baseline: *baseline, - ctrl: *ctrl, + baseline, + ctrl, kind: SegmentKind::Cubic, flags: SegmentFlags::empty(), } @@ -91,7 +91,7 @@ impl Segment { let (p0x, p0y) = (p3p0.z(), p3p0.w()); let (p1x, p1y) = (4.0 - p0x, (1.0 - p0x) * (3.0 - p0x) / p0y); let p2p1 = F32x4::new(p1x, -p1y, p1x, p1y) * F32x4::splat(1.0 / 3.0); - return Segment::cubic(&LineSegment2F(p3p0), &LineSegment2F(p2p1)); + return Segment::cubic(LineSegment2F(p3p0), LineSegment2F(p2p1)); } #[inline] @@ -100,7 +100,7 @@ impl Segment { let p1 = Vector2F::new(-SQRT_2 / 6.0 + 4.0 / 3.0, 7.0 * SQRT_2 / 6.0 - 4.0 / 3.0); let flip = Vector2F::new(1.0, -1.0); let (p2, p3) = (p1.scale_xy(flip), p0.scale_xy(flip)); - Segment::cubic(&LineSegment2F::new(p3, p0), &LineSegment2F::new(p2, p1)) + Segment::cubic(LineSegment2F::new(p3, p0), LineSegment2F::new(p2, p1)) } #[inline] @@ -198,7 +198,7 @@ impl Segment { // FIXME(pcwalton): Don't degree elevate! if self.is_line() { let (before, after) = self.as_line_segment().split(t); - (Segment::line(&before), Segment::line(&after)) + (Segment::line(before), Segment::line(after)) } else { self.to_cubic().as_cubic_segment().split(t) } @@ -215,10 +215,10 @@ impl Segment { } #[inline] - pub fn transform(self, transform: &Transform2DF) -> Segment { + pub fn transform(self, transform: &Transform2F) -> Segment { Segment { - baseline: transform.transform_line_segment(&self.baseline), - ctrl: transform.transform_line_segment(&self.ctrl), + baseline: *transform * self.baseline, + ctrl: *transform * self.ctrl, kind: self.kind, flags: self.flags, } diff --git a/content/src/stroke.rs b/content/src/stroke.rs index 48f68276..cd9e6034 100644 --- a/content/src/stroke.rs +++ b/content/src/stroke.rs @@ -14,7 +14,7 @@ use crate::outline::{ArcDirection, Contour, Outline, PushSegmentFlags}; use crate::segment::Segment; use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::rect::RectF; -use pathfinder_geometry::transform2d::Transform2DF; +use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::vector::Vector2F; use std::f32; @@ -104,7 +104,7 @@ impl<'a> OutlineStrokeToFill<'a> { stroker.output.add_join(self.style.line_width * 0.5, self.style.line_join, stroker.input.position_of(0), - &final_segment); + final_segment); } stroker.output.closed = true; @@ -138,9 +138,8 @@ impl<'a> OutlineStrokeToFill<'a> { LineCap::Round => { let scale = Vector2F::splat(width * 0.5); let offset = gradient.yx().scale_xy(Vector2F::new(-1.0, 1.0)); - let mut transform = Transform2DF::from_scale(scale); let translation = p1 + offset.scale(width * 0.5); - transform = transform.post_mul(&Transform2DF::from_translation(translation)); + let transform = Transform2F::from_scale(scale).translate(translation); let chord = LineSegment2F::new(-offset, offset); contour.push_arc_from_unit_chord(&transform, chord, ArcDirection::CW); } @@ -235,7 +234,7 @@ impl Offset for Segment { self.ctrl.from() }; - contour.add_join(distance, join, join_point, &LineSegment2F::new(p4, p3)); + contour.add_join(distance, join, join_point, LineSegment2F::new(p4, p3)); } // Push segment. @@ -245,7 +244,7 @@ impl Offset for Segment { fn offset_once(&self, distance: f32) -> Segment { if self.is_line() { - return Segment::line(&self.baseline.offset(distance)); + return Segment::line(self.baseline.offset(distance)); } if self.is_quadratic() { @@ -253,12 +252,12 @@ impl Offset for Segment { let mut segment_1 = LineSegment2F::new(self.ctrl.from(), self.baseline.to()); segment_0 = segment_0.offset(distance); segment_1 = segment_1.offset(distance); - let ctrl = match segment_0.intersection_t(&segment_1) { + let ctrl = match segment_0.intersection_t(segment_1) { Some(t) => segment_0.sample(t), None => segment_0.to().lerp(segment_1.from(), 0.5), }; let baseline = LineSegment2F::new(segment_0.from(), segment_1.to()); - return Segment::quadratic(&baseline, ctrl); + return Segment::quadratic(baseline, ctrl); } debug_assert!(self.is_cubic()); @@ -268,13 +267,13 @@ impl Offset for Segment { let mut segment_1 = LineSegment2F::new(self.ctrl.to(), self.baseline.to()); segment_0 = segment_0.offset(distance); segment_1 = segment_1.offset(distance); - let ctrl = match segment_0.intersection_t(&segment_1) { + let ctrl = match segment_0.intersection_t(segment_1) { Some(t) => segment_0.sample(t), None => segment_0.to().lerp(segment_1.from(), 0.5), }; let baseline = LineSegment2F::new(segment_0.from(), segment_1.to()); let ctrl = LineSegment2F::new(segment_0.from(), ctrl); - return Segment::cubic(&baseline, &ctrl); + return Segment::cubic(baseline, ctrl); } if self.ctrl.to() == self.baseline.to() { @@ -282,13 +281,13 @@ impl Offset for Segment { let mut segment_1 = LineSegment2F::new(self.ctrl.from(), self.baseline.to()); segment_0 = segment_0.offset(distance); segment_1 = segment_1.offset(distance); - let ctrl = match segment_0.intersection_t(&segment_1) { + let ctrl = match segment_0.intersection_t(segment_1) { Some(t) => segment_0.sample(t), None => segment_0.to().lerp(segment_1.from(), 0.5), }; let baseline = LineSegment2F::new(segment_0.from(), segment_1.to()); let ctrl = LineSegment2F::new(ctrl, segment_1.to()); - return Segment::cubic(&baseline, &ctrl); + return Segment::cubic(baseline, ctrl); } let mut segment_0 = LineSegment2F::new(self.baseline.from(), self.ctrl.from()); @@ -298,8 +297,8 @@ impl Offset for Segment { segment_1 = segment_1.offset(distance); segment_2 = segment_2.offset(distance); let (ctrl_0, ctrl_1) = match ( - segment_0.intersection_t(&segment_1), - segment_1.intersection_t(&segment_2), + segment_0.intersection_t(segment_1), + segment_1.intersection_t(segment_2), ) { (Some(t0), Some(t1)) => (segment_0.sample(t0), segment_1.sample(t1)), _ => ( @@ -309,7 +308,7 @@ impl Offset for Segment { }; let baseline = LineSegment2F::new(segment_0.from(), segment_2.to()); let ctrl = LineSegment2F::new(ctrl_0, ctrl_1); - Segment::cubic(&baseline, &ctrl) + Segment::cubic(baseline, ctrl) } fn error_is_within_tolerance(&self, other: &Segment, distance: f32) -> bool { @@ -357,14 +356,14 @@ impl Contour { distance: f32, join: LineJoin, join_point: Vector2F, - next_tangent: &LineSegment2F) { + next_tangent: LineSegment2F) { let (p0, p1) = (self.position_of_last(2), self.position_of_last(1)); let prev_tangent = LineSegment2F::new(p0, p1); match join { LineJoin::Bevel => {} LineJoin::Miter(miter_limit) => { - if let Some(prev_tangent_t) = prev_tangent.intersection_t(&next_tangent) { + if let Some(prev_tangent_t) = prev_tangent.intersection_t(next_tangent) { let miter_endpoint = prev_tangent.sample(prev_tangent_t); let threshold = miter_limit * distance; if (miter_endpoint - join_point).square_length() <= threshold * threshold { @@ -374,8 +373,7 @@ impl Contour { } LineJoin::Round => { let scale = Vector2F::splat(distance.abs()); - let mut transform = Transform2DF::from_scale(scale); - transform = transform.post_mul(&Transform2DF::from_translation(join_point)); + let transform = Transform2F::from_scale(scale).translate(join_point); let chord_from = (prev_tangent.to() - join_point).normalize(); let chord_to = (next_tangent.to() - join_point).normalize(); let chord = LineSegment2F::new(chord_from, chord_to); diff --git a/content/src/transform.rs b/content/src/transform.rs index 283b4386..4f2cde2b 100644 --- a/content/src/transform.rs +++ b/content/src/transform.rs @@ -11,19 +11,19 @@ //! Utilities for transforming paths. use crate::segment::Segment; -use pathfinder_geometry::transform2d::Transform2DF; +use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform3d::Perspective; /// Transforms a path with a SIMD 2D transform. -pub struct Transform2DFPathIter +pub struct Transform2FPathIter where I: Iterator, { iter: I, - transform: Transform2DF, + transform: Transform2F, } -impl Iterator for Transform2DFPathIter +impl Iterator for Transform2FPathIter where I: Iterator, { @@ -34,20 +34,12 @@ where // TODO(pcwalton): Can we go faster by transforming an entire line segment with SIMD? let mut segment = self.iter.next()?; if !segment.is_none() { - segment - .baseline - .set_from(&self.transform.transform_point(segment.baseline.from())); - segment - .baseline - .set_to(&self.transform.transform_point(segment.baseline.to())); + segment.baseline.set_from(self.transform * segment.baseline.from()); + segment.baseline.set_to(self.transform * segment.baseline.to()); if !segment.is_line() { - segment - .ctrl - .set_from(&self.transform.transform_point(segment.ctrl.from())); + segment.ctrl.set_from(self.transform * segment.ctrl.from()); if !segment.is_quadratic() { - segment - .ctrl - .set_to(&self.transform.transform_point(segment.ctrl.to())); + segment.ctrl.set_to(self.transform * segment.ctrl.to()); } } } @@ -55,13 +47,13 @@ where } } -impl Transform2DFPathIter +impl Transform2FPathIter where I: Iterator, { #[inline] - pub fn new(iter: I, transform: &Transform2DF) -> Transform2DFPathIter { - Transform2DFPathIter { + pub fn new(iter: I, transform: &Transform2F) -> Transform2FPathIter { + Transform2FPathIter { iter, transform: *transform, } @@ -87,22 +79,12 @@ where fn next(&mut self) -> Option { let mut segment = self.iter.next()?; if !segment.is_none() { - segment.baseline.set_from( - &self - .perspective - .transform_point_2d(&segment.baseline.from()), - ); - segment - .baseline - .set_to(&self.perspective.transform_point_2d(&segment.baseline.to())); + segment.baseline.set_from(self.perspective * segment.baseline.from()); + segment.baseline.set_to(self.perspective * segment.baseline.to()); if !segment.is_line() { - segment - .ctrl - .set_from(&self.perspective.transform_point_2d(&segment.ctrl.from())); + segment.ctrl.set_from(self.perspective * segment.ctrl.from()); if !segment.is_quadratic() { - segment - .ctrl - .set_to(&self.perspective.transform_point_2d(&segment.ctrl.to())); + segment.ctrl.set_to(self.perspective * segment.ctrl.to()); } } } diff --git a/demo/common/Cargo.toml b/demo/common/Cargo.toml index b6aa30e5..bf6a270b 100644 --- a/demo/common/Cargo.toml +++ b/demo/common/Cargo.toml @@ -25,6 +25,9 @@ features = ["release_max_level_warn"] [dependencies.pathfinder_content] path = "../../content" +[dependencies.pathfinder_export] +path = "../../export" + [dependencies.pathfinder_geometry] path = "../../geometry" diff --git a/demo/common/src/camera.rs b/demo/common/src/camera.rs index eea0649e..888abe48 100644 --- a/demo/common/src/camera.rs +++ b/demo/common/src/camera.rs @@ -14,10 +14,10 @@ // proper. use crate::window::{OcularTransform, View}; -use pathfinder_geometry::vector::{Vector2F, Vector2I, Vector4F}; +use pathfinder_geometry::vector::{Vector2I, Vector4F}; use pathfinder_geometry::rect::RectF; -use pathfinder_geometry::transform2d::Transform2DF; -use pathfinder_geometry::transform3d::{Perspective, Transform3DF}; +use pathfinder_geometry::transform2d::Transform2F; +use pathfinder_geometry::transform3d::{Perspective, Transform4F}; use std::f32::consts::FRAC_PI_4; const NEAR_CLIP_PLANE: f32 = 0.01; @@ -27,7 +27,7 @@ const FAR_CLIP_PLANE: f32 = 10.0; const DEFAULT_EYE_OFFSET: f32 = 0.025; pub enum Camera { - TwoD(Transform2DF), + TwoD(Transform2F), ThreeD { // The ocular transform used for rendering of the scene to the scene framebuffer. If we are // performing stereoscopic rendering, this is then reprojected according to the eye @@ -56,7 +56,7 @@ impl Camera { let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32 * scale_factor_for_view_box(view_box); let origin = viewport_size.to_f32().scale(0.5) - view_box.size().scale(scale * 0.5); - Camera::TwoD(Transform2DF::from_scale(Vector2F::splat(scale)).post_translate(origin)) + Camera::TwoD(Transform2F::from_uniform_scale(scale).translate(origin)) } fn new_3d(mode: Mode, view_box: RectF, viewport_size: Vector2I) -> Camera { @@ -65,15 +65,15 @@ impl Camera { let fov_y = FRAC_PI_4; let aspect = viewport_size.x() as f32 / viewport_size.y() as f32; let projection = - Transform3DF::from_perspective(fov_y, aspect, NEAR_CLIP_PLANE, FAR_CLIP_PLANE); + Transform4F::from_perspective(fov_y, aspect, NEAR_CLIP_PLANE, FAR_CLIP_PLANE); let perspective = Perspective::new(&projection, viewport_size); // Create a scene transform by moving the camera back from the center of the eyes so that // its field of view encompasses the field of view of both eyes. - let z_offset = -DEFAULT_EYE_OFFSET * projection.c0.x(); + let z_offset = Vector4F::new(0.0, 0.0, -DEFAULT_EYE_OFFSET * projection.c0.x(), 1.0); let scene_transform = OcularTransform { perspective, - modelview_to_eye: Transform3DF::from_translation(0.0, 0.0, z_offset), + modelview_to_eye: Transform4F::from_translation(z_offset), }; // For now, initialize the eye transforms as copies of the scene transform. @@ -85,9 +85,10 @@ impl Camera { } else { -eye_offset }; + let this_eye_offset = Vector4F::new(this_eye_offset, 0.0, 0.0, 1.0); OcularTransform { perspective, - modelview_to_eye: Transform3DF::from_translation(this_eye_offset, 0.0, 0.0), + modelview_to_eye: Transform4F::from_translation(this_eye_offset), } }) .collect(); @@ -145,25 +146,17 @@ impl CameraTransform3D { pub fn offset(&mut self, vector: Vector4F) -> bool { let update = !vector.is_zero(); if update { - let rotation = Transform3DF::from_rotation(-self.yaw, -self.pitch, 0.0); - self.position = self.position + rotation.transform_point(vector); + let rotation = Transform4F::from_rotation(-self.yaw, -self.pitch, 0.0); + self.position = self.position + rotation * vector; } update } - pub fn to_transform(&self) -> Transform3DF { - let mut transform = Transform3DF::from_rotation(self.yaw, self.pitch, 0.0); - transform = transform.post_mul(&Transform3DF::from_uniform_scale(2.0 * self.scale)); - transform = transform.post_mul(&Transform3DF::from_translation( - -self.position.x(), - -self.position.y(), - -self.position.z(), - )); - - // Flip Y. - transform = transform.post_mul(&Transform3DF::from_scale(1.0, -1.0, 1.0)); - - transform + pub fn to_transform(&self) -> Transform4F { + let flip = Vector4F::new(1.0, -1.0, 1.0, 1.0); + Transform4F::from_scale(flip).translate(-self.position) + .uniform_scale(2.0 * self.scale) + .rotate(self.yaw, self.pitch, 0.0) } } diff --git a/demo/common/src/lib.rs b/demo/common/src/lib.rs index e97231b2..832597b4 100644 --- a/demo/common/src/lib.rs +++ b/demo/common/src/lib.rs @@ -22,11 +22,12 @@ use crate::device::{GroundProgram, GroundVertexArray}; use crate::ui::{DemoUIModel, DemoUIPresenter, ScreenshotInfo, ScreenshotType, UIAction}; use crate::window::{Event, Keycode, SVGPath, Window, WindowSize}; use clap::{App, Arg}; -use pathfinder_geometry::vector::{Vector2F, Vector2I}; -use pathfinder_geometry::rect::RectF; -use pathfinder_geometry::transform2d::Transform2DF; -use pathfinder_geometry::transform3d::Transform3DF; use pathfinder_content::color::ColorU; +use pathfinder_export::{Export, FileFormat}; +use pathfinder_geometry::rect::RectF; +use pathfinder_geometry::transform2d::Transform2F; +use pathfinder_geometry::transform3d::Transform4F; +use pathfinder_geometry::vector::{Vector2F, Vector2I, Vector4F}; use pathfinder_gpu::resources::ResourceLoader; use pathfinder_gpu::Device; use pathfinder_renderer::concurrent::scene_proxy::{RenderCommandStream, SceneProxy}; @@ -38,7 +39,7 @@ use pathfinder_renderer::scene::Scene; use pathfinder_svg::BuiltSVG; use pathfinder_ui::{MousePosition, UIEvent}; use std::fs::File; -use std::io::{Read, Write}; +use std::io::{BufWriter, Read}; use std::path::PathBuf; use std::thread; use std::time::Duration; @@ -255,10 +256,9 @@ impl DemoApp where W: Window { if modelview_transform.offset(*velocity) { self.dirty = true; } - let perspective = scene_transform - .perspective - .post_mul(&scene_transform.modelview_to_eye) - .post_mul(&modelview_transform.to_transform()); + let perspective = scene_transform.perspective * + scene_transform.modelview_to_eye * + modelview_transform.to_transform(); Some(RenderTransform::Perspective(perspective)) } Camera::TwoD(transform) => Some(RenderTransform::Transform2D(transform)), @@ -327,10 +327,10 @@ impl DemoApp where W: Window { if let Camera::TwoD(ref mut transform) = self.camera { let backing_scale_factor = self.window_size.backing_scale_factor; let position = position.to_f32().scale(backing_scale_factor); - *transform = transform.post_translate(-position); let scale_delta = 1.0 + d_dist * CAMERA_SCALE_SPEED_2D; - *transform = transform.post_scale(Vector2F::splat(scale_delta)); - *transform = transform.post_translate(position); + *transform = transform.translate(-position) + .uniform_scale(scale_delta) + .translate(position); } } Event::Look { pitch, yaw } => { @@ -355,13 +355,21 @@ impl DemoApp where W: Window { *scene_transform = eye_transforms[0]; for (index, eye_transform) in eye_transforms.iter().enumerate().skip(1) { let weight = 1.0 / (index + 1) as f32; - scene_transform.perspective.transform = scene_transform.perspective.transform.lerp(weight, &eye_transform.perspective.transform); - scene_transform.modelview_to_eye = scene_transform.modelview_to_eye.lerp(weight, &eye_transform.modelview_to_eye); + scene_transform.perspective.transform = + scene_transform.perspective + .transform + .lerp(weight, &eye_transform.perspective.transform); + scene_transform.modelview_to_eye = + scene_transform.modelview_to_eye + .lerp(weight, &eye_transform.modelview_to_eye); } // TODO: calculate the eye offset from the eye transforms? - let z_offset = -DEFAULT_EYE_OFFSET * scene_transform.perspective.transform.c0.x(); - scene_transform.modelview_to_eye = scene_transform.modelview_to_eye - .pre_mul(&Transform3DF::from_translation(0.0, 0.0, z_offset)); + let z_offset = -DEFAULT_EYE_OFFSET * + scene_transform.perspective.transform.c0.x(); + let z_offset = Vector4F::new(0.0, 0.0, z_offset, 1.0); + scene_transform.modelview_to_eye = + Transform4F::from_translation(z_offset) * + scene_transform.modelview_to_eye; } } Event::KeyDown(Keycode::Alphanumeric(b'w')) => { @@ -550,7 +558,8 @@ impl DemoApp where W: Window { } Some(ScreenshotInfo { kind: ScreenshotType::SVG, path }) => { // FIXME(pcwalton): This won't work on Android. - File::create(path).unwrap().write_all(&mut self.scene_proxy.as_svg()).unwrap(); + let mut writer = BufWriter::new(File::create(path).unwrap()); + self.scene_proxy.copy_scene().export(&mut writer, FileFormat::SVG).unwrap(); } } } @@ -578,7 +587,7 @@ impl DemoApp where W: Window { } UIEvent::MouseDragged(position) => { if let Camera::TwoD(ref mut transform) = self.camera { - *transform = transform.post_translate(position.relative.to_f32()); + *transform = transform.translate(position.relative.to_f32()); } } _ => {} @@ -598,10 +607,7 @@ impl DemoApp where W: Window { if let Camera::TwoD(ref mut transform) = self.camera { let scale = Vector2F::splat(1.0 + CAMERA_ZOOM_AMOUNT_2D); let center = center_of_window(&self.window_size); - *transform = transform - .post_translate(-center) - .post_scale(scale) - .post_translate(center); + *transform = transform.translate(-center).scale(scale).translate(center); self.dirty = true; } } @@ -609,16 +615,13 @@ impl DemoApp where W: Window { if let Camera::TwoD(ref mut transform) = self.camera { let scale = Vector2F::splat(1.0 - CAMERA_ZOOM_AMOUNT_2D); let center = center_of_window(&self.window_size); - *transform = transform - .post_translate(-center) - .post_scale(scale) - .post_translate(center); + *transform = transform.translate(-center).scale(scale).translate(center); self.dirty = true; } } UIAction::ZoomActualSize => { if let Camera::TwoD(ref mut transform) = self.camera { - *transform = Transform2DF::default(); + *transform = Transform2F::default(); self.dirty = true; } } @@ -626,10 +629,9 @@ impl DemoApp where W: Window { if let Camera::TwoD(ref mut transform) = self.camera { let old_rotation = transform.rotation(); let center = center_of_window(&self.window_size); - *transform = transform - .post_translate(-center) - .post_rotate(*theta - old_rotation) - .post_translate(center); + *transform = transform.translate(-center) + .rotate(*theta - old_rotation) + .translate(center); } } } diff --git a/demo/common/src/renderer.rs b/demo/common/src/renderer.rs index c4bc0cc3..f66be371 100644 --- a/demo/common/src/renderer.rs +++ b/demo/common/src/renderer.rs @@ -18,8 +18,8 @@ use pathfinder_content::color::{ColorF, ColorU}; use pathfinder_gpu::{ClearOps, DepthFunc, DepthState, Device, Primitive, RenderOptions}; use pathfinder_gpu::{RenderState, RenderTarget, TextureData, TextureFormat, UniformData}; use pathfinder_geometry::rect::RectI; -use pathfinder_geometry::transform3d::Transform3DF; -use pathfinder_geometry::vector::Vector2I; +use pathfinder_geometry::transform3d::Transform4F; +use pathfinder_geometry::vector::{Vector2I, Vector4F}; use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions}; use pathfinder_renderer::gpu::renderer::RenderMode; use pathfinder_renderer::gpu_data::RenderCommand; @@ -163,24 +163,20 @@ impl DemoApp where W: Window { let scene_framebuffer = self.scene_framebuffer.as_ref().unwrap(); let scene_texture = self.renderer.device.framebuffer_texture(scene_framebuffer); - let quad_scale_transform = Transform3DF::from_scale( - self.scene_metadata.view_box.size().x(), - self.scene_metadata.view_box.size().y(), - 1.0, - ); + let mut quad_scale = self.scene_metadata.view_box.size().to_3d(); + quad_scale.set_z(1.0); + let quad_scale_transform = Transform4F::from_scale(quad_scale); - let scene_transform_matrix = scene_transform - .perspective - .post_mul(&scene_transform.modelview_to_eye) - .post_mul(&modelview_transform.to_transform()) - .post_mul(&quad_scale_transform); + let scene_transform_matrix = scene_transform.perspective * + scene_transform.modelview_to_eye * + modelview_transform.to_transform() * + quad_scale_transform; let eye_transform = &eye_transforms[render_scene_index as usize]; - let eye_transform_matrix = eye_transform - .perspective - .post_mul(&eye_transform.modelview_to_eye) - .post_mul(&modelview_transform.to_transform()) - .post_mul(&quad_scale_transform); + let eye_transform_matrix = eye_transform.perspective * + eye_transform.modelview_to_eye * + modelview_transform.to_transform() * + quad_scale_transform; debug!( "eye transform({}).modelview_to_eye={:?}", @@ -214,17 +210,14 @@ impl DemoApp where W: Window { let ground_scale = self.scene_metadata.view_box.max_x() * 2.0; - let mut base_transform = perspective.transform; - base_transform = base_transform.post_mul(&Transform3DF::from_translation( - -0.5 * self.scene_metadata.view_box.max_x(), - self.scene_metadata.view_box.max_y(), - -0.5 * ground_scale, - )); + let mut offset = self.scene_metadata.view_box.lower_right().to_3d(); + offset.set_z(ground_scale); + offset = offset * Vector4F::new(-0.5, 1.0, -0.5, 1.0); + let base_transform = perspective.transform * Transform4F::from_translation(offset); // Fill ground. - let mut transform = base_transform; - transform = - transform.post_mul(&Transform3DF::from_scale(ground_scale, 1.0, ground_scale)); + let transform = base_transform * + Transform4F::from_scale(Vector4F::new(ground_scale, 1.0, ground_scale, 1.0)); // Don't clear the first scene after drawing it. let clear_color = if render_scene_index == 0 { diff --git a/demo/common/src/window.rs b/demo/common/src/window.rs index edacf767..b86e115b 100644 --- a/demo/common/src/window.rs +++ b/demo/common/src/window.rs @@ -12,7 +12,7 @@ use pathfinder_geometry::vector::Vector2I; use pathfinder_geometry::rect::RectI; -use pathfinder_geometry::transform3d::{Perspective, Transform3DF}; +use pathfinder_geometry::transform3d::{Perspective, Transform4F}; use pathfinder_gpu::resources::ResourceLoader; use rayon::ThreadPoolBuilder; use std::path::PathBuf; @@ -109,7 +109,7 @@ pub struct OcularTransform { pub perspective: Perspective, // The view transform which converts from world coordinates to camera coordinates - pub modelview_to_eye: Transform3DF, + pub modelview_to_eye: Transform4F, } #[derive(Clone)] diff --git a/demo/magicleap/src/display.rs b/demo/magicleap/src/display.rs index 2176e43d..fed60f2a 100644 --- a/demo/magicleap/src/display.rs +++ b/demo/magicleap/src/display.rs @@ -13,7 +13,7 @@ use std::io; use pathfinder_geometry::point::Point2DI32; use pathfinder_geometry::rect::RectI32; use pathfinder_geometry::transform3d::Perspective; -use pathfinder_geometry::transform3d::Transform3DF32; +use pathfinder_geometry::transform3d::Transform4F32; use pathfinder_gl::GLVersion; use pathfinder_gpu::resources::ResourceLoader; @@ -36,7 +36,7 @@ pub trait DisplayCamera { type Error: DisplayError; fn bounds(&self) -> RectI32; - fn view(&self) -> Transform3DF32; + fn view(&self) -> Transform4F32; fn perspective(&self) -> Perspective; fn make_current(&mut self) -> Result<(), Self::Error>; diff --git a/demo/magicleap/src/glwindow.rs b/demo/magicleap/src/glwindow.rs index 50005428..fb26d467 100644 --- a/demo/magicleap/src/glwindow.rs +++ b/demo/magicleap/src/glwindow.rs @@ -27,7 +27,7 @@ use crate::display::DisplayError; use pathfinder_geometry::point::Point2DI32; use pathfinder_geometry::rect::RectI32; -use pathfinder_geometry::transform3d::Transform3DF32; +use pathfinder_geometry::transform3d::Transform4F32; use pathfinder_geometry::transform3d::Perspective; use pathfinder_gl::GLVersion; use pathfinder_gpu::resources::FilesystemResourceLoader; @@ -149,15 +149,15 @@ impl DisplayCamera for GlWindowCamera { // TODO: add eye offsets let bounds = self.bounds(); let aspect = bounds.size().x() as f32 / bounds.size().y() as f32; - let transform = Transform3DF32::from_perspective(FRAC_PI_4, aspect, NEAR_CLIP_PLANE, FAR_CLIP_PLANE); + let transform = Transform4F32::from_perspective(FRAC_PI_4, aspect, NEAR_CLIP_PLANE, FAR_CLIP_PLANE); Perspective::new(&transform, bounds.size()) } - fn view(&self) -> Transform3DF32 { + fn view(&self) -> Transform4F32 { let duration = Instant::now() - self.start; let rotation = duration.as_millis() as f32 / 1000.0; - Transform3DF32::from_rotation(rotation, 0.0, 0.0) - .pre_mul(&Transform3DF32::from_translation(0.0, 0.0, -CAMERA_DISTANCE)) + Transform4F32::from_rotation(rotation, 0.0, 0.0) + .pre_mul(&Transform4F32::from_translation(0.0, 0.0, -CAMERA_DISTANCE)) } } diff --git a/demo/magicleap/src/immersive.rs b/demo/magicleap/src/immersive.rs index b01741ce..891b92cc 100644 --- a/demo/magicleap/src/immersive.rs +++ b/demo/magicleap/src/immersive.rs @@ -19,8 +19,8 @@ use pathfinder_geometry::point::Point2DI32; use pathfinder_geometry::point::Point2DF32; use pathfinder_geometry::point::Point3DF32; use pathfinder_geometry::rect::RectI32; -use pathfinder_geometry::transform2d::Transform2DF32; -use pathfinder_geometry::transform3d::Transform3DF32; +use pathfinder_geometry::transform2d::Transform2F32; +use pathfinder_geometry::transform3d::Transform4F32; use pathfinder_geometry::transform3d::Perspective; use pathfinder_gpu::Device; use pathfinder_simd::default::F32x4; @@ -41,7 +41,7 @@ pub struct ImmersiveDemo { renderer: Renderer, scene_thread_proxy: SceneThreadProxy, svg_size: Point2DF32, - svg_to_world: Option, + svg_to_world: Option, } static DEFAULT_SVG_VIRTUAL_PATH: &'static str = "svg/Ghostscript_Tiger.svg"; @@ -87,13 +87,13 @@ impl ImmersiveDemo { let svg_size = self.svg_size; let svg_to_world = self.svg_to_world.get_or_insert_with(|| { - let view: Transform3DF32 = cameras[0].view(); + let view: Transform4F32 = cameras[0].view(); let svg_to_world_scale = f32::max(MAX_SVG_WIDTH / svg_size.x(), MAX_SVG_HEIGHT / svg_size.y()); let svg_width = svg_size.x() * svg_to_world_scale; let svg_height = svg_size.y() * svg_to_world_scale; - Transform3DF32::from_uniform_scale(svg_to_world_scale) - .pre_mul(&Transform3DF32::from_translation(-svg_width / 2.0, -svg_height / 2.0, -DEFAULT_SVG_DISTANCE)) - .pre_mul(&Transform3DF32::from_scale(1.0, -1.0, 1.0)) + Transform4F32::from_uniform_scale(svg_to_world_scale) + .pre_mul(&Transform4F32::from_translation(-svg_width / 2.0, -svg_height / 2.0, -DEFAULT_SVG_DISTANCE)) + .pre_mul(&Transform4F32::from_scale(1.0, -1.0, 1.0)) .pre_mul(&view.inverse()) }); diff --git a/demo/magicleap/src/lib.rs b/demo/magicleap/src/lib.rs index 5530e4ab..53b26528 100644 --- a/demo/magicleap/src/lib.rs +++ b/demo/magicleap/src/lib.rs @@ -32,7 +32,7 @@ use pathfinder_demo::window::SVGPath; use pathfinder_geometry::vector::Vector2F; use pathfinder_geometry::vector::Vector2I; use pathfinder_geometry::rect::RectI; -use pathfinder_geometry::transform2d::Transform2DF; +use pathfinder_geometry::transform2d::Transform2F; use pathfinder_content::color::ColorF; use pathfinder_gl::GLDevice; use pathfinder_gl::GLVersion; @@ -216,9 +216,9 @@ pub unsafe extern "C" fn magicleap_pathfinder_render(pf: *mut c_void, options: * let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32 / f32::max(svg.scene.bounds().size().x(), svg.scene.bounds().size().y()); - let transform = Transform2DF::from_translation(svg.scene.bounds().size().scale(-0.5)) - .post_mul(&Transform2DF::from_scale(Vector2F::splat(scale))) - .post_mul(&Transform2DF::from_translation(viewport_size.to_f32().scale(0.5))); + let transform = Transform2F::from_translation(svg.scene.bounds().size().scale(-0.5)) + .post_mul(&Transform2F::from_scale(Vector2F::splat(scale))) + .post_mul(&Transform2F::from_translation(viewport_size.to_f32().scale(0.5))); let render_options = RenderOptions { transform: RenderTransform::Transform2D(transform), diff --git a/demo/magicleap/src/magicleap.rs b/demo/magicleap/src/magicleap.rs index 29c74df9..a96040c0 100644 --- a/demo/magicleap/src/magicleap.rs +++ b/demo/magicleap/src/magicleap.rs @@ -53,7 +53,7 @@ use pathfinder_geometry::vector::Vector2F; use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectI; use pathfinder_geometry::transform3d::Perspective; -use pathfinder_geometry::transform3d::Transform3DF; +use pathfinder_geometry::transform3d::Transform4F; use pathfinder_geometry::util; use pathfinder_gl::GLVersion; use pathfinder_gpu::resources::FilesystemResourceLoader; @@ -77,7 +77,7 @@ pub struct MagicLeapWindow { graphics_client: MLHandle, size: Vector2I, virtual_camera_array: MLGraphicsVirtualCameraInfoArray, - initial_camera_transform: Option, + initial_camera_transform: Option, frame_handle: MLHandle, resource_loader: FilesystemResourceLoader, pose_event: Option>, @@ -234,21 +234,21 @@ impl MagicLeapWindow { } let virtual_camera_array = &self.virtual_camera_array; let initial_camera = self.initial_camera_transform.get_or_insert_with(|| { - let initial_offset = Transform3DF::from_translation(0.0, 0.0, 1.0); + let initial_offset = Transform4F::from_translation(0.0, 0.0, 1.0); let mut camera = virtual_camera_array.virtual_cameras[0].transform; for i in 1..virtual_camera_array.num_virtual_cameras { let next = virtual_camera_array.virtual_cameras[i as usize].transform; camera = camera.lerp(next, 1.0 / (i as f32 + 1.0)); } - Transform3DF::from(camera).post_mul(&initial_offset) + Transform4F::from(camera).post_mul(&initial_offset) }); let camera_transforms = (0..virtual_camera_array.num_virtual_cameras) .map(|i| { let camera = &virtual_camera_array.virtual_cameras[i as usize]; - let projection = Transform3DF::from(camera.projection); + let projection = Transform4F::from(camera.projection); let size = RectI::from(virtual_camera_array.viewport).size(); let perspective = Perspective::new(&projection, size); - let modelview_to_eye = Transform3DF::from(camera.transform).inverse().post_mul(initial_camera); + let modelview_to_eye = Transform4F::from(camera.transform).inverse().post_mul(initial_camera); OcularTransform { perspective, modelview_to_eye } }) .collect(); @@ -355,16 +355,16 @@ impl MLTransform { // Impl pathfinder traits for c-api types -impl From for Transform3DF { +impl From for Transform4F { fn from(mat: MLTransform) -> Self { - Transform3DF::from(mat.rotation) - .pre_mul(&Transform3DF::from(mat.position)) + Transform4F::from(mat.rotation) + .pre_mul(&Transform4F::from(mat.position)) } } -impl From for Transform3DF { +impl From for Transform4F { fn from(v: MLVec3f) -> Self { - Transform3DF::from_translation(v.x, v.y, v.z) + Transform4F::from_translation(v.x, v.y, v.z) } } @@ -380,16 +380,16 @@ impl From for RectI { } } -impl From for Transform3DF { +impl From for Transform4F { fn from(q: MLQuaternionf) -> Self { - Transform3DF::from_rotation_quaternion(F32x4::new(q.x, q.y, q.z, q.w)) + Transform4F::from_rotation_quaternion(F32x4::new(q.x, q.y, q.z, q.w)) } } -impl From for Transform3DF { +impl From for Transform4F { fn from(mat: MLMat4f) -> Self { let a = mat.matrix_colmajor; - Transform3DF::row_major(a[0], a[4], a[8], a[12], + Transform4F::row_major(a[0], a[4], a[8], a[12], a[1], a[5], a[9], a[13], a[2], a[6], a[10], a[14], a[3], a[7], a[11], a[15]) diff --git a/examples/c_canvas_minimal/c_canvas_minimal.c b/examples/c_canvas_minimal/c_canvas_minimal.c index bcfa09b9..8feabc65 100644 --- a/examples/c_canvas_minimal/c_canvas_minimal.c +++ b/examples/c_canvas_minimal/c_canvas_minimal.c @@ -87,7 +87,7 @@ int main(int argc, const char **argv) { // Render the canvas to screen. PFSceneRef scene = PFCanvasCreateScene(canvas); PFSceneProxyRef scene_proxy = PFSceneProxyCreateFromSceneAndRayonExecutor(scene); - PFSceneProxyBuildAndRenderGL(scene_proxy, renderer, &(PFBuildOptions){0}); + PFSceneProxyBuildAndRenderGL(scene_proxy, renderer, PFBuildOptionsCreate()); SDL_GL_SwapWindow(window); // Wait for a keypress. diff --git a/examples/canvas_minimal/src/main.rs b/examples/canvas_minimal/src/main.rs index dce34e5d..27d1469c 100644 --- a/examples/canvas_minimal/src/main.rs +++ b/examples/canvas_minimal/src/main.rs @@ -9,9 +9,9 @@ // except according to those terms. use pathfinder_canvas::{CanvasFontContext, CanvasRenderingContext2D, Path2D}; -use pathfinder_geometry::vector::{Vector2F, Vector2I}; -use pathfinder_geometry::rect::RectF; use pathfinder_content::color::ColorF; +use pathfinder_geometry::rect::RectF; +use pathfinder_geometry::vector::{Vector2F, Vector2I}; use pathfinder_gl::{GLDevice, GLVersion}; use pathfinder_gpu::resources::FilesystemResourceLoader; use pathfinder_renderer::concurrent::rayon::RayonExecutor; diff --git a/examples/macos_app/Pathfinder Example.xcodeproj/project.pbxproj b/examples/macos_app/Pathfinder Example.xcodeproj/project.pbxproj new file mode 100644 index 00000000..9a359ecf --- /dev/null +++ b/examples/macos_app/Pathfinder Example.xcodeproj/project.pbxproj @@ -0,0 +1,354 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 6A9A35B322C1E14700B86652 /* libpathfinder_c.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A9A35B222C1E14700B86652 /* libpathfinder_c.a */; }; + 6AFD6FFA22BD780D00AC1ED3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6AFD6FF922BD780D00AC1ED3 /* AppDelegate.m */; }; + 6AFD6FFC22BD781000AC1ED3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6AFD6FFB22BD781000AC1ED3 /* Assets.xcassets */; }; + 6AFD6FFF22BD781000AC1ED3 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6AFD6FFD22BD781000AC1ED3 /* MainMenu.xib */; }; + 6AFD700222BD781000AC1ED3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6AFD700122BD781000AC1ED3 /* main.m */; }; + 6AFD700B22BD7B7A00AC1ED3 /* PathfinderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6AFD700A22BD7B7A00AC1ED3 /* PathfinderView.m */; }; + 6AFD700F22BD930500AC1ED3 /* libharfbuzz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6AFD700E22BD930500AC1ED3 /* libharfbuzz.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 6A9A35B222C1E14700B86652 /* libpathfinder_c.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpathfinder_c.a; path = ../../../target/release/libpathfinder_c.a; sourceTree = ""; }; + 6AFD6FF522BD780D00AC1ED3 /* Pathfinder Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Pathfinder Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 6AFD6FF822BD780D00AC1ED3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 6AFD6FF922BD780D00AC1ED3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 6AFD6FFB22BD781000AC1ED3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 6AFD6FFE22BD781000AC1ED3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 6AFD700022BD781000AC1ED3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 6AFD700122BD781000AC1ED3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 6AFD700322BD781000AC1ED3 /* Pathfinder_Example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Pathfinder_Example.entitlements; sourceTree = ""; }; + 6AFD700922BD7B7A00AC1ED3 /* PathfinderView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PathfinderView.h; sourceTree = ""; }; + 6AFD700A22BD7B7A00AC1ED3 /* PathfinderView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PathfinderView.m; sourceTree = ""; }; + 6AFD700E22BD930500AC1ED3 /* libharfbuzz.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libharfbuzz.a; path = ../../../../../../../usr/local/lib/libharfbuzz.a; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6AFD6FF222BD780D00AC1ED3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6A9A35B322C1E14700B86652 /* libpathfinder_c.a in Frameworks */, + 6AFD700F22BD930500AC1ED3 /* libharfbuzz.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6AFD6FEC22BD780D00AC1ED3 = { + isa = PBXGroup; + children = ( + 6AFD6FF722BD780D00AC1ED3 /* Pathfinder Example */, + 6AFD6FF622BD780D00AC1ED3 /* Products */, + ); + sourceTree = ""; + }; + 6AFD6FF622BD780D00AC1ED3 /* Products */ = { + isa = PBXGroup; + children = ( + 6AFD6FF522BD780D00AC1ED3 /* Pathfinder Example.app */, + ); + name = Products; + sourceTree = ""; + }; + 6AFD6FF722BD780D00AC1ED3 /* Pathfinder Example */ = { + isa = PBXGroup; + children = ( + 6AFD6FF822BD780D00AC1ED3 /* AppDelegate.h */, + 6AFD6FF922BD780D00AC1ED3 /* AppDelegate.m */, + 6AFD6FFB22BD781000AC1ED3 /* Assets.xcassets */, + 6AFD6FFD22BD781000AC1ED3 /* MainMenu.xib */, + 6AFD700022BD781000AC1ED3 /* Info.plist */, + 6AFD700922BD7B7A00AC1ED3 /* PathfinderView.h */, + 6AFD700A22BD7B7A00AC1ED3 /* PathfinderView.m */, + 6AFD700122BD781000AC1ED3 /* main.m */, + 6AFD700322BD781000AC1ED3 /* Pathfinder_Example.entitlements */, + 6A9A35B222C1E14700B86652 /* libpathfinder_c.a */, + 6AFD700E22BD930500AC1ED3 /* libharfbuzz.a */, + ); + path = "Pathfinder Example"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 6AFD6FF422BD780D00AC1ED3 /* Pathfinder Example */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6AFD700622BD781000AC1ED3 /* Build configuration list for PBXNativeTarget "Pathfinder Example" */; + buildPhases = ( + 6AFD6FF122BD780D00AC1ED3 /* Sources */, + 6AFD6FF222BD780D00AC1ED3 /* Frameworks */, + 6AFD6FF322BD780D00AC1ED3 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Pathfinder Example"; + productName = "Pathfinder Example"; + productReference = 6AFD6FF522BD780D00AC1ED3 /* Pathfinder Example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6AFD6FED22BD780D00AC1ED3 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = "The Pathfinder Project Deelopers"; + TargetAttributes = { + 6AFD6FF422BD780D00AC1ED3 = { + CreatedOnToolsVersion = 10.2.1; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 0; + }; + }; + }; + }; + }; + buildConfigurationList = 6AFD6FF022BD780D00AC1ED3 /* Build configuration list for PBXProject "Pathfinder Example" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6AFD6FEC22BD780D00AC1ED3; + productRefGroup = 6AFD6FF622BD780D00AC1ED3 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6AFD6FF422BD780D00AC1ED3 /* Pathfinder Example */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6AFD6FF322BD780D00AC1ED3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6AFD6FFC22BD781000AC1ED3 /* Assets.xcassets in Resources */, + 6AFD6FFF22BD781000AC1ED3 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6AFD6FF122BD780D00AC1ED3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6AFD700222BD781000AC1ED3 /* main.m in Sources */, + 6AFD700B22BD7B7A00AC1ED3 /* PathfinderView.m in Sources */, + 6AFD6FFA22BD780D00AC1ED3 /* AppDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 6AFD6FFD22BD781000AC1ED3 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 6AFD6FFE22BD781000AC1ED3 /* Base */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 6AFD700422BD781000AC1ED3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 6AFD700522BD781000AC1ED3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + }; + name = Release; + }; + 6AFD700722BD781000AC1ED3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ""; + HEADER_SEARCH_PATHS = ../../c/build/include; + INFOPLIST_FILE = "Pathfinder Example/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + ../../target/release, + /usr/local/lib, + ); + PRODUCT_BUNDLE_IDENTIFIER = "graphics.pathfinder.Pathfinder-Example"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 6AFD700822BD781000AC1ED3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ""; + HEADER_SEARCH_PATHS = ../../c/build/include; + INFOPLIST_FILE = "Pathfinder Example/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + ../../target/release, + /usr/local/lib, + ); + PRODUCT_BUNDLE_IDENTIFIER = "graphics.pathfinder.Pathfinder-Example"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6AFD6FF022BD780D00AC1ED3 /* Build configuration list for PBXProject "Pathfinder Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6AFD700422BD781000AC1ED3 /* Debug */, + 6AFD700522BD781000AC1ED3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6AFD700622BD781000AC1ED3 /* Build configuration list for PBXNativeTarget "Pathfinder Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6AFD700722BD781000AC1ED3 /* Debug */, + 6AFD700822BD781000AC1ED3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6AFD6FED22BD780D00AC1ED3 /* Project object */; +} diff --git a/examples/macos_app/Pathfinder Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/macos_app/Pathfinder Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..acd84826 --- /dev/null +++ b/examples/macos_app/Pathfinder Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/macos_app/Pathfinder Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/macos_app/Pathfinder Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/examples/macos_app/Pathfinder Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/examples/macos_app/Pathfinder Example.xcodeproj/xcshareddata/xcschemes/Pathfinder Example.xcscheme b/examples/macos_app/Pathfinder Example.xcodeproj/xcshareddata/xcschemes/Pathfinder Example.xcscheme new file mode 100644 index 00000000..06c13d8b --- /dev/null +++ b/examples/macos_app/Pathfinder Example.xcodeproj/xcshareddata/xcschemes/Pathfinder Example.xcscheme @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/macos_app/Pathfinder Example.xcodeproj/xcuserdata/pcwalton.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/examples/macos_app/Pathfinder Example.xcodeproj/xcuserdata/pcwalton.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 00000000..8f940316 --- /dev/null +++ b/examples/macos_app/Pathfinder Example.xcodeproj/xcuserdata/pcwalton.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/examples/macos_app/Pathfinder Example.xcodeproj/xcuserdata/pcwalton.xcuserdatad/xcschemes/xcschememanagement.plist b/examples/macos_app/Pathfinder Example.xcodeproj/xcuserdata/pcwalton.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..29a687c5 --- /dev/null +++ b/examples/macos_app/Pathfinder Example.xcodeproj/xcuserdata/pcwalton.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + Pathfinder Example.xcscheme_^#shared#^_ + + orderHint + 0 + + + SuppressBuildableAutocreation + + 6AFD6FF422BD780D00AC1ED3 + + primary + + + + + diff --git a/examples/macos_app/Pathfinder Example/AppDelegate.h b/examples/macos_app/Pathfinder Example/AppDelegate.h new file mode 100644 index 00000000..e8197344 --- /dev/null +++ b/examples/macos_app/Pathfinder Example/AppDelegate.h @@ -0,0 +1,15 @@ +// +// AppDelegate.h +// Pathfinder Example +// +// Created by Patrick Walton on 6/21/19. +// Copyright © 2019 The Pathfinder Project Developers. All rights reserved. +// + +#import + +@interface AppDelegate : NSObject + + +@end + diff --git a/examples/macos_app/Pathfinder Example/AppDelegate.m b/examples/macos_app/Pathfinder Example/AppDelegate.m new file mode 100644 index 00000000..a2448b23 --- /dev/null +++ b/examples/macos_app/Pathfinder Example/AppDelegate.m @@ -0,0 +1,28 @@ +// +// AppDelegate.m +// Pathfinder Example +// +// Created by Patrick Walton on 6/21/19. +// Copyright © 2019 The Pathfinder Project Developers. All rights reserved. +// + +#import "AppDelegate.h" + +@interface AppDelegate () + +@property (weak) IBOutlet NSWindow *window; +@end + +@implementation AppDelegate + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + // Insert code here to initialize your application +} + + +- (void)applicationWillTerminate:(NSNotification *)aNotification { + // Insert code here to tear down your application +} + + +@end diff --git a/examples/macos_app/Pathfinder Example/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/macos_app/Pathfinder Example/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..2db2b1c7 --- /dev/null +++ b/examples/macos_app/Pathfinder Example/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/macos_app/Pathfinder Example/Assets.xcassets/Contents.json b/examples/macos_app/Pathfinder Example/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/examples/macos_app/Pathfinder Example/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/macos_app/Pathfinder Example/Base.lproj/MainMenu.xib b/examples/macos_app/Pathfinder Example/Base.lproj/MainMenu.xib new file mode 100644 index 00000000..7423b96f --- /dev/null +++ b/examples/macos_app/Pathfinder Example/Base.lproj/MainMenu.xib @@ -0,0 +1,373 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/macos_app/Pathfinder Example/Info.plist b/examples/macos_app/Pathfinder Example/Info.plist new file mode 100644 index 00000000..bff81736 --- /dev/null +++ b/examples/macos_app/Pathfinder Example/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2019 The Pathfinder Project Developers. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/examples/macos_app/Pathfinder Example/PathfinderView.h b/examples/macos_app/Pathfinder Example/PathfinderView.h new file mode 100644 index 00000000..59cf7f5d --- /dev/null +++ b/examples/macos_app/Pathfinder Example/PathfinderView.h @@ -0,0 +1,30 @@ +// +// PathfinderView.h +// Pathfinder Example +// +// Created by Patrick Walton on 6/21/19. +// Copyright © 2019 The Pathfinder Project Developers. All rights reserved. +// + +#import +#import +#include + +NS_ASSUME_NONNULL_BEGIN + +@interface PathfinderView : NSView { + id mDevice; + PFMetalRendererRef mRenderer; + PFCanvasFontContextRef mFontContext; + PFBuildOptionsRef mBuildOptions; + CVDisplayLinkRef mDisplayLink; + int32_t mFrameNumber; + CGSize mLayerSize; + NSLock *mRenderLock; +} + +- (void)_render; + +@end + +NS_ASSUME_NONNULL_END diff --git a/examples/macos_app/Pathfinder Example/PathfinderView.m b/examples/macos_app/Pathfinder Example/PathfinderView.m new file mode 100644 index 00000000..03e539d3 --- /dev/null +++ b/examples/macos_app/Pathfinder Example/PathfinderView.m @@ -0,0 +1,194 @@ +// +// PathfinderView.m +// Pathfinder Example +// +// Created by Patrick Walton on 6/21/19. +// Copyright © 2019 The Pathfinder Project Developers. All rights reserved. +// + +#import +#import "PathfinderView.h" +#import +#include + +static CVReturn outputCallback(CVDisplayLinkRef displayLink, + const CVTimeStamp *now, + const CVTimeStamp *outputTime, + CVOptionFlags flagsIn, + CVOptionFlags *flagsOut, + void *userData) { + [(__bridge PathfinderView *)userData _render]; + return kCVReturnSuccess; +} + +static CATransform3D createPerspectiveMatrix(CGFloat fovY, + CGFloat aspect, + CGFloat zNear, + CGFloat zFar) { + CGFloat f = tan(1.0 / (fovY * 0.5)); + CGFloat zDenom = 1.0 / (zNear - zFar); + + CATransform3D transform = CATransform3DIdentity; + transform.m11 = f / aspect; + transform.m22 = f; + transform.m33 = (zFar + zNear) * zDenom; + transform.m34 = -1.0; + transform.m43 = 2.0 * zFar * zNear * zDenom; + return transform; +} + +static PFTransform3DF pfTransformFromCATransform(const CATransform3D *transform) { + // Core Animation matrices are in column-major order, while Pathfinder matrices are in + // row-major order (at least in the latter's C API). So transpose here. + PFTransform3DF pfTransform; + pfTransform.m00 = (float)transform->m11; + pfTransform.m01 = (float)transform->m21; + pfTransform.m02 = (float)transform->m31; + pfTransform.m03 = (float)transform->m41; + pfTransform.m10 = (float)transform->m12; + pfTransform.m11 = (float)transform->m22; + pfTransform.m12 = (float)transform->m32; + pfTransform.m13 = (float)transform->m42; + pfTransform.m20 = (float)transform->m13; + pfTransform.m21 = (float)transform->m23; + pfTransform.m22 = (float)transform->m33; + pfTransform.m23 = (float)transform->m43; + pfTransform.m30 = (float)transform->m14; + pfTransform.m31 = (float)transform->m24; + pfTransform.m32 = (float)transform->m34; + pfTransform.m33 = (float)transform->m44; + return pfTransform; +} + +@implementation PathfinderView + +#define FONT_SIZE 256.0f + +- (void)_render { + [mRenderLock lock]; + + CGSize size = mLayerSize; + + PFCanvasRef canvas = PFCanvasCreate(mFontContext, &(PFVector2F){size.width, size.height}); + PFFillStyleRef fillStyle = + PFFillStyleCreateColor(&(PFColorU){0, 0, 0, 255}); + PFCanvasSetFillStyle(canvas, fillStyle); + PFCanvasSetFontSize(canvas, FONT_SIZE); + PFCanvasSetTextAlign(canvas, PF_TEXT_ALIGN_CENTER); + PFVector2F textOrigin; + textOrigin.x = 0.0; + textOrigin.y = FONT_SIZE * 0.25; + PFCanvasFillText(canvas, "Pathfinder", 0, &textOrigin); + PFCanvasFillRect(canvas, &(const PFRectF){0.0, 0.0, 1.0, 1.0}); + PFFillStyleDestroy(fillStyle); + + PFSceneRef scene = PFCanvasCreateScene(canvas); + PFSceneProxyRef sceneProxy = PFSceneProxyCreateFromSceneAndRayonExecutor(scene); + + int32_t frame = mFrameNumber; + int32_t nT = frame % 240; + if (nT > 120) + nT = 240 - nT; + + CATransform3D transform = + CATransform3DMakeTranslation(0.0, 0.0, -8.0 + (CGFloat)nT / 120.0 * 8.0); + transform = CATransform3DRotate(transform, + frame / 120.0 * M_PI * 2.0, + 0.0, + 1.0, + 0.0); + transform = CATransform3DScale(transform, -2.0 / size.width, 2.0 / size.height, 1.0); + CGFloat aspect = size.width / size.height; + transform = CATransform3DConcat(transform, + createPerspectiveMatrix(M_PI * 0.25, aspect, 0.01, 10.0)); + PFPerspective pfPerspective; + pfPerspective.transform = pfTransformFromCATransform(&transform); + pfPerspective.window_size.x = size.width; + pfPerspective.window_size.y = size.height; + + PFBuildOptionsRef buildOptions = PFBuildOptionsCreate(); + PFRenderTransformRef renderTransform = PFRenderTransformCreatePerspective(&pfPerspective); + PFBuildOptionsSetTransform(buildOptions, renderTransform); + PFSceneProxyBuildAndRenderMetal(sceneProxy, mRenderer, buildOptions); + + PFMetalDevicePresentDrawable(PFMetalRendererGetDevice(mRenderer)); + + mFrameNumber++; + + [mRenderLock unlock]; +} + +- (void)_checkCVResult:(CVReturn)result { + if (result != kCVReturnSuccess) { + @throw [NSException exceptionWithName:@"CoreVideoCallFailed" + reason:@"Core Video call failed" + userInfo:nil]; + } +} + +- (void)_initializeIfNecessary:(CAMetalLayer *)layer { + if (mDevice != nil) + return; + + mFrameNumber = 0; + + mDevice = MTLCreateSystemDefaultDevice(); + [layer setDevice:mDevice]; + [layer setContentsScale:[[self window] backingScaleFactor]]; + + mRenderLock = [[NSLock alloc] init]; + mLayerSize = [self convertSizeToBacking:[layer bounds].size]; + + PFMetalDeviceRef device = PFMetalDeviceCreate(layer); + PFResourceLoaderRef resourceLoader = PFFilesystemResourceLoaderLocate(); + PFMetalDestFramebufferRef destFramebuffer = + PFMetalDestFramebufferCreateFullWindow(&(PFVector2I){mLayerSize.width, mLayerSize.height}); + + PFRendererOptions rendererOptions; + rendererOptions.background_color = (PFColorF){1.0, 1.0, 1.0, 1.0}; + rendererOptions.flags = PF_RENDERER_OPTIONS_FLAGS_HAS_BACKGROUND_COLOR; + mRenderer = PFMetalRendererCreate(device, + resourceLoader, + destFramebuffer, + &rendererOptions); + + mFontContext = PFCanvasFontContextCreateWithSystemSource(); + + mBuildOptions = PFBuildOptionsCreate(); + + [self _checkCVResult:CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink)]; + [self _checkCVResult:CVDisplayLinkSetOutputCallback(mDisplayLink, + outputCallback, + (__bridge void *_Nullable)(self))]; + [self _checkCVResult:CVDisplayLinkStart(mDisplayLink)]; +} + +- (CALayer *)makeBackingLayer { + return [[CAMetalLayer alloc] init]; +} + +- (BOOL)wantsLayer { + return YES; +} + +- (BOOL)wantsUpdateLayer { + return YES; +} + +- (NSViewLayerContentsRedrawPolicy)layerContentsRedrawPolicy { + return NSViewLayerContentsRedrawOnSetNeedsDisplay; +} + +- (void)drawRect:(NSRect)dirtyRect { + [self _initializeIfNecessary:(CAMetalLayer *)[self layer]]; +} + +- (void)displayLayer:(CALayer *)layer { + [self _initializeIfNecessary:(CAMetalLayer *)layer]; +} + +- (void)awakeFromNib { + [self _initializeIfNecessary:(CAMetalLayer *)[self layer]]; +} + +@end diff --git a/examples/macos_app/Pathfinder Example/Pathfinder_Example.entitlements b/examples/macos_app/Pathfinder Example/Pathfinder_Example.entitlements new file mode 100644 index 00000000..0c67376e --- /dev/null +++ b/examples/macos_app/Pathfinder Example/Pathfinder_Example.entitlements @@ -0,0 +1,5 @@ + + + + + diff --git a/examples/macos_app/Pathfinder Example/main.m b/examples/macos_app/Pathfinder Example/main.m new file mode 100644 index 00000000..8f6058f1 --- /dev/null +++ b/examples/macos_app/Pathfinder Example/main.m @@ -0,0 +1,13 @@ +// +// main.m +// Pathfinder Example +// +// Created by Patrick Walton on 6/21/19. +// Copyright © 2019 The Pathfinder Project Developers. All rights reserved. +// + +#import + +int main(int argc, const char * argv[]) { + return NSApplicationMain(argc, argv); +} diff --git a/examples/svg2pdf/src/main.rs b/examples/svg2pdf/src/main.rs deleted file mode 100644 index 9dd7b88e..00000000 --- a/examples/svg2pdf/src/main.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::fs::File; -use std::io::{Read, BufWriter}; -use std::error::Error; -use pathfinder_svg::BuiltSVG; -use pathfinder_pdf::make_pdf; -use usvg::{Tree, Options}; - -fn main() -> Result<(), Box> { - let mut args = std::env::args().skip(1); - let input = args.next().expect("no input given"); - let output = args.next().expect("no output given"); - - let mut data = Vec::new(); - File::open(input)?.read_to_end(&mut data)?; - let svg = BuiltSVG::from_tree(Tree::from_data(&data, &Options::default()).unwrap()); - - make_pdf(BufWriter::new(File::create(output)?), &svg.scene); - - Ok(()) -} diff --git a/examples/swf_basic/src/main.rs b/examples/swf_basic/src/main.rs index cd790074..dc2388a5 100644 --- a/examples/swf_basic/src/main.rs +++ b/examples/swf_basic/src/main.rs @@ -24,7 +24,7 @@ use pathfinder_renderer::scene::Scene; use pathfinder_swf::{draw_paths_into_scene, process_swf_tags}; use std::env; use std::fs::read; -use pathfinder_geometry::transform2d::Transform2DF; +use pathfinder_geometry::transform2d::Transform2F; fn main() { let resource_loader = FilesystemResourceLoader::locate(); @@ -118,7 +118,7 @@ fn main() { // Render the canvas to screen. let scene = SceneProxy::from_scene(scene, RayonExecutor); let mut build_options = BuildOptions::default(); - let scale_transform = Transform2DF::from_scale( + let scale_transform = Transform2F::from_scale( Vector2F::new(device_pixel_ratio, device_pixel_ratio) ); build_options.transform = RenderTransform::Transform2D(scale_transform); diff --git a/pdf/Cargo.toml b/export/Cargo.toml similarity index 90% rename from pdf/Cargo.toml rename to export/Cargo.toml index d9b5ad70..84524894 100644 --- a/pdf/Cargo.toml +++ b/export/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "pathfinder_pdf" +name = "pathfinder_export" version = "0.1.0" authors = ["Sebastian Köln "] edition = "2018" diff --git a/export/src/lib.rs b/export/src/lib.rs new file mode 100644 index 00000000..9ec0c31f --- /dev/null +++ b/export/src/lib.rs @@ -0,0 +1,174 @@ +use pathfinder_renderer::scene::Scene; +use pathfinder_geometry::vector::Vector2F; +use pathfinder_content::segment::SegmentKind; +use std::io::{self, Write}; +use std::fmt; + +mod pdf; +use pdf::Pdf; + +pub enum FileFormat { + /// Scalable Vector Graphics + SVG, + + /// Portable Document Format + PDF, + + /// PostScript + PS, +} + +pub trait Export { + fn export(&self, writer: &mut W, format: FileFormat) -> io::Result<()>; +} + +impl Export for Scene { + fn export(&self, writer: &mut W, format: FileFormat) -> io::Result<()> { + match format { + FileFormat::SVG => export_svg(self, writer), + FileFormat::PDF => export_pdf(self, writer), + FileFormat::PS => export_ps(self, writer) + } + } +} + +fn export_svg(scene: &Scene, writer: &mut W) -> io::Result<()> { + let view_box = scene.view_box(); + writeln!( + writer, + "", + view_box.origin().x(), + view_box.origin().y(), + view_box.size().x(), + view_box.size().y() + )?; + for (paint, outline, name) in scene.paths() { + write!(writer, " ", + paint.color, outline + )?; + } + writeln!(writer, "")?; + Ok(()) +} + +fn export_pdf(scene: &Scene, writer: &mut W) -> io::Result<()> { + let mut pdf = Pdf::new(); + let view_box = scene.view_box(); + pdf.add_page(view_box.size()); + + let height = view_box.size().y(); + let tr = |v: Vector2F| -> Vector2F { + let r = v - view_box.origin(); + Vector2F::new(r.x(), height - r.y()) + }; + + for (paint, outline, _) in scene.paths() { + pdf.set_fill_color(paint.color); + + for contour in outline.contours() { + for (segment_index, segment) in contour.iter().enumerate() { + if segment_index == 0 { + pdf.move_to(tr(segment.baseline.from())); + } + + match segment.kind { + SegmentKind::None => {} + SegmentKind::Line => pdf.line_to(tr(segment.baseline.to())), + SegmentKind::Quadratic => { + let current = segment.baseline.from(); + let c = segment.ctrl.from(); + let p = segment.baseline.to(); + let c1 = Vector2F::splat(2./3.) * c + Vector2F::splat(1./3.) * current; + let c2 = Vector2F::splat(2./3.) * c + Vector2F::splat(1./3.) * p; + pdf.cubic_to(c1, c2, p); + } + SegmentKind::Cubic => pdf.cubic_to(tr(segment.ctrl.from()), tr(segment.ctrl.to()), tr(segment.baseline.to())) + } + } + + if contour.is_closed() { + pdf.close(); + } + } + + // closes implicitly + pdf.fill(); + } + pdf.write_to(writer) +} + +fn export_ps(scene: &Scene, writer: &mut W) -> io::Result<()> { + struct P(Vector2F); + impl fmt::Display for P { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} {}", self.0.x(), self.0.y()) + } + } + + let view_box = scene.view_box(); + writeln!(writer, "%!PS-Adobe-3.0 EPSF-3.0")?; + writeln!(writer, "%%BoundingBox: {:.0} {:.0}", + P(view_box.origin()), + P(view_box.size()), + )?; + writeln!(writer, "%%HiResBoundingBox: {} {}", + P(view_box.origin()), + P(view_box.size()), + )?; + writeln!(writer, "0 {} translate", view_box.size().y())?; + writeln!(writer, "1 -1 scale")?; + + for (paint, outline, name) in scene.paths() { + if !name.is_empty() { + writeln!(writer, "newpath % {}", name)?; + } else { + writeln!(writer, "newpath")?; + } + let color = paint.color.to_f32(); + for contour in outline.contours() { + for (segment_index, segment) in contour.iter().enumerate() { + if segment_index == 0 { + writeln!(writer, "{} moveto", P(segment.baseline.from()))?; + } + + match segment.kind { + SegmentKind::None => {} + SegmentKind::Line => { + writeln!(writer, "{} lineto", P(segment.baseline.to()))?; + } + SegmentKind::Quadratic => { + let current = segment.baseline.from(); + let c = segment.ctrl.from(); + let p = segment.baseline.to(); + let c1 = Vector2F::splat(2. / 3.) * c + Vector2F::splat(1. / 3.) * current; + let c2 = Vector2F::splat(2. / 3.) * c + Vector2F::splat(1. / 3.) * p; + writeln!(writer, "{} {} {} curveto", P(c1), P(c2), P(p))?; + } + SegmentKind::Cubic => { + writeln!(writer, "{} {} {} curveto", + P(segment.ctrl.from()), + P(segment.ctrl.to()), + P(segment.baseline.to()) + )?; + } + } + } + + if contour.is_closed() { + writeln!(writer, "closepath")?; + } + } + writeln!(writer, "{} {} {} setrgbcolor", color.r(), color.g(), color.b())?; + writeln!(writer, "fill")?; + } + writeln!(writer, "showpage")?; + Ok(()) +} + + diff --git a/pdf/src/pdf.rs b/export/src/pdf.rs similarity index 82% rename from pdf/src/pdf.rs rename to export/src/pdf.rs index 7de2ec3e..29f11451 100644 --- a/pdf/src/pdf.rs +++ b/export/src/pdf.rs @@ -1,8 +1,8 @@ //! This is a heavily modified version of the pdfpdf crate by Benjamin Kimock (aka. saethlin) -use pathfinder_geometry::{vector::Vector2F, rect::RectF}; +use pathfinder_geometry::vector::Vector2F; use pathfinder_content::color::ColorU; -use std::io::{self, Write, Cursor, Seek}; +use std::io::{self, Write}; use deflate::Compression; struct Counter { @@ -19,9 +19,6 @@ impl Counter { pub fn pos(&self) -> u64 { self.count } - pub fn into_inner(self) -> T { - self.inner - } } impl Write for Counter { fn write(&mut self, buf: &[u8]) -> io::Result { @@ -101,43 +98,6 @@ impl Pdf { self.objects.len() } - /// Sets the compression level for this document - /// Calls to this method do not affect data produced by operations before the last .add_page - #[inline] - pub fn set_compression(&mut self, compression: Option) { - self.compression = compression; - } - - /// Set the PDF clipping box for the current page - #[inline] - pub fn set_clipping_box(&mut self, rect: RectF) { - let origin = rect.origin(); - let size = rect.size(); - writeln!(self.page_buffer, "{} {} {} {} re W n", - origin.x(), - origin.y(), - size.x(), - size.y() - ).unwrap(); - } - - /// Set the current line width - #[inline] - pub fn set_line_width(&mut self, width: f32) { - writeln!(self.page_buffer, "{} w", width).unwrap(); - } - - /// Set the color for all subsequent drawing operations - #[inline] - pub fn set_stroke_color(&mut self, color: ColorU) { - let norm = |color| f32::from(color) / 255.0; - writeln!(self.page_buffer, "{} {} {} RG", - norm(color.r), - norm(color.g), - norm(color.b) - ).unwrap(); - } - /// Set the color for all subsequent drawing operations #[inline] pub fn set_fill_color(&mut self, color: ColorU) { @@ -178,10 +138,6 @@ impl Pdf { writeln!(self.page_buffer, "f").unwrap(); } - pub fn stroke(&mut self) { - writeln!(self.page_buffer, "s").unwrap(); - } - pub fn close(&mut self) { writeln!(self.page_buffer, "h").unwrap(); } @@ -218,7 +174,7 @@ impl Pdf { /Resources <<\n" .to_vec(); - for (idx, obj) in self.objects.iter().enumerate().filter(|&(_, o)| o.is_xobject) { + for (idx, _obj) in self.objects.iter().enumerate().filter(|&(_, o)| o.is_xobject) { write!(page_object, "/XObject {} 0 R ", idx+1).unwrap(); } @@ -258,7 +214,7 @@ impl Pdf { self.objects.iter().filter(|o| o.is_page).count() )?; out.write_all(b"/Kids [")?; - for (idx, obj) in self.objects.iter().enumerate().filter(|&(_, obj)| obj.is_page) { + for (idx, _obj) in self.objects.iter().enumerate().filter(|&(_, obj)| obj.is_page) { write!(out, "{} 0 R ", idx + 1)?; } out.write_all(b"] >>\nendobj\n")?; diff --git a/geometry/src/line_segment.rs b/geometry/src/line_segment.rs index 7842c893..4a15e471 100644 --- a/geometry/src/line_segment.rs +++ b/geometry/src/line_segment.rs @@ -10,8 +10,8 @@ //! Line segment types, optimized with SIMD. -use crate::vector::Vector2F; use crate::transform2d::Matrix2x2F; +use crate::vector::Vector2F; use crate::util; use pathfinder_simd::default::F32x4; use std::ops::{Add, Sub}; @@ -26,44 +26,44 @@ impl LineSegment2F { } #[inline] - pub fn from(&self) -> Vector2F { - Vector2F(self.0) + pub fn from(self) -> Vector2F { + Vector2F(self.0.xy()) } #[inline] - pub fn to(&self) -> Vector2F { - Vector2F(self.0.zwxy()) + pub fn to(self) -> Vector2F { + Vector2F(self.0.zw()) } #[inline] - pub fn set_from(&mut self, point: &Vector2F) { - self.0 = point.0.concat_xy_zw(self.0) + pub fn set_from(&mut self, point: Vector2F) { + self.0 = point.0.to_f32x4().concat_xy_zw(self.0) } #[inline] - pub fn set_to(&mut self, point: &Vector2F) { - self.0 = self.0.concat_xy_xy(point.0) + pub fn set_to(&mut self, point: Vector2F) { + self.0 = self.0.concat_xy_xy(point.0.to_f32x4()) } #[allow(clippy::wrong_self_convention)] #[inline] - pub fn from_x(&self) -> f32 { + pub fn from_x(self) -> f32 { self.0[0] } #[allow(clippy::wrong_self_convention)] #[inline] - pub fn from_y(&self) -> f32 { + pub fn from_y(self) -> f32 { self.0[1] } #[inline] - pub fn to_x(&self) -> f32 { + pub fn to_x(self) -> f32 { self.0[2] } #[inline] - pub fn to_y(&self) -> f32 { + pub fn to_y(self) -> f32 { self.0[3] } @@ -88,22 +88,22 @@ impl LineSegment2F { } #[inline] - pub fn translate(&self, offset: Vector2F) -> LineSegment2F { - LineSegment2F(self.0 + offset.0.xyxy()) + pub fn translate(self, offset: Vector2F) -> LineSegment2F { + LineSegment2F(self.0 + offset.0.to_f32x4().xyxy()) } #[inline] - pub fn scale(&self, factor: f32) -> LineSegment2F { + pub fn scale(self, factor: f32) -> LineSegment2F { LineSegment2F(self.0 * F32x4::splat(factor)) } #[inline] - pub fn scale_xy(&self, factors: Vector2F) -> LineSegment2F { - LineSegment2F(self.0 * factors.0.xyxy()) + pub fn scale_xy(self, factors: Vector2F) -> LineSegment2F { + LineSegment2F(self.0 * factors.0.to_f32x4().xyxy()) } #[inline] - pub fn split(&self, t: f32) -> (LineSegment2F, LineSegment2F) { + pub fn split(self, t: f32) -> (LineSegment2F, LineSegment2F) { debug_assert!(t >= 0.0 && t <= 1.0); let (from_from, to_to) = (self.0.xyxy(), self.0.zwzw()); let d_d = to_to - from_from; @@ -116,7 +116,7 @@ impl LineSegment2F { // Returns the left segment first, followed by the right segment. #[inline] - pub fn split_at_x(&self, x: f32) -> (LineSegment2F, LineSegment2F) { + pub fn split_at_x(self, x: f32) -> (LineSegment2F, LineSegment2F) { let (min_part, max_part) = self.split(self.solve_t_for_x(x)); if min_part.from_x() < max_part.from_x() { (min_part, max_part) @@ -127,7 +127,7 @@ impl LineSegment2F { // Returns the upper segment first, followed by the lower segment. #[inline] - pub fn split_at_y(&self, y: f32) -> (LineSegment2F, LineSegment2F) { + pub fn split_at_y(self, y: f32) -> (LineSegment2F, LineSegment2F) { let (min_part, max_part) = self.split(self.solve_t_for_y(y)); // Make sure we compare `from_y` and `to_y` to properly handle the case in which one of the @@ -140,32 +140,32 @@ impl LineSegment2F { } #[inline] - pub fn solve_t_for_x(&self, x: f32) -> f32 { + pub fn solve_t_for_x(self, x: f32) -> f32 { (x - self.from_x()) / (self.to_x() - self.from_x()) } #[inline] - pub fn solve_t_for_y(&self, y: f32) -> f32 { + pub fn solve_t_for_y(self, y: f32) -> f32 { (y - self.from_y()) / (self.to_y() - self.from_y()) } #[inline] - pub fn solve_x_for_y(&self, y: f32) -> f32 { + pub fn solve_x_for_y(self, y: f32) -> f32 { util::lerp(self.from_x(), self.to_x(), self.solve_t_for_y(y)) } #[inline] - pub fn solve_y_for_x(&self, x: f32) -> f32 { + pub fn solve_y_for_x(self, x: f32) -> f32 { util::lerp(self.from_y(), self.to_y(), self.solve_t_for_x(x)) } #[inline] - pub fn reversed(&self) -> LineSegment2F { + pub fn reversed(self) -> LineSegment2F { LineSegment2F(self.0.zwxy()) } #[inline] - pub fn upper_point(&self) -> Vector2F { + pub fn upper_point(self) -> Vector2F { if self.from_y() < self.to_y() { self.from() } else { @@ -174,27 +174,27 @@ impl LineSegment2F { } #[inline] - pub fn min_x(&self) -> f32 { + pub fn min_x(self) -> f32 { f32::min(self.from_x(), self.to_x()) } #[inline] - pub fn max_x(&self) -> f32 { + pub fn max_x(self) -> f32 { f32::max(self.from_x(), self.to_x()) } #[inline] - pub fn min_y(&self) -> f32 { + pub fn min_y(self) -> f32 { f32::min(self.from_y(), self.to_y()) } #[inline] - pub fn max_y(&self) -> f32 { + pub fn max_y(self) -> f32 { f32::max(self.from_y(), self.to_y()) } #[inline] - pub fn y_winding(&self) -> i32 { + pub fn y_winding(self) -> i32 { if self.from_y() < self.to_y() { 1 } else { @@ -205,9 +205,9 @@ impl LineSegment2F { // Reverses if necessary so that the from point is above the to point. Calling this method // again will undo the transformation. #[inline] - pub fn orient(&self, y_winding: i32) -> LineSegment2F { + pub fn orient(self, y_winding: i32) -> LineSegment2F { if y_winding >= 0 { - *self + self } else { self.reversed() } @@ -215,55 +215,50 @@ impl LineSegment2F { // TODO(pcwalton): Optimize with SIMD. #[inline] - pub fn square_length(&self) -> f32 { + pub fn square_length(self) -> f32 { let (dx, dy) = (self.to_x() - self.from_x(), self.to_y() - self.from_y()); dx * dx + dy * dy } #[inline] - pub fn vector(&self) -> Vector2F { + pub fn vector(self) -> Vector2F { self.to() - self.from() } // http://www.cs.swan.ac.uk/~cssimon/line_intersection.html - pub fn intersection_t(&self, other: &LineSegment2F) -> Option { + pub fn intersection_t(self, other: LineSegment2F) -> Option { let p0p1 = self.vector(); let matrix = Matrix2x2F(other.vector().0.concat_xy_xy((-p0p1).0)); if f32::abs(matrix.det()) < EPSILON { return None; } - return Some(matrix.inverse().transform_point(self.from() - other.from()).y()); + return Some((matrix.inverse() * (self.from() - other.from())).y()); const EPSILON: f32 = 0.0001; } #[inline] - pub fn sample(&self, t: f32) -> Vector2F { + pub fn sample(self, t: f32) -> Vector2F { self.from() + self.vector().scale(t) } #[inline] - pub fn midpoint(&self) -> Vector2F { + pub fn midpoint(self) -> Vector2F { self.sample(0.5) } #[inline] - pub fn offset(&self, distance: f32) -> LineSegment2F { + pub fn offset(self, distance: f32) -> LineSegment2F { if self.is_zero_length() { - *self + self } else { - *self - + self - .vector() - .yx() - .normalize() - .scale_xy(Vector2F::new(-distance, distance)) + self + self.vector().yx().normalize().scale_xy(Vector2F::new(-distance, distance)) } } #[inline] - pub fn is_zero_length(&self) -> bool { + pub fn is_zero_length(self) -> bool { self.vector().is_zero() } } @@ -272,7 +267,7 @@ impl Add for LineSegment2F { type Output = LineSegment2F; #[inline] fn add(self, point: Vector2F) -> LineSegment2F { - LineSegment2F(self.0 + point.0.xyxy()) + LineSegment2F(self.0 + point.0.to_f32x4().xyxy()) } } @@ -280,14 +275,22 @@ impl Sub for LineSegment2F { type Output = LineSegment2F; #[inline] fn sub(self, point: Vector2F) -> LineSegment2F { - LineSegment2F(self.0 - point.0.xyxy()) + LineSegment2F(self.0 - point.0.to_f32x4().xyxy()) } } #[derive(Clone, Copy, Debug, Default)] -#[repr(transparent)] -pub struct LineSegmentU4(pub u16); +#[repr(C)] +pub struct LineSegmentU4 { + pub from: u8, + pub to: u8, +} #[derive(Clone, Copy, Debug, Default)] -#[repr(transparent)] -pub struct LineSegmentU8(pub u32); +#[repr(C)] +pub struct LineSegmentU8 { + pub from_x: u8, + pub from_y: u8, + pub to_x: u8, + pub to_y: u8, +} diff --git a/geometry/src/rect.rs b/geometry/src/rect.rs index ec7e103a..9116acd1 100644 --- a/geometry/src/rect.rs +++ b/geometry/src/rect.rs @@ -29,36 +29,34 @@ impl RectF { #[inline] pub fn origin(&self) -> Vector2F { - Vector2F(self.0) + Vector2F(self.0.xy()) } #[inline] pub fn size(&self) -> Vector2F { - Vector2F(self.0.zwxy() - self.0.xyxy()) + Vector2F(self.0.zw() - self.0.xy()) } #[inline] pub fn upper_right(&self) -> Vector2F { - Vector2F(self.0.zyxw()) + Vector2F(self.0.zy()) } #[inline] pub fn lower_left(&self) -> Vector2F { - Vector2F(self.0.xwzy()) + Vector2F(self.0.xw()) } #[inline] pub fn lower_right(&self) -> Vector2F { - Vector2F(self.0.zwxy()) + Vector2F(self.0.zw()) } #[inline] pub fn contains_point(&self, point: Vector2F) -> bool { // self.origin <= point && point <= self.lower_right - self.0 - .concat_xy_xy(point.0) - .packed_le(point.0.concat_xy_zw(self.0)) - .is_all_ones() + let point = point.0.to_f32x4(); + self.0.concat_xy_xy(point).packed_le(point.concat_xy_zw(self.0)).is_all_ones() } #[inline] @@ -166,27 +164,27 @@ impl RectI { #[inline] pub fn origin(&self) -> Vector2I { - Vector2I(self.0) + Vector2I(self.0.xy()) } #[inline] pub fn size(&self) -> Vector2I { - Vector2I(self.0.zwxy() - self.0.xyxy()) + Vector2I(self.0.zw() - self.0.xy()) } #[inline] pub fn upper_right(&self) -> Vector2I { - Vector2I(self.0.zyxw()) + Vector2I(self.0.zy()) } #[inline] pub fn lower_left(&self) -> Vector2I { - Vector2I(self.0.xwzy()) + Vector2I(self.0.xw()) } #[inline] pub fn lower_right(&self) -> Vector2I { - Vector2I(self.0.zwxy()) + Vector2I(self.0.zw()) } #[inline] @@ -213,7 +211,8 @@ impl RectI { pub fn contains_point(&self, point: Vector2I) -> bool { // self.origin <= point && point <= self.lower_right - 1 let lower_right = self.lower_right() - Vector2I::splat(1); - self.0 + self.origin() + .0 .concat_xy_xy(point.0) .packed_le(point.0.concat_xy_xy(lower_right.0)) .is_all_ones() diff --git a/geometry/src/transform2d.rs b/geometry/src/transform2d.rs index b48c8882..5979ee21 100644 --- a/geometry/src/transform2d.rs +++ b/geometry/src/transform2d.rs @@ -13,10 +13,10 @@ use crate::line_segment::LineSegment2F; use crate::vector::Vector2F; use crate::rect::RectF; -use crate::transform3d::Transform3DF; +use crate::transform3d::Transform4F; use crate::unit_vector::UnitVector; 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. #[derive(Clone, Copy, Debug, PartialEq)] @@ -42,22 +42,12 @@ impl Matrix2x2F { #[inline] pub fn from_rotation_vector(vector: UnitVector) -> Matrix2x2F { - Matrix2x2F((vector.0).0.xyyx() * F32x4::new(1.0, 1.0, -1.0, 1.0)) + Matrix2x2F((vector.0).0.to_f32x4().xyyx() * F32x4::new(1.0, 1.0, -1.0, 1.0)) } #[inline] - pub fn row_major(m11: f32, m12: f32, m21: f32, m22: f32) -> Matrix2x2F { - Matrix2x2F(F32x4::new(m11, m21, m12, m22)) - } - - #[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) + pub fn row_major(m00: f32, m01: f32, m10: f32, m11: f32) -> Matrix2x2F { + Matrix2x2F(F32x4::new(m00, m10, m01, m11)) } #[inline] @@ -70,12 +60,6 @@ impl Matrix2x2F { Matrix2x2F(self.0.wyzx() * F32x4::new(1.0, -1.0, -1.0, 1.0)) } - #[inline] - pub fn transform_point(&self, point: Vector2F) -> Vector2F { - let halves = self.0 * point.0.xxyy(); - Vector2F(halves + halves.zwzw()) - } - #[inline] pub fn det(&self) -> f32 { self.0[0] * self.0[3] - self.0[2] * self.0[1] @@ -112,49 +96,71 @@ impl Sub for Matrix2x2F { } } -/// An affine transform, optimized with SIMD. -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Transform2DF { - // Row-major order. - matrix: Matrix2x2F, - vector: Vector2F, +impl Mul 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()) + } } -impl Default for Transform2DF { +impl Mul for Matrix2x2F { + type Output = Vector2F; #[inline] - fn default() -> Transform2DF { + fn mul(self, vector: Vector2F) -> Vector2F { + let halves = self.0 * vector.0.to_f32x4().xxyy(); + Vector2F(halves.xy() + halves.zw()) + } +} + +/// An affine transform, optimized with SIMD. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Transform2F { + // Row-major order. + pub matrix: Matrix2x2F, + pub vector: Vector2F, +} + +impl Default for Transform2F { + #[inline] + fn default() -> Transform2F { Self::from_scale(Vector2F::splat(1.0)) } } -impl Transform2DF { +impl Transform2F { #[inline] - pub fn from_scale(scale: Vector2F) -> Transform2DF { - Transform2DF { + pub fn from_scale(scale: Vector2F) -> Transform2F { + Transform2F { matrix: Matrix2x2F::from_scale(scale), vector: Vector2F::default(), } } #[inline] - pub fn from_rotation(theta: f32) -> Transform2DF { - Transform2DF { + pub fn from_uniform_scale(scale: f32) -> Transform2F { + Transform2F::from_scale(Vector2F::splat(scale)) + } + + #[inline] + pub fn from_rotation(theta: f32) -> Transform2F { + Transform2F { matrix: Matrix2x2F::from_rotation(theta), vector: Vector2F::default(), } } #[inline] - pub fn from_rotation_vector(vector: UnitVector) -> Transform2DF { - Transform2DF { + pub fn from_rotation_vector(vector: UnitVector) -> Transform2F { + Transform2F { matrix: Matrix2x2F::from_rotation_vector(vector), vector: Vector2F::default(), } } #[inline] - pub fn from_translation(vector: Vector2F) -> Transform2DF { - Transform2DF { matrix: Matrix2x2F::default(), vector } + pub fn from_translation(vector: Vector2F) -> Transform2F { + Transform2F { matrix: Matrix2x2F::default(), vector } } #[inline] @@ -162,58 +168,24 @@ impl Transform2DF { scale: Vector2F, theta: f32, translation: Vector2F, - ) -> Transform2DF { - let rotation = Transform2DF::from_rotation(theta); - let translation = Transform2DF::from_translation(translation); - Transform2DF::from_scale(scale).post_mul(&rotation).post_mul(&translation) + ) -> Transform2F { + let rotation = Transform2F::from_rotation(theta); + let translation = Transform2F::from_translation(translation); + Transform2F::from_scale(scale) * rotation * translation } #[inline] - pub fn row_major(m11: f32, m12: f32, m21: f32, m22: f32, m31: f32, m32: f32) -> Transform2DF { - Transform2DF { + pub fn row_major(m11: f32, m12: f32, m21: f32, m22: f32, m31: f32, m32: f32) -> Transform2F { + Transform2F { matrix: Matrix2x2F::row_major(m11, m12, m21, m22), vector: Vector2F::new(m31, m32), } } - #[inline] - pub fn transform_point(&self, point: Vector2F) -> Vector2F { - self.matrix.transform_point(point) + self.vector - } - - #[inline] - pub fn transform_line_segment(&self, line_segment: &LineSegment2F) -> LineSegment2F { - LineSegment2F::new(self.transform_point(line_segment.from()), - self.transform_point(line_segment.to())) - } - - #[inline] - pub fn transform_rect(&self, rect: &RectF) -> RectF { - let upper_left = self.transform_point(rect.origin()); - let upper_right = self.transform_point(rect.upper_right()); - let lower_left = self.transform_point(rect.lower_left()); - let lower_right = self.transform_point(rect.lower_right()); - let min_point = upper_left.min(upper_right).min(lower_left).min(lower_right); - let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right); - RectF::from_points(min_point, max_point) - } - - #[inline] - pub fn post_mul(&self, other: &Transform2DF) -> Transform2DF { - let matrix = self.matrix.post_mul(&other.matrix); - let vector = other.transform_point(self.vector); - Transform2DF { matrix, vector } - } - - #[inline] - pub fn pre_mul(&self, other: &Transform2DF) -> Transform2DF { - other.post_mul(self) - } - // TODO(pcwalton): Optimize better with SIMD. #[inline] - pub fn to_3d(&self) -> Transform3DF { - Transform3DF::row_major( + pub fn to_3d(&self) -> Transform4F { + Transform4F::row_major( self.matrix.0[0], self.matrix.0[1], 0.0, @@ -235,7 +207,7 @@ impl Transform2DF { #[inline] pub fn is_identity(&self) -> bool { - *self == Transform2DF::default() + *self == Transform2F::default() } #[inline] @@ -256,18 +228,23 @@ impl Transform2DF { } #[inline] - pub fn post_translate(&self, vector: Vector2F) -> Transform2DF { - self.post_mul(&Transform2DF::from_translation(vector)) + pub fn translate(&self, vector: Vector2F) -> Transform2F { + Transform2F::from_translation(vector) * *self } #[inline] - pub fn post_rotate(&self, theta: f32) -> Transform2DF { - self.post_mul(&Transform2DF::from_rotation(theta)) + pub fn rotate(&self, theta: f32) -> Transform2F { + Transform2F::from_rotation(theta) * *self } #[inline] - pub fn post_scale(&self, scale: Vector2F) -> Transform2DF { - self.post_mul(&Transform2DF::from_scale(scale)) + pub fn scale(&self, scale: Vector2F) -> Transform2F { + Transform2F::from_scale(scale) * *self + } + + #[inline] + pub fn uniform_scale(&self, scale: f32) -> Transform2F { + self.scale(Vector2F::splat(scale)) } /// Returns the translation part of this matrix. @@ -291,6 +268,52 @@ impl Transform2DF { /// This decomposition assumes that scale, rotation, and translation are applied in that order. #[inline] pub fn scale_factor(&self) -> f32 { - Vector2F(self.matrix.0.zwxy()).length() + Vector2F(self.matrix.0.zw()).length() + } +} + +impl Mul for Transform2F { + type Output = Transform2F; + #[inline] + fn mul(self, other: Transform2F) -> Transform2F { + Transform2F { + matrix: self.matrix * other.matrix, + vector: self * other.vector, + } + } +} + +impl Mul for Transform2F { + type Output = Vector2F; + #[inline] + fn mul(self, vector: Vector2F) -> Vector2F { + self.matrix * vector + self.vector + } +} + +impl Mul for Transform2F { + type Output = LineSegment2F; + #[inline] + fn mul(self, line_segment: LineSegment2F) -> LineSegment2F { + LineSegment2F::new(self * line_segment.from(), self * line_segment.to()) + } +} + +impl Mul for Transform2F { + type Output = RectF; + #[inline] + fn mul(self, rect: RectF) -> RectF { + let (upper_left, upper_right) = (self * rect.origin(), self * rect.upper_right()); + let (lower_left, lower_right) = (self * rect.lower_left(), self * rect.lower_right()); + let min_point = upper_left.min(upper_right).min(lower_left).min(lower_right); + let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right); + RectF::from_points(min_point, max_point) + } +} + +impl MulAssign for Transform2F { + #[inline] + fn mul_assign(&mut self, other: Transform2F) { + *self = *self * other } } diff --git a/geometry/src/transform3d.rs b/geometry/src/transform3d.rs index 7425b34c..9f29b659 100644 --- a/geometry/src/transform3d.rs +++ b/geometry/src/transform3d.rs @@ -14,24 +14,24 @@ use crate::vector::{Vector2F, Vector2I, Vector4F}; use crate::rect::RectF; use crate::transform2d::Matrix2x2F; use pathfinder_simd::default::F32x4; -use std::ops::{Add, Neg}; +use std::ops::{Add, Mul, MulAssign, Neg}; /// An transform, optimized with SIMD. /// /// In column-major order. #[derive(Clone, Copy, Debug, PartialEq)] #[repr(C)] -pub struct Transform3DF { +pub struct Transform4F { pub c0: F32x4, pub c1: F32x4, pub c2: F32x4, pub c3: F32x4, } -impl Default for Transform3DF { +impl Default for Transform4F { #[inline] - fn default() -> Transform3DF { - Transform3DF { + fn default() -> Transform4F { + Transform4F { c0: F32x4::new(1.0, 0.0, 0.0, 0.0), c1: F32x4::new(0.0, 1.0, 0.0, 0.0), c2: F32x4::new(0.0, 0.0, 1.0, 0.0), @@ -40,7 +40,7 @@ impl Default for Transform3DF { } } -impl Transform3DF { +impl Transform4F { #[inline] pub fn row_major( m00: f32, @@ -59,8 +59,8 @@ impl Transform3DF { m31: f32, m32: f32, m33: f32, - ) -> Transform3DF { - Transform3DF { + ) -> Transform4F { + Transform4F { c0: F32x4::new(m00, m10, m20, m30), c1: F32x4::new(m01, m11, m21, m31), c2: F32x4::new(m02, m12, m22, m32), @@ -69,26 +69,28 @@ impl Transform3DF { } #[inline] - pub fn from_scale(x: f32, y: f32, z: f32) -> Transform3DF { - Transform3DF::row_major( - x, 0.0, 0.0, 0.0, 0.0, y, 0.0, 0.0, 0.0, 0.0, z, 0.0, 0.0, 0.0, 0.0, 1.0, - ) + pub fn from_scale(scale: Vector4F) -> Transform4F { + Transform4F { + c0: F32x4::new(scale.x(), 0.0, 0.0, 0.0), + c1: F32x4::new(0.0, scale.y(), 0.0, 0.0), + c2: F32x4::new(0.0, 0.0, scale.z(), 0.0), + c3: F32x4::new(0.0, 0.0, 0.0, 1.0), + } } #[inline] - pub fn from_uniform_scale(factor: f32) -> Transform3DF { - Transform3DF::from_scale(factor, factor, factor) + pub fn from_uniform_scale(factor: f32) -> Transform4F { + Transform4F::from_scale(Vector4F::splat(factor)) } #[inline] - pub fn from_translation(x: f32, y: f32, z: f32) -> Transform3DF { - Transform3DF::row_major( - 1.0, 0.0, 0.0, x, 0.0, 1.0, 0.0, y, 0.0, 0.0, 1.0, z, 0.0, 0.0, 0.0, 1.0, - ) + pub fn from_translation(mut translation: Vector4F) -> Transform4F { + translation.set_w(1.0); + Transform4F { c3: translation.0, ..Transform4F::default() } } // TODO(pcwalton): Optimize. - pub fn from_rotation(yaw: f32, pitch: f32, roll: f32) -> Transform3DF { + pub fn from_rotation(yaw: f32, pitch: f32, roll: f32) -> Transform4F { let (cos_b, sin_b) = (yaw.cos(), yaw.sin()); let (cos_c, sin_c) = (pitch.cos(), pitch.sin()); let (cos_a, sin_a) = (roll.cos(), roll.sin()); @@ -101,7 +103,7 @@ impl Transform3DF { let m20 = -sin_b; let m21 = cos_b * sin_c; let m22 = cos_b * cos_c; - Transform3DF::row_major( + Transform4F::row_major( m00, m01, m02, 0.0, m10, m11, m12, 0.0, m20, m21, m22, 0.0, 0.0, 0.0, 0.0, 1.0, ) } @@ -110,7 +112,7 @@ impl Transform3DF { /// /// The quaternion is expected to be packed into a SIMD type (x, y, z, w) corresponding to /// x + yi + zj + wk. - pub fn from_rotation_quaternion(q: F32x4) -> Transform3DF { + pub fn from_rotation_quaternion(q: F32x4) -> Transform4F { // TODO(pcwalton): Optimize better with more shuffles. let (mut sq, mut w, mut xy_xz_yz) = (q * q, q.wwww() * q, q.xxyy() * q.yzzy()); sq += sq; @@ -119,7 +121,7 @@ impl Transform3DF { let diag = F32x4::splat(1.0) - (sq.yxxy() + sq.zzyy()); let (wx2, wy2, wz2) = (w.x(), w.y(), w.z()); let (xy2, xz2, yz2) = (xy_xz_yz.x(), xy_xz_yz.y(), xy_xz_yz.z()); - Transform3DF::row_major( + Transform4F::row_major( diag.x(), xy2 - wz2, xz2 + wy2, @@ -148,14 +150,14 @@ impl Transform3DF { top: f32, near_val: f32, far_val: f32, - ) -> Transform3DF { + ) -> Transform4F { let x_inv = 1.0 / (right - left); let y_inv = 1.0 / (top - bottom); let z_inv = 1.0 / (far_val - near_val); let tx = -(right + left) * x_inv; let ty = -(top + bottom) * y_inv; let tz = -(far_val + near_val) * z_inv; - Transform3DF::row_major( + Transform4F::row_major( 2.0 * x_inv, 0.0, 0.0, @@ -176,17 +178,17 @@ impl Transform3DF { } /// Linearly interpolate between transforms - pub fn lerp(&self, weight: f32, other: &Transform3DF) -> Transform3DF { + pub fn lerp(&self, weight: f32, other: &Transform4F) -> Transform4F { let c0 = self.c0 * F32x4::splat(weight) + other.c0 * F32x4::splat(1.0 - weight); let c1 = self.c1 * F32x4::splat(weight) + other.c1 * F32x4::splat(1.0 - weight); let c2 = self.c2 * F32x4::splat(weight) + other.c2 * F32x4::splat(1.0 - weight); let c3 = self.c3 * F32x4::splat(weight) + other.c3 * F32x4::splat(1.0 - weight); - Transform3DF { c0, c1, c2, c3 } + Transform4F { c0, c1, c2, c3 } } /// Just like `gluPerspective()`. #[inline] - pub fn from_perspective(fov_y: f32, aspect: f32, z_near: f32, z_far: f32) -> Transform3DF { + pub fn from_perspective(fov_y: f32, aspect: f32, z_near: f32, z_far: f32) -> Transform4F { let f = 1.0 / (fov_y * 0.5).tan(); let z_denom = 1.0 / (z_near - z_far); let m00 = f / aspect; @@ -194,7 +196,7 @@ impl Transform3DF { let m22 = (z_far + z_near) * z_denom; let m23 = 2.0 * z_far * z_near * z_denom; let m32 = -1.0; - Transform3DF::row_major( + Transform4F::row_major( m00, 0.0, 0.0, 0.0, 0.0, m11, 0.0, 0.0, 0.0, 0.0, m22, m23, 0.0, 0.0, m32, 0.0, ) } @@ -209,8 +211,8 @@ impl Transform3DF { b: Matrix2x2F, c: Matrix2x2F, d: Matrix2x2F, - ) -> Transform3DF { - Transform3DF { + ) -> Transform4F { + Transform4F { c0: a.0.concat_xy_xy(c.0), c1: a.0.concat_zw_zw(c.0), c2: b.0.concat_xy_xy(d.0), @@ -218,38 +220,24 @@ impl Transform3DF { } } - // FIXME(pcwalton): Is this right, due to transposition? I think we may have to reverse the - // two. - // - // https://stackoverflow.com/a/18508113 #[inline] - pub fn pre_mul(&self, other: &Transform3DF) -> Transform3DF { - return Transform3DF { - c0: mul_col(self.c0, other), - c1: mul_col(self.c1, other), - c2: mul_col(self.c2, other), - c3: mul_col(self.c3, other), - }; - - fn mul_col(a_col: F32x4, b: &Transform3DF) -> F32x4 { - let (a0, a1) = (F32x4::splat(a_col[0]), F32x4::splat(a_col[1])); - let (a2, a3) = (F32x4::splat(a_col[2]), F32x4::splat(a_col[3])); - a0 * b.c0 + a1 * b.c1 + a2 * b.c2 + a3 * b.c3 - } + pub fn rotate(&self, yaw: f32, pitch: f32, roll: f32) -> Transform4F { + Transform4F::from_rotation(yaw, pitch, roll) * *self } #[inline] - pub fn post_mul(&self, other: &Transform3DF) -> Transform3DF { - other.pre_mul(self) + pub fn scale(&self, scale: Vector4F) -> Transform4F { + Transform4F::from_scale(scale) * *self } #[inline] - pub fn transform_point(&self, point: Vector4F) -> Vector4F { - let term0 = self.c0 * F32x4::splat(point.x()); - let term1 = self.c1 * F32x4::splat(point.y()); - let term2 = self.c2 * F32x4::splat(point.z()); - let term3 = self.c3 * F32x4::splat(point.w()); - Vector4F(term0 + term1 + term2 + term3) + pub fn uniform_scale(&self, scale: f32) -> Transform4F { + Transform4F::from_uniform_scale(scale) * *self + } + + #[inline] + pub fn translate(&self, translation: Vector4F) -> Transform4F { + Transform4F::from_translation(translation) * *self } #[inline] @@ -276,26 +264,26 @@ impl Transform3DF { // // If A is the upper left submatrix of this matrix, this method assumes that A and the Schur // complement of A are invertible. - pub fn inverse(&self) -> Transform3DF { + pub fn inverse(&self) -> Transform4F { // Extract submatrices. let (a, b) = (self.upper_left(), self.upper_right()); let (c, d) = (self.lower_left(), self.lower_right()); // Compute temporary matrices. let a_inv = a.inverse(); - let x = c.post_mul(&a_inv); - let y = (d - x.post_mul(&b)).inverse(); - let z = a_inv.post_mul(&b); + let x = c * a_inv; + let y = (d - x * b).inverse(); + let z = a_inv * b; // Compute new submatrices. - let (a_new, b_new) = (a_inv + z.post_mul(&y).post_mul(&x), (-z).post_mul(&y)); - let (c_new, d_new) = ((-y).post_mul(&x), y); + let (a_new, b_new) = (a_inv + z * y * x, -z * y); + let (c_new, d_new) = (-y * x, y); // Construct inverse. - Transform3DF::from_submatrices(a_new, b_new, c_new, d_new) + Transform4F::from_submatrices(a_new, b_new, c_new, d_new) } - pub fn approx_eq(&self, other: &Transform3DF, epsilon: f32) -> bool { + pub fn approx_eq(&self, other: &Transform4F, epsilon: f32) -> bool { self.c0.approx_eq(other.c0, epsilon) && self.c1.approx_eq(other.c1, epsilon) && self.c2.approx_eq(other.c2, epsilon) @@ -306,6 +294,50 @@ impl Transform3DF { pub fn as_ptr(&self) -> *const f32 { (&self.c0) as *const F32x4 as *const f32 } + + #[inline] + pub fn to_columns(&self) -> [F32x4; 4] { + [self.c0, self.c1, self.c2, self.c3] + } +} + +impl Mul for Transform4F { + type Output = Transform4F; + + // https://stackoverflow.com/a/18508113 + #[inline] + fn mul(self, other: Transform4F) -> Transform4F { + return Transform4F { + c0: mul_col(&self, other.c0), + c1: mul_col(&self, other.c1), + c2: mul_col(&self, other.c2), + c3: mul_col(&self, other.c3), + }; + + #[inline] + fn mul_col(a: &Transform4F, b_col: F32x4) -> F32x4 { + a.c0 * b_col.xxxx() + a.c1 * b_col.yyyy() + a.c2 * b_col.zzzz() + a.c3 * b_col.wwww() + } + } +} + +impl Mul for Transform4F { + type Output = Vector4F; + + #[inline] + fn mul(self, vector: Vector4F) -> Vector4F { + let term0 = self.c0 * F32x4::splat(vector.x()); + let term1 = self.c1 * F32x4::splat(vector.y()); + let term2 = self.c2 * F32x4::splat(vector.z()); + let term3 = self.c3 * F32x4::splat(vector.w()); + Vector4F(term0 + term1 + term2 + term3) + } +} + +impl MulAssign for Transform4F { + fn mul_assign(&mut self, other: Transform4F) { + *self = *self * other + } } impl Add for Matrix2x2F { @@ -326,108 +358,110 @@ impl Neg for Matrix2x2F { #[derive(Clone, Copy, Debug)] pub struct Perspective { - pub transform: Transform3DF, + pub transform: Transform4F, pub window_size: Vector2I, } impl Perspective { #[inline] - pub fn new(transform: &Transform3DF, window_size: Vector2I) -> Perspective { + pub fn new(transform: &Transform4F, window_size: Vector2I) -> Perspective { Perspective { transform: *transform, window_size, } } +} +impl Mul for Perspective { + type Output = Perspective; #[inline] - pub fn transform_point_2d(&self, point: &Vector2F) -> Vector2F { - let point = self - .transform - .transform_point(point.to_3d()) - .perspective_divide() - .to_2d() - * Vector2F::new(1.0, -1.0); + fn mul(self, other: Transform4F) -> Perspective { + Perspective { + transform: self.transform * other, + window_size: self.window_size, + } + } +} + +impl Mul for Perspective { + type Output = Vector2F; + #[inline] + fn mul(self, vector: Vector2F) -> Vector2F { + let point = (self.transform * vector.to_3d()).perspective_divide().to_2d() * + Vector2F::new(1.0, -1.0); (point + Vector2F::splat(1.0)) * self.window_size.to_f32().scale(0.5) } +} - // TODO(pcwalton): SIMD? +impl Mul for Perspective { + type Output = RectF; #[inline] - pub fn transform_rect(&self, rect: RectF) -> RectF { - let upper_left = self.transform_point_2d(&rect.origin()); - let upper_right = self.transform_point_2d(&rect.upper_right()); - let lower_left = self.transform_point_2d(&rect.lower_left()); - let lower_right = self.transform_point_2d(&rect.lower_right()); + fn mul(self, rect: RectF) -> RectF { + let (upper_left, upper_right) = (self * rect.origin(), self * rect.upper_right()); + let (lower_left, lower_right) = (self * rect.lower_left(), self * rect.lower_right()); let min_point = upper_left.min(upper_right).min(lower_left).min(lower_right); let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right); RectF::from_points(min_point, max_point) } - - #[inline] - pub fn post_mul(&self, other: &Transform3DF) -> Perspective { - Perspective { - transform: self.transform.post_mul(other), - window_size: self.window_size, - } - } } #[cfg(test)] mod test { use crate::vector::Vector4F; - use crate::transform3d::Transform3DF; + use crate::transform3d::Transform4F; #[test] fn test_post_mul() { - let a = Transform3DF::row_major( + let a = Transform4F::row_major( 3.0, 1.0, 4.0, 5.0, 9.0, 2.0, 6.0, 5.0, 3.0, 5.0, 8.0, 9.0, 7.0, 9.0, 3.0, 2.0, ); - let b = Transform3DF::row_major( + let b = Transform4F::row_major( 3.0, 8.0, 4.0, 6.0, 2.0, 6.0, 4.0, 3.0, 3.0, 8.0, 3.0, 2.0, 7.0, 9.0, 5.0, 0.0, ); - let c = Transform3DF::row_major( + let c = Transform4F::row_major( 58.0, 107.0, 53.0, 29.0, 84.0, 177.0, 87.0, 72.0, 106.0, 199.0, 101.0, 49.0, 62.0, 152.0, 83.0, 75.0, ); - assert_eq!(a.post_mul(&b), c); + assert_eq!(a * b, c); } #[test] fn test_pre_mul() { - let a = Transform3DF::row_major( + let a = Transform4F::row_major( 3.0, 1.0, 4.0, 5.0, 9.0, 2.0, 6.0, 5.0, 3.0, 5.0, 8.0, 9.0, 7.0, 9.0, 3.0, 2.0, ); - let b = Transform3DF::row_major( + let b = Transform4F::row_major( 3.0, 8.0, 4.0, 6.0, 2.0, 6.0, 4.0, 3.0, 3.0, 8.0, 3.0, 2.0, 7.0, 9.0, 5.0, 0.0, ); - let c = Transform3DF::row_major( + let c = Transform4F::row_major( 135.0, 93.0, 110.0, 103.0, 93.0, 61.0, 85.0, 82.0, 104.0, 52.0, 90.0, 86.0, 117.0, 50.0, 122.0, 125.0, ); - assert_eq!(a.pre_mul(&b), c); + assert_eq!(b * a, c); } #[test] fn test_transform_point() { - let a = Transform3DF::row_major( + let a = Transform4F::row_major( 3.0, 1.0, 4.0, 5.0, 9.0, 2.0, 6.0, 5.0, 3.0, 5.0, 8.0, 9.0, 7.0, 9.0, 3.0, 2.0, ); let p = Vector4F::new(3.0, 8.0, 4.0, 6.0); let q = Vector4F::new(63.0, 97.0, 135.0, 117.0); - assert_eq!(a.transform_point(p), q); + assert_eq!(a * p, q); } #[test] fn test_inverse() { // Random matrix. - let m = Transform3DF::row_major( + let m = Transform4F::row_major( 0.86277982, 0.15986552, 0.90739898, 0.60066808, 0.17386167, 0.016353, 0.8535783, 0.12969608, 0.0946466, 0.43248631, 0.63480505, 0.08154603, 0.50305436, 0.48359687, 0.51057162, 0.24812012, ); let p0 = Vector4F::new(0.95536648, 0.80633691, 0.16357357, 0.5477598); - let p1 = m.transform_point(p0); + let p1 = m * p0; let m_inv = m.inverse(); - let m_inv_exp = Transform3DF::row_major( + let m_inv_exp = Transform4F::row_major( -2.47290136, 3.48865688, -6.12298336, @@ -446,7 +480,7 @@ mod test { -9.10374060, ); assert!(m_inv.approx_eq(&m_inv_exp, 0.0001)); - let p2 = m_inv.transform_point(p1); - assert!(p0.approx_eq(&p2, 0.0001)); + let p2 = m_inv * p1; + assert!(p0.approx_eq(p2, 0.0001)); } } diff --git a/geometry/src/unit_vector.rs b/geometry/src/unit_vector.rs index b46431b0..e06202dd 100644 --- a/geometry/src/unit_vector.rs +++ b/geometry/src/unit_vector.rs @@ -11,7 +11,7 @@ //! A utility module that allows unit vectors to be treated like angles. use crate::vector::Vector2F; -use pathfinder_simd::default::F32x4; +use pathfinder_simd::default::F32x2; #[derive(Clone, Copy, Debug)] pub struct UnitVector(pub Vector2F); @@ -25,14 +25,14 @@ impl UnitVector { /// Angle addition formula. #[inline] pub fn rotate_by(&self, other: UnitVector) -> UnitVector { - let products = (self.0).0.xyyx() * (other.0).0.xyxy(); + let products = (self.0).0.to_f32x4().xyyx() * (other.0).0.to_f32x4().xyxy(); UnitVector(Vector2F::new(products[0] - products[1], products[2] + products[3])) } /// Angle subtraction formula. #[inline] pub fn rev_rotate_by(&self, other: UnitVector) -> UnitVector { - let products = (self.0).0.xyyx() * (other.0).0.xyxy(); + let products = (self.0).0.to_f32x4().xyyx() * (other.0).0.to_f32x4().xyxy(); UnitVector(Vector2F::new(products[0] + products[1], products[2] - products[3])) } @@ -40,7 +40,7 @@ impl UnitVector { #[inline] pub fn halve_angle(&self) -> UnitVector { let x = self.0.x(); - let term = F32x4::new(x, -x, 0.0, 0.0); - UnitVector(Vector2F((F32x4::splat(0.5) * (F32x4::splat(1.0) + term)).sqrt())) + let term = F32x2::new(x, -x); + UnitVector(Vector2F((F32x2::splat(0.5) * (F32x2::splat(1.0) + term)).sqrt())) } } diff --git a/geometry/src/vector.rs b/geometry/src/vector.rs index 0d6e4941..768b6e58 100644 --- a/geometry/src/vector.rs +++ b/geometry/src/vector.rs @@ -10,36 +10,36 @@ //! A SIMD-optimized point type. -use pathfinder_simd::default::{F32x4, I32x4}; +use pathfinder_simd::default::{F32x2, F32x4, I32x2}; use std::ops::{Add, AddAssign, Mul, Neg, Sub}; /// 2D points with 32-bit floating point coordinates. #[derive(Clone, Copy, Debug, Default)] -pub struct Vector2F(pub F32x4); +pub struct Vector2F(pub F32x2); impl Vector2F { #[inline] pub fn new(x: f32, y: f32) -> Vector2F { - Vector2F(F32x4::new(x, y, 0.0, 0.0)) + Vector2F(F32x2::new(x, y)) } #[inline] pub fn splat(value: f32) -> Vector2F { - Vector2F(F32x4::splat(value)) + Vector2F(F32x2::splat(value)) } #[inline] pub fn to_3d(self) -> Vector4F { - Vector4F(self.0.concat_xy_xy(F32x4::new(0.0, 1.0, 0.0, 0.0))) + Vector4F(self.0.to_f32x4().concat_xy_zw(F32x4::new(0.0, 0.0, 0.0, 1.0))) } #[inline] - pub fn x(&self) -> f32 { + pub fn x(self) -> f32 { self.0[0] } #[inline] - pub fn y(&self) -> f32 { + pub fn y(self) -> f32 { self.0[1] } @@ -54,97 +54,96 @@ impl Vector2F { } #[inline] - pub fn min(&self, other: Vector2F) -> Vector2F { + pub fn min(self, other: Vector2F) -> Vector2F { Vector2F(self.0.min(other.0)) } #[inline] - pub fn max(&self, other: Vector2F) -> Vector2F { + pub fn max(self, other: Vector2F) -> Vector2F { Vector2F(self.0.max(other.0)) } #[inline] - pub fn clamp(&self, min_val: Vector2F, max_val: Vector2F) -> Vector2F { + pub fn clamp(self, min_val: Vector2F, max_val: Vector2F) -> Vector2F { self.max(min_val).min(max_val) } #[inline] - pub fn det(&self, other: Vector2F) -> f32 { + pub fn det(self, other: Vector2F) -> f32 { self.x() * other.y() - self.y() * other.x() } #[inline] - pub fn dot(&self, other: Vector2F) -> f32 { + pub fn dot(self, other: Vector2F) -> f32 { let xy = self.0 * other.0; xy.x() + xy.y() } #[inline] - pub fn scale(&self, x: f32) -> Vector2F { - Vector2F(self.0 * F32x4::splat(x)) + pub fn scale(self, x: f32) -> Vector2F { + Vector2F(self.0 * F32x2::splat(x)) } #[inline] - pub fn scale_xy(&self, factors: Vector2F) -> Vector2F { + pub fn scale_xy(self, factors: Vector2F) -> Vector2F { Vector2F(self.0 * factors.0) } #[inline] - pub fn floor(&self) -> Vector2F { + pub fn floor(self) -> Vector2F { Vector2F(self.0.floor()) } #[inline] - pub fn ceil(&self) -> Vector2F { + pub fn ceil(self) -> Vector2F { Vector2F(self.0.ceil()) } /// Treats this point as a vector and calculates its squared length. #[inline] - pub fn square_length(&self) -> f32 { + pub fn square_length(self) -> f32 { let squared = self.0 * self.0; squared[0] + squared[1] } /// Treats this point as a vector and calculates its length. #[inline] - pub fn length(&self) -> f32 { + pub fn length(self) -> f32 { f32::sqrt(self.square_length()) } /// Treats this point as a vector and normalizes it. #[inline] - pub fn normalize(&self) -> Vector2F { + pub fn normalize(self) -> Vector2F { self.scale(1.0 / self.length()) } /// Swaps y and x. #[inline] - pub fn yx(&self) -> Vector2F { - Vector2F(self.0.yxwz()) + pub fn yx(self) -> Vector2F { + Vector2F(self.0.yx()) } #[inline] - pub fn is_zero(&self) -> bool { - *self == Vector2F::default() + pub fn is_zero(self) -> bool { + self == Vector2F::default() } #[inline] - pub fn lerp(&self, other: Vector2F, t: f32) -> Vector2F { - *self + (other - *self).scale(t) + pub fn lerp(self, other: Vector2F, t: f32) -> Vector2F { + self + (other - self).scale(t) } #[inline] - pub fn to_i32(&self) -> Vector2I { - Vector2I(self.0.to_i32x4()) + pub fn to_i32(self) -> Vector2I { + Vector2I(self.0.to_i32x2()) } } impl PartialEq for Vector2F { #[inline] fn eq(&self, other: &Vector2F) -> bool { - let results = self.0.packed_eq(other.0); - results[0] != 0 && results[1] != 0 + self.0.packed_eq(other.0).is_all_ones() } } @@ -182,26 +181,26 @@ impl Neg for Vector2F { /// 2D points with 32-bit signed integer coordinates. #[derive(Clone, Copy, Debug, Default)] -pub struct Vector2I(pub I32x4); +pub struct Vector2I(pub I32x2); impl Vector2I { #[inline] pub fn new(x: i32, y: i32) -> Vector2I { - Vector2I(I32x4::new(x, y, 0, 0)) + Vector2I(I32x2::new(x, y)) } #[inline] pub fn splat(value: i32) -> Vector2I { - Vector2I(I32x4::splat(value)) + Vector2I(I32x2::splat(value)) } #[inline] - pub fn x(&self) -> i32 { + pub fn x(self) -> i32 { self.0[0] } #[inline] - pub fn y(&self) -> i32 { + pub fn y(self) -> i32 { self.0[1] } @@ -216,18 +215,18 @@ impl Vector2I { } #[inline] - pub fn scale(&self, factor: i32) -> Vector2I { - Vector2I(self.0 * I32x4::splat(factor)) + pub fn scale(self, factor: i32) -> Vector2I { + Vector2I(self.0 * I32x2::splat(factor)) } #[inline] - pub fn scale_xy(&self, factors: Vector2I) -> Vector2I { + pub fn scale_xy(self, factors: Vector2I) -> Vector2I { Vector2I(self.0 * factors.0) } #[inline] - pub fn to_f32(&self) -> Vector2F { - Vector2F(self.0.to_f32x4()) + pub fn to_f32(self) -> Vector2F { + Vector2F(self.0.to_f32x2()) } } @@ -257,8 +256,7 @@ impl Sub for Vector2I { impl PartialEq for Vector2I { #[inline] fn eq(&self, other: &Vector2I) -> bool { - let results = self.0.packed_eq(other.0); - results[0] != 0 && results[1] != 0 + self.0.packed_eq(other.0).is_all_ones() } } @@ -279,7 +277,7 @@ impl Vector4F { #[inline] pub fn to_2d(self) -> Vector2F { - Vector2F(self.0) + Vector2F(self.0.xy()) } #[inline] @@ -303,7 +301,7 @@ impl Vector4F { } #[inline] - pub fn scale(&self, x: f32) -> Vector4F { + pub fn scale(self, x: f32) -> Vector4F { let mut factors = F32x4::splat(x); factors[3] = 1.0; Vector4F(self.0 * factors) @@ -335,7 +333,7 @@ impl Vector4F { } #[inline] - pub fn approx_eq(&self, other: &Vector4F, epsilon: f32) -> bool { + pub fn approx_eq(self, other: Vector4F, epsilon: f32) -> bool { self.0.approx_eq(other.0, epsilon) } @@ -377,6 +375,16 @@ impl Mul for Vector4F { } } +impl Neg for Vector4F { + type Output = Vector4F; + /// NB: This does not negate w, because that is rarely what you what for homogeneous + /// coordinates. + #[inline] + fn neg(self) -> Vector4F { + Vector4F(self.0 * F32x4::new(-1.0, -1.0, -1.0, 1.0)) + } +} + impl Default for Vector4F { #[inline] fn default() -> Vector4F { diff --git a/gpu/src/lib.rs b/gpu/src/lib.rs index 70dcd132..1ef7ae76 100644 --- a/gpu/src/lib.rs +++ b/gpu/src/lib.rs @@ -14,9 +14,9 @@ use crate::resources::ResourceLoader; use image::ImageFormat; use pathfinder_content::color::ColorF; use pathfinder_geometry::rect::RectI; -use pathfinder_geometry::transform3d::Transform3DF; +use pathfinder_geometry::transform3d::Transform4F; use pathfinder_geometry::vector::Vector2I; -use pathfinder_simd::default::F32x4; +use pathfinder_simd::default::{F32x2, F32x4}; use std::time::Duration; pub mod resources; @@ -153,7 +153,7 @@ pub enum ShaderKind { pub enum UniformData { Int(i32), Mat4([F32x4; 4]), - Vec2(F32x4), + Vec2(F32x2), Vec4(F32x4), TextureUnit(u32), } @@ -286,7 +286,7 @@ pub enum TextureData { impl UniformData { #[inline] - pub fn from_transform_3d(transform: &Transform3DF) -> UniformData { + pub fn from_transform_3d(transform: &Transform4F) -> UniformData { UniformData::Mat4([transform.c0, transform.c1, transform.c2, transform.c3]) } } diff --git a/metal/src/lib.rs b/metal/src/lib.rs index 41a11c80..1535acd1 100644 --- a/metal/src/lib.rs +++ b/metal/src/lib.rs @@ -47,7 +47,7 @@ use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, Dep use pathfinder_gpu::{Primitive, RenderState, RenderTarget, ShaderKind, StencilFunc, TextureData}; use pathfinder_gpu::{TextureFormat, UniformData, VertexAttrClass}; use pathfinder_gpu::{VertexAttrDescriptor, VertexAttrType}; -use pathfinder_simd::default::F32x4; +use pathfinder_simd::default::{F32x2, F32x4}; use std::cell::{Cell, RefCell}; use std::mem; use std::ptr; @@ -1146,7 +1146,7 @@ impl UniformDataExt for UniformData { Some(slice::from_raw_parts(&data[0] as *const F32x4 as *const u8, 4 * 16)) } UniformData::Vec2(ref data) => { - Some(slice::from_raw_parts(data as *const F32x4 as *const u8, 4 * 2)) + Some(slice::from_raw_parts(data as *const F32x2 as *const u8, 4 * 2)) } UniformData::Vec4(ref data) => { Some(slice::from_raw_parts(data as *const F32x4 as *const u8, 4 * 4)) diff --git a/pdf/src/lib.rs b/pdf/src/lib.rs deleted file mode 100644 index fcab6a75..00000000 --- a/pdf/src/lib.rs +++ /dev/null @@ -1,73 +0,0 @@ -use pathfinder_renderer::{scene::Scene}; -use pathfinder_geometry::{vector::Vector2F, rect::RectF}; -use pathfinder_content::{outline::Outline, segment::{Segment, SegmentKind}, color::ColorF}; -use std::io::Write; - -mod pdf; -use pdf::Pdf; - -pub struct PdfBuilder { - pdf: Pdf -} - -impl PdfBuilder { - pub fn new() -> PdfBuilder { - PdfBuilder { - pdf: Pdf::new() - } - } - - pub fn add_scene(&mut self, scene: &Scene) { - let view_box = scene.view_box(); - self.pdf.add_page(view_box.size()); - - let height = view_box.size().y(); - let tr = |v: Vector2F| -> Vector2F { - let r = v - view_box.origin(); - Vector2F::new(r.x(), height - r.y()) - }; - - for (paint, outline) in scene.paths() { - self.pdf.set_fill_color(paint.color); - - for contour in outline.contours() { - for (segment_index, segment) in contour.iter().enumerate() { - if segment_index == 0 { - self.pdf.move_to(tr(segment.baseline.from())); - } - - match segment.kind { - SegmentKind::None => {} - SegmentKind::Line => self.pdf.line_to(tr(segment.baseline.to())), - SegmentKind::Quadratic => { - let current = segment.baseline.from(); - let c = segment.ctrl.from(); - let p = segment.baseline.to(); - let c1 = Vector2F::splat(2./3.) * c + Vector2F::splat(1./3.) * current; - let c2 = Vector2F::splat(2./3.) * c + Vector2F::splat(1./3.) * p; - self.pdf.cubic_to(c1, c2, p); - } - SegmentKind::Cubic => self.pdf.cubic_to(tr(segment.ctrl.from()), tr(segment.ctrl.to()), tr(segment.baseline.to())) - } - } - - if contour.is_closed() { - self.pdf.close(); - } - } - - // closes implicitly - self.pdf.fill(); - } - } - - pub fn write(mut self, out: W) { - self.pdf.write_to(out); - } -} - -pub fn make_pdf(mut writer: W, scene: &Scene) { - let mut pdf = PdfBuilder::new(); - pdf.add_scene(scene); - pdf.write(writer); -} diff --git a/renderer/src/builder.rs b/renderer/src/builder.rs index b3d4ca1c..9e53c492 100644 --- a/renderer/src/builder.rs +++ b/renderer/src/builder.rs @@ -160,7 +160,7 @@ impl BuiltObject { fn add_fill( &mut self, builder: &SceneBuilder, - segment: &LineSegment2F, + segment: LineSegment2F, tile_coords: Vector2I, ) { debug!("add_fill({:?} ({:?}))", segment, tile_coords); @@ -171,31 +171,19 @@ impl BuiltObject { }; debug_assert_eq!(TILE_WIDTH, TILE_HEIGHT); + + // Compute the upper left corner of the tile. let tile_size = F32x4::splat(TILE_WIDTH as f32); - let (min, max) = ( - F32x4::default(), - F32x4::splat((TILE_WIDTH * 256 - 1) as f32), - ); - let shuffle_mask = I32x4::new(0x0c08_0400, 0x0d05_0901, 0, 0).as_u8x16(); - - let tile_upper_left = tile_coords.to_f32().0.xyxy() * tile_size; + let tile_upper_left = tile_coords.to_f32().0.to_f32x4().xyxy() * tile_size; + // Convert to 4.8 fixed point. let segment = (segment.0 - tile_upper_left) * F32x4::splat(256.0); - let segment = segment - .clamp(min, max) - .to_i32x4() - .as_u8x16() - .shuffle(shuffle_mask) - .as_i32x4(); - - // Unpack whole and fractional pixels. - let px = LineSegmentU4((segment[1] | (segment[1] >> 12)) as u16); - let subpx = LineSegmentU8(segment[0] as u32); + let (min, max) = (F32x4::default(), F32x4::splat((TILE_WIDTH * 256 - 1) as f32)); + let segment = segment.clamp(min, max).to_i32x4(); + let (from_x, from_y, to_x, to_y) = (segment[0], segment[1], segment[2], segment[3]); // Cull degenerate fills. - if (px.0 & 0xf) as u8 == ((px.0 >> 8) & 0xf) as u8 - && (subpx.0 & 0xff) as u8 == ((subpx.0 >> 16) & 0xff) as u8 - { + if from_x == to_x { debug!("... culling!"); return; } @@ -203,10 +191,20 @@ impl BuiltObject { // Allocate global tile if necessary. let alpha_tile_index = self.get_or_allocate_alpha_tile_index(builder, tile_coords); + // Pack whole pixels. + let mut px = (segment & I32x4::splat(0xf00)) >> I32x4::new(8, 4, 8, 4); + px = px | px.yxwz(); + + // Pack instance data. debug!("... OK, pushing"); self.fills.push(FillBatchPrimitive { - px, - subpx, + px: LineSegmentU4 { from: px[0] as u8, to: px[2] as u8 }, + subpx: LineSegmentU8 { + from_x: from_x as u8, + from_y: from_y as u8, + to_x: to_x as u8, + to_y: to_y as u8, + }, alpha_tile_index, }); } @@ -256,7 +254,7 @@ impl BuiltObject { ); while winding != 0 { - self.add_fill(builder, &segment, tile_coords); + self.add_fill(builder, segment, tile_coords); if winding < 0 { winding += 1 } else { @@ -315,7 +313,7 @@ impl BuiltObject { let fill_segment = LineSegment2F::new(fill_from, fill_to); let fill_tile_coords = Vector2I::new(subsegment_tile_x, tile_y); - self.add_fill(builder, &fill_segment, fill_tile_coords); + self.add_fill(builder, fill_segment, fill_tile_coords); } } diff --git a/renderer/src/concurrent/scene_proxy.rs b/renderer/src/concurrent/scene_proxy.rs index 22e29155..075e25ce 100644 --- a/renderer/src/concurrent/scene_proxy.rs +++ b/renderer/src/concurrent/scene_proxy.rs @@ -90,9 +90,10 @@ impl SceneProxy { renderer.end_scene(); } - pub fn as_svg(&self) -> Vec { + #[inline] + pub fn copy_scene(&self) -> Scene { let (sender, receiver) = mpsc::channel(); - self.sender.send(MainToWorkerMsg::GetSVG(sender)).unwrap(); + self.sender.send(MainToWorkerMsg::CopyScene(sender)).unwrap(); receiver.recv().unwrap() } } @@ -104,22 +105,18 @@ fn scene_thread(mut scene: Scene, while let Ok(msg) = main_to_worker_receiver.recv() { match msg { MainToWorkerMsg::ReplaceScene(new_scene) => scene = new_scene, + MainToWorkerMsg::CopyScene(sender) => sender.send(scene.clone()).unwrap(), MainToWorkerMsg::SetViewBox(new_view_box) => scene.set_view_box(new_view_box), - MainToWorkerMsg::Build(options, listener) => scene.build(options, listener, &executor), - MainToWorkerMsg::GetSVG(sender) => { - let mut bytes = vec![]; - scene.write_svg(&mut bytes).unwrap(); - sender.send(bytes).unwrap(); - } + MainToWorkerMsg::Build(options, listener) => scene.build(options, listener, &executor) } } } enum MainToWorkerMsg { ReplaceScene(Scene), + CopyScene(Sender), SetViewBox(RectF), Build(BuildOptions, Box), - GetSVG(Sender>), } pub struct RenderCommandStream { diff --git a/renderer/src/gpu/renderer.rs b/renderer/src/gpu/renderer.rs index f70f56d7..26bbc513 100644 --- a/renderer/src/gpu/renderer.rs +++ b/renderer/src/gpu/renderer.rs @@ -16,14 +16,14 @@ use crate::post::DefringingKernel; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use pathfinder_geometry::vector::{Vector2I, Vector4F}; use pathfinder_geometry::rect::RectI; -use pathfinder_geometry::transform3d::Transform3DF; +use pathfinder_geometry::transform3d::Transform4F; use pathfinder_content::color::ColorF; use pathfinder_gpu::resources::ResourceLoader; use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, ClearOps}; use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderOptions, RenderState}; use pathfinder_gpu::{RenderTarget, StencilFunc, StencilState, TextureFormat, UniformData}; use pathfinder_gpu::{VertexAttrClass, VertexAttrDescriptor, VertexAttrType}; -use pathfinder_simd::default::{F32x4, I32x4}; +use pathfinder_simd::default::{F32x2, F32x4}; use std::cmp; use std::collections::VecDeque; use std::mem; @@ -447,15 +447,10 @@ where textures: &[&self.area_lut_texture], uniforms: &[ (&self.fill_program.framebuffer_size_uniform, - UniformData::Vec2(I32x4::new(MASK_FRAMEBUFFER_WIDTH, - MASK_FRAMEBUFFER_HEIGHT, - 0, - 0).to_f32x4())), + UniformData::Vec2(F32x2::new(MASK_FRAMEBUFFER_WIDTH as f32, + MASK_FRAMEBUFFER_HEIGHT as f32))), (&self.fill_program.tile_size_uniform, - UniformData::Vec2(I32x4::new(TILE_WIDTH as i32, - TILE_HEIGHT as i32, - 0, - 0).to_f32x4())), + UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))), (&self.fill_program.area_lut_uniform, UniformData::TextureUnit(0)), ], viewport: self.mask_viewport(), @@ -473,30 +468,28 @@ where self.buffered_fills.clear(); } + fn tile_transform(&self) -> Transform4F { + let draw_viewport = self.draw_viewport().size().to_f32(); + let scale = Vector4F::new(2.0 / draw_viewport.x(), -2.0 / draw_viewport.y(), 1.0, 1.0); + Transform4F::from_scale(scale).translate(Vector4F::new(-1.0, 1.0, 0.0, 1.0)) + } + fn draw_alpha_tiles(&mut self, count: u32) { let clear_color = self.clear_color_for_draw_operation(); let alpha_tile_vertex_array = self.alpha_tile_vertex_array(); let alpha_tile_program = self.alpha_tile_program(); - let draw_viewport = self.draw_viewport(); let mut textures = vec![self.device.framebuffer_texture(&self.mask_framebuffer)]; let mut uniforms = vec![ - (&alpha_tile_program.framebuffer_size_uniform, - UniformData::Vec2(draw_viewport.size().to_f32().0)), + (&alpha_tile_program.transform_uniform, + UniformData::Mat4(self.tile_transform().to_columns())), (&alpha_tile_program.tile_size_uniform, - UniformData::Vec2(I32x4::new(TILE_WIDTH as i32, - TILE_HEIGHT as i32, - 0, - 0).to_f32x4())), + UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))), (&alpha_tile_program.stencil_texture_uniform, UniformData::TextureUnit(0)), (&alpha_tile_program.stencil_texture_size_uniform, - UniformData::Vec2(I32x4::new(MASK_FRAMEBUFFER_WIDTH, - MASK_FRAMEBUFFER_HEIGHT, - 0, - 0).to_f32x4())), - // FIXME(pcwalton): Fill this in properly! - (&alpha_tile_program.view_box_origin_uniform, UniformData::Vec2(F32x4::default())), + UniformData::Vec2(F32x2::new(MASK_FRAMEBUFFER_WIDTH as f32, + MASK_FRAMEBUFFER_HEIGHT as f32))), ]; match self.render_mode { @@ -509,7 +502,7 @@ where UniformData::Vec2(self.device .texture_size(paint_texture) .0 - .to_f32x4()))); + .to_f32x2()))); } RenderMode::Monochrome { .. } if self.postprocessing_needed() => { uniforms.push((&self.alpha_monochrome_tile_program.color_uniform, @@ -528,7 +521,7 @@ where primitive: Primitive::Triangles, textures: &textures, uniforms: &uniforms, - viewport: draw_viewport, + viewport: self.draw_viewport(), options: RenderOptions { blend: BlendState::RGBSrcAlphaAlphaOneMinusSrcAlpha, stencil: self.stencil_state(), @@ -546,18 +539,12 @@ where let solid_tile_vertex_array = self.solid_tile_vertex_array(); let solid_tile_program = self.solid_tile_program(); - let draw_viewport = self.draw_viewport(); let mut textures = vec![]; let mut uniforms = vec![ - (&solid_tile_program.framebuffer_size_uniform, - UniformData::Vec2(draw_viewport.size().0.to_f32x4())), + (&solid_tile_program.transform_uniform, + UniformData::Mat4(self.tile_transform().to_columns())), (&solid_tile_program.tile_size_uniform, - UniformData::Vec2(I32x4::new(TILE_WIDTH as i32, - TILE_HEIGHT as i32, - 0, - 0).to_f32x4())), - // FIXME(pcwalton): Fill this in properly! - (&solid_tile_program.view_box_origin_uniform, UniformData::Vec2(F32x4::default())), + UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))), ]; match self.render_mode { @@ -570,7 +557,7 @@ where UniformData::Vec2(self.device .texture_size(paint_texture) .0 - .to_f32x4()))); + .to_f32x2()))); } RenderMode::Monochrome { .. } if self.postprocessing_needed() => { uniforms.push((&self.solid_monochrome_tile_program.color_uniform, @@ -589,7 +576,7 @@ where primitive: Primitive::Triangles, textures: &textures, uniforms: &uniforms, - viewport: draw_viewport, + viewport: self.draw_viewport(), options: RenderOptions { stencil: self.stencil_state(), clear_ops: ClearOps { color: clear_color, ..ClearOps::default() }, @@ -635,7 +622,7 @@ where UniformData::Vec2(main_viewport.size().to_f32().0)), (&self.postprocess_program.source_uniform, UniformData::TextureUnit(0)), (&self.postprocess_program.source_size_uniform, - UniformData::Vec2(source_texture_size.0.to_f32x4())), + UniformData::Vec2(source_texture_size.0.to_f32x2())), (&self.postprocess_program.gamma_lut_uniform, UniformData::TextureUnit(1)), (&self.postprocess_program.fg_color_uniform, UniformData::Vec4(fg_color.0)), (&self.postprocess_program.bg_color_uniform, UniformData::Vec4(bg_color.0)), @@ -747,8 +734,8 @@ where pub fn reproject_texture( &mut self, texture: &D::Texture, - old_transform: &Transform3DF, - new_transform: &Transform3DF, + old_transform: &Transform4F, + new_transform: &Transform4F, ) { let clear_color = self.clear_color_for_draw_operation(); @@ -1217,9 +1204,8 @@ where D: Device, { program: D::Program, - framebuffer_size_uniform: D::Uniform, + transform_uniform: D::Uniform, tile_size_uniform: D::Uniform, - view_box_origin_uniform: D::Uniform, } impl SolidTileProgram @@ -1233,15 +1219,9 @@ where program_name, "tile_solid", ); - let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); + let transform_uniform = device.get_uniform(&program, "Transform"); let tile_size_uniform = device.get_uniform(&program, "TileSize"); - let view_box_origin_uniform = device.get_uniform(&program, "ViewBoxOrigin"); - SolidTileProgram { - program, - framebuffer_size_uniform, - tile_size_uniform, - view_box_origin_uniform, - } + SolidTileProgram { program, transform_uniform, tile_size_uniform } } } @@ -1299,11 +1279,10 @@ where D: Device, { program: D::Program, - framebuffer_size_uniform: D::Uniform, + transform_uniform: D::Uniform, tile_size_uniform: D::Uniform, stencil_texture_uniform: D::Uniform, stencil_texture_size_uniform: D::Uniform, - view_box_origin_uniform: D::Uniform, } impl AlphaTileProgram @@ -1317,18 +1296,16 @@ where program_name, "tile_alpha", ); - let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); + let transform_uniform = device.get_uniform(&program, "Transform"); let tile_size_uniform = device.get_uniform(&program, "TileSize"); let stencil_texture_uniform = device.get_uniform(&program, "StencilTexture"); let stencil_texture_size_uniform = device.get_uniform(&program, "StencilTextureSize"); - let view_box_origin_uniform = device.get_uniform(&program, "ViewBoxOrigin"); AlphaTileProgram { program, - framebuffer_size_uniform, + transform_uniform, tile_size_uniform, stencil_texture_uniform, stencil_texture_size_uniform, - view_box_origin_uniform, } } } diff --git a/renderer/src/options.rs b/renderer/src/options.rs index 0266ec78..170d2cb7 100644 --- a/renderer/src/options.rs +++ b/renderer/src/options.rs @@ -12,7 +12,7 @@ use crate::gpu_data::RenderCommand; use pathfinder_geometry::rect::RectF; -use pathfinder_geometry::transform2d::Transform2DF; +use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform3d::Perspective; use pathfinder_geometry::vector::{Vector2F, Vector4F}; use pathfinder_content::clip::PolygonClipper3D; @@ -51,14 +51,14 @@ impl BuildOptions { #[derive(Clone)] pub enum RenderTransform { - Transform2D(Transform2DF), + Transform2D(Transform2F), Perspective(Perspective), } impl Default for RenderTransform { #[inline] fn default() -> RenderTransform { - RenderTransform::Transform2D(Transform2DF::default()) + RenderTransform::Transform2D(Transform2F::default()) } } @@ -83,7 +83,7 @@ impl RenderTransform { debug!("-----"); debug!("bounds={:?} ORIGINAL quad={:?}", bounds, points); for point in &mut points { - *point = perspective.transform.transform_point(*point); + *point = perspective.transform * *point; } debug!("... PERSPECTIVE quad={:?}", points); @@ -105,12 +105,7 @@ impl RenderTransform { let inverse_transform = perspective.transform.inverse(); let clip_polygon = points .into_iter() - .map(|point| { - inverse_transform - .transform_point(point) - .perspective_divide() - .to_2d() - }) + .map(|point| (inverse_transform * point).perspective_divide().to_2d()) .collect(); return PreparedRenderTransform::Perspective { perspective, @@ -140,7 +135,7 @@ pub(crate) type BoundingQuad = [Vector4F; 4]; pub(crate) enum PreparedRenderTransform { None, - Transform2D(Transform2DF), + Transform2D(Transform2F), Perspective { perspective: Perspective, clip_polygon: Vec, diff --git a/renderer/src/scene.rs b/renderer/src/scene.rs index f441d02f..6d2d9d36 100644 --- a/renderer/src/scene.rs +++ b/renderer/src/scene.rs @@ -18,10 +18,9 @@ use crate::paint::{Paint, PaintId}; use hashbrown::HashMap; use pathfinder_geometry::vector::Vector2F; use pathfinder_geometry::rect::RectF; -use pathfinder_geometry::transform2d::Transform2DF; +use pathfinder_geometry::transform2d::Transform2F; use pathfinder_content::color::ColorU; use pathfinder_content::outline::Outline; -use std::io::{self, Write}; #[derive(Clone)] pub struct Scene { @@ -116,12 +115,11 @@ impl Scene { if options.transform.is_2d() || options.subpixel_aa_enabled { let mut transform = match options.transform { PreparedRenderTransform::Transform2D(transform) => transform, - PreparedRenderTransform::None => Transform2DF::default(), + PreparedRenderTransform::None => Transform2F::default(), PreparedRenderTransform::Perspective { .. } => unreachable!(), }; if options.subpixel_aa_enabled { - transform = transform - .post_mul(&Transform2DF::from_scale(Vector2F::new(3.0, 1.0))) + transform *= Transform2F::from_scale(Vector2F::new(3.0, 1.0)) } outline.transform(&transform); } @@ -173,31 +171,6 @@ impl Scene { let prepared_options = options.prepare(self.bounds); SceneBuilder::new(self, &prepared_options, listener).build(executor) } - - pub fn write_svg(&self, writer: &mut W) -> io::Result<()> where W: Write { - writeln!( - writer, - "", - self.view_box.origin().x(), - self.view_box.origin().y(), - self.view_box.size().x(), - self.view_box.size().y() - )?; - for path_object in &self.paths { - let paint = &self.paints[path_object.paint.0 as usize]; - write!(writer, " ", - paint.color, path_object.outline - )?; - } - writeln!(writer, "")?; - Ok(()) - } pub fn paths<'a>(&'a self) -> PathIter { PathIter { @@ -211,10 +184,14 @@ pub struct PathIter<'a> { pos: usize } impl<'a> Iterator for PathIter<'a> { - type Item = (&'a Paint, &'a Outline); + type Item = (&'a Paint, &'a Outline, &'a str); fn next(&mut self) -> Option { let item = self.scene.paths.get(self.pos).map(|path_object| { - (self.scene.paints.get(path_object.paint.0 as usize).unwrap(), &path_object.outline) + ( + self.scene.paints.get(path_object.paint.0 as usize).unwrap(), + &path_object.outline, + &*path_object.name + ) }); self.pos += 1; item diff --git a/renderer/src/tile_map.rs b/renderer/src/tile_map.rs index f0d5ac40..d0ca65ca 100644 --- a/renderer/src/tile_map.rs +++ b/renderer/src/tile_map.rs @@ -44,15 +44,11 @@ impl DenseTileMap { #[inline] pub fn coords_to_index(&self, coords: Vector2I) -> Option { - // TODO(pcwalton): SIMD? - if coords.x() < self.rect.min_x() - || coords.x() >= self.rect.max_x() - || coords.y() < self.rect.min_y() - || coords.y() >= self.rect.max_y() - { - return None; + if self.rect.contains_point(coords) { + Some(self.coords_to_index_unchecked(coords)) + } else { + None } - Some(self.coords_to_index_unchecked(coords)) } #[inline] diff --git a/renderer/src/tiles.rs b/renderer/src/tiles.rs index 0c98f0a5..60384f8f 100644 --- a/renderer/src/tiles.rs +++ b/renderer/src/tiles.rs @@ -413,14 +413,11 @@ impl ActiveEdge { } else { segment.baseline.to() }; - ActiveEdge::from_segment_and_crossing(segment, &crossing) + ActiveEdge::from_segment_and_crossing(segment, crossing) } - fn from_segment_and_crossing(segment: &Segment, crossing: &Vector2F) -> ActiveEdge { - ActiveEdge { - segment: *segment, - crossing: *crossing, - } + fn from_segment_and_crossing(segment: &Segment, crossing: Vector2F) -> ActiveEdge { + ActiveEdge { segment: *segment, crossing } } fn process(&mut self, builder: &SceneBuilder, built_object: &mut BuiltObject, tile_y: i32) { @@ -436,8 +433,8 @@ impl ActiveEdge { if segment.is_line() { let line_segment = segment.as_line_segment(); self.segment = - match self.process_line_segment(&line_segment, builder, built_object, tile_y) { - Some(lower_part) => Segment::line(&lower_part), + match self.process_line_segment(line_segment, builder, built_object, tile_y) { + Some(lower_part) => Segment::line(lower_part), None => Segment::none(), }; return; @@ -453,7 +450,7 @@ impl ActiveEdge { let first_line_segment = LineSegment2F::new(self.crossing, segment.baseline.upper_point()).orient(winding); if self - .process_line_segment(&first_line_segment, builder, built_object, tile_y) + .process_line_segment(first_line_segment, builder, built_object, tile_y) .is_some() { return; @@ -484,9 +481,9 @@ impl ActiveEdge { ); let line = before_segment.baseline.orient(winding); - match self.process_line_segment(&line, builder, built_object, tile_y) { - Some(ref lower_part) if split_t == 1.0 => { - self.segment = Segment::line(&lower_part); + match self.process_line_segment(line, builder, built_object, tile_y) { + Some(lower_part) if split_t == 1.0 => { + self.segment = Segment::line(lower_part); return; } None if split_t == 1.0 => { @@ -504,7 +501,7 @@ impl ActiveEdge { fn process_line_segment( &mut self, - line_segment: &LineSegment2F, + line_segment: LineSegment2F, builder: &SceneBuilder, built_object: &mut BuiltObject, tile_y: i32, @@ -516,7 +513,7 @@ impl ActiveEdge { ); if line_segment.max_y() <= tile_bottom { - built_object.generate_fill_primitives_for_line(builder, *line_segment, tile_y); + built_object.generate_fill_primitives_for_line(builder, line_segment, tile_y); return None; } diff --git a/resources/shaders/gl3/tile_alpha_monochrome.vs.glsl b/resources/shaders/gl3/tile_alpha_monochrome.vs.glsl index df40901e..f36ab4a7 100644 --- a/resources/shaders/gl3/tile_alpha_monochrome.vs.glsl +++ b/resources/shaders/gl3/tile_alpha_monochrome.vs.glsl @@ -27,10 +27,9 @@ precision highp float; -uniform vec2 uFramebufferSize; +uniform mat4 uTransform; uniform vec2 uTileSize; uniform vec2 uStencilTextureSize; -uniform vec2 uViewBoxOrigin; in uvec2 aTessCoord; in uvec3 aTileOrigin; @@ -51,15 +50,14 @@ vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth){ void computeVaryings(){ vec2 origin = vec2(aTileOrigin . xy)+ vec2(aTileOrigin . z & 15u, aTileOrigin . z >> 4u)* 256.0; - vec2 pixelPosition =(origin + vec2(aTessCoord))* uTileSize + uViewBoxOrigin; - vec2 position =(pixelPosition / uFramebufferSize * 2.0 - 1.0)* vec2(1.0, - 1.0); + vec2 position =(origin + vec2(aTessCoord))* uTileSize; vec2 maskTexCoordOrigin = computeTileOffset(uint(aTileIndex), uStencilTextureSize . x); vec2 maskTexCoord = maskTexCoordOrigin + aTessCoord * uTileSize; vTexCoord = maskTexCoord / uStencilTextureSize; vBackdrop = float(aBackdrop); vColor = getColor(); - gl_Position = vec4(position, 0.0, 1.0); + gl_Position = uTransform * vec4(position, 0.0, 1.0); } diff --git a/resources/shaders/gl3/tile_alpha_multicolor.vs.glsl b/resources/shaders/gl3/tile_alpha_multicolor.vs.glsl index 2adf4dc3..3994dc28 100644 --- a/resources/shaders/gl3/tile_alpha_multicolor.vs.glsl +++ b/resources/shaders/gl3/tile_alpha_multicolor.vs.glsl @@ -27,10 +27,9 @@ precision highp float; -uniform vec2 uFramebufferSize; +uniform mat4 uTransform; uniform vec2 uTileSize; uniform vec2 uStencilTextureSize; -uniform vec2 uViewBoxOrigin; in uvec2 aTessCoord; in uvec3 aTileOrigin; @@ -51,15 +50,14 @@ vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth){ void computeVaryings(){ vec2 origin = vec2(aTileOrigin . xy)+ vec2(aTileOrigin . z & 15u, aTileOrigin . z >> 4u)* 256.0; - vec2 pixelPosition =(origin + vec2(aTessCoord))* uTileSize + uViewBoxOrigin; - vec2 position =(pixelPosition / uFramebufferSize * 2.0 - 1.0)* vec2(1.0, - 1.0); + vec2 position =(origin + vec2(aTessCoord))* uTileSize; vec2 maskTexCoordOrigin = computeTileOffset(uint(aTileIndex), uStencilTextureSize . x); vec2 maskTexCoord = maskTexCoordOrigin + aTessCoord * uTileSize; vTexCoord = maskTexCoord / uStencilTextureSize; vBackdrop = float(aBackdrop); vColor = getColor(); - gl_Position = vec4(position, 0.0, 1.0); + gl_Position = uTransform * vec4(position, 0.0, 1.0); } diff --git a/resources/shaders/gl3/tile_solid_monochrome.vs.glsl b/resources/shaders/gl3/tile_solid_monochrome.vs.glsl index c0808cbd..74437913 100644 --- a/resources/shaders/gl3/tile_solid_monochrome.vs.glsl +++ b/resources/shaders/gl3/tile_solid_monochrome.vs.glsl @@ -27,9 +27,8 @@ precision highp float; -uniform vec2 uFramebufferSize; +uniform mat4 uTransform; uniform vec2 uTileSize; -uniform vec2 uViewBoxOrigin; in uvec2 aTessCoord; in ivec2 aTileOrigin; @@ -39,11 +38,9 @@ out vec4 vColor; vec4 getColor(); void computeVaryings(){ - vec2 pixelPosition = vec2(aTileOrigin + ivec2(aTessCoord))* uTileSize + uViewBoxOrigin; - vec2 position =(pixelPosition / uFramebufferSize * 2.0 - 1.0)* vec2(1.0, - 1.0); - + vec2 position = vec2(aTileOrigin + ivec2(aTessCoord))* uTileSize; vColor = getColor(); - gl_Position = vec4(position, 0.0, 1.0); + gl_Position = uTransform * vec4(position, 0.0, 1.0); } diff --git a/resources/shaders/gl3/tile_solid_multicolor.vs.glsl b/resources/shaders/gl3/tile_solid_multicolor.vs.glsl index 4ff5208c..fbeeb949 100644 --- a/resources/shaders/gl3/tile_solid_multicolor.vs.glsl +++ b/resources/shaders/gl3/tile_solid_multicolor.vs.glsl @@ -27,9 +27,8 @@ precision highp float; -uniform vec2 uFramebufferSize; +uniform mat4 uTransform; uniform vec2 uTileSize; -uniform vec2 uViewBoxOrigin; in uvec2 aTessCoord; in ivec2 aTileOrigin; @@ -39,11 +38,9 @@ out vec4 vColor; vec4 getColor(); void computeVaryings(){ - vec2 pixelPosition = vec2(aTileOrigin + ivec2(aTessCoord))* uTileSize + uViewBoxOrigin; - vec2 position =(pixelPosition / uFramebufferSize * 2.0 - 1.0)* vec2(1.0, - 1.0); - + vec2 position = vec2(aTileOrigin + ivec2(aTessCoord))* uTileSize; vColor = getColor(); - gl_Position = vec4(position, 0.0, 1.0); + gl_Position = uTransform * vec4(position, 0.0, 1.0); } diff --git a/resources/shaders/metal/tile_alpha_monochrome.vs.metal b/resources/shaders/metal/tile_alpha_monochrome.vs.metal index 9c9e1b56..f2d70fca 100644 --- a/resources/shaders/metal/tile_alpha_monochrome.vs.metal +++ b/resources/shaders/metal/tile_alpha_monochrome.vs.metal @@ -9,10 +9,9 @@ using namespace metal; struct spvDescriptorSetBuffer0 { constant float2* uTileSize [[id(0)]]; - constant float2* uViewBoxOrigin [[id(1)]]; - constant float2* uFramebufferSize [[id(2)]]; - constant float2* uStencilTextureSize [[id(3)]]; - constant float4* uColor [[id(4)]]; + constant float2* uStencilTextureSize [[id(1)]]; + constant float4x4* uTransform [[id(2)]]; + constant float4* uColor [[id(3)]]; }; struct main0_out @@ -43,11 +42,10 @@ float4 getColor(thread float4 uColor) return uColor; } -void computeVaryings(thread float2 uTileSize, thread uint3& aTileOrigin, thread uint2& aTessCoord, thread float2 uViewBoxOrigin, thread float2 uFramebufferSize, thread int& aTileIndex, thread float2 uStencilTextureSize, thread float2& vTexCoord, thread float& vBackdrop, thread int& aBackdrop, thread float4& vColor, thread float4& gl_Position, thread float4 uColor) +void computeVaryings(thread float2 uTileSize, thread uint3& aTileOrigin, thread uint2& aTessCoord, thread int& aTileIndex, thread float2 uStencilTextureSize, thread float2& vTexCoord, thread float& vBackdrop, thread int& aBackdrop, thread float4& vColor, thread float4& gl_Position, thread float4x4 uTransform, thread float4 uColor) { float2 origin = float2(aTileOrigin.xy) + (float2(float(aTileOrigin.z & 15u), float(aTileOrigin.z >> 4u)) * 256.0); - float2 pixelPosition = ((origin + float2(aTessCoord)) * uTileSize) + uViewBoxOrigin; - float2 position = (((pixelPosition / uFramebufferSize) * 2.0) - float2(1.0)) * float2(1.0, -1.0); + float2 position = (origin + float2(aTessCoord)) * uTileSize; uint param = uint(aTileIndex); float param_1 = uStencilTextureSize.x; float2 maskTexCoordOrigin = computeTileOffset(param, param_1, uTileSize); @@ -55,13 +53,13 @@ void computeVaryings(thread float2 uTileSize, thread uint3& aTileOrigin, thread vTexCoord = maskTexCoord / uStencilTextureSize; vBackdrop = float(aBackdrop); vColor = getColor(uColor); - gl_Position = float4(position, 0.0, 1.0); + gl_Position = uTransform * float4(position, 0.0, 1.0); } vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; - computeVaryings((*spvDescriptorSet0.uTileSize), in.aTileOrigin, in.aTessCoord, (*spvDescriptorSet0.uViewBoxOrigin), (*spvDescriptorSet0.uFramebufferSize), in.aTileIndex, (*spvDescriptorSet0.uStencilTextureSize), out.vTexCoord, out.vBackdrop, in.aBackdrop, out.vColor, out.gl_Position, (*spvDescriptorSet0.uColor)); + computeVaryings((*spvDescriptorSet0.uTileSize), in.aTileOrigin, in.aTessCoord, in.aTileIndex, (*spvDescriptorSet0.uStencilTextureSize), out.vTexCoord, out.vBackdrop, in.aBackdrop, out.vColor, out.gl_Position, (*spvDescriptorSet0.uTransform), (*spvDescriptorSet0.uColor)); return out; } diff --git a/resources/shaders/metal/tile_alpha_multicolor.vs.metal b/resources/shaders/metal/tile_alpha_multicolor.vs.metal index 7e62af78..5a6cac08 100644 --- a/resources/shaders/metal/tile_alpha_multicolor.vs.metal +++ b/resources/shaders/metal/tile_alpha_multicolor.vs.metal @@ -9,11 +9,10 @@ using namespace metal; struct spvDescriptorSetBuffer0 { constant float2* uTileSize [[id(0)]]; - constant float2* uViewBoxOrigin [[id(1)]]; - constant float2* uFramebufferSize [[id(2)]]; - constant float2* uStencilTextureSize [[id(3)]]; - texture2d uPaintTexture [[id(4)]]; - sampler uPaintTextureSmplr [[id(5)]]; + constant float2* uStencilTextureSize [[id(1)]]; + constant float4x4* uTransform [[id(2)]]; + texture2d uPaintTexture [[id(3)]]; + sampler uPaintTextureSmplr [[id(4)]]; }; struct main0_out @@ -45,11 +44,10 @@ float4 getColor(thread texture2d uPaintTexture, thread const sampler uPai return uPaintTexture.sample(uPaintTextureSmplr, aColorTexCoord, level(0.0)); } -void computeVaryings(thread float2 uTileSize, thread uint3& aTileOrigin, thread uint2& aTessCoord, thread float2 uViewBoxOrigin, thread float2 uFramebufferSize, thread int& aTileIndex, thread float2 uStencilTextureSize, thread float2& vTexCoord, thread float& vBackdrop, thread int& aBackdrop, thread float4& vColor, thread float4& gl_Position, thread texture2d uPaintTexture, thread const sampler uPaintTextureSmplr, thread float2& aColorTexCoord) +void computeVaryings(thread float2 uTileSize, thread uint3& aTileOrigin, thread uint2& aTessCoord, thread int& aTileIndex, thread float2 uStencilTextureSize, thread float2& vTexCoord, thread float& vBackdrop, thread int& aBackdrop, thread float4& vColor, thread float4& gl_Position, thread float4x4 uTransform, thread texture2d uPaintTexture, thread const sampler uPaintTextureSmplr, thread float2& aColorTexCoord) { float2 origin = float2(aTileOrigin.xy) + (float2(float(aTileOrigin.z & 15u), float(aTileOrigin.z >> 4u)) * 256.0); - float2 pixelPosition = ((origin + float2(aTessCoord)) * uTileSize) + uViewBoxOrigin; - float2 position = (((pixelPosition / uFramebufferSize) * 2.0) - float2(1.0)) * float2(1.0, -1.0); + float2 position = (origin + float2(aTessCoord)) * uTileSize; uint param = uint(aTileIndex); float param_1 = uStencilTextureSize.x; float2 maskTexCoordOrigin = computeTileOffset(param, param_1, uTileSize); @@ -57,13 +55,13 @@ void computeVaryings(thread float2 uTileSize, thread uint3& aTileOrigin, thread vTexCoord = maskTexCoord / uStencilTextureSize; vBackdrop = float(aBackdrop); vColor = getColor(uPaintTexture, uPaintTextureSmplr, aColorTexCoord); - gl_Position = float4(position, 0.0, 1.0); + gl_Position = uTransform * float4(position, 0.0, 1.0); } vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; - computeVaryings((*spvDescriptorSet0.uTileSize), in.aTileOrigin, in.aTessCoord, (*spvDescriptorSet0.uViewBoxOrigin), (*spvDescriptorSet0.uFramebufferSize), in.aTileIndex, (*spvDescriptorSet0.uStencilTextureSize), out.vTexCoord, out.vBackdrop, in.aBackdrop, out.vColor, out.gl_Position, spvDescriptorSet0.uPaintTexture, spvDescriptorSet0.uPaintTextureSmplr, in.aColorTexCoord); + computeVaryings((*spvDescriptorSet0.uTileSize), in.aTileOrigin, in.aTessCoord, in.aTileIndex, (*spvDescriptorSet0.uStencilTextureSize), out.vTexCoord, out.vBackdrop, in.aBackdrop, out.vColor, out.gl_Position, (*spvDescriptorSet0.uTransform), spvDescriptorSet0.uPaintTexture, spvDescriptorSet0.uPaintTextureSmplr, in.aColorTexCoord); return out; } diff --git a/resources/shaders/metal/tile_solid_monochrome.vs.metal b/resources/shaders/metal/tile_solid_monochrome.vs.metal index 46328863..3a2d625f 100644 --- a/resources/shaders/metal/tile_solid_monochrome.vs.metal +++ b/resources/shaders/metal/tile_solid_monochrome.vs.metal @@ -9,9 +9,8 @@ using namespace metal; struct spvDescriptorSetBuffer0 { constant float2* uTileSize [[id(0)]]; - constant float2* uViewBoxOrigin [[id(1)]]; - constant float2* uFramebufferSize [[id(2)]]; - constant float4* uColor [[id(3)]]; + constant float4x4* uTransform [[id(1)]]; + constant float4* uColor [[id(2)]]; }; struct main0_out @@ -31,18 +30,17 @@ float4 getColor(thread float4 uColor) return uColor; } -void computeVaryings(thread int2& aTileOrigin, thread uint2& aTessCoord, thread float2 uTileSize, thread float2 uViewBoxOrigin, thread float2 uFramebufferSize, thread float4& vColor, thread float4& gl_Position, thread float4 uColor) +void computeVaryings(thread int2& aTileOrigin, thread uint2& aTessCoord, thread float2 uTileSize, thread float4& vColor, thread float4& gl_Position, thread float4x4 uTransform, thread float4 uColor) { - float2 pixelPosition = (float2(aTileOrigin + int2(aTessCoord)) * uTileSize) + uViewBoxOrigin; - float2 position = (((pixelPosition / uFramebufferSize) * 2.0) - float2(1.0)) * float2(1.0, -1.0); + float2 position = float2(aTileOrigin + int2(aTessCoord)) * uTileSize; vColor = getColor(uColor); - gl_Position = float4(position, 0.0, 1.0); + gl_Position = uTransform * float4(position, 0.0, 1.0); } vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; - computeVaryings(in.aTileOrigin, in.aTessCoord, (*spvDescriptorSet0.uTileSize), (*spvDescriptorSet0.uViewBoxOrigin), (*spvDescriptorSet0.uFramebufferSize), out.vColor, out.gl_Position, (*spvDescriptorSet0.uColor)); + computeVaryings(in.aTileOrigin, in.aTessCoord, (*spvDescriptorSet0.uTileSize), out.vColor, out.gl_Position, (*spvDescriptorSet0.uTransform), (*spvDescriptorSet0.uColor)); return out; } diff --git a/resources/shaders/metal/tile_solid_multicolor.vs.metal b/resources/shaders/metal/tile_solid_multicolor.vs.metal index 573f5173..bbc79172 100644 --- a/resources/shaders/metal/tile_solid_multicolor.vs.metal +++ b/resources/shaders/metal/tile_solid_multicolor.vs.metal @@ -9,10 +9,9 @@ using namespace metal; struct spvDescriptorSetBuffer0 { constant float2* uTileSize [[id(0)]]; - constant float2* uViewBoxOrigin [[id(1)]]; - constant float2* uFramebufferSize [[id(2)]]; - texture2d uPaintTexture [[id(3)]]; - sampler uPaintTextureSmplr [[id(4)]]; + constant float4x4* uTransform [[id(1)]]; + texture2d uPaintTexture [[id(2)]]; + sampler uPaintTextureSmplr [[id(3)]]; }; struct main0_out @@ -33,18 +32,17 @@ float4 getColor(thread texture2d uPaintTexture, thread const sampler uPai return uPaintTexture.sample(uPaintTextureSmplr, aColorTexCoord, level(0.0)); } -void computeVaryings(thread int2& aTileOrigin, thread uint2& aTessCoord, thread float2 uTileSize, thread float2 uViewBoxOrigin, thread float2 uFramebufferSize, thread float4& vColor, thread float4& gl_Position, thread texture2d uPaintTexture, thread const sampler uPaintTextureSmplr, thread float2& aColorTexCoord) +void computeVaryings(thread int2& aTileOrigin, thread uint2& aTessCoord, thread float2 uTileSize, thread float4& vColor, thread float4& gl_Position, thread float4x4 uTransform, thread texture2d uPaintTexture, thread const sampler uPaintTextureSmplr, thread float2& aColorTexCoord) { - float2 pixelPosition = (float2(aTileOrigin + int2(aTessCoord)) * uTileSize) + uViewBoxOrigin; - float2 position = (((pixelPosition / uFramebufferSize) * 2.0) - float2(1.0)) * float2(1.0, -1.0); + float2 position = float2(aTileOrigin + int2(aTessCoord)) * uTileSize; vColor = getColor(uPaintTexture, uPaintTextureSmplr, aColorTexCoord); - gl_Position = float4(position, 0.0, 1.0); + gl_Position = uTransform * float4(position, 0.0, 1.0); } vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) { main0_out out = {}; - computeVaryings(in.aTileOrigin, in.aTessCoord, (*spvDescriptorSet0.uTileSize), (*spvDescriptorSet0.uViewBoxOrigin), (*spvDescriptorSet0.uFramebufferSize), out.vColor, out.gl_Position, spvDescriptorSet0.uPaintTexture, spvDescriptorSet0.uPaintTextureSmplr, in.aColorTexCoord); + computeVaryings(in.aTileOrigin, in.aTessCoord, (*spvDescriptorSet0.uTileSize), out.vColor, out.gl_Position, (*spvDescriptorSet0.uTransform), spvDescriptorSet0.uPaintTexture, spvDescriptorSet0.uPaintTextureSmplr, in.aColorTexCoord); return out; } diff --git a/shaders/tile_alpha_vertex.inc.glsl b/shaders/tile_alpha_vertex.inc.glsl index e5211809..dee7eb36 100644 --- a/shaders/tile_alpha_vertex.inc.glsl +++ b/shaders/tile_alpha_vertex.inc.glsl @@ -8,10 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -uniform vec2 uFramebufferSize; +uniform mat4 uTransform; uniform vec2 uTileSize; uniform vec2 uStencilTextureSize; -uniform vec2 uViewBoxOrigin; in uvec2 aTessCoord; in uvec3 aTileOrigin; @@ -32,14 +31,13 @@ vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) { void computeVaryings() { vec2 origin = vec2(aTileOrigin.xy) + vec2(aTileOrigin.z & 15u, aTileOrigin.z >> 4u) * 256.0; - vec2 pixelPosition = (origin + vec2(aTessCoord)) * uTileSize + uViewBoxOrigin; - vec2 position = (pixelPosition / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0); + vec2 position = (origin + vec2(aTessCoord)) * uTileSize; vec2 maskTexCoordOrigin = computeTileOffset(uint(aTileIndex), uStencilTextureSize.x); vec2 maskTexCoord = maskTexCoordOrigin + aTessCoord * uTileSize; vTexCoord = maskTexCoord / uStencilTextureSize; vBackdrop = float(aBackdrop); vColor = getColor(); - gl_Position = vec4(position, 0.0, 1.0); + gl_Position = uTransform * vec4(position, 0.0, 1.0); } diff --git a/shaders/tile_solid_vertex.inc.glsl b/shaders/tile_solid_vertex.inc.glsl index fc7c8bc5..da7d19f2 100644 --- a/shaders/tile_solid_vertex.inc.glsl +++ b/shaders/tile_solid_vertex.inc.glsl @@ -8,9 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -uniform vec2 uFramebufferSize; +uniform mat4 uTransform; uniform vec2 uTileSize; -uniform vec2 uViewBoxOrigin; in uvec2 aTessCoord; in ivec2 aTileOrigin; @@ -20,9 +19,7 @@ out vec4 vColor; vec4 getColor(); void computeVaryings() { - vec2 pixelPosition = vec2(aTileOrigin + ivec2(aTessCoord)) * uTileSize + uViewBoxOrigin; - vec2 position = (pixelPosition / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0); - + vec2 position = vec2(aTileOrigin + ivec2(aTessCoord)) * uTileSize; vColor = getColor(); - gl_Position = vec4(position, 0.0, 1.0); + gl_Position = uTransform * vec4(position, 0.0, 1.0); } diff --git a/simd/src/arm/mod.rs b/simd/src/arm/mod.rs index f73c7303..e1705812 100644 --- a/simd/src/arm/mod.rs +++ b/simd/src/arm/mod.rs @@ -8,17 +8,198 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::arch::aarch64::{self, float32x4_t, int32x4_t, uint32x4_t, uint64x2_t, uint8x16_t}; -use std::arch::aarch64::{uint8x8_t, uint8x8x2_t}; +use std::arch::aarch64::{self, float32x2_t, float32x4_t, int32x2_t, int32x4_t}; +use std::arch::aarch64::{uint32x2_t, uint32x4_t}; use std::f32; use std::fmt::{self, Debug, Formatter}; use std::mem; -use std::ops::{Add, Index, IndexMut, Mul, Sub}; +use std::ops::{Add, BitAnd, BitOr, Index, IndexMut, Mul, Shr, Sub}; mod swizzle_f32x4; mod swizzle_i32x4; -// 32-bit floats +// Two 32-bit floats + +#[derive(Clone, Copy)] +pub struct F32x2(pub float32x2_t); + +impl F32x2 { + // Constructors + + #[inline] + pub fn new(a: f32, b: f32) -> F32x2 { + unsafe { F32x2(mem::transmute([a, b])) } + } + + #[inline] + pub fn splat(x: f32) -> F32x2 { + F32x2::new(x, x) + } + + // Basic operations + + #[inline] + pub fn approx_recip(self) -> F32x2 { + unsafe { F32x2(vrecpe_v2f32(self.0)) } + } + + #[inline] + pub fn min(self, other: F32x2) -> F32x2 { + unsafe { F32x2(simd_fmin(self.0, other.0)) } + } + + #[inline] + pub fn max(self, other: F32x2) -> F32x2 { + unsafe { F32x2(simd_fmax(self.0, other.0)) } + } + + #[inline] + pub fn clamp(self, min: F32x2, max: F32x2) -> F32x2 { + self.max(min).min(max) + } + + #[inline] + pub fn abs(self) -> F32x2 { + unsafe { F32x2(fabs_v2f32(self.0)) } + } + + #[inline] + pub fn floor(self) -> F32x2 { + unsafe { F32x2(floor_v2f32(self.0)) } + } + + #[inline] + pub fn ceil(self) -> F32x2 { + unsafe { F32x2(ceil_v2f32(self.0)) } + } + + #[inline] + pub fn round(self) -> F32x2 { + unsafe { F32x2(round_v2f32(self.0)) } + } + + #[inline] + pub fn sqrt(self) -> F32x2 { + unsafe { F32x2(sqrt_v2f32(self.0)) } + } + + // Packed comparisons + + #[inline] + pub fn packed_eq(self, other: F32x2) -> U32x2 { + unsafe { U32x2(simd_eq(self.0, other.0)) } + } + + #[inline] + pub fn packed_gt(self, other: F32x2) -> U32x2 { + unsafe { U32x2(simd_gt(self.0, other.0)) } + } + + #[inline] + pub fn packed_lt(self, other: F32x2) -> U32x2 { + unsafe { U32x2(simd_lt(self.0, other.0)) } + } + + #[inline] + pub fn packed_le(self, other: F32x2) -> U32x2 { + unsafe { U32x2(simd_le(self.0, other.0)) } + } + + // Conversions + + #[inline] + pub fn to_f32x4(self) -> F32x4 { + self.concat_xy_xy(F32x2::default()) + } + + #[inline] + pub fn to_i32x2(self) -> I32x2 { + unsafe { I32x2(simd_cast(self.0)) } + } + + #[inline] + pub fn to_i32x4(self) -> I32x4 { + self.to_i32x2().concat_xy_xy(I32x2::default()) + } + + // Swizzle + + #[inline] + pub fn yx(self) -> F32x2 { + unsafe { F32x2(simd_shuffle2(self.0, self.0, [1, 0])) } + } + + // Concatenations + + #[inline] + pub fn concat_xy_xy(self, other: F32x2) -> F32x4 { + unsafe { F32x4(simd_shuffle4(self.0, other.0, [0, 1, 0, 1])) } + } +} + +impl Default for F32x2 { + #[inline] + fn default() -> F32x2 { + F32x2::new(0.0, 0.0) + } +} + +impl Debug for F32x2 { + #[inline] + fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { + write!(f, "<{}, {}>", self[0], self[1]) + } +} + +impl Index for F32x2 { + type Output = f32; + #[inline] + fn index(&self, index: usize) -> &f32 { + unsafe { + assert!(index < 2); + let ptr = &self.0 as *const float32x2_t as *const f32; + mem::transmute::<*const f32, &f32>(ptr.offset(index as isize)) + } + } +} + +impl IndexMut for F32x2 { + #[inline] + fn index_mut(&mut self, index: usize) -> &mut f32 { + unsafe { + assert!(index < 2); + let ptr = &mut self.0 as *mut float32x2_t as *mut f32; + mem::transmute::<*mut f32, &mut f32>(ptr.offset(index as isize)) + } + } +} + + +impl Add for F32x2 { + type Output = F32x2; + #[inline] + fn add(self, other: F32x2) -> F32x2 { + unsafe { F32x2(simd_add(self.0, other.0)) } + } +} + +impl Mul for F32x2 { + type Output = F32x2; + #[inline] + fn mul(self, other: F32x2) -> F32x2 { + unsafe { F32x2(simd_mul(self.0, other.0)) } + } +} + +impl Sub for F32x2 { + type Output = F32x2; + #[inline] + fn sub(self, other: F32x2) -> F32x2 { + unsafe { F32x2(simd_sub(self.0, other.0)) } + } +} + +// Four 32-bit floats #[derive(Clone, Copy)] pub struct F32x4(pub float32x4_t); @@ -103,32 +284,56 @@ impl F32x4 { unsafe { U32x4(simd_lt(self.0, other.0)) } } - // Converts these packed floats to integers. + // Swizzle conversions + #[inline] - pub fn to_i32x4(self) -> I32x4 { - unsafe { I32x4(simd_cast(self.0)) } + pub fn xy(self) -> F32x2 { + unsafe { F32x2(simd_shuffle2(self.0, self.0, [0, 1])) } + } + + #[inline] + pub fn yx(self) -> F32x2 { + unsafe { F32x2(simd_shuffle2(self.0, self.0, [1, 0])) } + } + + #[inline] + pub fn xw(self) -> F32x2 { + unsafe { F32x2(simd_shuffle2(self.0, self.0, [0, 3])) } + } + + #[inline] + pub fn zy(self) -> F32x2 { + unsafe { F32x2(simd_shuffle2(self.0, self.0, [2, 1])) } + } + + #[inline] + pub fn zw(self) -> F32x2 { + unsafe { F32x2(simd_shuffle2(self.0, self.0, [2, 3])) } } // Concatenations #[inline] pub fn concat_xy_xy(self, other: F32x4) -> F32x4 { - unsafe { F32x4(simd_shuffle4(self.0, other.0, [0, 1, 4, 5])) } + unsafe { F32x4(simd_shuffle4(self.0, other.0, [0, 1, 0, 1])) } } #[inline] pub fn concat_xy_zw(self, other: F32x4) -> F32x4 { - unsafe { F32x4(simd_shuffle4(self.0, other.0, [0, 1, 6, 7])) } + unsafe { F32x4(simd_shuffle4(self.0, other.0, [0, 1, 2, 3])) } } #[inline] pub fn concat_zw_zw(self, other: F32x4) -> F32x4 { - unsafe { F32x4(simd_shuffle4(self.0, other.0, [2, 3, 6, 7])) } + unsafe { F32x4(simd_shuffle4(self.0, other.0, [2, 3, 2, 3])) } } + // Conversions + + // Converts these packed floats to integers. #[inline] - pub fn concat_wz_yx(self, other: F32x4) -> F32x4 { - unsafe { F32x4(simd_shuffle4(self.0, other.0, [3, 2, 5, 4])) } + pub fn to_i32x4(self) -> I32x4 { + unsafe { I32x4(simd_cast(self.0)) } } } @@ -200,7 +405,105 @@ impl Sub for F32x4 { } } -// 32-bit signed integers +// Two 32-bit signed integers + +#[derive(Clone, Copy, Debug)] +pub struct I32x2(pub int32x2_t); + +impl I32x2 { + #[inline] + pub fn new(x: i32, y: i32) -> I32x2 { + unsafe { I32x2(mem::transmute([x, y])) } + } + + #[inline] + pub fn splat(x: i32) -> I32x2 { + I32x2::new(x, x) + } + + #[inline] + pub fn packed_eq(self, other: I32x2) -> U32x2 { + unsafe { U32x2(simd_eq(self.0, other.0)) } + } + + // Concatenations + + #[inline] + pub fn concat_xy_xy(self, other: I32x2) -> I32x4 { + unsafe { I32x4(simd_shuffle4(self.0, other.0, [0, 1, 0, 1])) } + } + + // Conversions + + /// Converts these packed integers to floats. + #[inline] + pub fn to_f32x2(self) -> F32x2 { + unsafe { F32x2(simd_cast(self.0)) } + } +} + +impl Default for I32x2 { + #[inline] + fn default() -> I32x2 { + I32x2::splat(0) + } +} + +impl PartialEq for I32x2 { + #[inline] + fn eq(&self, other: &I32x2) -> bool { + self.packed_eq(*other).is_all_ones() + } +} + +impl Index for I32x2 { + type Output = i32; + #[inline] + fn index(&self, index: usize) -> &i32 { + unsafe { + assert!(index < 2); + let ptr = &self.0 as *const int32x2_t as *const i32; + mem::transmute::<*const i32, &i32>(ptr.offset(index as isize)) + } + } +} + +impl IndexMut for I32x2 { + #[inline] + fn index_mut(&mut self, index: usize) -> &mut i32 { + unsafe { + assert!(index < 2); + let ptr = &mut self.0 as *mut int32x2_t as *mut i32; + mem::transmute::<*mut i32, &mut i32>(ptr.offset(index as isize)) + } + } +} + +impl Add for I32x2 { + type Output = I32x2; + #[inline] + fn add(self, other: I32x2) -> I32x2 { + unsafe { I32x2(simd_add(self.0, other.0)) } + } +} + +impl Sub for I32x2 { + type Output = I32x2; + #[inline] + fn sub(self, other: I32x2) -> I32x2 { + unsafe { I32x2(simd_sub(self.0, other.0)) } + } +} + +impl Mul for I32x2 { + type Output = I32x2; + #[inline] + fn mul(self, other: I32x2) -> I32x2 { + unsafe { I32x2(simd_mul(self.0, other.0)) } + } +} + +// Four 32-bit signed integers #[derive(Clone, Copy, Debug)] pub struct I32x4(pub int32x4_t); @@ -216,11 +519,6 @@ impl I32x4 { I32x4::new(x, x, x, x) } - #[inline] - pub fn as_u8x16(self) -> U8x16 { - unsafe { U8x16(*mem::transmute::<&int32x4_t, &uint8x16_t>(&self.0)) } - } - #[inline] pub fn min(self, other: I32x4) -> I32x4 { unsafe { I32x4(simd_fmin(self.0, other.0)) } @@ -245,6 +543,33 @@ impl I32x4 { unsafe { I32x4(simd_shuffle4(self.0, other.0, [0, 1, 4, 5])) } } + // Swizzle conversions + + #[inline] + pub fn xy(self) -> I32x2 { + unsafe { I32x2(simd_shuffle2(self.0, self.0, [0, 1])) } + } + + #[inline] + pub fn yx(self) -> I32x2 { + unsafe { I32x2(simd_shuffle2(self.0, self.0, [1, 0])) } + } + + #[inline] + pub fn xw(self) -> I32x2 { + unsafe { I32x2(simd_shuffle2(self.0, self.0, [0, 3])) } + } + + #[inline] + pub fn zy(self) -> I32x2 { + unsafe { I32x2(simd_shuffle2(self.0, self.0, [2, 1])) } + } + + #[inline] + pub fn zw(self) -> I32x2 { + unsafe { I32x2(simd_shuffle2(self.0, self.0, [2, 3])) } + } + // Conversions /// Converts these packed integers to floats. @@ -315,7 +640,60 @@ impl PartialEq for I32x4 { } } -// 32-bit unsigned integers +impl BitAnd for I32x4 { + type Output = I32x4; + #[inline] + fn bitand(self, other: I32x4) -> I32x4 { + unsafe { I32x4(simd_and(self.0, other.0)) } + } +} + +impl BitOr for I32x4 { + type Output = I32x4; + #[inline] + fn bitor(self, other: I32x4) -> I32x4 { + unsafe { I32x4(simd_or(self.0, other.0)) } + } +} + +impl Shr for I32x4 { + type Output = I32x4; + #[inline] + fn shr(self, other: I32x4) -> I32x4 { + unsafe { I32x4(simd_shr(self.0, other.0)) } + } +} + +// Two 32-bit unsigned integers + +#[derive(Clone, Copy)] +pub struct U32x2(pub uint32x2_t); + +impl U32x2 { + #[inline] + pub fn is_all_ones(&self) -> bool { + unsafe { aarch64::vminv_u32(self.0) == !0 } + } + + #[inline] + pub fn is_all_zeroes(&self) -> bool { + unsafe { aarch64::vmaxv_u32(self.0) == 0 } + } +} + +impl Index for U32x2 { + type Output = u32; + #[inline] + fn index(&self, index: usize) -> &u32 { + unsafe { + assert!(index < 2); + let ptr = &self.0 as *const uint32x2_t as *const u32; + mem::transmute::<*const u32, &u32>(ptr.offset(index as isize)) + } + } +} + +// Four 32-bit unsigned integers #[derive(Clone, Copy)] pub struct U32x4(pub uint32x4_t); @@ -344,44 +722,6 @@ impl Index for U32x4 { } } -// 8-bit unsigned integers - -#[derive(Clone, Copy)] -pub struct U8x16(pub uint8x16_t); - -impl U8x16 { - #[inline] - pub fn as_i32x4(self) -> I32x4 { - unsafe { I32x4(*mem::transmute::<&uint8x16_t, &int32x4_t>(&self.0)) } - } - - #[inline] - pub fn shuffle(self, indices: U8x16) -> U8x16 { - unsafe { - let table = mem::transmute::(self.0); - let low = aarch64::vtbl2_u8(table, indices.extract_low()); - let high = aarch64::vtbl2_u8(table, indices.extract_high()); - U8x16(aarch64::vcombine_u8(low, high)) - } - } - - #[inline] - fn extract_low(self) -> uint8x8_t { - unsafe { - let low = simd_extract(mem::transmute::(self.0), 0); - mem::transmute::(low) - } - } - - #[inline] - fn extract_high(self) -> uint8x8_t { - unsafe { - let high = simd_extract(mem::transmute::(self.0), 1); - mem::transmute::(high) - } - } -} - // Intrinsics extern "platform-intrinsic" { @@ -389,6 +729,11 @@ extern "platform-intrinsic" { fn simd_mul(x: T, y: T) -> T; fn simd_sub(x: T, y: T) -> T; + fn simd_shr(x: T, y: T) -> T; + + fn simd_and(x: T, y: T) -> T; + fn simd_or(x: T, y: T) -> T; + fn simd_fmin(x: T, y: T) -> T; fn simd_fmax(x: T, y: T) -> T; @@ -397,15 +742,24 @@ extern "platform-intrinsic" { fn simd_le(x: T, y: T) -> U; fn simd_lt(x: T, y: T) -> U; + fn simd_shuffle2(x: T, y: T, idx: [u32; 2]) -> U; fn simd_shuffle4(x: T, y: T, idx: [u32; 4]) -> U; fn simd_cast(x: T) -> U; - - fn simd_insert(x: T, index: u32, value: U) -> T; - fn simd_extract(x: T, index: u32) -> U; } extern "C" { + #[link_name = "llvm.fabs.v2f32"] + fn fabs_v2f32(a: float32x2_t) -> float32x2_t; + #[link_name = "llvm.floor.v2f32"] + fn floor_v2f32(a: float32x2_t) -> float32x2_t; + #[link_name = "llvm.ceil.v2f32"] + fn ceil_v2f32(a: float32x2_t) -> float32x2_t; + #[link_name = "llvm.round.v2f32"] + fn round_v2f32(a: float32x2_t) -> float32x2_t; + #[link_name = "llvm.sqrt.v2f32"] + fn sqrt_v2f32(a: float32x2_t) -> float32x2_t; + #[link_name = "llvm.fabs.v4f32"] fn fabs_v4f32(a: float32x4_t) -> float32x4_t; #[link_name = "llvm.floor.v4f32"] @@ -417,6 +771,9 @@ extern "C" { #[link_name = "llvm.sqrt.v4f32"] fn sqrt_v4f32(a: float32x4_t) -> float32x4_t; + #[link_name = "llvm.aarch64.neon.frecpe.v2f32"] + fn vrecpe_v2f32(a: float32x2_t) -> float32x2_t; + #[link_name = "llvm.aarch64.neon.frecpe.v4f32"] fn vrecpe_v4f32(a: float32x4_t) -> float32x4_t; } diff --git a/simd/src/extras.rs b/simd/src/extras.rs index 8abea386..55e265cf 100644 --- a/simd/src/extras.rs +++ b/simd/src/extras.rs @@ -8,10 +8,84 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::default::{F32x4, I32x4}; +use crate::default::{F32x2, F32x4, I32x2, I32x4}; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; -// 32-bit floats +// Two 32-bit floats + +impl F32x2 { + // Constructors + + #[inline] + pub fn from_slice(slice: &[f32]) -> F32x2 { + F32x2::new(slice[0], slice[1]) + } + + // Accessors + + #[inline] + pub fn x(self) -> f32 { + self[0] + } + + #[inline] + pub fn y(self) -> f32 { + self[1] + } + + // Mutators + + #[inline] + pub fn set_x(&mut self, x: f32) { + self[0] = x + } + + #[inline] + pub fn set_y(&mut self, y: f32) { + self[1] = y + } + + // Comparisons + + #[inline] + pub fn approx_eq(self, other: F32x2, epsilon: f32) -> bool { + (self - other) + .abs() + .packed_gt(F32x2::splat(epsilon)) + .is_all_zeroes() + } +} + +impl AddAssign for F32x2 { + #[inline] + fn add_assign(&mut self, other: F32x2) { + *self = *self + other + } +} + +impl SubAssign for F32x2 { + #[inline] + fn sub_assign(&mut self, other: F32x2) { + *self = *self - other + } +} + +impl MulAssign for F32x2 { + #[inline] + fn mul_assign(&mut self, other: F32x2) { + *self = *self * other + } +} + +impl Neg for F32x2 { + type Output = F32x2; + #[inline] + fn neg(self) -> F32x2 { + F32x2::default() - self + } +} + +// Four 32-bit floats impl F32x4 { // Constructors @@ -105,7 +179,38 @@ impl Neg for F32x4 { } } -// 32-bit integers +// Two 32-bit integers + +impl AddAssign for I32x2 { + #[inline] + fn add_assign(&mut self, other: I32x2) { + *self = *self + other + } +} + +impl SubAssign for I32x2 { + #[inline] + fn sub_assign(&mut self, other: I32x2) { + *self = *self - other + } +} + +impl MulAssign for I32x2 { + #[inline] + fn mul_assign(&mut self, other: I32x2) { + *self = *self * other + } +} + +impl Neg for I32x2 { + type Output = I32x2; + #[inline] + fn neg(self) -> I32x2 { + I32x2::default() - self + } +} + +// Four 32-bit integers impl AddAssign for I32x4 { #[inline] diff --git a/simd/src/scalar/mod.rs b/simd/src/scalar/mod.rs index 19049d01..867dfe4b 100644 --- a/simd/src/scalar/mod.rs +++ b/simd/src/scalar/mod.rs @@ -10,13 +10,182 @@ use std::f32; use std::fmt::{self, Debug, Formatter}; -use std::mem; -use std::ops::{Add, Index, IndexMut, Mul, Sub}; +use std::ops::{Add, BitAnd, BitOr, Index, IndexMut, Mul, Shr, Sub}; mod swizzle_f32x4; mod swizzle_i32x4; -// 32-bit floats +// Two 32-bit floats + +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct F32x2(pub [f32; 2]); + +impl F32x2 { + // Constructors + + #[inline] + pub fn new(a: f32, b: f32) -> F32x2 { + F32x2([a, b]) + } + + #[inline] + pub fn splat(x: f32) -> F32x2 { + F32x2([x, x]) + } + + // Basic operations + + #[inline] + pub fn approx_recip(self) -> F32x2 { + F32x2([1.0 / self[0], 1.0 / self[1]]) + } + + #[inline] + pub fn min(self, other: F32x2) -> F32x2 { + F32x2([f32::min(self[0], other[0]), f32::min(self[1], other[1])]) + } + + #[inline] + pub fn max(self, other: F32x2) -> F32x2 { + F32x2([f32::max(self[0], other[0]), f32::max(self[1], other[1])]) + } + + #[inline] + pub fn clamp(self, min: F32x2, max: F32x2) -> F32x2 { + self.max(min).min(max) + } + + #[inline] + pub fn abs(self) -> F32x2 { + F32x2([self[0].abs(), self[1].abs()]) + } + + #[inline] + pub fn floor(self) -> F32x2 { + F32x2([self[0].floor(), self[1].floor()]) + } + + #[inline] + pub fn ceil(self) -> F32x2 { + F32x2([self[0].ceil(), self[1].ceil()]) + } + + #[inline] + pub fn round(self) -> F32x2 { + F32x2([self[0].round(), self[1].round()]) + } + + #[inline] + pub fn sqrt(self) -> F32x2 { + F32x2([self[0].sqrt(), self[1].sqrt()]) + } + + // Packed comparisons + + #[inline] + pub fn packed_eq(self, other: F32x2) -> U32x2 { + U32x2([ + if self[0] == other[0] { !0 } else { 0 }, + if self[1] == other[1] { !0 } else { 0 }, + ]) + } + + #[inline] + pub fn packed_gt(self, other: F32x2) -> U32x2 { + U32x2([ + if self[0] > other[0] { !0 } else { 0 }, + if self[1] > other[1] { !0 } else { 0 }, + ]) + } + + #[inline] + pub fn packed_lt(self, other: F32x2) -> U32x2 { + U32x2([ + if self[0] < other[0] { !0 } else { 0 }, + if self[1] < other[1] { !0 } else { 0 }, + ]) + } + + #[inline] + pub fn packed_le(self, other: F32x2) -> U32x2 { + U32x2([ + if self[0] <= other[0] { !0 } else { 0 }, + if self[1] <= other[1] { !0 } else { 0 }, + ]) + } + + // Conversions + + #[inline] + pub fn to_f32x4(self) -> F32x4 { + F32x4([self[0] as f32, self[1] as f32, 0.0, 0.0]) + } + + #[inline] + pub fn to_i32x2(self) -> I32x2 { + I32x2([self[0] as i32, self[1] as i32]) + } + + #[inline] + pub fn to_i32x4(self) -> I32x4 { + I32x4([self[0] as i32, self[1] as i32, 0, 0]) + } + + // Swizzle + + #[inline] + pub fn yx(self) -> F32x2 { + F32x2([self[1], self[0]]) + } + + // Concatenations + + #[inline] + pub fn concat_xy_xy(self, other: F32x2) -> F32x4 { + F32x4([self[0], self[1], other[0], other[1]]) + } +} + +impl Index for F32x2 { + type Output = f32; + #[inline] + fn index(&self, index: usize) -> &f32 { + &self.0[index] + } +} + +impl IndexMut for F32x2 { + #[inline] + fn index_mut(&mut self, index: usize) -> &mut f32 { + &mut self.0[index] + } +} + +impl Add for F32x2 { + type Output = F32x2; + #[inline] + fn add(self, other: F32x2) -> F32x2 { + F32x2([self[0] + other[0], self[1] + other[1]]) + } +} + +impl Mul for F32x2 { + type Output = F32x2; + #[inline] + fn mul(self, other: F32x2) -> F32x2 { + F32x2([self[0] * other[0], self[1] * other[1]]) + } +} + +impl Sub for F32x2 { + type Output = F32x2; + #[inline] + fn sub(self, other: F32x2) -> F32x2 { + F32x2([self[0] - other[0], self[1] - other[1]]) + } +} + +// Four 32-bit floats #[derive(Clone, Copy, Default, PartialEq)] pub struct F32x4(pub [f32; 4]); @@ -162,6 +331,33 @@ impl F32x4 { ]) } + // Swizzle conversions + + #[inline] + pub fn xy(self) -> F32x2 { + F32x2([self[0], self[1]]) + } + + #[inline] + pub fn xw(self) -> F32x2 { + F32x2([self[0], self[3]]) + } + + #[inline] + pub fn yx(self) -> F32x2 { + F32x2([self[1], self[0]]) + } + + #[inline] + pub fn zy(self) -> F32x2 { + F32x2([self[2], self[1]]) + } + + #[inline] + pub fn zw(self) -> F32x2 { + F32x2([self[2], self[3]]) + } + // Concatenations #[inline] @@ -246,7 +442,84 @@ impl Sub for F32x4 { } } -// 32-bit signed integers +// Two 32-bit signed integers + +#[derive(Clone, Copy, Default, Debug, PartialEq)] +pub struct I32x2([i32; 2]); + +impl I32x2 { + #[inline] + pub fn new(x: i32, y: i32) -> I32x2 { + I32x2([x, y]) + } + + #[inline] + pub fn splat(x: i32) -> I32x2 { + I32x2([x, x]) + } + + #[inline] + pub fn packed_eq(self, other: I32x2) -> U32x2 { + U32x2([ + if self[0] == other[0] { !0 } else { 0 }, + if self[1] == other[1] { !0 } else { 0 }, + ]) + } + + #[inline] + pub fn concat_xy_xy(self, other: I32x2) -> I32x4 { + I32x4([self[0], self[1], other[0], other[1]]) + } + + // Conversions + + /// Converts these packed integers to floats. + #[inline] + pub fn to_f32x2(self) -> F32x2 { + F32x2([self[0] as f32, self[1] as f32]) + } +} + +impl Index for I32x2 { + type Output = i32; + #[inline] + fn index(&self, index: usize) -> &i32 { + &self.0[index] + } +} + +impl IndexMut for I32x2 { + #[inline] + fn index_mut(&mut self, index: usize) -> &mut i32 { + &mut self.0[index] + } +} + +impl Add for I32x2 { + type Output = I32x2; + #[inline] + fn add(self, other: I32x2) -> I32x2 { + I32x2([self[0] + other[0], self[1] + other[1]]) + } +} + +impl Sub for I32x2 { + type Output = I32x2; + #[inline] + fn sub(self, other: I32x2) -> I32x2 { + I32x2([self[0] - other[0], self[1] - other[1]]) + } +} + +impl Mul for I32x2 { + type Output = I32x2; + #[inline] + fn mul(self, other: I32x2) -> I32x2 { + I32x2([self[0] * other[0], self[1] * other[1]]) + } +} + +// Four 32-bit signed integers #[derive(Clone, Copy, Default, Debug, PartialEq)] pub struct I32x4([i32; 4]); @@ -263,10 +536,6 @@ impl I32x4 { } #[inline] - pub fn as_u8x16(self) -> U8x16 { - unsafe { U8x16(*mem::transmute::<&[i32; 4], &[u8; 16]>(&self.0)) } - } - #[inline] pub fn min(self, other: I32x4) -> I32x4 { I32x4([ @@ -306,6 +575,28 @@ impl I32x4 { I32x4([self[0], self[1], other[0], other[1]]) } + // Swizzle conversions + + #[inline] + pub fn xy(self) -> I32x2 { + I32x2([self[0], self[1]]) + } + + #[inline] + pub fn xw(self) -> I32x2 { + I32x2([self[0], self[3]]) + } + + #[inline] + pub fn zy(self) -> I32x2 { + I32x2([self[2], self[1]]) + } + + #[inline] + pub fn zw(self) -> I32x2 { + I32x2([self[2], self[3]]) + } + // Conversions /// Converts these packed integers to floats. @@ -374,7 +665,61 @@ impl Mul for I32x4 { } } -// 32-bit unsigned integers +impl BitAnd for I32x4 { + type Output = I32x4; + #[inline] + fn bitand(self, other: I32x4) -> I32x4 { + I32x4([self[0] & other[0], self[1] & other[1], self[2] & other[2], self[3] & other[3]]) + } +} + +impl BitOr for I32x4 { + type Output = I32x4; + #[inline] + fn bitor(self, other: I32x4) -> I32x4 { + I32x4([self[0] | other[0], self[1] | other[1], self[2] | other[2], self[3] | other[3]]) + } +} + +impl Shr for I32x4 { + type Output = I32x4; + #[inline] + fn shr(self, other: I32x4) -> I32x4 { + I32x4([ + self[0] >> other[0], + self[1] >> other[1], + self[2] >> other[2], + self[3] >> other[3], + ]) + } +} + +// Two 32-bit unsigned integers + +#[derive(Clone, Copy)] +pub struct U32x2(pub [u32; 2]); + +impl U32x2 { + #[inline] + pub fn is_all_ones(&self) -> bool { + self[0] == !0 && self[1] == !0 + } + + #[inline] + pub fn is_all_zeroes(&self) -> bool { + self[0] == 0 && self[1] == 0 + } +} + +impl Index for U32x2 { + type Output = u32; + #[inline] + fn index(&self, index: usize) -> &u32 { + &self.0[index] + } +} + +// Four 32-bit unsigned integers #[derive(Clone, Copy)] pub struct U32x4(pub [u32; 4]); @@ -398,24 +743,3 @@ impl Index for U32x4 { &self.0[index] } } - -// 8-bit unsigned integers - -#[derive(Clone, Copy)] -pub struct U8x16([u8; 16]); - -impl U8x16 { - #[inline] - pub fn as_i32x4(self) -> I32x4 { - unsafe { I32x4(*mem::transmute::<&[u8; 16], &[i32; 4]>(&self.0)) } - } - - #[inline] - pub fn shuffle(self, indices: U8x16) -> U8x16 { - let mut result = [0; 16]; - for index in 0..16 { - result[index] = self.0[(indices.0[index] & 0x0f) as usize] - } - U8x16(result) - } -} diff --git a/simd/src/x86/mod.rs b/simd/src/x86/mod.rs index d10e1230..41e86e99 100644 --- a/simd/src/x86/mod.rs +++ b/simd/src/x86/mod.rs @@ -12,12 +12,195 @@ use std::arch::x86_64::{self, __m128, __m128i, _MM_FROUND_TO_NEAREST_INT}; use std::cmp::PartialEq; use std::fmt::{self, Debug, Formatter}; use std::mem; -use std::ops::{Add, BitXor, Index, IndexMut, Mul, Not, Sub}; +use std::ops::{Add, BitAnd, BitOr, BitXor, Index, IndexMut, Mul, Not, Shr, Sub}; mod swizzle_f32x4; mod swizzle_i32x4; -// 32-bit floats +// Two 32-bit floats + +#[derive(Clone, Copy)] +pub struct F32x2(pub u64); + +impl F32x2 { + // Constructors + + #[inline] + pub fn new(a: f32, b: f32) -> F32x2 { + unsafe { + let a = mem::transmute::<*const f32, *const u32>(&a); + let b = mem::transmute::<*const f32, *const u32>(&b); + F32x2((*a as u64) | ((*b as u64) << 32)) + } + } + + #[inline] + pub fn splat(x: f32) -> F32x2 { + F32x2::new(x, x) + } + + // Basic operations + + #[inline] + pub fn approx_recip(self) -> F32x2 { + self.to_f32x4().approx_recip().xy() + } + + #[inline] + pub fn min(self, other: F32x2) -> F32x2 { + self.to_f32x4().min(other.to_f32x4()).xy() + } + + #[inline] + pub fn max(self, other: F32x2) -> F32x2 { + self.to_f32x4().max(other.to_f32x4()).xy() + } + + #[inline] + pub fn clamp(self, min: F32x2, max: F32x2) -> F32x2 { + self.to_f32x4().clamp(min.to_f32x4(), max.to_f32x4()).xy() + } + + #[inline] + pub fn abs(self) -> F32x2 { + self.to_f32x4().abs().xy() + } + + #[inline] + pub fn floor(self) -> F32x2 { + self.to_f32x4().floor().xy() + } + + #[inline] + pub fn ceil(self) -> F32x2 { + self.to_f32x4().ceil().xy() + } + + #[inline] + pub fn round(self) -> F32x2 { + self.to_f32x4().round().xy() + } + + #[inline] + pub fn sqrt(self) -> F32x2 { + self.to_f32x4().sqrt().xy() + } + + // Packed comparisons + + #[inline] + pub fn packed_eq(self, other: F32x2) -> U32x2 { + self.to_f32x4().packed_eq(other.to_f32x4()).xy() + } + + #[inline] + pub fn packed_gt(self, other: F32x2) -> U32x2 { + self.to_f32x4().packed_gt(other.to_f32x4()).xy() + } + + #[inline] + pub fn packed_lt(self, other: F32x2) -> U32x2 { + self.to_f32x4().packed_lt(other.to_f32x4()).xy() + } + + #[inline] + pub fn packed_le(self, other: F32x2) -> U32x2 { + self.to_f32x4().packed_le(other.to_f32x4()).xy() + } + + // Conversions + + #[inline] + pub fn to_f32x4(self) -> F32x4 { + unsafe { F32x4(x86_64::_mm_castsi128_ps(x86_64::_mm_cvtsi64_si128(self.0 as i64))) } + } + + #[inline] + pub fn to_i32x2(self) -> I32x2 { + self.to_i32x4().xy() + } + + #[inline] + pub fn to_i32x4(self) -> I32x4 { + self.to_f32x4().to_i32x4() + } + + // Swizzle + + #[inline] + pub fn yx(self) -> F32x2 { + self.to_f32x4().yx() + } + + // Concatenations + + #[inline] + pub fn concat_xy_xy(self, other: F32x2) -> F32x4 { + self.to_f32x4().concat_xy_xy(other.to_f32x4()) + } +} + +impl Default for F32x2 { + #[inline] + fn default() -> F32x2 { + F32x2(0) + } +} + +impl Index for F32x2 { + type Output = f32; + #[inline] + fn index(&self, index: usize) -> &f32 { + unsafe { &mem::transmute::<&u64, &[f32; 2]>(&self.0)[index] } + } +} + +impl IndexMut for F32x2 { + #[inline] + fn index_mut(&mut self, index: usize) -> &mut f32 { + unsafe { &mut mem::transmute::<&mut u64, &mut [f32; 2]>(&mut self.0)[index] } + } +} + +impl Debug for F32x2 { + #[inline] + fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { + write!(f, "<{}, {}>", self[0], self[1]) + } +} + +impl PartialEq for F32x2 { + #[inline] + fn eq(&self, other: &F32x2) -> bool { + self.packed_eq(*other).is_all_ones() + } +} + +impl Add for F32x2 { + type Output = F32x2; + #[inline] + fn add(self, other: F32x2) -> F32x2 { + (self.to_f32x4() + other.to_f32x4()).xy() + } +} + +impl Mul for F32x2 { + type Output = F32x2; + #[inline] + fn mul(self, other: F32x2) -> F32x2 { + (self.to_f32x4() * other.to_f32x4()).xy() + } +} + +impl Sub for F32x2 { + type Output = F32x2; + #[inline] + fn sub(self, other: F32x2) -> F32x2 { + (self.to_f32x4() - other.to_f32x4()).xy() + } +} + +// Four 32-bit floats #[derive(Clone, Copy)] pub struct F32x4(pub __m128); @@ -126,6 +309,33 @@ impl F32x4 { unsafe { I32x4(x86_64::_mm_cvtps_epi32(self.0)) } } + // Extraction + + #[inline] + pub fn xy(self) -> F32x2 { + unsafe { F32x2(x86_64::_mm_cvtsi128_si64(x86_64::_mm_castps_si128(self.0)) as u64) } + } + + #[inline] + pub fn xw(self) -> F32x2 { + unsafe { F32x2(x86_64::_mm_cvtsi128_si64(x86_64::_mm_castps_si128(self.xwyz().0)) as u64) } + } + + #[inline] + pub fn yx(self) -> F32x2 { + unsafe { F32x2(x86_64::_mm_cvtsi128_si64(x86_64::_mm_castps_si128(self.yxwz().0)) as u64) } + } + + #[inline] + pub fn zy(self) -> F32x2 { + unsafe { F32x2(x86_64::_mm_cvtsi128_si64(x86_64::_mm_castps_si128(self.zyxw().0)) as u64) } + } + + #[inline] + pub fn zw(self) -> F32x2 { + unsafe { F32x2(x86_64::_mm_cvtsi128_si64(x86_64::_mm_castps_si128(self.zwxy().0)) as u64) } + } + // Concatenations #[inline] @@ -224,7 +434,140 @@ impl Sub for F32x4 { } } -// 32-bit signed integers +// Two 32-bit signed integers + +#[derive(Clone, Copy)] +pub struct I32x2(pub u64); + +impl I32x2 { + // Constructors + + #[inline] + pub fn new(a: i32, b: i32) -> I32x2 { + unsafe { + let a = mem::transmute::<*const i32, *const u32>(&a); + let b = mem::transmute::<*const i32, *const u32>(&b); + I32x2((*a as u64) | ((*b as u64) << 32)) + } + } + + #[inline] + pub fn splat(x: i32) -> I32x2 { + I32x2::new(x, x) + } + + // Concatenations + + #[inline] + pub fn concat_xy_xy(self, other: I32x2) -> I32x4 { + self.to_i32x4().concat_xy_xy(other.to_i32x4()) + } + + // Conversions + + #[inline] + pub fn to_i32x4(self) -> I32x4 { + unsafe { I32x4(x86_64::_mm_cvtsi64_si128(self.0 as i64)) } + } + + #[inline] + pub fn to_f32x4(self) -> F32x4 { + self.to_i32x4().to_f32x4() + } + + /// Converts these packed integers to floats. + #[inline] + pub fn to_f32x2(self) -> F32x2 { + self.to_f32x4().xy() + } + + // Basic operations + + #[inline] + pub fn min(self, other: I32x2) -> I32x2 { + self.to_i32x4().min(other.to_i32x4()).xy() + } + + // Comparisons + + // TODO(pcwalton): Make a `U32x2` type and use that! + #[inline] + pub fn packed_eq(self, other: I32x2) -> U32x4 { + self.to_i32x4().packed_eq(other.to_i32x4()) + } + + #[inline] + pub fn packed_gt(self, other: I32x2) -> U32x4 { + self.to_i32x4().packed_gt(other.to_i32x4()) + } + + #[inline] + pub fn packed_le(self, other: I32x2) -> U32x4 { + self.to_i32x4().packed_le(other.to_i32x4()) + } +} + +impl Default for I32x2 { + #[inline] + fn default() -> I32x2 { + I32x2(0) + } +} + +impl Index for I32x2 { + type Output = i32; + #[inline] + fn index(&self, index: usize) -> &i32 { + unsafe { &mem::transmute::<&u64, &[i32; 2]>(&self.0)[index] } + } +} + +impl IndexMut for I32x2 { + #[inline] + fn index_mut(&mut self, index: usize) -> &mut i32 { + unsafe { &mut mem::transmute::<&mut u64, &mut [i32; 2]>(&mut self.0)[index] } + } +} + +impl Add for I32x2 { + type Output = I32x2; + #[inline] + fn add(self, other: I32x2) -> I32x2 { + (self.to_i32x4() + other.to_i32x4()).xy() + } +} + +impl Sub for I32x2 { + type Output = I32x2; + #[inline] + fn sub(self, other: I32x2) -> I32x2 { + (self.to_i32x4() - other.to_i32x4()).xy() + } +} + +impl Mul for I32x2 { + type Output = I32x2; + #[inline] + fn mul(self, other: I32x2) -> I32x2 { + (self.to_i32x4() * other.to_i32x4()).xy() + } +} + +impl Debug for I32x2 { + #[inline] + fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { + write!(f, "<{}, {}>", self[0], self[1]) + } +} + +impl PartialEq for I32x2 { + #[inline] + fn eq(&self, other: &I32x2) -> bool { + self.packed_eq(*other).is_all_ones() + } +} + +// Four 32-bit signed integers #[derive(Clone, Copy)] pub struct I32x4(pub __m128i); @@ -245,6 +588,33 @@ impl I32x4 { unsafe { I32x4(x86_64::_mm_set1_epi32(x)) } } + // Extraction + + #[inline] + pub fn xy(self) -> I32x2 { + unsafe { I32x2(x86_64::_mm_cvtsi128_si64(self.0) as u64) } + } + + #[inline] + pub fn xw(self) -> I32x2 { + unsafe { I32x2(x86_64::_mm_cvtsi128_si64(self.xwyz().0) as u64) } + } + + #[inline] + pub fn yx(self) -> I32x2 { + unsafe { I32x2(x86_64::_mm_cvtsi128_si64(self.yxwz().0) as u64) } + } + + #[inline] + pub fn zy(self) -> I32x2 { + unsafe { I32x2(x86_64::_mm_cvtsi128_si64(self.zyxw().0) as u64) } + } + + #[inline] + pub fn zw(self) -> I32x2 { + unsafe { I32x2(x86_64::_mm_cvtsi128_si64(self.zwxy().0) as u64) } + } + // Concatenations #[inline] @@ -259,11 +629,6 @@ impl I32x4 { // Conversions - #[inline] - pub fn as_u8x16(self) -> U8x16 { - U8x16(self.0) - } - /// Converts these packed integers to floats. #[inline] pub fn to_f32x4(self) -> F32x4 { @@ -343,6 +708,30 @@ impl Mul for I32x4 { } } +impl BitAnd for I32x4 { + type Output = I32x4; + #[inline] + fn bitand(self, other: I32x4) -> I32x4 { + unsafe { I32x4(x86_64::_mm_and_si128(self.0, other.0)) } + } +} + +impl BitOr for I32x4 { + type Output = I32x4; + #[inline] + fn bitor(self, other: I32x4) -> I32x4 { + unsafe { I32x4(x86_64::_mm_or_si128(self.0, other.0)) } + } +} + +impl Shr for I32x4 { + type Output = I32x4; + #[inline] + fn shr(self, other: I32x4) -> I32x4 { + unsafe { I32x4(x86_64::_mm_srlv_epi32(self.0, other.0)) } + } +} + impl Debug for I32x4 { #[inline] fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { @@ -357,7 +746,24 @@ impl PartialEq for I32x4 { } } -// 32-bit unsigned integers +// Two 32-bit unsigned integers + +#[derive(Clone, Copy)] +pub struct U32x2(pub u64); + +impl U32x2 { + #[inline] + pub fn is_all_ones(self) -> bool { + self.0 == !0 + } + + #[inline] + pub fn is_all_zeroes(self) -> bool { + self.0 == 0 + } +} + +// Four 32-bit unsigned integers #[derive(Clone, Copy)] pub struct U32x4(pub __m128i); @@ -390,6 +796,13 @@ impl U32x4 { unsafe { x86_64::_mm_test_all_zeros(self.0, self.0) != 0 } } + // Extraction + + #[inline] + pub fn xy(self) -> U32x2 { + unsafe { U32x2(x86_64::_mm_cvtsi128_si64(self.0) as u64) } + } + // Packed comparisons #[inline] @@ -435,20 +848,3 @@ impl BitXor for U32x4 { unsafe { U32x4(x86_64::_mm_xor_si128(self.0, other.0)) } } } - -// 8-bit unsigned integers - -#[derive(Clone, Copy)] -pub struct U8x16(pub __m128i); - -impl U8x16 { - #[inline] - pub fn as_i32x4(self) -> I32x4 { - I32x4(self.0) - } - - #[inline] - pub fn shuffle(self, indices: U8x16) -> U8x16 { - unsafe { U8x16(x86_64::_mm_shuffle_epi8(self.0, indices.0)) } - } -} diff --git a/svg/src/lib.rs b/svg/src/lib.rs index 2d9b6454..11d1f4cf 100644 --- a/svg/src/lib.rs +++ b/svg/src/lib.rs @@ -17,10 +17,10 @@ use pathfinder_content::color::ColorU; use pathfinder_content::outline::Outline; use pathfinder_content::segment::{Segment, SegmentFlags}; use pathfinder_content::stroke::{LineCap, LineJoin, OutlineStrokeToFill, StrokeStyle}; -use pathfinder_content::transform::Transform2DFPathIter; +use pathfinder_content::transform::Transform2FPathIter; use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::rect::RectF; -use pathfinder_geometry::transform2d::Transform2DF; +use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::vector::Vector2F; use pathfinder_renderer::paint::Paint; use pathfinder_renderer::scene::{PathObject, Scene}; @@ -62,7 +62,7 @@ bitflags! { impl BuiltSVG { // TODO(pcwalton): Allow a global transform to be set. pub fn from_tree(tree: Tree) -> BuiltSVG { - let global_transform = Transform2DF::default(); + let global_transform = Transform2F::default(); let mut built_svg = BuiltSVG { scene: Scene::new(), @@ -87,9 +87,9 @@ impl BuiltSVG { built_svg } - fn process_node(&mut self, node: &Node, transform: &Transform2DF) { + fn process_node(&mut self, node: &Node, transform: &Transform2F) { 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() { NodeKind::Group(ref group) => { @@ -119,7 +119,7 @@ impl BuiltSVG { )); let path = UsvgPathToSegments::new(path.segments.iter().cloned()); - let path = Transform2DFPathIter::new(path, &transform); + let path = Transform2FPathIter::new(path, &transform); let outline = Outline::from_segments(path); let name = format!("Fill({})", node.id()); @@ -266,8 +266,8 @@ fn usvg_rect_to_euclid_rect(rect: &UsvgRect) -> RectF { ) } -fn usvg_transform_to_transform_2d(transform: &UsvgTransform) -> Transform2DF { - Transform2DF::row_major( +fn usvg_transform_to_transform_2d(transform: &UsvgTransform) -> Transform2F { + Transform2F::row_major( transform.a as f32, transform.b as f32, transform.c as f32, @@ -318,7 +318,7 @@ where } UsvgPathSegment::LineTo { x, y } => { let to = Vector2F::new(x as f32, y as f32); - let mut segment = Segment::line(&LineSegment2F::new(self.last_subpath_point, to)); + let mut segment = Segment::line(LineSegment2F::new(self.last_subpath_point, to)); if self.just_moved { segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH); } @@ -338,8 +338,8 @@ where let ctrl1 = Vector2F::new(x2 as f32, y2 as f32); let to = Vector2F::new(x as f32, y as f32); let mut segment = Segment::cubic( - &LineSegment2F::new(self.last_subpath_point, to), - &LineSegment2F::new(ctrl0, ctrl1), + LineSegment2F::new(self.last_subpath_point, to), + LineSegment2F::new(ctrl0, ctrl1), ); if self.just_moved { segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH); @@ -349,7 +349,7 @@ where Some(segment) } UsvgPathSegment::ClosePath => { - let mut segment = Segment::line(&LineSegment2F::new( + let mut segment = Segment::line(LineSegment2F::new( self.last_subpath_point, self.first_subpath_point, )); diff --git a/swf/src/shapes.rs b/swf/src/shapes.rs index ff0a7b02..6dbe6a0e 100644 --- a/swf/src/shapes.rs +++ b/swf/src/shapes.rs @@ -93,13 +93,13 @@ impl Shape { } #[inline] - fn first(&self) -> &LineSegment { - &self.outline.first().unwrap() + fn first(&self) -> LineSegment { + self.outline.first().unwrap() } #[inline] - fn last(&self) -> &LineSegment { - &self.outline.last().unwrap() + fn last(&self) -> LineSegment { + self.outline.last().unwrap() } #[inline] diff --git a/text/src/lib.rs b/text/src/lib.rs index 49dcd1ce..9de965a1 100644 --- a/text/src/lib.rs +++ b/text/src/lib.rs @@ -15,7 +15,7 @@ use font_kit::loader::Loader; use lyon_path::builder::{FlatPathBuilder, PathBuilder}; use pathfinder_content::outline::{Contour, Outline}; use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle}; -use pathfinder_geometry::transform2d::Transform2DF; +use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::vector::Vector2F; use pathfinder_renderer::paint::PaintId; use pathfinder_renderer::scene::{PathObject, Scene}; @@ -27,7 +27,7 @@ pub trait SceneExt { fn push_glyph(&mut self, font: &F, glyph_id: u32, - transform: &Transform2DF, + transform: &Transform2F, render_mode: TextRenderMode, hinting_options: HintingOptions, paint_id: PaintId) @@ -37,7 +37,7 @@ pub trait SceneExt { fn push_layout(&mut self, layout: &Layout, style: &TextStyle, - transform: &Transform2DF, + transform: &Transform2F, render_mode: TextRenderMode, hinting_options: HintingOptions, paint_id: PaintId) @@ -47,7 +47,7 @@ pub trait SceneExt { text: &str, style: &TextStyle, collection: &FontCollection, - transform: &Transform2DF, + transform: &Transform2F, render_mode: TextRenderMode, hinting_options: HintingOptions, paint_id: PaintId) @@ -59,7 +59,7 @@ impl SceneExt for Scene { fn push_glyph(&mut self, font: &F, glyph_id: u32, - transform: &Transform2DF, + transform: &Transform2F, render_mode: TextRenderMode, hinting_options: HintingOptions, paint_id: PaintId) @@ -82,7 +82,7 @@ impl SceneExt for Scene { fn push_layout(&mut self, layout: &Layout, style: &TextStyle, - transform: &Transform2DF, + transform: &Transform2F, render_mode: TextRenderMode, hinting_options: HintingOptions, paint_id: PaintId) @@ -93,8 +93,7 @@ impl SceneExt for Scene { // FIXME(pcwalton): Cache this! let scale = style.size / (font.metrics().units_per_em as f32); let scale = Vector2F::new(scale, -scale); - let transform = - Transform2DF::from_scale(scale).post_mul(transform).post_translate(offset); + let transform = *transform * Transform2F::from_scale(scale).translate(offset); self.push_glyph(font, glyph.glyph_id, &transform, @@ -110,7 +109,7 @@ impl SceneExt for Scene { text: &str, style: &TextStyle, collection: &FontCollection, - transform: &Transform2DF, + transform: &Transform2F, render_mode: TextRenderMode, hinting_options: HintingOptions, paint_id: PaintId) @@ -129,11 +128,11 @@ pub enum TextRenderMode { struct OutlinePathBuilder { outline: Outline, current_contour: Contour, - transform: Transform2DF, + transform: Transform2F, } impl OutlinePathBuilder { - fn new(transform: &Transform2DF) -> OutlinePathBuilder { + fn new(transform: &Transform2F) -> OutlinePathBuilder { OutlinePathBuilder { outline: Outline::new(), current_contour: Contour::new(), @@ -148,7 +147,7 @@ impl OutlinePathBuilder { } fn convert_point(&self, point: Point2D) -> Vector2F { - self.transform.transform_point(Vector2F::new(point.x, point.y)) + self.transform * Vector2F::new(point.x, point.y) } } diff --git a/ui/src/lib.rs b/ui/src/lib.rs index 7ae36ab3..377cb1ed 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -181,7 +181,7 @@ impl UIPresenter where D: Device { primitive, uniforms: &[ (&self.solid_program.framebuffer_size_uniform, - UniformData::Vec2(self.framebuffer_size.0.to_f32x4())), + UniformData::Vec2(self.framebuffer_size.0.to_f32x2())), (&self.solid_program.color_uniform, get_color_uniform(color)), ], textures: &[], @@ -414,11 +414,11 @@ impl UIPresenter where D: Device { textures: &[&texture], uniforms: &[ (&self.texture_program.framebuffer_size_uniform, - UniformData::Vec2(self.framebuffer_size.0.to_f32x4())), + UniformData::Vec2(self.framebuffer_size.0.to_f32x2())), (&self.texture_program.color_uniform, get_color_uniform(color)), (&self.texture_program.texture_uniform, UniformData::TextureUnit(0)), (&self.texture_program.texture_size_uniform, - UniformData::Vec2(device.texture_size(&texture).0.to_f32x4())) + UniformData::Vec2(device.texture_size(&texture).0.to_f32x2())) ], viewport: RectI::new(Vector2I::default(), self.framebuffer_size), options: RenderOptions { diff --git a/examples/svg2pdf/Cargo.toml b/utils/convert/Cargo.toml similarity index 80% rename from examples/svg2pdf/Cargo.toml rename to utils/convert/Cargo.toml index e656de8b..5d972ff5 100644 --- a/examples/svg2pdf/Cargo.toml +++ b/utils/convert/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "svg2pdf" +name = "convert" version = "0.1.0" authors = ["Sebastian Köln "] edition = "2018" @@ -7,6 +7,6 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +pathfinder_export = { path = "../../export" } pathfinder_svg = { path = "../../svg" } -pathfinder_pdf = { path = "../../pdf" } usvg = "*" diff --git a/utils/convert/src/main.rs b/utils/convert/src/main.rs new file mode 100644 index 00000000..63726a4c --- /dev/null +++ b/utils/convert/src/main.rs @@ -0,0 +1,27 @@ +use std::fs::File; +use std::io::{Read, BufWriter}; +use std::error::Error; +use std::path::PathBuf; +use pathfinder_svg::BuiltSVG; +use pathfinder_export::{Export, FileFormat}; +use usvg::{Tree, Options}; + +fn main() -> Result<(), Box> { + let mut args = std::env::args_os().skip(1); + let input = PathBuf::from(args.next().expect("no input given")); + let output = PathBuf::from(args.next().expect("no output given")); + + let mut data = Vec::new(); + File::open(input)?.read_to_end(&mut data)?; + let svg = BuiltSVG::from_tree(Tree::from_data(&data, &Options::default()).unwrap()); + + let scene = &svg.scene; + let mut writer = BufWriter::new(File::create(&output)?); + let format = match output.extension().and_then(|s| s.to_str()) { + Some("pdf") => FileFormat::PDF, + Some("ps") => FileFormat::PS, + _ => return Err("output filename must have .ps or .pdf extension".into()) + }; + scene.export(&mut writer, format).unwrap(); + Ok(()) +}