1 
2 #pragma once
3 
4 #define SANITY_CHECKS 0
5 
6 
7 
8 #define rounded_rect_top_left(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x, \
9                                                      r->bounds.origin.y, \
10                                                      r->corner[0].width, r->corner[0].height))
11 
12 #define rounded_rect_top_right(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x + r->bounds.size.width - r->corner[1].width, \
13                                                       r->bounds.origin.y, \
14                                                       r->corner[1].width, r->corner[1].height))
15 
16 #define rounded_rect_bottom_right(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x + r->bounds.size.width - r->corner[2].width, \
17                                                          r->bounds.origin.y + r->bounds.size.height - r->corner[2].height, \
18                                                          r->corner[2].width, r->corner[2].height))
19 
20 #define rounded_rect_bottom_left(r) (GRAPHENE_RECT_INIT(r->bounds.origin.x, \
21                                                          r->bounds.origin.y + r->bounds.size.height - r->corner[2].height, \
22                                                          r->corner[3].width, r->corner[3].height))
23 
24 
25 #define rounded_rect_corner0(r) rounded_rect_top_left(r)
26 #define rounded_rect_corner1(r) rounded_rect_top_right(r)
27 #define rounded_rect_corner2(r) rounded_rect_bottom_right(r)
28 #define rounded_rect_corner3(r) rounded_rect_bottom_left(r)
29 
30 #define rounded_rect_corner(r, i) (rounded_rect_corner ##i(r))
31 #define graphene_size_non_zero(s) (s->width > 0 && s->height > 0)
32 #define rounded_rect_has_corner(r, i) (r->corner[i].width > 0 && r->corner[i].height > 0)
33 
34 #define rect_contains_point(r, _x, _y) (_x >= (r)->origin.x && _x <= (r)->origin.x + (r)->size.width && \
35                                         _y >= (r)->origin.y && _y <= (r)->origin.y + (r)->size.height)
36 
37 enum {
38   NINE_SLICE_TOP_LEFT      = 0,
39   NINE_SLICE_TOP_CENTER    = 1,
40   NINE_SLICE_TOP_RIGHT     = 2,
41   NINE_SLICE_LEFT_CENTER   = 3,
42   NINE_SLICE_CENTER        = 4,
43   NINE_SLICE_RIGHT_CENTER  = 5,
44   NINE_SLICE_BOTTOM_LEFT   = 6,
45   NINE_SLICE_BOTTOM_CENTER = 7,
46   NINE_SLICE_BOTTOM_RIGHT  = 8,
47 };
48 #define NINE_SLICE_SIZE 9 /* Hah. */
49 
50 typedef struct
51 {
52   int texture_id;
53   float x;
54   float y;
55   float x2;
56   float y2;
57 } TextureRegion;
58 
59 static inline bool G_GNUC_PURE
slice_is_visible(const cairo_rectangle_int_t * r)60 slice_is_visible (const cairo_rectangle_int_t *r)
61 {
62   return (r->width > 0 && r->height > 0);
63 }
64 
65 static inline void
nine_slice_rounded_rect(const GskRoundedRect * rect,cairo_rectangle_int_t * out_rects)66 nine_slice_rounded_rect (const GskRoundedRect  *rect,
67                          cairo_rectangle_int_t *out_rects)
68 {
69   const graphene_point_t *origin = &rect->bounds.origin;
70   const graphene_size_t *size = &rect->bounds.size;
71   const int top_height = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].height,
72                                      rect->corner[GSK_CORNER_TOP_RIGHT].height));
73   const int bottom_height = ceilf (MAX (rect->corner[GSK_CORNER_BOTTOM_LEFT].height,
74                                         rect->corner[GSK_CORNER_BOTTOM_RIGHT].height));
75   const int right_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_RIGHT].width,
76                                       rect->corner[GSK_CORNER_BOTTOM_RIGHT].width));
77   const int left_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].width,
78                                      rect->corner[GSK_CORNER_BOTTOM_LEFT].width));
79 
80   /* Top left */
81   out_rects[0] = (cairo_rectangle_int_t) {
82                    origin->x, origin->y,
83                    left_width, top_height,
84                  };
85 
86   /* Top center */
87   out_rects[1] = (cairo_rectangle_int_t) {
88                    origin->x + size->width / 2.0 - 0.5, origin->y,
89                    1, top_height,
90                  };
91 
92   /* Top right */
93   out_rects[2] = (cairo_rectangle_int_t) {
94                    origin->x + size->width - right_width, origin->y,
95                    right_width, top_height
96                  };
97 
98   /* Left center */
99   out_rects[3] = (cairo_rectangle_int_t) {
100                    origin->x, origin->y + size->height / 2,
101                    left_width, 1,
102                  };
103 
104   /* center */
105   out_rects[4] = (cairo_rectangle_int_t) {
106                    origin->x + size->width / 2.0 - 0.5,
107                    origin->y + size->height / 2.0 - 0.5,
108                    1, 1
109                  };
110 
111   /* Right center */
112   out_rects[5] = (cairo_rectangle_int_t) {
113                    origin->x + size->width - right_width,
114                    origin->y + (size->height / 2.0) - 0.5,
115                    right_width,
116                    1,
117                  };
118 
119   /* Bottom Left */
120   out_rects[6] = (cairo_rectangle_int_t) {
121                    origin->x, origin->y + size->height - bottom_height,
122                    left_width, bottom_height,
123                  };
124 
125   /* Bottom center */
126   out_rects[7] = (cairo_rectangle_int_t) {
127                    origin->x + (size->width / 2.0) - 0.5,
128                      origin->y + size->height - bottom_height,
129                    1, bottom_height,
130                  };
131 
132   /* Bottom right */
133   out_rects[8] = (cairo_rectangle_int_t) {
134                    origin->x + size->width - right_width,
135                    origin->y + size->height - bottom_height,
136                    right_width, bottom_height,
137                  };
138 
139 #if SANITY_CHECKS
140   g_assert_cmpfloat (size->width, >=, left_width + right_width);
141   g_assert_cmpfloat (size->height, >=, top_height + bottom_height);
142 #endif
143 }
144 
145 static inline void
nine_slice_grow(cairo_rectangle_int_t * slices,const int amount)146 nine_slice_grow (cairo_rectangle_int_t *slices,
147                  const int              amount)
148 {
149   /* top left */
150   slices[0].x -= amount;
151   slices[0].y -= amount;
152   if (amount > slices[0].width)
153     slices[0].width += amount * 2;
154   else
155     slices[0].width += amount;
156 
157   if (amount > slices[0].height)
158     slices[0].height += amount * 2;
159   else
160     slices[0].height += amount;
161 
162 
163   /* Top center */
164   slices[1].y -= amount;
165   if (amount > slices[1].height)
166     slices[1].height += amount * 2;
167   else
168     slices[1].height += amount;
169 
170   /* top right */
171   slices[2].y -= amount;
172   if (amount > slices[2].width)
173     {
174       slices[2].x -= amount;
175       slices[2].width += amount * 2;
176     }
177   else
178     {
179      slices[2].width += amount;
180     }
181 
182   if (amount > slices[2].height)
183     slices[2].height += amount * 2;
184   else
185     slices[2].height += amount;
186 
187 
188 
189   slices[3].x -= amount;
190   if (amount > slices[3].width)
191     slices[3].width += amount * 2;
192   else
193     slices[3].width += amount;
194 
195   /* Leave Britney^Wcenter alone */
196 
197   if (amount > slices[5].width)
198     {
199       slices[5].x -= amount;
200       slices[5].width += amount * 2;
201     }
202   else
203     {
204       slices[5].width += amount;
205     }
206 
207 
208   /* Bottom left */
209   slices[6].x -= amount;
210   if (amount > slices[6].width)
211     {
212       slices[6].width += amount * 2;
213     }
214   else
215     {
216       slices[6].width += amount;
217     }
218 
219   if (amount > slices[6].height)
220     {
221       slices[6].y -= amount;
222       slices[6].height += amount * 2;
223     }
224   else
225     {
226       slices[6].height += amount;
227     }
228 
229 
230   /* Bottom center */
231   if (amount > slices[7].height)
232     {
233       slices[7].y -= amount;
234       slices[7].height += amount * 2;
235     }
236   else
237     {
238       slices[7].height += amount;
239     }
240 
241   if (amount > slices[8].width)
242     {
243       slices[8].x -= amount;
244       slices[8].width += amount * 2;
245     }
246   else
247     {
248       slices[8].width += amount;
249     }
250 
251   if (amount > slices[8].height)
252     {
253       slices[8].y -= amount;
254       slices[8].height += amount * 2;
255     }
256   else
257     {
258       slices[8].height += amount;
259     }
260 
261 #if SANITY_CHECKS
262   {
263     for (int i = 0; i < 9; i ++)
264       {
265         g_assert_cmpint (slices[i].x, >=, 0);
266         g_assert_cmpint (slices[i].y, >=, 0);
267         g_assert_cmpint (slices[i].width, >=, 0);
268         g_assert_cmpint (slices[i].height, >=, 0);
269       }
270 
271     /* Rows don't overlap */
272     for (int i = 0; i < 3; i++)
273       {
274         g_assert_cmpint (slices[i * 3 + 0].x + slices[i * 3 + 0].width, <, slices[i * 3 + 1].x);
275       }
276   }
277 #endif
278 
279 }
280 
281 static inline void
nine_slice_to_texture_coords(const cairo_rectangle_int_t * slices,const int texture_width,const int texture_height,TextureRegion * out_regions)282 nine_slice_to_texture_coords (const cairo_rectangle_int_t *slices,
283                               const int                    texture_width,
284                               const int                    texture_height,
285                               TextureRegion               *out_regions)
286 {
287   const float fw = (float)texture_width;
288   const float fh = (float)texture_height;
289   int i;
290 
291   for (i = 0; i < 9; i++)
292     {
293       out_regions[i] = (TextureRegion) {
294         0, /* Texture id */
295         slices[i].x / fw,
296         1.0 - ((slices[i].y + slices[i].height) / fh),
297         (slices[i].x + slices[i].width)  / fw,
298         1.0 - (slices[i].y / fh),
299       };
300     }
301 
302 #if SANITY_CHECKS
303   {
304     for (i = 0; i < 9; i++)
305       {
306         const TextureRegion *r = &out_regions[i];
307         g_assert_cmpfloat (r->x, >=, 0);
308         g_assert_cmpfloat (r->x, <=, 1);
309         g_assert_cmpfloat (r->y, >=, 0);
310         g_assert_cmpfloat (r->y, <=, 1);
311 
312         g_assert_cmpfloat (r->x, <, r->x2);
313         g_assert_cmpfloat (r->y, <, r->y2);
314       }
315   }
316 #endif
317 }
318