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 | hb_extents_t () {} |
35 | hb_extents_t (float xmin, float ymin, float xmax, float ymax) : |
36 | xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {} |
37 | |
38 | bool is_empty () const { return xmin >= xmax || ymin >= ymax; } |
39 | bool is_void () const { return xmin > xmax; } |
40 | |
41 | void union_ (const hb_extents_t &o) |
42 | { |
43 | xmin = hb_min (xmin, o.xmin); |
44 | ymin = hb_min (ymin, o.ymin); |
45 | xmax = hb_max (xmax, o.xmax); |
46 | ymax = hb_max (ymax, o.ymax); |
47 | } |
48 | |
49 | void intersect (const hb_extents_t &o) |
50 | { |
51 | xmin = hb_max (xmin, o.xmin); |
52 | ymin = hb_max (ymin, o.ymin); |
53 | xmax = hb_min (xmax, o.xmax); |
54 | ymax = hb_min (ymax, o.ymax); |
55 | } |
56 | |
57 | void |
58 | add_point (float x, float y) |
59 | { |
60 | if (unlikely (is_void ())) |
61 | { |
62 | xmin = xmax = x; |
63 | ymin = ymax = y; |
64 | } |
65 | else |
66 | { |
67 | xmin = hb_min (xmin, x); |
68 | ymin = hb_min (ymin, y); |
69 | xmax = hb_max (xmax, x); |
70 | ymax = hb_max (ymax, y); |
71 | } |
72 | } |
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 | hb_transform_t () {} |
83 | hb_transform_t (float xx, float yx, |
84 | float xy, float yy, |
85 | float x0, float y0) : |
86 | xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {} |
87 | |
88 | void multiply (const hb_transform_t &o) |
89 | { |
90 | /* Copied from cairo, with "o" being "a" there and "this" being "b" there. */ |
91 | hb_transform_t r; |
92 | |
93 | r.xx = o.xx * xx + o.yx * xy; |
94 | r.yx = o.xx * yx + o.yx * yy; |
95 | |
96 | r.xy = o.xy * xx + o.yy * xy; |
97 | r.yy = o.xy * yx + o.yy * yy; |
98 | |
99 | r.x0 = o.x0 * xx + o.y0 * xy + x0; |
100 | r.y0 = o.x0 * yx + o.y0 * yy + y0; |
101 | |
102 | *this = r; |
103 | } |
104 | |
105 | void transform_distance (float &dx, float &dy) const |
106 | { |
107 | float new_x = xx * dx + xy * dy; |
108 | float new_y = yx * dx + yy * dy; |
109 | dx = new_x; |
110 | dy = new_y; |
111 | } |
112 | |
113 | void transform_point (float &x, float &y) const |
114 | { |
115 | transform_distance (x, y); |
116 | x += x0; |
117 | y += y0; |
118 | } |
119 | |
120 | void transform_extents (hb_extents_t &extents) const |
121 | { |
122 | float quad_x[4], quad_y[4]; |
123 | |
124 | quad_x[0] = extents.xmin; |
125 | quad_y[0] = extents.ymin; |
126 | quad_x[1] = extents.xmin; |
127 | quad_y[1] = extents.ymax; |
128 | quad_x[2] = extents.xmax; |
129 | quad_y[2] = extents.ymin; |
130 | quad_x[3] = extents.xmax; |
131 | quad_y[3] = extents.ymax; |
132 | |
133 | extents = hb_extents_t {}; |
134 | for (unsigned i = 0; i < 4; i++) |
135 | { |
136 | transform_point (quad_x[i], quad_y[i]); |
137 | extents.add_point (quad_x[i], quad_y[i]); |
138 | } |
139 | } |
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 | hb_bounds_t (status_t status) : status (status) {} |
158 | hb_bounds_t (const hb_extents_t &extents) : |
159 | status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {} |
160 | |
161 | void union_ (const hb_bounds_t &o) |
162 | { |
163 | if (o.status == UNBOUNDED) |
164 | status = UNBOUNDED; |
165 | else if (o.status == BOUNDED) |
166 | { |
167 | if (status == EMPTY) |
168 | *this = o; |
169 | else if (status == BOUNDED) |
170 | extents.union_ (o.extents); |
171 | } |
172 | } |
173 | |
174 | void intersect (const hb_bounds_t &o) |
175 | { |
176 | if (o.status == EMPTY) |
177 | status = EMPTY; |
178 | else if (o.status == BOUNDED) |
179 | { |
180 | if (status == UNBOUNDED) |
181 | *this = o; |
182 | else if (status == BOUNDED) |
183 | { |
184 | extents.intersect (o.extents); |
185 | if (extents.is_empty ()) |
186 | status = EMPTY; |
187 | } |
188 | } |
189 | } |
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 | { |
201 | transforms.push (hb_transform_t{}); |
202 | clips.push (hb_bounds_t{hb_bounds_t::UNBOUNDED}); |
203 | groups.push (hb_bounds_t{hb_bounds_t::EMPTY}); |
204 | } |
205 | |
206 | hb_extents_t get_extents () |
207 | { |
208 | return groups.tail().extents; |
209 | } |
210 | |
211 | bool is_bounded () |
212 | { |
213 | return groups.tail().status != hb_bounds_t::UNBOUNDED; |
214 | } |
215 | |
216 | void push_transform (const hb_transform_t &trans) |
217 | { |
218 | hb_transform_t t = transforms.tail (); |
219 | t.multiply (trans); |
220 | transforms.push (t); |
221 | } |
222 | |
223 | void pop_transform () |
224 | { |
225 | transforms.pop (); |
226 | } |
227 | |
228 | void push_clip (hb_extents_t extents) |
229 | { |
230 | /* Transform extents and push a new clip. */ |
231 | const hb_transform_t &t = transforms.tail (); |
232 | t.transform_extents (extents); |
233 | |
234 | clips.push (hb_bounds_t {extents}); |
235 | } |
236 | |
237 | void pop_clip () |
238 | { |
239 | clips.pop (); |
240 | } |
241 | |
242 | void push_group () |
243 | { |
244 | groups.push (hb_bounds_t {hb_bounds_t::EMPTY}); |
245 | } |
246 | |
247 | void pop_group (hb_paint_composite_mode_t mode) |
248 | { |
249 | const hb_bounds_t src_bounds = groups.pop (); |
250 | hb_bounds_t &backdrop_bounds = groups.tail (); |
251 | |
252 | // https://learn.microsoft.com/en-us/typography/opentype/spec/colr#format-32-paintcomposite |
253 | switch ((int) mode) |
254 | { |
255 | case HB_PAINT_COMPOSITE_MODE_CLEAR: |
256 | backdrop_bounds.status = hb_bounds_t::EMPTY; |
257 | break; |
258 | case HB_PAINT_COMPOSITE_MODE_SRC: |
259 | case HB_PAINT_COMPOSITE_MODE_SRC_OUT: |
260 | backdrop_bounds = src_bounds; |
261 | break; |
262 | case HB_PAINT_COMPOSITE_MODE_DEST: |
263 | case HB_PAINT_COMPOSITE_MODE_DEST_OUT: |
264 | break; |
265 | case HB_PAINT_COMPOSITE_MODE_SRC_IN: |
266 | case HB_PAINT_COMPOSITE_MODE_DEST_IN: |
267 | backdrop_bounds.intersect (src_bounds); |
268 | break; |
269 | default: |
270 | backdrop_bounds.union_ (src_bounds); |
271 | break; |
272 | } |
273 | } |
274 | |
275 | void paint () |
276 | { |
277 | const hb_bounds_t &clip = clips.tail (); |
278 | hb_bounds_t &group = groups.tail (); |
279 | |
280 | group.union_ (clip); |
281 | } |
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 */ |
294 | |