meta-shaped-texture.c 27.4 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 19 20 21 22 23
 *
 * 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
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 */

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

30 31
#include <config.h>

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

37
#include "meta-shaped-texture-private.h"
38

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

43
static void meta_shaped_texture_dispose  (GObject    *object);
44

45 46 47
static void meta_shaped_texture_paint (ClutterActor       *actor);
static void meta_shaped_texture_pick  (ClutterActor       *actor,
				       const ClutterColor *color);
48

49 50 51 52 53 54 55 56 57
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);
58

59 60
static gboolean meta_shaped_texture_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume);

61
G_DEFINE_TYPE (MetaShapedTexture, meta_shaped_texture, CLUTTER_TYPE_ACTOR);
62

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

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

  CoglTexture *texture;
72
  CoglTexture *mask_texture;
73

Owen W. Taylor's avatar
Owen W. Taylor committed
74
  cairo_region_t *clip_region;
75
  cairo_region_t *input_shape_region;
76
  cairo_region_t *opaque_region;
77

78
  guint tex_width, tex_height;
79

80
  guint create_mipmaps : 1;
81 82 83
};

static void
84
meta_shaped_texture_class_init (MetaShapedTextureClass *klass)
85 86 87 88
{
  GObjectClass *gobject_class = (GObjectClass *) klass;
  ClutterActorClass *actor_class = (ClutterActorClass *) klass;

89
  gobject_class->dispose = meta_shaped_texture_dispose;
90

91 92
  actor_class->get_preferred_width = meta_shaped_texture_get_preferred_width;
  actor_class->get_preferred_height = meta_shaped_texture_get_preferred_height;
93 94
  actor_class->paint = meta_shaped_texture_paint;
  actor_class->pick = meta_shaped_texture_pick;
95
  actor_class->get_paint_volume = meta_shaped_texture_get_paint_volume;
96

97
  g_type_class_add_private (klass, sizeof (MetaShapedTexturePrivate));
98 99 100
}

static void
101
meta_shaped_texture_init (MetaShapedTexture *self)
102
{
103
  MetaShapedTexturePrivate *priv;
104

105
  priv = self->priv = META_SHAPED_TEXTURE_GET_PRIVATE (self);
106

107
  priv->paint_tower = meta_texture_tower_new ();
108

109 110
  priv->texture = NULL;
  priv->mask_texture = NULL;
111
  priv->create_mipmaps = TRUE;
112 113 114
}

static void
115
meta_shaped_texture_dispose (GObject *object)
116
{
117 118
  MetaShapedTexture *self = (MetaShapedTexture *) object;
  MetaShapedTexturePrivate *priv = self->priv;
119

120
  if (priv->paint_tower)
121
    meta_texture_tower_free (priv->paint_tower);
122 123
  priv->paint_tower = NULL;

124
  g_clear_pointer (&priv->texture, cogl_object_unref);
125
  g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
126

127
  meta_shaped_texture_set_mask_texture (self, NULL);
128
  meta_shaped_texture_set_clip_region (self, NULL);
129

130
  G_OBJECT_CLASS (meta_shaped_texture_parent_class)->dispose (object);
131 132
}

133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
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);
}

154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
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);
}

201 202 203 204 205 206 207 208 209 210 211 212 213 214
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);

215
  priv->texture = cogl_object_ref (cogl_tex);
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242

  if (cogl_tex != NULL)
    {
      width = cogl_texture_get_width (COGL_TEXTURE (cogl_tex));
      height = cogl_texture_get_height (COGL_TEXTURE (cogl_tex));

      if (width != priv->tex_width ||
          height != priv->tex_height)
        {
          priv->tex_width = width;
          priv->tex_height = height;

          clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
        }
    }
  else
    {
      /* size changed to 0 going to an invalid handle */
      priv->tex_width = 0;
      priv->tex_height = 0;
      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. */
243 244 245

  if (priv->create_mipmaps)
    meta_texture_tower_set_base_texture (priv->paint_tower, cogl_tex);
246 247
}

248
static void
249
meta_shaped_texture_paint (ClutterActor *actor)
250
{
251 252
  MetaShapedTexture *stex = (MetaShapedTexture *) actor;
  MetaShapedTexturePrivate *priv = stex->priv;
253
  guint tex_width, tex_height;
254
  guchar opacity;
255
  CoglContext *ctx;
256 257 258 259
  CoglFramebuffer *fb;
  CoglPipeline *pipeline = NULL;
  CoglTexture *paint_tex;
  ClutterActorBox alloc;
260
  cairo_region_t *blended_region = NULL;
261
  CoglPipelineFilter filter;
262

Owen W. Taylor's avatar
Owen W. Taylor committed
263
  if (priv->clip_region && cairo_region_is_empty (priv->clip_region))
264 265
    return;

266 267 268
  if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (stex)))
    clutter_actor_realize (CLUTTER_ACTOR (stex));

269 270 271 272
  /* 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:
273
   *
274 275
   *  - Updating mipmaps that we don't need
   *  - Having to reallocate pixmaps on the server into larger buffers
276
   *
277 278 279 280 281 282
   * 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.
283
   */
284
  if (priv->create_mipmaps)
285
    paint_tex = meta_texture_tower_get_paint_texture (priv->paint_tower);
286
  else
287
    paint_tex = COGL_TEXTURE (priv->texture);
288

289
  if (paint_tex == NULL)
290
    return;
291

292 293
  tex_width = priv->tex_width;
  tex_height = priv->tex_height;
294

295
  if (tex_width == 0 || tex_height == 0) /* no contents yet */
296 297
    return;

298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
  /* Use nearest-pixel interpolation if the texture is unscaled. This
   * improves performance, especially with software rendering.
   */

  filter = COGL_PIPELINE_FILTER_LINEAR;

  if (!clutter_actor_is_in_clone_paint (actor))
    {
      int x_origin, y_origin;

      if (meta_actor_is_untransformed (actor,
                                       &x_origin,
                                       &y_origin))
        filter = COGL_PIPELINE_FILTER_NEAREST;
    }

314
  ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
315
  fb = cogl_get_draw_framebuffer ();
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
  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);
342
      cogl_pipeline_set_layer_filters (opaque_pipeline, 0, filter, filter);
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375

      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;

376
  if (priv->mask_texture == NULL)
377
    {
378
      pipeline = get_unmasked_pipeline (ctx);
379 380
    }
  else
381
    {
382
      pipeline = get_masked_pipeline (ctx);
383
      cogl_pipeline_set_layer_texture (pipeline, 1, priv->mask_texture);
384
      cogl_pipeline_set_layer_filters (pipeline, 1, filter, filter);
385 386
    }

387
  cogl_pipeline_set_layer_texture (pipeline, 0, paint_tex);
388
  cogl_pipeline_set_layer_filters (pipeline, 0, filter, filter);
389

390 391
  {
    CoglColor color;
392
    cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity);
393
    cogl_pipeline_set_color (pipeline, &color);
394
  }
395

396
  if (blended_region != NULL)
397 398 399 400 401 402 403
    {
      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

404
      n_rects = cairo_region_num_rectangles (blended_region);
Owen W. Taylor's avatar
Owen W. Taylor committed
405
      if (n_rects <= MAX_RECTS)
406
	{
407 408
          int i;
          cairo_rectangle_int_t tex_rect = { 0, 0, tex_width, tex_height };
409

410 411
	  for (i = 0; i < n_rects; i++)
	    {
Owen W. Taylor's avatar
Owen W. Taylor committed
412
	      cairo_rectangle_int_t rect;
413

414
	      cairo_region_get_rectangle (blended_region, i, &rect);
415

416 417 418
	      if (!gdk_rectangle_intersect (&tex_rect, &rect, &rect))
		continue;

419
              paint_clipped_rectangle (fb, pipeline, &rect, &alloc);
420
            }
421

422
          goto out;
423 424 425
	}
    }

426 427 428 429
  cogl_framebuffer_draw_rectangle (fb, pipeline,
                                   0, 0,
                                   alloc.x2 - alloc.x1,
                                   alloc.y2 - alloc.y1);
430 431

 out:
432 433
  if (pipeline != NULL)
    cogl_object_unref (pipeline);
434 435
  if (blended_region != NULL)
    cairo_region_destroy (blended_region);
436 437 438
}

static void
439 440
meta_shaped_texture_pick (ClutterActor       *actor,
			  const ClutterColor *color)
441
{
442 443
  MetaShapedTexture *stex = (MetaShapedTexture *) actor;
  MetaShapedTexturePrivate *priv = stex->priv;
444

445 446 447 448
  if (!clutter_actor_should_pick_paint (actor) ||
      (priv->clip_region && cairo_region_is_empty (priv->clip_region)))
    return;

449
  /* If there is no region then use the regular pick */
450
  if (priv->input_shape_region == NULL)
451
    CLUTTER_ACTOR_CLASS (meta_shaped_texture_parent_class)->pick (actor, color);
452
  else
453
    {
454 455 456
      int n_rects;
      float *rectangles;
      int i;
457 458 459 460
      CoglPipeline *pipeline;
      CoglContext *ctx;
      CoglFramebuffer *fb;
      CoglColor cogl_color;
461

462 463 464 465 466 467 468 469 470 471
      /* Note: We don't bother trying to intersect the pick and clip regions
       * since needing to copy the region, do the intersection, and probably
       * increase the number of rectangles seems more likely to have a negative
       * effect.
       *
       * NB: Most of the time when just using rectangles for picking then
       * picking shouldn't involve any rendering, and minimizing the number of
       * rectangles has more benefit than reducing the area of the pick
       * region.
       */
472

473
      n_rects = cairo_region_num_rectangles (priv->input_shape_region);
474
      rectangles = g_alloca (sizeof (float) * 4 * n_rects);
475

476 477 478 479
      for (i = 0; i < n_rects; i++)
        {
          cairo_rectangle_int_t rect;
          int pos = i * 4;
480

481
          cairo_region_get_rectangle (priv->input_shape_region, i, &rect);
482

483 484 485 486 487
          rectangles[pos] = rect.x;
          rectangles[pos + 1] = rect.y;
          rectangles[pos + 2] = rect.x + rect.width;
          rectangles[pos + 3] = rect.y + rect.height;
        }
488

489 490 491 492 493 494 495
      ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
      fb = cogl_get_draw_framebuffer ();

      cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha);

      pipeline = cogl_pipeline_new (ctx);
      cogl_pipeline_set_color (pipeline, &cogl_color);
496

497 498 499
      cogl_framebuffer_draw_rectangles (fb, pipeline,
                                        rectangles, n_rects);
      cogl_object_unref (pipeline);
500 501 502
    }
}

503
static void
504 505 506 507
meta_shaped_texture_get_preferred_width (ClutterActor *self,
                                         gfloat        for_height,
                                         gfloat       *min_width_p,
                                         gfloat       *natural_width_p)
508
{
509
  MetaShapedTexturePrivate *priv;
510

511
  g_return_if_fail (META_IS_SHAPED_TEXTURE (self));
512

513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
  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;
539 540
}

541 542 543 544 545 546 547
static gboolean
meta_shaped_texture_get_paint_volume (ClutterActor *self,
                                      ClutterPaintVolume *volume)
{
  return clutter_paint_volume_set_from_allocation (volume, self);
}

548
void
549 550
meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
					gboolean           create_mipmaps)
551
{
552
  MetaShapedTexturePrivate *priv;
553

554
  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
555 556 557 558 559 560 561

  priv = stex->priv;

  create_mipmaps = create_mipmaps != FALSE;

  if (create_mipmaps != priv->create_mipmaps)
    {
562
      CoglTexture *base_texture;
563
      priv->create_mipmaps = create_mipmaps;
564
      base_texture = create_mipmaps ? priv->texture : NULL;
565
      meta_texture_tower_set_base_texture (priv->paint_tower, base_texture);
566 567 568
    }
}

569
void
570
meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
571
                                      CoglTexture       *mask_texture)
572
{
573
  MetaShapedTexturePrivate *priv;
574

575
  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
576 577 578

  priv = stex->priv;

579
  g_clear_pointer (&priv->mask_texture, cogl_object_unref);
580

581
  if (mask_texture != NULL)
582
    {
583
      priv->mask_texture = mask_texture;
584
      cogl_object_ref (priv->mask_texture);
585
    }
586

587
  clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
588 589
}

590
static gboolean
591 592 593 594 595 596
get_clip (MetaShapedTexture *stex,
          int x,
          int y,
          int width,
          int height,
          cairo_rectangle_int_t *clip)
597
{
598
  ClutterActor *self = CLUTTER_ACTOR (stex);
599
  MetaShapedTexturePrivate *priv;
600 601 602 603 604 605 606 607
  ClutterActorBox allocation;
  float scale_x;
  float scale_y;

  /* NB: clutter_actor_queue_redraw_with_clip expects a box in the actor's
   * coordinate space so we need to convert from surface coordinates to
   * actor coordinates...
   */
608

609 610 611 612 613 614 615
  /* Calling clutter_actor_get_allocation_box() is enormously expensive
   * if the actor has an out-of-date allocation, since it triggers
   * a full redraw. clutter_actor_queue_redraw_with_clip() would redraw
   * the whole stage anyways in that case, so just go ahead and do
   * it here.
   */
  if (!clutter_actor_has_allocation (self))
616
    return FALSE;
617 618 619

  priv = stex->priv;

620
  if (priv->tex_width == 0 || priv->tex_height == 0)
621
    return FALSE;
622

623
  clutter_actor_get_allocation_box (self, &allocation);
624

625 626
  scale_x = (allocation.x2 - allocation.x1) / priv->tex_width;
  scale_y = (allocation.y2 - allocation.y1) / priv->tex_height;
627

628 629 630 631
  clip->x = x * scale_x;
  clip->y = y * scale_y;
  clip->width = width * scale_x;
  clip->height = height * scale_y;
632 633

  return TRUE;
634
}
635

636 637 638 639 640 641 642 643 644 645 646 647
/**
 * 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
 * @unobscured_region: The unobscured region of the window or %NULL if
 * there is no valid one (like when the actor is transformed or
 * has a mapped clone)
 *
 * Repairs the damaged area indicated by @x, @y, @width and @height
648 649
 * and queues a redraw for the intersection @unobscured_region and
 * the damage area. If @unobscured_region is %NULL a redraw will always
650 651 652 653 654
 * get queued.
 *
 * Return value: Whether a redraw have been queued or not
 */
gboolean
655
meta_shaped_texture_update_area (MetaShapedTexture *stex,
656 657 658 659 660
				 int                x,
				 int                y,
				 int                width,
				 int                height,
				 cairo_region_t    *unobscured_region)
661 662
{
  MetaShapedTexturePrivate *priv;
663
  cairo_rectangle_int_t clip;
664
  gboolean has_clip;
665

666
  priv = stex->priv;
667

668
  if (priv->texture == NULL)
669
    return FALSE;
670 671 672

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

673
  has_clip = get_clip (stex, x, y, width, height, &clip);
674

675 676 677 678 679 680 681 682
  if (unobscured_region)
    {
      cairo_region_t *intersection;

      if (cairo_region_is_empty (unobscured_region))
        return FALSE;

      intersection = cairo_region_copy (unobscured_region);
683 684
      if (has_clip)
	cairo_region_intersect_rectangle (intersection, &clip);
685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700

      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;
    }

701 702 703 704
  if (has_clip)
    clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &clip);
  else
    clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
705 706

  return TRUE;
707
}
708

709
/**
710
 * meta_shaped_texture_set_texture:
711
 * @stex: The #MetaShapedTexture
712
 * @pixmap: The #CoglTexture to display
713 714
 */
void
715 716
meta_shaped_texture_set_texture (MetaShapedTexture *stex,
                                 CoglTexture       *texture)
717 718 719
{
  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));

720
  set_cogl_texture (stex, texture);
721 722 723 724 725 726 727 728
}

/**
 * meta_shaped_texture_get_texture:
 * @stex: The #MetaShapedTexture
 *
 * Returns: (transfer none): the unshaped texture
 */
729
CoglTexture *
730 731
meta_shaped_texture_get_texture (MetaShapedTexture *stex)
{
732 733
  g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL);
  return COGL_TEXTURE (stex->priv->texture);
734 735
}

736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
/**
 * meta_shaped_texture_set_input_shape_region:
 * @stex: a #MetaShapedTexture
 * @shape_region: the region of the texture that should respond to
 *    input.
 *
 * Determines what region of the texture should accept input. For
 * X based windows this is defined by the ShapeInput region of the
 * window.
 */
void
meta_shaped_texture_set_input_shape_region (MetaShapedTexture *stex,
                                            cairo_region_t    *shape_region)
{
  MetaShapedTexturePrivate *priv;

  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));

  priv = stex->priv;

  if (priv->input_shape_region != NULL)
    {
      cairo_region_destroy (priv->input_shape_region);
      priv->input_shape_region = NULL;
    }

  if (shape_region != NULL)
    {
      cairo_region_reference (shape_region);
      priv->input_shape_region = shape_region;
    }

  clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
}

771
/**
772
 * meta_shaped_texture_set_clip_region:
773
 * @stex: a #MetaShapedTexture
774 775
 * @clip_region: the region of the texture that is visible and
 *   should be painted.
776 777 778 779 780 781 782 783 784 785
 *
 * Provides a hint to the texture about what areas of the texture
 * are not completely obscured and thus need to be painted. This
 * is an optimization and is not supposed to have any effect on
 * the output.
 *
 * Typically a parent container will set the clip region before
 * painting its children, and then unset it afterwards.
 */
void
786 787
meta_shaped_texture_set_clip_region (MetaShapedTexture *stex,
				     cairo_region_t    *clip_region)
788
{
789
  MetaShapedTexturePrivate *priv;
790

791
  g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
792 793 794 795

  priv = stex->priv;

  if (priv->clip_region)
796
    cairo_region_destroy (priv->clip_region);
797

798 799 800 801
  if (clip_region)
    priv->clip_region = cairo_region_copy (clip_region);
  else
    priv->clip_region = NULL;
802
}
803

804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833
/**
 * 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;
}

834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
/**
 * 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)
{
852
  CoglTexture *texture, *mask_texture;
853 854 855 856 857
  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);

858
  texture = COGL_TEXTURE (stex->priv->texture);
859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894

  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;
895
  if (mask_texture != NULL)
896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930
    {
      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;
}
931 932 933 934 935 936

ClutterActor *
meta_shaped_texture_new (void)
{
  return g_object_new (META_TYPE_SHAPED_TEXTURE, NULL);
}