PixelMap 是 HarmonyOS 图片处理体系的核心

在 HarmonyOS NEXT 中,图片处理链路基本围绕:


ImageSource

↓

PixelMap

↓

ImagePacker

展开。

其中:

  • ImageSource 负责图片解码
  • PixelMap 负责内存中的像素操作
  • ImagePacker 负责重新编码输出

图片压缩真正发生的位置:

主要有两个:

  1. 解码阶段压缩
  2. 编码阶段压缩

很多开发者只关注:


JPEG Quality

实际上:

企业级图片压缩:

核心不是:

降低 Quality。

而是:

控制:


分辨率

像素数量

颜色信息

编码方式

内存占用

一、PixelMap 与图片压缩的关系

首先需要明确:

PixelMap 本身不是压缩格式。

它是:

未压缩的像素数据。

例如:

一张:

4000 × 3000

照片。

如果采用:

RGBA8888:

每个像素:

4 Byte。

计算:


4000 × 3000 × 4

结果:


48,000,000 Byte

约:

48MB。

这就是 PixelMap 在内存中的真实大小。


而保存成 JPEG:

可能只有:

3MB。

原因:

JPEG 保存的是:

压缩后的编码数据。

不是:

每个 Pixel。

所以:

关系:

如下:


JPEG 文件

小

↓

Decode

↓

PixelMap

大

↓

Encode

↓

JPEG 文件

小

二、为什么图片一解码就变大?

很多开发者遇到:

手机相册:

一张照片:

5MB。

但是:

读取后:

内存:

几十 MB。

原因:

图片文件:

是压缩状态。

PixelMap:

是展开状态。

例如:

手机照片:


HEIF

5MB

解码:


4000×3000 RGBA

≈48MB

甚至:

更多。

所以:

图片开发中:

最大的问题:

通常不是:

文件大小。

而是:

PixelMap 内存。


三、图片压缩的三个层级

企业级图片压缩:

通常分三个阶段。


1. 解码前压缩

也叫:

Decode Resize。

例如:

原图:


8000×6000

但是:

页面:

只需要:


800×600

不要:

先生成:

8000×6000 PixelMap。

正确:

方式:


ImageSource

↓

Decode Options

↓

800×600 PixelMap

这是:

最重要:

的优化。

因为:

避免:

创建:

巨大:

PixelMap。


2. PixelMap 内存优化

例如:

默认:

可能:

RGBA8888。

但是:

某些场景:

不需要:

Alpha。

可以:

使用:

RGB。

减少:

25%:

内存。


例如:

RGBA:


R
G
B
A

四个通道。

RGB:


R
G
B

三个通道。


3. 编码压缩

最后:

输出:

JPEG。

例如:


PixelMap

↓

ImagePacker

↓

JPEG

通过:

Quality:

控制:

文件大小。


四、为什么不能只靠 JPEG Quality 压缩?

很多项目:

写:


quality:50

认为:

完成:

压缩。

实际上:

效果:

有限。

因为:

如果:

图片:

4000×3000。

即使:

Quality=20。

仍然:

需要:

处理:

1200万:

Pixel。


更好的:

方案:

是:

先:

降低:

尺寸。

例如:

原图:


4000×3000

调整:


1280×960

然后:

JPEG Quality:

80。


结果:

通常:

比:

直接:

Quality=20:

效果:

更好。


五、HarmonyOS NEXT 图片压缩流程

企业中:

推荐:

流程:

如下:


原始图片

↓

ImageSource

↓

获取 ImageInfo

↓

判断尺寸

↓

计算目标尺寸

↓

创建目标 PixelMap

↓

编辑处理

↓

ImagePacker编码

↓

输出文件

核心思想:

不要:

处理:

超过需求:

的数据。


六、PixelMap resize 为什么影响压缩?

图片大小:

主要:

取决于:

像素数量。

例如:

两个图片:

图片A:


4000×3000

图片B:


1000×750

像素数量:

差:

16倍。

所以:

即使:

同样:

JPEG Quality。

文件大小:

差异:

巨大。


七、PixelMap Resize 原理

Resize:

本质:

是:

重新采样。

例如:

原图:


4×4

缩小:


2×2

需要:

决定:

新的:

Pixel:

颜色。


常见:

算法:

包括:

最近邻

Nearest Neighbor

速度:

最快。

效果:

较差。


双线性插值

Bilinear

根据:

周围:

4个 Pixel:

计算。

效果:

较好。


双三次插值

Bicubic

根据:

16个 Pixel:

计算。

质量:

更高。


Lanczos

高级:

缩放。

照片:

效果:

优秀。

计算:

较高。


八、大图片压缩为什么容易 OOM?

例如:

照片:


12000×9000

PixelMap:

RGBA:

计算:


12000×9000×4

约:

432MB。

如果:

同时:

存在:

三个:

对象:


原图PixelMap

处理PixelMap

输出PixelMap

内存:

超过:

1GB。

移动设备:

非常:

危险。


所以:

企业:

压缩:

不会:

这样:

做:

错误流程:


decode原图

↓

resize

↓

encode

正确:

流程:


ImageSource

↓

目标尺寸Decode

↓

PixelMap

↓

encode

九、desiredSize 为什么重要?

HarmonyOS 图片解码:

支持:

目标尺寸。

例如:

你需要:

头像:

200×200。

不应该:

加载:

4000×3000。

应该:

告诉:

Decoder:

目标:

尺寸。


Decoder:

可以:

提前:

降低:

采样。

减少:

解码:

数据。


优势:

包括:

  1. 减少内存
  2. 减少 CPU
  3. 加快速度
  4. 降低功耗

十、图片压缩中的质量控制

真正:

质量控制:

包含:

多个:

因素。

不是:

只有:

Quality。


分辨率

影响:

最大。

例如:

4000×3000

1000×750

影响:

巨大。


编码格式

例如:

JPEG:

适合:

照片。

PNG:

适合:

透明图片。

WebP:

适合:

网络。


压缩参数

例如:

JPEG:

Quality。

PNG:

Compression Level。

WebP:

Lossless / Quality。


十一、JPEG 压缩与 PixelMap

JPEG:

编码:

流程:

如下:


RGBA

↓

RGB

↓

YUV

↓

DCT

↓

量化

↓

熵编码

↓

JPEG

PixelMap:

在:

第一步。


所以:

JPEG:

压缩:

不是:

PixelMap:

直接:

变小。

而是:

经过:

复杂:

编码。


十二、PNG 压缩与 PixelMap

PNG:

流程:

如下:


RGBA

↓

Filter

↓

Deflate

↓

PNG

PNG:

不会:

删除:

信息。

因此:

适合:

保存:

PixelMap:

精确:

结果。

例如:

截图。

UI:

图标。

透明:

Logo。


十三、WebP 与 PixelMap

WebP:

支持:

RGBA。

所以:

可以:

替代:

很多:

PNG。

尤其:

网络:

传输。

例如:

商品图片。

头像。

文章图片。


十四、图片压缩中的颜色空间

PixelMap:

常见:

格式:


RGBA8888

但是:

编码:

可能:

转换:

到:


YUV

尤其:

JPEG。


颜色转换:

也会:

消耗:

CPU。

所以:

大量:

图片:

压缩:

需要:

异步:

处理。


十五、批量图片压缩架构

例如:

后台:

上传:

1000:

张图片。

不能:

同时:

压缩。

正确:

架构:

如下:


Upload Queue

↓

Compression Worker

↓

ImageSource

↓

PixelMap

↓

ImagePacker

↓

Upload

限制:

并发:

例如:

2~4。


十六、图片压缩最佳实践

HarmonyOS NEXT 中:

推荐:

遵循:

以下:

原则:

1.

不要:

直接:

解码:

超大图片。


2.

优先:

使用:

目标尺寸:

解码。


3.

不要:

频繁:

PixelMap复制。


4.

编辑:

完成后:

一次:

ImagePacker。


5.

照片:

优先:

JPEG/WebP。


6.

透明:

图片:

使用:

PNG/WebP。


7.

大批量:

压缩:

必须:

任务队列。


十七、完整企业级图片压缩架构

最终:

成熟:

方案:

如下:


用户选择图片

↓

PhotoAccessHelper

↓

ImageSource

↓

读取Info

↓

计算压缩策略

↓

目标尺寸Decode

↓

PixelMap

↓

Crop

↓

Rotate

↓

Filter

↓

Watermark

↓

ImagePacker

↓

JPEG/WebP

↓

上传服务器

总结

HarmonyOS NEXT 图片压缩的核心:

不是:

简单:

降低:

JPEG Quality。

真正:

高性能:

图片压缩:

依赖:

三个关键:

思想:


第一:

减少 Pixel 数量

比:

降低质量:

更加:

有效。


第二:

避免生成巨大 PixelMap

大图:

性能问题:

主要:

来自:

内存。


第三:

统一最后一次编码

避免:

重复:

JPEG 压缩。


PixelMap:

是:

HarmonyOS 图片体系:

核心:

数据结构。

理解:

PixelMap:

内存模型。

理解:

Decode。

理解:

Encode。

才能:

真正:

做好:

企业级:

图片处理。

十八、PixelMap 内存模型与压缩性能的关系

图片压缩性能问题,很多时候并不是出现在:


ImagePacker

而是:


PixelMap 创建阶段

因为:

PixelMap 是内存中的原始像素。

理解它的内存模型,是优化图片压缩的关键。


1. PixelMap 内存结构

一个 PixelMap 可以理解为:


PixelMap

├── Pixel Buffer
│
├── Width
│
├── Height
│
├── PixelFormat
│
├── AlphaType
│
├── RowStride
│
└── Metadata

其中:

最重要:

是:


Pixel Buffer

它保存:

真正:

RGBA 数据。


例如:

图片:


1000 × 1000

RGBA8888:

每个像素:


4 Byte

内存:

计算:


1000 × 1000 × 4

约:

4MB。


但是:

实际:

可能:

大于:

4MB。

原因:

还有:

Stride。


2. 什么是 RowStride?

很多开发者:

计算 PixelMap 内存:

直接:

使用:


width × height × byte

这是:

不完全:

正确。

真正:

内存:

通常:

按照:

行:

对齐。

例如:

每行:

可能:

不是:

4000 Byte。

而是:

4096 Byte。

原因:

CPU/GPU:

访问:

需要:

内存对齐。


结构:

类似:


Row0

xxxxxxxxxxxxxxxx


Row1

xxxxxxxxxxxxxxxx


Row2

xxxxxxxxxxxxxxxx

每一行:

可能:

存在:

padding。


所以:

真实:

大小:

类似:


RowStride × Height

而不是:


Width × Height × PixelSize

3. 为什么压缩前要关注 PixelMap 内存?

例如:

商城:

上传商品图。

流程:

错误:


选择图片

↓

解码原图

↓

生成PixelMap

↓

压缩

如果:

用户:

上传:

8000×6000:

可能:

瞬间:

占用:

几百 MB。


正确:

流程:

应该:


选择图片

↓

ImageSource

↓

读取尺寸

↓

计算目标尺寸

↓

缩放解码

↓

PixelMap

↓

压缩

十九、PixelMap Copy 对压缩性能的影响

很多代码:

里面:

隐藏:

大量:

复制。

例如:


const newPixelMap =
await pixelMap.create()

或者:

裁剪:

旋转:

返回:

新的:

PixelMap。


一个:

大图:

复制:

一次:

就是:

几十 MB。

如果:

连续:

操作:

如下:


Crop

↓

Rotate

↓

Filter

↓

Watermark

可能:

产生:

多个:

PixelMap。


内存:

变化:

可能:

如下:


原图

80MB


Crop

60MB


Rotate

60MB


Filter

60MB

瞬间:

超过:

200MB。


二十、图片压缩中的零拷贝思想

企业级:

图片框架:

非常:

关注:

Zero Copy。

目标:

减少:


PixelMap

↓

PixelMap

↓

PixelMap

这种:

链式:

复制。


理想:

流程:

应该:

类似:


Pixel Buffer

↓

GPU Texture

↓

Shader处理

↓

Output

尽可能:

减少:

CPU:

内存:

搬运。


二十一、PixelMap 与 GPU Texture

现代:

图片编辑器:

不会:

一直:

操作:

PixelMap。

因为:

PixelMap:

属于:

CPU:

内存。

而:

显示:

属于:

GPU。


流程:

通常:

如下:


PixelMap

↓

Upload Texture

↓

GPU处理

↓

Screen

例如:

滤镜:

调整:

亮度。

GPU:

只需要:

修改:

Shader。

不需要:

重新:

生成:

PixelMap。


二十二、为什么实时压缩预览不卡?

例如:

用户:

调整:

图片质量:

滑动:

进度条。

如果:

每次:

都:

执行:


PixelMap

↓

JPEG

↓

File

↓

Display

一定:

卡顿。


正确:

方式:

是:

模拟:

压缩:

效果。

例如:

GPU:

降低:

质量:

显示:

预览。

真正:

点击:

保存:

才:

执行:

ImagePacker。


二十三、图片压缩中的 Bitmap Pool 思想

Android:

大量:

使用:

Bitmap Pool。

HarmonyOS:

企业:

图片框架:

同样:

存在:

类似:

思想。


原因:

频繁:

创建:

PixelMap:

会:

造成:

内存:

抖动。

例如:

上传:

图片:

流程:

不断:

创建:

释放。


优化:

方式:

复用:

Buffer。

例如:


Buffer A

↓

处理图片1


Buffer A

↓

处理图片2

减少:

GC。


二十四、压缩大图时为什么要分块?

超大图片:

例如:

摄影:

照片:


12000×9000

直接:

完整:

Decode。

风险:

巨大。


企业:

可能:

采用:

Tile Processing。

即:

分块:

处理。

例如:

图片:

切:

成:


Tile1

Tile2

Tile3

Tile4

处理:

完成:

一块:

释放:

一块。


优势:

降低:

峰值:

内存。


二十五、图片压缩中的缩略图策略

相册:

最典型。

例如:

手机:

10000:

张:

照片。

不可能:

全部:

加载:

原图。


真实:

流程:

如下:


Original Image

↓

Thumbnail Decode

↓

Small PixelMap

↓

List Display

只有:

用户:

点击:

查看:

大图:

才:

执行:

Full Decode。


二十六、为什么相册滑动很流畅?

核心:

不是:

手机:

CPU:

强。

而是:

架构:

正确。


相册:

通常:

采用:

三级:

缓存:


一级:

GPU Texture


二级:

缩略PixelMap


三级:

ImageSource

而不是:

缓存:

所有:

原图。


二十七、压缩参数动态策略

企业:

不会:

固定:

Quality。

例如:

所有:

图片:

80。

不合理。


动态:

策略:

可能:

如下:

头像:


200×200

WebP

商品图:


1200×1200

JPEG

Quality 85

聊天图片:


1280最大边

JPEG

Quality 70

证件:


PNG

无损

二十八、图片压缩与网络传输

图片:

上传:

最大的:

瓶颈:

通常:

不是:

编码。

而是:

网络。


例如:

原图:


10MB

上传:

慢。

压缩:

之后:


500KB

体验:

提升:

巨大。


但是:

压缩:

不能:

无限。

需要:

平衡:


质量

↓

大小

↓

速度

二十九、图片压缩错误案例分析

错误1:先保存JPEG,再继续编辑

错误:


PixelMap

↓

JPEG

↓

PixelMap

↓

JPEG

问题:

重复:

损失。


正确:


PixelMap

↓

全部编辑

↓

一次JPEG

错误2:所有图片都使用PNG

问题:

照片:

巨大。


正确:

照片:

JPEG/WebP。


错误3:加载原图再缩小

问题:

OOM。


正确:

Decode阶段:

直接:

缩小。


三十、HarmonyOS NEXT 企业级图片压缩架构

最终:

推荐:

架构:

如下:


用户选择图片

        |

PhotoAccessHelper

        |

ImageSource

        |

ImageInfo分析

        |

压缩策略计算

        |

--------------------------------

        |

小图

        |

目标尺寸Decode


        |

PixelMap


        |

编辑Pipeline


        |

ImagePacker


        |

WebP/JPEG


        |

上传服务


--------------------------------

三十一、PixelMap 图片压缩核心原则

总结:

真正:

高质量:

图片压缩:

遵循:

以下:

原则:


原则1

不要:

处理:

不需要:

的像素。


原则2

不要:

让:

大图片:

进入:

完整:

PixelMap。


原则3

不要:

重复:

编码。


原则4

不要:

频繁:

复制:

PixelMap。


原则5

实时:

编辑:

交给:

GPU。


原则6

最终:

输出:

才:

使用:

ImagePacker。


至此:

《HarmonyOS NEXT 图片压缩与 PixelMap 深度解析》

关于:

压缩链路、PixelMap 内存、Decode优化、Encode优化、企业架构

三十二、PixelMap 生命周期与压缩过程中的内存管理

图片压缩真正困难的地方:

不是:


ImagePacker.encode()

而是:

整个生命周期:

如何控制:

PixelMap。


一个完整图片压缩流程:

实际上:

存在多个对象:


ImageSource

↓

Decoded PixelMap

↓

Processed PixelMap

↓

Encoded Data

↓

File

每一个阶段:

都有:

自己的:

内存。


1. ImageSource 生命周期

ImageSource:

属于:

轻量对象。

保存:

主要:

信息:


文件描述

Decoder状态

图片Metadata

格式信息

它:

不会:

保存:

完整:

像素。


例如:

一个:

50MB:

照片。

创建:

ImageSource:

可能:

只占:

KB:

级别。


所以:

企业:

相册:

列表:

通常:

缓存:

ImageSource。

而不是:

PixelMap。


2. PixelMap 生命周期

PixelMap:

是真正:

的大对象。

例如:


6000 × 4000

RGBA:

内存:

约:


6000×4000×4

≈96MB

所以:

PixelMap:

生命周期:

必须:

严格:

管理。

典型:

流程:

如下:


创建

↓

使用

↓

编码

↓

释放

不要:

长期:

持有。


三十三、PixelMap 内存释放机制

图片开发:

常见:

问题:

就是:

内存:

泄漏。

例如:


let pixelMap = await imageSource.createPixelMap()

// 图片处理

// 忘记释放

结果:

多个:

大图:

处理后。

内存:

不断:

上涨。


企业:

处理:

类似:

如下:


try {

  decode

  process

  encode

}

finally {

  release PixelMap

}

核心:

思想:

就是:

谁创建。

谁负责:

生命周期。


三十四、为什么 JavaScript/ArkTS GC 不能完全解决?

很多开发者:

认为:

ArkTS:

有:

垃圾回收。

所以:

不用:

管。

这是:

错误:

理解。


原因:

PixelMap:

内部:

通常:

包含:

Native Memory。

例如:


ArkTS对象

↓

Native Pixel Buffer

GC:

只能:

管理:

ArkTS:

对象。

不能:

保证:

Native:

大块:

Buffer:

立即:

释放。


所以:

图片:

场景:

必须:

主动:

管理。


三十五、PixelMap Copy-On-Write(写时复制)

现代:

图片:

框架:

大量:

使用:

Copy-On-Write。


什么意思?

例如:

两个:

PixelMap:

引用:

同一份:

数据。

开始:

不复制。


结构:

如下:


PixelMap A

        |

        |

 Pixel Buffer


PixelMap B

        |

如果:

只是:

读取:

无需:

复制。


如果:

修改:

才:

创建:

新的:

Buffer。


优势:

减少:

大量:

无意义:

复制。


三十六、为什么图片编辑器不会立即修改原图?

例如:

用户:

打开:

图片。

执行:

操作:


裁剪

旋转

滤镜

水印

如果:

每一步:

修改:

PixelMap。

会:

产生:

大量:

复制。


成熟:

架构:

保存:

操作:

状态:

例如:


{
 crop:{
   x:100,
   y:100
 },

 rotate:90,

 brightness:20,

 mirror:true
}

显示:

阶段:

GPU:

计算。

导出:

阶段:

统一:

生成。


三十七、图片压缩中的 Pipeline 架构

企业:

图片:

系统:

通常:

不是:

函数调用:

而是:

Pipeline。


例如:


Input

 |

Decode

 |

Transform

 |

Filter

 |

Compress

 |

Encode

 |

Output

每一个:

模块:

独立。

例如:

Decode:

负责:

读取。

Transform:

负责:

几何。

Filter:

负责:

颜色。

Encode:

负责:

压缩。


优势:

容易:

扩展。


三十八、PixelMap 与图片旋转压缩组合优化

一个常见:

场景:

手机:

拍照上传。

流程:

可能:

如下:


Camera JPEG

↓

EXIF Orientation

↓

Rotate

↓

Resize

↓

Compress

↓

Upload

错误:

方案:

先:

Rotate:

完整:

原图。

然后:

Resize。


正确:

方案:

根据:

目标尺寸:

直接:

Decode:

旋转:

缩放。


减少:

中间:

PixelMap。


三十九、大图片压缩的最佳实践

例如:

用户:

上传:

照片:


12000×9000

目标:

上传:


1080×1080

推荐:

流程:

如下:

第一步

读取:

ImageInfo。

得到:


width

height

第二步

计算:

缩放比例:

例如:


1080 / 12000

第三步

Decoder:

按照:

目标:

生成:

PixelMap。


第四步

ImagePacker:

输出:

WebP/JPEG。


最终:

内存:

可能:

从:

400MB+

降低:

到:

几 MB。


四十、图片压缩中的多线程设计

图片压缩:

天然:

CPU:

密集。


但是:

不能:

无限:

增加:

线程。


例如:

错误:


100张图片

↓

100线程压缩

结果:

CPU:

爆满。

内存:

爆炸。


正确:

方案:

任务队列:

例如:


Image Queue

      |

Worker1

Worker2

Worker3

并发:

控制:

通常:

根据:

设备:

动态:

调整。


四十一、压缩任务取消机制

移动端:

很重要。

例如:

用户:

正在:

上传:

图片。

突然:

退出页面。


如果:

没有:

取消:

机制。

后台:

仍然:

继续:

压缩。

浪费:

资源。


企业:

设计:

通常:

支持:


Cancel Token

流程:

如下:


开始压缩

↓

检查取消状态

↓

继续

↓

编码

↓

完成

四十二、图片压缩失败处理

真实:

环境:

存在:

很多:

异常:

例如:

  • 文件损坏
  • 格式不支持
  • 内存不足
  • 权限失败
  • 编码失败

不能:

简单:

认为:

一定:

成功。


企业:

流程:

如下:


Decode

↓

try

↓

Process

↓

Encode

↓

catch

↓

Fallback

四十三、压缩失败降级策略

例如:

大图:

编码:

失败。

可以:

降级:

策略:

如下:

第一次:


JPEG Quality 90

失败:


Quality 75

失败:


降低分辨率

保证:

用户:

体验。


四十四、图片压缩安全问题

图片:

也:

可能:

携带:

隐藏:

信息。

例如:

EXIF:

可能:

包含:


GPS

设备型号

拍摄时间

上传:

服务器:

之前:

可能:

需要:

清理:

Metadata。


流程:

如下:


Decode

↓

Remove EXIF

↓

Encode

↓

Upload

四十五、企业图片上传最终架构

完整:

商业:

App:

通常:

如下:


用户选择图片

↓

PhotoAccessHelper

↓

ImageSource

↓

读取Metadata

↓

安全检查

↓

压缩策略

↓

目标尺寸Decode

↓

PixelMap

↓

编辑处理

↓

清理Metadata

↓

ImagePacker

↓

WebP/JPEG

↓

网络上传

↓

服务器存储

四十六、总结:PixelMap 压缩核心思想

HarmonyOS NEXT 图片压缩:

真正:

核心:

不是:

“怎么把图片变小”。

而是:

控制:

整个:

生命周期。


核心:

包括:

1.

什么时候:

创建:

PixelMap。


2.

创建:

多大的:

PixelMap。


3.

是否:

需要:

复制。


4.

什么时候:

释放。


5.

最终:

如何:

编码。


掌握:

这些:

才能:

开发:

真正:

企业级:

图片系统。

Logo

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

更多推荐