性能优化
上一篇讲了怎么把压缩字节快速解成像素。这篇接着讲:解出来的像素,怎么做到一张几亿像素的大图也能流畅浏览——拖动、缩放都跟手不卡。
一张图能有多大
手机随手拍是 1200 万像素,单反 2400 万,而一张卫星图能到 18641 × 18641 ≈ 3.5 亿像素。如果老老实实把它全部解码进内存:
1.4 GB 内存、几秒等待——只为显示一张缩到屏幕里、肉眼看不清每个像素的图。绝大多数像素根本没机会被看到。 果核看图的思路就是:只在需要的清晰度、需要的区域上花力气。这靠三件事配合:金字塔、瓦块、按需加载。
金字塔:按需要的清晰度解码
把原图反复缩小一半,存成一摞从大到小的层——这就是图像金字塔。最底层是原始尺寸,往上每层边长减半,直到缩进一张 256×256 的小图。一张 18641² 的图大约 8 层。
打开图片"适应窗口"时,整张图缩进屏幕,根本不需要 18641² 的原始数据——直接拿某一个中间层来显示,又快又省。等你放大去看局部细节,再切到更靠下、更清晰的层。始终用刚好够清晰的那一层,这就是金字塔的意义。多出来的层因为每层只有上一层的四分之一大,全部加起来也只比原图多三分之一,代价很小。
瓦块:只解看得见的那部分
光有金字塔还不够——放大看细节时用的是底层大图,可它还是几亿像素。所以每一层都再切成 256×256 的小方块(瓦块),显示时只处理落在屏幕里的那些。
一个 4K 视口铺满,也就约 135 个瓦块的工作量——和"全图几亿像素"完全不是一个量级。瓦块还能并行解:把这一百多块分给多个 CPU 核同时处理,落地更快。
按需加载:拖到哪,解到哪
把金字塔和瓦块合起来,就有了一套"视口驱动"的加载机制。每次缩放或平移,画布会算出当前视口覆盖了哪一层、哪些瓦块,把还没解的那几块丢给后台线程去解,解好一块就贴上一块。
解码全在后台线程做,不卡住界面;已经解过的瓦块缓存住,拖回去不用重解。于是无论图多大,每一刻真正在解的,永远只是屏幕这一小片。
先看见,再变清晰
还有最后一步体验优化。切到一张大图的瞬间,可见瓦块还没解完,画布会先铺一张整图的低清预览兜底——让你立刻看到完整画面,然后清晰的瓦块一块块盖上来,清晰度只增不减。
这里有个容易翻车的细节:预览图和瓦块是两层独立绘制,如果切换时机没处理好,会出现"清晰预览 → 闪一下更糊 → 再变清晰"的倒退。我们专门保证了这条单调变清晰的路径,整个过程不会出现回退闪烁。
把这套拼起来
- 金字塔决定"用多清晰的数据"——按显示缩放选层,不浪费在看不清的细节上;
- 瓦块决定"解多大范围"——只动屏幕里的那一小片;
- 按需加载 + 后台并行决定"什么时候解"——拖到哪解到哪,不卡界面;
- 低清预览兜底决定"先看到什么"——立刻有画面,再逐步清晰。
四者合起来,内存和时间就只花在你真正看得见的像素上。这就是几亿像素的大图也能随手拖动、缩放都跟手不卡的原因。