mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-14 20:51:07 +00:00
963 lines
26 KiB
C
963 lines
26 KiB
C
#include "gskglrenderopsprivate.h"
|
|
#include "gsktransform.h"
|
|
|
|
typedef struct
|
|
{
|
|
GskRoundedRect rect;
|
|
bool is_rectilinear;
|
|
} ClipStackEntry;
|
|
|
|
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->clip_is_rectilinear = TRUE;
|
|
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)
|
|
{
|
|
ClipStackEntry entry;
|
|
|
|
if (G_UNLIKELY (self->clip_stack == NULL))
|
|
self->clip_stack = g_array_new (FALSE, TRUE, sizeof (ClipStackEntry));
|
|
|
|
g_assert (self->clip_stack != NULL);
|
|
|
|
entry.rect = *clip;
|
|
entry.is_rectilinear = gsk_rounded_rect_is_rectilinear (clip);
|
|
g_array_append_val (self->clip_stack, entry);
|
|
self->current_clip = &g_array_index (self->clip_stack, ClipStackEntry, self->clip_stack->len - 1).rect;
|
|
self->clip_is_rectilinear = entry.is_rectilinear;
|
|
ops_set_clip (self, clip);
|
|
}
|
|
|
|
void
|
|
ops_pop_clip (RenderOpBuilder *self)
|
|
{
|
|
const ClipStackEntry *head;
|
|
|
|
g_assert (self->clip_stack);
|
|
g_assert (self->clip_stack->len >= 1);
|
|
|
|
self->clip_stack->len --;
|
|
head = &g_array_index (self->clip_stack, ClipStackEntry, self->clip_stack->len - 1);
|
|
|
|
if (self->clip_stack->len >= 1)
|
|
{
|
|
self->current_clip = &head->rect;
|
|
self->clip_is_rectilinear = head->is_rectilinear;
|
|
ops_set_clip (self, &head->rect);
|
|
}
|
|
else
|
|
{
|
|
self->current_clip = NULL;
|
|
self->clip_is_rectilinear = TRUE;
|
|
}
|
|
}
|
|
|
|
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 (current_program_state != 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;
|
|
bool offset_equal;
|
|
|
|
offset_equal = memcmp (offset,
|
|
¤t_program_state->color_matrix.offset,
|
|
sizeof (graphene_vec4_t)) == 0;
|
|
|
|
if (memcmp (matrix,
|
|
¤t_program_state->color_matrix.matrix,
|
|
sizeof (graphene_matrix_t)) == 0 &&
|
|
offset_equal)
|
|
return;
|
|
|
|
current_program_state->color_matrix.matrix = *matrix;
|
|
|
|
op = ops_begin (builder, OP_CHANGE_COLOR_MATRIX);
|
|
op->matrix = matrix;
|
|
|
|
if (!offset_equal)
|
|
{
|
|
op->offset.value = offset;
|
|
op->offset.send = TRUE;
|
|
|
|
current_program_state->color_matrix.offset = *offset;
|
|
}
|
|
else
|
|
op->offset.send = FALSE;
|
|
}
|
|
|
|
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;
|
|
|
|
g_assert (current_program_state);
|
|
|
|
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;
|
|
}
|
|
|
|
void
|
|
ops_set_inset_shadow (RenderOpBuilder *self,
|
|
const GskRoundedRect outline,
|
|
float spread,
|
|
const GdkRGBA *color,
|
|
float dx,
|
|
float dy)
|
|
{
|
|
ProgramState *current_program_state = get_current_program_state (self);
|
|
OpShadow *op;
|
|
|
|
g_assert (current_program_state);
|
|
|
|
op = ops_begin (self, OP_CHANGE_INSET_SHADOW);
|
|
|
|
if (!rounded_rect_equal (&outline, ¤t_program_state->inset_shadow.outline))
|
|
{
|
|
op->outline.value = outline;
|
|
op->outline.send = TRUE;
|
|
op->outline.send_corners = !rounded_rect_corners_equal (¤t_program_state->inset_shadow.outline,
|
|
&outline);
|
|
current_program_state->inset_shadow.outline = outline;
|
|
}
|
|
else
|
|
op->outline.send = FALSE;
|
|
|
|
if (spread != current_program_state->inset_shadow.spread)
|
|
{
|
|
op->spread.value = spread;
|
|
op->spread.send = TRUE;
|
|
|
|
current_program_state->inset_shadow.spread = spread;
|
|
}
|
|
else
|
|
op->spread.send = FALSE;
|
|
|
|
if (!gdk_rgba_equal (color, ¤t_program_state->inset_shadow.color))
|
|
{
|
|
op->color.value = color;
|
|
op->color.send = TRUE;
|
|
|
|
current_program_state->inset_shadow.color = *color;
|
|
}
|
|
else
|
|
op->color.send = FALSE;
|
|
|
|
if (dx != current_program_state->inset_shadow.dx ||
|
|
dy != current_program_state->inset_shadow.dy)
|
|
{
|
|
op->offset.value[0] = dx;
|
|
op->offset.value[1] = dy;
|
|
op->offset.send = TRUE;
|
|
|
|
current_program_state->inset_shadow.dx = dx;
|
|
current_program_state->inset_shadow.dy = dy;
|
|
}
|
|
else
|
|
op->offset.send = FALSE;
|
|
|
|
if (!op->outline.send &&
|
|
!op->spread.send &&
|
|
!op->offset.send &&
|
|
!op->color.send)
|
|
{
|
|
op_buffer_pop_tail (&self->render_ops);
|
|
}
|
|
}
|
|
void
|
|
ops_set_unblurred_outset_shadow (RenderOpBuilder *self,
|
|
const GskRoundedRect outline,
|
|
float spread,
|
|
const GdkRGBA *color,
|
|
float dx,
|
|
float dy)
|
|
{
|
|
ProgramState *current_program_state = get_current_program_state (self);
|
|
OpShadow *op;
|
|
|
|
g_assert (current_program_state);
|
|
|
|
op = ops_begin (self, OP_CHANGE_UNBLURRED_OUTSET_SHADOW);
|
|
|
|
if (!rounded_rect_equal (&outline, ¤t_program_state->unblurred_outset_shadow.outline))
|
|
{
|
|
op->outline.value = outline;
|
|
op->outline.send = TRUE;
|
|
op->outline.send_corners = !rounded_rect_corners_equal (¤t_program_state->unblurred_outset_shadow.outline,
|
|
&outline);
|
|
current_program_state->unblurred_outset_shadow.outline = outline;
|
|
}
|
|
else
|
|
op->outline.send = FALSE;
|
|
|
|
if (spread != current_program_state->unblurred_outset_shadow.spread)
|
|
{
|
|
op->spread.value = spread;
|
|
op->spread.send = TRUE;
|
|
|
|
current_program_state->unblurred_outset_shadow.spread = spread;
|
|
}
|
|
else
|
|
op->spread.send = FALSE;
|
|
|
|
if (!gdk_rgba_equal (color, ¤t_program_state->unblurred_outset_shadow.color))
|
|
{
|
|
op->color.value = color;
|
|
op->color.send = TRUE;
|
|
|
|
current_program_state->unblurred_outset_shadow.color = *color;
|
|
}
|
|
else
|
|
op->color.send = FALSE;
|
|
|
|
if (dx != current_program_state->unblurred_outset_shadow.dx ||
|
|
dy != current_program_state->unblurred_outset_shadow.dy)
|
|
{
|
|
op->offset.value[0] = dx;
|
|
op->offset.value[1] = dy;
|
|
op->offset.send = TRUE;
|
|
|
|
current_program_state->unblurred_outset_shadow.dx = dx;
|
|
current_program_state->unblurred_outset_shadow.dy = dy;
|
|
}
|
|
else
|
|
op->offset.send = FALSE;
|
|
}
|
|
|
|
void
|
|
ops_set_linear_gradient (RenderOpBuilder *self,
|
|
guint n_color_stops,
|
|
const GskColorStop *color_stops,
|
|
float start_x,
|
|
float start_y,
|
|
float end_x,
|
|
float end_y)
|
|
{
|
|
ProgramState *current_program_state = get_current_program_state (self);
|
|
OpLinearGradient *op;
|
|
const guint real_n_color_stops = MIN (MAX_GRADIENT_STOPS, n_color_stops);
|
|
|
|
g_assert (current_program_state);
|
|
|
|
op = ops_begin (self, OP_CHANGE_LINEAR_GRADIENT);
|
|
|
|
/* We always save the n_color_stops value in the op so the renderer can use it in
|
|
* cases where we send the color stops, but not n_color_stops */
|
|
op->n_color_stops.value = real_n_color_stops;
|
|
if (current_program_state->linear_gradient.n_color_stops != real_n_color_stops)
|
|
{
|
|
op->n_color_stops.send = TRUE;
|
|
current_program_state->linear_gradient.n_color_stops = real_n_color_stops;
|
|
}
|
|
else
|
|
op->n_color_stops.send = FALSE;
|
|
|
|
op->color_stops.send = FALSE;
|
|
if (!op->n_color_stops.send)
|
|
{
|
|
g_assert (current_program_state->linear_gradient.n_color_stops == real_n_color_stops);
|
|
|
|
for (guint i = 0; i < real_n_color_stops; i ++)
|
|
{
|
|
const GskColorStop *s1 = &color_stops[i];
|
|
const GskColorStop *s2 = ¤t_program_state->linear_gradient.color_stops[i];
|
|
|
|
if (s1->offset != s2->offset ||
|
|
!gdk_rgba_equal (&s1->color, &s2->color))
|
|
{
|
|
op->color_stops.send = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
op->color_stops.send = TRUE;
|
|
|
|
if (op->color_stops.send)
|
|
{
|
|
op->color_stops.value = color_stops;
|
|
memcpy (¤t_program_state->linear_gradient.color_stops,
|
|
color_stops,
|
|
sizeof (GskColorStop) * real_n_color_stops);
|
|
}
|
|
|
|
op->start_point[0] = start_x;
|
|
op->start_point[1] = start_y;
|
|
op->end_point[0] = end_x;
|
|
op->end_point[1] = end_y;
|
|
}
|