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 "gskgltextureprivate.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 |
46 | nine_slice_is_visible (const GskGLTextureNineSlice *slice) |
47 | { |
48 | return slice->rect.width > 0 && slice->rect.height > 0; |
49 | } |
50 | |
51 | static inline void |
52 | nine_slice_rounded_rect (GskGLTextureNineSlice *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 |
133 | nine_slice_to_texture_coords (GskGLTextureNineSlice *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 | GskGLTextureNineSlice *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 |
161 | nine_slice_grow (GskGLTextureNineSlice *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 | |