From c3ca624c1e5f8d1b83938ec2440033144d7269bf Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Tue, 2 May 2023 14:07:25 -0400 Subject: [PATCH 1/3] Add resolve function for path only pipeline Adds a new `resolve_simple` function that doesn't handle late bound resources (gradients, images and glyph runs). --- crates/encoding/src/lib.rs | 2 +- crates/encoding/src/resolve.rs | 59 ++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/crates/encoding/src/lib.rs b/crates/encoding/src/lib.rs index 38da4a6..529fdc6 100644 --- a/crates/encoding/src/lib.rs +++ b/crates/encoding/src/lib.rs @@ -35,4 +35,4 @@ pub use path::{ Cubic, Path, PathBbox, PathEncoder, PathMonoid, PathSegment, PathSegmentType, PathTag, Tile, }; pub use ramp_cache::Ramps; -pub use resolve::{Layout, Patch, Resolver}; +pub use resolve::{resolve_simple, Layout, Patch, Resolver}; diff --git a/crates/encoding/src/resolve.rs b/crates/encoding/src/resolve.rs index 0f72939..fa1a641 100644 --- a/crates/encoding/src/resolve.rs +++ b/crates/encoding/src/resolve.rs @@ -100,6 +100,65 @@ impl Layout { } } +/// Resolves and packs an encoding that doesn't contain late bound resources +/// (gradients, images and glyph runs). +pub fn resolve_simple(encoding: &Encoding, packed: &mut Vec) -> Layout { + assert!( + encoding.patches.is_empty(), + "this resolve function doesn't support late bound resources" + ); + let sizes = StreamOffsets::default(); + let data = packed; + data.clear(); + let mut layout = Layout { + n_paths: encoding.n_paths, + n_clips: encoding.n_clips, + ..Layout::default() + }; + // Compute size of data buffer + let n_path_tags = encoding.path_tags.len() + sizes.path_tags + encoding.n_open_clips as usize; + let path_tag_padded = align_up(n_path_tags, 4 * crate::config::PATH_REDUCE_WG); + let capacity = path_tag_padded + + slice_size_in_bytes(&encoding.path_data, sizes.path_data) + + slice_size_in_bytes( + &encoding.draw_tags, + sizes.draw_tags + encoding.n_open_clips as usize, + ) + + slice_size_in_bytes(&encoding.draw_data, sizes.draw_data) + + slice_size_in_bytes(&encoding.transforms, sizes.transforms) + + slice_size_in_bytes(&encoding.linewidths, sizes.linewidths); + data.reserve(capacity); + // Path tag stream + layout.path_tag_base = size_to_words(data.len()); + data.extend_from_slice(bytemuck::cast_slice(&encoding.path_tags)); + for _ in 0..encoding.n_open_clips { + data.extend_from_slice(bytemuck::bytes_of(&PathTag::PATH)); + } + // Path data stream + layout.path_data_base = size_to_words(data.len()); + data.extend_from_slice(bytemuck::cast_slice(&encoding.path_data)); + // Draw tag stream + layout.draw_tag_base = size_to_words(data.len()); + // Bin data follows draw info + layout.bin_data_start = encoding.draw_tags.iter().map(|tag| tag.info_size()).sum(); + data.extend_from_slice(bytemuck::cast_slice(&encoding.draw_tags)); + for _ in 0..encoding.n_open_clips { + data.extend_from_slice(bytemuck::bytes_of(&DrawTag::END_CLIP)); + } + // Draw data stream + layout.draw_data_base = size_to_words(data.len()); + data.extend_from_slice(bytemuck::cast_slice(&encoding.draw_data)); + // Transform stream + layout.transform_base = size_to_words(data.len()); + data.extend_from_slice(bytemuck::cast_slice(&encoding.transforms)); + // Linewidth stream + layout.linewidth_base = size_to_words(data.len()); + data.extend_from_slice(bytemuck::cast_slice(&encoding.linewidths)); + layout.n_draw_objects = layout.n_paths; + assert_eq!(capacity, data.len()); + layout +} + /// Resolver for late bound resources. #[derive(Default)] pub struct Resolver { From 46328c7a2c7ec449395b673d21cba7b27d5a80d6 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Tue, 2 May 2023 16:42:50 -0400 Subject: [PATCH 2/3] cleanup for review * rename resolve_simple to resolve_solid_paths_only to better capture the semantics of the function * move duplicated buffer size computation code to separate function * change Resolver::resolve to call resolve_solid_paths_only when encoding.patches.is_empty() is true. This is likely to be slightly faster and will ensure that the "simple" code path is actually used. --- crates/encoding/src/image_cache.rs | 1 + crates/encoding/src/lib.rs | 2 +- crates/encoding/src/resolve.rs | 88 ++++++++++++++++++------------ 3 files changed, 56 insertions(+), 35 deletions(-) diff --git a/crates/encoding/src/image_cache.rs b/crates/encoding/src/image_cache.rs index 85b658e..78f5cf7 100644 --- a/crates/encoding/src/image_cache.rs +++ b/crates/encoding/src/image_cache.rs @@ -8,6 +8,7 @@ use std::collections::{hash_map::Entry, HashMap}; const DEFAULT_ATLAS_SIZE: i32 = 1024; const MAX_ATLAS_SIZE: i32 = 8192; +#[derive(Default)] pub struct Images<'a> { pub width: u32, pub height: u32, diff --git a/crates/encoding/src/lib.rs b/crates/encoding/src/lib.rs index 529fdc6..748354c 100644 --- a/crates/encoding/src/lib.rs +++ b/crates/encoding/src/lib.rs @@ -35,4 +35,4 @@ pub use path::{ Cubic, Path, PathBbox, PathEncoder, PathMonoid, PathSegment, PathSegmentType, PathTag, Tile, }; pub use ramp_cache::Ramps; -pub use resolve::{resolve_simple, Layout, Patch, Resolver}; +pub use resolve::{resolve_solid_paths_only, Layout, Patch, Resolver}; diff --git a/crates/encoding/src/resolve.rs b/crates/encoding/src/resolve.rs index fa1a641..1a5ba9e 100644 --- a/crates/encoding/src/resolve.rs +++ b/crates/encoding/src/resolve.rs @@ -100,14 +100,16 @@ impl Layout { } } -/// Resolves and packs an encoding that doesn't contain late bound resources -/// (gradients, images and glyph runs). -pub fn resolve_simple(encoding: &Encoding, packed: &mut Vec) -> Layout { +/// Resolves and packs an encoding that contains only paths with solid color +/// fills. +/// +/// Panics if the encoding contains any late bound resources (gradients, images +/// or glyph runs). +pub fn resolve_solid_paths_only(encoding: &Encoding, packed: &mut Vec) -> Layout { assert!( encoding.patches.is_empty(), "this resolve function doesn't support late bound resources" ); - let sizes = StreamOffsets::default(); let data = packed; data.clear(); let mut layout = Layout { @@ -115,25 +117,18 @@ pub fn resolve_simple(encoding: &Encoding, packed: &mut Vec) -> Layout { n_clips: encoding.n_clips, ..Layout::default() }; - // Compute size of data buffer - let n_path_tags = encoding.path_tags.len() + sizes.path_tags + encoding.n_open_clips as usize; - let path_tag_padded = align_up(n_path_tags, 4 * crate::config::PATH_REDUCE_WG); - let capacity = path_tag_padded - + slice_size_in_bytes(&encoding.path_data, sizes.path_data) - + slice_size_in_bytes( - &encoding.draw_tags, - sizes.draw_tags + encoding.n_open_clips as usize, - ) - + slice_size_in_bytes(&encoding.draw_data, sizes.draw_data) - + slice_size_in_bytes(&encoding.transforms, sizes.transforms) - + slice_size_in_bytes(&encoding.linewidths, sizes.linewidths); - data.reserve(capacity); + let SceneBufferSizes { + buffer_size, + path_tag_padded, + } = SceneBufferSizes::new(encoding, &StreamOffsets::default()); + data.reserve(buffer_size); // Path tag stream layout.path_tag_base = size_to_words(data.len()); data.extend_from_slice(bytemuck::cast_slice(&encoding.path_tags)); for _ in 0..encoding.n_open_clips { data.extend_from_slice(bytemuck::bytes_of(&PathTag::PATH)); } + data.resize(path_tag_padded, 0); // Path data stream layout.path_data_base = size_to_words(data.len()); data.extend_from_slice(bytemuck::cast_slice(&encoding.path_data)); @@ -155,7 +150,7 @@ pub fn resolve_simple(encoding: &Encoding, packed: &mut Vec) -> Layout { layout.linewidth_base = size_to_words(data.len()); data.extend_from_slice(bytemuck::cast_slice(&encoding.linewidths)); layout.n_draw_objects = layout.n_paths; - assert_eq!(capacity, data.len()); + assert_eq!(buffer_size, data.len()); layout } @@ -184,7 +179,11 @@ impl Resolver { encoding: &Encoding, packed: &mut Vec, ) -> (Layout, Ramps<'a>, Images<'a>) { - let sizes = self.resolve_patches(encoding); + if encoding.patches.is_empty() { + let layout = resolve_solid_paths_only(encoding, packed); + return (layout, Ramps::default(), Images::default()); + } + let patch_sizes = self.resolve_patches(encoding); self.resolve_pending_images(); let data = packed; data.clear(); @@ -193,20 +192,11 @@ impl Resolver { n_clips: encoding.n_clips, ..Layout::default() }; - // Compute size of data buffer - let n_path_tags = - encoding.path_tags.len() + sizes.path_tags + encoding.n_open_clips as usize; - let path_tag_padded = align_up(n_path_tags, 4 * crate::config::PATH_REDUCE_WG); - let capacity = path_tag_padded - + slice_size_in_bytes(&encoding.path_data, sizes.path_data) - + slice_size_in_bytes( - &encoding.draw_tags, - sizes.draw_tags + encoding.n_open_clips as usize, - ) - + slice_size_in_bytes(&encoding.draw_data, sizes.draw_data) - + slice_size_in_bytes(&encoding.transforms, sizes.transforms) - + slice_size_in_bytes(&encoding.linewidths, sizes.linewidths); - data.reserve(capacity); + let SceneBufferSizes { + buffer_size, + path_tag_padded, + } = SceneBufferSizes::new(encoding, &patch_sizes); + data.reserve(buffer_size); // Path tag stream layout.path_tag_base = size_to_words(data.len()); { @@ -382,7 +372,7 @@ impl Resolver { } } layout.n_draw_objects = layout.n_paths; - assert_eq!(capacity, data.len()); + assert_eq!(buffer_size, data.len()); (layout, self.ramp_cache.ramps(), self.image_cache.images()) } @@ -577,6 +567,36 @@ enum ResolvedPatch { }, } +struct SceneBufferSizes { + /// Full size of the scene buffer in bytes. + buffer_size: usize, + /// Padded length of the path tag stream in bytes. + path_tag_padded: usize, +} + +impl SceneBufferSizes { + /// Computes common scene buffer sizes for the given encoding and patch + /// stream sizes. + fn new(encoding: &Encoding, patch_sizes: &StreamOffsets) -> Self { + let n_path_tags = + encoding.path_tags.len() + patch_sizes.path_tags + encoding.n_open_clips as usize; + let path_tag_padded = align_up(n_path_tags, 4 * crate::config::PATH_REDUCE_WG); + let buffer_size = path_tag_padded + + slice_size_in_bytes(&encoding.path_data, patch_sizes.path_data) + + slice_size_in_bytes( + &encoding.draw_tags, + patch_sizes.draw_tags + encoding.n_open_clips as usize, + ) + + slice_size_in_bytes(&encoding.draw_data, patch_sizes.draw_data) + + slice_size_in_bytes(&encoding.transforms, patch_sizes.transforms) + + slice_size_in_bytes(&encoding.linewidths, patch_sizes.linewidths); + Self { + buffer_size, + path_tag_padded, + } + } +} + fn slice_size_in_bytes(slice: &[T], extra: usize) -> usize { (slice.len() + extra) * std::mem::size_of::() } From c68d011c7c27cb00ff3146ba11e37b120cf03c44 Mon Sep 17 00:00:00 2001 From: Chad Brokaw Date: Wed, 3 May 2023 12:22:51 -0400 Subject: [PATCH 3/3] feature gate full pipeline encoding support Adds a new feature called "full" (on by default) that enables encoding support for the full pipeline. --- crates/encoding/Cargo.toml | 10 +- crates/encoding/src/config.rs | 1 + crates/encoding/src/encoding.rs | 189 ++++++++++++++++++++------------ crates/encoding/src/lib.rs | 16 ++- crates/encoding/src/math.rs | 1 + crates/encoding/src/path.rs | 1 + crates/encoding/src/resolve.rs | 59 ++++++---- src/scene.rs | 25 +++-- 8 files changed, 192 insertions(+), 110 deletions(-) diff --git a/crates/encoding/Cargo.toml b/crates/encoding/Cargo.toml index f11c4bc..538dc36 100644 --- a/crates/encoding/Cargo.toml +++ b/crates/encoding/Cargo.toml @@ -3,8 +3,14 @@ name = "vello_encoding" version = "0.1.0" edition = "2021" +[features] +default = ["full"] +# Enables support for the full pipeline including late-bound +# resources (gradients, images and glyph runs) +full = ["fello", "guillotiere"] + [dependencies] bytemuck = { workspace = true } -fello = { workspace = true } +fello = { workspace = true, optional = true } peniko = { workspace = true } -guillotiere = "0.6.2" +guillotiere = { version = "0.6.2", optional = true } diff --git a/crates/encoding/src/config.rs b/crates/encoding/src/config.rs index ddd7e2d..2d3dba4 100644 --- a/crates/encoding/src/config.rs +++ b/crates/encoding/src/config.rs @@ -196,6 +196,7 @@ impl BufferSize { } /// Returns the number of elements. + #[allow(clippy::len_without_is_empty)] pub const fn len(self) -> u32 { self.len } diff --git a/crates/encoding/src/encoding.rs b/crates/encoding/src/encoding.rs index 65dcbbb..9834e64 100644 --- a/crates/encoding/src/encoding.rs +++ b/crates/encoding/src/encoding.rs @@ -1,13 +1,16 @@ // Copyright 2022 The Vello authors // SPDX-License-Identifier: Apache-2.0 OR MIT -use super::{ - resolve::Patch, DrawColor, DrawImage, DrawLinearGradient, DrawRadialGradient, DrawTag, Glyph, - GlyphRun, PathEncoder, PathTag, Transform, -}; +use super::{DrawColor, DrawTag, PathEncoder, PathTag, Transform}; -use fello::NormalizedCoord; -use peniko::{kurbo::Shape, BlendMode, BrushRef, ColorStop, Extend, GradientKind, Image}; +use peniko::{kurbo::Shape, BlendMode, BrushRef}; + +#[cfg(feature = "full")] +use { + super::{DrawImage, DrawLinearGradient, DrawRadialGradient, Glyph, GlyphRun, Patch}, + fello::NormalizedCoord, + peniko::{ColorStop, Extend, GradientKind, Image}, +}; /// Encoded data streams for a scene. #[derive(Clone, Default)] @@ -20,20 +23,13 @@ pub struct Encoding { pub draw_tags: Vec, /// The draw data stream. pub draw_data: Vec, - /// Draw data patches for late bound resources. - pub patches: Vec, - /// Color stop collection for gradients. - pub color_stops: Vec, /// The transform stream. pub transforms: Vec, /// The line width stream. pub linewidths: Vec, - /// Positioned glyph buffer. - pub glyphs: Vec, - /// Sequences of glyphs. - pub glyph_runs: Vec, - /// Normalized coordinate buffer for variable fonts. - pub normalized_coords: Vec, + /// Late bound resource data. + #[cfg(feature = "full")] + pub resources: Resources, /// Number of encoded paths. pub n_paths: u32, /// Number of encoded path segments. @@ -63,15 +59,12 @@ impl Encoding { self.linewidths.clear(); self.draw_data.clear(); self.draw_tags.clear(); - self.glyphs.clear(); - self.glyph_runs.clear(); - self.normalized_coords.clear(); self.n_paths = 0; self.n_path_segments = 0; self.n_clips = 0; self.n_open_clips = 0; - self.patches.clear(); - self.color_stops.clear(); + #[cfg(feature = "full")] + self.resources.reset(); if !is_fragment { self.transforms.push(Transform::IDENTITY); self.linewidths.push(-1.0); @@ -80,62 +73,74 @@ impl Encoding { /// Appends another encoding to this one with an optional transform. pub fn append(&mut self, other: &Self, transform: &Option) { - let stops_base = self.color_stops.len(); - let glyph_runs_base = self.glyph_runs.len(); - let glyphs_base = self.glyphs.len(); - let coords_base = self.normalized_coords.len(); - let offsets = self.stream_offsets(); + #[cfg(feature = "full")] + let glyph_runs_base = { + let offsets = self.stream_offsets(); + let stops_base = self.resources.color_stops.len(); + let glyph_runs_base = self.resources.glyph_runs.len(); + let glyphs_base = self.resources.glyphs.len(); + let coords_base = self.resources.normalized_coords.len(); + self.resources + .glyphs + .extend_from_slice(&other.resources.glyphs); + self.resources + .normalized_coords + .extend_from_slice(&other.resources.normalized_coords); + self.resources + .glyph_runs + .extend(other.resources.glyph_runs.iter().cloned().map(|mut run| { + run.glyphs.start += glyphs_base; + run.normalized_coords.start += coords_base; + run.stream_offsets.path_tags += offsets.path_tags; + run.stream_offsets.path_data += offsets.path_data; + run.stream_offsets.draw_tags += offsets.draw_tags; + run.stream_offsets.draw_data += offsets.draw_data; + run.stream_offsets.transforms += offsets.transforms; + run.stream_offsets.linewidths += offsets.linewidths; + run + })); + self.resources + .patches + .extend(other.resources.patches.iter().map(|patch| match patch { + Patch::Ramp { + draw_data_offset: offset, + stops, + } => { + let stops = stops.start + stops_base..stops.end + stops_base; + Patch::Ramp { + draw_data_offset: offset + offsets.draw_data, + stops, + } + } + Patch::GlyphRun { index } => Patch::GlyphRun { + index: index + glyph_runs_base, + }, + Patch::Image { + image, + draw_data_offset, + } => Patch::Image { + image: image.clone(), + draw_data_offset: *draw_data_offset + offsets.draw_data, + }, + })); + self.resources + .color_stops + .extend_from_slice(&other.resources.color_stops); + glyph_runs_base + }; self.path_tags.extend_from_slice(&other.path_tags); self.path_data.extend_from_slice(&other.path_data); self.draw_tags.extend_from_slice(&other.draw_tags); self.draw_data.extend_from_slice(&other.draw_data); - self.glyphs.extend_from_slice(&other.glyphs); - self.normalized_coords - .extend_from_slice(&other.normalized_coords); - self.glyph_runs - .extend(other.glyph_runs.iter().cloned().map(|mut run| { - run.glyphs.start += glyphs_base; - run.normalized_coords.start += coords_base; - run.stream_offsets.path_tags += offsets.path_tags; - run.stream_offsets.path_data += offsets.path_data; - run.stream_offsets.draw_tags += offsets.draw_tags; - run.stream_offsets.draw_data += offsets.draw_data; - run.stream_offsets.transforms += offsets.transforms; - run.stream_offsets.linewidths += offsets.linewidths; - run - })); self.n_paths += other.n_paths; self.n_path_segments += other.n_path_segments; self.n_clips += other.n_clips; self.n_open_clips += other.n_open_clips; - self.patches - .extend(other.patches.iter().map(|patch| match patch { - Patch::Ramp { - draw_data_offset: offset, - stops, - } => { - let stops = stops.start + stops_base..stops.end + stops_base; - Patch::Ramp { - draw_data_offset: offset + offsets.draw_data, - stops, - } - } - Patch::GlyphRun { index } => Patch::GlyphRun { - index: index + glyph_runs_base, - }, - Patch::Image { - image, - draw_data_offset, - } => Patch::Image { - image: image.clone(), - draw_data_offset: *draw_data_offset + offsets.draw_data, - }, - })); - self.color_stops.extend_from_slice(&other.color_stops); if let Some(transform) = *transform { self.transforms .extend(other.transforms.iter().map(|x| transform * *x)); - for run in &mut self.glyph_runs[glyph_runs_base..] { + #[cfg(feature = "full")] + for run in &mut self.resources.glyph_runs[glyph_runs_base..] { run.transform = transform * run.transform; } } else { @@ -199,7 +204,9 @@ impl Encoding { } /// Encodes a brush with an optional alpha modifier. + #[allow(unused_variables)] pub fn encode_brush<'b>(&mut self, brush: impl Into>, alpha: f32) { + #[cfg(feature = "full")] use super::math::point_to_f32; match brush.into() { BrushRef::Solid(color) => { @@ -210,6 +217,7 @@ impl Encoding { }; self.encode_color(DrawColor::new(color)); } + #[cfg(feature = "full")] BrushRef::Gradient(gradient) => match gradient.kind { GradientKind::Linear { start, end } => { self.encode_linear_gradient( @@ -246,9 +254,13 @@ impl Encoding { todo!("sweep gradients aren't supported yet!") } }, + #[cfg(feature = "full")] BrushRef::Image(image) => { + #[cfg(feature = "full")] self.encode_image(image, alpha); } + #[cfg(not(feature = "full"))] + _ => panic!("brushes other than solid require the 'full' feature to be enabled"), } } @@ -259,6 +271,7 @@ impl Encoding { } /// Encodes a linear gradient brush. + #[cfg(feature = "full")] pub fn encode_linear_gradient( &mut self, gradient: DrawLinearGradient, @@ -273,6 +286,7 @@ impl Encoding { } /// Encodes a radial gradient brush. + #[cfg(feature = "full")] pub fn encode_radial_gradient( &mut self, gradient: DrawRadialGradient, @@ -287,10 +301,11 @@ impl Encoding { } /// Encodes an image brush. + #[cfg(feature = "full")] pub fn encode_image(&mut self, image: &Image, _alpha: f32) { // TODO: feed the alpha multiplier through the full pipeline for consistency // with other brushes? - self.patches.push(Patch::Image { + self.resources.patches.push(Patch::Image { image: image.clone(), draw_data_offset: self.draw_data.len(), }); @@ -331,22 +346,51 @@ impl Encoding { self.path_tags.swap(len - 1, len - 2); } + #[cfg(feature = "full")] fn add_ramp(&mut self, color_stops: impl Iterator, alpha: f32) { let offset = self.draw_data.len(); - let stops_start = self.color_stops.len(); + let stops_start = self.resources.color_stops.len(); if alpha != 1.0 { - self.color_stops + self.resources + .color_stops .extend(color_stops.map(|stop| stop.with_alpha_factor(alpha))); } else { - self.color_stops.extend(color_stops); + self.resources.color_stops.extend(color_stops); } - self.patches.push(Patch::Ramp { + self.resources.patches.push(Patch::Ramp { draw_data_offset: offset, - stops: stops_start..self.color_stops.len(), + stops: stops_start..self.resources.color_stops.len(), }); } } +/// Encoded data for late bound resources. +#[cfg(feature = "full")] +#[derive(Clone, Default)] +pub struct Resources { + /// Draw data patches for late bound resources. + pub patches: Vec, + /// Color stop collection for gradients. + pub color_stops: Vec, + /// Positioned glyph buffer. + pub glyphs: Vec, + /// Sequences of glyphs. + pub glyph_runs: Vec, + /// Normalized coordinate buffer for variable fonts. + pub normalized_coords: Vec, +} + +#[cfg(feature = "full")] +impl Resources { + fn reset(&mut self) { + self.patches.clear(); + self.color_stops.clear(); + self.glyphs.clear(); + self.glyph_runs.clear(); + self.normalized_coords.clear(); + } +} + /// Snapshot of offsets for encoded streams. #[derive(Copy, Clone, Default, Debug)] pub struct StreamOffsets { @@ -365,6 +409,7 @@ pub struct StreamOffsets { } impl StreamOffsets { + #[cfg(feature = "full")] pub(crate) fn add(&mut self, other: &Self) { self.path_tags += other.path_tags; self.path_data += other.path_data; diff --git a/crates/encoding/src/lib.rs b/crates/encoding/src/lib.rs index 748354c..62a7d6c 100644 --- a/crates/encoding/src/lib.rs +++ b/crates/encoding/src/lib.rs @@ -8,12 +8,16 @@ mod clip; mod config; mod draw; mod encoding; +#[cfg(feature = "full")] mod glyph; +#[cfg(feature = "full")] mod glyph_cache; +#[cfg(feature = "full")] mod image_cache; mod math; mod monoid; mod path; +#[cfg(feature = "full")] mod ramp_cache; mod resolve; @@ -28,11 +32,17 @@ pub use draw::{ DrawRadialGradient, DrawTag, }; pub use encoding::{Encoding, StreamOffsets}; -pub use glyph::{Glyph, GlyphRun}; pub use math::Transform; pub use monoid::Monoid; pub use path::{ Cubic, Path, PathBbox, PathEncoder, PathMonoid, PathSegment, PathSegmentType, PathTag, Tile, }; -pub use ramp_cache::Ramps; -pub use resolve::{resolve_solid_paths_only, Layout, Patch, Resolver}; +pub use resolve::{resolve_solid_paths_only, Layout}; + +#[cfg(feature = "full")] +pub use { + encoding::Resources, + glyph::{Glyph, GlyphRun}, + ramp_cache::Ramps, + resolve::{Patch, Resolver}, +}; diff --git a/crates/encoding/src/math.rs b/crates/encoding/src/math.rs index 9ab9935..5873fba 100644 --- a/crates/encoding/src/math.rs +++ b/crates/encoding/src/math.rs @@ -72,6 +72,7 @@ impl Mul for Transform { } } +#[allow(dead_code)] pub fn point_to_f32(point: kurbo::Point) -> [f32; 2] { [point.x as f32, point.y as f32] } diff --git a/crates/encoding/src/path.rs b/crates/encoding/src/path.rs index 6c3cc5f..10e7499 100644 --- a/crates/encoding/src/path.rs +++ b/crates/encoding/src/path.rs @@ -403,6 +403,7 @@ impl<'a> PathEncoder<'a> { } } +#[cfg(feature = "full")] impl fello::scale::Pen for PathEncoder<'_> { fn move_to(&mut self, x: f32, y: f32) { self.move_to(x, y) diff --git a/crates/encoding/src/resolve.rs b/crates/encoding/src/resolve.rs index 1a5ba9e..ee95628 100644 --- a/crates/encoding/src/resolve.rs +++ b/crates/encoding/src/resolve.rs @@ -1,16 +1,19 @@ // Copyright 2022 The Vello authors // SPDX-License-Identifier: Apache-2.0 OR MIT -use std::ops::Range; - use bytemuck::{Pod, Zeroable}; -use peniko::Image; -use super::{ - glyph_cache::{CachedRange, GlyphCache, GlyphKey}, - image_cache::{ImageCache, Images}, - ramp_cache::{RampCache, Ramps}, - DrawTag, Encoding, PathTag, StreamOffsets, Transform, +use super::{DrawTag, Encoding, PathTag, StreamOffsets, Transform}; + +#[cfg(feature = "full")] +use { + super::{ + glyph_cache::{CachedRange, GlyphCache, GlyphKey}, + image_cache::{ImageCache, Images}, + ramp_cache::{RampCache, Ramps}, + }, + peniko::Image, + std::ops::Range, }; /// Layout of a packed encoding. @@ -106,8 +109,9 @@ impl Layout { /// Panics if the encoding contains any late bound resources (gradients, images /// or glyph runs). pub fn resolve_solid_paths_only(encoding: &Encoding, packed: &mut Vec) -> Layout { + #[cfg(feature = "full")] assert!( - encoding.patches.is_empty(), + encoding.resources.patches.is_empty(), "this resolve function doesn't support late bound resources" ); let data = packed; @@ -155,6 +159,7 @@ pub fn resolve_solid_paths_only(encoding: &Encoding, packed: &mut Vec) -> La } /// Resolver for late bound resources. +#[cfg(feature = "full")] #[derive(Default)] pub struct Resolver { glyph_cache: GlyphCache, @@ -166,6 +171,7 @@ pub struct Resolver { patches: Vec, } +#[cfg(feature = "full")] impl Resolver { /// Creates a new resource cache. pub fn new() -> Self { @@ -179,7 +185,8 @@ impl Resolver { encoding: &Encoding, packed: &mut Vec, ) -> (Layout, Ramps<'a>, Images<'a>) { - if encoding.patches.is_empty() { + let resources = &encoding.resources; + if resources.patches.is_empty() { let layout = resolve_solid_paths_only(encoding, packed); return (layout, Ramps::default(), Images::default()); } @@ -205,7 +212,7 @@ impl Resolver { for patch in &self.patches { if let ResolvedPatch::GlyphRun { index, glyphs, .. } = patch { layout.n_paths += 1; - let stream_offset = encoding.glyph_runs[*index].stream_offsets.path_tags; + let stream_offset = resources.glyph_runs[*index].stream_offsets.path_tags; if pos < stream_offset { data.extend_from_slice(bytemuck::cast_slice(&stream[pos..stream_offset])); pos = stream_offset; @@ -234,7 +241,9 @@ impl Resolver { let stream = &encoding.path_data; for patch in &self.patches { if let ResolvedPatch::GlyphRun { index, glyphs, .. } = patch { - let stream_offset = encoding.glyph_runs[*index].stream_offsets.path_data; + let stream_offset = encoding.resources.glyph_runs[*index] + .stream_offsets + .path_data; if pos < stream_offset { data.extend_from_slice(bytemuck::cast_slice(&stream[pos..stream_offset])); pos = stream_offset; @@ -316,14 +325,14 @@ impl Resolver { transform, } = patch { - let run = &encoding.glyph_runs[*index]; - let stream_offset = encoding.glyph_runs[*index].stream_offsets.transforms; + let run = &resources.glyph_runs[*index]; + let stream_offset = run.stream_offsets.transforms; if pos < stream_offset { data.extend_from_slice(bytemuck::cast_slice(&stream[pos..stream_offset])); pos = stream_offset; } if let Some(glyph_transform) = run.glyph_transform { - for glyph in &encoding.glyphs[run.glyphs.clone()] { + for glyph in &resources.glyphs[run.glyphs.clone()] { let xform = *transform * Transform { matrix: [1.0, 0.0, 0.0, -1.0], @@ -333,7 +342,7 @@ impl Resolver { data.extend_from_slice(bytemuck::bytes_of(&xform)); } } else { - for glyph in &encoding.glyphs[run.glyphs.clone()] { + for glyph in &resources.glyphs[run.glyphs.clone()] { let xform = *transform * Transform { matrix: [1.0, 0.0, 0.0, -1.0], @@ -355,7 +364,7 @@ impl Resolver { let stream = &encoding.linewidths; for patch in &self.patches { if let ResolvedPatch::GlyphRun { index, glyphs, .. } = patch { - let stream_offset = encoding.glyph_runs[*index].stream_offsets.linewidths; + let stream_offset = resources.glyph_runs[*index].stream_offsets.linewidths; if pos < stream_offset { data.extend_from_slice(bytemuck::cast_slice(&stream[pos..stream_offset])); pos = stream_offset; @@ -384,13 +393,14 @@ impl Resolver { self.pending_images.clear(); self.patches.clear(); let mut sizes = StreamOffsets::default(); - for patch in &encoding.patches { + let resources = &encoding.resources; + for patch in &resources.patches { match patch { Patch::Ramp { draw_data_offset, stops, } => { - let ramp_id = self.ramp_cache.add(&encoding.color_stops[stops.clone()]); + let ramp_id = self.ramp_cache.add(&resources.color_stops[stops.clone()]); self.patches.push(ResolvedPatch::Ramp { draw_data_offset: *draw_data_offset + sizes.draw_data, ramp_id, @@ -398,7 +408,7 @@ impl Resolver { } Patch::GlyphRun { index } => { let mut run_sizes = StreamOffsets::default(); - let run = &encoding.glyph_runs[*index]; + let run = &resources.glyph_runs[*index]; let font_id = run.font.data.id(); let font_size_u32 = run.font_size.to_bits(); let Ok(font_file) = fello::raw::FileRef::new(run.font.data.as_ref()) else { continue }; @@ -409,8 +419,8 @@ impl Resolver { } }; let Some(font) = font else { continue }; - let glyphs = &encoding.glyphs[run.glyphs.clone()]; - let coords = &encoding.normalized_coords[run.normalized_coords.clone()]; + let glyphs = &resources.glyphs[run.glyphs.clone()]; + let coords = &resources.normalized_coords[run.normalized_coords.clone()]; let key = fello::FontKey { data_id: font_id, index: run.font.index, @@ -512,8 +522,9 @@ impl Resolver { } } -#[derive(Clone)] /// Patch for a late bound resource. +#[cfg(feature = "full")] +#[derive(Clone)] pub enum Patch { /// Gradient ramp resource. Ramp { @@ -537,12 +548,14 @@ pub enum Patch { } /// Image to be allocated in the atlas. +#[cfg(feature = "full")] #[derive(Clone, Debug)] struct PendingImage { image: Image, xy: Option<(u32, u32)>, } +#[cfg(feature = "full")] #[derive(Clone, Debug)] enum ResolvedPatch { Ramp { diff --git a/src/scene.rs b/src/scene.rs index 647bf89..02e4006 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -205,8 +205,8 @@ impl<'a> DrawGlyphs<'a> { /// Creates a new builder for encoding a glyph run for the specified /// encoding with the given font. pub fn new(encoding: &'a mut Encoding, font: &Font) -> Self { - let coords_start = encoding.normalized_coords.len(); - let glyphs_start = encoding.glyphs.len(); + let coords_start = encoding.resources.normalized_coords.len(); + let glyphs_start = encoding.resources.glyphs.len(); let stream_offsets = encoding.stream_offsets(); Self { encoding, @@ -264,10 +264,14 @@ impl<'a> DrawGlyphs<'a> { /// Sets the normalized design space coordinates for a variable font instance. pub fn normalized_coords(mut self, coords: &[NormalizedCoord]) -> Self { self.encoding + .resources .normalized_coords .truncate(self.run.normalized_coords.start); - self.encoding.normalized_coords.extend_from_slice(coords); - self.run.normalized_coords.end = self.encoding.normalized_coords.len(); + self.encoding + .resources + .normalized_coords + .extend_from_slice(coords); + self.run.normalized_coords.end = self.encoding.resources.normalized_coords.len(); self } @@ -292,18 +296,19 @@ impl<'a> DrawGlyphs<'a> { /// /// The `style` parameter accepts either `Fill` or `&Stroke` types. pub fn draw(mut self, style: impl Into>, glyphs: impl Iterator) { + let resources = &mut self.encoding.resources; self.run.style = style.into().to_owned(); - self.encoding.glyphs.extend(glyphs); - self.run.glyphs.end = self.encoding.glyphs.len(); + resources.glyphs.extend(glyphs); + self.run.glyphs.end = resources.glyphs.len(); if self.run.glyphs.is_empty() { - self.encoding + resources .normalized_coords .truncate(self.run.normalized_coords.start); return; } - let index = self.encoding.glyph_runs.len(); - self.encoding.glyph_runs.push(self.run); - self.encoding.patches.push(Patch::GlyphRun { index }); + let index = resources.glyph_runs.len(); + resources.glyph_runs.push(self.run); + resources.patches.push(Patch::GlyphRun { index }); self.encoding.encode_brush(self.brush, self.brush_alpha); } }