fix: Windows 图标支持多分辨率 RGBA 圆角,修复 ICO 只有 16x16 单帧的问题
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -4,6 +4,8 @@
|
||||
from PIL import Image, ImageDraw
|
||||
import subprocess
|
||||
import os
|
||||
import struct
|
||||
import io
|
||||
|
||||
# 配置
|
||||
LOGO_PATH = "../../public/assets/logo.png"
|
||||
@@ -33,7 +35,8 @@ def generate_icon(size):
|
||||
# 加载并缩放 logo
|
||||
logo = Image.open(LOGO_PATH).convert("RGBA")
|
||||
logo_size = int(min(size) * LOGO_SCALE)
|
||||
logo.thumbnail((logo_size, logo_size), Image.LANCZOS)
|
||||
# 使用 resize 确保精确尺寸,避免 thumbnail 导致的尺寸偏差
|
||||
logo = logo.resize((logo_size, logo_size), Image.LANCZOS)
|
||||
|
||||
# 居中绘制 logo
|
||||
x = (size[0] - logo.width) // 2
|
||||
@@ -43,6 +46,36 @@ def generate_icon(size):
|
||||
return bg
|
||||
|
||||
|
||||
def save_ico(images, sizes, filepath):
|
||||
"""手动构建多帧 ICO 文件(PIL 原生 save 不支持 append_images for ICO)"""
|
||||
num_images = len(images)
|
||||
# ICONDIR: Reserved(2) + Type(2) + Count(2)
|
||||
header = struct.pack("<HHH", 0, 1, num_images)
|
||||
|
||||
offset = 6 + 16 * num_images
|
||||
entries = []
|
||||
data = b""
|
||||
|
||||
for img, size in zip(images, sizes):
|
||||
buf = io.BytesIO()
|
||||
img.save(buf, format="PNG")
|
||||
png_bytes = buf.getvalue()
|
||||
|
||||
w = size if size < 256 else 0
|
||||
h = size if size < 256 else 0
|
||||
# ICONDIRENTRY: Width(1) + Height(1) + Colors(1) + Reserved(1) + Planes(2) + BPP(2) + Size(4) + Offset(4)
|
||||
entry = struct.pack("<BBBBHHII", w, h, 0, 0, 1, 32, len(png_bytes), offset)
|
||||
entries.append(entry)
|
||||
data += png_bytes
|
||||
offset += len(png_bytes)
|
||||
|
||||
with open(filepath, "wb") as f:
|
||||
f.write(header)
|
||||
for e in entries:
|
||||
f.write(e)
|
||||
f.write(data)
|
||||
|
||||
|
||||
def main():
|
||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
@@ -82,24 +115,10 @@ def main():
|
||||
)
|
||||
print("Generated icon.icns")
|
||||
|
||||
# 生成 ICO(Windows 需要多分辨率)
|
||||
# 生成 ICO(Windows 需要多分辨率,保留 RGBA 透明通道实现圆角效果)
|
||||
ico_sizes = [16, 32, 48, 64, 128, 256]
|
||||
ico_images = []
|
||||
for s in ico_sizes:
|
||||
img = generate_icon((s, s))
|
||||
# ICO 需要 RGB 模式
|
||||
if img.mode == "RGBA":
|
||||
rgb = Image.new("RGB", img.size, (255, 255, 255))
|
||||
rgb.paste(img, mask=img.split()[3])
|
||||
ico_images.append(rgb)
|
||||
else:
|
||||
ico_images.append(img)
|
||||
|
||||
ico_images[0].save(
|
||||
f"{OUTPUT_DIR}/icon.ico",
|
||||
format="ICO",
|
||||
sizes=[(s, s) for s in ico_sizes],
|
||||
)
|
||||
ico_images = [generate_icon((s, s)) for s in ico_sizes]
|
||||
save_ico(ico_images, ico_sizes, f"{OUTPUT_DIR}/icon.ico")
|
||||
print("Generated icon.ico")
|
||||
|
||||
# 清理临时目录
|
||||
|
||||
|
Before Width: | Height: | Size: 379 B After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 27 KiB |