绕过Android原生Skia,TOP厂商原始图片解码器/链路分析

绕过Android原生Skia,TOP厂商原始图片解码器/链路分析

摘要:OEM厂商在图库图片解码优化上的核心思路是系统级协同而非单纯替换解码库,主要采取以下策略:

  1. 链路优化:构建从相机到图库的完整优化链路,通过缩略图预生成、数据共享和渐进式加载(先显示低清预览再替换高清图)提升用户体验;
  2. 格式专项优化:
  • JPEG:基于libjpeg-turbo进行NEON指令集优化,支持DCT缩放解码和区域解码
  • HEIC/HEIF:优先调用硬件解码器,减少YUV-RGB转换
  • AVIF:硬件解码优先,软件解码作为后备方案
  1. 关键技术:
  • 解码路由机制:根据格式/场景自动选择最优解码路径
  • 内存优化:减少数据拷贝,使用native内存池和硬件缓冲
  • 资源复用:充分利用内嵌缩略图和系统缓存
  1. 实施建议: 优先实现相机-图库数据共享机制和JPEG优化,再逐步扩展至其他格式和高级特性。这种系统级优化相比单纯替换解码库能带来更显著的首帧显示速度提升。

行业里 OEM 的图库/相机图片解码优化,通常不是简单"换一个开源解码库",而是 系统能力 + vendor 硬件能力 + 图库业务链路 + native decoder 路由策略 的组合优化。

1. OEM 定制解码器通常解决的不是"单点 decode",而是整条链路

以"相机拍照后点左下角缩略图进入图库大图"为例,普通 App 可能是:

复制代码
点击缩略图
  ↓
启动图库 Activity
  ↓
MediaStore 查询
  ↓
拿到 URI
  ↓
Skia / BitmapFactory / ImageDecoder 解码
  ↓
Bitmap 上传 GPU
  ↓
首帧显示

OEM 系统图库更可能是:

复制代码
拍照完成时相机已经生成缩略图 / preview / embedded thumbnail
  ↓
相机和图库共享最新照片信息 / 缩略图缓存
  ↓
点击缩略图后图库先显示 handoff thumbnail
  ↓
后台根据格式路由到最快 decoder
  ↓
JPEG 走 turbo/native/hardware path
  ↓
HEIC/HEIF 走硬解或厂商 codec path
  ↓
大图按屏幕尺寸/区域/tile 渐进加载
  ↓
最终高清替换

所以它们的优势不是"某个 decoder 一定比 Skia 快 3 倍",而是:

复制代码
不用等完整大图 decode 才出首帧
不用走多余 copy
不用重复查询/重复解析
不用每次都 cold start
不用所有格式都走通用 Skia 路径

2. 三星、小米、OPPO 常见的定制方向

2.1 JPEG:基于 libjpeg-turbo / 自研 NEON SIMD 路径优化

图库最常见格式仍然是 JPEG,尤其是相机拍照结果。

OEM 对 JPEG 的优化通常包括:

复制代码
1. 使用 libjpeg-turbo 或厂商 fork 版本
2. 针对 ARMv8/AArch64 NEON 做 SIMD 优化
3. 优化 YCbCr -> RGB 转换
4. 优化 IDCT
5. 优化下采样 / 上采样
6. 支持按目标尺寸 decode,避免全尺寸解码
7. 支持 region decode / tile decode
8. 减少中间 buffer copy
9. native 内存池复用

普通 Skia 解码路径更通用,考虑格式兼容、安全和跨平台;OEM decoder 更激进,可能针对自家设备常见图片特征优化:

复制代码
相机 JPEG 分辨率固定
EXIF 格式固定
色彩空间固定
YUV 采样格式固定
旋转角度常见为 0/90/270
缩略图尺寸固定

因此可以做大量 fast path。

2.2 HEIC/HEIF:优先走硬件解码或 vendor codec

现在很多手机相机支持 HEIC/HEIF。HEIC 背后通常是 HEVC still image,本质上比 JPEG 复杂。

普通 App 走系统 ImageDecoder,不同系统版本性能差异较大。OEM 图库可能会走更靠近底层的路径:

复制代码
1. MediaCodec / vendor HEVC decoder
2. 厂商私有 heif decoder so
3. SoC VPU / MFC / hardware codec
4. 直接解 HEIC 内嵌缩略图
5. 先解 preview item,再解 full image

对于 HEIC,真正关键不是"自己写一个 HEIC 解码器",而是:

能不能接入设备已有的 HEVC 硬解能力,并且减少 YUV 到 RGB、RGB 到 GPU texture 的多次转换。

2.3 AVIF:软件 dav1d/libgav1 + 有硬解时优先硬解

AVIF 解码计算量大。行业常见策略:

复制代码
Android 12+:
  优先系统 ImageDecoder / MediaCodec 能力

自有图库:
  优先硬件 AV1 still decode,如果设备支持
  否则 dav1d / libgav1 软件解码
  小图可以同步或短任务解
  大图必须后台线程 + 渐进式占位

但对"相机左下角缩略图进入图库首帧"来说,AVIF 通常不是第一优先级,除非相机默认产物就是 AVIF。

2.4 WebP:libwebp 或系统 decoder,重点是动画/透明图

WebP 的 OEM 定制价值通常低于 JPEG/HEIC,因为 Android 系统支持已经比较成熟。

优化点一般是:

复制代码
1. 静态 WebP 走 libwebp fast path
2. 动态 WebP 做帧缓存/帧复用
3. 透明 WebP 避免不必要 premultiply/unpremultiply
4. 小图批量解码时复用内存池

2.5 PNG:多数情况下不作为主攻方向

PNG 通常不是相机大图主格式,且解码瓶颈经常在 zlib inflate 和滤波。

OEM 会优化,但图库大图首帧收益一般不如 JPEG/HEIC。

可能做法:

复制代码
1. libpng + zlib-ng / cloudflare zlib / ISA-L 类优化
2. 针对常见 RGBA PNG 快路径
3. 避免 alpha premultiply 重复转换

但如果是图库展示应用,P0 不建议先重投入 PNG。

3. 更关键的 OEM 优化:不是"解码更快",而是"少解、早出、少拷贝"

3.1 先用相机 handoff thumbnail 出首帧

三星、小米、OPPO 这类系统级相机/图库通常能做相机和图库协同:

复制代码
相机拍照后:
  1. 相机已经有 preview frame
  2. 相机已经生成左下角缩略图
  3. 相机知道最新照片 uri/path/id/orientation
  4. 相机可把 thumbnail cache key 传给图库

图库点击进入时不需要马上 decode 原图,而是:

复制代码
第 1 帧:直接显示相机 handoff thumbnail
第 2 阶段:显示 embedded thumbnail / screen-size preview
第 3 阶段:显示完整高清图

这比换 decoder 更影响用户感知。

3.2 优先解内嵌缩略图,而不是原图

JPEG/HEIC 文件里通常有 embedded thumbnail。

OEM decoder 会先解析:

复制代码
EXIF thumbnail
HEIF item thumbnail
相机数据库里的缩略图
MediaProvider 缓存图
图库自有 cache

如果目标只是首帧,优先级通常是:

复制代码
相机 handoff bitmap
  >
EXIF embedded thumbnail
  >
MediaStore thumbnail
  >
图库磁盘 cache
  >
按屏幕尺寸 decode 原图
  >
全尺寸 decode 原图

普通业务如果直接 BitmapFactory.decodeStream(uri)ImageDecoder.decodeBitmap(source),很容易一步到位解大图,首帧自然慢。

3.3 按目标尺寸解码,不全尺寸解码

图库大图首帧通常不需要 8000x6000 全图。

OEM 会根据屏幕尺寸计算 sample size 或 decoder scale:

复制代码
原图:8000 x 6000
屏幕:1080 x 2400
首帧目标:约 1080 x 810 或 1440 x 1080

所以首帧 decode 可以只解:

复制代码
1/2
1/4
1/8

尤其 JPEG 原生支持 DCT scale:

复制代码
1/1, 1/2, 1/4, 1/8

libjpeg-turbo 对这种 downscale decode 很适合。

3.4 Region decode / Tile decode

对于超大图,OEM 图库通常不会一次性解完整 bitmap,而是:

复制代码
先解屏幕可见区域
缩放/平移时按 tile 请求
后台预解邻近 tile

类似地图瓦片。

这对于:

复制代码
长图
超高像素照片
全景图
扫描件
大尺寸 PNG/JPEG

收益非常明显。

技术形态:

复制代码
ImageRegionDecoder
BitmapRegionDecoder
自研 native tile decoder
libjpeg-turbo region crop + scale
HEIF grid item decode

3.5 减少 native -> Java -> GPU 的拷贝

很多图库卡顿不是纯 decode 慢,而是 copy 和上传慢:

复制代码
native decode buffer
  ↓ copy
Java Bitmap
  ↓ copy / lock
HardwareBitmap
  ↓ GPU upload
Texture

OEM 会尽量做:

复制代码
1. native 内存池复用
2. 直接解到目标像素格式
3. 避免 ARGB_8888/RGBA_8888 反复转换
4. 使用 HardwareBuffer / GraphicBuffer / DMA-BUF
5. 减少 Bitmap copy
6. 图片和 GL 纹理之间做更直接的路径

如果trace 里看到过大量 copyHWBitmapInto,这个方向尤其重要。

4. OEM 可能使用的底层能力

不同 SoC 厂商有不同能力,OEM 会按机型路由。

4.1 Qualcomm 平台常见方向

可能涉及:

复制代码
1. Qualcomm JPEG hardware decoder / encoder
2. Camera HAL 生成缩略图
3. DSP/VPU/codec path
4. ION / DMA-BUF / gralloc buffer
5. vendor libjpeg / mmjpeg 类库

在一些设备上可以看到类似 vendor so,但名字因平台和系统版本差异很大,例如:

复制代码
/vendor/lib64/libmmjpeg*
/vendor/lib64/libqomx*
/vendor/lib64/libjpeg*
/vendor/lib64/libimage*

这些不一定都能给普通 App 调用,很多是 HAL/vendor 进程内部使用。

4.2 Exynos / 三星平台常见方向

三星自家设备可能结合:

复制代码
1. Exynos MFC / hardware codec
2. Samsung 自有 media/image framework
3. 自家 Gallery/Camera handoff
4. HEIF/SEF/运动照片等私有扩展解析
5. 自家缩略图缓存和媒体数据库

三星图库不仅是 decode 快,更多是系统级协同强:

复制代码
相机产物
图库数据库
缩略图缓存
私有 metadata
硬件 codec
窗口转场

都可以被统一控制。

4.3 MediaTek 平台常见方向

可能涉及:

复制代码
1. MDP / VPU / JPEG hardware block
2. mhal image codec
3. vendor camera pipeline 直接生成缩略图
4. gralloc/dma buffer 共享

这类能力一般更靠近 HAL/vendor 层,普通三方 App 不一定能直接用。

5. 三星/小米/OPPO 类图库通常会做的"解码器路由器"

一个成熟图库不会只有一个 decoder,而是会有一个 ImageDecodeRouter。

可以理解成:

复制代码
ImageDecodeRouter
  ├── JPEG
  │     ├── embedded thumbnail fast path
  │     ├── libjpeg-turbo software path
  │     ├── hardware jpeg path, if available
  │     └── Skia fallback
  │
  ├── HEIC/HEIF
  │     ├── embedded thumbnail
  │     ├── MediaCodec/vendor HEVC still decode
  │     ├── libheif software path
  │     └── Skia/ImageDecoder fallback
  │
  ├── AVIF
  │     ├── hardware AV1 if available
  │     ├── dav1d/libgav1/libavif
  │     └── system fallback
  │
  ├── WebP
  │     ├── libwebp
  │     └── system fallback
  │
  └── PNG
        ├── libpng / Wuffs / system
        └── system fallback

路由条件包括:

复制代码
格式
图片尺寸
是否首帧
是否缩略图
是否动图
是否 HDR/10bit
是否广色域
是否需要 region decode
是否当前处于转场动画
设备型号
SoC 能力
系统版本
内存压力
温度/电量

6. OEM 对首帧的特别优化

针对"点击相机缩略图进入图库第一帧",OEM 通常重点做这些。

相机拍照完成后立即准备:

复制代码
latest_photo_uri
latest_photo_id
latest_photo_orientation
latest_photo_width/height
latest_thumbnail_bitmap
latest_screen_preview

图库启动时直接拿:

复制代码
不查库或少查库
不解原图
不等 MediaScanner
不等 EXIF 完整解析

6.2 图库进程预热

拍照后用户很可能点击缩略图,所以 OEM 可能会提前 warm up:

复制代码
Gallery process
native decoder so
图片加载线程池
MediaStore 最近一张查询
缩略图 cache
PhotoPage 关键 class

这类预热如果是系统应用/同签名应用更容易做。

6.3 轻量入口页

不是直接启动笨重的完整大图页,而是:

复制代码
QuickLookActivity / PhotoPreviewActivity
  ↓
只显示最新照片
  ↓
首帧后再加载完整 PhotoPage

这类方案比 decoder 替换更稳。

6.4 解码任务错峰

OEM 会避免在首帧同时做:

复制代码
原图 decode
邻页预加载
EXIF 全量解析
云同步状态查询
人脸/场景识别
缩略图写盘
日志落盘
GPU shader 编译

首帧只做必要任务。

7. 如果要仿照 OEM 做法,这样设计

7.1 不建议只做一个 native decoder

建议做:

复制代码
ImageDecodeEngine
  ├── DecodeRouter
  ├── ThumbnailProvider
  ├── NativeDecoder JNI
  ├── HardwareDecodeAdapter
  ├── Bitmap/Buffer Pool
  ├── Region/Tile Decoder
  ├── Progressive Loader
  └── Fallback to Android ImageDecoder/Skia

7.2 P0:JPEG 先打穿

图库场景 P0 推荐:

复制代码
1. libjpeg-turbo 接入
2. 支持 DCT scale decode
3. 支持 EXIF thumbnail decode
4. 支持屏幕尺寸 decode
5. 支持 native 内存池
6. 支持 orientation 一次性处理
7. 支持 Skia fallback

目标不是"所有图片都快",而是先让相机最新 JPEG 大图首帧变快。

7.3 P1:HEIC/HEIF 独立优化

如果相机默认 HEIC,需要单独做:

复制代码
1. 优先读 HEIC 内嵌 thumbnail
2. 优先系统/硬件 HEIF decoder
3. Android 版本差异化路由
4. libheif 作为 fallback
5. 避免首帧 full decode

这非常关键。

相机点击缩略图进入图库时,Intent 里不只传 URI,还可以传:

复制代码
media_uri
media_id
orientation
width/height
mime_type
thumbnail_cache_key
preview_cache_key
shot_timestamp

缩略图/preview 通过:

复制代码
共享内存
ContentProvider
文件缓存
内存 cache service
同进程 cache,如果相机图库同 APK

图库第一帧直接拿 handoff image。

7.5 P2:硬件解码适配层

如果你们是系统应用或有厂商平台权限,可以研究:

复制代码
MediaCodec still image decode
vendor jpeg decoder
vendor heif decoder
HardwareBuffer output
DMA-BUF / gralloc

如果只是普通应用,不建议强依赖 vendor 私有 so,兼容性和权限风险很高。

8. 怎么验证三星/小米/OPPO 设备实际用了什么

可以这样看。

8.1 看 APK native so

拉取图库 APK 后看:

复制代码
unzip Gallery.apk -d gallery_apk
find gallery_apk -name "*.so"

关注:

复制代码
libjpeg
libturbojpeg
libheif
libavif
libwebp
libimagecodec
libgallery
libnativeimage
libphotoeditor

再用:

复制代码
readelf -d xxx.so
nm -D xxx.so
strings xxx.so | grep -i jpeg
strings xxx.so | grep -i heif
strings xxx.so | grep -i turbo

可以看出是否静态/动态链接了相关 decoder。

8.2 看 trace

用图库打开大图,关注:

复制代码
jpeg_read_scanlines
tjDecompress
jsimd_*
idct
ycc_rgb_convert
SkJpegCodec
SkCodec
libheif
dav1d
libwebp
MediaCodec
vendor jpeg/heif symbols

如果符号被 strip,仍可以通过 so 名称和调用栈判断。

8.3 看 Perfetto

抓这些:

复制代码
gfx
view
wm
am
camera
freq
sched
disk
binder_driver
hal

关注:

复制代码
图库首帧前有没有 decode 线程
是否有 vendor codec 进程
是否有 MediaCodec
是否有大量 IO
是否有 GPU upload/copy
是否有 HardwareBuffer/gralloc
是否先显示 thumbnail 后替换高清图

8.4 看 logcat

过滤:

复制代码
logcat | grep -iE "jpeg|heif|heic|avif|thumbnail|decode|codec|gallery"

OEM 经常会有私有 tag,能看到一些线索。

9. 从行业实践看是组合拳

如果目标是大幅降低相机缩略图进图库首帧时间,建议优先级如下:

复制代码
P0:
1. 相机 handoff thumbnail / preview 给图库
2. 图库轻量 QuickLook 首帧
3. 首帧只显示缩略图,不 full decode
4. JPEG 走 libjpeg-turbo screen-size decode
5. 首帧后再高清替换

P1:
1. HEIC/HEIF 硬解或系统 ImageDecoder 路由
2. region/tile decode
3. native buffer pool
4. 减少 Bitmap/HardwareBitmap copy
5. 图库进程和 decoder 预热

P2:
1. 接入 vendor hardware jpeg/heif decoder
2. HardwareBuffer / DMA-BUF zero-copy
3. 平台级 RemoteTransition / SurfaceControl handoff

10. 总结

三星、小米、OPPO 这类 OEM 的定制解码器能力,通常不是单纯替换 Skia,而是:

复制代码
底层:
  libjpeg-turbo / libheif / libwebp / vendor codec / hardware decoder

中层:
  decoder router / native memory pool / region decode / tile decode / buffer reuse

系统层:
  Camera HAL thumbnail / MediaProvider cache / Gallery warmup / vendor hardware path

业务层:
  相机 handoff thumbnail / 轻量首帧页 / 高清图延后 / 非首屏任务错峰

如果要做类似能力,从 JPEG + 相机图库 handoff + 轻量首帧 + libjpeg-turbo screen-size decode 开始,这条路径收益最大、风险最低。后面再逐步扩展到 HEIC/AVIF、硬件解码、HardwareBuffer 零拷贝和 tile decode。

推荐一个人工智能网站