1 // Copyright 2023 Google LLC
2 //
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5
6 use crate::ffi;
7 use {
8 peniko::{
9 kurbo::{Affine, Cap, Join, PathEl, Point, Stroke},
10 Brush, Color, Fill, Mix,
11 },
12 std::pin::Pin,
13 vello_encoding::{
14 BumpEstimator, Encoding as VelloEncoding, PathEncoder, RenderConfig, Transform,
15 },
16 };
17
18 pub(crate) struct Encoding {
19 encoding: VelloEncoding,
20 estimator: BumpEstimator,
21 }
22
new_encoding() -> Box<Encoding>23 pub(crate) fn new_encoding() -> Box<Encoding> {
24 Box::new(Encoding::new())
25 }
26
27 impl Encoding {
new() -> Encoding28 fn new() -> Encoding {
29 // An encoding blob that doesn't represent a scene fragment (i.e. a reused blob that is
30 // appended to a root encoding), then we need to initialize the transform and linewidth
31 // streams with first entries (an identity transform and -1 linewidth value). Resetting
32 // the encoding as non-fragment achieves this.
33 let mut encoding = VelloEncoding::new();
34 encoding.reset();
35 Encoding { encoding, estimator: BumpEstimator::new(), }
36 }
37
is_empty(&self) -> bool38 pub fn is_empty(&self) -> bool {
39 self.encoding.is_empty()
40 }
41
reset(&mut self)42 pub fn reset(&mut self) {
43 self.encoding.reset();
44 self.estimator.reset();
45 }
46
fill( &mut self, style: ffi::Fill, transform: ffi::Affine, brush: &ffi::Brush, path_iter: Pin<&mut ffi::PathIterator>, )47 pub fn fill(
48 &mut self,
49 style: ffi::Fill,
50 transform: ffi::Affine,
51 brush: &ffi::Brush,
52 path_iter: Pin<&mut ffi::PathIterator>,
53 ) {
54 let t = Transform::from_kurbo(&transform.into());
55 self.encoding.encode_transform(t);
56 self.encoding.encode_fill_style(style.into());
57 if self.encode_path(path_iter, &t, None) {
58 self.encoding.encode_brush(&Brush::from(brush), 1.0)
59 }
60 }
61
stroke( &mut self, style: &ffi::Stroke, transform: ffi::Affine, brush: &ffi::Brush, path_iter: Pin<&mut ffi::PathIterator>, )62 pub fn stroke(
63 &mut self,
64 style: &ffi::Stroke,
65 transform: ffi::Affine,
66 brush: &ffi::Brush,
67 path_iter: Pin<&mut ffi::PathIterator>,
68 ) {
69 let t = Transform::from_kurbo(&transform.into());
70 self.encoding.encode_transform(t);
71
72 // TODO: process any dash pattern here using kurbo's dash expander unless Graphite
73 // handles dashing already.
74 let stroke = style.into();
75 self.encoding.encode_stroke_style(&stroke);
76 if self.encode_path(path_iter, &t, Some(&stroke)) {
77 self.encoding.encode_brush(&Brush::from(brush), 1.0);
78 }
79 }
80
begin_clip(&mut self, transform: ffi::Affine, path_iter: Pin<&mut ffi::PathIterator>)81 pub fn begin_clip(&mut self, transform: ffi::Affine, path_iter: Pin<&mut ffi::PathIterator>) {
82 let t = Transform::from_kurbo(&transform.into());
83 self.encoding.encode_transform(t);
84 self.encoding.encode_fill_style(Fill::NonZero);
85 self.encode_path(path_iter, &t, None);
86 self.encoding.encode_begin_clip(Mix::Clip.into(), /*alpha=*/ 1.0);
87 }
88
end_clip(&mut self)89 pub fn end_clip(&mut self) {
90 self.encoding.encode_end_clip();
91 }
92
append(&mut self, other: &Encoding)93 pub fn append(&mut self, other: &Encoding) {
94 self.encoding.append(&other.encoding, &None);
95 self.estimator.append(&other.estimator, None);
96 }
97
prepare_render( &self, width: u32, height: u32, background: &ffi::Color, ) -> Box<RenderConfiguration>98 pub fn prepare_render(
99 &self,
100 width: u32,
101 height: u32,
102 background: &ffi::Color,
103 ) -> Box<RenderConfiguration> {
104 let mut packed_scene = Vec::new();
105 let layout = vello_encoding::resolve_solid_paths_only(&self.encoding, &mut packed_scene);
106 let mut config = RenderConfig::new(&layout, width, height, &background.into());
107
108 let bump_estimate = self.estimator.tally(None);
109 //println!("bump: {bump_estimate}");
110 config.buffer_sizes.bin_data = bump_estimate.binning;
111 config.buffer_sizes.seg_counts = bump_estimate.seg_counts;
112 config.buffer_sizes.segments = bump_estimate.segments;
113 config.buffer_sizes.lines = bump_estimate.lines;
114 config.gpu.binning_size = bump_estimate.binning.len();
115 config.gpu.seg_counts_size = bump_estimate.seg_counts.len();
116 config.gpu.segments_size = bump_estimate.segments.len();
117 config.gpu.lines_size = bump_estimate.lines.len();
118
119 Box::new(RenderConfiguration {
120 packed_scene,
121 config,
122 })
123 }
124
encode_path( &mut self, iter: Pin<&mut ffi::PathIterator>, transform: &Transform, stroke: Option<&Stroke>, ) -> bool125 fn encode_path(
126 &mut self,
127 iter: Pin<&mut ffi::PathIterator>,
128 transform: &Transform,
129 stroke: Option<&Stroke>,
130 ) -> bool {
131 let mut encoder = self.encoding.encode_path(/*is_fill=*/ stroke.is_none());
132
133 // Wrap the input iterator inside a custom iterator, so that the path gets
134 // encoded as the estimator runs through it.
135 let path = IterablePathEncoder { iter, encoder: &mut encoder };
136 self.estimator.count_path(path, transform, stroke);
137 encoder.finish(/*insert_path_marker=*/ true) != 0
138 }
139 }
140
141 // This is path element iterator that encodes path elements as it gets polled.
142 struct IterablePathEncoder<'a, 'b> {
143 iter: Pin<&'a mut ffi::PathIterator>,
144 encoder: &'a mut PathEncoder<'b>,
145 }
146
147 impl Iterator for IterablePathEncoder<'_, '_> {
148 type Item = PathEl;
149
next(&mut self) -> Option<Self::Item>150 fn next(&mut self) -> Option<Self::Item> {
151 let mut path_el = ffi::PathElement::default();
152 if !unsafe { self.iter.as_mut().next_element(&mut path_el) } {
153 return None;
154 }
155 Some(match path_el.verb {
156 ffi::PathVerb::MoveTo => {
157 let p = &path_el.points[0];
158 self.encoder.move_to(p.x, p.y);
159 PathEl::MoveTo(p.into())
160 }
161 ffi::PathVerb::LineTo => {
162 let p = &path_el.points[1];
163 self.encoder.line_to(p.x, p.y);
164 PathEl::LineTo(p.into())
165 }
166 ffi::PathVerb::QuadTo => {
167 let p0 = &path_el.points[1];
168 let p1 = &path_el.points[2];
169 self.encoder.quad_to(p0.x, p0.y, p1.x, p1.y);
170 PathEl::QuadTo(p0.into(), p1.into())
171 }
172 ffi::PathVerb::CurveTo => {
173 let p0 = &path_el.points[1];
174 let p1 = &path_el.points[2];
175 let p2 = &path_el.points[3];
176 self.encoder.cubic_to(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y);
177 PathEl::CurveTo(p0.into(), p1.into(), p2.into())
178 }
179 ffi::PathVerb::Close => {
180 self.encoder.close();
181 PathEl::ClosePath
182 }
183 _ => panic!("invalid path verb"),
184 })
185 }
186 }
187
188 pub(crate) struct RenderConfiguration {
189 packed_scene: Vec<u8>,
190 config: RenderConfig,
191 }
192
193 impl RenderConfiguration {
config_uniform_buffer_size(self: &RenderConfiguration) -> usize194 pub fn config_uniform_buffer_size(self: &RenderConfiguration) -> usize {
195 std::mem::size_of::<vello_encoding::ConfigUniform>()
196 }
197
scene_buffer_size(self: &RenderConfiguration) -> usize198 pub fn scene_buffer_size(self: &RenderConfiguration) -> usize {
199 self.packed_scene.len()
200 }
201
write_config_uniform_buffer(self: &RenderConfiguration, out_buffer: &mut [u8]) -> bool202 pub fn write_config_uniform_buffer(self: &RenderConfiguration, out_buffer: &mut [u8]) -> bool {
203 let bytes = bytemuck::bytes_of(&self.config.gpu);
204 if out_buffer.len() < bytes.len() {
205 return false;
206 }
207 out_buffer.copy_from_slice(bytes);
208 true
209 }
210
write_scene_buffer(self: &RenderConfiguration, out_buffer: &mut [u8]) -> bool211 pub fn write_scene_buffer(self: &RenderConfiguration, out_buffer: &mut [u8]) -> bool {
212 if out_buffer.len() < self.packed_scene.len() {
213 return false;
214 }
215 out_buffer.copy_from_slice(&self.packed_scene);
216 true
217 }
218
workgroup_counts(self: &RenderConfiguration) -> ffi::DispatchInfo219 pub fn workgroup_counts(self: &RenderConfiguration) -> ffi::DispatchInfo {
220 (&self.config.workgroup_counts).into()
221 }
222
buffer_sizes(self: &RenderConfiguration) -> ffi::BufferSizes223 pub fn buffer_sizes(self: &RenderConfiguration) -> ffi::BufferSizes {
224 (&self.config.buffer_sizes).into()
225 }
226 }
227
228 impl Iterator for Pin<&mut ffi::PathIterator> {
229 type Item = PathEl;
230
next(&mut self) -> Option<PathEl>231 fn next(&mut self) -> Option<PathEl> {
232 let mut path_el = ffi::PathElement::default();
233 if !unsafe { self.as_mut().next_element(&mut path_el) } {
234 return None;
235 }
236 Some(match path_el.verb {
237 ffi::PathVerb::MoveTo => {
238 let p = &path_el.points[0];
239 PathEl::MoveTo(p.into())
240 }
241 ffi::PathVerb::LineTo => {
242 let p = &path_el.points[1];
243 PathEl::LineTo(p.into())
244 }
245 ffi::PathVerb::QuadTo => {
246 let p0 = &path_el.points[1];
247 let p1 = &path_el.points[2];
248 PathEl::QuadTo(p0.into(), p1.into())
249 }
250 ffi::PathVerb::CurveTo => {
251 let p0 = &path_el.points[1];
252 let p1 = &path_el.points[2];
253 let p2 = &path_el.points[3];
254 PathEl::CurveTo(p0.into(), p1.into(), p2.into())
255 }
256 ffi::PathVerb::Close => PathEl::ClosePath,
257 _ => panic!("invalid path verb"),
258 })
259 }
260 }
261
262 impl From<&ffi::Point> for Point {
from(src: &ffi::Point) -> Self263 fn from(src: &ffi::Point) -> Self {
264 Self::new(src.x.into(), src.y.into())
265 }
266 }
267
268 impl Default for ffi::PathVerb {
default() -> Self269 fn default() -> Self {
270 Self::MoveTo
271 }
272 }
273
274 impl From<ffi::Affine> for Affine {
from(src: ffi::Affine) -> Self275 fn from(src: ffi::Affine) -> Self {
276 Self::new([
277 src.matrix[0] as f64,
278 src.matrix[1] as f64,
279 src.matrix[2] as f64,
280 src.matrix[3] as f64,
281 src.matrix[4] as f64,
282 src.matrix[5] as f64,
283 ])
284 }
285 }
286
287 impl From<&ffi::Color> for Color {
from(src: &ffi::Color) -> Self288 fn from(src: &ffi::Color) -> Self {
289 Self {
290 r: src.r,
291 g: src.g,
292 b: src.b,
293 a: src.a,
294 }
295 }
296 }
297
298 impl From<&ffi::Brush> for Brush {
from(src: &ffi::Brush) -> Self299 fn from(src: &ffi::Brush) -> Self {
300 match src.kind {
301 ffi::BrushKind::Solid => Brush::Solid(Color::from(&src.data.solid)),
302 _ => panic!("invalid brush kind"),
303 }
304 }
305 }
306
307 impl From<ffi::Fill> for Fill {
from(src: ffi::Fill) -> Self308 fn from(src: ffi::Fill) -> Self {
309 match src {
310 ffi::Fill::NonZero => Self::NonZero,
311 ffi::Fill::EvenOdd => Self::EvenOdd,
312 _ => panic!("invalid fill type"),
313 }
314 }
315 }
316
317 impl From<&ffi::Stroke> for Stroke {
from(src: &ffi::Stroke) -> Self318 fn from(src: &ffi::Stroke) -> Self {
319 let cap = match src.cap {
320 ffi::CapStyle::Butt => Cap::Butt,
321 ffi::CapStyle::Square => Cap::Square,
322 ffi::CapStyle::Round => Cap::Round,
323 _ => panic!("invalid cap style"),
324 };
325 Self {
326 width: src.width as f64,
327 join: match src.join {
328 ffi::JoinStyle::Bevel => Join::Bevel,
329 ffi::JoinStyle::Miter => Join::Miter,
330 ffi::JoinStyle::Round => Join::Round,
331 _ => panic!("invalid join style"),
332 },
333 miter_limit: src.miter_limit as f64,
334 start_cap: cap,
335 end_cap: cap,
336 // Skia expands a dash effect by transforming the encoded path, so don't need to handle
337 // that here.
338 dash_pattern: Default::default(),
339 dash_offset: 0.,
340 }
341 }
342 }
343
344 impl From<&vello_encoding::WorkgroupSize> for ffi::WorkgroupSize {
from(src: &vello_encoding::WorkgroupSize) -> Self345 fn from(src: &vello_encoding::WorkgroupSize) -> Self {
346 Self {
347 x: src.0,
348 y: src.1,
349 z: src.2,
350 }
351 }
352 }
353
354 impl From<&vello_encoding::WorkgroupCounts> for ffi::DispatchInfo {
from(src: &vello_encoding::WorkgroupCounts) -> Self355 fn from(src: &vello_encoding::WorkgroupCounts) -> Self {
356 Self {
357 use_large_path_scan: src.use_large_path_scan,
358 path_reduce: (&src.path_reduce).into(),
359 path_reduce2: (&src.path_reduce2).into(),
360 path_scan1: (&src.path_scan1).into(),
361 path_scan: (&src.path_scan).into(),
362 bbox_clear: (&src.bbox_clear).into(),
363 flatten: (&src.flatten).into(),
364 draw_reduce: (&src.draw_reduce).into(),
365 draw_leaf: (&src.draw_leaf).into(),
366 clip_reduce: (&src.clip_reduce).into(),
367 clip_leaf: (&src.clip_leaf).into(),
368 binning: (&src.binning).into(),
369 tile_alloc: (&src.tile_alloc).into(),
370 path_count_setup: (&src.path_count_setup).into(),
371 backdrop: (&src.backdrop).into(),
372 coarse: (&src.coarse).into(),
373 path_tiling_setup: (&src.path_tiling_setup).into(),
374 fine: (&src.fine).into(),
375 }
376 }
377 }
378
379 impl From<&vello_encoding::BufferSizes> for ffi::BufferSizes {
from(src: &vello_encoding::BufferSizes) -> Self380 fn from(src: &vello_encoding::BufferSizes) -> Self {
381 Self {
382 path_reduced: src.path_reduced.size_in_bytes(),
383 path_reduced2: src.path_reduced2.size_in_bytes(),
384 path_reduced_scan: src.path_reduced_scan.size_in_bytes(),
385 path_monoids: src.path_monoids.size_in_bytes(),
386 path_bboxes: src.path_bboxes.size_in_bytes(),
387 draw_reduced: src.draw_reduced.size_in_bytes(),
388 draw_monoids: src.draw_monoids.size_in_bytes(),
389 info: src.info.size_in_bytes(),
390 clip_inps: src.clip_inps.size_in_bytes(),
391 clip_els: src.clip_els.size_in_bytes(),
392 clip_bics: src.clip_bics.size_in_bytes(),
393 clip_bboxes: src.clip_bboxes.size_in_bytes(),
394 draw_bboxes: src.draw_bboxes.size_in_bytes(),
395 bump_alloc: src.bump_alloc.size_in_bytes(),
396 indirect_count: src.indirect_count.size_in_bytes(),
397 bin_headers: src.bin_headers.size_in_bytes(),
398 paths: src.paths.size_in_bytes(),
399 lines: src.lines.size_in_bytes(),
400 bin_data: src.bin_data.size_in_bytes(),
401 tiles: src.tiles.size_in_bytes(),
402 seg_counts: src.seg_counts.size_in_bytes(),
403 segments: src.segments.size_in_bytes(),
404 ptcl: src.ptcl.size_in_bytes(),
405 }
406 }
407 }
408