图像编解码
ImageCodec 是 TGFX 对静态图像格式(PNG/JPEG/WebP/HEIF)的统一解码与编码调度中心。
在多平台环境中,屏蔽底层编解码 API(如 Android BitmapFactory 与 iOS ImageIO)的差异是核心诉求。通过仅在头部解析时获取元数据,避免了不必要的内存分配;同时允许在解码瞬间进行尺寸调整,确保在低内存设备上也能处理大幅照片。
1. ImageCodec 统一接口
ImageCodec 隐藏了不同图片格式间的解析差异,提供了跨平台一致的操作体验。
1.1 支持的格式与识别
TGFX 能够自动识别文件头标识(Magic Numbers),无需手动指定格式。
- 内置格式:PNG, JPEG, WebP。
- 平台特有:如 iOS 的 HEIF / HEIC。
代码示例:检查解码器创建
// 传入任意支持路径,内部自动判断格式
auto codec = ImageCodec::MakeFrom("test.heic");
if (codec) {
printf("Format identified, Size: %d x %d\n", codec->width(), codec->height());
}
1.2 协作流程
MakeFrom (属性识别) → orientation (旋转校正) → readPixels (下采样解码) → Bitmap (像素结果)
2. 解码:MakeFrom 与 readPixels
解码过程分为“元数据探测”和“像素生成”两个阶段,旨在实现极致的内存保护。
2.1 探测元数据 (MakeFrom)
MakeFrom 仅执行头文件解析,不分配庞大的像素数组。
代码示例:轻量探测
auto data = Data::MakeWithCopy(buffer, size);
auto codec = ImageCodec::MakeFrom(data); // 瞬间完成,无像素计算开销
auto colorSpace = codec->colorSpace(); // 获取图片的色彩空间属性
2.2 执行解码与下采样 (readPixels)
支持在读取字节流的同时进行 Box-filter 下采样。
代码示例:下采样解码 (4K 转缩略图)
// 1. 创建属性识别器
auto codec = ImageCodec::MakeFrom("4k_photo.jpg");
// 2. 目标:解码为原图 1/10 尺寸的位图
auto dstInfo = ImageInfo::Make(codec->width() / 10, codec->height() / 10, ColorType::RGBA_8888);
Bitmap bitmap;
bitmap.allocPixels(dstInfo.width(), dstInfo.height());
// 3. 执行下采样解码(内存峰值极低)
bool success = codec->readPixels(dstInfo, bitmap.lockPixels());
bitmap.unlockPixels();
2.3 Orientation 自动处理
ImageCodec 负责解析元数据,而高层 API 负责应用它。
代码示例:处理带旋转信息的图像
auto codec = ImageCodec::MakeFrom("portrait.jpg");
if (codec->orientation() != Orientation::TopLeft) {
// 解码出的像素已经是根据 orientation() 物理校正后的
// 无需手动对 Canvas 应用 Rotate 矩阵
}
3. 编码:导出与序列化
TGFX 提供两种编码方式,将内存中的像素数据持久化为压缩文件字节流:
Bitmap::encode(format, quality)(实例方法):直接对 Bitmap 内容编码,返回Data对象。默认格式为 PNG,质量 100。ImageCodec::Encode(pixmap, format, quality)(静态方法):接受Pixmap视图进行编码,适合在不持有 Bitmap 的场景下使用。
注意:Pixmap 本身没有 encode() 方法。如需编码 Pixmap 的像素数据,请使用静态方法 ImageCodec::Encode(pixmap, format, quality)。
3.1 编码接口与参数
导出操作支持指定格式和压缩质量。支持的 EncodedFormat 包括 JPEG、PNG、WEBP 三种。
注意:导出为 JPEG 时,原本透明的区域会被填充为纯黑色。
代码示例:保存截图为 WebP
// 1. 获取包含像素的 Bitmap
Bitmap screenshot = ...;
// 2. 编码为 WebP 格式,质量设为 85
auto webpData = screenshot.encode(EncodedFormat::WEBP, 85);
// 3. 写入文件系统
if (webpData) {
FILE* file = fopen("out.webp", "wb");
fwrite(webpData->data(), 1, webpData->size(), file);
fclose(file);
}
4. 各平台差异说明
4.1 编码格式支持汇总
TGFX 可以编码的格式受到 EncodedFormat 枚举限制,仅支持 PNG、JPEG、WebP 三种。但解码支持的格式则因平台而异。
4.2 各平台详细对比
跨平台内置编解码器(始终可用)
| 格式 | 解码 | 编码 | 库 | 备注 |
|---|---|---|---|---|
| PNG | ✅ | ✅ | libpng | 无损格式,编码质量参数无效 |
| JPEG | ✅ | ✅ | libjpeg-turbo | 有损格式,支持质量参数 (0-100) |
| WebP | ✅ | ✅ | libwebp | 支持有损/无损,质量参数 (0-100) |
Apple (iOS/macOS) - ImageIO 框架
| 格式 | 解码 | 编码 | 特性 | 最低版本 |
|---|---|---|---|---|
| PNG | ✅ | ❌ | 使用内置编解码器 | — |
| JPEG | ✅ | ❌ | 使用内置编解码器 | — |
| WebP | ✅ | ❌ | iOS 14+ / macOS 11+ | iOS 14 / macOS 11 |
| HEIF | ✅ | ✅ | 硬件加速(推荐) | iOS 11 / macOS 10.13 |
| HEIC | ✅ | ✅ | HEIF 容器 + H.265 编码 | iOS 11 / macOS 10.13 |
| GIF | ✅ | ❌ | 静态帧提取 | — |
| BMP | ✅ | ❌ | — | — |
| TIFF | ✅ | ❌ | 多页面支持 | — |
| ICO | ✅ | ❌ | 图标格式 | — |
推荐场景:iOS 项目首选 HEIF/HEIC,性能最佳。
Android - BitmapFactory
| 格式 | 解码 | 编码 | 特性 | 最低版本 |
|---|---|---|---|---|
| PNG | ✅ | ❌ | — | — |
| JPEG | ✅ | ❌ | 硬件编解码 | — |
| WebP | ✅ | ❌ | 静态和动画 | API 14+ |
| GIF | ✅ | ❌ | 静态帧提取 | — |
| BMP | ✅ | ❌ | — | — |
| HEIF | ✅ | ❌ | MediaCodec 支持 | Android 9+ |
| HEIC | ✅ | ❌ | 设备相关 | Android 9+ |
限制:Android 端不支持编码,需用平台 API 单独处理。编码请用 Java 侧的 Bitmap 方案。
Windows - Windows Imaging Component (WIC)
| 格式 | 解码 | 编码 | 特性 | 最低版本 |
|---|---|---|---|---|
| PNG | ✅ | ❌ | 使用内置编解码器 | — |
| JPEG | ✅ | ❌ | 使用内置编解码器 | — |
| BMP | ✅ | ❌ | — | — |
| GIF | ✅ | ❌ | 静态帧 | — |
| TIFF | ✅ | ❌ | 全页面支持 | — |
| ICO | ✅ | ❌ | — | — |
| WebP | ✅ | ❌ | — | Windows 10 Build 1903+ |
| HEIF | ✅ | ❌ | — | Windows 10 Build 1903+ |
| HEIC | ✅ | ❌ | — | Windows 10 Build 1903+ |
特点:格式支持最丰富的平台,自动提取 EXIF/XMP 方向元数据。
OpenHarmony (OHOS) - ImageSourceNative NAPI
| 格式 | 解码 | 编码 | 备注 |
|---|---|---|---|
| PNG | ✅ | ❌ | — |
| JPEG | ✅ | ❌ | — |
| GIF | ✅ | ❌ | — |
| WebP | ✅ | ❌ | — |
| BMP | ✅ | ❌ | — |
| HEIC | ✅ | ❌ | 设备相关 |
Web (Emscripten/浏览器)
| 格式 | 解码 | 编码 | 浏览器支持 |
|---|---|---|---|
| PNG | ✅ | ❌ | 所有浏览器 |
| JPEG | ✅ | ❌ | 所有浏览器 |
| GIF | ✅ | ❌ | 所有浏览器 |
| WebP | ✅ | ❌ | Chrome 23+, Firefox 65+, Safari 16+ |
| AVIF | ✅ | ❌ | Chrome 85+, Firefox 93+ |
| TIFF | ✅ | ❌ | 部分浏览器 |
依赖:浏览器原生支持,无额外库。
Linux 与其他平台
| 平台 | PNG | JPEG | WebP | 其他 |
|---|---|---|---|---|
| Linux | ✅ | ✅ | ✅ | ❌ |
| 其他 POSIX | ✅ | ✅ | ✅ | ❌ |
限制:仅支持内置三种格式,无平台原生编解码器支持。
4.3 格式自动识别机制
ImageCodec::MakeFrom() 会按以下优先级自动识别格式:
文件头特征识别(如果启用了内置编解码器):
- WebP:RIFF 头 + "WEBP" 标签 @ offset 8
- PNG:8 字节签名
89 50 4E 47 0D 0A 1A 0A - JPEG:3 字节 SOI 标记
FF D8 FF
平台原生解码器:
- Apple:CGImageSource(自动识别 HEIF/HEIC/BMP/GIF/TIFF 等)
- Android:BitmapFactory(自动识别平台支持的所有格式)
- Windows:WIC(自动识别 BMP/GIF/TIFF/ICO/HEIF/WebP 等)
- 其他平台:按此顺序尝试
失败返回 nullptr:若无编解码器支持该格式
无需手动指定格式,TGFX 会自动识别。
4.4 EXIF 方向元数据处理
所有平台的 ImageCodec 都会自动提取并应用图像方向信息:
- 标准来源:JPEG 的 EXIF Orientation 标签、TIFF IFD、HEIF/PNG 的方向字段
- 自动校正:通过
orientation()属性获取,Image::makeOriented()应用变换 - 无需手动处理:方向校正已在解码层完成
4.5 编码质量参数对比
// 在 ImageCodec::Encode(pixmap, format, quality) 中:
// quality 参数含义因格式而异
// PNG: 质量参数无效(总是无损,但压缩级别内部固定)
auto png = ImageCodec::Encode(pixmap, EncodedFormat::PNG, 100); // 参数被忽略
// JPEG: 质量 0-100 映射到 libjpeg-turbo 质量
// 推荐 85-95 获得最佳视觉质量
auto jpg = ImageCodec::Encode(pixmap, EncodedFormat::JPEG, 85);
// WebP: 质量 0-100(有损模式)
// 75+ 可保证高保真度
auto webp = ImageCodec::Encode(pixmap, EncodedFormat::WEBP, 80);
4.6 选择编码格式的建议
| 场景 | 推荐格式 | 原因 |
|---|---|---|
| 最大兼容性 | PNG | 所有平台/浏览器支持 |
| 文件大小优先 | WebP | 体积最小(需 Chrome/现代浏览器) |
| iOS 高性能 | HEIF/HEIC | 硬件编码,体积小,质量优 |
| 透明度 + 兼容 | PNG | PNG 支持透明,JPEG 会变黑 |
| Web 前端优化 | WebP + PNG 降级 | WebP 优先,不支持时用 PNG |
| 实时/流媒体 | JPEG | 最快编码速度 |
5. 代码实战:编码格式对比
以下示例展示了同一张图像经过不同格式和质量编码后的效果与文件大小差异:
// 1. Prepare source pixels from a Bitmap
Bitmap sourceBitmap(200, 200);
// ... fill bitmap with image data ...
// 2. Encode to different formats
auto pngData = sourceBitmap.encode(EncodedFormat::PNG, 100); // Lossless
auto jpegHigh = sourceBitmap.encode(EncodedFormat::JPEG, 90); // High quality
auto jpegLow = sourceBitmap.encode(EncodedFormat::JPEG, 10); // Low quality
auto webpData = sourceBitmap.encode(EncodedFormat::WEBP, 80); // Balanced
// 3. Decode back from encoded data
auto pngImage = Image::MakeFromEncoded(pngData);
auto jpegHighImage = Image::MakeFromEncoded(jpegHigh);
auto jpegLowImage = Image::MakeFromEncoded(jpegLow);
auto webpImage = Image::MakeFromEncoded(webpData);
// 4. Draw each image for visual comparison
canvas->drawImage(pngImage, 0, 0); // Identical to original
canvas->drawImage(jpegHighImage, 210, 0); // Nearly identical, 5x smaller
canvas->drawImage(jpegLowImage, 0, 230); // Visible block artifacts
canvas->drawImage(webpImage, 210, 230); // Good quality, smallest size
渲染结果对比
各格式/质量编码后的效果与文件大小差异(同一张 200×200 源图):

关键观察:
- PNG (103 KB):无损格式,像素与原图完全一致,适合需要精确还原的场景。
- JPEG q=90 (20 KB):体积仅为 PNG 的 1/5,肉眼几乎无法区分与原图的差异。
- JPEG q=10 (3 KB):体积极小,但可明显观察到块状压缩伪影(Block Artifact),仅适用于缩略图。
- WebP q=80 (13 KB):在文件大小和画质之间取得了最佳平衡,推荐作为 Web 和移动端的默认选择。
