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:
Chad Brokaw 2022-05-10 03:56:06 -04:00
parent b178741c19
commit 532b6ee808
10 changed files with 853 additions and 12 deletions

132
Cargo.lock generated
View file

@ -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"

View file

@ -1,6 +1,7 @@
[workspace]
members = [
"pgpu-render",
"piet-gpu",
"piet-gpu-derive",
"piet-gpu-hal",

21
pgpu-render/Cargo.toml Normal file
View 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
View 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
View 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
View 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
View 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())
}
}
}

View file

@ -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,

View file

@ -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)?;
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;

View file

@ -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>,