Auto merge of #365 - pcwalton:c-svg, r=pcwalton
Expose `pathfinder_svg` to the C API Closes #357.
This commit is contained in:
commit
265e781efa
|
@ -1698,6 +1698,8 @@ dependencies = [
|
||||||
"pathfinder_renderer 0.5.0",
|
"pathfinder_renderer 0.5.0",
|
||||||
"pathfinder_resources 0.5.0",
|
"pathfinder_resources 0.5.0",
|
||||||
"pathfinder_simd 0.5.0",
|
"pathfinder_simd 0.5.0",
|
||||||
|
"pathfinder_svg 0.5.0",
|
||||||
|
"usvg 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -12,6 +12,7 @@ font-kit = "0.6"
|
||||||
foreign-types = "0.3"
|
foreign-types = "0.3"
|
||||||
gl = "0.14"
|
gl = "0.14"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
usvg = "0.9"
|
||||||
|
|
||||||
[dependencies.pathfinder_canvas]
|
[dependencies.pathfinder_canvas]
|
||||||
features = ["pf-text"]
|
features = ["pf-text"]
|
||||||
|
@ -41,6 +42,9 @@ path = "../resources"
|
||||||
[dependencies.pathfinder_simd]
|
[dependencies.pathfinder_simd]
|
||||||
path = "../simd"
|
path = "../simd"
|
||||||
|
|
||||||
|
[dependencies.pathfinder_svg]
|
||||||
|
path = "../svg"
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
metal = "0.17"
|
metal = "0.17"
|
||||||
|
|
||||||
|
|
40
c/src/lib.rs
40
c/src/lib.rs
|
@ -34,11 +34,14 @@ use pathfinder_renderer::gpu::renderer::Renderer;
|
||||||
use pathfinder_renderer::options::{BuildOptions, RenderTransform};
|
use pathfinder_renderer::options::{BuildOptions, RenderTransform};
|
||||||
use pathfinder_renderer::scene::Scene;
|
use pathfinder_renderer::scene::Scene;
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
|
use pathfinder_svg::SVGScene;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::os::raw::{c_char, c_void};
|
use std::os::raw::{c_char, c_void};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::ptr;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
use usvg::{Options, Tree};
|
||||||
|
|
||||||
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
|
||||||
use metal::{self, CAMetalLayer, CoreAnimationLayerRef};
|
use metal::{self, CAMetalLayer, CoreAnimationLayerRef};
|
||||||
|
@ -204,6 +207,9 @@ pub type PFBuildOptionsRef = *mut BuildOptions;
|
||||||
pub type PFRenderTransformRef = *mut RenderTransform;
|
pub type PFRenderTransformRef = *mut RenderTransform;
|
||||||
pub type PFRendererLevel = u8;
|
pub type PFRendererLevel = u8;
|
||||||
|
|
||||||
|
// `svg`
|
||||||
|
pub type PFSVGSceneRef = *mut SVGScene;
|
||||||
|
|
||||||
// `canvas`
|
// `canvas`
|
||||||
|
|
||||||
/// This function internally adds a reference to the font context. Therefore, if you created the
|
/// This function internally adds a reference to the font context. Therefore, if you created the
|
||||||
|
@ -735,6 +741,40 @@ pub unsafe extern "C" fn PFSceneProxyDestroy(scene_proxy: PFSceneProxyRef) {
|
||||||
drop(Box::from_raw(scene_proxy))
|
drop(Box::from_raw(scene_proxy))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `svg`
|
||||||
|
|
||||||
|
/// Returns `NULL` on failure.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFSVGSceneCreateWithMemory(bytes: *const c_char, byte_len: usize)
|
||||||
|
-> PFSVGSceneRef {
|
||||||
|
let data = slice::from_raw_parts(bytes as *const _, byte_len);
|
||||||
|
let tree = match Tree::from_data(data, &Options::default()) {
|
||||||
|
Ok(tree) => tree,
|
||||||
|
Err(_) => return ptr::null_mut(),
|
||||||
|
};
|
||||||
|
let svg_scene = SVGScene::from_tree(&tree);
|
||||||
|
Box::into_raw(Box::new(svg_scene))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `NULL` on failure.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFSVGSceneCreateWithPath(path: *const c_char) -> PFSVGSceneRef {
|
||||||
|
let string = to_rust_string(&path, 0);
|
||||||
|
let path = PathBuf::from(string);
|
||||||
|
let tree = match Tree::from_file(path, &Options::default()) {
|
||||||
|
Ok(tree) => tree,
|
||||||
|
Err(_) => return ptr::null_mut(),
|
||||||
|
};
|
||||||
|
let svg_scene = SVGScene::from_tree(&tree);
|
||||||
|
Box::into_raw(Box::new(svg_scene))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destroys the SVG and returns the scene.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn PFSVGSceneIntoScene(svg: PFSVGSceneRef) -> PFSceneRef {
|
||||||
|
Box::into_raw(Box::new((*Box::from_raw(svg)).scene))
|
||||||
|
}
|
||||||
|
|
||||||
// Helpers for `canvas`
|
// Helpers for `canvas`
|
||||||
|
|
||||||
unsafe fn to_rust_string(ptr: &*const c_char, mut len: usize) -> &str {
|
unsafe fn to_rust_string(ptr: &*const c_char, mut len: usize) -> &str {
|
||||||
|
|
|
@ -42,7 +42,7 @@ use pathfinder_renderer::options::{BuildOptions, RenderTransform};
|
||||||
use pathfinder_renderer::paint::Paint;
|
use pathfinder_renderer::paint::Paint;
|
||||||
use pathfinder_renderer::scene::{DrawPath, RenderTarget, Scene};
|
use pathfinder_renderer::scene::{DrawPath, RenderTarget, Scene};
|
||||||
use pathfinder_resources::ResourceLoader;
|
use pathfinder_resources::ResourceLoader;
|
||||||
use pathfinder_svg::BuiltSVG;
|
use pathfinder_svg::SVGScene;
|
||||||
use pathfinder_ui::{MousePosition, UIEvent};
|
use pathfinder_ui::{MousePosition, UIEvent};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufWriter, Read};
|
use std::io::{BufWriter, Read};
|
||||||
|
@ -759,7 +759,7 @@ fn load_scene(resource_loader: &dyn ResourceLoader,
|
||||||
input_path: &SVGPath,
|
input_path: &SVGPath,
|
||||||
viewport_size: Vector2I,
|
viewport_size: Vector2I,
|
||||||
filter: Option<PatternFilter>)
|
filter: Option<PatternFilter>)
|
||||||
-> (BuiltSVG, Tree) {
|
-> (SVGScene, Tree) {
|
||||||
let mut data;
|
let mut data;
|
||||||
match *input_path {
|
match *input_path {
|
||||||
SVGPath::Default => data = resource_loader.slurp(DEFAULT_SVG_VIRTUAL_PATH).unwrap(),
|
SVGPath::Default => data = resource_loader.slurp(DEFAULT_SVG_VIRTUAL_PATH).unwrap(),
|
||||||
|
@ -778,7 +778,7 @@ fn load_scene(resource_loader: &dyn ResourceLoader,
|
||||||
// FIXME(pcwalton): Rework how transforms work in the demo. The transform affects the final
|
// FIXME(pcwalton): Rework how transforms work in the demo. The transform affects the final
|
||||||
// composite steps, breaking this approach.
|
// composite steps, breaking this approach.
|
||||||
fn build_svg_tree(tree: &Tree, viewport_size: Vector2I, filter: Option<PatternFilter>)
|
fn build_svg_tree(tree: &Tree, viewport_size: Vector2I, filter: Option<PatternFilter>)
|
||||||
-> BuiltSVG {
|
-> SVGScene {
|
||||||
let mut scene = Scene::new();
|
let mut scene = Scene::new();
|
||||||
let filter_info = filter.map(|filter| {
|
let filter_info = filter.map(|filter| {
|
||||||
let scale = match filter {
|
let scale = match filter {
|
||||||
|
@ -792,7 +792,7 @@ fn build_svg_tree(tree: &Tree, viewport_size: Vector2I, filter: Option<PatternFi
|
||||||
FilterInfo { filter, render_target_id, render_target_size }
|
FilterInfo { filter, render_target_id, render_target_size }
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut built_svg = BuiltSVG::from_tree_and_scene(&tree, scene);
|
let mut built_svg = SVGScene::from_tree_and_scene(&tree, scene);
|
||||||
if let Some(FilterInfo { filter, render_target_id, render_target_size }) = filter_info {
|
if let Some(FilterInfo { filter, render_target_id, render_target_size }) = filter_info {
|
||||||
let mut pattern = Pattern::from_render_target(render_target_id, render_target_size);
|
let mut pattern = Pattern::from_render_target(render_target_id, render_target_size);
|
||||||
pattern.set_filter(Some(filter));
|
pattern.set_filter(Some(filter));
|
||||||
|
@ -818,7 +818,7 @@ fn center_of_window(window_size: &WindowSize) -> Vector2F {
|
||||||
window_size.device_size().to_f32() * 0.5
|
window_size.device_size().to_f32() * 0.5
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_svg_building_message(built_svg: &BuiltSVG) -> String {
|
fn get_svg_building_message(built_svg: &SVGScene) -> String {
|
||||||
if built_svg.result_flags.is_empty() {
|
if built_svg.result_flags.is_empty() {
|
||||||
return String::new();
|
return String::new();
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ use pathfinder_geometry::transform3d::Transform4F32;
|
||||||
use pathfinder_geometry::transform3d::Perspective;
|
use pathfinder_geometry::transform3d::Perspective;
|
||||||
use pathfinder_gpu::Device;
|
use pathfinder_gpu::Device;
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
use pathfinder_svg::BuiltSVG;
|
use pathfinder_svg::SVGScene;
|
||||||
use pathfinder_renderer::scene::Scene;
|
use pathfinder_renderer::scene::Scene;
|
||||||
use pathfinder_renderer::builder::RenderTransform;
|
use pathfinder_renderer::builder::RenderTransform;
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ impl<D: Display> ImmersiveDemo<D> {
|
||||||
let options = Options::get();
|
let options = Options::get();
|
||||||
let svg_data = resources.slurp(DEFAULT_SVG_VIRTUAL_PATH)?;
|
let svg_data = resources.slurp(DEFAULT_SVG_VIRTUAL_PATH)?;
|
||||||
let tree = usvg::Tree::from_data(&svg_data[..], &usvg::Options::default())?;
|
let tree = usvg::Tree::from_data(&svg_data[..], &usvg::Options::default())?;
|
||||||
let svg = BuiltSVG::from_tree(tree);
|
let svg = SVGScene::from_tree(tree);
|
||||||
let svg_size = svg.scene.view_box.size();
|
let svg_size = svg.scene.view_box.size();
|
||||||
let scene_thread_proxy = SceneThreadProxy::new(svg.scene, options);
|
let scene_thread_proxy = SceneThreadProxy::new(svg.scene, options);
|
||||||
let _ = scene_thread_proxy.sender.send(MainToSceneMsg::SetDrawableSize(display.size()));
|
let _ = scene_thread_proxy.sender.send(MainToSceneMsg::SetDrawableSize(display.size()));
|
||||||
|
|
|
@ -48,7 +48,7 @@ use pathfinder_renderer::options::RenderTransform;
|
||||||
use pathfinder_resources::ResourceLoader;
|
use pathfinder_resources::ResourceLoader;
|
||||||
use pathfinder_resources::fs::FilesystemResourceLoader;
|
use pathfinder_resources::fs::FilesystemResourceLoader;
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
use pathfinder_svg::BuiltSVG;
|
use pathfinder_svg::SVGScene;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
@ -135,7 +135,7 @@ pub unsafe extern "C" fn magicleap_pathfinder_demo_load(app: *mut c_void, svg_fi
|
||||||
|
|
||||||
struct MagicLeapPathfinder {
|
struct MagicLeapPathfinder {
|
||||||
renderers: HashMap<(EGLSurface, EGLDisplay), Renderer<GLDevice>>,
|
renderers: HashMap<(EGLSurface, EGLDisplay), Renderer<GLDevice>>,
|
||||||
svgs: HashMap<String, BuiltSVG>,
|
svgs: HashMap<String, SVGScene>,
|
||||||
resources: FilesystemResourceLoader,
|
resources: FilesystemResourceLoader,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ pub unsafe extern "C" fn magicleap_pathfinder_render(pf: *mut c_void, options: *
|
||||||
let svg_filename = CStr::from_ptr(options.svg_filename).to_string_lossy();
|
let svg_filename = CStr::from_ptr(options.svg_filename).to_string_lossy();
|
||||||
let data = resources.slurp(&*svg_filename).unwrap();
|
let data = resources.slurp(&*svg_filename).unwrap();
|
||||||
let tree = Tree::from_data(&data, &UsvgOptions::default()).unwrap();
|
let tree = Tree::from_data(&data, &UsvgOptions::default()).unwrap();
|
||||||
BuiltSVG::from_tree(tree)
|
SVGScene::from_tree(tree)
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut width = 0;
|
let mut width = 0;
|
||||||
|
|
|
@ -37,7 +37,7 @@ use usvg::{Transform as UsvgTransform, Tree, Visibility};
|
||||||
|
|
||||||
const HAIRLINE_STROKE_WIDTH: f32 = 0.0333;
|
const HAIRLINE_STROKE_WIDTH: f32 = 0.0333;
|
||||||
|
|
||||||
pub struct BuiltSVG {
|
pub struct SVGScene {
|
||||||
pub scene: Scene,
|
pub scene: Scene,
|
||||||
pub result_flags: BuildResultFlags,
|
pub result_flags: BuildResultFlags,
|
||||||
pub clip_paths: HashMap<String, ClipPathId>,
|
pub clip_paths: HashMap<String, ClipPathId>,
|
||||||
|
@ -60,18 +60,18 @@ bitflags! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BuiltSVG {
|
impl SVGScene {
|
||||||
// TODO(pcwalton): Allow a global transform to be set.
|
// TODO(pcwalton): Allow a global transform to be set.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_tree(tree: &Tree) -> BuiltSVG {
|
pub fn from_tree(tree: &Tree) -> SVGScene {
|
||||||
BuiltSVG::from_tree_and_scene(tree, Scene::new())
|
SVGScene::from_tree_and_scene(tree, Scene::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(pcwalton): Allow a global transform to be set.
|
// TODO(pcwalton): Allow a global transform to be set.
|
||||||
pub fn from_tree_and_scene(tree: &Tree, scene: Scene) -> BuiltSVG {
|
pub fn from_tree_and_scene(tree: &Tree, scene: Scene) -> SVGScene {
|
||||||
// TODO(pcwalton): Maybe have a `SVGBuilder` type to hold the clip path IDs and other
|
// TODO(pcwalton): Maybe have a `SVGBuilder` type to hold the clip path IDs and other
|
||||||
// transient data separate from `BuiltSVG`?
|
// transient data separate from `SVGScene`?
|
||||||
let mut built_svg = BuiltSVG {
|
let mut built_svg = SVGScene {
|
||||||
scene,
|
scene,
|
||||||
result_flags: BuildResultFlags::empty(),
|
result_flags: BuildResultFlags::empty(),
|
||||||
clip_paths: HashMap::new(),
|
clip_paths: HashMap::new(),
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::fs::File;
|
||||||
use std::io::{Read, BufWriter};
|
use std::io::{Read, BufWriter};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use pathfinder_svg::BuiltSVG;
|
use pathfinder_svg::SVGScene;
|
||||||
use pathfinder_export::{Export, FileFormat};
|
use pathfinder_export::{Export, FileFormat};
|
||||||
use usvg::{Tree, Options};
|
use usvg::{Tree, Options};
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
File::open(input)?.read_to_end(&mut data)?;
|
File::open(input)?.read_to_end(&mut data)?;
|
||||||
let svg = BuiltSVG::from_tree(&Tree::from_data(&data, &Options::default()).unwrap());
|
let svg = SVGScene::from_tree(&Tree::from_data(&data, &Options::default()).unwrap());
|
||||||
|
|
||||||
let scene = &svg.scene;
|
let scene = &svg.scene;
|
||||||
let mut writer = BufWriter::new(File::create(&output)?);
|
let mut writer = BufWriter::new(File::create(&output)?);
|
||||||
|
|
Loading…
Reference in New Issue