refactor: organize arguments, update docstrings

The ability to pass a border radius scaling argument is also included.
This commit is contained in:
Travis Abendshien
2024-08-24 17:22:06 -07:00
parent 12d69baa98
commit 5c4a3c5856

View File

@@ -65,7 +65,8 @@ class ThumbRenderer(QObject):
super().__init__()
# Cached thumbnail elements.
# Key: Size + Pixel Ratio Tuple (Ex. (512, 512, 1.25))
# Key: Size + Pixel Ratio Tuple + Radius Scale
# (Ex. (512, 512, 1.25, 4))
self.thumb_masks: dict = {}
self.raised_edges: dict = {}
@@ -97,23 +98,36 @@ class ThumbRenderer(QObject):
return "file_generic"
def _get_mask(self, size: tuple[int, int], pixel_ratio: float) -> Image.Image:
def _get_mask(
self, size: tuple[int, int], pixel_ratio: float, scale_radius: bool = False
) -> Image.Image:
"""Return a thumbnail mask given a size, pixel ratio, and radius scaling option.
If one is not already cached, a new one will be rendered.
Args:
size (tuple[int, int]): The size of the graphic.
pixel_ratio (float): The screen pixel ratio.
scale_radius (bool): Option to scale the radius up (Used for Preview Panel).
"""
Returns a thumbnail mask given a size and pixel ratio.
If one is not already cached, then a new one will be rendered.
"""
item: Image.Image = self.thumb_masks.get((*size, pixel_ratio))
radius_scale: float = 1
if scale_radius:
radius_scale = max(size[0], size[1]) / 512
item: Image.Image = self.thumb_masks.get((*size, pixel_ratio, radius_scale))
if not item:
item = self._render_mask(size, pixel_ratio)
self.thumb_masks[(*size, pixel_ratio)] = item
item = self._render_mask(size, pixel_ratio, radius_scale)
self.thumb_masks[(*size, pixel_ratio, radius_scale)] = item
return item
def _get_edge(
self, size: tuple[int, int], pixel_ratio: float
) -> tuple[Image.Image, Image.Image]:
"""
Returns a thumbnail raised edge graphic given a size and pixel ratio.
If one is not already cached, then a new one will be rendered.
"""Return a thumbnail edge given a size, pixel ratio, and radius scaling option.
If one is not already cached, a new one will be rendered.
Args:
size (tuple[int, int]): The size of the graphic.
pixel_ratio (float): The screen pixel ratio.
"""
item: tuple[Image.Image, Image.Image] = self.raised_edges.get(
(*size, pixel_ratio)
@@ -126,7 +140,7 @@ class ThumbRenderer(QObject):
def _get_icon(
self, name: str, color: str, size: tuple[int, int], pixel_ratio: float = 1.0
) -> Image.Image:
"""Retrieves a new or cached icon.
"""Return an icon given a size, pixel ratio, and radius scaling option.
Args:
name (str): The name of the icon resource. "thumb_loading" will not draw a border.
@@ -149,8 +163,16 @@ class ThumbRenderer(QObject):
self.icons[(name, *color, size, pixel_ratio)] = item
return item
def _render_mask(self, size: tuple[int, int], pixel_ratio) -> Image.Image:
"""Renders a thumbnail mask."""
def _render_mask(
self, size: tuple[int, int], pixel_ratio: float, radius_scale: float = 1
) -> Image.Image:
"""Render a thumbnail mask graphic.
Args:
size (tuple[int,int]): The size of the graphic.
pixel_ratio (float): The screen pixel ratio.
radius scale (float): The scale factor of the border radius (Used by Preview Panel).
"""
SMOOTH_FACTOR: int = 2
RADIUS_FACTOR: int = 8
@@ -162,7 +184,9 @@ class ThumbRenderer(QObject):
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),
radius=math.ceil(
RADIUS_FACTOR * SMOOTH_FACTOR * pixel_ratio * radius_scale
),
fill="white",
)
im = im.resize(
@@ -172,9 +196,14 @@ class ThumbRenderer(QObject):
return im
def _render_edge(
self, size: tuple[int, int], pixel_ratio
self, size: tuple[int, int], pixel_ratio: float
) -> tuple[Image.Image, Image.Image]:
"""Renders a thumbnail highlight border."""
"""Render a thumbnail edge graphic.
Args:
size (tuple[int,int]): The size of the graphic.
pixel_ratio (float): The screen pixel ratio.
"""
SMOOTH_FACTOR: int = 2
RADIUS_FACTOR: int = 8
WIDTH: int = math.floor(pixel_ratio * 2)
@@ -229,6 +258,15 @@ class ThumbRenderer(QObject):
pixel_ratio: float,
draw_border: bool = True,
) -> Image.Image:
"""Render a thumbnail icon.
Args:
name (str): The name of the icon resource.
color (str): The color to use for the icon.
size (tuple[int,int]): The size of the icon.
pixel_ratio (float): The screen pixel ratio.
draw_border (bool): Option to draw a border.
"""
BORDER_FACTOR: int = 5
SMOOTH_FACTOR: int = math.ceil(2 * pixel_ratio)
RADIUS_FACTOR: int = 8
@@ -318,7 +356,12 @@ class ThumbRenderer(QObject):
def _apply_overlay_color(self, image: Image.Image, color: str) -> Image.Image:
"""Apply a color overlay effect to an image based on its color channel data.
Red channel for foreground, green channel for outline, none for background."""
Red channel for foreground, green channel for outline, none for background.
Args:
image (Image.Image): The image to apply an overlay to.
color (str): The name of the ColorType color to use.
"""
bg_color: str = (
get_ui_color(ColorType.DARK_ACCENT, color)
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
@@ -361,8 +404,10 @@ class ThumbRenderer(QObject):
Args:
image (Image.Image): The image to apply the edge to.
edge (Image.Image): The edge image to apply.
edge (tuple[Image.Image, Image.Image]): The edge images to apply.
Item 0 is the inner highlight, and item 1 is the outer shadow.
faded (bool): Whether or not to apply a faded version of the edge.
Used for light themes.
"""
opacity: float = 0.75 if not faded else 0.6
shade_reduction: float = (
@@ -390,7 +435,12 @@ class ThumbRenderer(QObject):
return im
def _audio_album_thumb(self, filepath: Path, ext: str) -> Image.Image | None:
"""Gets an album cover from an audio file if one is present."""
"""Return an album cover thumb from an audio file if a cover is present.
Args:
filepath (Path): The path of the file.
ext (str): The file extension (with leading ".").
"""
image: Image.Image = None
try:
if not filepath.is_file():
@@ -428,7 +478,14 @@ class ThumbRenderer(QObject):
def _audio_waveform_thumb(
self, filepath: Path, ext: str, size: int, pixel_ratio: float
) -> Image.Image | None:
"""Render a waveform image from an audio file."""
"""Render a waveform image from an audio file.
Args:
filepath (Path): The path of the file.
ext (str): The file extension (with leading ".").
size (tuple[int,int]): The size of the thumbnail.
pixel_ratio (float): The screen pixel ratio.
"""
# BASE_SCALE used for drawing on a larger image and resampling down
# to provide an antialiased effect.
BASE_SCALE: int = 2
@@ -510,6 +567,12 @@ class ThumbRenderer(QObject):
return im
def _blender(self, filepath: Path) -> Image.Image:
"""
Get an emended thumbnail from a Blender file, if a thumbnail is present.
Args:
filepath (Path): The path of the file.
"""
bg_color: str = (
"#1e1e1e"
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
@@ -541,7 +604,12 @@ class ThumbRenderer(QObject):
return im
def _font_short_thumb(self, filepath: Path, size: int) -> Image.Image:
"""Render a small font preview ("Aa") thumbnail from a font file."""
"""Render a small font preview ("Aa") thumbnail from a font file.
Args:
filepath (Path): The path of the file.
size (tuple[int,int]): The size of the thumbnail.
"""
im: Image.Image = None
try:
bg = Image.new("RGB", (size, size), color="#000000")
@@ -598,7 +666,12 @@ class ThumbRenderer(QObject):
return im
def _font_long_thumb(self, filepath: Path, size: int) -> Image.Image:
"""Render a large font preview ("Alphabet") thumbnail from a font file."""
"""Render a large font preview ("Alphabet") thumbnail from a font file.
Args:
filepath (Path): The path of the file.
size (tuple[int,int]): The size of the thumbnail.
"""
# Scale the sample font sizes to the preview image
# resolution,assuming the sizes are tuned for 256px.
im: Image.Image = None
@@ -628,6 +701,11 @@ class ThumbRenderer(QObject):
return im
def _image_raw_thumb(self, filepath: Path) -> Image.Image:
"""Render a thumbnail for a RAW image type.
Args:
filepath (Path): The path of the file.
"""
im: Image.Image = None
try:
with rawpy.imread(str(filepath)) as raw:
@@ -652,6 +730,11 @@ class ThumbRenderer(QObject):
return im
def _image_thumb(self, filepath: Path) -> Image.Image:
"""Render a thumbnail for a standard image type.
Args:
filepath (Path): The path of the file.
"""
im: Image.Image = None
try:
im = Image.open(filepath)
@@ -673,11 +756,23 @@ class ThumbRenderer(QObject):
return im
def _image_vector_thumb(self, filepath: Path, size: int) -> Image.Image:
"""Render a thumbnail for a vector image, such as SVG.
Args:
filepath (Path): The path of the file.
size (tuple[int,int]): The size of the thumbnail.
"""
# TODO: Implement.
im: Image.Image = None
return im
def _model_stl_thumb(self, filepath: Path, size: int) -> Image.Image:
"""Render a thumbnail for an STL file.
Args:
filepath (Path): The path of the file.
size (tuple[int,int]): The size of the icon.
"""
# TODO: Implement.
# The following commented code describes a method for rendering via
# matplotlib.
@@ -703,7 +798,12 @@ class ThumbRenderer(QObject):
return im
def _text_thumb(self, filepath: Path, size: int) -> Image.Image:
def _text_thumb(self, filepath: Path) -> Image.Image:
"""Render a thumbnail for a plaintext file.
Args:
filepath (Path): The path of the file.
"""
im: Image.Image = None
bg_color: str = (
@@ -738,6 +838,11 @@ class ThumbRenderer(QObject):
return im
def _video_thumb(self, filepath: Path) -> Image.Image:
"""Render a thumbnail for a video file.
Args:
filepath (Path): The path of the file.
"""
im: Image.Image = None
try:
if is_readable_video(filepath):
@@ -775,10 +880,22 @@ class ThumbRenderer(QObject):
base_size: tuple[int, int],
pixel_ratio: float,
is_loading=False,
is_thumb=False,
is_grid_thumb=False,
update_on_ratio_change=False,
):
"""Internal renderer. Renders an entry/element thumbnail for the GUI."""
"""Render a thumbnail or preview image.
Args:
timestamp (float): The timestamp for which this this job was dispatched.
filepath (str | Path): The path of the file to render a thumbnail for.
base_size (tuple[int,int]): The unmodified base size of the thumbnail.
pixel_ratio (float): The screen pixel ratio.
is_loading (bool): Is this a loading graphic?
is_grid_thumb (bool): Is this a thumbnail for the thumbnail grid?
Or else the Preview Pane?
update_on_ratio_change (bool): Should an updated ratio signal be sent?
"""
adj_size = math.ceil(max(base_size[0], base_size[1]) * pixel_ratio)
image: Image.Image = None
pixmap: QPixmap = None
@@ -831,10 +948,10 @@ class ThumbRenderer(QObject):
image = self._video_thumb(_filepath)
# Plain Text ===================================================
elif MediaType.PLAINTEXT in MediaCategories.get_types(ext):
image = self._text_thumb(_filepath, adj_size)
image = self._text_thumb(_filepath)
# Fonts ========================================================
elif MediaType.FONT in MediaCategories.get_types(ext, True):
if is_thumb:
if is_grid_thumb:
# Short (Aa) Preview
image = self._font_short_thumb(_filepath, adj_size)
else:
@@ -879,7 +996,7 @@ class ThumbRenderer(QObject):
)
image = image.resize((new_x, new_y), resample=resampling_method)
mask: Image.Image = None
if is_thumb:
if is_grid_thumb:
mask = self._get_mask((adj_size, adj_size), pixel_ratio)
edge: tuple[Image.Image, Image.Image] = self._get_edge(
(adj_size, adj_size), pixel_ratio
@@ -889,7 +1006,7 @@ class ThumbRenderer(QObject):
edge,
)
else:
mask = self._get_mask(image.size, pixel_ratio)
mask = self._get_mask(image.size, pixel_ratio, scale_radius=True)
final = Image.new("RGBA", image.size, (0, 0, 0, 0))
final.paste(image, mask=mask.getchannel(0))