1a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// Copyright 2009 Google Inc.
2a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich//
3a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// Licensed under the Apache License, Version 2.0 (the "License");
4a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// you may not use this file except in compliance with the License.
5a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// You may obtain a copy of the License at
6a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich//
7a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich//     http://www.apache.org/licenses/LICENSE-2.0
8a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich//
9a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// Unless required by applicable law or agreed to in writing, software
10a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// distributed under the License is distributed on an "AS IS" BASIS,
11a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// See the License for the specific language governing permissions and
13a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// limitations under the License.
14a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
15a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich#include <ETC1/etc1.h>
16a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
17a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich#include <string.h>
18a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
19a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich/* From http://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
20a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
21a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich The number of bits that represent a 4x4 texel block is 64 bits if
22a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich <internalformat> is given by ETC1_RGB8_OES.
23a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
24a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich The data for a block is a number of bytes,
25a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
26a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich {q0, q1, q2, q3, q4, q5, q6, q7}
27a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
28a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich where byte q0 is located at the lowest memory address and q7 at
29a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich the highest. The 64 bits specifying the block is then represented
30a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich by the following 64 bit integer:
31a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
32a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich int64bit = 256*(256*(256*(256*(256*(256*(256*q0+q1)+q2)+q3)+q4)+q5)+q6)+q7;
33a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
34a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich ETC1_RGB8_OES:
35a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
36a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich a) bit layout in bits 63 through 32 if diffbit = 0
37a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
38a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
39a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich -----------------------------------------------
40a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich | base col1 | base col2 | base col1 | base col2 |
41a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)|
42a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich -----------------------------------------------
43a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
44a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 47 46 45 44 43 42 41 40 39 38 37 36 35 34  33  32
45a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich ---------------------------------------------------
46a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich | base col1 | base col2 | table  | table  |diff|flip|
47a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich | B1 (4bits)| B2 (4bits)| cw 1   | cw 2   |bit |bit |
48a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich ---------------------------------------------------
49a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
50a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
51a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich b) bit layout in bits 63 through 32 if diffbit = 1
52a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
53a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
54a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich -----------------------------------------------
55a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich | base col1    | dcol 2 | base col1    | dcol 2 |
56a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich | R1' (5 bits) | dR2    | G1' (5 bits) | dG2    |
57a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich -----------------------------------------------
58a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
59a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 47 46 45 44 43 42 41 40 39 38 37 36 35 34  33  32
60a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich ---------------------------------------------------
61a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich | base col 1   | dcol 2 | table  | table  |diff|flip|
62a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich | B1' (5 bits) | dB2    | cw 1   | cw 2   |bit |bit |
63a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich ---------------------------------------------------
64a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
65a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
66a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich c) bit layout in bits 31 through 0 (in both cases)
67a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
68a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
69a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich -----------------------------------------------
70a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich |       most significant pixel index bits       |
71a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a|
72a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich -----------------------------------------------
73a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
74a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 15 14 13 12 11 10  9  8  7  6  5  4  3   2   1  0
75a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich --------------------------------------------------
76a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich |         least significant pixel index bits       |
77a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich | p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a |
78a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich --------------------------------------------------
79a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
80a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
81a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich Add table 3.17.2: Intensity modifier sets for ETC1 compressed textures:
82a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
83a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich table codeword                modifier table
84a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich ------------------        ----------------------
85a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 0                     -8  -2  2   8
86a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 1                    -17  -5  5  17
87a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 2                    -29  -9  9  29
88a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 3                    -42 -13 13  42
89a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 4                    -60 -18 18  60
90a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 5                    -80 -24 24  80
91a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 6                   -106 -33 33 106
92a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 7                   -183 -47 47 183
93a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
94a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
95a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich Add table 3.17.3 Mapping from pixel index values to modifier values for
96a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich ETC1 compressed textures:
97a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
98a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich pixel index value
99a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich ---------------
100a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich msb     lsb           resulting modifier value
101a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich -----   -----          -------------------------
102a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 1       1            -b (large negative value)
103a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 1       0            -a (small negative value)
104a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 0       0             a (small positive value)
105a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich 0       1             b (large positive value)
106a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
107a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
108a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich */
109a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
110a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic const int kModifierTable[] = {
111a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich/* 0 */2, 8, -2, -8,
112a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich/* 1 */5, 17, -5, -17,
113a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich/* 2 */9, 29, -9, -29,
114a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich/* 3 */13, 42, -13, -42,
115a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich/* 4 */18, 60, -18, -60,
116a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich/* 5 */24, 80, -24, -80,
117a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich/* 6 */33, 106, -33, -106,
118a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich/* 7 */47, 183, -47, -183 };
119a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
120a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic const int kLookup[8] = { 0, 1, 2, 3, -4, -3, -2, -1 };
121a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
122a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic inline etc1_byte clamp(int x) {
123a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return (etc1_byte) (x >= 0 ? (x < 255 ? x : 255) : 0);
124a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
125a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
126a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic
127a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichinline int convert4To8(int b) {
128a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int c = b & 0xf;
129a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return (c << 4) | c;
130a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
131a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
132a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic
133a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichinline int convert5To8(int b) {
134a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int c = b & 0x1f;
135a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return (c << 3) | (c >> 2);
136a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
137a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
138a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic
139a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichinline int convert6To8(int b) {
140a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int c = b & 0x3f;
141a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return (c << 2) | (c >> 4);
142a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
143a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
144a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic
145a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichinline int divideBy255(int d) {
146a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return (d + 128 + (d >> 8)) >> 8;
147a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
148a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
149a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic
150a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichinline int convert8To4(int b) {
151a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int c = b & 0xff;
152a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return divideBy255(b * 15);
153a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
154a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
155a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic
156a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichinline int convert8To5(int b) {
157a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int c = b & 0xff;
158a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return divideBy255(b * 31);
159a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
160a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
161a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic
162a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichinline int convertDiff(int base, int diff) {
163a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return convert5To8((0x1f & base) + kLookup[0x7 & diff]);
164a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
165a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
166a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic
167a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichvoid decode_subblock(etc1_byte* pOut, int r, int g, int b, const int* table,
168a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        etc1_uint32 low, bool second, bool flipped) {
169a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int baseX = 0;
170a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int baseY = 0;
171a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    if (second) {
172a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        if (flipped) {
173a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            baseY = 2;
174a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        } else {
175a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            baseX = 2;
176a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
177a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    }
178a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    for (int i = 0; i < 8; i++) {
179a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int x, y;
180a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        if (flipped) {
181a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            x = baseX + (i >> 1);
182a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            y = baseY + (i & 1);
183a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        } else {
184a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            x = baseX + (i >> 2);
185a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            y = baseY + (i & 3);
186a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
187a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int k = y + (x * 4);
188a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int offset = ((low >> k) & 1) | ((low >> (k + 15)) & 2);
189a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int delta = table[offset];
190a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        etc1_byte* q = pOut + 3 * (x + 4 * y);
191a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        *q++ = clamp(r + delta);
192a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        *q++ = clamp(g + delta);
193a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        *q++ = clamp(b + delta);
194a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    }
195a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
196a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
197a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// Input is an ETC1 compressed version of the data.
198a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// Output is a 4 x 4 square of 3-byte pixels in form R, G, B
199a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
200a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichvoid etc1_decode_block(const etc1_byte* pIn, etc1_byte* pOut) {
201a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 high = (pIn[0] << 24) | (pIn[1] << 16) | (pIn[2] << 8) | pIn[3];
202a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 low = (pIn[4] << 24) | (pIn[5] << 16) | (pIn[6] << 8) | pIn[7];
203a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int r1, r2, g1, g2, b1, b2;
204a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    if (high & 2) {
205a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        // differential
206a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int rBase = high >> 27;
207a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int gBase = high >> 19;
208a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int bBase = high >> 11;
209a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        r1 = convert5To8(rBase);
210a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        r2 = convertDiff(rBase, high >> 24);
211a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        g1 = convert5To8(gBase);
212a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        g2 = convertDiff(gBase, high >> 16);
213a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        b1 = convert5To8(bBase);
214a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        b2 = convertDiff(bBase, high >> 8);
215a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    } else {
216a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        // not differential
217a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        r1 = convert4To8(high >> 28);
218a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        r2 = convert4To8(high >> 24);
219a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        g1 = convert4To8(high >> 20);
220a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        g2 = convert4To8(high >> 16);
221a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        b1 = convert4To8(high >> 12);
222a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        b2 = convert4To8(high >> 8);
223a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    }
224a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int tableIndexA = 7 & (high >> 5);
225a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int tableIndexB = 7 & (high >> 2);
226a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    const int* tableA = kModifierTable + tableIndexA * 4;
227a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    const int* tableB = kModifierTable + tableIndexB * 4;
228a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    bool flipped = (high & 1) != 0;
229a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    decode_subblock(pOut, r1, g1, b1, tableA, low, false, flipped);
230a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    decode_subblock(pOut, r2, g2, b2, tableB, low, true, flipped);
231a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
232a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
233a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichtypedef struct {
234a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 high;
235a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 low;
236a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 score; // Lower is more accurate
237a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich} etc_compressed;
238a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
239a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic
240a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichinline void take_best(etc_compressed* a, const etc_compressed* b) {
241a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    if (a->score > b->score) {
242a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        *a = *b;
243a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    }
244a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
245a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
246a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic
247a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichvoid etc_average_colors_subblock(const etc1_byte* pIn, etc1_uint32 inMask,
248a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        etc1_byte* pColors, bool flipped, bool second) {
249a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int r = 0;
250a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int g = 0;
251a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int b = 0;
252a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
253a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    if (flipped) {
254a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int by = 0;
255a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        if (second) {
256a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            by = 2;
257a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
258a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        for (int y = 0; y < 2; y++) {
259a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            int yy = by + y;
260a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            for (int x = 0; x < 4; x++) {
261a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                int i = x + 4 * yy;
262a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                if (inMask & (1 << i)) {
263a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                    const etc1_byte* p = pIn + i * 3;
264a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                    r += *(p++);
265a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                    g += *(p++);
266a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                    b += *(p++);
267a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                }
268a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            }
269a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
270a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    } else {
271a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int bx = 0;
272a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        if (second) {
273a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            bx = 2;
274a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
275a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        for (int y = 0; y < 4; y++) {
276a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            for (int x = 0; x < 2; x++) {
277a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                int xx = bx + x;
278a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                int i = xx + 4 * y;
279a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                if (inMask & (1 << i)) {
280a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                    const etc1_byte* p = pIn + i * 3;
281a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                    r += *(p++);
282a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                    g += *(p++);
283a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                    b += *(p++);
284a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                }
285a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            }
286a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
287a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    }
288a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pColors[0] = (etc1_byte)((r + 4) >> 3);
289a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pColors[1] = (etc1_byte)((g + 4) >> 3);
290a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pColors[2] = (etc1_byte)((b + 4) >> 3);
291a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
292a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
293a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic
294a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichinline int square(int x) {
295a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return x * x;
296a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
297a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
298a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic etc1_uint32 chooseModifier(const etc1_byte* pBaseColors,
299a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        const etc1_byte* pIn, etc1_uint32 *pLow, int bitIndex,
300a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        const int* pModifierTable) {
301a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 bestScore = ~0;
302a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int bestIndex = 0;
303a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int pixelR = pIn[0];
304a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int pixelG = pIn[1];
305a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int pixelB = pIn[2];
306a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int r = pBaseColors[0];
307a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int g = pBaseColors[1];
308a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int b = pBaseColors[2];
309a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    for (int i = 0; i < 4; i++) {
310a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int modifier = pModifierTable[i];
311a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int decodedG = clamp(g + modifier);
312a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        etc1_uint32 score = (etc1_uint32) (6 * square(decodedG - pixelG));
313a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        if (score >= bestScore) {
314a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            continue;
315a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
316a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int decodedR = clamp(r + modifier);
317a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        score += (etc1_uint32) (3 * square(decodedR - pixelR));
318a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        if (score >= bestScore) {
319a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            continue;
320a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
321a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int decodedB = clamp(b + modifier);
322a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        score += (etc1_uint32) square(decodedB - pixelB);
323a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        if (score < bestScore) {
324a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            bestScore = score;
325a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            bestIndex = i;
326a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
327a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    }
328a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 lowMask = (((bestIndex >> 1) << 16) | (bestIndex & 1))
329a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            << bitIndex;
330a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    *pLow |= lowMask;
331a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return bestScore;
332a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
333a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
334a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic
335a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichvoid etc_encode_subblock_helper(const etc1_byte* pIn, etc1_uint32 inMask,
336a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        etc_compressed* pCompressed, bool flipped, bool second,
337a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        const etc1_byte* pBaseColors, const int* pModifierTable) {
338a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int score = pCompressed->score;
339a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    if (flipped) {
340a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int by = 0;
341a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        if (second) {
342a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            by = 2;
343a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
344a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        for (int y = 0; y < 2; y++) {
345a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            int yy = by + y;
346a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            for (int x = 0; x < 4; x++) {
347a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                int i = x + 4 * yy;
348a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                if (inMask & (1 << i)) {
349a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                    score += chooseModifier(pBaseColors, pIn + i * 3,
350a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                            &pCompressed->low, yy + x * 4, pModifierTable);
351a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                }
352a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            }
353a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
354a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    } else {
355a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int bx = 0;
356a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        if (second) {
357a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            bx = 2;
358a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
359a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        for (int y = 0; y < 4; y++) {
360a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            for (int x = 0; x < 2; x++) {
361a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                int xx = bx + x;
362a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                int i = xx + 4 * y;
363a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                if (inMask & (1 << i)) {
364a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                    score += chooseModifier(pBaseColors, pIn + i * 3,
365a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                            &pCompressed->low, y + xx * 4, pModifierTable);
366a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                }
367a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            }
368a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
369a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    }
370a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pCompressed->score = score;
371a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
372a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
373a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic bool inRange4bitSigned(int color) {
374a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return color >= -4 && color <= 3;
375a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
376a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
377a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic void etc_encodeBaseColors(etc1_byte* pBaseColors,
378a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        const etc1_byte* pColors, etc_compressed* pCompressed) {
379a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int r1, g1, b1, r2, g2, b2; // 8 bit base colors for sub-blocks
380a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    bool differential;
381a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    {
382a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int r51 = convert8To5(pColors[0]);
383a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int g51 = convert8To5(pColors[1]);
384a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int b51 = convert8To5(pColors[2]);
385a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int r52 = convert8To5(pColors[3]);
386a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int g52 = convert8To5(pColors[4]);
387a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int b52 = convert8To5(pColors[5]);
388a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
389a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        r1 = convert5To8(r51);
390a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        g1 = convert5To8(g51);
391a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        b1 = convert5To8(b51);
392a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
393a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int dr = r52 - r51;
394a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int dg = g52 - g51;
395a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int db = b52 - b51;
396a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
397a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        differential = inRange4bitSigned(dr) && inRange4bitSigned(dg)
398a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                && inRange4bitSigned(db);
399a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        if (differential) {
400a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            r2 = convert5To8(r51 + dr);
401a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            g2 = convert5To8(g51 + dg);
402a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            b2 = convert5To8(b51 + db);
403a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            pCompressed->high |= (r51 << 27) | ((7 & dr) << 24) | (g51 << 19)
404a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                    | ((7 & dg) << 16) | (b51 << 11) | ((7 & db) << 8) | 2;
405a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
406a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    }
407a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
408a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    if (!differential) {
409a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int r41 = convert8To4(pColors[0]);
410a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int g41 = convert8To4(pColors[1]);
411a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int b41 = convert8To4(pColors[2]);
412a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int r42 = convert8To4(pColors[3]);
413a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int g42 = convert8To4(pColors[4]);
414a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int b42 = convert8To4(pColors[5]);
415a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        r1 = convert4To8(r41);
416a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        g1 = convert4To8(g41);
417a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        b1 = convert4To8(b41);
418a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        r2 = convert4To8(r42);
419a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        g2 = convert4To8(g42);
420a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        b2 = convert4To8(b42);
421a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        pCompressed->high |= (r41 << 28) | (r42 << 24) | (g41 << 20) | (g42
422a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                << 16) | (b41 << 12) | (b42 << 8);
423a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    }
424a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pBaseColors[0] = r1;
425a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pBaseColors[1] = g1;
426a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pBaseColors[2] = b1;
427a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pBaseColors[3] = r2;
428a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pBaseColors[4] = g2;
429a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pBaseColors[5] = b2;
430a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
431a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
432a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic
433a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichvoid etc_encode_block_helper(const etc1_byte* pIn, etc1_uint32 inMask,
434a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        const etc1_byte* pColors, etc_compressed* pCompressed, bool flipped) {
435a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pCompressed->score = ~0;
436a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pCompressed->high = (flipped ? 1 : 0);
437a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pCompressed->low = 0;
438a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
439a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_byte pBaseColors[6];
440a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
441a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc_encodeBaseColors(pBaseColors, pColors, pCompressed);
442a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
443a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    int originalHigh = pCompressed->high;
444a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
445a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    const int* pModifierTable = kModifierTable;
446a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    for (int i = 0; i < 8; i++, pModifierTable += 4) {
447a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        etc_compressed temp;
448a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        temp.score = 0;
449a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        temp.high = originalHigh | (i << 5);
450a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        temp.low = 0;
451a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        etc_encode_subblock_helper(pIn, inMask, &temp, flipped, false,
452a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                pBaseColors, pModifierTable);
453a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        take_best(pCompressed, &temp);
454a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    }
455a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pModifierTable = kModifierTable;
456a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc_compressed firstHalf = *pCompressed;
457a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    for (int i = 0; i < 8; i++, pModifierTable += 4) {
458a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        etc_compressed temp;
459a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        temp.score = firstHalf.score;
460a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        temp.high = firstHalf.high | (i << 2);
461a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        temp.low = firstHalf.low;
462a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        etc_encode_subblock_helper(pIn, inMask, &temp, flipped, true,
463a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                pBaseColors + 3, pModifierTable);
464a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        if (i == 0) {
465a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            *pCompressed = temp;
466a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        } else {
467a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            take_best(pCompressed, &temp);
468a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
469a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    }
470a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
471a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
472a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic void writeBigEndian(etc1_byte* pOut, etc1_uint32 d) {
473a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pOut[0] = (etc1_byte)(d >> 24);
474a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pOut[1] = (etc1_byte)(d >> 16);
475a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pOut[2] = (etc1_byte)(d >> 8);
476a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pOut[3] = (etc1_byte) d;
477a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
478a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
479a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// Input is a 4 x 4 square of 3-byte pixels in form R, G, B
480a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// inmask is a 16-bit mask where bit (1 << (x + y * 4)) tells whether the corresponding (x,y)
481a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// pixel is valid or not. Invalid pixel color values are ignored when compressing.
482a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// Output is an ETC1 compressed version of the data.
483a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
484a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichvoid etc1_encode_block(const etc1_byte* pIn, etc1_uint32 inMask,
485a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        etc1_byte* pOut) {
486a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_byte colors[6];
487a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_byte flippedColors[6];
488a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc_average_colors_subblock(pIn, inMask, colors, false, false);
489a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc_average_colors_subblock(pIn, inMask, colors + 3, false, true);
490a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc_average_colors_subblock(pIn, inMask, flippedColors, true, false);
491a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc_average_colors_subblock(pIn, inMask, flippedColors + 3, true, true);
492a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
493a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc_compressed a, b;
494a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc_encode_block_helper(pIn, inMask, colors, &a, false);
495a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc_encode_block_helper(pIn, inMask, flippedColors, &b, true);
496a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    take_best(&a, &b);
497a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    writeBigEndian(pOut, a.high);
498a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    writeBigEndian(pOut + 4, a.low);
499a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
500a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
501a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// Return the size of the encoded image data (does not include size of PKM header).
502a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
503a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichetc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height) {
504a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return (((width + 3) & ~3) * ((height + 3) & ~3)) >> 1;
505a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
506a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
507a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// Encode an entire image.
508a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// pIn - pointer to the image data. Formatted such that the Red component of
509a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich//       pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
510a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// pOut - pointer to encoded data. Must be large enough to store entire encoded image.
511a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
512a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichint etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height,
513a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut) {
514a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    if (pixelSize < 2 || pixelSize > 3) {
515a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        return -1;
516a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    }
517a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    static const unsigned short kYMask[] = { 0x0, 0xf, 0xff, 0xfff, 0xffff };
518a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    static const unsigned short kXMask[] = { 0x0, 0x1111, 0x3333, 0x7777,
519a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            0xffff };
520a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
521a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_byte encoded[ETC1_ENCODED_BLOCK_SIZE];
522a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
523a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 encodedWidth = (width + 3) & ~3;
524a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 encodedHeight = (height + 3) & ~3;
525a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
526a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
527a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        etc1_uint32 yEnd = height - y;
528a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        if (yEnd > 4) {
529a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            yEnd = 4;
530a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
531a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        int ymask = kYMask[yEnd];
532a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
533a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            etc1_uint32 xEnd = width - x;
534a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            if (xEnd > 4) {
535a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                xEnd = 4;
536a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            }
537a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            int mask = ymask & kXMask[xEnd];
538a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
539a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                etc1_byte* q = block + (cy * 4) * 3;
540a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                const etc1_byte* p = pIn + pixelSize * x + stride * (y + cy);
541a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                if (pixelSize == 3) {
542a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                    memcpy(q, p, xEnd * 3);
543a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                } else {
544a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                    for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
545a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                        int pixel = (p[1] << 8) | p[0];
546a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                        *q++ = convert5To8(pixel >> 11);
547a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                        *q++ = convert6To8(pixel >> 5);
548a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                        *q++ = convert5To8(pixel);
549a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                        p += pixelSize;
550a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                    }
551a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                }
552a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            }
553a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            etc1_encode_block(block, mask, encoded);
554a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            memcpy(pOut, encoded, sizeof(encoded));
555a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            pOut += sizeof(encoded);
556a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
557a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    }
558a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return 0;
559a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
560a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
561a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// Decode an entire image.
562a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// pIn - pointer to encoded data.
563a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// pOut - pointer to the image data. Will be written such that the Red component of
564a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich//       pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset. Must be
565a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich//        large enough to store entire image.
566a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
567a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
568a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichint etc1_decode_image(const etc1_byte* pIn, etc1_byte* pOut,
569a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        etc1_uint32 width, etc1_uint32 height,
570a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        etc1_uint32 pixelSize, etc1_uint32 stride) {
571a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    if (pixelSize < 2 || pixelSize > 3) {
572a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        return -1;
573a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    }
574a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
575a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
576a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 encodedWidth = (width + 3) & ~3;
577a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 encodedHeight = (height + 3) & ~3;
578a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
579a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
580a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        etc1_uint32 yEnd = height - y;
581a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        if (yEnd > 4) {
582a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            yEnd = 4;
583a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
584a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
585a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            etc1_uint32 xEnd = width - x;
586a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            if (xEnd > 4) {
587a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                xEnd = 4;
588a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            }
589a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            etc1_decode_block(pIn, block);
590a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            pIn += ETC1_ENCODED_BLOCK_SIZE;
591a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
592a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                const etc1_byte* q = block + (cy * 4) * 3;
593a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                etc1_byte* p = pOut + pixelSize * x + stride * (y + cy);
594a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                if (pixelSize == 3) {
595a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                    memcpy(p, q, xEnd * 3);
596a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                } else {
597a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                    for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
598a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                        etc1_byte r = *q++;
599a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                        etc1_byte g = *q++;
600a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                        etc1_byte b = *q++;
601a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                        etc1_uint32 pixel = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
602a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                        *p++ = (etc1_byte) pixel;
603a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                        *p++ = (etc1_byte) (pixel >> 8);
604a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                    }
605a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich                }
606a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            }
607a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        }
608a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    }
609a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return 0;
610a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
611a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
612a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic const char kMagic[] = { 'P', 'K', 'M', ' ', '1', '0' };
613a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
614a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic const etc1_uint32 ETC1_PKM_FORMAT_OFFSET = 6;
615a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic const etc1_uint32 ETC1_PKM_ENCODED_WIDTH_OFFSET = 8;
616a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic const etc1_uint32 ETC1_PKM_ENCODED_HEIGHT_OFFSET = 10;
617a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic const etc1_uint32 ETC1_PKM_WIDTH_OFFSET = 12;
618a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic const etc1_uint32 ETC1_PKM_HEIGHT_OFFSET = 14;
619a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
620a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic const etc1_uint32 ETC1_RGB_NO_MIPMAPS = 0;
621a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
622a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic void writeBEUint16(etc1_byte* pOut, etc1_uint32 data) {
623a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pOut[0] = (etc1_byte) (data >> 8);
624a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    pOut[1] = (etc1_byte) data;
625a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
626a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
627a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichstatic etc1_uint32 readBEUint16(const etc1_byte* pIn) {
628a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return (pIn[0] << 8) | pIn[1];
629a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
630a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
631a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// Format a PKM header
632a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
633a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichvoid etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height) {
634a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    memcpy(pHeader, kMagic, sizeof(kMagic));
635a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 encodedWidth = (width + 3) & ~3;
636a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 encodedHeight = (height + 3) & ~3;
637a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    writeBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET, ETC1_RGB_NO_MIPMAPS);
638a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    writeBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET, encodedWidth);
639a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    writeBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET, encodedHeight);
640a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    writeBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET, width);
641a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    writeBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET, height);
642a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
643a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
644a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// Check if a PKM header is correctly formatted.
645a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
646a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichetc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader) {
647a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    if (memcmp(pHeader, kMagic, sizeof(kMagic))) {
648a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich        return false;
649a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    }
650a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 format = readBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET);
651a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 encodedWidth = readBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET);
652a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 encodedHeight = readBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET);
653a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 width = readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
654a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    etc1_uint32 height = readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
655a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return format == ETC1_RGB_NO_MIPMAPS &&
656a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            encodedWidth >= width && encodedWidth - width < 4 &&
657a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich            encodedHeight >= height && encodedHeight - height < 4;
658a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
659a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
660a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// Read the image width from a PKM header
661a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
662a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichetc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader) {
663a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
664a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
665a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
666a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich// Read the image height from a PKM header
667a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich
668a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevichetc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader){
669a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich    return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
670a6276fdd4253c3a7150ab675678c750473ab6c45Jack Palevich}
671