How I met this API
这两天在看 hammerspoon,先照葫芦画瓢实现了 window 控制功能,然后想着网上找找看有没有什么有意思的脚本,就翻到了这个 Hammerspoon replacement for Caffeine,作者不满于 Caffeine 官方 app 的图标在 Retina 屏幕上的效果,于是用 hammerspoon 实现了一个图标切换功能。如果再配合上 hs.application
API,实现点击开关 app 功能,就完美了。
但是这个脚本里的两段看似乱码的东西很有意思:
ampOnIcon = [[ASCII:
.....1a..........AC..........E
..............................
......4.......................
1..........aA..........CE.....
e.2......4.3...........h......
..............................
..............................
.......................h......
e.2......6.3..........t..q....
5..........c..........s.......
......6..................q....
......................s..t....
.....5c.......................
]]
ampOffIcon = [[ASCII:
.....1a.....x....AC.y.......zE
..............................
......4.......................
1..........aA..........CE.....
e.2......4.3...........h......
..............................
..............................
.......................h......
e.2......6.3..........t..q....
5..........c..........s.......
......6..................q....
......................s..t....
...x.5c....y.......z..........
]]
完整的脚本中,这两个变量被 setIcon
调用,最终作为图标显示到菜单栏上。代码如下 ⬇️
function setCaffeineDisplay(state)
if state then
caffeine:setIcon(ampOnIcon)
else
caffeine:setIcon(ampOffIcon)
end
end
桥豆麻袋!虽然我没有写过很多 macOS app,但是不要骗我。Apple 为了保证 macOS 应用图标的显示效果,在 Xcode 中设置了一套完整的尺寸参照,需要开发者上传所有尺寸的 icon,用过的都知道。
而这段乱码又是什么?
带着疑问的小脑袋开始搜索,然而搜索结果属实不尽人意:
完全没有有用的信息,只能换一个思路了,去看文档!
很快就翻到了 hs.menubar:setIcon,setIcon
这个函数接受两个参数,我们只用到了第一个 imageData:
imageData - This can one of the following:
- An hs.image object
- A string containing a path to an image file
- A string beginning with ASCII: which signifies that the rest of the string is interpreted as a special form of ASCII diagram, which will be rendered to an image and used as the icon. See the notes below for information about the special format of ASCII diagram.
- nil, indicating that the current image is to be removed
第三种解释:一个 ASCII 开头的字符串,表示一个 ASCII diagram。虽然后面注明了 “See the notes below for information about the special format of ASCII diagram“,但实际上文档里并没有找到这一块儿。
于是继续搜,ASCII diagram、a special form of ASCII diagram、ASCII draw icon......各种关键词都搜一遍,还是没找到什么有用的信息。就在要放弃的时候,突然想到:为什么要始终从 Hammerspoon 侧找?Hammerspoon 也只是通过 Lua 调用 macOS 系统命令,从 macOS 侧试一下呢?
于是继续搜,NSStatusItem ASCII icon,果然!第二条就是 hammerspoon 的源代码:
于是点进去看,结果竟然真的找到了 “See the notes below for information about the special format of ASCII diagram“ 里提到的 notes:
/// * To use the ASCII diagram image support, see http://cocoamine.net/blog/2015/03/20/replacing-photoshop-with-nsstring/ and be sure to preface your ASCII diagram with the special string `ASCII:
循着链接继续往下搜,跳到 Replacing Photoshop With NSString,原来是作者为了减少使用 code 画图标带来的体积开销,自定义了一套规则,写了一个绘图库 ASCIImage
,开源在了 Github 上。而 Hammerspoon 则是借助这个库,实现了使用 ASCII diagram 绘制图标的功能。
规则
ASCIImage 库设立的规则很简单:
- 图片以一个字符串数组来定义,每一个字符串代表一行
- 字符串数组的每一个元素在去除空格后,应该长度相等
- 1-9,A-Z,a-z 中除了 o(小写) 以外的字符构成形状,其余字符可以作为填充,但是在计算形状时会被忽略。
为了绘制多种图形,ASCIImage
还设置了一些形状规则:
- 连续的字符会被绘制成多边形
- 单个字符会被绘制成点
- 当一个字符被使用正好两次时,这两个点会组成一条线
- 当一个字符被使用多次时,这些点会被绘制成一个椭圆
完整的规则见:ASCIImage Bezier Paths。
根据上面的规则,再来看上面的乱码就很简单了:
ampOnIcon = [[ASCII:
.....1a..........AC..........E
..............................
......4.......................
1..........aA..........CE.....
e.2......4.3...........h......
..............................
..............................
.......................h......
e.2......6.3..........t..q....
5..........c..........s.......
......6..................q....
......................s..t....
.....5c.......................
]]
按照步骤:
1-6、A、C、E、a、c、e、h、q、s、t 均被使用了两次,所以这是一个完全由线组成的图,呈现出的效果是:
当我们想画一个六边形的时候,也很简(jian)单(lou):
hexagon = [[ASCII:
. . . . 8 1 . 1 2 . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. 8 . . . . . . . . . 2 .
. 7 . . . . . . . . . 3 .
. . . . . . . . . . . . .
. 7 . . . . . . . . . 3 .
. 6 . . . . . . . . . 4 .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . 6 5 . 5 4 . . . .
]]
总结
不得不佩服,这个创意真的是太棒了。虽然在前端看来好像没什么用,反而加大开发者负担,但是对于通过 app store 分发的原生应用来说,体积是非常敏感的一个指标,而通常占用一个 app 体积最大份额的往往就是图片,有了这个 ASCIImage
库之后,许多简单的图片就可以被转换成字符串,大大减少占用体积。而对于开发者来说,则又多了一项课外摸鱼的技能 😏。
参考
- Replacing photoshop with nsstring
- Hammerspoon – macOS automation with Lua
- https://codegolf.stackexchange.com/questions/128104/draw-hyperneutrinos-benzene-hegaxon-icon-in-ascii
- https://github.com/heptal/dotfiles/blob/master/roles/hammerspoon/files/asciicons.lua
- https://github.com/qqnluaq/asciicon/blob/master/ascii-icon.js
- https://github.com/olizilla/asciicon/blob/master/index.js