mirror of
https://github.com/brl/mutter.git
synced 2024-11-21 23:50:41 -05:00
Make shadows pretty and configurable
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
This commit is contained in:
parent
982a10ac44
commit
9f4942e9a7
@ -28,6 +28,8 @@ mutter_SOURCES= \
|
|||||||
compositor/meta-plugin.c \
|
compositor/meta-plugin.c \
|
||||||
compositor/meta-plugin-manager.c \
|
compositor/meta-plugin-manager.c \
|
||||||
compositor/meta-plugin-manager.h \
|
compositor/meta-plugin-manager.h \
|
||||||
|
compositor/meta-shadow-factory.c \
|
||||||
|
compositor/meta-shadow-factory.h \
|
||||||
compositor/meta-shaped-texture.c \
|
compositor/meta-shaped-texture.c \
|
||||||
compositor/meta-shaped-texture.h \
|
compositor/meta-shaped-texture.h \
|
||||||
compositor/meta-texture-tower.c \
|
compositor/meta-texture-tower.c \
|
||||||
@ -36,10 +38,10 @@ mutter_SOURCES= \
|
|||||||
compositor/meta-window-actor-private.h \
|
compositor/meta-window-actor-private.h \
|
||||||
compositor/meta-window-group.c \
|
compositor/meta-window-group.c \
|
||||||
compositor/meta-window-group.h \
|
compositor/meta-window-group.h \
|
||||||
compositor/shadow.c \
|
compositor/meta-window-shape.c \
|
||||||
compositor/shadow.h \
|
compositor/meta-window-shape.h \
|
||||||
compositor/tidy/tidy-texture-frame.c \
|
compositor/region-utils.c \
|
||||||
compositor/tidy/tidy-texture-frame.h \
|
compositor/region-utils.h \
|
||||||
include/compositor.h \
|
include/compositor.h \
|
||||||
include/meta-plugin.h \
|
include/meta-plugin.h \
|
||||||
include/meta-window-actor.h \
|
include/meta-window-actor.h \
|
||||||
|
677
src/compositor/meta-shadow-factory.c
Normal file
677
src/compositor/meta-shadow-factory.c
Normal file
@ -0,0 +1,677 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
/*
|
||||||
|
* MetaShadowFactory:
|
||||||
|
*
|
||||||
|
* Create and cache shadow textures for abritrary window shapes
|
||||||
|
*
|
||||||
|
* 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 <config.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "meta-shadow-factory.h"
|
||||||
|
#include "region-utils.h"
|
||||||
|
|
||||||
|
/* This file implements blurring the shape of a window to produce a
|
||||||
|
* shadow texture. The details are discussed below; a quick summary
|
||||||
|
* of the optimizations we use:
|
||||||
|
*
|
||||||
|
* - If the window shape is along the lines of a rounded rectangle -
|
||||||
|
* a rectangular center portion with stuff at the corners - then
|
||||||
|
* the blur of this - the shadow - can also be represented as a
|
||||||
|
* 9-sliced texture and the same texture can be used for different
|
||||||
|
* size.
|
||||||
|
*
|
||||||
|
* - We use the fact that a Gaussian blur is separable to do a
|
||||||
|
* 2D blur as 1D blur of the rows followed by a 1D blur of the
|
||||||
|
* columns.
|
||||||
|
*
|
||||||
|
* - For better cache efficiency, we blur rows, transpose the image
|
||||||
|
* in blocks, blur rows again, and then transpose back.
|
||||||
|
*
|
||||||
|
* - We approximate the 1D gaussian blur as 3 successive box filters.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct _MetaShadowCacheKey MetaShadowCacheKey;
|
||||||
|
|
||||||
|
struct _MetaShadowCacheKey
|
||||||
|
{
|
||||||
|
MetaWindowShape *shape;
|
||||||
|
int radius;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _MetaShadow
|
||||||
|
{
|
||||||
|
int ref_count;
|
||||||
|
|
||||||
|
MetaShadowFactory *factory;
|
||||||
|
MetaShadowCacheKey key;
|
||||||
|
CoglHandle texture;
|
||||||
|
CoglHandle material;
|
||||||
|
|
||||||
|
int spread;
|
||||||
|
int border_top;
|
||||||
|
int border_right;
|
||||||
|
int border_bottom;
|
||||||
|
int border_left;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _MetaShadowFactory
|
||||||
|
{
|
||||||
|
/* MetaShadowCacheKey => MetaShadow; the shadows are not referenced
|
||||||
|
* by the factory, they are simply removed from the table when freed */
|
||||||
|
GHashTable *shadows;
|
||||||
|
};
|
||||||
|
|
||||||
|
static guint
|
||||||
|
meta_shadow_cache_key_hash (gconstpointer val)
|
||||||
|
{
|
||||||
|
const MetaShadowCacheKey *key = val;
|
||||||
|
|
||||||
|
return 59 * key->radius + 67 * meta_window_shape_hash (key->shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
meta_shadow_cache_key_equal (gconstpointer a,
|
||||||
|
gconstpointer b)
|
||||||
|
{
|
||||||
|
const MetaShadowCacheKey *key_a = a;
|
||||||
|
const MetaShadowCacheKey *key_b = b;
|
||||||
|
|
||||||
|
return (key_a->radius == key_b->radius &&
|
||||||
|
meta_window_shape_equal (key_a->shape, key_b->shape));
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaShadow *
|
||||||
|
meta_shadow_ref (MetaShadow *shadow)
|
||||||
|
{
|
||||||
|
shadow->ref_count++;
|
||||||
|
|
||||||
|
return shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_shadow_unref (MetaShadow *shadow)
|
||||||
|
{
|
||||||
|
shadow->ref_count--;
|
||||||
|
if (shadow->ref_count == 0)
|
||||||
|
{
|
||||||
|
if (shadow->factory)
|
||||||
|
{
|
||||||
|
g_hash_table_remove (shadow->factory->shadows,
|
||||||
|
&shadow->key);
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_window_shape_unref (shadow->key.shape);
|
||||||
|
cogl_handle_unref (shadow->texture);
|
||||||
|
cogl_handle_unref (shadow->material);
|
||||||
|
|
||||||
|
g_slice_free (MetaShadow, shadow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meta_shadow_paint:
|
||||||
|
* @window_x: x position of the region to paint a shadow for
|
||||||
|
* @window_y: y position of the region to paint a shadow for
|
||||||
|
* @window_width: actual width of the region to paint a shadow for
|
||||||
|
* @window_height: actual height of the region to paint a shadow for
|
||||||
|
*
|
||||||
|
* Paints the shadow at the given position, for the specified actual
|
||||||
|
* size of the region. (Since a #MetaShadow can be shared between
|
||||||
|
* different sizes with the same extracted #MetaWindowShape the
|
||||||
|
* size needs to be passed in here.)
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
meta_shadow_paint (MetaShadow *shadow,
|
||||||
|
int window_x,
|
||||||
|
int window_y,
|
||||||
|
int window_width,
|
||||||
|
int window_height,
|
||||||
|
guint8 opacity)
|
||||||
|
{
|
||||||
|
float texture_width = cogl_texture_get_width (shadow->texture);
|
||||||
|
float texture_height = cogl_texture_get_height (shadow->texture);
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
cogl_material_set_color4ub (shadow->material,
|
||||||
|
opacity, opacity, opacity, opacity);
|
||||||
|
|
||||||
|
cogl_set_source (shadow->material);
|
||||||
|
|
||||||
|
if (window_width + 2 * shadow->spread == shadow->border_left &&
|
||||||
|
window_height + 2 * shadow->spread == shadow->border_top)
|
||||||
|
{
|
||||||
|
/* The non-scaled case - paint with a single rectangle */
|
||||||
|
cogl_rectangle_with_texture_coords (window_x - shadow->spread,
|
||||||
|
window_y - shadow->spread,
|
||||||
|
window_x + window_width + shadow->spread,
|
||||||
|
window_y + window_height + shadow->spread,
|
||||||
|
0.0, 0.0, 1.0, 1.0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float src_x[4];
|
||||||
|
float src_y[4];
|
||||||
|
float dest_x[4];
|
||||||
|
float dest_y[4];
|
||||||
|
|
||||||
|
src_x[0] = 0.0;
|
||||||
|
src_x[1] = shadow->border_left / texture_width;
|
||||||
|
src_x[2] = (texture_width - shadow->border_right) / texture_width;
|
||||||
|
src_x[3] = 1.0;
|
||||||
|
|
||||||
|
src_y[0] = 0.0;
|
||||||
|
src_y[1] = shadow->border_top / texture_height;
|
||||||
|
src_y[2] = (texture_height - shadow->border_bottom) / texture_height;
|
||||||
|
src_y[3] = 1.0;
|
||||||
|
|
||||||
|
dest_x[0] = window_x - shadow->spread;
|
||||||
|
dest_x[1] = window_x - shadow->spread + shadow->border_left;
|
||||||
|
dest_x[2] = window_x + window_width + shadow->spread - shadow->border_right;
|
||||||
|
dest_x[3] = window_x + window_width + shadow->spread;
|
||||||
|
|
||||||
|
dest_y[0] = window_y - shadow->spread;
|
||||||
|
dest_y[1] = window_y - shadow->spread + shadow->border_top;
|
||||||
|
dest_y[2] = window_y + window_height + shadow->spread - shadow->border_bottom;
|
||||||
|
dest_y[3] = window_y + window_height + shadow->spread;
|
||||||
|
|
||||||
|
for (j = 0; j < 3; j++)
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
cogl_rectangle_with_texture_coords (dest_x[i], dest_y[j],
|
||||||
|
dest_x[i + 1], dest_y[j + 1],
|
||||||
|
src_x[i], src_y[j],
|
||||||
|
src_x[i + 1], src_y[j + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meta_shadow_get_bounds:
|
||||||
|
* @shadow: a #MetaShadow
|
||||||
|
* @window_x: x position of the region to paint a shadow for
|
||||||
|
* @window_y: y position of the region to paint a shadow for
|
||||||
|
* @window_width: actual width of the region to paint a shadow for
|
||||||
|
* @window_height: actual height of the region to paint a shadow for
|
||||||
|
*
|
||||||
|
* Computes the bounds of the pixels that will be affected by
|
||||||
|
* meta_shadow_paints()
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
meta_shadow_get_bounds (MetaShadow *shadow,
|
||||||
|
int window_x,
|
||||||
|
int window_y,
|
||||||
|
int window_width,
|
||||||
|
int window_height,
|
||||||
|
cairo_rectangle_int_t *bounds)
|
||||||
|
{
|
||||||
|
bounds->x = window_x - shadow->spread;
|
||||||
|
bounds->y = window_x - shadow->spread;
|
||||||
|
bounds->width = window_width + 2 * shadow->spread;
|
||||||
|
bounds->height = window_width + 2 * shadow->spread;
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaShadowFactory *
|
||||||
|
meta_shadow_factory_new (void)
|
||||||
|
{
|
||||||
|
MetaShadowFactory *factory;
|
||||||
|
|
||||||
|
factory = g_slice_new0 (MetaShadowFactory);
|
||||||
|
|
||||||
|
factory->shadows = g_hash_table_new (meta_shadow_cache_key_hash,
|
||||||
|
meta_shadow_cache_key_equal);
|
||||||
|
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_shadow_factory_free (MetaShadowFactory *factory)
|
||||||
|
{
|
||||||
|
GHashTableIter iter;
|
||||||
|
gpointer key, value;
|
||||||
|
|
||||||
|
/* Detach from the shadows in the table so we won't try to
|
||||||
|
* remove them when they freed. */
|
||||||
|
g_hash_table_iter_init (&iter, factory->shadows);
|
||||||
|
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||||
|
{
|
||||||
|
MetaShadow *shadow = key;
|
||||||
|
shadow->factory = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_hash_table_destroy (factory->shadows);
|
||||||
|
|
||||||
|
g_slice_free (MetaShadowFactory, factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaShadowFactory *
|
||||||
|
meta_shadow_factory_get_default (void)
|
||||||
|
{
|
||||||
|
static MetaShadowFactory *factory;
|
||||||
|
|
||||||
|
if (factory == NULL)
|
||||||
|
factory = meta_shadow_factory_new ();
|
||||||
|
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We emulate a 1D Gaussian blur by using 3 consecutive box blurs;
|
||||||
|
* this produces a result that's within 3% of the original and can be
|
||||||
|
* implemented much faster for large filter sizes because of the
|
||||||
|
* efficiency of implementation of a box blur. Idea and formula
|
||||||
|
* for choosing the box blur size come from:
|
||||||
|
*
|
||||||
|
* http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
|
||||||
|
*
|
||||||
|
* The 2D blur is then done by blurring the rows, flipping the
|
||||||
|
* image and blurring the columns. (This is possible because the
|
||||||
|
* Gaussian kernel is separable - it's the product of a horizontal
|
||||||
|
* blur and a vertical blur.)
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
get_box_filter_size (int radius)
|
||||||
|
{
|
||||||
|
return (int)(0.5 + radius * (0.75 * sqrt(2*M_PI)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The "spread" of the filter is the number of pixels from an original
|
||||||
|
* pixel that it's blurred image extends. (A no-op blur that doesn't
|
||||||
|
* blur would have a spread of 0.) See comment in blur_rows() for why the
|
||||||
|
* odd and even cases are different
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
get_shadow_spread (int radius)
|
||||||
|
{
|
||||||
|
int d = get_box_filter_size (radius);
|
||||||
|
|
||||||
|
if (d % 2 == 1)
|
||||||
|
return 3 * (d / 2);
|
||||||
|
else
|
||||||
|
return 3 * (d / 2) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This applies a single box blur pass to a horizontal range of pixels;
|
||||||
|
* since the box blur has the same weight for all pixels, we can
|
||||||
|
* implement an efficient sliding window algorithm where we add
|
||||||
|
* in pixels coming into the window from the right and remove
|
||||||
|
* them when they leave the windw to the left.
|
||||||
|
*
|
||||||
|
* d is the filter width; for even d shift indicates how the blurred
|
||||||
|
* result is aligned with the original - does ' x ' go to ' yy' (shift=1)
|
||||||
|
* or 'yy ' (shift=-1)
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
blur_xspan (guchar *row,
|
||||||
|
guchar *tmp_buffer,
|
||||||
|
int row_width,
|
||||||
|
int x0,
|
||||||
|
int x1,
|
||||||
|
int d,
|
||||||
|
int shift)
|
||||||
|
{
|
||||||
|
int offset;
|
||||||
|
int sum = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (d % 2 == 1)
|
||||||
|
offset = d / 2;
|
||||||
|
else
|
||||||
|
offset = (d - shift) / 2;
|
||||||
|
|
||||||
|
/* All the conditionals in here look slow, but the branches will
|
||||||
|
* be well predicted and there are enough different possibilities
|
||||||
|
* that trying to write this as a series of unconditional loops
|
||||||
|
* is hard and not an obvious win. The main slow down here seems
|
||||||
|
* to be the integer division for pixel; one possible optimization
|
||||||
|
* would be to accumulate into two 16-bit integer buffers and
|
||||||
|
* only divide down after all three passes. (SSE parallel implementation
|
||||||
|
* of the divide step is possible.)
|
||||||
|
*/
|
||||||
|
for (i = x0 - d + offset; i < x1 + offset; i++)
|
||||||
|
{
|
||||||
|
if (i >= 0 && i < row_width)
|
||||||
|
sum += row[i];
|
||||||
|
|
||||||
|
if (i >= x0 + offset)
|
||||||
|
{
|
||||||
|
if (i >= d)
|
||||||
|
sum -= row[i - d];
|
||||||
|
|
||||||
|
tmp_buffer[i - offset] = (sum + d / 2) / d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(row + x0, tmp_buffer + x0, x1 - x0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
blur_rows (cairo_region_t *convolve_region,
|
||||||
|
int x_offset,
|
||||||
|
int y_offset,
|
||||||
|
guchar *buffer,
|
||||||
|
int buffer_width,
|
||||||
|
int buffer_height,
|
||||||
|
int d)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
int n_rectangles;
|
||||||
|
guchar *tmp_buffer;
|
||||||
|
|
||||||
|
tmp_buffer = g_malloc (buffer_width);
|
||||||
|
|
||||||
|
n_rectangles = cairo_region_num_rectangles (convolve_region);
|
||||||
|
for (i = 0; i < n_rectangles; i++)
|
||||||
|
{
|
||||||
|
cairo_rectangle_int_t rect;
|
||||||
|
|
||||||
|
cairo_region_get_rectangle (convolve_region, i, &rect);
|
||||||
|
|
||||||
|
for (j = y_offset + rect.y; j < y_offset + rect.y + rect.height; j++)
|
||||||
|
{
|
||||||
|
guchar *row = buffer + j * buffer_width;
|
||||||
|
int x0 = x_offset + rect.x;
|
||||||
|
int x1 = x0 + rect.width;
|
||||||
|
|
||||||
|
/* We want to produce a symmetric blur that spreads a pixel
|
||||||
|
* equally far to the left and right. If d is odd that happens
|
||||||
|
* naturally, but for d even, we approximate by using a blur
|
||||||
|
* on either side and then a centered blur of size d + 1.
|
||||||
|
* (techique also from the SVG specification)
|
||||||
|
*/
|
||||||
|
if (d % 2 == 1)
|
||||||
|
{
|
||||||
|
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 0);
|
||||||
|
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 0);
|
||||||
|
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 1);
|
||||||
|
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, -1);
|
||||||
|
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d + 1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (tmp_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Swaps width and height. Either swaps in-place and returns the original
|
||||||
|
* buffer or allocates a new buffer, frees the original buffer and returns
|
||||||
|
* the new buffer.
|
||||||
|
*/
|
||||||
|
static guchar *
|
||||||
|
flip_buffer (guchar *buffer,
|
||||||
|
int width,
|
||||||
|
int height)
|
||||||
|
{
|
||||||
|
/* Working in blocks increases cache efficiency, compared to reading
|
||||||
|
* or writing an entire column at once */
|
||||||
|
#define BLOCK_SIZE 16
|
||||||
|
|
||||||
|
if (width == height)
|
||||||
|
{
|
||||||
|
int i0, j0;
|
||||||
|
|
||||||
|
for (j0 = 0; j0 < height; j0 += BLOCK_SIZE)
|
||||||
|
for (i0 = 0; i0 <= j0; i0 += BLOCK_SIZE)
|
||||||
|
{
|
||||||
|
int max_j = MIN(j0 + BLOCK_SIZE, height);
|
||||||
|
int max_i = MIN(i0 + BLOCK_SIZE, width);
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
if (i0 == j0)
|
||||||
|
{
|
||||||
|
for (j = j0; j < max_j; j++)
|
||||||
|
for (i = i0; i < j; i++)
|
||||||
|
{
|
||||||
|
guchar tmp = buffer[j * width + i];
|
||||||
|
buffer[j * width + i] = buffer[i * width + j];
|
||||||
|
buffer[i * width + j] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (j = j0; j < max_j; j++)
|
||||||
|
for (i = i0; i < max_i; i++)
|
||||||
|
{
|
||||||
|
guchar tmp = buffer[j * width + i];
|
||||||
|
buffer[j * width + i] = buffer[i * width + j];
|
||||||
|
buffer[i * width + j] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
guchar *new_buffer = g_malloc (height * width);
|
||||||
|
int i0, j0;
|
||||||
|
|
||||||
|
for (i0 = 0; i0 < width; i0 += BLOCK_SIZE)
|
||||||
|
for (j0 = 0; j0 < height; j0 += BLOCK_SIZE)
|
||||||
|
{
|
||||||
|
int max_j = MIN(j0 + BLOCK_SIZE, height);
|
||||||
|
int max_i = MIN(i0 + BLOCK_SIZE, width);
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (i = i0; i < max_i; i++)
|
||||||
|
for (j = j0; j < max_j; j++)
|
||||||
|
new_buffer[i * height + j] = buffer[j * width + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (buffer);
|
||||||
|
|
||||||
|
return new_buffer;
|
||||||
|
}
|
||||||
|
#undef BLOCK_SIZE
|
||||||
|
}
|
||||||
|
|
||||||
|
static CoglHandle
|
||||||
|
make_shadow (cairo_region_t *region,
|
||||||
|
int radius)
|
||||||
|
{
|
||||||
|
int d = get_box_filter_size (radius);
|
||||||
|
int spread = get_shadow_spread (radius);
|
||||||
|
CoglHandle result;
|
||||||
|
cairo_rectangle_int_t extents;
|
||||||
|
cairo_region_t *row_convolve_region;
|
||||||
|
cairo_region_t *column_convolve_region;
|
||||||
|
guchar *buffer;
|
||||||
|
int buffer_width;
|
||||||
|
int buffer_height;
|
||||||
|
int n_rectangles, j, k;
|
||||||
|
|
||||||
|
cairo_region_get_extents (region, &extents);
|
||||||
|
|
||||||
|
buffer_width = extents.width + 2 * spread;
|
||||||
|
buffer_height = extents.height + 2 * spread;
|
||||||
|
|
||||||
|
/* Round up so we have aligned rows/columns */
|
||||||
|
buffer_width = (buffer_width + 3) & ~3;
|
||||||
|
buffer_height = (buffer_height + 3) & ~3;
|
||||||
|
|
||||||
|
/* Square buffer allows in-place swaps, which are roughly 70% faster, but we
|
||||||
|
* don't want to over-allocate too much memory.
|
||||||
|
*/
|
||||||
|
if (buffer_height < buffer_width && buffer_height > (3 * buffer_width) / 4)
|
||||||
|
buffer_height = buffer_width;
|
||||||
|
if (buffer_width < buffer_height && buffer_width > (3 * buffer_height) / 4)
|
||||||
|
buffer_width = buffer_height;
|
||||||
|
|
||||||
|
buffer = g_malloc0 (buffer_width * buffer_height);
|
||||||
|
|
||||||
|
/* Blurring with multiple box-blur passes is fast, but (especially for
|
||||||
|
* large shadow sizes) we can improve efficiency by restricting the blur
|
||||||
|
* to the region that actually needs to be blurred.
|
||||||
|
*/
|
||||||
|
row_convolve_region = meta_make_border_region (region, spread, 0, FALSE);
|
||||||
|
column_convolve_region = meta_make_border_region (region, spread, spread, TRUE);
|
||||||
|
|
||||||
|
/* Step 1: unblurred image */
|
||||||
|
n_rectangles = cairo_region_num_rectangles (region);
|
||||||
|
for (k = 0; k < n_rectangles; k++)
|
||||||
|
{
|
||||||
|
cairo_rectangle_int_t rect;
|
||||||
|
|
||||||
|
cairo_region_get_rectangle (region, k, &rect);
|
||||||
|
for (j = spread + rect.y; j < spread + rect.y + rect.height; j++)
|
||||||
|
memset (buffer + buffer_width * j + spread + rect.x, 255, rect.width);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step 2: blur rows */
|
||||||
|
blur_rows (row_convolve_region, spread, spread, buffer, buffer_width, buffer_height, d);
|
||||||
|
|
||||||
|
/* Step 2: swap rows and columns */
|
||||||
|
buffer = flip_buffer (buffer, buffer_width, buffer_height);
|
||||||
|
|
||||||
|
/* Step 3: blur rows (really columns) */
|
||||||
|
blur_rows (column_convolve_region, spread, spread, buffer, buffer_height, buffer_width, d);
|
||||||
|
|
||||||
|
/* Step 3: swap rows and columns */
|
||||||
|
buffer = flip_buffer (buffer, buffer_height, buffer_width);
|
||||||
|
|
||||||
|
result = cogl_texture_new_from_data (extents.width + 2 * spread,
|
||||||
|
extents.height + 2 * spread,
|
||||||
|
COGL_TEXTURE_NONE,
|
||||||
|
COGL_PIXEL_FORMAT_A_8,
|
||||||
|
COGL_PIXEL_FORMAT_ANY,
|
||||||
|
buffer_width,
|
||||||
|
buffer);
|
||||||
|
|
||||||
|
cairo_region_destroy (row_convolve_region);
|
||||||
|
cairo_region_destroy (column_convolve_region);
|
||||||
|
g_free (buffer);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meta_shadow_factory_get_shadow:
|
||||||
|
* @factory: a #MetaShadowFactory
|
||||||
|
* @shape: the size-invariant shape of the window's region
|
||||||
|
* @width: the actual width of the window's region
|
||||||
|
* @width: the actual height of the window's region
|
||||||
|
* @radius: the radius (gaussian standard deviation) of the shadow
|
||||||
|
*
|
||||||
|
* Gets the appropriate shadow object for drawing shadows for the
|
||||||
|
* specified window shape. The region that we are shadowing is specified
|
||||||
|
* as a combination of a size-invariant extracted shape and the size.
|
||||||
|
* In some cases, the same shadow object can be shared between sizes;
|
||||||
|
* in other cases a different shadow object is used for each size.
|
||||||
|
*
|
||||||
|
* Return value: (transfer full): a newly referenced #MetaShadow; unref with
|
||||||
|
* meta_shadow_unref()
|
||||||
|
*/
|
||||||
|
MetaShadow *
|
||||||
|
meta_shadow_factory_get_shadow (MetaShadowFactory *factory,
|
||||||
|
MetaWindowShape *shape,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int radius)
|
||||||
|
{
|
||||||
|
MetaShadowCacheKey key;
|
||||||
|
MetaShadow *shadow;
|
||||||
|
cairo_region_t *region;
|
||||||
|
int spread;
|
||||||
|
int border_top, border_right, border_bottom, border_left;
|
||||||
|
gboolean cacheable;
|
||||||
|
|
||||||
|
/* Using a single shadow texture for different window sizes only works
|
||||||
|
* when there is a central scaled area that is greater than twice
|
||||||
|
* the spread of the gaussian blur we are applying to get to the
|
||||||
|
* shadow image.
|
||||||
|
* ********* ***********
|
||||||
|
* /----------\ *###########* *#############*
|
||||||
|
* | | => **#*********#** => **#***********#**
|
||||||
|
* | | **#** **#** **#** **#**
|
||||||
|
* | | **#*********#** **#***********#**
|
||||||
|
* \----------/ *###########* *#############*
|
||||||
|
* ********** ************
|
||||||
|
* Original Blur Stretched Blur
|
||||||
|
*
|
||||||
|
* For smaller sizes, we create a separate shadow image for each size;
|
||||||
|
* since we assume that there will be little reuse, we don't try to
|
||||||
|
* cache such images but just recreate them. (Since the current cache
|
||||||
|
* policy is to only keep around referenced shadows, there wouldn't
|
||||||
|
* be any harm in caching them, it would just make the book-keeping
|
||||||
|
* a bit tricker.)
|
||||||
|
*/
|
||||||
|
spread = get_shadow_spread (radius);
|
||||||
|
meta_window_shape_get_borders (shape,
|
||||||
|
&border_top,
|
||||||
|
&border_right,
|
||||||
|
&border_bottom,
|
||||||
|
&border_left);
|
||||||
|
|
||||||
|
cacheable = (border_top + 2 * spread + border_bottom <= height &&
|
||||||
|
border_left + 2 * spread + border_right <= width);
|
||||||
|
|
||||||
|
if (cacheable)
|
||||||
|
{
|
||||||
|
key.shape = shape;
|
||||||
|
key.radius = radius;
|
||||||
|
|
||||||
|
shadow = g_hash_table_lookup (factory->shadows, &key);
|
||||||
|
if (shadow)
|
||||||
|
return meta_shadow_ref (shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
shadow = g_slice_new0 (MetaShadow);
|
||||||
|
|
||||||
|
shadow->ref_count = 1;
|
||||||
|
shadow->factory = factory;
|
||||||
|
shadow->key.shape = meta_window_shape_ref (shape);
|
||||||
|
shadow->key.radius = radius;
|
||||||
|
|
||||||
|
shadow->spread = spread;
|
||||||
|
|
||||||
|
if (cacheable)
|
||||||
|
{
|
||||||
|
shadow->border_top = border_top + 2 * spread;
|
||||||
|
shadow->border_right += border_right + 2 * spread;
|
||||||
|
shadow->border_bottom += border_bottom + 2 * spread;
|
||||||
|
shadow->border_left += border_left + 2 * spread;
|
||||||
|
|
||||||
|
region = meta_window_shape_to_region (shape, 2 * spread, 2 * spread);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* In the non-scaled case, we put the entire shadow into the
|
||||||
|
* upper-left-hand corner of the 9-slice */
|
||||||
|
shadow->border_top = height + 2 * spread;
|
||||||
|
shadow->border_right = 0;
|
||||||
|
shadow->border_bottom = 0;
|
||||||
|
shadow->border_left = width + 2 * spread;
|
||||||
|
|
||||||
|
region = meta_window_shape_to_region (shape,
|
||||||
|
width - border_left - border_right,
|
||||||
|
height - border_top - border_bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
shadow->texture = make_shadow (region, radius);
|
||||||
|
shadow->material = cogl_material_new ();
|
||||||
|
cogl_material_set_layer (shadow->material, 0, shadow->texture);
|
||||||
|
cairo_region_destroy (region);
|
||||||
|
|
||||||
|
if (cacheable)
|
||||||
|
g_hash_table_insert (factory->shadows, &shadow->key, shadow);
|
||||||
|
|
||||||
|
return shadow;
|
||||||
|
}
|
73
src/compositor/meta-shadow-factory.h
Normal file
73
src/compositor/meta-shadow-factory.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
/*
|
||||||
|
* MetaShadowFactory:
|
||||||
|
*
|
||||||
|
* Create and cache shadow textures for arbitrary window shapes
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __META_SHADOW_FACTORY_H__
|
||||||
|
#define __META_SHADOW_FACTORY_H__
|
||||||
|
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
#include "meta-window-shape.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MetaShadow:
|
||||||
|
* #MetaShadow holds a shadow texture along with information about how to
|
||||||
|
* apply that texture to draw a window texture. (E.g., it knows how big the
|
||||||
|
* unscaled borders are on each side of the shadow texture.)
|
||||||
|
*/
|
||||||
|
typedef struct _MetaShadow MetaShadow;
|
||||||
|
|
||||||
|
MetaShadow *meta_shadow_ref (MetaShadow *shadow);
|
||||||
|
void meta_shadow_unref (MetaShadow *shadow);
|
||||||
|
CoglHandle meta_shadow_get_texture (MetaShadow *shadow);
|
||||||
|
void meta_shadow_paint (MetaShadow *shadow,
|
||||||
|
int window_x,
|
||||||
|
int window_y,
|
||||||
|
int window_width,
|
||||||
|
int window_height,
|
||||||
|
guint8 opacity);
|
||||||
|
void meta_shadow_get_bounds (MetaShadow *shadow,
|
||||||
|
int window_x,
|
||||||
|
int window_y,
|
||||||
|
int window_width,
|
||||||
|
int window_height,
|
||||||
|
cairo_rectangle_int_t *bounds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MetaShadowFactory:
|
||||||
|
* #MetaShadowFactory is used to create window shadows. It caches shadows internally
|
||||||
|
* so that multiple shadows created for the same shape with the same radius will
|
||||||
|
* share the same MetaShadow.
|
||||||
|
*/
|
||||||
|
typedef struct _MetaShadowFactory MetaShadowFactory;
|
||||||
|
|
||||||
|
MetaShadowFactory *meta_shadow_factory_get_default (void);
|
||||||
|
|
||||||
|
MetaShadowFactory *meta_shadow_factory_new (void);
|
||||||
|
void meta_shadow_factory_free (MetaShadowFactory *factory);
|
||||||
|
MetaShadow * meta_shadow_factory_get_shadow (MetaShadowFactory *factory,
|
||||||
|
MetaWindowShape *shape,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int radius);
|
||||||
|
|
||||||
|
#endif /* __META_SHADOW_FACTORY_H__ */
|
@ -17,10 +17,9 @@
|
|||||||
#include "xprops.h"
|
#include "xprops.h"
|
||||||
|
|
||||||
#include "compositor-private.h"
|
#include "compositor-private.h"
|
||||||
|
#include "meta-shadow-factory.h"
|
||||||
#include "meta-shaped-texture.h"
|
#include "meta-shaped-texture.h"
|
||||||
#include "meta-window-actor-private.h"
|
#include "meta-window-actor-private.h"
|
||||||
#include "shadow.h"
|
|
||||||
#include "tidy/tidy-texture-frame.h"
|
|
||||||
|
|
||||||
struct _MetaWindowActorPrivate
|
struct _MetaWindowActorPrivate
|
||||||
{
|
{
|
||||||
@ -31,12 +30,13 @@ struct _MetaWindowActorPrivate
|
|||||||
MetaScreen *screen;
|
MetaScreen *screen;
|
||||||
|
|
||||||
ClutterActor *actor;
|
ClutterActor *actor;
|
||||||
ClutterActor *shadow;
|
MetaShadow *shadow;
|
||||||
Pixmap back_pixmap;
|
Pixmap back_pixmap;
|
||||||
|
|
||||||
Damage damage;
|
Damage damage;
|
||||||
|
|
||||||
guint8 opacity;
|
guint8 opacity;
|
||||||
|
guint8 shadow_opacity;
|
||||||
|
|
||||||
gchar * desc;
|
gchar * desc;
|
||||||
|
|
||||||
@ -46,8 +46,15 @@ struct _MetaWindowActorPrivate
|
|||||||
* texture */
|
* texture */
|
||||||
cairo_region_t *bounding_region;
|
cairo_region_t *bounding_region;
|
||||||
|
|
||||||
|
/* Extracted size-invariant shape used for shadows */
|
||||||
|
MetaWindowShape *shadow_shape;
|
||||||
|
|
||||||
gint freeze_count;
|
gint freeze_count;
|
||||||
|
|
||||||
|
gint shadow_radius;
|
||||||
|
gint shadow_x_offset;
|
||||||
|
gint shadow_y_offset;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These need to be counters rather than flags, since more plugins
|
* These need to be counters rather than flags, since more plugins
|
||||||
* can implement same effect; the practicality of stacking effects
|
* can implement same effect; the practicality of stacking effects
|
||||||
@ -71,6 +78,8 @@ struct _MetaWindowActorPrivate
|
|||||||
|
|
||||||
guint needs_pixmap : 1;
|
guint needs_pixmap : 1;
|
||||||
guint needs_reshape : 1;
|
guint needs_reshape : 1;
|
||||||
|
guint recompute_shadow : 1;
|
||||||
|
guint paint_shadow : 1;
|
||||||
guint size_changed : 1;
|
guint size_changed : 1;
|
||||||
|
|
||||||
guint needs_destroy : 1;
|
guint needs_destroy : 1;
|
||||||
@ -87,8 +96,16 @@ enum
|
|||||||
PROP_X_WINDOW,
|
PROP_X_WINDOW,
|
||||||
PROP_X_WINDOW_ATTRIBUTES,
|
PROP_X_WINDOW_ATTRIBUTES,
|
||||||
PROP_NO_SHADOW,
|
PROP_NO_SHADOW,
|
||||||
|
PROP_SHADOW_RADIUS,
|
||||||
|
PROP_SHADOW_X_OFFSET,
|
||||||
|
PROP_SHADOW_Y_OFFSET,
|
||||||
|
PROP_SHADOW_OPACITY
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define DEFAULT_SHADOW_RADIUS 12
|
||||||
|
#define DEFAULT_SHADOW_X_OFFSET 0
|
||||||
|
#define DEFAULT_SHADOW_Y_OFFSET 8
|
||||||
|
|
||||||
static void meta_window_actor_dispose (GObject *object);
|
static void meta_window_actor_dispose (GObject *object);
|
||||||
static void meta_window_actor_finalize (GObject *object);
|
static void meta_window_actor_finalize (GObject *object);
|
||||||
static void meta_window_actor_constructed (GObject *object);
|
static void meta_window_actor_constructed (GObject *object);
|
||||||
@ -101,6 +118,8 @@ static void meta_window_actor_get_property (GObject *object,
|
|||||||
GValue *value,
|
GValue *value,
|
||||||
GParamSpec *pspec);
|
GParamSpec *pspec);
|
||||||
|
|
||||||
|
static void meta_window_actor_paint (ClutterActor *actor);
|
||||||
|
|
||||||
static void meta_window_actor_detach (MetaWindowActor *self);
|
static void meta_window_actor_detach (MetaWindowActor *self);
|
||||||
static gboolean meta_window_actor_has_shadow (MetaWindowActor *self);
|
static gboolean meta_window_actor_has_shadow (MetaWindowActor *self);
|
||||||
|
|
||||||
@ -161,6 +180,7 @@ static void
|
|||||||
meta_window_actor_class_init (MetaWindowActorClass *klass)
|
meta_window_actor_class_init (MetaWindowActorClass *klass)
|
||||||
{
|
{
|
||||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||||
GParamSpec *pspec;
|
GParamSpec *pspec;
|
||||||
|
|
||||||
g_type_class_add_private (klass, sizeof (MetaWindowActorPrivate));
|
g_type_class_add_private (klass, sizeof (MetaWindowActorPrivate));
|
||||||
@ -171,6 +191,8 @@ meta_window_actor_class_init (MetaWindowActorClass *klass)
|
|||||||
object_class->get_property = meta_window_actor_get_property;
|
object_class->get_property = meta_window_actor_get_property;
|
||||||
object_class->constructed = meta_window_actor_constructed;
|
object_class->constructed = meta_window_actor_constructed;
|
||||||
|
|
||||||
|
actor_class->paint = meta_window_actor_paint;
|
||||||
|
|
||||||
pspec = g_param_spec_object ("meta-window",
|
pspec = g_param_spec_object ("meta-window",
|
||||||
"MetaWindow",
|
"MetaWindow",
|
||||||
"The displayed MetaWindow",
|
"The displayed MetaWindow",
|
||||||
@ -216,11 +238,52 @@ meta_window_actor_class_init (MetaWindowActorClass *klass)
|
|||||||
"No shadow",
|
"No shadow",
|
||||||
"Do not add shaddow to this window",
|
"Do not add shaddow to this window",
|
||||||
FALSE,
|
FALSE,
|
||||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
|
G_PARAM_READWRITE);
|
||||||
|
|
||||||
g_object_class_install_property (object_class,
|
g_object_class_install_property (object_class,
|
||||||
PROP_NO_SHADOW,
|
PROP_NO_SHADOW,
|
||||||
pspec);
|
pspec);
|
||||||
|
|
||||||
|
pspec = g_param_spec_int ("shadow-radius",
|
||||||
|
"Shadow Radius",
|
||||||
|
"Radius (standard deviation of gaussian blur) of window's shadow",
|
||||||
|
0, 128, DEFAULT_SHADOW_RADIUS,
|
||||||
|
G_PARAM_READWRITE);
|
||||||
|
|
||||||
|
g_object_class_install_property (object_class,
|
||||||
|
PROP_SHADOW_RADIUS,
|
||||||
|
pspec);
|
||||||
|
|
||||||
|
pspec = g_param_spec_int ("shadow-x-offset",
|
||||||
|
"Shadow X Offset",
|
||||||
|
"Distance shadow is offset in the horizontal direction in pixels",
|
||||||
|
G_MININT, G_MAXINT, DEFAULT_SHADOW_X_OFFSET,
|
||||||
|
G_PARAM_READWRITE);
|
||||||
|
|
||||||
|
g_object_class_install_property (object_class,
|
||||||
|
PROP_SHADOW_X_OFFSET,
|
||||||
|
pspec);
|
||||||
|
|
||||||
|
pspec = g_param_spec_int ("shadow-y-offset",
|
||||||
|
"Shadow Y Offset",
|
||||||
|
"Distance shadow is offset in the vertical direction in piyels",
|
||||||
|
G_MININT, G_MAXINT, DEFAULT_SHADOW_Y_OFFSET,
|
||||||
|
G_PARAM_READWRITE);
|
||||||
|
|
||||||
|
g_object_class_install_property (object_class,
|
||||||
|
PROP_SHADOW_Y_OFFSET,
|
||||||
|
pspec);
|
||||||
|
|
||||||
|
pspec = g_param_spec_uint ("shadow-opacity",
|
||||||
|
"Shadow Opacity",
|
||||||
|
"Opacity of the window's shadow",
|
||||||
|
0, 255,
|
||||||
|
255,
|
||||||
|
G_PARAM_READWRITE);
|
||||||
|
|
||||||
|
g_object_class_install_property (object_class,
|
||||||
|
PROP_SHADOW_OPACITY,
|
||||||
|
pspec);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -232,6 +295,11 @@ meta_window_actor_init (MetaWindowActor *self)
|
|||||||
META_TYPE_WINDOW_ACTOR,
|
META_TYPE_WINDOW_ACTOR,
|
||||||
MetaWindowActorPrivate);
|
MetaWindowActorPrivate);
|
||||||
priv->opacity = 0xff;
|
priv->opacity = 0xff;
|
||||||
|
priv->shadow_radius = DEFAULT_SHADOW_RADIUS;
|
||||||
|
priv->shadow_x_offset = DEFAULT_SHADOW_X_OFFSET;
|
||||||
|
priv->shadow_y_offset = DEFAULT_SHADOW_Y_OFFSET;
|
||||||
|
priv->shadow_opacity = 0xff;
|
||||||
|
priv->paint_shadow = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -291,18 +359,6 @@ window_decorated_notify (MetaWindow *mw,
|
|||||||
|
|
||||||
g_object_set (self, "x-window-attributes", &attrs, NULL);
|
g_object_set (self, "x-window-attributes", &attrs, NULL);
|
||||||
|
|
||||||
if (priv->shadow)
|
|
||||||
{
|
|
||||||
ClutterActor *p = clutter_actor_get_parent (priv->shadow);
|
|
||||||
|
|
||||||
if (CLUTTER_IS_CONTAINER (p))
|
|
||||||
clutter_container_remove_actor (CLUTTER_CONTAINER (p), priv->shadow);
|
|
||||||
else
|
|
||||||
clutter_actor_unparent (priv->shadow);
|
|
||||||
|
|
||||||
priv->shadow = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Recreate the contents.
|
* Recreate the contents.
|
||||||
*/
|
*/
|
||||||
@ -344,13 +400,6 @@ meta_window_actor_constructed (GObject *object)
|
|||||||
|
|
||||||
meta_window_actor_update_opacity (self);
|
meta_window_actor_update_opacity (self);
|
||||||
|
|
||||||
if (meta_window_actor_has_shadow (self))
|
|
||||||
{
|
|
||||||
priv->shadow = meta_create_shadow_frame (compositor);
|
|
||||||
|
|
||||||
clutter_container_add_actor (CLUTTER_CONTAINER (self), priv->shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!priv->actor)
|
if (!priv->actor)
|
||||||
{
|
{
|
||||||
priv->actor = meta_shaped_texture_new ();
|
priv->actor = meta_shaped_texture_new ();
|
||||||
@ -407,6 +456,18 @@ meta_window_actor_dispose (GObject *object)
|
|||||||
meta_window_actor_clear_shape_region (self);
|
meta_window_actor_clear_shape_region (self);
|
||||||
meta_window_actor_clear_bounding_region (self);
|
meta_window_actor_clear_bounding_region (self);
|
||||||
|
|
||||||
|
if (priv->shadow != NULL)
|
||||||
|
{
|
||||||
|
meta_shadow_unref (priv->shadow);
|
||||||
|
priv->shadow = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priv->shadow_shape != NULL)
|
||||||
|
{
|
||||||
|
meta_window_shape_unref (priv->shadow_shape);
|
||||||
|
priv->shadow_shape = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (priv->damage != None)
|
if (priv->damage != None)
|
||||||
{
|
{
|
||||||
meta_error_trap_push (display);
|
meta_error_trap_push (display);
|
||||||
@ -463,36 +524,60 @@ meta_window_actor_set_property (GObject *object,
|
|||||||
break;
|
break;
|
||||||
case PROP_NO_SHADOW:
|
case PROP_NO_SHADOW:
|
||||||
{
|
{
|
||||||
gboolean oldv = priv->no_shadow ? TRUE : FALSE;
|
|
||||||
gboolean newv = g_value_get_boolean (value);
|
gboolean newv = g_value_get_boolean (value);
|
||||||
|
|
||||||
if (oldv == newv)
|
if (newv == priv->no_shadow)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
priv->no_shadow = newv;
|
priv->no_shadow = newv;
|
||||||
|
|
||||||
if (newv && priv->shadow)
|
priv->recompute_shadow = TRUE;
|
||||||
{
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
|
||||||
clutter_container_remove_actor (CLUTTER_CONTAINER (object),
|
|
||||||
priv->shadow);
|
|
||||||
priv->shadow = NULL;
|
|
||||||
}
|
}
|
||||||
else if (!newv && !priv->shadow && meta_window_actor_has_shadow (self))
|
break;
|
||||||
|
case PROP_SHADOW_RADIUS:
|
||||||
{
|
{
|
||||||
gfloat w, h;
|
gint newv = g_value_get_int (value);
|
||||||
MetaDisplay *display = meta_screen_get_display (priv->screen);
|
|
||||||
MetaCompositor *compositor;
|
|
||||||
|
|
||||||
compositor = meta_display_get_compositor (display);
|
if (newv == priv->shadow_radius)
|
||||||
|
return;
|
||||||
|
|
||||||
clutter_actor_get_size (CLUTTER_ACTOR (self), &w, &h);
|
priv->shadow_radius = newv;
|
||||||
|
priv->recompute_shadow = TRUE;
|
||||||
priv->shadow = meta_create_shadow_frame (compositor);
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
|
||||||
|
|
||||||
clutter_actor_set_size (priv->shadow, w, h);
|
|
||||||
|
|
||||||
clutter_container_add_actor (CLUTTER_CONTAINER (self), priv->shadow);
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case PROP_SHADOW_X_OFFSET:
|
||||||
|
{
|
||||||
|
gint newv = g_value_get_int (value);
|
||||||
|
|
||||||
|
if (newv == priv->shadow_x_offset)
|
||||||
|
return;
|
||||||
|
|
||||||
|
priv->shadow_x_offset = newv;
|
||||||
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PROP_SHADOW_Y_OFFSET:
|
||||||
|
{
|
||||||
|
gint newv = g_value_get_int (value);
|
||||||
|
|
||||||
|
if (newv == priv->shadow_y_offset)
|
||||||
|
return;
|
||||||
|
|
||||||
|
priv->shadow_y_offset = newv;
|
||||||
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PROP_SHADOW_OPACITY:
|
||||||
|
{
|
||||||
|
guint newv = g_value_get_uint (value);
|
||||||
|
|
||||||
|
if (newv == priv->shadow_opacity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
priv->shadow_opacity = newv;
|
||||||
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -526,12 +611,57 @@ meta_window_actor_get_property (GObject *object,
|
|||||||
case PROP_NO_SHADOW:
|
case PROP_NO_SHADOW:
|
||||||
g_value_set_boolean (value, priv->no_shadow);
|
g_value_set_boolean (value, priv->no_shadow);
|
||||||
break;
|
break;
|
||||||
|
case PROP_SHADOW_RADIUS:
|
||||||
|
g_value_set_int (value, priv->shadow_radius);
|
||||||
|
break;
|
||||||
|
case PROP_SHADOW_X_OFFSET:
|
||||||
|
g_value_set_int (value, priv->shadow_x_offset);
|
||||||
|
break;
|
||||||
|
case PROP_SHADOW_Y_OFFSET:
|
||||||
|
g_value_set_int (value, priv->shadow_y_offset);
|
||||||
|
break;
|
||||||
|
case PROP_SHADOW_OPACITY:
|
||||||
|
g_value_set_uint (value, priv->shadow_opacity);
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_window_actor_get_shape_bounds (MetaWindowActor *self,
|
||||||
|
cairo_rectangle_int_t *bounds)
|
||||||
|
{
|
||||||
|
MetaWindowActorPrivate *priv = self->priv;
|
||||||
|
|
||||||
|
if (priv->shaped)
|
||||||
|
cairo_region_get_extents (priv->shape_region, bounds);
|
||||||
|
else
|
||||||
|
cairo_region_get_extents (priv->bounding_region, bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_window_actor_paint (ClutterActor *actor)
|
||||||
|
{
|
||||||
|
MetaWindowActor *self = META_WINDOW_ACTOR (actor);
|
||||||
|
MetaWindowActorPrivate *priv = self->priv;
|
||||||
|
|
||||||
|
if (priv->shadow != NULL && priv->paint_shadow)
|
||||||
|
{
|
||||||
|
cairo_rectangle_int_t shape_bounds;
|
||||||
|
meta_window_actor_get_shape_bounds (self, &shape_bounds);
|
||||||
|
|
||||||
|
meta_shadow_paint (priv->shadow,
|
||||||
|
priv->shadow_x_offset + shape_bounds.x,
|
||||||
|
priv->shadow_y_offset + shape_bounds.y,
|
||||||
|
shape_bounds.width,
|
||||||
|
shape_bounds.height,
|
||||||
|
(clutter_actor_get_paint_opacity (actor) * priv->shadow_opacity) / 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
CLUTTER_ACTOR_CLASS (meta_window_actor_parent_class)->paint (actor);
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
is_shaped (MetaDisplay *display, Window xwindow)
|
is_shaped (MetaDisplay *display, Window xwindow)
|
||||||
{
|
{
|
||||||
@ -560,10 +690,12 @@ meta_window_actor_has_shadow (MetaWindowActor *self)
|
|||||||
if (priv->no_shadow)
|
if (priv->no_shadow)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
if (priv->shadow_radius == 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Always put a shadow around windows with a frame - This should override
|
* Always put a shadow around windows with a frame - This should override
|
||||||
* the restriction about not putting a shadow around shaped windows
|
* the restriction about not putting a shadow around ARGB windows.
|
||||||
* as the frame might be the reason the window is shaped
|
|
||||||
*/
|
*/
|
||||||
if (priv->window)
|
if (priv->window)
|
||||||
{
|
{
|
||||||
@ -576,7 +708,8 @@ meta_window_actor_has_shadow (MetaWindowActor *self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do not add shadows to ARGB windows (since they are probably transparent)
|
* Do not add shadows to ARGB windows; eventually we should generate a
|
||||||
|
* shadow from the input shape for such windows.
|
||||||
*/
|
*/
|
||||||
if (priv->argb32 || priv->opacity != 0xff)
|
if (priv->argb32 || priv->opacity != 0xff)
|
||||||
{
|
{
|
||||||
@ -585,19 +718,8 @@ meta_window_actor_has_shadow (MetaWindowActor *self)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Never put a shadow around shaped windows
|
|
||||||
*/
|
|
||||||
if (priv->shaped)
|
|
||||||
{
|
|
||||||
meta_verbose ("Window 0x%x has no shadow as it is shaped\n",
|
|
||||||
(guint)priv->xwindow);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add shadows to override redirect windows (e.g., Gtk menus).
|
* Add shadows to override redirect windows (e.g., Gtk menus).
|
||||||
* This must have lower priority than window shape test.
|
|
||||||
*/
|
*/
|
||||||
if (priv->attrs.override_redirect)
|
if (priv->attrs.override_redirect)
|
||||||
{
|
{
|
||||||
@ -1366,9 +1488,26 @@ meta_window_actor_update_bounding_region (MetaWindowActor *self,
|
|||||||
MetaWindowActorPrivate *priv = self->priv;
|
MetaWindowActorPrivate *priv = self->priv;
|
||||||
cairo_rectangle_int_t bounding_rectangle = { 0, 0, width, height };
|
cairo_rectangle_int_t bounding_rectangle = { 0, 0, width, height };
|
||||||
|
|
||||||
|
if (priv->bounding_region != NULL)
|
||||||
|
{
|
||||||
|
cairo_rectangle_int_t old_bounding_rectangle;
|
||||||
|
cairo_region_get_extents (priv->bounding_region, &old_bounding_rectangle);
|
||||||
|
|
||||||
|
if (old_bounding_rectangle.width == width && old_bounding_rectangle.height == height)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
meta_window_actor_clear_bounding_region (self);
|
meta_window_actor_clear_bounding_region (self);
|
||||||
|
|
||||||
priv->bounding_region = cairo_region_create_rectangle (&bounding_rectangle);
|
priv->bounding_region = cairo_region_create_rectangle (&bounding_rectangle);
|
||||||
|
|
||||||
|
/* When we're shaped, we use the shape region to generate the shadow; the shape
|
||||||
|
* region only changes when we get ShapeNotify event; but for unshaped windows
|
||||||
|
* we generate the shadow from the bounding region, so we need to recompute
|
||||||
|
* the shadow when the size changes.
|
||||||
|
*/
|
||||||
|
if (!priv->shaped)
|
||||||
|
priv->recompute_shadow = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1500,8 +1639,8 @@ meta_window_actor_set_visible_region_beneath (MetaWindowActor *self,
|
|||||||
|
|
||||||
if (priv->shadow)
|
if (priv->shadow)
|
||||||
{
|
{
|
||||||
cairo_rectangle_int_t shadow_rect;
|
cairo_rectangle_int_t shape_bounds;
|
||||||
ClutterActorBox box;
|
cairo_rectangle_int_t shadow_bounds;
|
||||||
cairo_region_overlap_t overlap;
|
cairo_region_overlap_t overlap;
|
||||||
|
|
||||||
/* We could compute an full clip region as we do for the window
|
/* We could compute an full clip region as we do for the window
|
||||||
@ -1510,17 +1649,17 @@ meta_window_actor_set_visible_region_beneath (MetaWindowActor *self,
|
|||||||
* the shadow is completely obscured and doesn't need to be drawn
|
* the shadow is completely obscured and doesn't need to be drawn
|
||||||
* at all.
|
* at all.
|
||||||
*/
|
*/
|
||||||
clutter_actor_get_allocation_box (priv->shadow, &box);
|
meta_window_actor_get_shape_bounds (self, &shape_bounds);
|
||||||
|
|
||||||
shadow_rect.x = roundf (box.x1);
|
meta_shadow_get_bounds (priv->shadow,
|
||||||
shadow_rect.y = roundf (box.y1);
|
priv->shadow_x_offset + shape_bounds.x,
|
||||||
shadow_rect.width = roundf (box.x2 - box.x1);
|
priv->shadow_y_offset + shape_bounds.y,
|
||||||
shadow_rect.height = roundf (box.y2 - box.y1);
|
shape_bounds.width,
|
||||||
|
shape_bounds.height,
|
||||||
|
&shadow_bounds);
|
||||||
|
|
||||||
overlap = cairo_region_contains_rectangle (beneath_region, &shadow_rect);
|
overlap = cairo_region_contains_rectangle (beneath_region, &shadow_bounds);
|
||||||
|
priv->paint_shadow = overlap != CAIRO_REGION_OVERLAP_OUT;
|
||||||
tidy_texture_frame_set_needs_paint (TIDY_TEXTURE_FRAME (priv->shadow),
|
|
||||||
overlap != CAIRO_REGION_OVERLAP_OUT);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1538,8 +1677,7 @@ meta_window_actor_reset_visible_regions (MetaWindowActor *self)
|
|||||||
|
|
||||||
meta_shaped_texture_set_clip_region (META_SHAPED_TEXTURE (priv->actor),
|
meta_shaped_texture_set_clip_region (META_SHAPED_TEXTURE (priv->actor),
|
||||||
NULL);
|
NULL);
|
||||||
if (priv->shadow)
|
priv->paint_shadow = TRUE;
|
||||||
tidy_texture_frame_set_needs_paint (TIDY_TEXTURE_FRAME (priv->shadow), TRUE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1622,9 +1760,6 @@ check_needs_pixmap (MetaWindowActor *self)
|
|||||||
"pixmap-height", &pxm_height,
|
"pixmap-height", &pxm_height,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
if (priv->shadow)
|
|
||||||
clutter_actor_set_size (priv->shadow, pxm_width, pxm_height);
|
|
||||||
|
|
||||||
meta_window_actor_update_bounding_region (self, pxm_width, pxm_height);
|
meta_window_actor_update_bounding_region (self, pxm_width, pxm_height);
|
||||||
|
|
||||||
full = TRUE;
|
full = TRUE;
|
||||||
@ -1635,6 +1770,58 @@ check_needs_pixmap (MetaWindowActor *self)
|
|||||||
priv->needs_pixmap = FALSE;
|
priv->needs_pixmap = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
check_needs_shadow (MetaWindowActor *self)
|
||||||
|
{
|
||||||
|
MetaWindowActorPrivate *priv = self->priv;
|
||||||
|
MetaShadow *old_shadow = NULL;
|
||||||
|
gboolean should_have_shadow;
|
||||||
|
|
||||||
|
if (!priv->mapped)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Calling meta_window_actor_has_shadow() here at every pre-paint is cheap
|
||||||
|
* and avoids the need to explicitly handle window type changes, which
|
||||||
|
* we would do if tried to keep track of when we might be adding or removing
|
||||||
|
* a shadow more explicitly. We only keep track of changes to the *shape* of
|
||||||
|
* the shadow with priv->recompute_shadow.
|
||||||
|
*/
|
||||||
|
|
||||||
|
should_have_shadow = meta_window_actor_has_shadow (self);
|
||||||
|
|
||||||
|
if (priv->shadow != NULL && (!should_have_shadow || priv->recompute_shadow))
|
||||||
|
{
|
||||||
|
old_shadow = priv->shadow;
|
||||||
|
priv->shadow = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priv->shadow == NULL && should_have_shadow)
|
||||||
|
{
|
||||||
|
MetaShadowFactory *factory = meta_shadow_factory_get_default ();
|
||||||
|
cairo_rectangle_int_t shape_bounds;
|
||||||
|
|
||||||
|
if (priv->shadow_shape == NULL)
|
||||||
|
{
|
||||||
|
if (priv->shaped)
|
||||||
|
priv->shadow_shape = meta_window_shape_new (priv->shape_region);
|
||||||
|
else
|
||||||
|
priv->shadow_shape = meta_window_shape_new (priv->bounding_region);
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_window_actor_get_shape_bounds (self, &shape_bounds);
|
||||||
|
|
||||||
|
priv->shadow = meta_shadow_factory_get_shadow (factory,
|
||||||
|
priv->shadow_shape,
|
||||||
|
shape_bounds.width, shape_bounds.height,
|
||||||
|
priv->shadow_radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_shadow != NULL)
|
||||||
|
meta_shadow_unref (old_shadow);
|
||||||
|
|
||||||
|
priv->recompute_shadow = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
is_frozen (MetaWindowActor *self)
|
is_frozen (MetaWindowActor *self)
|
||||||
{
|
{
|
||||||
@ -1733,6 +1920,7 @@ check_needs_reshape (MetaWindowActor *self)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
priv->needs_reshape = FALSE;
|
priv->needs_reshape = FALSE;
|
||||||
|
priv->recompute_shadow = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1743,6 +1931,11 @@ meta_window_actor_update_shape (MetaWindowActor *self,
|
|||||||
|
|
||||||
priv->shaped = shaped;
|
priv->shaped = shaped;
|
||||||
priv->needs_reshape = TRUE;
|
priv->needs_reshape = TRUE;
|
||||||
|
if (priv->shadow_shape != NULL)
|
||||||
|
{
|
||||||
|
meta_window_shape_unref (priv->shadow_shape);
|
||||||
|
priv->shadow_shape = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
clutter_actor_queue_redraw (priv->actor);
|
clutter_actor_queue_redraw (priv->actor);
|
||||||
}
|
}
|
||||||
@ -1770,8 +1963,9 @@ meta_window_actor_pre_paint (MetaWindowActor *self)
|
|||||||
priv->received_damage = FALSE;
|
priv->received_damage = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
check_needs_reshape (self);
|
|
||||||
check_needs_pixmap (self);
|
check_needs_pixmap (self);
|
||||||
|
check_needs_reshape (self);
|
||||||
|
check_needs_shadow (self);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
254
src/compositor/meta-window-shape.c
Normal file
254
src/compositor/meta-window-shape.c
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
/* -*- 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;
|
||||||
|
}
|
||||||
|
|
60
src/compositor/meta-window-shape.h
Normal file
60
src/compositor/meta-window-shape.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/* -*- 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __META_WINDOW_SHAPE_H__
|
||||||
|
#define __META_WINDOW_SHAPE_H__
|
||||||
|
|
||||||
|
#include <cairo.h>
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MetaWindowShape:
|
||||||
|
* #MetaWindowShape represents a 9-sliced region with borders on all sides that
|
||||||
|
* are unscaled, and a constant central region that is scaled. For example,
|
||||||
|
* the regions representing two windows that are rounded rectangles,
|
||||||
|
* with the same corner radius but different sizes, have the
|
||||||
|
* same MetaWindowShape.
|
||||||
|
*
|
||||||
|
* #MetaWindowShape is designed to be used as part of a hash table key, so has
|
||||||
|
* efficient hash and equal functions.
|
||||||
|
*/
|
||||||
|
typedef struct _MetaWindowShape MetaWindowShape;
|
||||||
|
|
||||||
|
MetaWindowShape * meta_window_shape_new (cairo_region_t *region);
|
||||||
|
MetaWindowShape * meta_window_shape_ref (MetaWindowShape *shape);
|
||||||
|
void meta_window_shape_unref (MetaWindowShape *shape);
|
||||||
|
guint meta_window_shape_hash (MetaWindowShape *shape);
|
||||||
|
gboolean meta_window_shape_equal (MetaWindowShape *shape_a,
|
||||||
|
MetaWindowShape *shape_b);
|
||||||
|
void meta_window_shape_get_borders (MetaWindowShape *shape,
|
||||||
|
int *border_top,
|
||||||
|
int *border_right,
|
||||||
|
int *border_bottom,
|
||||||
|
int *border_left);
|
||||||
|
cairo_region_t *meta_window_shape_to_region (MetaWindowShape *shape,
|
||||||
|
int center_width,
|
||||||
|
int center_height);
|
||||||
|
|
||||||
|
#endif /* __META_WINDOW_SHAPE_H __*/
|
||||||
|
|
336
src/compositor/region-utils.c
Normal file
336
src/compositor/region-utils.c
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
/*
|
||||||
|
* Utilities for region manipulation
|
||||||
|
*
|
||||||
|
* 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 "region-utils.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
/* MetaRegionBuilder */
|
||||||
|
|
||||||
|
/* Various algorithms in this file require unioning together a set of rectangles
|
||||||
|
* that are unsorted or overlap; unioning such a set of rectangles 1-by-1
|
||||||
|
* using cairo_region_union_rectangle() produces O(N^2) behavior (if the union
|
||||||
|
* adds or removes rectangles in the middle of the region, then it has to
|
||||||
|
* move all the rectangles after that.) To avoid this behavior, MetaRegionBuilder
|
||||||
|
* creates regions for small groups of rectangles and merges them together in
|
||||||
|
* a binary tree.
|
||||||
|
*
|
||||||
|
* Possible improvement: From a glance at the code, accumulating all the rectangles
|
||||||
|
* into a flat array and then calling the (not usefully documented)
|
||||||
|
* cairo_region_create_rectangles() would have the same behavior and would be
|
||||||
|
* simpler and a bit more efficient.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Optimium performance seems to be with MAX_CHUNK_RECTANGLES=4; 8 is about 10% slower.
|
||||||
|
* But using 8 may be more robust to systems with slow malloc(). */
|
||||||
|
#define MAX_CHUNK_RECTANGLES 8
|
||||||
|
#define MAX_LEVELS 16
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/* To merge regions in binary tree order, we need to keep track of
|
||||||
|
* the regions that we've already merged together at different
|
||||||
|
* levels of the tree. We fill in an array in the pattern:
|
||||||
|
*
|
||||||
|
* |a |
|
||||||
|
* |b |a |
|
||||||
|
* |c | |ab |
|
||||||
|
* |d |c |ab |
|
||||||
|
* |e | | |abcd|
|
||||||
|
*/
|
||||||
|
cairo_region_t *levels[MAX_LEVELS];
|
||||||
|
int n_levels;
|
||||||
|
} MetaRegionBuilder;
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_region_builder_init (MetaRegionBuilder *builder)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < MAX_LEVELS; i++)
|
||||||
|
builder->levels[i] = NULL;
|
||||||
|
builder->n_levels = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_region_builder_add_rectangle (MetaRegionBuilder *builder,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
int width,
|
||||||
|
int height)
|
||||||
|
{
|
||||||
|
cairo_rectangle_int_t rect;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (builder->levels[0] == NULL)
|
||||||
|
builder->levels[0] = cairo_region_create ();
|
||||||
|
|
||||||
|
rect.x = x;
|
||||||
|
rect.y = y;
|
||||||
|
rect.width = width;
|
||||||
|
rect.height = height;
|
||||||
|
|
||||||
|
cairo_region_union_rectangle (builder->levels[0], &rect);
|
||||||
|
if (cairo_region_num_rectangles (builder->levels[0]) >= MAX_CHUNK_RECTANGLES)
|
||||||
|
{
|
||||||
|
for (i = 1; i < builder->n_levels + 1; i++)
|
||||||
|
{
|
||||||
|
if (builder->levels[i] == NULL)
|
||||||
|
{
|
||||||
|
if (i < MAX_LEVELS)
|
||||||
|
{
|
||||||
|
builder->levels[i] = builder->levels[i - 1];
|
||||||
|
builder->levels[i - 1] = NULL;
|
||||||
|
if (i == builder->n_levels)
|
||||||
|
builder->n_levels++;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cairo_region_union (builder->levels[i], builder->levels[i - 1]);
|
||||||
|
cairo_region_destroy (builder->levels[i - 1]);
|
||||||
|
builder->levels[i - 1] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static cairo_region_t *
|
||||||
|
meta_region_builder_finish (MetaRegionBuilder *builder)
|
||||||
|
{
|
||||||
|
cairo_region_t *result = NULL;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < builder->n_levels; i++)
|
||||||
|
{
|
||||||
|
if (builder->levels[i])
|
||||||
|
{
|
||||||
|
if (result == NULL)
|
||||||
|
result = builder->levels[i];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cairo_region_union(result, builder->levels[i]);
|
||||||
|
cairo_region_destroy (builder->levels[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == NULL)
|
||||||
|
result = cairo_region_create ();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* MetaRegionIterator */
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_region_iterator_init (MetaRegionIterator *iter,
|
||||||
|
cairo_region_t *region)
|
||||||
|
{
|
||||||
|
iter->region = region;
|
||||||
|
iter->i = 0;
|
||||||
|
iter->n_rectangles = cairo_region_num_rectangles (region);
|
||||||
|
iter->line_start = TRUE;
|
||||||
|
|
||||||
|
if (iter->n_rectangles > 1)
|
||||||
|
{
|
||||||
|
cairo_region_get_rectangle (region, 0, &iter->rectangle);
|
||||||
|
cairo_region_get_rectangle (region, 1, &iter->next_rectangle);
|
||||||
|
|
||||||
|
iter->line_end = iter->next_rectangle.y != iter->rectangle.y;
|
||||||
|
}
|
||||||
|
else if (iter->n_rectangles > 0)
|
||||||
|
{
|
||||||
|
cairo_region_get_rectangle (region, 0, &iter->rectangle);
|
||||||
|
iter->line_end = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
meta_region_iterator_at_end (MetaRegionIterator *iter)
|
||||||
|
{
|
||||||
|
return iter->i >= iter->n_rectangles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
meta_region_iterator_next (MetaRegionIterator *iter)
|
||||||
|
{
|
||||||
|
iter->i++;
|
||||||
|
iter->rectangle = iter->next_rectangle;
|
||||||
|
iter->line_start = iter->line_end;
|
||||||
|
|
||||||
|
if (iter->i < iter->n_rectangles)
|
||||||
|
{
|
||||||
|
cairo_region_get_rectangle (iter->region, iter->i + 1, &iter->next_rectangle);
|
||||||
|
iter->line_end = iter->next_rectangle.y != iter->rectangle.y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iter->line_end = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_expanded_rect (MetaRegionBuilder *builder,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int x_amount,
|
||||||
|
int y_amount,
|
||||||
|
gboolean flip)
|
||||||
|
{
|
||||||
|
if (flip)
|
||||||
|
meta_region_builder_add_rectangle (builder,
|
||||||
|
y - y_amount, x - x_amount,
|
||||||
|
height + 2 * y_amount, width + 2 * x_amount);
|
||||||
|
else
|
||||||
|
meta_region_builder_add_rectangle (builder,
|
||||||
|
x - x_amount, y - y_amount,
|
||||||
|
width + 2 * x_amount, height + 2 * y_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
static cairo_region_t *
|
||||||
|
expand_region (cairo_region_t *region,
|
||||||
|
int x_amount,
|
||||||
|
int y_amount,
|
||||||
|
gboolean flip)
|
||||||
|
{
|
||||||
|
MetaRegionBuilder builder;
|
||||||
|
int n;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
meta_region_builder_init (&builder);
|
||||||
|
|
||||||
|
n = cairo_region_num_rectangles (region);
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
cairo_rectangle_int_t rect;
|
||||||
|
|
||||||
|
cairo_region_get_rectangle (region, i, &rect);
|
||||||
|
add_expanded_rect (&builder,
|
||||||
|
rect.x, rect.y, rect.width, rect.height,
|
||||||
|
x_amount, y_amount, flip);
|
||||||
|
}
|
||||||
|
|
||||||
|
return meta_region_builder_finish (&builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This computes a (clipped version) of the inverse of the region
|
||||||
|
* and expands it by the given amount */
|
||||||
|
static cairo_region_t *
|
||||||
|
expand_region_inverse (cairo_region_t *region,
|
||||||
|
int x_amount,
|
||||||
|
int y_amount,
|
||||||
|
gboolean flip)
|
||||||
|
{
|
||||||
|
MetaRegionBuilder builder;
|
||||||
|
MetaRegionIterator iter;
|
||||||
|
cairo_rectangle_int_t extents;
|
||||||
|
cairo_region_t *chunk;
|
||||||
|
|
||||||
|
int last_x;
|
||||||
|
|
||||||
|
meta_region_builder_init (&builder);
|
||||||
|
|
||||||
|
cairo_region_get_extents (region, &extents);
|
||||||
|
add_expanded_rect (&builder,
|
||||||
|
extents.x, extents.y - 1, extents.width, 1,
|
||||||
|
x_amount, y_amount, flip);
|
||||||
|
add_expanded_rect (&builder,
|
||||||
|
extents.x - 1, extents.y, 1, extents.height,
|
||||||
|
x_amount, y_amount, flip);
|
||||||
|
add_expanded_rect (&builder,
|
||||||
|
extents.x + extents.width, extents.y, 1, extents.height,
|
||||||
|
x_amount, y_amount, flip);
|
||||||
|
add_expanded_rect (&builder,
|
||||||
|
extents.x, extents.y + extents.height, extents.width, 1,
|
||||||
|
x_amount, y_amount, flip);
|
||||||
|
|
||||||
|
chunk = NULL;
|
||||||
|
|
||||||
|
last_x = extents.x;
|
||||||
|
for (meta_region_iterator_init (&iter, region);
|
||||||
|
!meta_region_iterator_at_end (&iter);
|
||||||
|
meta_region_iterator_next (&iter))
|
||||||
|
{
|
||||||
|
if (chunk == NULL)
|
||||||
|
chunk = cairo_region_create ();
|
||||||
|
|
||||||
|
if (iter.rectangle.x > last_x)
|
||||||
|
add_expanded_rect (&builder,
|
||||||
|
last_x, iter.rectangle.y,
|
||||||
|
iter.rectangle.x - last_x, iter.rectangle.height,
|
||||||
|
x_amount, y_amount, flip);
|
||||||
|
|
||||||
|
if (iter.line_end)
|
||||||
|
{
|
||||||
|
if (extents.x + extents.width > iter.rectangle.x + iter.rectangle.width)
|
||||||
|
add_expanded_rect (&builder,
|
||||||
|
iter.rectangle.x + iter.rectangle.width, iter.rectangle.y,
|
||||||
|
(extents.x + extents.width) - (iter.rectangle.x + iter.rectangle.width), iter.rectangle.height,
|
||||||
|
x_amount, y_amount, flip);
|
||||||
|
last_x = extents.x;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
last_x = iter.rectangle.x + iter.rectangle.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
return meta_region_builder_finish (&builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meta_make_border_region:
|
||||||
|
* @region: a #cairo_region_t
|
||||||
|
* @x_amount: distance from the border to extend horizontally
|
||||||
|
* @y_amount: distance from the border to extend vertically
|
||||||
|
* @flip: if true, the result is computed with x and y interchanged
|
||||||
|
*
|
||||||
|
* Computes the "border region" of a given region, which is roughly
|
||||||
|
* speaking the set of points near the boundary of the region. If we
|
||||||
|
* define the operation of growing a region as computing the set of
|
||||||
|
* points within a given manhattan distance of the region, then the
|
||||||
|
* border is 'grow(region) intersect grow(inverse(region))'.
|
||||||
|
*
|
||||||
|
* If we create an image by filling the region with a solid color,
|
||||||
|
* the border is the region affected by blurring the region.
|
||||||
|
*
|
||||||
|
* Return value: a new region which is the border of the given region
|
||||||
|
*/
|
||||||
|
cairo_region_t *
|
||||||
|
meta_make_border_region (cairo_region_t *region,
|
||||||
|
int x_amount,
|
||||||
|
int y_amount,
|
||||||
|
gboolean flip)
|
||||||
|
{
|
||||||
|
cairo_region_t *border_region;
|
||||||
|
cairo_region_t *inverse_region;
|
||||||
|
|
||||||
|
border_region = expand_region (region, x_amount, y_amount, flip);
|
||||||
|
inverse_region = expand_region_inverse (region, x_amount, y_amount, flip);
|
||||||
|
cairo_region_intersect (border_region, inverse_region);
|
||||||
|
cairo_region_destroy (inverse_region);
|
||||||
|
|
||||||
|
return border_region;
|
||||||
|
}
|
76
src/compositor/region-utils.h
Normal file
76
src/compositor/region-utils.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
/*
|
||||||
|
* Utilities for region manipulation
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __META_REGION_UTILS_H__
|
||||||
|
#define __META_REGION_UTILS_H__
|
||||||
|
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
|
#include <cairo.h>
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MetaRegionIterator:
|
||||||
|
* @region: region being iterated
|
||||||
|
* @rectangle: current rectangle
|
||||||
|
* @line_start: whether the current rectangle starts a horizontal band
|
||||||
|
* @line_end: whether the current rectangle ends a horizontal band
|
||||||
|
*
|
||||||
|
* cairo_region_t is a yx banded region; sometimes its useful to iterate through
|
||||||
|
* such a region treating the start and end of each horizontal band in a distinct
|
||||||
|
* fashion.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
*
|
||||||
|
* MetaRegionIterator iter;
|
||||||
|
* for (meta_region_iterator_init (&iter, region);
|
||||||
|
* !meta_region_iterator_at_end (&iter);
|
||||||
|
* meta_region_iterator_next (&iter))
|
||||||
|
* {
|
||||||
|
* [ Use iter.rectangle, iter.line_start, iter.line_end ]
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
typedef struct _MetaRegionIterator MetaRegionIterator;
|
||||||
|
|
||||||
|
struct _MetaRegionIterator {
|
||||||
|
cairo_region_t *region;
|
||||||
|
cairo_rectangle_int_t rectangle;
|
||||||
|
gboolean line_start;
|
||||||
|
gboolean line_end;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/*< private >*/
|
||||||
|
int n_rectangles;
|
||||||
|
cairo_rectangle_int_t next_rectangle;
|
||||||
|
};
|
||||||
|
|
||||||
|
void meta_region_iterator_init (MetaRegionIterator *iter,
|
||||||
|
cairo_region_t *region);
|
||||||
|
gboolean meta_region_iterator_at_end (MetaRegionIterator *iter);
|
||||||
|
void meta_region_iterator_next (MetaRegionIterator *iter);
|
||||||
|
|
||||||
|
cairo_region_t *meta_make_border_region (cairo_region_t *region,
|
||||||
|
int x_amount,
|
||||||
|
int y_amount,
|
||||||
|
gboolean flip);
|
||||||
|
|
||||||
|
#endif /* __META_REGION_UTILS_H__ */
|
@ -1,350 +0,0 @@
|
|||||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
||||||
|
|
||||||
#define _GNU_SOURCE /* For M_PI */
|
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include "compositor-private.h"
|
|
||||||
#include "shadow.h"
|
|
||||||
#include "tidy/tidy-texture-frame.h"
|
|
||||||
|
|
||||||
#define SHADOW_RADIUS 8
|
|
||||||
#define SHADOW_OPACITY 0.9
|
|
||||||
#define SHADOW_OFFSET_X (SHADOW_RADIUS)
|
|
||||||
#define SHADOW_OFFSET_Y (SHADOW_RADIUS)
|
|
||||||
|
|
||||||
#define MAX_TILE_SZ 8 /* Must be <= shaddow radius */
|
|
||||||
#define TILE_WIDTH (3*MAX_TILE_SZ)
|
|
||||||
#define TILE_HEIGHT (3*MAX_TILE_SZ)
|
|
||||||
|
|
||||||
static unsigned char* shadow_gaussian_make_tile (void);
|
|
||||||
|
|
||||||
ClutterActor *
|
|
||||||
meta_create_shadow_frame (MetaCompositor *compositor)
|
|
||||||
{
|
|
||||||
ClutterActor *frame;
|
|
||||||
|
|
||||||
if (!compositor->shadow_src)
|
|
||||||
{
|
|
||||||
guchar *data;
|
|
||||||
|
|
||||||
data = shadow_gaussian_make_tile ();
|
|
||||||
|
|
||||||
compositor->shadow_src = clutter_texture_new ();
|
|
||||||
|
|
||||||
clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE (compositor->shadow_src),
|
|
||||||
data,
|
|
||||||
TRUE,
|
|
||||||
TILE_WIDTH,
|
|
||||||
TILE_HEIGHT,
|
|
||||||
TILE_WIDTH*4,
|
|
||||||
4,
|
|
||||||
0,
|
|
||||||
NULL);
|
|
||||||
g_free (data);
|
|
||||||
}
|
|
||||||
|
|
||||||
frame = tidy_texture_frame_new (CLUTTER_TEXTURE (compositor->shadow_src),
|
|
||||||
MAX_TILE_SZ,
|
|
||||||
MAX_TILE_SZ,
|
|
||||||
MAX_TILE_SZ,
|
|
||||||
MAX_TILE_SZ);
|
|
||||||
|
|
||||||
clutter_actor_set_position (frame,
|
|
||||||
SHADOW_OFFSET_X , SHADOW_OFFSET_Y);
|
|
||||||
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct GaussianMap
|
|
||||||
{
|
|
||||||
int size;
|
|
||||||
double * data;
|
|
||||||
} GaussianMap;
|
|
||||||
|
|
||||||
static double
|
|
||||||
gaussian (double r, double x, double y)
|
|
||||||
{
|
|
||||||
return ((1 / (sqrt (2 * M_PI * r))) *
|
|
||||||
exp ((- (x * x + y * y)) / (2 * r * r)));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static GaussianMap *
|
|
||||||
make_gaussian_map (double r)
|
|
||||||
{
|
|
||||||
GaussianMap *c;
|
|
||||||
int size = ((int) ceil ((r * 3)) + 1) & ~1;
|
|
||||||
int center = size / 2;
|
|
||||||
int x, y;
|
|
||||||
double t = 0.0;
|
|
||||||
double g;
|
|
||||||
|
|
||||||
c = g_malloc (sizeof (GaussianMap) + size * size * sizeof (double));
|
|
||||||
c->size = size;
|
|
||||||
|
|
||||||
c->data = (double *) (c + 1);
|
|
||||||
|
|
||||||
for (y = 0; y < size; y++)
|
|
||||||
for (x = 0; x < size; x++)
|
|
||||||
{
|
|
||||||
g = gaussian (r, (double) (x - center), (double) (y - center));
|
|
||||||
t += g;
|
|
||||||
c->data[y * size + x] = g;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (y = 0; y < size; y++)
|
|
||||||
for (x = 0; x < size; x++)
|
|
||||||
c->data[y*size + x] /= t;
|
|
||||||
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned char
|
|
||||||
sum_gaussian (GaussianMap * map, double opacity,
|
|
||||||
int x, int y, int width, int height)
|
|
||||||
{
|
|
||||||
int fx, fy;
|
|
||||||
double * g_data;
|
|
||||||
double * g_line = map->data;
|
|
||||||
int g_size = map->size;
|
|
||||||
int center = g_size / 2;
|
|
||||||
int fx_start, fx_end;
|
|
||||||
int fy_start, fy_end;
|
|
||||||
double v;
|
|
||||||
unsigned int r;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compute set of filter values which are "in range",
|
|
||||||
* that's the set with:
|
|
||||||
* 0 <= x + (fx-center) && x + (fx-center) < width &&
|
|
||||||
* 0 <= y + (fy-center) && y + (fy-center) < height
|
|
||||||
*
|
|
||||||
* 0 <= x + (fx - center) x + fx - center < width
|
|
||||||
* center - x <= fx fx < width + center - x
|
|
||||||
*/
|
|
||||||
|
|
||||||
fx_start = center - x;
|
|
||||||
if (fx_start < 0)
|
|
||||||
fx_start = 0;
|
|
||||||
fx_end = width + center - x;
|
|
||||||
if (fx_end > g_size)
|
|
||||||
fx_end = g_size;
|
|
||||||
|
|
||||||
fy_start = center - y;
|
|
||||||
if (fy_start < 0)
|
|
||||||
fy_start = 0;
|
|
||||||
fy_end = height + center - y;
|
|
||||||
if (fy_end > g_size)
|
|
||||||
fy_end = g_size;
|
|
||||||
|
|
||||||
g_line = g_line + fy_start * g_size + fx_start;
|
|
||||||
|
|
||||||
v = 0;
|
|
||||||
for (fy = fy_start; fy < fy_end; fy++)
|
|
||||||
{
|
|
||||||
g_data = g_line;
|
|
||||||
g_line += g_size;
|
|
||||||
|
|
||||||
for (fx = fx_start; fx < fx_end; fx++)
|
|
||||||
v += *g_data++;
|
|
||||||
}
|
|
||||||
if (v > 1)
|
|
||||||
v = 1;
|
|
||||||
|
|
||||||
v *= (opacity * 255.0);
|
|
||||||
|
|
||||||
r = (unsigned int) v;
|
|
||||||
|
|
||||||
return (unsigned char) r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned char *
|
|
||||||
shadow_gaussian_make_tile ()
|
|
||||||
{
|
|
||||||
unsigned char * data;
|
|
||||||
int size;
|
|
||||||
int center;
|
|
||||||
int x, y;
|
|
||||||
unsigned char d;
|
|
||||||
int pwidth, pheight;
|
|
||||||
double opacity = SHADOW_OPACITY;
|
|
||||||
static GaussianMap * gaussian_map = NULL;
|
|
||||||
|
|
||||||
struct _mypixel
|
|
||||||
{
|
|
||||||
unsigned char r;
|
|
||||||
unsigned char g;
|
|
||||||
unsigned char b;
|
|
||||||
unsigned char a;
|
|
||||||
} * _d;
|
|
||||||
|
|
||||||
|
|
||||||
if (!gaussian_map)
|
|
||||||
gaussian_map =
|
|
||||||
make_gaussian_map (SHADOW_RADIUS);
|
|
||||||
|
|
||||||
size = gaussian_map->size;
|
|
||||||
center = size / 2;
|
|
||||||
|
|
||||||
/* Top & bottom */
|
|
||||||
|
|
||||||
pwidth = MAX_TILE_SZ;
|
|
||||||
pheight = MAX_TILE_SZ;
|
|
||||||
|
|
||||||
data = g_malloc0 (4 * TILE_WIDTH * TILE_HEIGHT);
|
|
||||||
|
|
||||||
_d = (struct _mypixel*) data;
|
|
||||||
|
|
||||||
/* N */
|
|
||||||
for (y = 0; y < pheight; y++)
|
|
||||||
{
|
|
||||||
d = sum_gaussian (gaussian_map, opacity,
|
|
||||||
center, y - center,
|
|
||||||
TILE_WIDTH, TILE_HEIGHT);
|
|
||||||
for (x = 0; x < pwidth; x++)
|
|
||||||
{
|
|
||||||
_d[y*3*pwidth + x + pwidth].r = 0;
|
|
||||||
_d[y*3*pwidth + x + pwidth].g = 0;
|
|
||||||
_d[y*3*pwidth + x + pwidth].b = 0;
|
|
||||||
_d[y*3*pwidth + x + pwidth].a = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* S */
|
|
||||||
pwidth = MAX_TILE_SZ;
|
|
||||||
pheight = MAX_TILE_SZ;
|
|
||||||
|
|
||||||
for (y = 0; y < pheight; y++)
|
|
||||||
{
|
|
||||||
d = sum_gaussian (gaussian_map, opacity,
|
|
||||||
center, y - center,
|
|
||||||
TILE_WIDTH, TILE_HEIGHT);
|
|
||||||
for (x = 0; x < pwidth; x++)
|
|
||||||
{
|
|
||||||
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x + pwidth].r = 0;
|
|
||||||
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x + pwidth].g = 0;
|
|
||||||
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x + pwidth].b = 0;
|
|
||||||
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x + pwidth].a = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* w */
|
|
||||||
pwidth = MAX_TILE_SZ;
|
|
||||||
pheight = MAX_TILE_SZ;
|
|
||||||
|
|
||||||
for (x = 0; x < pwidth; x++)
|
|
||||||
{
|
|
||||||
d = sum_gaussian (gaussian_map, opacity,
|
|
||||||
x - center, center,
|
|
||||||
TILE_WIDTH, TILE_HEIGHT);
|
|
||||||
for (y = 0; y < pheight; y++)
|
|
||||||
{
|
|
||||||
_d[y*3*pwidth + 3*pwidth*pheight + x].r = 0;
|
|
||||||
_d[y*3*pwidth + 3*pwidth*pheight + x].g = 0;
|
|
||||||
_d[y*3*pwidth + 3*pwidth*pheight + x].b = 0;
|
|
||||||
_d[y*3*pwidth + 3*pwidth*pheight + x].a = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* E */
|
|
||||||
for (x = 0; x < pwidth; x++)
|
|
||||||
{
|
|
||||||
d = sum_gaussian (gaussian_map, opacity,
|
|
||||||
x - center, center,
|
|
||||||
TILE_WIDTH, TILE_HEIGHT);
|
|
||||||
for (y = 0; y < pheight; y++)
|
|
||||||
{
|
|
||||||
_d[y*3*pwidth + 3*pwidth*pheight + (pwidth-x-1) + 2*pwidth].r = 0;
|
|
||||||
_d[y*3*pwidth + 3*pwidth*pheight + (pwidth-x-1) + 2*pwidth].g = 0;
|
|
||||||
_d[y*3*pwidth + 3*pwidth*pheight + (pwidth-x-1) + 2*pwidth].b = 0;
|
|
||||||
_d[y*3*pwidth + 3*pwidth*pheight + (pwidth-x-1) + 2*pwidth].a = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NW */
|
|
||||||
pwidth = MAX_TILE_SZ;
|
|
||||||
pheight = MAX_TILE_SZ;
|
|
||||||
|
|
||||||
for (x = 0; x < pwidth; x++)
|
|
||||||
for (y = 0; y < pheight; y++)
|
|
||||||
{
|
|
||||||
d = sum_gaussian (gaussian_map, opacity,
|
|
||||||
x-center, y-center,
|
|
||||||
TILE_WIDTH, TILE_HEIGHT);
|
|
||||||
|
|
||||||
_d[y*3*pwidth + x].r = 0;
|
|
||||||
_d[y*3*pwidth + x].g = 0;
|
|
||||||
_d[y*3*pwidth + x].b = 0;
|
|
||||||
_d[y*3*pwidth + x].a = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SW */
|
|
||||||
for (x = 0; x < pwidth; x++)
|
|
||||||
for (y = 0; y < pheight; y++)
|
|
||||||
{
|
|
||||||
d = sum_gaussian (gaussian_map, opacity,
|
|
||||||
x-center, y-center,
|
|
||||||
TILE_WIDTH, TILE_HEIGHT);
|
|
||||||
|
|
||||||
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x].r = 0;
|
|
||||||
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x].g = 0;
|
|
||||||
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x].b = 0;
|
|
||||||
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x].a = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SE */
|
|
||||||
for (x = 0; x < pwidth; x++)
|
|
||||||
for (y = 0; y < pheight; y++)
|
|
||||||
{
|
|
||||||
d = sum_gaussian (gaussian_map, opacity,
|
|
||||||
x-center, y-center,
|
|
||||||
TILE_WIDTH, TILE_HEIGHT);
|
|
||||||
|
|
||||||
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + (pwidth-x-1) +
|
|
||||||
2*pwidth].r = 0;
|
|
||||||
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + (pwidth-x-1) +
|
|
||||||
2*pwidth].g = 0;
|
|
||||||
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + (pwidth-x-1) +
|
|
||||||
2*pwidth].b = 0;
|
|
||||||
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + (pwidth-x-1) +
|
|
||||||
2*pwidth].a = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NE */
|
|
||||||
for (x = 0; x < pwidth; x++)
|
|
||||||
for (y = 0; y < pheight; y++)
|
|
||||||
{
|
|
||||||
d = sum_gaussian (gaussian_map, opacity,
|
|
||||||
x-center, y-center,
|
|
||||||
TILE_WIDTH, TILE_HEIGHT);
|
|
||||||
|
|
||||||
_d[y*3*pwidth + (pwidth - x - 1) + 2*pwidth].r = 0;
|
|
||||||
_d[y*3*pwidth + (pwidth - x - 1) + 2*pwidth].g = 0;
|
|
||||||
_d[y*3*pwidth + (pwidth - x - 1) + 2*pwidth].b = 0;
|
|
||||||
_d[y*3*pwidth + (pwidth - x - 1) + 2*pwidth].a = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* center */
|
|
||||||
pwidth = MAX_TILE_SZ;
|
|
||||||
pheight = MAX_TILE_SZ;
|
|
||||||
|
|
||||||
d = sum_gaussian (gaussian_map, opacity,
|
|
||||||
center, center, TILE_WIDTH, TILE_HEIGHT);
|
|
||||||
|
|
||||||
for (x = 0; x < pwidth; x++)
|
|
||||||
for (y = 0; y < pheight; y++)
|
|
||||||
{
|
|
||||||
_d[y*3*pwidth + 3*pwidth*pheight + x + pwidth].r = 0;
|
|
||||||
_d[y*3*pwidth + 3*pwidth*pheight + x + pwidth].g = 0;
|
|
||||||
_d[y*3*pwidth + 3*pwidth*pheight + x + pwidth].b = 0;
|
|
||||||
_d[y*3*pwidth + 3*pwidth*pheight + x + pwidth].a = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
#ifndef SHADOW_H
|
|
||||||
#define SHADOW_H
|
|
||||||
|
|
||||||
#include <clutter/clutter.h>
|
|
||||||
#include "compositor.h"
|
|
||||||
|
|
||||||
ClutterActor *meta_create_shadow_frame (MetaCompositor *compositor);
|
|
||||||
|
|
||||||
#endif /* SHADOW_H */
|
|
@ -1,641 +0,0 @@
|
|||||||
/* tidy-texture-frame.h: Expandible texture actor
|
|
||||||
*
|
|
||||||
* Copyright (C) 2007 OpenedHand
|
|
||||||
*
|
|
||||||
* 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, write to the
|
|
||||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
* Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SECTION:tidy-texture-frame
|
|
||||||
* @short_description: Stretch a texture to fit the entire allocation
|
|
||||||
*
|
|
||||||
* #TidyTextureFrame
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <cogl/cogl.h>
|
|
||||||
|
|
||||||
#include "tidy-texture-frame.h"
|
|
||||||
|
|
||||||
#define TIDY_PARAM_READABLE \
|
|
||||||
(G_PARAM_READABLE | \
|
|
||||||
G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
|
|
||||||
|
|
||||||
#define TIDY_PARAM_READWRITE \
|
|
||||||
(G_PARAM_READABLE | G_PARAM_WRITABLE | \
|
|
||||||
G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
PROP_0,
|
|
||||||
|
|
||||||
PROP_PARENT_TEXTURE,
|
|
||||||
|
|
||||||
PROP_LEFT,
|
|
||||||
PROP_TOP,
|
|
||||||
PROP_RIGHT,
|
|
||||||
PROP_BOTTOM
|
|
||||||
};
|
|
||||||
|
|
||||||
G_DEFINE_TYPE (TidyTextureFrame, tidy_texture_frame, CLUTTER_TYPE_ACTOR);
|
|
||||||
|
|
||||||
#define TIDY_TEXTURE_FRAME_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TIDY_TYPE_TEXTURE_FRAME, TidyTextureFramePrivate))
|
|
||||||
|
|
||||||
struct _TidyTextureFramePrivate
|
|
||||||
{
|
|
||||||
ClutterTexture *parent_texture;
|
|
||||||
|
|
||||||
gfloat left;
|
|
||||||
gfloat top;
|
|
||||||
gfloat right;
|
|
||||||
gfloat bottom;
|
|
||||||
|
|
||||||
CoglHandle material;
|
|
||||||
|
|
||||||
guint needs_paint : 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
tidy_texture_frame_get_preferred_width (ClutterActor *self,
|
|
||||||
gfloat for_height,
|
|
||||||
gfloat *min_width_p,
|
|
||||||
gfloat *natural_width_p)
|
|
||||||
{
|
|
||||||
TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (self)->priv;
|
|
||||||
|
|
||||||
if (G_UNLIKELY (priv->parent_texture == NULL))
|
|
||||||
{
|
|
||||||
if (min_width_p)
|
|
||||||
*min_width_p = 0;
|
|
||||||
|
|
||||||
if (natural_width_p)
|
|
||||||
*natural_width_p = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ClutterActorClass *klass;
|
|
||||||
|
|
||||||
/* by directly querying the parent texture's class implementation
|
|
||||||
* we are going around any override mechanism the parent texture
|
|
||||||
* might have in place, and we ask directly for the original
|
|
||||||
* preferred width
|
|
||||||
*/
|
|
||||||
klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture);
|
|
||||||
klass->get_preferred_width (CLUTTER_ACTOR (priv->parent_texture),
|
|
||||||
for_height,
|
|
||||||
min_width_p,
|
|
||||||
natural_width_p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
tidy_texture_frame_get_preferred_height (ClutterActor *self,
|
|
||||||
gfloat for_width,
|
|
||||||
gfloat *min_height_p,
|
|
||||||
gfloat *natural_height_p)
|
|
||||||
{
|
|
||||||
TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (self)->priv;
|
|
||||||
|
|
||||||
if (G_UNLIKELY (priv->parent_texture == NULL))
|
|
||||||
{
|
|
||||||
if (min_height_p)
|
|
||||||
*min_height_p = 0;
|
|
||||||
|
|
||||||
if (natural_height_p)
|
|
||||||
*natural_height_p = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ClutterActorClass *klass;
|
|
||||||
|
|
||||||
/* by directly querying the parent texture's class implementation
|
|
||||||
* we are going around any override mechanism the parent texture
|
|
||||||
* might have in place, and we ask directly for the original
|
|
||||||
* preferred height
|
|
||||||
*/
|
|
||||||
klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture);
|
|
||||||
klass->get_preferred_height (CLUTTER_ACTOR (priv->parent_texture),
|
|
||||||
for_width,
|
|
||||||
min_height_p,
|
|
||||||
natural_height_p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
tidy_texture_frame_realize (ClutterActor *self)
|
|
||||||
{
|
|
||||||
TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (self)->priv;
|
|
||||||
|
|
||||||
if (priv->material != COGL_INVALID_HANDLE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
priv->material = cogl_material_new ();
|
|
||||||
|
|
||||||
CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
tidy_texture_frame_unrealize (ClutterActor *self)
|
|
||||||
{
|
|
||||||
TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (self)->priv;
|
|
||||||
|
|
||||||
if (priv->material == COGL_INVALID_HANDLE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
cogl_handle_unref (priv->material);
|
|
||||||
priv->material = COGL_INVALID_HANDLE;
|
|
||||||
|
|
||||||
CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
tidy_texture_frame_paint (ClutterActor *self)
|
|
||||||
{
|
|
||||||
TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (self)->priv;
|
|
||||||
CoglHandle cogl_texture = COGL_INVALID_HANDLE;
|
|
||||||
ClutterActorBox box = { 0, };
|
|
||||||
gfloat width, height;
|
|
||||||
gfloat tex_width, tex_height;
|
|
||||||
gfloat ex, ey;
|
|
||||||
gfloat tx1, ty1, tx2, ty2;
|
|
||||||
guint8 opacity;
|
|
||||||
|
|
||||||
/* no need to paint stuff if we don't have a texture */
|
|
||||||
if (G_UNLIKELY (priv->parent_texture == NULL))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!priv->needs_paint)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* parent texture may have been hidden, so need to make sure it gets
|
|
||||||
* realized
|
|
||||||
*/
|
|
||||||
if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_texture))
|
|
||||||
clutter_actor_realize (CLUTTER_ACTOR (priv->parent_texture));
|
|
||||||
|
|
||||||
cogl_texture = clutter_texture_get_cogl_texture (priv->parent_texture);
|
|
||||||
if (cogl_texture == COGL_INVALID_HANDLE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
tex_width = cogl_texture_get_width (cogl_texture);
|
|
||||||
tex_height = cogl_texture_get_height (cogl_texture);
|
|
||||||
|
|
||||||
clutter_actor_get_allocation_box (self, &box);
|
|
||||||
width = box.x2 - box.x1;
|
|
||||||
height = box.y2 - box.y1;
|
|
||||||
|
|
||||||
tx1 = priv->left / tex_width;
|
|
||||||
tx2 = (tex_width - priv->right) / tex_width;
|
|
||||||
ty1 = priv->top / tex_height;
|
|
||||||
ty2 = (tex_height - priv->bottom) / tex_height;
|
|
||||||
|
|
||||||
ex = width - priv->right;
|
|
||||||
if (ex < 0)
|
|
||||||
ex = priv->right; /* FIXME ? */
|
|
||||||
|
|
||||||
ey = height - priv->bottom;
|
|
||||||
if (ey < 0)
|
|
||||||
ey = priv->bottom; /* FIXME ? */
|
|
||||||
|
|
||||||
opacity = clutter_actor_get_paint_opacity (self);
|
|
||||||
|
|
||||||
g_assert (priv->material != COGL_INVALID_HANDLE);
|
|
||||||
|
|
||||||
/* set the source material using the parent texture's COGL handle */
|
|
||||||
cogl_material_set_color4ub (priv->material, opacity, opacity, opacity, opacity);
|
|
||||||
cogl_material_set_layer (priv->material, 0, cogl_texture);
|
|
||||||
cogl_set_source (priv->material);
|
|
||||||
|
|
||||||
/* top left corner */
|
|
||||||
cogl_rectangle_with_texture_coords (0, 0, priv->left, priv->top,
|
|
||||||
0.0, 0.0,
|
|
||||||
tx1, ty1);
|
|
||||||
|
|
||||||
/* top middle */
|
|
||||||
cogl_rectangle_with_texture_coords (priv->left, 0, ex, priv->top,
|
|
||||||
tx1, 0.0,
|
|
||||||
tx2, ty1);
|
|
||||||
|
|
||||||
/* top right */
|
|
||||||
cogl_rectangle_with_texture_coords (ex, 0, width, priv->top,
|
|
||||||
tx2, 0.0,
|
|
||||||
1.0, ty1);
|
|
||||||
|
|
||||||
/* mid left */
|
|
||||||
cogl_rectangle_with_texture_coords (0, priv->top, priv->left, ey,
|
|
||||||
0.0, ty1,
|
|
||||||
tx1, ty2);
|
|
||||||
|
|
||||||
/* center */
|
|
||||||
cogl_rectangle_with_texture_coords (priv->left, priv->top, ex, ey,
|
|
||||||
tx1, ty1,
|
|
||||||
tx2, ty2);
|
|
||||||
|
|
||||||
/* mid right */
|
|
||||||
cogl_rectangle_with_texture_coords (ex, priv->top, width, ey,
|
|
||||||
tx2, ty1,
|
|
||||||
1.0, ty2);
|
|
||||||
|
|
||||||
/* bottom left */
|
|
||||||
cogl_rectangle_with_texture_coords (0, ey, priv->left, height,
|
|
||||||
0.0, ty2,
|
|
||||||
tx1, 1.0);
|
|
||||||
|
|
||||||
/* bottom center */
|
|
||||||
cogl_rectangle_with_texture_coords (priv->left, ey, ex, height,
|
|
||||||
tx1, ty2,
|
|
||||||
tx2, 1.0);
|
|
||||||
|
|
||||||
/* bottom right */
|
|
||||||
cogl_rectangle_with_texture_coords (ex, ey, width, height,
|
|
||||||
tx2, ty2,
|
|
||||||
1.0, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
tidy_texture_frame_set_frame_internal (TidyTextureFrame *frame,
|
|
||||||
gfloat left,
|
|
||||||
gfloat top,
|
|
||||||
gfloat right,
|
|
||||||
gfloat bottom)
|
|
||||||
{
|
|
||||||
TidyTextureFramePrivate *priv = frame->priv;
|
|
||||||
GObject *gobject = G_OBJECT (frame);
|
|
||||||
gboolean changed = FALSE;
|
|
||||||
|
|
||||||
g_object_freeze_notify (gobject);
|
|
||||||
|
|
||||||
if (priv->top != top)
|
|
||||||
{
|
|
||||||
priv->top = top;
|
|
||||||
g_object_notify (gobject, "top");
|
|
||||||
changed = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->right != right)
|
|
||||||
{
|
|
||||||
priv->right = right;
|
|
||||||
g_object_notify (gobject, "right");
|
|
||||||
changed = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->bottom != bottom)
|
|
||||||
{
|
|
||||||
priv->bottom = bottom;
|
|
||||||
g_object_notify (gobject, "bottom");
|
|
||||||
changed = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->left != left)
|
|
||||||
{
|
|
||||||
priv->left = left;
|
|
||||||
g_object_notify (gobject, "left");
|
|
||||||
changed = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changed && CLUTTER_ACTOR_IS_VISIBLE (frame))
|
|
||||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (frame));
|
|
||||||
|
|
||||||
g_object_thaw_notify (gobject);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
tidy_texture_frame_set_property (GObject *gobject,
|
|
||||||
guint prop_id,
|
|
||||||
const GValue *value,
|
|
||||||
GParamSpec *pspec)
|
|
||||||
{
|
|
||||||
TidyTextureFrame *frame = TIDY_TEXTURE_FRAME (gobject);
|
|
||||||
TidyTextureFramePrivate *priv = frame->priv;
|
|
||||||
|
|
||||||
switch (prop_id)
|
|
||||||
{
|
|
||||||
case PROP_PARENT_TEXTURE:
|
|
||||||
tidy_texture_frame_set_parent_texture (frame,
|
|
||||||
g_value_get_object (value));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PROP_TOP:
|
|
||||||
tidy_texture_frame_set_frame_internal (frame,
|
|
||||||
priv->left,
|
|
||||||
g_value_get_float (value),
|
|
||||||
priv->right,
|
|
||||||
priv->bottom);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PROP_RIGHT:
|
|
||||||
tidy_texture_frame_set_frame_internal (frame,
|
|
||||||
priv->top,
|
|
||||||
g_value_get_float (value),
|
|
||||||
priv->bottom,
|
|
||||||
priv->left);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PROP_BOTTOM:
|
|
||||||
tidy_texture_frame_set_frame_internal (frame,
|
|
||||||
priv->top,
|
|
||||||
priv->right,
|
|
||||||
g_value_get_float (value),
|
|
||||||
priv->left);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PROP_LEFT:
|
|
||||||
tidy_texture_frame_set_frame_internal (frame,
|
|
||||||
priv->top,
|
|
||||||
priv->right,
|
|
||||||
priv->bottom,
|
|
||||||
g_value_get_float (value));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
tidy_texture_frame_get_property (GObject *gobject,
|
|
||||||
guint prop_id,
|
|
||||||
GValue *value,
|
|
||||||
GParamSpec *pspec)
|
|
||||||
{
|
|
||||||
TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (gobject)->priv;
|
|
||||||
|
|
||||||
switch (prop_id)
|
|
||||||
{
|
|
||||||
case PROP_PARENT_TEXTURE:
|
|
||||||
g_value_set_object (value, priv->parent_texture);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PROP_LEFT:
|
|
||||||
g_value_set_float (value, priv->left);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PROP_TOP:
|
|
||||||
g_value_set_float (value, priv->top);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PROP_RIGHT:
|
|
||||||
g_value_set_float (value, priv->right);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PROP_BOTTOM:
|
|
||||||
g_value_set_float (value, priv->bottom);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
tidy_texture_frame_dispose (GObject *gobject)
|
|
||||||
{
|
|
||||||
TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (gobject)->priv;
|
|
||||||
|
|
||||||
if (priv->parent_texture)
|
|
||||||
{
|
|
||||||
g_object_unref (priv->parent_texture);
|
|
||||||
priv->parent_texture = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->material)
|
|
||||||
{
|
|
||||||
cogl_handle_unref (priv->material);
|
|
||||||
priv->material = COGL_INVALID_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
G_OBJECT_CLASS (tidy_texture_frame_parent_class)->dispose (gobject);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
tidy_texture_frame_class_init (TidyTextureFrameClass *klass)
|
|
||||||
{
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
||||||
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
|
||||||
GParamSpec *pspec;
|
|
||||||
|
|
||||||
g_type_class_add_private (gobject_class, sizeof (TidyTextureFramePrivate));
|
|
||||||
|
|
||||||
actor_class->get_preferred_width =
|
|
||||||
tidy_texture_frame_get_preferred_width;
|
|
||||||
actor_class->get_preferred_height =
|
|
||||||
tidy_texture_frame_get_preferred_height;
|
|
||||||
actor_class->realize = tidy_texture_frame_realize;
|
|
||||||
actor_class->unrealize = tidy_texture_frame_unrealize;
|
|
||||||
actor_class->paint = tidy_texture_frame_paint;
|
|
||||||
|
|
||||||
gobject_class->set_property = tidy_texture_frame_set_property;
|
|
||||||
gobject_class->get_property = tidy_texture_frame_get_property;
|
|
||||||
gobject_class->dispose = tidy_texture_frame_dispose;
|
|
||||||
|
|
||||||
pspec = g_param_spec_object ("parent-texture",
|
|
||||||
"Parent Texture",
|
|
||||||
"The parent ClutterTexture",
|
|
||||||
CLUTTER_TYPE_TEXTURE,
|
|
||||||
TIDY_PARAM_READWRITE |
|
|
||||||
G_PARAM_CONSTRUCT);
|
|
||||||
g_object_class_install_property (gobject_class, PROP_PARENT_TEXTURE, pspec);
|
|
||||||
|
|
||||||
pspec = g_param_spec_float ("left",
|
|
||||||
"Left",
|
|
||||||
"Left offset",
|
|
||||||
0, G_MAXFLOAT,
|
|
||||||
0,
|
|
||||||
TIDY_PARAM_READWRITE);
|
|
||||||
g_object_class_install_property (gobject_class, PROP_LEFT, pspec);
|
|
||||||
|
|
||||||
pspec = g_param_spec_float ("top",
|
|
||||||
"Top",
|
|
||||||
"Top offset",
|
|
||||||
0, G_MAXFLOAT,
|
|
||||||
0,
|
|
||||||
TIDY_PARAM_READWRITE);
|
|
||||||
g_object_class_install_property (gobject_class, PROP_TOP, pspec);
|
|
||||||
|
|
||||||
pspec = g_param_spec_float ("bottom",
|
|
||||||
"Bottom",
|
|
||||||
"Bottom offset",
|
|
||||||
0, G_MAXFLOAT,
|
|
||||||
0,
|
|
||||||
TIDY_PARAM_READWRITE);
|
|
||||||
g_object_class_install_property (gobject_class, PROP_BOTTOM, pspec);
|
|
||||||
|
|
||||||
pspec = g_param_spec_float ("right",
|
|
||||||
"Right",
|
|
||||||
"Right offset",
|
|
||||||
0, G_MAXFLOAT,
|
|
||||||
0,
|
|
||||||
TIDY_PARAM_READWRITE);
|
|
||||||
g_object_class_install_property (gobject_class, PROP_RIGHT, pspec);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
tidy_texture_frame_init (TidyTextureFrame *self)
|
|
||||||
{
|
|
||||||
TidyTextureFramePrivate *priv;
|
|
||||||
|
|
||||||
self->priv = priv = TIDY_TEXTURE_FRAME_GET_PRIVATE (self);
|
|
||||||
|
|
||||||
priv->material = COGL_INVALID_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* tidy_texture_frame_new:
|
|
||||||
* @texture: a #ClutterTexture or %NULL
|
|
||||||
* @left: left margin preserving its content
|
|
||||||
* @top: top margin preserving its content
|
|
||||||
* @right: right margin preserving its content
|
|
||||||
* @bottom: bottom margin preserving its content
|
|
||||||
*
|
|
||||||
* A #TidyTextureFrame is a specialized texture that efficiently clones
|
|
||||||
* an area of the given @texture while keeping preserving portions of the
|
|
||||||
* same texture.
|
|
||||||
*
|
|
||||||
* A #TidyTextureFrame can be used to make a rectangular texture fit a
|
|
||||||
* given size without stretching its borders.
|
|
||||||
*
|
|
||||||
* Return value: the newly created #TidyTextureFrame
|
|
||||||
*/
|
|
||||||
ClutterActor*
|
|
||||||
tidy_texture_frame_new (ClutterTexture *texture,
|
|
||||||
gfloat left,
|
|
||||||
gfloat top,
|
|
||||||
gfloat right,
|
|
||||||
gfloat bottom)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture), NULL);
|
|
||||||
|
|
||||||
return g_object_new (TIDY_TYPE_TEXTURE_FRAME,
|
|
||||||
"parent-texture", texture,
|
|
||||||
"left", left,
|
|
||||||
"top", top,
|
|
||||||
"right", right,
|
|
||||||
"bottom", bottom,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
ClutterTexture *
|
|
||||||
tidy_texture_frame_get_parent_texture (TidyTextureFrame *frame)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (TIDY_IS_TEXTURE_FRAME (frame), NULL);
|
|
||||||
|
|
||||||
return frame->priv->parent_texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
tidy_texture_frame_set_parent_texture (TidyTextureFrame *frame,
|
|
||||||
ClutterTexture *texture)
|
|
||||||
{
|
|
||||||
TidyTextureFramePrivate *priv;
|
|
||||||
gboolean was_visible;
|
|
||||||
|
|
||||||
g_return_if_fail (TIDY_IS_TEXTURE_FRAME (frame));
|
|
||||||
g_return_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture));
|
|
||||||
|
|
||||||
priv = frame->priv;
|
|
||||||
|
|
||||||
was_visible = CLUTTER_ACTOR_IS_VISIBLE (frame);
|
|
||||||
|
|
||||||
if (priv->parent_texture == texture)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (priv->parent_texture)
|
|
||||||
{
|
|
||||||
g_object_unref (priv->parent_texture);
|
|
||||||
priv->parent_texture = NULL;
|
|
||||||
|
|
||||||
if (was_visible)
|
|
||||||
clutter_actor_hide (CLUTTER_ACTOR (frame));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (texture)
|
|
||||||
{
|
|
||||||
priv->parent_texture = g_object_ref (texture);
|
|
||||||
|
|
||||||
if (was_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->parent_texture))
|
|
||||||
clutter_actor_show (CLUTTER_ACTOR (frame));
|
|
||||||
}
|
|
||||||
|
|
||||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (frame));
|
|
||||||
|
|
||||||
g_object_notify (G_OBJECT (frame), "parent-texture");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
tidy_texture_frame_set_frame (TidyTextureFrame *frame,
|
|
||||||
gfloat top,
|
|
||||||
gfloat right,
|
|
||||||
gfloat bottom,
|
|
||||||
gfloat left)
|
|
||||||
{
|
|
||||||
g_return_if_fail (TIDY_IS_TEXTURE_FRAME (frame));
|
|
||||||
|
|
||||||
tidy_texture_frame_set_frame_internal (frame, top, right, bottom, left);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
tidy_texture_frame_get_frame (TidyTextureFrame *frame,
|
|
||||||
gfloat *top,
|
|
||||||
gfloat *right,
|
|
||||||
gfloat *bottom,
|
|
||||||
gfloat *left)
|
|
||||||
{
|
|
||||||
TidyTextureFramePrivate *priv;
|
|
||||||
|
|
||||||
g_return_if_fail (TIDY_IS_TEXTURE_FRAME (frame));
|
|
||||||
|
|
||||||
priv = frame->priv;
|
|
||||||
|
|
||||||
if (top)
|
|
||||||
*top = priv->top;
|
|
||||||
|
|
||||||
if (right)
|
|
||||||
*right = priv->right;
|
|
||||||
|
|
||||||
if (bottom)
|
|
||||||
*bottom = priv->bottom;
|
|
||||||
|
|
||||||
if (left)
|
|
||||||
*left = priv->left;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* tidy_texture_frame_set_needs_paint:
|
|
||||||
* @frame: a #TidyTextureframe
|
|
||||||
* @needs_paint: if %FALSE, the paint will be skipped
|
|
||||||
*
|
|
||||||
* Provides a hint to the texture frame that it is totally obscured
|
|
||||||
* and doesn't need to be painted. This would typically be called
|
|
||||||
* by a parent container if it detects the condition prior to
|
|
||||||
* painting its children and then unset afterwards.
|
|
||||||
*
|
|
||||||
* Since it is not supposed to have any effect on display, it does
|
|
||||||
* not queue a repaint.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
tidy_texture_frame_set_needs_paint (TidyTextureFrame *frame,
|
|
||||||
gboolean needs_paint)
|
|
||||||
{
|
|
||||||
TidyTextureFramePrivate *priv;
|
|
||||||
|
|
||||||
g_return_if_fail (TIDY_IS_TEXTURE_FRAME (frame));
|
|
||||||
|
|
||||||
priv = frame->priv;
|
|
||||||
|
|
||||||
priv->needs_paint = needs_paint;
|
|
||||||
}
|
|
@ -1,84 +0,0 @@
|
|||||||
/* tidy-texture-frame.h: Expandible texture actor
|
|
||||||
*
|
|
||||||
* Copyright (C) 2007, 2008 OpenedHand Ltd
|
|
||||||
* Copyright (C) 2009 Intel Corp.
|
|
||||||
*
|
|
||||||
* 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, write to the
|
|
||||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
* Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _HAVE_TIDY_TEXTURE_FRAME_H
|
|
||||||
#define _HAVE_TIDY_TEXTURE_FRAME_H
|
|
||||||
|
|
||||||
#include <clutter/clutter.h>
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
#define TIDY_TYPE_TEXTURE_FRAME (tidy_texture_frame_get_type ())
|
|
||||||
#define TIDY_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_TEXTURE_FRAME, TidyTextureFrame))
|
|
||||||
#define TIDY_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TIDY_TYPE_TEXTURE_FRAME, TidyTextureFrameClass))
|
|
||||||
#define TIDY_IS_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_TEXTURE_FRAME))
|
|
||||||
#define TIDY_IS_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TIDY_TYPE_TEXTURE_FRAME))
|
|
||||||
#define TIDY_TEXTURE_FRAME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TIDY_TYPE_TEXTURE_FRAME, TidyTextureFrameClass))
|
|
||||||
|
|
||||||
typedef struct _TidyTextureFrame TidyTextureFrame;
|
|
||||||
typedef struct _TidyTextureFramePrivate TidyTextureFramePrivate;
|
|
||||||
typedef struct _TidyTextureFrameClass TidyTextureFrameClass;
|
|
||||||
|
|
||||||
struct _TidyTextureFrame
|
|
||||||
{
|
|
||||||
/*< private >*/
|
|
||||||
ClutterActor parent_instance;
|
|
||||||
|
|
||||||
TidyTextureFramePrivate *priv;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct _TidyTextureFrameClass
|
|
||||||
{
|
|
||||||
ClutterActorClass parent_class;
|
|
||||||
|
|
||||||
/* padding for future expansion */
|
|
||||||
void (*_clutter_box_1) (void);
|
|
||||||
void (*_clutter_box_2) (void);
|
|
||||||
void (*_clutter_box_3) (void);
|
|
||||||
void (*_clutter_box_4) (void);
|
|
||||||
};
|
|
||||||
|
|
||||||
GType tidy_texture_frame_get_type (void) G_GNUC_CONST;
|
|
||||||
ClutterActor * tidy_texture_frame_new (ClutterTexture *texture,
|
|
||||||
gfloat top,
|
|
||||||
gfloat right,
|
|
||||||
gfloat bottom,
|
|
||||||
gfloat left);
|
|
||||||
void tidy_texture_frame_set_parent_texture (TidyTextureFrame *frame,
|
|
||||||
ClutterTexture *texture);
|
|
||||||
ClutterTexture *tidy_texture_frame_get_parent_texture (TidyTextureFrame *frame);
|
|
||||||
void tidy_texture_frame_set_frame (TidyTextureFrame *frame,
|
|
||||||
gfloat top,
|
|
||||||
gfloat right,
|
|
||||||
gfloat bottom,
|
|
||||||
gfloat left);
|
|
||||||
void tidy_texture_frame_get_frame (TidyTextureFrame *frame,
|
|
||||||
gfloat *top,
|
|
||||||
gfloat *right,
|
|
||||||
gfloat *bottom,
|
|
||||||
gfloat *left);
|
|
||||||
|
|
||||||
void tidy_texture_frame_set_needs_paint (TidyTextureFrame *frame,
|
|
||||||
gboolean needs_paint);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
|
|
||||||
#endif /* _HAVE_TIDY_TEXTURE_FRAME_H */
|
|
Loading…
Reference in New Issue
Block a user