feat(packaging): 桌面图标统一品牌渐变 — generate_icon.py 标记色改 #A78BFA→#7C3AED(影线 70%)并重新生成 ico/icns, tickflow-logo.svg 同步

This commit is contained in:
shy3130
2026-09-28 18:17:30 +08:00
parent 18cc8f27b2
commit b410f1530c
4 changed files with 58 additions and 21 deletions
+47 -17
View File
@@ -1,8 +1,8 @@
"""生成应用图标 — 白色 squircle 背景 + 紫色 Logo。
"""生成应用图标 — 白色 squircle 背景 + 品牌 gradient Logo。
设计 (与 frontend Logo.tsx / favicon.svg 一致):
设计 (与 frontend favicon.svg / brand/logo.svg 一致):
背景: 白色 squircle (macOS Big Sur+ 要求不透明 squircle 背景, 用白色填充)
内容: 紫色 #5B21B6 方括号 + K线 (上影短/下影长, bullish 站稳)
内容: 品牌渐变 (#A78BFA→#7C3AED, 亮端朝上) 方括号 + K线 (上影短/下影长, bullish 站稳)
蜡烛几何 (32x32 viewBox):
wick: y=7 ~ y=25
@@ -19,17 +19,35 @@ from __future__ import annotations
from pathlib import Path
from PIL import Image, ImageDraw
from PIL import Image, ImageChops, ImageDraw
OUT_ICO = Path(__file__).parent / "icon.ico"
OUT_ICNS = Path(__file__).parent / "icon.icns"
# 背景: 白色 squircle (不透明, macOS 规范)
BG = (255, 255, 255, 255)
# logo 线条色: 紫色 #5B21B6 (与 Logo.tsx / favicon.svg 一致)
LOGO = (91, 33, 182, 255) # #5B21B6
# wick 影线: 同色不透明 (半透明在小尺寸会糊掉)
LOGO_WICK = (91, 33, 182, 255)
# logo 线条色: 品牌渐变 #A78BFA(亮端,左上) → #7C3AED(品牌紫,右下), 与 favicon.svg/brand/logo.svg 一致
GRAD_A = (167, 139, 250) # #A78BFA
GRAD_B = (124, 58, 237) # #7C3AED
# wick 影线: 70% 不透明 (与 favicon.svg 一致; 16px 最小档经超采样仍可读)
WICK_ALPHA = 0.7
def _grad_image(size_px: int) -> Image.Image:
"""32-viewBox 对角渐变 (4,3)→(28,29), 64px 生成后放大 (线性渐变缩放无损)。"""
small = 64
ax, ay, bx, by = 4 / 32, 3 / 32, 28 / 32, 29 / 32
dx, dy = bx - ax, by - ay
len2 = dx * dx + dy * dy
img = Image.new("RGB", (small, small))
px = img.load()
for y in range(small):
vy = (y + 0.5) / small
for x in range(small):
vx = (x + 0.5) / small
t = max(0.0, min(1.0, ((vx - ax) * dx + (vy - ay) * dy) / len2))
px[x, y] = tuple(round(a + (b - a) * t) for a, b in zip(GRAD_A, GRAD_B))
return img.resize((size_px, size_px), Image.BILINEAR)
def _draw(size: int, sw_b: float, sw_w: float, body_w: float) -> Image.Image:
@@ -49,24 +67,36 @@ def _draw(size: int, sw_b: float, sw_w: float, body_w: float) -> Image.Image:
swb = max(1, int(round(sw_b * factor)))
sww = max(1, int(round(sw_w * factor)))
# 标记掩膜: 渐变经 alpha 通道贴入, 线条几何与旧版完全一致
mark = Image.new("L", (s, s), 0)
dm = ImageDraw.Draw(mark)
# 方括号 [ ]
for pts in [[(10, 4), (4, 4), (4, 28), (10, 28)], [(22, 4), (28, 4), (28, 28), (22, 28)]]:
scaled = [(p(x), p(y)) for x, y in pts]
for i in range(len(scaled) - 1):
d.line([scaled[i], scaled[i + 1]], fill=LOGO, width=swb, joint="curve")
# wick 影线 (上短下长)
d.line([(p(16), p(7)), (p(16), p(25))], fill=LOGO_WICK, width=sww)
wcap = sww // 2 + 1
for cy in [7, 25]:
d.ellipse([p(16) - wcap, p(cy) - wcap, p(16) + wcap, p(cy) + wcap], fill=LOGO_WICK)
dm.line([scaled[i], scaled[i + 1]], fill=255, width=swb, joint="curve")
# body 实体 (偏上 → 上影短下影长)
d.rounded_rectangle(
dm.rounded_rectangle(
[p(16 - body_w / 2), p(9), p(16 + body_w / 2), p(19)],
radius=p(0.5), fill=LOGO,
radius=p(0.5), fill=255,
)
# wick 影线 (上短下长, 70% 不透明)
wick = Image.new("L", (s, s), 0)
dw = ImageDraw.Draw(wick)
dw.line([(p(16), p(7)), (p(16), p(25))], fill=255, width=sww)
wcap = sww // 2 + 1
for cy in [7, 25]:
dw.ellipse([p(16) - wcap, p(cy) - wcap, p(16) + wcap, p(cy) + wcap], fill=255)
# 渐变层: alpha = mark + WICK_ALPHA * wick
alpha = ImageChops.add(mark, wick.point(lambda v: round(v * WICK_ALPHA)))
layer = _grad_image(s).convert("RGBA")
layer.putalpha(alpha)
img.alpha_composite(layer)
return img.resize((size, size), Image.LANCZOS)
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 16 KiB

+11 -4
View File
@@ -1,10 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="512" height="512" fill="none" role="img" aria-label="Tick Stock Panel">
<!-- 打包用 logo — 方案 0 品牌渐变版 (#A78BFA→#7C3AED), 与 favicon.svg / brand/logo.svg 一致 -->
<defs>
<linearGradient id="tsp-pkg" x1="4" y1="3" x2="28" y2="29" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#A78BFA"/>
<stop offset="1" stop-color="#7C3AED"/>
</linearGradient>
</defs>
<!-- 左方括号 -->
<path d="M10 4 L4 4 L4 28 L10 28" stroke="#8B5CF6" stroke-width="2" stroke-linejoin="miter" stroke-linecap="butt"/>
<path d="M10 4 L4 4 L4 28 L10 28" stroke="url(#tsp-pkg)" stroke-width="2" stroke-linejoin="miter" stroke-linecap="butt"/>
<!-- 右方括号 -->
<path d="M22 4 L28 4 L28 28 L22 28" stroke="#8B5CF6" stroke-width="2" stroke-linejoin="miter" stroke-linecap="butt"/>
<path d="M22 4 L28 4 L28 28 L22 28" stroke="url(#tsp-pkg)" stroke-width="2" stroke-linejoin="miter" stroke-linecap="butt"/>
<!-- K 线 wick(上下影线,半透明) -->
<line x1="16" y1="7" x2="16" y2="25" stroke="#8B5CF6" stroke-width="1.5" stroke-linecap="round" stroke-opacity="0.6"/>
<line x1="16" y1="7" x2="16" y2="25" stroke="url(#tsp-pkg)" stroke-width="1.5" stroke-linecap="round" stroke-opacity="0.6"/>
<!-- K 线 body — 偏上,上影短/下影长, bullish 站稳感 -->
<rect x="13" y="9" width="6" height="10" fill="#8B5CF6" rx="0.5"/>
<rect x="13" y="9" width="6" height="10" fill="url(#tsp-pkg)" rx="0.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 740 B

After

Width:  |  Height:  |  Size: 1.1 KiB