1use std::cell::RefCell;
2use std::collections::{HashMap, HashSet};
3use std::rc::Rc;
4
5use sequoia_core::{Point, Rect, Vec2};
6
7use crate::flatten::Contour;
8use crate::path::{FillRule, Path};
9use crate::raster::{self, FillStyle, RasterOptions};
10use crate::stroke::{self, StrokeStyle};
11use crate::{Span, union_rect};
12
13#[derive(Clone, Debug, PartialEq)]
14pub struct Shape {
15 pub spans: Rc<[Span]>,
16
17 pub bounds: Rect,
18}
19
20impl Shape {
21 pub fn new(spans: Vec<Span>) -> Self {
22 let mut bounds = Rect::ZERO;
23 for span in &spans {
24 bounds = union_rect(bounds, span.rect);
25 }
26 Self {
27 spans: spans.into(),
28 bounds,
29 }
30 }
31
32 pub fn empty() -> Self {
33 Self {
34 spans: Rc::from(Vec::new()),
35 bounds: Rect::ZERO,
36 }
37 }
38
39 pub fn len(&self) -> usize {
40 self.spans.len()
41 }
42
43 pub fn is_empty(&self) -> bool {
44 self.spans.is_empty()
45 }
46
47 pub fn iter(&self) -> std::slice::Iter<'_, Span> {
48 self.spans.iter()
49 }
50}
51
52impl<'a> IntoIterator for &'a Shape {
53 type Item = &'a Span;
54 type IntoIter = std::slice::Iter<'a, Span>;
55
56 fn into_iter(self) -> Self::IntoIter {
57 self.spans.iter()
58 }
59}
60
61#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
62pub struct ShapeStats {
63 pub hits: u64,
64
65 pub misses: u64,
66
67 pub evictions: u64,
68}
69
70impl ShapeStats {
71 pub fn hit_rate(&self) -> f32 {
72 let total = self.hits + self.misses;
73 if total == 0 {
74 0.0
75 } else {
76 self.hits as f32 / total as f32
77 }
78 }
79}
80
81#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
82struct Key {
83 kind: u8,
84 fingerprint: u64,
85 style: u64,
86 opts: u64,
87 offset: u64,
88}
89
90#[derive(Debug)]
91struct Entry {
92 shape: Rc<Shape>,
93 used: u64,
94}
95
96#[derive(Debug)]
99enum Request {
100 Fill {
101 path: Path,
102 rule: FillRule,
103 opts: RasterOptions,
104 offset: Vec2,
105 },
106 Stroke {
107 path: Path,
108 style: StrokeStyle,
109 opts: RasterOptions,
110 offset: Vec2,
111 },
112 Contours {
113 contours: Vec<Contour>,
114 rule: FillRule,
115 opts: RasterOptions,
116 },
117}
118
119impl Request {
120 fn opts(&self) -> RasterOptions {
121 match self {
122 Request::Fill { opts, .. }
123 | Request::Stroke { opts, .. }
124 | Request::Contours { opts, .. } => *opts,
125 }
126 }
127
128 fn cost(&self) -> usize {
131 match self {
132 Request::Fill { path, .. } | Request::Stroke { path, .. } => path.segs().len(),
133 Request::Contours { contours, .. } => contours.iter().map(|c| c.points.len()).sum(),
134 }
135 }
136
137 fn build(&self, opts: RasterOptions, out: &mut Vec<Span>) {
138 match self {
139 Request::Fill {
140 path, rule, offset, ..
141 } => {
142 if *offset == Vec2::ZERO {
143 raster::fill(path, *rule, opts, out);
144 } else {
145 raster::fill(&path.translated(offset.x, offset.y), *rule, opts, out);
146 }
147 }
148 Request::Stroke {
149 path,
150 style,
151 offset,
152 ..
153 } => {
154 if *offset == Vec2::ZERO {
155 stroke::stroke_path(path, style, opts, out);
156 } else {
157 stroke::stroke_path(&path.translated(offset.x, offset.y), style, opts, out);
158 }
159 }
160 Request::Contours { contours, rule, .. } => {
161 raster::fill_contours(contours, *rule, opts, out);
162 }
163 }
164 }
165}
166
167const BATCH_MIN_SHAPES: usize = 4;
170
171const BATCH_MIN_COST: usize = 256;
174
175const BATCH_MAX_WORKERS: usize = 8;
176
177pub const DEFAULT_CAPACITY: usize = 256;
178
179pub(crate) const MAX_CAPACITY: usize = 2048;
183
184#[derive(Debug)]
185pub struct ShapeCache {
186 inner: RefCell<Inner>,
187}
188
189#[derive(Debug)]
190struct Inner {
191 entries: HashMap<Key, Entry>,
192 capacity: usize,
193 max_capacity: usize,
194 tick: u64,
195 stats: ShapeStats,
196 prefetching: bool,
197 pending: Vec<(Key, Request)>,
198 pending_keys: HashSet<Key>,
199}
200
201impl Default for ShapeCache {
202 fn default() -> Self {
203 Self::growing(DEFAULT_CAPACITY, MAX_CAPACITY)
204 }
205}
206
207impl ShapeCache {
208 pub fn new(capacity: usize) -> Self {
209 Self::growing(capacity, capacity)
210 }
211
212 pub fn growing(capacity: usize, max_capacity: usize) -> Self {
218 let max_capacity = max_capacity.max(capacity);
219 Self {
220 inner: RefCell::new(Inner {
221 entries: HashMap::new(),
222 capacity,
223 max_capacity,
224 tick: 0,
225 stats: ShapeStats::default(),
226 prefetching: false,
227 pending: Vec::new(),
228 pending_keys: HashSet::new(),
229 }),
230 }
231 }
232
233 pub fn capacity(&self) -> usize {
234 self.inner.borrow().capacity
235 }
236
237 pub fn max_capacity(&self) -> usize {
238 self.inner.borrow().max_capacity
239 }
240
241 pub fn len(&self) -> usize {
242 self.inner.borrow().entries.len()
243 }
244
245 pub fn is_empty(&self) -> bool {
246 self.inner.borrow().entries.is_empty()
247 }
248
249 pub fn stats(&self) -> ShapeStats {
250 self.inner.borrow().stats
251 }
252
253 pub fn reset_stats(&self) {
254 self.inner.borrow_mut().stats = ShapeStats::default();
255 }
256
257 pub fn clear(&self) {
258 self.inner.borrow_mut().entries.clear();
259 }
260
261 pub fn fill(&self, path: &Path, rule: FillRule, opts: RasterOptions) -> Rc<Shape> {
262 self.fill_styled(path, FillStyle::new(rule, opts))
263 }
264
265 pub fn fill_styled(&self, path: &Path, style: FillStyle) -> Rc<Shape> {
266 let key = Key {
267 kind: 0,
268 fingerprint: path.fingerprint(),
269 style: style.rule as u64,
270 opts: opts_key(style.opts),
271 offset: 0,
272 };
273 self.get_or_build(
274 key,
275 |out| {
276 raster::fill(path, style.rule, style.opts, out);
277 },
278 || Request::Fill {
279 path: path.clone(),
280 rule: style.rule,
281 opts: style.opts,
282 offset: Vec2::ZERO,
283 },
284 )
285 }
286
287 pub fn fill_offset(
288 &self,
289 path: &Path,
290 rule: FillRule,
291 opts: RasterOptions,
292 offset: Vec2,
293 ) -> Rc<Shape> {
294 let key = Key {
295 kind: 0,
296 fingerprint: path.fingerprint(),
297 style: rule as u64,
298 opts: opts_key(opts),
299 offset: offset_key(offset),
300 };
301 self.get_or_build(
302 key,
303 |out| {
304 let translated = path.translated(offset.x, offset.y);
305 raster::fill(&translated, rule, opts, out);
306 },
307 || Request::Fill {
308 path: path.clone(),
309 rule,
310 opts,
311 offset,
312 },
313 )
314 }
315
316 pub fn stroke(&self, path: &Path, style: &StrokeStyle, opts: RasterOptions) -> Rc<Shape> {
317 let key = Key {
318 kind: 1,
319 fingerprint: path.fingerprint(),
320 style: style.fingerprint(),
321 opts: opts_key(opts),
322 offset: 0,
323 };
324 self.get_or_build(
325 key,
326 |out| {
327 stroke::stroke_path(path, style, opts, out);
328 },
329 || Request::Stroke {
330 path: path.clone(),
331 style: style.clone(),
332 opts,
333 offset: Vec2::ZERO,
334 },
335 )
336 }
337
338 pub fn stroke_offset(
339 &self,
340 path: &Path,
341 style: &StrokeStyle,
342 opts: RasterOptions,
343 offset: Vec2,
344 ) -> Rc<Shape> {
345 let key = Key {
346 kind: 1,
347 fingerprint: path.fingerprint(),
348 style: style.fingerprint(),
349 opts: opts_key(opts),
350 offset: offset_key(offset),
351 };
352 self.get_or_build(
353 key,
354 |out| {
355 let translated = path.translated(offset.x, offset.y);
356 stroke::stroke_path(&translated, style, opts, out);
357 },
358 || Request::Stroke {
359 path: path.clone(),
360 style: style.clone(),
361 opts,
362 offset,
363 },
364 )
365 }
366
367 pub fn fill_contours(
368 &self,
369 contours: &[crate::Contour],
370 rule: FillRule,
371 opts: RasterOptions,
372 ) -> Rc<Shape> {
373 let key = Key {
374 kind: 2,
375 fingerprint: contours_fingerprint(contours),
376 style: rule as u64,
377 opts: opts_key(opts),
378 offset: 0,
379 };
380 self.get_or_build(
381 key,
382 |out| {
383 raster::fill_contours(contours, rule, opts, out);
384 },
385 || Request::Contours {
386 contours: contours.to_vec(),
387 rule,
388 opts,
389 },
390 )
391 }
392
393 pub fn disk(&self, center: Point, radius: f32) -> Rc<Shape> {
394 if !(radius > 0.0) || !radius.is_finite() {
395 return Rc::new(Shape::empty());
396 }
397 Rc::new(Shape::new(vec![Span::rounded(
398 Rect::from_xywh(
399 center.x - radius,
400 center.y - radius,
401 radius * 2.0,
402 radius * 2.0,
403 ),
404 radius,
405 )]))
406 }
407
408 pub fn rounded_rect(&self, rect: Rect, radius: f32) -> Rc<Shape> {
409 let radius = radius
410 .clamp(0.0, rect.size.width.min(rect.size.height) * 0.5)
411 .max(0.0);
412 Rc::new(Shape::new(vec![Span::rounded(rect, radius)]))
413 }
414
415 pub fn rect(&self, rect: Rect) -> Rc<Shape> {
416 Rc::new(Shape::new(vec![Span::rect(rect)]))
417 }
418
419 pub fn begin_prefetch(&self) {
427 let mut inner = self.inner.borrow_mut();
428 inner.prefetching = true;
429 inner.pending.clear();
430 inner.pending_keys.clear();
431 }
432
433 pub fn end_prefetch(&self) -> usize {
436 let mut inner = self.inner.borrow_mut();
437 inner.prefetching = false;
438 inner.pending.len()
439 }
440
441 pub fn tessellate_prefetch(&self) -> usize {
450 let pending = {
451 let mut inner = self.inner.borrow_mut();
452 inner.prefetching = false;
453 inner.pending_keys.clear();
454 std::mem::take(&mut inner.pending)
455 };
456 if pending.is_empty() {
457 return 0;
458 }
459
460 let cost: usize = pending.iter().map(|(_, req)| req.cost()).sum();
461 let workers = if pending.len() >= BATCH_MIN_SHAPES && cost >= BATCH_MIN_COST {
462 std::thread::available_parallelism()
463 .map(|n| n.get())
464 .unwrap_or(1)
465 .min(BATCH_MAX_WORKERS)
466 .min(pending.len())
467 } else {
468 1
469 };
470
471 let built: Vec<(Key, Vec<Span>)> = if workers <= 1 {
472 pending
473 .iter()
474 .map(|(key, req)| (*key, build_request(req, req.opts())))
475 .collect()
476 } else {
477 let chunk = pending.len().div_ceil(workers);
478 let mut parts: Vec<Vec<(Key, Vec<Span>)>> = Vec::with_capacity(workers);
479 std::thread::scope(|scope| {
480 let mut handles = Vec::with_capacity(workers);
481 for chunk in pending.chunks(chunk) {
482 handles.push(scope.spawn(move || {
483 chunk
484 .iter()
485 .map(|(key, req)| {
486 (*key, build_request(req, req.opts().parallel(false)))
487 })
488 .collect::<Vec<_>>()
489 }));
490 }
491 for handle in handles {
494 if let Ok(part) = handle.join() {
495 parts.push(part);
496 }
497 }
498 });
499 parts.into_iter().flatten().collect()
500 };
501
502 let count = built.len();
503 let mut inner = self.inner.borrow_mut();
504 for (key, spans) in built {
505 inner.tick += 1;
506 let used = inner.tick;
507 inner.insert_used(key, Rc::new(Shape::new(spans)), used);
508 }
509 count
510 }
511
512 fn get_or_build(
513 &self,
514 key: Key,
515 build: impl FnOnce(&mut Vec<Span>),
516 request: impl FnOnce() -> Request,
517 ) -> Rc<Shape> {
518 let mut inner = self.inner.borrow_mut();
519 inner.tick += 1;
520 let tick = inner.tick;
521 if let Some(entry) = inner.entries.get_mut(&key) {
522 entry.used = tick;
523 let shape = entry.shape.clone();
524 inner.stats.hits += 1;
525 return shape;
526 }
527 if inner.prefetching {
528 if inner.pending_keys.insert(key) {
529 inner.stats.misses += 1;
530 inner.pending.push((key, request()));
531 } else {
532 inner.stats.hits += 1;
535 }
536 return Rc::new(Shape::empty());
537 }
538 inner.stats.misses += 1;
539 if inner.capacity == 0 {
540 drop(inner);
541 let mut spans = Vec::new();
542 build(&mut spans);
543 return Rc::new(Shape::new(spans));
544 }
545
546 let mut spans = Vec::new();
547 build(&mut spans);
548 let shape = Rc::new(Shape::new(spans));
549 inner.insert_used(key, shape.clone(), tick);
550 shape
551 }
552}
553
554impl Inner {
555 fn insert_used(&mut self, key: Key, shape: Rc<Shape>, used: u64) {
556 if self.capacity == 0 {
557 return;
558 }
559 if self.entries.len() >= self.capacity
560 && let Some((oldest, oldest_used)) = self
561 .entries
562 .iter()
563 .min_by_key(|(_, entry)| entry.used)
564 .map(|(key, entry)| (*key, entry.used))
565 {
566 let hot = used.saturating_sub(oldest_used) <= self.capacity as u64;
569 if hot && self.capacity < self.max_capacity {
570 self.capacity = self.capacity.saturating_mul(2).min(self.max_capacity);
571 } else {
572 self.entries.remove(&oldest);
573 self.stats.evictions += 1;
574 }
575 }
576 self.entries.insert(key, Entry { shape, used });
577 }
578}
579
580fn build_request(req: &Request, opts: RasterOptions) -> Vec<Span> {
581 let mut spans = Vec::new();
582 req.build(opts, &mut spans);
583 spans
584}
585
586fn offset_key(offset: Vec2) -> u64 {
587 let mut hash: u64 = 0xcbf2_9ce4_8422_2325;
588 for v in [offset.x, offset.y] {
589 let v = if v == 0.0 { 0.0 } else { v };
590 hash ^= u64::from(v.to_bits());
591 hash = hash.wrapping_mul(0x0000_0100_0000_01b3);
592 }
593 hash
594}
595
596fn opts_key(opts: RasterOptions) -> u64 {
597 let mut hash: u64 = 0xcbf2_9ce4_8422_2325;
598 for v in [opts.tolerance, opts.row_height, opts.merge_tol] {
599 hash ^= u64::from(v.to_bits());
600 hash = hash.wrapping_mul(0x0000_0100_0000_01b3);
601 }
602 hash ^= u64::from(opts.aa_samples);
603 hash = hash.wrapping_mul(0x0000_0100_0000_01b3);
604 hash ^= u64::from(opts.analytic);
605 hash = hash.wrapping_mul(0x0000_0100_0000_01b3);
606 hash ^= u64::from(opts.parallel);
607 hash.wrapping_mul(0x0000_0100_0000_01b3)
608}
609
610fn contours_fingerprint(contours: &[crate::Contour]) -> u64 {
611 let mut hash: u64 = 0xcbf2_9ce4_8422_2325;
612 let mut eat = |v: u64| {
613 hash ^= v;
614 hash = hash.wrapping_mul(0x0000_0100_0000_01b3);
615 };
616 for c in contours {
617 eat(c.points.len() as u64);
618 eat(u64::from(c.closed));
619 for p in &c.points {
620 eat(u64::from(p.x.to_bits()));
621 eat(u64::from(p.y.to_bits()));
622 }
623 }
624 hash
625}