Files
meijiaka-zy/scripts/generate-rounded-icon.py
T
小鱼开发 d7fa20a890 feat: 样式系统重构、图标更新、FFmpeg 模块调整及配置更新
- 更新 .gitignore 排除私钥和 IDE 配置
- 重构前端样式系统(新增 reset.css/animations.css/components/)
- 更新应用图标资源(多种尺寸)
- 调整 FFmpeg 命令模块
- 更新部署脚本和图标生成脚本
- 新增数据库迁移脚本
- 添加签名公钥文件
2026-05-21 10:45:04 +08:00

218 lines
6.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""生成圆角图标:原图(透明背景 logo)作为整体,缩放居中,圆角外透明"""
import os
from PIL import Image, ImageDraw
ICONS_DIR = "/Users/0fun/work/meijiaka-zy/tauri-app/src-tauri/icons"
SOURCE_PNG = "/Users/0fun/work/meijiaka-zy/tauri-app/public/assets/logo.png"
# macOS Big Sur 圆角比例 ≈ 22.6%
CORNER_RATIO_MACOS = 0.226
# Windows 11 风格圆角比例 ≈ 18%Fluent Design 圆角矩形)
CORNER_RATIO_WINDOWS = 0.18
# 内容占画布比例(参考腾讯视频 ≈ 80.5%)
CONTENT_RATIO = 0.805
PNG_SIZES = [
("icon.png", 512),
("128x128@2x.png", 256),
("128x128.png", 128),
("32x32.png", 32),
("64x64.png", 64),
]
SQUARE_SIZES = [
("Square310x310Logo.png", 310),
("Square284x284Logo.png", 284),
("Square150x150Logo.png", 150),
("Square142x142Logo.png", 142),
("Square107x107Logo.png", 107),
("Square89x89Logo.png", 89),
("Square71x71Logo.png", 71),
("Square44x44Logo.png", 44),
("Square30x30Logo.png", 30),
("StoreLogo.png", 50),
]
def create_rounded_rect_mask(size: int, radius: int) -> Image.Image:
"""创建圆角矩形蒙版(硬边缘)"""
mask = Image.new("L", (size, size), 0)
draw = ImageDraw.Draw(mask)
draw.rounded_rectangle((0, 0, size, size), radius=radius, fill=255)
return mask
def prepare_source(source: Image.Image, canvas_size: int = 1024) -> Image.Image:
"""将源图居中放置在正方形透明画布上,作为后续处理的统一源图"""
src_w, src_h = source.size
canvas = Image.new("RGBA", (canvas_size, canvas_size), (0, 0, 0, 0))
# 等比缩放,长边充满画布的 CONTENT_RATIO
target_size = int(canvas_size * CONTENT_RATIO)
ratio = max(target_size / src_w, target_size / src_h)
new_w = int(src_w * ratio)
new_h = int(src_h * ratio)
resized = source.resize((new_w, new_h), Image.LANCZOS)
# 居中放置
left = (canvas_size - new_w) // 2
top = (canvas_size - new_h) // 2
canvas.paste(resized, (left, top), resized)
return canvas
def compose_icon(size: int, source: Image.Image, rounded: bool = True) -> Image.Image:
"""macOS / Linux 图标:内容占画布 80.5%,大圆角"""
canvas = Image.new("RGBA", (size, size), (0, 0, 0, 0))
plate_size = int(size * CONTENT_RATIO)
plate_offset = (size - plate_size) // 2
# 源图等比缩放,短边充满 plate_size
src_w, src_h = source.size
ratio = max(plate_size / src_w, plate_size / src_h)
new_w = int(src_w * ratio)
new_h = int(src_h * ratio)
resized = source.resize((new_w, new_h), Image.LANCZOS)
# 居中裁剪到 plate_size
left = (new_w - plate_size) // 2
top = (new_h - plate_size) // 2
img = resized.crop((left, top, left + plate_size, top + plate_size))
if rounded:
# 圆角蒙版裁剪(macOS / Linux
radius = int(plate_size * CORNER_RATIO_MACOS)
mask = create_rounded_rect_mask(plate_size, radius)
canvas.paste(img, (plate_offset, plate_offset), mask)
else:
# 正方形填满
canvas.paste(img, (plate_offset, plate_offset))
return canvas
def compose_icon_windows(size: int, source: Image.Image, rounded: bool = True) -> Image.Image:
"""Windows 图标:原图填满整个画布 100%,支持轻微圆角"""
canvas = Image.new("RGBA", (size, size), (0, 0, 0, 0))
# 源图等比缩放,短边充满画布
src_w, src_h = source.size
ratio = max(size / src_w, size / src_h)
new_w = int(src_w * ratio)
new_h = int(src_h * ratio)
resized = source.resize((new_w, new_h), Image.LANCZOS)
# 居中裁剪到画布尺寸
left = (new_w - size) // 2
top = (new_h - size) // 2
img = resized.crop((left, top, left + size, top + size))
if rounded:
# Windows 11 风格轻微圆角
radius = max(1, int(size * CORNER_RATIO_WINDOWS))
mask = create_rounded_rect_mask(size, radius)
canvas.paste(img, (0, 0), mask)
else:
canvas.paste(img, (0, 0))
return canvas
def generate_icns(source: Image.Image, output_path: str):
"""生成 macOS .icns 文件"""
import tempfile
import subprocess
import shutil
sizes = [16, 32, 64, 128, 256, 512, 1024]
iconset_dir = tempfile.mkdtemp(suffix=".iconset")
for sz in sizes:
img = compose_icon(sz, source, rounded=True)
img.save(os.path.join(iconset_dir, f"icon_{sz}x{sz}.png"))
if sz <= 512:
img2x = compose_icon(sz * 2, source, rounded=True)
img2x.save(os.path.join(iconset_dir, f"icon_{sz}x{sz}@2x.png"))
subprocess.run(
["iconutil", "-c", "icns", iconset_dir, "-o", output_path],
check=True,
)
shutil.rmtree(iconset_dir)
def generate_ico(source: Image.Image, output_path: str):
"""生成 Windows .ico 文件,包含更多尺寸以支持高 DPI"""
import struct
import io
# 添加 20x20 和 40x40 以支持 Windows 高 DPI (125%, 150%)
sizes = [16, 20, 24, 32, 40, 48, 64, 128, 256]
png_datas = []
entries = []
for sz in sizes:
# Windows 图标使用轻微圆角
img = compose_icon_windows(sz, source, rounded=True)
buf = io.BytesIO()
img.save(buf, format="PNG")
data = buf.getvalue()
png_datas.append(data)
entries.append((sz, len(data)))
ico = struct.pack("<HHH", 0, 1, len(sizes))
data_offset = 6 + 16 * len(sizes)
for sz, size_bytes in entries:
width = sz if sz < 256 else 0
height = sz if sz < 256 else 0
ico += struct.pack(
"<BBBBHHII",
width, height, 0, 0, 1, 32, size_bytes, data_offset,
)
data_offset += size_bytes
for data in png_datas:
ico += data
with open(output_path, "wb") as f:
f.write(ico)
def main():
# 加载原始 logo(透明背景)
raw_source = Image.open(SOURCE_PNG).convert("RGBA")
print(f"原始 logo 尺寸: {raw_source.size}")
# 预处理:居中放置在 1024x1024 透明画布上
source = prepare_source(raw_source, canvas_size=1024)
print(f"预处理后源图尺寸: {source.size}")
# macOS / Linux PNG 图标(大圆角 + 透明边距)
for filename, size in PNG_SIZES:
path = os.path.join(ICONS_DIR, filename)
compose_icon(size, source, rounded=True).save(path)
print(f"已生成: {filename} ({size}x{size})")
# Windows Square Logo(轻微圆角 + 填满画布)
for filename, size in SQUARE_SIZES:
path = os.path.join(ICONS_DIR, filename)
compose_icon_windows(size, source, rounded=True).save(path)
print(f"已生成: {filename} ({size}x{size})")
generate_icns(source, os.path.join(ICONS_DIR, "icon.icns"))
print("已生成: icon.icns")
generate_ico(source, os.path.join(ICONS_DIR, "icon.ico"))
print("已生成: icon.ico")
print("\n全部完成!")
if __name__ == "__main__":
main()