马赛克切块提速 17.6×~38×:desiredRegion 重复读盘与一次解码管线(HarmonyOS)

现象: 马赛克切块 16×16=256 块耗时 31~43 秒;真机计时每块恒定 ~94-146ms,不随块尺寸缩小而下降(16×16 块只有 4×4 块 1/16 像素,每块却一样贵)。
原因: source.createPixelMap({desiredRegion}) 每次调用都重新读盘 + 解析整个源文件——256 块 = 256 次 MB 级文件 IO;输出侧优化(desiredSize 缩放)只减输出像素,减不了输入读盘。
解决: 一次整图解码(只读一次盘)→ readPixelsToBuffer → JS 内存切块(Uint8Array 行拷贝)→ image.createPixelMap 静态函数逐块建小图 → 打包写盘;旧路径保留 try-catch 回退。16×16:43,546→1,140ms(38×)/ 31,372→1,780ms(17.6×)。

一、先采基线再动刀

优化前先采了五档基线,找到成本结构:“每块成本不随块尺寸缩小而下降” = 每次调用含整文件级固定开销的判别信号。这个信号当场推翻了原计划的输出侧优化——瓶颈是重复读盘而非小文件 IO,主攻方向改为"消除重复读盘"。

二、新管线:一次读盘,内存切块

// 1. 一次整图解码:只读一次盘(目标宽 = areaW × density × 2)
const full = await source.createPixelMap({ desiredSize: { width: targetW } })
// 2. 像素整读进 JS 内存
await full.readPixelsToBuffer(buffer)
// 3. JS 内存切块:Uint8Array 按行拷贝出每块的像素
// 4. image.createPixelMap 静态函数逐块建小图
//    (@ohos.multimedia.image.d.ts :4468;srcPixelFormat: RGBA_8888 @since 12,:3872)
const tile = await image.createPixelMap(tileBuffer, {
  size: { width: tw, height: th },
  srcPixelFormat: image.PixelMapFormat.RGBA_8888
})
// 5. ImagePacker 打包写盘 → file:// URI → 发射器 config.src

旧 desiredRegion 逐块解码路径保留,新管线失败自动回退旧路径。

三、五档真机数据(ms,基线 → 优化)

档位块数动态照片非动态
4×4162,814 → 3492,335 → 358
6×6365,228 → 5354,207 → 434
8×8649,673 → 6896,273 → 662
12×1214425,096 → 56413,539 → 1,089
16×1625643,546 → 1,140(38×)31,372 → 1,780(17.6×)

四、两个结构性前提

  1. 一发射器一图是结构下限(坑8):ImageParticleParameters.src 是单值 ResourceStr 非数组(particle.d.ts :385);每发射器 config 仅一个(:602),count 只能复制同一 config(:628)。"每块 = 特定图 + 特定格位"在 Particle 架构内无解,N² 发射器是马赛克方案的结构下限——64×64=4096 发射器是压测档,512×512(26 万发射器)必死
  2. 精确拼合要 [0,0] 点发射(坑10):size 随机出生区是错位之源,详见《emitter.size 是双刃剑》

运行时裁的块还必须先落沙箱:cacheDir 免权限可写(相册写入需授权、资源目录只读),IMAGE 粒子只收 URI 字符串不收 PixelMap——管线:解码 → 切块 → ImagePacker 编 JPEG → 写 cacheDir → file:// URI 喂发射器。

标签

#HarmonyOS #ArkUI #性能优化 #PixelMap #desiredRegion #马赛克 #image

📹 效果演示:本文的「马赛克飞散」动态效果实录会发在抖音——号 581821159(天行健之初九),截图看不出的粒子细节,视频一眼就懂。

懿路向前 · AI辅助整理 · 2026-10-12

Logo

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

更多推荐