Skip to content

feat(ui): expanded thumbnail and preview features #390

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 54 commits into from
Aug 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
34f347b
Fix text and RAW image handling
CyanVoxel Jun 1, 2024
3c27b37
Use chardet for character encoding detection
CyanVoxel Jun 1, 2024
7ce3519
Add support for waveform + album cover thumbnails
CyanVoxel Jun 4, 2024
6b892ce
Rename "cover" variables for MyPy
CyanVoxel Jun 4, 2024
ff17b93
Rename "audio_tags" variables for MyPy + typing
CyanVoxel Jun 4, 2024
c1cd96f
Add # type: ignore to fromstring method
CyanVoxel Jun 4, 2024
3144440
Add GIF preview support
CyanVoxel Jun 4, 2024
d339f86
Add rough check for invalid video codecs
CyanVoxel Jun 8, 2024
dc135f7
Add ".plist" to PLAINTEXT_TYPES
CyanVoxel Jun 8, 2024
10d81b3
Add readable video tester
CyanVoxel Jun 9, 2024
087176e
Add ".psd" to IMAGE_TYPES; Handle ID3NoHeaderError
CyanVoxel Jun 13, 2024
cee4254
Improve and style waveform previews
CyanVoxel Jun 15, 2024
127fed7
Add final return statement to _album_artwork()
CyanVoxel Jun 15, 2024
32257f6
Add final return statement to _audio_waveform()
CyanVoxel Jun 15, 2024
3e00a77
Tweak waveform color and size
CyanVoxel Jun 15, 2024
1529714
Fix ItemThumb label text color in light mode
CyanVoxel Jun 15, 2024
d2b5e31
Fix most theme UI legibility issues
CyanVoxel Jun 15, 2024
05a4860
Match additional UI to color scheme
CyanVoxel Jun 16, 2024
c582f3d
ruff format
CyanVoxel Jul 19, 2024
c0e56dc
feat(ui): add UI color palette dict
CyanVoxel Jul 19, 2024
598aa4f
feat(ui) center and color small font previews
CyanVoxel Jul 19, 2024
ffdfd6c
fix(ui): large font previews follow app theme
CyanVoxel Jul 20, 2024
3bfeb3c
fix(ui): blender previews follow app theme
CyanVoxel Jul 20, 2024
086fc1e
feat(ui): add resizable thumbnail options
CyanVoxel Jul 20, 2024
ef8cc6c
fix: mkv files with "[0][0][0][0]" codec load properly
CyanVoxel Jul 20, 2024
ad53f10
fix: missing audio files properly handled
CyanVoxel Jul 20, 2024
91ee242
feat(ui): use system accent color for thumb selections
CyanVoxel Jul 20, 2024
196c1ba
fix(ui): hide gif preview in multi-selections
CyanVoxel Jul 20, 2024
3932414
feat(ui): add dynamic file thumb icons
CyanVoxel Jul 21, 2024
c6a5202
fix(ui): hide previous thumbnail before resizing
CyanVoxel Jul 22, 2024
ad12d64
(fix): catch ffmpeg errors in file tester
CyanVoxel Jul 25, 2024
8d2e67d
Squashed commit of the following:
CyanVoxel Jul 25, 2024
6883f9e
feat(ui): add media types and icon resources
CyanVoxel Jul 25, 2024
447b5e6
feat(ui): add more default media types and icons
CyanVoxel Aug 21, 2024
c070f84
fix: remove leading dot in preview panel ext
CyanVoxel Aug 21, 2024
a244098
refactor: remove edge from `four_corner_gradient()`
CyanVoxel Aug 21, 2024
f91861d
fix: handle missing files in `resource_manager`
CyanVoxel Aug 21, 2024
e4f7055
fix(ui): thumb edges fading on refresh
CyanVoxel Aug 21, 2024
387baae
Merge branch 'Alpha-v9.4' into thumbnails
CyanVoxel Aug 21, 2024
81dfb50
feat(ui): add default icons for audio+vector thumbs
CyanVoxel Aug 21, 2024
a658fc4
feat(ui): apply edge to default icon thumbs
CyanVoxel Aug 21, 2024
ccf3d78
chore: remove unused code
CyanVoxel Aug 21, 2024
148f792
refactor(ui): move loading icon to `ResourceManager`
CyanVoxel Aug 21, 2024
9f688cd
fix(ui) color for default icons follow theme
CyanVoxel Aug 23, 2024
c377b9d
fix: remove `theme_color` redef
CyanVoxel Aug 23, 2024
12d69ba
refactor: make some consts and args clearer
CyanVoxel Aug 24, 2024
5c4a3c5
refactor: organize arguments, update docstrings
CyanVoxel Aug 25, 2024
a037a3b
chore: format docstrings with ruff
CyanVoxel Aug 25, 2024
2796db6
refactor: replace magic numbers with named values
CyanVoxel Aug 30, 2024
dd90add
refactor: remove unused code, comments, & imports
CyanVoxel Aug 30, 2024
ad0f472
refactor: rename args to not shadow builtins
CyanVoxel Aug 30, 2024
2faed27
refactor: remove unused vars from `thumb_renderer`
CyanVoxel Aug 30, 2024
69e1b20
fix: handle ValueError in `render()`
CyanVoxel Aug 30, 2024
992aa82
docs: add FFmpeg requirement to README
CyanVoxel Aug 31, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ If you're interested in contributing to TagStudio, please take a look at the [co

To download TagStudio, visit the [Releases](https://github.com/TagStudioDev/TagStudio/releases) section of the GitHub repository and download the latest release for your system under the "Assets" section. TagStudio is available for **Windows**, **macOS** _(Apple Silicon & Intel)_, and **Linux**. Windows and Linux builds are also available in portable versions if you want a more self-contained executable to move around.

For video thumbnails and playback, you'll also need [FFmpeg](https://ffmpeg.org/download.html) installed on your system.

> [!IMPORTANT]
> On macOS, you may be met with a message saying _""TagStudio" can't be opened because Apple cannot check it for malicious software."_ If you encounter this, then you'll need to go to the "Settings" app, navigate to "Privacy & Security", and scroll down to a section that says _""TagStudio" was blocked from use because it is not from an identified developer."_ Click the "Open Anyway" button to allow TagStudio to run. You should only have to do this once after downloading the application.

Expand Down
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ numpy==1.26.4
rawpy==0.21.0
pillow-heif==0.16.0
chardet==5.2.0
pydub==0.25.1
mutagen==1.47.0
numpy==1.26.4
ffmpeg-python==0.2.0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see no update to the README to tell the user to install ffmpeg.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can confirm, video thumbnails/previews do not work without ffmpeg installed. (Fails to find ffprobe). Could this be something looked into being bundled into pyinstaller? Ffmpeg will be an incredibly useful tool for the future (could probably look into dropping opencv potentially)

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tagstudio/resources/qt/images/file_icons/font.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tagstudio/resources/qt/images/file_icons/text.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed tagstudio/resources/qt/images/thumb_border_512.png
Binary file not shown.
Binary file removed tagstudio/resources/qt/images/thumb_broken_512.png
Binary file not shown.
Binary file not shown.
Binary file added tagstudio/resources/qt/images/thumb_loading.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed tagstudio/resources/qt/images/thumb_loading_512.png
Binary file not shown.
Binary file not shown.
Binary file removed tagstudio/resources/qt/images/thumb_mask_128.png
Binary file not shown.
Binary file removed tagstudio/resources/qt/images/thumb_mask_512.png
Binary file not shown.
Binary file removed tagstudio/resources/qt/images/thumb_mask_hl_512.png
Diff not rendered.
1 change: 0 additions & 1 deletion tagstudio/src/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,5 @@
"cool gray",
"olive",
]

TAG_FAVORITE = 1
TAG_ARCHIVED = 0
4 changes: 3 additions & 1 deletion tagstudio/src/core/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ class SettingItems(str, enum.Enum):


class Theme(str, enum.Enum):
COLOR_BG = "#65000000"
COLOR_BG_DARK = "#65000000"
COLOR_BG_LIGHT = "#22000000"
COLOR_DARK_LABEL = "#DD000000"
COLOR_HOVER = "#65AAAAAA"
COLOR_PRESSED = "#65EEEEEE"
COLOR_DISABLED = "#65F39CAA"
Expand Down
60 changes: 49 additions & 11 deletions tagstudio/src/core/media_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
class MediaType(str, Enum):
"""Names of media types."""

ADOBE_PHOTOSHOP: str = "adobe_photoshop"
AFFINITY_PHOTO: str = "affinity_photo"
ARCHIVE: str = "archive"
AUDIO_MIDI: str = "audio_midi"
AUDIO: str = "audio"
BLENDER: str = "blender"
DATABASE: str = "database"
Expand All @@ -27,7 +30,7 @@ class MediaType(str, Enum):
MATERIAL: str = "material"
MODEL: str = "model"
PACKAGE: str = "package"
PHOTOSHOP: str = "photoshop"
PDF: str = "pdf"
PLAINTEXT: str = "plaintext"
PRESENTATION: str = "presentation"
PROGRAM: str = "program"
Expand Down Expand Up @@ -67,6 +70,12 @@ class MediaCategories:
# These sets are used either individually or together to form the final sets
# for the MediaCategory(s).
# These sets may be combined and are NOT 1:1 with the final categories.
_ADOBE_PHOTOSHOP_SET: set[str] = {
".pdd",
".psb",
".psd",
}
_AFFINITY_PHOTO_SET: set[str] = {".afphoto"}
_ARCHIVE_SET: set[str] = {
".7z",
".gz",
Expand All @@ -76,6 +85,10 @@ class MediaCategories:
".tgz",
".zip",
}
_AUDIO_MIDI_SET: set[str] = {
".mid",
".midi",
}
_AUDIO_SET: set[str] = {
".aac",
".aif",
Expand Down Expand Up @@ -182,6 +195,7 @@ class MediaCategories:
".jpg_large",
".jpg",
".jpg2",
".jxl",
".png",
".psb",
".psd",
Expand All @@ -192,11 +206,17 @@ class MediaCategories:
_INSTALLER_SET: set[str] = {".appx", ".msi", ".msix"}
_MATERIAL_SET: set[str] = {".mtl"}
_MODEL_SET: set[str] = {".3ds", ".fbx", ".obj", ".stl"}
_PACKAGE_SET: set[str] = {".pkg"}
_PHOTOSHOP_SET: set[str] = {
".pdd",
".psb",
".psd",
_PACKAGE_SET: set[str] = {
".aab",
".akp",
".apk",
".apkm",
".apks",
".pkg",
".xapk",
}
_PDF_SET: set[str] = {
".pdf",
}
_PLAINTEXT_SET: set[str] = {
".bat",
Expand Down Expand Up @@ -247,14 +267,29 @@ class MediaCategories:
".wmv",
}

ADOBE_PHOTOSHOP_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.ADOBE_PHOTOSHOP,
extensions=_ADOBE_PHOTOSHOP_SET,
is_iana=False,
)
AFFINITY_PHOTO_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.AFFINITY_PHOTO,
extensions=_AFFINITY_PHOTO_SET,
is_iana=False,
)
ARCHIVE_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.ARCHIVE,
extensions=_ARCHIVE_SET,
is_iana=False,
)
AUDIO_MIDI_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.AUDIO_MIDI,
extensions=_AUDIO_MIDI_SET,
is_iana=False,
)
AUDIO_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.AUDIO,
extensions=_AUDIO_SET,
extensions=_AUDIO_SET | _AUDIO_MIDI_SET,
is_iana=True,
)
BLENDER_TYPES: MediaCategory = MediaCategory(
Expand Down Expand Up @@ -317,9 +352,9 @@ class MediaCategories:
extensions=_PACKAGE_SET,
is_iana=False,
)
PHOTOSHOP_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.PHOTOSHOP,
extensions=_PHOTOSHOP_SET,
PDF_TYPES: MediaCategory = MediaCategory(
media_type=MediaType.PDF,
extensions=_PDF_SET,
is_iana=False,
)
PLAINTEXT_TYPES: MediaCategory = MediaCategory(
Expand Down Expand Up @@ -359,7 +394,10 @@ class MediaCategories:
)

ALL_CATEGORIES: list[MediaCategory] = [
ADOBE_PHOTOSHOP_TYPES,
AFFINITY_PHOTO_TYPES,
ARCHIVE_TYPES,
AUDIO_MIDI_TYPES,
AUDIO_TYPES,
BLENDER_TYPES,
DATABASE_TYPES,
Expand All @@ -373,7 +411,7 @@ class MediaCategories:
MATERIAL_TYPES,
MODEL_TYPES,
PACKAGE_TYPES,
PHOTOSHOP_TYPES,
PDF_TYPES,
PLAINTEXT_TYPES,
PRESENTATION_TYPES,
PROGRAM_TYPES,
Expand Down
55 changes: 50 additions & 5 deletions tagstudio/src/core/palette.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class ColorType(int, Enum):
DARK_ACCENT = 4


_TAG_COLORS = {
_TAG_COLORS: dict = {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are other dict like mapping structures that might be more suitable. like defaultdict.

This can solve the keyerror solution from get_tag_color

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defaultdict seems like it's well-suited for this, and would indeed replace the need for the try/except block in get_tag_color. However, since #332 is doing some refactor work in palette.py, I feel it would be best to wait until that one is merged to consider switching to defaultdict.

Custom user-defined tag colors is also a feature I've been wanting to add in the near future (post-#332) that would see this getting shaken up once more. Still, it's good to be aware of defaultdict now!

"": {
ColorType.PRIMARY: "#1e1e1e",
ColorType.TEXT: ColorType.LIGHT_ACCENT,
Expand Down Expand Up @@ -277,13 +277,58 @@ class ColorType(int, Enum):
},
}

_UI_COLORS: dict = {
"": {
ColorType.PRIMARY: "#333333",
ColorType.BORDER: "#555555",
ColorType.LIGHT_ACCENT: "#FFFFFF",
ColorType.DARK_ACCENT: "#1e1e1e",
},
"green": {
ColorType.PRIMARY: "#28bb48",
ColorType.BORDER: "#43c568",
ColorType.LIGHT_ACCENT: "#DDFFCC",
ColorType.DARK_ACCENT: "#0d3828",
},
"purple": {
ColorType.PRIMARY: "#C76FF3",
ColorType.BORDER: "#c364f2",
ColorType.LIGHT_ACCENT: "#EFD4FB",
ColorType.DARK_ACCENT: "#3E1555",
},
"red": {
ColorType.PRIMARY: "#e22c3c",
ColorType.BORDER: "#e54252",
ColorType.LIGHT_ACCENT: "#f39caa",
ColorType.DARK_ACCENT: "#440d12",
},
"theme_dark": {
ColorType.PRIMARY: "#333333",
ColorType.BORDER: "#555555",
ColorType.LIGHT_ACCENT: "#FFFFFF",
ColorType.DARK_ACCENT: "#1e1e1e",
},
"theme_light": {
ColorType.PRIMARY: "#FFFFFF",
ColorType.BORDER: "#333333",
ColorType.LIGHT_ACCENT: "#999999",
ColorType.DARK_ACCENT: "#888888",
},
}


def get_tag_color(type, color):
def get_tag_color(color_type, color):
color = color.lower()
try:
if type == ColorType.TEXT:
return get_tag_color(_TAG_COLORS[color][type], color)
if color_type == ColorType.TEXT:
return get_tag_color(_TAG_COLORS[color][color_type], color)
else:
return _TAG_COLORS[color][type]
return _TAG_COLORS[color][color_type]
except KeyError:
return "#FF00FF"


def get_ui_color(color_type: ColorType, color: str):
"""Returns a hex value given a color name and ColorType."""
color = color.lower()
return _UI_COLORS.get(color).get(color_type)
13 changes: 9 additions & 4 deletions tagstudio/src/qt/helpers/color_overlay.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,28 @@

# TODO: Consolidate the built-in QT theme values with the values
# here, in enums.py, and in palette.py.
_THEME_DARK_FG: str = "#FFFFFF55"
_THEME_DARK_FG: str = "#FFFFFF77"
_THEME_LIGHT_FG: str = "#000000DD"
_THEME_DARK_BG: str = "#000000DD"
_THEME_LIGHT_BG: str = "#FFFFFF55"


def theme_fg_overlay(image: Image.Image) -> Image.Image:
def theme_fg_overlay(image: Image.Image, use_alpha: bool = True) -> Image.Image:
"""
Overlay the foreground theme color onto an image.

Args:
image (Image): The PIL Image object to apply an overlay to.
"""
dark_fg: str = _THEME_DARK_FG[:-2] if not use_alpha else _THEME_DARK_FG
light_fg: str = _THEME_LIGHT_FG[:-2] if not use_alpha else _THEME_LIGHT_FG

overlay_color = (
_THEME_DARK_FG
dark_fg
if QGuiApplication.styleHints().colorScheme() is Qt.ColorScheme.Dark
else _THEME_LIGHT_FG
else light_fg
)

im = Image.new(mode="RGBA", size=image.size, color=overlay_color)
return _apply_overlay(image, im)

Expand Down
29 changes: 29 additions & 0 deletions tagstudio/src/qt/helpers/file_tester.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright (C) 2024 Travis Abendshien (CyanVoxel).
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio


import ffmpeg
from pathlib import Path


def is_readable_video(filepath: Path | str):
"""Test if a video is in a readable format. Examples of unreadable videos
include files with undetermined codecs and DRM-protected content.

Args:
filepath (Path | str):
"""
try:
probe = ffmpeg.probe(Path(filepath))
for stream in probe["streams"]:
# DRM check
if stream.get("codec_tag_string") in [
"drma",
"drms",
"drmi",
]:
return False
except ffmpeg.Error:
return False
Comment on lines +27 to +28
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this an exception that is also raised if ffmpeg is not installed on the system? or otherwise not available via PATH ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a great question - If so, it wasn't my intention to catch that with this method

return True
39 changes: 14 additions & 25 deletions tagstudio/src/qt/helpers/gradient.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,14 @@
# Licensed under the GPL-3.0 License.
# Created for TagStudio: https://github.com/CyanVoxel/TagStudio

from PIL import Image, ImageEnhance, ImageChops
from PIL import Image


def four_corner_gradient_background(
image: Image.Image, adj_size, mask, hl
def four_corner_gradient(
image: Image.Image, size: tuple[int, int], mask: Image.Image
) -> Image.Image:
if image.size != (adj_size, adj_size):
# Old 1 color method.
# bg_col = image.copy().resize((1, 1)).getpixel((0,0))
# bg = Image.new(mode='RGB',size=(adj_size,adj_size),color=bg_col)
# bg.thumbnail((1, 1))
# bg = bg.resize((adj_size,adj_size), resample=Image.Resampling.NEAREST)

# Small gradient background. Looks decent, and is only a one-liner.
# bg = image.copy().resize((2, 2), resample=Image.Resampling.BILINEAR).resize((adj_size,adj_size),resample=Image.Resampling.BILINEAR)

if image.size != size:
# Four-Corner Gradient Background.
# Not exactly a one-liner, but it's (subjectively) really cool.
tl = image.getpixel((0, 0))
tr = image.getpixel(((image.size[0] - 1), 0))
bl = image.getpixel((0, (image.size[1] - 1)))
Expand All @@ -29,26 +19,25 @@ def four_corner_gradient_background(
bg.paste(tr, (1, 0, 2, 2))
bg.paste(bl, (0, 1, 2, 2))
bg.paste(br, (1, 1, 2, 2))
bg = bg.resize((adj_size, adj_size), resample=Image.Resampling.BICUBIC)

bg = bg.resize(size, resample=Image.Resampling.BICUBIC)
bg.paste(
image,
box=(
(adj_size - image.size[0]) // 2,
(adj_size - image.size[1]) // 2,
(size[0] - image.size[0]) // 2,
(size[1] - image.size[1]) // 2,
),
)

bg.putalpha(mask)
final = bg
final = Image.new("RGBA", bg.size, (0, 0, 0, 0))
final.paste(bg, mask=mask.getchannel(0))

else:
image.putalpha(mask)
final = image
final = Image.new("RGBA", size, (0, 0, 0, 0))
final.paste(image, mask=mask.getchannel(0))

if final.mode != "RGBA":
final = final.convert("RGBA")

hl_soft = hl.copy()
hl_soft.putalpha(ImageEnhance.Brightness(hl.getchannel(3)).enhance(0.5))
final.paste(ImageChops.soft_light(final, hl_soft), mask=hl_soft.getchannel(3))
return final


Expand Down
Loading