ETC1压缩纹理格式详解-阿里云开发者社区

开发者社区> 梅花落> 正文

ETC1压缩纹理格式详解

简介: 本来以为,ETC1作为Android 设备的OpenGL标准,开源且最常用的的一种压缩纹理格式,总会有人去翻译一下khronos的文档,读一下代码,给大家作个普及的,不料就是搜不到。没办法,尽管英文不好,还是硬啃了下文档,把 ETC1压缩纹理的实现原理弄清楚了。 https://www.khronos.org/registry/gles/extensions/OES/OES_
+关注继续查看

本来以为,ETC1作为Android 设备的OpenGL标准,开源且最常用的的一种压缩纹理格式,总会有人去翻译一下khronos的文档,读一下代码,给大家作个普及的,不料就是搜不到。没办法,尽管英文不好,还是硬啃了下文档,把 ETC1压缩纹理的实现原理弄清楚了。
https://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt

至于什么是压缩纹理,如何使用,可以参考:
http://blog.csdn.net/wanglang3081/article/details/8869589

ETC1的文件头

文件头大小为16:

#define ETC_PKM_HEADER_SIZE 16

将ETC1纹理存成pkm文件时,加上这个文件头,便于读取时获知大小、格式,上传压缩纹理时把这个头去掉。

文件头内容为:特征符——编码宽——编码高——实际宽——实际高

static const char kMagic[] = { 'P', 'K', 'M', ' ', '1', '0' };

static const etc1_uint32 ETC1_PKM_FORMAT_OFFSET = 6;
static const etc1_uint32 ETC1_PKM_ENCODED_WIDTH_OFFSET = 8;
static const etc1_uint32 ETC1_PKM_ENCODED_HEIGHT_OFFSET = 10;
static const etc1_uint32 ETC1_PKM_WIDTH_OFFSET = 12;
static const etc1_uint32 ETC1_PKM_HEIGHT_OFFSET = 14;

尽管ETC1是固定的压缩比,但考虑到像素不对齐的情况,实际宽和实际高还是有必要存储的。

编码构成

Jpeg压缩标准是把图像划分为一系列8X8的像素块,然后每个像素块压缩成变长编码的。ETC1则是4X4的像素块压缩成固定的64位编码(8字节),由于固定,才有利于GPU内部实现并行解压缩。

#define ETC1_ENCODED_BLOCK_SIZE 8
#define ETC1_DECODED_BLOCK_SIZE 48 // RGB 三分量 * 4 * 4

因此,不考虑像素非4对齐的情况,它的压缩比是固定的48/8=6,至于常用的把ARGB分别存储为两张ETC1纹理的做法,压缩比是64/16=4。
像素不对齐的情况如何处理,就自己想想吧,反正不是大问题。

4X4的像素点与64位编码对应关系如下:
1、将 4X4 的像素点划分为两个subblock,横分或者竖分【1个位表示:flipbit】。
2、两部分的像素RGB求均值,总共用24个位表示两个RGB均值,分两种方案【1个位表示:diffbit】
(1)两个subblock的RGB均值都用4位表示。刚好4*3*2=24位。
(2)两个subblock的RGB值相差在 [-4,3]区间(5位表示的情况下,对应常规的8位表示是[-32,24]),第一个subblock的RGB值用5位表示,第二个用3位差值表示。
3、解码时,像素值由RGB均值加上差值而得,差值表为8X4大小。每一个subblock使用的是其中一行【行号需要3个位,两个subblock,加起来6个位】,每个像素点对应所取差值行中一个数【编号需要2个位,这样就16X2=32个位】。PS:RGB三个分量是用同一个差值。

64位的编码分成两部分,高位和低位。
低位存储每个像素点的差值行序号,按大端存储方式,分两部分,前一部分存储高位,后一部分存储低位。
高位存储RGB均值,subblock所用的差值表行号,再加上 flipbit 和 diffbit
详细内容直接上文档上的图:
total

横分/竖分示例图,图中每个字母代码一个像素:
flip

编码过程

ETC1的编码流程如下:
1、将图像划分为一系列 4X4 的子块。
2、对每个子块,尝试所有的编码可能性,取解码后和原block像素差值和最小的一种编码。
(1)是否flip,这个决定subblock如何划分
(2)每个subblock用哪一行差值表
(3)每个像素取哪一列的差值
注:决定好flip之后,颜色均值和是否能用diff方式已经确定,这个不用遍历。
3、合并所有子块编码。

不难看出,编码过程需要遍历所有可能性,其复杂度远大于解码,每一个 RGB 像素变成了一个精度较低的RGB均值和一个2位差值号,因此产生压缩。这种预测式表述自然本身就存在偏差,压缩损失亦来自于此。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
【Android 应用开发】Activity 状态保存 OnSaveInstanceState参数解析
【Android 应用开发】Activity 状态保存 OnSaveInstanceState参数解析
7 0
【嵌入式开发】 Linux Kernel 下载 配置 编译 安装 及 驱动简介(二)
【嵌入式开发】 Linux Kernel 下载 配置 编译 安装 及 驱动简介(二)
5 0
面试侃集合 | PriorityBlockingQueue篇
面试侃集合 | PriorityBlockingQueue篇
3 0
【Android 应用开发】Android 图表绘制 achartengine 示例解析(一)
【Android 应用开发】Android 图表绘制 achartengine 示例解析(一)
5 0
M1 MacBook可以运行Linux了,Linux之父宣布Linux 5.13公测开始!
Linux 5.13来了!周二Linus Torvalds宣布候选版本Linux 5.13可以进行公测,让人高兴的是,这次更新还引入了对苹果M1芯片的支持。
4 0
【Android 应用开发】Android 图表绘制 achartengine 示例解析(二)
【Android 应用开发】Android 图表绘制 achartengine 示例解析(二)
4 0
【Android 系统开发】Android JNI 之 JNIEnv 解析(二)
【Android 系统开发】Android JNI 之 JNIEnv 解析(二)
5 0
【Android 应用开发】Android 图表绘制 achartengine 示例解析(一)
【Android 应用开发】Android 图表绘制 achartengine 示例解析(一)
3 0
简单的Postman,还能玩出花?
简单的Postman,还能玩出花?
4 0
Spring集成redis实现分布式会话
在web项目开发过程中,会话通常用于存储一些业务中间数据、状态数据,是十分重要的一部分,在单机环境下这十分容易,但是在分布式的环境下,单机无法实现,就需要有一个完整的方案,目前业界常见的方案有两种:(1)Spring集成redis实现分布式会话(2)使用JWT(JSON Web Token)实现。 本文介绍在springboot环境下如何利用spring集成redis来实现分布式会话,并提供了相关代码和各项配置。
3 0
+关注
39
文章
3
问答
文章排行榜
最热
最新
相关电子书
更多
《Nacos架构&原理》
立即下载
《看见新力量:二》电子书
立即下载
云上自动化运维(CloudOps)白皮书
立即下载