gtk2/gtk/gtkrendericon.c
Benjamin Otte 1137483d15 snapshot: Work on pushing and popping again
It is now possible to call push() subfunctions for simple container
nodes with just a single child. So you can for example
gtk_snapshot_push_clip() a clip region that all the nodes that get
appended later will then obey.

gtk_snapshot_pop() will then not return a container node, but a clip
node containing the container node (and similar for the transform
example).

This is implemented internally by providing a "collect function" when
pushing that is called when popping to collects all the accumulated
nodes and combine them into the single node that gets returned.

To simplify things even more, gtk_snapshot_pop_and_append() has been
added, which pops the currently pushed node and appends it to the
parent.

The icon rendering code has been converted to this approach.
2016-12-20 18:01:10 +01:00

315 lines
12 KiB
C

/* GTK - The GIMP Toolkit
* Copyright (C) 2014,2015 Benjamin Otte
*
* Authors: Benjamin Otte <otte@gnome.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtkrendericonprivate.h"
#include "gtkcssimagebuiltinprivate.h"
#include "gtkcssimagevalueprivate.h"
#include "gtkcssshadowsvalueprivate.h"
#include "gtkcssstyleprivate.h"
#include "gtkcsstransformvalueprivate.h"
#include "gtksnapshotprivate.h"
#include <math.h>
void
gtk_css_style_render_icon (GtkCssStyle *style,
cairo_t *cr,
double x,
double y,
double width,
double height,
GtkCssImageBuiltinType builtin_type)
{
const GtkCssValue *shadows;
graphene_matrix_t graphene_matrix;
cairo_matrix_t matrix, transform_matrix, saved_matrix;
GtkCssImage *image;
g_return_if_fail (GTK_IS_CSS_STYLE (style));
g_return_if_fail (cr != NULL);
image = _gtk_css_image_value_get_image (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_SOURCE));
if (image == NULL)
return;
cairo_get_matrix (cr, &saved_matrix);
shadows = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_SHADOW);
cairo_translate (cr, x, y);
if (gtk_css_transform_value_get_matrix (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_TRANSFORM), &graphene_matrix) &&
graphene_matrix_is_2d (&graphene_matrix))
{
graphene_matrix_to_2d (&graphene_matrix,
&transform_matrix.xx, &transform_matrix.yx,
&transform_matrix.xy, &transform_matrix.yy,
&transform_matrix.x0, &transform_matrix.y0);
/* XXX: Implement -gtk-icon-transform-origin instead of hardcoding "50% 50%" here */
cairo_matrix_init_translate (&matrix, width / 2, height / 2);
cairo_matrix_multiply (&matrix, &transform_matrix, &matrix);
cairo_matrix_translate (&matrix, - width / 2, - height / 2);
if (_gtk_css_shadows_value_is_none (shadows))
{
cairo_transform (cr, &matrix);
gtk_css_image_builtin_draw (image, cr, width, height, builtin_type);
}
else
{
cairo_push_group (cr);
cairo_transform (cr, &matrix);
gtk_css_image_builtin_draw (image, cr, width, height, builtin_type);
cairo_pop_group_to_source (cr);
_gtk_css_shadows_value_paint_icon (shadows, cr);
cairo_paint (cr);
}
}
cairo_set_matrix (cr, &saved_matrix);
}
void
gtk_css_style_snapshot_icon (GtkCssStyle *style,
GtkSnapshot *snapshot,
double width,
double height,
GtkCssImageBuiltinType builtin_type)
{
const GtkCssValue *shadows, *transform;
static gboolean shadow_warning;
graphene_matrix_t transform_matrix;
GtkCssImage *image;
g_return_if_fail (GTK_IS_CSS_STYLE (style));
g_return_if_fail (snapshot != NULL);
image = _gtk_css_image_value_get_image (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_SOURCE));
if (image == NULL)
return;
shadows = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_SHADOW);
transform = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_TRANSFORM);
if (!gtk_css_transform_value_get_matrix (transform, &transform_matrix))
return;
if (!_gtk_css_shadows_value_is_none (shadows) && !shadow_warning)
{
g_warning ("Painting shadows not implemented for textures yet.");
shadow_warning = TRUE;
}
if (graphene_matrix_is_identity (&transform_matrix))
{
gtk_css_image_builtin_snapshot (image, snapshot, width, height, builtin_type);
}
else
{
graphene_matrix_t m1, m2, m3;
/* XXX: Implement -gtk-icon-transform-origin instead of hardcoding "50% 50%" here */
graphene_matrix_init_translate (&m1, &GRAPHENE_POINT3D_INIT (width / 2.0, height / 2.0, 0));
graphene_matrix_multiply (&transform_matrix, &m1, &m3);
graphene_matrix_init_translate (&m2, &GRAPHENE_POINT3D_INIT (- width / 2.0, - height / 2.0, 0));
graphene_matrix_multiply (&m2, &m3, &m1);
gtk_snapshot_push_transform (snapshot, &m1, "CSS Icon Transform Container");
gtk_css_image_builtin_snapshot (image, snapshot, width, height, builtin_type);
gtk_snapshot_pop_and_append (snapshot);
}
}
static gboolean
get_surface_extents (cairo_surface_t *surface,
GdkRectangle *out_extents)
{
cairo_t *cr;
gboolean result;
cr = cairo_create (surface);
result = gdk_cairo_get_clip_rectangle (cr, out_extents);
cairo_destroy (cr);
return result;
}
void
gtk_css_style_render_icon_surface (GtkCssStyle *style,
cairo_t *cr,
cairo_surface_t *surface,
double x,
double y)
{
const GtkCssValue *shadows;
graphene_matrix_t graphene_matrix;
cairo_matrix_t matrix, transform_matrix, saved_matrix;
GdkRectangle extents;
g_return_if_fail (GTK_IS_CSS_STYLE (style));
g_return_if_fail (cr != NULL);
g_return_if_fail (surface != NULL);
shadows = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_SHADOW);
if (!get_surface_extents (surface, &extents))
{
/* weird infinite surface, no special magic for you */
cairo_set_source_surface (cr, surface, x, y);
_gtk_css_shadows_value_paint_icon (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_SHADOW), cr);
cairo_paint (cr);
return;
}
cairo_get_matrix (cr, &saved_matrix);
cairo_translate (cr, x + extents.x, y + extents.y);
if (gtk_css_transform_value_get_matrix (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_TRANSFORM), &graphene_matrix) &&
graphene_matrix_is_2d (&graphene_matrix))
{
cairo_pattern_t *pattern;
graphene_matrix_to_2d (&graphene_matrix,
&transform_matrix.xx, &transform_matrix.yx,
&transform_matrix.xy, &transform_matrix.yy,
&transform_matrix.x0, &transform_matrix.y0);
/* XXX: Implement -gtk-icon-transform-origin instead of hardcoding "50% 50%" here */
cairo_matrix_init_translate (&matrix, extents.width / 2, extents.height / 2);
cairo_matrix_multiply (&matrix, &transform_matrix, &matrix);
cairo_matrix_translate (&matrix, - extents.width / 2, - extents.height / 2);
if (cairo_matrix_invert (&matrix) != CAIRO_STATUS_SUCCESS)
{
g_assert_not_reached ();
}
cairo_matrix_translate (&matrix, extents.x, extents.y);
pattern = cairo_pattern_create_for_surface (surface);
cairo_pattern_set_matrix (pattern, &matrix);
cairo_set_source (cr, pattern);
cairo_pattern_destroy (pattern);
_gtk_css_shadows_value_paint_icon (shadows, cr);
cairo_paint (cr);
}
cairo_set_matrix (cr, &saved_matrix);
}
void
gtk_css_style_render_icon_get_extents (GtkCssStyle *style,
GdkRectangle *extents,
gint x,
gint y,
gint width,
gint height)
{
graphene_matrix_t transform_matrix, translate_matrix, matrix;
graphene_rect_t bounds;
GtkBorder border;
g_return_if_fail (GTK_IS_CSS_STYLE (style));
g_return_if_fail (extents != NULL);
extents->x = x;
extents->y = y;
extents->width = width;
extents->height = height;
if (!gtk_css_transform_value_get_matrix (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_TRANSFORM), &transform_matrix))
return;
graphene_matrix_init_translate (&translate_matrix, &GRAPHENE_POINT3D_INIT(x + width / 2.0, y + height / 2.0, 0));
graphene_matrix_multiply (&transform_matrix, &translate_matrix, &matrix);
graphene_rect_init (&bounds,
- width / 2.0, - height / 2.0,
width, height);
/* need to round to full pixels */
graphene_matrix_transform_bounds (&matrix, &bounds, &bounds);
_gtk_css_shadows_value_get_extents (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_SHADOW), &border);
extents->x = floorf (bounds.origin.x) - border.left;
extents->y = floorf (bounds.origin.y) - border.top;
extents->width = ceilf (bounds.origin.x + bounds.size.width) - extents->x + border.right;
extents->height = ceilf (bounds.origin.y + bounds.size.height) - extents->y + border.bottom;
}
void
gtk_css_style_snapshot_icon_texture (GtkCssStyle *style,
GtkSnapshot *snapshot,
GskTexture *texture,
double texture_scale)
{
const GtkCssValue *shadows, *transform;
graphene_matrix_t transform_matrix;
graphene_rect_t bounds;
double width, height;
static gboolean shadow_warning;
g_return_if_fail (GTK_IS_CSS_STYLE (style));
g_return_if_fail (snapshot != NULL);
g_return_if_fail (GSK_IS_TEXTURE (texture));
g_return_if_fail (texture_scale > 0);
shadows = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_SHADOW);
transform = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_TRANSFORM);
width = gsk_texture_get_width (texture) / texture_scale;
height = gsk_texture_get_height (texture) / texture_scale;
if (!gtk_css_transform_value_get_matrix (transform, &transform_matrix))
return;
if (!_gtk_css_shadows_value_is_none (shadows) && !shadow_warning)
{
g_warning ("Painting shadows not implemented for textures yet.");
shadow_warning = TRUE;
}
if (graphene_matrix_is_identity (&transform_matrix))
{
graphene_rect_init (&bounds,
0, 0,
gsk_texture_get_width (texture) / texture_scale,
gsk_texture_get_height (texture) / texture_scale);
gtk_snapshot_append_texture_node (snapshot, texture, &bounds, "Icon");
}
else
{
graphene_matrix_t translate, matrix;
/* XXX: Implement -gtk-icon-transform-origin instead of hardcoding "50% 50%" here */
graphene_matrix_init_translate (&translate, &GRAPHENE_POINT3D_INIT (width / 2.0, height / 2.0, 0));
graphene_matrix_multiply (&transform_matrix, &translate, &matrix);
graphene_matrix_translate (&matrix, &GRAPHENE_POINT3D_INIT(- width / 2.0, - height / 2.0, 0));
graphene_matrix_scale (&matrix, 1.0 / texture_scale, 1.0 / texture_scale, 1);
gtk_snapshot_push_transform (snapshot, &matrix, "Icon Transform");
graphene_rect_init (&bounds, 0, 0, gsk_texture_get_width (texture), gsk_texture_get_height (texture));
gtk_snapshot_append_texture_node (snapshot, texture, &bounds, "Icon");
gtk_snapshot_pop_and_append (snapshot);
}
}