clutter-image.c 13 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * 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 <http://www.gnu.org/licenses/>.
 *
 * Author:
 *   Emmanuele Bassi <ebassi@linux.intel.com>
 */

Emmanuele Bassi's avatar
Emmanuele Bassi committed
25 26 27 28 29 30
/**
 * SECTION:clutter-image
 * @Title: ClutterImage
 * @Short_Description: Image data content
 *
 * #ClutterImage is a #ClutterContent implementation that displays
Emmanuele Bassi's avatar
Emmanuele Bassi committed
31
 * image data inside a #ClutterActor.
Emmanuele Bassi's avatar
Emmanuele Bassi committed
32
 *
33
 * See [image.c](https://git.gnome.org/browse/clutter/tree/examples/image-content.c?h=clutter-1.18)
Emmanuele Bassi's avatar
Emmanuele Bassi committed
34
 * for an example of how to use #ClutterImage.
35
 *
Emmanuele Bassi's avatar
Emmanuele Bassi committed
36 37 38
 * #ClutterImage is available since Clutter 1.10.
 */

39
#ifdef HAVE_CONFIG_H
40
#include "clutter-build-config.h"
41 42
#endif

43 44
#define CLUTTER_ENABLE_EXPERIMENTAL_API

45 46
#include "clutter-image.h"

47
#include "clutter-actor-private.h"
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
#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"

struct _ClutterImagePrivate
{
  CoglTexture *texture;
};

static void clutter_content_iface_init (ClutterContentIface *iface);

G_DEFINE_TYPE_WITH_CODE (ClutterImage, clutter_image, G_TYPE_OBJECT,
63
                         G_ADD_PRIVATE (ClutterImage)
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT,
                                                clutter_content_iface_init))

GQuark
clutter_image_error_quark (void)
{
  return g_quark_from_static_string ("clutter-image-error-quark");
}

static void
clutter_image_finalize (GObject *gobject)
{
  ClutterImagePrivate *priv = CLUTTER_IMAGE (gobject)->priv;

  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)
{
96
  self->priv = clutter_image_get_instance_private (self);
97 98 99 100 101 102 103 104 105 106 107 108 109
}

static void
clutter_image_paint_content (ClutterContent   *content,
                             ClutterActor     *actor,
                             ClutterPaintNode *root)
{
  ClutterImagePrivate *priv = CLUTTER_IMAGE (content)->priv;
  ClutterPaintNode *node;

  if (priv->texture == NULL)
    return;

110 111
  node = clutter_actor_create_texture_paint_node (actor, priv->texture);
  clutter_paint_node_set_name (node, "Image Content");
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
  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)
{
  ClutterImagePrivate *priv = CLUTTER_IMAGE (content)->priv;

  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 (ClutterContentIface *iface)
{
  iface->get_preferred_size = clutter_image_get_preferred_size;
  iface->paint_content = clutter_image_paint_content;
}

/**
 * clutter_image_new:
 *
Emmanuele Bassi's avatar
Emmanuele Bassi committed
145
 * Creates a new #ClutterImage instance.
146
 *
Emmanuele Bassi's avatar
Emmanuele Bassi committed
147 148
 * Return value: (transfer full): the newly created #ClutterImage instance.
 *   Use g_object_unref() when done.
149 150 151 152 153 154 155 156 157
 *
 * Since: 1.10
 */
ClutterContent *
clutter_image_new (void)
{
  return g_object_new (CLUTTER_TYPE_IMAGE, NULL);
}

Emmanuele Bassi's avatar
Emmanuele Bassi committed
158 159 160 161 162 163 164 165 166 167
/**
 * 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
 *
168
 * Sets the image data to be displayed by @image.
Emmanuele Bassi's avatar
Emmanuele Bassi committed
169 170 171 172 173 174 175 176
 *
 * 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.
 *
177 178 179 180 181 182 183 184 185 186 187
 * 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:
 *
 * |[<!-- language="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),
188
 *                           gdk_pixbuf_get_has_alpha (pixbuf)
189 190 191 192 193 194 195 196 197 198
 *                             ? 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);
 * ]|
 *
Emmanuele Bassi's avatar
Emmanuele Bassi committed
199 200 201 202 203
 * Return value: %TRUE if the image data was successfully loaded,
 *   and %FALSE otherwise.
 *
 * Since: 1.10
 */
204 205 206 207 208 209 210 211 212 213
gboolean
clutter_image_set_data (ClutterImage     *image,
                        const guint8     *data,
                        CoglPixelFormat   pixel_format,
                        guint             width,
                        guint             height,
                        guint             row_stride,
                        GError          **error)
{
  ClutterImagePrivate *priv;
214
  CoglTextureFlags flags;
215 216 217 218 219 220 221 222 223

  g_return_val_if_fail (CLUTTER_IS_IMAGE (image), FALSE);
  g_return_val_if_fail (data != NULL, FALSE);

  priv = image->priv;

  if (priv->texture != NULL)
    cogl_object_unref (priv->texture);

224 225 226 227
  flags = COGL_TEXTURE_NONE;
  if (width >= 512 && height >= 512)
    flags |= COGL_TEXTURE_NO_ATLAS;

228
  priv->texture = cogl_texture_new_from_data (width, height,
229
                                              flags,
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
                                              pixel_format,
                                              COGL_PIXEL_FORMAT_ANY,
                                              row_stride,
                                              data);
  if (priv->texture == NULL)
    {
      g_set_error_literal (error, CLUTTER_IMAGE_ERROR,
                           CLUTTER_IMAGE_ERROR_INVALID_DATA,
                           _("Unable to load image data"));
      return FALSE;
    }

  clutter_content_invalidate (CLUTTER_CONTENT (image));

  return TRUE;
}
246

247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
/**
 * 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.
 *
 * Since: 1.12
 */
gboolean
clutter_image_set_bytes (ClutterImage     *image,
                         GBytes           *data,
                         CoglPixelFormat   pixel_format,
                         guint             width,
                         guint             height,
                         guint             row_stride,
                         GError          **error)
{
  ClutterImagePrivate *priv;
282
  CoglTextureFlags flags;
283 284 285 286 287 288 289 290 291

  g_return_val_if_fail (CLUTTER_IS_IMAGE (image), FALSE);
  g_return_val_if_fail (data != NULL, FALSE);

  priv = image->priv;

  if (priv->texture != NULL)
    cogl_object_unref (priv->texture);

292 293 294 295
  flags = COGL_TEXTURE_NONE;
  if (width >= 512 && height >= 512)
    flags |= COGL_TEXTURE_NO_ATLAS;

296
  priv->texture = cogl_texture_new_from_data (width, height,
297
                                              flags,
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
                                              pixel_format,
                                              COGL_PIXEL_FORMAT_ANY,
                                              row_stride,
                                              g_bytes_get_data (data, NULL));
  if (priv->texture == NULL)
    {
      g_set_error_literal (error, CLUTTER_IMAGE_ERROR,
                           CLUTTER_IMAGE_ERROR_INVALID_DATA,
                           _("Unable to load image data"));
      return FALSE;
    }

  clutter_content_invalidate (CLUTTER_CONTENT (image));

  return TRUE;
}

315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
/**
 * 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.
 *
 * Since: 1.10
 */
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 = image->priv;

  if (priv->texture == NULL)
    {
362 363 364 365 366
      CoglTextureFlags flags = COGL_TEXTURE_NONE;

      if (area->width >= 512 && area->height >= 512)
        flags |= COGL_TEXTURE_NO_ATLAS;

367 368
      priv->texture = cogl_texture_new_from_data (area->width,
                                                  area->height,
369
                                                  flags,
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
                                                  pixel_format,
                                                  COGL_PIXEL_FORMAT_ANY,
                                                  row_stride,
                                                  data);
    }
  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)
    {
      g_set_error_literal (error, CLUTTER_IMAGE_ERROR,
                           CLUTTER_IMAGE_ERROR_INVALID_DATA,
                           _("Unable to load image data"));
      return FALSE;
    }

  clutter_content_invalidate (CLUTTER_CONTENT (image));

  return TRUE;
}

408 409 410 411 412 413
/**
 * clutter_image_get_texture:
 * @image: a #ClutterImage
 *
 * Retrieves a pointer to the Cogl texture used by @image.
 *
414 415 416 417
 * 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.
 *
418 419 420 421 422 423 424 425 426 427 428 429
 * Return value: (transfer none): a pointer to the Cogl texture, or %NULL
 *
 * Since: 1.10
 * Stability: unstable
 */
CoglTexture *
clutter_image_get_texture (ClutterImage *image)
{
  g_return_val_if_fail (CLUTTER_IS_IMAGE (image), NULL);

  return image->priv->texture;
}