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 { hb_extents_thb_extents_t34 hb_extents_t () {} hb_extents_thb_extents_t35 hb_extents_t (float xmin, float ymin, float xmax, float ymax) : 36 xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {} 37 is_emptyhb_extents_t38 bool is_empty () const { return xmin >= xmax || ymin >= ymax; } is_voidhb_extents_t39 bool is_void () const { return xmin > xmax; } 40 union_hb_extents_t41 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 intersecthb_extents_t49 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 add_pointhb_extents_t58 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 { hb_transform_thb_transform_t82 hb_transform_t () {} hb_transform_thb_transform_t83 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 multiplyhb_transform_t88 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 transform_distancehb_transform_t105 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 transform_pointhb_transform_t113 void transform_point (float &x, float &y) const 114 { 115 transform_distance (x, y); 116 x += x0; 117 y += y0; 118 } 119 transform_extentshb_transform_t120 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 hb_bounds_thb_bounds_t157 hb_bounds_t (status_t status) : status (status) {} hb_bounds_thb_bounds_t158 hb_bounds_t (const hb_extents_t &extents) : 159 status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {} 160 union_hb_bounds_t161 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 intersecthb_bounds_t174 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 { hb_paint_extents_context_thb_paint_extents_context_t199 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 get_extentshb_paint_extents_context_t206 hb_extents_t get_extents () 207 { 208 return groups.tail().extents; 209 } 210 is_boundedhb_paint_extents_context_t211 bool is_bounded () 212 { 213 return groups.tail().status != hb_bounds_t::UNBOUNDED; 214 } 215 push_transformhb_paint_extents_context_t216 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 pop_transformhb_paint_extents_context_t223 void pop_transform () 224 { 225 transforms.pop (); 226 } 227 push_cliphb_paint_extents_context_t228 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 pop_cliphb_paint_extents_context_t237 void pop_clip () 238 { 239 clips.pop (); 240 } 241 push_grouphb_paint_extents_context_t242 void push_group () 243 { 244 groups.push (hb_bounds_t {hb_bounds_t::EMPTY}); 245 } 246 pop_grouphb_paint_extents_context_t247 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 painthb_paint_extents_context_t275 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