1010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Copyright 2009 Google Inc.
2010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)//
3010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Licensed under the Apache License, Version 2.0 (the "License");
4010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// you may not use this file except in compliance with the License.
5010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// You may obtain a copy of the License at
6010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)//
7010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)//     http://www.apache.org/licenses/LICENSE-2.0
8010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)//
9010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Unless required by applicable law or agreed to in writing, software
10010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// distributed under the License is distributed on an "AS IS" BASIS,
11010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// See the License for the specific language governing permissions and
13010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// limitations under the License.
14010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
15010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// This is branched from frameworks/base/opengl/include/ETC1/etc1.cc
16010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)//
17010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// It has been modified as follows:
18010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// 1. Unused or not related to encoding methods have been removed.
19010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// 2. Methods related to determining the size of the output texture have been
20010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)//    added.
21010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// 3. EncodeImage has been modified to operate directly on a bitmap, work with
22010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)//    4bpp input, and resize the output to a power-of-two size in order to work
23010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)//    with the graphics driver.
24010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)//
25010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
26010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "etc1.h"
27010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
28010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include <string>
29010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include <cmath>
30010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
31010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)/* From http://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
32010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
33010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) The number of bits that represent a 4x4 texel block is 64 bits if
34010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) <internalformat> is given by ETC1_RGB8_OES.
35010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
36010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) The data for a block is a number of bytes,
37010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
38010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) {q0, q1, q2, q3, q4, q5, q6, q7}
39010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
40010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) where byte q0 is located at the lowest memory address and q7 at
41010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) the highest. The 64 bits specifying the block is then represented
42010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) by the following 64 bit integer:
43010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
44010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) int64bit = 256*(256*(256*(256*(256*(256*(256*q0+q1)+q2)+q3)+q4)+q5)+q6)+q7;
45010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
46010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) ETC1_RGB8_OES:
47010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
48010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) a) bit layout in bits 63 through 32 if diffbit = 0
49010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
50010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
51010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) -----------------------------------------------
52010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) | base col1 | base col2 | base col1 | base col2 |
53010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)|
54010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) -----------------------------------------------
55010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
56010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 47 46 45 44 43 42 41 40 39 38 37 36 35 34  33  32
57010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) ---------------------------------------------------
58010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) | base col1 | base col2 | table  | table  |diff|flip|
59010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) | B1 (4bits)| B2 (4bits)| cw 1   | cw 2   |bit |bit |
60010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) ---------------------------------------------------
61010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
62010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
63010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) b) bit layout in bits 63 through 32 if diffbit = 1
64010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
65010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
66010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) -----------------------------------------------
67010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) | base col1    | dcol 2 | base col1    | dcol 2 |
68010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) | R1' (5 bits) | dR2    | G1' (5 bits) | dG2    |
69010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) -----------------------------------------------
70010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
71010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 47 46 45 44 43 42 41 40 39 38 37 36 35 34  33  32
72010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) ---------------------------------------------------
73010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) | base col 1   | dcol 2 | table  | table  |diff|flip|
74010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) | B1' (5 bits) | dB2    | cw 1   | cw 2   |bit |bit |
75010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) ---------------------------------------------------
76010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
77010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
78010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) c) bit layout in bits 31 through 0 (in both cases)
79010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
80010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
81010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) -----------------------------------------------
82010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) |       most significant pixel index bits       |
83010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a|
84010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) -----------------------------------------------
85010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
86010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 15 14 13 12 11 10  9  8  7  6  5  4  3   2   1  0
87010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) --------------------------------------------------
88010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) |         least significant pixel index bits       |
89010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) | p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a |
90010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) --------------------------------------------------
91010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
92010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
93010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) Add table 3.17.2: Intensity modifier sets for ETC1 compressed textures:
94010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
95010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) table codeword                modifier table
96010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) ------------------        ----------------------
97010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 0                     -8  -2  2   8
98010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 1                    -17  -5  5  17
99010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 2                    -29  -9  9  29
100010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 3                    -42 -13 13  42
101010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 4                    -60 -18 18  60
102010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 5                    -80 -24 24  80
103010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 6                   -106 -33 33 106
104010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 7                   -183 -47 47 183
105010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
106010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
107010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) Add table 3.17.3 Mapping from pixel index values to modifier values for
108010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) ETC1 compressed textures:
109010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
110010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) pixel index value
111010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) ---------------
112010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) msb     lsb           resulting modifier value
113010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) -----   -----          -------------------------
114010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 1       1            -b (large negative value)
115010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 1       0            -a (small negative value)
116010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 0       0             a (small positive value)
117010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 0       1             b (large positive value)
118010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
119010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
120010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) */
121010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
122010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#define ETC1_ENCODED_BLOCK_SIZE 8
123010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#define ETC1_DECODED_BLOCK_SIZE 48
124010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
125010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)namespace {
126010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
127010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)typedef unsigned char etc1_byte;
128010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)typedef int etc1_bool;
129010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)typedef unsigned int etc1_uint32;
130010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
131010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static const int kModifierTable[] = {
132010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)/* 0 */2, 8, -2, -8,
133010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)/* 1 */5, 17, -5, -17,
134010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)/* 2 */9, 29, -9, -29,
135010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)/* 3 */13, 42, -13, -42,
136010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)/* 4 */18, 60, -18, -60,
137010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)/* 5 */24, 80, -24, -80,
138010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)/* 6 */33, 106, -33, -106,
139010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)/* 7 */47, 183, -47, -183 };
140010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccistatic const int kLookup[8] = { 0, 1, 2, 3, -4, -3, -2, -1 };
142010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
143010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static inline etc1_byte clamp(int x) {
144010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return (etc1_byte) (x >= 0 ? (x < 255 ? x : 255) : 0);
145010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
146010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
147010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static
148010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)inline int convert4To8(int b) {
149010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int c = b & 0xf;
150010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return (c << 4) | c;
151010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
152010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
153010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static
154010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)inline int convert5To8(int b) {
155010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int c = b & 0x1f;
156010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return (c << 3) | (c >> 2);
157010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
158010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
159010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static
160010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)inline int convert6To8(int b) {
161010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int c = b & 0x3f;
162010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return (c << 2) | (c >> 4);
163010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
164010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
165010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static
166010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)inline int divideBy255(int d) {
167010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return (d + 128 + (d >> 8)) >> 8;
168010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
169010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
170010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static
171010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)inline int convert8To4(int b) {
172010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int c = b & 0xff;
173010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return divideBy255(c * 15);
174010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
175010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
176010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static
177010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)inline int convert8To5(int b) {
178010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int c = b & 0xff;
179010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return divideBy255(c * 31);
180010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
181010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
1821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccistatic
1831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciinline int convertDiff(int base, int diff) {
1841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return convert5To8((0x1f & base) + kLookup[0x7 & diff]);
1851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
1861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccistatic
1881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid decode_subblock(etc1_byte* pOut, int r, int g, int b, const int* table,
1891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        etc1_uint32 low, bool second, bool flipped) {
1901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    int baseX = 0;
1911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    int baseY = 0;
1921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (second) {
1931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        if (flipped) {
1941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            baseY = 2;
1951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        } else {
1961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            baseX = 2;
1971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        }
1981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
1991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    for (int i = 0; i < 8; i++) {
2001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        int x, y;
2011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        if (flipped) {
2021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            x = baseX + (i >> 1);
2031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            y = baseY + (i & 1);
2041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        } else {
2051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            x = baseX + (i >> 2);
2061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            y = baseY + (i & 3);
2071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        }
2081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        int k = y + (x * 4);
2091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        int offset = ((low >> k) & 1) | ((low >> (k + 15)) & 2);
2101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        int delta = table[offset];
2111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        etc1_byte* q = pOut + 3 * (x + 4 * y);
2121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        *q++ = clamp(r + delta);
2131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        *q++ = clamp(g + delta);
2141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        *q++ = clamp(b + delta);
2151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
2161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Input is an ETC1 compressed version of the data.
2191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Output is a 4 x 4 square of 3-byte pixels in form R, G, B
2201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid etc1_decode_block(const etc1_byte* pIn, etc1_byte* pOut) {
2221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    etc1_uint32 high = (pIn[0] << 24) | (pIn[1] << 16) | (pIn[2] << 8) | pIn[3];
2231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    etc1_uint32 low = (pIn[4] << 24) | (pIn[5] << 16) | (pIn[6] << 8) | pIn[7];
2241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    int r1, r2, g1, g2, b1, b2;
2251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (high & 2) {
2261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        // differential
2271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        int rBase = high >> 27;
2281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        int gBase = high >> 19;
2291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        int bBase = high >> 11;
2301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        r1 = convert5To8(rBase);
2311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        r2 = convertDiff(rBase, high >> 24);
2321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        g1 = convert5To8(gBase);
2331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        g2 = convertDiff(gBase, high >> 16);
2341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        b1 = convert5To8(bBase);
2351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        b2 = convertDiff(bBase, high >> 8);
2361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    } else {
2371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        // not differential
2381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        r1 = convert4To8(high >> 28);
2391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        r2 = convert4To8(high >> 24);
2401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        g1 = convert4To8(high >> 20);
2411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        g2 = convert4To8(high >> 16);
2421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        b1 = convert4To8(high >> 12);
2431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        b2 = convert4To8(high >> 8);
2441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
2451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    int tableIndexA = 7 & (high >> 5);
2461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    int tableIndexB = 7 & (high >> 2);
2471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const int* tableA = kModifierTable + tableIndexA * 4;
2481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const int* tableB = kModifierTable + tableIndexB * 4;
2491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    bool flipped = (high & 1) != 0;
2501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    decode_subblock(pOut, r1, g1, b1, tableA, low, false, flipped);
2511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    decode_subblock(pOut, r2, g2, b2, tableB, low, true, flipped);
2521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
254010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)typedef struct {
255010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc1_uint32 high;
256010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc1_uint32 low;
257010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc1_uint32 score; // Lower is more accurate
258010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)} etc_compressed;
259010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
260010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static
261010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)inline void take_best(etc_compressed* a, const etc_compressed* b) {
262010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    if (a->score > b->score) {
263010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        *a = *b;
264010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    }
265010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
266010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
267010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static
268010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void etc_average_colors_subblock(const etc1_byte* pIn, etc1_uint32 inMask,
269010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        etc1_byte* pColors, bool flipped, bool second) {
270010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int r = 0;
271010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int g = 0;
272010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int b = 0;
273010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
274010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    if (flipped) {
275010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int by = 0;
276010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        if (second) {
277010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            by = 2;
278010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        }
279010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        for (int y = 0; y < 2; y++) {
280010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            int yy = by + y;
281010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            for (int x = 0; x < 4; x++) {
282010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                int i = x + 4 * yy;
283010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                if (inMask & (1 << i)) {
284010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    const etc1_byte* p = pIn + i * 3;
285010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    r += *(p++);
286010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    g += *(p++);
287010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    b += *(p++);
288010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                }
289010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            }
290010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        }
291010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    } else {
292010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int bx = 0;
293010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        if (second) {
294010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            bx = 2;
295010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        }
296010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        for (int y = 0; y < 4; y++) {
297010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            for (int x = 0; x < 2; x++) {
298010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                int xx = bx + x;
299010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                int i = xx + 4 * y;
300010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                if (inMask & (1 << i)) {
301010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    const etc1_byte* p = pIn + i * 3;
302010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    r += *(p++);
303010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    g += *(p++);
304010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    b += *(p++);
305010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                }
306010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            }
307010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        }
308010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    }
309010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pColors[0] = (etc1_byte)((r + 4) >> 3);
310010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pColors[1] = (etc1_byte)((g + 4) >> 3);
311010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pColors[2] = (etc1_byte)((b + 4) >> 3);
312010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
313010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
314010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static
315010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)inline int square(int x) {
316010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return x * x;
317010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
318010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
319010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static etc1_uint32 chooseModifier(const etc1_byte* pBaseColors,
320010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        const etc1_byte* pIn, etc1_uint32 *pLow, int bitIndex,
321010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        const int* pModifierTable) {
322010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc1_uint32 bestScore = ~0;
323010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int bestIndex = 0;
324010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int pixelR = pIn[0];
325010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int pixelG = pIn[1];
326010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int pixelB = pIn[2];
327010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int r = pBaseColors[0];
328010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int g = pBaseColors[1];
329010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int b = pBaseColors[2];
330010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    for (int i = 0; i < 4; i++) {
331010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int modifier = pModifierTable[i];
332010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int decodedG = clamp(g + modifier);
333010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        etc1_uint32 score = (etc1_uint32) (6 * square(decodedG - pixelG));
334010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        if (score >= bestScore) {
335010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            continue;
336010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        }
337010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int decodedR = clamp(r + modifier);
338010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        score += (etc1_uint32) (3 * square(decodedR - pixelR));
339010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        if (score >= bestScore) {
340010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            continue;
341010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        }
342010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int decodedB = clamp(b + modifier);
343010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        score += (etc1_uint32) square(decodedB - pixelB);
344010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        if (score < bestScore) {
345010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            bestScore = score;
346010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            bestIndex = i;
347010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        }
348010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    }
349010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc1_uint32 lowMask = (((bestIndex >> 1) << 16) | (bestIndex & 1))
350010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            << bitIndex;
351010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    *pLow |= lowMask;
352010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return bestScore;
353010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
354010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
355010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static
356010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void etc_encode_subblock_helper(const etc1_byte* pIn, etc1_uint32 inMask,
357010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        etc_compressed* pCompressed, bool flipped, bool second,
358010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        const etc1_byte* pBaseColors, const int* pModifierTable) {
359010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int score = pCompressed->score;
360010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    if (flipped) {
361010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int by = 0;
362010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        if (second) {
363010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            by = 2;
364010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        }
365010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        for (int y = 0; y < 2; y++) {
366010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            int yy = by + y;
367010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            for (int x = 0; x < 4; x++) {
368010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                int i = x + 4 * yy;
369010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                if (inMask & (1 << i)) {
370010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    score += chooseModifier(pBaseColors, pIn + i * 3,
371010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            &pCompressed->low, yy + x * 4, pModifierTable);
372010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                }
373010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            }
374010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        }
375010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    } else {
376010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int bx = 0;
377010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        if (second) {
378010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            bx = 2;
379010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        }
380010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        for (int y = 0; y < 4; y++) {
381010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            for (int x = 0; x < 2; x++) {
382010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                int xx = bx + x;
383010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                int i = xx + 4 * y;
384010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                if (inMask & (1 << i)) {
385010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    score += chooseModifier(pBaseColors, pIn + i * 3,
386010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            &pCompressed->low, y + xx * 4, pModifierTable);
387010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                }
388010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            }
389010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        }
390010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    }
391010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pCompressed->score = score;
392010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
393010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
394010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static bool inRange4bitSigned(int color) {
395010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return color >= -4 && color <= 3;
396010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
397010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
398010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static void etc_encodeBaseColors(etc1_byte* pBaseColors,
399010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        const etc1_byte* pColors, etc_compressed* pCompressed) {
400010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int r1, g1, b1, r2, g2, b2; // 8 bit base colors for sub-blocks
401010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    bool differential;
402010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    {
403010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int r51 = convert8To5(pColors[0]);
404010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int g51 = convert8To5(pColors[1]);
405010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int b51 = convert8To5(pColors[2]);
406010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int r52 = convert8To5(pColors[3]);
407010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int g52 = convert8To5(pColors[4]);
408010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int b52 = convert8To5(pColors[5]);
409010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
410010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        r1 = convert5To8(r51);
411010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        g1 = convert5To8(g51);
412010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        b1 = convert5To8(b51);
413010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
414010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int dr = r52 - r51;
415010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int dg = g52 - g51;
416010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int db = b52 - b51;
417010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
418010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        differential = inRange4bitSigned(dr) && inRange4bitSigned(dg)
419010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                && inRange4bitSigned(db);
420010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        if (differential) {
421010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            r2 = convert5To8(r51 + dr);
422010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            g2 = convert5To8(g51 + dg);
423010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            b2 = convert5To8(b51 + db);
424010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            pCompressed->high |= (r51 << 27) | ((7 & dr) << 24) | (g51 << 19)
425010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    | ((7 & dg) << 16) | (b51 << 11) | ((7 & db) << 8) | 2;
426010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        }
427010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    }
428010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
429010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    if (!differential) {
430010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int r41 = convert8To4(pColors[0]);
431010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int g41 = convert8To4(pColors[1]);
432010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int b41 = convert8To4(pColors[2]);
433010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int r42 = convert8To4(pColors[3]);
434010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int g42 = convert8To4(pColors[4]);
435010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int b42 = convert8To4(pColors[5]);
436010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        r1 = convert4To8(r41);
437010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        g1 = convert4To8(g41);
438010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        b1 = convert4To8(b41);
439010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        r2 = convert4To8(r42);
440010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        g2 = convert4To8(g42);
441010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        b2 = convert4To8(b42);
442010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        pCompressed->high |= (r41 << 28) | (r42 << 24) | (g41 << 20) | (g42
443010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                << 16) | (b41 << 12) | (b42 << 8);
444010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    }
445010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pBaseColors[0] = r1;
446010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pBaseColors[1] = g1;
447010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pBaseColors[2] = b1;
448010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pBaseColors[3] = r2;
449010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pBaseColors[4] = g2;
450010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pBaseColors[5] = b2;
451010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
452010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
453010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static
454010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void etc_encode_block_helper(const etc1_byte* pIn, etc1_uint32 inMask,
455010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        const etc1_byte* pColors, etc_compressed* pCompressed, bool flipped) {
456010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pCompressed->score = ~0;
457010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pCompressed->high = (flipped ? 1 : 0);
458010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pCompressed->low = 0;
459010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
460010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc1_byte pBaseColors[6];
461010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
462010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc_encodeBaseColors(pBaseColors, pColors, pCompressed);
463010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
464010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    int originalHigh = pCompressed->high;
465010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
466010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    const int* pModifierTable = kModifierTable;
467010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    for (int i = 0; i < 8; i++, pModifierTable += 4) {
468010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        etc_compressed temp;
469010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        temp.score = 0;
470010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        temp.high = originalHigh | (i << 5);
471010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        temp.low = 0;
472010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        etc_encode_subblock_helper(pIn, inMask, &temp, flipped, false,
473010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                pBaseColors, pModifierTable);
474010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        take_best(pCompressed, &temp);
475010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    }
476010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pModifierTable = kModifierTable;
477010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc_compressed firstHalf = *pCompressed;
478010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    for (int i = 0; i < 8; i++, pModifierTable += 4) {
479010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        etc_compressed temp;
480010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        temp.score = firstHalf.score;
481010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        temp.high = firstHalf.high | (i << 2);
482010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        temp.low = firstHalf.low;
483010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        etc_encode_subblock_helper(pIn, inMask, &temp, flipped, true,
484010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                pBaseColors + 3, pModifierTable);
485010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        if (i == 0) {
486010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            *pCompressed = temp;
487010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        } else {
488010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            take_best(pCompressed, &temp);
489010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        }
490010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    }
491010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
492010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
493010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static void writeBigEndian(etc1_byte* pOut, etc1_uint32 d) {
494010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pOut[0] = (etc1_byte)(d >> 24);
495010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pOut[1] = (etc1_byte)(d >> 16);
496010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pOut[2] = (etc1_byte)(d >> 8);
497010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    pOut[3] = (etc1_byte) d;
498010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
499010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
500010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Input is a 4 x 4 square of 3-byte pixels in form R, G, B
501010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// inmask is a 16-bit mask where bit (1 << (x + y * 4)) tells whether the corresponding (x,y)
502010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// pixel is valid or not. Invalid pixel color values are ignored when compressing.
503010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Output is an ETC1 compressed version of the data.
504010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
505010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static void etc1_encode_block(etc1_byte* pIn, int inMask, etc1_byte* pOut) {
506010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc1_byte colors[6];
507010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc1_byte flippedColors[6];
508010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc_average_colors_subblock(pIn, inMask, colors, false, false);
509010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc_average_colors_subblock(pIn, inMask, colors + 3, false, true);
510010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc_average_colors_subblock(pIn, inMask, flippedColors, true, false);
511010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc_average_colors_subblock(pIn, inMask, flippedColors + 3, true, true);
512010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
513010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc_compressed a, b;
514010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc_encode_block_helper(pIn, inMask, colors, &a, false);
515010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc_encode_block_helper(pIn, inMask, flippedColors, &b, true);
516010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    take_best(&a, &b);
517010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    writeBigEndian(pOut, a.high);
518010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    writeBigEndian(pOut + 4, a.low);
519010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
520010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
521010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}  // anonymous namespace
522010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
523010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Return the size of the encoded image data.
524010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
525010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height) {
526010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  return (((width + 3) & ~3) * ((height + 3) & ~3)) >> 1;
527010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
528010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
529010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Encode an entire image.
530010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// pIn - pointer to the image data. Formatted such that the Red component of
531010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)//       pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
532010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// pOut - pointer to encoded data. Must be large enough to store entire encoded image.
533010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Returns false if there was an error.
534010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
535010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)bool etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height,
536010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)         etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut, etc1_uint32 outWidth,
537010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)         etc1_uint32 outHeight) {
538010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    if (pixelSize < 2) {
539010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)          return false;
540010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    }
541010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    static const unsigned short kYMask[] = { 0x0, 0xf, 0xff, 0xfff, 0xffff };
542010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    static const unsigned short kXMask[] = { 0x0, 0x1111, 0x3333, 0x7777,
543010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            0xffff };
544010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
545010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc1_byte encoded[ETC1_ENCODED_BLOCK_SIZE];
546010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
547010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc1_uint32 encodedWidth = (outWidth + 3) & ~3;
548010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    etc1_uint32 encodedHeight = (outHeight + 3) & ~3;
549010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
550010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
551010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        etc1_uint32 yEnd = outHeight - y;
552010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        if (yEnd > 4) {
553010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            yEnd = 4;
554010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        }
555010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        int ymask = kYMask[yEnd];
556010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
557010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            etc1_uint32 xEnd = outWidth - x;
558010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            if (xEnd > 4) {
559010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                xEnd = 4;
560010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            }
561010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            const int mask = ymask & kXMask[xEnd];
562010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            // Shortcut to only encode blocks which overlap the input image.
563010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            // The outside region will be undefined garbage.
564010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            if (x < width && y < height) {
565010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
566010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    etc1_byte* q = block + (cy * 4) * 3;
567010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    const etc1_byte* p = pIn + pixelSize * x + stride * (y + cy);
568010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    if (y + cy < height) {
569010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                        for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
570010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            if (x + cx < width) {
571010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                if (pixelSize == 4) {
572010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                    // RGBA_8888: Filter out the input's alpha channel.
573010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                    *q++ = p[0];
574010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                    *q++ = p[1];
575010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                    *q++ = p[2];
576010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                } else {
577010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                    // RGB_565: Unpack input's 2 bytes to RGB.
578010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                    int pixel = (p[1] << 8) | p[0];
579010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                    *q++ = convert5To8(pixel >> 11);
580010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                    *q++ = convert6To8(pixel >> 5);
581010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                    *q++ = convert5To8(pixel);
582010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                }
583010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                p += pixelSize;
584010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            } else {
585010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                // Out of bounds of the input image but within a
586010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                // block that must be properly encoded, so pad
587010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                // the original image with the last pixel.
588010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                *(q + 0) = *(q - 3);
589010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                *(q + 1) = *(q - 2);
590010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                *(q + 2) = *(q - 1);
591010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                q += 3;
592010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            }
593010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                        }
594010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    } else {
595010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                      // Out of bounds of the input image but within a
596010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                      // block that must be properly encoded, so pad the
597010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                      // original image with the last pixel.
598010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                      *(q + 0) = *(q - 12);
599010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                      *(q + 1) = *(q - 11);
600010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                      *(q + 2) = *(q - 10);
601010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                      q += 3;
602010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    }
603010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                }
604010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                etc1_encode_block(block, mask, encoded);
605010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                memcpy(pOut, encoded, sizeof(encoded));
606010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            } else if (x == width && width > 0 && height > 0) {
607010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                // We need to extend the block right after to the last pixel of
608010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                // the source bitmap for the blending to work nicely.
609010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
610010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    etc1_byte* q = block + (cy * 4) * 3;
611010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    const etc1_byte* p = pIn + pixelSize * (width - 1) +
612010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            stride * std::min(y + cy, height - 1);
613010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
614010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                        if (pixelSize == 4) {
615010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            // RGBA_8888: Filter out the input's alpha channel.
616010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            *q++ = p[0];
617010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            *q++ = p[1];
618010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            *q++ = p[2];
619010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                        } else {
620010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            // RGB_565: Unpack input's 2 bytes to RGB.
621010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            int pixel = (p[1] << 8) | p[0];
622010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            *q++ = convert5To8(pixel >> 11);
623010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            *q++ = convert6To8(pixel >> 5);
624010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            *q++ = convert5To8(pixel);
625010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                        }
626010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    }
627010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                }
628010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                etc1_encode_block(block, mask, encoded);
629010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                memcpy(pOut, encoded, sizeof(encoded));
630010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            } else if (y == height && width > 0 && height > 0) {
631010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                // We need to extend the block right after to the last pixel of
632010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                // the source bitmap for the blending to work nicely.
633010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
634010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    etc1_byte* q = block + (cy * 4) * 3;
635010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
636010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                        const etc1_byte* p = pIn +
637010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                pixelSize * std::min(x + cx, width - 1) +
638010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                stride * (height - 1);
639010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                        if (pixelSize == 4) {
640010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            // RGBA_8888: Filter out the input's alpha channel.
641010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            *q++ = p[0];
642010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            *q++ = p[1];
643010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            *q++ = p[2];
644010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                        } else {
645010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            // RGB_565: Unpack input's 2 bytes to RGB.
646010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            int pixel = (p[1] << 8) | p[0];
647010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            *q++ = convert5To8(pixel >> 11);
648010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            *q++ = convert6To8(pixel >> 5);
649010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                            *q++ = convert5To8(pixel);
650010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                        }
651010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                    }
652010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                }
653010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                etc1_encode_block(block, mask, encoded);
654010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                memcpy(pOut, encoded, sizeof(encoded));
655010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            } else {
656010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                memset(pOut, 0xFF, sizeof(encoded));
657010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            }
658010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            pOut += sizeof(encoded);
659010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        }
660010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    }
661010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return true;
662010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
66303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
6641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Decode an entire image.
6651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// pIn - pointer to encoded data.
6661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// pOut - pointer to the image data. Will be written such that the Red component of
6671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci//       pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset. Must be
6681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci//        large enough to store entire image.
6691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
6701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
6711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool etc1_decode_image(const etc1_byte* pIn, etc1_byte* pOut,
6721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        etc1_uint32 width, etc1_uint32 height,
6731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        etc1_uint32 pixelSize, etc1_uint32 stride) {
6741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (pixelSize < 2 || pixelSize > 4) {
6751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        return false;
6761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
6771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
6781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
6791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    etc1_uint32 encodedWidth = (width + 3) & ~3;
6801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    etc1_uint32 encodedHeight = (height + 3) & ~3;
6811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
6821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
6831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        etc1_uint32 yEnd = height - y;
6841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        if (yEnd > 4) {
6851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            yEnd = 4;
6861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        }
6871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
6881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            etc1_uint32 xEnd = width - x;
6891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            if (xEnd > 4) {
6901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                xEnd = 4;
6911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            }
6921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            etc1_decode_block(pIn, block);
6931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            pIn += ETC1_ENCODED_BLOCK_SIZE;
6941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
6951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                const etc1_byte* q = block + (cy * 4) * 3;
6961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                etc1_byte* p = pOut + pixelSize * x + stride * (y + cy);
6971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                if (pixelSize == 3) {
6981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                    memcpy(p, q, xEnd * 3);
6991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                } else if (pixelSize == 2) {
7001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                    for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
7011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                        etc1_byte r = *q++;
7021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                        etc1_byte g = *q++;
7031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                        etc1_byte b = *q++;
7041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                        etc1_uint32 pixel = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
7051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                        *p++ = (etc1_byte) pixel;
7061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                        *p++ = (etc1_byte) (pixel >> 8);
7071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                    }
7081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                } else {
7091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                  for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
7101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                      etc1_byte r = *q++;
7111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                      etc1_byte g = *q++;
7121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                      etc1_byte b = *q++;
7131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                      *p++ = r;
7141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                      *p++ = g;
7151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                      *p++ = b;
7161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                      *p++ = (etc1_byte) 0xFF;
7171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                  }
7181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                }
7191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            }
7201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        }
7211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
7221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return true;
7231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
7241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
72503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)static const char kMagic[] = { 'P', 'K', 'M', ' ', '1', '0' };
72603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
72703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)static const etc1_uint32 ETC1_PKM_FORMAT_OFFSET = 6;
72803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)static const etc1_uint32 ETC1_PKM_ENCODED_WIDTH_OFFSET = 8;
72903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)static const etc1_uint32 ETC1_PKM_ENCODED_HEIGHT_OFFSET = 10;
73003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)static const etc1_uint32 ETC1_PKM_WIDTH_OFFSET = 12;
73103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)static const etc1_uint32 ETC1_PKM_HEIGHT_OFFSET = 14;
73203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
73303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)static const etc1_uint32 ETC1_RGB_NO_MIPMAPS = 0;
73403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
73503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)static void writeBEUint16(etc1_byte* pOut, etc1_uint32 data) {
73603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    pOut[0] = (etc1_byte) (data >> 8);
73703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    pOut[1] = (etc1_byte) data;
73803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
73903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
74003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)static etc1_uint32 readBEUint16(const etc1_byte* pIn) {
74103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return (pIn[0] << 8) | pIn[1];
74203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
74303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
74403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)// Format a PKM header
74503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
74603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height) {
74703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    memcpy(pHeader, kMagic, sizeof(kMagic));
74803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    etc1_uint32 encodedWidth = (width + 3) & ~3;
74903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    etc1_uint32 encodedHeight = (height + 3) & ~3;
75003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    writeBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET, ETC1_RGB_NO_MIPMAPS);
75103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    writeBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET, encodedWidth);
75203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    writeBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET, encodedHeight);
75303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    writeBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET, width);
75403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    writeBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET, height);
75503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
75603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
75703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)// Check if a PKM header is correctly formatted.
75803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
75903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)bool etc1_pkm_is_valid(const etc1_byte* pHeader) {
76003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    if (memcmp(pHeader, kMagic, sizeof(kMagic))) {
76103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        return false;
76203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    }
76303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    etc1_uint32 format = readBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET);
76403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    etc1_uint32 encodedWidth = readBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET);
76503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    etc1_uint32 encodedHeight = readBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET);
76603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    etc1_uint32 width = readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
76703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    etc1_uint32 height = readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
76803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return format == ETC1_RGB_NO_MIPMAPS &&
76903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)            encodedWidth >= width && encodedWidth - width < 4 &&
77003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)            encodedHeight >= height && encodedHeight - height < 4;
77103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
77203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
77303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)// Read the image width from a PKM header
77403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
77503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader) {
77603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
77703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
77803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
77903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)// Read the image height from a PKM header
78003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
78103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader){
78203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
78303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
784