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
31G_BEGIN_DECLS
32
33enum {
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
45static inline bool G_GNUC_PURE
46nine_slice_is_visible (const GskGLTextureNineSlice *slice)
47{
48 return slice->rect.width > 0 && slice->rect.height > 0;
49}
50
51static inline void
52nine_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
132static inline void
133nine_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
160static inline void
161nine_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
308G_END_DECLS
309
310#endif /* __NINE_SLICE_PRIVATE_H__ */
311

source code of gtk/gsk/gl/ninesliceprivate.h