• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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