This commit is contained in:
Sebastian Köln 2019-07-16 09:43:37 +03:00
commit 6acab74fae
81 changed files with 3768 additions and 1155 deletions

1
.gitignore vendored
View File

@ -8,6 +8,7 @@ node_modules
/examples/c_canvas_minimal/build /examples/c_canvas_minimal/build
/shaders/build /shaders/build
/c/build /c/build
/examples/macos_app/Pathfinder\ Example.xcodeproj/project.xcworkspace/xcuserdata/*
# Editors # Editors
*.swp *.swp

39
Cargo.lock generated
View File

@ -357,6 +357,15 @@ name = "constant_time_eq"
version = "0.1.3" version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.6.4" version = "0.6.4"
@ -1446,6 +1455,7 @@ dependencies = [
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "metal 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pathfinder_content 0.1.0", "pathfinder_content 0.1.0",
"pathfinder_export 0.1.0",
"pathfinder_geometry 0.3.0", "pathfinder_geometry 0.3.0",
"pathfinder_gl 0.1.0", "pathfinder_gl 0.1.0",
"pathfinder_gpu 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)", "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]] [[package]]
name = "pathfinder_geometry" name = "pathfinder_geometry"
version = "0.3.0" version = "0.3.0"
@ -1537,16 +1557,6 @@ dependencies = [
"pathfinder_simd 0.3.0", "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]] [[package]]
name = "pathfinder_renderer" name = "pathfinder_renderer"
version = "0.1.0" version = "0.1.0"
@ -2127,15 +2137,6 @@ dependencies = [
"usvg 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "svgdom" name = "svgdom"
version = "0.17.0" version = "0.17.0"

View File

@ -13,13 +13,12 @@ members = [
"examples/canvas_moire", "examples/canvas_moire",
"examples/canvas_text", "examples/canvas_text",
"examples/lottie_basic", "examples/lottie_basic",
"examples/svg2pdf",
"examples/swf_basic", "examples/swf_basic",
"geometry", "geometry",
"gl", "gl",
"gpu", "gpu",
"lottie", "lottie",
"pdf", "export",
"metal", "metal",
"renderer", "renderer",
"simd", "simd",
@ -29,5 +28,6 @@ members = [
"ui", "ui",
"utils/area-lut", "utils/area-lut",
"utils/gamma-lut", "utils/gamma-lut",
"utils/svg-to-skia" "utils/svg-to-skia",
"utils/convert",
] ]

View File

@ -3,7 +3,7 @@
# Pathfinder 3 # Pathfinder 3
Pathfinder 3 is a fast, practical, GPU-based rasterizer for fonts and vector graphics using OpenGL 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. 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 * 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 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. have no loops and minimal branching.
* Advanced font rendering. Pathfinder can render fonts with slight hinting and can perform subpixel * 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 [`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. 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 ## Authors
The primary author is Patrick Walton (@pcwalton), with contributions from the Servo development The primary author is Patrick Walton (@pcwalton), with contributions from the Servo development

View File

@ -2,18 +2,53 @@ language = "C"
header = """\ header = """\
/* Generated code. Do not edit; instead run `cargo build` in `pathfinder_c`. */ /* Generated code. Do not edit; instead run `cargo build` in `pathfinder_c`. */
#ifndef PF_PATHFINDER_H
#define PF_PATHFINDER_H
#ifdef __APPLE__
#include <QuartzCore/QuartzCore.h>
#endif
#ifdef __cplusplus
extern \"C\" { extern \"C\" {
#endif
"""
trailer = """\
#ifdef __cplusplus
}
#endif
#endif
""" """
trailer = "}"
include_guard = "PF_PATHFINDER_H"
include_version = true include_version = true
[parse] [parse]
parse_deps = true parse_deps = true
include = [ include = [
"font-kit",
"pathfinder_canvas", "pathfinder_canvas",
"pathfinder_content", "pathfinder_content",
"pathfinder_geometry", "pathfinder_geometry",
"pathfinder_gl", "pathfinder_gl",
"pathfinder_gpu",
"pathfinder_metal",
"pathfinder_renderer", "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"

View File

@ -19,6 +19,8 @@ use pathfinder_content::color::{ColorF, ColorU};
use pathfinder_content::outline::ArcDirection; use pathfinder_content::outline::ArcDirection;
use pathfinder_content::stroke::LineCap; use pathfinder_content::stroke::LineCap;
use pathfinder_geometry::rect::{RectF, RectI}; 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_geometry::vector::{Vector2F, Vector2I};
use pathfinder_gl::{GLDevice, GLVersion}; use pathfinder_gl::{GLDevice, GLVersion};
use pathfinder_gpu::resources::{FilesystemResourceLoader, ResourceLoader}; 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::concurrent::scene_proxy::SceneProxy;
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions}; use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions};
use pathfinder_renderer::gpu::renderer::Renderer; use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::BuildOptions; use pathfinder_renderer::options::{BuildOptions, RenderTransform};
use pathfinder_renderer::scene::Scene; use pathfinder_renderer::scene::Scene;
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::F32x4;
use std::ffi::CString; 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_CW: u8 = 0;
pub const PF_ARC_DIRECTION_CCW: u8 = 1; 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` // `renderer`
pub const PF_RENDERER_OPTIONS_FLAGS_HAS_BACKGROUND_COLOR: u8 = 0x1; pub const PF_RENDERER_OPTIONS_FLAGS_HAS_BACKGROUND_COLOR: u8 = 0x1;
@ -120,10 +127,35 @@ pub struct PFRectI {
pub origin: PFVector2I, pub origin: PFVector2I,
pub lower_right: 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` // `gl`
pub type PFGLDeviceRef = *mut GLDevice; 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) pub type PFGLFunctionLoader = extern "C" fn(name: *const c_char, userdata: *mut c_void)
-> *const c_void; -> *const c_void;
// `gpu` // `gpu`
@ -135,7 +167,8 @@ pub type PFMetalDestFramebufferRef = *mut DestFramebuffer<MetalDevice>;
pub type PFMetalRendererRef = *mut Renderer<MetalDevice>; pub type PFMetalRendererRef = *mut Renderer<MetalDevice>;
// FIXME(pcwalton): Double-boxing is unfortunate. Remove this when `std::raw::TraitObject` is // FIXME(pcwalton): Double-boxing is unfortunate. Remove this when `std::raw::TraitObject` is
// stable? // stable?
pub type PFResourceLoaderRef = *mut Box<dyn ResourceLoader>; pub type PFResourceLoaderRef = *mut ResourceLoaderWrapper;
pub struct ResourceLoaderWrapper(Box<dyn ResourceLoader>);
// `metal` // `metal`
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] #[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
@ -150,20 +183,18 @@ pub struct PFRendererOptions {
pub flags: PFRendererOptionsFlags, pub flags: PFRendererOptionsFlags,
} }
pub type PFRendererOptionsFlags = u8; pub type PFRendererOptionsFlags = u8;
// TODO(pcwalton) pub type PFBuildOptionsRef = *mut BuildOptions;
#[repr(C)] pub type PFRenderTransformRef = *mut RenderTransform;
pub struct PFBuildOptions {
pub placeholder: u32,
}
// `canvas` // `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] #[no_mangle]
pub unsafe extern "C" fn PFCanvasCreate(font_context: PFCanvasFontContextRef, pub unsafe extern "C" fn PFCanvasCreate(font_context: PFCanvasFontContextRef,
size: *const PFVector2F) size: *const PFVector2F)
-> PFCanvasRef { -> 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()))) (*size).to_rust())))
} }
@ -188,17 +219,18 @@ pub unsafe extern "C" fn PFCanvasFontContextCreateWithFonts(fonts: *const FKHand
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn PFCanvasFontContextDestroy(font_context: PFCanvasFontContextRef) { pub unsafe extern "C" fn PFCanvasFontContextAddRef(font_context: PFCanvasFontContextRef)
drop(Box::from_raw(font_context)) -> PFCanvasFontContextRef {
}
#[no_mangle]
pub unsafe extern "C" fn PFCanvasFontContextClone(font_context: PFCanvasFontContextRef)
-> PFCanvasFontContextRef {
Box::into_raw(Box::new((*font_context).clone())) 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] #[no_mangle]
pub unsafe extern "C" fn PFCanvasCreateScene(canvas: PFCanvasRef) -> PFSceneRef { pub unsafe extern "C" fn PFCanvasCreateScene(canvas: PFCanvasRef) -> PFSceneRef {
Box::into_raw(Box::new(Box::from_raw(canvas).into_scene())) 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) (*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] #[no_mangle]
pub unsafe extern "C" fn PFCanvasFillPath(canvas: PFCanvasRef, path: PFPathRef) { pub unsafe extern "C" fn PFCanvasFillPath(canvas: PFCanvasRef, path: PFPathRef) {
(*canvas).fill_path(*Box::from_raw(path)) (*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] #[no_mangle]
pub unsafe extern "C" fn PFCanvasStrokePath(canvas: PFCanvasRef, path: PFPathRef) { pub unsafe extern "C" fn PFCanvasStrokePath(canvas: PFCanvasRef, path: PFPathRef) {
(*canvas).stroke_path(*Box::from_raw(path)) (*canvas).stroke_path(*Box::from_raw(path))
@ -421,7 +455,7 @@ pub unsafe extern "C" fn PFFillStyleDestroy(fill_style: PFFillStyleRef) {
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn PFFilesystemResourceLoaderLocate() -> PFResourceLoaderRef { pub unsafe extern "C" fn PFFilesystemResourceLoaderLocate() -> PFResourceLoaderRef {
let loader = Box::new(FilesystemResourceLoader::locate()); let loader = Box::new(FilesystemResourceLoader::locate());
Box::into_raw(Box::new(loader as Box<dyn ResourceLoader>)) Box::into_raw(Box::new(ResourceLoaderWrapper(loader as Box<dyn ResourceLoader>)))
} }
#[no_mangle] #[no_mangle]
@ -435,6 +469,7 @@ pub unsafe extern "C" fn PFGLLoadWith(loader: PFGLFunctionLoader, userdata: *mut
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn PFGLDeviceCreate(version: PFGLVersion, default_framebuffer: u32) pub unsafe extern "C" fn PFGLDeviceCreate(version: PFGLVersion, default_framebuffer: u32)
-> PFGLDeviceRef { -> PFGLDeviceRef {
let version = match version { PF_GL_VERSION_GLES3 => GLVersion::GLES3, _ => GLVersion::GL3 };
Box::into_raw(Box::new(GLDevice::new(version, default_framebuffer))) 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)) 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] #[no_mangle]
pub unsafe extern "C" fn PFGLRendererCreate(device: PFGLDeviceRef, pub unsafe extern "C" fn PFGLRendererCreate(device: PFGLDeviceRef,
resources: PFResourceLoaderRef, resources: PFResourceLoaderRef,
@ -469,7 +506,7 @@ pub unsafe extern "C" fn PFGLRendererCreate(device: PFGLDeviceRef,
options: *const PFRendererOptions) options: *const PFRendererOptions)
-> PFGLRendererRef { -> PFGLRendererRef {
Box::into_raw(Box::new(Renderer::new(*Box::from_raw(device), Box::into_raw(Box::new(Renderer::new(*Box::from_raw(device),
&**resources, &*((*resources).0),
*Box::from_raw(dest_framebuffer), *Box::from_raw(dest_framebuffer),
(*options).to_rust()))) (*options).to_rust())))
} }
@ -498,7 +535,9 @@ pub unsafe extern "C" fn PFMetalDestFramebufferDestroy(dest_framebuffer:
drop(Box::from_raw(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")))] #[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn PFMetalRendererCreate(device: PFMetalDeviceRef, pub unsafe extern "C" fn PFMetalRendererCreate(device: PFMetalDeviceRef,
@ -507,7 +546,7 @@ pub unsafe extern "C" fn PFMetalRendererCreate(device: PFMetalDeviceRef,
options: *const PFRendererOptions) options: *const PFRendererOptions)
-> PFMetalRendererRef { -> PFMetalRendererRef {
Box::into_raw(Box::new(Renderer::new(*Box::from_raw(device), Box::into_raw(Box::new(Renderer::new(*Box::from_raw(device),
&**resources, &*((*resources).0),
*Box::from_raw(dest_framebuffer), *Box::from_raw(dest_framebuffer),
(*options).to_rust()))) (*options).to_rust())))
} }
@ -518,25 +557,33 @@ pub unsafe extern "C" fn PFMetalRendererDestroy(renderer: PFMetalRendererRef) {
drop(Box::from_raw(renderer)) 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")))] #[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn PFMetalRendererGetDevice(renderer: PFMetalRendererRef) -> PFMetalDeviceRef { pub unsafe extern "C" fn PFMetalRendererGetDevice(renderer: PFMetalRendererRef)
-> PFMetalDeviceRef {
&mut (*renderer).device &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] #[no_mangle]
pub unsafe extern "C" fn PFSceneProxyBuildAndRenderGL(scene_proxy: PFSceneProxyRef, pub unsafe extern "C" fn PFSceneProxyBuildAndRenderGL(scene_proxy: PFSceneProxyRef,
renderer: PFGLRendererRef, renderer: PFGLRendererRef,
build_options: *const PFBuildOptions) { build_options: PFBuildOptionsRef) {
(*scene_proxy).build_and_render(&mut *renderer, (*build_options).to_rust()) (*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")))] #[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn PFSceneProxyBuildAndRenderMetal(scene_proxy: PFSceneProxyRef, pub unsafe extern "C" fn PFSceneProxyBuildAndRenderMetal(scene_proxy: PFSceneProxyRef,
renderer: PFMetalRendererRef, renderer: PFMetalRendererRef,
build_options: *const PFBuildOptions) { build_options: PFBuildOptionsRef) {
(*scene_proxy).build_and_render(&mut *renderer, (*build_options).to_rust()) (*scene_proxy).build_and_render(&mut *renderer, (*build_options).clone())
} }
// `metal` // `metal`
@ -554,8 +601,60 @@ pub unsafe extern "C" fn PFMetalDeviceDestroy(device: PFMetalDeviceRef) {
drop(Box::from_raw(device)) 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` // `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] #[no_mangle]
pub unsafe extern "C" fn PFSceneDestroy(scene: PFSceneRef) { pub unsafe extern "C" fn PFSceneDestroy(scene: PFSceneRef) {
drop(Box::from_raw(scene)) 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` // Helpers for `renderer`
impl PFRendererOptions { impl PFRendererOptions {
@ -651,9 +784,3 @@ impl PFRendererOptions {
} }
} }
} }
impl PFBuildOptions {
pub fn to_rust(&self) -> BuildOptions {
BuildOptions::default()
}
}

View File

@ -25,7 +25,7 @@ use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle};
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::vector::Vector2F; use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2DF; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_renderer::paint::{Paint, PaintId}; use pathfinder_renderer::paint::{Paint, PaintId};
use pathfinder_renderer::scene::{PathObject, Scene}; use pathfinder_renderer::scene::{PathObject, Scene};
use pathfinder_text::{SceneExt, TextRenderMode}; use pathfinder_text::{SceneExt, TextRenderMode};
@ -125,8 +125,7 @@ impl CanvasRenderingContext2D {
TextAlign::Center => position.set_x(position.x() - layout.width() * 0.5), TextAlign::Center => position.set_x(position.x() - layout.width() * 0.5),
} }
let transform = Transform2DF::from_translation(position).post_mul(&self.current_state let transform = self.current_state.transform * Transform2F::from_translation(position);
.transform);
// TODO(pcwalton): Report errors. // TODO(pcwalton): Report errors.
drop(self.scene.push_layout(&layout, drop(self.scene.push_layout(&layout,
@ -296,7 +295,7 @@ impl CanvasRenderingContext2D {
let paint_id = self.scene.push_paint(&paint); let paint_id = self.scene.push_paint(&paint);
let mut outline = outline.clone(); 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())) self.scene.push_path(PathObject::new(outline, paint_id, String::new()))
} }
@ -306,18 +305,18 @@ impl CanvasRenderingContext2D {
// Transformations // Transformations
#[inline] #[inline]
pub fn current_transform(&self) -> Transform2DF { pub fn current_transform(&self) -> Transform2F {
self.current_state.transform self.current_state.transform
} }
#[inline] #[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; self.current_state.transform = *new_transform;
} }
#[inline] #[inline]
pub fn reset_transform(&mut self) { pub fn reset_transform(&mut self) {
self.current_state.transform = Transform2DF::default(); self.current_state.transform = Transform2F::default();
} }
// Compositing // Compositing
@ -349,7 +348,7 @@ impl CanvasRenderingContext2D {
#[derive(Clone)] #[derive(Clone)]
struct State { struct State {
transform: Transform2DF, transform: Transform2F,
font_collection: Arc<FontCollection>, font_collection: Arc<FontCollection>,
font_size: f32, font_size: f32,
line_width: f32, line_width: f32,
@ -369,7 +368,7 @@ struct State {
impl State { impl State {
fn default(default_font_collection: Arc<FontCollection>) -> State { fn default(default_font_collection: Arc<FontCollection>) -> State {
State { State {
transform: Transform2DF::default(), transform: Transform2F::default(),
font_collection: default_font_collection, font_collection: default_font_collection,
font_size: DEFAULT_FONT_SIZE, font_size: DEFAULT_FONT_SIZE,
line_width: 1.0, line_width: 1.0,
@ -452,8 +451,7 @@ impl Path2D {
start_angle: f32, start_angle: f32,
end_angle: f32, end_angle: f32,
direction: ArcDirection) { direction: ArcDirection) {
let mut transform = Transform2DF::from_scale(Vector2F::splat(radius)); let transform = Transform2F::from_scale(Vector2F::splat(radius)).translate(center);
transform = transform.post_mul(&Transform2DF::from_translation(center));
self.current_contour.push_arc(&transform, start_angle, end_angle, direction); self.current_contour.push_arc(&transform, start_angle, end_angle, direction);
} }
@ -467,8 +465,7 @@ impl Path2D {
let bisector = vu0 + vu1; let bisector = vu0 + vu1;
let center = ctrl + bisector.scale(hypot / bisector.length()); let center = ctrl + bisector.scale(hypot / bisector.length());
let mut transform = Transform2DF::from_scale(Vector2F::splat(radius)); let transform = Transform2F::from_scale(Vector2F::splat(radius)).translate(center);
transform = transform.post_mul(&Transform2DF::from_translation(center));
let chord = LineSegment2F::new(vu0.yx().scale_xy(Vector2F::new(-1.0, 1.0)), let chord = LineSegment2F::new(vu0.yx().scale_xy(Vector2F::new(-1.0, 1.0)),
vu1.yx().scale_xy(Vector2F::new(1.0, -1.0))); vu1.yx().scale_xy(Vector2F::new(1.0, -1.0)));
@ -494,9 +491,7 @@ impl Path2D {
end_angle: f32) { end_angle: f32) {
self.flush_current_contour(); self.flush_current_contour();
let mut transform = Transform2DF::from_rotation(rotation); let transform = Transform2F::from_scale(axes).rotate(rotation).translate(center);
transform = transform.post_mul(&Transform2DF::from_scale(axes));
transform = transform.post_mul(&Transform2DF::from_translation(center));
self.current_contour.push_arc(&transform, start_angle, end_angle, ArcDirection::CW); self.current_contour.push_arc(&transform, start_angle, end_angle, ArcDirection::CW);
if end_angle - start_angle >= 2.0 * PI { if end_angle - start_angle >= 2.0 * PI {

View File

@ -24,15 +24,15 @@ struct Edge(LineSegment2F);
impl TEdge for Edge { impl TEdge for Edge {
#[inline] #[inline]
fn point_is_inside(&self, point: &Vector2F) -> bool { fn point_is_inside(&self, point: Vector2F) -> bool {
let area = (self.0.to() - self.0.from()).det(*point - self.0.from()); let area = (self.0.to() - self.0.from()).det(point - self.0.from());
debug!("point_is_inside({:?}, {:?}), area={}", self, point, area); debug!("point_is_inside({:?}, {:?}), area={}", self, point, area);
area >= 0.0 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(); 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 { if t >= 0.0 && t <= 1.0 {
results.push(t); results.push(t);
} }
@ -51,7 +51,7 @@ enum AxisAlignedEdge {
impl TEdge for AxisAlignedEdge { impl TEdge for AxisAlignedEdge {
#[inline] #[inline]
fn point_is_inside(&self, point: &Vector2F) -> bool { fn point_is_inside(&self, point: Vector2F) -> bool {
match *self { match *self {
AxisAlignedEdge::Left(x) => point.x() >= x, AxisAlignedEdge::Left(x) => point.x() >= x,
AxisAlignedEdge::Top(y) => point.y() >= y, 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 mut results = ArrayVec::new();
let t = match *self { let t = match *self {
AxisAlignedEdge::Left(x) | AxisAlignedEdge::Right(x) => segment.solve_t_for_x(x), AxisAlignedEdge::Left(x) | AxisAlignedEdge::Right(x) => segment.solve_t_for_x(x),
@ -74,26 +74,26 @@ impl TEdge for AxisAlignedEdge {
} }
trait TEdge: Debug { trait TEdge: Debug {
fn point_is_inside(&self, point: &Vector2F) -> bool; fn point_is_inside(&self, point: Vector2F) -> bool;
fn intersect_line_segment(&self, segment: &LineSegment2F) -> ArrayVec<[f32; 3]>; fn intersect_line_segment(&self, segment: LineSegment2F) -> ArrayVec<[f32; 3]>;
fn trivially_test_segment(&self, segment: &Segment) -> EdgeRelativeLocation { 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!( debug!(
"point {:?} inside {:?}: {:?}", "point {:?} inside {:?}: {:?}",
segment.baseline.from(), segment.baseline.from(),
self, self,
from_inside 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; return EdgeRelativeLocation::Intersecting;
} }
if !segment.is_line() { 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; return EdgeRelativeLocation::Intersecting;
} }
if !segment.is_quadratic() { 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; return EdgeRelativeLocation::Intersecting;
} }
} }
@ -107,7 +107,7 @@ trait TEdge: Debug {
fn intersect_segment(&self, segment: &Segment) -> ArrayVec<[f32; 3]> { fn intersect_segment(&self, segment: &Segment) -> ArrayVec<[f32; 3]> {
if segment.is_line() { if segment.is_line() {
return self.intersect_line_segment(&segment.baseline); return self.intersect_line_segment(segment.baseline);
} }
let mut segment = *segment; let mut segment = *segment;
@ -173,10 +173,10 @@ trait TEdge: Debug {
} }
fn intersects_cubic_segment_hull(&self, cubic_segment: CubicSegment) -> bool { fn intersects_cubic_segment_hull(&self, cubic_segment: CubicSegment) -> bool {
let inside = self.point_is_inside(&cubic_segment.0.baseline.from()); 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.from())
|| inside != self.point_is_inside(&cubic_segment.0.ctrl.to()) || inside != self.point_is_inside(cubic_segment.0.ctrl.to())
|| inside != self.point_is_inside(&cubic_segment.0.baseline.to()) || inside != self.point_is_inside(cubic_segment.0.baseline.to())
} }
} }
@ -222,7 +222,7 @@ where
// We have a potential intersection. // We have a potential intersection.
debug!("potential intersection: {:?} edge: {:?}", segment, edge); 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 intersection_ts = edge.intersect_segment(&segment);
let mut last_t = 0.0; let mut last_t = 0.0;
debug!("... intersections: {:?}", intersection_ts); debug!("... intersections: {:?}", intersection_ts);

View File

@ -16,7 +16,7 @@ use crate::orientation::Orientation;
use crate::segment::{Segment, SegmentFlags, SegmentKind}; use crate::segment::{Segment, SegmentFlags, SegmentKind};
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2DF; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::transform3d::Perspective; use pathfinder_geometry::transform3d::Perspective;
use pathfinder_geometry::unit_vector::UnitVector; use pathfinder_geometry::unit_vector::UnitVector;
use pathfinder_geometry::vector::Vector2F; use pathfinder_geometry::vector::Vector2F;
@ -134,7 +134,7 @@ impl Outline {
self.contours.push(contour); self.contours.push(contour);
} }
pub fn transform(&mut self, transform: &Transform2DF) { pub fn transform(&mut self, transform: &Transform2F) {
if transform.is_identity() { if transform.is_identity() {
return; return;
} }
@ -353,7 +353,7 @@ impl Contour {
} }
pub fn push_arc(&mut self, pub fn push_arc(&mut self,
transform: &Transform2DF, transform: &Transform2F,
start_angle: f32, start_angle: f32,
end_angle: f32, end_angle: f32,
direction: ArcDirection) { direction: ArcDirection) {
@ -367,13 +367,13 @@ impl Contour {
} }
pub fn push_arc_from_unit_chord(&mut self, pub fn push_arc_from_unit_chord(&mut self,
transform: &Transform2DF, transform: &Transform2F,
mut chord: LineSegment2F, mut chord: LineSegment2F,
direction: ArcDirection) { direction: ArcDirection) {
let mut direction_transform = Transform2DF::default(); let mut direction_transform = Transform2F::default();
if direction == ArcDirection::CCW { if direction == ArcDirection::CCW {
chord = chord.scale_xy(Vector2F::new(-1.0, 1.0)); 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())); 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 half_sweep_vector = sweep_vector.halve_angle();
let rotation = Transform2DF::from_rotation_vector(half_sweep_vector.rotate_by(vector)); let rotation = Transform2F::from_rotation_vector(half_sweep_vector.rotate_by(vector));
segment = segment.transform(&direction_transform.post_mul(&rotation) segment = segment.transform(&(*transform * rotation * direction_transform));
.post_mul(&transform));
let mut push_segment_flags = PushSegmentFlags::UPDATE_BOUNDS; let mut push_segment_flags = PushSegmentFlags::UPDATE_BOUNDS;
if first_segment { if first_segment {
@ -413,19 +412,19 @@ impl Contour {
const EPSILON: f32 = 0.001; 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 segment = Segment::quarter_circle_arc();
let mut rotation; let mut rotation;
self.push_segment(&segment.transform(transform), self.push_segment(&segment.transform(transform),
PushSegmentFlags::UPDATE_BOUNDS | PushSegmentFlags::INCLUDE_FROM_POINT); PushSegmentFlags::UPDATE_BOUNDS | PushSegmentFlags::INCLUDE_FROM_POINT);
rotation = Transform2DF::from_rotation_vector(UnitVector(Vector2F::new( 0.0, 1.0))); rotation = Transform2F::from_rotation_vector(UnitVector(Vector2F::new( 0.0, 1.0)));
self.push_segment(&segment.transform(&rotation.post_mul(&transform)), self.push_segment(&segment.transform(&(*transform * rotation)),
PushSegmentFlags::UPDATE_BOUNDS); PushSegmentFlags::UPDATE_BOUNDS);
rotation = Transform2DF::from_rotation_vector(UnitVector(Vector2F::new(-1.0, 0.0))); rotation = Transform2F::from_rotation_vector(UnitVector(Vector2F::new(-1.0, 0.0)));
self.push_segment(&segment.transform(&rotation.post_mul(&transform)), self.push_segment(&segment.transform(&(*transform * rotation)),
PushSegmentFlags::UPDATE_BOUNDS); PushSegmentFlags::UPDATE_BOUNDS);
rotation = Transform2DF::from_rotation_vector(UnitVector(Vector2F::new( 0.0, -1.0))); rotation = Transform2F::from_rotation_vector(UnitVector(Vector2F::new( 0.0, -1.0)));
self.push_segment(&segment.transform(&rotation.post_mul(&transform)), self.push_segment(&segment.transform(&(*transform * rotation)),
PushSegmentFlags::UPDATE_BOUNDS); PushSegmentFlags::UPDATE_BOUNDS);
} }
@ -434,25 +433,25 @@ impl Contour {
debug_assert!(self.point_is_endpoint(point_index)); debug_assert!(self.point_is_endpoint(point_index));
let mut segment = Segment::none(); 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); let point1_index = self.add_to_point_index(point_index, 1);
if self.point_is_endpoint(point1_index) { 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; segment.kind = SegmentKind::Line;
} else { } 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); let point2_index = self.add_to_point_index(point_index, 2);
if self.point_is_endpoint(point2_index) { 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; segment.kind = SegmentKind::Quadratic;
} else { } else {
segment.ctrl.set_to(&self.position_of(point2_index)); segment.ctrl.set_to(self.position_of(point2_index));
segment.kind = SegmentKind::Cubic; segment.kind = SegmentKind::Cubic;
let point3_index = self.add_to_point_index(point_index, 3); 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() { if transform.is_identity() {
return; return;
} }
for (point_index, point) in self.points.iter_mut().enumerate() { 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); union_rect(&mut self.bounds, *point, point_index == 0);
} }
} }
pub fn apply_perspective(&mut self, perspective: &Perspective) { pub fn apply_perspective(&mut self, perspective: &Perspective) {
for (point_index, point) in self.points.iter_mut().enumerate() { 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); union_rect(&mut self.bounds, *point, point_index == 0);
} }
} }
@ -610,14 +609,14 @@ impl Contour {
let ctrl_position = &contour.points[ctrl_point_index]; let ctrl_position = &contour.points[ctrl_point_index];
handle_cubic( handle_cubic(
self, self,
&Segment::quadratic(&baseline, *ctrl_position).to_cubic(), &Segment::quadratic(baseline, *ctrl_position).to_cubic(),
); );
} else if point_count == 4 { } else if point_count == 4 {
let first_ctrl_point_index = last_endpoint_index as usize + 1; 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_0 = &contour.points[first_ctrl_point_index + 0];
let ctrl_position_1 = &contour.points[first_ctrl_point_index + 1]; let ctrl_position_1 = &contour.points[first_ctrl_point_index + 1];
let ctrl = LineSegment2F::new(*ctrl_position_0, *ctrl_position_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( self.push_point(
@ -802,21 +801,21 @@ impl<'a> Iterator for ContourIter<'a> {
if self.index == contour.len() { if self.index == contour.len() {
let point1 = contour.position_of(0); let point1 = contour.position_of(0);
self.index += 1; self.index += 1;
return Some(Segment::line(&LineSegment2F::new(point0, point1))); return Some(Segment::line(LineSegment2F::new(point0, point1)));
} }
let point1_index = self.index; let point1_index = self.index;
self.index += 1; self.index += 1;
let point1 = contour.position_of(point1_index); let point1 = contour.position_of(point1_index);
if contour.point_is_endpoint(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_index = self.index;
let point2 = contour.position_of(point2_index); let point2 = contour.position_of(point2_index);
self.index += 1; self.index += 1;
if contour.point_is_endpoint(point2_index) { 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; let point3_index = self.index;
@ -824,8 +823,8 @@ impl<'a> Iterator for ContourIter<'a> {
self.index += 1; self.index += 1;
debug_assert!(contour.point_is_endpoint(point3_index)); debug_assert!(contour.point_is_endpoint(point3_index));
return Some(Segment::cubic( return Some(Segment::cubic(
&LineSegment2F::new(point0, point3), LineSegment2F::new(point0, point3),
&LineSegment2F::new(point1, point2), LineSegment2F::new(point1, point2),
)); ));
} }
} }

View File

@ -11,7 +11,7 @@
//! Line or curve segments, optimized with SIMD. //! Line or curve segments, optimized with SIMD.
use pathfinder_geometry::line_segment::LineSegment2F; 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::util::{self, EPSILON};
use pathfinder_geometry::vector::Vector2F; use pathfinder_geometry::vector::Vector2F;
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::F32x4;
@ -39,9 +39,9 @@ impl Segment {
} }
#[inline] #[inline]
pub fn line(line: &LineSegment2F) -> Segment { pub fn line(line: LineSegment2F) -> Segment {
Segment { Segment {
baseline: *line, baseline: line,
ctrl: LineSegment2F::default(), ctrl: LineSegment2F::default(),
kind: SegmentKind::Line, kind: SegmentKind::Line,
flags: SegmentFlags::empty(), flags: SegmentFlags::empty(),
@ -49,9 +49,9 @@ impl Segment {
} }
#[inline] #[inline]
pub fn quadratic(baseline: &LineSegment2F, ctrl: Vector2F) -> Segment { pub fn quadratic(baseline: LineSegment2F, ctrl: Vector2F) -> Segment {
Segment { Segment {
baseline: *baseline, baseline,
ctrl: LineSegment2F::new(ctrl, Vector2F::default()), ctrl: LineSegment2F::new(ctrl, Vector2F::default()),
kind: SegmentKind::Quadratic, kind: SegmentKind::Quadratic,
flags: SegmentFlags::empty(), flags: SegmentFlags::empty(),
@ -59,10 +59,10 @@ impl Segment {
} }
#[inline] #[inline]
pub fn cubic(baseline: &LineSegment2F, ctrl: &LineSegment2F) -> Segment { pub fn cubic(baseline: LineSegment2F, ctrl: LineSegment2F) -> Segment {
Segment { Segment {
baseline: *baseline, baseline,
ctrl: *ctrl, ctrl,
kind: SegmentKind::Cubic, kind: SegmentKind::Cubic,
flags: SegmentFlags::empty(), flags: SegmentFlags::empty(),
} }
@ -91,7 +91,7 @@ impl Segment {
let (p0x, p0y) = (p3p0.z(), p3p0.w()); let (p0x, p0y) = (p3p0.z(), p3p0.w());
let (p1x, p1y) = (4.0 - p0x, (1.0 - p0x) * (3.0 - p0x) / p0y); 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); 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] #[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 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 flip = Vector2F::new(1.0, -1.0);
let (p2, p3) = (p1.scale_xy(flip), p0.scale_xy(flip)); 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] #[inline]
@ -198,7 +198,7 @@ impl Segment {
// FIXME(pcwalton): Don't degree elevate! // FIXME(pcwalton): Don't degree elevate!
if self.is_line() { if self.is_line() {
let (before, after) = self.as_line_segment().split(t); let (before, after) = self.as_line_segment().split(t);
(Segment::line(&before), Segment::line(&after)) (Segment::line(before), Segment::line(after))
} else { } else {
self.to_cubic().as_cubic_segment().split(t) self.to_cubic().as_cubic_segment().split(t)
} }
@ -215,10 +215,10 @@ impl Segment {
} }
#[inline] #[inline]
pub fn transform(self, transform: &Transform2DF) -> Segment { pub fn transform(self, transform: &Transform2F) -> Segment {
Segment { Segment {
baseline: transform.transform_line_segment(&self.baseline), baseline: *transform * self.baseline,
ctrl: transform.transform_line_segment(&self.ctrl), ctrl: *transform * self.ctrl,
kind: self.kind, kind: self.kind,
flags: self.flags, flags: self.flags,
} }

View File

@ -14,7 +14,7 @@ use crate::outline::{ArcDirection, Contour, Outline, PushSegmentFlags};
use crate::segment::Segment; use crate::segment::Segment;
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2DF; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::Vector2F; use pathfinder_geometry::vector::Vector2F;
use std::f32; use std::f32;
@ -104,7 +104,7 @@ impl<'a> OutlineStrokeToFill<'a> {
stroker.output.add_join(self.style.line_width * 0.5, stroker.output.add_join(self.style.line_width * 0.5,
self.style.line_join, self.style.line_join,
stroker.input.position_of(0), stroker.input.position_of(0),
&final_segment); final_segment);
} }
stroker.output.closed = true; stroker.output.closed = true;
@ -138,9 +138,8 @@ impl<'a> OutlineStrokeToFill<'a> {
LineCap::Round => { LineCap::Round => {
let scale = Vector2F::splat(width * 0.5); let scale = Vector2F::splat(width * 0.5);
let offset = gradient.yx().scale_xy(Vector2F::new(-1.0, 1.0)); let offset = gradient.yx().scale_xy(Vector2F::new(-1.0, 1.0));
let mut transform = Transform2DF::from_scale(scale);
let translation = p1 + offset.scale(width * 0.5); 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); let chord = LineSegment2F::new(-offset, offset);
contour.push_arc_from_unit_chord(&transform, chord, ArcDirection::CW); contour.push_arc_from_unit_chord(&transform, chord, ArcDirection::CW);
} }
@ -235,7 +234,7 @@ impl Offset for Segment {
self.ctrl.from() 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. // Push segment.
@ -245,7 +244,7 @@ impl Offset for Segment {
fn offset_once(&self, distance: f32) -> Segment { fn offset_once(&self, distance: f32) -> Segment {
if self.is_line() { if self.is_line() {
return Segment::line(&self.baseline.offset(distance)); return Segment::line(self.baseline.offset(distance));
} }
if self.is_quadratic() { if self.is_quadratic() {
@ -253,12 +252,12 @@ impl Offset for Segment {
let mut segment_1 = LineSegment2F::new(self.ctrl.from(), self.baseline.to()); let mut segment_1 = LineSegment2F::new(self.ctrl.from(), self.baseline.to());
segment_0 = segment_0.offset(distance); segment_0 = segment_0.offset(distance);
segment_1 = segment_1.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), Some(t) => segment_0.sample(t),
None => segment_0.to().lerp(segment_1.from(), 0.5), None => segment_0.to().lerp(segment_1.from(), 0.5),
}; };
let baseline = LineSegment2F::new(segment_0.from(), segment_1.to()); 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()); 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()); let mut segment_1 = LineSegment2F::new(self.ctrl.to(), self.baseline.to());
segment_0 = segment_0.offset(distance); segment_0 = segment_0.offset(distance);
segment_1 = segment_1.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), Some(t) => segment_0.sample(t),
None => segment_0.to().lerp(segment_1.from(), 0.5), None => segment_0.to().lerp(segment_1.from(), 0.5),
}; };
let baseline = LineSegment2F::new(segment_0.from(), segment_1.to()); let baseline = LineSegment2F::new(segment_0.from(), segment_1.to());
let ctrl = LineSegment2F::new(segment_0.from(), ctrl); 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() { 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()); let mut segment_1 = LineSegment2F::new(self.ctrl.from(), self.baseline.to());
segment_0 = segment_0.offset(distance); segment_0 = segment_0.offset(distance);
segment_1 = segment_1.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), Some(t) => segment_0.sample(t),
None => segment_0.to().lerp(segment_1.from(), 0.5), None => segment_0.to().lerp(segment_1.from(), 0.5),
}; };
let baseline = LineSegment2F::new(segment_0.from(), segment_1.to()); let baseline = LineSegment2F::new(segment_0.from(), segment_1.to());
let ctrl = LineSegment2F::new(ctrl, 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()); 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_1 = segment_1.offset(distance);
segment_2 = segment_2.offset(distance); segment_2 = segment_2.offset(distance);
let (ctrl_0, ctrl_1) = match ( let (ctrl_0, ctrl_1) = match (
segment_0.intersection_t(&segment_1), segment_0.intersection_t(segment_1),
segment_1.intersection_t(&segment_2), segment_1.intersection_t(segment_2),
) { ) {
(Some(t0), Some(t1)) => (segment_0.sample(t0), segment_1.sample(t1)), (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 baseline = LineSegment2F::new(segment_0.from(), segment_2.to());
let ctrl = LineSegment2F::new(ctrl_0, ctrl_1); 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 { fn error_is_within_tolerance(&self, other: &Segment, distance: f32) -> bool {
@ -357,14 +356,14 @@ impl Contour {
distance: f32, distance: f32,
join: LineJoin, join: LineJoin,
join_point: Vector2F, join_point: Vector2F,
next_tangent: &LineSegment2F) { next_tangent: LineSegment2F) {
let (p0, p1) = (self.position_of_last(2), self.position_of_last(1)); let (p0, p1) = (self.position_of_last(2), self.position_of_last(1));
let prev_tangent = LineSegment2F::new(p0, p1); let prev_tangent = LineSegment2F::new(p0, p1);
match join { match join {
LineJoin::Bevel => {} LineJoin::Bevel => {}
LineJoin::Miter(miter_limit) => { 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 miter_endpoint = prev_tangent.sample(prev_tangent_t);
let threshold = miter_limit * distance; let threshold = miter_limit * distance;
if (miter_endpoint - join_point).square_length() <= threshold * threshold { if (miter_endpoint - join_point).square_length() <= threshold * threshold {
@ -374,8 +373,7 @@ impl Contour {
} }
LineJoin::Round => { LineJoin::Round => {
let scale = Vector2F::splat(distance.abs()); let scale = Vector2F::splat(distance.abs());
let mut transform = Transform2DF::from_scale(scale); let transform = Transform2F::from_scale(scale).translate(join_point);
transform = transform.post_mul(&Transform2DF::from_translation(join_point));
let chord_from = (prev_tangent.to() - join_point).normalize(); let chord_from = (prev_tangent.to() - join_point).normalize();
let chord_to = (next_tangent.to() - join_point).normalize(); let chord_to = (next_tangent.to() - join_point).normalize();
let chord = LineSegment2F::new(chord_from, chord_to); let chord = LineSegment2F::new(chord_from, chord_to);

View File

@ -11,19 +11,19 @@
//! Utilities for transforming paths. //! Utilities for transforming paths.
use crate::segment::Segment; use crate::segment::Segment;
use pathfinder_geometry::transform2d::Transform2DF; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::transform3d::Perspective; use pathfinder_geometry::transform3d::Perspective;
/// Transforms a path with a SIMD 2D transform. /// Transforms a path with a SIMD 2D transform.
pub struct Transform2DFPathIter<I> pub struct Transform2FPathIter<I>
where where
I: Iterator<Item = Segment>, I: Iterator<Item = Segment>,
{ {
iter: I, iter: I,
transform: Transform2DF, transform: Transform2F,
} }
impl<I> Iterator for Transform2DFPathIter<I> impl<I> Iterator for Transform2FPathIter<I>
where where
I: Iterator<Item = Segment>, I: Iterator<Item = Segment>,
{ {
@ -34,20 +34,12 @@ where
// TODO(pcwalton): Can we go faster by transforming an entire line segment with SIMD? // TODO(pcwalton): Can we go faster by transforming an entire line segment with SIMD?
let mut segment = self.iter.next()?; let mut segment = self.iter.next()?;
if !segment.is_none() { if !segment.is_none() {
segment segment.baseline.set_from(self.transform * segment.baseline.from());
.baseline segment.baseline.set_to(self.transform * segment.baseline.to());
.set_from(&self.transform.transform_point(segment.baseline.from()));
segment
.baseline
.set_to(&self.transform.transform_point(segment.baseline.to()));
if !segment.is_line() { if !segment.is_line() {
segment segment.ctrl.set_from(self.transform * segment.ctrl.from());
.ctrl
.set_from(&self.transform.transform_point(segment.ctrl.from()));
if !segment.is_quadratic() { if !segment.is_quadratic() {
segment segment.ctrl.set_to(self.transform * segment.ctrl.to());
.ctrl
.set_to(&self.transform.transform_point(segment.ctrl.to()));
} }
} }
} }
@ -55,13 +47,13 @@ where
} }
} }
impl<I> Transform2DFPathIter<I> impl<I> Transform2FPathIter<I>
where where
I: Iterator<Item = Segment>, I: Iterator<Item = Segment>,
{ {
#[inline] #[inline]
pub fn new(iter: I, transform: &Transform2DF) -> Transform2DFPathIter<I> { pub fn new(iter: I, transform: &Transform2F) -> Transform2FPathIter<I> {
Transform2DFPathIter { Transform2FPathIter {
iter, iter,
transform: *transform, transform: *transform,
} }
@ -87,22 +79,12 @@ where
fn next(&mut self) -> Option<Segment> { fn next(&mut self) -> Option<Segment> {
let mut segment = self.iter.next()?; let mut segment = self.iter.next()?;
if !segment.is_none() { if !segment.is_none() {
segment.baseline.set_from( segment.baseline.set_from(self.perspective * segment.baseline.from());
&self segment.baseline.set_to(self.perspective * segment.baseline.to());
.perspective
.transform_point_2d(&segment.baseline.from()),
);
segment
.baseline
.set_to(&self.perspective.transform_point_2d(&segment.baseline.to()));
if !segment.is_line() { if !segment.is_line() {
segment segment.ctrl.set_from(self.perspective * segment.ctrl.from());
.ctrl
.set_from(&self.perspective.transform_point_2d(&segment.ctrl.from()));
if !segment.is_quadratic() { if !segment.is_quadratic() {
segment segment.ctrl.set_to(self.perspective * segment.ctrl.to());
.ctrl
.set_to(&self.perspective.transform_point_2d(&segment.ctrl.to()));
} }
} }
} }

View File

@ -25,6 +25,9 @@ features = ["release_max_level_warn"]
[dependencies.pathfinder_content] [dependencies.pathfinder_content]
path = "../../content" path = "../../content"
[dependencies.pathfinder_export]
path = "../../export"
[dependencies.pathfinder_geometry] [dependencies.pathfinder_geometry]
path = "../../geometry" path = "../../geometry"

View File

@ -14,10 +14,10 @@
// proper. // proper.
use crate::window::{OcularTransform, View}; 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::rect::RectF;
use pathfinder_geometry::transform2d::Transform2DF; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::transform3d::{Perspective, Transform3DF}; use pathfinder_geometry::transform3d::{Perspective, Transform4F};
use std::f32::consts::FRAC_PI_4; use std::f32::consts::FRAC_PI_4;
const NEAR_CLIP_PLANE: f32 = 0.01; 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; const DEFAULT_EYE_OFFSET: f32 = 0.025;
pub enum Camera { pub enum Camera {
TwoD(Transform2DF), TwoD(Transform2F),
ThreeD { ThreeD {
// The ocular transform used for rendering of the scene to the scene framebuffer. If we are // 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 // 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 let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32
* scale_factor_for_view_box(view_box); * scale_factor_for_view_box(view_box);
let origin = viewport_size.to_f32().scale(0.5) - view_box.size().scale(scale * 0.5); let origin = viewport_size.to_f32().scale(0.5) - view_box.size().scale(scale * 0.5);
Camera::TwoD(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 { 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 fov_y = FRAC_PI_4;
let aspect = viewport_size.x() as f32 / viewport_size.y() as f32; let aspect = viewport_size.x() as f32 / viewport_size.y() as f32;
let projection = 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); let perspective = Perspective::new(&projection, viewport_size);
// Create a scene transform by moving the camera back from the center of the eyes so that // 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. // 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 { let scene_transform = OcularTransform {
perspective, 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. // For now, initialize the eye transforms as copies of the scene transform.
@ -85,9 +85,10 @@ impl Camera {
} else { } else {
-eye_offset -eye_offset
}; };
let this_eye_offset = Vector4F::new(this_eye_offset, 0.0, 0.0, 1.0);
OcularTransform { OcularTransform {
perspective, perspective,
modelview_to_eye: Transform3DF::from_translation(this_eye_offset, 0.0, 0.0), modelview_to_eye: Transform4F::from_translation(this_eye_offset),
} }
}) })
.collect(); .collect();
@ -145,25 +146,17 @@ impl CameraTransform3D {
pub fn offset(&mut self, vector: Vector4F) -> bool { pub fn offset(&mut self, vector: Vector4F) -> bool {
let update = !vector.is_zero(); let update = !vector.is_zero();
if update { if update {
let rotation = Transform3DF::from_rotation(-self.yaw, -self.pitch, 0.0); let rotation = Transform4F::from_rotation(-self.yaw, -self.pitch, 0.0);
self.position = self.position + rotation.transform_point(vector); self.position = self.position + rotation * vector;
} }
update update
} }
pub fn to_transform(&self) -> Transform3DF { pub fn to_transform(&self) -> Transform4F {
let mut transform = Transform3DF::from_rotation(self.yaw, self.pitch, 0.0); let flip = Vector4F::new(1.0, -1.0, 1.0, 1.0);
transform = transform.post_mul(&Transform3DF::from_uniform_scale(2.0 * self.scale)); Transform4F::from_scale(flip).translate(-self.position)
transform = transform.post_mul(&Transform3DF::from_translation( .uniform_scale(2.0 * self.scale)
-self.position.x(), .rotate(self.yaw, self.pitch, 0.0)
-self.position.y(),
-self.position.z(),
));
// Flip Y.
transform = transform.post_mul(&Transform3DF::from_scale(1.0, -1.0, 1.0));
transform
} }
} }

View File

@ -22,11 +22,12 @@ use crate::device::{GroundProgram, GroundVertexArray};
use crate::ui::{DemoUIModel, DemoUIPresenter, ScreenshotInfo, ScreenshotType, UIAction}; use crate::ui::{DemoUIModel, DemoUIPresenter, ScreenshotInfo, ScreenshotType, UIAction};
use crate::window::{Event, Keycode, SVGPath, Window, WindowSize}; use crate::window::{Event, Keycode, SVGPath, Window, WindowSize};
use clap::{App, Arg}; 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_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::resources::ResourceLoader;
use pathfinder_gpu::Device; use pathfinder_gpu::Device;
use pathfinder_renderer::concurrent::scene_proxy::{RenderCommandStream, SceneProxy}; use pathfinder_renderer::concurrent::scene_proxy::{RenderCommandStream, SceneProxy};
@ -38,7 +39,7 @@ use pathfinder_renderer::scene::Scene;
use pathfinder_svg::BuiltSVG; use pathfinder_svg::BuiltSVG;
use pathfinder_ui::{MousePosition, UIEvent}; use pathfinder_ui::{MousePosition, UIEvent};
use std::fs::File; use std::fs::File;
use std::io::{Read, Write}; use std::io::{BufWriter, Read};
use std::path::PathBuf; use std::path::PathBuf;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
@ -255,10 +256,9 @@ impl<W> DemoApp<W> where W: Window {
if modelview_transform.offset(*velocity) { if modelview_transform.offset(*velocity) {
self.dirty = true; self.dirty = true;
} }
let perspective = scene_transform let perspective = scene_transform.perspective *
.perspective scene_transform.modelview_to_eye *
.post_mul(&scene_transform.modelview_to_eye) modelview_transform.to_transform();
.post_mul(&modelview_transform.to_transform());
Some(RenderTransform::Perspective(perspective)) Some(RenderTransform::Perspective(perspective))
} }
Camera::TwoD(transform) => Some(RenderTransform::Transform2D(transform)), Camera::TwoD(transform) => Some(RenderTransform::Transform2D(transform)),
@ -327,10 +327,10 @@ impl<W> DemoApp<W> where W: Window {
if let Camera::TwoD(ref mut transform) = self.camera { if let Camera::TwoD(ref mut transform) = self.camera {
let backing_scale_factor = self.window_size.backing_scale_factor; let backing_scale_factor = self.window_size.backing_scale_factor;
let position = position.to_f32().scale(backing_scale_factor); let position = position.to_f32().scale(backing_scale_factor);
*transform = transform.post_translate(-position);
let scale_delta = 1.0 + d_dist * CAMERA_SCALE_SPEED_2D; let scale_delta = 1.0 + d_dist * CAMERA_SCALE_SPEED_2D;
*transform = transform.post_scale(Vector2F::splat(scale_delta)); *transform = transform.translate(-position)
*transform = transform.post_translate(position); .uniform_scale(scale_delta)
.translate(position);
} }
} }
Event::Look { pitch, yaw } => { Event::Look { pitch, yaw } => {
@ -355,13 +355,21 @@ impl<W> DemoApp<W> where W: Window {
*scene_transform = eye_transforms[0]; *scene_transform = eye_transforms[0];
for (index, eye_transform) in eye_transforms.iter().enumerate().skip(1) { for (index, eye_transform) in eye_transforms.iter().enumerate().skip(1) {
let weight = 1.0 / (index + 1) as f32; let weight = 1.0 / (index + 1) as f32;
scene_transform.perspective.transform = scene_transform.perspective.transform.lerp(weight, &eye_transform.perspective.transform); scene_transform.perspective.transform =
scene_transform.modelview_to_eye = scene_transform.modelview_to_eye.lerp(weight, &eye_transform.modelview_to_eye); 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? // TODO: calculate the eye offset from the eye transforms?
let z_offset = -DEFAULT_EYE_OFFSET * scene_transform.perspective.transform.c0.x(); let z_offset = -DEFAULT_EYE_OFFSET *
scene_transform.modelview_to_eye = scene_transform.modelview_to_eye scene_transform.perspective.transform.c0.x();
.pre_mul(&Transform3DF::from_translation(0.0, 0.0, z_offset)); 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')) => { Event::KeyDown(Keycode::Alphanumeric(b'w')) => {
@ -550,7 +558,8 @@ impl<W> DemoApp<W> where W: Window {
} }
Some(ScreenshotInfo { kind: ScreenshotType::SVG, path }) => { Some(ScreenshotInfo { kind: ScreenshotType::SVG, path }) => {
// FIXME(pcwalton): This won't work on Android. // 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<W> DemoApp<W> where W: Window {
} }
UIEvent::MouseDragged(position) => { UIEvent::MouseDragged(position) => {
if let Camera::TwoD(ref mut transform) = self.camera { if let Camera::TwoD(ref mut transform) = self.camera {
*transform = transform.post_translate(position.relative.to_f32()); *transform = transform.translate(position.relative.to_f32());
} }
} }
_ => {} _ => {}
@ -598,10 +607,7 @@ impl<W> DemoApp<W> where W: Window {
if let Camera::TwoD(ref mut transform) = self.camera { if let Camera::TwoD(ref mut transform) = self.camera {
let scale = Vector2F::splat(1.0 + CAMERA_ZOOM_AMOUNT_2D); let scale = Vector2F::splat(1.0 + CAMERA_ZOOM_AMOUNT_2D);
let center = center_of_window(&self.window_size); let center = center_of_window(&self.window_size);
*transform = transform *transform = transform.translate(-center).scale(scale).translate(center);
.post_translate(-center)
.post_scale(scale)
.post_translate(center);
self.dirty = true; self.dirty = true;
} }
} }
@ -609,16 +615,13 @@ impl<W> DemoApp<W> where W: Window {
if let Camera::TwoD(ref mut transform) = self.camera { if let Camera::TwoD(ref mut transform) = self.camera {
let scale = Vector2F::splat(1.0 - CAMERA_ZOOM_AMOUNT_2D); let scale = Vector2F::splat(1.0 - CAMERA_ZOOM_AMOUNT_2D);
let center = center_of_window(&self.window_size); let center = center_of_window(&self.window_size);
*transform = transform *transform = transform.translate(-center).scale(scale).translate(center);
.post_translate(-center)
.post_scale(scale)
.post_translate(center);
self.dirty = true; self.dirty = true;
} }
} }
UIAction::ZoomActualSize => { UIAction::ZoomActualSize => {
if let Camera::TwoD(ref mut transform) = self.camera { if let Camera::TwoD(ref mut transform) = self.camera {
*transform = Transform2DF::default(); *transform = Transform2F::default();
self.dirty = true; self.dirty = true;
} }
} }
@ -626,10 +629,9 @@ impl<W> DemoApp<W> where W: Window {
if let Camera::TwoD(ref mut transform) = self.camera { if let Camera::TwoD(ref mut transform) = self.camera {
let old_rotation = transform.rotation(); let old_rotation = transform.rotation();
let center = center_of_window(&self.window_size); let center = center_of_window(&self.window_size);
*transform = transform *transform = transform.translate(-center)
.post_translate(-center) .rotate(*theta - old_rotation)
.post_rotate(*theta - old_rotation) .translate(center);
.post_translate(center);
} }
} }
} }

View File

@ -18,8 +18,8 @@ use pathfinder_content::color::{ColorF, ColorU};
use pathfinder_gpu::{ClearOps, DepthFunc, DepthState, Device, Primitive, RenderOptions}; use pathfinder_gpu::{ClearOps, DepthFunc, DepthState, Device, Primitive, RenderOptions};
use pathfinder_gpu::{RenderState, RenderTarget, TextureData, TextureFormat, UniformData}; use pathfinder_gpu::{RenderState, RenderTarget, TextureData, TextureFormat, UniformData};
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform3d::Transform3DF; use pathfinder_geometry::transform3d::Transform4F;
use pathfinder_geometry::vector::Vector2I; use pathfinder_geometry::vector::{Vector2I, Vector4F};
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions}; use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions};
use pathfinder_renderer::gpu::renderer::RenderMode; use pathfinder_renderer::gpu::renderer::RenderMode;
use pathfinder_renderer::gpu_data::RenderCommand; use pathfinder_renderer::gpu_data::RenderCommand;
@ -163,24 +163,20 @@ impl<W> DemoApp<W> where W: Window {
let scene_framebuffer = self.scene_framebuffer.as_ref().unwrap(); let scene_framebuffer = self.scene_framebuffer.as_ref().unwrap();
let scene_texture = self.renderer.device.framebuffer_texture(scene_framebuffer); let scene_texture = self.renderer.device.framebuffer_texture(scene_framebuffer);
let quad_scale_transform = Transform3DF::from_scale( let mut quad_scale = self.scene_metadata.view_box.size().to_3d();
self.scene_metadata.view_box.size().x(), quad_scale.set_z(1.0);
self.scene_metadata.view_box.size().y(), let quad_scale_transform = Transform4F::from_scale(quad_scale);
1.0,
);
let scene_transform_matrix = scene_transform let scene_transform_matrix = scene_transform.perspective *
.perspective scene_transform.modelview_to_eye *
.post_mul(&scene_transform.modelview_to_eye) modelview_transform.to_transform() *
.post_mul(&modelview_transform.to_transform()) quad_scale_transform;
.post_mul(&quad_scale_transform);
let eye_transform = &eye_transforms[render_scene_index as usize]; let eye_transform = &eye_transforms[render_scene_index as usize];
let eye_transform_matrix = eye_transform let eye_transform_matrix = eye_transform.perspective *
.perspective eye_transform.modelview_to_eye *
.post_mul(&eye_transform.modelview_to_eye) modelview_transform.to_transform() *
.post_mul(&modelview_transform.to_transform()) quad_scale_transform;
.post_mul(&quad_scale_transform);
debug!( debug!(
"eye transform({}).modelview_to_eye={:?}", "eye transform({}).modelview_to_eye={:?}",
@ -214,17 +210,14 @@ impl<W> DemoApp<W> where W: Window {
let ground_scale = self.scene_metadata.view_box.max_x() * 2.0; let ground_scale = self.scene_metadata.view_box.max_x() * 2.0;
let mut base_transform = perspective.transform; let mut offset = self.scene_metadata.view_box.lower_right().to_3d();
base_transform = base_transform.post_mul(&Transform3DF::from_translation( offset.set_z(ground_scale);
-0.5 * self.scene_metadata.view_box.max_x(), offset = offset * Vector4F::new(-0.5, 1.0, -0.5, 1.0);
self.scene_metadata.view_box.max_y(), let base_transform = perspective.transform * Transform4F::from_translation(offset);
-0.5 * ground_scale,
));
// Fill ground. // Fill ground.
let mut transform = base_transform; let transform = base_transform *
transform = Transform4F::from_scale(Vector4F::new(ground_scale, 1.0, ground_scale, 1.0));
transform.post_mul(&Transform3DF::from_scale(ground_scale, 1.0, ground_scale));
// Don't clear the first scene after drawing it. // Don't clear the first scene after drawing it.
let clear_color = if render_scene_index == 0 { let clear_color = if render_scene_index == 0 {

View File

@ -12,7 +12,7 @@
use pathfinder_geometry::vector::Vector2I; use pathfinder_geometry::vector::Vector2I;
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform3d::{Perspective, Transform3DF}; use pathfinder_geometry::transform3d::{Perspective, Transform4F};
use pathfinder_gpu::resources::ResourceLoader; use pathfinder_gpu::resources::ResourceLoader;
use rayon::ThreadPoolBuilder; use rayon::ThreadPoolBuilder;
use std::path::PathBuf; use std::path::PathBuf;
@ -109,7 +109,7 @@ pub struct OcularTransform {
pub perspective: Perspective, pub perspective: Perspective,
// The view transform which converts from world coordinates to camera coordinates // The view transform which converts from world coordinates to camera coordinates
pub modelview_to_eye: Transform3DF, pub modelview_to_eye: Transform4F,
} }
#[derive(Clone)] #[derive(Clone)]

View File

@ -13,7 +13,7 @@ use std::io;
use pathfinder_geometry::point::Point2DI32; use pathfinder_geometry::point::Point2DI32;
use pathfinder_geometry::rect::RectI32; use pathfinder_geometry::rect::RectI32;
use pathfinder_geometry::transform3d::Perspective; use pathfinder_geometry::transform3d::Perspective;
use pathfinder_geometry::transform3d::Transform3DF32; use pathfinder_geometry::transform3d::Transform4F32;
use pathfinder_gl::GLVersion; use pathfinder_gl::GLVersion;
use pathfinder_gpu::resources::ResourceLoader; use pathfinder_gpu::resources::ResourceLoader;
@ -36,7 +36,7 @@ pub trait DisplayCamera {
type Error: DisplayError; type Error: DisplayError;
fn bounds(&self) -> RectI32; fn bounds(&self) -> RectI32;
fn view(&self) -> Transform3DF32; fn view(&self) -> Transform4F32;
fn perspective(&self) -> Perspective; fn perspective(&self) -> Perspective;
fn make_current(&mut self) -> Result<(), Self::Error>; fn make_current(&mut self) -> Result<(), Self::Error>;

View File

@ -27,7 +27,7 @@ use crate::display::DisplayError;
use pathfinder_geometry::point::Point2DI32; use pathfinder_geometry::point::Point2DI32;
use pathfinder_geometry::rect::RectI32; use pathfinder_geometry::rect::RectI32;
use pathfinder_geometry::transform3d::Transform3DF32; use pathfinder_geometry::transform3d::Transform4F32;
use pathfinder_geometry::transform3d::Perspective; use pathfinder_geometry::transform3d::Perspective;
use pathfinder_gl::GLVersion; use pathfinder_gl::GLVersion;
use pathfinder_gpu::resources::FilesystemResourceLoader; use pathfinder_gpu::resources::FilesystemResourceLoader;
@ -149,15 +149,15 @@ impl DisplayCamera for GlWindowCamera {
// TODO: add eye offsets // TODO: add eye offsets
let bounds = self.bounds(); let bounds = self.bounds();
let aspect = bounds.size().x() as f32 / bounds.size().y() as f32; 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()) Perspective::new(&transform, bounds.size())
} }
fn view(&self) -> Transform3DF32 { fn view(&self) -> Transform4F32 {
let duration = Instant::now() - self.start; let duration = Instant::now() - self.start;
let rotation = duration.as_millis() as f32 / 1000.0; let rotation = duration.as_millis() as f32 / 1000.0;
Transform3DF32::from_rotation(rotation, 0.0, 0.0) Transform4F32::from_rotation(rotation, 0.0, 0.0)
.pre_mul(&Transform3DF32::from_translation(0.0, 0.0, -CAMERA_DISTANCE)) .pre_mul(&Transform4F32::from_translation(0.0, 0.0, -CAMERA_DISTANCE))
} }
} }

View File

@ -19,8 +19,8 @@ use pathfinder_geometry::point::Point2DI32;
use pathfinder_geometry::point::Point2DF32; use pathfinder_geometry::point::Point2DF32;
use pathfinder_geometry::point::Point3DF32; use pathfinder_geometry::point::Point3DF32;
use pathfinder_geometry::rect::RectI32; use pathfinder_geometry::rect::RectI32;
use pathfinder_geometry::transform2d::Transform2DF32; use pathfinder_geometry::transform2d::Transform2F32;
use pathfinder_geometry::transform3d::Transform3DF32; use pathfinder_geometry::transform3d::Transform4F32;
use pathfinder_geometry::transform3d::Perspective; use pathfinder_geometry::transform3d::Perspective;
use pathfinder_gpu::Device; use pathfinder_gpu::Device;
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::F32x4;
@ -41,7 +41,7 @@ pub struct ImmersiveDemo<D> {
renderer: Renderer<GLDevice>, renderer: Renderer<GLDevice>,
scene_thread_proxy: SceneThreadProxy, scene_thread_proxy: SceneThreadProxy,
svg_size: Point2DF32, svg_size: Point2DF32,
svg_to_world: Option<Transform3DF32>, svg_to_world: Option<Transform4F32>,
} }
static DEFAULT_SVG_VIRTUAL_PATH: &'static str = "svg/Ghostscript_Tiger.svg"; static DEFAULT_SVG_VIRTUAL_PATH: &'static str = "svg/Ghostscript_Tiger.svg";
@ -87,13 +87,13 @@ impl<D: Display> ImmersiveDemo<D> {
let svg_size = self.svg_size; let svg_size = self.svg_size;
let svg_to_world = self.svg_to_world.get_or_insert_with(|| { 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_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_width = svg_size.x() * svg_to_world_scale;
let svg_height = svg_size.y() * svg_to_world_scale; let svg_height = svg_size.y() * svg_to_world_scale;
Transform3DF32::from_uniform_scale(svg_to_world_scale) Transform4F32::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(&Transform4F32::from_translation(-svg_width / 2.0, -svg_height / 2.0, -DEFAULT_SVG_DISTANCE))
.pre_mul(&Transform3DF32::from_scale(1.0, -1.0, 1.0)) .pre_mul(&Transform4F32::from_scale(1.0, -1.0, 1.0))
.pre_mul(&view.inverse()) .pre_mul(&view.inverse())
}); });

View File

@ -32,7 +32,7 @@ use pathfinder_demo::window::SVGPath;
use pathfinder_geometry::vector::Vector2F; use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::vector::Vector2I; use pathfinder_geometry::vector::Vector2I;
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform2d::Transform2DF; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_content::color::ColorF; use pathfinder_content::color::ColorF;
use pathfinder_gl::GLDevice; use pathfinder_gl::GLDevice;
use pathfinder_gl::GLVersion; 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 / let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32 /
f32::max(svg.scene.bounds().size().x(), svg.scene.bounds().size().y()); f32::max(svg.scene.bounds().size().x(), svg.scene.bounds().size().y());
let transform = Transform2DF::from_translation(svg.scene.bounds().size().scale(-0.5)) let transform = Transform2F::from_translation(svg.scene.bounds().size().scale(-0.5))
.post_mul(&Transform2DF::from_scale(Vector2F::splat(scale))) .post_mul(&Transform2F::from_scale(Vector2F::splat(scale)))
.post_mul(&Transform2DF::from_translation(viewport_size.to_f32().scale(0.5))); .post_mul(&Transform2F::from_translation(viewport_size.to_f32().scale(0.5)));
let render_options = RenderOptions { let render_options = RenderOptions {
transform: RenderTransform::Transform2D(transform), transform: RenderTransform::Transform2D(transform),

View File

@ -53,7 +53,7 @@ use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform3d::Perspective; use pathfinder_geometry::transform3d::Perspective;
use pathfinder_geometry::transform3d::Transform3DF; use pathfinder_geometry::transform3d::Transform4F;
use pathfinder_geometry::util; use pathfinder_geometry::util;
use pathfinder_gl::GLVersion; use pathfinder_gl::GLVersion;
use pathfinder_gpu::resources::FilesystemResourceLoader; use pathfinder_gpu::resources::FilesystemResourceLoader;
@ -77,7 +77,7 @@ pub struct MagicLeapWindow {
graphics_client: MLHandle, graphics_client: MLHandle,
size: Vector2I, size: Vector2I,
virtual_camera_array: MLGraphicsVirtualCameraInfoArray, virtual_camera_array: MLGraphicsVirtualCameraInfoArray,
initial_camera_transform: Option<Transform3DF>, initial_camera_transform: Option<Transform4F>,
frame_handle: MLHandle, frame_handle: MLHandle,
resource_loader: FilesystemResourceLoader, resource_loader: FilesystemResourceLoader,
pose_event: Option<Vec<OcularTransform>>, pose_event: Option<Vec<OcularTransform>>,
@ -234,21 +234,21 @@ impl MagicLeapWindow {
} }
let virtual_camera_array = &self.virtual_camera_array; let virtual_camera_array = &self.virtual_camera_array;
let initial_camera = self.initial_camera_transform.get_or_insert_with(|| { 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; let mut camera = virtual_camera_array.virtual_cameras[0].transform;
for i in 1..virtual_camera_array.num_virtual_cameras { for i in 1..virtual_camera_array.num_virtual_cameras {
let next = virtual_camera_array.virtual_cameras[i as usize].transform; let next = virtual_camera_array.virtual_cameras[i as usize].transform;
camera = camera.lerp(next, 1.0 / (i as f32 + 1.0)); 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) let camera_transforms = (0..virtual_camera_array.num_virtual_cameras)
.map(|i| { .map(|i| {
let camera = &virtual_camera_array.virtual_cameras[i as usize]; 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 size = RectI::from(virtual_camera_array.viewport).size();
let perspective = Perspective::new(&projection, 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 } OcularTransform { perspective, modelview_to_eye }
}) })
.collect(); .collect();
@ -355,16 +355,16 @@ impl MLTransform {
// Impl pathfinder traits for c-api types // Impl pathfinder traits for c-api types
impl From<MLTransform> for Transform3DF { impl From<MLTransform> for Transform4F {
fn from(mat: MLTransform) -> Self { fn from(mat: MLTransform) -> Self {
Transform3DF::from(mat.rotation) Transform4F::from(mat.rotation)
.pre_mul(&Transform3DF::from(mat.position)) .pre_mul(&Transform4F::from(mat.position))
} }
} }
impl From<MLVec3f> for Transform3DF { impl From<MLVec3f> for Transform4F {
fn from(v: MLVec3f) -> Self { 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<MLRectf> for RectI {
} }
} }
impl From<MLQuaternionf> for Transform3DF { impl From<MLQuaternionf> for Transform4F {
fn from(q: MLQuaternionf) -> Self { 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<MLMat4f> for Transform3DF { impl From<MLMat4f> for Transform4F {
fn from(mat: MLMat4f) -> Self { fn from(mat: MLMat4f) -> Self {
let a = mat.matrix_colmajor; 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[1], a[5], a[9], a[13],
a[2], a[6], a[10], a[14], a[2], a[6], a[10], a[14],
a[3], a[7], a[11], a[15]) a[3], a[7], a[11], a[15])

View File

@ -87,7 +87,7 @@ int main(int argc, const char **argv) {
// Render the canvas to screen. // Render the canvas to screen.
PFSceneRef scene = PFCanvasCreateScene(canvas); PFSceneRef scene = PFCanvasCreateScene(canvas);
PFSceneProxyRef scene_proxy = PFSceneProxyCreateFromSceneAndRayonExecutor(scene); PFSceneProxyRef scene_proxy = PFSceneProxyCreateFromSceneAndRayonExecutor(scene);
PFSceneProxyBuildAndRenderGL(scene_proxy, renderer, &(PFBuildOptions){0}); PFSceneProxyBuildAndRenderGL(scene_proxy, renderer, PFBuildOptionsCreate());
SDL_GL_SwapWindow(window); SDL_GL_SwapWindow(window);
// Wait for a keypress. // Wait for a keypress.

View File

@ -9,9 +9,9 @@
// except according to those terms. // except according to those terms.
use pathfinder_canvas::{CanvasFontContext, CanvasRenderingContext2D, Path2D}; use pathfinder_canvas::{CanvasFontContext, CanvasRenderingContext2D, Path2D};
use pathfinder_geometry::vector::{Vector2F, Vector2I};
use pathfinder_geometry::rect::RectF;
use pathfinder_content::color::ColorF; use pathfinder_content::color::ColorF;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::{Vector2F, Vector2I};
use pathfinder_gl::{GLDevice, GLVersion}; use pathfinder_gl::{GLDevice, GLVersion};
use pathfinder_gpu::resources::FilesystemResourceLoader; use pathfinder_gpu::resources::FilesystemResourceLoader;
use pathfinder_renderer::concurrent::rayon::RayonExecutor; use pathfinder_renderer::concurrent::rayon::RayonExecutor;

View File

@ -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 = "<group>"; };
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 = "<group>"; };
6AFD6FF922BD780D00AC1ED3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
6AFD6FFB22BD781000AC1ED3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
6AFD6FFE22BD781000AC1ED3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
6AFD700022BD781000AC1ED3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
6AFD700122BD781000AC1ED3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
6AFD700322BD781000AC1ED3 /* Pathfinder_Example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Pathfinder_Example.entitlements; sourceTree = "<group>"; };
6AFD700922BD7B7A00AC1ED3 /* PathfinderView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PathfinderView.h; sourceTree = "<group>"; };
6AFD700A22BD7B7A00AC1ED3 /* PathfinderView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PathfinderView.m; sourceTree = "<group>"; };
6AFD700E22BD930500AC1ED3 /* libharfbuzz.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libharfbuzz.a; path = ../../../../../../../usr/local/lib/libharfbuzz.a; sourceTree = "<group>"; };
/* 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 = "<group>";
};
6AFD6FF622BD780D00AC1ED3 /* Products */ = {
isa = PBXGroup;
children = (
6AFD6FF522BD780D00AC1ED3 /* Pathfinder Example.app */,
);
name = Products;
sourceTree = "<group>";
};
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 = "<group>";
};
/* 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 = "<group>";
};
/* 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 */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Pathfinder Example.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6AFD6FF422BD780D00AC1ED3"
BuildableName = "Pathfinder Example.app"
BlueprintName = "Pathfinder Example"
ReferencedContainer = "container:Pathfinder Example.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6AFD6FF422BD780D00AC1ED3"
BuildableName = "Pathfinder Example.app"
BlueprintName = "Pathfinder Example"
ReferencedContainer = "container:Pathfinder Example.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "YES"
customWorkingDirectory = "/Users/pcwalton/Source/pathfinder/examples/macos_app"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6AFD6FF422BD780D00AC1ED3"
BuildableName = "Pathfinder Example.app"
BlueprintName = "Pathfinder Example"
ReferencedContainer = "container:Pathfinder Example.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
<AdditionalOption
key = "NSZombieEnabled"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
<AdditionalOption
key = "MallocScribble"
value = ""
isEnabled = "YES">
</AdditionalOption>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6AFD6FF422BD780D00AC1ED3"
BuildableName = "Pathfinder Example.app"
BlueprintName = "Pathfinder Example"
ReferencedContainer = "container:Pathfinder Example.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "1"
version = "2.0">
<Breakpoints>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint">
<BreakpointContent
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
scope = "0"
stopOnStyle = "0">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>Pathfinder Example.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>6AFD6FF422BD780D00AC1ED3</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@ -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 <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
@end

View File

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

View File

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

View File

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,373 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate">
<connections>
<outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="Pathfinder Example" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Pathfinder Example" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About Pathfinder Example" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" enabled="NO" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide Pathfinder Example" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit Pathfinder Example" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
</connections>
</menuItem>
<menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
<connections>
<action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
<connections>
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
<connections>
<action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
<connections>
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="pQI-g3-MTW"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
<menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="toggleSidebar:" target="-1" id="iwa-gc-5KM"/>
</connections>
</menuItem>
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="Pathfinder Example Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<window title="Pathfinder Example" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<rect key="contentRect" x="335" y="390" width="640" height="480"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="640" height="480"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<customView id="naY-wz-xJX" customClass="PathfinderView">
<rect key="frame" x="0.0" y="0.0" width="640" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</customView>
</subviews>
</view>
</window>
</objects>
</document>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2019 The Pathfinder Project Developers. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

View File

@ -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 <Cocoa/Cocoa.h>
#import <Metal/Metal.h>
#include <pathfinder/pathfinder.h>
NS_ASSUME_NONNULL_BEGIN
@interface PathfinderView : NSView {
id<MTLDevice> mDevice;
PFMetalRendererRef mRenderer;
PFCanvasFontContextRef mFontContext;
PFBuildOptionsRef mBuildOptions;
CVDisplayLinkRef mDisplayLink;
int32_t mFrameNumber;
CGSize mLayerSize;
NSLock *mRenderLock;
}
- (void)_render;
@end
NS_ASSUME_NONNULL_END

View File

@ -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 <QuartzCore/QuartzCore.h>
#import "PathfinderView.h"
#import <Metal/Metal.h>
#include <math.h>
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

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>

View File

@ -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 <Cocoa/Cocoa.h>
int main(int argc, const char * argv[]) {
return NSApplicationMain(argc, argv);
}

View File

@ -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<dyn Error>> {
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(())
}

View File

@ -24,7 +24,7 @@ use pathfinder_renderer::scene::Scene;
use pathfinder_swf::{draw_paths_into_scene, process_swf_tags}; use pathfinder_swf::{draw_paths_into_scene, process_swf_tags};
use std::env; use std::env;
use std::fs::read; use std::fs::read;
use pathfinder_geometry::transform2d::Transform2DF; use pathfinder_geometry::transform2d::Transform2F;
fn main() { fn main() {
let resource_loader = FilesystemResourceLoader::locate(); let resource_loader = FilesystemResourceLoader::locate();
@ -118,7 +118,7 @@ fn main() {
// Render the canvas to screen. // Render the canvas to screen.
let scene = SceneProxy::from_scene(scene, RayonExecutor); let scene = SceneProxy::from_scene(scene, RayonExecutor);
let mut build_options = BuildOptions::default(); 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) Vector2F::new(device_pixel_ratio, device_pixel_ratio)
); );
build_options.transform = RenderTransform::Transform2D(scale_transform); build_options.transform = RenderTransform::Transform2D(scale_transform);

View File

@ -1,5 +1,5 @@
[package] [package]
name = "pathfinder_pdf" name = "pathfinder_export"
version = "0.1.0" version = "0.1.0"
authors = ["Sebastian Köln <sebk@rynx.org>"] authors = ["Sebastian Köln <sebk@rynx.org>"]
edition = "2018" edition = "2018"

174
export/src/lib.rs Normal file
View File

@ -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<W: Write>(&self, writer: &mut W, format: FileFormat) -> io::Result<()>;
}
impl Export for Scene {
fn export<W: Write>(&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<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
let view_box = scene.view_box();
writeln!(
writer,
"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"{} {} {} {}\">",
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, " <path")?;
if !name.is_empty() {
write!(writer, " id=\"{}\"", name)?;
}
writeln!(
writer,
" fill=\"{:?}\" d=\"{:?}\" />",
paint.color, outline
)?;
}
writeln!(writer, "</svg>")?;
Ok(())
}
fn export_pdf<W: Write>(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<W: Write>(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(())
}

View File

@ -1,8 +1,8 @@
//! This is a heavily modified version of the pdfpdf crate by Benjamin Kimock <kimockb@gmail.com> (aka. saethlin) //! This is a heavily modified version of the pdfpdf crate by Benjamin Kimock <kimockb@gmail.com> (aka. saethlin)
use pathfinder_geometry::{vector::Vector2F, rect::RectF}; use pathfinder_geometry::vector::Vector2F;
use pathfinder_content::color::ColorU; use pathfinder_content::color::ColorU;
use std::io::{self, Write, Cursor, Seek}; use std::io::{self, Write};
use deflate::Compression; use deflate::Compression;
struct Counter<T> { struct Counter<T> {
@ -19,9 +19,6 @@ impl<T> Counter<T> {
pub fn pos(&self) -> u64 { pub fn pos(&self) -> u64 {
self.count self.count
} }
pub fn into_inner(self) -> T {
self.inner
}
} }
impl<W: Write> Write for Counter<W> { impl<W: Write> Write for Counter<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
@ -101,43 +98,6 @@ impl Pdf {
self.objects.len() 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<Compression>) {
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 /// Set the color for all subsequent drawing operations
#[inline] #[inline]
pub fn set_fill_color(&mut self, color: ColorU) { pub fn set_fill_color(&mut self, color: ColorU) {
@ -178,10 +138,6 @@ impl Pdf {
writeln!(self.page_buffer, "f").unwrap(); writeln!(self.page_buffer, "f").unwrap();
} }
pub fn stroke(&mut self) {
writeln!(self.page_buffer, "s").unwrap();
}
pub fn close(&mut self) { pub fn close(&mut self) {
writeln!(self.page_buffer, "h").unwrap(); writeln!(self.page_buffer, "h").unwrap();
} }
@ -218,7 +174,7 @@ impl Pdf {
/Resources <<\n" /Resources <<\n"
.to_vec(); .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(); write!(page_object, "/XObject {} 0 R ", idx+1).unwrap();
} }
@ -258,7 +214,7 @@ impl Pdf {
self.objects.iter().filter(|o| o.is_page).count() self.objects.iter().filter(|o| o.is_page).count()
)?; )?;
out.write_all(b"/Kids [")?; 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)?; write!(out, "{} 0 R ", idx + 1)?;
} }
out.write_all(b"] >>\nendobj\n")?; out.write_all(b"] >>\nendobj\n")?;

View File

@ -10,8 +10,8 @@
//! Line segment types, optimized with SIMD. //! Line segment types, optimized with SIMD.
use crate::vector::Vector2F;
use crate::transform2d::Matrix2x2F; use crate::transform2d::Matrix2x2F;
use crate::vector::Vector2F;
use crate::util; use crate::util;
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::F32x4;
use std::ops::{Add, Sub}; use std::ops::{Add, Sub};
@ -26,44 +26,44 @@ impl LineSegment2F {
} }
#[inline] #[inline]
pub fn from(&self) -> Vector2F { pub fn from(self) -> Vector2F {
Vector2F(self.0) Vector2F(self.0.xy())
} }
#[inline] #[inline]
pub fn to(&self) -> Vector2F { pub fn to(self) -> Vector2F {
Vector2F(self.0.zwxy()) Vector2F(self.0.zw())
} }
#[inline] #[inline]
pub fn set_from(&mut self, point: &Vector2F) { pub fn set_from(&mut self, point: Vector2F) {
self.0 = point.0.concat_xy_zw(self.0) self.0 = point.0.to_f32x4().concat_xy_zw(self.0)
} }
#[inline] #[inline]
pub fn set_to(&mut self, point: &Vector2F) { pub fn set_to(&mut self, point: Vector2F) {
self.0 = self.0.concat_xy_xy(point.0) self.0 = self.0.concat_xy_xy(point.0.to_f32x4())
} }
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
#[inline] #[inline]
pub fn from_x(&self) -> f32 { pub fn from_x(self) -> f32 {
self.0[0] self.0[0]
} }
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
#[inline] #[inline]
pub fn from_y(&self) -> f32 { pub fn from_y(self) -> f32 {
self.0[1] self.0[1]
} }
#[inline] #[inline]
pub fn to_x(&self) -> f32 { pub fn to_x(self) -> f32 {
self.0[2] self.0[2]
} }
#[inline] #[inline]
pub fn to_y(&self) -> f32 { pub fn to_y(self) -> f32 {
self.0[3] self.0[3]
} }
@ -88,22 +88,22 @@ impl LineSegment2F {
} }
#[inline] #[inline]
pub fn translate(&self, offset: Vector2F) -> LineSegment2F { pub fn translate(self, offset: Vector2F) -> LineSegment2F {
LineSegment2F(self.0 + offset.0.xyxy()) LineSegment2F(self.0 + offset.0.to_f32x4().xyxy())
} }
#[inline] #[inline]
pub fn scale(&self, factor: f32) -> LineSegment2F { pub fn scale(self, factor: f32) -> LineSegment2F {
LineSegment2F(self.0 * F32x4::splat(factor)) LineSegment2F(self.0 * F32x4::splat(factor))
} }
#[inline] #[inline]
pub fn scale_xy(&self, factors: Vector2F) -> LineSegment2F { pub fn scale_xy(self, factors: Vector2F) -> LineSegment2F {
LineSegment2F(self.0 * factors.0.xyxy()) LineSegment2F(self.0 * factors.0.to_f32x4().xyxy())
} }
#[inline] #[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); debug_assert!(t >= 0.0 && t <= 1.0);
let (from_from, to_to) = (self.0.xyxy(), self.0.zwzw()); let (from_from, to_to) = (self.0.xyxy(), self.0.zwzw());
let d_d = to_to - from_from; let d_d = to_to - from_from;
@ -116,7 +116,7 @@ impl LineSegment2F {
// Returns the left segment first, followed by the right segment. // Returns the left segment first, followed by the right segment.
#[inline] #[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)); let (min_part, max_part) = self.split(self.solve_t_for_x(x));
if min_part.from_x() < max_part.from_x() { if min_part.from_x() < max_part.from_x() {
(min_part, max_part) (min_part, max_part)
@ -127,7 +127,7 @@ impl LineSegment2F {
// Returns the upper segment first, followed by the lower segment. // Returns the upper segment first, followed by the lower segment.
#[inline] #[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)); 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 // 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] #[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()) (x - self.from_x()) / (self.to_x() - self.from_x())
} }
#[inline] #[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()) (y - self.from_y()) / (self.to_y() - self.from_y())
} }
#[inline] #[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)) util::lerp(self.from_x(), self.to_x(), self.solve_t_for_y(y))
} }
#[inline] #[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)) util::lerp(self.from_y(), self.to_y(), self.solve_t_for_x(x))
} }
#[inline] #[inline]
pub fn reversed(&self) -> LineSegment2F { pub fn reversed(self) -> LineSegment2F {
LineSegment2F(self.0.zwxy()) LineSegment2F(self.0.zwxy())
} }
#[inline] #[inline]
pub fn upper_point(&self) -> Vector2F { pub fn upper_point(self) -> Vector2F {
if self.from_y() < self.to_y() { if self.from_y() < self.to_y() {
self.from() self.from()
} else { } else {
@ -174,27 +174,27 @@ impl LineSegment2F {
} }
#[inline] #[inline]
pub fn min_x(&self) -> f32 { pub fn min_x(self) -> f32 {
f32::min(self.from_x(), self.to_x()) f32::min(self.from_x(), self.to_x())
} }
#[inline] #[inline]
pub fn max_x(&self) -> f32 { pub fn max_x(self) -> f32 {
f32::max(self.from_x(), self.to_x()) f32::max(self.from_x(), self.to_x())
} }
#[inline] #[inline]
pub fn min_y(&self) -> f32 { pub fn min_y(self) -> f32 {
f32::min(self.from_y(), self.to_y()) f32::min(self.from_y(), self.to_y())
} }
#[inline] #[inline]
pub fn max_y(&self) -> f32 { pub fn max_y(self) -> f32 {
f32::max(self.from_y(), self.to_y()) f32::max(self.from_y(), self.to_y())
} }
#[inline] #[inline]
pub fn y_winding(&self) -> i32 { pub fn y_winding(self) -> i32 {
if self.from_y() < self.to_y() { if self.from_y() < self.to_y() {
1 1
} else { } else {
@ -205,9 +205,9 @@ impl LineSegment2F {
// Reverses if necessary so that the from point is above the to point. Calling this method // Reverses if necessary so that the from point is above the to point. Calling this method
// again will undo the transformation. // again will undo the transformation.
#[inline] #[inline]
pub fn orient(&self, y_winding: i32) -> LineSegment2F { pub fn orient(self, y_winding: i32) -> LineSegment2F {
if y_winding >= 0 { if y_winding >= 0 {
*self self
} else { } else {
self.reversed() self.reversed()
} }
@ -215,55 +215,50 @@ impl LineSegment2F {
// TODO(pcwalton): Optimize with SIMD. // TODO(pcwalton): Optimize with SIMD.
#[inline] #[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()); let (dx, dy) = (self.to_x() - self.from_x(), self.to_y() - self.from_y());
dx * dx + dy * dy dx * dx + dy * dy
} }
#[inline] #[inline]
pub fn vector(&self) -> Vector2F { pub fn vector(self) -> Vector2F {
self.to() - self.from() self.to() - self.from()
} }
// http://www.cs.swan.ac.uk/~cssimon/line_intersection.html // http://www.cs.swan.ac.uk/~cssimon/line_intersection.html
pub fn intersection_t(&self, other: &LineSegment2F) -> Option<f32> { pub fn intersection_t(self, other: LineSegment2F) -> Option<f32> {
let p0p1 = self.vector(); let p0p1 = self.vector();
let matrix = Matrix2x2F(other.vector().0.concat_xy_xy((-p0p1).0)); let matrix = Matrix2x2F(other.vector().0.concat_xy_xy((-p0p1).0));
if f32::abs(matrix.det()) < EPSILON { if f32::abs(matrix.det()) < EPSILON {
return None; 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; const EPSILON: f32 = 0.0001;
} }
#[inline] #[inline]
pub fn sample(&self, t: f32) -> Vector2F { pub fn sample(self, t: f32) -> Vector2F {
self.from() + self.vector().scale(t) self.from() + self.vector().scale(t)
} }
#[inline] #[inline]
pub fn midpoint(&self) -> Vector2F { pub fn midpoint(self) -> Vector2F {
self.sample(0.5) self.sample(0.5)
} }
#[inline] #[inline]
pub fn offset(&self, distance: f32) -> LineSegment2F { pub fn offset(self, distance: f32) -> LineSegment2F {
if self.is_zero_length() { if self.is_zero_length() {
*self self
} else { } else {
*self self + self.vector().yx().normalize().scale_xy(Vector2F::new(-distance, distance))
+ self
.vector()
.yx()
.normalize()
.scale_xy(Vector2F::new(-distance, distance))
} }
} }
#[inline] #[inline]
pub fn is_zero_length(&self) -> bool { pub fn is_zero_length(self) -> bool {
self.vector().is_zero() self.vector().is_zero()
} }
} }
@ -272,7 +267,7 @@ impl Add<Vector2F> for LineSegment2F {
type Output = LineSegment2F; type Output = LineSegment2F;
#[inline] #[inline]
fn add(self, point: Vector2F) -> LineSegment2F { 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<Vector2F> for LineSegment2F {
type Output = LineSegment2F; type Output = LineSegment2F;
#[inline] #[inline]
fn sub(self, point: Vector2F) -> LineSegment2F { 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)] #[derive(Clone, Copy, Debug, Default)]
#[repr(transparent)] #[repr(C)]
pub struct LineSegmentU4(pub u16); pub struct LineSegmentU4 {
pub from: u8,
pub to: u8,
}
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
#[repr(transparent)] #[repr(C)]
pub struct LineSegmentU8(pub u32); pub struct LineSegmentU8 {
pub from_x: u8,
pub from_y: u8,
pub to_x: u8,
pub to_y: u8,
}

View File

@ -29,36 +29,34 @@ impl RectF {
#[inline] #[inline]
pub fn origin(&self) -> Vector2F { pub fn origin(&self) -> Vector2F {
Vector2F(self.0) Vector2F(self.0.xy())
} }
#[inline] #[inline]
pub fn size(&self) -> Vector2F { pub fn size(&self) -> Vector2F {
Vector2F(self.0.zwxy() - self.0.xyxy()) Vector2F(self.0.zw() - self.0.xy())
} }
#[inline] #[inline]
pub fn upper_right(&self) -> Vector2F { pub fn upper_right(&self) -> Vector2F {
Vector2F(self.0.zyxw()) Vector2F(self.0.zy())
} }
#[inline] #[inline]
pub fn lower_left(&self) -> Vector2F { pub fn lower_left(&self) -> Vector2F {
Vector2F(self.0.xwzy()) Vector2F(self.0.xw())
} }
#[inline] #[inline]
pub fn lower_right(&self) -> Vector2F { pub fn lower_right(&self) -> Vector2F {
Vector2F(self.0.zwxy()) Vector2F(self.0.zw())
} }
#[inline] #[inline]
pub fn contains_point(&self, point: Vector2F) -> bool { pub fn contains_point(&self, point: Vector2F) -> bool {
// self.origin <= point && point <= self.lower_right // self.origin <= point && point <= self.lower_right
self.0 let point = point.0.to_f32x4();
.concat_xy_xy(point.0) self.0.concat_xy_xy(point).packed_le(point.concat_xy_zw(self.0)).is_all_ones()
.packed_le(point.0.concat_xy_zw(self.0))
.is_all_ones()
} }
#[inline] #[inline]
@ -166,27 +164,27 @@ impl RectI {
#[inline] #[inline]
pub fn origin(&self) -> Vector2I { pub fn origin(&self) -> Vector2I {
Vector2I(self.0) Vector2I(self.0.xy())
} }
#[inline] #[inline]
pub fn size(&self) -> Vector2I { pub fn size(&self) -> Vector2I {
Vector2I(self.0.zwxy() - self.0.xyxy()) Vector2I(self.0.zw() - self.0.xy())
} }
#[inline] #[inline]
pub fn upper_right(&self) -> Vector2I { pub fn upper_right(&self) -> Vector2I {
Vector2I(self.0.zyxw()) Vector2I(self.0.zy())
} }
#[inline] #[inline]
pub fn lower_left(&self) -> Vector2I { pub fn lower_left(&self) -> Vector2I {
Vector2I(self.0.xwzy()) Vector2I(self.0.xw())
} }
#[inline] #[inline]
pub fn lower_right(&self) -> Vector2I { pub fn lower_right(&self) -> Vector2I {
Vector2I(self.0.zwxy()) Vector2I(self.0.zw())
} }
#[inline] #[inline]
@ -213,7 +211,8 @@ impl RectI {
pub fn contains_point(&self, point: Vector2I) -> bool { pub fn contains_point(&self, point: Vector2I) -> bool {
// self.origin <= point && point <= self.lower_right - 1 // self.origin <= point && point <= self.lower_right - 1
let lower_right = self.lower_right() - Vector2I::splat(1); let lower_right = self.lower_right() - Vector2I::splat(1);
self.0 self.origin()
.0
.concat_xy_xy(point.0) .concat_xy_xy(point.0)
.packed_le(point.0.concat_xy_xy(lower_right.0)) .packed_le(point.0.concat_xy_xy(lower_right.0))
.is_all_ones() .is_all_ones()

View File

@ -13,10 +13,10 @@
use crate::line_segment::LineSegment2F; use crate::line_segment::LineSegment2F;
use crate::vector::Vector2F; use crate::vector::Vector2F;
use crate::rect::RectF; use crate::rect::RectF;
use crate::transform3d::Transform3DF; use crate::transform3d::Transform4F;
use crate::unit_vector::UnitVector; use crate::unit_vector::UnitVector;
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::F32x4;
use std::ops::Sub; use std::ops::{Mul, MulAssign, Sub};
/// A 2x2 matrix, optimized with SIMD, in column-major order. /// A 2x2 matrix, optimized with SIMD, in column-major order.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
@ -42,22 +42,12 @@ impl Matrix2x2F {
#[inline] #[inline]
pub fn from_rotation_vector(vector: UnitVector) -> Matrix2x2F { 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] #[inline]
pub fn row_major(m11: f32, m12: f32, m21: f32, m22: f32) -> Matrix2x2F { pub fn row_major(m00: f32, m01: f32, m10: f32, m11: f32) -> Matrix2x2F {
Matrix2x2F(F32x4::new(m11, m21, m12, m22)) Matrix2x2F(F32x4::new(m00, m10, m01, m11))
}
#[inline]
pub fn post_mul(&self, other: &Matrix2x2F) -> Matrix2x2F {
Matrix2x2F(self.0.xyxy() * other.0.xxzz() + self.0.zwzw() * other.0.yyww())
}
#[inline]
pub fn pre_mul(&self, other: &Matrix2x2F) -> Matrix2x2F {
other.post_mul(self)
} }
#[inline] #[inline]
@ -70,12 +60,6 @@ impl Matrix2x2F {
Matrix2x2F(self.0.wyzx() * F32x4::new(1.0, -1.0, -1.0, 1.0)) 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] #[inline]
pub fn det(&self) -> f32 { pub fn det(&self) -> f32 {
self.0[0] * self.0[3] - self.0[2] * self.0[1] self.0[0] * self.0[3] - self.0[2] * self.0[1]
@ -112,49 +96,71 @@ impl Sub<Matrix2x2F> for Matrix2x2F {
} }
} }
/// An affine transform, optimized with SIMD. impl Mul<Matrix2x2F> for Matrix2x2F {
#[derive(Clone, Copy, Debug, PartialEq)] type Output = Matrix2x2F;
pub struct Transform2DF { #[inline]
// Row-major order. fn mul(self, other: Matrix2x2F) -> Matrix2x2F {
matrix: Matrix2x2F, Matrix2x2F(self.0.xyxy() * other.0.xxzz() + self.0.zwzw() * other.0.yyww())
vector: Vector2F, }
} }
impl Default for Transform2DF { impl Mul<Vector2F> for Matrix2x2F {
type Output = Vector2F;
#[inline] #[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)) Self::from_scale(Vector2F::splat(1.0))
} }
} }
impl Transform2DF { impl Transform2F {
#[inline] #[inline]
pub fn from_scale(scale: Vector2F) -> Transform2DF { pub fn from_scale(scale: Vector2F) -> Transform2F {
Transform2DF { Transform2F {
matrix: Matrix2x2F::from_scale(scale), matrix: Matrix2x2F::from_scale(scale),
vector: Vector2F::default(), vector: Vector2F::default(),
} }
} }
#[inline] #[inline]
pub fn from_rotation(theta: f32) -> Transform2DF { pub fn from_uniform_scale(scale: f32) -> Transform2F {
Transform2DF { Transform2F::from_scale(Vector2F::splat(scale))
}
#[inline]
pub fn from_rotation(theta: f32) -> Transform2F {
Transform2F {
matrix: Matrix2x2F::from_rotation(theta), matrix: Matrix2x2F::from_rotation(theta),
vector: Vector2F::default(), vector: Vector2F::default(),
} }
} }
#[inline] #[inline]
pub fn from_rotation_vector(vector: UnitVector) -> Transform2DF { pub fn from_rotation_vector(vector: UnitVector) -> Transform2F {
Transform2DF { Transform2F {
matrix: Matrix2x2F::from_rotation_vector(vector), matrix: Matrix2x2F::from_rotation_vector(vector),
vector: Vector2F::default(), vector: Vector2F::default(),
} }
} }
#[inline] #[inline]
pub fn from_translation(vector: Vector2F) -> Transform2DF { pub fn from_translation(vector: Vector2F) -> Transform2F {
Transform2DF { matrix: Matrix2x2F::default(), vector } Transform2F { matrix: Matrix2x2F::default(), vector }
} }
#[inline] #[inline]
@ -162,58 +168,24 @@ impl Transform2DF {
scale: Vector2F, scale: Vector2F,
theta: f32, theta: f32,
translation: Vector2F, translation: Vector2F,
) -> Transform2DF { ) -> Transform2F {
let rotation = Transform2DF::from_rotation(theta); let rotation = Transform2F::from_rotation(theta);
let translation = Transform2DF::from_translation(translation); let translation = Transform2F::from_translation(translation);
Transform2DF::from_scale(scale).post_mul(&rotation).post_mul(&translation) Transform2F::from_scale(scale) * rotation * translation
} }
#[inline] #[inline]
pub fn row_major(m11: f32, m12: f32, m21: f32, m22: f32, m31: f32, m32: f32) -> Transform2DF { pub fn row_major(m11: f32, m12: f32, m21: f32, m22: f32, m31: f32, m32: f32) -> Transform2F {
Transform2DF { Transform2F {
matrix: Matrix2x2F::row_major(m11, m12, m21, m22), matrix: Matrix2x2F::row_major(m11, m12, m21, m22),
vector: Vector2F::new(m31, m32), 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. // TODO(pcwalton): Optimize better with SIMD.
#[inline] #[inline]
pub fn to_3d(&self) -> Transform3DF { pub fn to_3d(&self) -> Transform4F {
Transform3DF::row_major( Transform4F::row_major(
self.matrix.0[0], self.matrix.0[0],
self.matrix.0[1], self.matrix.0[1],
0.0, 0.0,
@ -235,7 +207,7 @@ impl Transform2DF {
#[inline] #[inline]
pub fn is_identity(&self) -> bool { pub fn is_identity(&self) -> bool {
*self == Transform2DF::default() *self == Transform2F::default()
} }
#[inline] #[inline]
@ -256,18 +228,23 @@ impl Transform2DF {
} }
#[inline] #[inline]
pub fn post_translate(&self, vector: Vector2F) -> Transform2DF { pub fn translate(&self, vector: Vector2F) -> Transform2F {
self.post_mul(&Transform2DF::from_translation(vector)) Transform2F::from_translation(vector) * *self
} }
#[inline] #[inline]
pub fn post_rotate(&self, theta: f32) -> Transform2DF { pub fn rotate(&self, theta: f32) -> Transform2F {
self.post_mul(&Transform2DF::from_rotation(theta)) Transform2F::from_rotation(theta) * *self
} }
#[inline] #[inline]
pub fn post_scale(&self, scale: Vector2F) -> Transform2DF { pub fn scale(&self, scale: Vector2F) -> Transform2F {
self.post_mul(&Transform2DF::from_scale(scale)) 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. /// 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. /// This decomposition assumes that scale, rotation, and translation are applied in that order.
#[inline] #[inline]
pub fn scale_factor(&self) -> f32 { pub fn scale_factor(&self) -> f32 {
Vector2F(self.matrix.0.zwxy()).length() Vector2F(self.matrix.0.zw()).length()
}
}
impl Mul<Transform2F> for Transform2F {
type Output = Transform2F;
#[inline]
fn mul(self, other: Transform2F) -> Transform2F {
Transform2F {
matrix: self.matrix * other.matrix,
vector: self * other.vector,
}
}
}
impl Mul<Vector2F> for Transform2F {
type Output = Vector2F;
#[inline]
fn mul(self, vector: Vector2F) -> Vector2F {
self.matrix * vector + self.vector
}
}
impl Mul<LineSegment2F> 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<RectF> 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
} }
} }

View File

@ -14,24 +14,24 @@ use crate::vector::{Vector2F, Vector2I, Vector4F};
use crate::rect::RectF; use crate::rect::RectF;
use crate::transform2d::Matrix2x2F; use crate::transform2d::Matrix2x2F;
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::F32x4;
use std::ops::{Add, Neg}; use std::ops::{Add, Mul, MulAssign, Neg};
/// An transform, optimized with SIMD. /// An transform, optimized with SIMD.
/// ///
/// In column-major order. /// In column-major order.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)] #[repr(C)]
pub struct Transform3DF { pub struct Transform4F {
pub c0: F32x4, pub c0: F32x4,
pub c1: F32x4, pub c1: F32x4,
pub c2: F32x4, pub c2: F32x4,
pub c3: F32x4, pub c3: F32x4,
} }
impl Default for Transform3DF { impl Default for Transform4F {
#[inline] #[inline]
fn default() -> Transform3DF { fn default() -> Transform4F {
Transform3DF { Transform4F {
c0: F32x4::new(1.0, 0.0, 0.0, 0.0), c0: F32x4::new(1.0, 0.0, 0.0, 0.0),
c1: F32x4::new(0.0, 1.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), c2: F32x4::new(0.0, 0.0, 1.0, 0.0),
@ -40,7 +40,7 @@ impl Default for Transform3DF {
} }
} }
impl Transform3DF { impl Transform4F {
#[inline] #[inline]
pub fn row_major( pub fn row_major(
m00: f32, m00: f32,
@ -59,8 +59,8 @@ impl Transform3DF {
m31: f32, m31: f32,
m32: f32, m32: f32,
m33: f32, m33: f32,
) -> Transform3DF { ) -> Transform4F {
Transform3DF { Transform4F {
c0: F32x4::new(m00, m10, m20, m30), c0: F32x4::new(m00, m10, m20, m30),
c1: F32x4::new(m01, m11, m21, m31), c1: F32x4::new(m01, m11, m21, m31),
c2: F32x4::new(m02, m12, m22, m32), c2: F32x4::new(m02, m12, m22, m32),
@ -69,26 +69,28 @@ impl Transform3DF {
} }
#[inline] #[inline]
pub fn from_scale(x: f32, y: f32, z: f32) -> Transform3DF { pub fn from_scale(scale: Vector4F) -> Transform4F {
Transform3DF::row_major( Transform4F {
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, 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] #[inline]
pub fn from_uniform_scale(factor: f32) -> Transform3DF { pub fn from_uniform_scale(factor: f32) -> Transform4F {
Transform3DF::from_scale(factor, factor, factor) Transform4F::from_scale(Vector4F::splat(factor))
} }
#[inline] #[inline]
pub fn from_translation(x: f32, y: f32, z: f32) -> Transform3DF { pub fn from_translation(mut translation: Vector4F) -> Transform4F {
Transform3DF::row_major( translation.set_w(1.0);
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, Transform4F { c3: translation.0, ..Transform4F::default() }
)
} }
// TODO(pcwalton): Optimize. // 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_b, sin_b) = (yaw.cos(), yaw.sin());
let (cos_c, sin_c) = (pitch.cos(), pitch.sin()); let (cos_c, sin_c) = (pitch.cos(), pitch.sin());
let (cos_a, sin_a) = (roll.cos(), roll.sin()); let (cos_a, sin_a) = (roll.cos(), roll.sin());
@ -101,7 +103,7 @@ impl Transform3DF {
let m20 = -sin_b; let m20 = -sin_b;
let m21 = cos_b * sin_c; let m21 = cos_b * sin_c;
let m22 = cos_b * cos_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, 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 /// The quaternion is expected to be packed into a SIMD type (x, y, z, w) corresponding to
/// x + yi + zj + wk. /// 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. // 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()); let (mut sq, mut w, mut xy_xz_yz) = (q * q, q.wwww() * q, q.xxyy() * q.yzzy());
sq += sq; sq += sq;
@ -119,7 +121,7 @@ impl Transform3DF {
let diag = F32x4::splat(1.0) - (sq.yxxy() + sq.zzyy()); let diag = F32x4::splat(1.0) - (sq.yxxy() + sq.zzyy());
let (wx2, wy2, wz2) = (w.x(), w.y(), w.z()); 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()); let (xy2, xz2, yz2) = (xy_xz_yz.x(), xy_xz_yz.y(), xy_xz_yz.z());
Transform3DF::row_major( Transform4F::row_major(
diag.x(), diag.x(),
xy2 - wz2, xy2 - wz2,
xz2 + wy2, xz2 + wy2,
@ -148,14 +150,14 @@ impl Transform3DF {
top: f32, top: f32,
near_val: f32, near_val: f32,
far_val: f32, far_val: f32,
) -> Transform3DF { ) -> Transform4F {
let x_inv = 1.0 / (right - left); let x_inv = 1.0 / (right - left);
let y_inv = 1.0 / (top - bottom); let y_inv = 1.0 / (top - bottom);
let z_inv = 1.0 / (far_val - near_val); let z_inv = 1.0 / (far_val - near_val);
let tx = -(right + left) * x_inv; let tx = -(right + left) * x_inv;
let ty = -(top + bottom) * y_inv; let ty = -(top + bottom) * y_inv;
let tz = -(far_val + near_val) * z_inv; let tz = -(far_val + near_val) * z_inv;
Transform3DF::row_major( Transform4F::row_major(
2.0 * x_inv, 2.0 * x_inv,
0.0, 0.0,
0.0, 0.0,
@ -176,17 +178,17 @@ impl Transform3DF {
} }
/// Linearly interpolate between transforms /// 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 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 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 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); 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()`. /// Just like `gluPerspective()`.
#[inline] #[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 f = 1.0 / (fov_y * 0.5).tan();
let z_denom = 1.0 / (z_near - z_far); let z_denom = 1.0 / (z_near - z_far);
let m00 = f / aspect; let m00 = f / aspect;
@ -194,7 +196,7 @@ impl Transform3DF {
let m22 = (z_far + z_near) * z_denom; let m22 = (z_far + z_near) * z_denom;
let m23 = 2.0 * z_far * z_near * z_denom; let m23 = 2.0 * z_far * z_near * z_denom;
let m32 = -1.0; 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, 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, b: Matrix2x2F,
c: Matrix2x2F, c: Matrix2x2F,
d: Matrix2x2F, d: Matrix2x2F,
) -> Transform3DF { ) -> Transform4F {
Transform3DF { Transform4F {
c0: a.0.concat_xy_xy(c.0), c0: a.0.concat_xy_xy(c.0),
c1: a.0.concat_zw_zw(c.0), c1: a.0.concat_zw_zw(c.0),
c2: b.0.concat_xy_xy(d.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] #[inline]
pub fn pre_mul(&self, other: &Transform3DF) -> Transform3DF { pub fn rotate(&self, yaw: f32, pitch: f32, roll: f32) -> Transform4F {
return Transform3DF { Transform4F::from_rotation(yaw, pitch, roll) * *self
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
}
} }
#[inline] #[inline]
pub fn post_mul(&self, other: &Transform3DF) -> Transform3DF { pub fn scale(&self, scale: Vector4F) -> Transform4F {
other.pre_mul(self) Transform4F::from_scale(scale) * *self
} }
#[inline] #[inline]
pub fn transform_point(&self, point: Vector4F) -> Vector4F { pub fn uniform_scale(&self, scale: f32) -> Transform4F {
let term0 = self.c0 * F32x4::splat(point.x()); Transform4F::from_uniform_scale(scale) * *self
let term1 = self.c1 * F32x4::splat(point.y()); }
let term2 = self.c2 * F32x4::splat(point.z());
let term3 = self.c3 * F32x4::splat(point.w()); #[inline]
Vector4F(term0 + term1 + term2 + term3) pub fn translate(&self, translation: Vector4F) -> Transform4F {
Transform4F::from_translation(translation) * *self
} }
#[inline] #[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 // If A is the upper left submatrix of this matrix, this method assumes that A and the Schur
// complement of A are invertible. // complement of A are invertible.
pub fn inverse(&self) -> Transform3DF { pub fn inverse(&self) -> Transform4F {
// Extract submatrices. // Extract submatrices.
let (a, b) = (self.upper_left(), self.upper_right()); let (a, b) = (self.upper_left(), self.upper_right());
let (c, d) = (self.lower_left(), self.lower_right()); let (c, d) = (self.lower_left(), self.lower_right());
// Compute temporary matrices. // Compute temporary matrices.
let a_inv = a.inverse(); let a_inv = a.inverse();
let x = c.post_mul(&a_inv); let x = c * a_inv;
let y = (d - x.post_mul(&b)).inverse(); let y = (d - x * b).inverse();
let z = a_inv.post_mul(&b); let z = a_inv * b;
// Compute new submatrices. // Compute new submatrices.
let (a_new, b_new) = (a_inv + z.post_mul(&y).post_mul(&x), (-z).post_mul(&y)); let (a_new, b_new) = (a_inv + z * y * x, -z * y);
let (c_new, d_new) = ((-y).post_mul(&x), y); let (c_new, d_new) = (-y * x, y);
// Construct inverse. // Construct inverse.
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.c0.approx_eq(other.c0, epsilon)
&& self.c1.approx_eq(other.c1, epsilon) && self.c1.approx_eq(other.c1, epsilon)
&& self.c2.approx_eq(other.c2, epsilon) && self.c2.approx_eq(other.c2, epsilon)
@ -306,6 +294,50 @@ impl Transform3DF {
pub fn as_ptr(&self) -> *const f32 { pub fn as_ptr(&self) -> *const f32 {
(&self.c0) as *const F32x4 as *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<Transform4F> 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<Vector4F> 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<Transform4F> for Transform4F {
fn mul_assign(&mut self, other: Transform4F) {
*self = *self * other
}
} }
impl Add<Matrix2x2F> for Matrix2x2F { impl Add<Matrix2x2F> for Matrix2x2F {
@ -326,108 +358,110 @@ impl Neg for Matrix2x2F {
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct Perspective { pub struct Perspective {
pub transform: Transform3DF, pub transform: Transform4F,
pub window_size: Vector2I, pub window_size: Vector2I,
} }
impl Perspective { impl Perspective {
#[inline] #[inline]
pub fn new(transform: &Transform3DF, window_size: Vector2I) -> Perspective { pub fn new(transform: &Transform4F, window_size: Vector2I) -> Perspective {
Perspective { Perspective {
transform: *transform, transform: *transform,
window_size, window_size,
} }
} }
}
impl Mul<Transform4F> for Perspective {
type Output = Perspective;
#[inline] #[inline]
pub fn transform_point_2d(&self, point: &Vector2F) -> Vector2F { fn mul(self, other: Transform4F) -> Perspective {
let point = self Perspective {
.transform transform: self.transform * other,
.transform_point(point.to_3d()) window_size: self.window_size,
.perspective_divide() }
.to_2d() }
* Vector2F::new(1.0, -1.0); }
impl Mul<Vector2F> 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) (point + Vector2F::splat(1.0)) * self.window_size.to_f32().scale(0.5)
} }
}
// TODO(pcwalton): SIMD? impl Mul<RectF> for Perspective {
type Output = RectF;
#[inline] #[inline]
pub fn transform_rect(&self, rect: RectF) -> RectF { fn mul(self, rect: RectF) -> RectF {
let upper_left = self.transform_point_2d(&rect.origin()); let (upper_left, upper_right) = (self * rect.origin(), self * rect.upper_right());
let upper_right = self.transform_point_2d(&rect.upper_right()); let (lower_left, lower_right) = (self * rect.lower_left(), self * rect.lower_right());
let lower_left = self.transform_point_2d(&rect.lower_left());
let lower_right = self.transform_point_2d(&rect.lower_right());
let min_point = upper_left.min(upper_right).min(lower_left).min(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); let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right);
RectF::from_points(min_point, max_point) 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)] #[cfg(test)]
mod test { mod test {
use crate::vector::Vector4F; use crate::vector::Vector4F;
use crate::transform3d::Transform3DF; use crate::transform3d::Transform4F;
#[test] #[test]
fn test_post_mul() { 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, 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, 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, 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, 152.0, 83.0, 75.0,
); );
assert_eq!(a.post_mul(&b), c); assert_eq!(a * b, c);
} }
#[test] #[test]
fn test_pre_mul() { 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, 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, 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, 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, 50.0, 122.0, 125.0,
); );
assert_eq!(a.pre_mul(&b), c); assert_eq!(b * a, c);
} }
#[test] #[test]
fn test_transform_point() { 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, 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 p = Vector4F::new(3.0, 8.0, 4.0, 6.0);
let q = Vector4F::new(63.0, 97.0, 135.0, 117.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] #[test]
fn test_inverse() { fn test_inverse() {
// Random matrix. // 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.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.12969608, 0.0946466, 0.43248631, 0.63480505, 0.08154603, 0.50305436, 0.48359687,
0.51057162, 0.24812012, 0.51057162, 0.24812012,
); );
let p0 = Vector4F::new(0.95536648, 0.80633691, 0.16357357, 0.5477598); 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 = m.inverse();
let m_inv_exp = Transform3DF::row_major( let m_inv_exp = Transform4F::row_major(
-2.47290136, -2.47290136,
3.48865688, 3.48865688,
-6.12298336, -6.12298336,
@ -446,7 +480,7 @@ mod test {
-9.10374060, -9.10374060,
); );
assert!(m_inv.approx_eq(&m_inv_exp, 0.0001)); assert!(m_inv.approx_eq(&m_inv_exp, 0.0001));
let p2 = m_inv.transform_point(p1); let p2 = m_inv * p1;
assert!(p0.approx_eq(&p2, 0.0001)); assert!(p0.approx_eq(p2, 0.0001));
} }
} }

View File

@ -11,7 +11,7 @@
//! A utility module that allows unit vectors to be treated like angles. //! A utility module that allows unit vectors to be treated like angles.
use crate::vector::Vector2F; use crate::vector::Vector2F;
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::F32x2;
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct UnitVector(pub Vector2F); pub struct UnitVector(pub Vector2F);
@ -25,14 +25,14 @@ impl UnitVector {
/// Angle addition formula. /// Angle addition formula.
#[inline] #[inline]
pub fn rotate_by(&self, other: UnitVector) -> UnitVector { 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])) UnitVector(Vector2F::new(products[0] - products[1], products[2] + products[3]))
} }
/// Angle subtraction formula. /// Angle subtraction formula.
#[inline] #[inline]
pub fn rev_rotate_by(&self, other: UnitVector) -> UnitVector { 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])) UnitVector(Vector2F::new(products[0] + products[1], products[2] - products[3]))
} }
@ -40,7 +40,7 @@ impl UnitVector {
#[inline] #[inline]
pub fn halve_angle(&self) -> UnitVector { pub fn halve_angle(&self) -> UnitVector {
let x = self.0.x(); let x = self.0.x();
let term = F32x4::new(x, -x, 0.0, 0.0); let term = F32x2::new(x, -x);
UnitVector(Vector2F((F32x4::splat(0.5) * (F32x4::splat(1.0) + term)).sqrt())) UnitVector(Vector2F((F32x2::splat(0.5) * (F32x2::splat(1.0) + term)).sqrt()))
} }
} }

View File

@ -10,36 +10,36 @@
//! A SIMD-optimized point type. //! 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}; use std::ops::{Add, AddAssign, Mul, Neg, Sub};
/// 2D points with 32-bit floating point coordinates. /// 2D points with 32-bit floating point coordinates.
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub struct Vector2F(pub F32x4); pub struct Vector2F(pub F32x2);
impl Vector2F { impl Vector2F {
#[inline] #[inline]
pub fn new(x: f32, y: f32) -> Vector2F { pub fn new(x: f32, y: f32) -> Vector2F {
Vector2F(F32x4::new(x, y, 0.0, 0.0)) Vector2F(F32x2::new(x, y))
} }
#[inline] #[inline]
pub fn splat(value: f32) -> Vector2F { pub fn splat(value: f32) -> Vector2F {
Vector2F(F32x4::splat(value)) Vector2F(F32x2::splat(value))
} }
#[inline] #[inline]
pub fn to_3d(self) -> Vector4F { 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] #[inline]
pub fn x(&self) -> f32 { pub fn x(self) -> f32 {
self.0[0] self.0[0]
} }
#[inline] #[inline]
pub fn y(&self) -> f32 { pub fn y(self) -> f32 {
self.0[1] self.0[1]
} }
@ -54,97 +54,96 @@ impl Vector2F {
} }
#[inline] #[inline]
pub fn min(&self, other: Vector2F) -> Vector2F { pub fn min(self, other: Vector2F) -> Vector2F {
Vector2F(self.0.min(other.0)) Vector2F(self.0.min(other.0))
} }
#[inline] #[inline]
pub fn max(&self, other: Vector2F) -> Vector2F { pub fn max(self, other: Vector2F) -> Vector2F {
Vector2F(self.0.max(other.0)) Vector2F(self.0.max(other.0))
} }
#[inline] #[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) self.max(min_val).min(max_val)
} }
#[inline] #[inline]
pub fn det(&self, other: Vector2F) -> f32 { pub fn det(self, other: Vector2F) -> f32 {
self.x() * other.y() - self.y() * other.x() self.x() * other.y() - self.y() * other.x()
} }
#[inline] #[inline]
pub fn dot(&self, other: Vector2F) -> f32 { pub fn dot(self, other: Vector2F) -> f32 {
let xy = self.0 * other.0; let xy = self.0 * other.0;
xy.x() + xy.y() xy.x() + xy.y()
} }
#[inline] #[inline]
pub fn scale(&self, x: f32) -> Vector2F { pub fn scale(self, x: f32) -> Vector2F {
Vector2F(self.0 * F32x4::splat(x)) Vector2F(self.0 * F32x2::splat(x))
} }
#[inline] #[inline]
pub fn scale_xy(&self, factors: Vector2F) -> Vector2F { pub fn scale_xy(self, factors: Vector2F) -> Vector2F {
Vector2F(self.0 * factors.0) Vector2F(self.0 * factors.0)
} }
#[inline] #[inline]
pub fn floor(&self) -> Vector2F { pub fn floor(self) -> Vector2F {
Vector2F(self.0.floor()) Vector2F(self.0.floor())
} }
#[inline] #[inline]
pub fn ceil(&self) -> Vector2F { pub fn ceil(self) -> Vector2F {
Vector2F(self.0.ceil()) Vector2F(self.0.ceil())
} }
/// Treats this point as a vector and calculates its squared length. /// Treats this point as a vector and calculates its squared length.
#[inline] #[inline]
pub fn square_length(&self) -> f32 { pub fn square_length(self) -> f32 {
let squared = self.0 * self.0; let squared = self.0 * self.0;
squared[0] + squared[1] squared[0] + squared[1]
} }
/// Treats this point as a vector and calculates its length. /// Treats this point as a vector and calculates its length.
#[inline] #[inline]
pub fn length(&self) -> f32 { pub fn length(self) -> f32 {
f32::sqrt(self.square_length()) f32::sqrt(self.square_length())
} }
/// Treats this point as a vector and normalizes it. /// Treats this point as a vector and normalizes it.
#[inline] #[inline]
pub fn normalize(&self) -> Vector2F { pub fn normalize(self) -> Vector2F {
self.scale(1.0 / self.length()) self.scale(1.0 / self.length())
} }
/// Swaps y and x. /// Swaps y and x.
#[inline] #[inline]
pub fn yx(&self) -> Vector2F { pub fn yx(self) -> Vector2F {
Vector2F(self.0.yxwz()) Vector2F(self.0.yx())
} }
#[inline] #[inline]
pub fn is_zero(&self) -> bool { pub fn is_zero(self) -> bool {
*self == Vector2F::default() self == Vector2F::default()
} }
#[inline] #[inline]
pub fn lerp(&self, other: Vector2F, t: f32) -> Vector2F { pub fn lerp(self, other: Vector2F, t: f32) -> Vector2F {
*self + (other - *self).scale(t) self + (other - self).scale(t)
} }
#[inline] #[inline]
pub fn to_i32(&self) -> Vector2I { pub fn to_i32(self) -> Vector2I {
Vector2I(self.0.to_i32x4()) Vector2I(self.0.to_i32x2())
} }
} }
impl PartialEq for Vector2F { impl PartialEq for Vector2F {
#[inline] #[inline]
fn eq(&self, other: &Vector2F) -> bool { fn eq(&self, other: &Vector2F) -> bool {
let results = self.0.packed_eq(other.0); self.0.packed_eq(other.0).is_all_ones()
results[0] != 0 && results[1] != 0
} }
} }
@ -182,26 +181,26 @@ impl Neg for Vector2F {
/// 2D points with 32-bit signed integer coordinates. /// 2D points with 32-bit signed integer coordinates.
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub struct Vector2I(pub I32x4); pub struct Vector2I(pub I32x2);
impl Vector2I { impl Vector2I {
#[inline] #[inline]
pub fn new(x: i32, y: i32) -> Vector2I { pub fn new(x: i32, y: i32) -> Vector2I {
Vector2I(I32x4::new(x, y, 0, 0)) Vector2I(I32x2::new(x, y))
} }
#[inline] #[inline]
pub fn splat(value: i32) -> Vector2I { pub fn splat(value: i32) -> Vector2I {
Vector2I(I32x4::splat(value)) Vector2I(I32x2::splat(value))
} }
#[inline] #[inline]
pub fn x(&self) -> i32 { pub fn x(self) -> i32 {
self.0[0] self.0[0]
} }
#[inline] #[inline]
pub fn y(&self) -> i32 { pub fn y(self) -> i32 {
self.0[1] self.0[1]
} }
@ -216,18 +215,18 @@ impl Vector2I {
} }
#[inline] #[inline]
pub fn scale(&self, factor: i32) -> Vector2I { pub fn scale(self, factor: i32) -> Vector2I {
Vector2I(self.0 * I32x4::splat(factor)) Vector2I(self.0 * I32x2::splat(factor))
} }
#[inline] #[inline]
pub fn scale_xy(&self, factors: Vector2I) -> Vector2I { pub fn scale_xy(self, factors: Vector2I) -> Vector2I {
Vector2I(self.0 * factors.0) Vector2I(self.0 * factors.0)
} }
#[inline] #[inline]
pub fn to_f32(&self) -> Vector2F { pub fn to_f32(self) -> Vector2F {
Vector2F(self.0.to_f32x4()) Vector2F(self.0.to_f32x2())
} }
} }
@ -257,8 +256,7 @@ impl Sub<Vector2I> for Vector2I {
impl PartialEq for Vector2I { impl PartialEq for Vector2I {
#[inline] #[inline]
fn eq(&self, other: &Vector2I) -> bool { fn eq(&self, other: &Vector2I) -> bool {
let results = self.0.packed_eq(other.0); self.0.packed_eq(other.0).is_all_ones()
results[0] != 0 && results[1] != 0
} }
} }
@ -279,7 +277,7 @@ impl Vector4F {
#[inline] #[inline]
pub fn to_2d(self) -> Vector2F { pub fn to_2d(self) -> Vector2F {
Vector2F(self.0) Vector2F(self.0.xy())
} }
#[inline] #[inline]
@ -303,7 +301,7 @@ impl Vector4F {
} }
#[inline] #[inline]
pub fn scale(&self, x: f32) -> Vector4F { pub fn scale(self, x: f32) -> Vector4F {
let mut factors = F32x4::splat(x); let mut factors = F32x4::splat(x);
factors[3] = 1.0; factors[3] = 1.0;
Vector4F(self.0 * factors) Vector4F(self.0 * factors)
@ -335,7 +333,7 @@ impl Vector4F {
} }
#[inline] #[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) self.0.approx_eq(other.0, epsilon)
} }
@ -377,6 +375,16 @@ impl Mul<Vector4F> 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 { impl Default for Vector4F {
#[inline] #[inline]
fn default() -> Vector4F { fn default() -> Vector4F {

View File

@ -14,9 +14,9 @@ use crate::resources::ResourceLoader;
use image::ImageFormat; use image::ImageFormat;
use pathfinder_content::color::ColorF; use pathfinder_content::color::ColorF;
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform3d::Transform3DF; use pathfinder_geometry::transform3d::Transform4F;
use pathfinder_geometry::vector::Vector2I; use pathfinder_geometry::vector::Vector2I;
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::{F32x2, F32x4};
use std::time::Duration; use std::time::Duration;
pub mod resources; pub mod resources;
@ -153,7 +153,7 @@ pub enum ShaderKind {
pub enum UniformData { pub enum UniformData {
Int(i32), Int(i32),
Mat4([F32x4; 4]), Mat4([F32x4; 4]),
Vec2(F32x4), Vec2(F32x2),
Vec4(F32x4), Vec4(F32x4),
TextureUnit(u32), TextureUnit(u32),
} }
@ -286,7 +286,7 @@ pub enum TextureData {
impl UniformData { impl UniformData {
#[inline] #[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]) UniformData::Mat4([transform.c0, transform.c1, transform.c2, transform.c3])
} }
} }

View File

@ -47,7 +47,7 @@ use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, Dep
use pathfinder_gpu::{Primitive, RenderState, RenderTarget, ShaderKind, StencilFunc, TextureData}; use pathfinder_gpu::{Primitive, RenderState, RenderTarget, ShaderKind, StencilFunc, TextureData};
use pathfinder_gpu::{TextureFormat, UniformData, VertexAttrClass}; use pathfinder_gpu::{TextureFormat, UniformData, VertexAttrClass};
use pathfinder_gpu::{VertexAttrDescriptor, VertexAttrType}; use pathfinder_gpu::{VertexAttrDescriptor, VertexAttrType};
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::{F32x2, F32x4};
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::mem; use std::mem;
use std::ptr; 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)) Some(slice::from_raw_parts(&data[0] as *const F32x4 as *const u8, 4 * 16))
} }
UniformData::Vec2(ref data) => { 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) => { UniformData::Vec4(ref data) => {
Some(slice::from_raw_parts(data as *const F32x4 as *const u8, 4 * 4)) Some(slice::from_raw_parts(data as *const F32x4 as *const u8, 4 * 4))

View File

@ -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<W: Write>(mut self, out: W) {
self.pdf.write_to(out);
}
}
pub fn make_pdf<W: Write>(mut writer: W, scene: &Scene) {
let mut pdf = PdfBuilder::new();
pdf.add_scene(scene);
pdf.write(writer);
}

View File

@ -160,7 +160,7 @@ impl BuiltObject {
fn add_fill( fn add_fill(
&mut self, &mut self,
builder: &SceneBuilder, builder: &SceneBuilder,
segment: &LineSegment2F, segment: LineSegment2F,
tile_coords: Vector2I, tile_coords: Vector2I,
) { ) {
debug!("add_fill({:?} ({:?}))", segment, tile_coords); debug!("add_fill({:?} ({:?}))", segment, tile_coords);
@ -171,31 +171,19 @@ impl BuiltObject {
}; };
debug_assert_eq!(TILE_WIDTH, TILE_HEIGHT); 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 tile_size = F32x4::splat(TILE_WIDTH as f32);
let (min, max) = ( let tile_upper_left = tile_coords.to_f32().0.to_f32x4().xyxy() * tile_size;
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;
// Convert to 4.8 fixed point.
let segment = (segment.0 - tile_upper_left) * F32x4::splat(256.0); let segment = (segment.0 - tile_upper_left) * F32x4::splat(256.0);
let segment = segment let (min, max) = (F32x4::default(), F32x4::splat((TILE_WIDTH * 256 - 1) as f32));
.clamp(min, max) let segment = segment.clamp(min, max).to_i32x4();
.to_i32x4() let (from_x, from_y, to_x, to_y) = (segment[0], segment[1], segment[2], segment[3]);
.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);
// Cull degenerate fills. // Cull degenerate fills.
if (px.0 & 0xf) as u8 == ((px.0 >> 8) & 0xf) as u8 if from_x == to_x {
&& (subpx.0 & 0xff) as u8 == ((subpx.0 >> 16) & 0xff) as u8
{
debug!("... culling!"); debug!("... culling!");
return; return;
} }
@ -203,10 +191,20 @@ impl BuiltObject {
// Allocate global tile if necessary. // Allocate global tile if necessary.
let alpha_tile_index = self.get_or_allocate_alpha_tile_index(builder, tile_coords); 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"); debug!("... OK, pushing");
self.fills.push(FillBatchPrimitive { self.fills.push(FillBatchPrimitive {
px, px: LineSegmentU4 { from: px[0] as u8, to: px[2] as u8 },
subpx, 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, alpha_tile_index,
}); });
} }
@ -256,7 +254,7 @@ impl BuiltObject {
); );
while winding != 0 { while winding != 0 {
self.add_fill(builder, &segment, tile_coords); self.add_fill(builder, segment, tile_coords);
if winding < 0 { if winding < 0 {
winding += 1 winding += 1
} else { } else {
@ -315,7 +313,7 @@ impl BuiltObject {
let fill_segment = LineSegment2F::new(fill_from, fill_to); let fill_segment = LineSegment2F::new(fill_from, fill_to);
let fill_tile_coords = Vector2I::new(subsegment_tile_x, tile_y); 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);
} }
} }

View File

@ -90,9 +90,10 @@ impl SceneProxy {
renderer.end_scene(); renderer.end_scene();
} }
pub fn as_svg(&self) -> Vec<u8> { #[inline]
pub fn copy_scene(&self) -> Scene {
let (sender, receiver) = mpsc::channel(); let (sender, receiver) = mpsc::channel();
self.sender.send(MainToWorkerMsg::GetSVG(sender)).unwrap(); self.sender.send(MainToWorkerMsg::CopyScene(sender)).unwrap();
receiver.recv().unwrap() receiver.recv().unwrap()
} }
} }
@ -104,22 +105,18 @@ fn scene_thread<E>(mut scene: Scene,
while let Ok(msg) = main_to_worker_receiver.recv() { while let Ok(msg) = main_to_worker_receiver.recv() {
match msg { match msg {
MainToWorkerMsg::ReplaceScene(new_scene) => scene = new_scene, 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::SetViewBox(new_view_box) => scene.set_view_box(new_view_box),
MainToWorkerMsg::Build(options, listener) => scene.build(options, listener, &executor), 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();
}
} }
} }
} }
enum MainToWorkerMsg { enum MainToWorkerMsg {
ReplaceScene(Scene), ReplaceScene(Scene),
CopyScene(Sender<Scene>),
SetViewBox(RectF), SetViewBox(RectF),
Build(BuildOptions, Box<dyn RenderCommandListener>), Build(BuildOptions, Box<dyn RenderCommandListener>),
GetSVG(Sender<Vec<u8>>),
} }
pub struct RenderCommandStream { pub struct RenderCommandStream {

View File

@ -16,14 +16,14 @@ use crate::post::DefringingKernel;
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
use pathfinder_geometry::vector::{Vector2I, Vector4F}; use pathfinder_geometry::vector::{Vector2I, Vector4F};
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform3d::Transform3DF; use pathfinder_geometry::transform3d::Transform4F;
use pathfinder_content::color::ColorF; use pathfinder_content::color::ColorF;
use pathfinder_gpu::resources::ResourceLoader; use pathfinder_gpu::resources::ResourceLoader;
use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, ClearOps}; use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, ClearOps};
use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderOptions, RenderState}; use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderOptions, RenderState};
use pathfinder_gpu::{RenderTarget, StencilFunc, StencilState, TextureFormat, UniformData}; use pathfinder_gpu::{RenderTarget, StencilFunc, StencilState, TextureFormat, UniformData};
use pathfinder_gpu::{VertexAttrClass, VertexAttrDescriptor, VertexAttrType}; use pathfinder_gpu::{VertexAttrClass, VertexAttrDescriptor, VertexAttrType};
use pathfinder_simd::default::{F32x4, I32x4}; use pathfinder_simd::default::{F32x2, F32x4};
use std::cmp; use std::cmp;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::mem; use std::mem;
@ -447,15 +447,10 @@ where
textures: &[&self.area_lut_texture], textures: &[&self.area_lut_texture],
uniforms: &[ uniforms: &[
(&self.fill_program.framebuffer_size_uniform, (&self.fill_program.framebuffer_size_uniform,
UniformData::Vec2(I32x4::new(MASK_FRAMEBUFFER_WIDTH, UniformData::Vec2(F32x2::new(MASK_FRAMEBUFFER_WIDTH as f32,
MASK_FRAMEBUFFER_HEIGHT, MASK_FRAMEBUFFER_HEIGHT as f32))),
0,
0).to_f32x4())),
(&self.fill_program.tile_size_uniform, (&self.fill_program.tile_size_uniform,
UniformData::Vec2(I32x4::new(TILE_WIDTH as i32, UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))),
TILE_HEIGHT as i32,
0,
0).to_f32x4())),
(&self.fill_program.area_lut_uniform, UniformData::TextureUnit(0)), (&self.fill_program.area_lut_uniform, UniformData::TextureUnit(0)),
], ],
viewport: self.mask_viewport(), viewport: self.mask_viewport(),
@ -473,30 +468,28 @@ where
self.buffered_fills.clear(); 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) { fn draw_alpha_tiles(&mut self, count: u32) {
let clear_color = self.clear_color_for_draw_operation(); let clear_color = self.clear_color_for_draw_operation();
let alpha_tile_vertex_array = self.alpha_tile_vertex_array(); let alpha_tile_vertex_array = self.alpha_tile_vertex_array();
let alpha_tile_program = self.alpha_tile_program(); 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 textures = vec![self.device.framebuffer_texture(&self.mask_framebuffer)];
let mut uniforms = vec![ let mut uniforms = vec![
(&alpha_tile_program.framebuffer_size_uniform, (&alpha_tile_program.transform_uniform,
UniformData::Vec2(draw_viewport.size().to_f32().0)), UniformData::Mat4(self.tile_transform().to_columns())),
(&alpha_tile_program.tile_size_uniform, (&alpha_tile_program.tile_size_uniform,
UniformData::Vec2(I32x4::new(TILE_WIDTH as i32, UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))),
TILE_HEIGHT as i32,
0,
0).to_f32x4())),
(&alpha_tile_program.stencil_texture_uniform, UniformData::TextureUnit(0)), (&alpha_tile_program.stencil_texture_uniform, UniformData::TextureUnit(0)),
(&alpha_tile_program.stencil_texture_size_uniform, (&alpha_tile_program.stencil_texture_size_uniform,
UniformData::Vec2(I32x4::new(MASK_FRAMEBUFFER_WIDTH, UniformData::Vec2(F32x2::new(MASK_FRAMEBUFFER_WIDTH as f32,
MASK_FRAMEBUFFER_HEIGHT, MASK_FRAMEBUFFER_HEIGHT as f32))),
0,
0).to_f32x4())),
// FIXME(pcwalton): Fill this in properly!
(&alpha_tile_program.view_box_origin_uniform, UniformData::Vec2(F32x4::default())),
]; ];
match self.render_mode { match self.render_mode {
@ -509,7 +502,7 @@ where
UniformData::Vec2(self.device UniformData::Vec2(self.device
.texture_size(paint_texture) .texture_size(paint_texture)
.0 .0
.to_f32x4()))); .to_f32x2())));
} }
RenderMode::Monochrome { .. } if self.postprocessing_needed() => { RenderMode::Monochrome { .. } if self.postprocessing_needed() => {
uniforms.push((&self.alpha_monochrome_tile_program.color_uniform, uniforms.push((&self.alpha_monochrome_tile_program.color_uniform,
@ -528,7 +521,7 @@ where
primitive: Primitive::Triangles, primitive: Primitive::Triangles,
textures: &textures, textures: &textures,
uniforms: &uniforms, uniforms: &uniforms,
viewport: draw_viewport, viewport: self.draw_viewport(),
options: RenderOptions { options: RenderOptions {
blend: BlendState::RGBSrcAlphaAlphaOneMinusSrcAlpha, blend: BlendState::RGBSrcAlphaAlphaOneMinusSrcAlpha,
stencil: self.stencil_state(), stencil: self.stencil_state(),
@ -546,18 +539,12 @@ where
let solid_tile_vertex_array = self.solid_tile_vertex_array(); let solid_tile_vertex_array = self.solid_tile_vertex_array();
let solid_tile_program = self.solid_tile_program(); let solid_tile_program = self.solid_tile_program();
let draw_viewport = self.draw_viewport();
let mut textures = vec![]; let mut textures = vec![];
let mut uniforms = vec![ let mut uniforms = vec![
(&solid_tile_program.framebuffer_size_uniform, (&solid_tile_program.transform_uniform,
UniformData::Vec2(draw_viewport.size().0.to_f32x4())), UniformData::Mat4(self.tile_transform().to_columns())),
(&solid_tile_program.tile_size_uniform, (&solid_tile_program.tile_size_uniform,
UniformData::Vec2(I32x4::new(TILE_WIDTH as i32, UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))),
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())),
]; ];
match self.render_mode { match self.render_mode {
@ -570,7 +557,7 @@ where
UniformData::Vec2(self.device UniformData::Vec2(self.device
.texture_size(paint_texture) .texture_size(paint_texture)
.0 .0
.to_f32x4()))); .to_f32x2())));
} }
RenderMode::Monochrome { .. } if self.postprocessing_needed() => { RenderMode::Monochrome { .. } if self.postprocessing_needed() => {
uniforms.push((&self.solid_monochrome_tile_program.color_uniform, uniforms.push((&self.solid_monochrome_tile_program.color_uniform,
@ -589,7 +576,7 @@ where
primitive: Primitive::Triangles, primitive: Primitive::Triangles,
textures: &textures, textures: &textures,
uniforms: &uniforms, uniforms: &uniforms,
viewport: draw_viewport, viewport: self.draw_viewport(),
options: RenderOptions { options: RenderOptions {
stencil: self.stencil_state(), stencil: self.stencil_state(),
clear_ops: ClearOps { color: clear_color, ..ClearOps::default() }, clear_ops: ClearOps { color: clear_color, ..ClearOps::default() },
@ -635,7 +622,7 @@ where
UniformData::Vec2(main_viewport.size().to_f32().0)), UniformData::Vec2(main_viewport.size().to_f32().0)),
(&self.postprocess_program.source_uniform, UniformData::TextureUnit(0)), (&self.postprocess_program.source_uniform, UniformData::TextureUnit(0)),
(&self.postprocess_program.source_size_uniform, (&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.gamma_lut_uniform, UniformData::TextureUnit(1)),
(&self.postprocess_program.fg_color_uniform, UniformData::Vec4(fg_color.0)), (&self.postprocess_program.fg_color_uniform, UniformData::Vec4(fg_color.0)),
(&self.postprocess_program.bg_color_uniform, UniformData::Vec4(bg_color.0)), (&self.postprocess_program.bg_color_uniform, UniformData::Vec4(bg_color.0)),
@ -747,8 +734,8 @@ where
pub fn reproject_texture( pub fn reproject_texture(
&mut self, &mut self,
texture: &D::Texture, texture: &D::Texture,
old_transform: &Transform3DF, old_transform: &Transform4F,
new_transform: &Transform3DF, new_transform: &Transform4F,
) { ) {
let clear_color = self.clear_color_for_draw_operation(); let clear_color = self.clear_color_for_draw_operation();
@ -1217,9 +1204,8 @@ where
D: Device, D: Device,
{ {
program: D::Program, program: D::Program,
framebuffer_size_uniform: D::Uniform, transform_uniform: D::Uniform,
tile_size_uniform: D::Uniform, tile_size_uniform: D::Uniform,
view_box_origin_uniform: D::Uniform,
} }
impl<D> SolidTileProgram<D> impl<D> SolidTileProgram<D>
@ -1233,15 +1219,9 @@ where
program_name, program_name,
"tile_solid", "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 tile_size_uniform = device.get_uniform(&program, "TileSize");
let view_box_origin_uniform = device.get_uniform(&program, "ViewBoxOrigin"); SolidTileProgram { program, transform_uniform, tile_size_uniform }
SolidTileProgram {
program,
framebuffer_size_uniform,
tile_size_uniform,
view_box_origin_uniform,
}
} }
} }
@ -1299,11 +1279,10 @@ where
D: Device, D: Device,
{ {
program: D::Program, program: D::Program,
framebuffer_size_uniform: D::Uniform, transform_uniform: D::Uniform,
tile_size_uniform: D::Uniform, tile_size_uniform: D::Uniform,
stencil_texture_uniform: D::Uniform, stencil_texture_uniform: D::Uniform,
stencil_texture_size_uniform: D::Uniform, stencil_texture_size_uniform: D::Uniform,
view_box_origin_uniform: D::Uniform,
} }
impl<D> AlphaTileProgram<D> impl<D> AlphaTileProgram<D>
@ -1317,18 +1296,16 @@ where
program_name, program_name,
"tile_alpha", "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 tile_size_uniform = device.get_uniform(&program, "TileSize");
let stencil_texture_uniform = device.get_uniform(&program, "StencilTexture"); let stencil_texture_uniform = device.get_uniform(&program, "StencilTexture");
let stencil_texture_size_uniform = device.get_uniform(&program, "StencilTextureSize"); let stencil_texture_size_uniform = device.get_uniform(&program, "StencilTextureSize");
let view_box_origin_uniform = device.get_uniform(&program, "ViewBoxOrigin");
AlphaTileProgram { AlphaTileProgram {
program, program,
framebuffer_size_uniform, transform_uniform,
tile_size_uniform, tile_size_uniform,
stencil_texture_uniform, stencil_texture_uniform,
stencil_texture_size_uniform, stencil_texture_size_uniform,
view_box_origin_uniform,
} }
} }
} }

View File

@ -12,7 +12,7 @@
use crate::gpu_data::RenderCommand; use crate::gpu_data::RenderCommand;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2DF; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::transform3d::Perspective; use pathfinder_geometry::transform3d::Perspective;
use pathfinder_geometry::vector::{Vector2F, Vector4F}; use pathfinder_geometry::vector::{Vector2F, Vector4F};
use pathfinder_content::clip::PolygonClipper3D; use pathfinder_content::clip::PolygonClipper3D;
@ -51,14 +51,14 @@ impl BuildOptions {
#[derive(Clone)] #[derive(Clone)]
pub enum RenderTransform { pub enum RenderTransform {
Transform2D(Transform2DF), Transform2D(Transform2F),
Perspective(Perspective), Perspective(Perspective),
} }
impl Default for RenderTransform { impl Default for RenderTransform {
#[inline] #[inline]
fn default() -> RenderTransform { fn default() -> RenderTransform {
RenderTransform::Transform2D(Transform2DF::default()) RenderTransform::Transform2D(Transform2F::default())
} }
} }
@ -83,7 +83,7 @@ impl RenderTransform {
debug!("-----"); debug!("-----");
debug!("bounds={:?} ORIGINAL quad={:?}", bounds, points); debug!("bounds={:?} ORIGINAL quad={:?}", bounds, points);
for point in &mut points { for point in &mut points {
*point = perspective.transform.transform_point(*point); *point = perspective.transform * *point;
} }
debug!("... PERSPECTIVE quad={:?}", points); debug!("... PERSPECTIVE quad={:?}", points);
@ -105,12 +105,7 @@ impl RenderTransform {
let inverse_transform = perspective.transform.inverse(); let inverse_transform = perspective.transform.inverse();
let clip_polygon = points let clip_polygon = points
.into_iter() .into_iter()
.map(|point| { .map(|point| (inverse_transform * point).perspective_divide().to_2d())
inverse_transform
.transform_point(point)
.perspective_divide()
.to_2d()
})
.collect(); .collect();
return PreparedRenderTransform::Perspective { return PreparedRenderTransform::Perspective {
perspective, perspective,
@ -140,7 +135,7 @@ pub(crate) type BoundingQuad = [Vector4F; 4];
pub(crate) enum PreparedRenderTransform { pub(crate) enum PreparedRenderTransform {
None, None,
Transform2D(Transform2DF), Transform2D(Transform2F),
Perspective { Perspective {
perspective: Perspective, perspective: Perspective,
clip_polygon: Vec<Vector2F>, clip_polygon: Vec<Vector2F>,

View File

@ -18,10 +18,9 @@ use crate::paint::{Paint, PaintId};
use hashbrown::HashMap; use hashbrown::HashMap;
use pathfinder_geometry::vector::Vector2F; use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2DF; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_content::color::ColorU; use pathfinder_content::color::ColorU;
use pathfinder_content::outline::Outline; use pathfinder_content::outline::Outline;
use std::io::{self, Write};
#[derive(Clone)] #[derive(Clone)]
pub struct Scene { pub struct Scene {
@ -116,12 +115,11 @@ impl Scene {
if options.transform.is_2d() || options.subpixel_aa_enabled { if options.transform.is_2d() || options.subpixel_aa_enabled {
let mut transform = match options.transform { let mut transform = match options.transform {
PreparedRenderTransform::Transform2D(transform) => transform, PreparedRenderTransform::Transform2D(transform) => transform,
PreparedRenderTransform::None => Transform2DF::default(), PreparedRenderTransform::None => Transform2F::default(),
PreparedRenderTransform::Perspective { .. } => unreachable!(), PreparedRenderTransform::Perspective { .. } => unreachable!(),
}; };
if options.subpixel_aa_enabled { if options.subpixel_aa_enabled {
transform = transform transform *= Transform2F::from_scale(Vector2F::new(3.0, 1.0))
.post_mul(&Transform2DF::from_scale(Vector2F::new(3.0, 1.0)))
} }
outline.transform(&transform); outline.transform(&transform);
} }
@ -173,31 +171,6 @@ impl Scene {
let prepared_options = options.prepare(self.bounds); let prepared_options = options.prepare(self.bounds);
SceneBuilder::new(self, &prepared_options, listener).build(executor) SceneBuilder::new(self, &prepared_options, listener).build(executor)
} }
pub fn write_svg<W>(&self, writer: &mut W) -> io::Result<()> where W: Write {
writeln!(
writer,
"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"{} {} {} {}\">",
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, " <path")?;
if !path_object.name.is_empty() {
write!(writer, " id=\"{}\"", path_object.name)?;
}
writeln!(
writer,
" fill=\"{:?}\" d=\"{:?}\" />",
paint.color, path_object.outline
)?;
}
writeln!(writer, "</svg>")?;
Ok(())
}
pub fn paths<'a>(&'a self) -> PathIter { pub fn paths<'a>(&'a self) -> PathIter {
PathIter { PathIter {
@ -211,10 +184,14 @@ pub struct PathIter<'a> {
pos: usize pos: usize
} }
impl<'a> Iterator for PathIter<'a> { 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<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let item = self.scene.paths.get(self.pos).map(|path_object| { 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; self.pos += 1;
item item

View File

@ -44,15 +44,11 @@ impl<T> DenseTileMap<T> {
#[inline] #[inline]
pub fn coords_to_index(&self, coords: Vector2I) -> Option<usize> { pub fn coords_to_index(&self, coords: Vector2I) -> Option<usize> {
// TODO(pcwalton): SIMD? if self.rect.contains_point(coords) {
if coords.x() < self.rect.min_x() Some(self.coords_to_index_unchecked(coords))
|| coords.x() >= self.rect.max_x() } else {
|| coords.y() < self.rect.min_y() None
|| coords.y() >= self.rect.max_y()
{
return None;
} }
Some(self.coords_to_index_unchecked(coords))
} }
#[inline] #[inline]

View File

@ -413,14 +413,11 @@ impl ActiveEdge {
} else { } else {
segment.baseline.to() 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 { fn from_segment_and_crossing(segment: &Segment, crossing: Vector2F) -> ActiveEdge {
ActiveEdge { ActiveEdge { segment: *segment, crossing }
segment: *segment,
crossing: *crossing,
}
} }
fn process(&mut self, builder: &SceneBuilder, built_object: &mut BuiltObject, tile_y: i32) { fn process(&mut self, builder: &SceneBuilder, built_object: &mut BuiltObject, tile_y: i32) {
@ -436,8 +433,8 @@ impl ActiveEdge {
if segment.is_line() { if segment.is_line() {
let line_segment = segment.as_line_segment(); let line_segment = segment.as_line_segment();
self.segment = self.segment =
match self.process_line_segment(&line_segment, builder, built_object, tile_y) { match self.process_line_segment(line_segment, builder, built_object, tile_y) {
Some(lower_part) => Segment::line(&lower_part), Some(lower_part) => Segment::line(lower_part),
None => Segment::none(), None => Segment::none(),
}; };
return; return;
@ -453,7 +450,7 @@ impl ActiveEdge {
let first_line_segment = let first_line_segment =
LineSegment2F::new(self.crossing, segment.baseline.upper_point()).orient(winding); LineSegment2F::new(self.crossing, segment.baseline.upper_point()).orient(winding);
if self 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() .is_some()
{ {
return; return;
@ -484,9 +481,9 @@ impl ActiveEdge {
); );
let line = before_segment.baseline.orient(winding); let line = before_segment.baseline.orient(winding);
match self.process_line_segment(&line, builder, built_object, tile_y) { match self.process_line_segment(line, builder, built_object, tile_y) {
Some(ref lower_part) if split_t == 1.0 => { Some(lower_part) if split_t == 1.0 => {
self.segment = Segment::line(&lower_part); self.segment = Segment::line(lower_part);
return; return;
} }
None if split_t == 1.0 => { None if split_t == 1.0 => {
@ -504,7 +501,7 @@ impl ActiveEdge {
fn process_line_segment( fn process_line_segment(
&mut self, &mut self,
line_segment: &LineSegment2F, line_segment: LineSegment2F,
builder: &SceneBuilder, builder: &SceneBuilder,
built_object: &mut BuiltObject, built_object: &mut BuiltObject,
tile_y: i32, tile_y: i32,
@ -516,7 +513,7 @@ impl ActiveEdge {
); );
if line_segment.max_y() <= tile_bottom { 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; return None;
} }

View File

@ -27,10 +27,9 @@ precision highp float;
uniform vec2 uFramebufferSize; uniform mat4 uTransform;
uniform vec2 uTileSize; uniform vec2 uTileSize;
uniform vec2 uStencilTextureSize; uniform vec2 uStencilTextureSize;
uniform vec2 uViewBoxOrigin;
in uvec2 aTessCoord; in uvec2 aTessCoord;
in uvec3 aTileOrigin; in uvec3 aTileOrigin;
@ -51,15 +50,14 @@ vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth){
void computeVaryings(){ void computeVaryings(){
vec2 origin = vec2(aTileOrigin . xy)+ vec2(aTileOrigin . z & 15u, aTileOrigin . z >> 4u)* 256.0; vec2 origin = vec2(aTileOrigin . xy)+ vec2(aTileOrigin . z & 15u, aTileOrigin . z >> 4u)* 256.0;
vec2 pixelPosition =(origin + vec2(aTessCoord))* uTileSize + uViewBoxOrigin; vec2 position =(origin + vec2(aTessCoord))* uTileSize;
vec2 position =(pixelPosition / uFramebufferSize * 2.0 - 1.0)* vec2(1.0, - 1.0);
vec2 maskTexCoordOrigin = computeTileOffset(uint(aTileIndex), uStencilTextureSize . x); vec2 maskTexCoordOrigin = computeTileOffset(uint(aTileIndex), uStencilTextureSize . x);
vec2 maskTexCoord = maskTexCoordOrigin + aTessCoord * uTileSize; vec2 maskTexCoord = maskTexCoordOrigin + aTessCoord * uTileSize;
vTexCoord = maskTexCoord / uStencilTextureSize; vTexCoord = maskTexCoord / uStencilTextureSize;
vBackdrop = float(aBackdrop); vBackdrop = float(aBackdrop);
vColor = getColor(); vColor = getColor();
gl_Position = vec4(position, 0.0, 1.0); gl_Position = uTransform * vec4(position, 0.0, 1.0);
} }

View File

@ -27,10 +27,9 @@ precision highp float;
uniform vec2 uFramebufferSize; uniform mat4 uTransform;
uniform vec2 uTileSize; uniform vec2 uTileSize;
uniform vec2 uStencilTextureSize; uniform vec2 uStencilTextureSize;
uniform vec2 uViewBoxOrigin;
in uvec2 aTessCoord; in uvec2 aTessCoord;
in uvec3 aTileOrigin; in uvec3 aTileOrigin;
@ -51,15 +50,14 @@ vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth){
void computeVaryings(){ void computeVaryings(){
vec2 origin = vec2(aTileOrigin . xy)+ vec2(aTileOrigin . z & 15u, aTileOrigin . z >> 4u)* 256.0; vec2 origin = vec2(aTileOrigin . xy)+ vec2(aTileOrigin . z & 15u, aTileOrigin . z >> 4u)* 256.0;
vec2 pixelPosition =(origin + vec2(aTessCoord))* uTileSize + uViewBoxOrigin; vec2 position =(origin + vec2(aTessCoord))* uTileSize;
vec2 position =(pixelPosition / uFramebufferSize * 2.0 - 1.0)* vec2(1.0, - 1.0);
vec2 maskTexCoordOrigin = computeTileOffset(uint(aTileIndex), uStencilTextureSize . x); vec2 maskTexCoordOrigin = computeTileOffset(uint(aTileIndex), uStencilTextureSize . x);
vec2 maskTexCoord = maskTexCoordOrigin + aTessCoord * uTileSize; vec2 maskTexCoord = maskTexCoordOrigin + aTessCoord * uTileSize;
vTexCoord = maskTexCoord / uStencilTextureSize; vTexCoord = maskTexCoord / uStencilTextureSize;
vBackdrop = float(aBackdrop); vBackdrop = float(aBackdrop);
vColor = getColor(); vColor = getColor();
gl_Position = vec4(position, 0.0, 1.0); gl_Position = uTransform * vec4(position, 0.0, 1.0);
} }

View File

@ -27,9 +27,8 @@ precision highp float;
uniform vec2 uFramebufferSize; uniform mat4 uTransform;
uniform vec2 uTileSize; uniform vec2 uTileSize;
uniform vec2 uViewBoxOrigin;
in uvec2 aTessCoord; in uvec2 aTessCoord;
in ivec2 aTileOrigin; in ivec2 aTileOrigin;
@ -39,11 +38,9 @@ out vec4 vColor;
vec4 getColor(); vec4 getColor();
void computeVaryings(){ void computeVaryings(){
vec2 pixelPosition = vec2(aTileOrigin + ivec2(aTessCoord))* uTileSize + uViewBoxOrigin; vec2 position = vec2(aTileOrigin + ivec2(aTessCoord))* uTileSize;
vec2 position =(pixelPosition / uFramebufferSize * 2.0 - 1.0)* vec2(1.0, - 1.0);
vColor = getColor(); vColor = getColor();
gl_Position = vec4(position, 0.0, 1.0); gl_Position = uTransform * vec4(position, 0.0, 1.0);
} }

View File

@ -27,9 +27,8 @@ precision highp float;
uniform vec2 uFramebufferSize; uniform mat4 uTransform;
uniform vec2 uTileSize; uniform vec2 uTileSize;
uniform vec2 uViewBoxOrigin;
in uvec2 aTessCoord; in uvec2 aTessCoord;
in ivec2 aTileOrigin; in ivec2 aTileOrigin;
@ -39,11 +38,9 @@ out vec4 vColor;
vec4 getColor(); vec4 getColor();
void computeVaryings(){ void computeVaryings(){
vec2 pixelPosition = vec2(aTileOrigin + ivec2(aTessCoord))* uTileSize + uViewBoxOrigin; vec2 position = vec2(aTileOrigin + ivec2(aTessCoord))* uTileSize;
vec2 position =(pixelPosition / uFramebufferSize * 2.0 - 1.0)* vec2(1.0, - 1.0);
vColor = getColor(); vColor = getColor();
gl_Position = vec4(position, 0.0, 1.0); gl_Position = uTransform * vec4(position, 0.0, 1.0);
} }

View File

@ -9,10 +9,9 @@ using namespace metal;
struct spvDescriptorSetBuffer0 struct spvDescriptorSetBuffer0
{ {
constant float2* uTileSize [[id(0)]]; constant float2* uTileSize [[id(0)]];
constant float2* uViewBoxOrigin [[id(1)]]; constant float2* uStencilTextureSize [[id(1)]];
constant float2* uFramebufferSize [[id(2)]]; constant float4x4* uTransform [[id(2)]];
constant float2* uStencilTextureSize [[id(3)]]; constant float4* uColor [[id(3)]];
constant float4* uColor [[id(4)]];
}; };
struct main0_out struct main0_out
@ -43,11 +42,10 @@ float4 getColor(thread float4 uColor)
return 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 origin = float2(aTileOrigin.xy) + (float2(float(aTileOrigin.z & 15u), float(aTileOrigin.z >> 4u)) * 256.0);
float2 pixelPosition = ((origin + float2(aTessCoord)) * uTileSize) + uViewBoxOrigin; float2 position = (origin + float2(aTessCoord)) * uTileSize;
float2 position = (((pixelPosition / uFramebufferSize) * 2.0) - float2(1.0)) * float2(1.0, -1.0);
uint param = uint(aTileIndex); uint param = uint(aTileIndex);
float param_1 = uStencilTextureSize.x; float param_1 = uStencilTextureSize.x;
float2 maskTexCoordOrigin = computeTileOffset(param, param_1, uTileSize); float2 maskTexCoordOrigin = computeTileOffset(param, param_1, uTileSize);
@ -55,13 +53,13 @@ void computeVaryings(thread float2 uTileSize, thread uint3& aTileOrigin, thread
vTexCoord = maskTexCoord / uStencilTextureSize; vTexCoord = maskTexCoord / uStencilTextureSize;
vBackdrop = float(aBackdrop); vBackdrop = float(aBackdrop);
vColor = getColor(uColor); 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)]]) vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]])
{ {
main0_out out = {}; 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; return out;
} }

View File

@ -9,11 +9,10 @@ using namespace metal;
struct spvDescriptorSetBuffer0 struct spvDescriptorSetBuffer0
{ {
constant float2* uTileSize [[id(0)]]; constant float2* uTileSize [[id(0)]];
constant float2* uViewBoxOrigin [[id(1)]]; constant float2* uStencilTextureSize [[id(1)]];
constant float2* uFramebufferSize [[id(2)]]; constant float4x4* uTransform [[id(2)]];
constant float2* uStencilTextureSize [[id(3)]]; texture2d<float> uPaintTexture [[id(3)]];
texture2d<float> uPaintTexture [[id(4)]]; sampler uPaintTextureSmplr [[id(4)]];
sampler uPaintTextureSmplr [[id(5)]];
}; };
struct main0_out struct main0_out
@ -45,11 +44,10 @@ float4 getColor(thread texture2d<float> uPaintTexture, thread const sampler uPai
return uPaintTexture.sample(uPaintTextureSmplr, aColorTexCoord, level(0.0)); 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<float> 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<float> 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 origin = float2(aTileOrigin.xy) + (float2(float(aTileOrigin.z & 15u), float(aTileOrigin.z >> 4u)) * 256.0);
float2 pixelPosition = ((origin + float2(aTessCoord)) * uTileSize) + uViewBoxOrigin; float2 position = (origin + float2(aTessCoord)) * uTileSize;
float2 position = (((pixelPosition / uFramebufferSize) * 2.0) - float2(1.0)) * float2(1.0, -1.0);
uint param = uint(aTileIndex); uint param = uint(aTileIndex);
float param_1 = uStencilTextureSize.x; float param_1 = uStencilTextureSize.x;
float2 maskTexCoordOrigin = computeTileOffset(param, param_1, uTileSize); float2 maskTexCoordOrigin = computeTileOffset(param, param_1, uTileSize);
@ -57,13 +55,13 @@ void computeVaryings(thread float2 uTileSize, thread uint3& aTileOrigin, thread
vTexCoord = maskTexCoord / uStencilTextureSize; vTexCoord = maskTexCoord / uStencilTextureSize;
vBackdrop = float(aBackdrop); vBackdrop = float(aBackdrop);
vColor = getColor(uPaintTexture, uPaintTextureSmplr, aColorTexCoord); 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)]]) vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]])
{ {
main0_out out = {}; 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; return out;
} }

View File

@ -9,9 +9,8 @@ using namespace metal;
struct spvDescriptorSetBuffer0 struct spvDescriptorSetBuffer0
{ {
constant float2* uTileSize [[id(0)]]; constant float2* uTileSize [[id(0)]];
constant float2* uViewBoxOrigin [[id(1)]]; constant float4x4* uTransform [[id(1)]];
constant float2* uFramebufferSize [[id(2)]]; constant float4* uColor [[id(2)]];
constant float4* uColor [[id(3)]];
}; };
struct main0_out struct main0_out
@ -31,18 +30,17 @@ float4 getColor(thread float4 uColor)
return 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 = float2(aTileOrigin + int2(aTessCoord)) * uTileSize;
float2 position = (((pixelPosition / uFramebufferSize) * 2.0) - float2(1.0)) * float2(1.0, -1.0);
vColor = getColor(uColor); 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)]]) vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]])
{ {
main0_out out = {}; 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; return out;
} }

View File

@ -9,10 +9,9 @@ using namespace metal;
struct spvDescriptorSetBuffer0 struct spvDescriptorSetBuffer0
{ {
constant float2* uTileSize [[id(0)]]; constant float2* uTileSize [[id(0)]];
constant float2* uViewBoxOrigin [[id(1)]]; constant float4x4* uTransform [[id(1)]];
constant float2* uFramebufferSize [[id(2)]]; texture2d<float> uPaintTexture [[id(2)]];
texture2d<float> uPaintTexture [[id(3)]]; sampler uPaintTextureSmplr [[id(3)]];
sampler uPaintTextureSmplr [[id(4)]];
}; };
struct main0_out struct main0_out
@ -33,18 +32,17 @@ float4 getColor(thread texture2d<float> uPaintTexture, thread const sampler uPai
return uPaintTexture.sample(uPaintTextureSmplr, aColorTexCoord, level(0.0)); 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<float> 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<float> uPaintTexture, thread const sampler uPaintTextureSmplr, thread float2& aColorTexCoord)
{ {
float2 pixelPosition = (float2(aTileOrigin + int2(aTessCoord)) * uTileSize) + uViewBoxOrigin; float2 position = float2(aTileOrigin + int2(aTessCoord)) * uTileSize;
float2 position = (((pixelPosition / uFramebufferSize) * 2.0) - float2(1.0)) * float2(1.0, -1.0);
vColor = getColor(uPaintTexture, uPaintTextureSmplr, aColorTexCoord); 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)]]) vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]])
{ {
main0_out out = {}; 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; return out;
} }

View File

@ -8,10 +8,9 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
uniform vec2 uFramebufferSize; uniform mat4 uTransform;
uniform vec2 uTileSize; uniform vec2 uTileSize;
uniform vec2 uStencilTextureSize; uniform vec2 uStencilTextureSize;
uniform vec2 uViewBoxOrigin;
in uvec2 aTessCoord; in uvec2 aTessCoord;
in uvec3 aTileOrigin; in uvec3 aTileOrigin;
@ -32,14 +31,13 @@ vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) {
void computeVaryings() { void computeVaryings() {
vec2 origin = vec2(aTileOrigin.xy) + vec2(aTileOrigin.z & 15u, aTileOrigin.z >> 4u) * 256.0; vec2 origin = vec2(aTileOrigin.xy) + vec2(aTileOrigin.z & 15u, aTileOrigin.z >> 4u) * 256.0;
vec2 pixelPosition = (origin + vec2(aTessCoord)) * uTileSize + uViewBoxOrigin; vec2 position = (origin + vec2(aTessCoord)) * uTileSize;
vec2 position = (pixelPosition / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0);
vec2 maskTexCoordOrigin = computeTileOffset(uint(aTileIndex), uStencilTextureSize.x); vec2 maskTexCoordOrigin = computeTileOffset(uint(aTileIndex), uStencilTextureSize.x);
vec2 maskTexCoord = maskTexCoordOrigin + aTessCoord * uTileSize; vec2 maskTexCoord = maskTexCoordOrigin + aTessCoord * uTileSize;
vTexCoord = maskTexCoord / uStencilTextureSize; vTexCoord = maskTexCoord / uStencilTextureSize;
vBackdrop = float(aBackdrop); vBackdrop = float(aBackdrop);
vColor = getColor(); vColor = getColor();
gl_Position = vec4(position, 0.0, 1.0); gl_Position = uTransform * vec4(position, 0.0, 1.0);
} }

View File

@ -8,9 +8,8 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
uniform vec2 uFramebufferSize; uniform mat4 uTransform;
uniform vec2 uTileSize; uniform vec2 uTileSize;
uniform vec2 uViewBoxOrigin;
in uvec2 aTessCoord; in uvec2 aTessCoord;
in ivec2 aTileOrigin; in ivec2 aTileOrigin;
@ -20,9 +19,7 @@ out vec4 vColor;
vec4 getColor(); vec4 getColor();
void computeVaryings() { void computeVaryings() {
vec2 pixelPosition = vec2(aTileOrigin + ivec2(aTessCoord)) * uTileSize + uViewBoxOrigin; vec2 position = vec2(aTileOrigin + ivec2(aTessCoord)) * uTileSize;
vec2 position = (pixelPosition / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0);
vColor = getColor(); vColor = getColor();
gl_Position = vec4(position, 0.0, 1.0); gl_Position = uTransform * vec4(position, 0.0, 1.0);
} }

View File

@ -8,17 +8,198 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::arch::aarch64::{self, float32x4_t, int32x4_t, uint32x4_t, uint64x2_t, uint8x16_t}; use std::arch::aarch64::{self, float32x2_t, float32x4_t, int32x2_t, int32x4_t};
use std::arch::aarch64::{uint8x8_t, uint8x8x2_t}; use std::arch::aarch64::{uint32x2_t, uint32x4_t};
use std::f32; use std::f32;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::mem; 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_f32x4;
mod swizzle_i32x4; 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<usize> 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<usize> 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<F32x2> for F32x2 {
type Output = F32x2;
#[inline]
fn add(self, other: F32x2) -> F32x2 {
unsafe { F32x2(simd_add(self.0, other.0)) }
}
}
impl Mul<F32x2> for F32x2 {
type Output = F32x2;
#[inline]
fn mul(self, other: F32x2) -> F32x2 {
unsafe { F32x2(simd_mul(self.0, other.0)) }
}
}
impl Sub<F32x2> 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)] #[derive(Clone, Copy)]
pub struct F32x4(pub float32x4_t); pub struct F32x4(pub float32x4_t);
@ -103,32 +284,56 @@ impl F32x4 {
unsafe { U32x4(simd_lt(self.0, other.0)) } unsafe { U32x4(simd_lt(self.0, other.0)) }
} }
// Converts these packed floats to integers. // Swizzle conversions
#[inline] #[inline]
pub fn to_i32x4(self) -> I32x4 { pub fn xy(self) -> F32x2 {
unsafe { I32x4(simd_cast(self.0)) } 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 // Concatenations
#[inline] #[inline]
pub fn concat_xy_xy(self, other: F32x4) -> F32x4 { 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] #[inline]
pub fn concat_xy_zw(self, other: F32x4) -> F32x4 { 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] #[inline]
pub fn concat_zw_zw(self, other: F32x4) -> F32x4 { 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] #[inline]
pub fn concat_wz_yx(self, other: F32x4) -> F32x4 { pub fn to_i32x4(self) -> I32x4 {
unsafe { F32x4(simd_shuffle4(self.0, other.0, [3, 2, 5, 4])) } unsafe { I32x4(simd_cast(self.0)) }
} }
} }
@ -200,7 +405,105 @@ impl Sub<F32x4> 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<usize> 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<usize> 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<I32x2> for I32x2 {
type Output = I32x2;
#[inline]
fn add(self, other: I32x2) -> I32x2 {
unsafe { I32x2(simd_add(self.0, other.0)) }
}
}
impl Sub<I32x2> for I32x2 {
type Output = I32x2;
#[inline]
fn sub(self, other: I32x2) -> I32x2 {
unsafe { I32x2(simd_sub(self.0, other.0)) }
}
}
impl Mul<I32x2> 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)] #[derive(Clone, Copy, Debug)]
pub struct I32x4(pub int32x4_t); pub struct I32x4(pub int32x4_t);
@ -216,11 +519,6 @@ impl I32x4 {
I32x4::new(x, x, x, x) I32x4::new(x, x, x, x)
} }
#[inline]
pub fn as_u8x16(self) -> U8x16 {
unsafe { U8x16(*mem::transmute::<&int32x4_t, &uint8x16_t>(&self.0)) }
}
#[inline] #[inline]
pub fn min(self, other: I32x4) -> I32x4 { pub fn min(self, other: I32x4) -> I32x4 {
unsafe { I32x4(simd_fmin(self.0, other.0)) } 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])) } 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 // Conversions
/// Converts these packed integers to floats. /// Converts these packed integers to floats.
@ -315,7 +640,60 @@ impl PartialEq for I32x4 {
} }
} }
// 32-bit unsigned integers impl BitAnd<I32x4> for I32x4 {
type Output = I32x4;
#[inline]
fn bitand(self, other: I32x4) -> I32x4 {
unsafe { I32x4(simd_and(self.0, other.0)) }
}
}
impl BitOr<I32x4> for I32x4 {
type Output = I32x4;
#[inline]
fn bitor(self, other: I32x4) -> I32x4 {
unsafe { I32x4(simd_or(self.0, other.0)) }
}
}
impl Shr<I32x4> 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<usize> 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)] #[derive(Clone, Copy)]
pub struct U32x4(pub uint32x4_t); pub struct U32x4(pub uint32x4_t);
@ -344,44 +722,6 @@ impl Index<usize> 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::<uint8x16_t, uint8x8x2_t>(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::<uint8x16_t, uint64x2_t>(self.0), 0);
mem::transmute::<u64, uint8x8_t>(low)
}
}
#[inline]
fn extract_high(self) -> uint8x8_t {
unsafe {
let high = simd_extract(mem::transmute::<uint8x16_t, uint64x2_t>(self.0), 1);
mem::transmute::<u64, uint8x8_t>(high)
}
}
}
// Intrinsics // Intrinsics
extern "platform-intrinsic" { extern "platform-intrinsic" {
@ -389,6 +729,11 @@ extern "platform-intrinsic" {
fn simd_mul<T>(x: T, y: T) -> T; fn simd_mul<T>(x: T, y: T) -> T;
fn simd_sub<T>(x: T, y: T) -> T; fn simd_sub<T>(x: T, y: T) -> T;
fn simd_shr<T>(x: T, y: T) -> T;
fn simd_and<T>(x: T, y: T) -> T;
fn simd_or<T>(x: T, y: T) -> T;
fn simd_fmin<T>(x: T, y: T) -> T; fn simd_fmin<T>(x: T, y: T) -> T;
fn simd_fmax<T>(x: T, y: T) -> T; fn simd_fmax<T>(x: T, y: T) -> T;
@ -397,15 +742,24 @@ extern "platform-intrinsic" {
fn simd_le<T, U>(x: T, y: T) -> U; fn simd_le<T, U>(x: T, y: T) -> U;
fn simd_lt<T, U>(x: T, y: T) -> U; fn simd_lt<T, U>(x: T, y: T) -> U;
fn simd_shuffle2<T, U>(x: T, y: T, idx: [u32; 2]) -> U;
fn simd_shuffle4<T, U>(x: T, y: T, idx: [u32; 4]) -> U; fn simd_shuffle4<T, U>(x: T, y: T, idx: [u32; 4]) -> U;
fn simd_cast<T, U>(x: T) -> U; fn simd_cast<T, U>(x: T) -> U;
fn simd_insert<T, U>(x: T, index: u32, value: U) -> T;
fn simd_extract<T, U>(x: T, index: u32) -> U;
} }
extern "C" { 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"] #[link_name = "llvm.fabs.v4f32"]
fn fabs_v4f32(a: float32x4_t) -> float32x4_t; fn fabs_v4f32(a: float32x4_t) -> float32x4_t;
#[link_name = "llvm.floor.v4f32"] #[link_name = "llvm.floor.v4f32"]
@ -417,6 +771,9 @@ extern "C" {
#[link_name = "llvm.sqrt.v4f32"] #[link_name = "llvm.sqrt.v4f32"]
fn sqrt_v4f32(a: float32x4_t) -> float32x4_t; 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"] #[link_name = "llvm.aarch64.neon.frecpe.v4f32"]
fn vrecpe_v4f32(a: float32x4_t) -> float32x4_t; fn vrecpe_v4f32(a: float32x4_t) -> float32x4_t;
} }

View File

@ -8,10 +8,84 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use crate::default::{F32x4, I32x4}; use crate::default::{F32x2, F32x4, I32x2, I32x4};
use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; 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 { impl F32x4 {
// Constructors // 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 { impl AddAssign for I32x4 {
#[inline] #[inline]

View File

@ -10,13 +10,182 @@
use std::f32; use std::f32;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::mem; use std::ops::{Add, BitAnd, BitOr, Index, IndexMut, Mul, Shr, Sub};
use std::ops::{Add, Index, IndexMut, Mul, Sub};
mod swizzle_f32x4; mod swizzle_f32x4;
mod swizzle_i32x4; 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<usize> for F32x2 {
type Output = f32;
#[inline]
fn index(&self, index: usize) -> &f32 {
&self.0[index]
}
}
impl IndexMut<usize> for F32x2 {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut f32 {
&mut self.0[index]
}
}
impl Add<F32x2> for F32x2 {
type Output = F32x2;
#[inline]
fn add(self, other: F32x2) -> F32x2 {
F32x2([self[0] + other[0], self[1] + other[1]])
}
}
impl Mul<F32x2> for F32x2 {
type Output = F32x2;
#[inline]
fn mul(self, other: F32x2) -> F32x2 {
F32x2([self[0] * other[0], self[1] * other[1]])
}
}
impl Sub<F32x2> 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)] #[derive(Clone, Copy, Default, PartialEq)]
pub struct F32x4(pub [f32; 4]); 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 // Concatenations
#[inline] #[inline]
@ -246,7 +442,84 @@ impl Sub<F32x4> 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<usize> for I32x2 {
type Output = i32;
#[inline]
fn index(&self, index: usize) -> &i32 {
&self.0[index]
}
}
impl IndexMut<usize> for I32x2 {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut i32 {
&mut self.0[index]
}
}
impl Add<I32x2> for I32x2 {
type Output = I32x2;
#[inline]
fn add(self, other: I32x2) -> I32x2 {
I32x2([self[0] + other[0], self[1] + other[1]])
}
}
impl Sub<I32x2> for I32x2 {
type Output = I32x2;
#[inline]
fn sub(self, other: I32x2) -> I32x2 {
I32x2([self[0] - other[0], self[1] - other[1]])
}
}
impl Mul<I32x2> 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)] #[derive(Clone, Copy, Default, Debug, PartialEq)]
pub struct I32x4([i32; 4]); pub struct I32x4([i32; 4]);
@ -263,10 +536,6 @@ impl I32x4 {
} }
#[inline] #[inline]
pub fn as_u8x16(self) -> U8x16 {
unsafe { U8x16(*mem::transmute::<&[i32; 4], &[u8; 16]>(&self.0)) }
}
#[inline] #[inline]
pub fn min(self, other: I32x4) -> I32x4 { pub fn min(self, other: I32x4) -> I32x4 {
I32x4([ I32x4([
@ -306,6 +575,28 @@ impl I32x4 {
I32x4([self[0], self[1], other[0], other[1]]) 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 // Conversions
/// Converts these packed integers to floats. /// Converts these packed integers to floats.
@ -374,7 +665,61 @@ impl Mul<I32x4> for I32x4 {
} }
} }
// 32-bit unsigned integers impl BitAnd<I32x4> 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<I32x4> 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<I32x4> 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<usize> for U32x2 {
type Output = u32;
#[inline]
fn index(&self, index: usize) -> &u32 {
&self.0[index]
}
}
// Four 32-bit unsigned integers
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct U32x4(pub [u32; 4]); pub struct U32x4(pub [u32; 4]);
@ -398,24 +743,3 @@ impl Index<usize> for U32x4 {
&self.0[index] &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)
}
}

View File

@ -12,12 +12,195 @@ use std::arch::x86_64::{self, __m128, __m128i, _MM_FROUND_TO_NEAREST_INT};
use std::cmp::PartialEq; use std::cmp::PartialEq;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::mem; 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_f32x4;
mod swizzle_i32x4; 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<usize> for F32x2 {
type Output = f32;
#[inline]
fn index(&self, index: usize) -> &f32 {
unsafe { &mem::transmute::<&u64, &[f32; 2]>(&self.0)[index] }
}
}
impl IndexMut<usize> 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<F32x2> for F32x2 {
type Output = F32x2;
#[inline]
fn add(self, other: F32x2) -> F32x2 {
(self.to_f32x4() + other.to_f32x4()).xy()
}
}
impl Mul<F32x2> for F32x2 {
type Output = F32x2;
#[inline]
fn mul(self, other: F32x2) -> F32x2 {
(self.to_f32x4() * other.to_f32x4()).xy()
}
}
impl Sub<F32x2> 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)] #[derive(Clone, Copy)]
pub struct F32x4(pub __m128); pub struct F32x4(pub __m128);
@ -126,6 +309,33 @@ impl F32x4 {
unsafe { I32x4(x86_64::_mm_cvtps_epi32(self.0)) } 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 // Concatenations
#[inline] #[inline]
@ -224,7 +434,140 @@ impl Sub<F32x4> 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<usize> for I32x2 {
type Output = i32;
#[inline]
fn index(&self, index: usize) -> &i32 {
unsafe { &mem::transmute::<&u64, &[i32; 2]>(&self.0)[index] }
}
}
impl IndexMut<usize> 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<I32x2> for I32x2 {
type Output = I32x2;
#[inline]
fn add(self, other: I32x2) -> I32x2 {
(self.to_i32x4() + other.to_i32x4()).xy()
}
}
impl Sub<I32x2> for I32x2 {
type Output = I32x2;
#[inline]
fn sub(self, other: I32x2) -> I32x2 {
(self.to_i32x4() - other.to_i32x4()).xy()
}
}
impl Mul<I32x2> 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)] #[derive(Clone, Copy)]
pub struct I32x4(pub __m128i); pub struct I32x4(pub __m128i);
@ -245,6 +588,33 @@ impl I32x4 {
unsafe { I32x4(x86_64::_mm_set1_epi32(x)) } 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 // Concatenations
#[inline] #[inline]
@ -259,11 +629,6 @@ impl I32x4 {
// Conversions // Conversions
#[inline]
pub fn as_u8x16(self) -> U8x16 {
U8x16(self.0)
}
/// Converts these packed integers to floats. /// Converts these packed integers to floats.
#[inline] #[inline]
pub fn to_f32x4(self) -> F32x4 { pub fn to_f32x4(self) -> F32x4 {
@ -343,6 +708,30 @@ impl Mul<I32x4> for I32x4 {
} }
} }
impl BitAnd<I32x4> 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<I32x4> 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<I32x4> 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 { impl Debug for I32x4 {
#[inline] #[inline]
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { 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)] #[derive(Clone, Copy)]
pub struct U32x4(pub __m128i); pub struct U32x4(pub __m128i);
@ -390,6 +796,13 @@ impl U32x4 {
unsafe { x86_64::_mm_test_all_zeros(self.0, self.0) != 0 } 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 // Packed comparisons
#[inline] #[inline]
@ -435,20 +848,3 @@ impl BitXor<U32x4> for U32x4 {
unsafe { U32x4(x86_64::_mm_xor_si128(self.0, other.0)) } 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)) }
}
}

View File

@ -17,10 +17,10 @@ use pathfinder_content::color::ColorU;
use pathfinder_content::outline::Outline; use pathfinder_content::outline::Outline;
use pathfinder_content::segment::{Segment, SegmentFlags}; use pathfinder_content::segment::{Segment, SegmentFlags};
use pathfinder_content::stroke::{LineCap, LineJoin, OutlineStrokeToFill, StrokeStyle}; 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::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2DF; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::Vector2F; use pathfinder_geometry::vector::Vector2F;
use pathfinder_renderer::paint::Paint; use pathfinder_renderer::paint::Paint;
use pathfinder_renderer::scene::{PathObject, Scene}; use pathfinder_renderer::scene::{PathObject, Scene};
@ -62,7 +62,7 @@ bitflags! {
impl BuiltSVG { impl BuiltSVG {
// TODO(pcwalton): Allow a global transform to be set. // TODO(pcwalton): Allow a global transform to be set.
pub fn from_tree(tree: Tree) -> BuiltSVG { pub fn from_tree(tree: Tree) -> BuiltSVG {
let global_transform = Transform2DF::default(); let global_transform = Transform2F::default();
let mut built_svg = BuiltSVG { let mut built_svg = BuiltSVG {
scene: Scene::new(), scene: Scene::new(),
@ -87,9 +87,9 @@ impl BuiltSVG {
built_svg 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 node_transform = usvg_transform_to_transform_2d(&node.transform());
let transform = transform.pre_mul(&node_transform); let transform = node_transform * *transform;
match *node.borrow() { match *node.borrow() {
NodeKind::Group(ref group) => { NodeKind::Group(ref group) => {
@ -119,7 +119,7 @@ impl BuiltSVG {
)); ));
let path = UsvgPathToSegments::new(path.segments.iter().cloned()); 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 outline = Outline::from_segments(path);
let name = format!("Fill({})", node.id()); 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 { fn usvg_transform_to_transform_2d(transform: &UsvgTransform) -> Transform2F {
Transform2DF::row_major( Transform2F::row_major(
transform.a as f32, transform.a as f32,
transform.b as f32, transform.b as f32,
transform.c as f32, transform.c as f32,
@ -318,7 +318,7 @@ where
} }
UsvgPathSegment::LineTo { x, y } => { UsvgPathSegment::LineTo { x, y } => {
let to = Vector2F::new(x as f32, y as f32); 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 { if self.just_moved {
segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH); segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH);
} }
@ -338,8 +338,8 @@ where
let ctrl1 = Vector2F::new(x2 as f32, y2 as f32); let ctrl1 = Vector2F::new(x2 as f32, y2 as f32);
let to = Vector2F::new(x as f32, y as f32); let to = Vector2F::new(x as f32, y as f32);
let mut segment = Segment::cubic( let mut segment = Segment::cubic(
&LineSegment2F::new(self.last_subpath_point, to), LineSegment2F::new(self.last_subpath_point, to),
&LineSegment2F::new(ctrl0, ctrl1), LineSegment2F::new(ctrl0, ctrl1),
); );
if self.just_moved { if self.just_moved {
segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH); segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH);
@ -349,7 +349,7 @@ where
Some(segment) Some(segment)
} }
UsvgPathSegment::ClosePath => { UsvgPathSegment::ClosePath => {
let mut segment = Segment::line(&LineSegment2F::new( let mut segment = Segment::line(LineSegment2F::new(
self.last_subpath_point, self.last_subpath_point,
self.first_subpath_point, self.first_subpath_point,
)); ));

View File

@ -93,13 +93,13 @@ impl Shape {
} }
#[inline] #[inline]
fn first(&self) -> &LineSegment { fn first(&self) -> LineSegment {
&self.outline.first().unwrap() self.outline.first().unwrap()
} }
#[inline] #[inline]
fn last(&self) -> &LineSegment { fn last(&self) -> LineSegment {
&self.outline.last().unwrap() self.outline.last().unwrap()
} }
#[inline] #[inline]

View File

@ -15,7 +15,7 @@ use font_kit::loader::Loader;
use lyon_path::builder::{FlatPathBuilder, PathBuilder}; use lyon_path::builder::{FlatPathBuilder, PathBuilder};
use pathfinder_content::outline::{Contour, Outline}; use pathfinder_content::outline::{Contour, Outline};
use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle}; use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle};
use pathfinder_geometry::transform2d::Transform2DF; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::Vector2F; use pathfinder_geometry::vector::Vector2F;
use pathfinder_renderer::paint::PaintId; use pathfinder_renderer::paint::PaintId;
use pathfinder_renderer::scene::{PathObject, Scene}; use pathfinder_renderer::scene::{PathObject, Scene};
@ -27,7 +27,7 @@ pub trait SceneExt {
fn push_glyph<F>(&mut self, fn push_glyph<F>(&mut self,
font: &F, font: &F,
glyph_id: u32, glyph_id: u32,
transform: &Transform2DF, transform: &Transform2F,
render_mode: TextRenderMode, render_mode: TextRenderMode,
hinting_options: HintingOptions, hinting_options: HintingOptions,
paint_id: PaintId) paint_id: PaintId)
@ -37,7 +37,7 @@ pub trait SceneExt {
fn push_layout(&mut self, fn push_layout(&mut self,
layout: &Layout, layout: &Layout,
style: &TextStyle, style: &TextStyle,
transform: &Transform2DF, transform: &Transform2F,
render_mode: TextRenderMode, render_mode: TextRenderMode,
hinting_options: HintingOptions, hinting_options: HintingOptions,
paint_id: PaintId) paint_id: PaintId)
@ -47,7 +47,7 @@ pub trait SceneExt {
text: &str, text: &str,
style: &TextStyle, style: &TextStyle,
collection: &FontCollection, collection: &FontCollection,
transform: &Transform2DF, transform: &Transform2F,
render_mode: TextRenderMode, render_mode: TextRenderMode,
hinting_options: HintingOptions, hinting_options: HintingOptions,
paint_id: PaintId) paint_id: PaintId)
@ -59,7 +59,7 @@ impl SceneExt for Scene {
fn push_glyph<F>(&mut self, fn push_glyph<F>(&mut self,
font: &F, font: &F,
glyph_id: u32, glyph_id: u32,
transform: &Transform2DF, transform: &Transform2F,
render_mode: TextRenderMode, render_mode: TextRenderMode,
hinting_options: HintingOptions, hinting_options: HintingOptions,
paint_id: PaintId) paint_id: PaintId)
@ -82,7 +82,7 @@ impl SceneExt for Scene {
fn push_layout(&mut self, fn push_layout(&mut self,
layout: &Layout, layout: &Layout,
style: &TextStyle, style: &TextStyle,
transform: &Transform2DF, transform: &Transform2F,
render_mode: TextRenderMode, render_mode: TextRenderMode,
hinting_options: HintingOptions, hinting_options: HintingOptions,
paint_id: PaintId) paint_id: PaintId)
@ -93,8 +93,7 @@ impl SceneExt for Scene {
// FIXME(pcwalton): Cache this! // FIXME(pcwalton): Cache this!
let scale = style.size / (font.metrics().units_per_em as f32); let scale = style.size / (font.metrics().units_per_em as f32);
let scale = Vector2F::new(scale, -scale); let scale = Vector2F::new(scale, -scale);
let transform = let transform = *transform * Transform2F::from_scale(scale).translate(offset);
Transform2DF::from_scale(scale).post_mul(transform).post_translate(offset);
self.push_glyph(font, self.push_glyph(font,
glyph.glyph_id, glyph.glyph_id,
&transform, &transform,
@ -110,7 +109,7 @@ impl SceneExt for Scene {
text: &str, text: &str,
style: &TextStyle, style: &TextStyle,
collection: &FontCollection, collection: &FontCollection,
transform: &Transform2DF, transform: &Transform2F,
render_mode: TextRenderMode, render_mode: TextRenderMode,
hinting_options: HintingOptions, hinting_options: HintingOptions,
paint_id: PaintId) paint_id: PaintId)
@ -129,11 +128,11 @@ pub enum TextRenderMode {
struct OutlinePathBuilder { struct OutlinePathBuilder {
outline: Outline, outline: Outline,
current_contour: Contour, current_contour: Contour,
transform: Transform2DF, transform: Transform2F,
} }
impl OutlinePathBuilder { impl OutlinePathBuilder {
fn new(transform: &Transform2DF) -> OutlinePathBuilder { fn new(transform: &Transform2F) -> OutlinePathBuilder {
OutlinePathBuilder { OutlinePathBuilder {
outline: Outline::new(), outline: Outline::new(),
current_contour: Contour::new(), current_contour: Contour::new(),
@ -148,7 +147,7 @@ impl OutlinePathBuilder {
} }
fn convert_point(&self, point: Point2D<f32>) -> Vector2F { fn convert_point(&self, point: Point2D<f32>) -> Vector2F {
self.transform.transform_point(Vector2F::new(point.x, point.y)) self.transform * Vector2F::new(point.x, point.y)
} }
} }

View File

@ -181,7 +181,7 @@ impl<D> UIPresenter<D> where D: Device {
primitive, primitive,
uniforms: &[ uniforms: &[
(&self.solid_program.framebuffer_size_uniform, (&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)), (&self.solid_program.color_uniform, get_color_uniform(color)),
], ],
textures: &[], textures: &[],
@ -414,11 +414,11 @@ impl<D> UIPresenter<D> where D: Device {
textures: &[&texture], textures: &[&texture],
uniforms: &[ uniforms: &[
(&self.texture_program.framebuffer_size_uniform, (&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.color_uniform, get_color_uniform(color)),
(&self.texture_program.texture_uniform, UniformData::TextureUnit(0)), (&self.texture_program.texture_uniform, UniformData::TextureUnit(0)),
(&self.texture_program.texture_size_uniform, (&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), viewport: RectI::new(Vector2I::default(), self.framebuffer_size),
options: RenderOptions { options: RenderOptions {

View File

@ -1,5 +1,5 @@
[package] [package]
name = "svg2pdf" name = "convert"
version = "0.1.0" version = "0.1.0"
authors = ["Sebastian Köln <sebk@rynx.org>"] authors = ["Sebastian Köln <sebk@rynx.org>"]
edition = "2018" edition = "2018"
@ -7,6 +7,6 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
pathfinder_export = { path = "../../export" }
pathfinder_svg = { path = "../../svg" } pathfinder_svg = { path = "../../svg" }
pathfinder_pdf = { path = "../../pdf" }
usvg = "*" usvg = "*"

27
utils/convert/src/main.rs Normal file
View File

@ -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<dyn Error>> {
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(())
}