mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-26 13:41:07 +00:00
Merge branch 'scaled-textures' into 'main'
Scaled textures See merge request GNOME/gtk!5488
This commit is contained in:
commit
2285ef3824
@ -3,7 +3,8 @@
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_PAINTABLE = 1,
|
||||
PROP_TEXTURE = 1,
|
||||
PROP_FILTER,
|
||||
PROP_SCALE
|
||||
};
|
||||
|
||||
@ -11,8 +12,9 @@ struct _Demo3Widget
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
GdkPaintable *paintable;
|
||||
GdkTexture *texture;
|
||||
float scale;
|
||||
GskScalingFilter filter;
|
||||
|
||||
GtkWidget *menu;
|
||||
};
|
||||
@ -28,6 +30,7 @@ static void
|
||||
demo3_widget_init (Demo3Widget *self)
|
||||
{
|
||||
self->scale = 1.f;
|
||||
self->filter = GSK_SCALING_FILTER_LINEAR;
|
||||
gtk_widget_init_template (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
@ -36,7 +39,7 @@ demo3_widget_dispose (GObject *object)
|
||||
{
|
||||
Demo3Widget *self = DEMO3_WIDGET (object);
|
||||
|
||||
g_clear_object (&self->paintable);
|
||||
g_clear_object (&self->texture);
|
||||
|
||||
gtk_widget_dispose_template (GTK_WIDGET (self), DEMO3_TYPE_WIDGET);
|
||||
|
||||
@ -50,12 +53,13 @@ demo3_widget_snapshot (GtkWidget *widget,
|
||||
Demo3Widget *self = DEMO3_WIDGET (widget);
|
||||
int x, y, width, height;
|
||||
double w, h;
|
||||
GskRenderNode *node;
|
||||
|
||||
width = gtk_widget_get_width (widget);
|
||||
height = gtk_widget_get_height (widget);
|
||||
|
||||
w = self->scale * gdk_paintable_get_intrinsic_width (self->paintable);
|
||||
h = self->scale * gdk_paintable_get_intrinsic_height (self->paintable);
|
||||
w = self->scale * gdk_texture_get_width (self->texture);
|
||||
h = self->scale * gdk_texture_get_height (self->texture);
|
||||
|
||||
x = MAX (0, (width - ceil (w)) / 2);
|
||||
y = MAX (0, (height - ceil (h)) / 2);
|
||||
@ -63,7 +67,11 @@ demo3_widget_snapshot (GtkWidget *widget,
|
||||
gtk_snapshot_push_clip (snapshot, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
gtk_snapshot_save (snapshot);
|
||||
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (x, y));
|
||||
gdk_paintable_snapshot (self->paintable, snapshot, w, h);
|
||||
node = gsk_texture_scale_node_new (self->texture,
|
||||
&GRAPHENE_RECT_INIT (0, 0, w, h),
|
||||
self->filter);
|
||||
gtk_snapshot_append_node (snapshot, node);
|
||||
gsk_render_node_unref (node);
|
||||
gtk_snapshot_restore (snapshot);
|
||||
gtk_snapshot_pop (snapshot);
|
||||
}
|
||||
@ -81,9 +89,9 @@ demo3_widget_measure (GtkWidget *widget,
|
||||
int size;
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
size = gdk_paintable_get_intrinsic_width (self->paintable);
|
||||
size = gdk_texture_get_width (self->texture);
|
||||
else
|
||||
size = gdk_paintable_get_intrinsic_height (self->paintable);
|
||||
size = gdk_texture_get_height (self->texture);
|
||||
|
||||
*minimum = *natural = self->scale * size;
|
||||
}
|
||||
@ -113,9 +121,9 @@ demo3_widget_set_property (GObject *object,
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PAINTABLE:
|
||||
g_clear_object (&self->paintable);
|
||||
self->paintable = g_value_dup_object (value);
|
||||
case PROP_TEXTURE:
|
||||
g_clear_object (&self->texture);
|
||||
self->texture = g_value_dup_object (value);
|
||||
gtk_widget_queue_resize (GTK_WIDGET (object));
|
||||
break;
|
||||
|
||||
@ -124,6 +132,11 @@ demo3_widget_set_property (GObject *object,
|
||||
gtk_widget_queue_resize (GTK_WIDGET (object));
|
||||
break;
|
||||
|
||||
case PROP_FILTER:
|
||||
self->filter = g_value_get_enum (value);
|
||||
gtk_widget_queue_resize (GTK_WIDGET (object));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@ -140,14 +153,18 @@ demo3_widget_get_property (GObject *object,
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_PAINTABLE:
|
||||
g_value_set_object (value, self->paintable);
|
||||
case PROP_TEXTURE:
|
||||
g_value_set_object (value, self->texture);
|
||||
break;
|
||||
|
||||
case PROP_SCALE:
|
||||
g_value_set_float (value, self->scale);
|
||||
break;
|
||||
|
||||
case PROP_FILTER:
|
||||
g_value_set_enum (value, self->filter);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@ -205,16 +222,21 @@ demo3_widget_class_init (Demo3WidgetClass *class)
|
||||
widget_class->measure = demo3_widget_measure;
|
||||
widget_class->size_allocate = demo3_widget_size_allocate;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_PAINTABLE,
|
||||
g_param_spec_object ("paintable", "Paintable", "Paintable",
|
||||
GDK_TYPE_PAINTABLE,
|
||||
g_object_class_install_property (object_class, PROP_TEXTURE,
|
||||
g_param_spec_object ("texture", NULL, NULL,
|
||||
GDK_TYPE_TEXTURE,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_SCALE,
|
||||
g_param_spec_float ("scale", "Scale", "Scale",
|
||||
g_param_spec_float ("scale", NULL, NULL,
|
||||
0.0, 10.0, 1.0,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_FILTER,
|
||||
g_param_spec_enum ("filter", NULL, NULL,
|
||||
GSK_TYPE_SCALING_FILTER, GSK_SCALING_FILTER_LINEAR,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
/* These are the actions that we are using in the menu */
|
||||
gtk_widget_class_install_action (widget_class, "zoom.in", NULL, zoom_cb);
|
||||
gtk_widget_class_install_action (widget_class, "zoom.out", NULL, zoom_cb);
|
||||
@ -229,16 +251,13 @@ GtkWidget *
|
||||
demo3_widget_new (const char *resource)
|
||||
{
|
||||
Demo3Widget *self;
|
||||
GdkPixbuf *pixbuf;
|
||||
GdkPaintable *paintable;
|
||||
GdkTexture *texture;
|
||||
|
||||
pixbuf = gdk_pixbuf_new_from_resource (resource, NULL);
|
||||
paintable = GDK_PAINTABLE (gdk_texture_new_for_pixbuf (pixbuf));
|
||||
texture = gdk_texture_new_from_resource (resource);
|
||||
|
||||
self = g_object_new (DEMO3_TYPE_WIDGET, "paintable", paintable, NULL);
|
||||
self = g_object_new (DEMO3_TYPE_WIDGET, "texture", texture, NULL);
|
||||
|
||||
g_object_unref (pixbuf);
|
||||
g_object_unref (paintable);
|
||||
g_object_unref (texture);
|
||||
|
||||
return GTK_WIDGET (self);
|
||||
}
|
||||
|
@ -22,9 +22,11 @@ do_menu (GtkWidget *do_widget)
|
||||
if (!window)
|
||||
{
|
||||
GtkWidget *box;
|
||||
GtkWidget *box2;
|
||||
GtkWidget *sw;
|
||||
GtkWidget *widget;
|
||||
GtkWidget *scale;
|
||||
GtkWidget *dropdown;
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Menu");
|
||||
@ -43,10 +45,19 @@ do_menu (GtkWidget *do_widget)
|
||||
widget = demo3_widget_new ("/transparent/portland-rose.jpg");
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), widget);
|
||||
|
||||
box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_box_append (GTK_BOX (box), box2);
|
||||
|
||||
scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0.01, 10.0, 0.1);
|
||||
gtk_range_set_value (GTK_RANGE (scale), 1.0);
|
||||
gtk_box_append (GTK_BOX (box), scale);
|
||||
gtk_widget_set_hexpand (scale, TRUE);
|
||||
gtk_box_append (GTK_BOX (box2), scale);
|
||||
|
||||
dropdown = gtk_drop_down_new (G_LIST_MODEL (gtk_string_list_new ((const char *[]){ "Linear", "Nearest", "Trilinear", NULL })), NULL);
|
||||
gtk_box_append (GTK_BOX (box2), dropdown);
|
||||
|
||||
g_object_bind_property (dropdown, "selected", widget, "filter", G_BINDING_DEFAULT);
|
||||
|
||||
g_object_bind_property (gtk_range_get_adjustment (GTK_RANGE (scale)), "value",
|
||||
widget, "scale",
|
||||
G_BINDING_BIDIRECTIONAL);
|
||||
|
@ -297,6 +297,21 @@ The default texture is a 10x10 checkerboard with the top left and bottom right
|
||||
representation for this texture is `url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABmJLR0QA/wD/AP+gvaeTAAAAKUlEQVQYlWP8z3DmPwMaYGQwYUQXY0IXwAUGUCGGoxkYGBiweXAoeAYAz44F3e3U1xUAAAAASUVORK5CYII=")
|
||||
`.
|
||||
|
||||
### texture-scale
|
||||
|
||||
| property | syntax | default | printed |
|
||||
| -------- | ---------------- | ---------------------- | ----------- |
|
||||
| bounds | `<rect>` | 50 | always |
|
||||
| texture | `<url>` | *see below* | always |
|
||||
| filter | `filter` | *see below* | non-default |
|
||||
|
||||
Creates a node like `gsk_texture_scale_node_new()` with the given properties.
|
||||
|
||||
The default texture is a 10x10 checkerboard, just like for texture.
|
||||
|
||||
The possible filter values are `linear`, `nearest` and `trilinear`, with
|
||||
`linear` being the default.
|
||||
|
||||
### transform
|
||||
|
||||
| property | syntax | default | printed |
|
||||
|
@ -250,6 +250,7 @@ collect_reused_child_nodes (GskRenderer *renderer,
|
||||
/* Leaf nodes */
|
||||
|
||||
case GSK_TEXTURE_NODE:
|
||||
case GSK_TEXTURE_SCALE_NODE:
|
||||
case GSK_CAIRO_NODE:
|
||||
case GSK_COLOR_NODE:
|
||||
case GSK_BORDER_NODE:
|
||||
@ -845,6 +846,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
|
||||
}
|
||||
break; /* Fallback */
|
||||
|
||||
case GSK_TEXTURE_SCALE_NODE:
|
||||
case GSK_TEXT_NODE:
|
||||
case GSK_RADIAL_GRADIENT_NODE:
|
||||
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
|
||||
|
@ -1439,8 +1439,7 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
|
||||
|
||||
g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
|
||||
g_assert (!GDK_IS_GL_TEXTURE (texture));
|
||||
g_assert (min_filter == GL_LINEAR || min_filter == GL_NEAREST);
|
||||
g_assert (mag_filter == GL_LINEAR || min_filter == GL_NEAREST);
|
||||
g_assert (mag_filter == GL_LINEAR || mag_filter == GL_NEAREST);
|
||||
|
||||
width = gdk_texture_get_width (texture);
|
||||
height = gdk_texture_get_height (texture);
|
||||
@ -1464,6 +1463,9 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
|
||||
|
||||
gsk_gl_command_queue_do_upload_texture (self, texture);
|
||||
|
||||
if (min_filter == GL_LINEAR_MIPMAP_LINEAR)
|
||||
glGenerateMipmap (GL_TEXTURE_2D);
|
||||
|
||||
/* Restore previous texture state if any */
|
||||
if (self->attachments->textures[0].id > 0)
|
||||
glBindTexture (self->attachments->textures[0].target,
|
||||
|
@ -753,7 +753,7 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
|
||||
{
|
||||
if ((t = gdk_texture_get_render_data (texture, self)))
|
||||
{
|
||||
if (t->min_filter == min_filter && t->mag_filter == mag_filter)
|
||||
if (t->min_filter == min_filter && t->mag_filter == mag_filter && t->texture_id)
|
||||
return t->texture_id;
|
||||
}
|
||||
|
||||
@ -1195,6 +1195,10 @@ gsk_gl_driver_create_command_queue (GskGLDriver *self,
|
||||
void
|
||||
gsk_gl_driver_add_texture_slices (GskGLDriver *self,
|
||||
GdkTexture *texture,
|
||||
int min_filter,
|
||||
int mag_filter,
|
||||
guint min_cols,
|
||||
guint min_rows,
|
||||
GskGLTextureSlice **out_slices,
|
||||
guint *out_n_slices)
|
||||
{
|
||||
@ -1216,31 +1220,37 @@ gsk_gl_driver_add_texture_slices (GskGLDriver *self,
|
||||
|
||||
/* XXX: Too much? */
|
||||
max_texture_size = self->command_queue->max_texture_size / 4;
|
||||
|
||||
tex_width = texture->width;
|
||||
tex_height = texture->height;
|
||||
cols = (texture->width / max_texture_size) + 1;
|
||||
rows = (texture->height / max_texture_size) + 1;
|
||||
|
||||
cols = MAX ((texture->width / max_texture_size) + 1, min_cols);
|
||||
rows = MAX ((texture->height / max_texture_size) + 1, min_rows);
|
||||
|
||||
n_slices = cols * rows;
|
||||
|
||||
if ((t = gdk_texture_get_render_data (texture, self)))
|
||||
{
|
||||
*out_slices = t->slices;
|
||||
*out_n_slices = t->n_slices;
|
||||
return;
|
||||
if (t->n_slices == n_slices)
|
||||
{
|
||||
*out_slices = t->slices;
|
||||
*out_n_slices = t->n_slices;
|
||||
return;
|
||||
}
|
||||
|
||||
gdk_texture_clear_render_data (texture);
|
||||
}
|
||||
|
||||
n_slices = cols * rows;
|
||||
slices = g_new0 (GskGLTextureSlice, n_slices);
|
||||
memtex = gdk_memory_texture_from_texture (texture,
|
||||
gdk_texture_get_format (texture));
|
||||
|
||||
for (guint col = 0; col < cols; col ++)
|
||||
for (guint col = 0; col < cols; col++)
|
||||
{
|
||||
int slice_width = MIN (max_texture_size, texture->width - x);
|
||||
int slice_width = col + 1 < cols ? tex_width / cols : tex_width - x;
|
||||
|
||||
for (guint row = 0; row < rows; row ++)
|
||||
for (guint row = 0; row < rows; row++)
|
||||
{
|
||||
int slice_height = MIN (max_texture_size, texture->height - y);
|
||||
int slice_height = row + 1 < rows ? tex_height / rows : tex_height - y;
|
||||
int slice_index = (col * rows) + row;
|
||||
GdkTexture *subtex;
|
||||
guint texture_id;
|
||||
@ -1250,7 +1260,7 @@ gsk_gl_driver_add_texture_slices (GskGLDriver *self,
|
||||
slice_width, slice_height);
|
||||
texture_id = gsk_gl_command_queue_upload_texture (self->command_queue,
|
||||
subtex,
|
||||
GL_NEAREST, GL_NEAREST);
|
||||
min_filter, mag_filter);
|
||||
g_object_unref (subtex);
|
||||
|
||||
slices[slice_index].rect.x = x;
|
||||
|
@ -177,6 +177,10 @@ GskGLTexture * gsk_gl_driver_mark_texture_permanent (GskGLDriver *s
|
||||
guint texture_id);
|
||||
void gsk_gl_driver_add_texture_slices (GskGLDriver *self,
|
||||
GdkTexture *texture,
|
||||
int min_filter,
|
||||
int mag_filter,
|
||||
guint min_cols,
|
||||
guint min_rows,
|
||||
GskGLTextureSlice **out_slices,
|
||||
guint *out_n_slices);
|
||||
GskGLProgram * gsk_gl_driver_lookup_shader (GskGLDriver *self,
|
||||
@ -228,6 +232,10 @@ gsk_gl_driver_lookup_texture (GskGLDriver *self,
|
||||
static inline void
|
||||
gsk_gl_driver_slice_texture (GskGLDriver *self,
|
||||
GdkTexture *texture,
|
||||
int min_filter,
|
||||
int mag_filter,
|
||||
guint min_cols,
|
||||
guint min_rows,
|
||||
GskGLTextureSlice **out_slices,
|
||||
guint *out_n_slices)
|
||||
{
|
||||
@ -235,12 +243,15 @@ gsk_gl_driver_slice_texture (GskGLDriver *self,
|
||||
|
||||
if ((t = gdk_texture_get_render_data (texture, self)))
|
||||
{
|
||||
*out_slices = t->slices;
|
||||
*out_n_slices = t->n_slices;
|
||||
return;
|
||||
if (min_cols == 0 && min_rows == 0)
|
||||
{
|
||||
*out_slices = t->slices;
|
||||
*out_n_slices = t->n_slices;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
gsk_gl_driver_add_texture_slices (self, texture, out_slices, out_n_slices);
|
||||
gsk_gl_driver_add_texture_slices (self, texture, min_filter, mag_filter, min_cols, min_rows, out_slices, out_n_slices);
|
||||
}
|
||||
|
||||
G_END_DECLS
|
||||
|
@ -3443,9 +3443,13 @@ gsk_gl_render_job_visit_gl_shader_node (GskGLRenderJob *job,
|
||||
static void
|
||||
gsk_gl_render_job_upload_texture (GskGLRenderJob *job,
|
||||
GdkTexture *texture,
|
||||
int min_filter,
|
||||
int mag_filter,
|
||||
GskGLRenderOffscreen *offscreen)
|
||||
{
|
||||
if (gsk_gl_texture_library_can_cache ((GskGLTextureLibrary *)job->driver->icons_library,
|
||||
if (min_filter == GL_LINEAR &&
|
||||
mag_filter == GL_LINEAR &&
|
||||
gsk_gl_texture_library_can_cache ((GskGLTextureLibrary *)job->driver->icons_library,
|
||||
texture->width,
|
||||
texture->height) &&
|
||||
!GDK_IS_GL_TEXTURE (texture))
|
||||
@ -3458,16 +3462,16 @@ gsk_gl_render_job_upload_texture (GskGLRenderJob *job,
|
||||
}
|
||||
else
|
||||
{
|
||||
offscreen->texture_id = gsk_gl_driver_load_texture (job->driver, texture, GL_LINEAR, GL_LINEAR);
|
||||
offscreen->texture_id = gsk_gl_driver_load_texture (job->driver, texture, min_filter, mag_filter);
|
||||
init_full_texture_region (offscreen);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_gl_render_job_visit_texture_node (GskGLRenderJob *job,
|
||||
const GskRenderNode *node)
|
||||
gsk_gl_render_job_visit_texture (GskGLRenderJob *job,
|
||||
GdkTexture *texture,
|
||||
const graphene_rect_t *bounds)
|
||||
{
|
||||
GdkTexture *texture = gsk_texture_node_get_texture (node);
|
||||
int max_texture_size = job->command_queue->max_texture_size;
|
||||
|
||||
if G_LIKELY (texture->width <= max_texture_size &&
|
||||
@ -3475,7 +3479,7 @@ gsk_gl_render_job_visit_texture_node (GskGLRenderJob *job,
|
||||
{
|
||||
GskGLRenderOffscreen offscreen = {0};
|
||||
|
||||
gsk_gl_render_job_upload_texture (job, texture, &offscreen);
|
||||
gsk_gl_render_job_upload_texture (job, texture, GL_LINEAR, GL_LINEAR, &offscreen);
|
||||
|
||||
g_assert (offscreen.texture_id);
|
||||
g_assert (offscreen.was_offscreen == FALSE);
|
||||
@ -3486,21 +3490,21 @@ gsk_gl_render_job_visit_texture_node (GskGLRenderJob *job,
|
||||
GL_TEXTURE_2D,
|
||||
GL_TEXTURE0,
|
||||
offscreen.texture_id);
|
||||
gsk_gl_render_job_draw_offscreen (job, &node->bounds, &offscreen);
|
||||
gsk_gl_render_job_draw_offscreen (job, bounds, &offscreen);
|
||||
gsk_gl_render_job_end_draw (job);
|
||||
}
|
||||
else
|
||||
{
|
||||
float min_x = job->offset_x + node->bounds.origin.x;
|
||||
float min_y = job->offset_y + node->bounds.origin.y;
|
||||
float max_x = min_x + node->bounds.size.width;
|
||||
float max_y = min_y + node->bounds.size.height;
|
||||
float min_x = job->offset_x + bounds->origin.x;
|
||||
float min_y = job->offset_y + bounds->origin.y;
|
||||
float max_x = min_x + bounds->size.width;
|
||||
float max_y = min_y + bounds->size.height;
|
||||
float scale_x = (max_x - min_x) / texture->width;
|
||||
float scale_y = (max_y - min_y) / texture->height;
|
||||
GskGLTextureSlice *slices = NULL;
|
||||
guint n_slices = 0;
|
||||
|
||||
gsk_gl_driver_slice_texture (job->driver, texture, &slices, &n_slices);
|
||||
gsk_gl_driver_slice_texture (job->driver, texture, GL_NEAREST, GL_NEAREST, 0, 0, &slices, &n_slices);
|
||||
|
||||
g_assert (slices != NULL);
|
||||
g_assert (n_slices > 0);
|
||||
@ -3535,6 +3539,151 @@ gsk_gl_render_job_visit_texture_node (GskGLRenderJob *job,
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_gl_render_job_visit_texture_node (GskGLRenderJob *job,
|
||||
const GskRenderNode *node)
|
||||
{
|
||||
GdkTexture *texture = gsk_texture_node_get_texture (node);
|
||||
const graphene_rect_t *bounds = &node->bounds;
|
||||
|
||||
gsk_gl_render_job_visit_texture (job, texture, bounds);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_gl_render_job_visit_texture_scale_node (GskGLRenderJob *job,
|
||||
const GskRenderNode *node)
|
||||
{
|
||||
GdkTexture *texture = gsk_texture_scale_node_get_texture (node);
|
||||
const graphene_rect_t *bounds = &node->bounds;
|
||||
GskScalingFilter scaling_filter = gsk_texture_scale_node_get_filter (node);
|
||||
int min_filters[] = { GL_LINEAR, GL_NEAREST, GL_LINEAR_MIPMAP_LINEAR };
|
||||
int mag_filters[] = { GL_LINEAR, GL_NEAREST, GL_LINEAR };
|
||||
int min_filter = min_filters[scaling_filter];
|
||||
int mag_filter = mag_filters[scaling_filter];
|
||||
int max_texture_size = job->command_queue->max_texture_size;
|
||||
|
||||
if (scaling_filter == GSK_SCALING_FILTER_LINEAR)
|
||||
{
|
||||
gsk_gl_render_job_visit_texture (job, texture, bounds);
|
||||
return;
|
||||
}
|
||||
|
||||
if G_LIKELY (texture->width <= max_texture_size &&
|
||||
texture->height <= max_texture_size)
|
||||
{
|
||||
GskGLRenderTarget *render_target;
|
||||
GskGLRenderOffscreen offscreen = {0};
|
||||
graphene_rect_t viewport;
|
||||
graphene_rect_t prev_viewport;
|
||||
graphene_matrix_t prev_projection;
|
||||
float prev_alpha;
|
||||
guint prev_fbo;
|
||||
guint texture_id;
|
||||
|
||||
viewport = GRAPHENE_RECT_INIT (0, 0,
|
||||
bounds->size.width,
|
||||
bounds->size.height);
|
||||
|
||||
if (!gsk_gl_driver_create_render_target (job->driver,
|
||||
(int) ceilf (viewport.size.width),
|
||||
(int) ceilf (viewport.size.height),
|
||||
get_target_format (job, node),
|
||||
GL_LINEAR, GL_LINEAR,
|
||||
&render_target))
|
||||
{
|
||||
/* viewport is too big, slice the texture and try again */
|
||||
goto slice;
|
||||
}
|
||||
|
||||
gsk_gl_render_job_upload_texture (job, texture, min_filter, mag_filter, &offscreen);
|
||||
|
||||
g_assert (offscreen.texture_id);
|
||||
g_assert (offscreen.was_offscreen == FALSE);
|
||||
|
||||
gsk_gl_render_job_set_viewport (job, &viewport, &prev_viewport);
|
||||
gsk_gl_render_job_set_projection_from_rect (job, &viewport, &prev_projection);
|
||||
gsk_gl_render_job_set_modelview (job, NULL);
|
||||
prev_alpha = gsk_gl_render_job_set_alpha (job, 1.0f);
|
||||
gsk_gl_render_job_push_clip (job, &GSK_ROUNDED_RECT_INIT_FROM_RECT (viewport));
|
||||
|
||||
prev_fbo = gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id);
|
||||
gsk_gl_command_queue_clear (job->command_queue, 0, &viewport);
|
||||
|
||||
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit));
|
||||
gsk_gl_program_set_uniform_texture (job->current_program,
|
||||
UNIFORM_SHARED_SOURCE, 0,
|
||||
GL_TEXTURE_2D,
|
||||
GL_TEXTURE0,
|
||||
offscreen.texture_id);
|
||||
gsk_gl_render_job_draw_offscreen (job, &viewport, &offscreen);
|
||||
gsk_gl_render_job_end_draw (job);
|
||||
|
||||
gsk_gl_render_job_pop_clip (job);
|
||||
gsk_gl_render_job_pop_modelview (job);
|
||||
gsk_gl_render_job_set_viewport (job, &prev_viewport, NULL);
|
||||
gsk_gl_render_job_set_projection (job, &prev_projection);
|
||||
gsk_gl_render_job_set_alpha (job, prev_alpha);
|
||||
gsk_gl_command_queue_bind_framebuffer (job->command_queue, prev_fbo);
|
||||
|
||||
texture_id = gsk_gl_driver_release_render_target (job->driver, render_target, FALSE);
|
||||
|
||||
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit));
|
||||
gsk_gl_program_set_uniform_texture (job->current_program,
|
||||
UNIFORM_SHARED_SOURCE, 0,
|
||||
GL_TEXTURE_2D,
|
||||
GL_TEXTURE0,
|
||||
texture_id);
|
||||
gsk_gl_render_job_draw_offscreen_rect (job, bounds);
|
||||
gsk_gl_render_job_end_draw (job);
|
||||
}
|
||||
else
|
||||
{
|
||||
slice:
|
||||
float min_x = bounds->origin.x;
|
||||
float min_y = bounds->origin.y;
|
||||
float max_x = min_x + bounds->size.width;
|
||||
float max_y = min_y + bounds->size.height;
|
||||
float scale_x = (max_x - min_x) / texture->width;
|
||||
float scale_y = (max_y - min_y) / texture->height;
|
||||
GskGLTextureSlice *slices = NULL;
|
||||
guint n_slices = 0;
|
||||
GdkGLContext *context = gsk_gl_driver_get_context (job->driver);
|
||||
guint rows, cols;
|
||||
|
||||
/* Slice enough that neither the original texture nor the scaled texture
|
||||
* exceed the texture size limit
|
||||
*/
|
||||
cols = (int)(MAX (bounds->size.width, texture->width) / (max_texture_size / 4)) + 1;
|
||||
rows = (int)(MAX (bounds->size.height, texture->height) / (max_texture_size / 4)) + 1;
|
||||
|
||||
gsk_gl_driver_slice_texture (job->driver, texture, GL_NEAREST, GL_NEAREST, cols, rows, &slices, &n_slices);
|
||||
|
||||
g_assert (slices != NULL);
|
||||
g_assert (n_slices > 0);
|
||||
|
||||
for (guint i = 0; i < n_slices; i ++)
|
||||
{
|
||||
const GskGLTextureSlice *slice = &slices[i];
|
||||
float x1, x2, y1, y2;
|
||||
GdkTexture *sub_texture;
|
||||
GskRenderNode *sub_node;
|
||||
|
||||
x1 = min_x + (scale_x * slice->rect.x);
|
||||
x2 = x1 + (slice->rect.width * scale_x);
|
||||
y1 = min_y + (scale_y * slice->rect.y);
|
||||
y2 = y1 + (slice->rect.height * scale_y);
|
||||
|
||||
sub_texture = gdk_gl_texture_new (context, slice->texture_id, slice->rect.width, slice->rect.height, NULL, NULL);
|
||||
|
||||
sub_node = gsk_texture_scale_node_new (sub_texture, &GRAPHENE_RECT_INIT (x1, y1, x2 - x1, y2 - y1), scaling_filter);
|
||||
|
||||
gsk_gl_render_job_visit_node (job, sub_node);
|
||||
gsk_render_node_unref (sub_node);
|
||||
g_object_unref (sub_texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_gl_render_job_visit_repeat_node (GskGLRenderJob *job,
|
||||
const GskRenderNode *node)
|
||||
@ -3762,6 +3911,10 @@ gsk_gl_render_job_visit_node (GskGLRenderJob *job,
|
||||
gsk_gl_render_job_visit_texture_node (job, node);
|
||||
break;
|
||||
|
||||
case GSK_TEXTURE_SCALE_NODE:
|
||||
gsk_gl_render_job_visit_texture_scale_node (job, node);
|
||||
break;
|
||||
|
||||
case GSK_TRANSFORM_NODE:
|
||||
gsk_gl_render_job_visit_transform_node (job, node);
|
||||
break;
|
||||
@ -3808,7 +3961,7 @@ gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob *job,
|
||||
offscreen->force_offscreen == FALSE)
|
||||
{
|
||||
GdkTexture *texture = gsk_texture_node_get_texture (node);
|
||||
gsk_gl_render_job_upload_texture (job, texture, offscreen);
|
||||
gsk_gl_render_job_upload_texture (job, texture, GL_LINEAR, GL_LINEAR, offscreen);
|
||||
g_assert (offscreen->was_offscreen == FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -35,6 +35,7 @@
|
||||
* @GSK_CONIC_GRADIENT_NODE: A node drawing a conic gradient
|
||||
* @GSK_BORDER_NODE: A node stroking a border around an area
|
||||
* @GSK_TEXTURE_NODE: A node drawing a `GdkTexture`
|
||||
* @GSK_TEXTURE_SCALE_NODE: A node drawing a `GdkTexture` scaled and filtered
|
||||
* @GSK_INSET_SHADOW_NODE: A node drawing an inset shadow
|
||||
* @GSK_OUTSET_SHADOW_NODE: A node drawing an outset shadow
|
||||
* @GSK_TRANSFORM_NODE: A node that renders its child after applying a matrix transform
|
||||
@ -65,6 +66,7 @@ typedef enum {
|
||||
GSK_CONIC_GRADIENT_NODE,
|
||||
GSK_BORDER_NODE,
|
||||
GSK_TEXTURE_NODE,
|
||||
GSK_TEXTURE_SCALE_NODE,
|
||||
GSK_INSET_SHADOW_NODE,
|
||||
GSK_OUTSET_SHADOW_NODE,
|
||||
GSK_TRANSFORM_NODE,
|
||||
|
@ -142,6 +142,7 @@ GskRenderNode * gsk_render_node_deserialize (GBytes
|
||||
#define GSK_TYPE_DEBUG_NODE (gsk_debug_node_get_type())
|
||||
#define GSK_TYPE_COLOR_NODE (gsk_color_node_get_type())
|
||||
#define GSK_TYPE_TEXTURE_NODE (gsk_texture_node_get_type())
|
||||
#define GSK_TYPE_TEXTURE_SCALE_NODE (gsk_texture_scale_node_get_type())
|
||||
#define GSK_TYPE_LINEAR_GRADIENT_NODE (gsk_linear_gradient_node_get_type())
|
||||
#define GSK_TYPE_REPEATING_LINEAR_GRADIENT_NODE (gsk_repeating_linear_gradient_node_get_type())
|
||||
#define GSK_TYPE_RADIAL_GRADIENT_NODE (gsk_radial_gradient_node_get_type())
|
||||
@ -168,6 +169,7 @@ GskRenderNode * gsk_render_node_deserialize (GBytes
|
||||
typedef struct _GskDebugNode GskDebugNode;
|
||||
typedef struct _GskColorNode GskColorNode;
|
||||
typedef struct _GskTextureNode GskTextureNode;
|
||||
typedef struct _GskTextureScaleNode GskTextureScaleNode;
|
||||
typedef struct _GskLinearGradientNode GskLinearGradientNode;
|
||||
typedef struct _GskRepeatingLinearGradientNode GskRepeatingLinearGradientNode;
|
||||
typedef struct _GskRadialGradientNode GskRadialGradientNode;
|
||||
@ -217,6 +219,17 @@ GskRenderNode * gsk_texture_node_new (GdkTexture
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GdkTexture * gsk_texture_node_get_texture (const GskRenderNode *node) G_GNUC_PURE;
|
||||
|
||||
GDK_AVAILABLE_IN_4_10
|
||||
GType gsk_texture_scale_node_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_4_10
|
||||
GskRenderNode * gsk_texture_scale_node_new (GdkTexture *texture,
|
||||
const graphene_rect_t *bounds,
|
||||
GskScalingFilter filter);
|
||||
GDK_AVAILABLE_IN_4_10
|
||||
GdkTexture * gsk_texture_scale_node_get_texture (const GskRenderNode *node) G_GNUC_PURE;
|
||||
GDK_AVAILABLE_IN_4_10
|
||||
GskScalingFilter gsk_texture_scale_node_get_filter (const GskRenderNode *node) G_GNUC_PURE;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gsk_linear_gradient_node_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
|
@ -1581,6 +1581,191 @@ gsk_texture_node_new (GdkTexture *texture,
|
||||
return node;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ GSK_TEXTURE_SCALE_NODE */
|
||||
|
||||
/**
|
||||
* GskTextureScaleNode:
|
||||
*
|
||||
* A render node for a `GdkTexture`.
|
||||
*/
|
||||
struct _GskTextureScaleNode
|
||||
{
|
||||
GskRenderNode render_node;
|
||||
|
||||
GdkTexture *texture;
|
||||
GskScalingFilter filter;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_texture_scale_node_finalize (GskRenderNode *node)
|
||||
{
|
||||
GskTextureScaleNode *self = (GskTextureScaleNode *) node;
|
||||
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_TEXTURE_SCALE_NODE));
|
||||
|
||||
g_clear_object (&self->texture);
|
||||
|
||||
parent_class->finalize (node);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_texture_scale_node_draw (GskRenderNode *node,
|
||||
cairo_t *cr)
|
||||
{
|
||||
GskTextureScaleNode *self = (GskTextureScaleNode *) node;
|
||||
cairo_surface_t *surface;
|
||||
cairo_pattern_t *pattern;
|
||||
cairo_matrix_t matrix;
|
||||
cairo_filter_t filters[] = {
|
||||
CAIRO_FILTER_BILINEAR,
|
||||
CAIRO_FILTER_NEAREST,
|
||||
CAIRO_FILTER_GOOD,
|
||||
};
|
||||
cairo_t *cr2;
|
||||
cairo_surface_t *surface2;
|
||||
|
||||
surface2 = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
||||
(int) ceilf (node->bounds.size.width),
|
||||
(int) ceilf (node->bounds.size.height));
|
||||
cr2 = cairo_create (surface2);
|
||||
|
||||
cairo_set_source_rgba (cr2, 0, 0, 0, 0);
|
||||
cairo_paint (cr2);
|
||||
|
||||
surface = gdk_texture_download_surface (self->texture);
|
||||
pattern = cairo_pattern_create_for_surface (surface);
|
||||
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
|
||||
|
||||
cairo_matrix_init_scale (&matrix,
|
||||
gdk_texture_get_width (self->texture) / node->bounds.size.width,
|
||||
gdk_texture_get_height (self->texture) / node->bounds.size.height);
|
||||
cairo_pattern_set_matrix (pattern, &matrix);
|
||||
cairo_pattern_set_filter (pattern, filters[self->filter]);
|
||||
|
||||
cairo_set_source (cr2, pattern);
|
||||
cairo_pattern_destroy (pattern);
|
||||
cairo_surface_destroy (surface);
|
||||
|
||||
cairo_rectangle (cr2, 0, 0, node->bounds.size.width, node->bounds.size.height);
|
||||
cairo_fill (cr2);
|
||||
|
||||
cairo_destroy (cr2);
|
||||
|
||||
cairo_save (cr);
|
||||
|
||||
pattern = cairo_pattern_create_for_surface (surface2);
|
||||
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
|
||||
|
||||
cairo_matrix_init_identity (&matrix);
|
||||
cairo_matrix_translate (&matrix,
|
||||
-node->bounds.origin.x,
|
||||
-node->bounds.origin.y);
|
||||
cairo_pattern_set_matrix (pattern, &matrix);
|
||||
cairo_set_source (cr, pattern);
|
||||
cairo_pattern_destroy (pattern);
|
||||
cairo_surface_destroy (surface2);
|
||||
|
||||
gsk_cairo_rectangle (cr, &node->bounds);
|
||||
cairo_fill (cr);
|
||||
|
||||
cairo_restore (cr);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_texture_scale_node_diff (GskRenderNode *node1,
|
||||
GskRenderNode *node2,
|
||||
cairo_region_t *region)
|
||||
{
|
||||
GskTextureScaleNode *self1 = (GskTextureScaleNode *) node1;
|
||||
GskTextureScaleNode *self2 = (GskTextureScaleNode *) node2;
|
||||
|
||||
if (graphene_rect_equal (&node1->bounds, &node2->bounds) &&
|
||||
self1->texture == self2->texture &&
|
||||
self1->filter == self2->filter)
|
||||
return;
|
||||
|
||||
gsk_render_node_diff_impossible (node1, node2, region);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_texture_scale_node_get_texture:
|
||||
* @node: (type GskTextureNode): a `GskRenderNode` of type %GSK_TEXTURE_SCALE_NODE
|
||||
*
|
||||
* Retrieves the `GdkTexture` used when creating this `GskRenderNode`.
|
||||
*
|
||||
* Returns: (transfer none): the `GdkTexture`
|
||||
*
|
||||
* Since: 4.10
|
||||
*/
|
||||
GdkTexture *
|
||||
gsk_texture_scale_node_get_texture (const GskRenderNode *node)
|
||||
{
|
||||
const GskTextureScaleNode *self = (const GskTextureScaleNode *) node;
|
||||
|
||||
return self->texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_texture_scale_node_get_filter:
|
||||
* @node: (type GskTextureNode): a `GskRenderNode` of type %GSK_TEXTURE_SCALE_NODE
|
||||
*
|
||||
* Retrieves the `GskScalingFilter` used when creating this `GskRenderNode`.
|
||||
*
|
||||
* Returns: (transfer none): the `GskScalingFilter`
|
||||
*
|
||||
* Since: 4.10
|
||||
*/
|
||||
GskScalingFilter
|
||||
gsk_texture_scale_node_get_filter (const GskRenderNode *node)
|
||||
{
|
||||
const GskTextureScaleNode *self = (const GskTextureScaleNode *) node;
|
||||
|
||||
return self->filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_texture_scale_node_new:
|
||||
* @texture: the texture to scale
|
||||
* @bounds: the size of the texture to scale to
|
||||
* @filter: how to scale the texture
|
||||
*
|
||||
* Creates a node that scales the texture to the size given by the
|
||||
* bounds and the filter and then places it at the bounds' position.
|
||||
*
|
||||
* This node is intended for tight control over scaling applied
|
||||
* to a texture, such as in image editors and requires the
|
||||
* application to be aware of the whole render tree as further
|
||||
* transforms may be applied that conflict with the desired effect
|
||||
* of this node.
|
||||
*
|
||||
* Returns: (transfer full) (type GskTextureScaleNode): A new `GskRenderNode`
|
||||
*
|
||||
* Since: 4.10
|
||||
*/
|
||||
GskRenderNode *
|
||||
gsk_texture_scale_node_new (GdkTexture *texture,
|
||||
const graphene_rect_t *bounds,
|
||||
GskScalingFilter filter)
|
||||
{
|
||||
GskTextureScaleNode *self;
|
||||
GskRenderNode *node;
|
||||
|
||||
g_return_val_if_fail (GDK_IS_TEXTURE (texture), NULL);
|
||||
g_return_val_if_fail (bounds != NULL, NULL);
|
||||
|
||||
self = gsk_render_node_alloc (GSK_TEXTURE_SCALE_NODE);
|
||||
node = (GskRenderNode *) self;
|
||||
node->offscreen_for_opacity = FALSE;
|
||||
|
||||
self->texture = g_object_ref (texture);
|
||||
graphene_rect_init_from_rect (&node->bounds, bounds);
|
||||
self->filter = filter;
|
||||
|
||||
node->prefers_high_depth = gdk_memory_format_prefers_high_depth (gdk_texture_get_format (texture));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
/* {{{ GSK_INSET_SHADOW_NODE */
|
||||
|
||||
@ -5357,6 +5542,7 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_repeating_radial_gradient_node, GSK_REPEATING_R
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_conic_gradient_node, GSK_CONIC_GRADIENT_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_border_node, GSK_BORDER_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_texture_node, GSK_TEXTURE_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_texture_scale_node, GSK_TEXTURE_SCALE_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_inset_shadow_node, GSK_INSET_SHADOW_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_outset_shadow_node, GSK_OUTSET_SHADOW_NODE)
|
||||
GSK_DEFINE_RENDER_NODE_TYPE (gsk_transform_node, GSK_TRANSFORM_NODE)
|
||||
@ -5536,6 +5722,22 @@ gsk_render_node_init_types_once (void)
|
||||
gsk_render_node_types[GSK_TEXTURE_NODE] = node_type;
|
||||
}
|
||||
|
||||
{
|
||||
const GskRenderNodeTypeInfo node_info =
|
||||
{
|
||||
GSK_TEXTURE_SCALE_NODE,
|
||||
sizeof (GskTextureScaleNode),
|
||||
NULL,
|
||||
gsk_texture_scale_node_finalize,
|
||||
gsk_texture_scale_node_draw,
|
||||
NULL,
|
||||
gsk_texture_scale_node_diff,
|
||||
};
|
||||
|
||||
GType node_type = gsk_render_node_type_register_static (I_("GskTextureScaleNode"), &node_info);
|
||||
gsk_render_node_types[GSK_TEXTURE_SCALE_NODE] = node_type;
|
||||
}
|
||||
|
||||
{
|
||||
const GskRenderNodeTypeInfo node_info =
|
||||
{
|
||||
|
@ -600,6 +600,32 @@ clear_shadows (gpointer inout_shadows)
|
||||
g_array_set_size (*(GArray **) inout_shadows, 0);
|
||||
}
|
||||
|
||||
static const struct
|
||||
{
|
||||
GskScalingFilter filter;
|
||||
const char *name;
|
||||
} scaling_filters[] = {
|
||||
{ GSK_SCALING_FILTER_LINEAR, "linear" },
|
||||
{ GSK_SCALING_FILTER_NEAREST, "nearest" },
|
||||
{ GSK_SCALING_FILTER_TRILINEAR, "trilinear" },
|
||||
};
|
||||
|
||||
static gboolean
|
||||
parse_scaling_filter (GtkCssParser *parser,
|
||||
gpointer out_filter)
|
||||
{
|
||||
for (unsigned int i = 0; i < G_N_ELEMENTS (scaling_filters); i++)
|
||||
{
|
||||
if (gtk_css_parser_try_ident (parser, scaling_filters[i].name))
|
||||
{
|
||||
*(GskScalingFilter *) out_filter = scaling_filters[i].filter;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static const struct
|
||||
{
|
||||
GskBlendMode mode;
|
||||
@ -1389,6 +1415,30 @@ parse_texture_node (GtkCssParser *parser)
|
||||
return node;
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
parse_texture_scale_node (GtkCssParser *parser)
|
||||
{
|
||||
graphene_rect_t bounds = GRAPHENE_RECT_INIT (0, 0, 50, 50);
|
||||
GdkTexture *texture = NULL;
|
||||
GskScalingFilter filter = GSK_SCALING_FILTER_LINEAR;
|
||||
const Declaration declarations[] = {
|
||||
{ "bounds", parse_rect, NULL, &bounds },
|
||||
{ "texture", parse_texture, clear_texture, &texture },
|
||||
{ "filter", parse_scaling_filter, NULL, &filter }
|
||||
};
|
||||
GskRenderNode *node;
|
||||
|
||||
parse_declarations (parser, declarations, G_N_ELEMENTS (declarations));
|
||||
|
||||
if (texture == NULL)
|
||||
texture = create_default_texture ();
|
||||
|
||||
node = gsk_texture_scale_node_new (texture, &bounds, filter);
|
||||
g_object_unref (texture);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static GskRenderNode *
|
||||
parse_cairo_node (GtkCssParser *parser)
|
||||
{
|
||||
@ -1861,6 +1911,7 @@ parse_node (GtkCssParser *parser,
|
||||
{ "shadow", parse_shadow_node },
|
||||
{ "text", parse_text_node },
|
||||
{ "texture", parse_texture_node },
|
||||
{ "texture-scale", parse_texture_scale_node },
|
||||
{ "transform", parse_transform_node },
|
||||
{ "glshader", parse_glshader_node },
|
||||
};
|
||||
@ -2757,6 +2808,73 @@ render_node_print (Printer *p,
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_TEXTURE_SCALE_NODE:
|
||||
{
|
||||
GdkTexture *texture = gsk_texture_scale_node_get_texture (node);
|
||||
GskScalingFilter filter = gsk_texture_scale_node_get_filter (node);
|
||||
GBytes *bytes;
|
||||
|
||||
start_node (p, "texture-scale");
|
||||
append_rect_param (p, "bounds", &node->bounds);
|
||||
|
||||
if (filter != GSK_SCALING_FILTER_LINEAR)
|
||||
{
|
||||
_indent (p);
|
||||
for (unsigned int i = 0; i < G_N_ELEMENTS (scaling_filters); i++)
|
||||
{
|
||||
if (scaling_filters[i].filter == filter)
|
||||
{
|
||||
g_string_append_printf (p->str, "filter: %s;\n", scaling_filters[i].name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_indent (p);
|
||||
|
||||
switch (gdk_texture_get_format (texture))
|
||||
{
|
||||
case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED:
|
||||
case GDK_MEMORY_A8R8G8B8_PREMULTIPLIED:
|
||||
case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED:
|
||||
case GDK_MEMORY_B8G8R8A8:
|
||||
case GDK_MEMORY_A8R8G8B8:
|
||||
case GDK_MEMORY_R8G8B8A8:
|
||||
case GDK_MEMORY_A8B8G8R8:
|
||||
case GDK_MEMORY_R8G8B8:
|
||||
case GDK_MEMORY_B8G8R8:
|
||||
case GDK_MEMORY_R16G16B16:
|
||||
case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED:
|
||||
case GDK_MEMORY_R16G16B16A16:
|
||||
bytes = gdk_texture_save_to_png_bytes (texture);
|
||||
g_string_append (p->str, "texture: url(\"data:image/png;base64,");
|
||||
break;
|
||||
|
||||
case GDK_MEMORY_R16G16B16_FLOAT:
|
||||
case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
|
||||
case GDK_MEMORY_R16G16B16A16_FLOAT:
|
||||
case GDK_MEMORY_R32G32B32_FLOAT:
|
||||
case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
|
||||
case GDK_MEMORY_R32G32B32A32_FLOAT:
|
||||
bytes = gdk_texture_save_to_tiff_bytes (texture);
|
||||
g_string_append (p->str, "texture: url(\"data:image/tiff;base64,");
|
||||
break;
|
||||
|
||||
case GDK_MEMORY_N_FORMATS:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
b64 = base64_encode_with_linebreaks (g_bytes_get_data (bytes, NULL),
|
||||
g_bytes_get_size (bytes));
|
||||
append_escaping_newlines (p->str, b64);
|
||||
g_free (b64);
|
||||
g_string_append (p->str, "\");\n");
|
||||
end_node (p);
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_TEXT_NODE:
|
||||
{
|
||||
const graphene_point_t *offset = gsk_text_node_get_offset (node);
|
||||
|
@ -440,6 +440,9 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
|
||||
g_array_append_val (self->render_ops, op);
|
||||
return;
|
||||
|
||||
case GSK_TEXTURE_SCALE_NODE:
|
||||
goto fallback;
|
||||
|
||||
case GSK_COLOR_NODE:
|
||||
if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
|
||||
pipeline_type = GSK_VULKAN_PIPELINE_COLOR;
|
||||
|
@ -1918,6 +1918,10 @@ gtk_snapshot_append_cairo (GtkSnapshot *snapshot,
|
||||
* Creates a new render node drawing the @texture
|
||||
* into the given @bounds and appends it to the
|
||||
* current render node of @snapshot.
|
||||
*
|
||||
* If the texture needs to be scaled to fill @bounds,
|
||||
* linear filtering is used. See [method@Gtk.Snapshot.append_scaled_texture]
|
||||
* if you need other filtering, such as nearest-neighbour.
|
||||
*/
|
||||
void
|
||||
gtk_snapshot_append_texture (GtkSnapshot *snapshot,
|
||||
@ -1939,6 +1943,44 @@ gtk_snapshot_append_texture (GtkSnapshot *snapshot,
|
||||
gtk_snapshot_append_node_internal (snapshot, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_snapshot_append_scaled_texture:
|
||||
* @snapshot: a `GtkSnapshot`
|
||||
* @texture: the texture to render
|
||||
* @filter: the filter to use
|
||||
* @bounds: the bounds for the new node
|
||||
*
|
||||
* Creates a new render node drawing the @texture
|
||||
* into the given @bounds and appends it to the
|
||||
* current render node of @snapshot.
|
||||
*
|
||||
* In contrast to [method@Gtk.Snapshot.append_texture],
|
||||
* this function provides control about how the filter
|
||||
* that is used when scaling.
|
||||
*
|
||||
* Since: 4.10
|
||||
*/
|
||||
void
|
||||
gtk_snapshot_append_scaled_texture (GtkSnapshot *snapshot,
|
||||
GdkTexture *texture,
|
||||
GskScalingFilter filter,
|
||||
const graphene_rect_t *bounds)
|
||||
{
|
||||
GskRenderNode *node;
|
||||
graphene_rect_t real_bounds;
|
||||
float scale_x, scale_y, dx, dy;
|
||||
|
||||
g_return_if_fail (snapshot != NULL);
|
||||
g_return_if_fail (GDK_IS_TEXTURE (texture));
|
||||
g_return_if_fail (bounds != NULL);
|
||||
|
||||
gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
|
||||
gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &real_bounds);
|
||||
node = gsk_texture_scale_node_new (texture, &real_bounds, filter);
|
||||
|
||||
gtk_snapshot_append_node_internal (snapshot, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_snapshot_append_color:
|
||||
* @snapshot: a `GtkSnapshot`
|
||||
|
@ -152,6 +152,11 @@ GDK_AVAILABLE_IN_ALL
|
||||
void gtk_snapshot_append_texture (GtkSnapshot *snapshot,
|
||||
GdkTexture *texture,
|
||||
const graphene_rect_t *bounds);
|
||||
GDK_AVAILABLE_IN_4_10
|
||||
void gtk_snapshot_append_scaled_texture (GtkSnapshot *snapshot,
|
||||
GdkTexture *texture,
|
||||
GskScalingFilter filter,
|
||||
const graphene_rect_t *bounds);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_snapshot_append_color (GtkSnapshot *snapshot,
|
||||
const GdkRGBA *color,
|
||||
|
@ -264,6 +264,7 @@ create_list_model_for_render_node (GskRenderNode *node)
|
||||
case GSK_CAIRO_NODE:
|
||||
case GSK_TEXT_NODE:
|
||||
case GSK_TEXTURE_NODE:
|
||||
case GSK_TEXTURE_SCALE_NODE:
|
||||
case GSK_COLOR_NODE:
|
||||
case GSK_LINEAR_GRADIENT_NODE:
|
||||
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
|
||||
@ -402,6 +403,8 @@ node_type_name (GskRenderNodeType type)
|
||||
return "Border";
|
||||
case GSK_TEXTURE_NODE:
|
||||
return "Texture";
|
||||
case GSK_TEXTURE_SCALE_NODE:
|
||||
return "Scaled Texture";
|
||||
case GSK_INSET_SHADOW_NODE:
|
||||
return "Inset Shadow";
|
||||
case GSK_OUTSET_SHADOW_NODE:
|
||||
@ -476,6 +479,11 @@ node_name (GskRenderNode *node)
|
||||
GdkTexture *texture = gsk_texture_node_get_texture (node);
|
||||
return g_strdup_printf ("%dx%d Texture", gdk_texture_get_width (texture), gdk_texture_get_height (texture));
|
||||
}
|
||||
case GSK_TEXTURE_SCALE_NODE:
|
||||
{
|
||||
GdkTexture *texture = gsk_texture_node_get_texture (node);
|
||||
return g_strdup_printf ("%dx%d Texture, Filter %d", gdk_texture_get_width (texture), gdk_texture_get_height (texture), gsk_texture_scale_node_get_filter (node));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -933,6 +941,18 @@ populate_render_node_properties (GListStore *store,
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_TEXTURE_SCALE_NODE:
|
||||
{
|
||||
GdkTexture *texture = g_object_ref (gsk_texture_scale_node_get_texture (node));
|
||||
GskScalingFilter filter = gsk_texture_scale_node_get_filter (node);
|
||||
g_list_store_append (store, object_property_new ("Texture", "", texture));
|
||||
|
||||
tmp = g_enum_to_string (GSK_TYPE_SCALING_FILTER, filter);
|
||||
add_text_row (store, "Filter", tmp);
|
||||
g_free (tmp);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_COLOR_NODE:
|
||||
add_color_row (store, "Color", gsk_color_node_get_color (node));
|
||||
break;
|
||||
|
7
testsuite/gsk/compare/scaled-texture.node
Normal file
7
testsuite/gsk/compare/scaled-texture.node
Normal file
@ -0,0 +1,7 @@
|
||||
container {
|
||||
texture-scale {
|
||||
texture: url("some-10x10-image-with-content.png");
|
||||
bounds: 0 0 100 100;
|
||||
filter: nearest;
|
||||
}
|
||||
}
|
BIN
testsuite/gsk/compare/scaled-texture.png
Normal file
BIN
testsuite/gsk/compare/scaled-texture.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 334 B |
@ -75,6 +75,7 @@ compare_render_tests = [
|
||||
'transform-in-transform',
|
||||
'transform-in-transform-in-transform',
|
||||
'rounded-clip-in-clip-3d', # not really 3d, but cairo fails it
|
||||
'scaled-texture',
|
||||
]
|
||||
|
||||
# these are too sensitive to differences in the renderers
|
||||
|
Loading…
Reference in New Issue
Block a user