Merge branch 'wip/chergert/opbuffer' into 'master'

Add OpBuffer helper for building op buffer

See merge request GNOME/gtk!1131
This commit is contained in:
Matthias Clasen 2019-10-17 11:37:07 +00:00
commit 6c92b824f3
15 changed files with 1118 additions and 716 deletions

View File

@ -269,13 +269,7 @@ gsk_gl_glyph_cache_lookup_or_add (GskGLGlyphCache *cache,
if (value)
{
if (value->atlas && !value->used)
{
gsk_gl_texture_atlas_mark_used (value->atlas, value->draw_width, value->draw_height);
value->used = TRUE;
}
value->accessed = TRUE;
gsk_gl_glyph_cache_entry_validate (cache, value);
*cached_glyph_out = value;
return;
}
@ -334,6 +328,8 @@ gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self,
{
guint dropped = 0;
self->atlas_timestamp++;
g_hash_table_iter_init (&iter, self->hash_table);
while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
{
@ -366,6 +362,12 @@ gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self,
{
gsk_gl_driver_destroy_texture (driver, value->texture_id);
g_hash_table_iter_remove (&iter);
/* Sadly, if we drop an atlas-less cached glyph, we
* have to treat it like a dropped atlas and purge
* text node render data.
*/
self->atlas_timestamp++;
}
}
else
@ -375,3 +377,15 @@ gsk_gl_glyph_cache_begin_frame (GskGLGlyphCache *self,
GSK_NOTE(GLYPH_CACHE, g_message ("%d glyphs cached", g_hash_table_size (self->hash_table)));
}
}
void
gsk_gl_glyph_cache_entry_validate (GskGLGlyphCache *cache,
GskGLCachedGlyph *value)
{
value->accessed = TRUE;
if (value->atlas && !value->used)
{
gsk_gl_texture_atlas_mark_used (value->atlas, value->draw_width, value->draw_height);
value->used = TRUE;
}
}

View File

@ -16,6 +16,7 @@ typedef struct
GskGLTextureAtlases *atlases;
int timestamp;
int atlas_timestamp; /* incremented whenever an atlas is dropped */
} GskGLGlyphCache;
typedef struct
@ -79,5 +80,7 @@ void gsk_gl_glyph_cache_lookup_or_add (GskGLGlyphCache
GlyphCacheKey *lookup,
GskGLDriver *driver,
const GskGLCachedGlyph **cached_glyph_out);
void gsk_gl_glyph_cache_entry_validate (GskGLGlyphCache *cache,
GskGLCachedGlyph *entry);
#endif

View File

@ -7,19 +7,13 @@
#define MAX_FRAME_AGE 60
typedef struct
{
graphene_rect_t texture_rect;
GskGLTextureAtlas *atlas;
GdkTexture *source_texture;
guint accessed : 1;
guint used : 1;
} IconData;
static void
icon_data_free (gpointer p)
{
g_object_unref (((IconData *)p)->source_texture);
IconData *icon_data = p;
gdk_texture_clear_render_data (icon_data->source_texture);
g_object_unref (icon_data->source_texture);
g_free (p);
}
@ -100,12 +94,12 @@ gsk_gl_icon_cache_begin_frame (GskGLIconCache *self,
{
if (icon_data->used)
{
const int w = icon_data->texture_rect.size.width * icon_data->atlas->width;
const int h = icon_data->texture_rect.size.height * icon_data->atlas->height;
gsk_gl_texture_atlas_mark_unused (icon_data->atlas, w + 2, h + 2);
const int width = icon_data->source_texture->width;
const int height = icon_data->source_texture->height;
gsk_gl_texture_atlas_mark_unused (icon_data->atlas, width + 2, height + 2);
icon_data->used = FALSE;
}
}
}
icon_data->accessed = FALSE;
}
@ -117,32 +111,32 @@ gsk_gl_icon_cache_begin_frame (GskGLIconCache *self,
void
gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
GdkTexture *texture,
int *out_texture_id,
graphene_rect_t *out_texture_rect)
const IconData **out_icon_data)
{
IconData *icon_data = g_hash_table_lookup (self->icons, texture);
IconData *icon_data;
icon_data = gdk_texture_get_render_data (texture, self);
if (!icon_data)
icon_data = g_hash_table_lookup (self->icons, texture);
if (icon_data)
{
if (!icon_data->used)
{
const int w = icon_data->texture_rect.size.width * icon_data->atlas->width;
const int h = icon_data->texture_rect.size.height * icon_data->atlas->height;
gsk_gl_texture_atlas_mark_used (icon_data->atlas, w + 2, h + 2);
gsk_gl_texture_atlas_mark_used (icon_data->atlas, texture->width + 2, texture->height + 2);
icon_data->used = TRUE;
}
icon_data->accessed = TRUE;
*out_texture_id = icon_data->atlas->texture_id;
*out_texture_rect = icon_data->texture_rect;
*out_icon_data = icon_data;
return;
}
/* texture not on any atlas yet. Find a suitable one. */
{
const int width = gdk_texture_get_width (texture);
const int height = gdk_texture_get_height (texture);
const int width = texture->width;
const int height = texture->height;
GskGLTextureAtlas *atlas = NULL;
int packed_x = 0;
int packed_y = 0;
@ -155,12 +149,12 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
icon_data->atlas = atlas;
icon_data->accessed = TRUE;
icon_data->used = TRUE;
icon_data->texture_id = atlas->texture_id;
icon_data->source_texture = g_object_ref (texture);
graphene_rect_init (&icon_data->texture_rect,
(float)(packed_x + 1) / atlas->width,
(float)(packed_y + 1) / atlas->height,
(float)width / atlas->width,
(float)height / atlas->height);
icon_data->x = (float)(packed_x + 1) / atlas->width;
icon_data->y = (float)(packed_y + 1) / atlas->width;
icon_data->x2 = icon_data->x + (float)width / atlas->width;
icon_data->y2 = icon_data->y + (float)height / atlas->height;
g_hash_table_insert (self->icons, texture, icon_data);
@ -240,8 +234,9 @@ gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
*out_texture_id = atlas->texture_id;
*out_texture_rect = icon_data->texture_rect;
gdk_texture_set_render_data (texture, self, icon_data, NULL);
*out_icon_data = icon_data;
cairo_surface_destroy (surface);

View File

@ -21,6 +21,16 @@ typedef struct
int timestamp;
} GskGLIconCache;
typedef struct
{
float x, y, x2, y2;
GskGLTextureAtlas *atlas;
guint used : 1;
guint accessed : 1;
int texture_id;
GdkTexture *source_texture;
} IconData;
GskGLIconCache * gsk_gl_icon_cache_new (GdkDisplay *display,
GskGLTextureAtlases *atlases);
GskGLIconCache * gsk_gl_icon_cache_ref (GskGLIconCache *self);
@ -29,7 +39,6 @@ void gsk_gl_icon_cache_begin_frame (GskGLIconCache *self,
GPtrArray *removed_atlases);
void gsk_gl_icon_cache_lookup_or_add (GskGLIconCache *self,
GdkTexture *texture,
int *out_texture_id,
graphene_rect_t *out_texture_rect);
const IconData **out_icon_data);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,6 @@ ops_finish (RenderOpBuilder *builder)
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;
@ -57,23 +56,23 @@ ops_dump_framebuffer (RenderOpBuilder *builder,
int width,
int height)
{
RenderOp *op;
OpDumpFrameBuffer *op;
op = ops_begin (builder, OP_DUMP_FRAMEBUFFER);
op->dump.filename = g_strdup (filename);
op->dump.width = width;
op->dump.height = height;
op->filename = g_strdup (filename);
op->width = width;
op->height = height;
}
void
ops_push_debug_group (RenderOpBuilder *builder,
const char *text)
{
RenderOp *op;
OpDebugGroup *op;
op = ops_begin (builder, 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 */
strncpy (op->text, text, sizeof(op->text) - 1);
op->text[sizeof(op->text) - 1] = 0; /* Ensure zero terminated */
}
void
@ -182,6 +181,9 @@ ops_init (RenderOpBuilder *builder)
builder->current_opacity = 1.0f;
op_buffer_init (&builder->render_ops);
builder->vertices = g_array_new (FALSE, TRUE, sizeof (GskQuadVertex));
for (i = 0; i < GL_N_PROGRAMS; i ++)
{
builder->program_state[i].opacity = 1.0f;
@ -197,6 +199,9 @@ ops_free (RenderOpBuilder *builder)
{
gsk_transform_unref (builder->program_state[i].modelview);
}
g_array_unref (builder->vertices);
op_buffer_destroy (&builder->render_ops);
}
void
@ -208,7 +213,7 @@ ops_set_program (RenderOpBuilder *builder,
static const GskRoundedRect empty_clip;
static const graphene_matrix_t empty_matrix;
static const graphene_rect_t empty_rect;
RenderOp *op;
OpProgram *op;
ProgramState *program_state;
if (builder->current_program == program)
@ -225,16 +230,20 @@ ops_set_program (RenderOpBuilder *builder,
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 = ops_begin (builder, OP_CHANGE_PROJECTION);
op->projection = builder->current_projection;
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))
{
op = ops_begin (builder, OP_CHANGE_MODELVIEW);
gsk_transform_to_matrix (builder->current_modelview, &op->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);
}
@ -242,23 +251,29 @@ ops_set_program (RenderOpBuilder *builder,
if (rect_equal (&empty_rect, &program_state->viewport) ||
!rect_equal (&builder->current_viewport, &program_state->viewport))
{
op = ops_begin (builder, OP_CHANGE_VIEWPORT);
op->viewport = builder->current_viewport;
OpViewport *opv;
opv = ops_begin (builder, OP_CHANGE_VIEWPORT);
opv->viewport = builder->current_viewport;
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 = ops_begin (builder, OP_CHANGE_CLIP);
op->clip = *builder->current_clip;
OpClip *opc;
opc = ops_begin (builder, OP_CHANGE_CLIP);
opc->clip = *builder->current_clip;
program_state->clip = *builder->current_clip;
}
if (program_state->opacity != builder->current_opacity)
{
op = ops_begin (builder, OP_CHANGE_OPACITY);
op->opacity = builder->current_opacity;
OpOpacity *opo;
opo = ops_begin (builder, OP_CHANGE_OPACITY);
opo->opacity = builder->current_opacity;
program_state->opacity = builder->current_opacity;
}
}
@ -267,29 +282,17 @@ static void
ops_set_clip (RenderOpBuilder *builder,
const GskRoundedRect *clip)
{
RenderOp *last_op;
ProgramState *current_program_state = get_current_program_state (builder);
OpClip *op;
if (current_program_state &&
memcmp (&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 (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_CLIP)))
op = op_buffer_add (&builder->render_ops, OP_CHANGE_CLIP);
if (last_op->op == OP_CHANGE_CLIP)
{
last_op->clip = *clip;
}
else
{
RenderOp *op;
op = ops_begin (builder, OP_CHANGE_CLIP);
op->clip = *clip;
}
}
op->clip = *clip;
if (builder->current_program != NULL)
current_program_state->clip = *clip;
@ -343,8 +346,8 @@ ops_set_modelview_internal (RenderOpBuilder *builder,
GskTransform *transform)
{
ProgramState *current_program_state = get_current_program_state (builder);
RenderOp *op;
graphene_matrix_t matrix;
OpMatrix *op;
#if 0
XXX This is not possible if we want pop() to work.
@ -355,24 +358,10 @@ ops_set_modelview_internal (RenderOpBuilder *builder,
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 = ops_begin (builder, OP_CHANGE_MODELVIEW);
op->modelview = matrix;
}
}
else
{
op = ops_begin (builder, OP_CHANGE_MODELVIEW);
op->modelview = matrix;
}
if (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_MODELVIEW)))
op = op_buffer_add (&builder->render_ops, OP_CHANGE_MODELVIEW);
op->matrix = matrix;
if (builder->current_program != NULL)
{
@ -490,27 +479,13 @@ ops_set_projection (RenderOpBuilder *builder,
const graphene_matrix_t *projection)
{
ProgramState *current_program_state = get_current_program_state (builder);
RenderOp *op;
graphene_matrix_t prev_mv;
OpMatrix *op;
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 = ops_begin (builder, OP_CHANGE_PROJECTION);
op->projection = *projection;
}
}
else
{
op = ops_begin (builder, OP_CHANGE_PROJECTION);
op->projection = *projection;
}
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;
@ -526,7 +501,7 @@ ops_set_viewport (RenderOpBuilder *builder,
const graphene_rect_t *viewport)
{
ProgramState *current_program_state = get_current_program_state (builder);
RenderOp *op;
OpViewport *op;
graphene_rect_t prev_viewport;
if (current_program_state != NULL &&
@ -549,7 +524,7 @@ void
ops_set_texture (RenderOpBuilder *builder,
int texture_id)
{
RenderOp *op;
OpTexture *op;
if (builder->current_texture == texture_id)
return;
@ -563,7 +538,7 @@ int
ops_set_render_target (RenderOpBuilder *builder,
int render_target_id)
{
RenderOp *op;
OpRenderTarget *op;
int prev_render_target;
if (builder->current_render_target == render_target_id)
@ -571,24 +546,10 @@ ops_set_render_target (RenderOpBuilder *builder,
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 = ops_begin (builder, OP_CHANGE_RENDER_TARGET);
op->render_target_id = render_target_id;
}
}
else
{
op = ops_begin (builder, OP_CHANGE_RENDER_TARGET);
op->render_target_id = render_target_id;
}
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;
@ -600,32 +561,16 @@ ops_set_opacity (RenderOpBuilder *builder,
float opacity)
{
ProgramState *current_program_state = get_current_program_state (builder);
RenderOp *op;
OpOpacity *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 (!(op = op_buffer_peek_tail_checked (&builder->render_ops, OP_CHANGE_OPACITY)))
op = op_buffer_add (&builder->render_ops, OP_CHANGE_OPACITY);
if (last_op->op == OP_CHANGE_OPACITY)
{
last_op->opacity = opacity;
}
else
{
op = ops_begin (builder, OP_CHANGE_OPACITY);
op->opacity = opacity;
}
}
else
{
op = ops_begin (builder, OP_CHANGE_OPACITY);
op->opacity = opacity;
}
op->opacity = opacity;
prev_opacity = builder->current_opacity;
builder->current_opacity = opacity;
@ -641,7 +586,7 @@ ops_set_color (RenderOpBuilder *builder,
const GdkRGBA *color)
{
ProgramState *current_program_state = get_current_program_state (builder);
RenderOp *op;
OpColor *op;
if (gdk_rgba_equal (color, &current_program_state->color))
return;
@ -649,7 +594,7 @@ ops_set_color (RenderOpBuilder *builder,
current_program_state->color = *color;
op = ops_begin (builder, OP_CHANGE_COLOR);
op->color = color;
op->rgba = *color;
}
void
@ -658,7 +603,7 @@ ops_set_color_matrix (RenderOpBuilder *builder,
const graphene_vec4_t *offset)
{
ProgramState *current_program_state = get_current_program_state (builder);
RenderOp *op;
OpColorMatrix *op;
if (memcmp (matrix,
&current_program_state->color_matrix.matrix,
@ -672,8 +617,8 @@ ops_set_color_matrix (RenderOpBuilder *builder,
current_program_state->color_matrix.offset = *offset;
op = ops_begin (builder, OP_CHANGE_COLOR_MATRIX);
op->color_matrix.matrix = *matrix;
op->color_matrix.offset = *offset;
op->matrix = *matrix;
op->offset = *offset;
}
void
@ -681,7 +626,7 @@ ops_set_border (RenderOpBuilder *builder,
const GskRoundedRect *outline)
{
ProgramState *current_program_state = get_current_program_state (builder);
RenderOp *op;
OpBorder *op;
if (memcmp (&current_program_state->border.outline,
outline, sizeof (GskRoundedRect)) == 0)
@ -690,7 +635,7 @@ ops_set_border (RenderOpBuilder *builder,
current_program_state->border.outline = *outline;
op = ops_begin (builder, OP_CHANGE_BORDER);
op->border.outline = *outline;
op->outline = *outline;
}
void
@ -698,7 +643,7 @@ ops_set_border_width (RenderOpBuilder *builder,
const float *widths)
{
ProgramState *current_program_state = get_current_program_state (builder);
RenderOp *op;
OpBorder *op;
if (memcmp (current_program_state->border.widths,
widths, sizeof (float) * 4) == 0)
@ -708,10 +653,10 @@ ops_set_border_width (RenderOpBuilder *builder,
widths, sizeof (float) * 4);
op = ops_begin (builder, 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];
op->widths[0] = widths[0];
op->widths[1] = widths[1];
op->widths[2] = widths[2];
op->widths[3] = widths[3];
}
void
@ -719,68 +664,37 @@ ops_set_border_color (RenderOpBuilder *builder,
const GdkRGBA *color)
{
ProgramState *current_program_state = get_current_program_state (builder);
RenderOp op;
op.op = OP_CHANGE_BORDER_COLOR;
rgba_to_float (color, op.border.color);
OpBorder *op;
float fcolor[4];
if (memcmp (&op.border.color, &current_program_state->border.color,
sizeof (float) * 4) == 0)
rgba_to_float (color, fcolor);
if (memcmp (fcolor, &current_program_state->border.color, sizeof fcolor) == 0)
return;
rgba_to_float (color, current_program_state->border.color);
g_array_append_val (builder->render_ops, op);
op = op_buffer_add (&builder->render_ops, OP_CHANGE_BORDER_COLOR);
memcpy (op->color, fcolor, sizeof (float[4]));
memcpy (current_program_state->border.color, fcolor, sizeof (float[4]));
}
void
ops_draw (RenderOpBuilder *builder,
const GskQuadVertex vertex_data[GL_N_VERTICES])
{
RenderOp *last_op;
OpDraw *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)
if ((op = op_buffer_peek_tail_checked (&builder->render_ops, 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 = ops_begin (builder, OP_DRAW);
/* last_op may have moved in memory */
last_op = &g_array_index (builder->render_ops, RenderOp, builder->render_ops->len - 2);
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);
op->vao_size += GL_N_VERTICES;
}
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;
op = op_buffer_add (&builder->render_ops, OP_DRAW);
op->vao_offset = builder->vertices->len;
op->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;
g_array_append_vals (builder->vertices, vertex_data, GL_N_VERTICES);
}
/* The offset is only valid for the current modelview.
@ -795,15 +709,22 @@ ops_offset (RenderOpBuilder *builder,
builder->dy += y;
}
RenderOp *
gpointer
ops_begin (RenderOpBuilder *builder,
guint kind)
OpKind kind)
{
RenderOp *op;
g_array_set_size (builder->render_ops, builder->render_ops->len + 1);
op = &g_array_index (builder->render_ops, RenderOp, builder->render_ops->len - 1);
op->op = kind;
return op;
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;
}

View File

@ -10,6 +10,8 @@
#include "gskglrenderer.h"
#include "gskrendernodeprivate.h"
#include "opbuffer.h"
#define GL_N_VERTICES 6
#define GL_N_PROGRAMS 13
@ -32,38 +34,7 @@ typedef struct
OpsMatrixMetadata metadata;
} MatrixStackEntry;
enum {
OP_NONE,
OP_CHANGE_OPACITY = 1,
OP_CHANGE_COLOR = 2,
OP_CHANGE_PROJECTION = 3,
OP_CHANGE_MODELVIEW = 4,
OP_CHANGE_PROGRAM = 5,
OP_CHANGE_RENDER_TARGET = 6,
OP_CHANGE_CLIP = 7,
OP_CHANGE_VIEWPORT = 8,
OP_CHANGE_SOURCE_TEXTURE = 9,
OP_CHANGE_VAO = 10,
OP_CHANGE_LINEAR_GRADIENT = 11,
OP_CHANGE_COLOR_MATRIX = 12,
OP_CHANGE_BLUR = 13,
OP_CHANGE_INSET_SHADOW = 14,
OP_CHANGE_OUTSET_SHADOW = 15,
OP_CHANGE_BORDER = 16,
OP_CHANGE_BORDER_COLOR = 17,
OP_CHANGE_BORDER_WIDTH = 18,
OP_CHANGE_CROSS_FADE = 19,
OP_CHANGE_UNBLURRED_OUTSET_SHADOW = 20,
OP_CLEAR = 21,
OP_DRAW = 22,
OP_DUMP_FRAMEBUFFER = 23,
OP_PUSH_DEBUG_GROUP = 24,
OP_POP_DEBUG_GROUP = 25,
OP_CHANGE_BLEND = 26,
OP_CHANGE_REPEAT = 27,
};
typedef struct
struct _Program
{
int index; /* Into the renderer's program array */
@ -145,101 +116,7 @@ typedef struct
int texture_rect_location;
} repeat;
};
} Program;
typedef struct
{
guint op;
union {
float opacity;
graphene_matrix_t modelview;
graphene_matrix_t projection;
const Program *program;
int texture_id;
int render_target_id;
const GdkRGBA *color;
GskQuadVertex vertex_data[6];
GskRoundedRect clip;
graphene_rect_t viewport;
struct {
int n_color_stops;
float color_offsets[8];
float color_stops[4 * 8];
graphene_point_t start_point;
graphene_point_t end_point;
} linear_gradient;
struct {
gsize vao_offset;
gsize vao_size;
} draw;
struct {
graphene_matrix_t matrix;
graphene_vec4_t offset;
} color_matrix;
struct {
float radius;
graphene_size_t size;
float dir[2];
} blur;
struct {
float outline[4];
float corner_widths[4];
float corner_heights[4];
float radius;
float spread;
float offset[2];
float color[4];
} inset_shadow;
struct {
float outline[4];
float corner_widths[4];
float corner_heights[4];
float radius;
float spread;
float offset[2];
float color[4];
} outset_shadow;
struct {
float outline[4];
float corner_widths[4];
float corner_heights[4];
float radius;
float spread;
float offset[2];
float color[4];
} unblurred_outset_shadow;
struct {
float color[4];
} shadow;
struct {
float widths[4];
float color[4];
GskRoundedRect outline;
} border;
struct {
float progress;
int source2;
} cross_fade;
struct {
int source2;
int mode;
} blend;
struct {
float child_bounds[4];
float texture_rect[4];
} repeat;
struct {
char *filename;
int width;
int height;
} dump;
struct {
char text[180]; /* Size of linear_gradient, so 'should be enough' without growing RenderOp */
} debug_group;
};
} RenderOp;
};
typedef struct
{
@ -276,9 +153,9 @@ typedef struct
float current_opacity;
float dx, dy;
gsize buffer_size;
OpBuffer render_ops;
GArray *vertices;
GArray *render_ops;
GskGLRenderer *renderer;
/* Stack of modelview matrices */
@ -298,6 +175,7 @@ void ops_dump_framebuffer (RenderOpBuilder *builder,
int height);
void ops_init (RenderOpBuilder *builder);
void ops_free (RenderOpBuilder *builder);
void ops_reset (RenderOpBuilder *builder);
void ops_push_debug_group (RenderOpBuilder *builder,
const char *text);
void ops_pop_debug_group (RenderOpBuilder *builder);
@ -358,7 +236,8 @@ void ops_offset (RenderOpBuilder *builder,
float x,
float y);
RenderOp *ops_begin (RenderOpBuilder *builder,
guint kind);
gpointer ops_begin (RenderOpBuilder *builder,
OpKind kind);
OpBuffer *ops_get_buffer (RenderOpBuilder *builder);
#endif

133
gsk/gl/opbuffer.c Normal file
View File

@ -0,0 +1,133 @@
#include "opbuffer.h"
#include <string.h>
static guint op_sizes[OP_LAST] = {
0,
sizeof (OpOpacity),
sizeof (OpColor),
sizeof (OpMatrix),
sizeof (OpMatrix),
sizeof (OpProgram),
sizeof (OpRenderTarget),
sizeof (OpClip),
sizeof (OpViewport),
sizeof (OpTexture),
sizeof (OpRepeat),
sizeof (OpLinearGradient),
sizeof (OpColorMatrix),
sizeof (OpBlur),
sizeof (OpShadow),
sizeof (OpShadow),
sizeof (OpBorder),
sizeof (OpBorder),
sizeof (OpBorder),
sizeof (OpCrossFade),
sizeof (OpShadow),
0,
sizeof (OpDraw),
sizeof (OpDumpFrameBuffer),
sizeof (OpDebugGroup),
0,
sizeof (OpBlend),
};
void
op_buffer_init (OpBuffer *buffer)
{
static gsize initialized = FALSE;
if (g_once_init_enter (&initialized))
{
guint i;
for (i = 0; i < G_N_ELEMENTS (op_sizes); i++)
{
guint size = op_sizes[i];
if (size > 0)
{
/* Round all op entry sizes to the nearest 16 to ensure
* that we guarantee proper alignments for all op entries.
* This is only done once on first use.
*/
#define CHECK_SIZE(s) else if (size < (s)) { size = s; }
if (0) {}
CHECK_SIZE (16)
CHECK_SIZE (32)
CHECK_SIZE (48)
CHECK_SIZE (64)
CHECK_SIZE (80)
CHECK_SIZE (96)
CHECK_SIZE (112)
CHECK_SIZE (128)
CHECK_SIZE (144)
CHECK_SIZE (160)
CHECK_SIZE (176)
CHECK_SIZE (192)
else g_assert_not_reached ();
#undef CHECK_SIZE
op_sizes[i] = size;
}
}
g_once_init_leave (&initialized, TRUE);
}
memset (buffer, 0, sizeof *buffer);
buffer->buflen = 4096;
buffer->bufpos = 0;
buffer->buf = g_malloc (buffer->buflen);
buffer->index = g_array_new (FALSE, FALSE, sizeof (OpBufferEntry));
/* Add dummy entry to guarantee non-empty index */
op_buffer_add (buffer, OP_NONE);
}
void
op_buffer_destroy (OpBuffer *buffer)
{
g_free (buffer->buf);
g_array_unref (buffer->index);
}
void
op_buffer_clear (OpBuffer *buffer)
{
if (buffer->index->len > 1)
g_array_remove_range (buffer->index, 1, buffer->index->len - 1);
buffer->bufpos = 0;
}
static inline void
ensure_buffer_space_for (OpBuffer *buffer,
guint size)
{
if G_UNLIKELY (buffer->bufpos + size >= buffer->buflen)
{
buffer->buflen *= 2;
buffer->buf = g_realloc (buffer->buf, buffer->buflen);
}
}
gpointer
op_buffer_add (OpBuffer *buffer,
OpKind kind)
{
guint size = op_sizes[kind];
OpBufferEntry entry;
entry.pos = buffer->bufpos;
entry.kind = kind;
if (size > 0)
ensure_buffer_space_for (buffer, size);
g_array_append_val (buffer->index, entry);
buffer->bufpos += size;
return &buffer->buf[entry.pos];
}

257
gsk/gl/opbuffer.h Normal file
View File

@ -0,0 +1,257 @@
#ifndef __OP_BUFFER_H__
#define __OP_BUFFER_H__
#include <gdk/gdk.h>
#include <gsk/gsk.h>
#include <graphene.h>
#include "gskgldriverprivate.h"
typedef struct _Program Program;
typedef enum
{
OP_NONE = 0,
OP_CHANGE_OPACITY = 1,
OP_CHANGE_COLOR = 2,
OP_CHANGE_PROJECTION = 3,
OP_CHANGE_MODELVIEW = 4,
OP_CHANGE_PROGRAM = 5,
OP_CHANGE_RENDER_TARGET = 6,
OP_CHANGE_CLIP = 7,
OP_CHANGE_VIEWPORT = 8,
OP_CHANGE_SOURCE_TEXTURE = 9,
OP_CHANGE_REPEAT = 10,
OP_CHANGE_LINEAR_GRADIENT = 11,
OP_CHANGE_COLOR_MATRIX = 12,
OP_CHANGE_BLUR = 13,
OP_CHANGE_INSET_SHADOW = 14,
OP_CHANGE_OUTSET_SHADOW = 15,
OP_CHANGE_BORDER = 16,
OP_CHANGE_BORDER_COLOR = 17,
OP_CHANGE_BORDER_WIDTH = 18,
OP_CHANGE_CROSS_FADE = 19,
OP_CHANGE_UNBLURRED_OUTSET_SHADOW = 20,
OP_CLEAR = 21,
OP_DRAW = 22,
OP_DUMP_FRAMEBUFFER = 23,
OP_PUSH_DEBUG_GROUP = 24,
OP_POP_DEBUG_GROUP = 25,
OP_CHANGE_BLEND = 26,
OP_LAST
} OpKind;
/* OpNode are allocated within OpBuffer.pos, but we keep
* a secondary index into the locations of that buffer
* from OpBuffer.index. This allows peeking at the kind
* and quickly replacing existing entries when necessary.
*/
typedef struct
{
guint pos;
OpKind kind;
} OpBufferEntry;
typedef struct
{
guint8 *buf;
gsize buflen;
gsize bufpos;
GArray *index;
} OpBuffer;
typedef struct
{
float opacity;
} OpOpacity;
typedef struct
{
graphene_matrix_t matrix;
} OpMatrix;
typedef struct
{
const Program *program;
} OpProgram;
typedef struct
{
GdkRGBA rgba;
} OpColor;
typedef struct
{
int render_target_id;
} OpRenderTarget;
typedef struct
{
GskRoundedRect clip;
} OpClip;
typedef struct
{
graphene_rect_t viewport;
} OpViewport;
typedef struct
{
int texture_id;
} OpTexture;
typedef struct
{
gsize vao_offset;
gsize vao_size;
} OpDraw;
typedef struct
{
float color_offsets[8];
float color_stops[4 * 8];
graphene_point_t start_point;
graphene_point_t end_point;
int n_color_stops;
} OpLinearGradient;
typedef struct
{
graphene_matrix_t matrix;
graphene_vec4_t offset;
} OpColorMatrix;
typedef struct
{
float radius;
graphene_size_t size;
float dir[2];
} OpBlur;
typedef struct
{
float outline[4];
float corner_widths[4];
float corner_heights[4];
float radius;
float spread;
float offset[2];
float color[4];
} OpShadow;
typedef struct
{
float widths[4];
float color[4];
GskRoundedRect outline;
} OpBorder;
typedef struct
{
float progress;
int source2;
} OpCrossFade;
typedef struct
{
char *filename;
int width;
int height;
} OpDumpFrameBuffer;
typedef struct
{
char text[64];
} OpDebugGroup;
typedef struct
{
int source2;
int mode;
} OpBlend;
typedef struct
{
float child_bounds[4];
float texture_rect[4];
} OpRepeat;
void op_buffer_init (OpBuffer *buffer);
void op_buffer_destroy (OpBuffer *buffer);
void op_buffer_clear (OpBuffer *buffer);
gpointer op_buffer_add (OpBuffer *buffer,
OpKind kind);
typedef struct
{
GArray *index;
OpBuffer *buffer;
guint pos;
} OpBufferIter;
static inline void
op_buffer_iter_init (OpBufferIter *iter,
OpBuffer *buffer)
{
iter->index = buffer->index;
iter->buffer = buffer;
iter->pos = 1; /* Skip first OP_NONE */
}
static inline gpointer
op_buffer_iter_next (OpBufferIter *iter,
OpKind *kind)
{
const OpBufferEntry *entry;
if (iter->pos == iter->index->len)
return NULL;
entry = &g_array_index (iter->index, OpBufferEntry, iter->pos);
iter->pos++;
*kind = entry->kind;
return &iter->buffer->buf[entry->pos];
}
static inline void
op_buffer_pop_tail (OpBuffer *buffer)
{
/* Never truncate the first OP_NONE */
if G_LIKELY (buffer->index->len > 0)
buffer->index->len--;
}
static inline gpointer
op_buffer_peek_tail (OpBuffer *buffer,
OpKind *kind)
{
const OpBufferEntry *entry;
entry = &g_array_index (buffer->index, OpBufferEntry, buffer->index->len - 1);
*kind = entry->kind;
return &buffer->buf[entry->pos];
}
static inline gpointer
op_buffer_peek_tail_checked (OpBuffer *buffer,
OpKind kind)
{
const OpBufferEntry *entry;
entry = &g_array_index (buffer->index, OpBufferEntry, buffer->index->len - 1);
if (entry->kind == kind)
return &buffer->buf[entry->pos];
return NULL;
}
static inline guint
op_buffer_n_ops (OpBuffer *buffer)
{
return buffer->index->len - 1;
}
#endif /* __OP_BUFFER_H__ */

View File

@ -3414,6 +3414,7 @@ struct _GskTextNode
GdkRGBA color;
graphene_point_t offset;
gpointer render_data;
guint num_glyphs;
PangoGlyphInfo glyphs[];
};
@ -3423,6 +3424,7 @@ gsk_text_node_finalize (GskRenderNode *node)
{
GskTextNode *self = (GskTextNode *) node;
g_free (self->render_data);
g_object_unref (self->font);
}
@ -3545,6 +3547,7 @@ gsk_text_node_new (PangoFont *font,
self->has_color_glyphs = font_has_color_glyphs (font);
self->color = *color;
self->offset = *offset;
self->render_data = NULL;
self->num_glyphs = glyphs->num_glyphs;
memcpy (self->glyphs, glyphs->glyphs, sizeof (PangoGlyphInfo) * glyphs->num_glyphs);
@ -3625,6 +3628,24 @@ gsk_text_node_get_offset (GskRenderNode *node)
return &self->offset;
}
void
gsk_text_node_set_render_data (GskRenderNode *node,
gpointer data)
{
GskTextNode *self = (GskTextNode *) node;
self->render_data = data;
}
gpointer
gsk_text_node_get_render_data (GskRenderNode *node)
{
GskTextNode *self = (GskTextNode *) node;
return self->render_data;
}
/*** GSK_BLUR_NODE ***/
typedef struct _GskBlurNode GskBlurNode;

View File

@ -46,6 +46,10 @@ void gsk_render_node_diff (GskRenderNode *nod
void gsk_render_node_diff_impossible (GskRenderNode *node1,
GskRenderNode *node2,
cairo_region_t *region);
void gsk_text_node_set_render_data (GskRenderNode *node,
gpointer data);
gpointer gsk_text_node_get_render_data (GskRenderNode *node);
G_END_DECLS

View File

@ -48,6 +48,7 @@ gsk_private_sources = files([
'gl/gskglnodesample.c',
'gl/gskgltextureatlas.c',
'gl/gskgliconcache.c',
'gl/opbuffer.c',
'gl/stb_rect_pack.c',
])

View File

@ -22,13 +22,35 @@
G_BEGIN_DECLS
GdkPixbuf *_gdk_pixbuf_new_from_stream_scaled (GInputStream *stream,
gdouble scale,
GdkPixbuf *_gdk_pixbuf_new_from_stream (GInputStream *stream,
const char *format,
GCancellable *cancellable,
GError **error);
GdkPixbuf *_gdk_pixbuf_new_from_stream_at_scale (GInputStream *stream,
const char *format,
int width,
int height,
gboolean aspect,
GCancellable *cancellable,
GError **error);
GdkPixbuf *_gdk_pixbuf_new_from_resource_scaled (const gchar *resource_path,
gdouble scale,
GdkPixbuf *_gdk_pixbuf_new_from_stream_scaled (GInputStream *stream,
const char *format,
double scale,
GCancellable *cancellable,
GError **error);
GdkPixbuf *_gdk_pixbuf_new_from_resource (const char *resource_path,
const char *format,
GError **error);
GdkPixbuf *_gdk_pixbuf_new_from_resource_at_scale (const char *resource_path,
const char *format,
int width,
int height,
gboolean preserve_aspect,
GError **error);
GdkPixbuf *_gdk_pixbuf_new_from_resource_scaled (const char *resource_path,
const char *format,
double scale,
GError **error);
GdkPixbuf *gtk_make_symbolic_pixbuf_from_data (const char *data,
gsize len,

View File

@ -3276,17 +3276,16 @@ gtk_icon_info_get_filename (GtkIconInfo *icon_info)
gboolean
gtk_icon_info_is_symbolic (GtkIconInfo *icon_info)
{
gchar *icon_uri;
const char *path;
gboolean is_symbolic;
g_return_val_if_fail (GTK_IS_ICON_INFO (icon_info), FALSE);
icon_uri = NULL;
path = NULL;
if (icon_info->icon_file)
icon_uri = g_file_get_uri (icon_info->icon_file);
path = g_file_peek_path (icon_info->icon_file);
is_symbolic = (icon_uri != NULL) && (icon_uri_is_symbolic (icon_uri, -1));
g_free (icon_uri);
is_symbolic = (path != NULL) && (icon_uri_is_symbolic (path, -1));
return is_symbolic;
}
@ -3384,16 +3383,19 @@ icon_info_ensure_scale_and_texture (GtkIconInfo *icon_info)
&icon_info->load_error);
else if (size == 0)
source_pixbuf = _gdk_pixbuf_new_from_resource_scaled (icon_info->filename,
"svg",
icon_info->desired_scale,
&icon_info->load_error);
else
source_pixbuf = gdk_pixbuf_new_from_resource_at_scale (icon_info->filename,
size, size, TRUE,
&icon_info->load_error);
source_pixbuf = _gdk_pixbuf_new_from_resource_at_scale (icon_info->filename,
"svg",
size, size, TRUE,
&icon_info->load_error);
}
else
source_pixbuf = gdk_pixbuf_new_from_resource (icon_info->filename,
&icon_info->load_error);
source_pixbuf = _gdk_pixbuf_new_from_resource (icon_info->filename,
"png",
&icon_info->load_error);
}
else
{
@ -3425,19 +3427,22 @@ icon_info_ensure_scale_and_texture (GtkIconInfo *icon_info)
&icon_info->load_error);
else if (size == 0)
source_pixbuf = _gdk_pixbuf_new_from_stream_scaled (stream,
"svg",
icon_info->desired_scale,
NULL,
&icon_info->load_error);
else
source_pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
size, size,
TRUE, NULL,
source_pixbuf = _gdk_pixbuf_new_from_stream_at_scale (stream,
"svg",
size, size,
TRUE, NULL,
&icon_info->load_error);
}
else
source_pixbuf = gdk_pixbuf_new_from_stream (stream,
NULL,
&icon_info->load_error);
source_pixbuf = _gdk_pixbuf_new_from_stream (stream,
"png",
NULL,
&icon_info->load_error);
g_object_unref (stream);
}
}
@ -3938,7 +3943,8 @@ gtk_icon_info_load_symbolic_svg (GtkIconInfo *icon_info,
g_free (height);
stream = g_memory_input_stream_new_from_data (data, -1, g_free);
pixbuf = gdk_pixbuf_new_from_stream_at_scale (stream,
pixbuf = _gdk_pixbuf_new_from_stream_at_scale (stream,
"svg",
gdk_texture_get_width (icon_info->texture),
gdk_texture_get_height (icon_info->texture),
TRUE,

View File

@ -90,6 +90,7 @@ size_prepared_cb (GdkPixbufLoader *loader,
*/
GdkPixbuf *
_gdk_pixbuf_new_from_stream_scaled (GInputStream *stream,
const char *format,
gdouble scale,
GCancellable *cancellable,
GError **error)
@ -97,10 +98,18 @@ _gdk_pixbuf_new_from_stream_scaled (GInputStream *stream,
GdkPixbufLoader *loader;
GdkPixbuf *pixbuf;
loader = gdk_pixbuf_loader_new ();
if (format)
{
loader = gdk_pixbuf_loader_new_with_type (format, error);
if (!loader)
return NULL;
}
else
loader = gdk_pixbuf_loader_new ();
g_signal_connect (loader, "size-prepared",
G_CALLBACK (size_prepared_cb), &scale);
if (scale != 0)
g_signal_connect (loader, "size-prepared",
G_CALLBACK (size_prepared_cb), &scale);
pixbuf = load_from_stream (loader, stream, cancellable, error);
@ -109,14 +118,69 @@ _gdk_pixbuf_new_from_stream_scaled (GInputStream *stream,
return pixbuf;
}
static void
size_prepared_cb2 (GdkPixbufLoader *loader,
gint width,
gint height,
gpointer data)
{
int *scales = data;
gdk_pixbuf_loader_set_size (loader, scales[0], scales[1]);
}
GdkPixbuf *
_gdk_pixbuf_new_from_stream_at_scale (GInputStream *stream,
const char *format,
int width,
int height,
gboolean aspect,
GCancellable *cancellable,
GError **error)
{
GdkPixbufLoader *loader;
GdkPixbuf *pixbuf;
int scales[2];
if (format)
{
loader = gdk_pixbuf_loader_new_with_type (format, error);
if (!loader)
return NULL;
}
else
loader = gdk_pixbuf_loader_new ();
scales[0] = width;
scales[1] = height;
g_signal_connect (loader, "size-prepared",
G_CALLBACK (size_prepared_cb2), scales);
pixbuf = load_from_stream (loader, stream, cancellable, error);
g_object_unref (loader);
return pixbuf;
}
GdkPixbuf *
_gdk_pixbuf_new_from_stream (GInputStream *stream,
const char *format,
GCancellable *cancellable,
GError **error)
{
return _gdk_pixbuf_new_from_stream_scaled (stream, format, 0, cancellable, error);
}
/* Like gdk_pixbuf_new_from_resource_at_scale, but
* load the image at its original size times the
* given scale.
*/
GdkPixbuf *
_gdk_pixbuf_new_from_resource_scaled (const gchar *resource_path,
gdouble scale,
GError **error)
_gdk_pixbuf_new_from_resource_scaled (const char *resource_path,
const char *format,
double scale,
GError **error)
{
GInputStream *stream;
GdkPixbuf *pixbuf;
@ -125,12 +189,42 @@ _gdk_pixbuf_new_from_resource_scaled (const gchar *resource_path,
if (stream == NULL)
return NULL;
pixbuf = _gdk_pixbuf_new_from_stream_scaled (stream, scale, NULL, error);
pixbuf = _gdk_pixbuf_new_from_stream_scaled (stream, format, scale, NULL, error);
g_object_unref (stream);
return pixbuf;
}
GdkPixbuf *
_gdk_pixbuf_new_from_resource (const char *resource_path,
const char *format,
GError **error)
{
return _gdk_pixbuf_new_from_resource_scaled (resource_path, format, 0, error);
}
GdkPixbuf *
_gdk_pixbuf_new_from_resource_at_scale (const char *resource_path,
const char *format,
int width,
int height,
gboolean preserve_aspect,
GError **error)
{
GInputStream *stream;
GdkPixbuf *pixbuf;
stream = g_resources_open_stream (resource_path, 0, error);
if (stream == NULL)
return NULL;
pixbuf = _gdk_pixbuf_new_from_stream_at_scale (stream, format, width, height, preserve_aspect, NULL, error);
g_object_unref (stream);
return pixbuf;
}
static GdkPixbuf *
load_symbolic_svg (const char *escaped_file_data,
int width,
@ -332,11 +426,11 @@ gtk_make_symbolic_pixbuf_from_resource (const char *path,
}
GdkPixbuf *
gtk_make_symbolic_pixbuf_from_file (GFile *file,
int width,
int height,
double scale,
GError **error)
gtk_make_symbolic_pixbuf_from_file (GFile *file,
int width,
int height,
double scale,
GError **error)
{
char *data;
gsize size;
@ -373,11 +467,11 @@ gtk_make_symbolic_texture_from_resource (const char *path,
}
GdkTexture *
gtk_make_symbolic_texture_from_file (GFile *file,
int width,
int height,
double scale,
GError **error)
gtk_make_symbolic_texture_from_file (GFile *file,
int width,
int height,
double scale,
GError **error)
{
GdkPixbuf *pixbuf;
GdkTexture *texture;