d7fa20a890
- 更新 .gitignore 排除私钥和 IDE 配置 - 重构前端样式系统(新增 reset.css/animations.css/components/) - 更新应用图标资源(多种尺寸) - 调整 FFmpeg 命令模块 - 更新部署脚本和图标生成脚本 - 新增数据库迁移脚本 - 添加签名公钥文件
218 lines
6.8 KiB
Python
218 lines
6.8 KiB
Python
#!/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()
|