前言
最近看了下这方面的资料,顺便也做个整理记录,就先以用得较广泛的ETC1纹理压缩开个头,着重讲下是如何把ETC数据还原成RGB图像数据。
主要是基于这篇资料的翻译和理解,如有错漏之处,还望斧正。
介绍
Ericsson Texture Compression (ETC)是由 Khronos 支持的开放标准,在移动平台中广泛采用。它是一种为感知质量设计的有损算法,其依据是人眼对亮度改变的反应要高于色度改变。对于24位的RGB数据,提供6倍的压缩率。最大的弊端是不支持Alpha通道的存储,不过在后续的ETC2中也得到了支持。 维基百科
原理
ETC的压缩纹理以4x4的像素块为单位的方式描述出来。如果纹理在任意方向小于4个像素,那么只有位于左上角的像素块才是可用的。
单个像素块
---- ---- ---- ----
|a |e |i |m |
| | | | |
---- ---- ---- ----
|b |f |j |n |
| | | | |
---- ---- ---- ----
|c |g |k |o |
| | | | |
---- ---- ---- ----
|d |h |l |p |
| | | | |
---- ---- ---- ----
一个4x4的像素块的数据用64bit的数据来表示。4x4的块被分成两个子块,垂直分(2x4)或者水平分(4x2),每一个子块有一组基础颜色(分别是RGB444/RGB444或RGB555/RGB333格式)、亮度索引、像素索引。
每个像素的颜色等于所属子块基础颜色加上索引指向的亮度修正。
垂直分块
子块 1 子块 2
---- ---- ---- ----
|a e |i m |
| | |
| | |
|b f |j n |
| | |
| | |
|c g |k o |
| | |
| | |
|d h |l p |
| | |
---- ---- ---- ----
水平分块
---- ---- ---- ----
|a e i m |
| |
| | 子块 1
|b f j n |
| |
-------------------
|c g k o |
| |
| | 子块 2
|d h l p |
| |
---- ---- ---- ----
下面讲下这64bit的数据是存放和使用的
子块基础颜色计算
2个子块的基础颜色根据32-63bit的数据而决定。ETC1里有两种模式:individual模式和differential模式。
这两种模式决定了子块存储的颜色是以RGB444/RGB444(individual模式)还是RGB555/RGB333(differential模式)格式存储颜色的数据,由bit 33(称为 diffbit)的值来决定,值为0时是individual模式,为1时是differential模式。
32bit到63bit,当 diffbit = 0时的布局
63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
-----------------------------------------------
| base col1 | base col2 | base col1 | base col2 |
| R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)|
-----------------------------------------------
47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
---------------------------------------------------
| base col1 | base col2 | table | table |diff|flip|
| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit |
---------------------------------------------------
32bit到63bit,当 diffbit = 1时的布局
63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
-----------------------------------------------
| base col1 | dcol 2 | base col1 | dcol 2 |
| R1' (5 bits) | dR2 | G1' (5 bits) | dG2 |
-----------------------------------------------
47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
---------------------------------------------------
| base col 1 | dcol 2 | table | table |diff|flip|
| B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit |
---------------------------------------------------
- Individual模式
每个子块以RGB444/RGB444方式存储,将4bit颜色扩充为8bit,高4位和低4位相同
举例:子块1存储的RGB数据是R1=14=1110b,G1=3=0011b,B1=8=1000b
那么还原后的子块1基础RGB为R1=11101110b=238,G1=00110011b=51,B1=10001000b=136
子块2同理
base col subblock1 = extend_4to8bits(R1, G1, B1)
base col subblock2 = extend_4to8bits(R2, G2, B2)
- Differential模式
子块1以RGB555,子块2以RGB333方式存储,子块1的颜色是以5bit的高3位作为扩充为8位之后的低3位
举例:子块1存储的RGB数据是R1=28=11100b,G1=4=00100b,B1=3=00011b
那么还原后的子块1基础RGB为R1=11100111b=231,G1=00100001b=33,B1=00011000b=24
子块2计算时,先要通过和子块1存储的RGB叠加。把RGB333转为RGB555,然后再扩充为RGB888
原有RGB333可看作是带符号位的,所以取值范围是[-4, 3]
举例:和上面一样有R1’=28,的dR2=100b=-4,R1’+dR2=24=11000b,转成8bit则为,R2=11000110=198,G2B2同理
base col subblock1 = extend_5to8bits(R1’, G1’, B1’)
base col subblock2 = extend_5to8bits(R1’+dR2, G1’+dG2, B1’+dG2)
分块方式
分块方式根据bit 32来决定(称为flipbit),flipbit=0为垂直分(2x4),flipbit=1为水平分(4x2)。
像素RGB的最终确定
通过上面的步骤可以得出两个分块的基础颜色,然后需要确定每个像素的修正值,和基础颜色进行叠加,最后限定在[0, 255]内。
- 亮度修正表
亮度修正表定义了8组修正的值(固定不变的),每组包含4个值,具体的像素采用哪个修正值还需要通过修正结果映射表(下面会说到)来决定。
继续引用上面表格,37bit到39bit这3bit存储的是子块1的亮度修正表索引,34bit到36bit是存了子块2的亮度修正表索引。
举例:37bit到39bit的值为010b=2,则得到修正值为{-17,-5,5,17}
codewords |
modifier value1 |
modifier value2 |
modifier value3 |
modifier value4 |
0 |
-8 |
-2 |
2 |
8 |
1 |
-17 |
-5 |
5 |
17 |
2 |
-29 |
-9 |
9 |
29 |
3 |
-42 |
-13 |
13 |
42 |
4 |
-60 |
-18 |
18 |
60 |
5 |
-80 |
-24 |
24 |
80 |
6 |
-106 |
-33 |
33 |
106 |
7 |
-183 |
-47 |
47 |
183 |
- 修正结果映射表
0bit到31bit之间是记录4x4个像素采用的索引值,每个像素由2bit(msb和lsb)决定。
0-15bit之间的值为lsb(least significant bit),16-31bit之间的值为msb(most significant bit),第一个像素对应0bit和16bit,第二个像素对应1bit和17bit…如此类推。
msb和lsb可以称为像素下标位。
上面已经根据亮度索引从亮度修正表里面得出了四个值,现在可以通过得到的msb和lsb,在修正结果映射表里面得到一个最终的修正值。
msb |
lsb |
最后的修正值 |
1 |
1 |
modifier value1 |
1 |
0 |
modifier value2 |
0 |
0 |
modifier value3 |
0 |
1 |
modifier value4 |
- 最终结果计算举例
算出像素所在子块的基础色为(231, 8, 16),亮度修正索引为010b=2,对应值为{-29, -9, 9, 29},像素下标位为01b=1,所以取得修正值为29,叠加后得到(260, 37, 45),修正在[0, 255],所以最终结果是(255, 37, 45)