1 | use alloc::vec; |
2 | use ttf_parser::colr::{ClipBox, CompositeMode, Paint}; |
3 | use ttf_parser::{GlyphId, RectF, Transform}; |
4 | |
5 | /* |
6 | * This file implements bounds-extraction as well as boundedness |
7 | * computation of COLRv1 fonts as described in: |
8 | * |
9 | * https://learn.microsoft.com/en-us/typography/opentype/spec/colr#glyph-metrics-and-boundedness |
10 | */ |
11 | |
12 | #[derive (Copy, Clone)] |
13 | pub(crate) struct hb_extents_t { |
14 | pub x_min: f32, |
15 | pub y_min: f32, |
16 | pub x_max: f32, |
17 | pub y_max: f32, |
18 | } |
19 | |
20 | impl hb_extents_t { |
21 | pub fn is_empty(&self) -> bool { |
22 | self.x_min >= self.x_max || self.y_min >= self.y_max |
23 | } |
24 | pub fn is_void(&self) -> bool { |
25 | self.x_min > self.x_max |
26 | } |
27 | |
28 | pub fn union_(&mut self, o: &hb_extents_t) { |
29 | self.x_min = o.x_min.min(o.x_min); |
30 | self.y_min = o.y_min.min(o.y_min); |
31 | self.x_max = o.x_max.max(o.x_max); |
32 | self.y_max = o.y_max.max(o.y_max); |
33 | } |
34 | |
35 | pub fn intersect(&mut self, o: &hb_extents_t) { |
36 | self.x_min = o.x_min.max(o.x_min); |
37 | self.y_min = o.y_min.max(o.y_min); |
38 | self.x_max = o.x_max.min(o.x_max); |
39 | self.y_max = o.y_max.min(o.y_max); |
40 | } |
41 | } |
42 | |
43 | impl From<RectF> for hb_extents_t { |
44 | fn from(val: RectF) -> Self { |
45 | Self { |
46 | x_min: val.x_min, |
47 | y_min: val.y_min, |
48 | x_max: val.x_max, |
49 | y_max: val.y_max, |
50 | } |
51 | } |
52 | } |
53 | |
54 | #[derive (PartialEq, Eq, Clone, Copy)] |
55 | enum status_t { |
56 | EMPTY, |
57 | BOUNDED, |
58 | UNBOUNDED, |
59 | } |
60 | |
61 | #[derive (Clone, Copy)] |
62 | pub(crate) struct hb_bounds_t { |
63 | status: status_t, |
64 | extents: hb_extents_t, |
65 | } |
66 | |
67 | impl hb_bounds_t { |
68 | fn from_extents(extents: &hb_extents_t) -> Self { |
69 | let status = if extents.is_empty() { |
70 | status_t::EMPTY |
71 | } else { |
72 | status_t::BOUNDED |
73 | }; |
74 | |
75 | hb_bounds_t { |
76 | extents: *extents, |
77 | status, |
78 | } |
79 | } |
80 | |
81 | fn from_status(status: status_t) -> Self { |
82 | hb_bounds_t { |
83 | status, |
84 | ..hb_bounds_t::default() |
85 | } |
86 | } |
87 | |
88 | fn union(&mut self, o: &hb_bounds_t) { |
89 | if o.status == status_t::UNBOUNDED { |
90 | self.status = status_t::UNBOUNDED; |
91 | } else if o.status == status_t::BOUNDED { |
92 | if self.status == status_t::EMPTY { |
93 | *self = *o; |
94 | } else if self.status == status_t::BOUNDED { |
95 | self.extents.union_(&o.extents); |
96 | } |
97 | } |
98 | } |
99 | |
100 | fn intersect(&mut self, o: &hb_bounds_t) { |
101 | if o.status == status_t::EMPTY { |
102 | self.status = status_t::EMPTY; |
103 | } else if o.status == status_t::BOUNDED { |
104 | if self.status == status_t::UNBOUNDED { |
105 | *self = *o; |
106 | } else if self.status == status_t::BOUNDED { |
107 | self.extents.intersect(&o.extents); |
108 | |
109 | if self.extents.is_empty() { |
110 | self.status = status_t::EMPTY; |
111 | } |
112 | } |
113 | } |
114 | } |
115 | } |
116 | |
117 | impl Default for hb_bounds_t { |
118 | fn default() -> Self { |
119 | Self::from_extents(&hb_extents_t { |
120 | x_min: 0.0, |
121 | x_max: 0.0, |
122 | y_min: 0.0, |
123 | y_max: 0.0, |
124 | }) |
125 | } |
126 | } |
127 | |
128 | pub(crate) struct hb_paint_extents_context_t<'a> { |
129 | clips: vec::Vec<hb_bounds_t>, |
130 | groups: vec::Vec<hb_bounds_t>, |
131 | transforms: vec::Vec<Transform>, |
132 | // Doesn't exist in harfbuzz. The reason we need it is that in harfbuzz, composite modes |
133 | // are passed as part of `pop`, while ttf-parser passes it as part of `push`, so we need to |
134 | // store it in the meanwhile. |
135 | composite_modes: vec::Vec<CompositeMode>, |
136 | face: &'a ttf_parser::Face<'a>, |
137 | current_glyph: GlyphId, |
138 | } |
139 | |
140 | impl<'a> hb_paint_extents_context_t<'a> { |
141 | pub(crate) fn new(face: &'a ttf_parser::Face<'a>) -> Self { |
142 | Self { |
143 | clips: vec![hb_bounds_t::from_status(status_t::UNBOUNDED)], |
144 | groups: vec![hb_bounds_t::from_status(status_t::EMPTY)], |
145 | transforms: vec![Transform::default()], |
146 | composite_modes: vec![CompositeMode::SourceOver], |
147 | face, |
148 | current_glyph: Default::default(), |
149 | } |
150 | } |
151 | |
152 | pub(crate) fn get_extents(&self) -> hb_extents_t { |
153 | // harfbuzz doesn't have the unwrap_or_default part, but in a valid font |
154 | // this should always be valid anyway. |
155 | self.groups.last().copied().unwrap_or_default().extents |
156 | } |
157 | |
158 | fn push_transform(&mut self, trans: &Transform) { |
159 | let t = self |
160 | .transforms |
161 | .last() |
162 | .copied() |
163 | .unwrap_or(Transform::default()); |
164 | let new = Transform::combine(t, *trans); |
165 | self.transforms.push(new); |
166 | } |
167 | |
168 | fn pop_transform(&mut self) { |
169 | self.transforms.pop(); |
170 | } |
171 | |
172 | fn push_clip(&mut self, mut extents: hb_extents_t) { |
173 | if let Some(r) = self.transforms.last_mut() { |
174 | r.transform_extents(&mut extents); |
175 | } |
176 | |
177 | let b = hb_bounds_t::from_extents(&extents); |
178 | self.clips.push(b); |
179 | } |
180 | |
181 | fn pop_clip(&mut self) { |
182 | self.clips.pop(); |
183 | } |
184 | |
185 | fn push_group(&mut self) { |
186 | self.groups.push(hb_bounds_t::default()); |
187 | } |
188 | |
189 | fn pop_group(&mut self) { |
190 | if let Some(mode) = self.composite_modes.pop() { |
191 | if let Some(src_bounds) = self.groups.pop() { |
192 | if let Some(backdrop_bounds) = self.groups.last_mut() { |
193 | match mode { |
194 | CompositeMode::Clear => backdrop_bounds.status = status_t::EMPTY, |
195 | CompositeMode::Source | CompositeMode::SourceOut => { |
196 | *backdrop_bounds = src_bounds |
197 | } |
198 | CompositeMode::Destination | CompositeMode::DestinationOut => {} |
199 | CompositeMode::SourceIn | CompositeMode::DestinationIn => { |
200 | backdrop_bounds.intersect(&src_bounds) |
201 | } |
202 | _ => backdrop_bounds.union(&src_bounds), |
203 | } |
204 | } |
205 | } |
206 | } |
207 | } |
208 | |
209 | fn paint(&mut self) { |
210 | if let (Some(clip), Some(group)) = (self.clips.last(), self.groups.last_mut()) { |
211 | group.union(clip); |
212 | } |
213 | } |
214 | } |
215 | |
216 | impl ttf_parser::colr::Painter<'_> for hb_paint_extents_context_t<'_> { |
217 | fn outline_glyph(&mut self, glyph_id: GlyphId) { |
218 | self.current_glyph = glyph_id; |
219 | } |
220 | |
221 | fn paint(&mut self, _: Paint<'_>) { |
222 | self.paint(); |
223 | } |
224 | |
225 | fn push_clip(&mut self) { |
226 | if let Some(glyph_bbox) = self.face.glyph_bounding_box(self.current_glyph) { |
227 | self.push_clip(hb_extents_t { |
228 | x_min: glyph_bbox.x_min as f32, |
229 | y_min: glyph_bbox.y_min as f32, |
230 | x_max: glyph_bbox.x_max as f32, |
231 | y_max: glyph_bbox.y_max as f32, |
232 | }); |
233 | } |
234 | } |
235 | |
236 | fn push_clip_box(&mut self, clipbox: ClipBox) { |
237 | self.push_clip(clipbox.into()); |
238 | } |
239 | |
240 | fn pop_clip(&mut self) { |
241 | self.pop_clip(); |
242 | } |
243 | |
244 | fn push_layer(&mut self, mode: CompositeMode) { |
245 | self.composite_modes.push(mode); |
246 | self.push_group(); |
247 | } |
248 | |
249 | fn pop_layer(&mut self) { |
250 | self.pop_group(); |
251 | } |
252 | |
253 | fn push_transform(&mut self, transform: Transform) { |
254 | self.push_transform(&transform); |
255 | } |
256 | |
257 | fn pop_transform(&mut self) { |
258 | self.pop_transform(); |
259 | } |
260 | } |
261 | |
262 | trait TransformExt { |
263 | fn transform_distance(&self, dx: &mut f32, dy: &mut f32); |
264 | fn transform_point(&self, x: &mut f32, y: &mut f32); |
265 | fn transform_extents(&self, extents: &mut hb_extents_t); |
266 | } |
267 | |
268 | impl TransformExt for Transform { |
269 | fn transform_distance(&self, dx: &mut f32, dy: &mut f32) { |
270 | let new_x = self.a * (*dx) + self.c * (*dy); |
271 | let new_y = self.b * (*dx) + self.d * (*dy); |
272 | *dx = new_x; |
273 | *dy = new_y; |
274 | } |
275 | |
276 | fn transform_point(&self, x: &mut f32, y: &mut f32) { |
277 | self.transform_distance(x, y); |
278 | *x += self.e; |
279 | *y += self.f; |
280 | } |
281 | |
282 | fn transform_extents(&self, extents: &mut hb_extents_t) { |
283 | let mut quad_x = [0.0f32; 4]; |
284 | let mut quad_y = [0.0f32; 4]; |
285 | |
286 | quad_x[0] = extents.x_min; |
287 | quad_y[0] = extents.y_min; |
288 | quad_x[1] = extents.x_min; |
289 | quad_y[1] = extents.y_max; |
290 | quad_x[2] = extents.x_max; |
291 | quad_y[2] = extents.y_min; |
292 | quad_x[3] = extents.x_max; |
293 | quad_y[3] = extents.y_max; |
294 | |
295 | for i in 0..4 { |
296 | self.transform_point(&mut quad_x[i], &mut quad_y[i]) |
297 | } |
298 | |
299 | extents.x_max = quad_x[0]; |
300 | extents.x_min = extents.x_max; |
301 | extents.y_max = quad_y[0]; |
302 | extents.y_min = extents.y_max; |
303 | |
304 | for i in 1..4 { |
305 | extents.x_min = extents.x_min.min(quad_x[i]); |
306 | extents.y_min = extents.y_min.min(quad_y[i]); |
307 | extents.x_max = extents.x_max.max(quad_x[i]); |
308 | extents.y_max = extents.y_max.max(quad_y[i]); |
309 | } |
310 | } |
311 | } |
312 | |