1 /* ninesliceprivate.h
2  *
3  * Copyright 2017 Timm Bäder <mail@baedert.org>
4  * Copyright 2021 Christian Hergert <chergert@redhat.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  * SPDX-License-Identifier: LGPL-2.1-or-later
20  */
21 
22 #ifndef __NINE_SLICE_PRIVATE_H__
23 #define __NINE_SLICE_PRIVATE_H__
24 
25 #include "gskngltexturepoolprivate.h"
26 
27 #if 0
28 # define DEBUG_NINE_SLICE
29 #endif
30 
31 G_BEGIN_DECLS
32 
33 enum {
34   NINE_SLICE_TOP_LEFT      = 0,
35   NINE_SLICE_TOP_CENTER    = 1,
36   NINE_SLICE_TOP_RIGHT     = 2,
37   NINE_SLICE_LEFT_CENTER   = 3,
38   NINE_SLICE_CENTER        = 4,
39   NINE_SLICE_RIGHT_CENTER  = 5,
40   NINE_SLICE_BOTTOM_LEFT   = 6,
41   NINE_SLICE_BOTTOM_CENTER = 7,
42   NINE_SLICE_BOTTOM_RIGHT  = 8,
43 };
44 
45 static inline bool G_GNUC_PURE
nine_slice_is_visible(const GskNglTextureNineSlice * slice)46 nine_slice_is_visible (const GskNglTextureNineSlice *slice)
47 {
48   return slice->rect.width > 0 && slice->rect.height > 0;
49 }
50 
51 static inline void
nine_slice_rounded_rect(GskNglTextureNineSlice * slices,const GskRoundedRect * rect)52 nine_slice_rounded_rect (GskNglTextureNineSlice *slices,
53                          const GskRoundedRect   *rect)
54 {
55   const graphene_point_t *origin = &rect->bounds.origin;
56   const graphene_size_t *size = &rect->bounds.size;
57   int top_height = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].height,
58                                rect->corner[GSK_CORNER_TOP_RIGHT].height));
59   int bottom_height = ceilf (MAX (rect->corner[GSK_CORNER_BOTTOM_LEFT].height,
60                                   rect->corner[GSK_CORNER_BOTTOM_RIGHT].height));
61   int right_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_RIGHT].width,
62                                 rect->corner[GSK_CORNER_BOTTOM_RIGHT].width));
63   int left_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].width,
64                                rect->corner[GSK_CORNER_BOTTOM_LEFT].width));
65 
66   /* Top left */
67   slices[0].rect.x = origin->x;
68   slices[0].rect.y = origin->y;
69   slices[0].rect.width = left_width;
70   slices[0].rect.height = top_height;
71 
72   /* Top center */
73   slices[1].rect.x = origin->x + size->width / 2.0 - 0.5;
74   slices[1].rect.y = origin->y;
75   slices[1].rect.width = 1;
76   slices[1].rect.height = top_height;
77 
78   /* Top right */
79   slices[2].rect.x = origin->x + size->width - right_width;
80   slices[2].rect.y = origin->y;
81   slices[2].rect.width = right_width;
82   slices[2].rect.height = top_height;
83 
84   /* Left center */
85   slices[3].rect.x = origin->x;
86   slices[3].rect.y = origin->y + size->height / 2;
87   slices[3].rect.width = left_width;
88   slices[3].rect.height = 1;
89 
90   /* center */
91   slices[4].rect.x = origin->x + size->width / 2.0 - 0.5;
92   slices[4].rect.y = origin->y + size->height / 2.0 - 0.5;
93   slices[4].rect.width = 1;
94   slices[4].rect.height = 1;
95 
96   /* Right center */
97   slices[5].rect.x = origin->x + size->width - right_width;
98   slices[5].rect.y = origin->y + (size->height / 2.0) - 0.5;
99   slices[5].rect.width = right_width;
100   slices[5].rect.height = 1;
101 
102   /* Bottom Left */
103   slices[6].rect.x = origin->x;
104   slices[6].rect.y = origin->y + size->height - bottom_height;
105   slices[6].rect.width = left_width;
106   slices[6].rect.height = bottom_height;
107 
108   /* Bottom center */
109   slices[7].rect.x = origin->x + (size->width / 2.0) - 0.5;
110   slices[7].rect.y = origin->y + size->height - bottom_height;
111   slices[7].rect.width = 1;
112   slices[7].rect.height = bottom_height;
113 
114   /* Bottom right */
115   slices[8].rect.x = origin->x + size->width - right_width;
116   slices[8].rect.y = origin->y + size->height - bottom_height;
117   slices[8].rect.width = right_width;
118   slices[8].rect.height = bottom_height;
119 
120 #ifdef DEBUG_NINE_SLICE
121   /* These only hold true when the values from ceilf() above
122    * are greater than one. Otherwise they fail, like will happen
123    * with the node editor viewing the textures zoomed out.
124    */
125   if (size->width > 1)
126     g_assert_cmpfloat (size->width, >=, left_width + right_width);
127   if (size->height > 1)
128   g_assert_cmpfloat (size->height, >=, top_height + bottom_height);
129 #endif
130 }
131 
132 static inline void
nine_slice_to_texture_coords(GskNglTextureNineSlice * slices,int texture_width,int texture_height)133 nine_slice_to_texture_coords (GskNglTextureNineSlice *slices,
134                               int                     texture_width,
135                               int                     texture_height)
136 {
137   float fw = texture_width;
138   float fh = texture_height;
139 
140   for (guint i = 0; i < 9; i++)
141     {
142       GskNglTextureNineSlice *slice = &slices[i];
143 
144       slice->area.x = slice->rect.x / fw;
145       slice->area.y = 1.0 - ((slice->rect.y + slice->rect.height) / fh);
146       slice->area.x2 = ((slice->rect.x + slice->rect.width) / fw);
147       slice->area.y2 = (1.0 - (slice->rect.y / fh));
148 
149 #ifdef DEBUG_NINE_SLICE
150       g_assert_cmpfloat (slice->area.x, >=, 0);
151       g_assert_cmpfloat (slice->area.x, <=, 1);
152       g_assert_cmpfloat (slice->area.y, >=, 0);
153       g_assert_cmpfloat (slice->area.y, <=, 1);
154       g_assert_cmpfloat (slice->area.x2, >, slice->area.x);
155       g_assert_cmpfloat (slice->area.y2, >, slice->area.y);
156 #endif
157     }
158 }
159 
160 static inline void
nine_slice_grow(GskNglTextureNineSlice * slices,int amount_x,int amount_y)161 nine_slice_grow (GskNglTextureNineSlice *slices,
162                  int                     amount_x,
163                  int                     amount_y)
164 {
165   if (amount_x == 0 && amount_y == 0)
166     return;
167 
168   /* top left */
169   slices[0].rect.x -= amount_x;
170   slices[0].rect.y -= amount_y;
171   if (amount_x > slices[0].rect.width)
172     slices[0].rect.width += amount_x * 2;
173   else
174     slices[0].rect.width += amount_x;
175 
176   if (amount_y > slices[0].rect.height)
177     slices[0].rect.height += amount_y * 2;
178   else
179     slices[0].rect.height += amount_y;
180 
181 
182   /* Top center */
183   slices[1].rect.y -= amount_y;
184   if (amount_y > slices[1].rect.height)
185     slices[1].rect.height += amount_y * 2;
186   else
187     slices[1].rect.height += amount_y;
188 
189   /* top right */
190   slices[2].rect.y -= amount_y;
191   if (amount_x > slices[2].rect.width)
192     {
193       slices[2].rect.x -= amount_x;
194       slices[2].rect.width += amount_x * 2;
195     }
196   else
197     {
198      slices[2].rect.width += amount_x;
199     }
200 
201   if (amount_y > slices[2].rect.height)
202     slices[2].rect.height += amount_y * 2;
203   else
204     slices[2].rect.height += amount_y;
205 
206 
207 
208   slices[3].rect.x -= amount_x;
209   if (amount_x > slices[3].rect.width)
210     slices[3].rect.width += amount_x * 2;
211   else
212     slices[3].rect.width += amount_x;
213 
214   /* Leave center alone */
215 
216   if (amount_x > slices[5].rect.width)
217     {
218       slices[5].rect.x -= amount_x;
219       slices[5].rect.width += amount_x * 2;
220     }
221   else
222     {
223       slices[5].rect.width += amount_x;
224     }
225 
226 
227   /* Bottom left */
228   slices[6].rect.x -= amount_x;
229   if (amount_x > slices[6].rect.width)
230     {
231       slices[6].rect.width += amount_x * 2;
232     }
233   else
234     {
235       slices[6].rect.width += amount_x;
236     }
237 
238   if (amount_y > slices[6].rect.height)
239     {
240       slices[6].rect.y -= amount_y;
241       slices[6].rect.height += amount_y * 2;
242     }
243   else
244     {
245       slices[6].rect.height += amount_y;
246     }
247 
248 
249   /* Bottom center */
250   if (amount_y > slices[7].rect.height)
251     {
252       slices[7].rect.y -= amount_y;
253       slices[7].rect.height += amount_y * 2;
254     }
255   else
256     {
257       slices[7].rect.height += amount_y;
258     }
259 
260   if (amount_x > slices[8].rect.width)
261     {
262       slices[8].rect.x -= amount_x;
263       slices[8].rect.width += amount_x * 2;
264     }
265   else
266     {
267       slices[8].rect.width += amount_x;
268     }
269 
270   if (amount_y > slices[8].rect.height)
271     {
272       slices[8].rect.y -= amount_y;
273       slices[8].rect.height += amount_y * 2;
274     }
275   else
276     {
277       slices[8].rect.height += amount_y;
278     }
279 
280 #ifdef DEBUG_NINE_SLICE
281   /* These cannot be relied on in all cases right now, specifically
282    * when viewing data zoomed out.
283    */
284   for (guint i = 0; i < 9; i ++)
285     {
286       g_assert_cmpint (slices[i].rect.x, >=, 0);
287       g_assert_cmpint (slices[i].rect.y, >=, 0);
288       g_assert_cmpint (slices[i].rect.width, >=, 0);
289       g_assert_cmpint (slices[i].rect.height, >=, 0);
290     }
291 
292   /* Rows don't overlap */
293   for (guint i = 0; i < 3; i++)
294     {
295       int lhs = slices[i * 3 + 0].rect.x + slices[i * 3 + 0].rect.width;
296       int rhs = slices[i * 3 + 1].rect.x;
297 
298       /* Ignore the case where we are scaled out and the
299        * positioning is degenerate, such as from node-editor.
300        */
301       if (rhs > 1)
302         g_assert_cmpint (lhs, <, rhs);
303     }
304 #endif
305 
306 }
307 
308 G_END_DECLS
309 
310 #endif /* __NINE_SLICE_PRIVATE_H__ */
311