From 491bc4919cc985ef10698cfcaddbb5ed228adccb Mon Sep 17 00:00:00 2001 From: kramo <93832451+kra-mo@users.noreply.github.com> Date: Fri, 5 May 2023 16:59:56 +0200 Subject: [PATCH] Blur game covers properly --- data/gtk/style.css | 5 +++++ data/gtk/window.blp | 2 ++ src/game_cover.py | 35 +++++++++++++++++++++++++++++++++++ src/window.py | 37 ++++++------------------------------- 4 files changed, 48 insertions(+), 31 deletions(-) diff --git a/data/gtk/style.css b/data/gtk/style.css index 72c1868..4ae6ecf 100644 --- a/data/gtk/style.css +++ b/data/gtk/style.css @@ -9,4 +9,9 @@ color: @light_1; background-color: @dark_5; opacity: 0.8; +} + +/* This is to hide a weird GdkPixbuf upscaling glitch */ +#details_view_blurred_cover { + transform: scale(1.2); } \ No newline at end of file diff --git a/data/gtk/window.blp b/data/gtk/window.blp index 831faab..1a88e1c 100644 --- a/data/gtk/window.blp +++ b/data/gtk/window.blp @@ -35,6 +35,7 @@ template CartridgesWindow : Adw.ApplicationWindow { Overlay details_view { name: "details_view"; + overflow: hidden; [overlay] Box details_view_box { @@ -219,6 +220,7 @@ template CartridgesWindow : Adw.ApplicationWindow { } Picture details_view_blurred_cover { + name: "details_view_blurred_cover"; keep-aspect-ratio: false; } } diff --git a/src/game_cover.py b/src/game_cover.py index f9d0141..6f25025 100644 --- a/src/game_cover.py +++ b/src/game_cover.py @@ -18,10 +18,13 @@ # SPDX-License-Identifier: GPL-3.0-or-later from gi.repository import GdkPixbuf, Gio, GLib +from PIL import Image, ImageFilter, ImageStat class GameCover: pixbuf = None + blurred = None + luminance = None path = None animation = None anim_iter = None @@ -47,6 +50,8 @@ class GameCover: def new_cover(self, path=None): self.animation = None self.pixbuf = None + self.blurred = None + self.luminance = None self.path = path if path: @@ -62,6 +67,36 @@ class GameCover: def get_pixbuf(self): return self.animation.get_static_image() if self.animation else self.pixbuf + def get_blurred(self): + if not self.blurred: + if self.path: + with Image.open(self.path) as image: + image = ( + image.convert("RGB") + .resize((8, 12)) + .filter(ImageFilter.GaussianBlur(3)) + ) + + tmp_path = Gio.File.new_tmp(None)[0].get_path() + image.save(tmp_path, "tiff", compression=None) + + self.blurred = GdkPixbuf.Pixbuf.new_from_file(tmp_path) + + stat = ImageStat.Stat(image.convert("L")) + + self.luminance = ( + (stat.mean[0] + stat.extrema[0][0]) / 510, + (stat.mean[0] + stat.extrema[0][1]) / 510, + ) + else: + self.blurred = GdkPixbuf.Pixbuf.new_from_resource_at_scale( + "/hu/kramo/Cartridges/library_placeholder.svg", 2, 3, False + ) + + self.luminance = (0.5, 0.5) + + return self.blurred + def add_picture(self, picture): self.pictures.add(picture) if not self.animation: diff --git a/src/window.py b/src/window.py index 08348ff..7587a99 100644 --- a/src/window.py +++ b/src/window.py @@ -21,9 +21,8 @@ import json import os from datetime import datetime from pathlib import Path -from struct import unpack_from -from gi.repository import Adw, Gdk, GdkPixbuf, Gio, GLib, Gtk +from gi.repository import Adw, Gdk, Gio, GLib, Gtk from .game import Game @@ -71,7 +70,6 @@ class CartridgesWindow(Adw.ApplicationWindow): game_covers = {} toasts = {} active_game = None - scaled_pixbuf = None details_view_game_cover = None sort_state = "a-z" @@ -236,11 +234,9 @@ class CartridgesWindow(Adw.ApplicationWindow): self.details_view_game_cover = game.game_cover self.details_view_game_cover.add_picture(self.details_view_cover) - self.scaled_pixbuf = ( - self.details_view_game_cover.get_pixbuf() - or self.details_view_game_cover.placeholder_pixbuf - ).scale_simple(2, 3, GdkPixbuf.InterpType.BILINEAR) - self.details_view_blurred_cover.set_pixbuf(self.scaled_pixbuf) + self.details_view_blurred_cover.set_pixbuf( + self.details_view_game_cover.get_blurred() + ) self.details_view_title.set_label(game.name) self.details_view_header_bar_title.set_title(game.name) @@ -274,33 +270,12 @@ class CartridgesWindow(Adw.ApplicationWindow): self.details_view_blurred_cover.set_opacity(0.3) return - colors = { - unpack_from( - "BBBB", - self.scaled_pixbuf.get_pixels(), - offset=index * self.scaled_pixbuf.get_n_channels(), - ) - for index in range(6) - } - dark_theme = style_manager.get_dark() - luminances = [] - - for red, green, blue, alpha in colors: - # https://en.wikipedia.org/wiki/Relative_luminance - luminance = red * 0.2126 + green * 0.7152 + blue * 0.0722 - - luminances.append( - (luminance * alpha) / 255**2 - if dark_theme - else (alpha * (luminance - 255)) / 255**2 + 1 - ) - self.details_view_blurred_cover.set_opacity( - 1.3 - (sum(luminances) / len(luminances) + max(luminances)) / 2 + 1.2 - self.details_view_game_cover.luminance[0] if dark_theme - else 0.2 + (sum(luminances) / len(luminances) + min(luminances)) / 2 + else 0.2 + self.details_view_game_cover.luminance[1] ) def sort_func(self, child1, child2):