/src/harfbuzz/src/hb-paint-extents.hh
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright © 2022 Behdad Esfahbod |
3 | | * |
4 | | * This is part of HarfBuzz, a text shaping library. |
5 | | * |
6 | | * Permission is hereby granted, without written agreement and without |
7 | | * license or royalty fees, to use, copy, modify, and distribute this |
8 | | * software and its documentation for any purpose, provided that the |
9 | | * above copyright notice and the following two paragraphs appear in |
10 | | * all copies of this software. |
11 | | * |
12 | | * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
13 | | * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
14 | | * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
15 | | * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
16 | | * DAMAGE. |
17 | | * |
18 | | * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
19 | | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
20 | | * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
21 | | * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
22 | | * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
23 | | */ |
24 | | |
25 | | #ifndef HB_PAINT_EXTENTS_HH |
26 | | #define HB_PAINT_EXTENTS_HH |
27 | | |
28 | | #include "hb.hh" |
29 | | #include "hb-paint.h" |
30 | | |
31 | | |
32 | | typedef struct hb_extents_t |
33 | | { |
34 | 5.55M | hb_extents_t () {} |
35 | | hb_extents_t (float xmin, float ymin, float xmax, float ymax) : |
36 | 50.6k | xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {} |
37 | | |
38 | 200k | bool is_empty () const { return xmin >= xmax || ymin >= ymax; } |
39 | 2.10M | bool is_void () const { return xmin > xmax; } |
40 | | |
41 | | void union_ (const hb_extents_t &o) |
42 | 85.4k | { |
43 | 85.4k | xmin = hb_min (xmin, o.xmin); |
44 | 85.4k | ymin = hb_min (ymin, o.ymin); |
45 | 85.4k | xmax = hb_max (xmax, o.xmax); |
46 | 85.4k | ymax = hb_max (ymax, o.ymax); |
47 | 85.4k | } |
48 | | |
49 | | void intersect (const hb_extents_t &o) |
50 | 48 | { |
51 | 48 | xmin = hb_max (xmin, o.xmin); |
52 | 48 | ymin = hb_max (ymin, o.ymin); |
53 | 48 | xmax = hb_min (xmax, o.xmax); |
54 | 48 | ymax = hb_min (ymax, o.ymax); |
55 | 48 | } |
56 | | |
57 | | void |
58 | | add_point (float x, float y) |
59 | 2.04M | { |
60 | 2.04M | if (unlikely (is_void ())) |
61 | 223k | { |
62 | 223k | xmin = xmax = x; |
63 | 223k | ymin = ymax = y; |
64 | 223k | } |
65 | 1.82M | else |
66 | 1.82M | { |
67 | 1.82M | xmin = hb_min (xmin, x); |
68 | 1.82M | ymin = hb_min (ymin, y); |
69 | 1.82M | xmax = hb_max (xmax, x); |
70 | 1.82M | ymax = hb_max (ymax, y); |
71 | 1.82M | } |
72 | 2.04M | } |
73 | | |
74 | | float xmin = 0.f; |
75 | | float ymin = 0.f; |
76 | | float xmax = -1.f; |
77 | | float ymax = -1.f; |
78 | | } hb_extents_t; |
79 | | |
80 | | typedef struct hb_transform_t |
81 | | { |
82 | 682k | hb_transform_t () {} |
83 | | hb_transform_t (float xx, float yx, |
84 | | float xy, float yy, |
85 | | float x0, float y0) : |
86 | 566k | xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {} |
87 | | |
88 | | void multiply (const hb_transform_t &o) |
89 | 566k | { |
90 | | /* Copied from cairo, with "o" being "a" there and "this" being "b" there. */ |
91 | 566k | hb_transform_t r; |
92 | | |
93 | 566k | r.xx = o.xx * xx + o.yx * xy; |
94 | 566k | r.yx = o.xx * yx + o.yx * yy; |
95 | | |
96 | 566k | r.xy = o.xy * xx + o.yy * xy; |
97 | 566k | r.yy = o.xy * yx + o.yy * yy; |
98 | | |
99 | 566k | r.x0 = o.x0 * xx + o.y0 * xy + x0; |
100 | 566k | r.y0 = o.x0 * yx + o.y0 * yy + y0; |
101 | | |
102 | 566k | *this = r; |
103 | 566k | } |
104 | | |
105 | | void transform_distance (float &dx, float &dy) const |
106 | 801k | { |
107 | 801k | float new_x = xx * dx + xy * dy; |
108 | 801k | float new_y = yx * dx + yy * dy; |
109 | 801k | dx = new_x; |
110 | 801k | dy = new_y; |
111 | 801k | } |
112 | | |
113 | | void transform_point (float &x, float &y) const |
114 | 801k | { |
115 | 801k | transform_distance (x, y); |
116 | 801k | x += x0; |
117 | 801k | y += y0; |
118 | 801k | } |
119 | | |
120 | | void transform_extents (hb_extents_t &extents) const |
121 | 200k | { |
122 | 200k | float quad_x[4], quad_y[4]; |
123 | | |
124 | 200k | quad_x[0] = extents.xmin; |
125 | 200k | quad_y[0] = extents.ymin; |
126 | 200k | quad_x[1] = extents.xmin; |
127 | 200k | quad_y[1] = extents.ymax; |
128 | 200k | quad_x[2] = extents.xmax; |
129 | 200k | quad_y[2] = extents.ymin; |
130 | 200k | quad_x[3] = extents.xmax; |
131 | 200k | quad_y[3] = extents.ymax; |
132 | | |
133 | 200k | extents = hb_extents_t {}; |
134 | 1.00M | for (unsigned i = 0; i < 4; i++) |
135 | 801k | { |
136 | 801k | transform_point (quad_x[i], quad_y[i]); |
137 | 801k | extents.add_point (quad_x[i], quad_y[i]); |
138 | 801k | } |
139 | 200k | } |
140 | | |
141 | | float xx = 1.f; |
142 | | float yx = 0.f; |
143 | | float xy = 0.f; |
144 | | float yy = 1.f; |
145 | | float x0 = 0.f; |
146 | | float y0 = 0.f; |
147 | | } hb_transform_t; |
148 | | |
149 | | typedef struct hb_bounds_t |
150 | | { |
151 | | enum status_t { |
152 | | UNBOUNDED, |
153 | | BOUNDED, |
154 | | EMPTY, |
155 | | }; |
156 | | |
157 | 5.20M | hb_bounds_t (status_t status) : status (status) {} |
158 | | hb_bounds_t (const hb_extents_t &extents) : |
159 | 200k | status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {} |
160 | | |
161 | | void union_ (const hb_bounds_t &o) |
162 | 5.14M | { |
163 | 5.14M | if (o.status == UNBOUNDED) |
164 | 394k | status = UNBOUNDED; |
165 | 4.75M | else if (o.status == BOUNDED) |
166 | 134k | { |
167 | 134k | if (status == EMPTY) |
168 | 44.7k | *this = o; |
169 | 89.5k | else if (status == BOUNDED) |
170 | 85.4k | extents.union_ (o.extents); |
171 | 134k | } |
172 | 5.14M | } |
173 | | |
174 | | void intersect (const hb_bounds_t &o) |
175 | 3.70k | { |
176 | 3.70k | if (o.status == EMPTY) |
177 | 3.05k | status = EMPTY; |
178 | 645 | else if (o.status == BOUNDED) |
179 | 260 | { |
180 | 260 | if (status == UNBOUNDED) |
181 | 4 | *this = o; |
182 | 256 | else if (status == BOUNDED) |
183 | 48 | { |
184 | 48 | extents.intersect (o.extents); |
185 | 48 | if (extents.is_empty ()) |
186 | 29 | status = EMPTY; |
187 | 48 | } |
188 | 260 | } |
189 | 3.70k | } |
190 | | |
191 | | status_t status; |
192 | | hb_extents_t extents; |
193 | | } hb_bounds_t; |
194 | | |
195 | | typedef struct hb_paint_extents_context_t hb_paint_extents_context_t; |
196 | | |
197 | | struct hb_paint_extents_context_t |
198 | | { |
199 | | hb_paint_extents_context_t () |
200 | 116k | { |
201 | 116k | transforms.push (hb_transform_t{}); |
202 | 116k | clips.push (hb_bounds_t{hb_bounds_t::UNBOUNDED}); |
203 | 116k | groups.push (hb_bounds_t{hb_bounds_t::EMPTY}); |
204 | 116k | } |
205 | | |
206 | | hb_extents_t get_extents () |
207 | 116k | { |
208 | 116k | return groups.tail().extents; |
209 | 116k | } |
210 | | |
211 | | bool is_bounded () |
212 | 51.2k | { |
213 | 51.2k | return groups.tail().status != hb_bounds_t::UNBOUNDED; |
214 | 51.2k | } |
215 | | |
216 | | void push_transform (const hb_transform_t &trans) |
217 | 566k | { |
218 | 566k | hb_transform_t t = transforms.tail (); |
219 | 566k | t.multiply (trans); |
220 | 566k | transforms.push (t); |
221 | 566k | } |
222 | | |
223 | | void pop_transform () |
224 | 566k | { |
225 | 566k | transforms.pop (); |
226 | 566k | } |
227 | | |
228 | | void push_clip (hb_extents_t extents) |
229 | 200k | { |
230 | | /* Transform extents and push a new clip. */ |
231 | 200k | const hb_transform_t &t = transforms.tail (); |
232 | 200k | t.transform_extents (extents); |
233 | | |
234 | 200k | clips.push (hb_bounds_t {extents}); |
235 | 200k | } |
236 | | |
237 | | void pop_clip () |
238 | 200k | { |
239 | 200k | clips.pop (); |
240 | 200k | } |
241 | | |
242 | | void push_group () |
243 | 4.96M | { |
244 | 4.96M | groups.push (hb_bounds_t {hb_bounds_t::EMPTY}); |
245 | 4.96M | } |
246 | | |
247 | | void pop_group (hb_paint_composite_mode_t mode) |
248 | 4.96M | { |
249 | 4.96M | const hb_bounds_t src_bounds = groups.pop (); |
250 | 4.96M | hb_bounds_t &backdrop_bounds = groups.tail (); |
251 | | |
252 | | // https://learn.microsoft.com/en-us/typography/opentype/spec/colr#format-32-paintcomposite |
253 | 4.96M | switch ((int) mode) |
254 | 4.96M | { |
255 | 6.92k | case HB_PAINT_COMPOSITE_MODE_CLEAR: |
256 | 6.92k | backdrop_bounds.status = hb_bounds_t::EMPTY; |
257 | 6.92k | break; |
258 | 1.83k | case HB_PAINT_COMPOSITE_MODE_SRC: |
259 | 2.65k | case HB_PAINT_COMPOSITE_MODE_SRC_OUT: |
260 | 2.65k | backdrop_bounds = src_bounds; |
261 | 2.65k | break; |
262 | 2.00k | case HB_PAINT_COMPOSITE_MODE_DEST: |
263 | 2.65k | case HB_PAINT_COMPOSITE_MODE_DEST_OUT: |
264 | 2.65k | break; |
265 | 654 | case HB_PAINT_COMPOSITE_MODE_SRC_IN: |
266 | 3.70k | case HB_PAINT_COMPOSITE_MODE_DEST_IN: |
267 | 3.70k | backdrop_bounds.intersect (src_bounds); |
268 | 3.70k | break; |
269 | 4.95M | default: |
270 | 4.95M | backdrop_bounds.union_ (src_bounds); |
271 | 4.95M | break; |
272 | 4.96M | } |
273 | 4.96M | } |
274 | | |
275 | | void paint () |
276 | 196k | { |
277 | 196k | const hb_bounds_t &clip = clips.tail (); |
278 | 196k | hb_bounds_t &group = groups.tail (); |
279 | | |
280 | 196k | group.union_ (clip); |
281 | 196k | } |
282 | | |
283 | | protected: |
284 | | hb_vector_t<hb_transform_t> transforms; |
285 | | hb_vector_t<hb_bounds_t> clips; |
286 | | hb_vector_t<hb_bounds_t> groups; |
287 | | }; |
288 | | |
289 | | HB_INTERNAL hb_paint_funcs_t * |
290 | | hb_paint_extents_get_funcs (); |
291 | | |
292 | | |
293 | | #endif /* HB_PAINT_EXTENTS_HH */ |