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
/shaders/build
/c/build
/examples/macos_app/Pathfinder\ Example.xcodeproj/project.xcworkspace/xcuserdata/*
# Editors
*.swp

39
Cargo.lock generated
View File

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

View File

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

View File

@ -3,7 +3,7 @@
# Pathfinder 3
Pathfinder 3 is a fast, practical, GPU-based rasterizer for fonts and vector graphics using OpenGL
and OpenGL ES 3.0+.
3.0+, OpenGL ES 3.0+, or Metal.
Please note that Pathfinder is under heavy development and is incomplete in various areas.
@ -34,7 +34,7 @@ The project features:
* Fast GPU rendering, even at small pixel sizes. Even on lower-end GPUs, Pathfinder typically
matches or exceeds the performance of the best CPU rasterizers. The difference is particularly
pronouced at large sizes, where Pathfinder regularly achieves multi-factor speedups. All shaders
pronounced at large sizes, where Pathfinder regularly achieves multi-factor speedups. All shaders
have no loops and minimal branching.
* Advanced font rendering. Pathfinder can render fonts with slight hinting and can perform subpixel
@ -75,12 +75,6 @@ The SDL 2 library requires some additional manual installation steps. Follow the
[`rust-sdl2` installation instructions](https://github.com/Rust-SDL2/rust-sdl2#sdl20-development-libraries) to make sure the libraries are installed. Note that SDL2 is
only required to run the demo; Pathfinder itself has no dependency on the library.
On macOS, it is recommended that you force the use of the integrated GPU, as issues with Apple's
OpenGL drivers may limit performance on discrete GPUs. You can use
[gfxCardStatus.app](https://gfx.io/) for this. See the
[wiki](https://github.com/pcwalton/pathfinder/wiki/GPU-driver-compatibility) for more information
on GPU compatibility issues.
## Authors
The primary author is Patrick Walton (@pcwalton), with contributions from the Servo development

View File

@ -2,18 +2,53 @@ language = "C"
header = """\
/* Generated code. Do not edit; instead run `cargo build` in `pathfinder_c`. */
#ifndef PF_PATHFINDER_H
#define PF_PATHFINDER_H
#ifdef __APPLE__
#include <QuartzCore/QuartzCore.h>
#endif
#ifdef __cplusplus
extern \"C\" {
#endif
"""
trailer = """\
#ifdef __cplusplus
}
#endif
#endif
"""
trailer = "}"
include_guard = "PF_PATHFINDER_H"
include_version = true
[parse]
parse_deps = true
include = [
"font-kit",
"pathfinder_canvas",
"pathfinder_content",
"pathfinder_geometry",
"pathfinder_gl",
"pathfinder_gpu",
"pathfinder_metal",
"pathfinder_renderer",
]
[export.rename]
"BuildOptions" = "PFBuildOptionsPrivate"
"CanvasFontContext" = "PFCanvasFontContextPrivate"
"CanvasRenderingContext2D" = "PFCanvasRenderingContext2DPrivate"
"DestFramebuffer_GLDevice" = "PFDestFramebufferGLDevicePrivate"
"DestFramebuffer_MetalDevice" = "PFDestFramebufferMetalDevicePrivate"
"FillStyle" = "PFFillStylePrivate"
"GLDevice" = "PFGLDevicePrivate"
"Handle" = "FKHandlePrivate"
"MetalDevice" = "PFMetalDevicePrivate"
"Path2D" = "PFPath2DPrivate"
"RenderTransform" = "PFRenderTransformPrivate"
"Renderer_GLDevice" = "PFRendererGLDevicePrivate"
"Renderer_MetalDevice" = "PFRendererMetalDevicePrivate"
"ResourceLoaderWrapper" = "PFResourceLoaderWrapperPrivate"
"Scene" = "PFScenePrivate"
"SceneProxy" = "PFSceneProxyPrivate"

View File

@ -19,6 +19,8 @@ use pathfinder_content::color::{ColorF, ColorU};
use pathfinder_content::outline::ArcDirection;
use pathfinder_content::stroke::LineCap;
use pathfinder_geometry::rect::{RectF, RectI};
use pathfinder_geometry::transform2d::{Matrix2x2F, Transform2F};
use pathfinder_geometry::transform3d::{Perspective, Transform4F};
use pathfinder_geometry::vector::{Vector2F, Vector2I};
use pathfinder_gl::{GLDevice, GLVersion};
use pathfinder_gpu::resources::{FilesystemResourceLoader, ResourceLoader};
@ -26,7 +28,7 @@ use pathfinder_renderer::concurrent::rayon::RayonExecutor;
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions};
use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::BuildOptions;
use pathfinder_renderer::options::{BuildOptions, RenderTransform};
use pathfinder_renderer::scene::Scene;
use pathfinder_simd::default::F32x4;
use std::ffi::CString;
@ -60,6 +62,11 @@ pub const PF_TEXT_ALIGN_RIGHT: u8 = 2;
pub const PF_ARC_DIRECTION_CW: u8 = 0;
pub const PF_ARC_DIRECTION_CCW: u8 = 1;
// `gl`
pub const PF_GL_VERSION_GL3: u8 = 0;
pub const PF_GL_VERSION_GLES3: u8 = 1;
// `renderer`
pub const PF_RENDERER_OPTIONS_FLAGS_HAS_BACKGROUND_COLOR: u8 = 0x1;
@ -120,10 +127,35 @@ pub struct PFRectI {
pub origin: PFVector2I,
pub lower_right: PFVector2I,
}
/// Row-major order.
#[repr(C)]
pub struct PFMatrix2x2F {
pub m00: f32, pub m01: f32,
pub m10: f32, pub m11: f32,
}
/// Row-major order.
#[repr(C)]
pub struct PFTransform2F {
pub matrix: PFMatrix2x2F,
pub vector: PFVector2F,
}
/// Row-major order.
#[repr(C)]
pub struct PFTransform4F {
pub m00: f32, pub m01: f32, pub m02: f32, pub m03: f32,
pub m10: f32, pub m11: f32, pub m12: f32, pub m13: f32,
pub m20: f32, pub m21: f32, pub m22: f32, pub m23: f32,
pub m30: f32, pub m31: f32, pub m32: f32, pub m33: f32,
}
#[repr(C)]
pub struct PFPerspective {
pub transform: PFTransform4F,
pub window_size: PFVector2I,
}
// `gl`
pub type PFGLDeviceRef = *mut GLDevice;
pub type PFGLVersion = GLVersion;
pub type PFGLVersion = u8;
pub type PFGLFunctionLoader = extern "C" fn(name: *const c_char, userdata: *mut c_void)
-> *const c_void;
// `gpu`
@ -135,7 +167,8 @@ pub type PFMetalDestFramebufferRef = *mut DestFramebuffer<MetalDevice>;
pub type PFMetalRendererRef = *mut Renderer<MetalDevice>;
// FIXME(pcwalton): Double-boxing is unfortunate. Remove this when `std::raw::TraitObject` is
// stable?
pub type PFResourceLoaderRef = *mut Box<dyn ResourceLoader>;
pub type PFResourceLoaderRef = *mut ResourceLoaderWrapper;
pub struct ResourceLoaderWrapper(Box<dyn ResourceLoader>);
// `metal`
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
@ -150,20 +183,18 @@ pub struct PFRendererOptions {
pub flags: PFRendererOptionsFlags,
}
pub type PFRendererOptionsFlags = u8;
// TODO(pcwalton)
#[repr(C)]
pub struct PFBuildOptions {
pub placeholder: u32,
}
pub type PFBuildOptionsRef = *mut BuildOptions;
pub type PFRenderTransformRef = *mut RenderTransform;
// `canvas`
/// Consumes the font context.
/// This function internally adds a reference to the font context. Therefore, if you created the
/// font context, you must release it yourself to avoid a leak.
#[no_mangle]
pub unsafe extern "C" fn PFCanvasCreate(font_context: PFCanvasFontContextRef,
size: *const PFVector2F)
-> PFCanvasRef {
Box::into_raw(Box::new(CanvasRenderingContext2D::new(*Box::from_raw(font_context),
Box::into_raw(Box::new(CanvasRenderingContext2D::new((*font_context).clone(),
(*size).to_rust())))
}
@ -188,17 +219,18 @@ pub unsafe extern "C" fn PFCanvasFontContextCreateWithFonts(fonts: *const FKHand
}
#[no_mangle]
pub unsafe extern "C" fn PFCanvasFontContextDestroy(font_context: PFCanvasFontContextRef) {
drop(Box::from_raw(font_context))
}
#[no_mangle]
pub unsafe extern "C" fn PFCanvasFontContextClone(font_context: PFCanvasFontContextRef)
-> PFCanvasFontContextRef {
pub unsafe extern "C" fn PFCanvasFontContextAddRef(font_context: PFCanvasFontContextRef)
-> PFCanvasFontContextRef {
Box::into_raw(Box::new((*font_context).clone()))
}
/// Consumes the canvas.
#[no_mangle]
pub unsafe extern "C" fn PFCanvasFontContextRelease(font_context: PFCanvasFontContextRef) {
drop(Box::from_raw(font_context))
}
/// This function takes ownership of the supplied canvas and will automatically destroy it when
/// the scene is destroyed.
#[no_mangle]
pub unsafe extern "C" fn PFCanvasCreateScene(canvas: PFCanvasRef) -> PFSceneRef {
Box::into_raw(Box::new(Box::from_raw(canvas).into_scene()))
@ -315,13 +347,15 @@ pub unsafe extern "C" fn PFCanvasSetStrokeStyle(canvas: PFCanvasRef,
(*canvas).set_stroke_style(*stroke_style)
}
/// Consumes the path.
/// This function automatically destroys the path. If you wish to use the path again, clone it
/// first.
#[no_mangle]
pub unsafe extern "C" fn PFCanvasFillPath(canvas: PFCanvasRef, path: PFPathRef) {
(*canvas).fill_path(*Box::from_raw(path))
}
/// Consumes the path.
/// This function automatically destroys the path. If you wish to use the path again, clone it
/// first.
#[no_mangle]
pub unsafe extern "C" fn PFCanvasStrokePath(canvas: PFCanvasRef, path: PFPathRef) {
(*canvas).stroke_path(*Box::from_raw(path))
@ -421,7 +455,7 @@ pub unsafe extern "C" fn PFFillStyleDestroy(fill_style: PFFillStyleRef) {
#[no_mangle]
pub unsafe extern "C" fn PFFilesystemResourceLoaderLocate() -> PFResourceLoaderRef {
let loader = Box::new(FilesystemResourceLoader::locate());
Box::into_raw(Box::new(loader as Box<dyn ResourceLoader>))
Box::into_raw(Box::new(ResourceLoaderWrapper(loader as Box<dyn ResourceLoader>)))
}
#[no_mangle]
@ -435,6 +469,7 @@ pub unsafe extern "C" fn PFGLLoadWith(loader: PFGLFunctionLoader, userdata: *mut
#[no_mangle]
pub unsafe extern "C" fn PFGLDeviceCreate(version: PFGLVersion, default_framebuffer: u32)
-> PFGLDeviceRef {
let version = match version { PF_GL_VERSION_GLES3 => GLVersion::GLES3, _ => GLVersion::GL3 };
Box::into_raw(Box::new(GLDevice::new(version, default_framebuffer)))
}
@ -461,7 +496,9 @@ pub unsafe extern "C" fn PFGLDestFramebufferDestroy(dest_framebuffer: PFGLDestFr
drop(Box::from_raw(dest_framebuffer))
}
/// Takes ownership of `device` and `dest_framebuffer`, but not `resources`.
/// This function takes ownership of and automatically takes responsibility for destroying `device`
/// and `dest_framebuffer`. However, it does not take ownership of `resources`; therefore, if you
/// created the resource loader, you must destroy it yourself to avoid a memory leak.
#[no_mangle]
pub unsafe extern "C" fn PFGLRendererCreate(device: PFGLDeviceRef,
resources: PFResourceLoaderRef,
@ -469,7 +506,7 @@ pub unsafe extern "C" fn PFGLRendererCreate(device: PFGLDeviceRef,
options: *const PFRendererOptions)
-> PFGLRendererRef {
Box::into_raw(Box::new(Renderer::new(*Box::from_raw(device),
&**resources,
&*((*resources).0),
*Box::from_raw(dest_framebuffer),
(*options).to_rust())))
}
@ -498,7 +535,9 @@ pub unsafe extern "C" fn PFMetalDestFramebufferDestroy(dest_framebuffer:
drop(Box::from_raw(dest_framebuffer))
}
/// Takes ownership of `device` and `dest_framebuffer`, but not `resources`.
/// This function takes ownership of and automatically takes responsibility for destroying `device`
/// and `dest_framebuffer`. However, it does not take ownership of `resources`; therefore, if you
/// created the resource loader, you must destroy it yourself to avoid a memory leak.
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
#[no_mangle]
pub unsafe extern "C" fn PFMetalRendererCreate(device: PFMetalDeviceRef,
@ -507,7 +546,7 @@ pub unsafe extern "C" fn PFMetalRendererCreate(device: PFMetalDeviceRef,
options: *const PFRendererOptions)
-> PFMetalRendererRef {
Box::into_raw(Box::new(Renderer::new(*Box::from_raw(device),
&**resources,
&*((*resources).0),
*Box::from_raw(dest_framebuffer),
(*options).to_rust())))
}
@ -518,25 +557,33 @@ pub unsafe extern "C" fn PFMetalRendererDestroy(renderer: PFMetalRendererRef) {
drop(Box::from_raw(renderer))
}
/// Returns a reference to the Metal device in the renderer.
///
/// This reference remains valid as long as the device is alive.
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
#[no_mangle]
pub unsafe extern "C" fn PFMetalRendererGetDevice(renderer: PFMetalRendererRef) -> PFMetalDeviceRef {
pub unsafe extern "C" fn PFMetalRendererGetDevice(renderer: PFMetalRendererRef)
-> PFMetalDeviceRef {
&mut (*renderer).device
}
/// This function does not take ownership of `renderer` or `build_options`. Therefore, if you
/// created the renderer and/or options, you must destroy them yourself to avoid a leak.
#[no_mangle]
pub unsafe extern "C" fn PFSceneProxyBuildAndRenderGL(scene_proxy: PFSceneProxyRef,
renderer: PFGLRendererRef,
build_options: *const PFBuildOptions) {
(*scene_proxy).build_and_render(&mut *renderer, (*build_options).to_rust())
build_options: PFBuildOptionsRef) {
(*scene_proxy).build_and_render(&mut *renderer, (*build_options).clone())
}
/// This function does not take ownership of `renderer` or `build_options`. Therefore, if you
/// created the renderer and/or options, you must destroy them yourself to avoid a leak.
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
#[no_mangle]
pub unsafe extern "C" fn PFSceneProxyBuildAndRenderMetal(scene_proxy: PFSceneProxyRef,
renderer: PFMetalRendererRef,
build_options: *const PFBuildOptions) {
(*scene_proxy).build_and_render(&mut *renderer, (*build_options).to_rust())
build_options: PFBuildOptionsRef) {
(*scene_proxy).build_and_render(&mut *renderer, (*build_options).clone())
}
// `metal`
@ -554,8 +601,60 @@ pub unsafe extern "C" fn PFMetalDeviceDestroy(device: PFMetalDeviceRef) {
drop(Box::from_raw(device))
}
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
#[no_mangle]
pub unsafe extern "C" fn PFMetalDevicePresentDrawable(device: PFMetalDeviceRef) {
(*device).present_drawable()
}
// `renderer`
#[no_mangle]
pub unsafe extern "C" fn PFRenderTransformCreate2D(transform: *const PFTransform2F)
-> PFRenderTransformRef {
Box::into_raw(Box::new(RenderTransform::Transform2D((*transform).to_rust())))
}
#[no_mangle]
pub unsafe extern "C" fn PFRenderTransformCreatePerspective(perspective: *const PFPerspective)
-> PFRenderTransformRef {
Box::into_raw(Box::new(RenderTransform::Perspective((*perspective).to_rust())))
}
#[no_mangle]
pub unsafe extern "C" fn PFRenderTransformDestroy(transform: PFRenderTransformRef) {
drop(Box::from_raw(transform))
}
#[no_mangle]
pub unsafe extern "C" fn PFBuildOptionsCreate() -> PFBuildOptionsRef {
Box::into_raw(Box::new(BuildOptions::default()))
}
#[no_mangle]
pub unsafe extern "C" fn PFBuildOptionsDestroy(options: PFBuildOptionsRef) {
drop(Box::from_raw(options))
}
/// Consumes the transform.
#[no_mangle]
pub unsafe extern "C" fn PFBuildOptionsSetTransform(options: PFBuildOptionsRef,
transform: PFRenderTransformRef) {
(*options).transform = *Box::from_raw(transform)
}
#[no_mangle]
pub unsafe extern "C" fn PFBuildOptionsSetDilation(options: PFBuildOptionsRef,
dilation: *const PFVector2F) {
(*options).dilation = (*dilation).to_rust()
}
#[no_mangle]
pub unsafe extern "C" fn PFBuildOptionsSetSubpixelAAEnabled(options: PFBuildOptionsRef,
subpixel_aa_enabled: bool) {
(*options).subpixel_aa_enabled = subpixel_aa_enabled
}
#[no_mangle]
pub unsafe extern "C" fn PFSceneDestroy(scene: PFSceneRef) {
drop(Box::from_raw(scene))
@ -637,6 +736,40 @@ impl PFVector2I {
}
}
impl PFMatrix2x2F {
#[inline]
pub fn to_rust(&self) -> Matrix2x2F {
Matrix2x2F::row_major(self.m00, self.m01, self.m10, self.m11)
}
}
impl PFTransform2F {
#[inline]
pub fn to_rust(&self) -> Transform2F {
Transform2F { matrix: self.matrix.to_rust(), vector: self.vector.to_rust() }
}
}
impl PFTransform4F {
#[inline]
pub fn to_rust(&self) -> Transform4F {
Transform4F::row_major(self.m00, self.m01, self.m02, self.m03,
self.m10, self.m11, self.m12, self.m13,
self.m20, self.m21, self.m22, self.m23,
self.m30, self.m31, self.m32, self.m33)
}
}
impl PFPerspective {
#[inline]
pub fn to_rust(&self) -> Perspective {
Perspective {
transform: self.transform.to_rust(),
window_size: self.window_size.to_rust(),
}
}
}
// Helpers for `renderer`
impl PFRendererOptions {
@ -651,9 +784,3 @@ impl PFRendererOptions {
}
}
}
impl PFBuildOptions {
pub fn to_rust(&self) -> BuildOptions {
BuildOptions::default()
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -19,8 +19,8 @@ use pathfinder_geometry::point::Point2DI32;
use pathfinder_geometry::point::Point2DF32;
use pathfinder_geometry::point::Point3DF32;
use pathfinder_geometry::rect::RectI32;
use pathfinder_geometry::transform2d::Transform2DF32;
use pathfinder_geometry::transform3d::Transform3DF32;
use pathfinder_geometry::transform2d::Transform2F32;
use pathfinder_geometry::transform3d::Transform4F32;
use pathfinder_geometry::transform3d::Perspective;
use pathfinder_gpu::Device;
use pathfinder_simd::default::F32x4;
@ -41,7 +41,7 @@ pub struct ImmersiveDemo<D> {
renderer: Renderer<GLDevice>,
scene_thread_proxy: SceneThreadProxy,
svg_size: Point2DF32,
svg_to_world: Option<Transform3DF32>,
svg_to_world: Option<Transform4F32>,
}
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_to_world = self.svg_to_world.get_or_insert_with(|| {
let view: Transform3DF32 = cameras[0].view();
let view: Transform4F32 = cameras[0].view();
let svg_to_world_scale = f32::max(MAX_SVG_WIDTH / svg_size.x(), MAX_SVG_HEIGHT / svg_size.y());
let svg_width = svg_size.x() * svg_to_world_scale;
let svg_height = svg_size.y() * svg_to_world_scale;
Transform3DF32::from_uniform_scale(svg_to_world_scale)
.pre_mul(&Transform3DF32::from_translation(-svg_width / 2.0, -svg_height / 2.0, -DEFAULT_SVG_DISTANCE))
.pre_mul(&Transform3DF32::from_scale(1.0, -1.0, 1.0))
Transform4F32::from_uniform_scale(svg_to_world_scale)
.pre_mul(&Transform4F32::from_translation(-svg_width / 2.0, -svg_height / 2.0, -DEFAULT_SVG_DISTANCE))
.pre_mul(&Transform4F32::from_scale(1.0, -1.0, 1.0))
.pre_mul(&view.inverse())
});

View File

@ -32,7 +32,7 @@ use pathfinder_demo::window::SVGPath;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::vector::Vector2I;
use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform2d::Transform2DF;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_content::color::ColorF;
use pathfinder_gl::GLDevice;
use pathfinder_gl::GLVersion;
@ -216,9 +216,9 @@ pub unsafe extern "C" fn magicleap_pathfinder_render(pf: *mut c_void, options: *
let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32 /
f32::max(svg.scene.bounds().size().x(), svg.scene.bounds().size().y());
let transform = Transform2DF::from_translation(svg.scene.bounds().size().scale(-0.5))
.post_mul(&Transform2DF::from_scale(Vector2F::splat(scale)))
.post_mul(&Transform2DF::from_translation(viewport_size.to_f32().scale(0.5)));
let transform = Transform2F::from_translation(svg.scene.bounds().size().scale(-0.5))
.post_mul(&Transform2F::from_scale(Vector2F::splat(scale)))
.post_mul(&Transform2F::from_translation(viewport_size.to_f32().scale(0.5)));
let render_options = RenderOptions {
transform: RenderTransform::Transform2D(transform),

View File

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

View File

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

View File

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

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

View File

@ -1,5 +1,5 @@
[package]
name = "pathfinder_pdf"
name = "pathfinder_export"
version = "0.1.0"
authors = ["Sebastian Köln <sebk@rynx.org>"]
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)
use pathfinder_geometry::{vector::Vector2F, rect::RectF};
use pathfinder_geometry::vector::Vector2F;
use pathfinder_content::color::ColorU;
use std::io::{self, Write, Cursor, Seek};
use std::io::{self, Write};
use deflate::Compression;
struct Counter<T> {
@ -19,9 +19,6 @@ impl<T> Counter<T> {
pub fn pos(&self) -> u64 {
self.count
}
pub fn into_inner(self) -> T {
self.inner
}
}
impl<W: Write> Write for Counter<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
@ -101,43 +98,6 @@ impl Pdf {
self.objects.len()
}
/// Sets the compression level for this document
/// Calls to this method do not affect data produced by operations before the last .add_page
#[inline]
pub fn set_compression(&mut self, compression: Option<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
#[inline]
pub fn set_fill_color(&mut self, color: ColorU) {
@ -178,10 +138,6 @@ impl Pdf {
writeln!(self.page_buffer, "f").unwrap();
}
pub fn stroke(&mut self) {
writeln!(self.page_buffer, "s").unwrap();
}
pub fn close(&mut self) {
writeln!(self.page_buffer, "h").unwrap();
}
@ -218,7 +174,7 @@ impl Pdf {
/Resources <<\n"
.to_vec();
for (idx, obj) in self.objects.iter().enumerate().filter(|&(_, o)| o.is_xobject) {
for (idx, _obj) in self.objects.iter().enumerate().filter(|&(_, o)| o.is_xobject) {
write!(page_object, "/XObject {} 0 R ", idx+1).unwrap();
}
@ -258,7 +214,7 @@ impl Pdf {
self.objects.iter().filter(|o| o.is_page).count()
)?;
out.write_all(b"/Kids [")?;
for (idx, obj) in self.objects.iter().enumerate().filter(|&(_, obj)| obj.is_page) {
for (idx, _obj) in self.objects.iter().enumerate().filter(|&(_, obj)| obj.is_page) {
write!(out, "{} 0 R ", idx + 1)?;
}
out.write_all(b"] >>\nendobj\n")?;

View File

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

View File

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

View File

@ -13,10 +13,10 @@
use crate::line_segment::LineSegment2F;
use crate::vector::Vector2F;
use crate::rect::RectF;
use crate::transform3d::Transform3DF;
use crate::transform3d::Transform4F;
use crate::unit_vector::UnitVector;
use pathfinder_simd::default::F32x4;
use std::ops::Sub;
use std::ops::{Mul, MulAssign, Sub};
/// A 2x2 matrix, optimized with SIMD, in column-major order.
#[derive(Clone, Copy, Debug, PartialEq)]
@ -42,22 +42,12 @@ impl Matrix2x2F {
#[inline]
pub fn from_rotation_vector(vector: UnitVector) -> Matrix2x2F {
Matrix2x2F((vector.0).0.xyyx() * F32x4::new(1.0, 1.0, -1.0, 1.0))
Matrix2x2F((vector.0).0.to_f32x4().xyyx() * F32x4::new(1.0, 1.0, -1.0, 1.0))
}
#[inline]
pub fn row_major(m11: f32, m12: f32, m21: f32, m22: f32) -> Matrix2x2F {
Matrix2x2F(F32x4::new(m11, m21, m12, m22))
}
#[inline]
pub fn post_mul(&self, other: &Matrix2x2F) -> Matrix2x2F {
Matrix2x2F(self.0.xyxy() * other.0.xxzz() + self.0.zwzw() * other.0.yyww())
}
#[inline]
pub fn pre_mul(&self, other: &Matrix2x2F) -> Matrix2x2F {
other.post_mul(self)
pub fn row_major(m00: f32, m01: f32, m10: f32, m11: f32) -> Matrix2x2F {
Matrix2x2F(F32x4::new(m00, m10, m01, m11))
}
#[inline]
@ -70,12 +60,6 @@ impl Matrix2x2F {
Matrix2x2F(self.0.wyzx() * F32x4::new(1.0, -1.0, -1.0, 1.0))
}
#[inline]
pub fn transform_point(&self, point: Vector2F) -> Vector2F {
let halves = self.0 * point.0.xxyy();
Vector2F(halves + halves.zwzw())
}
#[inline]
pub fn det(&self) -> f32 {
self.0[0] * self.0[3] - self.0[2] * self.0[1]
@ -112,49 +96,71 @@ impl Sub<Matrix2x2F> for Matrix2x2F {
}
}
/// An affine transform, optimized with SIMD.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Transform2DF {
// Row-major order.
matrix: Matrix2x2F,
vector: Vector2F,
impl Mul<Matrix2x2F> for Matrix2x2F {
type Output = Matrix2x2F;
#[inline]
fn mul(self, other: Matrix2x2F) -> Matrix2x2F {
Matrix2x2F(self.0.xyxy() * other.0.xxzz() + self.0.zwzw() * other.0.yyww())
}
}
impl Default for Transform2DF {
impl Mul<Vector2F> for Matrix2x2F {
type Output = Vector2F;
#[inline]
fn default() -> Transform2DF {
fn mul(self, vector: Vector2F) -> Vector2F {
let halves = self.0 * vector.0.to_f32x4().xxyy();
Vector2F(halves.xy() + halves.zw())
}
}
/// An affine transform, optimized with SIMD.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Transform2F {
// Row-major order.
pub matrix: Matrix2x2F,
pub vector: Vector2F,
}
impl Default for Transform2F {
#[inline]
fn default() -> Transform2F {
Self::from_scale(Vector2F::splat(1.0))
}
}
impl Transform2DF {
impl Transform2F {
#[inline]
pub fn from_scale(scale: Vector2F) -> Transform2DF {
Transform2DF {
pub fn from_scale(scale: Vector2F) -> Transform2F {
Transform2F {
matrix: Matrix2x2F::from_scale(scale),
vector: Vector2F::default(),
}
}
#[inline]
pub fn from_rotation(theta: f32) -> Transform2DF {
Transform2DF {
pub fn from_uniform_scale(scale: f32) -> Transform2F {
Transform2F::from_scale(Vector2F::splat(scale))
}
#[inline]
pub fn from_rotation(theta: f32) -> Transform2F {
Transform2F {
matrix: Matrix2x2F::from_rotation(theta),
vector: Vector2F::default(),
}
}
#[inline]
pub fn from_rotation_vector(vector: UnitVector) -> Transform2DF {
Transform2DF {
pub fn from_rotation_vector(vector: UnitVector) -> Transform2F {
Transform2F {
matrix: Matrix2x2F::from_rotation_vector(vector),
vector: Vector2F::default(),
}
}
#[inline]
pub fn from_translation(vector: Vector2F) -> Transform2DF {
Transform2DF { matrix: Matrix2x2F::default(), vector }
pub fn from_translation(vector: Vector2F) -> Transform2F {
Transform2F { matrix: Matrix2x2F::default(), vector }
}
#[inline]
@ -162,58 +168,24 @@ impl Transform2DF {
scale: Vector2F,
theta: f32,
translation: Vector2F,
) -> Transform2DF {
let rotation = Transform2DF::from_rotation(theta);
let translation = Transform2DF::from_translation(translation);
Transform2DF::from_scale(scale).post_mul(&rotation).post_mul(&translation)
) -> Transform2F {
let rotation = Transform2F::from_rotation(theta);
let translation = Transform2F::from_translation(translation);
Transform2F::from_scale(scale) * rotation * translation
}
#[inline]
pub fn row_major(m11: f32, m12: f32, m21: f32, m22: f32, m31: f32, m32: f32) -> Transform2DF {
Transform2DF {
pub fn row_major(m11: f32, m12: f32, m21: f32, m22: f32, m31: f32, m32: f32) -> Transform2F {
Transform2F {
matrix: Matrix2x2F::row_major(m11, m12, m21, m22),
vector: Vector2F::new(m31, m32),
}
}
#[inline]
pub fn transform_point(&self, point: Vector2F) -> Vector2F {
self.matrix.transform_point(point) + self.vector
}
#[inline]
pub fn transform_line_segment(&self, line_segment: &LineSegment2F) -> LineSegment2F {
LineSegment2F::new(self.transform_point(line_segment.from()),
self.transform_point(line_segment.to()))
}
#[inline]
pub fn transform_rect(&self, rect: &RectF) -> RectF {
let upper_left = self.transform_point(rect.origin());
let upper_right = self.transform_point(rect.upper_right());
let lower_left = self.transform_point(rect.lower_left());
let lower_right = self.transform_point(rect.lower_right());
let min_point = upper_left.min(upper_right).min(lower_left).min(lower_right);
let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right);
RectF::from_points(min_point, max_point)
}
#[inline]
pub fn post_mul(&self, other: &Transform2DF) -> Transform2DF {
let matrix = self.matrix.post_mul(&other.matrix);
let vector = other.transform_point(self.vector);
Transform2DF { matrix, vector }
}
#[inline]
pub fn pre_mul(&self, other: &Transform2DF) -> Transform2DF {
other.post_mul(self)
}
// TODO(pcwalton): Optimize better with SIMD.
#[inline]
pub fn to_3d(&self) -> Transform3DF {
Transform3DF::row_major(
pub fn to_3d(&self) -> Transform4F {
Transform4F::row_major(
self.matrix.0[0],
self.matrix.0[1],
0.0,
@ -235,7 +207,7 @@ impl Transform2DF {
#[inline]
pub fn is_identity(&self) -> bool {
*self == Transform2DF::default()
*self == Transform2F::default()
}
#[inline]
@ -256,18 +228,23 @@ impl Transform2DF {
}
#[inline]
pub fn post_translate(&self, vector: Vector2F) -> Transform2DF {
self.post_mul(&Transform2DF::from_translation(vector))
pub fn translate(&self, vector: Vector2F) -> Transform2F {
Transform2F::from_translation(vector) * *self
}
#[inline]
pub fn post_rotate(&self, theta: f32) -> Transform2DF {
self.post_mul(&Transform2DF::from_rotation(theta))
pub fn rotate(&self, theta: f32) -> Transform2F {
Transform2F::from_rotation(theta) * *self
}
#[inline]
pub fn post_scale(&self, scale: Vector2F) -> Transform2DF {
self.post_mul(&Transform2DF::from_scale(scale))
pub fn scale(&self, scale: Vector2F) -> Transform2F {
Transform2F::from_scale(scale) * *self
}
#[inline]
pub fn uniform_scale(&self, scale: f32) -> Transform2F {
self.scale(Vector2F::splat(scale))
}
/// Returns the translation part of this matrix.
@ -291,6 +268,52 @@ impl Transform2DF {
/// This decomposition assumes that scale, rotation, and translation are applied in that order.
#[inline]
pub fn scale_factor(&self) -> f32 {
Vector2F(self.matrix.0.zwxy()).length()
Vector2F(self.matrix.0.zw()).length()
}
}
impl Mul<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::transform2d::Matrix2x2F;
use pathfinder_simd::default::F32x4;
use std::ops::{Add, Neg};
use std::ops::{Add, Mul, MulAssign, Neg};
/// An transform, optimized with SIMD.
///
/// In column-major order.
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
pub struct Transform3DF {
pub struct Transform4F {
pub c0: F32x4,
pub c1: F32x4,
pub c2: F32x4,
pub c3: F32x4,
}
impl Default for Transform3DF {
impl Default for Transform4F {
#[inline]
fn default() -> Transform3DF {
Transform3DF {
fn default() -> Transform4F {
Transform4F {
c0: F32x4::new(1.0, 0.0, 0.0, 0.0),
c1: F32x4::new(0.0, 1.0, 0.0, 0.0),
c2: F32x4::new(0.0, 0.0, 1.0, 0.0),
@ -40,7 +40,7 @@ impl Default for Transform3DF {
}
}
impl Transform3DF {
impl Transform4F {
#[inline]
pub fn row_major(
m00: f32,
@ -59,8 +59,8 @@ impl Transform3DF {
m31: f32,
m32: f32,
m33: f32,
) -> Transform3DF {
Transform3DF {
) -> Transform4F {
Transform4F {
c0: F32x4::new(m00, m10, m20, m30),
c1: F32x4::new(m01, m11, m21, m31),
c2: F32x4::new(m02, m12, m22, m32),
@ -69,26 +69,28 @@ impl Transform3DF {
}
#[inline]
pub fn from_scale(x: f32, y: f32, z: f32) -> Transform3DF {
Transform3DF::row_major(
x, 0.0, 0.0, 0.0, 0.0, y, 0.0, 0.0, 0.0, 0.0, z, 0.0, 0.0, 0.0, 0.0, 1.0,
)
pub fn from_scale(scale: Vector4F) -> Transform4F {
Transform4F {
c0: F32x4::new(scale.x(), 0.0, 0.0, 0.0),
c1: F32x4::new(0.0, scale.y(), 0.0, 0.0),
c2: F32x4::new(0.0, 0.0, scale.z(), 0.0),
c3: F32x4::new(0.0, 0.0, 0.0, 1.0),
}
}
#[inline]
pub fn from_uniform_scale(factor: f32) -> Transform3DF {
Transform3DF::from_scale(factor, factor, factor)
pub fn from_uniform_scale(factor: f32) -> Transform4F {
Transform4F::from_scale(Vector4F::splat(factor))
}
#[inline]
pub fn from_translation(x: f32, y: f32, z: f32) -> Transform3DF {
Transform3DF::row_major(
1.0, 0.0, 0.0, x, 0.0, 1.0, 0.0, y, 0.0, 0.0, 1.0, z, 0.0, 0.0, 0.0, 1.0,
)
pub fn from_translation(mut translation: Vector4F) -> Transform4F {
translation.set_w(1.0);
Transform4F { c3: translation.0, ..Transform4F::default() }
}
// TODO(pcwalton): Optimize.
pub fn from_rotation(yaw: f32, pitch: f32, roll: f32) -> Transform3DF {
pub fn from_rotation(yaw: f32, pitch: f32, roll: f32) -> Transform4F {
let (cos_b, sin_b) = (yaw.cos(), yaw.sin());
let (cos_c, sin_c) = (pitch.cos(), pitch.sin());
let (cos_a, sin_a) = (roll.cos(), roll.sin());
@ -101,7 +103,7 @@ impl Transform3DF {
let m20 = -sin_b;
let m21 = cos_b * sin_c;
let m22 = cos_b * cos_c;
Transform3DF::row_major(
Transform4F::row_major(
m00, m01, m02, 0.0, m10, m11, m12, 0.0, m20, m21, m22, 0.0, 0.0, 0.0, 0.0, 1.0,
)
}
@ -110,7 +112,7 @@ impl Transform3DF {
///
/// The quaternion is expected to be packed into a SIMD type (x, y, z, w) corresponding to
/// x + yi + zj + wk.
pub fn from_rotation_quaternion(q: F32x4) -> Transform3DF {
pub fn from_rotation_quaternion(q: F32x4) -> Transform4F {
// TODO(pcwalton): Optimize better with more shuffles.
let (mut sq, mut w, mut xy_xz_yz) = (q * q, q.wwww() * q, q.xxyy() * q.yzzy());
sq += sq;
@ -119,7 +121,7 @@ impl Transform3DF {
let diag = F32x4::splat(1.0) - (sq.yxxy() + sq.zzyy());
let (wx2, wy2, wz2) = (w.x(), w.y(), w.z());
let (xy2, xz2, yz2) = (xy_xz_yz.x(), xy_xz_yz.y(), xy_xz_yz.z());
Transform3DF::row_major(
Transform4F::row_major(
diag.x(),
xy2 - wz2,
xz2 + wy2,
@ -148,14 +150,14 @@ impl Transform3DF {
top: f32,
near_val: f32,
far_val: f32,
) -> Transform3DF {
) -> Transform4F {
let x_inv = 1.0 / (right - left);
let y_inv = 1.0 / (top - bottom);
let z_inv = 1.0 / (far_val - near_val);
let tx = -(right + left) * x_inv;
let ty = -(top + bottom) * y_inv;
let tz = -(far_val + near_val) * z_inv;
Transform3DF::row_major(
Transform4F::row_major(
2.0 * x_inv,
0.0,
0.0,
@ -176,17 +178,17 @@ impl Transform3DF {
}
/// Linearly interpolate between transforms
pub fn lerp(&self, weight: f32, other: &Transform3DF) -> Transform3DF {
pub fn lerp(&self, weight: f32, other: &Transform4F) -> Transform4F {
let c0 = self.c0 * F32x4::splat(weight) + other.c0 * F32x4::splat(1.0 - weight);
let c1 = self.c1 * F32x4::splat(weight) + other.c1 * F32x4::splat(1.0 - weight);
let c2 = self.c2 * F32x4::splat(weight) + other.c2 * F32x4::splat(1.0 - weight);
let c3 = self.c3 * F32x4::splat(weight) + other.c3 * F32x4::splat(1.0 - weight);
Transform3DF { c0, c1, c2, c3 }
Transform4F { c0, c1, c2, c3 }
}
/// Just like `gluPerspective()`.
#[inline]
pub fn from_perspective(fov_y: f32, aspect: f32, z_near: f32, z_far: f32) -> Transform3DF {
pub fn from_perspective(fov_y: f32, aspect: f32, z_near: f32, z_far: f32) -> Transform4F {
let f = 1.0 / (fov_y * 0.5).tan();
let z_denom = 1.0 / (z_near - z_far);
let m00 = f / aspect;
@ -194,7 +196,7 @@ impl Transform3DF {
let m22 = (z_far + z_near) * z_denom;
let m23 = 2.0 * z_far * z_near * z_denom;
let m32 = -1.0;
Transform3DF::row_major(
Transform4F::row_major(
m00, 0.0, 0.0, 0.0, 0.0, m11, 0.0, 0.0, 0.0, 0.0, m22, m23, 0.0, 0.0, m32, 0.0,
)
}
@ -209,8 +211,8 @@ impl Transform3DF {
b: Matrix2x2F,
c: Matrix2x2F,
d: Matrix2x2F,
) -> Transform3DF {
Transform3DF {
) -> Transform4F {
Transform4F {
c0: a.0.concat_xy_xy(c.0),
c1: a.0.concat_zw_zw(c.0),
c2: b.0.concat_xy_xy(d.0),
@ -218,38 +220,24 @@ impl Transform3DF {
}
}
// FIXME(pcwalton): Is this right, due to transposition? I think we may have to reverse the
// two.
//
// https://stackoverflow.com/a/18508113
#[inline]
pub fn pre_mul(&self, other: &Transform3DF) -> Transform3DF {
return Transform3DF {
c0: mul_col(self.c0, other),
c1: mul_col(self.c1, other),
c2: mul_col(self.c2, other),
c3: mul_col(self.c3, other),
};
fn mul_col(a_col: F32x4, b: &Transform3DF) -> F32x4 {
let (a0, a1) = (F32x4::splat(a_col[0]), F32x4::splat(a_col[1]));
let (a2, a3) = (F32x4::splat(a_col[2]), F32x4::splat(a_col[3]));
a0 * b.c0 + a1 * b.c1 + a2 * b.c2 + a3 * b.c3
}
pub fn rotate(&self, yaw: f32, pitch: f32, roll: f32) -> Transform4F {
Transform4F::from_rotation(yaw, pitch, roll) * *self
}
#[inline]
pub fn post_mul(&self, other: &Transform3DF) -> Transform3DF {
other.pre_mul(self)
pub fn scale(&self, scale: Vector4F) -> Transform4F {
Transform4F::from_scale(scale) * *self
}
#[inline]
pub fn transform_point(&self, point: Vector4F) -> Vector4F {
let term0 = self.c0 * F32x4::splat(point.x());
let term1 = self.c1 * F32x4::splat(point.y());
let term2 = self.c2 * F32x4::splat(point.z());
let term3 = self.c3 * F32x4::splat(point.w());
Vector4F(term0 + term1 + term2 + term3)
pub fn uniform_scale(&self, scale: f32) -> Transform4F {
Transform4F::from_uniform_scale(scale) * *self
}
#[inline]
pub fn translate(&self, translation: Vector4F) -> Transform4F {
Transform4F::from_translation(translation) * *self
}
#[inline]
@ -276,26 +264,26 @@ impl Transform3DF {
//
// If A is the upper left submatrix of this matrix, this method assumes that A and the Schur
// complement of A are invertible.
pub fn inverse(&self) -> Transform3DF {
pub fn inverse(&self) -> Transform4F {
// Extract submatrices.
let (a, b) = (self.upper_left(), self.upper_right());
let (c, d) = (self.lower_left(), self.lower_right());
// Compute temporary matrices.
let a_inv = a.inverse();
let x = c.post_mul(&a_inv);
let y = (d - x.post_mul(&b)).inverse();
let z = a_inv.post_mul(&b);
let x = c * a_inv;
let y = (d - x * b).inverse();
let z = a_inv * b;
// Compute new submatrices.
let (a_new, b_new) = (a_inv + z.post_mul(&y).post_mul(&x), (-z).post_mul(&y));
let (c_new, d_new) = ((-y).post_mul(&x), y);
let (a_new, b_new) = (a_inv + z * y * x, -z * y);
let (c_new, d_new) = (-y * x, y);
// Construct inverse.
Transform3DF::from_submatrices(a_new, b_new, c_new, d_new)
Transform4F::from_submatrices(a_new, b_new, c_new, d_new)
}
pub fn approx_eq(&self, other: &Transform3DF, epsilon: f32) -> bool {
pub fn approx_eq(&self, other: &Transform4F, epsilon: f32) -> bool {
self.c0.approx_eq(other.c0, epsilon)
&& self.c1.approx_eq(other.c1, epsilon)
&& self.c2.approx_eq(other.c2, epsilon)
@ -306,6 +294,50 @@ impl Transform3DF {
pub fn as_ptr(&self) -> *const f32 {
(&self.c0) as *const F32x4 as *const f32
}
#[inline]
pub fn to_columns(&self) -> [F32x4; 4] {
[self.c0, self.c1, self.c2, self.c3]
}
}
impl Mul<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 {
@ -326,108 +358,110 @@ impl Neg for Matrix2x2F {
#[derive(Clone, Copy, Debug)]
pub struct Perspective {
pub transform: Transform3DF,
pub transform: Transform4F,
pub window_size: Vector2I,
}
impl Perspective {
#[inline]
pub fn new(transform: &Transform3DF, window_size: Vector2I) -> Perspective {
pub fn new(transform: &Transform4F, window_size: Vector2I) -> Perspective {
Perspective {
transform: *transform,
window_size,
}
}
}
impl Mul<Transform4F> for Perspective {
type Output = Perspective;
#[inline]
pub fn transform_point_2d(&self, point: &Vector2F) -> Vector2F {
let point = self
.transform
.transform_point(point.to_3d())
.perspective_divide()
.to_2d()
* Vector2F::new(1.0, -1.0);
fn mul(self, other: Transform4F) -> Perspective {
Perspective {
transform: self.transform * other,
window_size: self.window_size,
}
}
}
impl Mul<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)
}
}
// TODO(pcwalton): SIMD?
impl Mul<RectF> for Perspective {
type Output = RectF;
#[inline]
pub fn transform_rect(&self, rect: RectF) -> RectF {
let upper_left = self.transform_point_2d(&rect.origin());
let upper_right = self.transform_point_2d(&rect.upper_right());
let lower_left = self.transform_point_2d(&rect.lower_left());
let lower_right = self.transform_point_2d(&rect.lower_right());
fn mul(self, rect: RectF) -> RectF {
let (upper_left, upper_right) = (self * rect.origin(), self * rect.upper_right());
let (lower_left, lower_right) = (self * rect.lower_left(), self * rect.lower_right());
let min_point = upper_left.min(upper_right).min(lower_left).min(lower_right);
let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right);
RectF::from_points(min_point, max_point)
}
#[inline]
pub fn post_mul(&self, other: &Transform3DF) -> Perspective {
Perspective {
transform: self.transform.post_mul(other),
window_size: self.window_size,
}
}
}
#[cfg(test)]
mod test {
use crate::vector::Vector4F;
use crate::transform3d::Transform3DF;
use crate::transform3d::Transform4F;
#[test]
fn test_post_mul() {
let a = Transform3DF::row_major(
let a = Transform4F::row_major(
3.0, 1.0, 4.0, 5.0, 9.0, 2.0, 6.0, 5.0, 3.0, 5.0, 8.0, 9.0, 7.0, 9.0, 3.0, 2.0,
);
let b = Transform3DF::row_major(
let b = Transform4F::row_major(
3.0, 8.0, 4.0, 6.0, 2.0, 6.0, 4.0, 3.0, 3.0, 8.0, 3.0, 2.0, 7.0, 9.0, 5.0, 0.0,
);
let c = Transform3DF::row_major(
let c = Transform4F::row_major(
58.0, 107.0, 53.0, 29.0, 84.0, 177.0, 87.0, 72.0, 106.0, 199.0, 101.0, 49.0, 62.0,
152.0, 83.0, 75.0,
);
assert_eq!(a.post_mul(&b), c);
assert_eq!(a * b, c);
}
#[test]
fn test_pre_mul() {
let a = Transform3DF::row_major(
let a = Transform4F::row_major(
3.0, 1.0, 4.0, 5.0, 9.0, 2.0, 6.0, 5.0, 3.0, 5.0, 8.0, 9.0, 7.0, 9.0, 3.0, 2.0,
);
let b = Transform3DF::row_major(
let b = Transform4F::row_major(
3.0, 8.0, 4.0, 6.0, 2.0, 6.0, 4.0, 3.0, 3.0, 8.0, 3.0, 2.0, 7.0, 9.0, 5.0, 0.0,
);
let c = Transform3DF::row_major(
let c = Transform4F::row_major(
135.0, 93.0, 110.0, 103.0, 93.0, 61.0, 85.0, 82.0, 104.0, 52.0, 90.0, 86.0, 117.0,
50.0, 122.0, 125.0,
);
assert_eq!(a.pre_mul(&b), c);
assert_eq!(b * a, c);
}
#[test]
fn test_transform_point() {
let a = Transform3DF::row_major(
let a = Transform4F::row_major(
3.0, 1.0, 4.0, 5.0, 9.0, 2.0, 6.0, 5.0, 3.0, 5.0, 8.0, 9.0, 7.0, 9.0, 3.0, 2.0,
);
let p = Vector4F::new(3.0, 8.0, 4.0, 6.0);
let q = Vector4F::new(63.0, 97.0, 135.0, 117.0);
assert_eq!(a.transform_point(p), q);
assert_eq!(a * p, q);
}
#[test]
fn test_inverse() {
// Random matrix.
let m = Transform3DF::row_major(
let m = Transform4F::row_major(
0.86277982, 0.15986552, 0.90739898, 0.60066808, 0.17386167, 0.016353, 0.8535783,
0.12969608, 0.0946466, 0.43248631, 0.63480505, 0.08154603, 0.50305436, 0.48359687,
0.51057162, 0.24812012,
);
let p0 = Vector4F::new(0.95536648, 0.80633691, 0.16357357, 0.5477598);
let p1 = m.transform_point(p0);
let p1 = m * p0;
let m_inv = m.inverse();
let m_inv_exp = Transform3DF::row_major(
let m_inv_exp = Transform4F::row_major(
-2.47290136,
3.48865688,
-6.12298336,
@ -446,7 +480,7 @@ mod test {
-9.10374060,
);
assert!(m_inv.approx_eq(&m_inv_exp, 0.0001));
let p2 = m_inv.transform_point(p1);
assert!(p0.approx_eq(&p2, 0.0001));
let p2 = m_inv * p1;
assert!(p0.approx_eq(p2, 0.0001));
}
}

View File

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

View File

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

View File

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

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::{TextureFormat, UniformData, VertexAttrClass};
use pathfinder_gpu::{VertexAttrDescriptor, VertexAttrType};
use pathfinder_simd::default::F32x4;
use pathfinder_simd::default::{F32x2, F32x4};
use std::cell::{Cell, RefCell};
use std::mem;
use std::ptr;
@ -1146,7 +1146,7 @@ impl UniformDataExt for UniformData {
Some(slice::from_raw_parts(&data[0] as *const F32x4 as *const u8, 4 * 16))
}
UniformData::Vec2(ref data) => {
Some(slice::from_raw_parts(data as *const F32x4 as *const u8, 4 * 2))
Some(slice::from_raw_parts(data as *const F32x2 as *const u8, 4 * 2))
}
UniformData::Vec4(ref data) => {
Some(slice::from_raw_parts(data as *const F32x4 as *const u8, 4 * 4))

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(
&mut self,
builder: &SceneBuilder,
segment: &LineSegment2F,
segment: LineSegment2F,
tile_coords: Vector2I,
) {
debug!("add_fill({:?} ({:?}))", segment, tile_coords);
@ -171,31 +171,19 @@ impl BuiltObject {
};
debug_assert_eq!(TILE_WIDTH, TILE_HEIGHT);
// Compute the upper left corner of the tile.
let tile_size = F32x4::splat(TILE_WIDTH as f32);
let (min, max) = (
F32x4::default(),
F32x4::splat((TILE_WIDTH * 256 - 1) as f32),
);
let shuffle_mask = I32x4::new(0x0c08_0400, 0x0d05_0901, 0, 0).as_u8x16();
let tile_upper_left = tile_coords.to_f32().0.xyxy() * tile_size;
let tile_upper_left = tile_coords.to_f32().0.to_f32x4().xyxy() * tile_size;
// Convert to 4.8 fixed point.
let segment = (segment.0 - tile_upper_left) * F32x4::splat(256.0);
let segment = segment
.clamp(min, max)
.to_i32x4()
.as_u8x16()
.shuffle(shuffle_mask)
.as_i32x4();
// Unpack whole and fractional pixels.
let px = LineSegmentU4((segment[1] | (segment[1] >> 12)) as u16);
let subpx = LineSegmentU8(segment[0] as u32);
let (min, max) = (F32x4::default(), F32x4::splat((TILE_WIDTH * 256 - 1) as f32));
let segment = segment.clamp(min, max).to_i32x4();
let (from_x, from_y, to_x, to_y) = (segment[0], segment[1], segment[2], segment[3]);
// Cull degenerate fills.
if (px.0 & 0xf) as u8 == ((px.0 >> 8) & 0xf) as u8
&& (subpx.0 & 0xff) as u8 == ((subpx.0 >> 16) & 0xff) as u8
{
if from_x == to_x {
debug!("... culling!");
return;
}
@ -203,10 +191,20 @@ impl BuiltObject {
// Allocate global tile if necessary.
let alpha_tile_index = self.get_or_allocate_alpha_tile_index(builder, tile_coords);
// Pack whole pixels.
let mut px = (segment & I32x4::splat(0xf00)) >> I32x4::new(8, 4, 8, 4);
px = px | px.yxwz();
// Pack instance data.
debug!("... OK, pushing");
self.fills.push(FillBatchPrimitive {
px,
subpx,
px: LineSegmentU4 { from: px[0] as u8, to: px[2] as u8 },
subpx: LineSegmentU8 {
from_x: from_x as u8,
from_y: from_y as u8,
to_x: to_x as u8,
to_y: to_y as u8,
},
alpha_tile_index,
});
}
@ -256,7 +254,7 @@ impl BuiltObject {
);
while winding != 0 {
self.add_fill(builder, &segment, tile_coords);
self.add_fill(builder, segment, tile_coords);
if winding < 0 {
winding += 1
} else {
@ -315,7 +313,7 @@ impl BuiltObject {
let fill_segment = LineSegment2F::new(fill_from, fill_to);
let fill_tile_coords = Vector2I::new(subsegment_tile_x, tile_y);
self.add_fill(builder, &fill_segment, fill_tile_coords);
self.add_fill(builder, fill_segment, fill_tile_coords);
}
}

View File

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

View File

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

View File

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

View File

@ -18,10 +18,9 @@ use crate::paint::{Paint, PaintId};
use hashbrown::HashMap;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2DF;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_content::color::ColorU;
use pathfinder_content::outline::Outline;
use std::io::{self, Write};
#[derive(Clone)]
pub struct Scene {
@ -116,12 +115,11 @@ impl Scene {
if options.transform.is_2d() || options.subpixel_aa_enabled {
let mut transform = match options.transform {
PreparedRenderTransform::Transform2D(transform) => transform,
PreparedRenderTransform::None => Transform2DF::default(),
PreparedRenderTransform::None => Transform2F::default(),
PreparedRenderTransform::Perspective { .. } => unreachable!(),
};
if options.subpixel_aa_enabled {
transform = transform
.post_mul(&Transform2DF::from_scale(Vector2F::new(3.0, 1.0)))
transform *= Transform2F::from_scale(Vector2F::new(3.0, 1.0))
}
outline.transform(&transform);
}
@ -174,31 +172,6 @@ impl Scene {
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 {
PathIter {
scene: self,
@ -211,10 +184,14 @@ pub struct PathIter<'a> {
pos: usize
}
impl<'a> Iterator for PathIter<'a> {
type Item = (&'a Paint, &'a Outline);
type Item = (&'a Paint, &'a Outline, &'a str);
fn next(&mut self) -> Option<Self::Item> {
let item = self.scene.paths.get(self.pos).map(|path_object| {
(self.scene.paints.get(path_object.paint.0 as usize).unwrap(), &path_object.outline)
(
self.scene.paints.get(path_object.paint.0 as usize).unwrap(),
&path_object.outline,
&*path_object.name
)
});
self.pos += 1;
item

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,17 +8,198 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::arch::aarch64::{self, float32x4_t, int32x4_t, uint32x4_t, uint64x2_t, uint8x16_t};
use std::arch::aarch64::{uint8x8_t, uint8x8x2_t};
use std::arch::aarch64::{self, float32x2_t, float32x4_t, int32x2_t, int32x4_t};
use std::arch::aarch64::{uint32x2_t, uint32x4_t};
use std::f32;
use std::fmt::{self, Debug, Formatter};
use std::mem;
use std::ops::{Add, Index, IndexMut, Mul, Sub};
use std::ops::{Add, BitAnd, BitOr, Index, IndexMut, Mul, Shr, Sub};
mod swizzle_f32x4;
mod swizzle_i32x4;
// 32-bit floats
// Two 32-bit floats
#[derive(Clone, Copy)]
pub struct F32x2(pub float32x2_t);
impl F32x2 {
// Constructors
#[inline]
pub fn new(a: f32, b: f32) -> F32x2 {
unsafe { F32x2(mem::transmute([a, b])) }
}
#[inline]
pub fn splat(x: f32) -> F32x2 {
F32x2::new(x, x)
}
// Basic operations
#[inline]
pub fn approx_recip(self) -> F32x2 {
unsafe { F32x2(vrecpe_v2f32(self.0)) }
}
#[inline]
pub fn min(self, other: F32x2) -> F32x2 {
unsafe { F32x2(simd_fmin(self.0, other.0)) }
}
#[inline]
pub fn max(self, other: F32x2) -> F32x2 {
unsafe { F32x2(simd_fmax(self.0, other.0)) }
}
#[inline]
pub fn clamp(self, min: F32x2, max: F32x2) -> F32x2 {
self.max(min).min(max)
}
#[inline]
pub fn abs(self) -> F32x2 {
unsafe { F32x2(fabs_v2f32(self.0)) }
}
#[inline]
pub fn floor(self) -> F32x2 {
unsafe { F32x2(floor_v2f32(self.0)) }
}
#[inline]
pub fn ceil(self) -> F32x2 {
unsafe { F32x2(ceil_v2f32(self.0)) }
}
#[inline]
pub fn round(self) -> F32x2 {
unsafe { F32x2(round_v2f32(self.0)) }
}
#[inline]
pub fn sqrt(self) -> F32x2 {
unsafe { F32x2(sqrt_v2f32(self.0)) }
}
// Packed comparisons
#[inline]
pub fn packed_eq(self, other: F32x2) -> U32x2 {
unsafe { U32x2(simd_eq(self.0, other.0)) }
}
#[inline]
pub fn packed_gt(self, other: F32x2) -> U32x2 {
unsafe { U32x2(simd_gt(self.0, other.0)) }
}
#[inline]
pub fn packed_lt(self, other: F32x2) -> U32x2 {
unsafe { U32x2(simd_lt(self.0, other.0)) }
}
#[inline]
pub fn packed_le(self, other: F32x2) -> U32x2 {
unsafe { U32x2(simd_le(self.0, other.0)) }
}
// Conversions
#[inline]
pub fn to_f32x4(self) -> F32x4 {
self.concat_xy_xy(F32x2::default())
}
#[inline]
pub fn to_i32x2(self) -> I32x2 {
unsafe { I32x2(simd_cast(self.0)) }
}
#[inline]
pub fn to_i32x4(self) -> I32x4 {
self.to_i32x2().concat_xy_xy(I32x2::default())
}
// Swizzle
#[inline]
pub fn yx(self) -> F32x2 {
unsafe { F32x2(simd_shuffle2(self.0, self.0, [1, 0])) }
}
// Concatenations
#[inline]
pub fn concat_xy_xy(self, other: F32x2) -> F32x4 {
unsafe { F32x4(simd_shuffle4(self.0, other.0, [0, 1, 0, 1])) }
}
}
impl Default for F32x2 {
#[inline]
fn default() -> F32x2 {
F32x2::new(0.0, 0.0)
}
}
impl Debug for F32x2 {
#[inline]
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "<{}, {}>", self[0], self[1])
}
}
impl Index<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)]
pub struct F32x4(pub float32x4_t);
@ -103,32 +284,56 @@ impl F32x4 {
unsafe { U32x4(simd_lt(self.0, other.0)) }
}
// Converts these packed floats to integers.
// Swizzle conversions
#[inline]
pub fn to_i32x4(self) -> I32x4 {
unsafe { I32x4(simd_cast(self.0)) }
pub fn xy(self) -> F32x2 {
unsafe { F32x2(simd_shuffle2(self.0, self.0, [0, 1])) }
}
#[inline]
pub fn yx(self) -> F32x2 {
unsafe { F32x2(simd_shuffle2(self.0, self.0, [1, 0])) }
}
#[inline]
pub fn xw(self) -> F32x2 {
unsafe { F32x2(simd_shuffle2(self.0, self.0, [0, 3])) }
}
#[inline]
pub fn zy(self) -> F32x2 {
unsafe { F32x2(simd_shuffle2(self.0, self.0, [2, 1])) }
}
#[inline]
pub fn zw(self) -> F32x2 {
unsafe { F32x2(simd_shuffle2(self.0, self.0, [2, 3])) }
}
// Concatenations
#[inline]
pub fn concat_xy_xy(self, other: F32x4) -> F32x4 {
unsafe { F32x4(simd_shuffle4(self.0, other.0, [0, 1, 4, 5])) }
unsafe { F32x4(simd_shuffle4(self.0, other.0, [0, 1, 0, 1])) }
}
#[inline]
pub fn concat_xy_zw(self, other: F32x4) -> F32x4 {
unsafe { F32x4(simd_shuffle4(self.0, other.0, [0, 1, 6, 7])) }
unsafe { F32x4(simd_shuffle4(self.0, other.0, [0, 1, 2, 3])) }
}
#[inline]
pub fn concat_zw_zw(self, other: F32x4) -> F32x4 {
unsafe { F32x4(simd_shuffle4(self.0, other.0, [2, 3, 6, 7])) }
unsafe { F32x4(simd_shuffle4(self.0, other.0, [2, 3, 2, 3])) }
}
// Conversions
// Converts these packed floats to integers.
#[inline]
pub fn concat_wz_yx(self, other: F32x4) -> F32x4 {
unsafe { F32x4(simd_shuffle4(self.0, other.0, [3, 2, 5, 4])) }
pub fn to_i32x4(self) -> I32x4 {
unsafe { I32x4(simd_cast(self.0)) }
}
}
@ -200,7 +405,105 @@ impl Sub<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)]
pub struct I32x4(pub int32x4_t);
@ -216,11 +519,6 @@ impl I32x4 {
I32x4::new(x, x, x, x)
}
#[inline]
pub fn as_u8x16(self) -> U8x16 {
unsafe { U8x16(*mem::transmute::<&int32x4_t, &uint8x16_t>(&self.0)) }
}
#[inline]
pub fn min(self, other: I32x4) -> I32x4 {
unsafe { I32x4(simd_fmin(self.0, other.0)) }
@ -245,6 +543,33 @@ impl I32x4 {
unsafe { I32x4(simd_shuffle4(self.0, other.0, [0, 1, 4, 5])) }
}
// Swizzle conversions
#[inline]
pub fn xy(self) -> I32x2 {
unsafe { I32x2(simd_shuffle2(self.0, self.0, [0, 1])) }
}
#[inline]
pub fn yx(self) -> I32x2 {
unsafe { I32x2(simd_shuffle2(self.0, self.0, [1, 0])) }
}
#[inline]
pub fn xw(self) -> I32x2 {
unsafe { I32x2(simd_shuffle2(self.0, self.0, [0, 3])) }
}
#[inline]
pub fn zy(self) -> I32x2 {
unsafe { I32x2(simd_shuffle2(self.0, self.0, [2, 1])) }
}
#[inline]
pub fn zw(self) -> I32x2 {
unsafe { I32x2(simd_shuffle2(self.0, self.0, [2, 3])) }
}
// Conversions
/// Converts these packed integers to floats.
@ -315,7 +640,60 @@ impl PartialEq for I32x4 {
}
}
// 32-bit unsigned integers
impl BitAnd<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)]
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
extern "platform-intrinsic" {
@ -389,6 +729,11 @@ extern "platform-intrinsic" {
fn simd_mul<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_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_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_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" {
#[link_name = "llvm.fabs.v2f32"]
fn fabs_v2f32(a: float32x2_t) -> float32x2_t;
#[link_name = "llvm.floor.v2f32"]
fn floor_v2f32(a: float32x2_t) -> float32x2_t;
#[link_name = "llvm.ceil.v2f32"]
fn ceil_v2f32(a: float32x2_t) -> float32x2_t;
#[link_name = "llvm.round.v2f32"]
fn round_v2f32(a: float32x2_t) -> float32x2_t;
#[link_name = "llvm.sqrt.v2f32"]
fn sqrt_v2f32(a: float32x2_t) -> float32x2_t;
#[link_name = "llvm.fabs.v4f32"]
fn fabs_v4f32(a: float32x4_t) -> float32x4_t;
#[link_name = "llvm.floor.v4f32"]
@ -417,6 +771,9 @@ extern "C" {
#[link_name = "llvm.sqrt.v4f32"]
fn sqrt_v4f32(a: float32x4_t) -> float32x4_t;
#[link_name = "llvm.aarch64.neon.frecpe.v2f32"]
fn vrecpe_v2f32(a: float32x2_t) -> float32x2_t;
#[link_name = "llvm.aarch64.neon.frecpe.v4f32"]
fn vrecpe_v4f32(a: float32x4_t) -> float32x4_t;
}

View File

@ -8,10 +8,84 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::default::{F32x4, I32x4};
use crate::default::{F32x2, F32x4, I32x2, I32x4};
use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
// 32-bit floats
// Two 32-bit floats
impl F32x2 {
// Constructors
#[inline]
pub fn from_slice(slice: &[f32]) -> F32x2 {
F32x2::new(slice[0], slice[1])
}
// Accessors
#[inline]
pub fn x(self) -> f32 {
self[0]
}
#[inline]
pub fn y(self) -> f32 {
self[1]
}
// Mutators
#[inline]
pub fn set_x(&mut self, x: f32) {
self[0] = x
}
#[inline]
pub fn set_y(&mut self, y: f32) {
self[1] = y
}
// Comparisons
#[inline]
pub fn approx_eq(self, other: F32x2, epsilon: f32) -> bool {
(self - other)
.abs()
.packed_gt(F32x2::splat(epsilon))
.is_all_zeroes()
}
}
impl AddAssign for F32x2 {
#[inline]
fn add_assign(&mut self, other: F32x2) {
*self = *self + other
}
}
impl SubAssign for F32x2 {
#[inline]
fn sub_assign(&mut self, other: F32x2) {
*self = *self - other
}
}
impl MulAssign for F32x2 {
#[inline]
fn mul_assign(&mut self, other: F32x2) {
*self = *self * other
}
}
impl Neg for F32x2 {
type Output = F32x2;
#[inline]
fn neg(self) -> F32x2 {
F32x2::default() - self
}
}
// Four 32-bit floats
impl F32x4 {
// Constructors
@ -105,7 +179,38 @@ impl Neg for F32x4 {
}
}
// 32-bit integers
// Two 32-bit integers
impl AddAssign for I32x2 {
#[inline]
fn add_assign(&mut self, other: I32x2) {
*self = *self + other
}
}
impl SubAssign for I32x2 {
#[inline]
fn sub_assign(&mut self, other: I32x2) {
*self = *self - other
}
}
impl MulAssign for I32x2 {
#[inline]
fn mul_assign(&mut self, other: I32x2) {
*self = *self * other
}
}
impl Neg for I32x2 {
type Output = I32x2;
#[inline]
fn neg(self) -> I32x2 {
I32x2::default() - self
}
}
// Four 32-bit integers
impl AddAssign for I32x4 {
#[inline]

View File

@ -10,13 +10,182 @@
use std::f32;
use std::fmt::{self, Debug, Formatter};
use std::mem;
use std::ops::{Add, Index, IndexMut, Mul, Sub};
use std::ops::{Add, BitAnd, BitOr, Index, IndexMut, Mul, Shr, Sub};
mod swizzle_f32x4;
mod swizzle_i32x4;
// 32-bit floats
// Two 32-bit floats
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct F32x2(pub [f32; 2]);
impl F32x2 {
// Constructors
#[inline]
pub fn new(a: f32, b: f32) -> F32x2 {
F32x2([a, b])
}
#[inline]
pub fn splat(x: f32) -> F32x2 {
F32x2([x, x])
}
// Basic operations
#[inline]
pub fn approx_recip(self) -> F32x2 {
F32x2([1.0 / self[0], 1.0 / self[1]])
}
#[inline]
pub fn min(self, other: F32x2) -> F32x2 {
F32x2([f32::min(self[0], other[0]), f32::min(self[1], other[1])])
}
#[inline]
pub fn max(self, other: F32x2) -> F32x2 {
F32x2([f32::max(self[0], other[0]), f32::max(self[1], other[1])])
}
#[inline]
pub fn clamp(self, min: F32x2, max: F32x2) -> F32x2 {
self.max(min).min(max)
}
#[inline]
pub fn abs(self) -> F32x2 {
F32x2([self[0].abs(), self[1].abs()])
}
#[inline]
pub fn floor(self) -> F32x2 {
F32x2([self[0].floor(), self[1].floor()])
}
#[inline]
pub fn ceil(self) -> F32x2 {
F32x2([self[0].ceil(), self[1].ceil()])
}
#[inline]
pub fn round(self) -> F32x2 {
F32x2([self[0].round(), self[1].round()])
}
#[inline]
pub fn sqrt(self) -> F32x2 {
F32x2([self[0].sqrt(), self[1].sqrt()])
}
// Packed comparisons
#[inline]
pub fn packed_eq(self, other: F32x2) -> U32x2 {
U32x2([
if self[0] == other[0] { !0 } else { 0 },
if self[1] == other[1] { !0 } else { 0 },
])
}
#[inline]
pub fn packed_gt(self, other: F32x2) -> U32x2 {
U32x2([
if self[0] > other[0] { !0 } else { 0 },
if self[1] > other[1] { !0 } else { 0 },
])
}
#[inline]
pub fn packed_lt(self, other: F32x2) -> U32x2 {
U32x2([
if self[0] < other[0] { !0 } else { 0 },
if self[1] < other[1] { !0 } else { 0 },
])
}
#[inline]
pub fn packed_le(self, other: F32x2) -> U32x2 {
U32x2([
if self[0] <= other[0] { !0 } else { 0 },
if self[1] <= other[1] { !0 } else { 0 },
])
}
// Conversions
#[inline]
pub fn to_f32x4(self) -> F32x4 {
F32x4([self[0] as f32, self[1] as f32, 0.0, 0.0])
}
#[inline]
pub fn to_i32x2(self) -> I32x2 {
I32x2([self[0] as i32, self[1] as i32])
}
#[inline]
pub fn to_i32x4(self) -> I32x4 {
I32x4([self[0] as i32, self[1] as i32, 0, 0])
}
// Swizzle
#[inline]
pub fn yx(self) -> F32x2 {
F32x2([self[1], self[0]])
}
// Concatenations
#[inline]
pub fn concat_xy_xy(self, other: F32x2) -> F32x4 {
F32x4([self[0], self[1], other[0], other[1]])
}
}
impl Index<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)]
pub struct F32x4(pub [f32; 4]);
@ -162,6 +331,33 @@ impl F32x4 {
])
}
// Swizzle conversions
#[inline]
pub fn xy(self) -> F32x2 {
F32x2([self[0], self[1]])
}
#[inline]
pub fn xw(self) -> F32x2 {
F32x2([self[0], self[3]])
}
#[inline]
pub fn yx(self) -> F32x2 {
F32x2([self[1], self[0]])
}
#[inline]
pub fn zy(self) -> F32x2 {
F32x2([self[2], self[1]])
}
#[inline]
pub fn zw(self) -> F32x2 {
F32x2([self[2], self[3]])
}
// Concatenations
#[inline]
@ -246,7 +442,84 @@ impl Sub<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)]
pub struct I32x4([i32; 4]);
@ -263,10 +536,6 @@ impl I32x4 {
}
#[inline]
pub fn as_u8x16(self) -> U8x16 {
unsafe { U8x16(*mem::transmute::<&[i32; 4], &[u8; 16]>(&self.0)) }
}
#[inline]
pub fn min(self, other: I32x4) -> I32x4 {
I32x4([
@ -306,6 +575,28 @@ impl I32x4 {
I32x4([self[0], self[1], other[0], other[1]])
}
// Swizzle conversions
#[inline]
pub fn xy(self) -> I32x2 {
I32x2([self[0], self[1]])
}
#[inline]
pub fn xw(self) -> I32x2 {
I32x2([self[0], self[3]])
}
#[inline]
pub fn zy(self) -> I32x2 {
I32x2([self[2], self[1]])
}
#[inline]
pub fn zw(self) -> I32x2 {
I32x2([self[2], self[3]])
}
// Conversions
/// Converts these packed integers to floats.
@ -374,7 +665,61 @@ impl Mul<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)]
pub struct U32x4(pub [u32; 4]);
@ -398,24 +743,3 @@ impl Index<usize> for U32x4 {
&self.0[index]
}
}
// 8-bit unsigned integers
#[derive(Clone, Copy)]
pub struct U8x16([u8; 16]);
impl U8x16 {
#[inline]
pub fn as_i32x4(self) -> I32x4 {
unsafe { I32x4(*mem::transmute::<&[u8; 16], &[i32; 4]>(&self.0)) }
}
#[inline]
pub fn shuffle(self, indices: U8x16) -> U8x16 {
let mut result = [0; 16];
for index in 0..16 {
result[index] = self.0[(indices.0[index] & 0x0f) as usize]
}
U8x16(result)
}
}

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::fmt::{self, Debug, Formatter};
use std::mem;
use std::ops::{Add, BitXor, Index, IndexMut, Mul, Not, Sub};
use std::ops::{Add, BitAnd, BitOr, BitXor, Index, IndexMut, Mul, Not, Shr, Sub};
mod swizzle_f32x4;
mod swizzle_i32x4;
// 32-bit floats
// Two 32-bit floats
#[derive(Clone, Copy)]
pub struct F32x2(pub u64);
impl F32x2 {
// Constructors
#[inline]
pub fn new(a: f32, b: f32) -> F32x2 {
unsafe {
let a = mem::transmute::<*const f32, *const u32>(&a);
let b = mem::transmute::<*const f32, *const u32>(&b);
F32x2((*a as u64) | ((*b as u64) << 32))
}
}
#[inline]
pub fn splat(x: f32) -> F32x2 {
F32x2::new(x, x)
}
// Basic operations
#[inline]
pub fn approx_recip(self) -> F32x2 {
self.to_f32x4().approx_recip().xy()
}
#[inline]
pub fn min(self, other: F32x2) -> F32x2 {
self.to_f32x4().min(other.to_f32x4()).xy()
}
#[inline]
pub fn max(self, other: F32x2) -> F32x2 {
self.to_f32x4().max(other.to_f32x4()).xy()
}
#[inline]
pub fn clamp(self, min: F32x2, max: F32x2) -> F32x2 {
self.to_f32x4().clamp(min.to_f32x4(), max.to_f32x4()).xy()
}
#[inline]
pub fn abs(self) -> F32x2 {
self.to_f32x4().abs().xy()
}
#[inline]
pub fn floor(self) -> F32x2 {
self.to_f32x4().floor().xy()
}
#[inline]
pub fn ceil(self) -> F32x2 {
self.to_f32x4().ceil().xy()
}
#[inline]
pub fn round(self) -> F32x2 {
self.to_f32x4().round().xy()
}
#[inline]
pub fn sqrt(self) -> F32x2 {
self.to_f32x4().sqrt().xy()
}
// Packed comparisons
#[inline]
pub fn packed_eq(self, other: F32x2) -> U32x2 {
self.to_f32x4().packed_eq(other.to_f32x4()).xy()
}
#[inline]
pub fn packed_gt(self, other: F32x2) -> U32x2 {
self.to_f32x4().packed_gt(other.to_f32x4()).xy()
}
#[inline]
pub fn packed_lt(self, other: F32x2) -> U32x2 {
self.to_f32x4().packed_lt(other.to_f32x4()).xy()
}
#[inline]
pub fn packed_le(self, other: F32x2) -> U32x2 {
self.to_f32x4().packed_le(other.to_f32x4()).xy()
}
// Conversions
#[inline]
pub fn to_f32x4(self) -> F32x4 {
unsafe { F32x4(x86_64::_mm_castsi128_ps(x86_64::_mm_cvtsi64_si128(self.0 as i64))) }
}
#[inline]
pub fn to_i32x2(self) -> I32x2 {
self.to_i32x4().xy()
}
#[inline]
pub fn to_i32x4(self) -> I32x4 {
self.to_f32x4().to_i32x4()
}
// Swizzle
#[inline]
pub fn yx(self) -> F32x2 {
self.to_f32x4().yx()
}
// Concatenations
#[inline]
pub fn concat_xy_xy(self, other: F32x2) -> F32x4 {
self.to_f32x4().concat_xy_xy(other.to_f32x4())
}
}
impl Default for F32x2 {
#[inline]
fn default() -> F32x2 {
F32x2(0)
}
}
impl Index<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)]
pub struct F32x4(pub __m128);
@ -126,6 +309,33 @@ impl F32x4 {
unsafe { I32x4(x86_64::_mm_cvtps_epi32(self.0)) }
}
// Extraction
#[inline]
pub fn xy(self) -> F32x2 {
unsafe { F32x2(x86_64::_mm_cvtsi128_si64(x86_64::_mm_castps_si128(self.0)) as u64) }
}
#[inline]
pub fn xw(self) -> F32x2 {
unsafe { F32x2(x86_64::_mm_cvtsi128_si64(x86_64::_mm_castps_si128(self.xwyz().0)) as u64) }
}
#[inline]
pub fn yx(self) -> F32x2 {
unsafe { F32x2(x86_64::_mm_cvtsi128_si64(x86_64::_mm_castps_si128(self.yxwz().0)) as u64) }
}
#[inline]
pub fn zy(self) -> F32x2 {
unsafe { F32x2(x86_64::_mm_cvtsi128_si64(x86_64::_mm_castps_si128(self.zyxw().0)) as u64) }
}
#[inline]
pub fn zw(self) -> F32x2 {
unsafe { F32x2(x86_64::_mm_cvtsi128_si64(x86_64::_mm_castps_si128(self.zwxy().0)) as u64) }
}
// Concatenations
#[inline]
@ -224,7 +434,140 @@ impl Sub<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)]
pub struct I32x4(pub __m128i);
@ -245,6 +588,33 @@ impl I32x4 {
unsafe { I32x4(x86_64::_mm_set1_epi32(x)) }
}
// Extraction
#[inline]
pub fn xy(self) -> I32x2 {
unsafe { I32x2(x86_64::_mm_cvtsi128_si64(self.0) as u64) }
}
#[inline]
pub fn xw(self) -> I32x2 {
unsafe { I32x2(x86_64::_mm_cvtsi128_si64(self.xwyz().0) as u64) }
}
#[inline]
pub fn yx(self) -> I32x2 {
unsafe { I32x2(x86_64::_mm_cvtsi128_si64(self.yxwz().0) as u64) }
}
#[inline]
pub fn zy(self) -> I32x2 {
unsafe { I32x2(x86_64::_mm_cvtsi128_si64(self.zyxw().0) as u64) }
}
#[inline]
pub fn zw(self) -> I32x2 {
unsafe { I32x2(x86_64::_mm_cvtsi128_si64(self.zwxy().0) as u64) }
}
// Concatenations
#[inline]
@ -259,11 +629,6 @@ impl I32x4 {
// Conversions
#[inline]
pub fn as_u8x16(self) -> U8x16 {
U8x16(self.0)
}
/// Converts these packed integers to floats.
#[inline]
pub fn to_f32x4(self) -> F32x4 {
@ -343,6 +708,30 @@ impl Mul<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 {
#[inline]
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
@ -357,7 +746,24 @@ impl PartialEq for I32x4 {
}
}
// 32-bit unsigned integers
// Two 32-bit unsigned integers
#[derive(Clone, Copy)]
pub struct U32x2(pub u64);
impl U32x2 {
#[inline]
pub fn is_all_ones(self) -> bool {
self.0 == !0
}
#[inline]
pub fn is_all_zeroes(self) -> bool {
self.0 == 0
}
}
// Four 32-bit unsigned integers
#[derive(Clone, Copy)]
pub struct U32x4(pub __m128i);
@ -390,6 +796,13 @@ impl U32x4 {
unsafe { x86_64::_mm_test_all_zeros(self.0, self.0) != 0 }
}
// Extraction
#[inline]
pub fn xy(self) -> U32x2 {
unsafe { U32x2(x86_64::_mm_cvtsi128_si64(self.0) as u64) }
}
// Packed comparisons
#[inline]
@ -435,20 +848,3 @@ impl BitXor<U32x4> for U32x4 {
unsafe { U32x4(x86_64::_mm_xor_si128(self.0, other.0)) }
}
}
// 8-bit unsigned integers
#[derive(Clone, Copy)]
pub struct U8x16(pub __m128i);
impl U8x16 {
#[inline]
pub fn as_i32x4(self) -> I32x4 {
I32x4(self.0)
}
#[inline]
pub fn shuffle(self, indices: U8x16) -> U8x16 {
unsafe { U8x16(x86_64::_mm_shuffle_epi8(self.0, indices.0)) }
}
}

View File

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

View File

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

View File

@ -15,7 +15,7 @@ use font_kit::loader::Loader;
use lyon_path::builder::{FlatPathBuilder, PathBuilder};
use pathfinder_content::outline::{Contour, Outline};
use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle};
use pathfinder_geometry::transform2d::Transform2DF;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_renderer::paint::PaintId;
use pathfinder_renderer::scene::{PathObject, Scene};
@ -27,7 +27,7 @@ pub trait SceneExt {
fn push_glyph<F>(&mut self,
font: &F,
glyph_id: u32,
transform: &Transform2DF,
transform: &Transform2F,
render_mode: TextRenderMode,
hinting_options: HintingOptions,
paint_id: PaintId)
@ -37,7 +37,7 @@ pub trait SceneExt {
fn push_layout(&mut self,
layout: &Layout,
style: &TextStyle,
transform: &Transform2DF,
transform: &Transform2F,
render_mode: TextRenderMode,
hinting_options: HintingOptions,
paint_id: PaintId)
@ -47,7 +47,7 @@ pub trait SceneExt {
text: &str,
style: &TextStyle,
collection: &FontCollection,
transform: &Transform2DF,
transform: &Transform2F,
render_mode: TextRenderMode,
hinting_options: HintingOptions,
paint_id: PaintId)
@ -59,7 +59,7 @@ impl SceneExt for Scene {
fn push_glyph<F>(&mut self,
font: &F,
glyph_id: u32,
transform: &Transform2DF,
transform: &Transform2F,
render_mode: TextRenderMode,
hinting_options: HintingOptions,
paint_id: PaintId)
@ -82,7 +82,7 @@ impl SceneExt for Scene {
fn push_layout(&mut self,
layout: &Layout,
style: &TextStyle,
transform: &Transform2DF,
transform: &Transform2F,
render_mode: TextRenderMode,
hinting_options: HintingOptions,
paint_id: PaintId)
@ -93,8 +93,7 @@ impl SceneExt for Scene {
// FIXME(pcwalton): Cache this!
let scale = style.size / (font.metrics().units_per_em as f32);
let scale = Vector2F::new(scale, -scale);
let transform =
Transform2DF::from_scale(scale).post_mul(transform).post_translate(offset);
let transform = *transform * Transform2F::from_scale(scale).translate(offset);
self.push_glyph(font,
glyph.glyph_id,
&transform,
@ -110,7 +109,7 @@ impl SceneExt for Scene {
text: &str,
style: &TextStyle,
collection: &FontCollection,
transform: &Transform2DF,
transform: &Transform2F,
render_mode: TextRenderMode,
hinting_options: HintingOptions,
paint_id: PaintId)
@ -129,11 +128,11 @@ pub enum TextRenderMode {
struct OutlinePathBuilder {
outline: Outline,
current_contour: Contour,
transform: Transform2DF,
transform: Transform2F,
}
impl OutlinePathBuilder {
fn new(transform: &Transform2DF) -> OutlinePathBuilder {
fn new(transform: &Transform2F) -> OutlinePathBuilder {
OutlinePathBuilder {
outline: Outline::new(),
current_contour: Contour::new(),
@ -148,7 +147,7 @@ impl OutlinePathBuilder {
}
fn convert_point(&self, point: Point2D<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,
uniforms: &[
(&self.solid_program.framebuffer_size_uniform,
UniformData::Vec2(self.framebuffer_size.0.to_f32x4())),
UniformData::Vec2(self.framebuffer_size.0.to_f32x2())),
(&self.solid_program.color_uniform, get_color_uniform(color)),
],
textures: &[],
@ -414,11 +414,11 @@ impl<D> UIPresenter<D> where D: Device {
textures: &[&texture],
uniforms: &[
(&self.texture_program.framebuffer_size_uniform,
UniformData::Vec2(self.framebuffer_size.0.to_f32x4())),
UniformData::Vec2(self.framebuffer_size.0.to_f32x2())),
(&self.texture_program.color_uniform, get_color_uniform(color)),
(&self.texture_program.texture_uniform, UniformData::TextureUnit(0)),
(&self.texture_program.texture_size_uniform,
UniformData::Vec2(device.texture_size(&texture).0.to_f32x4()))
UniformData::Vec2(device.texture_size(&texture).0.to_f32x2()))
],
viewport: RectI::new(Vector2I::default(), self.framebuffer_size),
options: RenderOptions {

View File

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

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(())
}