mirror of
https://github.com/shy3130/tick-stock-panel.git
synced 2026-11-02 10:26:13 +00:00
feat(packaging): 桌面图标统一品牌渐变 — generate_icon.py 标记色改 #A78BFA→#7C3AED(影线 70%)并重新生成 ico/icns, tickflow-logo.svg 同步
This commit is contained in:
+47
-17
@@ -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 |
@@ -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 |
Reference in New Issue
Block a user