diff --git a/c/Cargo.toml b/c/Cargo.toml index 69f7f4b4..7d8e428c 100644 --- a/c/Cargo.toml +++ b/c/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" authors = ["Patrick Walton "] edition = "2018" +[features] +metal = ["dep:core-foundation", "dep:io-surface", "dep:metal", "dep:pathfinder_metal"] + [lib] crate-type = ["staticlib", "cdylib"] name = "pathfinder" @@ -51,10 +54,18 @@ path = "../simd" [dependencies.pathfinder_svg] path = "../svg" -[target.'cfg(target_os = "macos")'.dependencies] -core-foundation = "0.6" -io-surface = "0.12" -metal = "0.18" +[target.'cfg(target_os = "macos")'.dependencies.core-foundation] +version = "0.6" +optional = true + +[target.'cfg(target_os = "macos")'.dependencies.io-surface] +version = "0.12" +optional = true + +[target.'cfg(target_os = "macos")'.dependencies.metal] +version = "0.18" +optional = true [target.'cfg(target_os = "macos")'.dependencies.pathfinder_metal] path = "../metal" +optional = true diff --git a/c/src/lib.rs b/c/src/lib.rs index efc9e3cc..06ecdaa1 100644 --- a/c/src/lib.rs +++ b/c/src/lib.rs @@ -38,19 +38,21 @@ use pathfinder_renderer::options::{BuildOptions, RenderTransform}; use pathfinder_renderer::scene::Scene; use pathfinder_simd::default::F32x4; use pathfinder_svg::SVGScene; +use std::convert::TryFrom; use std::ffi::CString; +use std::mem::{self, MaybeUninit}; use std::os::raw::{c_char, c_void}; use std::path::PathBuf; -use std::ptr::{self, NonNull}; +use std::ptr::NonNull; use std::slice; use std::str; use usvg::{Options, Tree}; -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +#[cfg(all(target_os = "macos", feature = "metal"))] use io_surface::IOSurfaceRef; -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +#[cfg(all(target_os = "macos", feature = "metal"))] use metal::{self, CoreAnimationDrawableRef, DeviceRef as NativeMetalDeviceRef}; -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +#[cfg(all(target_os = "macos", feature = "metal"))] use pathfinder_metal::MetalDevice; #[no_mangle] @@ -100,22 +102,40 @@ pub const PF_RENDERER_OPTIONS_FLAGS_SHOW_DEBUG_UI: u8 = 0x2; pub const PF_RENDERER_LEVEL_D3D9: u8 = 0x1; pub const PF_RENDERER_LEVEL_D3D11: u8 = 0x2; +pub const PF_OK: PFResult = PFResult(0); + // Types +/// `0` indicates success. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct PFResult(pub u32); + // External: `font-kit` -pub type FKHandleRef = *mut Handle; +pub type FKHandleRef = NonNull; // `canvas` -pub type PFCanvasRef = *mut CanvasRenderingContext2D; -pub type PFPathRef = *mut Path2D; -pub type PFCanvasFontContextRef = *mut CanvasFontContext; -pub type FKFontRef = NonNull; -pub type PFFillStyleRef = *mut FillStyle; +pub type PFCanvas = CanvasRenderingContext2D; +pub type PFCanvasRef = NonNull; + +pub type PFPath = Path2D; +pub type PFPathRef = NonNull; + +pub type PFCanvasFontContext = CanvasFontContext; +pub type PFCanvasFontContextRef = NonNull; + +pub type FKFont = Font; +pub type FKFontRef = NonNull; + +pub type PFFillStyle = FillStyle; +pub type PFFillStyleRef = NonNull; + pub type PFLineCap = u8; pub type PFLineJoin = u8; pub type PFArcDirection = u8; pub type PFTextAlign = u8; pub type PFTextBaseline = u8; + #[repr(C)] pub struct PFTextMetrics { pub width: f32, @@ -129,7 +149,8 @@ pub struct PFColorF { pub b: f32, pub a: f32, } -#[repr(C)] + +#[repr(C, align(4))] pub struct PFColorU { pub r: u8, pub g: u8, @@ -143,33 +164,39 @@ pub struct PFVector2F { pub x: f32, pub y: f32, } + #[repr(C)] pub struct PFVector2I { pub x: i32, pub y: i32, } + #[repr(C)] pub struct PFRectF { pub origin: PFVector2F, pub lower_right: PFVector2F, } + #[repr(C)] pub struct PFRectI { pub origin: PFVector2I, pub lower_right: PFVector2I, } + /// Row-major order. #[repr(C)] pub struct PFMatrix2x2F { pub m00: f32, pub m01: f32, pub m10: f32, pub m11: f32, } + /// Row-major order. #[repr(C)] pub struct PFTransform2F { pub matrix: PFMatrix2x2F, pub vector: PFVector2F, } + /// Row-major order. #[repr(C)] pub struct PFTransform4F { @@ -178,6 +205,7 @@ pub struct PFTransform4F { pub m20: f32, pub m21: f32, pub m22: f32, pub m23: f32, pub m30: f32, pub m31: f32, pub m32: f32, pub m33: f32, } + #[repr(C)] pub struct PFPerspective { pub transform: PFTransform4F, @@ -185,67 +213,157 @@ pub struct PFPerspective { } // `gl` -pub type PFGLDeviceRef = *mut GLDevice; +pub type PFGLDevice = GLDevice; +pub type PFGLDeviceRef = NonNull; pub type PFGLVersion = u8; pub type PFGLFunctionLoader = extern "C" fn(name: *const c_char, userdata: *mut c_void) -> *const c_void; // `gpu` -pub type PFGLDestFramebufferRef = *mut DestFramebuffer; -pub type PFGLRendererRef = *mut Renderer; -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] -pub type PFMetalDestFramebufferRef = *mut DestFramebuffer; -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] -pub type PFMetalRendererRef = *mut Renderer; +pub type PFGLRenderer = Renderer; +pub type PFGLRendererRef = NonNull; + +pub type PFGLDestFramebuffer = DestFramebuffer; +pub type PFGLDestFramebufferRef = NonNull; + +#[cfg(all(target_os = "macos", feature = "metal"))] +pub type PFMetalDestFramebuffer = DestFramebuffer; +#[cfg(all(target_os = "macos", feature = "metal"))] +pub type PFMetalDestFramebufferRef = NonNull; + +#[cfg(all(target_os = "macos", feature = "metal"))] +pub type PFMetalRenderer = Renderer; +#[cfg(all(target_os = "macos", feature = "metal"))] +pub type PFMetalRendererRef = NonNull; + // FIXME(pcwalton): Double-boxing is unfortunate. Remove this when `std::raw::TraitObject` is // stable? -pub type PFResourceLoaderRef = *mut ResourceLoaderWrapper; -pub struct ResourceLoaderWrapper(Box); +pub struct PFResourceLoader(Box); +pub type PFResourceLoaderRef = NonNull; // `metal` -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] -pub type PFMetalDeviceRef = *mut MetalDevice; +#[cfg(all(target_os = "macos", feature = "metal"))] +pub type PFMetalDeviceRef = NonNull; // `renderer` -pub type PFSceneRef = *mut Scene; -pub type PFSceneProxyRef = *mut SceneProxy; +pub type PFScene = Scene; +pub type PFSceneRef = NonNull; + +pub type PFSceneProxy = SceneProxy; +pub type PFSceneProxyRef = NonNull; + #[repr(C)] pub struct PFRendererMode { pub level: PFRendererLevel, } -pub type PFDestFramebufferRef = *mut c_void; + +pub type PFDestFramebufferRef = NonNull>; + #[repr(C)] -pub struct PFRendererOptions { - pub dest: PFDestFramebufferRef, +pub struct PFRendererOptions { + pub dest: DestFramebuffer, pub background_color: PFColorF, pub flags: PFRendererOptionsFlags, } + pub type PFRendererOptionsFlags = u8; -pub type PFBuildOptionsRef = *mut BuildOptions; -pub type PFRenderTransformRef = *mut RenderTransform; + +pub type PFBuildOptions = BuildOptions; +pub type PFBuildOptionsRef = NonNull; + +pub type PFRenderTransform = RenderTransform; +pub type PFRenderTransformRef = NonNull; + pub type PFRendererLevel = u8; // `svg` -pub type PFSVGSceneRef = *mut SVGScene; +pub type PFSVGScene = SVGScene; +pub type PFSVGSceneRef = NonNull; + +// type layouts + +#[repr(C)] +pub struct TypeLayout { + size: usize, + align: usize, +} + +impl TypeLayout { + pub const fn of() -> Self { + Self { + size: mem::size_of::(), + align: mem::align_of::(), + } + } +} + +#[no_mangle] +pub static TYPE_LAYOUT_FKFont: TypeLayout = TypeLayout::of::(); + +#[no_mangle] +pub static TYPE_LAYOUT_PFCanvas: TypeLayout = TypeLayout::of::(); + +#[no_mangle] +pub static TYPE_LAYOUT_PFCanvasFontContext: TypeLayout = TypeLayout::of::(); + +#[no_mangle] +pub static TYPE_LAYOUT_PFFillStyle: TypeLayout = TypeLayout::of::(); + +#[no_mangle] +pub static TYPE_LAYOUT_PFGLDestFramebuffer: TypeLayout = TypeLayout::of::(); + +#[no_mangle] +pub static TYPE_LAYOUT_PFGLDevice: TypeLayout = TypeLayout::of::(); + +#[no_mangle] +pub static TYPE_LAYOUT_PFGLRenderer: TypeLayout = TypeLayout::of::(); + +#[no_mangle] +pub static TYPE_LAYOUT_PFResourceLoader: TypeLayout = TypeLayout::of::(); + +#[no_mangle] +pub static TYPE_LAYOUT_PFScene: TypeLayout = TypeLayout::of::(); + +#[no_mangle] +pub static TYPE_LAYOUT_PFSceneProxy: TypeLayout = TypeLayout::of::(); + +#[cfg(all(target_os = "macos", feature = "metal"))] +#[no_mangle] +pub static TYPE_LAYOUT_PFMetalDestFramebuffer: TypeLayout = TypeLayout::of::(); + +#[no_mangle] +pub static TYPE_LAYOUT_PFinternalRenderTransform: TypeLayout = TypeLayout::of::(); + +#[no_mangle] +pub static TYPE_LAYOUT_PFinternalVector2F: TypeLayout = TypeLayout::of::(); // `canvas` /// This function internally adds a reference to the font context. Therefore, if you created the /// font context, you must release it yourself to avoid a leak. +/// +/// You must call [`PFCanvasSetSize`] before drawing with the canvas. You should also call that +/// function when the canvas size should change (i.e. on window resize). #[no_mangle] -pub unsafe extern "C" fn PFCanvasCreate(font_context: PFCanvasFontContextRef, - size: *const PFVector2F) - -> PFCanvasRef { - Box::into_raw(Box::new(Canvas::new((*size).to_rust()).get_context_2d((*font_context).clone()))) +pub unsafe extern "C" fn PFCanvasInit(canvas: &mut MaybeUninit, font_context: PFCanvasFontContextRef) -> PFResult { + canvas.write(Canvas::new().get_context_2d(font_context.as_ref().clone())); + PF_OK } #[no_mangle] -pub unsafe extern "C" fn PFCanvasDestroy(canvas: PFCanvasRef) { - drop(Box::from_raw(canvas)) +pub unsafe extern "C" fn PFCanvasDeinit(canvas: PFCanvasRef) { + canvas.drop_in_place(); } #[no_mangle] -pub unsafe extern "C" fn PFCanvasFontContextCreateWithSystemSource() -> PFCanvasFontContextRef { - Box::into_raw(Box::new(CanvasFontContext::from_system_source())) +pub extern "C" fn PFCanvasSetSize(canvas: &mut PFCanvas, size: PFVector2F) -> PFResult { + canvas.canvas_mut().set_size(size.to_rust()); + PF_OK +} + +#[no_mangle] +pub unsafe extern "C" fn PFCanvasFontContextInitWithSystemSource(context: PFCanvasFontContextRef) -> PFResult { + context.write(CanvasFontContext::from_system_source()); + PF_OK } /// Creates a Pathfinder font context from a set of `font-kit` fonts. @@ -253,184 +371,194 @@ pub unsafe extern "C" fn PFCanvasFontContextCreateWithSystemSource() -> PFCanvas /// Note that `font-kit` itself has a C API. You can use this to load fonts from memory with e.g. /// `FKHandleCreateWithMemory()`. #[no_mangle] -pub unsafe extern "C" fn PFCanvasFontContextCreateWithFonts(fonts: *const FKHandleRef, +pub unsafe extern "C" fn PFCanvasFontContextInitWithFonts(context: PFCanvasFontContextRef, + fonts: NonNull, font_count: usize) - -> PFCanvasFontContextRef { - let fonts = slice::from_raw_parts(fonts, font_count); - Box::into_raw(Box::new(CanvasFontContext::from_fonts(fonts.into_iter().map(|font| { - (**font).clone() - })))) + -> PFResult { + let fonts = NonNull::slice_from_raw_parts(fonts, font_count).as_ref(); + context.write(CanvasFontContext::from_fonts(fonts.into_iter().map(|font| { + font.as_ref().clone() + }))); + PF_OK } #[no_mangle] -pub unsafe extern "C" fn PFCanvasFontContextAddRef(font_context: PFCanvasFontContextRef) - -> PFCanvasFontContextRef { - Box::into_raw(Box::new((*font_context).clone())) +pub unsafe extern "C" fn PFCanvasFontContextDeinit(context: PFCanvasFontContextRef) { + context.drop_in_place(); } +/// This function clears the canvas after moving its scene into the supplied destination. #[no_mangle] -pub unsafe extern "C" fn PFCanvasFontContextRelease(font_context: PFCanvasFontContextRef) { - drop(Box::from_raw(font_context)) -} - -/// This function takes ownership of the supplied canvas and will automatically destroy it when -/// the scene is destroyed. -#[no_mangle] -pub unsafe extern "C" fn PFCanvasCreateScene(canvas: PFCanvasRef) -> PFSceneRef { - Box::into_raw(Box::new(Box::from_raw(canvas).into_canvas().into_scene())) +pub unsafe extern "C" fn PFCanvasTakeScene(mut canvas: PFCanvasRef, scene_dst: PFSceneRef) { + scene_dst.write(canvas.as_mut().canvas_mut().take_scene()); } // Extensions #[no_mangle] -pub unsafe extern "C" fn PFCanvasClear(canvas: PFCanvasRef) { - (*canvas).clear() +pub unsafe extern "C" fn PFCanvasClear(mut canvas: PFCanvasRef) { + canvas.as_mut().clear() } // Drawing rectangles #[no_mangle] -pub unsafe extern "C" fn PFCanvasFillRect(canvas: PFCanvasRef, rect: *const PFRectF) { - (*canvas).fill_rect((*rect).to_rust()) +pub unsafe extern "C" fn PFCanvasFillRect(mut canvas: PFCanvasRef, rect: PFRectF) { + canvas.as_mut().fill_rect(rect.to_rust()) } #[no_mangle] -pub unsafe extern "C" fn PFCanvasStrokeRect(canvas: PFCanvasRef, rect: *const PFRectF) { - (*canvas).stroke_rect((*rect).to_rust()) +pub unsafe extern "C" fn PFCanvasStrokeRect(mut canvas: PFCanvasRef, rect: PFRectF) { + canvas.as_mut().stroke_rect(rect.to_rust()) } #[no_mangle] -pub unsafe extern "C" fn PFCanvasClearRect(canvas: PFCanvasRef, rect: *const PFRectF) { - (*canvas).clear_rect((*rect).to_rust()) +pub unsafe extern "C" fn PFCanvasClearRect(mut canvas: PFCanvasRef, rect: PFRectF) { + canvas.as_mut().clear_rect(rect.to_rust()) } // Drawing text #[no_mangle] -pub unsafe extern "C" fn PFCanvasFillText(canvas: PFCanvasRef, +pub unsafe extern "C" fn PFCanvasFillText(mut canvas: PFCanvasRef, string: *const c_char, string_len: usize, - position: *const PFVector2F) { - (*canvas).fill_text(to_rust_string(&string, string_len), (*position).to_rust()) + position: PFVector2F) -> PFResult { + match canvas.as_mut().fill_text(to_rust_string(&string, string_len), position.to_rust()) { + Ok(()) => PF_OK, + Err(e) => { + log::error!("Failed to fill text on canvas: {e:?}"); + PFResult(1) + } + } } #[no_mangle] -pub unsafe extern "C" fn PFCanvasStrokeText(canvas: PFCanvasRef, +pub unsafe extern "C" fn PFCanvasStrokeText(mut canvas: PFCanvasRef, string: *const c_char, string_len: usize, - position: *const PFVector2F) { - (*canvas).stroke_text(to_rust_string(&string, string_len), (*position).to_rust()) + position: PFVector2F) -> PFResult { + match canvas.as_mut().stroke_text(to_rust_string(&string, string_len), position.to_rust()) { + Ok(()) => PF_OK, + Err(e) => { + log::error!("Failed to fill text on canvas: {e:?}"); + PFResult(1) + } + } } #[no_mangle] pub unsafe extern "C" fn PFCanvasMeasureText(canvas: PFCanvasRef, string: *const c_char, - string_len: usize, - out_text_metrics: NonNull) { - out_text_metrics.write((*canvas).measure_text(to_rust_string(&string, string_len)).to_c()) + string_len: usize) -> PFTextMetrics { + canvas.as_ref().measure_text(to_rust_string(&string, string_len)).to_c() } #[no_mangle] -pub unsafe extern "C" fn PFCanvasSetLineWidth(canvas: PFCanvasRef, new_line_width: f32) { - (*canvas).set_line_width(new_line_width) +pub unsafe extern "C" fn PFCanvasSetLineWidth(mut canvas: PFCanvasRef, new_line_width: f32) { + canvas.as_mut().set_line_width(new_line_width) } #[no_mangle] -pub unsafe extern "C" fn PFCanvasSetLineCap(canvas: PFCanvasRef, new_line_cap: PFLineCap) { - (*canvas).set_line_cap(match new_line_cap { +pub unsafe extern "C" fn PFCanvasSetLineCap(mut canvas: PFCanvasRef, new_line_cap: PFLineCap) { + canvas.as_mut().set_line_cap(match new_line_cap { PF_LINE_CAP_BUTT => LineCap::Butt, PF_LINE_CAP_SQUARE => LineCap::Square, PF_LINE_CAP_ROUND => LineCap::Round, - _ => panic!("Invalid Pathfinder line cap style!"), + _ => panic!("Invalid Pathfinder line cap style: {}", new_line_cap), }); } #[no_mangle] -pub unsafe extern "C" fn PFCanvasSetLineJoin(canvas: PFCanvasRef, new_line_join: PFLineJoin) { - (*canvas).set_line_join(match new_line_join { +pub unsafe extern "C" fn PFCanvasSetLineJoin(mut canvas: PFCanvasRef, new_line_join: PFLineJoin) { + canvas.as_mut().set_line_join(match new_line_join { PF_LINE_JOIN_MITER => LineJoin::Miter, PF_LINE_JOIN_BEVEL => LineJoin::Bevel, PF_LINE_JOIN_ROUND => LineJoin::Round, - _ => panic!("Invalid Pathfinder line join style!"), + _ => panic!("Invalid Pathfinder line join style: {}", new_line_join), }); } #[no_mangle] -pub unsafe extern "C" fn PFCanvasSetMiterLimit(canvas: PFCanvasRef, new_miter_limit: f32) { - (*canvas).set_miter_limit(new_miter_limit); +pub unsafe extern "C" fn PFCanvasSetMiterLimit(mut canvas: PFCanvasRef, new_miter_limit: f32) { + canvas.as_mut().set_miter_limit(new_miter_limit); } #[no_mangle] -pub unsafe extern "C" fn PFCanvasSetLineDash(canvas: PFCanvasRef, +pub unsafe extern "C" fn PFCanvasSetLineDash(mut canvas: PFCanvasRef, new_line_dashes: *const f32, new_line_dash_count: usize) { - (*canvas).set_line_dash(slice::from_raw_parts(new_line_dashes, new_line_dash_count).to_vec()) + canvas.as_mut().set_line_dash(slice::from_raw_parts(new_line_dashes, new_line_dash_count).to_vec()) } #[no_mangle] -pub unsafe extern "C" fn PFCanvasSetTransform(canvas: PFCanvasRef, +pub unsafe extern "C" fn PFCanvasSetTransform(mut canvas: PFCanvasRef, transform: *const PFTransform2F) { - (*canvas).set_transform(&(*transform).to_rust()); + canvas.as_mut().set_transform(&(*transform).to_rust()); } #[no_mangle] -pub unsafe extern "C" fn PFCanvasResetTransform(canvas: PFCanvasRef) { - (*canvas).reset_transform(); +pub unsafe extern "C" fn PFCanvasResetTransform(mut canvas: PFCanvasRef) { + canvas.as_mut().reset_transform(); } #[no_mangle] -pub unsafe extern "C" fn PFCanvasSave(canvas: PFCanvasRef) { - (*canvas).save(); +pub unsafe extern "C" fn PFCanvasSave(mut canvas: PFCanvasRef) { + canvas.as_mut().save(); } #[no_mangle] -pub unsafe extern "C" fn PFCanvasRestore(canvas: PFCanvasRef) { - (*canvas).restore(); +pub unsafe extern "C" fn PFCanvasRestore(mut canvas: PFCanvasRef) { + canvas.as_mut().restore(); } #[no_mangle] -pub unsafe extern "C" fn PFCanvasSetLineDashOffset(canvas: PFCanvasRef, new_offset: f32) { - (*canvas).set_line_dash_offset(new_offset) +pub unsafe extern "C" fn PFCanvasSetLineDashOffset(mut canvas: PFCanvasRef, new_offset: f32) { + canvas.as_mut().set_line_dash_offset(new_offset) } #[no_mangle] pub unsafe extern "C" fn PFCanvasFontContextGetFontByPostScriptName(context: PFCanvasFontContextRef, postscript_name: *const c_char, - postscript_name_len: usize) -> Option { + postscript_name_len: usize, + font_dst: FKFontRef) -> PFResult { let name = to_rust_string(&postscript_name, postscript_name_len); - match (*context).get_font_by_postscript_name(name) { - Ok(font) => NonNull::new(Box::into_raw(Box::new(font))), + match context.as_ref().get_font_by_postscript_name(name) { + Ok(font) => { + font_dst.write(font); + PF_OK + } Err(e) => { log::error!("Failed to get font {:?}: {:?}", name, e); - None + PFResult(1) } } } #[no_mangle] -pub unsafe extern "C" fn FKFontDestroy(font: FKFontRef) { - drop(Box::from_raw(font.as_ptr())) +pub unsafe extern "C" fn FKFontDeinit(font: FKFontRef) { + font.drop_in_place(); } #[no_mangle] -pub unsafe extern "C" fn PFCanvasSetFont(canvas: PFCanvasRef, font: FKFontRef) -> i32 { - match (*canvas).set_font(font.as_ref().clone()) { - Ok(_) => 0, +pub unsafe extern "C" fn PFCanvasSetFont(mut canvas: PFCanvasRef, font: FKFontRef) -> PFResult { + match canvas.as_mut().set_font(font.as_ref().clone()) { + Ok(_) => PF_OK, Err(e) => { log::error!("Failed to set font: {:?}", e); - 1 + PFResult(1) } } } #[no_mangle] -pub unsafe extern "C" fn PFCanvasSetFontSize(canvas: PFCanvasRef, new_font_size: f32) { - (*canvas).set_font_size(new_font_size) +pub unsafe extern "C" fn PFCanvasSetFontSize(mut canvas: PFCanvasRef, new_font_size: f32) { + canvas.as_mut().set_font_size(new_font_size) } #[no_mangle] -pub unsafe extern "C" fn PFCanvasSetTextAlign(canvas: PFCanvasRef, new_text_align: PFTextAlign) { - (*canvas).set_text_align(match new_text_align { +pub unsafe extern "C" fn PFCanvasSetTextAlign(mut canvas: PFCanvasRef, new_text_align: PFTextAlign) { + canvas.as_mut().set_text_align(match new_text_align { PF_TEXT_ALIGN_CENTER => TextAlign::Center, PF_TEXT_ALIGN_RIGHT => TextAlign::Right, _ => TextAlign::Left, @@ -438,89 +566,89 @@ pub unsafe extern "C" fn PFCanvasSetTextAlign(canvas: PFCanvasRef, new_text_alig } #[no_mangle] -pub unsafe extern "C" fn PFCanvasSetTextBaseline(canvas: PFCanvasRef, - new_text_baseline: PFTextBaseline) { - (*canvas).set_text_baseline(match new_text_baseline { +pub unsafe extern "C" fn PFCanvasSetTextBaseline(mut canvas: PFCanvasRef, + new_text_baseline: PFTextBaseline) -> PFResult { + canvas.as_mut().set_text_baseline(match new_text_baseline { PF_TEXT_BASELINE_ALPHABETIC => TextBaseline::Alphabetic, PF_TEXT_BASELINE_TOP => TextBaseline::Top, PF_TEXT_BASELINE_HANGING => TextBaseline::Hanging, PF_TEXT_BASELINE_MIDDLE => TextBaseline::Middle, PF_TEXT_BASELINE_IDEOGRAPHIC => TextBaseline::Ideographic, - _ => TextBaseline::Bottom, + PF_TEXT_BASELINE_BOTTOM => TextBaseline::Bottom, + _ => return PFResult(1), }); + PF_OK } #[no_mangle] -pub unsafe extern "C" fn PFCanvasSetFillStyle(canvas: PFCanvasRef, fill_style: PFFillStyleRef) { +pub unsafe extern "C" fn PFCanvasSetFillStyle(mut canvas: PFCanvasRef, fill_style: PFFillStyleRef) { // FIXME(pcwalton): Avoid the copy? - (*canvas).set_fill_style((*fill_style).clone()) + canvas.as_mut().set_fill_style(fill_style.as_ref().clone()) } #[no_mangle] -pub unsafe extern "C" fn PFCanvasSetStrokeStyle(canvas: PFCanvasRef, +pub unsafe extern "C" fn PFCanvasSetStrokeStyle(mut canvas: PFCanvasRef, stroke_style: PFFillStyleRef) { // FIXME(pcwalton): Avoid the copy? - (*canvas).set_stroke_style((*stroke_style).clone()) + canvas.as_mut().set_stroke_style(stroke_style.as_ref().clone()) } -/// This function automatically destroys the path. If you wish to use the path again, clone it -/// first. +/// This function deinitializes the path. If you wish to use the path again, clone it first. #[no_mangle] -pub unsafe extern "C" fn PFCanvasFillPath(canvas: PFCanvasRef, path: PFPathRef) { +pub unsafe extern "C" fn PFCanvasFillPath(mut canvas: PFCanvasRef, path: PFPathRef) { // TODO(pcwalton): Expose fill rules to the C API. - (*canvas).fill_path(*Box::from_raw(path), FillRule::Winding) + canvas.as_mut().fill_path(path.read(), FillRule::Winding) } -/// This function automatically destroys the path. If you wish to use the path again, clone it -/// first. +/// This function deinitializes the path. If you wish to use the path again, clone it first. #[no_mangle] -pub unsafe extern "C" fn PFCanvasStrokePath(canvas: PFCanvasRef, path: PFPathRef) { - (*canvas).stroke_path(*Box::from_raw(path)) +pub unsafe extern "C" fn PFCanvasStrokePath(mut canvas: PFCanvasRef, path: PFPathRef) { + canvas.as_mut().stroke_path(path.read()) } #[no_mangle] -pub unsafe extern "C" fn PFPathCreate() -> PFPathRef { - Box::into_raw(Box::new(Path2D::new())) +pub unsafe extern "C" fn PFPathInit(path: PFPathRef) { + path.write(Path2D::new()); } #[no_mangle] -pub unsafe extern "C" fn PFPathDestroy(path: PFPathRef) { - drop(Box::from_raw(path)) +pub unsafe extern "C" fn PFPathDeinit(path: PFPathRef) { + path.drop_in_place(); } #[no_mangle] -pub unsafe extern "C" fn PFPathClone(path: PFPathRef) -> PFPathRef { - Box::into_raw(Box::new((*path).clone())) +pub unsafe extern "C" fn PFPathClone(path_src: PFPathRef, path_dst: PFPathRef) { + path_dst.write(path_src.as_ref().clone()); } #[no_mangle] -pub unsafe extern "C" fn PFPathMoveTo(path: PFPathRef, to: *const PFVector2F) { - (*path).move_to((*to).to_rust()) +pub unsafe extern "C" fn PFPathMoveTo(mut path: PFPathRef, to: PFVector2F) { + path.as_mut().move_to(to.to_rust()) } #[no_mangle] -pub unsafe extern "C" fn PFPathLineTo(path: PFPathRef, to: *const PFVector2F) { - (*path).line_to((*to).to_rust()) +pub unsafe extern "C" fn PFPathLineTo(mut path: PFPathRef, to: PFVector2F) { + path.as_mut().line_to(to.to_rust()) } #[no_mangle] -pub unsafe extern "C" fn PFPathQuadraticCurveTo(path: PFPathRef, - ctrl: *const PFVector2F, - to: *const PFVector2F) { - (*path).quadratic_curve_to((*ctrl).to_rust(), (*to).to_rust()) +pub unsafe extern "C" fn PFPathQuadraticCurveTo(mut path: PFPathRef, + ctrl: PFVector2F, + to: PFVector2F) { + path.as_mut().quadratic_curve_to(ctrl.to_rust(), to.to_rust()) } #[no_mangle] -pub unsafe extern "C" fn PFPathBezierCurveTo(path: PFPathRef, - ctrl0: *const PFVector2F, - ctrl1: *const PFVector2F, - to: *const PFVector2F) { - (*path).bezier_curve_to((*ctrl0).to_rust(), (*ctrl1).to_rust(), (*to).to_rust()) +pub unsafe extern "C" fn PFPathBezierCurveTo(mut path: PFPathRef, + ctrl0: PFVector2F, + ctrl1: PFVector2F, + to: PFVector2F) { + path.as_mut().bezier_curve_to(ctrl0.to_rust(), ctrl1.to_rust(), to.to_rust()) } #[no_mangle] -pub unsafe extern "C" fn PFPathArc(path: PFPathRef, - center: *const PFVector2F, +pub unsafe extern "C" fn PFPathArc(mut path: PFPathRef, + center: PFVector2F, radius: f32, start_angle: f32, end_angle: f32, @@ -528,74 +656,78 @@ pub unsafe extern "C" fn PFPathArc(path: PFPathRef, let direction = match direction { PF_ARC_DIRECTION_CW => ArcDirection::CW, PF_ARC_DIRECTION_CCW => ArcDirection::CCW, - _ => panic!("Invalid Pathfinder arc direction!"), + _ => panic!("Invalid Pathfinder arc direction: {}", direction), }; - (*path).arc((*center).to_rust(), radius, start_angle, end_angle, direction) + path.as_mut().arc(center.to_rust(), radius, start_angle, end_angle, direction) } #[no_mangle] -pub unsafe extern "C" fn PFPathArcTo(path: PFPathRef, - ctrl: *const PFVector2F, - to: *const PFVector2F, +pub unsafe extern "C" fn PFPathArcTo(mut path: PFPathRef, + ctrl: PFVector2F, + to: PFVector2F, radius: f32) { - (*path).arc_to((*ctrl).to_rust(), (*to).to_rust(), radius) + path.as_mut().arc_to(ctrl.to_rust(), to.to_rust(), radius) } #[no_mangle] -pub unsafe extern "C" fn PFPathRect(path: PFPathRef, rect: *const PFRectF) { - (*path).rect((*rect).to_rust()) +pub unsafe extern "C" fn PFPathRect(mut path: PFPathRef, rect: PFRectF) { + path.as_mut().rect(rect.to_rust()) } #[no_mangle] -pub unsafe extern "C" fn PFPathEllipse(path: PFPathRef, - center: *const PFVector2F, - axes: *const PFVector2F, +pub unsafe extern "C" fn PFPathEllipse(mut path: PFPathRef, + center: PFVector2F, + axes: PFVector2F, rotation: f32, start_angle: f32, end_angle: f32) { - (*path).ellipse((*center).to_rust(), (*axes).to_rust(), rotation, start_angle, end_angle) + path.as_mut().ellipse(center.to_rust(), axes.to_rust(), rotation, start_angle, end_angle) } #[no_mangle] -pub unsafe extern "C" fn PFPathClosePath(path: PFPathRef) { - (*path).close_path() +pub unsafe extern "C" fn PFPathClosePath(mut path: PFPathRef) { + path.as_mut().close_path() } #[no_mangle] -pub unsafe extern "C" fn PFFillStyleCreateColor(color: *const PFColorU) -> PFFillStyleRef { - Box::into_raw(Box::new(FillStyle::Color((*color).to_rust()))) +pub unsafe extern "C" fn PFFillStyleInitColor(fill_style: PFFillStyleRef, color: PFColorU) -> PFResult { + fill_style.write(FillStyle::Color(color.to_rust())); + PF_OK } #[no_mangle] -pub unsafe extern "C" fn PFFillStyleDestroy(fill_style: PFFillStyleRef) { - drop(Box::from_raw(fill_style)) +pub unsafe extern "C" fn PFFillStyleDeinit(fill_style: PFFillStyleRef) { + fill_style.drop_in_place() } // `resources` #[no_mangle] -pub unsafe extern "C" fn PFEmbeddedResourceLoaderCreate() -> PFResourceLoaderRef { - let loader = Box::new(EmbeddedResourceLoader::new()); - Box::into_raw(Box::new(ResourceLoaderWrapper(loader as Box))) +pub unsafe extern "C" fn PFEmbeddedResourceLoaderInit(loader: PFResourceLoaderRef) -> PFResult { + let inner = Box::new(EmbeddedResourceLoader::new()); + loader.write(PFResourceLoader(inner as Box)); + PF_OK } #[no_mangle] -pub unsafe extern "C" fn PFFilesystemResourceLoaderLocate() -> PFResourceLoaderRef { - let loader = Box::new(FilesystemResourceLoader::locate()); - Box::into_raw(Box::new(ResourceLoaderWrapper(loader as Box))) +pub unsafe extern "C" fn PFFilesystemResourceLoaderLocateInit(loader: PFResourceLoaderRef) -> PFResult { + let inner = Box::new(FilesystemResourceLoader::locate()); + loader.write(PFResourceLoader(inner as Box)); + PF_OK } #[no_mangle] -pub unsafe extern "C" fn PFFilesystemResourceLoaderFromPath(path: *const c_char) -> PFResourceLoaderRef { +pub unsafe extern "C" fn PFFilesystemResourceLoaderFromPathInit(loader: PFResourceLoaderRef, path: *const c_char) -> PFResult { let string = to_rust_string(&path, 0); let directory = PathBuf::from(string); - let loader = Box::new(FilesystemResourceLoader { directory }); - Box::into_raw(Box::new(ResourceLoaderWrapper(loader as Box))) + let inner = Box::new(FilesystemResourceLoader { directory }); + loader.write(PFResourceLoader(inner as Box)); + PF_OK } #[no_mangle] -pub unsafe extern "C" fn PFResourceLoaderDestroy(loader: PFResourceLoaderRef) { - drop(Box::from_raw(loader)) +pub unsafe extern "C" fn PFResourceLoaderDeinit(loader: PFResourceLoaderRef) { + loader.drop_in_place() } // `gl` @@ -609,302 +741,310 @@ pub unsafe extern "C" fn PFGLLoadWith(loader: PFGLFunctionLoader, userdata: *mut } #[no_mangle] -pub unsafe extern "C" fn PFGLDeviceCreate(version: PFGLVersion, default_framebuffer: u32) - -> PFGLDeviceRef { +pub unsafe extern "C" fn PFGLDeviceInit(device: PFGLDeviceRef, version: PFGLVersion, default_framebuffer: u32) -> PFResult { let version = match version { PF_GL_VERSION_GL3 => GLVersion::GL3, PF_GL_VERSION_GLES3 => GLVersion::GLES3, PF_GL_VERSION_GL4 => GLVersion::GL4, - _ => panic!("Invalid Pathfinder OpenGL version!"), + _ => { + log::error!("Invalid Pathfinder OpenGL version: {}", version); + return PFResult(1); + } }; - Box::into_raw(Box::new(GLDevice::new(version, default_framebuffer))) + device.write(GLDevice::new(version, default_framebuffer)); + PF_OK } #[no_mangle] -pub unsafe extern "C" fn PFGLDeviceDestroy(device: PFGLDeviceRef) { - drop(Box::from_raw(device)) +pub unsafe extern "C" fn PFGLDeviceDeinit(device: PFGLDeviceRef) { + device.drop_in_place() } // `gpu` #[no_mangle] -pub unsafe extern "C" fn PFGLDestFramebufferCreateFullWindow(window_size: *const PFVector2I) - -> PFGLDestFramebufferRef { - Box::into_raw(Box::new(DestFramebuffer::full_window((*window_size).to_rust()))) +pub unsafe extern "C" fn PFGLDestFramebufferInitFullWindow(dest_framebuffer: PFGLDestFramebufferRef, window_size: PFVector2I) -> PFResult { + dest_framebuffer.write(DestFramebuffer::full_window(window_size.to_rust())); + PF_OK } #[no_mangle] -pub unsafe extern "C" fn PFGLDestFramebufferDestroy(dest_framebuffer: PFGLDestFramebufferRef) { - drop(Box::from_raw(dest_framebuffer)) +pub unsafe extern "C" fn PFGLDestFramebufferDeinit(dest_framebuffer: PFGLDestFramebufferRef) { + dest_framebuffer.drop_in_place(); } -/// This function takes ownership of and automatically takes responsibility for destroying `device` -/// and `dest_framebuffer`. However, it does not take ownership of `resources`; therefore, if you +/// This function does not take ownership of `resources`; therefore, if you /// created the resource loader, you must destroy it yourself to avoid a memory leak. #[no_mangle] -pub unsafe extern "C" fn PFGLRendererCreate(device: PFGLDeviceRef, +pub unsafe extern "C" fn PFGLRendererInit(renderer: PFGLRendererRef, + device: PFGLDeviceRef, resources: PFResourceLoaderRef, - mode: *const PFRendererMode, - options: *const PFRendererOptions) - -> PFGLRendererRef { - Box::into_raw(Box::new(Renderer::new(*Box::from_raw(device), - &*((*resources).0), - (*mode).to_rust(), - (*options).to_rust()))) + mode: PFRendererMode, + options: NonNull>) + -> PFResult { + let mode = match RendererMode::try_from(mode) { + Ok(t) => t, + Err(e) => return e, + }; + renderer.write(Renderer::new(device.read(), + &*(resources.as_ref().0), + mode, + options.read().into_rust())); + PF_OK } #[no_mangle] -pub unsafe extern "C" fn PFGLRendererDestroy(renderer: PFGLRendererRef) { - drop(Box::from_raw(renderer)) +pub unsafe extern "C" fn PFGLRendererDeinit(renderer: PFGLRendererRef) { + renderer.drop_in_place() } #[no_mangle] -pub unsafe extern "C" fn PFGLRendererGetDevice(renderer: PFGLRendererRef) -> PFGLDeviceRef { - (*renderer).device_mut() +pub unsafe extern "C" fn PFGLRendererGetDevice(mut renderer: PFGLRendererRef) -> PFGLDeviceRef { + renderer.as_mut().device_mut().into() } #[no_mangle] -pub unsafe extern "C" fn PFGLRendererSetViewport(renderer: PFGLRendererRef, new_viewport: PFRectI) -> i32 { - match (*renderer).options_mut().dest { +pub unsafe extern "C" fn PFGLRendererSetViewport(mut renderer: PFGLRendererRef, new_viewport: PFRectI) -> PFResult { + match renderer.as_mut().options_mut().dest { DestFramebuffer::Default { ref mut viewport, .. } => { *viewport = new_viewport.to_rust(); - 0 + PF_OK } - DestFramebuffer::Other(_) => 1, + DestFramebuffer::Other(_) => PFResult(1), } } #[no_mangle] -pub unsafe extern "C" fn PFGLRendererSetWindowSize(renderer: PFGLRendererRef, new_window_size: PFVector2I) -> i32 { - match (*renderer).options_mut().dest { +pub unsafe extern "C" fn PFGLRendererSetWindowSize(mut renderer: PFGLRendererRef, new_window_size: PFVector2I) -> PFResult { + match renderer.as_mut().options_mut().dest { DestFramebuffer::Default { ref mut window_size, .. } => { *window_size = new_window_size.to_rust(); - 0 + PF_OK } - DestFramebuffer::Other(_) => 1, + DestFramebuffer::Other(_) => PFResult(1), } } #[no_mangle] -pub unsafe extern "C" fn PFGLRendererDestFramebufferSizeChanged(renderer: PFGLRendererRef) { - (*renderer).dest_framebuffer_size_changed() +pub unsafe extern "C" fn PFGLRendererDestFramebufferSizeChanged(mut renderer: PFGLRendererRef) { + renderer.as_mut().dest_framebuffer_size_changed() } -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +#[cfg(all(target_os = "macos", feature = "metal"))] #[no_mangle] -pub unsafe extern "C" fn PFMetalDestFramebufferCreateFullWindow(window_size: *const PFVector2I) - -> PFMetalDestFramebufferRef { - Box::into_raw(Box::new(DestFramebuffer::full_window((*window_size).to_rust()))) +pub unsafe extern "C" fn PFMetalDestFramebufferInitFullWindow(dest_framebuffer: PFMetalDestFramebufferRef, window_size: PFVector2I) -> PFResult { + dest_framebuffer.write(DestFramebuffer::full_window(window_size.to_rust())); + PF_OK } -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +#[cfg(all(target_os = "macos", feature = "metal"))] #[no_mangle] -pub unsafe extern "C" fn PFMetalDestFramebufferDestroy(dest_framebuffer: - PFMetalDestFramebufferRef) { - drop(Box::from_raw(dest_framebuffer)) +pub unsafe extern "C" fn PFMetalDestFramebufferDeinit(dest_framebuffer: PFMetalDestFramebufferRef) { + dest_framebuffer.drop_in_place() } /// This function takes ownership of and automatically takes responsibility for destroying `device` /// and `dest_framebuffer`. However, it does not take ownership of `resources`; therefore, if you /// created the resource loader, you must destroy it yourself to avoid a memory leak. -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +#[cfg(all(target_os = "macos", feature = "metal"))] #[no_mangle] -pub unsafe extern "C" fn PFMetalRendererCreate(device: PFMetalDeviceRef, +pub unsafe extern "C" fn PFMetalRendererInit(renderer: PFMetalRendererRef, + device: PFMetalDeviceRef, resources: PFResourceLoaderRef, - mode: *const PFRendererMode, - options: *const PFRendererOptions) - -> PFMetalRendererRef { - Box::into_raw(Box::new(Renderer::new(*Box::from_raw(device), - &*((*resources).0), - (*mode).to_rust(), - (*options).to_rust()))) + mode: PFRendererMode, + options: NonNull>) + -> PFResult { + let mode = match RendererMode::try_from(mode) { + Ok(t) => t, + Err(e) => return e, + }; + renderer.write(Renderer::new(device.read(), + &*(resources.as_ref().0), + mode, + options.into_rust())); + PF_OK } -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +#[cfg(all(target_os = "macos", feature = "metal"))] #[no_mangle] -pub unsafe extern "C" fn PFMetalRendererDestroy(renderer: PFMetalRendererRef) { - drop(Box::from_raw(renderer)) +pub unsafe extern "C" fn PFMetalRendererDeinit(renderer: PFMetalRendererRef) { + renderer.drop_in_place() } /// Returns a reference to the Metal device in the renderer. /// /// This reference remains valid as long as the device is alive. -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +#[cfg(all(target_os = "macos", feature = "metal"))] #[no_mangle] -pub unsafe extern "C" fn PFMetalRendererGetDevice(renderer: PFMetalRendererRef) - -> PFMetalDeviceRef { - (*renderer).device_mut() +pub unsafe extern "C" fn PFMetalRendererGetDevice(mut renderer: PFMetalRendererRef) -> PFMetalDeviceRef { + renderer.as_mut().device_mut().into() } -/// This function does not take ownership of `renderer` or `build_options`. Therefore, if you +/// This function does not take ownership of `renderer`. Therefore, if you /// created the renderer and/or options, you must destroy them yourself to avoid a leak. #[no_mangle] -pub unsafe extern "C" fn PFSceneProxyBuildAndRenderGL(scene_proxy: PFSceneProxyRef, - renderer: PFGLRendererRef, +pub unsafe extern "C" fn PFSceneProxyBuildAndRenderGL(mut scene_proxy: PFSceneProxyRef, + mut renderer: PFGLRendererRef, build_options: PFBuildOptionsRef) { - (*scene_proxy).build_and_render(&mut *renderer, (*build_options).clone()) + scene_proxy.as_mut().build_and_render(renderer.as_mut(), build_options.read()) } -/// This function does not take ownership of `renderer` or `build_options`. Therefore, if you +/// This function does not take ownership of `renderer`. Therefore, if you /// created the renderer and/or options, you must destroy them yourself to avoid a leak. -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +#[cfg(all(target_os = "macos", feature = "metal"))] #[no_mangle] -pub unsafe extern "C" fn PFSceneProxyBuildAndRenderMetal(scene_proxy: PFSceneProxyRef, - renderer: PFMetalRendererRef, +pub unsafe extern "C" fn PFSceneProxyBuildAndRenderMetal(mut scene_proxy: PFSceneProxyRef, + mut renderer: PFMetalRendererRef, build_options: PFBuildOptionsRef) { - (*scene_proxy).build_and_render(&mut *renderer, (*build_options).clone()) + scene_proxy.as_mut().build_and_render(renderer.as_mut(), build_options.read()) } // `metal` -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +#[cfg(all(target_os = "macos", feature = "metal"))] #[no_mangle] -pub unsafe extern "C" fn PFMetalDeviceCreateWithIOSurface(metal_device: &NativeMetalDeviceRef, +pub unsafe extern "C" fn PFMetalDeviceInitWithIOSurface(device: PFMetalDeviceRef, + metal_device: &NativeMetalDeviceRef, io_surface: IOSurfaceRef) - -> PFMetalDeviceRef { - Box::into_raw(Box::new(MetalDevice::new(metal_device, io_surface))) + -> PFResult { + device.write(MetalDevice::new(metal_device, io_surface)); + PF_OK } -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +#[cfg(all(target_os = "macos", feature = "metal"))] #[no_mangle] -pub unsafe extern "C" fn PFMetalDeviceCreateWithDrawable(metal_device: &NativeMetalDeviceRef, +pub unsafe extern "C" fn PFMetalDeviceInitWithDrawable(device: PFMetalDeviceRef, + metal_device: &NativeMetalDeviceRef, ca_drawable: &CoreAnimationDrawableRef) - -> PFMetalDeviceRef { - Box::into_raw(Box::new(MetalDevice::new(metal_device, ca_drawable))) + -> PFResult { + device.write(MetalDevice::new(metal_device, ca_drawable)); + PF_OK } -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +#[cfg(all(target_os = "macos", feature = "metal"))] #[no_mangle] -pub unsafe extern "C" fn PFMetalDeviceSwapIOSurface(device: PFMetalDeviceRef, +pub unsafe extern "C" fn PFMetalDeviceSwapIOSurface(mut device: PFMetalDeviceRef, new_io_surface: IOSurfaceRef) { - drop((*device).swap_texture(new_io_surface)) + drop(device.as_mut().swap_texture(new_io_surface)) } -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +#[cfg(all(target_os = "macos", feature = "metal"))] #[no_mangle] -pub unsafe extern "C" fn PFMetalDeviceSwapDrawable(device: PFMetalDeviceRef, +pub unsafe extern "C" fn PFMetalDeviceSwapDrawable(mut device: PFMetalDeviceRef, new_ca_drawable: &CoreAnimationDrawableRef) { - drop((*device).swap_texture(new_ca_drawable)) + drop(device.as_mut().swap_texture(new_ca_drawable)) } -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +#[cfg(all(target_os = "macos", feature = "metal"))] #[no_mangle] -pub unsafe extern "C" fn PFMetalDevicePresentDrawable(device: PFMetalDeviceRef, +pub unsafe extern "C" fn PFMetalDevicePresentDrawable(mut device: PFMetalDeviceRef, ca_drawable: &CoreAnimationDrawableRef) { - (*device).present_drawable(ca_drawable) + device.as_mut().present_drawable(ca_drawable) } -#[cfg(all(target_os = "macos", not(feature = "pf-gl")))] +#[cfg(all(target_os = "macos", feature = "metal"))] #[no_mangle] -pub unsafe extern "C" fn PFMetalDeviceDestroy(device: PFMetalDeviceRef) { - drop(Box::from_raw(device)) +pub unsafe extern "C" fn PFMetalDeviceDeinit(device: PFMetalDeviceRef) { + device.drop_in_place() } // `renderer` #[no_mangle] -pub unsafe extern "C" fn PFRenderTransformCreate2D(transform: *const PFTransform2F) - -> PFRenderTransformRef { - Box::into_raw(Box::new(RenderTransform::Transform2D((*transform).to_rust()))) +pub unsafe extern "C" fn PFRenderTransformInit2D(transform_dst: PFRenderTransformRef, transform: PFTransform2F) -> PFResult { + transform_dst.write(RenderTransform::Transform2D(transform.to_rust())); + PF_OK } #[no_mangle] -pub unsafe extern "C" fn PFRenderTransformCreatePerspective(perspective: *const PFPerspective) - -> PFRenderTransformRef { - Box::into_raw(Box::new(RenderTransform::Perspective((*perspective).to_rust()))) +pub unsafe extern "C" fn PFRenderTransformInitPerspective(transform_dst: PFRenderTransformRef, perspective: PFPerspective) -> PFResult { + transform_dst.write(RenderTransform::Perspective(perspective.to_rust())); + PF_OK } #[no_mangle] -pub unsafe extern "C" fn PFRenderTransformDestroy(transform: PFRenderTransformRef) { - drop(Box::from_raw(transform)) +pub unsafe extern "C" fn PFRenderTransformDeinit(transform: PFRenderTransformRef) { + transform.drop_in_place() } #[no_mangle] -pub unsafe extern "C" fn PFBuildOptionsCreate() -> PFBuildOptionsRef { - Box::into_raw(Box::new(BuildOptions::default())) +pub unsafe extern "C" fn PFBuildOptionsInit(options: PFBuildOptionsRef) -> PFResult { + options.write(BuildOptions::default()); + PF_OK } #[no_mangle] -pub unsafe extern "C" fn PFBuildOptionsDestroy(options: PFBuildOptionsRef) { - drop(Box::from_raw(options)) -} - -/// Consumes the transform. -#[no_mangle] -pub unsafe extern "C" fn PFBuildOptionsSetTransform(options: PFBuildOptionsRef, - transform: PFRenderTransformRef) { - (*options).transform = *Box::from_raw(transform) +pub unsafe extern "C" fn PFBuildOptionsSetDilation(mut options: PFBuildOptionsRef, + dilation: PFVector2F) { + options.as_mut().dilation = dilation.to_rust() } #[no_mangle] -pub unsafe extern "C" fn PFBuildOptionsSetDilation(options: PFBuildOptionsRef, - dilation: *const PFVector2F) { - (*options).dilation = (*dilation).to_rust() +pub unsafe extern "C" fn PFSceneDeinit(scene: PFSceneRef) { + scene.drop_in_place() } #[no_mangle] -pub unsafe extern "C" fn PFBuildOptionsSetSubpixelAAEnabled(options: PFBuildOptionsRef, - subpixel_aa_enabled: bool) { - (*options).subpixel_aa_enabled = subpixel_aa_enabled +pub unsafe extern "C" fn PFSceneProxyInitWithSequentialExecutor(scene_proxy: PFSceneProxyRef, level: PFRendererLevel) -> PFResult { + let level = match to_rust_renderer_level(level) { + Ok(t) => t, + Err(e) => return e, + }; + scene_proxy.write(SceneProxy::new(level, SequentialExecutor)); + PF_OK } #[no_mangle] -pub unsafe extern "C" fn PFSceneDestroy(scene: PFSceneRef) { - drop(Box::from_raw(scene)) +pub unsafe extern "C" fn PFSceneProxyInitWithRayonExecutor(scene_proxy: PFSceneProxyRef, level: PFRendererLevel) -> PFResult { + let level = match to_rust_renderer_level(level) { + Ok(t) => t, + Err(e) => return e, + }; + scene_proxy.write(SceneProxy::new(level, RayonExecutor)); + PF_OK } #[no_mangle] -pub unsafe extern "C" fn PFSceneProxyCreateWithSequentialExecutor(level: PFRendererLevel) -> PFSceneProxyRef { - Box::into_raw(Box::new(SceneProxy::new(to_rust_renderer_level(level), SequentialExecutor))) +pub unsafe extern "C" fn PFSceneProxyReplaceScene(mut scene_proxy: PFSceneProxyRef, scene: PFSceneRef) { + scene_proxy.as_mut().replace_scene(scene.read()) } #[no_mangle] -pub unsafe extern "C" fn PFSceneProxyCreateWithRayonExecutor(level: PFRendererLevel) -> PFSceneProxyRef { - Box::into_raw(Box::new(SceneProxy::new(to_rust_renderer_level(level), RayonExecutor))) -} - -#[no_mangle] -pub unsafe extern "C" fn PFSceneProxyReplaceScene(scene_proxy: PFSceneProxyRef, scene: PFSceneRef) { - (*scene_proxy).replace_scene(*Box::from_raw(scene)) -} - -#[no_mangle] -pub unsafe extern "C" fn PFSceneProxyDestroy(scene_proxy: PFSceneProxyRef) { - drop(Box::from_raw(scene_proxy)) +pub unsafe extern "C" fn PFSceneProxyDeinit(scene_proxy: PFSceneProxyRef) { + scene_proxy.drop_in_place() } // `svg` -/// Returns `NULL` on failure. #[no_mangle] -pub unsafe extern "C" fn PFSVGSceneCreateWithMemory(bytes: *const c_char, byte_len: usize) - -> PFSVGSceneRef { +pub unsafe extern "C" fn PFSVGSceneInitWithMemory(svg_scene: PFSVGSceneRef, bytes: *const c_char, byte_len: usize) -> PFResult { 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(), + Err(_) => return PFResult(1), }; - let svg_scene = SVGScene::from_tree(&tree); - Box::into_raw(Box::new(svg_scene)) + svg_scene.write(SVGScene::from_tree(&tree)); + PF_OK } -/// Returns `NULL` on failure. #[no_mangle] -pub unsafe extern "C" fn PFSVGSceneCreateWithPath(path: *const c_char) -> PFSVGSceneRef { +pub unsafe extern "C" fn PFSVGSceneInitWithPath(svg_scene: PFSVGSceneRef, path: *const c_char) -> PFResult { 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(), + Err(_) => return PFResult(1), }; - let svg_scene = SVGScene::from_tree(&tree); - Box::into_raw(Box::new(svg_scene)) + svg_scene.write(SVGScene::from_tree(&tree)); + PF_OK } -/// Destroys the SVG and returns the scene. +/// Deinitializes 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)) +pub unsafe extern "C" fn PFSVGSceneIntoScene(svg: PFSVGSceneRef, scene_dst: PFSceneRef) -> PFResult { + let svg = svg.read(); + scene_dst.write(svg.scene); + PF_OK } // Helpers for `canvas` @@ -1008,36 +1148,42 @@ impl PFPerspective { // Helpers for `renderer` -impl PFRendererMode { - pub fn to_rust(&self) -> RendererMode { - RendererMode { - level: to_rust_renderer_level(self.level), - } +impl TryFrom for RendererMode { + type Error = PFResult; + + #[inline] + fn try_from(value: PFRendererMode) -> Result { + Ok(Self { + level: to_rust_renderer_level(value.level)?, + }) } } -impl PFRendererOptions { - pub fn to_rust(&self) -> RendererOptions where D: Device { +impl PFRendererOptions { + #[inline] + pub fn into_rust(self) -> RendererOptions { let has_background_color = self.flags & PF_RENDERER_OPTIONS_FLAGS_HAS_BACKGROUND_COLOR; let show_debug_ui = (self.flags & PF_RENDERER_OPTIONS_FLAGS_SHOW_DEBUG_UI) != 0; - unsafe { - RendererOptions { - background_color: if has_background_color != 0 { - Some(self.background_color.to_rust()) - } else { - None - }, - dest: *Box::from_raw(self.dest as *mut DestFramebuffer), - show_debug_ui, - } + RendererOptions { + background_color: if has_background_color != 0 { + Some(self.background_color.to_rust()) + } else { + None + }, + dest: self.dest, + show_debug_ui, } } } -fn to_rust_renderer_level(level: PFRendererLevel) -> RendererLevel { +#[inline] +fn to_rust_renderer_level(level: PFRendererLevel) -> Result { match level { - PF_RENDERER_LEVEL_D3D9 => RendererLevel::D3D9, - PF_RENDERER_LEVEL_D3D11 => RendererLevel::D3D11, - _ => panic!("Invalid Pathfinder renderer level!"), + PF_RENDERER_LEVEL_D3D9 => Ok(RendererLevel::D3D9), + PF_RENDERER_LEVEL_D3D11 => Ok(RendererLevel::D3D11), + _ => { + log::error!("Invalid Pathfinder renderer level: {}", level); + Err(PFResult(1)) + } } } diff --git a/canvas/src/lib.rs b/canvas/src/lib.rs index f813749e..1c3c3a30 100644 --- a/canvas/src/lib.rs +++ b/canvas/src/lib.rs @@ -78,10 +78,8 @@ pub struct Canvas { impl Canvas { #[inline] - pub fn new(size: Vector2F) -> Canvas { - let mut scene = Scene::new(); - scene.set_view_box(RectF::new(Vector2F::zero(), size)); - Canvas::from_scene(scene) + pub fn new() -> Canvas { + Canvas::from_scene(Scene::new()) } #[inline] @@ -130,10 +128,10 @@ impl Canvas { self.scene.view_box().size().ceil().to_i32() } - pub fn set_size(&mut self, new_size: Vector2I) { - let new_view_box = RectI::new(Vector2I::default(), new_size).to_f32(); - self.scene.set_bounds(new_view_box); - self.scene.set_view_box(new_view_box); + /// This resets the canvas' bounds. Do not call this mid-draw. + pub fn set_size(&mut self, new_size: Vector2F) { + self.scene.set_bounds(RectF::default()); + self.scene.set_view_box(RectF::new(Vector2F::zero(), new_size)); } } diff --git a/canvas/src/text.rs b/canvas/src/text.rs index c4ceaf7f..1ba2b36d 100644 --- a/canvas/src/text.rs +++ b/canvas/src/text.rs @@ -10,7 +10,7 @@ use crate::{CanvasRenderingContext2D, State, TextAlign, TextBaseline}; use font_kit::canvas::RasterizationOptions; -use font_kit::error::{FontLoadingError, SelectionError}; +use font_kit::error::{GlyphLoadingError, FontLoadingError, SelectionError}; use font_kit::family_name::FamilyName; use font_kit::handle::Handle; use font_kit::hinting::HintingOptions; @@ -36,10 +36,10 @@ impl CanvasRenderingContext2D { /// fill the text that you passed into `measure_text()` with the layout-related style /// properties set at the time you called that function. This allows Pathfinder to skip having /// to lay out the text again. - pub fn fill_text(&mut self, text: &T, position: Vector2F) where T: ToTextLayout + ?Sized { + pub fn fill_text(&mut self, text: &T, position: Vector2F) -> Result<(), GlyphLoadingError> where T: ToTextLayout + ?Sized { let paint = self.current_state.resolve_paint(&self.current_state.fill_paint); let paint_id = self.canvas.scene.push_paint(&paint); - self.fill_or_stroke_text(text, position, paint_id, TextRenderMode::Fill); + self.fill_or_stroke_text(text, position, paint_id, TextRenderMode::Fill) } /// Strokes the given text using the current style. @@ -48,11 +48,11 @@ impl CanvasRenderingContext2D { /// stroke the text that you passed into `measure_text()` with the layout-related style /// properties set at the time you called that function. This allows Pathfinder to skip having /// to lay out the text again. - pub fn stroke_text(&mut self, text: &T, position: Vector2F) where T: ToTextLayout + ?Sized { + pub fn stroke_text(&mut self, text: &T, position: Vector2F) -> Result<(), GlyphLoadingError> where T: ToTextLayout + ?Sized { let paint = self.current_state.resolve_paint(&self.current_state.stroke_paint); let paint_id = self.canvas.scene.push_paint(&paint); let render_mode = TextRenderMode::Stroke(self.current_state.resolve_stroke_style()); - self.fill_or_stroke_text(text, position, paint_id, render_mode); + self.fill_or_stroke_text(text, position, paint_id, render_mode) } /// Returns metrics of the given text using the current style. @@ -68,7 +68,7 @@ impl CanvasRenderingContext2D { text: &T, mut position: Vector2F, paint_id: PaintId, - render_mode: TextRenderMode) + render_mode: TextRenderMode) -> Result<(), GlyphLoadingError> where T: ToTextLayout + ?Sized { let layout = text.layout(CanvasState(&self.current_state)); @@ -78,8 +78,7 @@ impl CanvasRenderingContext2D { position += layout.text_origin(); let transform = self.current_state.transform * Transform2F::from_translation(position); - // TODO(pcwalton): Report errors. - drop(self.canvas_font_context + self.canvas_font_context .0 .borrow_mut() .font_context @@ -93,7 +92,9 @@ impl CanvasRenderingContext2D { clip_path, blend_mode, paint_id, - })); + })?; + + Ok(()) } // Text styles diff --git a/demo/magicleap/Cargo.toml b/demo/magicleap/Cargo.toml index 3d60f29a..79345419 100644 --- a/demo/magicleap/Cargo.toml +++ b/demo/magicleap/Cargo.toml @@ -12,7 +12,7 @@ egl = "0.2" log = "0.4" smallvec = "1.2" glutin = { version = "0.23", optional = true } -crossbeam-channel = "0.4" +flume = { version = "0.11", default-features = false, features = ["eventual-fairness"] } [lib] crate-type = ["cdylib"] diff --git a/demo/magicleap/src/lib.rs b/demo/magicleap/src/lib.rs index ff8d9a0c..319c6826 100644 --- a/demo/magicleap/src/lib.rs +++ b/demo/magicleap/src/lib.rs @@ -66,8 +66,8 @@ mod magicleap; mod mocked_c_api; struct ImmersiveApp { - sender: crossbeam_channel::Sender, - receiver: crossbeam_channel::Receiver, + sender: flume::Sender, + receiver: flume::Receiver, demo: DemoApp, } @@ -96,7 +96,7 @@ pub extern "C" fn magicleap_pathfinder_demo_init(egl_display: EGLDisplay, egl_co let demo = DemoApp::new(window, window_size, options); info!("Initialized app"); - let (sender, receiver) = crossbeam_channel::unbounded(); + let (sender, receiver) = flume::unbounded(); Box::into_raw(Box::new(ImmersiveApp { sender, receiver, demo })) as *mut c_void } diff --git a/gl/src/lib.rs b/gl/src/lib.rs index f4489039..007bb85d 100644 --- a/gl/src/lib.rs +++ b/gl/src/lib.rs @@ -785,7 +785,7 @@ impl Device for GLDevice { } fn end_commands(&self) { - unsafe { gl::Flush(); } + //unsafe { gl::Flush(); } } fn draw_arrays(&self, index_count: u32, render_state: &RenderState) { diff --git a/renderer/Cargo.toml b/renderer/Cargo.toml index df36c77f..b89e58fc 100644 --- a/renderer/Cargo.toml +++ b/renderer/Cargo.toml @@ -12,7 +12,7 @@ homepage = "https://github.com/servo/pathfinder" bitflags = "1.0" byte-slice-cast = "0.3" byteorder = "1.2" -crossbeam-channel = "0.4" +flume = { version = "0.11", default-features = false, features = ["eventual-fairness"] } fxhash = "0.2" half = "1.5" hashbrown = "0.7" diff --git a/renderer/src/concurrent/scene_proxy.rs b/renderer/src/concurrent/scene_proxy.rs index 611be177..fd59fc5b 100644 --- a/renderer/src/concurrent/scene_proxy.rs +++ b/renderer/src/concurrent/scene_proxy.rs @@ -25,7 +25,7 @@ use crate::gpu::renderer::Renderer; use crate::gpu_data::RenderCommand; use crate::options::{BuildOptions, RenderCommandListener}; use crate::scene::{Scene, SceneSink}; -use crossbeam_channel::{self, Receiver, Sender}; +use flume::{self, Receiver, Sender}; use pathfinder_geometry::rect::RectF; use pathfinder_gpu::Device; use std::thread; @@ -60,9 +60,9 @@ impl SceneProxy { -> SceneProxy where E: Executor + Send + 'static { let (main_to_worker_sender, main_to_worker_receiver) = - crossbeam_channel::bounded(MAX_MESSAGES_IN_FLIGHT); + flume::bounded(MAX_MESSAGES_IN_FLIGHT); let (worker_to_main_sender, worker_to_main_receiver) = - crossbeam_channel::bounded(MAX_MESSAGES_IN_FLIGHT); + flume::bounded(MAX_MESSAGES_IN_FLIGHT); let listener = RenderCommandListener::new(Box::new(move |command| { drop(worker_to_main_sender.send(command)) })); @@ -122,7 +122,7 @@ impl SceneProxy { /// Returns a copy of the wrapped scene. #[inline] pub fn copy_scene(&self) -> Scene { - let (sender, receiver) = crossbeam_channel::bounded(MAX_MESSAGES_IN_FLIGHT); + let (sender, receiver) = flume::bounded(MAX_MESSAGES_IN_FLIGHT); self.sender.send(MainToWorkerMsg::CopyScene(sender)).unwrap(); receiver.recv().unwrap() } diff --git a/renderer/src/gpu/renderer.rs b/renderer/src/gpu/renderer.rs index 73988f47..b49dde06 100644 --- a/renderer/src/gpu/renderer.rs +++ b/renderer/src/gpu/renderer.rs @@ -646,7 +646,7 @@ impl Renderer where D: Device { location: TextureLocation { page: TexturePageId(!0), rect: RectI::default() }, }); } - let mut render_target = + let render_target = &mut self.core.render_targets[render_target_id.render_target as usize]; debug_assert_eq!(render_target.location.page, TexturePageId(!0)); render_target.location = location; diff --git a/renderer/src/options.rs b/renderer/src/options.rs index 0b2bfc17..e6283568 100644 --- a/renderer/src/options.rs +++ b/renderer/src/options.rs @@ -49,7 +49,8 @@ impl<'a> RenderCommandListener<'a> { } /// Options that influence scene building. -#[derive(Clone, Default)] +#[derive(Clone, Copy, Default)] +#[repr(C)] pub struct BuildOptions { /// A global transform to be applied to the scene. pub transform: RenderTransform, @@ -71,7 +72,7 @@ impl BuildOptions { } /// A global transform to apply to the scene. -#[derive(Clone)] +#[derive(Clone, Copy)] pub enum RenderTransform { /// A 2D transform. Transform2D(Transform2F), diff --git a/renderer/src/paint.rs b/renderer/src/paint.rs index 438e30cd..238f0e64 100644 --- a/renderer/src/paint.rs +++ b/renderer/src/paint.rs @@ -587,7 +587,7 @@ impl Palette { texture_manager: &mut PaintTextureManager, render_transform: Transform2F) { for (paint, metadata) in self.paints.iter().zip(paint_metadata.iter_mut()) { - let mut color_texture_metadata = match metadata.color_texture_metadata { + let color_texture_metadata = match metadata.color_texture_metadata { None => continue, Some(ref mut color_texture_metadata) => color_texture_metadata, }; @@ -843,7 +843,7 @@ impl GradientTileBuilder { }) } - let mut data = self.tiles.last_mut().unwrap(); + let data = self.tiles.last_mut().unwrap(); let location = TextureLocation { page: data.page, rect: RectI::new(vec2i(0, data.next_index as i32), diff --git a/renderer/src/scene.rs b/renderer/src/scene.rs index d813dd8a..351d777a 100644 --- a/renderer/src/scene.rs +++ b/renderer/src/scene.rs @@ -34,6 +34,8 @@ use std::u64; static NEXT_SCENE_ID: AtomicUsize = AtomicUsize::new(0); +const MAX_MESSAGES_IN_FLIGHT: usize = 1024; + /// The vector scene to be rendered. #[derive(Clone)] pub struct Scene { @@ -370,11 +372,29 @@ impl Scene { renderer: &mut Renderer, build_options: BuildOptions, executor: E) - where D: Device, E: Executor { - let commands = self.build_into_vector(renderer, build_options, executor); - renderer.begin_scene(); - commands.into_iter().for_each(|command| renderer.render_command(&command)); - renderer.end_scene(); + where D: Device, E: Executor + Send { + std::thread::scope(move |scope| { + let (tx, rx) = flume::bounded(MAX_MESSAGES_IN_FLIGHT); + + let level = renderer.mode().level; + + // TODO: avoid this auxiliary thread + scope.spawn(move || { + let listener = RenderCommandListener::new(Box::new(move |command| { + tx.send(command).unwrap() + })); + let mut sink = SceneSink::new(listener, level); + self.build(build_options, &mut sink, &executor); + }); + + renderer.begin_scene(); + + for command in rx { + renderer.render_command(&command); + } + + renderer.end_scene(); + }); } }