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