1use crate::clip::{ClipState, PathDataRef};
7use crate::filter::FilterData;
8use crate::kurbo::{Affine, BezPath};
9use crate::strip_generator::StripGenerator;
10use alloc::vec::Vec;
11use fearless_simd::Level;
12use peniko::Fill;
13
14#[derive(Debug)]
17pub struct ViewportState {
18 clip_state: ClipState,
19 strip_generator: StripGenerator,
20 strip_generator_stack: Vec<StripGenerator>,
21 level: Level,
22}
23
24impl ViewportState {
25 pub fn new(width: u16, height: u16, level: Level) -> Self {
27 Self {
28 clip_state: ClipState::new(),
29 strip_generator: StripGenerator::new(width, height, level),
30 strip_generator_stack: Vec::new(),
31 level,
32 }
33 }
34
35 pub fn width(&self) -> u16 {
37 self.strip_generator.width()
38 }
39
40 pub fn height(&self) -> u16 {
42 self.strip_generator.height()
43 }
44
45 pub fn clip(&self) -> Option<PathDataRef<'_>> {
47 self.clip_state.get()
48 }
49
50 pub fn has_root_viewports(&self) -> bool {
52 !self.strip_generator_stack.is_empty()
53 }
54
55 pub fn with_generator_and_clip<R>(
57 &mut self,
58 f: impl FnOnce(&mut StripGenerator, Option<PathDataRef<'_>>) -> R,
59 ) -> R {
60 let clip = self.clip_state.get();
61
62 f(&mut self.strip_generator, clip)
63 }
64
65 pub fn push_clip(
67 &mut self,
68 path: &BezPath,
69 fill_rule: Fill,
70 transform: Affine,
71 aliasing_threshold: Option<u8>,
72 ) {
73 self.clip_state.push_clip(
74 path,
75 &mut self.strip_generator,
76 fill_rule,
77 transform,
78 aliasing_threshold,
79 );
80 }
81
82 pub fn pop_clip(&mut self) {
84 self.clip_state.pop_clip();
85 }
86
87 pub fn push_root_viewport(&mut self, filter_data: &FilterData) {
89 let padding = filter_data.source_padding;
90 let width = self
91 .strip_generator
92 .width()
93 .saturating_add(padding.left)
94 .saturating_add(padding.right);
95 let height = self
96 .strip_generator
97 .height()
98 .saturating_add(padding.top)
99 .saturating_add(padding.bottom);
100 let filter_generator = StripGenerator::new(width, height, self.level);
102 let parent_generator = core::mem::replace(&mut self.strip_generator, filter_generator);
103 self.strip_generator_stack.push(parent_generator);
104
105 self.clip_state
106 .push_root_viewport(filter_data.source_shift(), &mut self.strip_generator);
107 }
108
109 pub fn pop_root_viewport(&mut self) {
111 self.strip_generator = self
112 .strip_generator_stack
113 .pop()
114 .expect("root viewport stack underflow");
115
116 self.clip_state.pop_root_viewport(&mut self.strip_generator);
117 }
118
119 pub fn reset(&mut self, width: u16, height: u16) {
121 self.clip_state.reset();
122 self.strip_generator_stack.clear();
123 self.strip_generator.reset(width, height);
124 }
125}