From 39324142f1f1e27d09077e2119ebff2533ac05b4 Mon Sep 17 00:00:00 2001 From: Travis Abendshien Date: Sun, 21 Jul 2024 08:40:19 -0700 Subject: [PATCH] feat(ui): add dynamic file thumb icons --- .../resources/qt/images/broken_link_icon.png | Bin 0 -> 18571 bytes .../qt/images/file_icons/generic.png | Bin 0 -> 5768 bytes .../resources/qt/images/thumb_broken_512.png | Bin 24707 -> 0 bytes .../qt/images/thumb_file_default_512.png | Bin 12661 -> 0 bytes tagstudio/src/core/palette.py | 13 +- tagstudio/src/qt/resource_manager.py | 10 +- tagstudio/src/qt/resources.json | 8 + tagstudio/src/qt/widgets/thumb_renderer.py | 252 ++++++++++++------ 8 files changed, 201 insertions(+), 82 deletions(-) create mode 100644 tagstudio/resources/qt/images/broken_link_icon.png create mode 100644 tagstudio/resources/qt/images/file_icons/generic.png delete mode 100644 tagstudio/resources/qt/images/thumb_broken_512.png delete mode 100644 tagstudio/resources/qt/images/thumb_file_default_512.png diff --git a/tagstudio/resources/qt/images/broken_link_icon.png b/tagstudio/resources/qt/images/broken_link_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d431097084777d038020ea832bdbe5d5e16641f8 GIT binary patch literal 18571 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 literal 0 HcmV?d00001 diff --git a/tagstudio/resources/qt/images/file_icons/generic.png b/tagstudio/resources/qt/images/file_icons/generic.png new file mode 100644 index 0000000000000000000000000000000000000000..13685e3a60e24547d254059427ee09ec56b95cb2 GIT binary patch literal 5768 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 Ym$pBgQy!fa1rI=4YWjCdZaX~w541AvfdBvi diff --git a/tagstudio/resources/qt/images/thumb_file_default_512.png b/tagstudio/resources/qt/images/thumb_file_default_512.png deleted file mode 100644 index 28dfbd433568600ee6397a0baec5ad25791308ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12661 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 G-})bN^c95w diff --git a/tagstudio/src/core/palette.py b/tagstudio/src/core/palette.py index 0b36f953..b8e93d3a 100644 --- a/tagstudio/src/core/palette.py +++ b/tagstudio/src/core/palette.py @@ -279,11 +279,16 @@ _TAG_COLORS: dict = { _UI_COLORS: dict = { "": { - ColorType.PRIMARY: "#1e1e1e", - ColorType.TEXT: ColorType.LIGHT_ACCENT, - ColorType.BORDER: "#333333", + ColorType.PRIMARY: "#333333", + ColorType.BORDER: "#555555", ColorType.LIGHT_ACCENT: "#FFFFFF", - ColorType.DARK_ACCENT: "#222222", + ColorType.DARK_ACCENT: "#1e1e1e", + }, + "red": { + ColorType.PRIMARY: "#e22c3c", + ColorType.BORDER: "#e54252", + ColorType.LIGHT_ACCENT: "#f39caa", + ColorType.DARK_ACCENT: "#440d12", }, "green": { ColorType.PRIMARY: "#28bb48", diff --git a/tagstudio/src/qt/resource_manager.py b/tagstudio/src/qt/resource_manager.py index 0db8bb19..5d1d1851 100644 --- a/tagstudio/src/qt/resource_manager.py +++ b/tagstudio/src/qt/resource_manager.py @@ -5,6 +5,7 @@ import logging from pathlib import Path from typing import Any +from PIL import Image import ujson @@ -46,7 +47,7 @@ class ResourceManager: return cached_res else: res: dict = ResourceManager._map.get(id) - if res.get("mode") in ["r", "rb"]: + if res and res.get("mode") in ["r", "rb"]: with open( (Path(__file__).parents[2] / "resources" / res.get("path")), res.get("mode"), @@ -56,7 +57,12 @@ class ResourceManager: data = bytes(data) ResourceManager._cache[id] = data return data - elif res.get("mode") in ["qt"]: + elif res and res.get("mode") == "pil": + data = Image.open( + Path(__file__).parents[2] / "resources" / res.get("path") + ) + return data + elif res and res.get("mode") in ["qt"]: # TODO: Qt resource loading logic pass diff --git a/tagstudio/src/qt/resources.json b/tagstudio/src/qt/resources.json index 1f8663d3..9f3d3e49 100644 --- a/tagstudio/src/qt/resources.json +++ b/tagstudio/src/qt/resources.json @@ -14,5 +14,13 @@ "volume_mute_icon": { "path": "qt/images/volume_mute.svg", "mode": "rb" + }, + "broken_link_icon": { + "path": "qt/images/broken_link_icon.png", + "mode": "pil" + }, + "file_generic": { + "path": "qt/images/file_icons/generic.png", + "mode": "pil" } } diff --git a/tagstudio/src/qt/widgets/thumb_renderer.py b/tagstudio/src/qt/widgets/thumb_renderer.py index 469635ef..88933b9d 100644 --- a/tagstudio/src/qt/widgets/thumb_renderer.py +++ b/tagstudio/src/qt/widgets/thumb_renderer.py @@ -25,6 +25,7 @@ from pydub import AudioSegment, exceptions from mutagen import id3, flac, mp4, MutagenError from PySide6.QtCore import Qt, QObject, Signal, QSize from PySide6.QtGui import QGuiApplication, QPixmap +from src.qt.resource_manager import ResourceManager from src.qt.helpers.color_overlay import theme_fg_overlay from src.qt.helpers.gradient import four_corner_gradient_background from src.qt.helpers.text_wrapper import wrap_full_text @@ -44,6 +45,7 @@ from src.core.palette import ColorType, get_ui_color from src.qt.helpers.blender_thumbnailer import blend_thumb from src.qt.helpers.file_tester import is_readable_video + ImageFile.LOAD_TRUNCATED_IMAGES = True ERROR = "[ERROR]" @@ -56,36 +58,23 @@ register_avif_opener() class ThumbRenderer(QObject): - # finished = Signal() + rm: ResourceManager = ResourceManager() updated = Signal(float, QPixmap, QSize, str) updated_ratio = Signal(float) - # updatedImage = Signal(QPixmap) - # updatedSize = Signal(QSize) # Cached thumbnail elements. # Key: Size + Pixel Ratio Tuple (Ex. (512, 512, 1.25)) thumb_masks: dict = {} thumb_borders: dict = {} + # Key: ("name", "color", 512, 512, 1.25) + icons: dict = {} + thumb_loading_512: Image.Image = Image.open( Path(__file__).parents[3] / "resources/qt/images/thumb_loading_512.png" ) thumb_loading_512.load() - thumb_broken_512: Image.Image = Image.open( - Path(__file__).parents[3] / "resources/qt/images/thumb_broken_512.png" - ) - thumb_broken_512.load() - - thumb_file_default_512: Image.Image = Image.open( - Path(__file__).parents[3] / "resources/qt/images/thumb_file_default_512.png" - ) - thumb_file_default_512.load() - - # thumb_debug: Image.Image = Image.open(Path( - # f'{Path(__file__).parents[2]}/resources/qt/images/temp.jpg')) - # thumb_debug.load() - # TODO: Make dynamic font sized given different pixel ratios font_pixel_ratio: float = 1 ext_font = ImageFont.truetype( @@ -106,22 +95,33 @@ class ThumbRenderer(QObject): return item @staticmethod - def _get_border(size: tuple[int, int], pixel_ratio: float) -> Image.Image: + def _get_hl_border(size: tuple[int, int], pixel_ratio: float) -> Image.Image: """ Returns a thumbnail border given a size and pixel ratio. If one is not already cached, then a new one will be rendered. """ item: Image.Image = ThumbRenderer.thumb_borders.get((*size, pixel_ratio)) if not item: - item = ThumbRenderer._render_border(size, pixel_ratio) + item = ThumbRenderer._render_hl_border(size, pixel_ratio) ThumbRenderer.thumb_borders[(*size, pixel_ratio)] = item return item + @staticmethod + def _get_icon( + name: str, color: str, size: tuple[int, int], pixel_ratio: float + ) -> Image.Image: + item: Image.Image = ThumbRenderer.icons.get((name, color, *size, pixel_ratio)) + if not item: + item = ThumbRenderer._render_icon(name, color, size, pixel_ratio) + ThumbRenderer.thumb_borders[(name, *color, size, pixel_ratio)] = item + return item + @staticmethod def _render_mask(size: tuple[int, int], pixel_ratio) -> Image.Image: """Renders a thumbnail mask.""" - smooth_factor: int = math.ceil(2 * pixel_ratio) + smooth_factor: int = 2 radius_factor: int = 8 + im: Image.Image = Image.new( mode="L", size=tuple([d * smooth_factor for d in size]), # type: ignore @@ -140,9 +140,9 @@ class ThumbRenderer(QObject): return im @staticmethod - def _render_border(size: tuple[int, int], pixel_ratio) -> Image.Image: - """Renders a thumbnail border.""" - smooth_factor: int = math.ceil(2 * pixel_ratio) + def _render_hl_border(size: tuple[int, int], pixel_ratio) -> Image.Image: + """Renders a thumbnail highlight border.""" + smooth_factor: int = 2 radius_factor: int = 8 im: Image.Image = Image.new( mode="RGBA", @@ -163,6 +163,127 @@ class ThumbRenderer(QObject): ) return im + @staticmethod + def _render_icon( + name: str, color: str, size: tuple[int, int], pixel_ratio: float + ) -> Image.Image: + smooth_factor: int = math.ceil(2 * pixel_ratio) + radius_factor: int = 8 + icon_ratio: float = 1.75 + + # Create larger blank image based on smooth_factor + im: Image.Image = Image.new( + "RGBA", + size=tuple([d * smooth_factor for d in size]), # type: ignore + color="#00000000", + ) + + # Create solid background color + bg: Image.Image = Image.new( + "RGB", + size=tuple([d * smooth_factor for d in size]), # type: ignore + color="#000000", + ) + + # Paste background color with rounded rectangle mask onto blank image + im.paste( + bg, + (0, 0), + mask=ThumbRenderer._get_mask( + tuple([d * smooth_factor for d in size]), # type: ignore + (pixel_ratio * smooth_factor), + ), + ) + + # Draw rounded rectangle border + draw = ImageDraw.Draw(im) + draw.rounded_rectangle( + (0, 0) + tuple([d - 1 for d in im.size]), + radius=math.ceil(radius_factor * smooth_factor * pixel_ratio), + fill="black", + outline="#FF0000", + width=math.floor(pixel_ratio * 8), + ) + + # Resize image to final size + im = im.resize( + size, + resample=Image.Resampling.BILINEAR, + ) + fg: Image.Image = Image.new("RGB", size=size, color="#00FF00") + + # Get icon by name + icon: Image.Image = ThumbRenderer.rm.get(name) + + # Resize icon to fit icon_ratio + icon = icon.resize( + (math.ceil(size[0] // icon_ratio), math.ceil(size[1] // icon_ratio)) + ) + + # Paste icon centered + im.paste( + im=fg.resize( + (math.ceil(size[0] // icon_ratio), math.ceil(size[1] // icon_ratio)) + ), + box=( + math.ceil((size[0] - (size[0] // icon_ratio)) // 2), + math.ceil((size[1] - (size[1] // icon_ratio)) // 2), + ), + mask=icon.getchannel(3), + ) + + # Apply color overlay + im = ThumbRenderer._apply_overlay_color( + im, + color, + ) + + return im + + @staticmethod + def _apply_overlay_color(image: Image.Image, color: str) -> Image.Image: + """Apply a gradient effect over an an image. + Red channel for foreground, green channel for outline, none for background.""" + bg_color: str = ( + get_ui_color(ColorType.DARK_ACCENT, color) + if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark + else get_ui_color(ColorType.PRIMARY, color) + ) + fg_color: str = ( + get_ui_color(ColorType.PRIMARY, color) + if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark + else get_ui_color(ColorType.LIGHT_ACCENT, color) + ) + ol_color: str = ( + get_ui_color(ColorType.BORDER, color) + if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark + else "#FFFFFF" + ) + + bg: Image.Image = Image.new(image.mode, image.size, color=bg_color) + fg: Image.Image = Image.new(image.mode, image.size, color=fg_color) + ol: Image.Image = Image.new(image.mode, image.size, color=ol_color) + + bg.paste(fg, (0, 0), mask=image.getchannel(0)) + bg.paste(ol, (0, 0), mask=image.getchannel(1)) + + if image.mode == "RGBA": + alpha_bg: Image.Image = bg.copy() + alpha_bg.convert("RGBA") + alpha_bg.putalpha(0) + alpha_bg.paste(bg, (0, 0), mask=image.getchannel(3)) + bg = alpha_bg + + return bg + + @staticmethod + def get_mime_icon_resource(ext: str = "") -> str: + if ext in IMAGE_TYPES: + return "image_photo" + elif ext in VIDEO_TYPES: + return "doc_presentation" + return "" + def render( self, timestamp: float, @@ -271,17 +392,15 @@ class ThumbRenderer(QObject): # count, seeking halfway does not work and the thumb # must be pulled from the earliest available frame. video.set(cv2.CAP_PROP_POS_FRAMES, 0) - success, frame = video.read() - if not success: - # Depending on the video format, compression, and frame - # count, seeking halfway does not work and the thumb - # must be pulled from the earliest available frame. - video.set(cv2.CAP_PROP_POS_FRAMES, 0) - success, frame = video.read() frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) image = Image.fromarray(frame) else: - image = self.thumb_file_default_512 + image = ThumbRenderer._get_icon( + name="file_generic", + color="red", + size=(adj_size, adj_size), + pixel_ratio=pixel_ratio, + ) # Plain Text =================================================== elif ext in PLAINTEXT_TYPES: @@ -308,7 +427,7 @@ class ThumbRenderer(QObject): _filepath, ext, adj_size, pixel_ratio ) if image is not None: - image = self._apply_overlay_color(image, "green") + image = ThumbRenderer._apply_overlay_color(image, "green") # 3D =========================================================== # elif extension == 'stl': @@ -355,16 +474,7 @@ class ThumbRenderer(QObject): f"[ThumbRenderer]{ERROR}: Couldn't render thumbnail for {_filepath.name} ({type(e).__name__})" ) - image = ThumbRenderer.thumb_file_default_512.resize( - (adj_size, adj_size), resample=Image.Resampling.BILINEAR - ) - # No Rendered Thumbnail ======================================== - else: - image = ThumbRenderer.thumb_file_default_512.resize( - (adj_size, adj_size), resample=Image.Resampling.BILINEAR - ) - if not image: raise UnidentifiedImageError @@ -392,7 +502,7 @@ class ThumbRenderer(QObject): mask: Image.Image = ThumbRenderer._get_mask( (adj_size, adj_size), pixel_ratio ) - hl: Image.Image = ThumbRenderer._get_border( + hl: Image.Image = ThumbRenderer._get_hl_border( (adj_size, adj_size), pixel_ratio ) final = four_corner_gradient_background(image, adj_size, mask, hl) @@ -415,21 +525,37 @@ class ThumbRenderer(QObject): ) final = Image.new("RGBA", image.size, (0, 0, 0, 0)) final.paste(image, mask=rec.getchannel(0)) + except FileNotFoundError as e: + logging.info( + f"[ThumbRenderer]{ERROR}: Couldn't render thumbnail for {_filepath.name} ({type(e).__name__})" + ) + if update_on_ratio_change: + self.updated_ratio.emit(1) + final = ThumbRenderer._get_icon( + name="broken_link_icon", + color="red", + size=(adj_size, adj_size), + pixel_ratio=pixel_ratio, + ) except ( UnidentifiedImageError, - FileNotFoundError, cv2.error, DecompressionBombError, UnicodeDecodeError, ) as e: - if e is not UnicodeDecodeError: - logging.info( - f"[ThumbRenderer]{ERROR}: Couldn't render thumbnail for {_filepath.name} ({type(e).__name__})" - ) + # if e is not UnicodeDecodeError: + logging.info( + f"[ThumbRenderer]{ERROR}: Couldn't render thumbnail for {_filepath.name} ({type(e).__name__})" + ) + if update_on_ratio_change: self.updated_ratio.emit(1) - final = ThumbRenderer.thumb_broken_512.resize( - (adj_size, adj_size), resample=resampling_method + final = ThumbRenderer._get_icon( + # name=ThumbRenderer.get_mime_icon_resource(_filepath.suffix.lower()), + name="file_generic", + color="", + size=(adj_size, adj_size), + pixel_ratio=pixel_ratio, ) qim = ImageQt.ImageQt(final) if image: @@ -620,7 +746,7 @@ class ThumbRenderer(QObject): cropped_im, box=(margin, margin + ((size - new_y) // 2)), ) - return self._apply_overlay_color(bg, "purple") + return ThumbRenderer._apply_overlay_color(bg, "purple") def _font_preview_long(self, filepath: Path, size: int) -> Image.Image: """Renders a large font preview ("Alphabet") thumbnail from a font file.""" @@ -644,29 +770,3 @@ class ThumbRenderer(QObject): len(text_wrapped.split("\n")) + lines_of_padding ) * draw.textbbox((0, 0), "A", font=font)[-1] return theme_fg_overlay(bg, use_alpha=False) - - def _apply_overlay_color(self, image: Image.Image, color: str) -> Image.Image: - """Apply a gradient effect over an an image. - Red channel for foreground, green channel for outline, none for background.""" - bg_color: str = ( - get_ui_color(ColorType.DARK_ACCENT, color) - if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark - else get_ui_color(ColorType.PRIMARY, color) - ) - fg_color: str = ( - get_ui_color(ColorType.PRIMARY, color) - if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark - else get_ui_color(ColorType.LIGHT_ACCENT, color) - ) - ol_color: str = ( - get_ui_color(ColorType.BORDER, color) - if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark - else "#FFFFFF" - ) - - bg: Image.Image = Image.new("RGB", image.size, color=bg_color) - fg: Image.Image = Image.new("RGB", image.size, color=fg_color) - ol: Image.Image = Image.new("RGB", image.size, color=ol_color) - bg.paste(fg, (0, 0), mask=image.getchannel(0)) - bg.paste(ol, (0, 0), mask=image.getchannel(1)) - return bg