From e04cc273eec352e3a5da1a96b3f026cbfdc676b6 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 24 May 2019 17:45:19 -0700 Subject: [PATCH] Add enough C bindings to recreate `canvas_minimal` in C. Closes #12. --- .gitignore | 1 + Cargo.lock | 13 + Cargo.toml | 1 + c/Cargo.toml | 29 ++ c/include/pathfinder/pathfinder.h | 158 ++++++++ c/src/lib.rs | 360 +++++++++++++++++++ canvas/Cargo.toml | 3 + examples/c_canvas_minimal/Makefile | 47 +++ examples/c_canvas_minimal/c_canvas_minimal.c | 117 ++++++ gl/Cargo.toml | 3 + gl/src/lib.rs | 6 +- 11 files changed, 736 insertions(+), 2 deletions(-) create mode 100644 c/Cargo.toml create mode 100644 c/include/pathfinder/pathfinder.h create mode 100644 c/src/lib.rs create mode 100644 examples/c_canvas_minimal/Makefile create mode 100644 examples/c_canvas_minimal/c_canvas_minimal.c diff --git a/.gitignore b/.gitignore index 7fd32a8d..82600a0a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ target /site/package-lock.json /site/dist node_modules +/examples/c_canvas_minimal/build # Editors *.swp diff --git a/Cargo.lock b/Cargo.lock index 912cbd46..5b0b1404 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1184,6 +1184,19 @@ dependencies = [ "pathfinder_gpu 0.1.0", ] +[[package]] +name = "pathfinder_c" +version = "0.1.0" +dependencies = [ + "gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "pathfinder_canvas 0.1.0", + "pathfinder_geometry 0.3.0", + "pathfinder_gl 0.1.0", + "pathfinder_gpu 0.1.0", + "pathfinder_renderer 0.1.0", + "pathfinder_simd 0.3.0", +] + [[package]] name = "pathfinder_canvas" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 3163c514..d477c5b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "c", "canvas", "demo/android/rust", "demo/common", diff --git a/c/Cargo.toml b/c/Cargo.toml new file mode 100644 index 00000000..7c17d4b2 --- /dev/null +++ b/c/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "pathfinder_c" +version = "0.1.0" +authors = ["Patrick Walton "] +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" diff --git a/c/include/pathfinder/pathfinder.h b/c/include/pathfinder/pathfinder.h new file mode 100644 index 00000000..fd82dbb3 --- /dev/null +++ b/c/include/pathfinder/pathfinder.h @@ -0,0 +1,158 @@ +// pathfinder/c/include/pathfinder/pathfinder.h +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , 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 + +#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 diff --git a/c/src/lib.rs b/c/src/lib.rs new file mode 100644 index 00000000..72475994 --- /dev/null +++ b/c/src/lib.rs @@ -0,0 +1,360 @@ +// pathfinder/c/src/lib.rs +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , 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; +pub type PFGLRendererRef = *mut Renderer; +// FIXME(pcwalton): Double-boxing is unfortunate. Remove this when `std::raw::TraitObject` is +// stable? +pub type PFResourceLoaderRef = *mut Box; +#[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)) +} + +#[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() + } +} diff --git a/canvas/Cargo.toml b/canvas/Cargo.toml index 0ca7b922..d93ac0cf 100644 --- a/canvas/Cargo.toml +++ b/canvas/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" authors = ["Patrick Walton "] edition = "2018" +[lib] +crate-type = ["rlib", "staticlib"] + [dependencies] font-kit = "0.1" diff --git a/examples/c_canvas_minimal/Makefile b/examples/c_canvas_minimal/Makefile new file mode 100644 index 00000000..35f4b9ef --- /dev/null +++ b/examples/c_canvas_minimal/Makefile @@ -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) diff --git a/examples/c_canvas_minimal/c_canvas_minimal.c b/examples/c_canvas_minimal/c_canvas_minimal.c new file mode 100644 index 00000000..9b5f3777 --- /dev/null +++ b/examples/c_canvas_minimal/c_canvas_minimal.c @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#include +#include +#include +#include +#include +#include +#include + +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); +} diff --git a/gl/Cargo.toml b/gl/Cargo.toml index edf674e7..ce219884 100644 --- a/gl/Cargo.toml +++ b/gl/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2018" authors = ["Patrick Walton "] +[lib] +crate-type = ["rlib", "staticlib"] + [dependencies] gl = "0.6" rustache = "0.1" diff --git a/gl/src/lib.rs b/gl/src/lib.rs index cb4e002a..dd2f4072 100644 --- a/gl/src/lib.rs +++ b/gl/src/lib.rs @@ -859,11 +859,13 @@ impl VertexAttrTypeExt for VertexAttrType { } /// The version/dialect of OpenGL we should render with. +#[derive(Clone, Copy)] +#[repr(u32)] pub enum GLVersion { /// OpenGL 3.0+, core profile. - GL3, + GL3 = 0, /// OpenGL ES 3.0+. - GLES3, + GLES3 = 1, } impl GLVersion {