Merge remote-tracking branch 'origin/master' into pathfinder-unity-fun
This commit is contained in:
commit
ea3cdbb2a9
|
@ -5,6 +5,7 @@ target
|
||||||
/site/package-lock.json
|
/site/package-lock.json
|
||||||
/site/dist
|
/site/dist
|
||||||
node_modules
|
node_modules
|
||||||
|
/examples/c_canvas_minimal/build
|
||||||
|
|
||||||
# Editors
|
# Editors
|
||||||
*.swp
|
*.swp
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,6 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
|
"c",
|
||||||
"canvas",
|
"canvas",
|
||||||
"demo/android/rust",
|
"demo/android/rust",
|
||||||
"demo/common",
|
"demo/common",
|
||||||
|
@ -8,9 +9,11 @@ members = [
|
||||||
"examples/canvas_minimal",
|
"examples/canvas_minimal",
|
||||||
"examples/canvas_moire",
|
"examples/canvas_moire",
|
||||||
"examples/canvas_text",
|
"examples/canvas_text",
|
||||||
|
"examples/lottie_basic",
|
||||||
"geometry",
|
"geometry",
|
||||||
"gl",
|
"gl",
|
||||||
"gpu",
|
"gpu",
|
||||||
|
"lottie",
|
||||||
"renderer",
|
"renderer",
|
||||||
"simd",
|
"simd",
|
||||||
"svg",
|
"svg",
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
[package]
|
||||||
|
name = "pathfinder_c"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
gl = "0.6"
|
||||||
|
|
||||||
|
[dependencies.pathfinder_canvas]
|
||||||
|
path = "../canvas"
|
||||||
|
|
||||||
|
[dependencies.pathfinder_geometry]
|
||||||
|
path = "../geometry"
|
||||||
|
|
||||||
|
[dependencies.pathfinder_gl]
|
||||||
|
path = "../gl"
|
||||||
|
|
||||||
|
[dependencies.pathfinder_gpu]
|
||||||
|
path = "../gpu"
|
||||||
|
|
||||||
|
[dependencies.pathfinder_renderer]
|
||||||
|
path = "../renderer"
|
||||||
|
|
||||||
|
[dependencies.pathfinder_simd]
|
||||||
|
path = "../simd"
|
|
@ -0,0 +1,158 @@
|
||||||
|
// pathfinder/c/include/pathfinder/pathfinder.h
|
||||||
|
//
|
||||||
|
// Copyright © 2019 The Pathfinder Project Developers.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
#ifndef PF_PATHFINDER_H
|
||||||
|
#define PF_PATHFINDER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Macros
|
||||||
|
|
||||||
|
// `gl`
|
||||||
|
|
||||||
|
#define PF_GL_VERSION_GL3 0
|
||||||
|
#define PF_GL_VERSION_GLES3 1
|
||||||
|
|
||||||
|
// `gpu`
|
||||||
|
|
||||||
|
#define PF_CLEAR_FLAGS_HAS_COLOR 0x1
|
||||||
|
#define PF_CLEAR_FLAGS_HAS_DEPTH 0x2
|
||||||
|
#define PF_CLEAR_FLAGS_HAS_STENCIL 0x4
|
||||||
|
#define PF_CLEAR_FLAGS_HAS_RECT 0x8
|
||||||
|
|
||||||
|
// Types
|
||||||
|
|
||||||
|
// `canvas`
|
||||||
|
|
||||||
|
struct PFCanvas;
|
||||||
|
typedef struct PFCanvas *PFCanvasRef;
|
||||||
|
struct PFPath;
|
||||||
|
typedef struct PFPath *PFPathRef;
|
||||||
|
|
||||||
|
// `geometry`
|
||||||
|
|
||||||
|
struct PFColorF {
|
||||||
|
float r, g, b, a;
|
||||||
|
};
|
||||||
|
typedef struct PFColorF PFColorF;
|
||||||
|
struct PFPoint2DF {
|
||||||
|
float x, y;
|
||||||
|
};
|
||||||
|
typedef struct PFPoint2DF PFPoint2DF;
|
||||||
|
struct PFPoint2DI {
|
||||||
|
int32_t x, y;
|
||||||
|
};
|
||||||
|
typedef struct PFPoint2DI PFPoint2DI;
|
||||||
|
struct PFRectF {
|
||||||
|
PFPoint2DF origin, lower_right;
|
||||||
|
};
|
||||||
|
typedef struct PFRectF PFRectF;
|
||||||
|
struct PFRectI {
|
||||||
|
PFPoint2DI origin, lower_right;
|
||||||
|
};
|
||||||
|
typedef struct PFRectI PFRectI;
|
||||||
|
|
||||||
|
// `gl`
|
||||||
|
|
||||||
|
struct PFGLDevice;
|
||||||
|
typedef struct PFGLDevice *PFGLDeviceRef;
|
||||||
|
struct PFGLDestFramebuffer;
|
||||||
|
typedef struct PFGLDestFramebuffer *PFGLDestFramebufferRef;
|
||||||
|
typedef const void *(*PFGLFunctionLoader)(const char *data, void *userdata);
|
||||||
|
struct PFGLRenderer;
|
||||||
|
typedef struct PFGLRenderer *PFGLRendererRef;
|
||||||
|
typedef uint32_t PFGLVersion;
|
||||||
|
|
||||||
|
// `gpu`
|
||||||
|
|
||||||
|
typedef uint8_t PFClearFlags;
|
||||||
|
struct PFClearParams {
|
||||||
|
PFColorF color;
|
||||||
|
float depth;
|
||||||
|
uint8_t stencil;
|
||||||
|
PFRectI rect;
|
||||||
|
PFClearFlags flags;
|
||||||
|
};
|
||||||
|
typedef struct PFClearParams PFClearParams;
|
||||||
|
struct PFResourceLoader;
|
||||||
|
typedef struct PFResourceLoader *PFResourceLoaderRef;
|
||||||
|
|
||||||
|
// `renderer`
|
||||||
|
|
||||||
|
struct PFRenderOptions {
|
||||||
|
uint32_t placeholder;
|
||||||
|
};
|
||||||
|
typedef struct PFRenderOptions PFRenderOptions;
|
||||||
|
struct PFScene;
|
||||||
|
typedef struct PFScene *PFSceneRef;
|
||||||
|
struct PFSceneProxy;
|
||||||
|
typedef struct PFSceneProxy *PFSceneProxyRef;
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
|
||||||
|
// `canvas`
|
||||||
|
|
||||||
|
PFCanvasRef PFCanvasCreate(const PFPoint2DF *size);
|
||||||
|
void PFCanvasDestroy(PFCanvasRef canvas);
|
||||||
|
PFSceneRef PFCanvasCreateScene(PFCanvasRef canvas);
|
||||||
|
void PFCanvasFillRect(PFCanvasRef canvas, const PFRectF *rect);
|
||||||
|
void PFCanvasStrokeRect(PFCanvasRef canvas, const PFRectF *rect);
|
||||||
|
void PFCanvasSetLineWidth(PFCanvasRef canvas, float new_line_width);
|
||||||
|
void PFCanvasFillPath(PFCanvasRef canvas, PFPathRef path);
|
||||||
|
void PFCanvasStrokePath(PFCanvasRef canvas, PFPathRef path);
|
||||||
|
PFPathRef PFPathCreate();
|
||||||
|
void PFPathDestroy(PFPathRef path);
|
||||||
|
PFPathRef PFPathClone(PFPathRef path);
|
||||||
|
void PFPathMoveTo(PFPathRef path, const PFPoint2DF *to);
|
||||||
|
void PFPathLineTo(PFPathRef path, const PFPoint2DF *to);
|
||||||
|
void PFPathQuadraticCurveTo(PFPathRef path, const PFPoint2DF *ctrl, const PFPoint2DF *to);
|
||||||
|
void PFPathBezierCurveTo(PFPathRef path,
|
||||||
|
const PFPoint2DF *ctrl0,
|
||||||
|
const PFPoint2DF *ctrl1,
|
||||||
|
const PFPoint2DF *to);
|
||||||
|
void PFPathClosePath(PFPathRef path);
|
||||||
|
|
||||||
|
// `gl`
|
||||||
|
|
||||||
|
PFGLDestFramebufferRef PFGLDestFramebufferCreateFullWindow(const PFPoint2DI *window_size);
|
||||||
|
void PFGLDestFramebufferDestroy(PFGLDestFramebufferRef dest_framebuffer);
|
||||||
|
PFGLDeviceRef PFGLDeviceCreate(PFGLVersion version, uint32_t default_framebuffer);
|
||||||
|
void PFGLDeviceDestroy(PFGLDeviceRef device);
|
||||||
|
void PFGLDeviceClear(PFGLDeviceRef device, const PFClearParams *params);
|
||||||
|
void PFGLLoadWith(PFGLFunctionLoader loader, void *userdata);
|
||||||
|
PFGLRendererRef PFGLRendererCreate(PFGLDeviceRef device,
|
||||||
|
PFResourceLoaderRef resources,
|
||||||
|
PFGLDestFramebufferRef dest_framebuffer);
|
||||||
|
void PFGLRendererDestroy(PFGLRendererRef renderer);
|
||||||
|
/// Returns a borrowed reference to the device.
|
||||||
|
PFGLDeviceRef PFGLRendererGetDevice(PFGLRendererRef renderer);
|
||||||
|
void PFSceneProxyBuildAndRenderGL(PFSceneProxyRef scene_proxy,
|
||||||
|
PFGLRendererRef renderer,
|
||||||
|
const PFRenderOptions *options);
|
||||||
|
|
||||||
|
// `gpu`
|
||||||
|
|
||||||
|
PFResourceLoaderRef PFFilesystemResourceLoaderLocate();
|
||||||
|
void PFResourceLoaderDestroy(PFResourceLoaderRef loader);
|
||||||
|
|
||||||
|
// `renderer`
|
||||||
|
|
||||||
|
PFSceneProxyRef PFSceneProxyCreateFromSceneAndRayonExecutor(PFSceneRef scene);
|
||||||
|
void PFSceneProxyDestroy(PFSceneProxyRef scene_proxy);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,360 @@
|
||||||
|
// pathfinder/c/src/lib.rs
|
||||||
|
//
|
||||||
|
// Copyright © 2019 The Pathfinder Project Developers.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
//! C bindings to Pathfinder.
|
||||||
|
|
||||||
|
use gl;
|
||||||
|
use pathfinder_canvas::{CanvasRenderingContext2D, Path2D};
|
||||||
|
use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32};
|
||||||
|
use pathfinder_geometry::basic::rect::{RectF32, RectI32};
|
||||||
|
use pathfinder_geometry::color::ColorF;
|
||||||
|
use pathfinder_gl::{GLDevice, GLVersion};
|
||||||
|
use pathfinder_gpu::resources::{FilesystemResourceLoader, ResourceLoader};
|
||||||
|
use pathfinder_gpu::{ClearParams, Device};
|
||||||
|
use pathfinder_renderer::concurrent::rayon::RayonExecutor;
|
||||||
|
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
|
||||||
|
use pathfinder_renderer::gpu::renderer::{DestFramebuffer, Renderer};
|
||||||
|
use pathfinder_renderer::options::RenderOptions;
|
||||||
|
use pathfinder_renderer::scene::Scene;
|
||||||
|
use pathfinder_simd::default::F32x4;
|
||||||
|
use std::ffi::CString;
|
||||||
|
use std::os::raw::{c_char, c_void};
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
|
||||||
|
pub const PF_CLEAR_FLAGS_HAS_COLOR: u8 = 0x1;
|
||||||
|
pub const PF_CLEAR_FLAGS_HAS_DEPTH: u8 = 0x2;
|
||||||
|
pub const PF_CLEAR_FLAGS_HAS_STENCIL: u8 = 0x4;
|
||||||
|
pub const PF_CLEAR_FLAGS_HAS_RECT: u8 = 0x8;
|
||||||
|
|
||||||
|
// Types
|
||||||
|
|
||||||
|
// `canvas`
|
||||||
|
pub type PFCanvasRef = *mut CanvasRenderingContext2D;
|
||||||
|
pub type PFPathRef = *mut Path2D;
|
||||||
|
|
||||||
|
// `geometry`
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PFPoint2DF {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PFPoint2DI {
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PFRectF {
|
||||||
|
pub origin: PFPoint2DF,
|
||||||
|
pub lower_right: PFPoint2DF,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PFRectI {
|
||||||
|
pub origin: PFPoint2DI,
|
||||||
|
pub lower_right: PFPoint2DI,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PFColorF {
|
||||||
|
pub r: f32,
|
||||||
|
pub g: f32,
|
||||||
|
pub b: f32,
|
||||||
|
pub a: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// `gl`
|
||||||
|
pub type PFGLDeviceRef = *mut GLDevice;
|
||||||
|
pub type PFGLVersion = GLVersion;
|
||||||
|
pub type PFGLFunctionLoader = extern "C" fn(name: *const c_char, userdata: *mut c_void)
|
||||||
|
-> *const c_void;
|
||||||
|
// `gpu`
|
||||||
|
pub type PFGLDestFramebufferRef = *mut DestFramebuffer<GLDevice>;
|
||||||
|
pub type PFGLRendererRef = *mut Renderer<GLDevice>;
|
||||||
|
// FIXME(pcwalton): Double-boxing is unfortunate. Remove this when `std::raw::TraitObject` is
|
||||||
|
// stable?
|
||||||
|
pub type PFResourceLoaderRef = *mut Box<dyn ResourceLoader>;
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PFClearParams {
|
||||||
|
pub color: PFColorF,
|
||||||
|
pub depth: f32,
|
||||||
|
pub stencil: u8,
|
||||||
|
pub rect: PFRectI,
|
||||||
|
pub flags: PFClearFlags,
|
||||||
|
}
|
||||||
|
pub type PFClearFlags = u8;
|
||||||
|
|
||||||
|
// `renderer`
|
||||||
|
pub type PFSceneRef = *mut Scene;
|
||||||
|
pub type PFSceneProxyRef = *mut SceneProxy;
|
||||||
|
// TODO(pcwalton)
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PFRenderOptions {
|
||||||
|
pub placeholder: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// `canvas`
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFCanvasCreate(size: *const PFPoint2DF) -> PFCanvasRef {
|
||||||
|
Box::into_raw(Box::new(CanvasRenderingContext2D::new((*size).to_rust())))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFCanvasDestroy(canvas: PFCanvasRef) {
|
||||||
|
drop(Box::from_raw(canvas))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the canvas.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFCanvasCreateScene(canvas: PFCanvasRef) -> PFSceneRef {
|
||||||
|
Box::into_raw(Box::new(Box::from_raw(canvas).into_scene()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFCanvasFillRect(canvas: PFCanvasRef, rect: *const PFRectF) {
|
||||||
|
(*canvas).fill_rect((*rect).to_rust())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFCanvasStrokeRect(canvas: PFCanvasRef, rect: *const PFRectF) {
|
||||||
|
(*canvas).stroke_rect((*rect).to_rust())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFCanvasSetLineWidth(canvas: PFCanvasRef, new_line_width: f32) {
|
||||||
|
(*canvas).set_line_width(new_line_width)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the path.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFCanvasFillPath(canvas: PFCanvasRef, path: PFPathRef) {
|
||||||
|
(*canvas).fill_path(*Box::from_raw(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the path.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFCanvasStrokePath(canvas: PFCanvasRef, path: PFPathRef) {
|
||||||
|
(*canvas).stroke_path(*Box::from_raw(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFPathCreate() -> PFPathRef {
|
||||||
|
Box::into_raw(Box::new(Path2D::new()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFPathDestroy(path: PFPathRef) {
|
||||||
|
drop(Box::from_raw(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFPathClone(path: PFPathRef) -> PFPathRef {
|
||||||
|
Box::into_raw(Box::new((*path).clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFPathMoveTo(path: PFPathRef, to: *const PFPoint2DF) {
|
||||||
|
(*path).move_to((*to).to_rust())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFPathLineTo(path: PFPathRef, to: *const PFPoint2DF) {
|
||||||
|
(*path).line_to((*to).to_rust())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFPathQuadraticCurveTo(path: PFPathRef,
|
||||||
|
ctrl: *const PFPoint2DF,
|
||||||
|
to: *const PFPoint2DF) {
|
||||||
|
(*path).quadratic_curve_to((*ctrl).to_rust(), (*to).to_rust())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFPathBezierCurveTo(path: PFPathRef,
|
||||||
|
ctrl0: *const PFPoint2DF,
|
||||||
|
ctrl1: *const PFPoint2DF,
|
||||||
|
to: *const PFPoint2DF) {
|
||||||
|
(*path).bezier_curve_to((*ctrl0).to_rust(), (*ctrl1).to_rust(), (*to).to_rust())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFPathClosePath(path: PFPathRef) {
|
||||||
|
(*path).close_path()
|
||||||
|
}
|
||||||
|
|
||||||
|
// `gl`
|
||||||
|
|
||||||
|
#[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>))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFGLLoadWith(loader: PFGLFunctionLoader, userdata: *mut c_void) {
|
||||||
|
gl::load_with(|name| {
|
||||||
|
let name = CString::new(name).unwrap();
|
||||||
|
loader(name.as_ptr(), userdata)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFGLDeviceCreate(version: PFGLVersion, default_framebuffer: u32)
|
||||||
|
-> PFGLDeviceRef {
|
||||||
|
Box::into_raw(Box::new(GLDevice::new(version, default_framebuffer)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFGLDeviceDestroy(device: PFGLDeviceRef) {
|
||||||
|
drop(Box::from_raw(device))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFGLDeviceClear(device: PFGLDeviceRef, params: *const PFClearParams) {
|
||||||
|
(*device).clear(&(*params).to_rust())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFResourceLoaderDestroy(loader: PFResourceLoaderRef) {
|
||||||
|
drop(Box::from_raw(loader))
|
||||||
|
}
|
||||||
|
|
||||||
|
// `gpu`
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFGLDestFramebufferCreateFullWindow(window_size: *const PFPoint2DI)
|
||||||
|
-> PFGLDestFramebufferRef {
|
||||||
|
Box::into_raw(Box::new(DestFramebuffer::full_window((*window_size).to_rust())))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFGLDestFramebufferDestroy(dest_framebuffer: PFGLDestFramebufferRef) {
|
||||||
|
drop(Box::from_raw(dest_framebuffer))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes ownership of `device` and `dest_framebuffer`, but not `resources`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFGLRendererCreate(device: PFGLDeviceRef,
|
||||||
|
resources: PFResourceLoaderRef,
|
||||||
|
dest_framebuffer: PFGLDestFramebufferRef)
|
||||||
|
-> PFGLRendererRef {
|
||||||
|
Box::into_raw(Box::new(Renderer::new(*Box::from_raw(device),
|
||||||
|
&**resources,
|
||||||
|
*Box::from_raw(dest_framebuffer))))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFGLRendererDestroy(renderer: PFGLRendererRef) {
|
||||||
|
drop(Box::from_raw(renderer))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFGLRendererGetDevice(renderer: PFGLRendererRef) -> PFGLDeviceRef {
|
||||||
|
&mut (*renderer).device
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFSceneProxyBuildAndRenderGL(scene_proxy: PFSceneProxyRef,
|
||||||
|
renderer: PFGLRendererRef,
|
||||||
|
options: *const PFRenderOptions) {
|
||||||
|
(*scene_proxy).build_and_render(&mut *renderer, (*options).to_rust())
|
||||||
|
}
|
||||||
|
|
||||||
|
// `renderer`
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFSceneDestroy(scene: PFSceneRef) {
|
||||||
|
drop(Box::from_raw(scene))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFSceneProxyCreateFromSceneAndRayonExecutor(scene: PFSceneRef)
|
||||||
|
-> PFSceneProxyRef {
|
||||||
|
Box::into_raw(Box::new(SceneProxy::from_scene(*Box::from_raw(scene), RayonExecutor)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFSceneProxyDestroy(scene_proxy: PFSceneProxyRef) {
|
||||||
|
drop(Box::from_raw(scene_proxy))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers for `geometry`
|
||||||
|
|
||||||
|
impl PFColorF {
|
||||||
|
#[inline]
|
||||||
|
pub fn to_rust(&self) -> ColorF {
|
||||||
|
ColorF(F32x4::new(self.r, self.g, self.b, self.a))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PFRectF {
|
||||||
|
#[inline]
|
||||||
|
pub fn to_rust(&self) -> RectF32 {
|
||||||
|
RectF32::from_points(self.origin.to_rust(), self.lower_right.to_rust())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PFRectI {
|
||||||
|
#[inline]
|
||||||
|
pub fn to_rust(&self) -> RectI32 {
|
||||||
|
RectI32::from_points(self.origin.to_rust(), self.lower_right.to_rust())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PFPoint2DF {
|
||||||
|
#[inline]
|
||||||
|
pub fn to_rust(&self) -> Point2DF32 {
|
||||||
|
Point2DF32::new(self.x, self.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PFPoint2DI {
|
||||||
|
#[inline]
|
||||||
|
pub fn to_rust(&self) -> Point2DI32 {
|
||||||
|
Point2DI32::new(self.x, self.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers for `gpu`
|
||||||
|
|
||||||
|
impl PFClearParams {
|
||||||
|
pub fn to_rust(&self) -> ClearParams {
|
||||||
|
ClearParams {
|
||||||
|
color: if (self.flags & PF_CLEAR_FLAGS_HAS_COLOR) != 0 {
|
||||||
|
Some(self.color.to_rust())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
rect: if (self.flags & PF_CLEAR_FLAGS_HAS_RECT) != 0 {
|
||||||
|
Some(self.rect.to_rust())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
depth: if (self.flags & PF_CLEAR_FLAGS_HAS_DEPTH) != 0 {
|
||||||
|
Some(self.depth)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
stencil: if (self.flags & PF_CLEAR_FLAGS_HAS_STENCIL) != 0 {
|
||||||
|
Some(self.stencil)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers for `renderer`
|
||||||
|
|
||||||
|
impl PFRenderOptions {
|
||||||
|
pub fn to_rust(&self) -> RenderOptions {
|
||||||
|
RenderOptions::default()
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,9 @@ version = "0.1.0"
|
||||||
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["rlib", "staticlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
font-kit = "0.1"
|
font-kit = "0.1"
|
||||||
|
|
||||||
|
@ -18,4 +21,4 @@ path = "../text"
|
||||||
|
|
||||||
[dependencies.skribo]
|
[dependencies.skribo]
|
||||||
git = "https://github.com/linebender/skribo.git"
|
git = "https://github.com/linebender/skribo.git"
|
||||||
rev = "f759c2d2c188b668bce998179f03b6319835353e"
|
rev = "a89e9ca99e0d6736ea1b7754517f4df14fd96a2b"
|
||||||
|
|
|
@ -19,8 +19,9 @@ use pathfinder_geometry::basic::rect::RectF32;
|
||||||
use pathfinder_geometry::basic::transform2d::Transform2DF32;
|
use pathfinder_geometry::basic::transform2d::Transform2DF32;
|
||||||
use pathfinder_geometry::color::ColorU;
|
use pathfinder_geometry::color::ColorU;
|
||||||
use pathfinder_geometry::outline::{Contour, Outline};
|
use pathfinder_geometry::outline::{Contour, Outline};
|
||||||
use pathfinder_geometry::stroke::OutlineStrokeToFill;
|
use pathfinder_geometry::stroke::{LineCap, LineJoin, OutlineStrokeToFill, StrokeStyle};
|
||||||
use pathfinder_renderer::scene::{Paint, PathObject, Scene};
|
use pathfinder_renderer::paint::Paint;
|
||||||
|
use pathfinder_renderer::scene::{PathObject, Scene};
|
||||||
use pathfinder_text::{SceneExt, TextRenderMode};
|
use pathfinder_text::{SceneExt, TextRenderMode};
|
||||||
use skribo::{FontCollection, FontFamily, TextStyle};
|
use skribo::{FontCollection, FontFamily, TextStyle};
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
|
@ -94,10 +95,12 @@ impl CanvasRenderingContext2D {
|
||||||
pub fn fill_text(&mut self, string: &str, position: Point2DF32) {
|
pub fn fill_text(&mut self, string: &str, position: Point2DF32) {
|
||||||
// TODO(pcwalton): Report errors.
|
// TODO(pcwalton): Report errors.
|
||||||
let paint_id = self.scene.push_paint(&self.current_state.fill_paint);
|
let paint_id = self.scene.push_paint(&self.current_state.fill_paint);
|
||||||
|
let transform = Transform2DF32::from_translation(position).post_mul(&self.current_state
|
||||||
|
.transform);
|
||||||
drop(self.scene.push_text(string,
|
drop(self.scene.push_text(string,
|
||||||
&TextStyle { size: self.current_state.font_size },
|
&TextStyle { size: self.current_state.font_size },
|
||||||
&self.current_state.font_collection,
|
&self.current_state.font_collection,
|
||||||
&Transform2DF32::from_translation(&position),
|
&transform,
|
||||||
TextRenderMode::Fill,
|
TextRenderMode::Fill,
|
||||||
HintingOptions::None,
|
HintingOptions::None,
|
||||||
paint_id));
|
paint_id));
|
||||||
|
@ -106,11 +109,13 @@ impl CanvasRenderingContext2D {
|
||||||
pub fn stroke_text(&mut self, string: &str, position: Point2DF32) {
|
pub fn stroke_text(&mut self, string: &str, position: Point2DF32) {
|
||||||
// TODO(pcwalton): Report errors.
|
// TODO(pcwalton): Report errors.
|
||||||
let paint_id = self.scene.push_paint(&self.current_state.stroke_paint);
|
let paint_id = self.scene.push_paint(&self.current_state.stroke_paint);
|
||||||
|
let transform = Transform2DF32::from_translation(position).post_mul(&self.current_state
|
||||||
|
.transform);
|
||||||
drop(self.scene.push_text(string,
|
drop(self.scene.push_text(string,
|
||||||
&TextStyle { size: self.current_state.font_size },
|
&TextStyle { size: self.current_state.font_size },
|
||||||
&self.current_state.font_collection,
|
&self.current_state.font_collection,
|
||||||
&Transform2DF32::from_translation(&position),
|
&transform,
|
||||||
TextRenderMode::Stroke(self.current_state.line_width),
|
TextRenderMode::Stroke(self.current_state.stroke_style),
|
||||||
HintingOptions::None,
|
HintingOptions::None,
|
||||||
paint_id));
|
paint_id));
|
||||||
}
|
}
|
||||||
|
@ -119,7 +124,17 @@ impl CanvasRenderingContext2D {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_line_width(&mut self, new_line_width: f32) {
|
pub fn set_line_width(&mut self, new_line_width: f32) {
|
||||||
self.current_state.line_width = new_line_width
|
self.current_state.stroke_style.line_width = new_line_width
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_line_cap(&mut self, new_line_cap: LineCap) {
|
||||||
|
self.current_state.stroke_style.line_cap = new_line_cap
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_line_join(&mut self, new_line_join: LineJoin) {
|
||||||
|
self.current_state.stroke_style.line_join = new_line_join
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -139,23 +154,64 @@ impl CanvasRenderingContext2D {
|
||||||
self.current_state.font_size = new_font_size;
|
self.current_state.font_size = new_font_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Paths
|
// Drawing paths
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn fill_path(&mut self, path: Path2D) {
|
pub fn fill_path(&mut self, path: Path2D) {
|
||||||
let paint_id = self.scene.push_paint(&self.current_state.fill_paint);
|
let mut outline = path.into_outline();
|
||||||
self.scene.push_path(PathObject::new(path.into_outline(), paint_id, String::new()))
|
outline.transform(&self.current_state.transform);
|
||||||
|
|
||||||
|
let paint = self.current_state.resolve_paint(self.current_state.fill_paint);
|
||||||
|
let paint_id = self.scene.push_paint(&paint);
|
||||||
|
|
||||||
|
self.scene.push_path(PathObject::new(outline, paint_id, String::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn stroke_path(&mut self, path: Path2D) {
|
pub fn stroke_path(&mut self, path: Path2D) {
|
||||||
let paint_id = self.scene.push_paint(&self.current_state.stroke_paint);
|
let paint = self.current_state.resolve_paint(self.current_state.stroke_paint);
|
||||||
let stroke_width = f32::max(self.current_state.line_width, HAIRLINE_STROKE_WIDTH);
|
let paint_id = self.scene.push_paint(&paint);
|
||||||
let mut stroke_to_fill = OutlineStrokeToFill::new(path.into_outline(), stroke_width);
|
|
||||||
|
let mut stroke_style = self.current_state.stroke_style;
|
||||||
|
stroke_style.line_width = f32::max(stroke_style.line_width, HAIRLINE_STROKE_WIDTH);
|
||||||
|
|
||||||
|
let mut stroke_to_fill = OutlineStrokeToFill::new(path.into_outline(), stroke_style);
|
||||||
stroke_to_fill.offset();
|
stroke_to_fill.offset();
|
||||||
|
stroke_to_fill.outline.transform(&self.current_state.transform);
|
||||||
self.scene.push_path(PathObject::new(stroke_to_fill.outline, paint_id, String::new()))
|
self.scene.push_path(PathObject::new(stroke_to_fill.outline, paint_id, String::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transformations
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn current_transform(&self) -> Transform2DF32 {
|
||||||
|
self.current_state.transform
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_current_transform(&mut self, new_transform: &Transform2DF32) {
|
||||||
|
self.current_state.transform = *new_transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn reset_transform(&mut self) {
|
||||||
|
self.current_state.transform = Transform2DF32::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compositing
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn global_alpha(&self) -> f32 {
|
||||||
|
self.current_state.global_alpha
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_global_alpha(&mut self, new_global_alpha: f32) {
|
||||||
|
self.current_state.global_alpha = new_global_alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The canvas state
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn save(&mut self) {
|
pub fn save(&mut self) {
|
||||||
self.saved_states.push(self.current_state.clone());
|
self.saved_states.push(self.current_state.clone());
|
||||||
|
@ -171,23 +227,32 @@ impl CanvasRenderingContext2D {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
transform: Transform2DF32,
|
||||||
font_collection: Arc<FontCollection>,
|
font_collection: Arc<FontCollection>,
|
||||||
font_size: f32,
|
font_size: f32,
|
||||||
fill_paint: Paint,
|
fill_paint: Paint,
|
||||||
stroke_paint: Paint,
|
stroke_paint: Paint,
|
||||||
line_width: f32,
|
stroke_style: StrokeStyle,
|
||||||
|
global_alpha: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
fn default(default_font_collection: Arc<FontCollection>) -> State {
|
fn default(default_font_collection: Arc<FontCollection>) -> State {
|
||||||
State {
|
State {
|
||||||
|
transform: Transform2DF32::default(),
|
||||||
font_collection: default_font_collection,
|
font_collection: default_font_collection,
|
||||||
font_size: DEFAULT_FONT_SIZE,
|
font_size: DEFAULT_FONT_SIZE,
|
||||||
fill_paint: Paint { color: ColorU::black() },
|
fill_paint: Paint { color: ColorU::black() },
|
||||||
stroke_paint: Paint { color: ColorU::black() },
|
stroke_paint: Paint { color: ColorU::black() },
|
||||||
line_width: 1.0,
|
stroke_style: StrokeStyle::default(),
|
||||||
|
global_alpha: 1.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_paint(&self, mut paint: Paint) -> Paint {
|
||||||
|
paint.color.a = (paint.color.a as f32 * self.global_alpha).round() as u8;
|
||||||
|
paint
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -196,7 +261,7 @@ pub struct Path2D {
|
||||||
current_contour: Contour,
|
current_contour: Contour,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(pcwalton): `arc`, `ellipse`
|
// TODO(pcwalton): `ellipse`
|
||||||
impl Path2D {
|
impl Path2D {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Path2D {
|
pub fn new() -> Path2D {
|
||||||
|
@ -230,6 +295,11 @@ impl Path2D {
|
||||||
self.current_contour.push_cubic(ctrl0, ctrl1, to);
|
self.current_contour.push_cubic(ctrl0, ctrl1, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn arc(&mut self, center: Point2DF32, radius: f32, start_angle: f32, end_angle: f32) {
|
||||||
|
self.current_contour.push_arc(center, radius, start_angle, end_angle);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn rect(&mut self, rect: RectF32) {
|
pub fn rect(&mut self, rect: RectF32) {
|
||||||
self.flush_current_contour();
|
self.flush_current_contour();
|
||||||
self.current_contour.push_endpoint(rect.origin());
|
self.current_contour.push_endpoint(rect.origin());
|
||||||
|
|
|
@ -56,7 +56,7 @@ impl Camera {
|
||||||
let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32
|
let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32
|
||||||
* scale_factor_for_view_box(view_box);
|
* scale_factor_for_view_box(view_box);
|
||||||
let origin = viewport_size.to_f32().scale(0.5) - view_box.size().scale(scale * 0.5);
|
let origin = viewport_size.to_f32().scale(0.5) - view_box.size().scale(scale * 0.5);
|
||||||
Camera::TwoD(Transform2DF32::from_scale(&Point2DF32::splat(scale)).post_translate(origin))
|
Camera::TwoD(Transform2DF32::from_scale(Point2DF32::splat(scale)).post_translate(origin))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_3d(mode: Mode, view_box: RectF32, viewport_size: Point2DI32) -> Camera {
|
fn new_3d(mode: Mode, view_box: RectF32, viewport_size: Point2DI32) -> Camera {
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
//! GPU rendering code specifically for the demo.
|
//! GPU rendering code specifically for the demo.
|
||||||
|
|
||||||
use pathfinder_gpu::resources::ResourceLoader;
|
use pathfinder_gpu::resources::ResourceLoader;
|
||||||
use pathfinder_gpu::{BufferTarget, Device, VertexAttrType};
|
use pathfinder_gpu::{BufferTarget, Device, VertexAttrClass, VertexAttrDescriptor, VertexAttrType};
|
||||||
|
|
||||||
pub struct GroundProgram<D>
|
pub struct GroundProgram<D>
|
||||||
where
|
where
|
||||||
|
@ -67,7 +67,14 @@ where
|
||||||
device.bind_vertex_array(&vertex_array);
|
device.bind_vertex_array(&vertex_array);
|
||||||
device.use_program(&ground_program.program);
|
device.use_program(&ground_program.program);
|
||||||
device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex);
|
device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex);
|
||||||
device.configure_float_vertex_attr(&position_attr, 2, VertexAttrType::U8, false, 0, 0, 0);
|
device.configure_vertex_attr(&position_attr, &VertexAttrDescriptor {
|
||||||
|
size: 2,
|
||||||
|
class: VertexAttrClass::Float,
|
||||||
|
attr_type: VertexAttrType::U8,
|
||||||
|
stride: 0,
|
||||||
|
offset: 0,
|
||||||
|
divisor: 0,
|
||||||
|
});
|
||||||
|
|
||||||
GroundVertexArray { vertex_array }
|
GroundVertexArray { vertex_array }
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,7 +143,7 @@ impl<W> DemoApp<W> where W: Window {
|
||||||
viewport.size());
|
viewport.size());
|
||||||
let camera = Camera::new(options.mode, scene_metadata.view_box, viewport.size());
|
let camera = Camera::new(options.mode, scene_metadata.view_box, viewport.size());
|
||||||
|
|
||||||
let scene_proxy = SceneProxy::new(built_svg.scene, executor);
|
let scene_proxy = SceneProxy::from_scene(built_svg.scene, executor);
|
||||||
|
|
||||||
let ground_program = GroundProgram::new(&renderer.device, resources);
|
let ground_program = GroundProgram::new(&renderer.device, resources);
|
||||||
let ground_vertex_array = GroundVertexArray::new(&renderer.device,
|
let ground_vertex_array = GroundVertexArray::new(&renderer.device,
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
OBJ_DIR?=build
|
||||||
|
TARGET_DIR?=build
|
||||||
|
SRC_DIR?=.
|
||||||
|
RUST_TARGET_DIR?=../../target
|
||||||
|
RUST_SRC_DIR?=../../c
|
||||||
|
RUSTFLAGS?=-C target-cpu=native
|
||||||
|
|
||||||
|
CFLAGS?=-Wall -g -I../../c/include
|
||||||
|
LIBS?=-lpathfinder_c
|
||||||
|
MKDIR?=mkdir -p
|
||||||
|
RM?=rm
|
||||||
|
CARGO?=cargo
|
||||||
|
|
||||||
|
UNAME=$(shell uname -s)
|
||||||
|
ifeq ($(UNAME),Darwin)
|
||||||
|
# FIXME(pcwalton): Don't link against HarfBuzz!!
|
||||||
|
LIBS+=-framework OpenGL -framework CoreFoundation -framework CoreGraphics -framework CoreText
|
||||||
|
LIBS+=-lharfbuzz
|
||||||
|
else
|
||||||
|
LIBS+=-lGL
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(DEBUG),)
|
||||||
|
CFLAGS+=-O2
|
||||||
|
LDFLAGS?=-L$(RUST_TARGET_DIR)/release
|
||||||
|
CARGOFLAGS?=--release
|
||||||
|
else
|
||||||
|
CFLAGS+=-O0
|
||||||
|
LDFLAGS?=-L$(RUST_TARGET_DIR)/debug
|
||||||
|
CARGOFLAGS?=
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: $(TARGET_DIR)/c_canvas_minimal
|
||||||
|
|
||||||
|
.PHONY: clean rustlib
|
||||||
|
|
||||||
|
$(TARGET_DIR)/c_canvas_minimal: $(OBJ_DIR)/c_canvas_minimal.o rustlib
|
||||||
|
$(MKDIR) $(TARGET_DIR) && $(CC) $(LDFLAGS) $(LIBS) `sdl2-config --libs` -o $@ $<
|
||||||
|
|
||||||
|
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
|
||||||
|
$(MKDIR) $(OBJ_DIR) && $(CC) -c $(CFLAGS) `sdl2-config --cflags` -o $@ $<
|
||||||
|
|
||||||
|
rustlib:
|
||||||
|
cd $(RUST_SRC_DIR) && RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) build $(CARGOFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) -rf $(TARGET_DIR) && $(RM) -rf $(OBJ_DIR)
|
|
@ -0,0 +1,117 @@
|
||||||
|
// pathfinder/examples/c_canvas_minimal/c_canvas_minimal.c
|
||||||
|
//
|
||||||
|
// Copyright © 2019 The Pathfinder Project Developers.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <SDL2/SDL_opengl.h>
|
||||||
|
#include <pathfinder/pathfinder.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
static const void *LoadGLFunction(const char *name, void *userdata);
|
||||||
|
static void SDLFailed(const char *msg);
|
||||||
|
|
||||||
|
int main(int argc, const char **argv) {
|
||||||
|
// Set up SDL2.
|
||||||
|
if (SDL_Init(SDL_INIT_EVENTS | SDL_INIT_VIDEO) != 0)
|
||||||
|
SDLFailed("Failed to initialize SDL");
|
||||||
|
|
||||||
|
// Make sure we have at least a GL 3.0 context. Pathfinder requires this.
|
||||||
|
if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3) != 0)
|
||||||
|
SDLFailed("Failed to set GL major version");
|
||||||
|
if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2) != 0)
|
||||||
|
SDLFailed("Failed to set GL minor version");
|
||||||
|
if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE) != 0)
|
||||||
|
SDLFailed("Failed to set GL profile");
|
||||||
|
if (SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1) != 0)
|
||||||
|
SDLFailed("Failed to make GL context double-buffered");
|
||||||
|
|
||||||
|
// Open a window.
|
||||||
|
SDL_Window *window = SDL_CreateWindow("Minimal canvas example (C API)",
|
||||||
|
SDL_WINDOWPOS_CENTERED,
|
||||||
|
SDL_WINDOWPOS_CENTERED,
|
||||||
|
640,
|
||||||
|
480,
|
||||||
|
SDL_WINDOW_OPENGL);
|
||||||
|
if (window == NULL)
|
||||||
|
SDLFailed("Failed to create SDL window");
|
||||||
|
|
||||||
|
// Create the GL context, and make it current.
|
||||||
|
SDL_GLContext gl_context = SDL_GL_CreateContext(window);
|
||||||
|
if (gl_context == NULL)
|
||||||
|
SDLFailed("Failed to create GL context");
|
||||||
|
|
||||||
|
// Put the window on screen.
|
||||||
|
SDL_ShowWindow(window);
|
||||||
|
|
||||||
|
// Create a Pathfinder renderer.
|
||||||
|
PFGLLoadWith(LoadGLFunction, NULL);
|
||||||
|
PFGLDestFramebufferRef dest_framebuffer =
|
||||||
|
PFGLDestFramebufferCreateFullWindow(&(PFPoint2DI){640, 480});
|
||||||
|
PFGLRendererRef renderer = PFGLRendererCreate(PFGLDeviceCreate(PF_GL_VERSION_GL3, 0),
|
||||||
|
PFFilesystemResourceLoaderLocate(),
|
||||||
|
dest_framebuffer);
|
||||||
|
|
||||||
|
// Clear to white.
|
||||||
|
PFGLDeviceClear(PFGLRendererGetDevice(renderer), &(PFClearParams){
|
||||||
|
(PFColorF){1.0, 1.0, 1.0, 1.0}, 0.0, 0, {0}, PF_CLEAR_FLAGS_HAS_COLOR
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make a canvas. We're going to draw a house.
|
||||||
|
PFCanvasRef canvas = PFCanvasCreate(&(PFPoint2DF){640.0f, 480.0f});
|
||||||
|
|
||||||
|
// Set line width.
|
||||||
|
PFCanvasSetLineWidth(canvas, 10.0f);
|
||||||
|
|
||||||
|
// Draw walls.
|
||||||
|
PFCanvasStrokeRect(canvas, &(PFRectF){{75.0f, 140.0f}, {225.0f, 250.0f}});
|
||||||
|
|
||||||
|
// Draw door.
|
||||||
|
PFCanvasFillRect(canvas, &(PFRectF){{130.0f, 190.0f}, {170.0f, 250.0f}});
|
||||||
|
|
||||||
|
// Draw roof.
|
||||||
|
PFPathRef path = PFPathCreate();
|
||||||
|
PFPathMoveTo(path, &(PFPoint2DF){50.0, 140.0});
|
||||||
|
PFPathLineTo(path, &(PFPoint2DF){150.0, 60.0});
|
||||||
|
PFPathLineTo(path, &(PFPoint2DF){250.0, 140.0});
|
||||||
|
PFPathClosePath(path);
|
||||||
|
PFCanvasStrokePath(canvas, path);
|
||||||
|
|
||||||
|
// Render the canvas to screen.
|
||||||
|
PFSceneRef scene = PFCanvasCreateScene(canvas);
|
||||||
|
PFSceneProxyRef scene_proxy = PFSceneProxyCreateFromSceneAndRayonExecutor(scene);
|
||||||
|
PFSceneProxyBuildAndRenderGL(scene_proxy, renderer, &(PFRenderOptions){0});
|
||||||
|
SDL_GL_SwapWindow(window);
|
||||||
|
|
||||||
|
// Wait for a keypress.
|
||||||
|
while (true) {
|
||||||
|
SDL_Event event;
|
||||||
|
if (SDL_WaitEvent(&event) == 0)
|
||||||
|
SDLFailed("Failed to get SDL event");
|
||||||
|
if (event.type == SDL_QUIT ||
|
||||||
|
(event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finish up.
|
||||||
|
SDL_Quit();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const void *LoadGLFunction(const char *name, void *userdata) {
|
||||||
|
return SDL_GL_GetProcAddress(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SDLFailed(const char *msg) {
|
||||||
|
fprintf(stderr, "%s: %s\n", msg, SDL_GetError());
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
|
@ -74,7 +74,7 @@ fn main() {
|
||||||
canvas.stroke_path(path);
|
canvas.stroke_path(path);
|
||||||
|
|
||||||
// Render the canvas to screen.
|
// Render the canvas to screen.
|
||||||
let scene = SceneProxy::new(canvas.into_scene(), RayonExecutor);
|
let scene = SceneProxy::from_scene(canvas.into_scene(), RayonExecutor);
|
||||||
scene.build_and_render(&mut renderer, RenderOptions::default());
|
scene.build_and_render(&mut renderer, RenderOptions::default());
|
||||||
window.gl_swap_window();
|
window.gl_swap_window();
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ use pathfinder_renderer::options::RenderOptions;
|
||||||
use sdl2::event::Event;
|
use sdl2::event::Event;
|
||||||
use sdl2::keyboard::Keycode;
|
use sdl2::keyboard::Keycode;
|
||||||
use sdl2::video::GLProfile;
|
use sdl2::video::GLProfile;
|
||||||
|
use std::f32::consts::PI;
|
||||||
use std::f32;
|
use std::f32;
|
||||||
|
|
||||||
const VELOCITY: f32 = 0.02;
|
const VELOCITY: f32 = 0.02;
|
||||||
|
@ -84,7 +85,7 @@ fn main() {
|
||||||
|
|
||||||
struct MoireRenderer {
|
struct MoireRenderer {
|
||||||
renderer: Renderer<GLDevice>,
|
renderer: Renderer<GLDevice>,
|
||||||
scene: Option<SceneProxy>,
|
scene: SceneProxy,
|
||||||
frame: i32,
|
frame: i32,
|
||||||
window_size: Point2DI32,
|
window_size: Point2DI32,
|
||||||
drawable_size: Point2DI32,
|
drawable_size: Point2DI32,
|
||||||
|
@ -97,7 +98,7 @@ impl MoireRenderer {
|
||||||
-> MoireRenderer {
|
-> MoireRenderer {
|
||||||
MoireRenderer {
|
MoireRenderer {
|
||||||
renderer,
|
renderer,
|
||||||
scene: None,
|
scene: SceneProxy::new(RayonExecutor),
|
||||||
frame: 0,
|
frame: 0,
|
||||||
window_size,
|
window_size,
|
||||||
drawable_size,
|
drawable_size,
|
||||||
|
@ -130,47 +131,28 @@ impl MoireRenderer {
|
||||||
let mut canvas = CanvasRenderingContext2D::new(self.drawable_size.to_f32());
|
let mut canvas = CanvasRenderingContext2D::new(self.drawable_size.to_f32());
|
||||||
canvas.set_line_width(CIRCLE_THICKNESS * self.device_pixel_ratio);
|
canvas.set_line_width(CIRCLE_THICKNESS * self.device_pixel_ratio);
|
||||||
canvas.set_stroke_style(FillStyle::Color(foreground_color.to_u8()));
|
canvas.set_stroke_style(FillStyle::Color(foreground_color.to_u8()));
|
||||||
|
canvas.set_global_alpha(0.75);
|
||||||
|
|
||||||
// Draw circles.
|
// Draw circles.
|
||||||
self.draw_circles(&mut canvas, outer_center);
|
self.draw_circles(&mut canvas, outer_center);
|
||||||
self.draw_circles(&mut canvas, inner_center);
|
self.draw_circles(&mut canvas, inner_center);
|
||||||
|
|
||||||
// Build scene if necessary.
|
// Build and render scene.
|
||||||
// TODO(pcwalton): Allow the user to build an empty scene proxy so they don't have to do this.
|
self.scene.replace_scene(canvas.into_scene());
|
||||||
match self.scene {
|
self.scene.build_and_render(&mut self.renderer, RenderOptions::default());
|
||||||
None => self.scene = Some(SceneProxy::new(canvas.into_scene(), RayonExecutor)),
|
|
||||||
Some(ref mut scene) => scene.replace_scene(canvas.into_scene()),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render the scene.
|
|
||||||
self.scene.as_mut().unwrap().build_and_render(&mut self.renderer,
|
|
||||||
RenderOptions::default());
|
|
||||||
|
|
||||||
self.frame += 1;
|
self.frame += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_circles(&self, canvas: &mut CanvasRenderingContext2D, center: Point2DF32) {
|
fn draw_circles(&self, canvas: &mut CanvasRenderingContext2D, center: Point2DF32) {
|
||||||
|
let center = center.scale(self.device_pixel_ratio);
|
||||||
for index in 0..CIRCLE_COUNT {
|
for index in 0..CIRCLE_COUNT {
|
||||||
|
let radius = (index + 1) as f32 * CIRCLE_SPACING * self.device_pixel_ratio;
|
||||||
let mut path = Path2D::new();
|
let mut path = Path2D::new();
|
||||||
self.add_circle_subpath(&mut path,
|
path.arc(center, radius, 0.0, PI * 2.0);
|
||||||
center.scale(self.device_pixel_ratio),
|
|
||||||
index as f32 * CIRCLE_SPACING * self.device_pixel_ratio);
|
|
||||||
canvas.stroke_path(path);
|
canvas.stroke_path(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_circle_subpath(&self, path: &mut Path2D, center: Point2DF32, radius: f32) {
|
|
||||||
path.move_to(center + Point2DF32::new(0.0, -radius));
|
|
||||||
path.quadratic_curve_to(center + Point2DF32::new(radius, -radius),
|
|
||||||
center + Point2DF32::new(radius, 0.0));
|
|
||||||
path.quadratic_curve_to(center + Point2DF32::new(radius, radius),
|
|
||||||
center + Point2DF32::new(0.0, radius));
|
|
||||||
path.quadratic_curve_to(center + Point2DF32::new(-radius, radius),
|
|
||||||
center + Point2DF32::new(-radius, 0.0));
|
|
||||||
path.quadratic_curve_to(center + Point2DF32::new(-radius, -radius),
|
|
||||||
center + Point2DF32::new(0.0, -radius));
|
|
||||||
path.close_path();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ColorGradient([ColorF; 5]);
|
struct ColorGradient([ColorF; 5]);
|
||||||
|
|
|
@ -8,9 +8,8 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use pathfinder_canvas::{CanvasRenderingContext2D, Path2D};
|
use pathfinder_canvas::CanvasRenderingContext2D;
|
||||||
use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32};
|
use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32};
|
||||||
use pathfinder_geometry::basic::rect::RectF32;
|
|
||||||
use pathfinder_geometry::color::ColorF;
|
use pathfinder_geometry::color::ColorF;
|
||||||
use pathfinder_gl::{GLDevice, GLVersion};
|
use pathfinder_gl::{GLDevice, GLVersion};
|
||||||
use pathfinder_gpu::resources::FilesystemResourceLoader;
|
use pathfinder_gpu::resources::FilesystemResourceLoader;
|
||||||
|
@ -62,7 +61,7 @@ fn main() {
|
||||||
canvas.stroke_text("Goodbye Pathfinder!", Point2DF32::new(32.0, 96.0));
|
canvas.stroke_text("Goodbye Pathfinder!", Point2DF32::new(32.0, 96.0));
|
||||||
|
|
||||||
// Render the canvas to screen.
|
// Render the canvas to screen.
|
||||||
let scene = SceneProxy::new(canvas.into_scene(), RayonExecutor);
|
let scene = SceneProxy::from_scene(canvas.into_scene(), RayonExecutor);
|
||||||
scene.build_and_render(&mut renderer, RenderOptions::default());
|
scene.build_and_render(&mut renderer, RenderOptions::default());
|
||||||
window.gl_swap_window();
|
window.gl_swap_window();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "lottie_basic"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[dependencies.pathfinder_lottie]
|
||||||
|
path = "../../lottie"
|
|
@ -0,0 +1,23 @@
|
||||||
|
// pathfinder/examples/lottie_basic/src/main.rs
|
||||||
|
//
|
||||||
|
// Copyright © 2019 The Pathfinder Project Developers.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
//! Experimental example for reading Lottie animations. This is very incomplete.
|
||||||
|
|
||||||
|
use pathfinder_lottie::Lottie;
|
||||||
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::BufReader;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let path = env::args().skip(1).next().unwrap();
|
||||||
|
let file = BufReader::new(File::open(path).unwrap());
|
||||||
|
let lottie = Lottie::from_reader(file).unwrap();
|
||||||
|
println!("{:#?}", lottie);
|
||||||
|
}
|
|
@ -11,6 +11,7 @@
|
||||||
//! Line segment types, optimized with SIMD.
|
//! Line segment types, optimized with SIMD.
|
||||||
|
|
||||||
use crate::basic::point::Point2DF32;
|
use crate::basic::point::Point2DF32;
|
||||||
|
use crate::basic::transform2d::Matrix2x2F32;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
use std::ops::{Add, Sub};
|
use std::ops::{Add, Sub};
|
||||||
|
@ -20,7 +21,7 @@ pub struct LineSegmentF32(pub F32x4);
|
||||||
|
|
||||||
impl LineSegmentF32 {
|
impl LineSegmentF32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(from: &Point2DF32, to: &Point2DF32) -> LineSegmentF32 {
|
pub fn new(from: Point2DF32, to: Point2DF32) -> LineSegmentF32 {
|
||||||
LineSegmentF32(from.0.concat_xy_xy(to.0))
|
LineSegmentF32(from.0.concat_xy_xy(to.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,15 +228,12 @@ impl LineSegmentF32 {
|
||||||
|
|
||||||
// http://www.cs.swan.ac.uk/~cssimon/line_intersection.html
|
// http://www.cs.swan.ac.uk/~cssimon/line_intersection.html
|
||||||
pub fn intersection_t(&self, other: &LineSegmentF32) -> Option<f32> {
|
pub fn intersection_t(&self, other: &LineSegmentF32) -> Option<f32> {
|
||||||
let d0d1 = self.vector().0.concat_xy_xy(other.vector().0);
|
let p0p1 = self.vector();
|
||||||
let offset = other.from() - self.from();
|
let matrix = Matrix2x2F32(other.vector().0.concat_xy_xy((-p0p1).0));
|
||||||
let factors = d0d1.concat_wz_yx(offset.0);
|
if f32::abs(matrix.det()) < EPSILON {
|
||||||
let terms = d0d1 * factors;
|
|
||||||
let denom = terms[0] - terms[1];
|
|
||||||
if f32::abs(denom) < EPSILON {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
return Some((terms[3] - terms[2]) / denom);
|
return Some(matrix.inverse().transform_point(self.from() - other.from()).y());
|
||||||
|
|
||||||
const EPSILON: f32 = 0.0001;
|
const EPSILON: f32 = 0.0001;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
//! 2D affine transforms.
|
//! 2D affine transforms.
|
||||||
|
|
||||||
|
use crate::basic::line_segment::LineSegmentF32;
|
||||||
use crate::basic::point::Point2DF32;
|
use crate::basic::point::Point2DF32;
|
||||||
use crate::basic::rect::RectF32;
|
use crate::basic::rect::RectF32;
|
||||||
use crate::basic::transform3d::Transform3DF32;
|
use crate::basic::transform3d::Transform3DF32;
|
||||||
|
@ -24,13 +25,13 @@ pub struct Matrix2x2F32(pub F32x4);
|
||||||
impl Default for Matrix2x2F32 {
|
impl Default for Matrix2x2F32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn default() -> Matrix2x2F32 {
|
fn default() -> Matrix2x2F32 {
|
||||||
Self::from_scale(&Point2DF32::splat(1.0))
|
Self::from_scale(Point2DF32::splat(1.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Matrix2x2F32 {
|
impl Matrix2x2F32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_scale(scale: &Point2DF32) -> Matrix2x2F32 {
|
pub fn from_scale(scale: Point2DF32) -> Matrix2x2F32 {
|
||||||
Matrix2x2F32(F32x4::new(scale.x(), 0.0, 0.0, scale.y()))
|
Matrix2x2F32(F32x4::new(scale.x(), 0.0, 0.0, scale.y()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +67,7 @@ impl Matrix2x2F32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn transform_point(&self, point: &Point2DF32) -> Point2DF32 {
|
pub fn transform_point(&self, point: Point2DF32) -> Point2DF32 {
|
||||||
let halves = self.0 * point.0.xxyy();
|
let halves = self.0 * point.0.xxyy();
|
||||||
Point2DF32(halves + halves.zwzw())
|
Point2DF32(halves + halves.zwzw())
|
||||||
}
|
}
|
||||||
|
@ -118,13 +119,13 @@ pub struct Transform2DF32 {
|
||||||
impl Default for Transform2DF32 {
|
impl Default for Transform2DF32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn default() -> Transform2DF32 {
|
fn default() -> Transform2DF32 {
|
||||||
Self::from_scale(&Point2DF32::splat(1.0))
|
Self::from_scale(Point2DF32::splat(1.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transform2DF32 {
|
impl Transform2DF32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_scale(scale: &Point2DF32) -> Transform2DF32 {
|
pub fn from_scale(scale: Point2DF32) -> Transform2DF32 {
|
||||||
Transform2DF32 {
|
Transform2DF32 {
|
||||||
matrix: Matrix2x2F32::from_scale(scale),
|
matrix: Matrix2x2F32::from_scale(scale),
|
||||||
vector: Point2DF32::default(),
|
vector: Point2DF32::default(),
|
||||||
|
@ -140,11 +141,8 @@ impl Transform2DF32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_translation(vector: &Point2DF32) -> Transform2DF32 {
|
pub fn from_translation(vector: Point2DF32) -> Transform2DF32 {
|
||||||
Transform2DF32 {
|
Transform2DF32 { matrix: Matrix2x2F32::default(), vector }
|
||||||
matrix: Matrix2x2F32::default(),
|
|
||||||
vector: *vector,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -154,10 +152,8 @@ impl Transform2DF32 {
|
||||||
translation: Point2DF32,
|
translation: Point2DF32,
|
||||||
) -> Transform2DF32 {
|
) -> Transform2DF32 {
|
||||||
let rotation = Transform2DF32::from_rotation(theta);
|
let rotation = Transform2DF32::from_rotation(theta);
|
||||||
let translation = Transform2DF32::from_translation(&translation);
|
let translation = Transform2DF32::from_translation(translation);
|
||||||
Transform2DF32::from_scale(&scale)
|
Transform2DF32::from_scale(scale).post_mul(&rotation).post_mul(&translation)
|
||||||
.post_mul(&rotation)
|
|
||||||
.post_mul(&translation)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -169,16 +165,22 @@ impl Transform2DF32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn transform_point(&self, point: &Point2DF32) -> Point2DF32 {
|
pub fn transform_point(&self, point: Point2DF32) -> Point2DF32 {
|
||||||
self.matrix.transform_point(point) + self.vector
|
self.matrix.transform_point(point) + self.vector
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn transform_line_segment(&self, line_segment: &LineSegmentF32) -> LineSegmentF32 {
|
||||||
|
LineSegmentF32::new(self.transform_point(line_segment.from()),
|
||||||
|
self.transform_point(line_segment.to()))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn transform_rect(&self, rect: &RectF32) -> RectF32 {
|
pub fn transform_rect(&self, rect: &RectF32) -> RectF32 {
|
||||||
let upper_left = self.transform_point(&rect.origin());
|
let upper_left = self.transform_point(rect.origin());
|
||||||
let upper_right = self.transform_point(&rect.upper_right());
|
let upper_right = self.transform_point(rect.upper_right());
|
||||||
let lower_left = self.transform_point(&rect.lower_left());
|
let lower_left = self.transform_point(rect.lower_left());
|
||||||
let lower_right = self.transform_point(&rect.lower_right());
|
let lower_right = self.transform_point(rect.lower_right());
|
||||||
let min_point = upper_left.min(upper_right).min(lower_left).min(lower_right);
|
let min_point = upper_left.min(upper_right).min(lower_left).min(lower_right);
|
||||||
let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right);
|
let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right);
|
||||||
RectF32::from_points(min_point, max_point)
|
RectF32::from_points(min_point, max_point)
|
||||||
|
@ -187,7 +189,7 @@ impl Transform2DF32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn post_mul(&self, other: &Transform2DF32) -> Transform2DF32 {
|
pub fn post_mul(&self, other: &Transform2DF32) -> Transform2DF32 {
|
||||||
let matrix = self.matrix.post_mul(&other.matrix);
|
let matrix = self.matrix.post_mul(&other.matrix);
|
||||||
let vector = other.transform_point(&self.vector);
|
let vector = other.transform_point(self.vector);
|
||||||
Transform2DF32 { matrix, vector }
|
Transform2DF32 { matrix, vector }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +245,7 @@ impl Transform2DF32 {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn post_translate(&self, vector: Point2DF32) -> Transform2DF32 {
|
pub fn post_translate(&self, vector: Point2DF32) -> Transform2DF32 {
|
||||||
self.post_mul(&Transform2DF32::from_translation(&vector))
|
self.post_mul(&Transform2DF32::from_translation(vector))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -253,7 +255,7 @@ impl Transform2DF32 {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn post_scale(&self, scale: Point2DF32) -> Transform2DF32 {
|
pub fn post_scale(&self, scale: Point2DF32) -> Transform2DF32 {
|
||||||
self.post_mul(&Transform2DF32::from_scale(&scale))
|
self.post_mul(&Transform2DF32::from_scale(scale))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the translation part of this matrix.
|
/// Returns the translation part of this matrix.
|
||||||
|
@ -303,18 +305,18 @@ where
|
||||||
if !segment.is_none() {
|
if !segment.is_none() {
|
||||||
segment
|
segment
|
||||||
.baseline
|
.baseline
|
||||||
.set_from(&self.transform.transform_point(&segment.baseline.from()));
|
.set_from(&self.transform.transform_point(segment.baseline.from()));
|
||||||
segment
|
segment
|
||||||
.baseline
|
.baseline
|
||||||
.set_to(&self.transform.transform_point(&segment.baseline.to()));
|
.set_to(&self.transform.transform_point(segment.baseline.to()));
|
||||||
if !segment.is_line() {
|
if !segment.is_line() {
|
||||||
segment
|
segment
|
||||||
.ctrl
|
.ctrl
|
||||||
.set_from(&self.transform.transform_point(&segment.ctrl.from()));
|
.set_from(&self.transform.transform_point(segment.ctrl.from()));
|
||||||
if !segment.is_quadratic() {
|
if !segment.is_quadratic() {
|
||||||
segment
|
segment
|
||||||
.ctrl
|
.ctrl
|
||||||
.set_to(&self.transform.transform_point(&segment.ctrl.to()));
|
.set_to(&self.transform.transform_point(segment.ctrl.to()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -325,7 +325,7 @@ impl ContourPolygonClipper {
|
||||||
Some(prev) => *prev,
|
Some(prev) => *prev,
|
||||||
};
|
};
|
||||||
for &next in &clip_polygon {
|
for &next in &clip_polygon {
|
||||||
self.clip_against(Edge(LineSegmentF32::new(&prev, &next)));
|
self.clip_against(Edge(LineSegmentF32::new(prev, next)));
|
||||||
prev = next;
|
prev = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,9 +19,12 @@ use crate::clip::{self, ContourPolygonClipper, ContourRectClipper};
|
||||||
use crate::dilation::ContourDilator;
|
use crate::dilation::ContourDilator;
|
||||||
use crate::orientation::Orientation;
|
use crate::orientation::Orientation;
|
||||||
use crate::segment::{Segment, SegmentFlags, SegmentKind};
|
use crate::segment::{Segment, SegmentFlags, SegmentKind};
|
||||||
|
use std::f32::consts::{FRAC_PI_2, PI};
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
const TWO_PI: f32 = PI * 2.0;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Outline {
|
pub struct Outline {
|
||||||
pub(crate) contours: Vec<Contour>,
|
pub(crate) contours: Vec<Contour>,
|
||||||
|
@ -126,6 +129,10 @@ impl Outline {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transform(&mut self, transform: &Transform2DF32) {
|
pub fn transform(&mut self, transform: &Transform2DF32) {
|
||||||
|
if transform.is_identity() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let mut new_bounds = None;
|
let mut new_bounds = None;
|
||||||
for contour in &mut self.contours {
|
for contour in &mut self.contours {
|
||||||
contour.transform(transform);
|
contour.transform(transform);
|
||||||
|
@ -262,6 +269,11 @@ impl Contour {
|
||||||
self.points[index as usize]
|
self.points[index as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn position_of_last(&self, index: u32) -> Point2DF32 {
|
||||||
|
self.points[self.points.len() - index as usize]
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn last_position(&self) -> Option<Point2DF32> {
|
pub(crate) fn last_position(&self) -> Option<Point2DF32> {
|
||||||
self.points.last().cloned()
|
self.points.last().cloned()
|
||||||
|
@ -292,7 +304,10 @@ impl Contour {
|
||||||
|
|
||||||
// TODO(pcwalton): SIMD.
|
// TODO(pcwalton): SIMD.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_point(&mut self, point: Point2DF32, flags: PointFlags, update_bounds: bool) {
|
pub(crate) fn push_point(&mut self,
|
||||||
|
point: Point2DF32,
|
||||||
|
flags: PointFlags,
|
||||||
|
update_bounds: bool) {
|
||||||
debug_assert!(!point.x().is_nan() && !point.y().is_nan());
|
debug_assert!(!point.x().is_nan() && !point.y().is_nan());
|
||||||
|
|
||||||
if update_bounds {
|
if update_bounds {
|
||||||
|
@ -354,6 +369,45 @@ impl Contour {
|
||||||
self.push_point(segment.baseline.to(), PointFlags::empty(), update_bounds);
|
self.push_point(segment.baseline.to(), PointFlags::empty(), update_bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push_arc(&mut self,
|
||||||
|
center: Point2DF32,
|
||||||
|
radius: f32,
|
||||||
|
mut start_angle: f32,
|
||||||
|
mut end_angle: f32) {
|
||||||
|
start_angle %= TWO_PI;
|
||||||
|
end_angle %= TWO_PI;
|
||||||
|
if end_angle <= start_angle {
|
||||||
|
end_angle += TWO_PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
let scale = Transform2DF32::from_scale(Point2DF32::splat(radius));
|
||||||
|
let translation = Transform2DF32::from_translation(center);
|
||||||
|
|
||||||
|
let (mut angle, mut first_segment) = (start_angle, true);
|
||||||
|
while angle < end_angle {
|
||||||
|
let sweep_angle = f32::min(FRAC_PI_2, end_angle - angle);
|
||||||
|
let mut segment = Segment::arc(sweep_angle);
|
||||||
|
let rotation = Transform2DF32::from_rotation(sweep_angle * 0.5 + angle);
|
||||||
|
segment = segment.transform(&scale.post_mul(&rotation).post_mul(&translation));
|
||||||
|
|
||||||
|
debug!("angle={} start_angle={} end_angle={} sweep_angle={} segment={:?}",
|
||||||
|
angle,
|
||||||
|
start_angle,
|
||||||
|
end_angle,
|
||||||
|
sweep_angle,
|
||||||
|
segment);
|
||||||
|
|
||||||
|
if first_segment {
|
||||||
|
self.push_full_segment(&segment, true);
|
||||||
|
first_segment = false;
|
||||||
|
} else {
|
||||||
|
self.push_segment(segment, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
angle += sweep_angle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn segment_after(&self, point_index: u32) -> Segment {
|
pub fn segment_after(&self, point_index: u32) -> Segment {
|
||||||
debug_assert!(self.point_is_endpoint(point_index));
|
debug_assert!(self.point_is_endpoint(point_index));
|
||||||
|
@ -388,8 +442,8 @@ impl Contour {
|
||||||
pub fn hull_segment_after(&self, prev_point_index: u32) -> LineSegmentF32 {
|
pub fn hull_segment_after(&self, prev_point_index: u32) -> LineSegmentF32 {
|
||||||
let next_point_index = self.next_point_index_of(prev_point_index);
|
let next_point_index = self.next_point_index_of(prev_point_index);
|
||||||
LineSegmentF32::new(
|
LineSegmentF32::new(
|
||||||
&self.points[prev_point_index as usize],
|
self.points[prev_point_index as usize],
|
||||||
&self.points[next_point_index as usize],
|
self.points[next_point_index as usize],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,8 +508,12 @@ impl Contour {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transform(&mut self, transform: &Transform2DF32) {
|
pub fn transform(&mut self, transform: &Transform2DF32) {
|
||||||
|
if transform.is_identity() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (point_index, point) in self.points.iter_mut().enumerate() {
|
for (point_index, point) in self.points.iter_mut().enumerate() {
|
||||||
*point = transform.transform_point(point);
|
*point = transform.transform_point(*point);
|
||||||
union_rect(&mut self.bounds, *point, point_index == 0);
|
union_rect(&mut self.bounds, *point, point_index == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -522,8 +580,8 @@ impl Contour {
|
||||||
point_index
|
point_index
|
||||||
};
|
};
|
||||||
let baseline = LineSegmentF32::new(
|
let baseline = LineSegmentF32::new(
|
||||||
&contour.points[last_endpoint_index as usize],
|
contour.points[last_endpoint_index as usize],
|
||||||
&contour.points[position_index as usize],
|
contour.points[position_index as usize],
|
||||||
);
|
);
|
||||||
let point_count = point_index - last_endpoint_index + 1;
|
let point_count = point_index - last_endpoint_index + 1;
|
||||||
if point_count == 3 {
|
if point_count == 3 {
|
||||||
|
@ -531,13 +589,13 @@ impl Contour {
|
||||||
let ctrl_position = &contour.points[ctrl_point_index];
|
let ctrl_position = &contour.points[ctrl_point_index];
|
||||||
handle_cubic(
|
handle_cubic(
|
||||||
self,
|
self,
|
||||||
Segment::quadratic(&baseline, &ctrl_position).to_cubic(),
|
Segment::quadratic(&baseline, *ctrl_position).to_cubic(),
|
||||||
);
|
);
|
||||||
} else if point_count == 4 {
|
} else if point_count == 4 {
|
||||||
let first_ctrl_point_index = last_endpoint_index as usize + 1;
|
let first_ctrl_point_index = last_endpoint_index as usize + 1;
|
||||||
let ctrl_position_0 = &contour.points[first_ctrl_point_index + 0];
|
let ctrl_position_0 = &contour.points[first_ctrl_point_index + 0];
|
||||||
let ctrl_position_1 = &contour.points[first_ctrl_point_index + 1];
|
let ctrl_position_1 = &contour.points[first_ctrl_point_index + 1];
|
||||||
let ctrl = LineSegmentF32::new(&ctrl_position_0, &ctrl_position_1);
|
let ctrl = LineSegmentF32::new(*ctrl_position_0, *ctrl_position_1);
|
||||||
handle_cubic(self, Segment::cubic(&baseline, &ctrl));
|
handle_cubic(self, Segment::cubic(&baseline, &ctrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -723,24 +781,21 @@ impl<'a> Iterator for ContourIter<'a> {
|
||||||
if self.index == contour.len() {
|
if self.index == contour.len() {
|
||||||
let point1 = contour.position_of(0);
|
let point1 = contour.position_of(0);
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
return Some(Segment::line(&LineSegmentF32::new(&point0, &point1)));
|
return Some(Segment::line(&LineSegmentF32::new(point0, point1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let point1_index = self.index;
|
let point1_index = self.index;
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
let point1 = contour.position_of(point1_index);
|
let point1 = contour.position_of(point1_index);
|
||||||
if contour.point_is_endpoint(point1_index) {
|
if contour.point_is_endpoint(point1_index) {
|
||||||
return Some(Segment::line(&LineSegmentF32::new(&point0, &point1)));
|
return Some(Segment::line(&LineSegmentF32::new(point0, point1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let point2_index = self.index;
|
let point2_index = self.index;
|
||||||
let point2 = contour.position_of(point2_index);
|
let point2 = contour.position_of(point2_index);
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
if contour.point_is_endpoint(point2_index) {
|
if contour.point_is_endpoint(point2_index) {
|
||||||
return Some(Segment::quadratic(
|
return Some(Segment::quadratic(&LineSegmentF32::new(point0, point2), point1));
|
||||||
&LineSegmentF32::new(&point0, &point2),
|
|
||||||
&point1,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let point3_index = self.index;
|
let point3_index = self.index;
|
||||||
|
@ -748,8 +803,8 @@ impl<'a> Iterator for ContourIter<'a> {
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
debug_assert!(contour.point_is_endpoint(point3_index));
|
debug_assert!(contour.point_is_endpoint(point3_index));
|
||||||
return Some(Segment::cubic(
|
return Some(Segment::cubic(
|
||||||
&LineSegmentF32::new(&point0, &point3),
|
&LineSegmentF32::new(point0, point3),
|
||||||
&LineSegmentF32::new(&point1, &point2),
|
&LineSegmentF32::new(point1, point2),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,10 @@
|
||||||
|
|
||||||
use crate::basic::line_segment::LineSegmentF32;
|
use crate::basic::line_segment::LineSegmentF32;
|
||||||
use crate::basic::point::Point2DF32;
|
use crate::basic::point::Point2DF32;
|
||||||
|
use crate::basic::transform2d::Transform2DF32;
|
||||||
use crate::util::{self, EPSILON};
|
use crate::util::{self, EPSILON};
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
|
use std::f32::consts::SQRT_2;
|
||||||
|
|
||||||
const MAX_NEWTON_ITERATIONS: u32 = 32;
|
const MAX_NEWTON_ITERATIONS: u32 = 32;
|
||||||
|
|
||||||
|
@ -47,10 +49,10 @@ impl Segment {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn quadratic(baseline: &LineSegmentF32, ctrl: &Point2DF32) -> Segment {
|
pub fn quadratic(baseline: &LineSegmentF32, ctrl: Point2DF32) -> Segment {
|
||||||
Segment {
|
Segment {
|
||||||
baseline: *baseline,
|
baseline: *baseline,
|
||||||
ctrl: LineSegmentF32::new(ctrl, &Point2DF32::default()),
|
ctrl: LineSegmentF32::new(ctrl, Point2DF32::default()),
|
||||||
kind: SegmentKind::Quadratic,
|
kind: SegmentKind::Quadratic,
|
||||||
flags: SegmentFlags::empty(),
|
flags: SegmentFlags::empty(),
|
||||||
}
|
}
|
||||||
|
@ -66,6 +68,24 @@ impl Segment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Approximates an unit-length arc with a cubic Bézier curve.
|
||||||
|
///
|
||||||
|
/// The maximum supported `sweep_angle` is π/2 (i.e. 90°).
|
||||||
|
pub fn arc(sweep_angle: f32) -> Segment {
|
||||||
|
// Aleksas Riškus, "Approximation of a Cubic Bézier Curve by Circular Arcs and Vice Versa"
|
||||||
|
// 2006.
|
||||||
|
//
|
||||||
|
// https://pdfs.semanticscholar.org/1639/0db1a470bd13fe428e0896671a9a5745070a.pdf
|
||||||
|
let phi = 0.5 * sweep_angle;
|
||||||
|
let p0 = Point2DF32::new(f32::cos(phi), f32::sin(phi));
|
||||||
|
let p3 = p0.scale_xy(Point2DF32::new(1.0, -1.0));
|
||||||
|
let p1 = p0 - p3.yx().scale(K);
|
||||||
|
let p2 = p3 + p0.yx().scale(K);
|
||||||
|
return Segment::cubic(&LineSegmentF32::new(p3, p0), &LineSegmentF32::new(p2, p1));
|
||||||
|
|
||||||
|
const K: f32 = 4.0 / 3.0 * (SQRT_2 - 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_line_segment(&self) -> LineSegmentF32 {
|
pub fn as_line_segment(&self) -> LineSegmentF32 {
|
||||||
debug_assert!(self.is_line());
|
debug_assert!(self.is_line());
|
||||||
|
@ -109,7 +129,7 @@ impl Segment {
|
||||||
let mut new_segment = *self;
|
let mut new_segment = *self;
|
||||||
let p1_2 = self.ctrl.from() + self.ctrl.from();
|
let p1_2 = self.ctrl.from() + self.ctrl.from();
|
||||||
new_segment.ctrl =
|
new_segment.ctrl =
|
||||||
LineSegmentF32::new(&(self.baseline.from() + p1_2), &(p1_2 + self.baseline.to()))
|
LineSegmentF32::new(self.baseline.from() + p1_2, p1_2 + self.baseline.to())
|
||||||
.scale(1.0 / 3.0);
|
.scale(1.0 / 3.0);
|
||||||
new_segment.kind = SegmentKind::Cubic;
|
new_segment.kind = SegmentKind::Cubic;
|
||||||
new_segment
|
new_segment
|
||||||
|
@ -176,6 +196,16 @@ impl Segment {
|
||||||
self.to_cubic().as_cubic_segment().sample(t)
|
self.to_cubic().as_cubic_segment().sample(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn transform(self, transform: &Transform2DF32) -> Segment {
|
||||||
|
Segment {
|
||||||
|
baseline: transform.transform_line_segment(&self.baseline),
|
||||||
|
ctrl: transform.transform_line_segment(&self.ctrl),
|
||||||
|
kind: self.kind,
|
||||||
|
flags: self.flags,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
@ -215,16 +245,16 @@ impl<'s> CubicSegment<'s> {
|
||||||
let (baseline0, ctrl0, baseline1, ctrl1);
|
let (baseline0, ctrl0, baseline1, ctrl1);
|
||||||
if t <= 0.0 {
|
if t <= 0.0 {
|
||||||
let from = &self.0.baseline.from();
|
let from = &self.0.baseline.from();
|
||||||
baseline0 = LineSegmentF32::new(from, from);
|
baseline0 = LineSegmentF32::new(*from, *from);
|
||||||
ctrl0 = LineSegmentF32::new(from, from);
|
ctrl0 = LineSegmentF32::new(*from, *from);
|
||||||
baseline1 = self.0.baseline;
|
baseline1 = self.0.baseline;
|
||||||
ctrl1 = self.0.ctrl;
|
ctrl1 = self.0.ctrl;
|
||||||
} else if t >= 1.0 {
|
} else if t >= 1.0 {
|
||||||
let to = &self.0.baseline.to();
|
let to = &self.0.baseline.to();
|
||||||
baseline0 = self.0.baseline;
|
baseline0 = self.0.baseline;
|
||||||
ctrl0 = self.0.ctrl;
|
ctrl0 = self.0.ctrl;
|
||||||
baseline1 = LineSegmentF32::new(to, to);
|
baseline1 = LineSegmentF32::new(*to, *to);
|
||||||
ctrl1 = LineSegmentF32::new(to, to);
|
ctrl1 = LineSegmentF32::new(*to, *to);
|
||||||
} else {
|
} else {
|
||||||
let tttt = F32x4::splat(t);
|
let tttt = F32x4::splat(t);
|
||||||
|
|
||||||
|
|
|
@ -11,96 +11,183 @@
|
||||||
//! Utilities for converting path strokes to fills.
|
//! Utilities for converting path strokes to fills.
|
||||||
|
|
||||||
use crate::basic::line_segment::LineSegmentF32;
|
use crate::basic::line_segment::LineSegmentF32;
|
||||||
|
use crate::basic::point::Point2DF32;
|
||||||
use crate::basic::rect::RectF32;
|
use crate::basic::rect::RectF32;
|
||||||
use crate::outline::{Contour, Outline};
|
use crate::outline::{Contour, Outline};
|
||||||
use crate::segment::Segment;
|
use crate::segment::Segment;
|
||||||
|
use std::f32::consts::FRAC_PI_2;
|
||||||
|
use std::f32;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
const TOLERANCE: f32 = 0.01;
|
const TOLERANCE: f32 = 0.01;
|
||||||
|
|
||||||
pub struct OutlineStrokeToFill {
|
pub struct OutlineStrokeToFill {
|
||||||
pub outline: Outline,
|
pub outline: Outline,
|
||||||
pub stroke_width: f32,
|
pub style: StrokeStyle,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub struct StrokeStyle {
|
||||||
|
pub line_width: f32,
|
||||||
|
pub line_cap: LineCap,
|
||||||
|
pub line_join: LineJoin,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum LineCap {
|
||||||
|
Butt,
|
||||||
|
Square,
|
||||||
|
Round,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum LineJoin {
|
||||||
|
Miter,
|
||||||
|
Bevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutlineStrokeToFill {
|
impl OutlineStrokeToFill {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(outline: Outline, stroke_width: f32) -> OutlineStrokeToFill {
|
pub fn new(outline: Outline, style: StrokeStyle) -> OutlineStrokeToFill {
|
||||||
OutlineStrokeToFill {
|
OutlineStrokeToFill { outline, style }
|
||||||
outline,
|
|
||||||
stroke_width,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn offset(&mut self) {
|
pub fn offset(&mut self) {
|
||||||
let mut new_bounds = None;
|
let mut new_contours = vec![];
|
||||||
for contour in &mut self.outline.contours {
|
for input in mem::replace(&mut self.outline.contours, vec![]) {
|
||||||
let input = mem::replace(contour, Contour::new());
|
let closed = input.closed;
|
||||||
let mut contour_stroke_to_fill =
|
let mut stroker = ContourStrokeToFill::new(input,
|
||||||
ContourStrokeToFill::new(input, Contour::new(), self.stroke_width * 0.5);
|
Contour::new(),
|
||||||
contour_stroke_to_fill.offset_forward();
|
self.style.line_width * 0.5,
|
||||||
contour_stroke_to_fill.offset_backward();
|
self.style.line_join);
|
||||||
*contour = contour_stroke_to_fill.output;
|
|
||||||
contour.update_bounds(&mut new_bounds);
|
stroker.offset_forward();
|
||||||
|
if closed {
|
||||||
|
self.push_stroked_contour(&mut new_contours, stroker.output, true);
|
||||||
|
stroker = ContourStrokeToFill::new(stroker.input,
|
||||||
|
Contour::new(),
|
||||||
|
self.style.line_width * 0.5,
|
||||||
|
self.style.line_join);
|
||||||
|
} else {
|
||||||
|
self.add_cap(&mut stroker.output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stroker.offset_backward();
|
||||||
|
if !closed {
|
||||||
|
self.add_cap(&mut stroker.output);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.push_stroked_contour(&mut new_contours, stroker.output, closed);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_bounds = None;
|
||||||
|
new_contours.iter().for_each(|contour| contour.update_bounds(&mut new_bounds));
|
||||||
|
|
||||||
|
self.outline.contours = new_contours;
|
||||||
self.outline.bounds = new_bounds.unwrap_or_else(|| RectF32::default());
|
self.outline.bounds = new_bounds.unwrap_or_else(|| RectF32::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn push_stroked_contour(&mut self,
|
||||||
|
new_contours: &mut Vec<Contour>,
|
||||||
|
mut contour: Contour,
|
||||||
|
closed: bool) {
|
||||||
|
// Add join if necessary.
|
||||||
|
if closed && contour.needs_join(self.style.line_join) {
|
||||||
|
let (p1, p0) = (contour.position_of(1), contour.position_of(0));
|
||||||
|
contour.add_join(self.style.line_join, &LineSegmentF32::new(p0, p1));
|
||||||
|
}
|
||||||
|
|
||||||
|
contour.closed = true;
|
||||||
|
new_contours.push(contour);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_cap(&mut self, contour: &mut Contour) {
|
||||||
|
if self.style.line_cap == LineCap::Butt || contour.len() < 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let width = self.style.line_width;
|
||||||
|
let (p0, p1) = (contour.position_of_last(2), contour.position_of_last(1));
|
||||||
|
let gradient = (p1 - p0).normalize();
|
||||||
|
|
||||||
|
match self.style.line_cap {
|
||||||
|
LineCap::Butt => unreachable!(),
|
||||||
|
LineCap::Square => {
|
||||||
|
let offset = gradient.scale(width * 0.5);
|
||||||
|
|
||||||
|
let p2 = p1 + offset;
|
||||||
|
let p3 = p2 + gradient.yx().scale_xy(Point2DF32::new(-width, width));
|
||||||
|
let p4 = p3 - offset;
|
||||||
|
|
||||||
|
contour.push_endpoint(p2);
|
||||||
|
contour.push_endpoint(p3);
|
||||||
|
contour.push_endpoint(p4);
|
||||||
|
}
|
||||||
|
LineCap::Round => {
|
||||||
|
// FIXME(pcwalton): Should we really be using angles here at all?
|
||||||
|
let offset = gradient.yx().scale_xy(Point2DF32::new(-width * 0.5, width * 0.5));
|
||||||
|
let angle = f32::atan2(gradient.y(), gradient.x());
|
||||||
|
contour.push_arc(p1 + offset, width * 0.5, angle - FRAC_PI_2, angle + FRAC_PI_2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ContourStrokeToFill {
|
struct ContourStrokeToFill {
|
||||||
input: Contour,
|
input: Contour,
|
||||||
output: Contour,
|
output: Contour,
|
||||||
radius: f32,
|
radius: f32,
|
||||||
|
join: LineJoin,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContourStrokeToFill {
|
impl ContourStrokeToFill {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn new(input: Contour, output: Contour, radius: f32) -> ContourStrokeToFill {
|
fn new(input: Contour, output: Contour, radius: f32, join: LineJoin) -> ContourStrokeToFill {
|
||||||
ContourStrokeToFill {
|
ContourStrokeToFill { input, output, radius, join }
|
||||||
input,
|
|
||||||
output,
|
|
||||||
radius,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn offset_forward(&mut self) {
|
fn offset_forward(&mut self) {
|
||||||
for segment in self.input.iter() {
|
for (segment_index, segment) in self.input.iter().enumerate() {
|
||||||
segment.offset(self.radius, &mut self.output);
|
// FIXME(pcwalton): We negate the radius here so that round end caps can be drawn
|
||||||
|
// clockwise. Of course, we should just implement anticlockwise arcs to begin with...
|
||||||
|
let join = if segment_index == 0 { LineJoin::Bevel } else { self.join };
|
||||||
|
segment.offset(-self.radius, join, &mut self.output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn offset_backward(&mut self) {
|
fn offset_backward(&mut self) {
|
||||||
// FIXME(pcwalton)
|
|
||||||
let mut segments: Vec<_> = self
|
let mut segments: Vec<_> = self
|
||||||
.input
|
.input
|
||||||
.iter()
|
.iter()
|
||||||
.map(|segment| segment.reversed())
|
.map(|segment| segment.reversed())
|
||||||
.collect();
|
.collect();
|
||||||
segments.reverse();
|
segments.reverse();
|
||||||
for segment in &segments {
|
for (segment_index, segment) in segments.iter().enumerate() {
|
||||||
segment.offset(self.radius, &mut self.output);
|
// FIXME(pcwalton): We negate the radius here so that round end caps can be drawn
|
||||||
|
// clockwise. Of course, we should just implement anticlockwise arcs to begin with...
|
||||||
|
let join = if segment_index == 0 { LineJoin::Bevel } else { self.join };
|
||||||
|
segment.offset(-self.radius, join, &mut self.output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Offset {
|
trait Offset {
|
||||||
fn offset(&self, distance: f32, contour: &mut Contour);
|
fn offset(&self, distance: f32, join: LineJoin, contour: &mut Contour);
|
||||||
|
fn add_to_contour(&self, join: LineJoin, contour: &mut Contour);
|
||||||
fn offset_once(&self, distance: f32) -> Self;
|
fn offset_once(&self, distance: f32) -> Self;
|
||||||
fn error_is_within_tolerance(&self, other: &Segment, distance: f32) -> bool;
|
fn error_is_within_tolerance(&self, other: &Segment, distance: f32) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Offset for Segment {
|
impl Offset for Segment {
|
||||||
fn offset(&self, distance: f32, contour: &mut Contour) {
|
fn offset(&self, distance: f32, join: LineJoin, contour: &mut Contour) {
|
||||||
if self.baseline.square_length() < TOLERANCE * TOLERANCE {
|
if self.baseline.square_length() < TOLERANCE * TOLERANCE {
|
||||||
contour.push_full_segment(self, true);
|
self.add_to_contour(join, contour);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let candidate = self.offset_once(distance);
|
let candidate = self.offset_once(distance);
|
||||||
if self.error_is_within_tolerance(&candidate, distance) {
|
if self.error_is_within_tolerance(&candidate, distance) {
|
||||||
contour.push_full_segment(&candidate, true);
|
candidate.add_to_contour(join, contour);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,8 +195,27 @@ impl Offset for Segment {
|
||||||
debug!("... PRE-SPLIT: {:?}", self);
|
debug!("... PRE-SPLIT: {:?}", self);
|
||||||
let (before, after) = self.split(0.5);
|
let (before, after) = self.split(0.5);
|
||||||
debug!("... AFTER-SPLIT: {:?} {:?}", before, after);
|
debug!("... AFTER-SPLIT: {:?} {:?}", before, after);
|
||||||
before.offset(distance, contour);
|
before.offset(distance, join, contour);
|
||||||
after.offset(distance, contour);
|
after.offset(distance, join, contour);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_to_contour(&self, join: LineJoin, contour: &mut Contour) {
|
||||||
|
// Add join if necessary.
|
||||||
|
if contour.needs_join(join) {
|
||||||
|
let p3 = self.baseline.from();
|
||||||
|
let p4 = if self.is_line() {
|
||||||
|
self.baseline.to()
|
||||||
|
} else {
|
||||||
|
// NB: If you change the representation of quadratic curves, you will need to
|
||||||
|
// change this.
|
||||||
|
self.ctrl.from()
|
||||||
|
};
|
||||||
|
|
||||||
|
contour.add_join(join, &LineSegmentF32::new(p4, p3));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push segment.
|
||||||
|
contour.push_full_segment(self, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn offset_once(&self, distance: f32) -> Segment {
|
fn offset_once(&self, distance: f32) -> Segment {
|
||||||
|
@ -118,51 +224,51 @@ impl Offset for Segment {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.is_quadratic() {
|
if self.is_quadratic() {
|
||||||
let mut segment_0 = LineSegmentF32::new(&self.baseline.from(), &self.ctrl.from());
|
let mut segment_0 = LineSegmentF32::new(self.baseline.from(), self.ctrl.from());
|
||||||
let mut segment_1 = LineSegmentF32::new(&self.ctrl.from(), &self.baseline.to());
|
let mut segment_1 = LineSegmentF32::new(self.ctrl.from(), self.baseline.to());
|
||||||
segment_0 = segment_0.offset(distance);
|
segment_0 = segment_0.offset(distance);
|
||||||
segment_1 = segment_1.offset(distance);
|
segment_1 = segment_1.offset(distance);
|
||||||
let ctrl = match segment_0.intersection_t(&segment_1) {
|
let ctrl = match segment_0.intersection_t(&segment_1) {
|
||||||
Some(t) => segment_0.sample(t),
|
Some(t) => segment_0.sample(t),
|
||||||
None => segment_0.to().lerp(segment_1.from(), 0.5),
|
None => segment_0.to().lerp(segment_1.from(), 0.5),
|
||||||
};
|
};
|
||||||
let baseline = LineSegmentF32::new(&segment_0.from(), &segment_1.to());
|
let baseline = LineSegmentF32::new(segment_0.from(), segment_1.to());
|
||||||
return Segment::quadratic(&baseline, &ctrl);
|
return Segment::quadratic(&baseline, ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert!(self.is_cubic());
|
debug_assert!(self.is_cubic());
|
||||||
|
|
||||||
if self.baseline.from() == self.ctrl.from() {
|
if self.baseline.from() == self.ctrl.from() {
|
||||||
let mut segment_0 = LineSegmentF32::new(&self.baseline.from(), &self.ctrl.to());
|
let mut segment_0 = LineSegmentF32::new(self.baseline.from(), self.ctrl.to());
|
||||||
let mut segment_1 = LineSegmentF32::new(&self.ctrl.to(), &self.baseline.to());
|
let mut segment_1 = LineSegmentF32::new(self.ctrl.to(), self.baseline.to());
|
||||||
segment_0 = segment_0.offset(distance);
|
segment_0 = segment_0.offset(distance);
|
||||||
segment_1 = segment_1.offset(distance);
|
segment_1 = segment_1.offset(distance);
|
||||||
let ctrl = match segment_0.intersection_t(&segment_1) {
|
let ctrl = match segment_0.intersection_t(&segment_1) {
|
||||||
Some(t) => segment_0.sample(t),
|
Some(t) => segment_0.sample(t),
|
||||||
None => segment_0.to().lerp(segment_1.from(), 0.5),
|
None => segment_0.to().lerp(segment_1.from(), 0.5),
|
||||||
};
|
};
|
||||||
let baseline = LineSegmentF32::new(&segment_0.from(), &segment_1.to());
|
let baseline = LineSegmentF32::new(segment_0.from(), segment_1.to());
|
||||||
let ctrl = LineSegmentF32::new(&segment_0.from(), &ctrl);
|
let ctrl = LineSegmentF32::new(segment_0.from(), ctrl);
|
||||||
return Segment::cubic(&baseline, &ctrl);
|
return Segment::cubic(&baseline, &ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.ctrl.to() == self.baseline.to() {
|
if self.ctrl.to() == self.baseline.to() {
|
||||||
let mut segment_0 = LineSegmentF32::new(&self.baseline.from(), &self.ctrl.from());
|
let mut segment_0 = LineSegmentF32::new(self.baseline.from(), self.ctrl.from());
|
||||||
let mut segment_1 = LineSegmentF32::new(&self.ctrl.from(), &self.baseline.to());
|
let mut segment_1 = LineSegmentF32::new(self.ctrl.from(), self.baseline.to());
|
||||||
segment_0 = segment_0.offset(distance);
|
segment_0 = segment_0.offset(distance);
|
||||||
segment_1 = segment_1.offset(distance);
|
segment_1 = segment_1.offset(distance);
|
||||||
let ctrl = match segment_0.intersection_t(&segment_1) {
|
let ctrl = match segment_0.intersection_t(&segment_1) {
|
||||||
Some(t) => segment_0.sample(t),
|
Some(t) => segment_0.sample(t),
|
||||||
None => segment_0.to().lerp(segment_1.from(), 0.5),
|
None => segment_0.to().lerp(segment_1.from(), 0.5),
|
||||||
};
|
};
|
||||||
let baseline = LineSegmentF32::new(&segment_0.from(), &segment_1.to());
|
let baseline = LineSegmentF32::new(segment_0.from(), segment_1.to());
|
||||||
let ctrl = LineSegmentF32::new(&ctrl, &segment_1.to());
|
let ctrl = LineSegmentF32::new(ctrl, segment_1.to());
|
||||||
return Segment::cubic(&baseline, &ctrl);
|
return Segment::cubic(&baseline, &ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut segment_0 = LineSegmentF32::new(&self.baseline.from(), &self.ctrl.from());
|
let mut segment_0 = LineSegmentF32::new(self.baseline.from(), self.ctrl.from());
|
||||||
let mut segment_1 = LineSegmentF32::new(&self.ctrl.from(), &self.ctrl.to());
|
let mut segment_1 = LineSegmentF32::new(self.ctrl.from(), self.ctrl.to());
|
||||||
let mut segment_2 = LineSegmentF32::new(&self.ctrl.to(), &self.baseline.to());
|
let mut segment_2 = LineSegmentF32::new(self.ctrl.to(), self.baseline.to());
|
||||||
segment_0 = segment_0.offset(distance);
|
segment_0 = segment_0.offset(distance);
|
||||||
segment_1 = segment_1.offset(distance);
|
segment_1 = segment_1.offset(distance);
|
||||||
segment_2 = segment_2.offset(distance);
|
segment_2 = segment_2.offset(distance);
|
||||||
|
@ -176,8 +282,8 @@ impl Offset for Segment {
|
||||||
segment_1.to().lerp(segment_2.from(), 0.5),
|
segment_1.to().lerp(segment_2.from(), 0.5),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
let baseline = LineSegmentF32::new(&segment_0.from(), &segment_2.to());
|
let baseline = LineSegmentF32::new(segment_0.from(), segment_2.to());
|
||||||
let ctrl = LineSegmentF32::new(&ctrl_0, &ctrl_1);
|
let ctrl = LineSegmentF32::new(ctrl_0, ctrl_1);
|
||||||
Segment::cubic(&baseline, &ctrl)
|
Segment::cubic(&baseline, &ctrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,3 +315,40 @@ impl Offset for Segment {
|
||||||
const SAMPLE_COUNT: u32 = 16;
|
const SAMPLE_COUNT: u32 = 16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Contour {
|
||||||
|
fn needs_join(&self, join: LineJoin) -> bool {
|
||||||
|
// TODO(pcwalton): Miter limit.
|
||||||
|
join == LineJoin::Miter && self.len() >= 2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_join(&mut self, _: LineJoin, next_tangent: &LineSegmentF32) {
|
||||||
|
// TODO(pcwalton): Round joins.
|
||||||
|
let (p0, p1) = (self.position_of_last(2), self.position_of_last(1));
|
||||||
|
let prev_tangent = LineSegmentF32::new(p0, p1);
|
||||||
|
if let Some(prev_tangent_t) = prev_tangent.intersection_t(&next_tangent) {
|
||||||
|
self.push_endpoint(prev_tangent.sample(prev_tangent_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for StrokeStyle {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> StrokeStyle {
|
||||||
|
StrokeStyle {
|
||||||
|
line_width: 1.0,
|
||||||
|
line_cap: LineCap::default(),
|
||||||
|
line_join: LineJoin::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for LineCap {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> LineCap { LineCap::Butt }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for LineJoin {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> LineJoin { LineJoin::Miter }
|
||||||
|
}
|
||||||
|
|
|
@ -4,9 +4,11 @@ version = "0.1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["rlib", "staticlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gl = "0.6"
|
gl = "0.6"
|
||||||
rustache = "0.1"
|
|
||||||
|
|
||||||
[dependencies.image]
|
[dependencies.image]
|
||||||
version = "0.21"
|
version = "0.21"
|
||||||
|
|
111
gl/src/lib.rs
111
gl/src/lib.rs
|
@ -16,13 +16,13 @@ extern crate log;
|
||||||
use gl::types::{GLboolean, GLchar, GLenum, GLfloat, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
use gl::types::{GLboolean, GLchar, GLenum, GLfloat, GLint, GLsizei, GLsizeiptr, GLuint, GLvoid};
|
||||||
use pathfinder_geometry::basic::point::Point2DI32;
|
use pathfinder_geometry::basic::point::Point2DI32;
|
||||||
use pathfinder_geometry::basic::rect::RectI32;
|
use pathfinder_geometry::basic::rect::RectI32;
|
||||||
|
use pathfinder_gpu::resources::ResourceLoader;
|
||||||
use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, ClearParams};
|
use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, ClearParams};
|
||||||
use pathfinder_gpu::{DepthFunc, Device, Primitive, RenderState, ShaderKind, StencilFunc};
|
use pathfinder_gpu::{DepthFunc, Device, Primitive, RenderState, ShaderKind, StencilFunc};
|
||||||
use pathfinder_gpu::{TextureFormat, UniformData, VertexAttrType};
|
use pathfinder_gpu::{TextureFormat, UniformData, VertexAttrClass};
|
||||||
|
use pathfinder_gpu::{VertexAttrDescriptor, VertexAttrType};
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
use rustache::{HashBuilder, Render};
|
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::io::Cursor;
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
@ -226,18 +226,18 @@ impl Device for GLDevice {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_shader_from_source(&self,
|
fn create_shader_from_source(&self,
|
||||||
|
resources: &dyn ResourceLoader,
|
||||||
name: &str,
|
name: &str,
|
||||||
source: &[u8],
|
source: &[u8],
|
||||||
kind: ShaderKind,
|
kind: ShaderKind,
|
||||||
mut template_input: HashBuilder)
|
includes: &[&str])
|
||||||
-> GLShader {
|
-> GLShader {
|
||||||
// FIXME(pcwalton): Do this once and cache it.
|
// FIXME(pcwalton): Do this once and cache it.
|
||||||
let glsl_version_spec = self.version.to_glsl_version_spec();
|
let glsl_version_spec = self.version.to_glsl_version_spec();
|
||||||
template_input = template_input.insert("version", glsl_version_spec);
|
|
||||||
|
|
||||||
let mut output = Cursor::new(vec![]);
|
let mut output = vec![];
|
||||||
template_input.render(str::from_utf8(source).unwrap(), &mut output).unwrap();
|
self.preprocess(&mut output, resources, source, includes, glsl_version_spec);
|
||||||
let source = output.into_inner();
|
let source = output;
|
||||||
|
|
||||||
let gl_shader_kind = match kind {
|
let gl_shader_kind = match kind {
|
||||||
ShaderKind::Vertex => gl::VERTEX_SHADER,
|
ShaderKind::Vertex => gl::VERTEX_SHADER,
|
||||||
|
@ -271,6 +271,7 @@ impl Device for GLDevice {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_program_from_shaders(&self,
|
fn create_program_from_shaders(&self,
|
||||||
|
resources: &dyn ResourceLoader,
|
||||||
name: &str,
|
name: &str,
|
||||||
vertex_shader: GLShader,
|
vertex_shader: GLShader,
|
||||||
fragment_shader: GLShader)
|
fragment_shader: GLShader)
|
||||||
|
@ -331,40 +332,33 @@ impl Device for GLDevice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configure_float_vertex_attr(&self,
|
fn configure_vertex_attr(&self, attr: &GLVertexAttr, descriptor: &VertexAttrDescriptor) {
|
||||||
attr: &GLVertexAttr,
|
|
||||||
size: usize,
|
|
||||||
attr_type: VertexAttrType,
|
|
||||||
normalized: bool,
|
|
||||||
stride: usize,
|
|
||||||
offset: usize,
|
|
||||||
divisor: u32) {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
let attr_type = descriptor.attr_type.to_gl_type();
|
||||||
|
match descriptor.class {
|
||||||
|
VertexAttrClass::Float | VertexAttrClass::FloatNorm => {
|
||||||
|
let normalized = if descriptor.class == VertexAttrClass::FloatNorm {
|
||||||
|
gl::TRUE
|
||||||
|
} else {
|
||||||
|
gl::FALSE
|
||||||
|
};
|
||||||
gl::VertexAttribPointer(attr.attr,
|
gl::VertexAttribPointer(attr.attr,
|
||||||
size as GLint,
|
descriptor.size as GLint,
|
||||||
attr_type.to_gl_type(),
|
attr_type,
|
||||||
if normalized { gl::TRUE } else { gl::FALSE },
|
normalized,
|
||||||
stride as GLint,
|
descriptor.stride as GLint,
|
||||||
offset as *const GLvoid); ck();
|
descriptor.offset as *const GLvoid); ck();
|
||||||
gl::VertexAttribDivisor(attr.attr, divisor); ck();
|
}
|
||||||
gl::EnableVertexAttribArray(attr.attr); ck();
|
VertexAttrClass::Int => {
|
||||||
|
gl::VertexAttribIPointer(attr.attr,
|
||||||
|
descriptor.size as GLint,
|
||||||
|
attr_type,
|
||||||
|
descriptor.stride as GLint,
|
||||||
|
descriptor.offset as *const GLvoid); ck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configure_int_vertex_attr(&self,
|
gl::VertexAttribDivisor(attr.attr, descriptor.divisor); ck();
|
||||||
attr: &GLVertexAttr,
|
|
||||||
size: usize,
|
|
||||||
attr_type: VertexAttrType,
|
|
||||||
stride: usize,
|
|
||||||
offset: usize,
|
|
||||||
divisor: u32) {
|
|
||||||
unsafe {
|
|
||||||
gl::VertexAttribIPointer(attr.attr,
|
|
||||||
size as GLint,
|
|
||||||
attr_type.to_gl_type(),
|
|
||||||
stride as GLint,
|
|
||||||
offset as *const GLvoid); ck();
|
|
||||||
gl::VertexAttribDivisor(attr.attr, divisor); ck();
|
|
||||||
gl::EnableVertexAttribArray(attr.attr); ck();
|
gl::EnableVertexAttribArray(attr.attr); ck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -654,6 +648,41 @@ impl Device for GLDevice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GLDevice {
|
||||||
|
fn preprocess(&self,
|
||||||
|
output: &mut Vec<u8>,
|
||||||
|
resources: &dyn ResourceLoader,
|
||||||
|
source: &[u8],
|
||||||
|
includes: &[&str],
|
||||||
|
version: &str) {
|
||||||
|
let mut index = 0;
|
||||||
|
while index < source.len() {
|
||||||
|
if source[index..].starts_with(b"{{") {
|
||||||
|
let end_index = source[index..].iter()
|
||||||
|
.position(|character| *character == b'}')
|
||||||
|
.expect("Expected `}`!") + index;
|
||||||
|
assert_eq!(source[end_index + 1], b'}');
|
||||||
|
let ident = String::from_utf8_lossy(&source[(index + 2)..end_index]);
|
||||||
|
if ident == "version" {
|
||||||
|
output.extend_from_slice(version.as_bytes());
|
||||||
|
} else if ident.starts_with("include_") {
|
||||||
|
let include_name = &ident["include_".len()..];
|
||||||
|
if includes.iter().any(|include| *include == include_name) {
|
||||||
|
let include_source = self.load_shader_include(resources, include_name);
|
||||||
|
self.preprocess(output, resources, &include_source, includes, version);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("unknown template variable: `{}`", ident);
|
||||||
|
}
|
||||||
|
index = end_index + 2;
|
||||||
|
} else {
|
||||||
|
output.push(source[index]);
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct GLVertexArray {
|
pub struct GLVertexArray {
|
||||||
pub gl_vertex_array: GLuint,
|
pub gl_vertex_array: GLuint,
|
||||||
}
|
}
|
||||||
|
@ -869,11 +898,13 @@ impl VertexAttrTypeExt for VertexAttrType {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The version/dialect of OpenGL we should render with.
|
/// The version/dialect of OpenGL we should render with.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(u32)]
|
||||||
pub enum GLVersion {
|
pub enum GLVersion {
|
||||||
/// OpenGL 3.0+, core profile.
|
/// OpenGL 3.0+, core profile.
|
||||||
GL3,
|
GL3 = 0,
|
||||||
/// OpenGL ES 3.0+.
|
/// OpenGL ES 3.0+.
|
||||||
GLES3,
|
GLES3 = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GLVersion {
|
impl GLVersion {
|
||||||
|
@ -899,3 +930,5 @@ fn ck() {
|
||||||
|
|
||||||
#[cfg(not(debug))]
|
#[cfg(not(debug))]
|
||||||
fn ck() {}
|
fn ck() {}
|
||||||
|
|
||||||
|
// Shader preprocessing
|
||||||
|
|
|
@ -5,7 +5,6 @@ authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rustache = "0.1"
|
|
||||||
|
|
||||||
[dependencies.image]
|
[dependencies.image]
|
||||||
version = "0.21"
|
version = "0.21"
|
||||||
|
|
|
@ -17,11 +17,19 @@ use pathfinder_geometry::basic::rect::RectI32;
|
||||||
use pathfinder_geometry::basic::transform3d::Transform3DF32;
|
use pathfinder_geometry::basic::transform3d::Transform3DF32;
|
||||||
use pathfinder_geometry::color::ColorF;
|
use pathfinder_geometry::color::ColorF;
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
use rustache::HashBuilder;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub mod resources;
|
pub mod resources;
|
||||||
|
|
||||||
|
static INCLUDES: [&str; 6] = [
|
||||||
|
"tile_alpha_vertex",
|
||||||
|
"tile_monochrome",
|
||||||
|
"tile_multicolor",
|
||||||
|
"tile_solid_vertex",
|
||||||
|
"post_convolve",
|
||||||
|
"post_gamma_correct",
|
||||||
|
];
|
||||||
|
|
||||||
pub trait Device {
|
pub trait Device {
|
||||||
type Buffer;
|
type Buffer;
|
||||||
type Framebuffer;
|
type Framebuffer;
|
||||||
|
@ -37,14 +45,16 @@ pub trait Device {
|
||||||
fn create_texture_from_data(&self, size: Point2DI32, data: &[u8]) -> Self::Texture;
|
fn create_texture_from_data(&self, size: Point2DI32, data: &[u8]) -> Self::Texture;
|
||||||
fn create_shader_from_source(
|
fn create_shader_from_source(
|
||||||
&self,
|
&self,
|
||||||
|
resources: &dyn ResourceLoader,
|
||||||
name: &str,
|
name: &str,
|
||||||
source: &[u8],
|
source: &[u8],
|
||||||
kind: ShaderKind,
|
kind: ShaderKind,
|
||||||
template_input: HashBuilder,
|
includes: &[&str],
|
||||||
) -> Self::Shader;
|
) -> Self::Shader;
|
||||||
fn create_vertex_array(&self) -> Self::VertexArray;
|
fn create_vertex_array(&self) -> Self::VertexArray;
|
||||||
fn create_program_from_shaders(
|
fn create_program_from_shaders(
|
||||||
&self,
|
&self,
|
||||||
|
resources: &dyn ResourceLoader,
|
||||||
name: &str,
|
name: &str,
|
||||||
vertex_shader: Self::Shader,
|
vertex_shader: Self::Shader,
|
||||||
fragment_shader: Self::Shader,
|
fragment_shader: Self::Shader,
|
||||||
|
@ -52,25 +62,7 @@ pub trait Device {
|
||||||
fn get_vertex_attr(&self, program: &Self::Program, name: &str) -> Self::VertexAttr;
|
fn get_vertex_attr(&self, program: &Self::Program, name: &str) -> Self::VertexAttr;
|
||||||
fn get_uniform(&self, program: &Self::Program, name: &str) -> Self::Uniform;
|
fn get_uniform(&self, program: &Self::Program, name: &str) -> Self::Uniform;
|
||||||
fn use_program(&self, program: &Self::Program);
|
fn use_program(&self, program: &Self::Program);
|
||||||
fn configure_float_vertex_attr(
|
fn configure_vertex_attr(&self, attr: &Self::VertexAttr, descriptor: &VertexAttrDescriptor);
|
||||||
&self,
|
|
||||||
attr: &Self::VertexAttr,
|
|
||||||
size: usize,
|
|
||||||
attr_type: VertexAttrType,
|
|
||||||
normalized: bool,
|
|
||||||
stride: usize,
|
|
||||||
offset: usize,
|
|
||||||
divisor: u32,
|
|
||||||
);
|
|
||||||
fn configure_int_vertex_attr(
|
|
||||||
&self,
|
|
||||||
attr: &Self::VertexAttr,
|
|
||||||
size: usize,
|
|
||||||
attr_type: VertexAttrType,
|
|
||||||
stride: usize,
|
|
||||||
offset: usize,
|
|
||||||
divisor: u32,
|
|
||||||
);
|
|
||||||
fn set_uniform(&self, uniform: &Self::Uniform, data: UniformData);
|
fn set_uniform(&self, uniform: &Self::Uniform, data: UniformData);
|
||||||
fn create_framebuffer(&self, texture: Self::Texture) -> Self::Framebuffer;
|
fn create_framebuffer(&self, texture: Self::Texture) -> Self::Framebuffer;
|
||||||
fn create_buffer(&self) -> Self::Buffer;
|
fn create_buffer(&self) -> Self::Buffer;
|
||||||
|
@ -127,39 +119,8 @@ pub trait Device {
|
||||||
ShaderKind::Vertex => 'v',
|
ShaderKind::Vertex => 'v',
|
||||||
ShaderKind::Fragment => 'f',
|
ShaderKind::Fragment => 'f',
|
||||||
};
|
};
|
||||||
let source = resources
|
let source = resources.slurp(&format!("shaders/{}.{}s.glsl", name, suffix)).unwrap();
|
||||||
.slurp(&format!("shaders/{}.{}s.glsl", name, suffix))
|
self.create_shader_from_source(resources, name, &source, kind, &INCLUDES)
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut load_include_tile_alpha_vertex =
|
|
||||||
|_| load_shader_include(resources, "tile_alpha_vertex");
|
|
||||||
let mut load_include_tile_monochrome =
|
|
||||||
|_| load_shader_include(resources, "tile_monochrome");
|
|
||||||
let mut load_include_tile_multicolor =
|
|
||||||
|_| load_shader_include(resources, "tile_multicolor");
|
|
||||||
let mut load_include_tile_solid_vertex =
|
|
||||||
|_| load_shader_include(resources, "tile_solid_vertex");
|
|
||||||
let mut load_include_post_convolve = |_| load_shader_include(resources, "post_convolve");
|
|
||||||
let mut load_include_post_gamma_correct =
|
|
||||||
|_| load_shader_include(resources, "post_gamma_correct");
|
|
||||||
let template_input = HashBuilder::new()
|
|
||||||
.insert_lambda(
|
|
||||||
"include_tile_alpha_vertex",
|
|
||||||
&mut load_include_tile_alpha_vertex,
|
|
||||||
)
|
|
||||||
.insert_lambda("include_tile_monochrome", &mut load_include_tile_monochrome)
|
|
||||||
.insert_lambda("include_tile_multicolor", &mut load_include_tile_multicolor)
|
|
||||||
.insert_lambda(
|
|
||||||
"include_tile_solid_vertex",
|
|
||||||
&mut load_include_tile_solid_vertex,
|
|
||||||
)
|
|
||||||
.insert_lambda("include_post_convolve", &mut load_include_post_convolve)
|
|
||||||
.insert_lambda(
|
|
||||||
"include_post_gamma_correct",
|
|
||||||
&mut load_include_post_gamma_correct,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.create_shader_from_source(name, &source, kind, template_input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_program_from_shader_names(
|
fn create_program_from_shader_names(
|
||||||
|
@ -172,12 +133,16 @@ pub trait Device {
|
||||||
let vertex_shader = self.create_shader(resources, vertex_shader_name, ShaderKind::Vertex);
|
let vertex_shader = self.create_shader(resources, vertex_shader_name, ShaderKind::Vertex);
|
||||||
let fragment_shader =
|
let fragment_shader =
|
||||||
self.create_shader(resources, fragment_shader_name, ShaderKind::Fragment);
|
self.create_shader(resources, fragment_shader_name, ShaderKind::Fragment);
|
||||||
self.create_program_from_shaders(program_name, vertex_shader, fragment_shader)
|
self.create_program_from_shaders(resources, program_name, vertex_shader, fragment_shader)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_program(&self, resources: &dyn ResourceLoader, name: &str) -> Self::Program {
|
fn create_program(&self, resources: &dyn ResourceLoader, name: &str) -> Self::Program {
|
||||||
self.create_program_from_shader_names(resources, name, name, name)
|
self.create_program_from_shader_names(resources, name, name, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_shader_include(&self, resources: &dyn ResourceLoader, include_name: &str) -> Vec<u8> {
|
||||||
|
resources.slurp(&format!("shaders/{}.inc.glsl", include_name)).unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
@ -340,9 +305,19 @@ impl UniformData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_shader_include(resources: &dyn ResourceLoader, include_name: &str) -> String {
|
#[derive(Clone, Copy, Debug)]
|
||||||
let resource = resources
|
pub struct VertexAttrDescriptor {
|
||||||
.slurp(&format!("shaders/{}.inc.glsl", include_name))
|
pub size: usize,
|
||||||
.unwrap();
|
pub class: VertexAttrClass,
|
||||||
String::from_utf8_lossy(&resource).to_string()
|
pub attr_type: VertexAttrType,
|
||||||
|
pub stride: usize,
|
||||||
|
pub offset: usize,
|
||||||
|
pub divisor: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum VertexAttrClass {
|
||||||
|
Float,
|
||||||
|
FloatNorm,
|
||||||
|
Int,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "pathfinder_lottie"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde_json = "1.0"
|
||||||
|
|
||||||
|
[dependencies.serde]
|
||||||
|
version = "1.0"
|
||||||
|
features = ["derive"]
|
|
@ -0,0 +1,313 @@
|
||||||
|
// pathfinder/lottie/src/lib.rs
|
||||||
|
//
|
||||||
|
// Copyright © 2019 The Pathfinder Project Developers.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
//! Experimental support for Lottie. This is very incomplete.
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Error as JSONError;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Lottie {
|
||||||
|
#[serde(rename = "v")]
|
||||||
|
pub version: String,
|
||||||
|
#[serde(rename = "fr")]
|
||||||
|
pub frame_rate: i64,
|
||||||
|
#[serde(rename = "ip")]
|
||||||
|
pub in_point: i64,
|
||||||
|
#[serde(rename = "op")]
|
||||||
|
pub out_point: i64,
|
||||||
|
#[serde(rename = "w")]
|
||||||
|
pub width: f64,
|
||||||
|
#[serde(rename = "h")]
|
||||||
|
pub height: f64,
|
||||||
|
#[serde(rename = "ddd")]
|
||||||
|
pub three_d: i64,
|
||||||
|
pub assets: Vec<Asset>,
|
||||||
|
pub layers: Vec<Layer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Asset {}
|
||||||
|
|
||||||
|
// FIXME(pcwalton): Using an untagged enum is a botch here. There actually is a tag: it's just an
|
||||||
|
// integer, which `serde_json` doesn't support natively.
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum Layer {
|
||||||
|
Shape {
|
||||||
|
#[serde(rename = "ddd")]
|
||||||
|
three_d: i64,
|
||||||
|
#[serde(rename = "ind")]
|
||||||
|
index: i64,
|
||||||
|
#[serde(rename = "nm")]
|
||||||
|
name: String,
|
||||||
|
#[serde(rename = "ks")]
|
||||||
|
transform: Transform,
|
||||||
|
#[serde(rename = "ao")]
|
||||||
|
auto_orient: i64,
|
||||||
|
#[serde(rename = "ip")]
|
||||||
|
in_point: i64,
|
||||||
|
#[serde(rename = "op")]
|
||||||
|
out_point: i64,
|
||||||
|
#[serde(rename = "st")]
|
||||||
|
start_time: i64,
|
||||||
|
#[serde(rename = "bm")]
|
||||||
|
blend_mode: i64,
|
||||||
|
#[serde(rename = "sr")]
|
||||||
|
stretch: i64,
|
||||||
|
#[serde(rename = "ln")]
|
||||||
|
#[serde(default)]
|
||||||
|
layer_id: Option<String>,
|
||||||
|
shapes: Vec<Shape>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Transform {
|
||||||
|
#[serde(rename = "p")]
|
||||||
|
pub position: MultidimensionalPropertyValue,
|
||||||
|
#[serde(rename = "a")]
|
||||||
|
pub anchor_point: MultidimensionalPropertyValue,
|
||||||
|
#[serde(rename = "s")]
|
||||||
|
pub scale: MultidimensionalPropertyValue,
|
||||||
|
#[serde(rename = "r")]
|
||||||
|
pub rotation: PropertyValue,
|
||||||
|
#[serde(rename = "o")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub opacity: Option<PropertyValue>,
|
||||||
|
#[serde(rename = "px")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub position_x: Option<PropertyValue>,
|
||||||
|
#[serde(rename = "py")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub position_y: Option<PropertyValue>,
|
||||||
|
#[serde(rename = "pz")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub position_z: Option<PropertyValue>,
|
||||||
|
#[serde(rename = "sk")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub skew: Option<PropertyValue>,
|
||||||
|
#[serde(rename = "sa")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub skew_axis: Option<PropertyValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum PropertyValue {
|
||||||
|
Value {
|
||||||
|
#[serde(rename = "k")]
|
||||||
|
value: f32,
|
||||||
|
#[serde(rename = "x")]
|
||||||
|
#[serde(default)]
|
||||||
|
expression: Option<String>,
|
||||||
|
#[serde(rename = "ix")]
|
||||||
|
#[serde(default)]
|
||||||
|
index: Option<i64>,
|
||||||
|
},
|
||||||
|
KeyframedValue {
|
||||||
|
#[serde(rename = "k")]
|
||||||
|
keyframes: Vec<KeyframeValue>,
|
||||||
|
#[serde(rename = "x")]
|
||||||
|
#[serde(default)]
|
||||||
|
expression: Option<String>,
|
||||||
|
#[serde(rename = "ix")]
|
||||||
|
#[serde(default)]
|
||||||
|
index: Option<i64>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct KeyframeValue {
|
||||||
|
#[serde(rename = "s")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub start: Option<Vec<f32>>,
|
||||||
|
#[serde(rename = "t")]
|
||||||
|
pub time: i64,
|
||||||
|
#[serde(rename = "i")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub interpolation: Option<OffsetInterpolation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Interpolation {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct OffsetInterpolation {
|
||||||
|
pub x: Vec<f32>,
|
||||||
|
pub y: Vec<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct OffsetKeyframe {
|
||||||
|
#[serde(rename = "s")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub start: Option<Vec<f32>>,
|
||||||
|
#[serde(rename = "t")]
|
||||||
|
pub time: i64,
|
||||||
|
#[serde(rename = "i")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub in_value: Option<OffsetInterpolation>,
|
||||||
|
#[serde(rename = "o")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub out_value: Option<OffsetInterpolation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum MultidimensionalPropertyValue {
|
||||||
|
Value {
|
||||||
|
#[serde(rename = "k")]
|
||||||
|
value: Vec<f32>,
|
||||||
|
#[serde(rename = "x")]
|
||||||
|
#[serde(default)]
|
||||||
|
expression: Option<String>,
|
||||||
|
#[serde(rename = "ix")]
|
||||||
|
#[serde(default)]
|
||||||
|
index: Option<i64>,
|
||||||
|
},
|
||||||
|
KeyframedValue {
|
||||||
|
#[serde(rename = "k")]
|
||||||
|
keyframes: Vec<OffsetKeyframe>,
|
||||||
|
#[serde(rename = "x")]
|
||||||
|
#[serde(default)]
|
||||||
|
expression: Option<String>,
|
||||||
|
#[serde(rename = "ix")]
|
||||||
|
#[serde(default)]
|
||||||
|
index: Option<i64>,
|
||||||
|
#[serde(rename = "ti")]
|
||||||
|
#[serde(default)]
|
||||||
|
in_tangent: Option<i64>,
|
||||||
|
#[serde(rename = "to")]
|
||||||
|
#[serde(default)]
|
||||||
|
out_tangent: Option<i64>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "ty")]
|
||||||
|
pub enum Shape {
|
||||||
|
#[serde(rename = "gr")]
|
||||||
|
Group {
|
||||||
|
#[serde(rename = "it")]
|
||||||
|
items: Vec<Shape>,
|
||||||
|
#[serde(rename = "nm")]
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
#[serde(rename = "sh")]
|
||||||
|
Shape {
|
||||||
|
#[serde(rename = "ks")]
|
||||||
|
vertices: ShapeVertices,
|
||||||
|
#[serde(rename = "d")]
|
||||||
|
#[serde(default)]
|
||||||
|
direction: Option<i64>,
|
||||||
|
},
|
||||||
|
#[serde(rename = "fl")]
|
||||||
|
Fill {
|
||||||
|
#[serde(rename = "nm")]
|
||||||
|
#[serde(default)]
|
||||||
|
name: Option<String>,
|
||||||
|
#[serde(rename = "o")]
|
||||||
|
#[serde(default)]
|
||||||
|
opacity: Option<PropertyValue>,
|
||||||
|
#[serde(rename = "c")]
|
||||||
|
color: MultidimensionalPropertyValue,
|
||||||
|
},
|
||||||
|
#[serde(rename = "tr")]
|
||||||
|
Transform {
|
||||||
|
#[serde(rename = "r")]
|
||||||
|
rotation: PropertyValue,
|
||||||
|
#[serde(rename = "sk")]
|
||||||
|
skew: PropertyValue,
|
||||||
|
#[serde(rename = "sa")]
|
||||||
|
skew_axis: PropertyValue,
|
||||||
|
#[serde(rename = "p")]
|
||||||
|
position: MultidimensionalPropertyValue,
|
||||||
|
#[serde(rename = "a")]
|
||||||
|
anchor_point: MultidimensionalPropertyValue,
|
||||||
|
#[serde(rename = "s")]
|
||||||
|
scale: MultidimensionalPropertyValue,
|
||||||
|
},
|
||||||
|
#[serde(other)]
|
||||||
|
Unimplemented,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum ShapeVertices {
|
||||||
|
Shape {
|
||||||
|
#[serde(rename = "k")]
|
||||||
|
value: ShapeProperty,
|
||||||
|
#[serde(rename = "x")]
|
||||||
|
#[serde(default)]
|
||||||
|
expression: Option<String>,
|
||||||
|
#[serde(rename = "ix")]
|
||||||
|
#[serde(default)]
|
||||||
|
index: Option<i64>,
|
||||||
|
#[serde(rename = "a")]
|
||||||
|
animated: i64,
|
||||||
|
},
|
||||||
|
ShapeKeyframed {
|
||||||
|
#[serde(rename = "k")]
|
||||||
|
value: Vec<ShapeKeyframeProperty>,
|
||||||
|
#[serde(rename = "x")]
|
||||||
|
#[serde(default)]
|
||||||
|
expression: Option<String>,
|
||||||
|
#[serde(rename = "ix")]
|
||||||
|
#[serde(default)]
|
||||||
|
index: Option<i64>,
|
||||||
|
#[serde(rename = "a")]
|
||||||
|
animated: i64,
|
||||||
|
#[serde(rename = "ti")]
|
||||||
|
#[serde(default)]
|
||||||
|
in_tangent: Vec<i64>,
|
||||||
|
#[serde(rename = "to")]
|
||||||
|
#[serde(default)]
|
||||||
|
out_tangent: Vec<i64>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ShapeProperty {
|
||||||
|
#[serde(rename = "c")]
|
||||||
|
pub closed: bool,
|
||||||
|
#[serde(rename = "i")]
|
||||||
|
pub in_points: Vec<[f32; 2]>,
|
||||||
|
#[serde(rename = "o")]
|
||||||
|
pub out_points: Vec<[f32; 2]>,
|
||||||
|
#[serde(rename = "v")]
|
||||||
|
pub vertices: Vec<[f32; 2]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ShapeKeyframeProperty {
|
||||||
|
#[serde(rename = "s")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub start: Vec<Option<ShapeProperty>>,
|
||||||
|
#[serde(rename = "t")]
|
||||||
|
pub time: i64,
|
||||||
|
#[serde(rename = "i")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub in_value: Option<OffsetInterpolation>,
|
||||||
|
#[serde(rename = "o")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub out_value: Option<OffsetInterpolation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lottie {
|
||||||
|
#[inline]
|
||||||
|
pub fn from_reader<R>(reader: R) -> Result<Lottie, JSONError> where R: Read {
|
||||||
|
serde_json::from_reader(reader)
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,4 +30,4 @@ path = "../simd"
|
||||||
path = "../ui"
|
path = "../ui"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
quickcheck = "0.7"
|
quickcheck = "0.8"
|
||||||
|
|
|
@ -59,7 +59,7 @@ impl<'a> SceneBuilder<'a> {
|
||||||
let path_count = self.scene.paths.len();
|
let path_count = self.scene.paths.len();
|
||||||
self.listener.send(RenderCommand::Start { bounding_quad, path_count });
|
self.listener.send(RenderCommand::Start { bounding_quad, path_count });
|
||||||
|
|
||||||
self.listener.send(RenderCommand::AddShaders(self.scene.build_shaders()));
|
self.listener.send(RenderCommand::AddPaintData(self.scene.build_paint_data()));
|
||||||
|
|
||||||
let effective_view_box = self.scene.effective_view_box(self.built_options);
|
let effective_view_box = self.scene.effective_view_box(self.built_options);
|
||||||
let alpha_tiles = executor.flatten_into_vector(path_count, |path_index| {
|
let alpha_tiles = executor.flatten_into_vector(path_count, |path_index| {
|
||||||
|
@ -81,8 +81,16 @@ impl<'a> SceneBuilder<'a> {
|
||||||
) -> Vec<AlphaTileBatchPrimitive> {
|
) -> Vec<AlphaTileBatchPrimitive> {
|
||||||
let path_object = &scene.paths[path_index];
|
let path_object = &scene.paths[path_index];
|
||||||
let outline = scene.apply_render_options(path_object.outline(), built_options);
|
let outline = scene.apply_render_options(path_object.outline(), built_options);
|
||||||
|
let paint_id = path_object.paint();
|
||||||
|
let object_is_opaque = scene.paints[paint_id.0 as usize].is_opaque();
|
||||||
|
|
||||||
|
let mut tiler = Tiler::new(self,
|
||||||
|
&outline,
|
||||||
|
view_box,
|
||||||
|
path_index as u16,
|
||||||
|
paint_id,
|
||||||
|
object_is_opaque);
|
||||||
|
|
||||||
let mut tiler = Tiler::new(self, &outline, view_box, path_index as u16);
|
|
||||||
tiler.generate_tiles();
|
tiler.generate_tiles();
|
||||||
|
|
||||||
self.listener.send(RenderCommand::AddFills(tiler.built_object.fills));
|
self.listener.send(RenderCommand::AddFills(tiler.built_object.fills));
|
||||||
|
@ -108,7 +116,7 @@ impl<'a> SceneBuilder<'a> {
|
||||||
|
|
||||||
fn pack_alpha_tiles(&mut self, alpha_tiles: Vec<AlphaTileBatchPrimitive>) {
|
fn pack_alpha_tiles(&mut self, alpha_tiles: Vec<AlphaTileBatchPrimitive>) {
|
||||||
let path_count = self.scene.paths.len() as u32;
|
let path_count = self.scene.paths.len() as u32;
|
||||||
let solid_tiles = self.z_buffer.build_solid_tiles(0..path_count);
|
let solid_tiles = self.z_buffer.build_solid_tiles(&self.scene.paths, 0..path_count);
|
||||||
if !solid_tiles.is_empty() {
|
if !solid_tiles.is_empty() {
|
||||||
self.listener.send(RenderCommand::SolidTile(solid_tiles));
|
self.listener.send(RenderCommand::SolidTile(solid_tiles));
|
||||||
}
|
}
|
||||||
|
@ -234,9 +242,9 @@ impl BuiltObject {
|
||||||
let right = Point2DF32::new(right, tile_origin_y);
|
let right = Point2DF32::new(right, tile_origin_y);
|
||||||
|
|
||||||
let segment = if winding < 0 {
|
let segment = if winding < 0 {
|
||||||
LineSegmentF32::new(&left, &right)
|
LineSegmentF32::new(left, right)
|
||||||
} else {
|
} else {
|
||||||
LineSegmentF32::new(&right, &left)
|
LineSegmentF32::new(right, left)
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
|
@ -298,14 +306,14 @@ impl BuiltObject {
|
||||||
let point = Point2DF32::new(x, segment.solve_y_for_x(x));
|
let point = Point2DF32::new(x, segment.solve_y_for_x(x));
|
||||||
if !winding {
|
if !winding {
|
||||||
fill_to = point;
|
fill_to = point;
|
||||||
segment = LineSegmentF32::new(&point, &segment.to());
|
segment = LineSegmentF32::new(point, segment.to());
|
||||||
} else {
|
} else {
|
||||||
fill_from = point;
|
fill_from = point;
|
||||||
segment = LineSegmentF32::new(&segment.from(), &point);
|
segment = LineSegmentF32::new(segment.from(), point);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let fill_segment = LineSegmentF32::new(&fill_from, &fill_to);
|
let fill_segment = LineSegmentF32::new(fill_from, fill_to);
|
||||||
let fill_tile_coords = Point2DI32::new(subsegment_tile_x, tile_y);
|
let fill_tile_coords = Point2DI32::new(subsegment_tile_x, tile_y);
|
||||||
self.add_fill(builder, &fill_segment, fill_tile_coords);
|
self.add_fill(builder, &fill_segment, fill_tile_coords);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,12 @@ pub struct SceneProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SceneProxy {
|
impl SceneProxy {
|
||||||
pub fn new<E>(scene: Scene, executor: E) -> SceneProxy where E: Executor + Send + 'static {
|
pub fn new<E>(executor: E) -> SceneProxy where E: Executor + Send + 'static {
|
||||||
|
SceneProxy::from_scene(Scene::new(), executor)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_scene<E>(scene: Scene, executor: E) -> SceneProxy
|
||||||
|
where E: Executor + Send + 'static {
|
||||||
let (main_to_worker_sender, main_to_worker_receiver) = mpsc::channel();
|
let (main_to_worker_sender, main_to_worker_receiver) = mpsc::channel();
|
||||||
thread::spawn(move || scene_thread(scene, executor, main_to_worker_receiver));
|
thread::spawn(move || scene_thread(scene, executor, main_to_worker_receiver));
|
||||||
SceneProxy { sender: main_to_worker_sender }
|
SceneProxy { sender: main_to_worker_sender }
|
||||||
|
|
|
@ -9,10 +9,9 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use crate::gpu::debug::DebugUIPresenter;
|
use crate::gpu::debug::DebugUIPresenter;
|
||||||
use crate::gpu_data::{AlphaTileBatchPrimitive, FillBatchPrimitive};
|
use crate::gpu_data::{AlphaTileBatchPrimitive, FillBatchPrimitive, PaintData};
|
||||||
use crate::gpu_data::{RenderCommand, SolidTileBatchPrimitive};
|
use crate::gpu_data::{RenderCommand, SolidTileBatchPrimitive};
|
||||||
use crate::post::DefringingKernel;
|
use crate::post::DefringingKernel;
|
||||||
use crate::scene::ObjectShader;
|
|
||||||
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
|
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
|
||||||
use pathfinder_geometry::basic::point::{Point2DI32, Point3DF32};
|
use pathfinder_geometry::basic::point::{Point2DI32, Point3DF32};
|
||||||
use pathfinder_geometry::basic::rect::RectI32;
|
use pathfinder_geometry::basic::rect::RectI32;
|
||||||
|
@ -21,7 +20,8 @@ use pathfinder_geometry::color::ColorF;
|
||||||
use pathfinder_gpu::resources::ResourceLoader;
|
use pathfinder_gpu::resources::ResourceLoader;
|
||||||
use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, ClearParams};
|
use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, ClearParams};
|
||||||
use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderState, StencilFunc};
|
use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderState, StencilFunc};
|
||||||
use pathfinder_gpu::{StencilState, TextureFormat, UniformData, VertexAttrType};
|
use pathfinder_gpu::{StencilState, TextureFormat, UniformData, VertexAttrClass};
|
||||||
|
use pathfinder_gpu::{VertexAttrDescriptor, VertexAttrType};
|
||||||
use pathfinder_simd::default::{F32x4, I32x4};
|
use pathfinder_simd::default::{F32x4, I32x4};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
@ -38,11 +38,8 @@ const MASK_FRAMEBUFFER_HEIGHT: i32 = TILE_HEIGHT as i32 * 256;
|
||||||
|
|
||||||
// TODO(pcwalton): Replace with `mem::size_of` calls?
|
// TODO(pcwalton): Replace with `mem::size_of` calls?
|
||||||
const FILL_INSTANCE_SIZE: usize = 8;
|
const FILL_INSTANCE_SIZE: usize = 8;
|
||||||
const SOLID_TILE_INSTANCE_SIZE: usize = 6;
|
const SOLID_TILE_INSTANCE_SIZE: usize = 10;
|
||||||
const MASK_TILE_INSTANCE_SIZE: usize = 8;
|
const MASK_TILE_INSTANCE_SIZE: usize = 12;
|
||||||
|
|
||||||
const FILL_COLORS_TEXTURE_WIDTH: i32 = 256;
|
|
||||||
const FILL_COLORS_TEXTURE_HEIGHT: i32 = 256;
|
|
||||||
|
|
||||||
const MAX_FILLS_PER_BATCH: usize = 0x4000;
|
const MAX_FILLS_PER_BATCH: usize = 0x4000;
|
||||||
|
|
||||||
|
@ -68,7 +65,7 @@ where
|
||||||
quad_vertex_positions_buffer: D::Buffer,
|
quad_vertex_positions_buffer: D::Buffer,
|
||||||
fill_vertex_array: FillVertexArray<D>,
|
fill_vertex_array: FillVertexArray<D>,
|
||||||
mask_framebuffer: D::Framebuffer,
|
mask_framebuffer: D::Framebuffer,
|
||||||
fill_colors_texture: D::Texture,
|
paint_texture: Option<D::Texture>,
|
||||||
|
|
||||||
// Postprocessing shader
|
// Postprocessing shader
|
||||||
postprocess_source_framebuffer: Option<D::Framebuffer>,
|
postprocess_source_framebuffer: Option<D::Framebuffer>,
|
||||||
|
@ -171,10 +168,6 @@ where
|
||||||
device.create_texture(TextureFormat::R16F, mask_framebuffer_size);
|
device.create_texture(TextureFormat::R16F, mask_framebuffer_size);
|
||||||
let mask_framebuffer = device.create_framebuffer(mask_framebuffer_texture);
|
let mask_framebuffer = device.create_framebuffer(mask_framebuffer_texture);
|
||||||
|
|
||||||
let fill_colors_size =
|
|
||||||
Point2DI32::new(FILL_COLORS_TEXTURE_WIDTH, FILL_COLORS_TEXTURE_HEIGHT);
|
|
||||||
let fill_colors_texture = device.create_texture(TextureFormat::RGBA8, fill_colors_size);
|
|
||||||
|
|
||||||
let window_size = dest_framebuffer.window_size(&device);
|
let window_size = dest_framebuffer.window_size(&device);
|
||||||
let debug_ui_presenter = DebugUIPresenter::new(&device, resources, window_size);
|
let debug_ui_presenter = DebugUIPresenter::new(&device, resources, window_size);
|
||||||
|
|
||||||
|
@ -195,7 +188,7 @@ where
|
||||||
quad_vertex_positions_buffer,
|
quad_vertex_positions_buffer,
|
||||||
fill_vertex_array,
|
fill_vertex_array,
|
||||||
mask_framebuffer,
|
mask_framebuffer,
|
||||||
fill_colors_texture,
|
paint_texture: None,
|
||||||
|
|
||||||
postprocess_source_framebuffer: None,
|
postprocess_source_framebuffer: None,
|
||||||
postprocess_program,
|
postprocess_program,
|
||||||
|
@ -242,7 +235,7 @@ where
|
||||||
}
|
}
|
||||||
self.stats.path_count = path_count;
|
self.stats.path_count = path_count;
|
||||||
}
|
}
|
||||||
RenderCommand::AddShaders(ref shaders) => self.upload_shaders(shaders),
|
RenderCommand::AddPaintData(ref paint_data) => self.upload_paint_data(paint_data),
|
||||||
RenderCommand::AddFills(ref fills) => self.add_fills(fills),
|
RenderCommand::AddFills(ref fills) => self.add_fills(fills),
|
||||||
RenderCommand::FlushFills => {
|
RenderCommand::FlushFills => {
|
||||||
self.begin_composite_timer_query();
|
self.begin_composite_timer_query();
|
||||||
|
@ -345,17 +338,19 @@ where
|
||||||
&self.quad_vertex_positions_buffer
|
&self.quad_vertex_positions_buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upload_shaders(&mut self, shaders: &[ObjectShader]) {
|
fn upload_paint_data(&mut self, paint_data: &PaintData) {
|
||||||
let size = Point2DI32::new(FILL_COLORS_TEXTURE_WIDTH, FILL_COLORS_TEXTURE_HEIGHT);
|
match self.paint_texture {
|
||||||
let mut fill_colors = vec![0; size.x() as usize * size.y() as usize * 4];
|
Some(ref paint_texture) if
|
||||||
for (shader_index, shader) in shaders.iter().enumerate() {
|
self.device.texture_size(paint_texture) == paint_data.size => {}
|
||||||
fill_colors[shader_index * 4 + 0] = shader.fill_color.r;
|
_ => {
|
||||||
fill_colors[shader_index * 4 + 1] = shader.fill_color.g;
|
let texture = self.device.create_texture(TextureFormat::RGBA8, paint_data.size);
|
||||||
fill_colors[shader_index * 4 + 2] = shader.fill_color.b;
|
self.paint_texture = Some(texture)
|
||||||
fill_colors[shader_index * 4 + 3] = shader.fill_color.a;
|
|
||||||
}
|
}
|
||||||
self.device
|
}
|
||||||
.upload_to_texture(&self.fill_colors_texture, size, &fill_colors);
|
|
||||||
|
self.device.upload_to_texture(self.paint_texture.as_ref().unwrap(),
|
||||||
|
paint_data.size,
|
||||||
|
&paint_data.texels);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upload_solid_tiles(&mut self, solid_tiles: &[SolidTileBatchPrimitive]) {
|
fn upload_solid_tiles(&mut self, solid_tiles: &[SolidTileBatchPrimitive]) {
|
||||||
|
@ -493,32 +488,26 @@ where
|
||||||
|
|
||||||
match self.render_mode {
|
match self.render_mode {
|
||||||
RenderMode::Multicolor => {
|
RenderMode::Multicolor => {
|
||||||
self.device.bind_texture(&self.fill_colors_texture, 1);
|
let paint_texture = self.paint_texture.as_ref().unwrap();
|
||||||
|
self.device.bind_texture(paint_texture, 1);
|
||||||
self.device.set_uniform(
|
self.device.set_uniform(
|
||||||
&self
|
&self.alpha_multicolor_tile_program.paint_texture_uniform,
|
||||||
.alpha_multicolor_tile_program
|
|
||||||
.fill_colors_texture_uniform,
|
|
||||||
UniformData::TextureUnit(1),
|
UniformData::TextureUnit(1),
|
||||||
);
|
);
|
||||||
self.device.set_uniform(
|
self.device.set_uniform(
|
||||||
&self
|
&self.alpha_multicolor_tile_program.paint_texture_size_uniform,
|
||||||
.alpha_multicolor_tile_program
|
UniformData::Vec2(self.device.texture_size(paint_texture).0.to_f32x4())
|
||||||
.fill_colors_texture_size_uniform,
|
|
||||||
UniformData::Vec2(
|
|
||||||
I32x4::new(FILL_COLORS_TEXTURE_WIDTH, FILL_COLORS_TEXTURE_HEIGHT, 0, 0)
|
|
||||||
.to_f32x4(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
RenderMode::Monochrome { .. } if self.postprocessing_needed() => {
|
RenderMode::Monochrome { .. } if self.postprocessing_needed() => {
|
||||||
self.device.set_uniform(
|
self.device.set_uniform(
|
||||||
&self.alpha_monochrome_tile_program.fill_color_uniform,
|
&self.alpha_monochrome_tile_program.color_uniform,
|
||||||
UniformData::Vec4(F32x4::splat(1.0)),
|
UniformData::Vec4(F32x4::splat(1.0)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
RenderMode::Monochrome { fg_color, .. } => {
|
RenderMode::Monochrome { fg_color, .. } => {
|
||||||
self.device.set_uniform(
|
self.device.set_uniform(
|
||||||
&self.alpha_monochrome_tile_program.fill_color_uniform,
|
&self.alpha_monochrome_tile_program.color_uniform,
|
||||||
UniformData::Vec4(fg_color.0),
|
UniformData::Vec4(fg_color.0),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -558,32 +547,30 @@ where
|
||||||
|
|
||||||
match self.render_mode {
|
match self.render_mode {
|
||||||
RenderMode::Multicolor => {
|
RenderMode::Multicolor => {
|
||||||
self.device.bind_texture(&self.fill_colors_texture, 0);
|
let paint_texture = self.paint_texture.as_ref().unwrap();
|
||||||
|
self.device.bind_texture(paint_texture, 0);
|
||||||
self.device.set_uniform(
|
self.device.set_uniform(
|
||||||
&self
|
&self
|
||||||
.solid_multicolor_tile_program
|
.solid_multicolor_tile_program
|
||||||
.fill_colors_texture_uniform,
|
.paint_texture_uniform,
|
||||||
UniformData::TextureUnit(0),
|
UniformData::TextureUnit(0),
|
||||||
);
|
);
|
||||||
self.device.set_uniform(
|
self.device.set_uniform(
|
||||||
&self
|
&self
|
||||||
.solid_multicolor_tile_program
|
.solid_multicolor_tile_program
|
||||||
.fill_colors_texture_size_uniform,
|
.paint_texture_size_uniform,
|
||||||
UniformData::Vec2(
|
UniformData::Vec2(self.device.texture_size(paint_texture).0.to_f32x4())
|
||||||
I32x4::new(FILL_COLORS_TEXTURE_WIDTH, FILL_COLORS_TEXTURE_HEIGHT, 0, 0)
|
|
||||||
.to_f32x4(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
RenderMode::Monochrome { .. } if self.postprocessing_needed() => {
|
RenderMode::Monochrome { .. } if self.postprocessing_needed() => {
|
||||||
self.device.set_uniform(
|
self.device.set_uniform(
|
||||||
&self.solid_monochrome_tile_program.fill_color_uniform,
|
&self.solid_monochrome_tile_program.color_uniform,
|
||||||
UniformData::Vec4(F32x4::splat(1.0)),
|
UniformData::Vec4(F32x4::splat(1.0)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
RenderMode::Monochrome { fg_color, .. } => {
|
RenderMode::Monochrome { fg_color, .. } => {
|
||||||
self.device.set_uniform(
|
self.device.set_uniform(
|
||||||
&self.solid_monochrome_tile_program.fill_color_uniform,
|
&self.solid_monochrome_tile_program.color_uniform,
|
||||||
UniformData::Vec4(fg_color.0),
|
UniformData::Vec4(fg_color.0),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -932,55 +919,57 @@ where
|
||||||
device.bind_vertex_array(&vertex_array);
|
device.bind_vertex_array(&vertex_array);
|
||||||
device.use_program(&fill_program.program);
|
device.use_program(&fill_program.program);
|
||||||
device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex);
|
device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex);
|
||||||
device.configure_float_vertex_attr(&tess_coord_attr, 2, VertexAttrType::U8, false, 0, 0, 0);
|
device.configure_vertex_attr(&tess_coord_attr, &VertexAttrDescriptor {
|
||||||
|
size: 2,
|
||||||
|
class: VertexAttrClass::Float,
|
||||||
|
attr_type: VertexAttrType::U8,
|
||||||
|
stride: 0,
|
||||||
|
offset: 0,
|
||||||
|
divisor: 0,
|
||||||
|
});
|
||||||
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
|
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
|
||||||
device.configure_int_vertex_attr(
|
device.configure_vertex_attr(&from_px_attr, &VertexAttrDescriptor {
|
||||||
&from_px_attr,
|
size: 1,
|
||||||
1,
|
class: VertexAttrClass::Int,
|
||||||
VertexAttrType::U8,
|
attr_type: VertexAttrType::U8,
|
||||||
FILL_INSTANCE_SIZE,
|
stride: FILL_INSTANCE_SIZE,
|
||||||
0,
|
offset: 0,
|
||||||
1,
|
divisor: 1,
|
||||||
);
|
});
|
||||||
device.configure_int_vertex_attr(
|
device.configure_vertex_attr(&to_px_attr, &VertexAttrDescriptor {
|
||||||
&to_px_attr,
|
size: 1,
|
||||||
1,
|
class: VertexAttrClass::Int,
|
||||||
VertexAttrType::U8,
|
attr_type: VertexAttrType::U8,
|
||||||
FILL_INSTANCE_SIZE,
|
stride: FILL_INSTANCE_SIZE,
|
||||||
1,
|
offset: 1,
|
||||||
1,
|
divisor: 1,
|
||||||
);
|
});
|
||||||
device.configure_float_vertex_attr(
|
device.configure_vertex_attr(&from_subpx_attr, &VertexAttrDescriptor {
|
||||||
&from_subpx_attr,
|
size: 2,
|
||||||
2,
|
class: VertexAttrClass::FloatNorm,
|
||||||
VertexAttrType::U8,
|
attr_type: VertexAttrType::U8,
|
||||||
true,
|
stride: FILL_INSTANCE_SIZE,
|
||||||
FILL_INSTANCE_SIZE,
|
offset: 2,
|
||||||
2,
|
divisor: 1,
|
||||||
1,
|
});
|
||||||
);
|
device.configure_vertex_attr(&to_subpx_attr, &VertexAttrDescriptor {
|
||||||
device.configure_float_vertex_attr(
|
size: 2,
|
||||||
&to_subpx_attr,
|
class: VertexAttrClass::FloatNorm,
|
||||||
2,
|
attr_type: VertexAttrType::U8,
|
||||||
VertexAttrType::U8,
|
stride: FILL_INSTANCE_SIZE,
|
||||||
true,
|
offset: 4,
|
||||||
FILL_INSTANCE_SIZE,
|
divisor: 1,
|
||||||
4,
|
});
|
||||||
1,
|
device.configure_vertex_attr(&tile_index_attr, &VertexAttrDescriptor {
|
||||||
);
|
size: 1,
|
||||||
device.configure_int_vertex_attr(
|
class: VertexAttrClass::Int,
|
||||||
&tile_index_attr,
|
attr_type: VertexAttrType::U16,
|
||||||
1,
|
stride: FILL_INSTANCE_SIZE,
|
||||||
VertexAttrType::U16,
|
offset: 6,
|
||||||
FILL_INSTANCE_SIZE,
|
divisor: 1,
|
||||||
6,
|
});
|
||||||
1,
|
|
||||||
);
|
|
||||||
|
|
||||||
FillVertexArray {
|
FillVertexArray { vertex_array, vertex_buffer }
|
||||||
vertex_array,
|
|
||||||
vertex_buffer,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1006,53 +995,58 @@ where
|
||||||
let tess_coord_attr = device.get_vertex_attr(&alpha_tile_program.program, "TessCoord");
|
let tess_coord_attr = device.get_vertex_attr(&alpha_tile_program.program, "TessCoord");
|
||||||
let tile_origin_attr = device.get_vertex_attr(&alpha_tile_program.program, "TileOrigin");
|
let tile_origin_attr = device.get_vertex_attr(&alpha_tile_program.program, "TileOrigin");
|
||||||
let backdrop_attr = device.get_vertex_attr(&alpha_tile_program.program, "Backdrop");
|
let backdrop_attr = device.get_vertex_attr(&alpha_tile_program.program, "Backdrop");
|
||||||
let object_attr = device.get_vertex_attr(&alpha_tile_program.program, "Object");
|
|
||||||
let tile_index_attr = device.get_vertex_attr(&alpha_tile_program.program, "TileIndex");
|
let tile_index_attr = device.get_vertex_attr(&alpha_tile_program.program, "TileIndex");
|
||||||
|
let color_tex_coord_attr = device.get_vertex_attr(&alpha_tile_program.program,
|
||||||
|
"ColorTexCoord");
|
||||||
|
|
||||||
// NB: The object must be of type `I16`, not `U16`, to work around a macOS Radeon
|
// NB: The object must be of type `I16`, not `U16`, to work around a macOS Radeon
|
||||||
// driver bug.
|
// driver bug.
|
||||||
device.bind_vertex_array(&vertex_array);
|
device.bind_vertex_array(&vertex_array);
|
||||||
device.use_program(&alpha_tile_program.program);
|
device.use_program(&alpha_tile_program.program);
|
||||||
device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex);
|
device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex);
|
||||||
device.configure_float_vertex_attr(&tess_coord_attr, 2, VertexAttrType::U8, false, 0, 0, 0);
|
device.configure_vertex_attr(&tess_coord_attr, &VertexAttrDescriptor {
|
||||||
|
size: 2,
|
||||||
|
class: VertexAttrClass::Float,
|
||||||
|
attr_type: VertexAttrType::U8,
|
||||||
|
stride: 0,
|
||||||
|
offset: 0,
|
||||||
|
divisor: 0,
|
||||||
|
});
|
||||||
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
|
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
|
||||||
device.configure_int_vertex_attr(
|
device.configure_vertex_attr(&tile_origin_attr, &VertexAttrDescriptor {
|
||||||
&tile_origin_attr,
|
size: 3,
|
||||||
3,
|
class: VertexAttrClass::Int,
|
||||||
VertexAttrType::U8,
|
attr_type: VertexAttrType::U8,
|
||||||
MASK_TILE_INSTANCE_SIZE,
|
stride: MASK_TILE_INSTANCE_SIZE,
|
||||||
0,
|
offset: 0,
|
||||||
1,
|
divisor: 1,
|
||||||
);
|
});
|
||||||
device.configure_int_vertex_attr(
|
device.configure_vertex_attr(&backdrop_attr, &VertexAttrDescriptor {
|
||||||
&backdrop_attr,
|
size: 1,
|
||||||
1,
|
class: VertexAttrClass::Int,
|
||||||
VertexAttrType::I8,
|
attr_type: VertexAttrType::I8,
|
||||||
MASK_TILE_INSTANCE_SIZE,
|
stride: MASK_TILE_INSTANCE_SIZE,
|
||||||
3,
|
offset: 3,
|
||||||
1,
|
divisor: 1,
|
||||||
);
|
});
|
||||||
device.configure_int_vertex_attr(
|
device.configure_vertex_attr(&tile_index_attr, &VertexAttrDescriptor {
|
||||||
&object_attr,
|
size: 1,
|
||||||
2,
|
class: VertexAttrClass::Int,
|
||||||
VertexAttrType::I16,
|
attr_type: VertexAttrType::I16,
|
||||||
MASK_TILE_INSTANCE_SIZE,
|
stride: MASK_TILE_INSTANCE_SIZE,
|
||||||
4,
|
offset: 6,
|
||||||
1,
|
divisor: 1,
|
||||||
);
|
});
|
||||||
device.configure_int_vertex_attr(
|
device.configure_vertex_attr(&color_tex_coord_attr, &VertexAttrDescriptor {
|
||||||
&tile_index_attr,
|
size: 2,
|
||||||
2,
|
class: VertexAttrClass::FloatNorm,
|
||||||
VertexAttrType::I16,
|
attr_type: VertexAttrType::U16,
|
||||||
MASK_TILE_INSTANCE_SIZE,
|
stride: MASK_TILE_INSTANCE_SIZE,
|
||||||
6,
|
offset: 8,
|
||||||
1,
|
divisor: 1,
|
||||||
);
|
});
|
||||||
|
|
||||||
AlphaTileVertexArray {
|
AlphaTileVertexArray { vertex_array, vertex_buffer }
|
||||||
vertex_array,
|
|
||||||
vertex_buffer,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1077,37 +1071,41 @@ where
|
||||||
|
|
||||||
let tess_coord_attr = device.get_vertex_attr(&solid_tile_program.program, "TessCoord");
|
let tess_coord_attr = device.get_vertex_attr(&solid_tile_program.program, "TessCoord");
|
||||||
let tile_origin_attr = device.get_vertex_attr(&solid_tile_program.program, "TileOrigin");
|
let tile_origin_attr = device.get_vertex_attr(&solid_tile_program.program, "TileOrigin");
|
||||||
let object_attr = device.get_vertex_attr(&solid_tile_program.program, "Object");
|
let color_tex_coord_attr = device.get_vertex_attr(&solid_tile_program.program,
|
||||||
|
"ColorTexCoord");
|
||||||
|
|
||||||
// NB: The object must be of type short, not unsigned short, to work around a macOS
|
// NB: The object must be of type short, not unsigned short, to work around a macOS
|
||||||
// Radeon driver bug.
|
// Radeon driver bug.
|
||||||
device.bind_vertex_array(&vertex_array);
|
device.bind_vertex_array(&vertex_array);
|
||||||
device.use_program(&solid_tile_program.program);
|
device.use_program(&solid_tile_program.program);
|
||||||
device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex);
|
device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex);
|
||||||
device.configure_float_vertex_attr(&tess_coord_attr, 2, VertexAttrType::U8, false, 0, 0, 0);
|
device.configure_vertex_attr(&tess_coord_attr, &VertexAttrDescriptor {
|
||||||
|
size: 2,
|
||||||
|
class: VertexAttrClass::Float,
|
||||||
|
attr_type: VertexAttrType::U8,
|
||||||
|
stride: 0,
|
||||||
|
offset: 0,
|
||||||
|
divisor: 0,
|
||||||
|
});
|
||||||
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
|
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
|
||||||
device.configure_float_vertex_attr(
|
device.configure_vertex_attr(&tile_origin_attr, &VertexAttrDescriptor {
|
||||||
&tile_origin_attr,
|
size: 2,
|
||||||
2,
|
class: VertexAttrClass::Float,
|
||||||
VertexAttrType::I16,
|
attr_type: VertexAttrType::I16,
|
||||||
false,
|
stride: SOLID_TILE_INSTANCE_SIZE,
|
||||||
SOLID_TILE_INSTANCE_SIZE,
|
offset: 0,
|
||||||
0,
|
divisor: 1,
|
||||||
1,
|
});
|
||||||
);
|
device.configure_vertex_attr(&color_tex_coord_attr, &VertexAttrDescriptor {
|
||||||
device.configure_int_vertex_attr(
|
size: 2,
|
||||||
&object_attr,
|
class: VertexAttrClass::FloatNorm,
|
||||||
1,
|
attr_type: VertexAttrType::U16,
|
||||||
VertexAttrType::I16,
|
stride: SOLID_TILE_INSTANCE_SIZE,
|
||||||
SOLID_TILE_INSTANCE_SIZE,
|
offset: 4,
|
||||||
4,
|
divisor: 1,
|
||||||
1,
|
});
|
||||||
);
|
|
||||||
|
|
||||||
SolidTileVertexArray {
|
SolidTileVertexArray { vertex_array, vertex_buffer }
|
||||||
vertex_array,
|
|
||||||
vertex_buffer,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1177,8 +1175,8 @@ where
|
||||||
D: Device,
|
D: Device,
|
||||||
{
|
{
|
||||||
solid_tile_program: SolidTileProgram<D>,
|
solid_tile_program: SolidTileProgram<D>,
|
||||||
fill_colors_texture_uniform: D::Uniform,
|
paint_texture_uniform: D::Uniform,
|
||||||
fill_colors_texture_size_uniform: D::Uniform,
|
paint_texture_size_uniform: D::Uniform,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> SolidTileMulticolorProgram<D>
|
impl<D> SolidTileMulticolorProgram<D>
|
||||||
|
@ -1187,14 +1185,14 @@ where
|
||||||
{
|
{
|
||||||
fn new(device: &D, resources: &dyn ResourceLoader) -> SolidTileMulticolorProgram<D> {
|
fn new(device: &D, resources: &dyn ResourceLoader) -> SolidTileMulticolorProgram<D> {
|
||||||
let solid_tile_program = SolidTileProgram::new(device, "tile_solid_multicolor", resources);
|
let solid_tile_program = SolidTileProgram::new(device, "tile_solid_multicolor", resources);
|
||||||
let fill_colors_texture_uniform =
|
let paint_texture_uniform =
|
||||||
device.get_uniform(&solid_tile_program.program, "FillColorsTexture");
|
device.get_uniform(&solid_tile_program.program, "PaintTexture");
|
||||||
let fill_colors_texture_size_uniform =
|
let paint_texture_size_uniform =
|
||||||
device.get_uniform(&solid_tile_program.program, "FillColorsTextureSize");
|
device.get_uniform(&solid_tile_program.program, "PaintTextureSize");
|
||||||
SolidTileMulticolorProgram {
|
SolidTileMulticolorProgram {
|
||||||
solid_tile_program,
|
solid_tile_program,
|
||||||
fill_colors_texture_uniform,
|
paint_texture_uniform,
|
||||||
fill_colors_texture_size_uniform,
|
paint_texture_size_uniform,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1204,7 +1202,7 @@ where
|
||||||
D: Device,
|
D: Device,
|
||||||
{
|
{
|
||||||
solid_tile_program: SolidTileProgram<D>,
|
solid_tile_program: SolidTileProgram<D>,
|
||||||
fill_color_uniform: D::Uniform,
|
color_uniform: D::Uniform,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> SolidTileMonochromeProgram<D>
|
impl<D> SolidTileMonochromeProgram<D>
|
||||||
|
@ -1213,10 +1211,10 @@ where
|
||||||
{
|
{
|
||||||
fn new(device: &D, resources: &dyn ResourceLoader) -> SolidTileMonochromeProgram<D> {
|
fn new(device: &D, resources: &dyn ResourceLoader) -> SolidTileMonochromeProgram<D> {
|
||||||
let solid_tile_program = SolidTileProgram::new(device, "tile_solid_monochrome", resources);
|
let solid_tile_program = SolidTileProgram::new(device, "tile_solid_monochrome", resources);
|
||||||
let fill_color_uniform = device.get_uniform(&solid_tile_program.program, "FillColor");
|
let color_uniform = device.get_uniform(&solid_tile_program.program, "Color");
|
||||||
SolidTileMonochromeProgram {
|
SolidTileMonochromeProgram {
|
||||||
solid_tile_program,
|
solid_tile_program,
|
||||||
fill_color_uniform,
|
color_uniform,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1265,8 +1263,8 @@ where
|
||||||
D: Device,
|
D: Device,
|
||||||
{
|
{
|
||||||
alpha_tile_program: AlphaTileProgram<D>,
|
alpha_tile_program: AlphaTileProgram<D>,
|
||||||
fill_colors_texture_uniform: D::Uniform,
|
paint_texture_uniform: D::Uniform,
|
||||||
fill_colors_texture_size_uniform: D::Uniform,
|
paint_texture_size_uniform: D::Uniform,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> AlphaTileMulticolorProgram<D>
|
impl<D> AlphaTileMulticolorProgram<D>
|
||||||
|
@ -1275,14 +1273,14 @@ where
|
||||||
{
|
{
|
||||||
fn new(device: &D, resources: &dyn ResourceLoader) -> AlphaTileMulticolorProgram<D> {
|
fn new(device: &D, resources: &dyn ResourceLoader) -> AlphaTileMulticolorProgram<D> {
|
||||||
let alpha_tile_program = AlphaTileProgram::new(device, "tile_alpha_multicolor", resources);
|
let alpha_tile_program = AlphaTileProgram::new(device, "tile_alpha_multicolor", resources);
|
||||||
let fill_colors_texture_uniform =
|
let paint_texture_uniform =
|
||||||
device.get_uniform(&alpha_tile_program.program, "FillColorsTexture");
|
device.get_uniform(&alpha_tile_program.program, "PaintTexture");
|
||||||
let fill_colors_texture_size_uniform =
|
let paint_texture_size_uniform =
|
||||||
device.get_uniform(&alpha_tile_program.program, "FillColorsTextureSize");
|
device.get_uniform(&alpha_tile_program.program, "PaintTextureSize");
|
||||||
AlphaTileMulticolorProgram {
|
AlphaTileMulticolorProgram {
|
||||||
alpha_tile_program,
|
alpha_tile_program,
|
||||||
fill_colors_texture_uniform,
|
paint_texture_uniform,
|
||||||
fill_colors_texture_size_uniform,
|
paint_texture_size_uniform,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1292,7 +1290,7 @@ where
|
||||||
D: Device,
|
D: Device,
|
||||||
{
|
{
|
||||||
alpha_tile_program: AlphaTileProgram<D>,
|
alpha_tile_program: AlphaTileProgram<D>,
|
||||||
fill_color_uniform: D::Uniform,
|
color_uniform: D::Uniform,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> AlphaTileMonochromeProgram<D>
|
impl<D> AlphaTileMonochromeProgram<D>
|
||||||
|
@ -1301,10 +1299,10 @@ where
|
||||||
{
|
{
|
||||||
fn new(device: &D, resources: &dyn ResourceLoader) -> AlphaTileMonochromeProgram<D> {
|
fn new(device: &D, resources: &dyn ResourceLoader) -> AlphaTileMonochromeProgram<D> {
|
||||||
let alpha_tile_program = AlphaTileProgram::new(device, "tile_alpha_monochrome", resources);
|
let alpha_tile_program = AlphaTileProgram::new(device, "tile_alpha_monochrome", resources);
|
||||||
let fill_color_uniform = device.get_uniform(&alpha_tile_program.program, "FillColor");
|
let color_uniform = device.get_uniform(&alpha_tile_program.program, "Color");
|
||||||
AlphaTileMonochromeProgram {
|
AlphaTileMonochromeProgram {
|
||||||
alpha_tile_program,
|
alpha_tile_program,
|
||||||
fill_color_uniform,
|
color_uniform,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1375,7 +1373,14 @@ where
|
||||||
device.bind_vertex_array(&vertex_array);
|
device.bind_vertex_array(&vertex_array);
|
||||||
device.use_program(&postprocess_program.program);
|
device.use_program(&postprocess_program.program);
|
||||||
device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex);
|
device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex);
|
||||||
device.configure_float_vertex_attr(&position_attr, 2, VertexAttrType::U8, false, 0, 0, 0);
|
device.configure_vertex_attr(&position_attr, &VertexAttrDescriptor {
|
||||||
|
size: 2,
|
||||||
|
class: VertexAttrClass::Float,
|
||||||
|
attr_type: VertexAttrType::U8,
|
||||||
|
stride: 0,
|
||||||
|
offset: 0,
|
||||||
|
divisor: 0,
|
||||||
|
});
|
||||||
|
|
||||||
PostprocessVertexArray { vertex_array }
|
PostprocessVertexArray { vertex_array }
|
||||||
}
|
}
|
||||||
|
@ -1418,20 +1423,16 @@ where
|
||||||
device.bind_vertex_array(&vertex_array);
|
device.bind_vertex_array(&vertex_array);
|
||||||
device.use_program(&stencil_program.program);
|
device.use_program(&stencil_program.program);
|
||||||
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
|
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
|
||||||
device.configure_float_vertex_attr(
|
device.configure_vertex_attr(&position_attr, &VertexAttrDescriptor {
|
||||||
&position_attr,
|
size: 3,
|
||||||
3,
|
class: VertexAttrClass::Float,
|
||||||
VertexAttrType::F32,
|
attr_type: VertexAttrType::F32,
|
||||||
false,
|
stride: 4 * 4,
|
||||||
4 * 4,
|
offset: 0,
|
||||||
0,
|
divisor: 0,
|
||||||
0,
|
});
|
||||||
);
|
|
||||||
|
|
||||||
StencilVertexArray {
|
StencilVertexArray { vertex_array, vertex_buffer }
|
||||||
vertex_array,
|
|
||||||
vertex_buffer,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1487,7 +1488,14 @@ where
|
||||||
device.bind_vertex_array(&vertex_array);
|
device.bind_vertex_array(&vertex_array);
|
||||||
device.use_program(&reprojection_program.program);
|
device.use_program(&reprojection_program.program);
|
||||||
device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex);
|
device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex);
|
||||||
device.configure_float_vertex_attr(&position_attr, 2, VertexAttrType::U8, false, 0, 0, 0);
|
device.configure_vertex_attr(&position_attr, &VertexAttrDescriptor {
|
||||||
|
size: 2,
|
||||||
|
class: VertexAttrClass::Float,
|
||||||
|
attr_type: VertexAttrType::U8,
|
||||||
|
stride: 0,
|
||||||
|
offset: 0,
|
||||||
|
divisor: 0,
|
||||||
|
});
|
||||||
|
|
||||||
ReprojectionVertexArray { vertex_array }
|
ReprojectionVertexArray { vertex_array }
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
//! Packed data ready to be sent to the GPU.
|
//! Packed data ready to be sent to the GPU.
|
||||||
|
|
||||||
use crate::options::BoundingQuad;
|
use crate::options::BoundingQuad;
|
||||||
use crate::scene::ObjectShader;
|
|
||||||
use crate::tile_map::DenseTileMap;
|
use crate::tile_map::DenseTileMap;
|
||||||
use pathfinder_geometry::basic::line_segment::{LineSegmentU4, LineSegmentU8};
|
use pathfinder_geometry::basic::line_segment::{LineSegmentU4, LineSegmentU8};
|
||||||
|
use pathfinder_geometry::basic::point::Point2DI32;
|
||||||
use pathfinder_geometry::basic::rect::RectF32;
|
use pathfinder_geometry::basic::rect::RectF32;
|
||||||
use std::fmt::{Debug, Formatter, Result as DebugResult};
|
use std::fmt::{Debug, Formatter, Result as DebugResult};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -28,7 +28,7 @@ pub(crate) struct BuiltObject {
|
||||||
|
|
||||||
pub enum RenderCommand {
|
pub enum RenderCommand {
|
||||||
Start { path_count: usize, bounding_quad: BoundingQuad },
|
Start { path_count: usize, bounding_quad: BoundingQuad },
|
||||||
AddShaders(Vec<ObjectShader>),
|
AddPaintData(PaintData),
|
||||||
AddFills(Vec<FillBatchPrimitive>),
|
AddFills(Vec<FillBatchPrimitive>),
|
||||||
FlushFills,
|
FlushFills,
|
||||||
AlphaTile(Vec<AlphaTileBatchPrimitive>),
|
AlphaTile(Vec<AlphaTileBatchPrimitive>),
|
||||||
|
@ -36,6 +36,12 @@ pub enum RenderCommand {
|
||||||
Finish { build_time: Duration },
|
Finish { build_time: Duration },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PaintData {
|
||||||
|
pub size: Point2DI32,
|
||||||
|
pub texels: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct FillObjectPrimitive {
|
pub struct FillObjectPrimitive {
|
||||||
pub px: LineSegmentU4,
|
pub px: LineSegmentU4,
|
||||||
|
@ -66,6 +72,8 @@ pub struct FillBatchPrimitive {
|
||||||
pub struct SolidTileBatchPrimitive {
|
pub struct SolidTileBatchPrimitive {
|
||||||
pub tile_x: i16,
|
pub tile_x: i16,
|
||||||
pub tile_y: i16,
|
pub tile_y: i16,
|
||||||
|
pub origin_u: u16,
|
||||||
|
pub origin_v: u16,
|
||||||
pub object_index: u16,
|
pub object_index: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,14 +86,16 @@ pub struct AlphaTileBatchPrimitive {
|
||||||
pub backdrop: i8,
|
pub backdrop: i8,
|
||||||
pub object_index: u16,
|
pub object_index: u16,
|
||||||
pub tile_index: u16,
|
pub tile_index: u16,
|
||||||
|
pub origin_u: u16,
|
||||||
|
pub origin_v: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for RenderCommand {
|
impl Debug for RenderCommand {
|
||||||
fn fmt(&self, formatter: &mut Formatter) -> DebugResult {
|
fn fmt(&self, formatter: &mut Formatter) -> DebugResult {
|
||||||
match *self {
|
match *self {
|
||||||
RenderCommand::Start { .. } => write!(formatter, "Start"),
|
RenderCommand::Start { .. } => write!(formatter, "Start"),
|
||||||
RenderCommand::AddShaders(ref shaders) => {
|
RenderCommand::AddPaintData(ref paint_data) => {
|
||||||
write!(formatter, "AddShaders(x{})", shaders.len())
|
write!(formatter, "AddPaintData({}x{})", paint_data.size.x(), paint_data.size.y())
|
||||||
}
|
}
|
||||||
RenderCommand::AddFills(ref fills) => write!(formatter, "AddFills(x{})", fills.len()),
|
RenderCommand::AddFills(ref fills) => write!(formatter, "AddFills(x{})", fills.len()),
|
||||||
RenderCommand::FlushFills => write!(formatter, "FlushFills"),
|
RenderCommand::FlushFills => write!(formatter, "FlushFills"),
|
||||||
|
|
|
@ -17,6 +17,7 @@ pub mod concurrent;
|
||||||
pub mod gpu;
|
pub mod gpu;
|
||||||
pub mod gpu_data;
|
pub mod gpu_data;
|
||||||
pub mod options;
|
pub mod options;
|
||||||
|
pub mod paint;
|
||||||
pub mod post;
|
pub mod post;
|
||||||
pub mod scene;
|
pub mod scene;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
// pathfinder/renderer/src/paint.rs
|
||||||
|
//
|
||||||
|
// Copyright © 2019 The Pathfinder Project Developers.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
use crate::gpu_data::PaintData;
|
||||||
|
use crate::scene::Scene;
|
||||||
|
use pathfinder_geometry::basic::point::Point2DI32;
|
||||||
|
use pathfinder_geometry::color::ColorU;
|
||||||
|
|
||||||
|
const PAINT_TEXTURE_WIDTH: i32 = 256;
|
||||||
|
const PAINT_TEXTURE_HEIGHT: i32 = 256;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Paint {
|
||||||
|
pub color: ColorU,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
|
pub struct PaintId(pub u16);
|
||||||
|
|
||||||
|
impl Paint {
|
||||||
|
pub(crate) fn is_opaque(&self) -> bool {
|
||||||
|
self.color.a == 255
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scene {
|
||||||
|
pub fn build_paint_data(&self) -> PaintData {
|
||||||
|
let size = Point2DI32::new(PAINT_TEXTURE_WIDTH, PAINT_TEXTURE_HEIGHT);
|
||||||
|
let mut texels = vec![0; size.x() as usize * size.y() as usize * 4];
|
||||||
|
for (paint_index, paint) in self.paints.iter().enumerate() {
|
||||||
|
texels[paint_index * 4 + 0] = paint.color.r;
|
||||||
|
texels[paint_index * 4 + 1] = paint.color.g;
|
||||||
|
texels[paint_index * 4 + 2] = paint.color.b;
|
||||||
|
texels[paint_index * 4 + 3] = paint.color.a;
|
||||||
|
}
|
||||||
|
PaintData { size, texels }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn paint_id_to_tex_coords(paint_id: PaintId) -> Point2DI32 {
|
||||||
|
let tex_coords = Point2DI32::new(paint_id.0 as i32 % PAINT_TEXTURE_WIDTH,
|
||||||
|
paint_id.0 as i32 / PAINT_TEXTURE_WIDTH);
|
||||||
|
tex_coords.scale(256) + Point2DI32::new(128, 128)
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ use crate::builder::SceneBuilder;
|
||||||
use crate::concurrent::executor::Executor;
|
use crate::concurrent::executor::Executor;
|
||||||
use crate::options::{PreparedRenderOptions, PreparedRenderTransform};
|
use crate::options::{PreparedRenderOptions, PreparedRenderTransform};
|
||||||
use crate::options::{RenderCommandListener, RenderOptions};
|
use crate::options::{RenderCommandListener, RenderOptions};
|
||||||
|
use crate::paint::{Paint, PaintId};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use pathfinder_geometry::basic::point::Point2DF32;
|
use pathfinder_geometry::basic::point::Point2DF32;
|
||||||
use pathfinder_geometry::basic::rect::RectF32;
|
use pathfinder_geometry::basic::rect::RectF32;
|
||||||
|
@ -25,7 +26,7 @@ use std::io::{self, Write};
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Scene {
|
pub struct Scene {
|
||||||
pub(crate) paths: Vec<PathObject>,
|
pub(crate) paths: Vec<PathObject>,
|
||||||
paints: Vec<Paint>,
|
pub(crate) paints: Vec<Paint>,
|
||||||
paint_cache: HashMap<Paint, PaintId>,
|
paint_cache: HashMap<Paint, PaintId>,
|
||||||
bounds: RectF32,
|
bounds: RectF32,
|
||||||
view_box: RectF32,
|
view_box: RectF32,
|
||||||
|
@ -85,15 +86,6 @@ impl Scene {
|
||||||
self.view_box = new_view_box;
|
self.view_box = new_view_box;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_shaders(&self) -> Vec<ObjectShader> {
|
|
||||||
self.paths
|
|
||||||
.iter()
|
|
||||||
.map(|path_object| {
|
|
||||||
ObjectShader { fill_color: self.paints[path_object.paint.0 as usize].color }
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn apply_render_options(
|
pub(crate) fn apply_render_options(
|
||||||
&self,
|
&self,
|
||||||
original_outline: &Outline,
|
original_outline: &Outline,
|
||||||
|
@ -129,7 +121,7 @@ impl Scene {
|
||||||
};
|
};
|
||||||
if options.subpixel_aa_enabled {
|
if options.subpixel_aa_enabled {
|
||||||
transform = transform
|
transform = transform
|
||||||
.post_mul(&Transform2DF32::from_scale(&Point2DF32::new(3.0, 1.0)))
|
.post_mul(&Transform2DF32::from_scale(Point2DF32::new(3.0, 1.0)))
|
||||||
}
|
}
|
||||||
outline.transform(&transform);
|
outline.transform(&transform);
|
||||||
}
|
}
|
||||||
|
@ -225,17 +217,9 @@ impl PathObject {
|
||||||
pub fn outline(&self) -> &Outline {
|
pub fn outline(&self) -> &Outline {
|
||||||
&self.outline
|
&self.outline
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[inline]
|
||||||
pub struct Paint {
|
pub(crate) fn paint(&self) -> PaintId {
|
||||||
pub color: ColorU,
|
self.paint
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
|
||||||
pub struct PaintId(pub u16);
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
pub struct ObjectShader {
|
|
||||||
pub fill_color: ColorU,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
use crate::builder::SceneBuilder;
|
use crate::builder::SceneBuilder;
|
||||||
use crate::gpu_data::{AlphaTileBatchPrimitive, BuiltObject, TileObjectPrimitive};
|
use crate::gpu_data::{AlphaTileBatchPrimitive, BuiltObject, TileObjectPrimitive};
|
||||||
|
use crate::paint::{self, PaintId};
|
||||||
use crate::sorted_vector::SortedVector;
|
use crate::sorted_vector::SortedVector;
|
||||||
use pathfinder_geometry::basic::line_segment::LineSegmentF32;
|
use pathfinder_geometry::basic::line_segment::LineSegmentF32;
|
||||||
use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32};
|
use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32};
|
||||||
|
@ -29,7 +30,9 @@ pub(crate) struct Tiler<'a> {
|
||||||
builder: &'a SceneBuilder<'a>,
|
builder: &'a SceneBuilder<'a>,
|
||||||
outline: &'a Outline,
|
outline: &'a Outline,
|
||||||
pub built_object: BuiltObject,
|
pub built_object: BuiltObject,
|
||||||
|
paint_id: PaintId,
|
||||||
object_index: u16,
|
object_index: u16,
|
||||||
|
object_is_opaque: bool,
|
||||||
|
|
||||||
point_queue: SortedVector<QueuedEndpoint>,
|
point_queue: SortedVector<QueuedEndpoint>,
|
||||||
active_edges: SortedVector<ActiveEdge>,
|
active_edges: SortedVector<ActiveEdge>,
|
||||||
|
@ -43,6 +46,8 @@ impl<'a> Tiler<'a> {
|
||||||
outline: &'a Outline,
|
outline: &'a Outline,
|
||||||
view_box: RectF32,
|
view_box: RectF32,
|
||||||
object_index: u16,
|
object_index: u16,
|
||||||
|
paint_id: PaintId,
|
||||||
|
object_is_opaque: bool,
|
||||||
) -> Tiler<'a> {
|
) -> Tiler<'a> {
|
||||||
let bounds = outline
|
let bounds = outline
|
||||||
.bounds()
|
.bounds()
|
||||||
|
@ -55,6 +60,8 @@ impl<'a> Tiler<'a> {
|
||||||
outline,
|
outline,
|
||||||
built_object,
|
built_object,
|
||||||
object_index,
|
object_index,
|
||||||
|
paint_id,
|
||||||
|
object_is_opaque,
|
||||||
|
|
||||||
point_queue: SortedVector::new(),
|
point_queue: SortedVector::new(),
|
||||||
active_edges: SortedVector::new(),
|
active_edges: SortedVector::new(),
|
||||||
|
@ -107,18 +114,28 @@ impl<'a> Tiler<'a> {
|
||||||
let tile_coords = self
|
let tile_coords = self
|
||||||
.built_object
|
.built_object
|
||||||
.local_tile_index_to_coords(tile_index as u32);
|
.local_tile_index_to_coords(tile_index as u32);
|
||||||
|
|
||||||
if tile.is_solid() {
|
if tile.is_solid() {
|
||||||
if tile.backdrop != 0 {
|
// Blank tiles are always skipped.
|
||||||
self.builder.z_buffer.update(tile_coords, self.object_index);
|
if tile.backdrop == 0 {
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is a solid tile, poke it into the Z-buffer and stop here.
|
||||||
|
if self.object_is_opaque {
|
||||||
|
self.builder.z_buffer.update(tile_coords, self.object_index);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let origin_uv = paint::paint_id_to_tex_coords(self.paint_id);
|
||||||
|
|
||||||
let alpha_tile = AlphaTileBatchPrimitive::new(
|
let alpha_tile = AlphaTileBatchPrimitive::new(
|
||||||
tile_coords,
|
tile_coords,
|
||||||
tile.backdrop,
|
tile.backdrop,
|
||||||
self.object_index,
|
self.object_index,
|
||||||
tile.alpha_tile_index as u16,
|
tile.alpha_tile_index as u16,
|
||||||
|
origin_uv,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.built_object.alpha_tiles.push(alpha_tile);
|
self.built_object.alpha_tiles.push(alpha_tile);
|
||||||
|
@ -434,8 +451,7 @@ impl ActiveEdge {
|
||||||
// If necessary, draw initial line.
|
// If necessary, draw initial line.
|
||||||
if self.crossing.y() < segment.baseline.min_y() {
|
if self.crossing.y() < segment.baseline.min_y() {
|
||||||
let first_line_segment =
|
let first_line_segment =
|
||||||
LineSegmentF32::new(&self.crossing, &segment.baseline.upper_point())
|
LineSegmentF32::new(self.crossing, segment.baseline.upper_point()).orient(winding);
|
||||||
.orient(winding);
|
|
||||||
if self
|
if self
|
||||||
.process_line_segment(&first_line_segment, builder, built_object, tile_y)
|
.process_line_segment(&first_line_segment, builder, built_object, tile_y)
|
||||||
.is_some()
|
.is_some()
|
||||||
|
@ -522,7 +538,8 @@ impl AlphaTileBatchPrimitive {
|
||||||
fn new(tile_coords: Point2DI32,
|
fn new(tile_coords: Point2DI32,
|
||||||
backdrop: i8,
|
backdrop: i8,
|
||||||
object_index: u16,
|
object_index: u16,
|
||||||
tile_index: u16)
|
tile_index: u16,
|
||||||
|
origin_uv: Point2DI32)
|
||||||
-> AlphaTileBatchPrimitive {
|
-> AlphaTileBatchPrimitive {
|
||||||
AlphaTileBatchPrimitive {
|
AlphaTileBatchPrimitive {
|
||||||
tile_x_lo: (tile_coords.x() & 0xff) as u8,
|
tile_x_lo: (tile_coords.x() & 0xff) as u8,
|
||||||
|
@ -531,6 +548,8 @@ impl AlphaTileBatchPrimitive {
|
||||||
backdrop,
|
backdrop,
|
||||||
object_index,
|
object_index,
|
||||||
tile_index,
|
tile_index,
|
||||||
|
origin_u: origin_uv.x() as u16,
|
||||||
|
origin_v: origin_uv.y() as u16,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
//! Software occlusion culling.
|
//! Software occlusion culling.
|
||||||
|
|
||||||
use crate::gpu_data::SolidTileBatchPrimitive;
|
use crate::gpu_data::SolidTileBatchPrimitive;
|
||||||
|
use crate::paint;
|
||||||
|
use crate::scene::PathObject;
|
||||||
use crate::tile_map::DenseTileMap;
|
use crate::tile_map::DenseTileMap;
|
||||||
use crate::tiles;
|
use crate::tiles;
|
||||||
use pathfinder_geometry::basic::point::Point2DI32;
|
use pathfinder_geometry::basic::point::Point2DI32;
|
||||||
|
@ -54,7 +56,8 @@ impl ZBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_solid_tiles(&self, object_range: Range<u32>) -> Vec<SolidTileBatchPrimitive> {
|
pub fn build_solid_tiles(&self, paths: &[PathObject], object_range: Range<u32>)
|
||||||
|
-> Vec<SolidTileBatchPrimitive> {
|
||||||
let mut solid_tiles = vec![];
|
let mut solid_tiles = vec![];
|
||||||
for tile_index in 0..self.buffer.data.len() {
|
for tile_index in 0..self.buffer.data.len() {
|
||||||
let depth = self.buffer.data[tile_index].load(AtomicOrdering::Relaxed);
|
let depth = self.buffer.data[tile_index].load(AtomicOrdering::Relaxed);
|
||||||
|
@ -67,13 +70,27 @@ impl ZBuffer {
|
||||||
if object_index < object_range.start || object_index >= object_range.end {
|
if object_index < object_range.start || object_index >= object_range.end {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
solid_tiles.push(SolidTileBatchPrimitive {
|
|
||||||
tile_x: (tile_coords.x() + self.buffer.rect.min_x()) as i16,
|
let origin_uv = paint::paint_id_to_tex_coords(paths[object_index as usize].paint());
|
||||||
tile_y: (tile_coords.y() + self.buffer.rect.min_y()) as i16,
|
|
||||||
object_index: object_index as u16,
|
solid_tiles.push(SolidTileBatchPrimitive::new(tile_coords + self.buffer.rect.origin(),
|
||||||
});
|
object_index as u16,
|
||||||
|
origin_uv));
|
||||||
}
|
}
|
||||||
|
|
||||||
solid_tiles
|
solid_tiles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SolidTileBatchPrimitive {
|
||||||
|
fn new(tile_coords: Point2DI32, object_index: u16, origin_uv: Point2DI32)
|
||||||
|
-> SolidTileBatchPrimitive {
|
||||||
|
SolidTileBatchPrimitive {
|
||||||
|
tile_x: tile_coords.x() as i16,
|
||||||
|
tile_y: tile_coords.y() as i16,
|
||||||
|
object_index: object_index,
|
||||||
|
origin_u: origin_uv.x() as u16,
|
||||||
|
origin_v: origin_uv.y() as u16,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ in vec2 vTexCoord;
|
||||||
|
|
||||||
out vec4 oFragColor;
|
out vec4 oFragColor;
|
||||||
|
|
||||||
{{{include_post_gamma_correct}}}
|
{{include_post_gamma_correct}}
|
||||||
{{{include_post_convolve}}}
|
{{include_post_convolve}}
|
||||||
|
|
||||||
// Convolve horizontally in this pass.
|
// Convolve horizontally in this pass.
|
||||||
float sample1Tap(float offset) {
|
float sample1Tap(float offset) {
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
|
|
||||||
precision highp float;
|
precision highp float;
|
||||||
|
|
||||||
{{{include_tile_alpha_vertex}}}
|
{{include_tile_alpha_vertex}}
|
||||||
{{{include_tile_monochrome}}}
|
{{include_tile_monochrome}}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
computeVaryings();
|
computeVaryings();
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
|
|
||||||
precision highp float;
|
precision highp float;
|
||||||
|
|
||||||
{{{include_tile_alpha_vertex}}}
|
{{include_tile_alpha_vertex}}
|
||||||
{{{include_tile_multicolor}}}
|
{{include_tile_multicolor}}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
computeVaryings();
|
computeVaryings();
|
||||||
|
|
|
@ -16,14 +16,13 @@ uniform vec2 uViewBoxOrigin;
|
||||||
in vec2 aTessCoord;
|
in vec2 aTessCoord;
|
||||||
in uvec3 aTileOrigin;
|
in uvec3 aTileOrigin;
|
||||||
in int aBackdrop;
|
in int aBackdrop;
|
||||||
in uint aObject;
|
|
||||||
in uint aTileIndex;
|
in uint aTileIndex;
|
||||||
|
|
||||||
out vec2 vTexCoord;
|
out vec2 vTexCoord;
|
||||||
out float vBackdrop;
|
out float vBackdrop;
|
||||||
out vec4 vColor;
|
out vec4 vColor;
|
||||||
|
|
||||||
vec4 getFillColor(uint object);
|
vec4 getColor();
|
||||||
|
|
||||||
vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) {
|
vec2 computeTileOffset(uint tileIndex, float stencilTextureWidth) {
|
||||||
uint tilesPerRow = uint(stencilTextureWidth / uTileSize.x);
|
uint tilesPerRow = uint(stencilTextureWidth / uTileSize.x);
|
||||||
|
@ -35,11 +34,12 @@ void computeVaryings() {
|
||||||
vec2 origin = vec2(aTileOrigin.xy) + vec2(aTileOrigin.z & 15u, aTileOrigin.z >> 4u) * 256.0;
|
vec2 origin = vec2(aTileOrigin.xy) + vec2(aTileOrigin.z & 15u, aTileOrigin.z >> 4u) * 256.0;
|
||||||
vec2 pixelPosition = (origin + aTessCoord) * uTileSize + uViewBoxOrigin;
|
vec2 pixelPosition = (origin + aTessCoord) * uTileSize + uViewBoxOrigin;
|
||||||
vec2 position = (pixelPosition / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0);
|
vec2 position = (pixelPosition / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0);
|
||||||
vec2 texCoord = computeTileOffset(aTileIndex, uStencilTextureSize.x) + aTessCoord * uTileSize;
|
vec2 maskTexCoordOrigin = computeTileOffset(aTileIndex, uStencilTextureSize.x);
|
||||||
|
vec2 maskTexCoord = maskTexCoordOrigin + aTessCoord * uTileSize;
|
||||||
|
|
||||||
vTexCoord = texCoord / uStencilTextureSize;
|
vTexCoord = maskTexCoord / uStencilTextureSize;
|
||||||
vBackdrop = float(aBackdrop);
|
vBackdrop = float(aBackdrop);
|
||||||
vColor = getFillColor(aObject);
|
vColor = getColor();
|
||||||
gl_Position = vec4(position, 0.0, 1.0);
|
gl_Position = vec4(position, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
uniform vec4 uFillColor;
|
uniform vec4 uColor;
|
||||||
|
|
||||||
vec4 getFillColor(uint object) {
|
vec4 getColor() {
|
||||||
return uFillColor;
|
return uColor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,15 +8,11 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
uniform sampler2D uFillColorsTexture;
|
uniform sampler2D uPaintTexture;
|
||||||
uniform vec2 uFillColorsTextureSize;
|
uniform vec2 uPaintTextureSize;
|
||||||
|
|
||||||
vec2 computeFillColorTexCoord(uint object, vec2 textureSize) {
|
in vec2 aColorTexCoord;
|
||||||
uint width = uint(textureSize.x);
|
|
||||||
return (vec2(float(object % width), float(object / width)) + vec2(0.5)) / textureSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 getFillColor(uint object) {
|
vec4 getColor() {
|
||||||
vec2 colorTexCoord = computeFillColorTexCoord(object, uFillColorsTextureSize);
|
return texture(uPaintTexture, aColorTexCoord);
|
||||||
return texture(uFillColorsTexture, colorTexCoord);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
|
|
||||||
precision highp float;
|
precision highp float;
|
||||||
|
|
||||||
{{{include_tile_solid_vertex}}}
|
{{include_tile_solid_vertex}}
|
||||||
{{{include_tile_monochrome}}}
|
{{include_tile_monochrome}}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
computeVaryings();
|
computeVaryings();
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
|
|
||||||
precision highp float;
|
precision highp float;
|
||||||
|
|
||||||
{{{include_tile_solid_vertex}}}
|
{{include_tile_solid_vertex}}
|
||||||
{{{include_tile_multicolor}}}
|
{{include_tile_multicolor}}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
computeVaryings();
|
computeVaryings();
|
||||||
|
|
|
@ -14,16 +14,16 @@ uniform vec2 uViewBoxOrigin;
|
||||||
|
|
||||||
in vec2 aTessCoord;
|
in vec2 aTessCoord;
|
||||||
in vec2 aTileOrigin;
|
in vec2 aTileOrigin;
|
||||||
in uint aObject;
|
|
||||||
|
|
||||||
out vec4 vColor;
|
out vec4 vColor;
|
||||||
|
|
||||||
vec4 getFillColor(uint object);
|
vec4 getColor();
|
||||||
|
|
||||||
void computeVaryings() {
|
void computeVaryings() {
|
||||||
vec2 pixelPosition = (aTileOrigin + aTessCoord) * uTileSize + uViewBoxOrigin;
|
vec2 pixelPosition = (aTileOrigin + aTessCoord) * uTileSize + uViewBoxOrigin;
|
||||||
vec2 position = (pixelPosition / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0);
|
vec2 position = (pixelPosition / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0);
|
||||||
|
|
||||||
vColor = getFillColor(aObject);
|
vColor = getColor();
|
||||||
|
//vColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||||
gl_Position = vec4(position, 0.0, 1.0);
|
gl_Position = vec4(position, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,11 +173,6 @@ impl F32x4 {
|
||||||
pub fn concat_wz_yx(self, other: F32x4) -> F32x4 {
|
pub fn concat_wz_yx(self, other: F32x4) -> F32x4 {
|
||||||
F32x4([self[3], self[2], other[1], other[0]])
|
F32x4([self[3], self[2], other[1], other[0]])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn cross(&self, other: F32x4) -> F32x4 {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<usize> for F32x4 {
|
impl Index<usize> for F32x4 {
|
||||||
|
|
|
@ -20,13 +20,14 @@ use pathfinder_geometry::basic::transform2d::{Transform2DF32, Transform2DF32Path
|
||||||
use pathfinder_geometry::color::ColorU;
|
use pathfinder_geometry::color::ColorU;
|
||||||
use pathfinder_geometry::outline::Outline;
|
use pathfinder_geometry::outline::Outline;
|
||||||
use pathfinder_geometry::segment::{Segment, SegmentFlags};
|
use pathfinder_geometry::segment::{Segment, SegmentFlags};
|
||||||
use pathfinder_geometry::stroke::OutlineStrokeToFill;
|
use pathfinder_geometry::stroke::{LineCap, LineJoin, OutlineStrokeToFill, StrokeStyle};
|
||||||
use pathfinder_renderer::scene::{Paint, PathObject, Scene};
|
use pathfinder_renderer::paint::Paint;
|
||||||
|
use pathfinder_renderer::scene::{PathObject, Scene};
|
||||||
use std::fmt::{Display, Formatter, Result as FormatResult};
|
use std::fmt::{Display, Formatter, Result as FormatResult};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use usvg::{Color as SvgColor, Node, NodeExt, NodeKind, Paint as UsvgPaint};
|
use usvg::{Color as SvgColor, LineCap as UsvgLineCap, LineJoin as UsvgLineJoin, Node, NodeExt};
|
||||||
use usvg::{PathSegment as UsvgPathSegment, Rect as UsvgRect, Transform as UsvgTransform};
|
use usvg::{NodeKind, Opacity, Paint as UsvgPaint, PathSegment as UsvgPathSegment};
|
||||||
use usvg::{Tree, Visibility};
|
use usvg::{Rect as UsvgRect, Transform as UsvgTransform, Tree, Visibility};
|
||||||
|
|
||||||
const HAIRLINE_STROKE_WIDTH: f32 = 0.0333;
|
const HAIRLINE_STROKE_WIDTH: f32 = 0.0333;
|
||||||
|
|
||||||
|
@ -114,9 +115,11 @@ impl BuiltSVG {
|
||||||
}
|
}
|
||||||
NodeKind::Path(ref path) if path.visibility == Visibility::Visible => {
|
NodeKind::Path(ref path) if path.visibility == Visibility::Visible => {
|
||||||
if let Some(ref fill) = path.fill {
|
if let Some(ref fill) = path.fill {
|
||||||
let style = self
|
let style = self.scene.push_paint(&Paint::from_svg_paint(
|
||||||
.scene
|
&fill.paint,
|
||||||
.push_paint(&Paint::from_svg_paint(&fill.paint, &mut self.result_flags));
|
fill.opacity,
|
||||||
|
&mut self.result_flags,
|
||||||
|
));
|
||||||
|
|
||||||
let path = UsvgPathToSegments::new(path.segments.iter().cloned());
|
let path = UsvgPathToSegments::new(path.segments.iter().cloned());
|
||||||
let path = Transform2DF32PathIter::new(path, &transform);
|
let path = Transform2DF32PathIter::new(path, &transform);
|
||||||
|
@ -129,14 +132,20 @@ impl BuiltSVG {
|
||||||
if let Some(ref stroke) = path.stroke {
|
if let Some(ref stroke) = path.stroke {
|
||||||
let style = self.scene.push_paint(&Paint::from_svg_paint(
|
let style = self.scene.push_paint(&Paint::from_svg_paint(
|
||||||
&stroke.paint,
|
&stroke.paint,
|
||||||
|
stroke.opacity,
|
||||||
&mut self.result_flags,
|
&mut self.result_flags,
|
||||||
));
|
));
|
||||||
let stroke_width = f32::max(stroke.width.value() as f32, HAIRLINE_STROKE_WIDTH);
|
|
||||||
|
let stroke_style = StrokeStyle {
|
||||||
|
line_width: f32::max(stroke.width.value() as f32, HAIRLINE_STROKE_WIDTH),
|
||||||
|
line_cap: LineCap::from_usvg_line_cap(stroke.linecap),
|
||||||
|
line_join: LineJoin::from_usvg_line_join(stroke.linejoin),
|
||||||
|
};
|
||||||
|
|
||||||
let path = UsvgPathToSegments::new(path.segments.iter().cloned());
|
let path = UsvgPathToSegments::new(path.segments.iter().cloned());
|
||||||
let outline = Outline::from_segments(path);
|
let outline = Outline::from_segments(path);
|
||||||
|
|
||||||
let mut stroke_to_fill = OutlineStrokeToFill::new(outline, stroke_width);
|
let mut stroke_to_fill = OutlineStrokeToFill::new(outline, stroke_style);
|
||||||
stroke_to_fill.offset();
|
stroke_to_fill.offset();
|
||||||
let mut outline = stroke_to_fill.outline;
|
let mut outline = stroke_to_fill.outline;
|
||||||
outline.transform(&transform);
|
outline.transform(&transform);
|
||||||
|
@ -235,15 +244,17 @@ impl Display for BuildResultFlags {
|
||||||
}
|
}
|
||||||
|
|
||||||
trait PaintExt {
|
trait PaintExt {
|
||||||
fn from_svg_paint(svg_paint: &UsvgPaint, result_flags: &mut BuildResultFlags) -> Self;
|
fn from_svg_paint(svg_paint: &UsvgPaint, opacity: Opacity, result_flags: &mut BuildResultFlags)
|
||||||
|
-> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaintExt for Paint {
|
impl PaintExt for Paint {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_svg_paint(svg_paint: &UsvgPaint, result_flags: &mut BuildResultFlags) -> Paint {
|
fn from_svg_paint(svg_paint: &UsvgPaint, opacity: Opacity, result_flags: &mut BuildResultFlags)
|
||||||
|
-> Paint {
|
||||||
Paint {
|
Paint {
|
||||||
color: match *svg_paint {
|
color: match *svg_paint {
|
||||||
UsvgPaint::Color(color) => ColorU::from_svg_color(color),
|
UsvgPaint::Color(color) => ColorU::from_svg_color(color, opacity),
|
||||||
UsvgPaint::Link(_) => {
|
UsvgPaint::Link(_) => {
|
||||||
// TODO(pcwalton)
|
// TODO(pcwalton)
|
||||||
result_flags.insert(BuildResultFlags::UNSUPPORTED_LINK_PAINT);
|
result_flags.insert(BuildResultFlags::UNSUPPORTED_LINK_PAINT);
|
||||||
|
@ -313,8 +324,7 @@ where
|
||||||
}
|
}
|
||||||
UsvgPathSegment::LineTo { x, y } => {
|
UsvgPathSegment::LineTo { x, y } => {
|
||||||
let to = Point2DF32::new(x as f32, y as f32);
|
let to = Point2DF32::new(x as f32, y as f32);
|
||||||
let mut segment =
|
let mut segment = Segment::line(&LineSegmentF32::new(self.last_subpath_point, to));
|
||||||
Segment::line(&LineSegmentF32::new(&self.last_subpath_point, &to));
|
|
||||||
if self.just_moved {
|
if self.just_moved {
|
||||||
segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH);
|
segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH);
|
||||||
}
|
}
|
||||||
|
@ -334,8 +344,8 @@ where
|
||||||
let ctrl1 = Point2DF32::new(x2 as f32, y2 as f32);
|
let ctrl1 = Point2DF32::new(x2 as f32, y2 as f32);
|
||||||
let to = Point2DF32::new(x as f32, y as f32);
|
let to = Point2DF32::new(x as f32, y as f32);
|
||||||
let mut segment = Segment::cubic(
|
let mut segment = Segment::cubic(
|
||||||
&LineSegmentF32::new(&self.last_subpath_point, &to),
|
&LineSegmentF32::new(self.last_subpath_point, to),
|
||||||
&LineSegmentF32::new(&ctrl0, &ctrl1),
|
&LineSegmentF32::new(ctrl0, ctrl1),
|
||||||
);
|
);
|
||||||
if self.just_moved {
|
if self.just_moved {
|
||||||
segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH);
|
segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH);
|
||||||
|
@ -346,8 +356,8 @@ where
|
||||||
}
|
}
|
||||||
UsvgPathSegment::ClosePath => {
|
UsvgPathSegment::ClosePath => {
|
||||||
let mut segment = Segment::line(&LineSegmentF32::new(
|
let mut segment = Segment::line(&LineSegmentF32::new(
|
||||||
&self.last_subpath_point,
|
self.last_subpath_point,
|
||||||
&self.first_subpath_point,
|
self.first_subpath_point,
|
||||||
));
|
));
|
||||||
segment.flags.insert(SegmentFlags::CLOSES_SUBPATH);
|
segment.flags.insert(SegmentFlags::CLOSES_SUBPATH);
|
||||||
self.just_moved = false;
|
self.just_moved = false;
|
||||||
|
@ -359,17 +369,53 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
trait ColorUExt {
|
trait ColorUExt {
|
||||||
fn from_svg_color(svg_color: SvgColor) -> Self;
|
fn from_svg_color(svg_color: SvgColor, opacity: Opacity) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ColorUExt for ColorU {
|
impl ColorUExt for ColorU {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_svg_color(svg_color: SvgColor) -> ColorU {
|
fn from_svg_color(svg_color: SvgColor, opacity: Opacity) -> ColorU {
|
||||||
ColorU {
|
ColorU {
|
||||||
r: svg_color.red,
|
r: svg_color.red,
|
||||||
g: svg_color.green,
|
g: svg_color.green,
|
||||||
b: svg_color.blue,
|
b: svg_color.blue,
|
||||||
a: 255,
|
a: (opacity.value() * 255.0).round() as u8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait LineCapExt {
|
||||||
|
fn from_usvg_line_cap(usvg_line_cap: UsvgLineCap) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LineCapExt for LineCap {
|
||||||
|
#[inline]
|
||||||
|
fn from_usvg_line_cap(usvg_line_cap: UsvgLineCap) -> LineCap {
|
||||||
|
match usvg_line_cap {
|
||||||
|
UsvgLineCap::Butt => LineCap::Butt,
|
||||||
|
UsvgLineCap::Round => {
|
||||||
|
// TODO(pcwalton)
|
||||||
|
LineCap::Square
|
||||||
|
}
|
||||||
|
UsvgLineCap::Square => LineCap::Square,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait LineJoinExt {
|
||||||
|
fn from_usvg_line_join(usvg_line_join: UsvgLineJoin) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LineJoinExt for LineJoin {
|
||||||
|
#[inline]
|
||||||
|
fn from_usvg_line_join(usvg_line_join: UsvgLineJoin) -> LineJoin {
|
||||||
|
match usvg_line_join {
|
||||||
|
UsvgLineJoin::Miter => LineJoin::Miter,
|
||||||
|
UsvgLineJoin::Round => {
|
||||||
|
// TODO(pcwalton)
|
||||||
|
LineJoin::Miter
|
||||||
|
}
|
||||||
|
UsvgLineJoin::Bevel => LineJoin::Bevel,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,4 +17,4 @@ path = "../renderer"
|
||||||
|
|
||||||
[dependencies.skribo]
|
[dependencies.skribo]
|
||||||
git = "https://github.com/linebender/skribo.git"
|
git = "https://github.com/linebender/skribo.git"
|
||||||
rev = "f759c2d2c188b668bce998179f03b6319835353e"
|
rev = "a89e9ca99e0d6736ea1b7754517f4df14fd96a2b"
|
||||||
|
|
|
@ -16,8 +16,9 @@ use lyon_path::builder::{FlatPathBuilder, PathBuilder};
|
||||||
use pathfinder_geometry::basic::point::Point2DF32;
|
use pathfinder_geometry::basic::point::Point2DF32;
|
||||||
use pathfinder_geometry::basic::transform2d::Transform2DF32;
|
use pathfinder_geometry::basic::transform2d::Transform2DF32;
|
||||||
use pathfinder_geometry::outline::{Contour, Outline};
|
use pathfinder_geometry::outline::{Contour, Outline};
|
||||||
use pathfinder_geometry::stroke::OutlineStrokeToFill;
|
use pathfinder_geometry::stroke::{OutlineStrokeToFill, StrokeStyle};
|
||||||
use pathfinder_renderer::scene::{PaintId, PathObject, Scene};
|
use pathfinder_renderer::paint::PaintId;
|
||||||
|
use pathfinder_renderer::scene::{PathObject, Scene};
|
||||||
use skribo::{FontCollection, Layout, TextStyle};
|
use skribo::{FontCollection, Layout, TextStyle};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
@ -68,8 +69,8 @@ impl SceneExt for Scene {
|
||||||
font.outline(glyph_id, hinting_options, &mut outline_builder)?;
|
font.outline(glyph_id, hinting_options, &mut outline_builder)?;
|
||||||
let mut outline = outline_builder.build();
|
let mut outline = outline_builder.build();
|
||||||
|
|
||||||
if let TextRenderMode::Stroke(stroke_width) = render_mode {
|
if let TextRenderMode::Stroke(stroke_style) = render_mode {
|
||||||
let mut stroke_to_fill = OutlineStrokeToFill::new(outline, stroke_width);
|
let mut stroke_to_fill = OutlineStrokeToFill::new(outline, stroke_style);
|
||||||
stroke_to_fill.offset();
|
stroke_to_fill.offset();
|
||||||
outline = stroke_to_fill.outline;
|
outline = stroke_to_fill.outline;
|
||||||
}
|
}
|
||||||
|
@ -93,7 +94,7 @@ impl SceneExt for Scene {
|
||||||
let scale = style.size / (font.metrics().units_per_em as f32);
|
let scale = style.size / (font.metrics().units_per_em as f32);
|
||||||
let scale = Point2DF32::new(scale, -scale);
|
let scale = Point2DF32::new(scale, -scale);
|
||||||
let transform =
|
let transform =
|
||||||
Transform2DF32::from_scale(&scale).post_mul(transform).post_translate(offset);
|
Transform2DF32::from_scale(scale).post_mul(transform).post_translate(offset);
|
||||||
self.push_glyph(font,
|
self.push_glyph(font,
|
||||||
glyph.glyph_id,
|
glyph.glyph_id,
|
||||||
&transform,
|
&transform,
|
||||||
|
@ -122,7 +123,7 @@ impl SceneExt for Scene {
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
pub enum TextRenderMode {
|
pub enum TextRenderMode {
|
||||||
Fill,
|
Fill,
|
||||||
Stroke(f32),
|
Stroke(StrokeStyle),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OutlinePathBuilder {
|
struct OutlinePathBuilder {
|
||||||
|
@ -147,7 +148,7 @@ impl OutlinePathBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_point(&self, point: Point2D<f32>) -> Point2DF32 {
|
fn convert_point(&self, point: Point2D<f32>) -> Point2DF32 {
|
||||||
self.transform.transform_point(&Point2DF32::new(point.x, point.y))
|
self.transform.transform_point(Point2DF32::new(point.x, point.y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,8 @@ use pathfinder_geometry::basic::rect::RectI32;
|
||||||
use pathfinder_geometry::color::ColorU;
|
use pathfinder_geometry::color::ColorU;
|
||||||
use pathfinder_gpu::resources::ResourceLoader;
|
use pathfinder_gpu::resources::ResourceLoader;
|
||||||
use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, Device, Primitive};
|
use pathfinder_gpu::{BlendState, BufferData, BufferTarget, BufferUploadMode, Device, Primitive};
|
||||||
use pathfinder_gpu::{RenderState, UniformData, VertexAttrType};
|
use pathfinder_gpu::{RenderState, UniformData, VertexAttrClass};
|
||||||
|
use pathfinder_gpu::{VertexAttrDescriptor, VertexAttrType};
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -591,20 +592,22 @@ impl<D> DebugTextureVertexArray<D> where D: Device {
|
||||||
device.use_program(&debug_texture_program.program);
|
device.use_program(&debug_texture_program.program);
|
||||||
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
|
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
|
||||||
device.bind_buffer(&index_buffer, BufferTarget::Index);
|
device.bind_buffer(&index_buffer, BufferTarget::Index);
|
||||||
device.configure_float_vertex_attr(&position_attr,
|
device.configure_vertex_attr(&position_attr, &VertexAttrDescriptor {
|
||||||
2,
|
size: 2,
|
||||||
VertexAttrType::U16,
|
class: VertexAttrClass::Float,
|
||||||
false,
|
attr_type: VertexAttrType::U16,
|
||||||
DEBUG_TEXTURE_VERTEX_SIZE,
|
stride: DEBUG_TEXTURE_VERTEX_SIZE,
|
||||||
0,
|
offset: 0,
|
||||||
0);
|
divisor: 0,
|
||||||
device.configure_float_vertex_attr(&tex_coord_attr,
|
});
|
||||||
2,
|
device.configure_vertex_attr(&tex_coord_attr, &VertexAttrDescriptor {
|
||||||
VertexAttrType::U16,
|
size: 2,
|
||||||
false,
|
class: VertexAttrClass::Float,
|
||||||
DEBUG_TEXTURE_VERTEX_SIZE,
|
attr_type: VertexAttrType::U16,
|
||||||
4,
|
stride: DEBUG_TEXTURE_VERTEX_SIZE,
|
||||||
0);
|
offset: 4,
|
||||||
|
divisor: 0,
|
||||||
|
});
|
||||||
|
|
||||||
DebugTextureVertexArray { vertex_array, vertex_buffer, index_buffer }
|
DebugTextureVertexArray { vertex_array, vertex_buffer, index_buffer }
|
||||||
}
|
}
|
||||||
|
@ -626,13 +629,14 @@ impl<D> DebugSolidVertexArray<D> where D: Device {
|
||||||
device.use_program(&debug_solid_program.program);
|
device.use_program(&debug_solid_program.program);
|
||||||
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
|
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
|
||||||
device.bind_buffer(&index_buffer, BufferTarget::Index);
|
device.bind_buffer(&index_buffer, BufferTarget::Index);
|
||||||
device.configure_float_vertex_attr(&position_attr,
|
device.configure_vertex_attr(&position_attr, &VertexAttrDescriptor {
|
||||||
2,
|
size: 2,
|
||||||
VertexAttrType::U16,
|
class: VertexAttrClass::Float,
|
||||||
false,
|
attr_type: VertexAttrType::U16,
|
||||||
DEBUG_SOLID_VERTEX_SIZE,
|
stride: DEBUG_SOLID_VERTEX_SIZE,
|
||||||
0,
|
offset: 0,
|
||||||
0);
|
divisor: 0,
|
||||||
|
});
|
||||||
|
|
||||||
DebugSolidVertexArray { vertex_array, vertex_buffer, index_buffer }
|
DebugSolidVertexArray { vertex_array, vertex_buffer, index_buffer }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue