Merge branch 'texture-diff-against-distant-ancestors' into 'main'
texture: Enable texture diff against arbitrarily distant ancestors See merge request GNOME/gtk!6644
This commit is contained in:
140
gdk/gdktexture.c
140
gdk/gdktexture.c
@@ -68,6 +68,33 @@ enum {
|
||||
|
||||
static GParamSpec *properties[N_PROPS];
|
||||
|
||||
static GdkTextureChain *
|
||||
gdk_texture_chain_new (void)
|
||||
{
|
||||
GdkTextureChain *chain = g_new0 (GdkTextureChain, 1);
|
||||
|
||||
g_atomic_ref_count_init (&chain->ref_count);
|
||||
g_mutex_init (&chain->lock);
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_texture_chain_ref (GdkTextureChain *chain)
|
||||
{
|
||||
g_atomic_ref_count_inc (&chain->ref_count);
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_texture_chain_unref (GdkTextureChain *chain)
|
||||
{
|
||||
if (g_atomic_ref_count_dec (&chain->ref_count))
|
||||
{
|
||||
g_mutex_clear (&chain->lock);
|
||||
g_free (chain);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_texture_paintable_snapshot (GdkPaintable *paintable,
|
||||
GdkSnapshot *snapshot,
|
||||
@@ -283,7 +310,30 @@ gdk_texture_dispose (GObject *object)
|
||||
{
|
||||
GdkTexture *self = GDK_TEXTURE (object);
|
||||
|
||||
g_clear_pointer (&self->diff_to_previous, cairo_region_destroy);
|
||||
if (self->chain)
|
||||
{
|
||||
g_mutex_lock (&self->chain->lock);
|
||||
|
||||
if (self->next_texture && self->previous_texture)
|
||||
{
|
||||
cairo_region_union (self->next_texture->diff_to_previous,
|
||||
self->diff_to_previous);
|
||||
self->next_texture->previous_texture = self->previous_texture;
|
||||
self->previous_texture->next_texture = self->next_texture;
|
||||
}
|
||||
else if (self->next_texture)
|
||||
self->next_texture->previous_texture = NULL;
|
||||
else if (self->previous_texture)
|
||||
self->previous_texture->next_texture = NULL;
|
||||
|
||||
self->next_texture = NULL;
|
||||
self->previous_texture = NULL;
|
||||
g_clear_pointer (&self->diff_to_previous, cairo_region_destroy);
|
||||
|
||||
g_mutex_unlock (&self->chain->lock);
|
||||
|
||||
g_clear_pointer (&self->chain, gdk_texture_chain_unref);
|
||||
}
|
||||
|
||||
gdk_texture_clear_render_data (self);
|
||||
|
||||
@@ -679,34 +729,64 @@ gdk_texture_do_download (GdkTexture *texture,
|
||||
GDK_TEXTURE_GET_CLASS (texture)->download (texture, format, data, stride);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gdk_texture_has_ancestor (GdkTexture *self,
|
||||
GdkTexture *other)
|
||||
{
|
||||
GdkTexture *texture;
|
||||
|
||||
for (texture = self->previous_texture;
|
||||
texture != NULL;
|
||||
texture = texture->previous_texture)
|
||||
{
|
||||
if (texture == other)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gdk_texture_diff_from_known_ancestor (GdkTexture *self,
|
||||
GdkTexture *ancestor,
|
||||
cairo_region_t *region)
|
||||
{
|
||||
GdkTexture *texture;
|
||||
|
||||
for (texture = self; texture != ancestor; texture = texture->previous_texture)
|
||||
cairo_region_union (region, texture->diff_to_previous);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_texture_diff (GdkTexture *self,
|
||||
GdkTexture *other,
|
||||
cairo_region_t *region)
|
||||
{
|
||||
if (self == other)
|
||||
cairo_rectangle_int_t fill = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = MAX (self->width, other->width),
|
||||
.height = MAX (self->height, other->height),
|
||||
};
|
||||
GdkTextureChain *chain;
|
||||
|
||||
if (other == self)
|
||||
return;
|
||||
|
||||
if (self->previous_texture == other &&
|
||||
g_atomic_pointer_get (&other->next_texture) == self)
|
||||
chain = g_atomic_pointer_get (&self->chain);
|
||||
if (chain == NULL || chain != g_atomic_pointer_get (&other->chain))
|
||||
{
|
||||
cairo_region_union (region, self->diff_to_previous);
|
||||
}
|
||||
else if (other->previous_texture == self &&
|
||||
g_atomic_pointer_get (&self->next_texture) == other)
|
||||
{
|
||||
cairo_region_union (region, other->diff_to_previous);
|
||||
cairo_region_union_rectangle (region, &fill);
|
||||
return;
|
||||
}
|
||||
|
||||
g_mutex_lock (&chain->lock);
|
||||
if (gdk_texture_has_ancestor (self, other))
|
||||
gdk_texture_diff_from_known_ancestor (self, other, region);
|
||||
else if (gdk_texture_has_ancestor (other, self))
|
||||
gdk_texture_diff_from_known_ancestor (other, self, region);
|
||||
else
|
||||
{
|
||||
cairo_region_union_rectangle (region,
|
||||
&(cairo_rectangle_int_t) {
|
||||
0,
|
||||
0,
|
||||
MAX (self->width, other->width),
|
||||
MAX (self->height, other->height)
|
||||
});
|
||||
}
|
||||
cairo_region_union_rectangle (region, &fill);
|
||||
g_mutex_unlock (&chain->lock);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -715,10 +795,30 @@ gdk_texture_set_diff (GdkTexture *self,
|
||||
cairo_region_t *diff)
|
||||
{
|
||||
g_assert (self->diff_to_previous == NULL);
|
||||
g_assert (self->chain == NULL);
|
||||
|
||||
self->chain = g_atomic_pointer_get (&previous->chain);
|
||||
if (self->chain == NULL)
|
||||
{
|
||||
self->chain = gdk_texture_chain_new ();
|
||||
if (!g_atomic_pointer_compare_and_exchange (&previous->chain,
|
||||
NULL,
|
||||
self->chain))
|
||||
gdk_texture_chain_unref (self->chain);
|
||||
self->chain = previous->chain;
|
||||
}
|
||||
gdk_texture_chain_ref (self->chain);
|
||||
|
||||
g_mutex_lock (&self->chain->lock);
|
||||
if (previous->next_texture)
|
||||
{
|
||||
previous->next_texture->previous_texture = NULL;
|
||||
g_clear_pointer (&previous->next_texture->diff_to_previous, cairo_region_destroy);
|
||||
}
|
||||
previous->next_texture = self;
|
||||
self->previous_texture = previous;
|
||||
self->diff_to_previous = diff;
|
||||
g_atomic_pointer_set (&previous->next_texture, self);
|
||||
g_mutex_unlock (&self->chain->lock);
|
||||
}
|
||||
|
||||
cairo_surface_t *
|
||||
|
||||
@@ -6,6 +6,14 @@
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GdkTextureChain GdkTextureChain;
|
||||
|
||||
struct _GdkTextureChain
|
||||
{
|
||||
gatomicrefcount ref_count;
|
||||
GMutex lock;
|
||||
};
|
||||
|
||||
#define GDK_TEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_TEXTURE, GdkTextureClass))
|
||||
#define GDK_IS_TEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_TEXTURE))
|
||||
#define GDK_TEXTURE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_TEXTURE, GdkTextureClass))
|
||||
@@ -23,10 +31,13 @@ struct _GdkTexture
|
||||
GDestroyNotify render_notify;
|
||||
|
||||
/* for diffing swapchain-like textures.
|
||||
* Links are only valid if both textures agree on them */
|
||||
gpointer next_texture; /* atomic, no reference, may be invalid pointer */
|
||||
gpointer previous_texture; /* no reference, may be invalid pointer */
|
||||
cairo_region_t *diff_to_previous;
|
||||
* Textures in the same chain are connected in a double linked list which is
|
||||
* protected using the chain's shared mutex.
|
||||
*/
|
||||
GdkTextureChain *chain; /* lazy, atomic, shared by all chain links */
|
||||
GdkTexture *next_texture; /* no reference, guarded by chain lock */
|
||||
GdkTexture *previous_texture; /* no reference, guarded by chain lock */
|
||||
cairo_region_t *diff_to_previous; /* guarded by chain lock */
|
||||
};
|
||||
|
||||
struct _GdkTextureClass {
|
||||
|
||||
@@ -3,6 +3,21 @@
|
||||
#include "gdk/gdkmemorytextureprivate.h"
|
||||
#include "gdk/gdktextureprivate.h"
|
||||
|
||||
|
||||
#define assert_texture_diff_equal(a, b, expected) G_STMT_START { \
|
||||
cairo_region_t *_r; \
|
||||
\
|
||||
_r = cairo_region_create (); \
|
||||
gdk_texture_diff (a, b, _r); \
|
||||
g_assert_true (cairo_region_equal (_r, expected)); \
|
||||
cairo_region_destroy(_r); \
|
||||
\
|
||||
_r = cairo_region_create (); \
|
||||
gdk_texture_diff (b, a, _r); \
|
||||
g_assert_true (cairo_region_equal (_r, expected)); \
|
||||
cairo_region_destroy(_r); \
|
||||
}G_STMT_END
|
||||
|
||||
static void
|
||||
compare_pixels (int width,
|
||||
int height,
|
||||
@@ -312,47 +327,49 @@ test_texture_icon_serialize (void)
|
||||
static void
|
||||
test_texture_diff (void)
|
||||
{
|
||||
GdkTexture *texture0;
|
||||
GdkTexture *texture;
|
||||
GdkTexture *texture2;
|
||||
cairo_region_t *empty;
|
||||
cairo_region_t *full;
|
||||
cairo_region_t *center;
|
||||
cairo_region_t *r;
|
||||
cairo_region_t *left;
|
||||
cairo_region_t *left_center;
|
||||
|
||||
texture0 = gdk_texture_new_from_resource ("/org/gtk/libgtk/icons/16x16/places/user-trash.png");
|
||||
texture = gdk_texture_new_from_resource ("/org/gtk/libgtk/icons/16x16/places/user-trash.png");
|
||||
texture2 = gdk_texture_new_from_resource ("/org/gtk/libgtk/icons/16x16/places/user-trash.png");
|
||||
|
||||
empty = cairo_region_create();
|
||||
full = cairo_region_create_rectangle (&(cairo_rectangle_int_t) { 0, 0, 16, 16 });
|
||||
center = cairo_region_create_rectangle (&(cairo_rectangle_int_t) { 4, 4, 8 ,8 });
|
||||
left = cairo_region_create_rectangle (&(cairo_rectangle_int_t) { 0, 4, 4, 4 });
|
||||
left_center = cairo_region_copy (left);
|
||||
cairo_region_union (left_center, center);
|
||||
|
||||
r = cairo_region_create ();
|
||||
gdk_texture_diff (texture, texture, r);
|
||||
|
||||
g_assert_true (cairo_region_is_empty (r));
|
||||
|
||||
gdk_texture_diff (texture, texture2, r);
|
||||
assert_texture_diff_equal (texture, texture, empty);
|
||||
|
||||
/* No diff set, so we get the full area */
|
||||
g_assert_true (cairo_region_equal (r, full));
|
||||
cairo_region_destroy (r);
|
||||
assert_texture_diff_equal (texture, texture2, full);
|
||||
|
||||
gdk_texture_set_diff (texture, texture2, cairo_region_copy (center));
|
||||
|
||||
r = cairo_region_create ();
|
||||
gdk_texture_diff (texture, texture2, r);
|
||||
assert_texture_diff_equal (texture, texture2, center);
|
||||
|
||||
g_assert_true (cairo_region_equal (r, center));
|
||||
cairo_region_destroy (r);
|
||||
gdk_texture_set_diff (texture0, texture, cairo_region_copy (left));
|
||||
|
||||
r = cairo_region_create ();
|
||||
gdk_texture_diff (texture2, texture, r);
|
||||
assert_texture_diff_equal (texture0, texture2, left_center);
|
||||
|
||||
g_assert_true (cairo_region_equal (r, center));
|
||||
cairo_region_destroy (r);
|
||||
g_object_unref (texture);
|
||||
|
||||
assert_texture_diff_equal (texture0, texture2, left_center);
|
||||
|
||||
cairo_region_destroy (full);
|
||||
cairo_region_destroy (center);
|
||||
g_object_unref (texture);
|
||||
cairo_region_destroy (left);
|
||||
cairo_region_destroy (left_center);
|
||||
g_object_unref (texture2);
|
||||
g_object_unref (texture0);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
Reference in New Issue
Block a user