/* * Clutter. * * An OpenGL based 'interactive image' library. * * Copyright (C) 2012 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * ClutterImage: * * Image data content * * #ClutterImage is a #ClutterContent implementation that displays * image data inside a [class@Actor]. * * See [image.c](https://git.gnome.org/browse/clutter/tree/examples/image-content.c?h=clutter-1.18) * for an example of how to use #ClutterImage.. */ #include "clutter-build-config.h" #include "clutter-image.h" #include "clutter-actor-private.h" #include "clutter-color.h" #include "clutter-content-private.h" #include "clutter-debug.h" #include "clutter-paint-node.h" #include "clutter-paint-nodes.h" #include "clutter-private.h" typedef struct { CoglTexture *texture; gint width; gint height; } ClutterImagePrivate; static void clutter_content_iface_init (ClutterContentInterface *iface); G_DEFINE_TYPE_WITH_CODE (ClutterImage, clutter_image, G_TYPE_OBJECT, G_ADD_PRIVATE (ClutterImage) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT, clutter_content_iface_init)) 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) { CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); CoglTexture2D *texture_2d; texture_2d = cogl_texture_2d_new_from_data (ctx, width, height, pixel_format, row_stride, data, error); return texture_2d ? COGL_TEXTURE (texture_2d) : NULL; } static void update_image_size (ClutterImage *self) { ClutterImagePrivate *priv = clutter_image_get_instance_private (self); gint width, height; if (priv->texture == NULL) return; width = cogl_texture_get_width (priv->texture); height = cogl_texture_get_height (priv->texture); if (priv->width == width && priv->height == height) return; priv->width = width; priv->height = height; clutter_content_invalidate_size (CLUTTER_CONTENT (self)); } static void clutter_image_finalize (GObject *gobject) { ClutterImage *image = CLUTTER_IMAGE (gobject); ClutterImagePrivate *priv = clutter_image_get_instance_private (image); if (priv->texture != NULL) { cogl_object_unref (priv->texture); priv->texture = NULL; } G_OBJECT_CLASS (clutter_image_parent_class)->finalize (gobject); } static void clutter_image_class_init (ClutterImageClass *klass) { G_OBJECT_CLASS (klass)->finalize = clutter_image_finalize; } static void clutter_image_init (ClutterImage *self) { } static void clutter_image_paint_content (ClutterContent *content, ClutterActor *actor, ClutterPaintNode *root, ClutterPaintContext *paint_context) { ClutterImage *image = CLUTTER_IMAGE (content); ClutterImagePrivate *priv = clutter_image_get_instance_private (image); ClutterPaintNode *node; if (priv->texture == NULL) return; node = clutter_actor_create_texture_paint_node (actor, priv->texture); clutter_paint_node_set_static_name (node, "Image Content"); clutter_paint_node_add_child (root, node); clutter_paint_node_unref (node); } static gboolean clutter_image_get_preferred_size (ClutterContent *content, gfloat *width, gfloat *height) { ClutterImage *image = CLUTTER_IMAGE (content); ClutterImagePrivate *priv = clutter_image_get_instance_private (image); if (priv->texture == NULL) return FALSE; if (width != NULL) *width = cogl_texture_get_width (priv->texture); if (height != NULL) *height = cogl_texture_get_height (priv->texture); return TRUE; } static void clutter_content_iface_init (ClutterContentInterface *iface) { iface->get_preferred_size = clutter_image_get_preferred_size; iface->paint_content = clutter_image_paint_content; } /** * clutter_image_new: * * Creates a new #ClutterImage instance. * * Return value: (transfer full): the newly created #ClutterImage instance. * Use g_object_unref() when done. */ ClutterContent * clutter_image_new (void) { return g_object_new (CLUTTER_TYPE_IMAGE, NULL); } /** * clutter_image_set_data: * @image: a #ClutterImage * @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 @image. * * If the image data was successfully loaded, the @image 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 * ClutterContent *image = clutter_image_new (); * * GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (filename, NULL); * * clutter_image_set_data (CLUTTER_IMAGE (image), * 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 clutter_image_set_data (ClutterImage *image, const guint8 *data, CoglPixelFormat pixel_format, guint width, guint height, guint row_stride, GError **error) { ClutterImagePrivate *priv; g_return_val_if_fail (CLUTTER_IS_IMAGE (image), FALSE); g_return_val_if_fail (data != NULL, FALSE); priv = clutter_image_get_instance_private (image); if (priv->texture != NULL) cogl_object_unref (priv->texture); priv->texture = create_texture_from_data (width, height, pixel_format, row_stride, data, error); if (priv->texture == NULL) return FALSE; clutter_content_invalidate (CLUTTER_CONTENT (image)); update_image_size (image); return TRUE; } /** * clutter_image_set_bytes: * @image: a #ClutterImage * @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 @image. * * If the image data was successfully loaded, the @image 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 clutter_image_set_bytes (ClutterImage *image, GBytes *data, CoglPixelFormat pixel_format, guint width, guint height, guint row_stride, GError **error) { ClutterImagePrivate *priv; g_return_val_if_fail (CLUTTER_IS_IMAGE (image), FALSE); g_return_val_if_fail (data != NULL, FALSE); priv = clutter_image_get_instance_private (image); if (priv->texture != NULL) cogl_object_unref (priv->texture); priv->texture = create_texture_from_data (width, height, pixel_format, row_stride, g_bytes_get_data (data, NULL), error); if (priv->texture == NULL) return FALSE; clutter_content_invalidate (CLUTTER_CONTENT (image)); update_image_size (image); return TRUE; } /** * clutter_image_set_area: * @image: a #ClutterImage * @data: (array): the image data, as an array of bytes * @pixel_format: the Cogl pixel format of the image data * @rect: a rectangle indicating the area that should be set * @row_stride: the length of each row inside @data * @error: return location for a #GError, or %NULL * * Sets the image data to be display by @image, using @rect to indicate * the position and size of the image data to be set. * * If the @image does not have any image data set when this function is * called, a new texture will be created with the size of the width and * height of the rectangle, i.e. calling this function on a newly created * #ClutterImage will be the equivalent of calling clutter_image_set_data(). * * If the image data was successfully loaded, the @image 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. * * Return value: %TRUE if the image data was successfully loaded, * and %FALSE otherwise. */ gboolean clutter_image_set_area (ClutterImage *image, const guint8 *data, CoglPixelFormat pixel_format, const cairo_rectangle_int_t *area, guint row_stride, GError **error) { ClutterImagePrivate *priv; g_return_val_if_fail (CLUTTER_IS_IMAGE (image), FALSE); g_return_val_if_fail (data != NULL, FALSE); g_return_val_if_fail (area != NULL, FALSE); priv = clutter_image_get_instance_private (image); if (priv->texture == NULL) { priv->texture = create_texture_from_data (area->width, area->height, pixel_format, row_stride, data, error); } else { gboolean res; res = cogl_texture_set_region (priv->texture, 0, 0, area->x, area->y, area->width, area->height, area->width, area->height, pixel_format, row_stride, data); if (!res) { cogl_object_unref (priv->texture); priv->texture = NULL; } } if (priv->texture == NULL) return FALSE; clutter_content_invalidate (CLUTTER_CONTENT (image)); update_image_size (image); return TRUE; } /** * clutter_image_get_texture: * @image: a #ClutterImage * * Retrieves a pointer to the Cogl texture used by @image. * * If you change the contents of the returned Cogl texture you will need * to manually invalidate the @image with clutter_content_invalidate() * in order to update the actors using @image as their content. * * Return value: (transfer none): a pointer to the Cogl texture, or %NULL */ CoglTexture * clutter_image_get_texture (ClutterImage *image) { ClutterImagePrivate *priv; g_return_val_if_fail (CLUTTER_IS_IMAGE (image), NULL); priv = clutter_image_get_instance_private (image); return priv->texture; }