mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-09 12:21:31 +11:00
Add C api for glyph rendering
First cut at a public C api that supports glyph rendering on Metal targets.
This commit is contained in:
parent
b178741c19
commit
532b6ee808
132
Cargo.lock
generated
132
Cargo.lock
generated
|
@ -127,6 +127,25 @@ dependencies = [
|
|||
"nix 0.18.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cbindgen"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51e3973b165dc0f435831a9e426de67e894de532754ff7a3f307c03ee5dec7dc"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn",
|
||||
"tempfile",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
|
@ -452,6 +471,15 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -501,6 +529,21 @@ version = "1.8.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
|
@ -516,6 +559,16 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
|
@ -525,6 +578,12 @@ dependencies = [
|
|||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||
|
||||
[[package]]
|
||||
name = "jni-sys"
|
||||
version = "0.3.0"
|
||||
|
@ -876,6 +935,19 @@ version = "2.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "pgpu-render"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cbindgen",
|
||||
"cocoa",
|
||||
"metal",
|
||||
"objc",
|
||||
"piet-gpu",
|
||||
"piet-gpu-hal",
|
||||
"piet-scene",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "piet"
|
||||
version = "0.2.0"
|
||||
|
@ -1131,6 +1203,15 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roxmltree"
|
||||
version = "0.13.1"
|
||||
|
@ -1150,6 +1231,12 @@ dependencies = [
|
|||
"owned_ttf_parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
|
@ -1176,6 +1263,31 @@ name = "serde"
|
|||
version = "1.0.137"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.137"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
|
@ -1235,6 +1347,20 @@ dependencies = [
|
|||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"fastrand",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
|
@ -1330,6 +1456,12 @@ dependencies = [
|
|||
"unic-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.9"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[workspace]
|
||||
|
||||
members = [
|
||||
"pgpu-render",
|
||||
"piet-gpu",
|
||||
"piet-gpu-derive",
|
||||
"piet-gpu-hal",
|
||||
|
|
21
pgpu-render/Cargo.toml
Normal file
21
pgpu-render/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
|||
[package]
|
||||
name = "pgpu-render"
|
||||
version = "0.1.0"
|
||||
description = "C interface for glyph rendering using piet-gpu."
|
||||
license = "MIT/Apache-2.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
piet-gpu = { path = "../piet-gpu" }
|
||||
piet-gpu-hal = { path = "../piet-gpu-hal" }
|
||||
piet-scene = { path = "../piet-scene" }
|
||||
|
||||
metal = "0.22"
|
||||
cocoa = "0.24.0"
|
||||
objc = "0.2.5"
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = "0.20.0"
|
29
pgpu-render/build.rs
Normal file
29
pgpu-render/build.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2022 The piet-gpu authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Also licensed under MIT license, at your choice.
|
||||
|
||||
extern crate cbindgen;
|
||||
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
|
||||
cbindgen::Builder::new()
|
||||
.with_crate(crate_dir)
|
||||
.generate()
|
||||
.expect("Unable to generate bindings")
|
||||
.write_to_file("pgpu.h");
|
||||
}
|
140
pgpu-render/pgpu.h
Normal file
140
pgpu-render/pgpu.h
Normal file
|
@ -0,0 +1,140 @@
|
|||
#include <cstdarg>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <ostream>
|
||||
#include <new>
|
||||
|
||||
/// Encoded (possibly color) outline for a glyph.
|
||||
struct PgpuGlyph;
|
||||
|
||||
/// Context for loading and scaling glyphs.
|
||||
struct PgpuGlyphContext;
|
||||
|
||||
/// Context for loading a scaling glyphs from a specific font.
|
||||
struct PgpuGlyphProvider;
|
||||
|
||||
/// State and resources for rendering a scene.
|
||||
struct PgpuRenderer;
|
||||
|
||||
/// Encoded streams and resources describing a vector graphics scene.
|
||||
struct PgpuScene;
|
||||
|
||||
/// Builder for constructing an encoded scene.
|
||||
struct PgpuSceneBuilder;
|
||||
|
||||
/// Tag and value for a font variation axis.
|
||||
struct PgpuFontVariation {
|
||||
/// Tag that specifies the axis.
|
||||
uint32_t tag;
|
||||
/// Requested setting for the axis.
|
||||
float value;
|
||||
};
|
||||
|
||||
/// Description of a font.
|
||||
struct PgpuFontDesc {
|
||||
/// Pointer to the context of the font file.
|
||||
const uint8_t *data;
|
||||
/// Size of the font file data in bytes.
|
||||
uintptr_t data_len;
|
||||
/// Index of the requested font in the font file.
|
||||
uint32_t index;
|
||||
/// Unique identifier for the font.
|
||||
uint64_t unique_id;
|
||||
/// Requested size in pixels per em unit. Set to 0.0 for
|
||||
/// unscaled outlines.
|
||||
float ppem;
|
||||
/// Pointer to array of font variation settings.
|
||||
const PgpuFontVariation *variations;
|
||||
/// Number of font variation settings.
|
||||
uintptr_t variations_len;
|
||||
};
|
||||
|
||||
/// Rectangle defined by minimum and maximum points.
|
||||
struct PgpuRect {
|
||||
float x0;
|
||||
float y0;
|
||||
float x1;
|
||||
float y1;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
/// Creates a new piet-gpu renderer for the specified Metal device and
|
||||
/// command queue.
|
||||
///
|
||||
/// device: MTLDevice*
|
||||
/// queue: MTLCommandQueue*
|
||||
PgpuRenderer *pgpu_renderer_new(void *device, void *queue);
|
||||
|
||||
/// Renders a prepared scene into a texture target. Commands for rendering are
|
||||
/// recorded into the specified command buffer. Returns an id representing
|
||||
/// resources that may have been allocated during this process. After the
|
||||
/// command buffer has been retired, call `pgpu_renderer_release` with this id
|
||||
/// to drop any associated resources.
|
||||
///
|
||||
/// target: MTLTexture*
|
||||
/// cmdbuf: MTLCommandBuffer*
|
||||
uint32_t pgpu_renderer_render(PgpuRenderer *renderer,
|
||||
const PgpuScene *scene,
|
||||
void *target,
|
||||
void *cmdbuf);
|
||||
|
||||
/// Releases the internal resources associated with the specified id from a
|
||||
/// previous render operation.
|
||||
void pgpu_renderer_release(PgpuRenderer *renderer, uint32_t id);
|
||||
|
||||
/// Destroys the piet-gpu renderer.
|
||||
void pgpu_renderer_destroy(PgpuRenderer *renderer);
|
||||
|
||||
/// Creates a new, empty piet-gpu scene.
|
||||
PgpuScene *pgpu_scene_new();
|
||||
|
||||
/// Destroys the piet-gpu scene.
|
||||
void pgpu_scene_destroy(PgpuScene *scene);
|
||||
|
||||
/// Creates a new builder for filling a piet-gpu scene. The specified scene
|
||||
/// should not be accessed while the builder is live.
|
||||
PgpuSceneBuilder *pgpu_scene_builder_new(PgpuScene *scene);
|
||||
|
||||
/// Adds a glyph with the specified transform to the underlying scene.
|
||||
void pgpu_scene_builder_add_glyph(PgpuSceneBuilder *builder,
|
||||
const PgpuGlyph *glyph,
|
||||
const float (*transform)[6]);
|
||||
|
||||
/// Finalizes the scene builder, making the underlying scene ready for
|
||||
/// rendering. This takes ownership and consumes the builder.
|
||||
void pgpu_scene_builder_finish(PgpuSceneBuilder *builder);
|
||||
|
||||
/// Creates a new context for loading glyph outlines.
|
||||
PgpuGlyphContext *pgpu_glyph_context_new();
|
||||
|
||||
/// Destroys the glyph context.
|
||||
void pgpu_glyph_context_destroy(PgpuGlyphContext *gcx);
|
||||
|
||||
/// Creates a new glyph provider for the specified glyph context and font
|
||||
/// descriptor. May return nullptr if the font data is invalid. Only one glyph
|
||||
/// provider may be live for a glyph context.
|
||||
PgpuGlyphProvider *pgpu_glyph_provider_new(PgpuGlyphContext *gcx, const PgpuFontDesc *font);
|
||||
|
||||
/// Returns an encoded outline for the specified glyph provider and glyph id.
|
||||
/// May return nullptr if the requested glyph is not available.
|
||||
const PgpuGlyph *pgpu_glyph_provider_get(PgpuGlyphProvider *provider, uint16_t gid);
|
||||
|
||||
/// Returns an encoded color outline for the specified glyph provider, color
|
||||
/// palette index and glyph id. May return nullptr if the requested glyph is
|
||||
/// not available.
|
||||
PgpuGlyph *pgpu_glyph_provider_get_color(PgpuGlyphProvider *provider,
|
||||
uint16_t palette_index,
|
||||
uint16_t gid);
|
||||
|
||||
/// Destroys the glyph provider.
|
||||
void pgpu_glyph_provider_destroy(PgpuGlyphProvider *provider);
|
||||
|
||||
/// Computes the bounding box for the glyph after applying the specified
|
||||
/// transform.
|
||||
PgpuRect pgpu_glyph_bbox(const PgpuGlyph *glyph, const float (*transform)[6]);
|
||||
|
||||
/// Destroys the glyph.
|
||||
void pgpu_glyph_destroy(PgpuGlyph *glyph);
|
||||
|
||||
} // extern "C"
|
233
pgpu-render/src/lib.rs
Normal file
233
pgpu-render/src/lib.rs
Normal file
|
@ -0,0 +1,233 @@
|
|||
// Copyright 2022 The piet-gpu authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Also licensed under MIT license, at your choice.
|
||||
|
||||
mod render;
|
||||
|
||||
use render::*;
|
||||
use std::ffi::c_void;
|
||||
use std::mem::transmute;
|
||||
|
||||
/// Creates a new piet-gpu renderer for the specified Metal device and
|
||||
/// command queue.
|
||||
///
|
||||
/// device: MTLDevice*
|
||||
/// queue: MTLCommandQueue*
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pgpu_renderer_new(
|
||||
device: *mut c_void,
|
||||
queue: *mut c_void,
|
||||
) -> *mut PgpuRenderer {
|
||||
let device: &metal::DeviceRef = transmute(device);
|
||||
let queue: &metal::CommandQueueRef = transmute(queue);
|
||||
Box::into_raw(Box::new(PgpuRenderer::new(device, queue)))
|
||||
}
|
||||
|
||||
/// Renders a prepared scene into a texture target. Commands for rendering are
|
||||
/// recorded into the specified command buffer. Returns an id representing
|
||||
/// resources that may have been allocated during this process. After the
|
||||
/// command buffer has been retired, call `pgpu_renderer_release` with this id
|
||||
/// to drop any associated resources.
|
||||
///
|
||||
/// target: MTLTexture*
|
||||
/// cmdbuf: MTLCommandBuffer*
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pgpu_renderer_render(
|
||||
renderer: *mut PgpuRenderer,
|
||||
scene: *const PgpuScene,
|
||||
target: *mut c_void,
|
||||
cmdbuf: *mut c_void,
|
||||
) -> u32 {
|
||||
let cmdbuf: &metal::CommandBufferRef = transmute(cmdbuf);
|
||||
let target: &metal::TextureRef = transmute(target);
|
||||
(*renderer).render(&*scene, cmdbuf, target)
|
||||
}
|
||||
|
||||
/// Releases the internal resources associated with the specified id from a
|
||||
/// previous render operation.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pgpu_renderer_release(renderer: *mut PgpuRenderer, id: u32) {
|
||||
(*renderer).release(id);
|
||||
}
|
||||
|
||||
/// Destroys the piet-gpu renderer.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pgpu_renderer_destroy(renderer: *mut PgpuRenderer) {
|
||||
Box::from_raw(renderer);
|
||||
}
|
||||
|
||||
/// Creates a new, empty piet-gpu scene.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pgpu_scene_new() -> *mut PgpuScene {
|
||||
Box::into_raw(Box::new(PgpuScene::new()))
|
||||
}
|
||||
|
||||
/// Destroys the piet-gpu scene.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pgpu_scene_destroy(scene: *mut PgpuScene) {
|
||||
Box::from_raw(scene);
|
||||
}
|
||||
|
||||
/// Creates a new builder for filling a piet-gpu scene. The specified scene
|
||||
/// should not be accessed while the builder is live.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pgpu_scene_builder_new(
|
||||
scene: *mut PgpuScene,
|
||||
) -> *mut PgpuSceneBuilder<'static> {
|
||||
Box::into_raw(Box::new((*scene).build()))
|
||||
}
|
||||
|
||||
/// Adds a glyph with the specified transform to the underlying scene.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pgpu_scene_builder_add_glyph(
|
||||
builder: *mut PgpuSceneBuilder<'static>,
|
||||
glyph: *const PgpuGlyph,
|
||||
transform: &[f32; 6],
|
||||
) {
|
||||
let transform = piet_scene::geometry::Affine::new(transform);
|
||||
(*builder).add_glyph(&*glyph, &transform);
|
||||
}
|
||||
|
||||
/// Finalizes the scene builder, making the underlying scene ready for
|
||||
/// rendering. This takes ownership and consumes the builder.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pgpu_scene_builder_finish(builder: *mut PgpuSceneBuilder<'static>) {
|
||||
let builder = Box::from_raw(builder);
|
||||
builder.finish();
|
||||
}
|
||||
|
||||
/// Creates a new context for loading glyph outlines.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pgpu_glyph_context_new() -> *mut PgpuGlyphContext {
|
||||
Box::into_raw(Box::new(PgpuGlyphContext::new()))
|
||||
}
|
||||
|
||||
/// Destroys the glyph context.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pgpu_glyph_context_destroy(gcx: *mut PgpuGlyphContext) {
|
||||
Box::from_raw(gcx);
|
||||
}
|
||||
|
||||
/// Description of a font.
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct PgpuFontDesc {
|
||||
/// Pointer to the context of the font file.
|
||||
data: *const u8,
|
||||
/// Size of the font file data in bytes.
|
||||
data_len: usize,
|
||||
/// Index of the requested font in the font file.
|
||||
index: u32,
|
||||
/// Unique identifier for the font.
|
||||
unique_id: u64,
|
||||
/// Requested size in pixels per em unit. Set to 0.0 for
|
||||
/// unscaled outlines.
|
||||
ppem: f32,
|
||||
/// Pointer to array of font variation settings.
|
||||
variations: *const PgpuFontVariation,
|
||||
/// Number of font variation settings.
|
||||
variations_len: usize,
|
||||
}
|
||||
|
||||
/// Creates a new glyph provider for the specified glyph context and font
|
||||
/// descriptor. May return nullptr if the font data is invalid. Only one glyph
|
||||
/// provider may be live for a glyph context.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pgpu_glyph_provider_new(
|
||||
gcx: *mut PgpuGlyphContext,
|
||||
font: *const PgpuFontDesc,
|
||||
) -> *mut PgpuGlyphProvider<'static> {
|
||||
let font = &*font;
|
||||
let font_data = std::slice::from_raw_parts(font.data, font.data_len);
|
||||
let variations = std::slice::from_raw_parts(font.variations, font.variations_len);
|
||||
if let Some(provider) = (*gcx).new_provider(
|
||||
font_data,
|
||||
font.index,
|
||||
font.unique_id,
|
||||
font.ppem,
|
||||
false,
|
||||
variations,
|
||||
) {
|
||||
Box::into_raw(Box::new(provider))
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an encoded outline for the specified glyph provider and glyph id.
|
||||
/// May return nullptr if the requested glyph is not available.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pgpu_glyph_provider_get(
|
||||
provider: *mut PgpuGlyphProvider,
|
||||
gid: u16,
|
||||
) -> *const PgpuGlyph {
|
||||
if let Some(glyph) = (*provider).get(gid) {
|
||||
Box::into_raw(Box::new(glyph))
|
||||
} else {
|
||||
std::ptr::null()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an encoded color outline for the specified glyph provider, color
|
||||
/// palette index and glyph id. May return nullptr if the requested glyph is
|
||||
/// not available.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pgpu_glyph_provider_get_color(
|
||||
provider: *mut PgpuGlyphProvider,
|
||||
palette_index: u16,
|
||||
gid: u16,
|
||||
) -> *mut PgpuGlyph {
|
||||
if let Some(glyph) = (*provider).get_color(palette_index, gid) {
|
||||
Box::into_raw(Box::new(glyph))
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// Destroys the glyph provider.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pgpu_glyph_provider_destroy(provider: *mut PgpuGlyphProvider) {
|
||||
Box::from_raw(provider);
|
||||
}
|
||||
|
||||
/// Rectangle defined by minimum and maximum points.
|
||||
#[derive(Copy, Clone, Default)]
|
||||
#[repr(C)]
|
||||
pub struct PgpuRect {
|
||||
pub x0: f32,
|
||||
pub y0: f32,
|
||||
pub x1: f32,
|
||||
pub y1: f32,
|
||||
}
|
||||
|
||||
/// Computes the bounding box for the glyph after applying the specified
|
||||
/// transform.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pgpu_glyph_bbox(glyph: *const PgpuGlyph, transform: &[f32; 6]) -> PgpuRect {
|
||||
let transform = piet_scene::geometry::Affine::new(transform);
|
||||
let rect = (*glyph).bbox(Some(transform));
|
||||
PgpuRect {
|
||||
x0: rect.min.x,
|
||||
y0: rect.min.y,
|
||||
x1: rect.max.x,
|
||||
y1: rect.max.y,
|
||||
}
|
||||
}
|
||||
|
||||
/// Destroys the glyph.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn pgpu_glyph_destroy(glyph: *mut PgpuGlyph) {
|
||||
Box::from_raw(glyph);
|
||||
}
|
222
pgpu-render/src/render.rs
Normal file
222
pgpu-render/src/render.rs
Normal file
|
@ -0,0 +1,222 @@
|
|||
// Copyright 2022 The piet-gpu authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Also licensed under MIT license, at your choice.
|
||||
|
||||
use piet_gpu::{EncodedSceneRef, PixelFormat, RenderConfig};
|
||||
use piet_gpu_hal::{QueryPool, Session};
|
||||
use piet_scene::glyph::pinot::{types::Tag, FontDataRef};
|
||||
use piet_scene::geometry::{Affine, Rect};
|
||||
use piet_scene::glyph::{GlyphContext, GlyphProvider};
|
||||
use piet_scene::resource::ResourceContext;
|
||||
use piet_scene::scene::{Fragment, Scene};
|
||||
|
||||
/// State and resources for rendering a scene.
|
||||
pub struct PgpuRenderer {
|
||||
session: Session,
|
||||
pgpu_renderer: Option<piet_gpu::Renderer>,
|
||||
query_pool: QueryPool,
|
||||
width: u32,
|
||||
height: u32,
|
||||
is_color: bool,
|
||||
}
|
||||
|
||||
impl PgpuRenderer {
|
||||
pub fn new(device: &metal::DeviceRef, queue: &metal::CommandQueueRef) -> Self {
|
||||
let piet_device = piet_gpu_hal::Device::new_from_raw_mtl(device, &queue);
|
||||
let session = Session::new(piet_device);
|
||||
let query_pool = session.create_query_pool(12).unwrap();
|
||||
Self {
|
||||
session,
|
||||
pgpu_renderer: None,
|
||||
query_pool,
|
||||
width: 0,
|
||||
height: 0,
|
||||
is_color: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(
|
||||
&mut self,
|
||||
scene: &PgpuScene,
|
||||
cmdbuf: &metal::CommandBufferRef,
|
||||
target: &metal::TextureRef,
|
||||
) -> u32 {
|
||||
let is_color = target.pixel_format() != metal::MTLPixelFormat::A8Unorm;
|
||||
let width = target.width() as u32;
|
||||
let height = target.height() as u32;
|
||||
if self.pgpu_renderer.is_none()
|
||||
|| self.width != width
|
||||
|| self.height != height
|
||||
|| self.is_color != is_color
|
||||
{
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
self.is_color = is_color;
|
||||
let format = if is_color {
|
||||
PixelFormat::Rgba8
|
||||
} else {
|
||||
PixelFormat::A8
|
||||
};
|
||||
let config = RenderConfig::new(width as usize, height as usize).pixel_format(format);
|
||||
unsafe {
|
||||
self.pgpu_renderer =
|
||||
piet_gpu::Renderer::new_from_config(&self.session, config, 1).ok();
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
let mut cmd_buf = self.session.cmd_buf_from_raw_mtl(cmdbuf);
|
||||
let dst_image = self
|
||||
.session
|
||||
.image_from_raw_mtl(target, self.width, self.height);
|
||||
if let Some(renderer) = &mut self.pgpu_renderer {
|
||||
renderer.upload_scene(&scene.encoded_scene(), 0).unwrap();
|
||||
renderer.record(&mut cmd_buf, &self.query_pool, 0);
|
||||
// TODO later: we can bind the destination image and avoid the copy.
|
||||
cmd_buf.blit_image(&renderer.image_dev, &dst_image);
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
pub fn release(&mut self, _id: u32) {
|
||||
// TODO: worry about freeing resources / managing overlapping submits
|
||||
}
|
||||
}
|
||||
|
||||
/// Encoded streams and resources describing a vector graphics scene.
|
||||
pub struct PgpuScene {
|
||||
scene: Scene,
|
||||
rcx: ResourceContext,
|
||||
}
|
||||
|
||||
impl PgpuScene {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
scene: Scene::default(),
|
||||
rcx: ResourceContext::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(&mut self) -> PgpuSceneBuilder {
|
||||
self.rcx.advance();
|
||||
PgpuSceneBuilder(piet_scene::scene::build_scene(
|
||||
&mut self.scene,
|
||||
&mut self.rcx,
|
||||
))
|
||||
}
|
||||
|
||||
fn encoded_scene<'a>(&'a self) -> EncodedSceneRef<'a, piet_scene::geometry::Affine> {
|
||||
let d = self.scene.data();
|
||||
EncodedSceneRef {
|
||||
transform_stream: &d.transform_stream,
|
||||
tag_stream: &d.tag_stream,
|
||||
pathseg_stream: &d.pathseg_stream,
|
||||
linewidth_stream: &d.linewidth_stream,
|
||||
drawtag_stream: &d.drawtag_stream,
|
||||
drawdata_stream: &d.drawdata_stream,
|
||||
n_path: d.n_path,
|
||||
n_pathseg: d.n_pathseg,
|
||||
n_clip: d.n_clip,
|
||||
ramp_data: self.rcx.ramp_data(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for constructing an encoded scene.
|
||||
pub struct PgpuSceneBuilder<'a>(piet_scene::scene::Builder<'a>);
|
||||
|
||||
impl<'a> PgpuSceneBuilder<'a> {
|
||||
pub fn add_glyph(&mut self, glyph: &PgpuGlyph, transform: &piet_scene::geometry::Affine) {
|
||||
self.0.push_transform(*transform);
|
||||
self.0.append(&glyph.fragment);
|
||||
self.0.pop_transform();
|
||||
}
|
||||
|
||||
pub fn finish(self) {
|
||||
self.0.finish();
|
||||
}
|
||||
}
|
||||
|
||||
/// Tag and value for a font variation axis.
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct PgpuFontVariation {
|
||||
/// Tag that specifies the axis.
|
||||
pub tag: u32,
|
||||
/// Requested setting for the axis.
|
||||
pub value: f32,
|
||||
}
|
||||
|
||||
/// Context for loading and scaling glyphs.
|
||||
pub struct PgpuGlyphContext(GlyphContext);
|
||||
|
||||
impl PgpuGlyphContext {
|
||||
pub fn new() -> Self {
|
||||
Self(GlyphContext::new())
|
||||
}
|
||||
|
||||
pub fn new_provider<'a>(
|
||||
&'a mut self,
|
||||
font_data: &'a [u8],
|
||||
font_index: u32,
|
||||
font_id: u64,
|
||||
ppem: f32,
|
||||
hint: bool,
|
||||
variations: &[PgpuFontVariation],
|
||||
) -> Option<PgpuGlyphProvider> {
|
||||
let font = FontDataRef::new(font_data).and_then(|f| f.get(font_index))?;
|
||||
Some(PgpuGlyphProvider(
|
||||
self.0.new_provider(
|
||||
&font,
|
||||
Some(font_id),
|
||||
ppem,
|
||||
hint,
|
||||
variations
|
||||
.iter()
|
||||
.map(|variation| (Tag(variation.tag), variation.value)),
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Context for loading a scaling glyphs from a specific font.
|
||||
pub struct PgpuGlyphProvider<'a>(GlyphProvider<'a>);
|
||||
|
||||
impl<'a> PgpuGlyphProvider<'a> {
|
||||
pub fn get(&mut self, gid: u16) -> Option<PgpuGlyph> {
|
||||
let fragment = self.0.get(gid)?;
|
||||
Some(PgpuGlyph { fragment })
|
||||
}
|
||||
|
||||
pub fn get_color(&mut self, palette_index: u16, gid: u16) -> Option<PgpuGlyph> {
|
||||
let fragment = self.0.get_color(palette_index, gid)?;
|
||||
Some(PgpuGlyph { fragment })
|
||||
}
|
||||
}
|
||||
|
||||
/// Encoded (possibly color) outline for a glyph.
|
||||
pub struct PgpuGlyph {
|
||||
fragment: Fragment,
|
||||
}
|
||||
|
||||
impl PgpuGlyph {
|
||||
pub fn bbox(&self, transform: Option<Affine>) -> Rect {
|
||||
if let Some(transform) = &transform {
|
||||
Rect::from_points(self.fragment.points().iter().map(|p| p.transform(transform)))
|
||||
} else {
|
||||
Rect::from_points(self.fragment.points())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ use core::borrow::Borrow;
|
|||
use core::hash::{Hash, Hasher};
|
||||
|
||||
/// Two dimensional point.
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Default, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Default, Debug, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct Point {
|
||||
pub x: f32,
|
||||
|
@ -168,7 +168,7 @@ impl std::ops::Mul for Affine {
|
|||
}
|
||||
|
||||
/// Axis-aligned rectangle represented as minimum and maximum points.
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
#[derive(Copy, Clone, Default, Debug, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct Rect {
|
||||
pub min: Point,
|
||||
|
|
|
@ -17,11 +17,15 @@
|
|||
pub use pinot;
|
||||
|
||||
use crate::brush::{Brush, Color};
|
||||
use crate::geometry::Affine;
|
||||
use crate::path::Element;
|
||||
use crate::scene::{build_fragment, Fill, Fragment};
|
||||
|
||||
use moscato::{Context, Scaler};
|
||||
use pinot::{types::Tag, FontRef};
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
/// General context for creating scene fragments for glyph outlines.
|
||||
pub struct GlyphContext {
|
||||
ctx: Context,
|
||||
|
@ -99,40 +103,70 @@ impl<'a> GlyphProvider<'a> {
|
|||
let glyph = self.scaler.color_glyph(palette_index, gid)?;
|
||||
let mut fragment = Fragment::default();
|
||||
let mut builder = build_fragment(&mut fragment);
|
||||
let mut xform_stack: SmallVec<[Affine; 8]> = SmallVec::new();
|
||||
for command in glyph.commands() {
|
||||
match command {
|
||||
Command::PushTransform(xform) => {
|
||||
builder.push_transform(convert_transform(xform));
|
||||
xform_stack.push(convert_transform(xform));
|
||||
}
|
||||
Command::PopTransform => builder.pop_transform(),
|
||||
Command::PopTransform => { xform_stack.pop(); },
|
||||
Command::PushClip(path_index) => {
|
||||
let path = glyph.path(*path_index)?;
|
||||
builder.push_layer(Default::default(), convert_path(path.elements()));
|
||||
if let Some(xform) = xform_stack.last() {
|
||||
builder.push_layer(
|
||||
Default::default(),
|
||||
convert_transformed_path(path.elements(), xform),
|
||||
);
|
||||
} else {
|
||||
builder.push_layer(Default::default(), convert_path(path.elements()));
|
||||
}
|
||||
}
|
||||
Command::PopClip => builder.pop_layer(),
|
||||
Command::PushLayer(bounds) => {
|
||||
let rect = Rect {
|
||||
let mut rect = Rect {
|
||||
min: Point::new(bounds.min.x, bounds.min.y),
|
||||
max: Point::new(bounds.max.x, bounds.max.y),
|
||||
};
|
||||
if let Some(xform) = xform_stack.last() {
|
||||
rect.min = rect.min.transform(xform);
|
||||
rect.max = rect.max.transform(xform);
|
||||
}
|
||||
builder.push_layer(Default::default(), rect.elements());
|
||||
}
|
||||
Command::PopLayer => builder.pop_layer(),
|
||||
Command::BeginBlend(bounds, mode) => {
|
||||
let rect = Rect {
|
||||
let mut rect = Rect {
|
||||
min: Point::new(bounds.min.x, bounds.min.y),
|
||||
max: Point::new(bounds.max.x, bounds.max.y),
|
||||
};
|
||||
if let Some(xform) = xform_stack.last() {
|
||||
rect.min = rect.min.transform(xform);
|
||||
rect.max = rect.max.transform(xform);
|
||||
}
|
||||
builder.push_layer(convert_blend(*mode), rect.elements())
|
||||
}
|
||||
Command::EndBlend => builder.pop_layer(),
|
||||
Command::SimpleFill(path_index, brush, xform) => {
|
||||
Command::SimpleFill(path_index, brush, brush_xform) => {
|
||||
let path = glyph.path(*path_index)?;
|
||||
let brush = convert_brush(brush);
|
||||
let xform = xform.map(|xform| convert_transform(&xform));
|
||||
builder.fill(Fill::NonZero, &brush, xform, convert_path(path.elements()));
|
||||
let brush_xform = brush_xform.map(|xform| convert_transform(&xform));
|
||||
if let Some(xform) = xform_stack.last() {
|
||||
builder.fill(
|
||||
Fill::NonZero,
|
||||
&brush,
|
||||
brush_xform,
|
||||
convert_transformed_path(path.elements(), xform),
|
||||
);
|
||||
} else {
|
||||
builder.fill(
|
||||
Fill::NonZero,
|
||||
&brush,
|
||||
brush_xform,
|
||||
convert_path(path.elements()),
|
||||
);
|
||||
}
|
||||
}
|
||||
Command::Fill(brush, xform) => {
|
||||
Command::Fill(brush, brush_xform) => {
|
||||
// TODO: this needs to compute a bounding box for
|
||||
// the parent clips
|
||||
}
|
||||
|
@ -161,6 +195,28 @@ fn convert_path(
|
|||
})
|
||||
}
|
||||
|
||||
fn convert_transformed_path(
|
||||
path: impl Iterator<Item = moscato::Element> + Clone,
|
||||
xform: &Affine,
|
||||
) -> impl Iterator<Item = Element> + Clone {
|
||||
use crate::geometry::Point;
|
||||
let xform = *xform;
|
||||
path.map(move |el| match el {
|
||||
moscato::Element::MoveTo(p0) => Element::MoveTo(Point::new(p0.x, p0.y).transform(&xform)),
|
||||
moscato::Element::LineTo(p0) => Element::LineTo(Point::new(p0.x, p0.y).transform(&xform)),
|
||||
moscato::Element::QuadTo(p0, p1) => Element::QuadTo(
|
||||
Point::new(p0.x, p0.y).transform(&xform),
|
||||
Point::new(p1.x, p1.y).transform(&xform),
|
||||
),
|
||||
moscato::Element::CurveTo(p0, p1, p2) => Element::CurveTo(
|
||||
Point::new(p0.x, p0.y).transform(&xform),
|
||||
Point::new(p1.x, p1.y).transform(&xform),
|
||||
Point::new(p2.x, p2.y).transform(&xform),
|
||||
),
|
||||
moscato::Element::Close => Element::Close,
|
||||
})
|
||||
}
|
||||
|
||||
fn convert_blend(mode: moscato::CompositeMode) -> crate::scene::Blend {
|
||||
use crate::scene::{Blend, Compose, Mix};
|
||||
use moscato::CompositeMode;
|
||||
|
|
|
@ -23,8 +23,9 @@ pub use builder::{build_fragment, build_scene, Builder};
|
|||
pub use style::*;
|
||||
|
||||
use super::brush::*;
|
||||
use super::geometry::{Affine, Rect};
|
||||
use super::geometry::{Affine, Point, Rect};
|
||||
use super::path::Element;
|
||||
|
||||
use core::ops::Range;
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -94,6 +95,12 @@ pub struct Fragment {
|
|||
resources: FragmentResources,
|
||||
}
|
||||
|
||||
impl Fragment {
|
||||
pub fn points(&self) -> &[Point] {
|
||||
bytemuck::cast_slice(&self.data.pathseg_stream)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct FragmentResources {
|
||||
patches: Vec<ResourcePatch>,
|
||||
|
|
Loading…
Reference in a new issue