新增: 电子相册全屏模式+开机启动+Win10兼容
This commit is contained in:
98
scripts/build_tray_icon.py
Normal file
98
scripts/build_tray_icon.py
Normal file
@@ -0,0 +1,98 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image, ImageChops
|
||||
|
||||
|
||||
ICON_SIZES = (16, 20, 24, 32, 40, 48, 64, 128, 256)
|
||||
|
||||
|
||||
def remove_green_background(image: Image.Image) -> Image.Image:
|
||||
rgba = image.convert("RGBA")
|
||||
pixels = rgba.load()
|
||||
width, height = rgba.size
|
||||
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
r, g, b, a = pixels[x, y]
|
||||
green_score = g - max(r, b)
|
||||
is_key = g > 90 and green_score > 35
|
||||
is_fringe = g > 32 and g > r * 1.35 and g > b * 1.20
|
||||
if is_key or is_fringe:
|
||||
edge = max(0, min(255, (green_score - 18) * 5))
|
||||
alpha = max(0, 255 - edge)
|
||||
if alpha < 56:
|
||||
pixels[x, y] = (0, 0, 0, 0)
|
||||
else:
|
||||
despilled_g = min(g, max(r, b))
|
||||
pixels[x, y] = (r, despilled_g, b, min(a, alpha))
|
||||
|
||||
return rgba
|
||||
|
||||
|
||||
def crop_to_alpha(image: Image.Image, padding_ratio: float) -> Image.Image:
|
||||
alpha = image.getchannel("A")
|
||||
bbox = alpha.getbbox()
|
||||
if not bbox:
|
||||
raise ValueError("source image has no visible pixels after background removal")
|
||||
|
||||
cropped = image.crop(bbox)
|
||||
side = max(cropped.size)
|
||||
padding = round(side * padding_ratio)
|
||||
canvas_side = side + padding * 2
|
||||
canvas = Image.new("RGBA", (canvas_side, canvas_side), (0, 0, 0, 0))
|
||||
canvas.alpha_composite(cropped, ((canvas_side - cropped.width) // 2, (canvas_side - cropped.height) // 2))
|
||||
return canvas
|
||||
|
||||
|
||||
def trim_transparent_padding(image: Image.Image) -> Image.Image:
|
||||
empty = Image.new("RGBA", image.size, (0, 0, 0, 0))
|
||||
bbox = ImageChops.difference(image, empty).getbbox()
|
||||
if bbox is None:
|
||||
return image
|
||||
return image.crop(bbox)
|
||||
|
||||
|
||||
def build_icons(source: Path, out_dir: Path, padding_ratio: float) -> None:
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
source_image = Image.open(source)
|
||||
transparent = remove_green_background(source_image)
|
||||
master = crop_to_alpha(transparent, padding_ratio)
|
||||
master = trim_transparent_padding(master)
|
||||
|
||||
master_path = out_dir / "tray-icon.png"
|
||||
master.save(master_path)
|
||||
|
||||
resized_images: list[Image.Image] = []
|
||||
for size in ICON_SIZES:
|
||||
resized = master.resize((size, size), Image.Resampling.LANCZOS)
|
||||
resized.save(out_dir / f"tray-icon-{size}.png")
|
||||
resized_images.append(resized)
|
||||
|
||||
ico_path = out_dir / "tray.ico"
|
||||
resized_images[-1].save(
|
||||
ico_path,
|
||||
format="ICO",
|
||||
sizes=[(size, size) for size in ICON_SIZES],
|
||||
append_images=resized_images[:-1],
|
||||
)
|
||||
|
||||
print(f"wrote {master_path}")
|
||||
print(f"wrote {ico_path}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Build u-desktop tray icon assets from a generated source image.")
|
||||
parser.add_argument("--source", type=Path, required=True, help="Generated source PNG on a green chroma-key background.")
|
||||
parser.add_argument("--out-dir", type=Path, default=Path("assets/icons"), help="Directory for PNG sizes and tray.ico.")
|
||||
parser.add_argument("--padding", type=float, default=0.04, help="Transparent padding ratio around the cropped icon.")
|
||||
args = parser.parse_args()
|
||||
|
||||
build_icons(args.source, args.out_dir, args.padding)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user