diff options
author | Victor Kareh <[email protected]> | 2019-10-05 22:52:11 -0400 |
---|---|---|
committer | raveit65 <[email protected]> | 2019-10-07 12:40:38 +0200 |
commit | 87cf621d7dd53987b095323dde27677302c32052 (patch) | |
tree | c40d358db3965ee1a9ce234ea1e2c407a88a7c30 | |
parent | 926e3e5989f9d74d2dee28c2fb82cc0036c5fbee (diff) | |
download | marco-87cf621d7dd53987b095323dde27677302c32052.tar.bz2 marco-87cf621d7dd53987b095323dde27677302c32052.tar.xz |
theme: Render window control buttons and icons as surfaces
When loading window control buttons and icon as pixbufs, we just set
them as the source for the cairo context used to paint them. Instead, we
now convert them to cairo surfaces and scale them to the correct display
density before painting them.
This allows us to load higher resolution assets (i.e. at twice the size)
and by explicitly setting the intended size in the theme draw_ops, we
can then scale them down to fit lower resolution displays, or render
them at full density for HiDPI displays.
-rw-r--r-- | src/ui/theme.c | 314 | ||||
-rw-r--r-- | src/ui/theme.h | 1 |
2 files changed, 291 insertions, 24 deletions
diff --git a/src/ui/theme.c b/src/ui/theme.c index 11cf3608..e15cf2ec 100644 --- a/src/ui/theme.c +++ b/src/ui/theme.c @@ -96,6 +96,114 @@ static void hls_to_rgb (gdouble *h, */ static MetaTheme *meta_current_theme = NULL; +static cairo_surface_t * +scale_surface (cairo_surface_t *surface, + gdouble old_width, + gdouble old_height, + gdouble new_width, + gdouble new_height, + gboolean vertical_stripes, + gboolean horizontal_stripes) +{ + gdouble scale_x; + gdouble scale_y; + cairo_content_t content; + gint width; + gint height; + cairo_surface_t *scaled; + cairo_t *cr; + + scale_x = new_width / old_width; + scale_y = new_height / old_height; + + if (horizontal_stripes && !vertical_stripes) + { + new_width = old_width; + scale_x = 1.0; + } + else if (vertical_stripes && !horizontal_stripes) + { + new_height = old_height; + scale_y = 1.0; + } + + content = CAIRO_CONTENT_COLOR_ALPHA; + width = ceil (new_width); + height = ceil (new_height); + + scaled = cairo_surface_create_similar (surface, content, width, height); + cr = cairo_create (scaled); + + cairo_scale (cr, scale_x, scale_y); + cairo_set_source_surface (cr, surface, 0, 0); + + cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_PAD); + + cairo_paint (cr); + cairo_destroy (cr); + + return scaled; +} + +static cairo_surface_t * +get_surface_from_pixbuf (GdkPixbuf *pixbuf, + MetaImageFillType fill_type, + gdouble width, + gdouble height, + gboolean vertical_stripes, + gboolean horizontal_stripes) +{ + gdouble pixbuf_width; + gdouble pixbuf_height; + cairo_surface_t *surface; + cairo_content_t content; + cairo_surface_t *copy; + cairo_t *cr; + + pixbuf_width = gdk_pixbuf_get_width (pixbuf); + pixbuf_height = gdk_pixbuf_get_height (pixbuf); + surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, 1, NULL); + + if (pixbuf_width == width && pixbuf_height == height) + { + return surface; + } + + if (fill_type != META_IMAGE_FILL_TILE) + { + cairo_surface_t *scaled; + + scaled = scale_surface (surface, pixbuf_width, pixbuf_height, + width, height, vertical_stripes, + horizontal_stripes); + + cairo_surface_destroy (surface); + surface = scaled; + } + + content = CAIRO_CONTENT_COLOR_ALPHA; + width = ceil (width); + height = ceil (height); + + copy = cairo_surface_create_similar (surface, content, width, height); + cr = cairo_create (copy); + + cairo_set_source_surface (cr, surface, 0, 0); + + if (fill_type == META_IMAGE_FILL_TILE || + vertical_stripes || horizontal_stripes) + { + cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT); + } + + cairo_paint (cr); + cairo_destroy (cr); + + cairo_surface_destroy (surface); + + return copy; +} + static GdkPixbuf * colorize_pixbuf (GdkPixbuf *orig, GdkRGBA *new_color) @@ -1149,6 +1257,38 @@ meta_alpha_gradient_spec_free (MetaAlphaGradientSpec *spec) g_free (spec); } +cairo_pattern_t * +meta_alpha_gradient_spec_get_mask (const MetaAlphaGradientSpec *spec) +{ + gint n_alphas; + cairo_pattern_t *pattern; + gint i; + + /* Hardcoded in theme-parser.c */ + g_assert (spec->type == META_GRADIENT_HORIZONTAL); + + n_alphas = spec->n_alphas; + if (n_alphas == 0) + return NULL; + + if (n_alphas == 1) + return cairo_pattern_create_rgba (0, 0, 0, spec->alphas[0] / 255.0); + + pattern = cairo_pattern_create_linear (0, 0, 1, 0); + + for (i = 0; i < n_alphas; i++) + cairo_pattern_add_color_stop_rgba (pattern, i / (gfloat) (n_alphas - 1), + 0, 0, 0, spec->alphas[i] / 255.0); + + if (cairo_pattern_status (pattern) != CAIRO_STATUS_SUCCESS) + { + cairo_pattern_destroy (pattern); + return NULL; + } + + return pattern; +} + MetaColorSpec* meta_color_spec_new (MetaColorSpecType type) { @@ -3625,6 +3765,94 @@ draw_op_as_pixbuf (const MetaDrawOp *op, return pixbuf; } +static cairo_surface_t * +draw_op_as_surface (const MetaDrawOp *op, + GtkStyleContext *style, + const MetaDrawInfo *info, + gdouble width, + gdouble height) +{ + cairo_surface_t *surface; + + surface = NULL; + + switch (op->type) + { + case META_DRAW_IMAGE: + { + if (op->data.image.colorize_spec) + { + GdkRGBA color; + + meta_color_spec_render (op->data.image.colorize_spec, + style, &color); + + if (op->data.image.colorize_cache_pixbuf == NULL || + op->data.image.colorize_cache_pixel != GDK_COLOR_RGB (color)) + { + if (op->data.image.colorize_cache_pixbuf) + g_object_unref (G_OBJECT (op->data.image.colorize_cache_pixbuf)); + + /* const cast here */ + ((MetaDrawOp*)op)->data.image.colorize_cache_pixbuf = + colorize_pixbuf (op->data.image.pixbuf, + &color); + ((MetaDrawOp*)op)->data.image.colorize_cache_pixel = + GDK_COLOR_RGB (color); + } + + if (op->data.image.colorize_cache_pixbuf) + { + surface = get_surface_from_pixbuf (op->data.image.colorize_cache_pixbuf, + op->data.image.fill_type, + width, height, + op->data.image.vertical_stripes, + op->data.image.horizontal_stripes); + } + } + else + { + surface = get_surface_from_pixbuf (op->data.image.pixbuf, + op->data.image.fill_type, + width, height, + op->data.image.vertical_stripes, + op->data.image.horizontal_stripes); + } + break; + } + + case META_DRAW_ICON: + if (info->mini_icon && + width <= gdk_pixbuf_get_width (info->mini_icon) && + height <= gdk_pixbuf_get_height (info->mini_icon)) + surface = get_surface_from_pixbuf (info->mini_icon, op->data.icon.fill_type, + width, height, FALSE, FALSE); + else if (info->icon) + surface = get_surface_from_pixbuf (info->icon, op->data.icon.fill_type, + width, height, FALSE, FALSE); + break; + + case META_DRAW_TINT: + case META_DRAW_LINE: + case META_DRAW_RECTANGLE: + case META_DRAW_ARC: + case META_DRAW_CLIP: + case META_DRAW_GRADIENT: + case META_DRAW_GTK_ARROW: + case META_DRAW_GTK_BOX: + case META_DRAW_GTK_VLINE: + case META_DRAW_TITLE: + case META_DRAW_OP_LIST: + case META_DRAW_TILE: + break; + + default: + break; + } + + return surface; +} + static void fill_env (MetaPositionExprEnv *env, const MetaDrawInfo *info, @@ -3897,8 +4125,12 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op, case META_DRAW_IMAGE: { - int rx, ry, rwidth, rheight; - GdkPixbuf *pixbuf; + gint scale; + gdouble rx, ry, rwidth, rheight; + cairo_surface_t *surface; + + scale = gdk_window_get_scale_factor (gdk_get_default_root_window ()); + cairo_scale (cr, 1.0 / scale, 1.0 / scale); if (op->data.image.pixbuf) { @@ -3906,21 +4138,36 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op, env->object_height = gdk_pixbuf_get_height (op->data.image.pixbuf); } - rwidth = parse_size_unchecked (op->data.image.width, env); - rheight = parse_size_unchecked (op->data.image.height, env); + rwidth = parse_size_unchecked (op->data.image.width, env) * scale; + rheight = parse_size_unchecked (op->data.image.height, env) * scale; - pixbuf = draw_op_as_pixbuf (op, style_gtk, info, - rwidth, rheight); + surface = draw_op_as_surface (op, style_gtk, info, rwidth, rheight); - if (pixbuf) + if (surface) { - rx = parse_x_position_unchecked (op->data.image.x, env); - ry = parse_y_position_unchecked (op->data.image.y, env); + rx = parse_x_position_unchecked (op->data.image.x, env) * scale; + ry = parse_y_position_unchecked (op->data.image.y, env) * scale; - gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry); - cairo_paint (cr); + cairo_set_source_surface (cr, surface, rx, ry); - g_object_unref (G_OBJECT (pixbuf)); + if (op->data.image.alpha_spec) + { + cairo_pattern_t *pattern; + + cairo_translate (cr, rx, ry); + cairo_scale (cr, rwidth, rheight); + + pattern = meta_alpha_gradient_spec_get_mask (op->data.image.alpha_spec); + cairo_mask (cr, pattern); + + cairo_pattern_destroy (pattern); + } + else + { + cairo_paint (cr); + } + + cairo_surface_destroy (surface); } } break; @@ -3989,24 +4236,43 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op, case META_DRAW_ICON: { - int rx, ry, rwidth, rheight; - GdkPixbuf *pixbuf; + gint scale; + gdouble rx, ry, rwidth, rheight; + cairo_surface_t *surface; + + scale = gdk_window_get_scale_factor (gdk_get_default_root_window ()); + cairo_scale (cr, 1.0 / scale, 1.0 / scale); - rwidth = parse_size_unchecked (op->data.icon.width, env); - rheight = parse_size_unchecked (op->data.icon.height, env); + rwidth = parse_size_unchecked (op->data.icon.width, env) * scale; + rheight = parse_size_unchecked (op->data.icon.height, env) * scale; - pixbuf = draw_op_as_pixbuf (op, style_gtk, info, - rwidth, rheight); + surface = draw_op_as_surface (op, style_gtk, info, rwidth, rheight); - if (pixbuf) + if (surface) { - rx = parse_x_position_unchecked (op->data.icon.x, env); - ry = parse_y_position_unchecked (op->data.icon.y, env); + rx = parse_x_position_unchecked (op->data.icon.x, env) * scale; + ry = parse_y_position_unchecked (op->data.icon.y, env) * scale; - gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry); - cairo_paint (cr); + cairo_set_source_surface (cr, surface, rx, ry); - g_object_unref (G_OBJECT (pixbuf)); + if (op->data.icon.alpha_spec) + { + cairo_pattern_t *pattern; + + cairo_translate (cr, rx, ry); + cairo_scale (cr, rwidth, rheight); + + pattern = meta_alpha_gradient_spec_get_mask (op->data.icon.alpha_spec); + cairo_mask (cr, pattern); + + cairo_pattern_destroy (pattern); + } + else + { + cairo_paint (cr); + } + + cairo_surface_destroy (surface); } } break; diff --git a/src/ui/theme.h b/src/ui/theme.h index 34b98935..9dfb8d89 100644 --- a/src/ui/theme.h +++ b/src/ui/theme.h @@ -964,6 +964,7 @@ gboolean meta_gradient_spec_validate (MetaGradientSpec *spec, MetaAlphaGradientSpec* meta_alpha_gradient_spec_new (MetaGradientType type, int n_alphas); void meta_alpha_gradient_spec_free (MetaAlphaGradientSpec *spec); +cairo_pattern_t * meta_alpha_gradient_spec_get_mask (const MetaAlphaGradientSpec *spec); MetaFrameStyle* meta_frame_style_new (MetaFrameStyle *parent); |