fix: Windows icons use square fill, macOS keeps rounded
- Windows .ico and SquareLogo: content fills 100% canvas, no transparency - macOS .icns and PNG: keep 80.5% rounded rectangle - Fixes white/grey square background and blur on Windows
@@ -43,13 +43,12 @@ def create_rounded_rect_mask(size: int, radius: int) -> Image.Image:
|
||||
return mask
|
||||
|
||||
|
||||
def compose_icon(size: int, source: Image.Image) -> Image.Image:
|
||||
"""原图作为整体,缩放至画布 CONTENT_RATIO,居中,裁成圆角矩形"""
|
||||
def compose_icon(size: int, source: Image.Image, rounded: bool = True) -> Image.Image:
|
||||
"""原图作为整体,缩放至画布 CONTENT_RATIO,居中"""
|
||||
canvas = Image.new("RGBA", (size, size), (0, 0, 0, 0))
|
||||
|
||||
plate_size = int(size * CONTENT_RATIO)
|
||||
plate_offset = (size - plate_size) // 2
|
||||
radius = int(plate_size * CORNER_RATIO)
|
||||
|
||||
# 原图等比缩放,短边充满 plate_size
|
||||
src_w, src_h = source.size
|
||||
@@ -63,12 +62,32 @@ def compose_icon(size: int, source: Image.Image) -> Image.Image:
|
||||
top = (new_h - plate_size) // 2
|
||||
img = resized.crop((left, top, left + plate_size, top + plate_size))
|
||||
|
||||
# 圆角蒙版裁剪
|
||||
mask = create_rounded_rect_mask(plate_size, radius)
|
||||
canvas.paste(img, (plate_offset, plate_offset), mask)
|
||||
if rounded:
|
||||
# 圆角蒙版裁剪(macOS / Linux)
|
||||
radius = int(plate_size * CORNER_RATIO)
|
||||
mask = create_rounded_rect_mask(plate_size, radius)
|
||||
canvas.paste(img, (plate_offset, plate_offset), mask)
|
||||
else:
|
||||
# 正方形填满(Windows 专用)
|
||||
canvas.paste(img, (plate_offset, plate_offset))
|
||||
return canvas
|
||||
|
||||
|
||||
def compose_icon_windows(size: int, source: Image.Image) -> Image.Image:
|
||||
"""Windows 专用:原图填满整个画布 100%,无圆角,无透明边距"""
|
||||
# 原图等比缩放,短边充满画布
|
||||
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
|
||||
return resized.crop((left, top, left + size, top + size))
|
||||
|
||||
|
||||
def generate_icns(source: Image.Image, output_path: str):
|
||||
"""生成 macOS .icns 文件"""
|
||||
import tempfile
|
||||
@@ -79,10 +98,10 @@ def generate_icns(source: Image.Image, output_path: str):
|
||||
iconset_dir = tempfile.mkdtemp(suffix=".iconset")
|
||||
|
||||
for sz in sizes:
|
||||
img = compose_icon(sz, source)
|
||||
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)
|
||||
img2x = compose_icon(sz * 2, source, rounded=True)
|
||||
img2x.save(os.path.join(iconset_dir, f"icon_{sz}x{sz}@2x.png"))
|
||||
|
||||
subprocess.run(
|
||||
@@ -102,7 +121,8 @@ def generate_ico(source: Image.Image, output_path: str):
|
||||
entries = []
|
||||
|
||||
for sz in sizes:
|
||||
img = compose_icon(sz, source)
|
||||
# Windows .ico 填满画布,无圆角,无透明边距
|
||||
img = compose_icon_windows(sz, source)
|
||||
buf = io.BytesIO()
|
||||
img.save(buf, format="PNG")
|
||||
data = buf.getvalue()
|
||||
@@ -137,9 +157,10 @@ def main():
|
||||
compose_icon(size, source).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(size, source).save(path)
|
||||
compose_icon_windows(size, source).save(path)
|
||||
print(f"已生成: {filename} ({size}x{size})")
|
||||
|
||||
generate_icns(source, os.path.join(ICONS_DIR, "icon.icns"))
|
||||
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 997 B After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 23 KiB |