同样一张图,文件只有 3MB,解码以后内存占了几十 MB——这就是像素格式没搞对。

文章封面

做图片处理功能的时候,最开始的思路很简单:拿一张图,解码成 PixelMap,处理完再编码存出去。

跑大图的时候出问题了:4000×3000 的图,文件大小才 3MB,解码完内存直接涨了几十 MB。手机差点直接 OOM。

这时候才意识到:图片文件大小和内存占用完全是两回事。文件是压缩的,解码以后是原始像素,每个像素都要占内存。

一、先算一笔账

拿一张 4000×3000 的图片算一下:

项目数值
图片文件大小约 3MB(JPEG 压缩)
解码后像素数4000 × 3000 = 1200 万像素
每像素字节数4 字节(ARGB_8888)
内存占用1200 万 × 4 = 48MB

看到了吧?文件才 3MB,解码以后内存直接 48MB。这还是正常情况。如果是 RGBA_F16 格式,每像素 8 字节,直接 96MB。

很多人只看文件大小判断内存,这是错的。文件大小是压缩后的,内存是解码后的原始像素。

二、像素格式为什么重要

不同的像素格式,每个像素占的字节数不一样。

格式每像素字节用途
RGB_5652不透明图片,省内存
ARGB_88884通用,带透明通道
Y81灰度图,黑白照片
ALPHA_F168高精度透明通道

很多人不管什么图都用 ARGB_8888。灰度图也用这个格式,那就是浪费——明明 1 字节就能存的灰度,用了 4 字节。

这段代码解决什么问题: 指定像素格式解码图片。
文件: image/ImageDecoder.ets
用途: 控制解码内存
接入位置: 图片加载逻辑

import image from '@ohos.multimedia.image';

async decodeGrayImage(buf: ArrayBuffer) {
  const src = image.createImageSource(buf);
  const opts: image.DecodingOptions = {
    desiredPixelFormat: image.PixelMapFormat.Y8
  };
  const pixelMap = await src.createPixelMap(opts);
  return pixelMap;
}

灰度图就用 Y8 格式,内存直接少了 3/4。

三、大图为什么要降采样

很多人做大图的时候,直接整张解码。4000×3000 的图,解码完 48MB,然后缩放成 800×600 显示。那前面解码的 48MB 不就浪费了吗?

正确的做法是:解码的时候就降采样。直接解码成你需要的尺寸,不要解码成原始尺寸再缩放。

错误做法正确做法
解码原图 → 缩放解码时直接降采样到目标尺寸
内存 48MB内存 2MB

差了几十倍。

四、TIFF 打包和普通 JPEG 有什么不一样

API 26 的 Image Kit 新增了 TIFF 打包配置。很多人把 TIFF 参数照搬 JPEG 的思路,这不对。

对比JPEGTIFF
压缩方式有损无损/多种压缩
像素格式有限支持更多格式(Y8、ALPHA_F16)
适用场景照片扫描件、高精度图
大小控制质量参数PackingSizeLimit

TIFF 适合存扫描件、灰度图这种高精度的东西。不要用 TIFF 存普通照片,文件会大很多。

业务流程图

五、编码完的 PixelMap 为什么要释放

还有个容易踩的坑:编码完了,PixelMap 不释放。

很多人写完图片就不管了,内存一直占着。处理几张大图,内存直接爆了。

正确的做法是:编码完之后,立刻 release PixelMap。

六、方向信息为什么会导致图片旋转

还有个反常识的坑:图片方向。

手机拍的照片,方向信息存在 EXIF 里。图片本身的像素可能是横的,EXIF 里记录了"要旋转 90 度显示"。

很多人处理图片的时候,只解码像素,不管 EXIF。结果就是:拍的竖照片,存出去变成横的了。

正确的做法是:处理图片的时候,把 EXIF 里的方向信息也考虑进去。

七、几个容易踩的坑

第一个坑:只看图片文件 KB 数判断内存。文件小不代表内存小。

第二个坑:大图直接整张解码。不解采样,内存直接爆。

第三个坑:忽略像素格式的 Bytes Per Pixel。灰度图也用 ARGB_8888,浪费内存。

第四个坑:编码完成后 PixelMap 不释放。处理几张图,内存涨几十 MB。

第五个坑:TIFF 参数照搬 JPEG 思路。TIFF 有自己的配置,不能直接套。

运行效果图

这次做图片处理最大的体会是:图片内存不是看文件大小,是看像素数乘以每像素字节数。选对格式、做好降采样、用完及时释放,这三点做好了,大图内存问题就解决了一大半。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐