st: Merge ClutterImage with ImageContent

ClutterImage as is, is very basic and doesn't really serve any purposes
other than being inherited by StImageContent. As we cannot easily push
ImageContent to Mutter by merging it with ClutterImage because it will
bring more GdkPixbuf usages and also because Clutter doesn't really deal
with theming bits.

Instead, merge it with StImageContent and let GNOME Shell take over from
here.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3547>
This commit is contained in:
Bilal Elmoussaoui 2024-11-15 12:36:00 +01:00 committed by Marge Bot
parent ea88ba687e
commit 64d5d01360
4 changed files with 274 additions and 34 deletions

View File

@ -25,7 +25,9 @@
struct _StImageContent struct _StImageContent
{ {
ClutterImage parent_instance; GObject parent_instance;
CoglTexture *texture;
int width; int width;
int height; int height;
@ -43,7 +45,7 @@ static void clutter_content_interface_init (ClutterContentInterface *iface);
static void g_icon_interface_init (GIconIface *iface); static void g_icon_interface_init (GIconIface *iface);
static void g_loadable_icon_interface_init (GLoadableIconIface *iface); static void g_loadable_icon_interface_init (GLoadableIconIface *iface);
G_DEFINE_FINAL_TYPE_WITH_CODE (StImageContent, st_image_content, CLUTTER_TYPE_IMAGE, G_DEFINE_FINAL_TYPE_WITH_CODE (StImageContent, st_image_content, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT,
clutter_content_interface_init) clutter_content_interface_init)
G_IMPLEMENT_INTERFACE (G_TYPE_ICON, G_IMPLEMENT_INTERFACE (G_TYPE_ICON,
@ -114,6 +116,16 @@ st_image_content_set_property (GObject *object,
} }
} }
static void
st_image_content_finalize (GObject *gobject)
{
StImageContent *content = ST_IMAGE_CONTENT (gobject);
g_clear_object (&content->texture);
G_OBJECT_CLASS (st_image_content_parent_class)->finalize (gobject);
}
static void static void
st_image_content_class_init (StImageContentClass *klass) st_image_content_class_init (StImageContentClass *klass)
{ {
@ -123,6 +135,7 @@ st_image_content_class_init (StImageContentClass *klass)
object_class->constructed = st_image_content_constructed; object_class->constructed = st_image_content_constructed;
object_class->get_property = st_image_content_get_property; object_class->get_property = st_image_content_get_property;
object_class->set_property = st_image_content_set_property; object_class->set_property = st_image_content_set_property;
object_class->finalize = st_image_content_finalize;
pspec = g_param_spec_int ("preferred-width", NULL, NULL, pspec = g_param_spec_int ("preferred-width", NULL, NULL,
-1, G_MAXINT, -1, -1, G_MAXINT, -1,
@ -141,11 +154,8 @@ st_image_content_get_preferred_size (ClutterContent *content,
float *height) float *height)
{ {
StImageContent *self = ST_IMAGE_CONTENT (content); StImageContent *self = ST_IMAGE_CONTENT (content);
CoglTexture *texture;
texture = clutter_image_get_texture (CLUTTER_IMAGE (content)); if (self->texture == NULL)
if (texture == NULL)
return FALSE; return FALSE;
g_assert_cmpint (self->width, >, -1); g_assert_cmpint (self->width, >, -1);
@ -167,7 +177,7 @@ pixbuf_from_image (StImageContent *image)
int width, height, rowstride; int width, height, rowstride;
uint8_t *data; uint8_t *data;
texture = clutter_image_get_texture (CLUTTER_IMAGE (image)); texture = st_image_content_get_texture (image);
if (!texture || !cogl_texture_is_get_data_supported (texture)) if (!texture || !cogl_texture_is_get_data_supported (texture))
return NULL; return NULL;
@ -184,10 +194,29 @@ pixbuf_from_image (StImageContent *image)
(GdkPixbufDestroyNotify)g_free, NULL); (GdkPixbufDestroyNotify)g_free, NULL);
} }
static void
st_image_content_paint_content (ClutterContent *content,
ClutterActor *actor,
ClutterPaintNode *root,
ClutterPaintContext *paint_context)
{
StImageContent *image_content = ST_IMAGE_CONTENT (content);
ClutterPaintNode *node;
if (image_content->texture == NULL)
return;
node = clutter_actor_create_texture_paint_node (actor, image_content->texture);
clutter_paint_node_set_static_name (node, "Image Content");
clutter_paint_node_add_child (root, node);
clutter_paint_node_unref (node);
}
static void static void
clutter_content_interface_init (ClutterContentInterface *iface) clutter_content_interface_init (ClutterContentInterface *iface)
{ {
iface->get_preferred_size = st_image_content_get_preferred_size; iface->get_preferred_size = st_image_content_get_preferred_size;
iface->paint_content = st_image_content_paint_content;
} }
static guint static guint
@ -318,9 +347,6 @@ g_loadable_icon_interface_init (GLoadableIconIface *iface)
* *
* Creates a new #StImageContent, a simple content for sized images. * Creates a new #StImageContent, a simple content for sized images.
* *
* See #ClutterImage for setting the actual image to display or #StIcon for
* displaying icons.
*
* Returns: (transfer full): the newly created #StImageContent content * Returns: (transfer full): the newly created #StImageContent content
* Use g_object_unref() when done. * Use g_object_unref() when done.
*/ */
@ -350,3 +376,198 @@ st_image_content_get_is_symbolic (StImageContent *content)
return content->is_symbolic; return content->is_symbolic;
} }
static CoglTexture *
create_texture_from_data (unsigned int width,
unsigned int height,
CoglPixelFormat pixel_format,
unsigned int row_stride,
const uint8_t *data,
GError **error)
{
ClutterBackend *backend = clutter_get_default_backend ();
CoglContext *cogl_context = clutter_backend_get_cogl_context (backend);
CoglTexture *texture_2d;
texture_2d = cogl_texture_2d_new_from_data (cogl_context,
width,
height,
pixel_format,
row_stride,
data,
error);
return texture_2d;
}
static void
update_image_size (StImageContent *self)
{
int width, height;
if (self->texture == NULL)
return;
width = cogl_texture_get_width (self->texture);
height = cogl_texture_get_height (self->texture);
if (self->width == width &&
self->height == height)
return;
self->width = width;
self->height = height;
clutter_content_invalidate_size (CLUTTER_CONTENT (self));
}
/**
* st_image_content_set_data:
* @content: a #StImageContentImage
* @data: (array): the image data, as an array of bytes
* @pixel_format: the Cogl pixel format of the image data
* @width: the width of the image data
* @height: the height of the image data
* @row_stride: the length of each row inside @data
* @error: return location for a #GError, or %NULL
*
* Sets the image data to be displayed by @content.
*
* If the image data was successfully loaded, the @content will be invalidated.
*
* In case of error, the @error value will be set, and this function will
* return %FALSE.
*
* The image data is copied in texture memory.
*
* The image data is expected to be a linear array of RGBA or RGB pixel data;
* how to retrieve that data is left to platform specific image loaders. For
* instance, if you use the GdkPixbuf library:
*
* ```c
* StImageContent *content =
* st_image_content_new_with_preferred_size ();
* GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
*
* st_image_content_set_data (content,
* gdk_pixbuf_get_pixels (pixbuf),
* gdk_pixbuf_get_has_alpha (pixbuf)
* ? COGL_PIXEL_FORMAT_RGBA_8888
* : COGL_PIXEL_FORMAT_RGB_888,
* gdk_pixbuf_get_width (pixbuf),
* gdk_pixbuf_get_height (pixbuf),
* gdk_pixbuf_get_rowstride (pixbuf),
* &error);
*
* g_object_unref (pixbuf);
* ```
*
* Return value: %TRUE if the image data was successfully loaded,
* and %FALSE otherwise.
*/
gboolean
st_image_content_set_data (StImageContent *content,
const guint8 *data,
CoglPixelFormat pixel_format,
guint width,
guint height,
guint row_stride,
GError **error)
{
g_return_val_if_fail (ST_IS_IMAGE_CONTENT (content), FALSE);
g_return_val_if_fail (data != NULL, FALSE);
if (content->texture != NULL)
g_object_unref (content->texture);
content->texture = create_texture_from_data (width,
height,
pixel_format,
row_stride,
data,
error);
if (content->texture == NULL)
return FALSE;
clutter_content_invalidate (CLUTTER_CONTENT (content));
update_image_size (content);
return TRUE;
}
/**
* st_image_content_set_bytes:
* @content: a #StImageContent
* @data: the image data, as a #GBytes
* @pixel_format: the Cogl pixel format of the image data
* @width: the width of the image data
* @height: the height of the image data
* @row_stride: the length of each row inside @data
* @error: return location for a #GError, or %NULL
*
* Sets the image data stored inside a #GBytes to be displayed by @content.
*
* If the image data was successfully loaded, the @content will be invalidated.
*
* In case of error, the @error value will be set, and this function will
* return %FALSE.
*
* The image data contained inside the #GBytes is copied in texture memory,
* and no additional reference is acquired on the @data.
*
* Return value: %TRUE if the image data was successfully loaded,
* and %FALSE otherwise.
*/
gboolean
st_image_content_set_bytes (StImageContent *content,
GBytes *data,
CoglPixelFormat pixel_format,
guint width,
guint height,
guint row_stride,
GError **error)
{
g_return_val_if_fail (ST_IS_IMAGE_CONTENT (content), FALSE);
g_return_val_if_fail (data != NULL, FALSE);
if (content->texture != NULL)
g_object_unref (content->texture);
content->texture = create_texture_from_data (width,
height,
pixel_format,
row_stride,
g_bytes_get_data (data, NULL),
error);
if (content->texture == NULL)
return FALSE;
clutter_content_invalidate (CLUTTER_CONTENT (content));
update_image_size (content);
return TRUE;
}
/**
* st_image_content_get_texture:
* @content: a #StcontentContent
*
* Retrieves a pointer to the Cogl texture used by @content.
*
* If you change the contents of the returned Cogl texture you will need
* to manually invalidate the @content with [method@Clutter.Content.invalidate]
* in order to update the actors using @content as their content.
*
* Return value: (transfer none): a pointer to the Cogl texture, or %NULL
*/
CoglTexture *
st_image_content_get_texture (StImageContent *content)
{
g_return_val_if_fail (ST_IS_IMAGE_CONTENT (content), NULL);
return content->texture;
}

View File

@ -24,7 +24,25 @@
#define ST_TYPE_IMAGE_CONTENT (st_image_content_get_type ()) #define ST_TYPE_IMAGE_CONTENT (st_image_content_get_type ())
G_DECLARE_FINAL_TYPE (StImageContent, st_image_content, G_DECLARE_FINAL_TYPE (StImageContent, st_image_content,
ST, IMAGE_CONTENT, ClutterImage) ST, IMAGE_CONTENT, GObject)
ClutterContent *st_image_content_new_with_preferred_size (int width, ClutterContent *st_image_content_new_with_preferred_size (int width,
int height); int height);
gboolean st_image_content_set_data (StImageContent *content,
const guint8 *data,
CoglPixelFormat pixel_format,
guint width,
guint height,
guint row_stride,
GError **error);
gboolean st_image_content_set_bytes (StImageContent *content,
GBytes *data,
CoglPixelFormat pixel_format,
guint width,
guint height,
guint row_stride,
GError **error);
CoglTexture * st_image_content_get_texture (StImageContent *content);

View File

@ -23,6 +23,7 @@
#include <string.h> #include <string.h>
#include "st-private.h" #include "st-private.h"
#include "st-image-content.h"
/** /**
* _st_actor_get_preferred_width: * _st_actor_get_preferred_width:
@ -528,11 +529,11 @@ _st_create_shadow_pipeline_from_actor (StShadow *shadow_spec,
height = ceilf (height * resource_scale); height = ceilf (height * resource_scale);
image = clutter_actor_get_content (actor); image = clutter_actor_get_content (actor);
if (image && CLUTTER_IS_IMAGE (image)) if (image && ST_IS_IMAGE_CONTENT (image))
{ {
CoglTexture *texture; CoglTexture *texture;
texture = clutter_image_get_texture (CLUTTER_IMAGE (image)); texture = st_image_content_get_texture (ST_IMAGE_CONTENT (image));
if (texture && if (texture &&
cogl_texture_get_width (texture) == width && cogl_texture_get_width (texture) == width &&
cogl_texture_get_height (texture) == height) cogl_texture_get_height (texture) == height)

View File

@ -39,7 +39,7 @@ struct _StTextureCachePrivate
StIconTheme *icon_theme; StIconTheme *icon_theme;
/* Things that were loaded with a cache policy != NONE */ /* Things that were loaded with a cache policy != NONE */
GHashTable *keyed_cache; /* char * -> ClutterImage* */ GHashTable *keyed_cache; /* char * -> StImageContent* */
GHashTable *keyed_surface_cache; /* char * -> cairo_surface_t* */ GHashTable *keyed_surface_cache; /* char * -> cairo_surface_t* */
GHashTable *used_scales; /* Set: double */ GHashTable *used_scales; /* Set: double */
@ -85,7 +85,7 @@ static void
set_content_from_image (ClutterActor *actor, set_content_from_image (ClutterActor *actor,
ClutterContent *image) ClutterContent *image)
{ {
g_assert (image && CLUTTER_IS_IMAGE (image)); g_assert (image && ST_IS_IMAGE_CONTENT (image));
clutter_actor_set_content (actor, image); clutter_actor_set_content (actor, image);
clutter_actor_set_opacity (actor, 255); clutter_actor_set_opacity (actor, 255);
@ -523,14 +523,14 @@ pixbuf_to_st_content_image (GdkPixbuf *pixbuf,
} }
image = st_image_content_new_with_preferred_size (width, height); image = st_image_content_new_with_preferred_size (width, height);
clutter_image_set_data (CLUTTER_IMAGE (image), st_image_content_set_data (ST_IMAGE_CONTENT (image),
gdk_pixbuf_get_pixels (pixbuf), gdk_pixbuf_get_pixels (pixbuf),
gdk_pixbuf_get_has_alpha (pixbuf) ? gdk_pixbuf_get_has_alpha (pixbuf) ?
COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888, COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888,
gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_width (pixbuf),
gdk_pixbuf_get_height (pixbuf), gdk_pixbuf_get_height (pixbuf),
gdk_pixbuf_get_rowstride (pixbuf), gdk_pixbuf_get_rowstride (pixbuf),
&error); &error);
if (error) if (error)
{ {
@ -824,14 +824,14 @@ st_texture_cache_load_surface (ClutterContent **image,
if (*image == NULL) if (*image == NULL)
*image = st_image_content_new_with_preferred_size (size, size); *image = st_image_content_new_with_preferred_size (size, size);
clutter_image_set_data (CLUTTER_IMAGE (*image), st_image_content_set_data (ST_IMAGE_CONTENT (image),
cairo_image_surface_get_data (surface), cairo_image_surface_get_data (surface),
cairo_image_surface_get_format (surface) == CAIRO_FORMAT_ARGB32 ? cairo_image_surface_get_format (surface) == CAIRO_FORMAT_ARGB32 ?
COGL_PIXEL_FORMAT_BGRA_8888 : COGL_PIXEL_FORMAT_BGR_888, COGL_PIXEL_FORMAT_BGRA_8888 : COGL_PIXEL_FORMAT_BGR_888,
width, width,
height, height,
cairo_image_surface_get_stride (surface), cairo_image_surface_get_stride (surface),
&error); &error);
if (error) if (error)
g_warning ("Failed to allocate texture: %s", error->message); g_warning ("Failed to allocate texture: %s", error->message);
@ -1519,9 +1519,9 @@ st_texture_cache_load_file_sync_to_cogl_texture (StTextureCache *cache,
} }
/* Because the texture is loaded synchronously, we won't call /* Because the texture is loaded synchronously, we won't call
* clutter_image_set_data(), so it's safe to use the texture * st_image_content_set_data(), so it's safe to use the texture
* of ClutterImage here. */ * of StImageContent here. */
texdata = clutter_image_get_texture (CLUTTER_IMAGE (image)); texdata = st_image_content_get_texture (ST_IMAGE_CONTENT (image));
g_object_ref (texdata); g_object_ref (texdata);
ensure_monitor_for_file (cache, file); ensure_monitor_for_file (cache, file);