meta-shaped-texture.c 25.3 KB
Newer Older
1 2
/*
 * Authored By Neil Roberts  <neil@linux.intel.com>
3
 * and Jasper St. Pierre <jstpierre@mecheye.net>
4 5
 *
 * Copyright (C) 2008 Intel Corporation
6
 * Copyright (C) 2012 Red Hat, Inc.
7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 21
 */

22 23 24 25 26 27
/**
 * SECTION:meta-shaped-texture
 * @title: MetaShapedTexture
 * @short_description: An actor to draw a masked texture.
 */

28 29
#include <config.h>

30
#include <meta/meta-shaped-texture.h>
31
#include <meta/util.h>
32
#include "clutter-utils.h"
33
#include "meta-texture-tower.h"
Thomas Wood's avatar
Thomas Wood committed
34

35
#include "meta-shaped-texture-private.h"
36
#include "meta-window-actor-private.h"
37

Thomas Wood's avatar
Thomas Wood committed
38
#include <clutter/clutter.h>
39
#include <cogl/cogl.h>
40
#include <gdk/gdk.h> /* for gdk_rectangle_intersect() */
41
#include "meta-cullable.h"
42

43
static void meta_shaped_texture_dispose  (GObject    *object);
44

45
static void meta_shaped_texture_paint (ClutterActor       *actor);
46

47 48 49 50 51 52 53 54 55
static void meta_shaped_texture_get_preferred_width (ClutterActor *self,
                                                     gfloat        for_height,
                                                     gfloat       *min_width_p,
                                                     gfloat       *natural_width_p);

static void meta_shaped_texture_get_preferred_height (ClutterActor *self,
                                                      gfloat        for_width,
                                                      gfloat       *min_height_p,
                                                      gfloat       *natural_height_p);
56

57 58
static gboolean meta_shaped_texture_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume);

59 60 61 62
static void cullable_iface_init (MetaCullableInterface *iface);

G_DEFINE_TYPE_WITH_CODE (MetaShapedTexture, meta_shaped_texture, CLUTTER_TYPE_ACTOR,
                         G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init));
63

64 65 66
#define META_SHAPED_TEXTURE_GET_PRIVATE(obj) \
  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), META_TYPE_SHAPED_TEXTURE, \
                                MetaShapedTexturePrivate))
67

68
struct _MetaShapedTexturePrivate
69
{
70
  MetaTextureTower *paint_tower;
71 72

  CoglTexture *texture;
73
  CoglTexture *mask_texture;
74

75
  /* The region containing only fully opaque pixels */
76
  cairo_region_t *opaque_region;
77

78 79 80 81
  /* MetaCullable regions, see that documentation for more details */
  cairo_region_t *clip_region;
  cairo_region_t *unobscured_region;

82
  guint tex_width, tex_height;
83

84
  guint create_mipmaps : 1;
85 86 87
};

static void
88
meta_shaped_texture_class_init (MetaShapedTextureClass *klass)
89 90 91 92
{
  GObjectClass *gobject_class = (GObjectClass *) klass;
  ClutterActorClass *actor_class = (ClutterActorClass *) klass;

93
  gobject_class->dispose = meta_shaped_texture_dispose;
94

95 96
  actor_class->get_preferred_width = meta_shaped_texture_get_preferred_width;
  actor_class->get_preferred_height = meta_shaped_texture_get_preferred_height;
97
  actor_class->paint = meta_shaped_texture_paint;
98
  actor_class->get_paint_volume = meta_shaped_texture_get_paint_volume;
99

100
  g_type_class_add_private (klass, sizeof (MetaShapedTexturePrivate));
101 102 103
}

static void
104
meta_shaped_texture_init (MetaShapedTexture *self)
105
{
106
  MetaShapedTexturePrivate *priv;
107

108
  priv = self->priv = META_SHAPED_TEXTURE_GET_PRIVATE (self);
109

110
  priv->paint_tower = meta_texture_tower_new ();
111

112 113
  priv->texture = NULL;
  priv->mask_texture = NULL;
114
  priv->create_mipmaps = TRUE;
115 116
}

117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
static void
set_unobscured_region (MetaShapedTexture *self,
                       cairo_region_t    *unobscured_region)
{
  MetaShapedTexturePrivate *priv = self->priv;

  g_clear_pointer (&priv->unobscured_region, (GDestroyNotify) cairo_region_destroy);
  if (unobscured_region)
    {
      cairo_rectangle_int_t bounds = { 0, 0, priv->tex_width, priv->tex_height };
      priv->unobscured_region = cairo_region_copy (unobscured_region);
      cairo_region_intersect_rectangle (priv->unobscured_region, &bounds);
    }
}

132 133 134 135 136 137 138 139 140 141 142
static void
set_clip_region (MetaShapedTexture *self,
                 cairo_region_t    *clip_region)
{
  MetaShapedTexturePrivate *priv = self->priv;

  g_clear_pointer (&priv->clip_region, (GDestroyNotify) cairo_region_destroy);
  if (clip_region)
    priv->clip_region = cairo_region_copy (clip_region);
}

143
static void
144
meta_shaped_texture_dispose (GObject *object)
145
{
146 147
  MetaShapedTexture *self = (MetaShapedTexture *) object;
  MetaShapedTexturePrivate *priv = self->priv;
148

149
  if (priv->paint_tower)
150
    meta_texture_tower_free (priv->paint_tower);
151 152
  priv->paint_tower = NULL;

153
  g_clear_pointer (&priv->texture, cogl_object_unref);
154
  g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
155

156
  meta_shaped_texture_set_mask_texture (self, NULL);
157
  set_unobscured_region (self, NULL);
158
  set_clip_region (self, NULL);
159

160
  G_OBJECT_CLASS (meta_shaped_texture_parent_class)->dispose (object);
161 162
}

163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
static CoglPipeline *
get_unmasked_pipeline (CoglContext *ctx)
{
  return cogl_pipeline_new (ctx);
}

static CoglPipeline *
get_masked_pipeline (CoglContext *ctx)
{
  static CoglPipeline *template = NULL;
  if (G_UNLIKELY (template == NULL))
    {
      template = cogl_pipeline_new (ctx);
      cogl_pipeline_set_layer_combine (template, 1,
                                       "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
                                       NULL);
    }

  return cogl_pipeline_copy (template);
}

184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
static CoglPipeline *
get_unblended_pipeline (CoglContext *ctx)
{
  static CoglPipeline *template = NULL;
  if (G_UNLIKELY (template == NULL))
    {
      CoglColor color;
      template = cogl_pipeline_new (ctx);
      cogl_color_init_from_4ub (&color, 255, 255, 255, 255);
      cogl_pipeline_set_blend (template,
                               "RGBA = ADD (SRC_COLOR, 0)",
                               NULL);
      cogl_pipeline_set_color (template, &color);
    }

  return cogl_pipeline_copy (template);
}

static void
paint_clipped_rectangle (CoglFramebuffer       *fb,
                         CoglPipeline          *pipeline,
                         cairo_rectangle_int_t *rect,
                         ClutterActorBox       *alloc)
{
  float coords[8];
  float x1, y1, x2, y2;

  x1 = rect->x;
  y1 = rect->y;
  x2 = rect->x + rect->width;
  y2 = rect->y + rect->height;

  coords[0] = rect->x / (alloc->x2 - alloc->x1);
  coords[1] = rect->y / (alloc->y2 - alloc->y1);
  coords[2] = (rect->x + rect->width) / (alloc->x2 - alloc->x1);
  coords[3] = (rect->y + rect->height) / (alloc->y2 - alloc->y1);

  coords[4] = coords[0];
  coords[5] = coords[1];
  coords[6] = coords[2];
  coords[7] = coords[3];

  cogl_framebuffer_draw_multitextured_rectangle (fb, pipeline,
                                                 x1, y1, x2, y2,
                                                 &coords[0], 8);
}

231 232 233 234 235 236 237 238 239 240 241 242 243 244
static void
set_cogl_texture (MetaShapedTexture *stex,
                  CoglTexture       *cogl_tex)
{
  MetaShapedTexturePrivate *priv;
  guint width, height;

  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));

  priv = stex->priv;

  if (priv->texture)
    cogl_object_unref (priv->texture);

245
  priv->texture = cogl_tex;
246 247 248

  if (cogl_tex != NULL)
    {
249
      cogl_object_ref (cogl_tex);
250 251 252 253 254
      width = cogl_texture_get_width (COGL_TEXTURE (cogl_tex));
      height = cogl_texture_get_height (COGL_TEXTURE (cogl_tex));
    }
  else
    {
255 256 257 258 259 260 261 262 263
      width = 0;
      height = 0;
    }

  if (priv->tex_width != width ||
      priv->tex_height != height)
    {
      priv->tex_width = width;
      priv->tex_height = height;
264 265 266 267 268 269 270
      clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
    }

  /* NB: We don't queue a redraw of the actor here because we don't
   * know how much of the buffer has changed with respect to the
   * previous buffer. We only queue a redraw in response to surface
   * damage. */
271 272 273

  if (priv->create_mipmaps)
    meta_texture_tower_set_base_texture (priv->paint_tower, cogl_tex);
274 275
}

276
static void
277
meta_shaped_texture_paint (ClutterActor *actor)
278
{
279 280
  MetaShapedTexture *stex = (MetaShapedTexture *) actor;
  MetaShapedTexturePrivate *priv = stex->priv;
281
  guint tex_width, tex_height;
282
  guchar opacity;
283
  CoglContext *ctx;
284 285 286 287
  CoglFramebuffer *fb;
  CoglPipeline *pipeline = NULL;
  CoglTexture *paint_tex;
  ClutterActorBox alloc;
288
  cairo_region_t *blended_region = NULL;
289
  CoglPipelineFilter filter;
290

Owen W. Taylor's avatar
Owen W. Taylor committed
291
  if (priv->clip_region && cairo_region_is_empty (priv->clip_region))
292 293
    return;

294 295 296
  if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (stex)))
    clutter_actor_realize (CLUTTER_ACTOR (stex));

297 298 299 300
  /* The GL EXT_texture_from_pixmap extension does allow for it to be
   * used together with SGIS_generate_mipmap, however this is very
   * rarely supported. Also, even when it is supported there
   * are distinct performance implications from:
301
   *
302 303
   *  - Updating mipmaps that we don't need
   *  - Having to reallocate pixmaps on the server into larger buffers
304
   *
305 306 307 308 309 310
   * So, we just unconditionally use our mipmap emulation code. If we
   * wanted to use SGIS_generate_mipmap, we'd have to  query COGL to
   * see if it was supported (no API currently), and then if and only
   * if that was the case, set the clutter texture quality to HIGH.
   * Setting the texture quality to high without SGIS_generate_mipmap
   * support for TFP textures will result in fallbacks to XGetImage.
311
   */
312
  if (priv->create_mipmaps)
313
    paint_tex = meta_texture_tower_get_paint_texture (priv->paint_tower);
314
  else
315
    paint_tex = COGL_TEXTURE (priv->texture);
316

317
  if (paint_tex == NULL)
318
    return;
319

320 321
  tex_width = priv->tex_width;
  tex_height = priv->tex_height;
322

323
  if (tex_width == 0 || tex_height == 0) /* no contents yet */
324 325
    return;

326 327 328 329 330 331
  /* Use nearest-pixel interpolation if the texture is unscaled. This
   * improves performance, especially with software rendering.
   */

  filter = COGL_PIPELINE_FILTER_LINEAR;

332 333
  if (!clutter_actor_is_in_clone_paint (actor) && meta_actor_is_untransformed (actor, NULL, NULL))
    filter = COGL_PIPELINE_FILTER_NEAREST;
334

335
  ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
336
  fb = cogl_get_draw_framebuffer ();
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 362
  opacity = clutter_actor_get_paint_opacity (actor);
  clutter_actor_get_allocation_box (actor, &alloc);

  if (priv->opaque_region != NULL && opacity == 255)
    {
      CoglPipeline *opaque_pipeline;
      cairo_region_t *region;
      int n_rects;
      int i;

      if (priv->clip_region != NULL)
        {
          region = cairo_region_copy (priv->clip_region);
          cairo_region_intersect (region, priv->opaque_region);
        }
      else
        {
          region = cairo_region_reference (priv->opaque_region);
        }

      if (cairo_region_is_empty (region))
        goto paint_blended;

      opaque_pipeline = get_unblended_pipeline (ctx);
      cogl_pipeline_set_layer_texture (opaque_pipeline, 0, paint_tex);
363
      cogl_pipeline_set_layer_filters (opaque_pipeline, 0, filter, filter);
364 365 366 367 368 369 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

      n_rects = cairo_region_num_rectangles (region);
      for (i = 0; i < n_rects; i++)
        {
          cairo_rectangle_int_t rect;
          cairo_region_get_rectangle (region, i, &rect);
          paint_clipped_rectangle (fb, opaque_pipeline, &rect, &alloc);
        }

      cogl_object_unref (opaque_pipeline);

      if (priv->clip_region != NULL)
        {
          blended_region = cairo_region_copy (priv->clip_region);
        }
      else
        {
          cairo_rectangle_int_t rect = { 0, 0, tex_width, tex_height };
          blended_region = cairo_region_create_rectangle (&rect);
        }

      cairo_region_subtract (blended_region, priv->opaque_region);

    paint_blended:
      cairo_region_destroy (region);
    }

  if (blended_region == NULL && priv->clip_region != NULL)
    blended_region = cairo_region_reference (priv->clip_region);

  if (blended_region != NULL && cairo_region_is_empty (blended_region))
    goto out;

397
  if (priv->mask_texture == NULL)
398
    {
399
      pipeline = get_unmasked_pipeline (ctx);
400 401
    }
  else
402
    {
403
      pipeline = get_masked_pipeline (ctx);
404
      cogl_pipeline_set_layer_texture (pipeline, 1, priv->mask_texture);
405
      cogl_pipeline_set_layer_filters (pipeline, 1, filter, filter);
406 407
    }

408
  cogl_pipeline_set_layer_texture (pipeline, 0, paint_tex);
409
  cogl_pipeline_set_layer_filters (pipeline, 0, filter, filter);
410

411 412
  {
    CoglColor color;
413
    cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity);
414
    cogl_pipeline_set_color (pipeline, &color);
415
  }
416

417
  if (blended_region != NULL)
418 419 420 421 422 423 424
    {
      int n_rects;

      /* Limit to how many separate rectangles we'll draw; beyond this just
       * fall back and draw the whole thing */
#     define MAX_RECTS 16

425
      n_rects = cairo_region_num_rectangles (blended_region);
Owen W. Taylor's avatar
Owen W. Taylor committed
426
      if (n_rects <= MAX_RECTS)
427
	{
428 429
          int i;
          cairo_rectangle_int_t tex_rect = { 0, 0, tex_width, tex_height };
430

431 432
	  for (i = 0; i < n_rects; i++)
	    {
Owen W. Taylor's avatar
Owen W. Taylor committed
433
	      cairo_rectangle_int_t rect;
434

435
	      cairo_region_get_rectangle (blended_region, i, &rect);
436

437 438 439
	      if (!gdk_rectangle_intersect (&tex_rect, &rect, &rect))
		continue;

440
              paint_clipped_rectangle (fb, pipeline, &rect, &alloc);
441
            }
442

443
          goto out;
444 445 446
	}
    }

447 448 449 450
  cogl_framebuffer_draw_rectangle (fb, pipeline,
                                   0, 0,
                                   alloc.x2 - alloc.x1,
                                   alloc.y2 - alloc.y1);
451 452

 out:
453 454
  if (pipeline != NULL)
    cogl_object_unref (pipeline);
455 456
  if (blended_region != NULL)
    cairo_region_destroy (blended_region);
457 458
}

459
static void
460 461 462 463
meta_shaped_texture_get_preferred_width (ClutterActor *self,
                                         gfloat        for_height,
                                         gfloat       *min_width_p,
                                         gfloat       *natural_width_p)
464
{
465
  MetaShapedTexturePrivate *priv;
466

467
  g_return_if_fail (META_IS_SHAPED_TEXTURE (self));
468

469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
  priv = META_SHAPED_TEXTURE (self)->priv;

  if (min_width_p)
    *min_width_p = 0;

  if (natural_width_p)
    *natural_width_p = priv->tex_width;
}

static void
meta_shaped_texture_get_preferred_height (ClutterActor *self,
                                          gfloat        for_width,
                                          gfloat       *min_height_p,
                                          gfloat       *natural_height_p)
{
  MetaShapedTexturePrivate *priv;

  g_return_if_fail (META_IS_SHAPED_TEXTURE (self));

  priv = META_SHAPED_TEXTURE (self)->priv;

  if (min_height_p)
    *min_height_p = 0;

  if (natural_height_p)
    *natural_height_p = priv->tex_height;
495 496
}

497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
static cairo_region_t *
effective_unobscured_region (MetaShapedTexture *self)
{
  MetaShapedTexturePrivate *priv = self->priv;
  ClutterActor *parent = clutter_actor_get_parent (CLUTTER_ACTOR (self));

  if (clutter_actor_has_mapped_clones (CLUTTER_ACTOR (self)))
    return NULL;

  while (parent && !META_IS_WINDOW_ACTOR (parent))
    parent = clutter_actor_get_parent (parent);

  if (parent && clutter_actor_has_mapped_clones (parent))
    return NULL;

  return priv->unobscured_region;
}

static gboolean
get_unobscured_bounds (MetaShapedTexture     *self,
                       cairo_rectangle_int_t *unobscured_bounds)
{
  cairo_region_t *unobscured_region = effective_unobscured_region (self);

  if (unobscured_region)
    {
      cairo_region_get_extents (unobscured_region, unobscured_bounds);
      return TRUE;
    }
  else
    return FALSE;
}

530
static gboolean
531
meta_shaped_texture_get_paint_volume (ClutterActor *actor,
532 533
                                      ClutterPaintVolume *volume)
{
534
  MetaShapedTexture *self = META_SHAPED_TEXTURE (actor);
535
  ClutterActorBox box;
536 537
  cairo_rectangle_int_t unobscured_bounds;

538
  if (!clutter_actor_has_allocation (actor))
539 540
    return FALSE;

541 542
  clutter_actor_get_allocation_box (actor, &box);

543
  if (get_unobscured_bounds (self, &unobscured_bounds))
544
    {
545 546 547 548
      box.x1 = MAX (unobscured_bounds.x, box.x1);
      box.x2 = MIN (unobscured_bounds.x + unobscured_bounds.width, box.x2);
      box.y1 = MAX (unobscured_bounds.y, box.y1);
      box.y2 = MIN (unobscured_bounds.y + unobscured_bounds.height, box.y2);
549
    }
550 551
  box.x2 = MAX (box.x2, box.x1);
  box.y2 = MAX (box.y2, box.y1);
552

553
  clutter_paint_volume_union_box (volume, &box);
554
  return TRUE;
555 556
}

557
void
558 559
meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
					gboolean           create_mipmaps)
560
{
561
  MetaShapedTexturePrivate *priv;
562

563
  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
564 565 566 567 568 569 570

  priv = stex->priv;

  create_mipmaps = create_mipmaps != FALSE;

  if (create_mipmaps != priv->create_mipmaps)
    {
571
      CoglTexture *base_texture;
572
      priv->create_mipmaps = create_mipmaps;
573
      base_texture = create_mipmaps ? priv->texture : NULL;
574
      meta_texture_tower_set_base_texture (priv->paint_tower, base_texture);
575 576 577
    }
}

578
void
579
meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
580
                                      CoglTexture       *mask_texture)
581
{
582
  MetaShapedTexturePrivate *priv;
583

584
  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
585 586 587

  priv = stex->priv;

588
  g_clear_pointer (&priv->mask_texture, cogl_object_unref);
589

590
  if (mask_texture != NULL)
591
    {
592
      priv->mask_texture = mask_texture;
593
      cogl_object_ref (priv->mask_texture);
594
    }
595

596
  clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
597 598
}

599 600 601 602 603 604 605 606 607 608 609
gboolean
meta_shaped_texture_is_obscured (MetaShapedTexture *self)
{
  cairo_region_t *unobscured_region = effective_unobscured_region (self);

  if (unobscured_region)
    return cairo_region_is_empty (unobscured_region);
  else
    return FALSE;
}

610 611 612 613 614 615 616 617 618
/**
 * meta_shaped_texture_update_area:
 * @stex: #MetaShapedTexture
 * @x: the x coordinate of the damaged area
 * @y: the y coordinate of the damaged area
 * @width: the width of the damaged area
 * @height: the height of the damaged area
 *
 * Repairs the damaged area indicated by @x, @y, @width and @height
619
 * and potentially queues a redraw.
620 621 622 623
 *
 * Return value: Whether a redraw have been queued or not
 */
gboolean
624
meta_shaped_texture_update_area (MetaShapedTexture *stex,
625 626 627
				 int                x,
				 int                y,
				 int                width,
628
				 int                height)
629 630
{
  MetaShapedTexturePrivate *priv;
631
  cairo_region_t *unobscured_region;
632
  const cairo_rectangle_int_t clip = { x, y, width, height };
633

634
  priv = stex->priv;
635

636
  if (priv->texture == NULL)
637
    return FALSE;
638 639 640

  meta_texture_tower_update_area (priv->paint_tower, x, y, width, height);

641
  unobscured_region = effective_unobscured_region (stex);
642 643 644 645 646 647 648 649
  if (unobscured_region)
    {
      cairo_region_t *intersection;

      if (cairo_region_is_empty (unobscured_region))
        return FALSE;

      intersection = cairo_region_copy (unobscured_region);
650
      cairo_region_intersect_rectangle (intersection, &clip);
651 652 653 654 655 656 657 658 659 660 661 662 663

      if (!cairo_region_is_empty (intersection))
        {
          cairo_rectangle_int_t damage_rect;
          cairo_region_get_extents (intersection, &damage_rect);
          clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &damage_rect);
          cairo_region_destroy (intersection);
          return TRUE;
        }

      cairo_region_destroy (intersection);
      return FALSE;
    }
664 665 666 667 668
  else
    {
      clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &clip);
      return TRUE;
    }
669
}
670

671
/**
672
 * meta_shaped_texture_set_texture:
673
 * @stex: The #MetaShapedTexture
674
 * @pixmap: The #CoglTexture to display
675 676
 */
void
677 678
meta_shaped_texture_set_texture (MetaShapedTexture *stex,
                                 CoglTexture       *texture)
679 680 681
{
  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));

682
  set_cogl_texture (stex, texture);
683 684 685 686 687 688 689 690
}

/**
 * meta_shaped_texture_get_texture:
 * @stex: The #MetaShapedTexture
 *
 * Returns: (transfer none): the unshaped texture
 */
691
CoglTexture *
692 693
meta_shaped_texture_get_texture (MetaShapedTexture *stex)
{
694 695
  g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL);
  return COGL_TEXTURE (stex->priv->texture);
696 697
}

698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727
/**
 * meta_shaped_texture_set_opaque_region:
 * @stex: a #MetaShapedTexture
 * @opaque_region: (transfer full): the region of the texture that
 *   can have blending turned off.
 *
 * As most windows have a large portion that does not require blending,
 * we can easily turn off blending if we know the areas that do not
 * require blending. This sets the region where we will not blend for
 * optimization purposes.
 */
void
meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex,
                                       cairo_region_t    *opaque_region)
{
  MetaShapedTexturePrivate *priv;

  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));

  priv = stex->priv;

  if (priv->opaque_region)
    cairo_region_destroy (priv->opaque_region);

  if (opaque_region)
    priv->opaque_region = cairo_region_reference (opaque_region);
  else
    priv->opaque_region = NULL;
}

728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
/**
 * meta_shaped_texture_get_image:
 * @stex: A #MetaShapedTexture
 * @clip: A clipping rectangle, to help prevent extra processing.
 * In the case that the clipping rectangle is partially or fully
 * outside the bounds of the texture, the rectangle will be clipped.
 *
 * Flattens the two layers of the shaped texture into one ARGB32
 * image by alpha blending the two images, and returns the flattened
 * image.
 *
 * Returns: (transfer full): a new cairo surface to be freed with
 * cairo_surface_destroy().
 */
cairo_surface_t *
meta_shaped_texture_get_image (MetaShapedTexture     *stex,
                               cairo_rectangle_int_t *clip)
{
746
  CoglTexture *texture, *mask_texture;
747 748 749 750 751
  cairo_rectangle_int_t texture_rect = { 0, 0, 0, 0 };
  cairo_surface_t *surface;

  g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL);

752
  texture = COGL_TEXTURE (stex->priv->texture);
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788

  if (texture == NULL)
    return NULL;

  texture_rect.width = cogl_texture_get_width (texture);
  texture_rect.height = cogl_texture_get_height (texture);

  if (clip != NULL)
    {
      /* GdkRectangle is just a typedef of cairo_rectangle_int_t,
       * so we can use the gdk_rectangle_* APIs on these. */
      if (!gdk_rectangle_intersect (&texture_rect, clip, clip))
        return NULL;
    }

  if (clip != NULL)
    texture = cogl_texture_new_from_sub_texture (texture,
                                                 clip->x,
                                                 clip->y,
                                                 clip->width,
                                                 clip->height);

  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
                                        cogl_texture_get_width (texture),
                                        cogl_texture_get_height (texture));

  cogl_texture_get_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32,
                         cairo_image_surface_get_stride (surface),
                         cairo_image_surface_get_data (surface));

  cairo_surface_mark_dirty (surface);

  if (clip != NULL)
    cogl_object_unref (texture);

  mask_texture = stex->priv->mask_texture;
789
  if (mask_texture != NULL)
790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824
    {
      cairo_t *cr;
      cairo_surface_t *mask_surface;

      if (clip != NULL)
        mask_texture = cogl_texture_new_from_sub_texture (mask_texture,
                                                          clip->x,
                                                          clip->y,
                                                          clip->width,
                                                          clip->height);

      mask_surface = cairo_image_surface_create (CAIRO_FORMAT_A8,
                                                 cogl_texture_get_width (mask_texture),
                                                 cogl_texture_get_height (mask_texture));

      cogl_texture_get_data (mask_texture, COGL_PIXEL_FORMAT_A_8,
                             cairo_image_surface_get_stride (mask_surface),
                             cairo_image_surface_get_data (mask_surface));

      cairo_surface_mark_dirty (mask_surface);

      cr = cairo_create (surface);
      cairo_set_source_surface (cr, mask_surface, 0, 0);
      cairo_set_operator (cr, CAIRO_OPERATOR_DEST_IN);
      cairo_paint (cr);
      cairo_destroy (cr);

      cairo_surface_destroy (mask_surface);

      if (clip != NULL)
        cogl_object_unref (mask_texture);
    }

  return surface;
}
825

826 827 828 829 830 831 832 833
static void
meta_shaped_texture_cull_out (MetaCullable   *cullable,
                              cairo_region_t *unobscured_region,
                              cairo_region_t *clip_region)
{
  MetaShapedTexture *self = META_SHAPED_TEXTURE (cullable);
  MetaShapedTexturePrivate *priv = self->priv;

834
  set_unobscured_region (self, unobscured_region);
835 836 837 838 839 840
  set_clip_region (self, clip_region);

  if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (self)) == 0xff)
    {
      if (priv->opaque_region)
        {
841 842 843 844
          if (unobscured_region)
            cairo_region_subtract (unobscured_region, priv->opaque_region);
          if (clip_region)
            cairo_region_subtract (clip_region, priv->opaque_region);
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
        }
    }
}

static void
meta_shaped_texture_reset_culling (MetaCullable *cullable)
{
  MetaShapedTexture *self = META_SHAPED_TEXTURE (cullable);
  set_clip_region (self, NULL);
}

static void
cullable_iface_init (MetaCullableInterface *iface)
{
  iface->cull_out = meta_shaped_texture_cull_out;
  iface->reset_culling = meta_shaped_texture_reset_culling;
}

863 864 865 866 867
ClutterActor *
meta_shaped_texture_new (void)
{
  return g_object_new (META_TYPE_SHAPED_TEXTURE, NULL);
}