mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-30 15:31:34 +00:00
794 lines
22 KiB
C
794 lines
22 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;
|
|
}
|
|
|
|
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->buffer_size = 0;
|
|
builder->dx = 0;
|
|
builder->dy = 0;
|
|
builder->current_modelview = NULL;
|
|
builder->current_clip = NULL;
|
|
builder->current_render_target = 0;
|
|
builder->current_texture = 0;
|
|
builder->current_program = NULL;
|
|
builder->current_program_state = NULL;
|
|
graphene_matrix_init_identity (&builder->current_projection);
|
|
builder->current_viewport = GRAPHENE_RECT_INIT (0, 0, 0, 0);
|
|
}
|
|
|
|
static inline void
|
|
rgba_to_float (const GdkRGBA *c,
|
|
float *f)
|
|
{
|
|
f[0] = c->red;
|
|
f[1] = c->green;
|
|
f[2] = c->blue;
|
|
f[3] = c->alpha;
|
|
}
|
|
|
|
/* Debugging only! */
|
|
void
|
|
ops_dump_framebuffer (RenderOpBuilder *builder,
|
|
const char *filename,
|
|
int width,
|
|
int height)
|
|
{
|
|
RenderOp op;
|
|
|
|
op.op = OP_DUMP_FRAMEBUFFER;
|
|
op.dump.filename = g_strdup (filename);
|
|
op.dump.width = width;
|
|
op.dump.height = height;
|
|
|
|
g_array_append_val (builder->render_ops, op);
|
|
}
|
|
|
|
void
|
|
ops_push_debug_group (RenderOpBuilder *builder,
|
|
const char *text)
|
|
{
|
|
RenderOp op;
|
|
|
|
op.op = OP_PUSH_DEBUG_GROUP;
|
|
strncpy (op.debug_group.text, text, sizeof(op.debug_group.text) - 1);
|
|
op.debug_group.text[sizeof(op.debug_group.text) - 1] = 0; /* Ensure zero terminated */
|
|
|
|
g_array_append_val (builder->render_ops, op);
|
|
}
|
|
|
|
void
|
|
ops_pop_debug_group (RenderOpBuilder *builder)
|
|
{
|
|
RenderOp op;
|
|
|
|
op.op = OP_POP_DEBUG_GROUP;
|
|
g_array_append_val (builder->render_ops, op);
|
|
}
|
|
|
|
float
|
|
ops_get_scale (const RenderOpBuilder *builder)
|
|
{
|
|
const MatrixStackEntry *head;
|
|
|
|
g_assert (builder->mv_stack != NULL);
|
|
g_assert (builder->mv_stack->len >= 1);
|
|
|
|
head = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
|
|
|
|
/* TODO: Use two separate values */
|
|
return MAX (head->metadata.scale_x,
|
|
head->metadata.scale_y);
|
|
}
|
|
|
|
static void
|
|
extract_matrix_metadata (GskTransform *transform,
|
|
OpsMatrixMetadata *md)
|
|
{
|
|
switch (gsk_transform_get_category (transform))
|
|
{
|
|
case GSK_TRANSFORM_CATEGORY_IDENTITY:
|
|
md->scale_x = 1;
|
|
md->scale_y = 1;
|
|
break;
|
|
|
|
case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
|
|
gsk_transform_to_translate (transform, &md->translate_x, &md->translate_y);
|
|
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,
|
|
&md->translate_x, &md->translate_y);
|
|
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. */
|
|
md->translate_x = graphene_matrix_get_value (&m, 3, 0);
|
|
md->translate_y = graphene_matrix_get_value (&m, 3, 1);
|
|
|
|
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)
|
|
{
|
|
const MatrixStackEntry *head;
|
|
|
|
g_assert (builder->mv_stack != NULL);
|
|
g_assert (builder->mv_stack->len >= 1);
|
|
|
|
head = &g_array_index (builder->mv_stack, MatrixStackEntry, builder->mv_stack->len - 1);
|
|
|
|
gsk_transform_transform_bounds (builder->current_modelview, src, dst);
|
|
|
|
dst->origin.x += builder->dx * head->metadata.scale_x;
|
|
dst->origin.y += builder->dy * head->metadata.scale_y;
|
|
}
|
|
|
|
void
|
|
ops_init (RenderOpBuilder *builder)
|
|
{
|
|
int i;
|
|
|
|
memset (builder, 0, sizeof (*builder));
|
|
|
|
builder->current_opacity = 1.0f;
|
|
|
|
for (i = 0; i < GL_N_PROGRAMS; i ++)
|
|
{
|
|
builder->program_state[i].opacity = 1.0f;
|
|
}
|
|
}
|
|
|
|
void
|
|
ops_set_program (RenderOpBuilder *builder,
|
|
const Program *program)
|
|
{
|
|
/* The tricky part about this is that we want to initialize all uniforms of a program
|
|
* to the current value from the builder, but only once. */
|
|
static const GskRoundedRect empty_clip;
|
|
static const graphene_matrix_t empty_matrix;
|
|
static const graphene_rect_t empty_rect;
|
|
RenderOp op;
|
|
ProgramState *program_state;
|
|
|
|
if (builder->current_program == program)
|
|
return;
|
|
|
|
op.op = OP_CHANGE_PROGRAM;
|
|
op.program = program;
|
|
g_array_append_val (builder->render_ops, op);
|
|
builder->current_program = program;
|
|
|
|
program_state = &builder->program_state[program->index];
|
|
|
|
/* If the projection is not yet set for this program, we use the current one. */
|
|
if (memcmp (&empty_matrix, &program_state->projection, sizeof (graphene_matrix_t)) == 0 ||
|
|
memcmp (&builder->current_projection, &program_state->projection, sizeof (graphene_matrix_t)) != 0)
|
|
{
|
|
op.op = OP_CHANGE_PROJECTION;
|
|
op.projection = builder->current_projection;
|
|
g_array_append_val (builder->render_ops, op);
|
|
program_state->projection = builder->current_projection;
|
|
}
|
|
|
|
if (program_state->modelview == NULL ||
|
|
!gsk_transform_equal (builder->current_modelview, program_state->modelview))
|
|
{
|
|
op.op = OP_CHANGE_MODELVIEW;
|
|
gsk_transform_to_matrix (builder->current_modelview, &op.modelview);
|
|
g_array_append_val (builder->render_ops, op);
|
|
program_state->modelview = gsk_transform_ref (builder->current_modelview);
|
|
}
|
|
|
|
if (rect_equal (&empty_rect, &program_state->viewport) ||
|
|
!rect_equal (&builder->current_viewport, &program_state->viewport))
|
|
{
|
|
op.op = OP_CHANGE_VIEWPORT;
|
|
op.viewport = builder->current_viewport;
|
|
g_array_append_val (builder->render_ops, op);
|
|
program_state->viewport = builder->current_viewport;
|
|
}
|
|
|
|
if (memcmp (&empty_clip, &program_state->clip, sizeof (GskRoundedRect)) == 0 ||
|
|
memcmp (&builder->current_clip, &program_state->clip, sizeof (GskRoundedRect)) != 0)
|
|
{
|
|
op.op = OP_CHANGE_CLIP;
|
|
op.clip = *builder->current_clip;
|
|
g_array_append_val (builder->render_ops, op);
|
|
program_state->clip = *builder->current_clip;
|
|
}
|
|
|
|
if (program_state->opacity != builder->current_opacity)
|
|
{
|
|
op.op = OP_CHANGE_OPACITY;
|
|
op.opacity = builder->current_opacity;
|
|
g_array_append_val (builder->render_ops, op);
|
|
program_state->opacity = builder->current_opacity;
|
|
}
|
|
|
|
builder->current_program_state = &builder->program_state[program->index];
|
|
}
|
|
|
|
static void
|
|
ops_set_clip (RenderOpBuilder *builder,
|
|
const GskRoundedRect *clip)
|
|
{
|
|
RenderOp *last_op;
|
|
|
|
if (builder->current_program_state &&
|
|
memcmp (&builder->current_program_state->clip, clip,sizeof (GskRoundedRect)) == 0)
|
|
return;
|
|
|
|
if (builder->render_ops->len > 0)
|
|
{
|
|
last_op = &g_array_index (builder->render_ops, RenderOp, builder->render_ops->len - 1);
|
|
|
|
if (last_op->op == OP_CHANGE_CLIP)
|
|
{
|
|
last_op->clip = *clip;
|
|
}
|
|
else
|
|
{
|
|
RenderOp op;
|
|
|
|
op.op = OP_CHANGE_CLIP;
|
|
op.clip = *clip;
|
|
g_array_append_val (builder->render_ops, op);
|
|
}
|
|
}
|
|
|
|
if (builder->current_program != NULL)
|
|
builder->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)
|
|
{
|
|
RenderOp op;
|
|
graphene_matrix_t matrix;
|
|
|
|
if (builder->current_program &&
|
|
gsk_transform_equal (builder->current_program_state->modelview, transform))
|
|
return;
|
|
|
|
gsk_transform_to_matrix (transform, &matrix);
|
|
|
|
if (builder->render_ops->len > 0)
|
|
{
|
|
RenderOp *last_op = &g_array_index (builder->render_ops, RenderOp, builder->render_ops->len - 1);
|
|
if (last_op->op == OP_CHANGE_MODELVIEW)
|
|
{
|
|
last_op->modelview = matrix;
|
|
}
|
|
else
|
|
{
|
|
op.op = OP_CHANGE_MODELVIEW;
|
|
op.modelview = matrix;
|
|
g_array_append_val (builder->render_ops, op);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
op.op = OP_CHANGE_MODELVIEW;
|
|
op.modelview = matrix;
|
|
g_array_append_val (builder->render_ops, op);
|
|
}
|
|
|
|
if (builder->current_program != NULL)
|
|
builder->current_program_state->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 = gsk_transform_ref (transform);
|
|
|
|
entry->metadata.dx_before = builder->dx;
|
|
entry->metadata.dy_before = builder->dy;
|
|
extract_matrix_metadata (transform, &entry->metadata);
|
|
|
|
builder->dx = 0;
|
|
builder->dy = 0;
|
|
builder->current_modelview = entry->transform;
|
|
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)
|
|
{
|
|
float scale = ops_get_scale (builder);
|
|
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 * scale, builder->dy * scale});
|
|
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->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->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)
|
|
{
|
|
RenderOp op;
|
|
graphene_matrix_t prev_mv;
|
|
|
|
if (builder->render_ops->len > 0)
|
|
{
|
|
RenderOp *last_op = &g_array_index (builder->render_ops, RenderOp, builder->render_ops->len - 1);
|
|
if (last_op->op == OP_CHANGE_PROJECTION)
|
|
{
|
|
last_op->projection = *projection;
|
|
}
|
|
else
|
|
{
|
|
op.op = OP_CHANGE_PROJECTION;
|
|
op.projection = *projection;
|
|
g_array_append_val (builder->render_ops, op);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
op.op = OP_CHANGE_PROJECTION;
|
|
op.projection = *projection;
|
|
g_array_append_val (builder->render_ops, op);
|
|
}
|
|
|
|
if (builder->current_program != NULL)
|
|
builder->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)
|
|
{
|
|
RenderOp op;
|
|
graphene_rect_t prev_viewport;
|
|
|
|
if (builder->current_program_state != NULL &&
|
|
rect_equal (&builder->current_program_state->viewport, viewport))
|
|
return builder->current_program_state->viewport;
|
|
|
|
op.op = OP_CHANGE_VIEWPORT;
|
|
op.viewport = *viewport;
|
|
g_array_append_val (builder->render_ops, op);
|
|
|
|
if (builder->current_program != NULL)
|
|
builder->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)
|
|
{
|
|
RenderOp op;
|
|
|
|
if (builder->current_texture == texture_id)
|
|
return;
|
|
|
|
op.op = OP_CHANGE_SOURCE_TEXTURE;
|
|
op.texture_id = texture_id;
|
|
g_array_append_val (builder->render_ops, op);
|
|
builder->current_texture = texture_id;
|
|
}
|
|
|
|
int
|
|
ops_set_render_target (RenderOpBuilder *builder,
|
|
int render_target_id)
|
|
{
|
|
RenderOp 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 (builder->render_ops->len > 0)
|
|
{
|
|
RenderOp *last_op = &g_array_index (builder->render_ops, RenderOp, builder->render_ops->len - 1);
|
|
if (last_op->op == OP_CHANGE_RENDER_TARGET)
|
|
{
|
|
last_op->render_target_id = render_target_id;
|
|
}
|
|
else
|
|
{
|
|
op.op = OP_CHANGE_RENDER_TARGET;
|
|
op.render_target_id = render_target_id;
|
|
g_array_append_val (builder->render_ops, op);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
op.op = OP_CHANGE_RENDER_TARGET;
|
|
op.render_target_id = render_target_id;
|
|
g_array_append_val (builder->render_ops, op);
|
|
}
|
|
|
|
builder->current_render_target = render_target_id;
|
|
|
|
return prev_render_target;
|
|
}
|
|
|
|
float
|
|
ops_set_opacity (RenderOpBuilder *builder,
|
|
float opacity)
|
|
{
|
|
RenderOp op;
|
|
float prev_opacity;
|
|
RenderOp *last_op;
|
|
|
|
if (builder->current_opacity == opacity)
|
|
return opacity;
|
|
|
|
if (builder->render_ops->len > 0)
|
|
{
|
|
last_op = &g_array_index (builder->render_ops, RenderOp, builder->render_ops->len - 1);
|
|
|
|
if (last_op->op == OP_CHANGE_OPACITY)
|
|
{
|
|
last_op->opacity = opacity;
|
|
}
|
|
else
|
|
{
|
|
op.op = OP_CHANGE_OPACITY;
|
|
op.opacity = opacity;
|
|
g_array_append_val (builder->render_ops, op);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
op.op = OP_CHANGE_OPACITY;
|
|
op.opacity = opacity;
|
|
g_array_append_val (builder->render_ops, op);
|
|
}
|
|
|
|
prev_opacity = builder->current_opacity;
|
|
builder->current_opacity = opacity;
|
|
|
|
if (builder->current_program != NULL)
|
|
builder->current_program_state->opacity = opacity;
|
|
|
|
return prev_opacity;
|
|
}
|
|
|
|
void
|
|
ops_set_color (RenderOpBuilder *builder,
|
|
const GdkRGBA *color)
|
|
{
|
|
RenderOp op;
|
|
|
|
if (gdk_rgba_equal (color, &builder->current_program_state->color))
|
|
return;
|
|
|
|
builder->current_program_state->color = *color;
|
|
|
|
op.op = OP_CHANGE_COLOR;
|
|
op.color = *color;
|
|
g_array_append_val (builder->render_ops, op);
|
|
}
|
|
|
|
void
|
|
ops_set_color_matrix (RenderOpBuilder *builder,
|
|
const graphene_matrix_t *matrix,
|
|
const graphene_vec4_t *offset)
|
|
{
|
|
RenderOp op;
|
|
|
|
if (memcmp (matrix,
|
|
&builder->current_program_state->color_matrix.matrix,
|
|
sizeof (graphene_matrix_t)) == 0 &&
|
|
memcmp (offset,
|
|
&builder->current_program_state->color_matrix.offset,
|
|
sizeof (graphene_vec4_t)) == 0)
|
|
return;
|
|
|
|
builder->current_program_state->color_matrix.matrix = *matrix;
|
|
builder->current_program_state->color_matrix.offset = *offset;
|
|
|
|
op.op = OP_CHANGE_COLOR_MATRIX;
|
|
op.color_matrix.matrix = *matrix;
|
|
op.color_matrix.offset = *offset;
|
|
g_array_append_val (builder->render_ops, op);
|
|
}
|
|
|
|
void
|
|
ops_set_border (RenderOpBuilder *builder,
|
|
const GskRoundedRect *outline)
|
|
{
|
|
RenderOp op;
|
|
|
|
if (memcmp (&builder->current_program_state->border.outline,
|
|
outline, sizeof (GskRoundedRect)) == 0)
|
|
return;
|
|
|
|
builder->current_program_state->border.outline = *outline;
|
|
|
|
op.op = OP_CHANGE_BORDER;
|
|
op.border.outline = *outline;
|
|
g_array_append_val (builder->render_ops, op);
|
|
}
|
|
|
|
void
|
|
ops_set_border_width (RenderOpBuilder *builder,
|
|
const float *widths)
|
|
{
|
|
RenderOp op;
|
|
|
|
if (memcmp (builder->current_program_state->border.widths,
|
|
widths, sizeof (float) * 4) == 0)
|
|
return;
|
|
|
|
memcpy (&builder->current_program_state->border.widths,
|
|
widths, sizeof (float) * 4);
|
|
|
|
op.op = OP_CHANGE_BORDER_WIDTH;
|
|
op.border.widths[0] = widths[0];
|
|
op.border.widths[1] = widths[1];
|
|
op.border.widths[2] = widths[2];
|
|
op.border.widths[3] = widths[3];
|
|
|
|
g_array_append_val (builder->render_ops, op);
|
|
}
|
|
|
|
void
|
|
ops_set_border_color (RenderOpBuilder *builder,
|
|
const GdkRGBA *color)
|
|
{
|
|
RenderOp op;
|
|
op.op = OP_CHANGE_BORDER_COLOR;
|
|
rgba_to_float (color, op.border.color);
|
|
|
|
if (memcmp (&op.border.color, &builder->current_program_state->border.color,
|
|
sizeof (float) * 4) == 0)
|
|
return;
|
|
|
|
rgba_to_float (color, builder->current_program_state->border.color);
|
|
|
|
g_array_append_val (builder->render_ops, op);
|
|
}
|
|
|
|
void
|
|
ops_draw (RenderOpBuilder *builder,
|
|
const GskQuadVertex vertex_data[GL_N_VERTICES])
|
|
{
|
|
RenderOp *last_op;
|
|
|
|
last_op = &g_array_index (builder->render_ops, RenderOp, builder->render_ops->len - 1);
|
|
/* If the previous op was a DRAW as well, we didn't change anything between the two calls,
|
|
* so these are just 2 subsequent draw calls. Same VAO, same program etc.
|
|
* And the offsets into the vao are in order as well, so make it one draw call. */
|
|
if (last_op->op == OP_DRAW)
|
|
{
|
|
/* We allow ourselves a little trick here. We still have to add a CHANGE_VAO op for
|
|
* this draw call so we can add our vertex data there, but we want it to be placed before
|
|
* the last draw call, so we reorder those. */
|
|
RenderOp new_draw;
|
|
new_draw.op = OP_DRAW;
|
|
new_draw.draw.vao_offset = last_op->draw.vao_offset;
|
|
new_draw.draw.vao_size = last_op->draw.vao_size + GL_N_VERTICES;
|
|
|
|
last_op->op = OP_CHANGE_VAO;
|
|
memcpy (&last_op->vertex_data, vertex_data, sizeof(GskQuadVertex) * GL_N_VERTICES);
|
|
|
|
/* Now add the DRAW */
|
|
g_array_append_val (builder->render_ops, new_draw);
|
|
}
|
|
else
|
|
{
|
|
const gsize n_ops = builder->render_ops->len;
|
|
RenderOp *op;
|
|
gsize offset = builder->buffer_size / sizeof (GskQuadVertex);
|
|
|
|
/* We will add two render ops here. */
|
|
g_array_set_size (builder->render_ops, n_ops + 2);
|
|
|
|
op = &g_array_index (builder->render_ops, RenderOp, n_ops);
|
|
op->op = OP_CHANGE_VAO;
|
|
memcpy (&op->vertex_data, vertex_data, sizeof(GskQuadVertex) * GL_N_VERTICES);
|
|
|
|
op = &g_array_index (builder->render_ops, RenderOp, n_ops + 1);
|
|
op->op = OP_DRAW;
|
|
op->draw.vao_offset = offset;
|
|
op->draw.vao_size = GL_N_VERTICES;
|
|
}
|
|
|
|
/* We added new vertex data in both cases so increase the buffer size */
|
|
builder->buffer_size += sizeof (GskQuadVertex) * 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;
|
|
}
|
|
|
|
void
|
|
ops_add (RenderOpBuilder *builder,
|
|
const RenderOp *op)
|
|
{
|
|
g_array_append_val (builder->render_ops, *op);
|
|
}
|