2020-05-20 06:01:48 +00:00
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#define SANITY_CHECKS 0
|
|
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
NINE_SLICE_TOP_LEFT = 0,
|
|
|
|
NINE_SLICE_TOP_CENTER = 1,
|
|
|
|
NINE_SLICE_TOP_RIGHT = 2,
|
|
|
|
NINE_SLICE_LEFT_CENTER = 3,
|
|
|
|
NINE_SLICE_CENTER = 4,
|
|
|
|
NINE_SLICE_RIGHT_CENTER = 5,
|
|
|
|
NINE_SLICE_BOTTOM_LEFT = 6,
|
|
|
|
NINE_SLICE_BOTTOM_CENTER = 7,
|
|
|
|
NINE_SLICE_BOTTOM_RIGHT = 8,
|
|
|
|
};
|
|
|
|
#define NINE_SLICE_SIZE 9 /* Hah. */
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
int texture_id;
|
|
|
|
float x;
|
|
|
|
float y;
|
|
|
|
float x2;
|
|
|
|
float y2;
|
|
|
|
} TextureRegion;
|
|
|
|
|
|
|
|
static inline bool G_GNUC_PURE
|
|
|
|
slice_is_visible (const cairo_rectangle_int_t *r)
|
|
|
|
{
|
|
|
|
return (r->width > 0 && r->height > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
nine_slice_rounded_rect (const GskRoundedRect *rect,
|
|
|
|
cairo_rectangle_int_t *out_rects)
|
|
|
|
{
|
|
|
|
const graphene_point_t *origin = &rect->bounds.origin;
|
|
|
|
const graphene_size_t *size = &rect->bounds.size;
|
|
|
|
const int top_height = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].height,
|
|
|
|
rect->corner[GSK_CORNER_TOP_RIGHT].height));
|
|
|
|
const int bottom_height = ceilf (MAX (rect->corner[GSK_CORNER_BOTTOM_LEFT].height,
|
|
|
|
rect->corner[GSK_CORNER_BOTTOM_RIGHT].height));
|
|
|
|
const int right_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_RIGHT].width,
|
|
|
|
rect->corner[GSK_CORNER_BOTTOM_RIGHT].width));
|
|
|
|
const int left_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].width,
|
|
|
|
rect->corner[GSK_CORNER_BOTTOM_LEFT].width));
|
|
|
|
|
|
|
|
/* Top left */
|
|
|
|
out_rects[0] = (cairo_rectangle_int_t) {
|
|
|
|
origin->x, origin->y,
|
|
|
|
left_width, top_height,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Top center */
|
|
|
|
out_rects[1] = (cairo_rectangle_int_t) {
|
2020-05-22 17:37:15 +00:00
|
|
|
origin->x + size->width / 2.0 - 0.5, origin->y,
|
2020-05-20 06:01:48 +00:00
|
|
|
1, top_height,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Top right */
|
|
|
|
out_rects[2] = (cairo_rectangle_int_t) {
|
|
|
|
origin->x + size->width - right_width, origin->y,
|
|
|
|
right_width, top_height
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Left center */
|
|
|
|
out_rects[3] = (cairo_rectangle_int_t) {
|
|
|
|
origin->x, origin->y + size->height / 2,
|
|
|
|
left_width, 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* center */
|
|
|
|
out_rects[4] = (cairo_rectangle_int_t) {
|
2020-05-22 17:37:15 +00:00
|
|
|
origin->x + size->width / 2.0 - 0.5,
|
|
|
|
origin->y + size->height / 2.0 - 0.5,
|
2020-05-20 06:01:48 +00:00
|
|
|
1, 1
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Right center */
|
|
|
|
out_rects[5] = (cairo_rectangle_int_t) {
|
|
|
|
origin->x + size->width - right_width,
|
2020-05-22 17:37:15 +00:00
|
|
|
origin->y + (size->height / 2.0) - 0.5,
|
2020-05-20 06:01:48 +00:00
|
|
|
right_width,
|
|
|
|
1,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Bottom Left */
|
|
|
|
out_rects[6] = (cairo_rectangle_int_t) {
|
|
|
|
origin->x, origin->y + size->height - bottom_height,
|
|
|
|
left_width, bottom_height,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Bottom center */
|
|
|
|
out_rects[7] = (cairo_rectangle_int_t) {
|
2020-05-22 17:37:15 +00:00
|
|
|
origin->x + (size->width / 2.0) - 0.5,
|
|
|
|
origin->y + size->height - bottom_height,
|
2020-05-20 06:01:48 +00:00
|
|
|
1, bottom_height,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Bottom right */
|
|
|
|
out_rects[8] = (cairo_rectangle_int_t) {
|
|
|
|
origin->x + size->width - right_width,
|
|
|
|
origin->y + size->height - bottom_height,
|
|
|
|
right_width, bottom_height,
|
|
|
|
};
|
|
|
|
|
|
|
|
#if SANITY_CHECKS
|
|
|
|
g_assert_cmpfloat (size->width, >=, left_width + right_width);
|
|
|
|
g_assert_cmpfloat (size->height, >=, top_height + bottom_height);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
nine_slice_grow (cairo_rectangle_int_t *slices,
|
|
|
|
const int amount)
|
|
|
|
{
|
|
|
|
/* top left */
|
|
|
|
slices[0].x -= amount;
|
|
|
|
slices[0].y -= amount;
|
|
|
|
if (amount > slices[0].width)
|
|
|
|
slices[0].width += amount * 2;
|
|
|
|
else
|
|
|
|
slices[0].width += amount;
|
|
|
|
|
|
|
|
if (amount > slices[0].height)
|
|
|
|
slices[0].height += amount * 2;
|
|
|
|
else
|
|
|
|
slices[0].height += amount;
|
|
|
|
|
|
|
|
|
|
|
|
/* Top center */
|
|
|
|
slices[1].y -= amount;
|
|
|
|
if (amount > slices[1].height)
|
|
|
|
slices[1].height += amount * 2;
|
|
|
|
else
|
|
|
|
slices[1].height += amount;
|
|
|
|
|
|
|
|
/* top right */
|
|
|
|
slices[2].y -= amount;
|
|
|
|
if (amount > slices[2].width)
|
|
|
|
{
|
|
|
|
slices[2].x -= amount;
|
|
|
|
slices[2].width += amount * 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slices[2].width += amount;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (amount > slices[2].height)
|
|
|
|
slices[2].height += amount * 2;
|
|
|
|
else
|
|
|
|
slices[2].height += amount;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
slices[3].x -= amount;
|
|
|
|
if (amount > slices[3].width)
|
|
|
|
slices[3].width += amount * 2;
|
|
|
|
else
|
|
|
|
slices[3].width += amount;
|
|
|
|
|
|
|
|
/* Leave Britney^Wcenter alone */
|
|
|
|
|
|
|
|
if (amount > slices[5].width)
|
|
|
|
{
|
|
|
|
slices[5].x -= amount;
|
|
|
|
slices[5].width += amount * 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slices[5].width += amount;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Bottom left */
|
|
|
|
slices[6].x -= amount;
|
|
|
|
if (amount > slices[6].width)
|
|
|
|
{
|
|
|
|
slices[6].width += amount * 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slices[6].width += amount;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (amount > slices[6].height)
|
|
|
|
{
|
|
|
|
slices[6].y -= amount;
|
|
|
|
slices[6].height += amount * 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slices[6].height += amount;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Bottom center */
|
|
|
|
if (amount > slices[7].height)
|
|
|
|
{
|
|
|
|
slices[7].y -= amount;
|
|
|
|
slices[7].height += amount * 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slices[7].height += amount;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (amount > slices[8].width)
|
|
|
|
{
|
|
|
|
slices[8].x -= amount;
|
|
|
|
slices[8].width += amount * 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slices[8].width += amount;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (amount > slices[8].height)
|
|
|
|
{
|
|
|
|
slices[8].y -= amount;
|
|
|
|
slices[8].height += amount * 2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slices[8].height += amount;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if SANITY_CHECKS
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 9; i ++)
|
|
|
|
{
|
|
|
|
g_assert_cmpint (slices[i].x, >=, 0);
|
|
|
|
g_assert_cmpint (slices[i].y, >=, 0);
|
|
|
|
g_assert_cmpint (slices[i].width, >=, 0);
|
|
|
|
g_assert_cmpint (slices[i].height, >=, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Rows don't overlap */
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
g_assert_cmpint (slices[i * 3 + 0].x + slices[i * 3 + 0].width, <, slices[i * 3 + 1].x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
const float fw = (float)texture_width;
|
|
|
|
const float fh = (float)texture_height;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 9; i++)
|
|
|
|
{
|
|
|
|
out_regions[i] = (TextureRegion) {
|
|
|
|
0, /* Texture id */
|
|
|
|
slices[i].x / fw,
|
|
|
|
1.0 - ((slices[i].y + slices[i].height) / fh),
|
|
|
|
(slices[i].x + slices[i].width) / fw,
|
|
|
|
1.0 - (slices[i].y / fh),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
#if SANITY_CHECKS
|
|
|
|
{
|
|
|
|
for (i = 0; i < 9; i++)
|
|
|
|
{
|
|
|
|
const TextureRegion *r = &out_regions[i];
|
|
|
|
g_assert_cmpfloat (r->x, >=, 0);
|
|
|
|
g_assert_cmpfloat (r->x, <=, 1);
|
|
|
|
g_assert_cmpfloat (r->y, >=, 0);
|
|
|
|
g_assert_cmpfloat (r->y, <=, 1);
|
|
|
|
|
|
|
|
g_assert_cmpfloat (r->x, <, r->x2);
|
|
|
|
g_assert_cmpfloat (r->y, <, r->y2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|