Blur game covers properly

This commit is contained in:
kramo
2023-05-05 16:59:56 +02:00
parent 92e818af64
commit 491bc4919c
4 changed files with 48 additions and 31 deletions

View File

@@ -9,4 +9,9 @@
color: @light_1; color: @light_1;
background-color: @dark_5; background-color: @dark_5;
opacity: 0.8; opacity: 0.8;
}
/* This is to hide a weird GdkPixbuf upscaling glitch */
#details_view_blurred_cover {
transform: scale(1.2);
} }

View File

@@ -35,6 +35,7 @@ template CartridgesWindow : Adw.ApplicationWindow {
Overlay details_view { Overlay details_view {
name: "details_view"; name: "details_view";
overflow: hidden;
[overlay] [overlay]
Box details_view_box { Box details_view_box {
@@ -219,6 +220,7 @@ template CartridgesWindow : Adw.ApplicationWindow {
} }
Picture details_view_blurred_cover { Picture details_view_blurred_cover {
name: "details_view_blurred_cover";
keep-aspect-ratio: false; keep-aspect-ratio: false;
} }
} }

View File

@@ -18,10 +18,13 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from gi.repository import GdkPixbuf, Gio, GLib from gi.repository import GdkPixbuf, Gio, GLib
from PIL import Image, ImageFilter, ImageStat
class GameCover: class GameCover:
pixbuf = None pixbuf = None
blurred = None
luminance = None
path = None path = None
animation = None animation = None
anim_iter = None anim_iter = None
@@ -47,6 +50,8 @@ class GameCover:
def new_cover(self, path=None): def new_cover(self, path=None):
self.animation = None self.animation = None
self.pixbuf = None self.pixbuf = None
self.blurred = None
self.luminance = None
self.path = path self.path = path
if path: if path:
@@ -62,6 +67,36 @@ class GameCover:
def get_pixbuf(self): def get_pixbuf(self):
return self.animation.get_static_image() if self.animation else self.pixbuf 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): def add_picture(self, picture):
self.pictures.add(picture) self.pictures.add(picture)
if not self.animation: if not self.animation:

View File

@@ -21,9 +21,8 @@ import json
import os import os
from datetime import datetime from datetime import datetime
from pathlib import Path 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 from .game import Game
@@ -71,7 +70,6 @@ class CartridgesWindow(Adw.ApplicationWindow):
game_covers = {} game_covers = {}
toasts = {} toasts = {}
active_game = None active_game = None
scaled_pixbuf = None
details_view_game_cover = None details_view_game_cover = None
sort_state = "a-z" 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 = game.game_cover
self.details_view_game_cover.add_picture(self.details_view_cover) self.details_view_game_cover.add_picture(self.details_view_cover)
self.scaled_pixbuf = ( self.details_view_blurred_cover.set_pixbuf(
self.details_view_game_cover.get_pixbuf() self.details_view_game_cover.get_blurred()
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_title.set_label(game.name) self.details_view_title.set_label(game.name)
self.details_view_header_bar_title.set_title(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) self.details_view_blurred_cover.set_opacity(0.3)
return 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() 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( 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 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): def sort_func(self, child1, child2):