101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// Copyright 2009 Google Inc.
201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich//
301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// Licensed under the Apache License, Version 2.0 (the "License");
401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// you may not use this file except in compliance with the License.
501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// You may obtain a copy of the License at
601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich//
701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich//     http://www.apache.org/licenses/LICENSE-2.0
801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich//
901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// Unless required by applicable law or agreed to in writing, software
1001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// distributed under the License is distributed on an "AS IS" BASIS,
1101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// See the License for the specific language governing permissions and
1301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// limitations under the License.
1401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
1501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich#include <ETC1/etc1.h>
1601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
1701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich#include <string.h>
1801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
1901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich/* From http://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
2001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
2101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich The number of bits that represent a 4x4 texel block is 64 bits if
2201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich <internalformat> is given by ETC1_RGB8_OES.
2301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
2401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich The data for a block is a number of bytes,
2501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
2601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich {q0, q1, q2, q3, q4, q5, q6, q7}
2701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
2801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich where byte q0 is located at the lowest memory address and q7 at
2901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich the highest. The 64 bits specifying the block is then represented
3001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich by the following 64 bit integer:
3101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
3201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich int64bit = 256*(256*(256*(256*(256*(256*(256*q0+q1)+q2)+q3)+q4)+q5)+q6)+q7;
3301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
3401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich ETC1_RGB8_OES:
3501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
3601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich a) bit layout in bits 63 through 32 if diffbit = 0
3701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
3801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
3901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich -----------------------------------------------
4001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich | base col1 | base col2 | base col1 | base col2 |
4101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)|
4201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich -----------------------------------------------
4301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
4401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 47 46 45 44 43 42 41 40 39 38 37 36 35 34  33  32
4501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich ---------------------------------------------------
4601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich | base col1 | base col2 | table  | table  |diff|flip|
4701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich | B1 (4bits)| B2 (4bits)| cw 1   | cw 2   |bit |bit |
4801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich ---------------------------------------------------
4901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
5001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
5101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich b) bit layout in bits 63 through 32 if diffbit = 1
5201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
5301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
5401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich -----------------------------------------------
5501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich | base col1    | dcol 2 | base col1    | dcol 2 |
5601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich | R1' (5 bits) | dR2    | G1' (5 bits) | dG2    |
5701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich -----------------------------------------------
5801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
5901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 47 46 45 44 43 42 41 40 39 38 37 36 35 34  33  32
6001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich ---------------------------------------------------
6101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich | base col 1   | dcol 2 | table  | table  |diff|flip|
6201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich | B1' (5 bits) | dB2    | cw 1   | cw 2   |bit |bit |
6301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich ---------------------------------------------------
6401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
6501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
6601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich c) bit layout in bits 31 through 0 (in both cases)
6701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
6801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
6901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich -----------------------------------------------
7001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich |       most significant pixel index bits       |
7101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a|
7201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich -----------------------------------------------
7301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
7401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 15 14 13 12 11 10  9  8  7  6  5  4  3   2   1  0
7501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich --------------------------------------------------
7601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich |         least significant pixel index bits       |
7701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich | p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a |
7801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich --------------------------------------------------
7901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
8001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
8101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich Add table 3.17.2: Intensity modifier sets for ETC1 compressed textures:
8201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
8301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich table codeword                modifier table
8401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich ------------------        ----------------------
8501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 0                     -8  -2  2   8
8601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 1                    -17  -5  5  17
8701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 2                    -29  -9  9  29
8801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 3                    -42 -13 13  42
8901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 4                    -60 -18 18  60
9001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 5                    -80 -24 24  80
9101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 6                   -106 -33 33 106
9201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 7                   -183 -47 47 183
9301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
9401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
9501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich Add table 3.17.3 Mapping from pixel index values to modifier values for
9601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich ETC1 compressed textures:
9701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
9801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich pixel index value
9901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich ---------------
10001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich msb     lsb           resulting modifier value
10101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich -----   -----          -------------------------
10201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 1       1            -b (large negative value)
10301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 1       0            -a (small negative value)
10401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 0       0             a (small positive value)
10501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich 0       1             b (large positive value)
10601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
10701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
10801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich */
10901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
11001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic const int kModifierTable[] = {
11101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich/* 0 */2, 8, -2, -8,
11201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich/* 1 */5, 17, -5, -17,
11301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich/* 2 */9, 29, -9, -29,
11401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich/* 3 */13, 42, -13, -42,
11501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich/* 4 */18, 60, -18, -60,
11601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich/* 5 */24, 80, -24, -80,
11701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich/* 6 */33, 106, -33, -106,
11801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich/* 7 */47, 183, -47, -183 };
11901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
12001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic const int kLookup[8] = { 0, 1, 2, 3, -4, -3, -2, -1 };
12101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
12201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic inline etc1_byte clamp(int x) {
12301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    return (etc1_byte) (x >= 0 ? (x < 255 ? x : 255) : 0);
12401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
12501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
12601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic
12701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichinline int convert4To8(int b) {
12801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int c = b & 0xf;
12901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    return (c << 4) | c;
13001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
13101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
13201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic
13301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichinline int convert5To8(int b) {
13401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int c = b & 0x1f;
13501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    return (c << 3) | (c >> 2);
13601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
13701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
13801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic
13901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichinline int convert6To8(int b) {
14001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int c = b & 0x3f;
14101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    return (c << 2) | (c >> 4);
14201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
14301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
14401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic
14501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichinline int divideBy255(int d) {
14601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    return (d + 128 + (d >> 8)) >> 8;
14701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
14801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
14901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic
15001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichinline int convert8To4(int b) {
15101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int c = b & 0xff;
1522b93f0bf448160b002dec10c5bb1c9985a5950e6Jack Palevich    return divideBy255(c * 15);
15301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
15401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
15501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic
15601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichinline int convert8To5(int b) {
15701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int c = b & 0xff;
1582b93f0bf448160b002dec10c5bb1c9985a5950e6Jack Palevich    return divideBy255(c * 31);
15901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
16001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
16101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic
16201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichinline int convertDiff(int base, int diff) {
16301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    return convert5To8((0x1f & base) + kLookup[0x7 & diff]);
16401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
16501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
16601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic
16701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichvoid decode_subblock(etc1_byte* pOut, int r, int g, int b, const int* table,
16801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        etc1_uint32 low, bool second, bool flipped) {
16901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int baseX = 0;
17001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int baseY = 0;
17101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    if (second) {
17201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        if (flipped) {
17301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            baseY = 2;
17401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        } else {
17501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            baseX = 2;
17601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
17701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    }
17801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    for (int i = 0; i < 8; i++) {
17901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int x, y;
18001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        if (flipped) {
18101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            x = baseX + (i >> 1);
18201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            y = baseY + (i & 1);
18301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        } else {
18401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            x = baseX + (i >> 2);
18501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            y = baseY + (i & 3);
18601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
18701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int k = y + (x * 4);
18801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int offset = ((low >> k) & 1) | ((low >> (k + 15)) & 2);
18901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int delta = table[offset];
19001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        etc1_byte* q = pOut + 3 * (x + 4 * y);
19101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        *q++ = clamp(r + delta);
19201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        *q++ = clamp(g + delta);
19301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        *q++ = clamp(b + delta);
19401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    }
19501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
19601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
19701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// Input is an ETC1 compressed version of the data.
19801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// Output is a 4 x 4 square of 3-byte pixels in form R, G, B
19901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
20001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichvoid etc1_decode_block(const etc1_byte* pIn, etc1_byte* pOut) {
20101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 high = (pIn[0] << 24) | (pIn[1] << 16) | (pIn[2] << 8) | pIn[3];
20201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 low = (pIn[4] << 24) | (pIn[5] << 16) | (pIn[6] << 8) | pIn[7];
20301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int r1, r2, g1, g2, b1, b2;
20401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    if (high & 2) {
20501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        // differential
20601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int rBase = high >> 27;
20701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int gBase = high >> 19;
20801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int bBase = high >> 11;
20901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        r1 = convert5To8(rBase);
21001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        r2 = convertDiff(rBase, high >> 24);
21101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        g1 = convert5To8(gBase);
21201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        g2 = convertDiff(gBase, high >> 16);
21301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        b1 = convert5To8(bBase);
21401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        b2 = convertDiff(bBase, high >> 8);
21501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    } else {
21601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        // not differential
21701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        r1 = convert4To8(high >> 28);
21801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        r2 = convert4To8(high >> 24);
21901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        g1 = convert4To8(high >> 20);
22001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        g2 = convert4To8(high >> 16);
22101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        b1 = convert4To8(high >> 12);
22201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        b2 = convert4To8(high >> 8);
22301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    }
22401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int tableIndexA = 7 & (high >> 5);
22501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int tableIndexB = 7 & (high >> 2);
22601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    const int* tableA = kModifierTable + tableIndexA * 4;
22701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    const int* tableB = kModifierTable + tableIndexB * 4;
22801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    bool flipped = (high & 1) != 0;
22901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    decode_subblock(pOut, r1, g1, b1, tableA, low, false, flipped);
23001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    decode_subblock(pOut, r2, g2, b2, tableB, low, true, flipped);
23101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
23201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
23301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichtypedef struct {
23401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 high;
23501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 low;
23601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 score; // Lower is more accurate
23701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich} etc_compressed;
23801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
23901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic
24001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichinline void take_best(etc_compressed* a, const etc_compressed* b) {
24101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    if (a->score > b->score) {
24201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        *a = *b;
24301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    }
24401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
24501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
24601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic
24701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichvoid etc_average_colors_subblock(const etc1_byte* pIn, etc1_uint32 inMask,
24801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        etc1_byte* pColors, bool flipped, bool second) {
24901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int r = 0;
25001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int g = 0;
25101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int b = 0;
25201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
25301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    if (flipped) {
25401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int by = 0;
25501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        if (second) {
25601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            by = 2;
25701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
25801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        for (int y = 0; y < 2; y++) {
25901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            int yy = by + y;
26001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            for (int x = 0; x < 4; x++) {
26101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                int i = x + 4 * yy;
26201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                if (inMask & (1 << i)) {
26301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                    const etc1_byte* p = pIn + i * 3;
26401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                    r += *(p++);
26501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                    g += *(p++);
26601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                    b += *(p++);
26701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                }
26801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            }
26901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
27001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    } else {
27101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int bx = 0;
27201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        if (second) {
27301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            bx = 2;
27401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
27501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        for (int y = 0; y < 4; y++) {
27601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            for (int x = 0; x < 2; x++) {
27701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                int xx = bx + x;
27801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                int i = xx + 4 * y;
27901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                if (inMask & (1 << i)) {
28001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                    const etc1_byte* p = pIn + i * 3;
28101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                    r += *(p++);
28201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                    g += *(p++);
28301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                    b += *(p++);
28401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                }
28501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            }
28601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
28701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    }
28801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pColors[0] = (etc1_byte)((r + 4) >> 3);
28901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pColors[1] = (etc1_byte)((g + 4) >> 3);
29001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pColors[2] = (etc1_byte)((b + 4) >> 3);
29101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
29201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
29301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic
29401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichinline int square(int x) {
29501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    return x * x;
29601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
29701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
29801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic etc1_uint32 chooseModifier(const etc1_byte* pBaseColors,
29901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        const etc1_byte* pIn, etc1_uint32 *pLow, int bitIndex,
30001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        const int* pModifierTable) {
30101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 bestScore = ~0;
30201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int bestIndex = 0;
30301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int pixelR = pIn[0];
30401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int pixelG = pIn[1];
30501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int pixelB = pIn[2];
30601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int r = pBaseColors[0];
30701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int g = pBaseColors[1];
30801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int b = pBaseColors[2];
30901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    for (int i = 0; i < 4; i++) {
31001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int modifier = pModifierTable[i];
31101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int decodedG = clamp(g + modifier);
31201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        etc1_uint32 score = (etc1_uint32) (6 * square(decodedG - pixelG));
31301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        if (score >= bestScore) {
31401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            continue;
31501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
31601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int decodedR = clamp(r + modifier);
31701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        score += (etc1_uint32) (3 * square(decodedR - pixelR));
31801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        if (score >= bestScore) {
31901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            continue;
32001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
32101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int decodedB = clamp(b + modifier);
32201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        score += (etc1_uint32) square(decodedB - pixelB);
32301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        if (score < bestScore) {
32401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            bestScore = score;
32501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            bestIndex = i;
32601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
32701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    }
32801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 lowMask = (((bestIndex >> 1) << 16) | (bestIndex & 1))
32901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            << bitIndex;
33001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    *pLow |= lowMask;
33101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    return bestScore;
33201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
33301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
33401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic
33501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichvoid etc_encode_subblock_helper(const etc1_byte* pIn, etc1_uint32 inMask,
33601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        etc_compressed* pCompressed, bool flipped, bool second,
33701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        const etc1_byte* pBaseColors, const int* pModifierTable) {
33801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int score = pCompressed->score;
33901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    if (flipped) {
34001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int by = 0;
34101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        if (second) {
34201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            by = 2;
34301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
34401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        for (int y = 0; y < 2; y++) {
34501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            int yy = by + y;
34601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            for (int x = 0; x < 4; x++) {
34701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                int i = x + 4 * yy;
34801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                if (inMask & (1 << i)) {
34901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                    score += chooseModifier(pBaseColors, pIn + i * 3,
35001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                            &pCompressed->low, yy + x * 4, pModifierTable);
35101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                }
35201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            }
35301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
35401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    } else {
35501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int bx = 0;
35601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        if (second) {
35701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            bx = 2;
35801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
35901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        for (int y = 0; y < 4; y++) {
36001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            for (int x = 0; x < 2; x++) {
36101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                int xx = bx + x;
36201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                int i = xx + 4 * y;
36301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                if (inMask & (1 << i)) {
36401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                    score += chooseModifier(pBaseColors, pIn + i * 3,
36501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                            &pCompressed->low, y + xx * 4, pModifierTable);
36601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                }
36701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            }
36801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
36901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    }
37001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pCompressed->score = score;
37101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
37201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
37301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic bool inRange4bitSigned(int color) {
37401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    return color >= -4 && color <= 3;
37501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
37601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
37701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic void etc_encodeBaseColors(etc1_byte* pBaseColors,
37801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        const etc1_byte* pColors, etc_compressed* pCompressed) {
37901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int r1, g1, b1, r2, g2, b2; // 8 bit base colors for sub-blocks
38001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    bool differential;
38101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    {
38201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int r51 = convert8To5(pColors[0]);
38301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int g51 = convert8To5(pColors[1]);
38401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int b51 = convert8To5(pColors[2]);
38501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int r52 = convert8To5(pColors[3]);
38601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int g52 = convert8To5(pColors[4]);
38701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int b52 = convert8To5(pColors[5]);
38801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
38901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        r1 = convert5To8(r51);
39001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        g1 = convert5To8(g51);
39101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        b1 = convert5To8(b51);
39201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
39301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int dr = r52 - r51;
39401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int dg = g52 - g51;
39501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int db = b52 - b51;
39601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
39701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        differential = inRange4bitSigned(dr) && inRange4bitSigned(dg)
39801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                && inRange4bitSigned(db);
39901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        if (differential) {
40001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            r2 = convert5To8(r51 + dr);
40101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            g2 = convert5To8(g51 + dg);
40201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            b2 = convert5To8(b51 + db);
40301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            pCompressed->high |= (r51 << 27) | ((7 & dr) << 24) | (g51 << 19)
40401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                    | ((7 & dg) << 16) | (b51 << 11) | ((7 & db) << 8) | 2;
40501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
40601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    }
40701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
40801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    if (!differential) {
40901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int r41 = convert8To4(pColors[0]);
41001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int g41 = convert8To4(pColors[1]);
41101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int b41 = convert8To4(pColors[2]);
41201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int r42 = convert8To4(pColors[3]);
41301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int g42 = convert8To4(pColors[4]);
41401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int b42 = convert8To4(pColors[5]);
41501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        r1 = convert4To8(r41);
41601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        g1 = convert4To8(g41);
41701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        b1 = convert4To8(b41);
41801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        r2 = convert4To8(r42);
41901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        g2 = convert4To8(g42);
42001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        b2 = convert4To8(b42);
42101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        pCompressed->high |= (r41 << 28) | (r42 << 24) | (g41 << 20) | (g42
42201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                << 16) | (b41 << 12) | (b42 << 8);
42301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    }
42401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pBaseColors[0] = r1;
42501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pBaseColors[1] = g1;
42601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pBaseColors[2] = b1;
42701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pBaseColors[3] = r2;
42801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pBaseColors[4] = g2;
42901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pBaseColors[5] = b2;
43001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
43101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
43201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic
43301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichvoid etc_encode_block_helper(const etc1_byte* pIn, etc1_uint32 inMask,
43401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        const etc1_byte* pColors, etc_compressed* pCompressed, bool flipped) {
43501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pCompressed->score = ~0;
43601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pCompressed->high = (flipped ? 1 : 0);
43701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pCompressed->low = 0;
43801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
43901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_byte pBaseColors[6];
44001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
44101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc_encodeBaseColors(pBaseColors, pColors, pCompressed);
44201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
44301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    int originalHigh = pCompressed->high;
44401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
44501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    const int* pModifierTable = kModifierTable;
44601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    for (int i = 0; i < 8; i++, pModifierTable += 4) {
44701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        etc_compressed temp;
44801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        temp.score = 0;
44901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        temp.high = originalHigh | (i << 5);
45001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        temp.low = 0;
45101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        etc_encode_subblock_helper(pIn, inMask, &temp, flipped, false,
45201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                pBaseColors, pModifierTable);
45301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        take_best(pCompressed, &temp);
45401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    }
45501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pModifierTable = kModifierTable;
45601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc_compressed firstHalf = *pCompressed;
45701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    for (int i = 0; i < 8; i++, pModifierTable += 4) {
45801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        etc_compressed temp;
45901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        temp.score = firstHalf.score;
46001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        temp.high = firstHalf.high | (i << 2);
46101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        temp.low = firstHalf.low;
46201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        etc_encode_subblock_helper(pIn, inMask, &temp, flipped, true,
46301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                pBaseColors + 3, pModifierTable);
46401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        if (i == 0) {
46501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            *pCompressed = temp;
46601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        } else {
46701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            take_best(pCompressed, &temp);
46801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
46901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    }
47001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
47101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
47201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic void writeBigEndian(etc1_byte* pOut, etc1_uint32 d) {
47301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pOut[0] = (etc1_byte)(d >> 24);
47401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pOut[1] = (etc1_byte)(d >> 16);
47501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pOut[2] = (etc1_byte)(d >> 8);
47601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pOut[3] = (etc1_byte) d;
47701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
47801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
47901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// Input is a 4 x 4 square of 3-byte pixels in form R, G, B
48001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// inmask is a 16-bit mask where bit (1 << (x + y * 4)) tells whether the corresponding (x,y)
48101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// pixel is valid or not. Invalid pixel color values are ignored when compressing.
48201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// Output is an ETC1 compressed version of the data.
48301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
48401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichvoid etc1_encode_block(const etc1_byte* pIn, etc1_uint32 inMask,
48501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        etc1_byte* pOut) {
48601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_byte colors[6];
48701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_byte flippedColors[6];
48801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc_average_colors_subblock(pIn, inMask, colors, false, false);
48901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc_average_colors_subblock(pIn, inMask, colors + 3, false, true);
49001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc_average_colors_subblock(pIn, inMask, flippedColors, true, false);
49101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc_average_colors_subblock(pIn, inMask, flippedColors + 3, true, true);
49201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
49301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc_compressed a, b;
49401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc_encode_block_helper(pIn, inMask, colors, &a, false);
49501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc_encode_block_helper(pIn, inMask, flippedColors, &b, true);
49601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    take_best(&a, &b);
49701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    writeBigEndian(pOut, a.high);
49801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    writeBigEndian(pOut + 4, a.low);
49901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
50001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
50101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// Return the size of the encoded image data (does not include size of PKM header).
50201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
50301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichetc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height) {
50401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    return (((width + 3) & ~3) * ((height + 3) & ~3)) >> 1;
50501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
50601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
50701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// Encode an entire image.
50801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// pIn - pointer to the image data. Formatted such that the Red component of
50901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich//       pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
51001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// pOut - pointer to encoded data. Must be large enough to store entire encoded image.
51101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
51201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichint etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height,
51301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut) {
51401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    if (pixelSize < 2 || pixelSize > 3) {
51501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        return -1;
51601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    }
51701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    static const unsigned short kYMask[] = { 0x0, 0xf, 0xff, 0xfff, 0xffff };
51801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    static const unsigned short kXMask[] = { 0x0, 0x1111, 0x3333, 0x7777,
51901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            0xffff };
52001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
52101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_byte encoded[ETC1_ENCODED_BLOCK_SIZE];
52201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
52301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 encodedWidth = (width + 3) & ~3;
52401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 encodedHeight = (height + 3) & ~3;
52501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
52601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
52701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        etc1_uint32 yEnd = height - y;
52801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        if (yEnd > 4) {
52901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            yEnd = 4;
53001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
53101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        int ymask = kYMask[yEnd];
53201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
53301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            etc1_uint32 xEnd = width - x;
53401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            if (xEnd > 4) {
53501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                xEnd = 4;
53601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            }
53701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            int mask = ymask & kXMask[xEnd];
53801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
53901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                etc1_byte* q = block + (cy * 4) * 3;
54001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                const etc1_byte* p = pIn + pixelSize * x + stride * (y + cy);
54101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                if (pixelSize == 3) {
54201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                    memcpy(q, p, xEnd * 3);
54301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                } else {
54401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                    for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
54501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                        int pixel = (p[1] << 8) | p[0];
54601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                        *q++ = convert5To8(pixel >> 11);
54701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                        *q++ = convert6To8(pixel >> 5);
54801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                        *q++ = convert5To8(pixel);
54901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                        p += pixelSize;
55001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                    }
55101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                }
55201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            }
55301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            etc1_encode_block(block, mask, encoded);
55401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            memcpy(pOut, encoded, sizeof(encoded));
55501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            pOut += sizeof(encoded);
55601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
55701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    }
55801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    return 0;
55901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
56001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
56101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// Decode an entire image.
56201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// pIn - pointer to encoded data.
56301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// pOut - pointer to the image data. Will be written such that the Red component of
56401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich//       pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset. Must be
56501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich//        large enough to store entire image.
56601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
56701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
56801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichint etc1_decode_image(const etc1_byte* pIn, etc1_byte* pOut,
56901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        etc1_uint32 width, etc1_uint32 height,
57001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        etc1_uint32 pixelSize, etc1_uint32 stride) {
57101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    if (pixelSize < 2 || pixelSize > 3) {
57201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        return -1;
57301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    }
57401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
57501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
57601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 encodedWidth = (width + 3) & ~3;
57701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 encodedHeight = (height + 3) & ~3;
57801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
57901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
58001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        etc1_uint32 yEnd = height - y;
58101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        if (yEnd > 4) {
58201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            yEnd = 4;
58301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
58401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
58501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            etc1_uint32 xEnd = width - x;
58601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            if (xEnd > 4) {
58701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                xEnd = 4;
58801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            }
58901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            etc1_decode_block(pIn, block);
59001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            pIn += ETC1_ENCODED_BLOCK_SIZE;
59101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
59201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                const etc1_byte* q = block + (cy * 4) * 3;
59301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                etc1_byte* p = pOut + pixelSize * x + stride * (y + cy);
59401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                if (pixelSize == 3) {
59501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                    memcpy(p, q, xEnd * 3);
59601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                } else {
59701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                    for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
59801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                        etc1_byte r = *q++;
59901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                        etc1_byte g = *q++;
60001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                        etc1_byte b = *q++;
60101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                        etc1_uint32 pixel = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
60201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                        *p++ = (etc1_byte) pixel;
60301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                        *p++ = (etc1_byte) (pixel >> 8);
60401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                    }
60501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich                }
60601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            }
60701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        }
60801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    }
60901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    return 0;
61001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
61101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
61201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic const char kMagic[] = { 'P', 'K', 'M', ' ', '1', '0' };
61301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
61401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic const etc1_uint32 ETC1_PKM_FORMAT_OFFSET = 6;
61501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic const etc1_uint32 ETC1_PKM_ENCODED_WIDTH_OFFSET = 8;
61601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic const etc1_uint32 ETC1_PKM_ENCODED_HEIGHT_OFFSET = 10;
61701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic const etc1_uint32 ETC1_PKM_WIDTH_OFFSET = 12;
61801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic const etc1_uint32 ETC1_PKM_HEIGHT_OFFSET = 14;
61901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
62001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic const etc1_uint32 ETC1_RGB_NO_MIPMAPS = 0;
62101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
62201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic void writeBEUint16(etc1_byte* pOut, etc1_uint32 data) {
62301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pOut[0] = (etc1_byte) (data >> 8);
62401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    pOut[1] = (etc1_byte) data;
62501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
62601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
62701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichstatic etc1_uint32 readBEUint16(const etc1_byte* pIn) {
62801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    return (pIn[0] << 8) | pIn[1];
62901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
63001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
63101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// Format a PKM header
63201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
63301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichvoid etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height) {
63401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    memcpy(pHeader, kMagic, sizeof(kMagic));
63501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 encodedWidth = (width + 3) & ~3;
63601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 encodedHeight = (height + 3) & ~3;
63701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    writeBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET, ETC1_RGB_NO_MIPMAPS);
63801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    writeBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET, encodedWidth);
63901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    writeBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET, encodedHeight);
64001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    writeBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET, width);
64101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    writeBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET, height);
64201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
64301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
64401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// Check if a PKM header is correctly formatted.
64501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
64601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichetc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader) {
64701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    if (memcmp(pHeader, kMagic, sizeof(kMagic))) {
64801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich        return false;
64901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    }
65001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 format = readBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET);
65101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 encodedWidth = readBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET);
65201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 encodedHeight = readBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET);
65301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 width = readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
65401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    etc1_uint32 height = readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
65501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    return format == ETC1_RGB_NO_MIPMAPS &&
65601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            encodedWidth >= width && encodedWidth - width < 4 &&
65701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich            encodedHeight >= height && encodedHeight - height < 4;
65801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
65901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
66001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// Read the image width from a PKM header
66101cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
66201cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichetc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader) {
66301cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    return readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
66401cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
66501cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
66601cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich// Read the image height from a PKM header
66701cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich
66801cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevichetc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader){
66901cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich    return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
67001cc538b290ecc9ccfdf1907fb714b76cf0f648bJack Palevich}
671