From 5bdfd5a1f2dbe9df80b49e243c9f0278689c01c3 Mon Sep 17 00:00:00 2001 From: Leo Date: Mon, 6 Feb 2023 12:35:06 +0900 Subject: [PATCH] ShadowEffect: inline namespace, copy to PIP (#1520) --- plugins/pip/PopupWindow.vala | 2 +- plugins/pip/ShadowEffect.vala | 309 ++++++++++++++++--------------- src/ShadowEffect.vala | 337 +++++++++++++++++----------------- 3 files changed, 329 insertions(+), 319 deletions(-) diff --git a/plugins/pip/PopupWindow.vala b/plugins/pip/PopupWindow.vala index 058fc976..3b9fbf0d 100644 --- a/plugins/pip/PopupWindow.vala +++ b/plugins/pip/PopupWindow.vala @@ -105,7 +105,7 @@ public class Gala.Plugins.PIP.PopupWindow : Clutter.Actor { container = new Clutter.Actor (); container.reactive = true; container.set_scale (0.35f, 0.35f); - container.add_effect (new ShadowEffect (SHADOW_SIZE, 2)); + container.add_effect (new ShadowEffect (SHADOW_SIZE) { css_class = "window-clone" }); container.add_child (clone); container.add_action (move_action); diff --git a/plugins/pip/ShadowEffect.vala b/plugins/pip/ShadowEffect.vala index 2779140c..d22c0c52 100644 --- a/plugins/pip/ShadowEffect.vala +++ b/plugins/pip/ShadowEffect.vala @@ -1,150 +1,169 @@ -// -// Copyright (C) 2014 Tom Beckmann -// -// 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 3 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, see . -// +/* + * Copyright 2014 Tom Beckmann + * Copyright 2023 elementary, Inc. + * SPDX-License-Identifier: GPL-3.0-or-later + */ -using Clutter; + public class Gala.Plugins.PIP.ShadowEffect : Clutter.Effect { + private class Shadow { + public int users; + public Cogl.Texture texture; -namespace Gala.Plugins.PIP { - public class ShadowEffect : Effect { - private class Shadow { - public int users; - public Cogl.Texture texture; - - public Shadow (Cogl.Texture _texture) { - texture = _texture; - users = 1; - } - } - - // the sizes of the textures often repeat, especially for the background actor - // so we keep a cache to avoid creating the same texture all over again. - static Gee.HashMap shadow_cache; - - static construct { - shadow_cache = new Gee.HashMap (); - } - - public int shadow_size { get; construct; } - public int shadow_spread { get; construct; } - - public float scale_factor { get; set; default = 1; } - public uint8 shadow_opacity { get; set; default = 255; } - - Cogl.Pipeline pipeline; - - string? current_key = null; - - public ShadowEffect (int shadow_size, int shadow_spread) { - Object (shadow_size: shadow_size, shadow_spread: shadow_spread); - } - - construct { - pipeline = new Cogl.Pipeline (Clutter.get_default_backend ().get_cogl_context ()); - } - - ~ShadowEffect () { - if (current_key != null) - decrement_shadow_users (current_key); - } - - Cogl.Texture? get_shadow (Cogl.Context context, int width, int height, int shadow_size, int shadow_spread) { - var old_key = current_key; - - current_key = "%ix%i:%i:%i".printf (width, height, shadow_size, shadow_spread); - if (old_key == current_key) - return null; - - if (old_key != null) - decrement_shadow_users (old_key); - - Shadow? shadow = null; - if ((shadow = shadow_cache.@get (current_key)) != null) { - shadow.users++; - return shadow.texture; - } - - // fill a new texture for this size - var buffer = new Drawing.BufferSurface (width, height); - buffer.context.rectangle (shadow_size - shadow_spread, shadow_size - shadow_spread, - width - shadow_size * 2 + shadow_spread * 2, height - shadow_size * 2 + shadow_spread * 2); - buffer.context.set_source_rgba (0, 0, 0, 0.7); - buffer.context.fill (); - - buffer.exponential_blur (shadow_size / 2); - - var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, width, height); - var cr = new Cairo.Context (surface); - - cr.set_source_surface (buffer.surface, 0, 0); - cr.paint (); - - try { - var texture = new Cogl.Texture2D.from_data (context, width, height, Cogl.PixelFormat.BGRA_8888_PRE, - surface.get_stride (), surface.get_data ()); - - shadow_cache.@set (current_key, new Shadow (texture)); - return texture; - } catch (GLib.Error e) { - debug (e.message); - return null; - } - } - - void decrement_shadow_users (string key) { - var shadow = shadow_cache.@get (key); - - if (shadow == null) - return; - - if (--shadow.users == 0) - shadow_cache.unset (key); - } - -#if HAS_MUTTER40 - public override void paint (Clutter.PaintNode node, Clutter.PaintContext context, Clutter.EffectPaintFlags flags) { -#else - public override void paint (Clutter.PaintContext context, EffectPaintFlags flags) { -#endif - var bounding_box = get_bounding_box (); - - var shadow = get_shadow (context.get_framebuffer ().get_context (), (int) (bounding_box.x2 - bounding_box.x1), - (int) (bounding_box.y2 - bounding_box.y1), shadow_size, shadow_spread); - - if (shadow != null) - pipeline.set_layer_texture (0, shadow); - - var opacity = actor.get_paint_opacity () * shadow_opacity / 255; - var alpha = Cogl.Color.from_4ub (255, 255, 255, opacity); - alpha.premultiply (); - - pipeline.set_color (alpha); - - context.get_framebuffer ().draw_rectangle (pipeline, bounding_box.x1, bounding_box.y1, bounding_box.x2, bounding_box.y2); - - actor.continue_paint (context); - } - - public virtual ActorBox get_bounding_box () { - var size = shadow_size * scale_factor; - var bounding_box = ActorBox (); - - bounding_box.set_origin (-size, -size); - bounding_box.set_size (actor.width + size * 2, actor.height + size * 2); - - return bounding_box; + public Shadow (Cogl.Texture _texture) { + texture = _texture; + users = 1; } } + + // the sizes of the textures often repeat, especially for the background actor + // so we keep a cache to avoid creating the same texture all over again. + private static Gee.HashMap shadow_cache; + private static Gtk.StyleContext style_context; + + class construct { + shadow_cache = new Gee.HashMap (); + + var style_path = new Gtk.WidgetPath (); + var id = style_path.append_type (typeof (Gtk.Window)); + + style_context = new Gtk.StyleContext (); + style_context.add_provider (Gala.Utils.get_gala_css (), Gtk.STYLE_PROVIDER_PRIORITY_FALLBACK); + style_context.add_class ("decoration"); + style_context.set_path (style_path); + } + + public int shadow_size { get; construct; } + + public float scale_factor { get; set; default = 1; } + public uint8 shadow_opacity { get; set; default = 255; } + public string? css_class { get; set; default = null; } + + private Cogl.Pipeline pipeline; + private string? current_key = null; + + public ShadowEffect (int shadow_size) { + Object (shadow_size: shadow_size); + } + + construct { + pipeline = new Cogl.Pipeline (Clutter.get_default_backend ().get_cogl_context ()); + } + + ~ShadowEffect () { + if (current_key != null) { + decrement_shadow_users (current_key); + } + } + + private Cogl.Texture? get_shadow (Cogl.Context context, int width, int height, int shadow_size) { + var old_key = current_key; + current_key = "%ix%i:%i".printf (width, height, shadow_size); + if (old_key == current_key) { + return null; + } + + if (old_key != null) { + decrement_shadow_users (old_key); + } + + Shadow? shadow = null; + if ((shadow = shadow_cache.@get (current_key)) != null) { + shadow.users++; + return shadow.texture; + } + + var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, width, height); + var cr = new Cairo.Context (surface); + cr.set_source_rgba (0, 0, 0, 0); + cr.fill (); + + cr.set_operator (Cairo.Operator.OVER); + cr.save (); + cr.scale (scale_factor, scale_factor); + style_context.save (); + if (css_class != null) { + style_context.add_class (css_class); + } + + style_context.set_scale ((int)scale_factor); + style_context.render_background (cr, shadow_size, shadow_size, width - shadow_size * 2, height - shadow_size * 2); + style_context.restore (); + cr.restore (); + + cr.paint (); + + try { + var texture = new Cogl.Texture2D.from_data (context, width, height, Cogl.PixelFormat.BGRA_8888_PRE, + surface.get_stride (), surface.get_data ()); + shadow_cache.@set (current_key, new Shadow (texture)); + + return texture; + } catch (Error e) { + debug (e.message); + return null; + } + } + + private void decrement_shadow_users (string key) { + var shadow = shadow_cache.@get (key); + + if (shadow == null) { + return; + } + + if (--shadow.users == 0) { + shadow_cache.unset (key); + } + } + +#if HAS_MUTTER40 + public override void paint (Clutter.PaintNode node, Clutter.PaintContext context, Clutter.EffectPaintFlags flags) { +#else + public override void paint (Clutter.PaintContext context, EffectPaintFlags flags) { +#endif + var bounding_box = get_bounding_box (); + var width = (int) (bounding_box.x2 - bounding_box.x1); + var height = (int) (bounding_box.y2 - bounding_box.y1); + + var shadow = get_shadow (context.get_framebuffer ().get_context (), width, height, shadow_size); + if (shadow != null) { + pipeline.set_layer_texture (0, shadow); + } + + var opacity = actor.get_paint_opacity () * shadow_opacity / 255; + var alpha = Cogl.Color.from_4ub (255, 255, 255, opacity); + alpha.premultiply (); + + pipeline.set_color (alpha); + + context.get_framebuffer ().draw_rectangle (pipeline, bounding_box.x1, bounding_box.y1, bounding_box.x2, bounding_box.y2); + + actor.continue_paint (context); + } + + public virtual Clutter.ActorBox get_bounding_box () { + var size = shadow_size * scale_factor; + var bounding_box = Clutter.ActorBox (); + + bounding_box.set_origin (-size, -size); + bounding_box.set_size (actor.width + size * 2, actor.height + size * 2); + + return bounding_box; + } + + public override bool modify_paint_volume (Clutter.PaintVolume volume) { + var bounding_box = get_bounding_box (); + + volume.set_width (bounding_box.get_width ()); + volume.set_height (bounding_box.get_height ()); + + float origin_x, origin_y; + bounding_box.get_origin (out origin_x, out origin_y); + var origin = volume.get_origin (); + origin.x += origin_x; + origin.y += origin_y; + volume.set_origin (origin); + + return true; + } } diff --git a/src/ShadowEffect.vala b/src/ShadowEffect.vala index b4f3aa0c..6e0c247b 100644 --- a/src/ShadowEffect.vala +++ b/src/ShadowEffect.vala @@ -1,178 +1,169 @@ -// -// Copyright (C) 2014 Tom Beckmann -// -// 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 3 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, see . -// +/* + * Copyright 2014 Tom Beckmann + * Copyright 2023 elementary, Inc. + * SPDX-License-Identifier: GPL-3.0-or-later + */ -using Clutter; +public class Gala.ShadowEffect : Clutter.Effect { + private class Shadow { + public int users; + public Cogl.Texture texture; -namespace Gala { - public class ShadowEffect : Effect { - private class Shadow { - public int users; - public Cogl.Texture texture; - - public Shadow (Cogl.Texture _texture) { - texture = _texture; - users = 1; - } - } - - // the sizes of the textures often repeat, especially for the background actor - // so we keep a cache to avoid creating the same texture all over again. - static Gee.HashMap shadow_cache; - static Gtk.StyleContext style_context; - - class construct { - shadow_cache = new Gee.HashMap (); - - var style_path = new Gtk.WidgetPath (); - var id = style_path.append_type (typeof (Gtk.Window)); - - style_context = new Gtk.StyleContext (); - style_context.add_provider (Gala.Utils.get_gala_css (), Gtk.STYLE_PROVIDER_PRIORITY_FALLBACK); - style_context.add_class ("decoration"); - style_context.set_path (style_path); - } - - public int shadow_size { get; construct; } - - public float scale_factor { get; set; default = 1; } - public uint8 shadow_opacity { get; set; default = 255; } - public string? css_class { get; set; default = null; } - - Cogl.Pipeline pipeline; - string? current_key = null; - - public ShadowEffect (int shadow_size) { - Object (shadow_size: shadow_size); - } - - construct { - pipeline = new Cogl.Pipeline (Clutter.get_default_backend ().get_cogl_context ()); - } - - ~ShadowEffect () { - if (current_key != null) - decrement_shadow_users (current_key); - } - - Cogl.Texture? get_shadow (Cogl.Context context, int width, int height, int shadow_size) { - var old_key = current_key; - current_key = "%ix%i:%i".printf (width, height, shadow_size); - if (old_key == current_key) - return null; - - if (old_key != null) - decrement_shadow_users (old_key); - - Shadow? shadow = null; - if ((shadow = shadow_cache.@get (current_key)) != null) { - shadow.users++; - return shadow.texture; - } - - var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, width, height); - var cr = new Cairo.Context (surface); - cr.set_source_rgba (0, 0, 0, 0); - cr.fill (); - - cr.set_operator (Cairo.Operator.OVER); - cr.save (); - cr.scale (scale_factor, scale_factor); - style_context.save (); - if (css_class != null) { - style_context.add_class (css_class); - } - - style_context.set_scale ((int)scale_factor); - style_context.render_background (cr, shadow_size, shadow_size, width - shadow_size * 2, height - shadow_size * 2); - style_context.restore (); - cr.restore (); - - cr.paint (); - - try { - var texture = new Cogl.Texture2D.from_data (context, width, height, Cogl.PixelFormat.BGRA_8888_PRE, - surface.get_stride (), surface.get_data ()); - shadow_cache.@set (current_key, new Shadow (texture)); - - return texture; - } catch (Error e) { - debug (e.message); - return null; - } - } - - void decrement_shadow_users (string key) { - var shadow = shadow_cache.@get (key); - - if (shadow == null) - return; - - if (--shadow.users == 0) - shadow_cache.unset (key); - } - -#if HAS_MUTTER40 - public override void paint (Clutter.PaintNode node, Clutter.PaintContext context, Clutter.EffectPaintFlags flags) { -#else - public override void paint (Clutter.PaintContext context, EffectPaintFlags flags) { -#endif - var bounding_box = get_bounding_box (); - var width = (int) (bounding_box.x2 - bounding_box.x1); - var height = (int) (bounding_box.y2 - bounding_box.y1); - - var shadow = get_shadow (context.get_framebuffer ().get_context (), width, height, shadow_size); - if (shadow != null) - pipeline.set_layer_texture (0, shadow); - - var opacity = actor.get_paint_opacity () * shadow_opacity / 255; - var alpha = Cogl.Color.from_4ub (255, 255, 255, opacity); - alpha.premultiply (); - - pipeline.set_color (alpha); - - context.get_framebuffer ().draw_rectangle (pipeline, bounding_box.x1, bounding_box.y1, bounding_box.x2, bounding_box.y2); - - actor.continue_paint (context); - } - - public virtual ActorBox get_bounding_box () { - var size = shadow_size * scale_factor; - var bounding_box = ActorBox (); - - bounding_box.set_origin (-size, -size); - bounding_box.set_size (actor.width + size * 2, actor.height + size * 2); - - return bounding_box; - } - - public override bool modify_paint_volume (Clutter.PaintVolume volume) { - var bounding_box = get_bounding_box (); - - volume.set_width (bounding_box.get_width ()); - volume.set_height (bounding_box.get_height ()); - - float origin_x, origin_y; - bounding_box.get_origin (out origin_x, out origin_y); - var origin = volume.get_origin (); - origin.x += origin_x; - origin.y += origin_y; - volume.set_origin (origin); - - return true; + public Shadow (Cogl.Texture _texture) { + texture = _texture; + users = 1; } } + + // the sizes of the textures often repeat, especially for the background actor + // so we keep a cache to avoid creating the same texture all over again. + private static Gee.HashMap shadow_cache; + private static Gtk.StyleContext style_context; + + class construct { + shadow_cache = new Gee.HashMap (); + + var style_path = new Gtk.WidgetPath (); + var id = style_path.append_type (typeof (Gtk.Window)); + + style_context = new Gtk.StyleContext (); + style_context.add_provider (Gala.Utils.get_gala_css (), Gtk.STYLE_PROVIDER_PRIORITY_FALLBACK); + style_context.add_class ("decoration"); + style_context.set_path (style_path); + } + + public int shadow_size { get; construct; } + + public float scale_factor { get; set; default = 1; } + public uint8 shadow_opacity { get; set; default = 255; } + public string? css_class { get; set; default = null; } + + private Cogl.Pipeline pipeline; + private string? current_key = null; + + public ShadowEffect (int shadow_size) { + Object (shadow_size: shadow_size); + } + + construct { + pipeline = new Cogl.Pipeline (Clutter.get_default_backend ().get_cogl_context ()); + } + + ~ShadowEffect () { + if (current_key != null) { + decrement_shadow_users (current_key); + } + } + + private Cogl.Texture? get_shadow (Cogl.Context context, int width, int height, int shadow_size) { + var old_key = current_key; + current_key = "%ix%i:%i".printf (width, height, shadow_size); + if (old_key == current_key) { + return null; + } + + if (old_key != null) { + decrement_shadow_users (old_key); + } + + Shadow? shadow = null; + if ((shadow = shadow_cache.@get (current_key)) != null) { + shadow.users++; + return shadow.texture; + } + + var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, width, height); + var cr = new Cairo.Context (surface); + cr.set_source_rgba (0, 0, 0, 0); + cr.fill (); + + cr.set_operator (Cairo.Operator.OVER); + cr.save (); + cr.scale (scale_factor, scale_factor); + style_context.save (); + if (css_class != null) { + style_context.add_class (css_class); + } + + style_context.set_scale ((int)scale_factor); + style_context.render_background (cr, shadow_size, shadow_size, width - shadow_size * 2, height - shadow_size * 2); + style_context.restore (); + cr.restore (); + + cr.paint (); + + try { + var texture = new Cogl.Texture2D.from_data (context, width, height, Cogl.PixelFormat.BGRA_8888_PRE, + surface.get_stride (), surface.get_data ()); + shadow_cache.@set (current_key, new Shadow (texture)); + + return texture; + } catch (Error e) { + debug (e.message); + return null; + } + } + + private void decrement_shadow_users (string key) { + var shadow = shadow_cache.@get (key); + + if (shadow == null) { + return; + } + + if (--shadow.users == 0) { + shadow_cache.unset (key); + } + } + +#if HAS_MUTTER40 + public override void paint (Clutter.PaintNode node, Clutter.PaintContext context, Clutter.EffectPaintFlags flags) { +#else + public override void paint (Clutter.PaintContext context, EffectPaintFlags flags) { +#endif + var bounding_box = get_bounding_box (); + var width = (int) (bounding_box.x2 - bounding_box.x1); + var height = (int) (bounding_box.y2 - bounding_box.y1); + + var shadow = get_shadow (context.get_framebuffer ().get_context (), width, height, shadow_size); + if (shadow != null) { + pipeline.set_layer_texture (0, shadow); + } + + var opacity = actor.get_paint_opacity () * shadow_opacity / 255; + var alpha = Cogl.Color.from_4ub (255, 255, 255, opacity); + alpha.premultiply (); + + pipeline.set_color (alpha); + + context.get_framebuffer ().draw_rectangle (pipeline, bounding_box.x1, bounding_box.y1, bounding_box.x2, bounding_box.y2); + + actor.continue_paint (context); + } + + public virtual Clutter.ActorBox get_bounding_box () { + var size = shadow_size * scale_factor; + var bounding_box = Clutter.ActorBox (); + + bounding_box.set_origin (-size, -size); + bounding_box.set_size (actor.width + size * 2, actor.height + size * 2); + + return bounding_box; + } + + public override bool modify_paint_volume (Clutter.PaintVolume volume) { + var bounding_box = get_bounding_box (); + + volume.set_width (bounding_box.get_width ()); + volume.set_height (bounding_box.get_height ()); + + float origin_x, origin_y; + bounding_box.get_origin (out origin_x, out origin_y); + var origin = volume.get_origin (); + origin.x += origin_x; + origin.y += origin_y; + volume.set_origin (origin); + + return true; + } }