mirror of
https://github.com/brl/mutter.git
synced 2024-12-01 04:10:43 -05:00
9f4942e9a7
The current shadow code just uses a single fixed texture (the Gaussian blur of a rectangle with a fixed blur radius) for drawing all window shadows. This patch adds the ability * Implement efficient blurring of arbitrary regions by approximating a Gaussian blur with multiple box blurs. * Detect when multiple windows can use the same shadow texture by converting their shape into a size-invariant MetaWindowShape. * Add properties shadow-radius, shadow-x-offset, shadow-y-offset, shadow-opacity to allow the shadow for a window to be configured. * Add meta_window_actor_paint() and draw the shadow directly from there rather than using a child actor. * Remove TidyTextureFrame, which is no longer used https://bugzilla.gnome.org/show_bug.cgi?id=592382
255 lines
7.0 KiB
C
255 lines
7.0 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
/*
|
|
* MetaWindowShape
|
|
*
|
|
* Extracted invariant window shape
|
|
*
|
|
* Copyright (C) 2010 Red Hat, Inc.
|
|
*
|
|
* 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.
|
|
*/
|
|
#include <string.h>
|
|
|
|
#include "meta-window-shape.h"
|
|
#include "region-utils.h"
|
|
|
|
struct _MetaWindowShape
|
|
{
|
|
guint ref_count;
|
|
|
|
int top, right, bottom, left;
|
|
int n_rectangles;
|
|
cairo_rectangle_int_t *rectangles;
|
|
guint hash;
|
|
};
|
|
|
|
MetaWindowShape *
|
|
meta_window_shape_new (cairo_region_t *region)
|
|
{
|
|
MetaWindowShape *shape;
|
|
MetaRegionIterator iter;
|
|
cairo_rectangle_int_t extents;
|
|
int max_yspan_y1 = 0;
|
|
int max_yspan_y2 = 0;
|
|
int max_xspan_x1 = -1;
|
|
int max_xspan_x2 = -1;
|
|
guint hash;
|
|
|
|
shape = g_slice_new0 (MetaWindowShape);
|
|
shape->ref_count = 1;
|
|
|
|
cairo_region_get_extents (region, &extents);
|
|
|
|
shape->n_rectangles = cairo_region_num_rectangles (region);
|
|
|
|
if (shape->n_rectangles == 0)
|
|
{
|
|
shape->rectangles = NULL;
|
|
shape->top = shape->right = shape->bottom = shape->left = 0;
|
|
shape->hash = 0;
|
|
return shape;
|
|
}
|
|
|
|
for (meta_region_iterator_init (&iter, region);
|
|
!meta_region_iterator_at_end (&iter);
|
|
meta_region_iterator_next (&iter))
|
|
{
|
|
int max_line_xspan_x1 = -1;
|
|
int max_line_xspan_x2 = -1;
|
|
|
|
if (iter.rectangle.width > max_line_xspan_x2 - max_line_xspan_x1)
|
|
{
|
|
max_line_xspan_x1 = iter.rectangle.x;
|
|
max_line_xspan_x2 = iter.rectangle.x + iter.rectangle.width;
|
|
}
|
|
|
|
if (iter.line_end)
|
|
{
|
|
if (iter.rectangle.height > max_yspan_y2 - max_yspan_y1)
|
|
{
|
|
max_yspan_y1 = iter.rectangle.y;
|
|
max_yspan_y2 = iter.rectangle.y + iter.rectangle.height;
|
|
}
|
|
|
|
if (max_xspan_x1 < 0) /* First line */
|
|
{
|
|
max_xspan_x1 = max_line_xspan_x1;
|
|
max_xspan_x2 = max_line_xspan_x2;
|
|
}
|
|
else
|
|
{
|
|
max_xspan_x1 = MAX (max_xspan_x1, max_line_xspan_x1);
|
|
max_xspan_x2 = MIN (max_xspan_x2, max_line_xspan_x2);
|
|
|
|
if (max_xspan_x2 < max_xspan_x1)
|
|
max_xspan_x2 = max_xspan_x1;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
g_print ("xspan: %d -> %d, yspan: %d -> %d\n",
|
|
max_xspan_x1, max_xspan_x2,
|
|
max_yspan_y1, max_yspan_y2);
|
|
#endif
|
|
|
|
shape->top = max_yspan_y1 - extents.y;
|
|
shape->right = extents.x + extents.width - max_xspan_x2;
|
|
shape->bottom = extents.y + extents.height - max_yspan_y2;
|
|
shape->left = max_xspan_x1 - extents.x;
|
|
|
|
shape->rectangles = g_new (cairo_rectangle_int_t, shape->n_rectangles);
|
|
|
|
hash = 0;
|
|
for (meta_region_iterator_init (&iter, region);
|
|
!meta_region_iterator_at_end (&iter);
|
|
meta_region_iterator_next (&iter))
|
|
{
|
|
int x1, x2, y1, y2;
|
|
|
|
x1 = iter.rectangle.x;
|
|
x2 = iter.rectangle.x + iter.rectangle.width;
|
|
y1 = iter.rectangle.y;
|
|
y2 = iter.rectangle.y + iter.rectangle.height;
|
|
|
|
if (x1 > max_xspan_x1)
|
|
x1 -= MIN (x1, max_xspan_x2 - 1) - max_xspan_x1;
|
|
if (x2 > max_xspan_x1)
|
|
x2 -= MIN (x2, max_xspan_x2 - 1) - max_xspan_x1;
|
|
if (y1 > max_yspan_y1)
|
|
y1 -= MIN (y1, max_yspan_y2 - 1) - max_yspan_y1;
|
|
if (y2 > max_yspan_y1)
|
|
y2 -= MIN (y2, max_yspan_y2 - 1) - max_yspan_y1;
|
|
|
|
shape->rectangles[iter.i].x = x1 - extents.x;
|
|
shape->rectangles[iter.i].y = y1 - extents.y;
|
|
shape->rectangles[iter.i].width = x2 - x1;
|
|
shape->rectangles[iter.i].height = y2 - y1;
|
|
|
|
#if 0
|
|
g_print ("%d: +%d+%dx%dx%d => +%d+%dx%dx%d\n",
|
|
iter.i, iter.rectangle.x, iter.rectangle.y, iter.rectangle.width, iter.rectangle.height,
|
|
shape->rectangles[iter.i].x, shape->rectangles[iter.i].y,
|
|
hape->rectangles[iter.i].width, shape->rectangles[iter.i].height);
|
|
#endif
|
|
|
|
hash = hash * 31 + x1 * 17 + x2 * 27 + y1 * 37 + y2 * 43;
|
|
}
|
|
|
|
shape->hash = hash;
|
|
|
|
#if 0
|
|
g_print ("%d %d %d %d: %#x\n\n", shape->top, shape->right, shape->bottom, shape->left, shape->hash);
|
|
#endif
|
|
|
|
return shape;
|
|
}
|
|
|
|
MetaWindowShape *
|
|
meta_window_shape_ref (MetaWindowShape *shape)
|
|
{
|
|
shape->ref_count++;
|
|
|
|
return shape;
|
|
}
|
|
|
|
void
|
|
meta_window_shape_unref (MetaWindowShape *shape)
|
|
{
|
|
shape->ref_count--;
|
|
if (shape->ref_count == 0)
|
|
{
|
|
g_free (shape->rectangles);
|
|
g_slice_free (MetaWindowShape, shape);
|
|
}
|
|
}
|
|
|
|
guint
|
|
meta_window_shape_hash (MetaWindowShape *shape)
|
|
{
|
|
return shape->hash;
|
|
}
|
|
|
|
gboolean
|
|
meta_window_shape_equal (MetaWindowShape *shape_a,
|
|
MetaWindowShape *shape_b)
|
|
{
|
|
if (shape_a->n_rectangles != shape_b->n_rectangles)
|
|
return FALSE;
|
|
|
|
return memcmp (shape_a->rectangles, shape_b->rectangles,
|
|
sizeof (cairo_rectangle_int_t) * shape_a->n_rectangles) == 0;
|
|
}
|
|
|
|
void
|
|
meta_window_shape_get_borders (MetaWindowShape *shape,
|
|
int *border_top,
|
|
int *border_right,
|
|
int *border_bottom,
|
|
int *border_left)
|
|
{
|
|
if (border_top)
|
|
*border_top = shape->top;
|
|
if (border_right)
|
|
*border_right = shape->right;
|
|
if (border_bottom)
|
|
*border_bottom = shape->bottom;
|
|
if (border_left)
|
|
*border_left = shape->left;
|
|
}
|
|
|
|
/**
|
|
* meta_window_shape_to_region:
|
|
* @shape: a #MetaWindowShape
|
|
* @center_width: size of the central region horizontally
|
|
* @center_height: size of the central region vertically
|
|
*
|
|
* Converts the shape to to a cairo_region_t using the given width
|
|
* and height for the central scaled region.
|
|
*
|
|
* Return value: a newly created region
|
|
*/
|
|
cairo_region_t *
|
|
meta_window_shape_to_region (MetaWindowShape *shape,
|
|
int center_width,
|
|
int center_height)
|
|
{
|
|
cairo_region_t *region;
|
|
int i;
|
|
|
|
region = cairo_region_create ();
|
|
|
|
for (i = 0; i < shape->n_rectangles; i++)
|
|
{
|
|
cairo_rectangle_int_t rect = shape->rectangles[i];
|
|
|
|
if (rect.x <= shape->left && rect.x + rect.width >= shape->left + 1)
|
|
rect.width += center_width;
|
|
else if (rect.x >= shape->left + 1)
|
|
rect.x += center_width;
|
|
|
|
if (rect.y <= shape->top && rect.y + rect.height >= shape->top + 1)
|
|
rect.height += center_height;
|
|
else if (rect.y >= shape->top + 1)
|
|
rect.y += center_height;
|
|
|
|
cairo_region_union_rectangle (region, &rect);
|
|
}
|
|
|
|
return region;
|
|
}
|
|
|