机型覆盖 17 → 55 款,补 LICENSE 与 CHANGELOG

机型
  新增 38 款圆屏 + CIQ ≥ 4.2 的机型:fenix 7 Pro 全系、Venu 2/3S、
  Approach S50/S70、Descent G2/Mk3、D2 系列、Instinct 3 AMOLED、
  FR255S/265S/570/955 等。其中 34 款的屏宽已有现成图标资源。

  机型列表和 monkey.jungle 改由 tools/gen_devices.py 扫描本机 SDK 生成,
  筛选条件是「支持 watchFace + 圆屏 + CIQ ≥ 4.2」(Complications 要 4.2)。
  55 条资源路径不再手写。

  新增 218px 与 360px 两档图标。218px 上图标只有 14×14,设计稿里太阳与云
  之间那道 1.7 设计像素的暗缝连一个物理像素都占不到,光栅化出来是一坨没有
  结构的橙块 —— 该尺寸改用按目标像素直接摆的简化标记。

常亮模式:结论是不改
  为了压所谓的 10% 点亮率,一度把进度环也砍掉了。换成平均亮度(AMOLED 功耗
  的合理代理)重新量之后发现:普通模式 14.1%,常亮 2.9%,本来就只有 1/5;
  砍掉环只从 3.01% 降到 2.91%。0.1 个百分点换掉一个可见特征不划算,环留着。
  两种度量的差异和这个决定都写进了代码注释和 README。

其它
  - LICENSE(MIT)。只覆盖代码 —— design/ 下的设计稿版权归第三方,
    发布前需取得原作者授权或替换视觉,文件内已注明。
  - UserProfile.getProfile() 加异常保护(这个 API 家族已经坑过三次)
  - CHANGELOG.md,版本号 1.0.0 → 1.1.0

验证:55 款全部编译通过(分三批 18+18+19);.iq 上架包可正常导出。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-10 11:27:54 +08:00
parent d9d45d983b
commit 22340b09fa
24 changed files with 374 additions and 25 deletions

View File

@@ -41,8 +41,11 @@ ICONS = {
"weather": ("weather-icon", 343.55, 89.5, False),
}
# screen width -> whether the panel can blend alpha (MIP cannot)
WIDTHS = {240: False, 260: False, 280: False, 390: True, 416: True, 454: True}
# 屏宽 -> 该尺寸下的屏幕能否做 alpha 混合(MIP 屏不能)。
# 这张表和启动图标尺寸表都由 tools/gen_devices.py 扫描出的机型集合决定,
# 改完设备列表要跟着更新这里 —— 少一个尺寸就会有机型编译不过。
WIDTHS = {218: False, 240: False, 260: False, 280: False,
360: True, 390: True, 416: True, 454: True}
SS = 8 # supersampling factor
@@ -118,8 +121,8 @@ def render(paths, size, cx, cy, blend):
return img
# Launcher icon sizes the target devices ask for.
LAUNCHER = [40, 56, 60, 65, 70]
# 各机型要求的启动图标尺寸Garmin 每款表都有自己的规定值,不能用缩放糊弄)。
LAUNCHER = [38, 40, 54, 56, 60, 61, 65, 70]
def launcher(size):
@@ -160,6 +163,46 @@ def launcher(size):
return img.resize((size, size), Image.LANCZOS)
def tiny_weather(size):
"""给 16px 以下的屏幕专画的天气标记。
设计稿的天气图标是「太阳被云挡住一半」,靠一道暗缝把两者分开。那道缝在
500 画布上只有 1.7px,到 218px 屏(图标 14×14连一个物理像素都占不到
光栅化出来就是一坨没有结构的橙块。
所以这个尺寸不再走设计稿路径,改用按目标像素直接摆的简化标记:
右上一个太阳圆盘,左下一朵云,中间强制留 1 个像素的空隙。
形不如原图,但至少还认得出是「天气」。
"""
from PIL import ImageDraw
ss = 8
n = size * ss
img = Image.new("L", (n, n), 0)
d = ImageDraw.Draw(img)
u = n / 14.0 # 以 14 格为基准摆放,任何尺寸都等比
def disc(cx, cy, r, fill):
d.ellipse([cx - r, cy - r, cx + r, cy + r], fill=fill)
# 太阳 + 四道光芒(光芒画成小方块,比线段在低分辨率下更容易留下来)
disc(9.2 * u, 4.6 * u, 2.7 * u, 255)
for dx, dy in ((0, -4.4), (4.3, 0), (-3.2, -3.2), (3.2, -3.2)):
d.rectangle([9.2 * u + dx * u - 0.55 * u, 4.6 * u + dy * u - 0.55 * u,
9.2 * u + dx * u + 0.55 * u, 4.6 * u + dy * u + 0.55 * u],
fill=255)
# 先用黑色把云的轮廓“撑大”一圈,留出与太阳之间的缝,再画回本体
for grow, fill in ((1.0 * u, 0), (0.0, 255)):
disc(5.0 * u, 8.6 * u, 3.3 * u + grow, fill)
disc(8.6 * u, 9.6 * u, 2.4 * u + grow, fill)
d.rectangle([1.7 * u - grow, 8.8 * u - grow, 11.0 * u + grow, 12.2 * u],
fill=fill)
small = img.resize((size, size), Image.LANCZOS)
small = small.point(lambda v: 255 if v >= 110 else 0)
out = Image.new("RGB", (size, size), (255, 255, 255)).convert("RGBA")
out.putalpha(small)
return out
def main():
svg = open(SRC).read()
for size in LAUNCHER:
@@ -182,7 +225,11 @@ def main():
if key not in cache:
p = subpaths(svg, pid)
cache[key] = flip_chevron(p) if flip else p
img = render(cache[key], size, cx, cy, blend)
# 天气图标在极小尺寸下用专门的简化画法,日出/日落还撑得住。
if name == "weather" and size < 16:
img = tiny_weather(size)
else:
img = render(cache[key], size, cx, cy, blend)
img.save(os.path.join(outdir, "%s.png" % name))
names.append(name)
with open(os.path.join(outdir, "drawables.xml"), "w") as f: