aad34202f1
Co-Authored-By: Claude <noreply@anthropic.com>
284 lines
10 KiB
Python
284 lines
10 KiB
Python
"""图标设计方案预览 — 生成多个精致方案供挑选。
|
|
|
|
每个方案以 1024x1024 超清绘制 (4x 超采样抗锯齿), 输出 PNG 预览。
|
|
选定后, 用 generate_icon.py 产出最终多尺寸 .ico。
|
|
|
|
品牌色系 (从 favicon #5B21B6 延伸):
|
|
深紫 #2E1065 主紫 #5B21B6 亮紫 #7C3AED 浅紫 #A78BFA
|
|
强调金 #FBBF24 (金融上涨感)
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
import math
|
|
|
|
from PIL import Image, ImageDraw, ImageFilter
|
|
|
|
OUT = Path(__file__).parent / "designs"
|
|
OUT.mkdir(exist_ok=True)
|
|
|
|
# 品牌色
|
|
DEEP = (46, 16, 101) # #2E1065
|
|
MAIN = (91, 33, 182) # #5B21B6
|
|
BRIGHT = (124, 58, 237) # #7C3AED
|
|
LIGHT = (167, 139, 250) # #A78BFA
|
|
GOLD = (251, 191, 36) # #FBBF24
|
|
WHITE = (255, 255, 255)
|
|
|
|
|
|
def _supersample(size: int, factor: int = 4):
|
|
"""返回 (大画布, 缩放比, 最终尺寸)。绘制后缩放回 size, 抗锯齿。"""
|
|
s = size * factor
|
|
return Image.new("RGBA", (s, s), (0, 0, 0, 0)), factor, s
|
|
|
|
|
|
def _finish(img: Image.Image, size: int) -> Image.Image:
|
|
"""超采样缩小到目标尺寸。"""
|
|
return img.resize((size, size), Image.LANCZOS)
|
|
|
|
|
|
def _v_gradient(size: int, c_top, c_bot, radius_ratio: float = 0.0):
|
|
"""竖直渐变填充 (可选圆角)。返回 RGBA Image。"""
|
|
img = Image.new("RGBA", (size, size), (0, 0, 0, 0))
|
|
px = img.load()
|
|
for y in range(size):
|
|
t = y / max(1, size - 1)
|
|
r = int(c_top[0] + (c_bot[0] - c_top[0]) * t)
|
|
g = int(c_top[1] + (c_bot[1] - c_top[1]) * t)
|
|
b = int(c_top[2] + (c_bot[2] - c_top[2]) * t)
|
|
for x in range(size):
|
|
px[x, y] = (r, g, b, 255)
|
|
if radius_ratio > 0:
|
|
mask = Image.new("L", (size, size), 0)
|
|
md = ImageDraw.Draw(mask)
|
|
r = int(size * radius_ratio)
|
|
md.rounded_rectangle([0, 0, size - 1, size - 1], radius=r, fill=255)
|
|
img.putalpha(mask)
|
|
return img
|
|
|
|
|
|
def _glow(size: int, draw_fn, color, blur_ratio: float = 0.04):
|
|
"""对绘制内容做柔和光晕: 先画到独立图层, 模糊后叠加。"""
|
|
layer = Image.new("RGBA", (size, size), (0, 0, 0, 0))
|
|
d = ImageDraw.Draw(layer)
|
|
draw_fn(d)
|
|
glow = layer.filter(ImageFilter.GaussianBlur(size * blur_ratio))
|
|
return glow
|
|
|
|
|
|
# ── 方案 1: 渐变紫底 + 白色发光主体 (现代 App 风格) ──────────────
|
|
def design_1(size: int = 1024) -> Image.Image:
|
|
img, f, s = _supersample(size)
|
|
|
|
# 圆角渐变背景 (深紫 → 亮紫)
|
|
bg = _v_gradient(s, BRIGHT, DEEP, radius_ratio=0.22)
|
|
img.alpha_composite(bg)
|
|
|
|
d = ImageDraw.Draw(img)
|
|
u = s / 32 # 单位 (基于32基准)
|
|
|
|
# 白色方括号 (圆头粗线)
|
|
sw = 2.8 * u
|
|
def draw_brackets(dd):
|
|
# 左 [
|
|
dd.line([(11, 6), (5, 6)], fill=WHITE, width=int(sw), joint="curve")
|
|
dd.line([(5, 6), (5, 26)], fill=WHITE, width=int(sw))
|
|
dd.line([(5, 26), (11, 26)], fill=WHITE, width=int(sw))
|
|
# 右 ]
|
|
dd.line([(21, 6), (27, 6)], fill=WHITE, width=int(sw))
|
|
dd.line([(27, 6), (27, 26)], fill=WHITE, width=int(sw))
|
|
dd.line([(27, 26), (21, 26)], fill=WHITE, width=int(sw))
|
|
|
|
# 白色 K 线 wick
|
|
def draw_wick(dd):
|
|
dd.line([(16, 8), (16, 24)], fill=LIGHT, width=int(1.6 * u))
|
|
# 白色 K 线 body
|
|
def draw_body(dd):
|
|
dd.rounded_rectangle(
|
|
[12.5 * u, 12 * u, 19.5 * u, 21 * u],
|
|
radius=1.2 * u, fill=WHITE,
|
|
)
|
|
|
|
# 光晕层 (放大模糊)
|
|
glow = Image.new("RGBA", (s, s), (0, 0, 0, 0))
|
|
gd = ImageDraw.Draw(glow)
|
|
draw_body(gd)
|
|
glow = glow.filter(ImageFilter.GaussianBlur(s * 0.025))
|
|
# 光晕染色为亮紫
|
|
tint = Image.new("RGBA", (s, s), BRIGHT + (0,))
|
|
glow = Image.composite(tint, Image.new("RGBA", (s, s), (0, 0, 0, 0)),
|
|
glow.point(lambda a: min(255, int(a * 0.6))))
|
|
img.alpha_composite(glow)
|
|
|
|
draw_brackets(d)
|
|
draw_wick(d)
|
|
draw_body(d)
|
|
|
|
return _finish(img, size)
|
|
|
|
|
|
# ── 方案 2: 透明底 + 渐变紫发光主体 (优雅线条) ──────────────────
|
|
def design_2(size: int = 1024) -> Image.Image:
|
|
img, f, s = _supersample(size)
|
|
d = ImageDraw.Draw(img)
|
|
u = s / 32
|
|
|
|
sw = 3.0 * u
|
|
|
|
# 渐变括号: 用浅紫到亮紫。Pillow line 不支持渐变, 分段填色模拟。
|
|
def gradient_brackets(dd):
|
|
# 左括号三段, 从上(浅)到下(亮) — 简化为整体亮紫, 配光晕显层次
|
|
col = BRIGHT
|
|
# 左 [
|
|
dd.line([(11, 5), (5, 5)], fill=col, width=int(sw))
|
|
dd.line([(5, 5), (5, 27)], fill=col, width=int(sw))
|
|
dd.line([(5, 27), (11, 27)], fill=col, width=int(sw))
|
|
# 右 ]
|
|
dd.line([(21, 5), (27, 5)], fill=col, width=int(sw))
|
|
dd.line([(27, 5), (27, 27)], fill=col, width=int(sw))
|
|
dd.line([(27, 27), (21, 27)], fill=col, width=int(sw))
|
|
|
|
# 主体光晕 (亮紫大模糊)
|
|
glow_layer = Image.new("RGBA", (s, s), (0, 0, 0, 0))
|
|
gd = ImageDraw.Draw(glow_layer)
|
|
gradient_brackets(gd)
|
|
gd.rounded_rectangle([12.5 * u, 12 * u, 19.5 * u, 21 * u], radius=1.2 * u, fill=BRIGHT)
|
|
glow = glow_layer.filter(ImageFilter.GaussianBlur(s * 0.05))
|
|
img.alpha_composite(glow)
|
|
|
|
# 主体 (亮紫)
|
|
gradient_brackets(d)
|
|
d.line([(16, 8), (16, 24)], fill=LIGHT, width=int(1.8 * u))
|
|
d.rounded_rectangle([12.5 * u, 12 * u, 19.5 * u, 21 * u], radius=1.2 * u, fill=BRIGHT)
|
|
|
|
# 高光: body 顶部一道浅紫
|
|
d.rounded_rectangle([13.5 * u, 12.8 * u, 18.5 * u, 14.5 * u], radius=0.8 * u, fill=LIGHT)
|
|
|
|
return _finish(img, size)
|
|
|
|
|
|
# ── 方案 3: 深色底 + 金紫上涨 K 线柱 (金融图表感) ────────────────
|
|
def design_3(size: int = 1024) -> Image.Image:
|
|
img, f, s = _supersample(size)
|
|
|
|
# 深紫黑圆角底
|
|
bg = _v_gradient(s, (30, 27, 75), (15, 12, 41), radius_ratio=0.22)
|
|
img.alpha_composite(bg)
|
|
d = ImageDraw.Draw(img)
|
|
u = s / 32
|
|
|
|
# 三根上涨 K 线柱 (紫→亮紫→金), 从左到右升高
|
|
bars = [
|
|
# (x_center, body_top, body_bottom, wick_top, wick_bottom, color)
|
|
(10, 17, 24, 14, 26, LIGHT),
|
|
(16, 13, 20, 10, 22, BRIGHT),
|
|
(22, 8, 15, 5, 17, GOLD),
|
|
]
|
|
bw = 3.2 * u
|
|
for cx, bt, bb, wt, wb, col in bars:
|
|
# wick
|
|
d.line([(cx * u, wt * u), (cx * u, wb * u)], fill=col, width=int(1.2 * u))
|
|
# body (圆角)
|
|
d.rounded_rectangle(
|
|
[cx * u - bw / 2, bt * u, cx * u + bw / 2, bb * u],
|
|
radius=0.8 * u, fill=col,
|
|
)
|
|
|
|
# 金色柱的光晕
|
|
glow = Image.new("RGBA", (s, s), (0, 0, 0, 0))
|
|
gd = ImageDraw.Draw(glow)
|
|
gd.rounded_rectangle([22 * u - bw / 2, 8 * u, 22 * u + bw / 2, 15 * u], radius=0.8 * u, fill=GOLD)
|
|
glow = glow.filter(ImageFilter.GaussianBlur(s * 0.03))
|
|
img.alpha_composite(glow)
|
|
|
|
# 上涨趋势线 (淡, 连接柱顶)
|
|
d.line([(10 * u, 17 * u), (16 * u, 13 * u), (22 * u, 8 * u)],
|
|
fill=(255, 255, 255, 120), width=int(0.6 * u))
|
|
|
|
return _finish(img, size)
|
|
|
|
|
|
# ── 方案 4: 渐变底 + K线被方括号"框选" (品牌强调) ────────────────
|
|
def design_4(size: int = 1024) -> Image.Image:
|
|
img, f, s = _supersample(size)
|
|
|
|
# 斜向渐变背景 (浅紫→主紫), 圆角
|
|
bg = _v_gradient(s, (139, 92, 246), MAIN, radius_ratio=0.22)
|
|
img.alpha_composite(bg)
|
|
d = ImageDraw.Draw(img)
|
|
u = s / 32
|
|
|
|
# 白色粗括号 (圆角连接)
|
|
sw = 2.2 * u
|
|
def brackets(dd):
|
|
dd.rounded_rectangle(
|
|
[5 * u, 6 * u, 11 * u, 26 * u],
|
|
radius=2.0 * u, width=int(sw), outline=WHITE,
|
|
) # 左括号外形
|
|
dd.rounded_rectangle(
|
|
[21 * u, 6 * u, 27 * u, 26 * u],
|
|
radius=2.0 * u, width=int(sw), outline=WHITE,
|
|
) # 右括号外形
|
|
# 上面的画法画出的是矩形框, 改回线条式括号但圆头
|
|
def brackets2(dd):
|
|
cap = int(sw / 2)
|
|
# 左 [
|
|
dd.line([(10.5, 6), (6, 6)], fill=WHITE, width=int(sw))
|
|
dd.rounded_rectangle([6 * u - cap, 6 * u - cap, 6 * u + cap, 26 * u + cap],
|
|
radius=cap, fill=WHITE) # 竖干
|
|
dd.line([(6, 26), (10.5, 26)], fill=WHITE, width=int(sw))
|
|
# 圆角补点
|
|
for cx, cy in [(6, 6), (6, 26)]:
|
|
dd.ellipse([cx * u - cap, cy * u - cap, cx * u + cap, cy * u + cap], fill=WHITE)
|
|
# 右 ]
|
|
dd.line([(21.5, 6), (26, 6)], fill=WHITE, width=int(sw))
|
|
dd.rounded_rectangle([26 * u - cap, 6 * u - cap, 26 * u + cap, 26 * u + cap],
|
|
radius=cap, fill=WHITE)
|
|
dd.line([(26, 26), (21.5, 26)], fill=WHITE, width=int(sw))
|
|
for cx, cy in [(26, 6), (26, 26)]:
|
|
dd.ellipse([cx * u - cap, cy * u - cap, cx * u + cap, cy * u + cap], fill=WHITE)
|
|
|
|
# K 线光晕
|
|
glow = Image.new("RGBA", (s, s), (0, 0, 0, 0))
|
|
gd = ImageDraw.Draw(glow)
|
|
gd.rounded_rectangle([12.8 * u, 11.5 * u, 19.2 * u, 21.5 * u], radius=1.4 * u, fill=WHITE)
|
|
glow = glow.filter(ImageFilter.GaussianBlur(s * 0.02))
|
|
img.alpha_composite(glow)
|
|
|
|
brackets2(d)
|
|
# wick
|
|
d.line([(16, 8), (16, 24)], fill=(255, 255, 255, 200), width=int(1.5 * u))
|
|
# body (实心白)
|
|
d.rounded_rectangle([12.8 * u, 11.5 * u, 19.2 * u, 21.5 * u], radius=1.4 * u, fill=WHITE)
|
|
|
|
return _finish(img, size)
|
|
|
|
|
|
def main() -> None:
|
|
designs = [
|
|
("方案1_渐变紫底白色发光", design_1),
|
|
("方案2_透明底渐变紫线条", design_2),
|
|
("方案3_深色底金紫上涨K线", design_3),
|
|
("方案4_浅紫底框选K线", design_4),
|
|
]
|
|
# 拼一张对比图 (2x2)
|
|
cell = 512
|
|
grid = Image.new("RGBA", (cell * 2 + 60, cell * 2 + 60), (245, 245, 247, 255))
|
|
gd = ImageDraw.Draw(grid)
|
|
positions = [(20, 20), (cell + 40, 20), (20, cell + 40), (cell + 40, cell + 40)]
|
|
for (name, fn), pos in zip(designs, positions):
|
|
img = fn(cell)
|
|
# 棋盘格背景透显
|
|
grid.alpha_composite(img, pos)
|
|
# 单独存
|
|
fn(1024).save(OUT / f"{name}.png")
|
|
print(f" 生成: {name}.png")
|
|
|
|
grid.save(OUT / "对比图_2x2.png")
|
|
print(f"\n对比图: {OUT / '对比图_2x2.png'}")
|
|
print(f"单图目录: {OUT}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|