gtk2/gsk/ngl/ninesliceprivate.h
Matthias Clasen 849692b24b ngl: Fix unevenly scaled shadows
Ensure that we don't cut them off at the edges.
2021-04-11 22:35:02 -04:00

311 lines
8.5 KiB
C

/* ninesliceprivate.h
*
* Copyright 2017 Timm Bäder <mail@baedert.org>
* Copyright 2021 Christian Hergert <chergert@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef __NINE_SLICE_PRIVATE_H__
#define __NINE_SLICE_PRIVATE_H__
#include "gskngltexturepoolprivate.h"
#if 0
# define DEBUG_NINE_SLICE
#endif
G_BEGIN_DECLS
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,
};
static inline bool G_GNUC_PURE
nine_slice_is_visible (const GskNglTextureNineSlice *slice)
{
return slice->rect.width > 0 && slice->rect.height > 0;
}
static inline void
nine_slice_rounded_rect (GskNglTextureNineSlice *slices,
const GskRoundedRect *rect)
{
const graphene_point_t *origin = &rect->bounds.origin;
const graphene_size_t *size = &rect->bounds.size;
int top_height = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].height,
rect->corner[GSK_CORNER_TOP_RIGHT].height));
int bottom_height = ceilf (MAX (rect->corner[GSK_CORNER_BOTTOM_LEFT].height,
rect->corner[GSK_CORNER_BOTTOM_RIGHT].height));
int right_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_RIGHT].width,
rect->corner[GSK_CORNER_BOTTOM_RIGHT].width));
int left_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].width,
rect->corner[GSK_CORNER_BOTTOM_LEFT].width));
/* Top left */
slices[0].rect.x = origin->x;
slices[0].rect.y = origin->y;
slices[0].rect.width = left_width;
slices[0].rect.height = top_height;
/* Top center */
slices[1].rect.x = origin->x + size->width / 2.0 - 0.5;
slices[1].rect.y = origin->y;
slices[1].rect.width = 1;
slices[1].rect.height = top_height;
/* Top right */
slices[2].rect.x = origin->x + size->width - right_width;
slices[2].rect.y = origin->y;
slices[2].rect.width = right_width;
slices[2].rect.height = top_height;
/* Left center */
slices[3].rect.x = origin->x;
slices[3].rect.y = origin->y + size->height / 2;
slices[3].rect.width = left_width;
slices[3].rect.height = 1;
/* center */
slices[4].rect.x = origin->x + size->width / 2.0 - 0.5;
slices[4].rect.y = origin->y + size->height / 2.0 - 0.5;
slices[4].rect.width = 1;
slices[4].rect.height = 1;
/* Right center */
slices[5].rect.x = origin->x + size->width - right_width;
slices[5].rect.y = origin->y + (size->height / 2.0) - 0.5;
slices[5].rect.width = right_width;
slices[5].rect.height = 1;
/* Bottom Left */
slices[6].rect.x = origin->x;
slices[6].rect.y = origin->y + size->height - bottom_height;
slices[6].rect.width = left_width;
slices[6].rect.height = bottom_height;
/* Bottom center */
slices[7].rect.x = origin->x + (size->width / 2.0) - 0.5;
slices[7].rect.y = origin->y + size->height - bottom_height;
slices[7].rect.width = 1;
slices[7].rect.height = bottom_height;
/* Bottom right */
slices[8].rect.x = origin->x + size->width - right_width;
slices[8].rect.y = origin->y + size->height - bottom_height;
slices[8].rect.width = right_width;
slices[8].rect.height = bottom_height;
#ifdef DEBUG_NINE_SLICE
/* These only hold true when the values from ceilf() above
* are greater than one. Otherwise they fail, like will happen
* with the node editor viewing the textures zoomed out.
*/
if (size->width > 1)
g_assert_cmpfloat (size->width, >=, left_width + right_width);
if (size->height > 1)
g_assert_cmpfloat (size->height, >=, top_height + bottom_height);
#endif
}
static inline void
nine_slice_to_texture_coords (GskNglTextureNineSlice *slices,
int texture_width,
int texture_height)
{
float fw = texture_width;
float fh = texture_height;
for (guint i = 0; i < 9; i++)
{
GskNglTextureNineSlice *slice = &slices[i];
slice->area.x = slice->rect.x / fw;
slice->area.y = 1.0 - ((slice->rect.y + slice->rect.height) / fh);
slice->area.x2 = ((slice->rect.x + slice->rect.width) / fw);
slice->area.y2 = (1.0 - (slice->rect.y / fh));
#ifdef DEBUG_NINE_SLICE
g_assert_cmpfloat (slice->area.x, >=, 0);
g_assert_cmpfloat (slice->area.x, <=, 1);
g_assert_cmpfloat (slice->area.y, >=, 0);
g_assert_cmpfloat (slice->area.y, <=, 1);
g_assert_cmpfloat (slice->area.x2, >, slice->area.x);
g_assert_cmpfloat (slice->area.y2, >, slice->area.y);
#endif
}
}
static inline void
nine_slice_grow (GskNglTextureNineSlice *slices,
int amount_x,
int amount_y)
{
if (amount_x == 0 && amount_y == 0)
return;
/* top left */
slices[0].rect.x -= amount_x;
slices[0].rect.y -= amount_y;
if (amount_x > slices[0].rect.width)
slices[0].rect.width += amount_x * 2;
else
slices[0].rect.width += amount_x;
if (amount_y > slices[0].rect.height)
slices[0].rect.height += amount_y * 2;
else
slices[0].rect.height += amount_y;
/* Top center */
slices[1].rect.y -= amount_y;
if (amount_y > slices[1].rect.height)
slices[1].rect.height += amount_y * 2;
else
slices[1].rect.height += amount_y;
/* top right */
slices[2].rect.y -= amount_y;
if (amount_x > slices[2].rect.width)
{
slices[2].rect.x -= amount_x;
slices[2].rect.width += amount_x * 2;
}
else
{
slices[2].rect.width += amount_x;
}
if (amount_y > slices[2].rect.height)
slices[2].rect.height += amount_y * 2;
else
slices[2].rect.height += amount_y;
slices[3].rect.x -= amount_x;
if (amount_x > slices[3].rect.width)
slices[3].rect.width += amount_x * 2;
else
slices[3].rect.width += amount_x;
/* Leave center alone */
if (amount_x > slices[5].rect.width)
{
slices[5].rect.x -= amount_x;
slices[5].rect.width += amount_x * 2;
}
else
{
slices[5].rect.width += amount_x;
}
/* Bottom left */
slices[6].rect.x -= amount_x;
if (amount_x > slices[6].rect.width)
{
slices[6].rect.width += amount_x * 2;
}
else
{
slices[6].rect.width += amount_x;
}
if (amount_y > slices[6].rect.height)
{
slices[6].rect.y -= amount_y;
slices[6].rect.height += amount_y * 2;
}
else
{
slices[6].rect.height += amount_y;
}
/* Bottom center */
if (amount_y > slices[7].rect.height)
{
slices[7].rect.y -= amount_y;
slices[7].rect.height += amount_y * 2;
}
else
{
slices[7].rect.height += amount_y;
}
if (amount_x > slices[8].rect.width)
{
slices[8].rect.x -= amount_x;
slices[8].rect.width += amount_x * 2;
}
else
{
slices[8].rect.width += amount_x;
}
if (amount_y > slices[8].rect.height)
{
slices[8].rect.y -= amount_y;
slices[8].rect.height += amount_y * 2;
}
else
{
slices[8].rect.height += amount_y;
}
#ifdef DEBUG_NINE_SLICE
/* These cannot be relied on in all cases right now, specifically
* when viewing data zoomed out.
*/
for (guint i = 0; i < 9; i ++)
{
g_assert_cmpint (slices[i].rect.x, >=, 0);
g_assert_cmpint (slices[i].rect.y, >=, 0);
g_assert_cmpint (slices[i].rect.width, >=, 0);
g_assert_cmpint (slices[i].rect.height, >=, 0);
}
/* Rows don't overlap */
for (guint i = 0; i < 3; i++)
{
int lhs = slices[i * 3 + 0].rect.x + slices[i * 3 + 0].rect.width;
int rhs = slices[i * 3 + 1].rect.x;
/* Ignore the case where we are scaled out and the
* positioning is degenerate, such as from node-editor.
*/
if (rhs > 1)
g_assert_cmpint (lhs, <, rhs);
}
#endif
}
G_END_DECLS
#endif /* __NINE_SLICE_PRIVATE_H__ */