填色 App 是怎么保存进度的?
直觉的答案是把图存下来。正确的答案是每个区块存一个字节,然后把图重新画出来——这个区别决定了保存要花多少钱、以及它能扛住什么。
保存一张填了一半的画,最直觉的做法是把这张画存下来。这个做法几乎每一处都不太对。
直觉做法错在哪
直觉做法是:把当前画面渲染成一张位图,写进磁盘,下次打开再读回来。它的问题不是慢,是这张位图**丢掉了信息**。
位图只记录了每个像素最终是什么颜色。它不知道哪一块是你填的、哪一块是原本的线稿;不知道某块灰是「填了 3 号色」还是「还没填,底色就是灰」。于是一旦你想做撤销、想统计进度、想在换了配色之后重新上色,全都做不到——信息已经在渲染那一步被压掉了。
第二个问题是尺寸。一张高分辨率的位图动辄几兆,而你每点一下都得重存一次。第三个问题更麻烦:这张位图是**过期品**。如果哪天线稿本身修了个小 bug,旧存档还是老样子,两边对不上。
该存的其实非常小
换个角度想:一张填色图的全部状态,其实就是「每个区块现在是什么状态」。区块是有限的,通常几百到几千个;每个区块的状态也就是「空着」或者「填了几号色」。
一个字节能表示 256 种取值,用来记一个区块的状态绰绰有余。于是两千个区块的完整进度,就是两千字节——两个 KB,比一张缩略图还小。
存得小只是顺带的好处。真正的好处是这份数据**没有丢信息**:它知道哪块填了、填的是几号,因此撤销、进度百分比、换配色重绘、跨设备恢复,全都从同一份数据里能算出来。
恢复的时候发生了什么
打开一张没填完的图,App 做的事是:读线稿,读那份几 KB 的状态表,然后照着状态表把对应区块重新上色。这跟你第一次打开它时做的事完全一样,只是初始状态不是全空。
这么做还有个不太起眼的性质:**原始画稿从来不会被写**。你的进度是一份独立的小文件,画稿始终是只读的。这意味着进度坏掉最多损失这一张的填色记录,不会把图本身弄脏;也意味着同一张图可以有多份互不干扰的进度。
什么时候写
每点一下就写一次磁盘,次数太多;只在退出时写,又会在 App 被系统回收的时候丢掉一段。常见的折中是两条一起用:改动之后延迟一会儿再合并写一次,同时在 App 转入后台的那一刻强制写一次。
后一条尤其重要。手机上 App 很少是被「关掉」的,多数时候是被切走之后在后台被系统结束。那一刻能不能可靠地落盘,直接决定了用户会不会遇到「我明明填了半天」。
常见问题
换手机之后进度还在吗?
取决于这份进度存在哪儿。存在设备本地的,跟着设备走;跟着账号同步的,跟着账号走。这两种做法各有代价——本地的不需要账号也不需要联网,同步的换设备方便但要求你有账号。没有哪种是绝对更好,只是取舍不同。
为什么有的 App 恢复得特别慢?
多半是它在恢复的时候要重新做一遍图像处理,而不是直接读一份现成的状态表。区块划分、编号计算这些事如果每次打开都重算,慢是必然的;算一次存起来,之后就都是瞬时的。
填了一半的图会占很多空间吗?
按上面这种存法不会。一张图的进度是几百字节到几 KB,几百张也就是几兆。真正占空间的永远是画稿本身,不是进度。
拿真画布试一遍
Numbrush 是给成年人的数字填色:曼陀罗、玫瑰花窗和几何纹样,每幅十二色,窄的地方有放大镜,点错了没有任何代价。免费、离线、无广告、无账号。