mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-27 06:00:22 +00:00
7b0b3d9547
This is similar to how we share texture atlases. Some added complexity in that the program state also needed to be shared, so it had to move to the shared Programs object. With this change realization of additional GskRenderers when opening popups went from ~60msec to ~35 msec on average.
746 lines
20 KiB
C
746 lines
20 KiB
C
#include "gskglrenderopsprivate.h"
|
|
#include "gsktransform.h"
|
|
|
|
static inline gboolean
|
|
rect_equal (const graphene_rect_t *a,
|
|
const graphene_rect_t *b)
|
|
{
|
|
return memcmp (a, b, sizeof (graphene_rect_t)) == 0;
|
|
}
|
|
|
|
static inline gboolean G_GNUC_PURE
|
|
rounded_rect_equal (const GskRoundedRect *r1,
|
|
const GskRoundedRect *r2)
|
|
{
|
|
int i;
|
|
|
|
if (!r1)
|
|
return FALSE;
|
|
|
|
if (r1->bounds.origin.x != r2->bounds.origin.x ||
|
|
r1->bounds.origin.y != r2->bounds.origin.y ||
|
|
r1->bounds.size.width != r2->bounds.size.width ||
|
|
r1->bounds.size.height != r2->bounds.size.height)
|
|
return FALSE;
|
|
|
|
for (i = 0; i < 4; i ++)
|
|
if (r1->corner[i].width != r2->corner[i].width ||
|
|
r1->corner[i].height != r2->corner[i].height)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean G_GNUC_PURE
|
|
rounded_rect_corners_equal (const GskRoundedRect *r1,
|
|
const GskRoundedRect *r2)
|
|
{
|
|
int i;
|
|
|
|
if (!r1)
|
|
return FALSE;
|
|
|
|
for (i = 0; i < 4; i ++)
|
|
if (r1->corner[i].width != r2->corner[i].width ||
|
|
r1->corner[i].height != r2->corner[i].height)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline ProgramState *
|
|
get_current_program_state (RenderOpBuilder *builder)
|
|
{
|
|
if (!builder->current_program)
|
|
return NULL;
|
|
|
|
return &builder->programs->state[builder->current_program->index];
|
|
}
|
|
|
|
void
|
|
ops_finish (RenderOpBuilder *builder)
|
|
{
|
|
if (builder->mv_stack)
|
|
g_array_free (builder->mv_stack, TRUE);
|
|
builder->mv_stack = NULL;
|
|
|
|
if (builder->clip_stack)
|
|
g_array_free (builder->clip_stack, TRUE);
|
|
builder->clip_stack = NULL;
|
|
|
|
builder->dx = 0;
|
|
builder->dy = 0;
|
|
builder->scale_x = 1;
|
|
builder->scale_y = 1;
|
|
builder->current_modelview = NULL;
|
|
builder->current_clip = NULL;
|
|
builder->current_render_target = 0;
|
|
builder->current_texture = 0;
|
|
builder->current_program = NULL;
|
|
graphene_matrix_init_identity (&builder->current_projection);
|
|
builder->current_viewport = GRAPHENE_RECT_INIT (0, 0, 0, 0);
|
|
}
|
|
|
|
/* Debugging only! */
|
|
void
|
|
ops_dump_framebuffer (RenderOpBuilder *builder,
|
|
const char *filename,
|
|
int width,
|
|
int height)
|
|
{
|
|
OpDumpFrameBuffer *op;
|
|
|
|
op = ops_begin (builder, OP_DUMP_FRAMEBUFFER);
|
|
op->filename = g_strdup (filename);
|
|
op->width = width;
|
|
op->height = height;
|
|
}
|
|
|
|
void
|
|
ops_push_debug_group (RenderOpBuilder *builder,
|
|
const char *text)
|
|
{
|
|
OpDebugGroup *op;
|
|
|
|
op = ops_begin (builder, OP_PUSH_DEBUG_GROUP);
|
|
strncpy (op->text, text, sizeof(op->text) - 1);
|
|
op->text[sizeof(op->text) - 1] = 0; /* Ensure zero terminated */
|
|
}
|
|
|
|
void
|
|
ops_pop_debug_group (RenderOpBuilder *builder)
|
|
{
|
|
ops_begin (builder, OP_POP_DEBUG_GROUP);
|
|
}
|
|
|
|
float
|
|
ops_get_scale (const RenderOpBuilder *builder)
|
|
{
|
|
g_assert (builder->mv_stack != NULL);
|
|
g_assert (builder->mv_stack->len >= 1);
|
|
|
|
/* TODO: Use two separate values */
|
|
return MAX (builder->scale_x, builder->scale_y);
|
|
}
|
|
|
|
static void
|
|
extract_matrix_metadata (GskTransform *transform,
|
|
OpsMatrixMetadata *md)
|
|
{
|
|
float dummy;
|
|
|
|
switch (gsk_transform_get_category (transform))
|
|
{
|
|
case GSK_TRANSFORM_CATEGORY_IDENTITY:
|
|
case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
|
|
md->scale_x = 1;
|
|
md->scale_y = 1;
|
|
break;
|
|
|
|
case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
|
|
gsk_transform_to_affine (transform,
|
|
&md->scale_x, &md->scale_y,
|
|
&dummy, &dummy);
|
|
break;
|
|
|
|
case GSK_TRANSFORM_CATEGORY_UNKNOWN:
|
|
case GSK_TRANSFORM_CATEGORY_ANY:
|
|
case GSK_TRANSFORM_CATEGORY_3D:
|
|
case GSK_TRANSFORM_CATEGORY_2D:
|
|
{
|
|
graphene_vec3_t col1;
|
|
graphene_vec3_t col2;
|
|
graphene_matrix_t m;
|
|
|
|
gsk_transform_to_matrix (transform, &m);
|
|
|
|
/* TODO: 90% sure this is incorrect. But we should never hit this code
|
|
* path anyway. */
|
|
graphene_vec3_init (&col1,
|
|
graphene_matrix_get_value (&m, 0, 0),
|
|
graphene_matrix_get_value (&m, 1, 0),
|
|
graphene_matrix_get_value (&m, 2, 0));
|
|
|
|
graphene_vec3_init (&col2,
|
|
graphene_matrix_get_value (&m, 0, 1),
|
|
graphene_matrix_get_value (&m, 1, 1),
|
|
graphene_matrix_get_value (&m, 2, 1));
|
|
|
|
md->scale_x = graphene_vec3_length (&col1);
|
|
md->scale_y = graphene_vec3_length (&col2);
|
|
}
|
|
break;
|
|
default:
|
|
{}
|
|
}
|
|
}
|
|
|
|
void
|
|
ops_transform_bounds_modelview (const RenderOpBuilder *builder,
|
|
const graphene_rect_t *src,
|
|
graphene_rect_t *dst)
|
|
{
|
|
graphene_rect_t r = *src;
|
|
|
|
g_assert (builder->mv_stack != NULL);
|
|
g_assert (builder->mv_stack->len >= 1);
|
|
|
|
r.origin.x += builder->dx;
|
|
r.origin.y += builder->dy;
|
|
|
|
gsk_transform_transform_bounds (builder->current_modelview, &r, dst);
|
|
}
|
|
|
|
void
|
|
ops_init (RenderOpBuilder *builder)
|
|
{
|
|
memset (builder, 0, sizeof (*builder));
|
|
|
|
builder->current_opacity = 1.0f;
|
|
|
|
op_buffer_init (&builder->render_ops);
|
|
builder->vertices = g_array_new (FALSE, TRUE, sizeof (GskQuadVertex));
|
|
}
|
|
|
|
void
|
|
ops_free (RenderOpBuilder *builder)
|
|
{
|
|
g_array_unref (builder->vertices);
|
|
op_buffer_destroy (&builder->render_ops);
|
|
}
|
|
|
|
void
|
|
ops_set_program (RenderOpBuilder *builder,
|
|
const Program *program)
|
|
{
|
|
OpProgram *op;
|
|
ProgramState *program_state;
|
|
|
|
if (builder->current_program == program)
|
|
return;
|
|
|
|
op = ops_begin (builder, OP_CHANGE_PROGRAM);
|
|
op->program = program;
|
|
|
|
builder->current_program = program;
|
|
|
|
program_state = &builder->programs->state[program->index];
|
|
|
|
if (memcmp (&builder->current_projection, &program_state->projection, sizeof (graphene_matrix_t)) != 0)
|
|
{
|
|
OpMatrix *opm;
|
|
|
|
opm = ops_begin (builder, OP_CHANGE_PROJECTION);
|
|
opm->matrix = builder->current_projection;
|
|
program_state->projection = builder->current_projection;
|
|
}
|
|
|
|
if (program_state->modelview == NULL ||
|
|
!gsk_transform_equal (builder->current_modelview, program_state->modelview))
|
|
{
|
|
OpMatrix *opm;
|
|
|
|
opm = ops_begin (builder, OP_CHANGE_MODELVIEW);
|
|
gsk_transform_to_matrix (builder->current_modelview, &opm->matrix);
|
|
gsk_transform_unref (program_state->modelview);
|
|
program_state->modelview = gsk_transform_ref (builder->current_modelview);
|
|
}
|
|
|
|
if (!rect_equal (&builder->current_viewport, &program_state->viewport))
|
|
{
|
|
OpViewport *opv;
|
|
|
|
opv = ops_begin (builder, OP_CHANGE_VIEWPORT);
|
|
opv->viewport = builder->current_viewport;
|
|
program_state->viewport = builder->current_viewport;
|
|
}
|
|
|
|
if (!rounded_rect_equal (builder->current_clip, &program_state->clip))
|
|
{
|
|
OpClip *opc;
|
|
|
|
opc = ops_begin (builder, OP_CHANGE_CLIP);
|
|
opc->clip = *builder->current_clip;
|
|
opc->send_corners = !rounded_rect_corners_equal (builder->current_clip, &program_state->clip);
|
|
program_state->clip = *builder->current_clip;
|
|
}
|
|
|
|
if (program_state->opacity != builder->current_opacity)
|
|
{
|
|
OpOpacity *opo;
|
|
|
|
opo = ops_begin (builder, OP_CHANGE_OPACITY);
|
|
opo->opacity = builder->current_opacity;
|
|
program_state->opacity = builder->current_opacity;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ops_set_clip (RenderOpBuilder *builder,
|
|
const GskRoundedRect *clip)
|
|
{
|
|
ProgramState *current_program_state = get_current_program_state (builder);
|
|
OpClip *op;
|
|
|
|
if (current_program_state &&
|
|
rounded_rect_equal (¤t_program_state->clip, clip))
|
|
return;
|
|
|
|
if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_CLIP)))
|
|
{
|
|
op = op_buffer_add (&builder->render_ops, OP_CHANGE_CLIP);
|
|
op->send_corners = !current_program_state ||
|
|
!rounded_rect_corners_equal (¤t_program_state->clip, clip);
|
|
}
|
|
else
|
|
{
|
|
/* If the op before sent the corners, this one needs, too */
|
|
op->send_corners |= !current_program_state ||
|
|
!rounded_rect_corners_equal (¤t_program_state->clip, clip);
|
|
}
|
|
|
|
op->clip = *clip;
|
|
|
|
if (current_program_state)
|
|
current_program_state->clip = *clip;
|
|
}
|
|
|
|
void
|
|
ops_push_clip (RenderOpBuilder *self,
|
|
const GskRoundedRect *clip)
|
|
{
|
|
if (G_UNLIKELY (self->clip_stack == NULL))
|
|
self->clip_stack = g_array_new (FALSE, TRUE, sizeof (GskRoundedRect));
|
|
|
|
g_assert (self->clip_stack != NULL);
|
|
|
|
g_array_append_val (self->clip_stack, *clip);
|
|
self->current_clip = &g_array_index (self->clip_stack, GskRoundedRect, self->clip_stack->len - 1);
|
|
ops_set_clip (self, clip);
|
|
}
|
|
|
|
void
|
|
ops_pop_clip (RenderOpBuilder *self)
|
|
{
|
|
const GskRoundedRect *head;
|
|
|
|
g_assert (self->clip_stack);
|
|
g_assert (self->clip_stack->len >= 1);
|
|
|
|
self->clip_stack->len --;
|
|
head = &g_array_index (self->clip_stack, GskRoundedRect, self->clip_stack->len - 1);
|
|
|
|
if (self->clip_stack->len >= 1)
|
|
{
|
|
self->current_clip = head;
|
|
ops_set_clip (self, head);
|
|
}
|
|
else
|
|
{
|
|
self->current_clip = NULL;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
ops_has_clip (RenderOpBuilder *self)
|
|
{
|
|
return self->clip_stack != NULL &&
|
|
self->clip_stack->len > 1;
|
|
}
|
|
|
|
static void
|
|
ops_set_modelview_internal (RenderOpBuilder *builder,
|
|
GskTransform *transform)
|
|
{
|
|
ProgramState *current_program_state = get_current_program_state (builder);
|
|
OpMatrix *op;
|
|
|
|
#if 0
|
|
XXX This is not possible if we want pop() to work.
|
|
if (builder->current_program &&
|
|
gsk_transform_equal (builder->current_program_state->modelview, transform))
|
|
return;
|
|
#endif
|
|
|
|
if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_MODELVIEW)))
|
|
op = op_buffer_add (&builder->render_ops, OP_CHANGE_MODELVIEW);
|
|
|
|
gsk_transform_to_matrix (transform, &op->matrix);
|
|
|
|
if (builder->current_program != NULL)
|
|
{
|
|
gsk_transform_unref (current_program_state->modelview);
|
|
current_program_state->modelview = gsk_transform_ref (transform);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ops_set_modelview:
|
|
* @builder
|
|
* @transform: (transfer full): The new modelview transform
|
|
*
|
|
* This sets the modelview to the given one without looking at the
|
|
* one that's currently set */
|
|
void
|
|
ops_set_modelview (RenderOpBuilder *builder,
|
|
GskTransform *transform)
|
|
{
|
|
MatrixStackEntry *entry;
|
|
|
|
if (G_UNLIKELY (builder->mv_stack == NULL))
|
|
builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry));
|
|
|
|
g_assert (builder->mv_stack != NULL);
|
|
|
|
g_array_set_size (builder->mv_stack, builder->mv_stack->len + 1);
|
|
entry = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
|
|
|
|
entry->transform = transform;
|
|
|
|
entry->metadata.dx_before = builder->dx;
|
|
entry->metadata.dy_before = builder->dy;
|
|
extract_matrix_metadata (entry->transform, &entry->metadata);
|
|
|
|
builder->dx = 0;
|
|
builder->dy = 0;
|
|
builder->current_modelview = entry->transform;
|
|
builder->scale_x = entry->metadata.scale_x;
|
|
builder->scale_y = entry->metadata.scale_y;
|
|
ops_set_modelview_internal (builder, entry->transform);
|
|
}
|
|
|
|
/* This sets the given modelview to the one we get when multiplying
|
|
* the given modelview with the current one. */
|
|
void
|
|
ops_push_modelview (RenderOpBuilder *builder,
|
|
GskTransform *transform)
|
|
{
|
|
MatrixStackEntry *entry;
|
|
|
|
if (G_UNLIKELY (builder->mv_stack == NULL))
|
|
builder->mv_stack = g_array_new (FALSE, TRUE, sizeof (MatrixStackEntry));
|
|
|
|
g_assert (builder->mv_stack != NULL);
|
|
|
|
g_array_set_size (builder->mv_stack, builder->mv_stack->len + 1);
|
|
entry = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
|
|
|
|
if (G_LIKELY (builder->mv_stack->len >= 2))
|
|
{
|
|
const MatrixStackEntry *cur;
|
|
GskTransform *t = NULL;
|
|
|
|
cur = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 2);
|
|
/* Multiply given matrix with current modelview */
|
|
|
|
t = gsk_transform_translate (gsk_transform_ref (cur->transform),
|
|
&(graphene_point_t) { builder->dx, builder->dy});
|
|
t = gsk_transform_transform (t, transform);
|
|
entry->transform = t;
|
|
}
|
|
else
|
|
{
|
|
entry->transform = gsk_transform_ref (transform);
|
|
}
|
|
|
|
entry->metadata.dx_before = builder->dx;
|
|
entry->metadata.dy_before = builder->dy;
|
|
extract_matrix_metadata (entry->transform, &entry->metadata);
|
|
|
|
builder->dx = 0;
|
|
builder->dy = 0;
|
|
builder->scale_x = entry->metadata.scale_x;
|
|
builder->scale_y = entry->metadata.scale_y;
|
|
builder->current_modelview = entry->transform;
|
|
ops_set_modelview_internal (builder, entry->transform);
|
|
}
|
|
|
|
void
|
|
ops_pop_modelview (RenderOpBuilder *builder)
|
|
{
|
|
const MatrixStackEntry *head;
|
|
|
|
g_assert (builder->mv_stack);
|
|
g_assert (builder->mv_stack->len >= 1);
|
|
|
|
head = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
|
|
builder->dx = head->metadata.dx_before;
|
|
builder->dy = head->metadata.dy_before;
|
|
gsk_transform_unref (head->transform);
|
|
|
|
builder->mv_stack->len --;
|
|
head = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
|
|
|
|
if (builder->mv_stack->len >= 1)
|
|
{
|
|
builder->scale_x = head->metadata.scale_x;
|
|
builder->scale_y = head->metadata.scale_y;
|
|
builder->current_modelview = head->transform;
|
|
ops_set_modelview_internal (builder, head->transform);
|
|
}
|
|
else
|
|
{
|
|
builder->current_modelview = NULL;
|
|
}
|
|
}
|
|
|
|
graphene_matrix_t
|
|
ops_set_projection (RenderOpBuilder *builder,
|
|
const graphene_matrix_t *projection)
|
|
{
|
|
ProgramState *current_program_state = get_current_program_state (builder);
|
|
graphene_matrix_t prev_mv;
|
|
OpMatrix *op;
|
|
|
|
if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_PROJECTION)))
|
|
op = op_buffer_add (&builder->render_ops, OP_CHANGE_PROJECTION);
|
|
|
|
op->matrix = *projection;
|
|
|
|
if (builder->current_program != NULL)
|
|
current_program_state->projection = *projection;
|
|
|
|
prev_mv = builder->current_projection;
|
|
builder->current_projection = *projection;
|
|
|
|
return prev_mv;
|
|
}
|
|
|
|
graphene_rect_t
|
|
ops_set_viewport (RenderOpBuilder *builder,
|
|
const graphene_rect_t *viewport)
|
|
{
|
|
ProgramState *current_program_state = get_current_program_state (builder);
|
|
OpViewport *op;
|
|
graphene_rect_t prev_viewport;
|
|
|
|
if (current_program_state != NULL &&
|
|
rect_equal (¤t_program_state->viewport, viewport))
|
|
return current_program_state->viewport;
|
|
|
|
op = ops_begin (builder, OP_CHANGE_VIEWPORT);
|
|
op->viewport = *viewport;
|
|
|
|
if (builder->current_program != NULL)
|
|
current_program_state->viewport = *viewport;
|
|
|
|
prev_viewport = builder->current_viewport;
|
|
builder->current_viewport = *viewport;
|
|
|
|
return prev_viewport;
|
|
}
|
|
|
|
void
|
|
ops_set_texture (RenderOpBuilder *builder,
|
|
int texture_id)
|
|
{
|
|
OpTexture *op;
|
|
|
|
if (builder->current_texture == texture_id)
|
|
return;
|
|
|
|
op = ops_begin (builder, OP_CHANGE_SOURCE_TEXTURE);
|
|
op->texture_id = texture_id;
|
|
builder->current_texture = texture_id;
|
|
}
|
|
|
|
int
|
|
ops_set_render_target (RenderOpBuilder *builder,
|
|
int render_target_id)
|
|
{
|
|
OpRenderTarget *op;
|
|
int prev_render_target;
|
|
|
|
if (builder->current_render_target == render_target_id)
|
|
return render_target_id;
|
|
|
|
prev_render_target = builder->current_render_target;
|
|
|
|
if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_RENDER_TARGET)))
|
|
op = op_buffer_add (&builder->render_ops, OP_CHANGE_RENDER_TARGET);
|
|
|
|
op->render_target_id = render_target_id;
|
|
|
|
builder->current_render_target = render_target_id;
|
|
|
|
return prev_render_target;
|
|
}
|
|
|
|
float
|
|
ops_set_opacity (RenderOpBuilder *builder,
|
|
float opacity)
|
|
{
|
|
ProgramState *current_program_state = get_current_program_state (builder);
|
|
OpOpacity *op;
|
|
float prev_opacity;
|
|
|
|
if (builder->current_opacity == opacity)
|
|
return opacity;
|
|
|
|
if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_OPACITY)))
|
|
op = op_buffer_add (&builder->render_ops, OP_CHANGE_OPACITY);
|
|
|
|
op->opacity = opacity;
|
|
|
|
prev_opacity = builder->current_opacity;
|
|
builder->current_opacity = opacity;
|
|
|
|
if (builder->current_program != NULL)
|
|
current_program_state->opacity = opacity;
|
|
|
|
return prev_opacity;
|
|
}
|
|
|
|
void
|
|
ops_set_color (RenderOpBuilder *builder,
|
|
const GdkRGBA *color)
|
|
{
|
|
ProgramState *current_program_state = get_current_program_state (builder);
|
|
OpColor *op;
|
|
|
|
if (gdk_rgba_equal (color, ¤t_program_state->color))
|
|
return;
|
|
|
|
current_program_state->color = *color;
|
|
|
|
op = ops_begin (builder, OP_CHANGE_COLOR);
|
|
op->rgba = color;
|
|
}
|
|
|
|
void
|
|
ops_set_color_matrix (RenderOpBuilder *builder,
|
|
const graphene_matrix_t *matrix,
|
|
const graphene_vec4_t *offset)
|
|
{
|
|
ProgramState *current_program_state = get_current_program_state (builder);
|
|
OpColorMatrix *op;
|
|
|
|
if (memcmp (matrix,
|
|
¤t_program_state->color_matrix.matrix,
|
|
sizeof (graphene_matrix_t)) == 0 &&
|
|
memcmp (offset,
|
|
¤t_program_state->color_matrix.offset,
|
|
sizeof (graphene_vec4_t)) == 0)
|
|
return;
|
|
|
|
current_program_state->color_matrix.matrix = *matrix;
|
|
current_program_state->color_matrix.offset = *offset;
|
|
|
|
op = ops_begin (builder, OP_CHANGE_COLOR_MATRIX);
|
|
op->matrix = matrix;
|
|
op->offset = offset;
|
|
}
|
|
|
|
void
|
|
ops_set_border (RenderOpBuilder *builder,
|
|
const GskRoundedRect *outline)
|
|
{
|
|
ProgramState *current_program_state = get_current_program_state (builder);
|
|
OpBorder *op;
|
|
|
|
if (memcmp (¤t_program_state->border.outline,
|
|
outline, sizeof (GskRoundedRect)) == 0)
|
|
return;
|
|
|
|
current_program_state->border.outline = *outline;
|
|
|
|
op = ops_begin (builder, OP_CHANGE_BORDER);
|
|
op->outline = *outline;
|
|
}
|
|
|
|
void
|
|
ops_set_border_width (RenderOpBuilder *builder,
|
|
const float *widths)
|
|
{
|
|
ProgramState *current_program_state = get_current_program_state (builder);
|
|
OpBorder *op;
|
|
|
|
if (memcmp (current_program_state->border.widths,
|
|
widths, sizeof (float) * 4) == 0)
|
|
return;
|
|
|
|
memcpy (¤t_program_state->border.widths,
|
|
widths, sizeof (float) * 4);
|
|
|
|
op = ops_begin (builder, OP_CHANGE_BORDER_WIDTH);
|
|
op->widths[0] = widths[0];
|
|
op->widths[1] = widths[1];
|
|
op->widths[2] = widths[2];
|
|
op->widths[3] = widths[3];
|
|
}
|
|
|
|
void
|
|
ops_set_border_color (RenderOpBuilder *builder,
|
|
const GdkRGBA *color)
|
|
{
|
|
ProgramState *current_program_state = get_current_program_state (builder);
|
|
OpBorder *op;
|
|
|
|
if (gdk_rgba_equal (color, ¤t_program_state->border.color))
|
|
return;
|
|
|
|
op = op_buffer_add (&builder->render_ops, OP_CHANGE_BORDER_COLOR);
|
|
op->color = color;
|
|
|
|
current_program_state->border.color = *color;
|
|
}
|
|
|
|
GskQuadVertex *
|
|
ops_draw (RenderOpBuilder *builder,
|
|
const GskQuadVertex vertex_data[GL_N_VERTICES])
|
|
{
|
|
OpDraw *op;
|
|
|
|
if ((op = op_buffer_peek_tail_checked (&builder->render_ops, OP_DRAW)))
|
|
{
|
|
op->vao_size += GL_N_VERTICES;
|
|
}
|
|
else
|
|
{
|
|
op = op_buffer_add (&builder->render_ops, OP_DRAW);
|
|
op->vao_offset = builder->vertices->len;
|
|
op->vao_size = GL_N_VERTICES;
|
|
}
|
|
|
|
if (vertex_data)
|
|
{
|
|
g_array_append_vals (builder->vertices, vertex_data, GL_N_VERTICES);
|
|
return NULL; /* Better not use this on the caller side */
|
|
}
|
|
|
|
g_array_set_size (builder->vertices, builder->vertices->len + GL_N_VERTICES);
|
|
return &g_array_index (builder->vertices, GskQuadVertex, builder->vertices->len - GL_N_VERTICES);
|
|
}
|
|
|
|
/* The offset is only valid for the current modelview.
|
|
* Setting a new modelview will add the offset to that matrix
|
|
* and reset the internal offset to 0. */
|
|
void
|
|
ops_offset (RenderOpBuilder *builder,
|
|
float x,
|
|
float y)
|
|
{
|
|
builder->dx += x;
|
|
builder->dy += y;
|
|
}
|
|
|
|
gpointer
|
|
ops_begin (RenderOpBuilder *builder,
|
|
OpKind kind)
|
|
{
|
|
return op_buffer_add (&builder->render_ops, kind);
|
|
}
|
|
|
|
void
|
|
ops_reset (RenderOpBuilder *builder)
|
|
{
|
|
op_buffer_clear (&builder->render_ops);
|
|
g_array_set_size (builder->vertices, 0);
|
|
}
|
|
|
|
OpBuffer *
|
|
ops_get_buffer (RenderOpBuilder *builder)
|
|
{
|
|
return &builder->render_ops;
|
|
}
|