/* GTK - The GIMP Toolkit * Copyright (C) 2014,2015 Benjamin Otte * * Authors: Benjamin Otte * * 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 . */ #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 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; 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), &transform_matrix)) { /* 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; cairo_matrix_t transform_matrix; graphene_matrix_t m1, m2, m3, saved_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; graphene_matrix_init_from_matrix (&saved_matrix, gtk_snapshot_get_transform (snapshot)); /* XXX: Implement -gtk-icon-transform-origin instead of hardcoding "50% 50%" here */ graphene_matrix_init_translate (&m1, &(graphene_point3d_t)GRAPHENE_POINT3D_INIT(width / 2.0, height / 2.0, 0)); graphene_matrix_init_from_2d (&m2, transform_matrix.xx, transform_matrix.yx, transform_matrix.xy, transform_matrix.yy, transform_matrix.x0, transform_matrix.y0); graphene_matrix_multiply (&m2, &m1, &m3); graphene_matrix_init_translate (&m2, &(graphene_point3d_t)GRAPHENE_POINT3D_INIT(- width / 2.0, - height / 2.0, 0)); graphene_matrix_multiply (&m2, &m3, &m1); gtk_snapshot_transform (snapshot, &m1); if (!_gtk_css_shadows_value_is_none (shadows)) { g_warning ("Painting shadows not implemented for textures yet."); } gtk_css_image_builtin_snapshot (image, snapshot, width, height, builtin_type); gtk_snapshot_set_transform (snapshot, &saved_matrix); } 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; 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), &transform_matrix)) { cairo_pattern_t *pattern; /* 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); } static void gtk_cairo_rectangle_transform (cairo_rectangle_int_t *dest, const cairo_rectangle_int_t *src, const cairo_matrix_t *matrix) { double x1, x2, x3, x4; double y1, y2, y3, y4; g_return_if_fail (dest != NULL); g_return_if_fail (src != NULL); g_return_if_fail (matrix != NULL); x1 = src->x; y1 = src->y; x2 = src->x + src->width; y2 = src->y; x3 = src->x + src->width; y3 = src->y + src->height; x4 = src->x; y4 = src->y + src->height; cairo_matrix_transform_point (matrix, &x1, &y1); cairo_matrix_transform_point (matrix, &x2, &y2); cairo_matrix_transform_point (matrix, &x3, &y3); cairo_matrix_transform_point (matrix, &x4, &y4); dest->x = floor (MIN (MIN (x1, x2), MIN (x3, x4))); dest->y = floor (MIN (MIN (y1, y2), MIN (y3, y4))); dest->width = ceil (MAX (MAX (x1, x2), MAX (x3, x4))) - dest->x; dest->height = ceil (MAX (MAX (y1, y2), MAX (y3, y4))) - dest->y; } void gtk_css_style_render_icon_get_extents (GtkCssStyle *style, GdkRectangle *extents, gint x, gint y, gint width, gint height) { cairo_matrix_t transform_matrix, matrix; GtkBorder border; GdkRectangle rect; 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; cairo_matrix_init_translate (&matrix, x + width / 2.0, y + height / 2.0); cairo_matrix_multiply (&matrix, &transform_matrix, &matrix); /* need to round to full pixels */ rect.x = - (width + 1) / 2; rect.y = - (height + 1) / 2; rect.width = (width + 1) & ~1; rect.height = (height + 1) & ~1; gtk_cairo_rectangle_transform (extents, &rect, &matrix); _gtk_css_shadows_value_get_extents (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_ICON_SHADOW), &border); extents->x -= border.left; extents->y -= border.top; extents->width += border.left + border.right; extents->height += border.top + border.bottom; } void gtk_css_style_snapshot_icon_texture (GtkCssStyle *style, GtkSnapshot *snapshot, GskTexture *texture) { const GtkCssValue *shadows, *transform; cairo_matrix_t transform_matrix; graphene_matrix_t m1, m2, m3, saved_matrix; graphene_rect_t bounds; GskRenderNode *node; int width, height; g_return_if_fail (GTK_IS_CSS_STYLE (style)); g_return_if_fail (snapshot != NULL); g_return_if_fail (GSK_IS_TEXTURE (texture)); 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); height = gsk_texture_get_height (texture); if (!_gtk_css_transform_value_get_matrix (transform, &transform_matrix)) return; graphene_matrix_init_from_matrix (&saved_matrix, gtk_snapshot_get_transform (snapshot)); /* XXX: Implement -gtk-icon-transform-origin instead of hardcoding "50% 50%" here */ graphene_matrix_init_translate (&m1, &(graphene_point3d_t)GRAPHENE_POINT3D_INIT(width / 2.0, height / 2.0, 0)); graphene_matrix_init_from_2d (&m2, transform_matrix.xx, transform_matrix.yx, transform_matrix.xy, transform_matrix.yy, transform_matrix.x0, transform_matrix.y0); graphene_matrix_multiply (&m2, &m1, &m3); graphene_matrix_init_translate (&m2, &(graphene_point3d_t)GRAPHENE_POINT3D_INIT(- width / 2.0, - height / 2.0, 0)); graphene_matrix_multiply (&m2, &m3, &m1); gtk_snapshot_transform (snapshot, &m1); graphene_rect_init (&bounds, 0, 0, width, height); node = gtk_snapshot_append (snapshot, &bounds, "Icon"); if (!_gtk_css_shadows_value_is_none (shadows)) { g_warning ("Painting shadows not implemented for textures yet."); } gsk_render_node_set_texture (node, texture); gsk_render_node_unref (node); gtk_snapshot_set_transform (snapshot, &saved_matrix); }