18cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// Copyright 2009 Google Inc.
28cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com//
38cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// Licensed under the Apache License, Version 2.0 (the "License");
48cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// you may not use this file except in compliance with the License.
58cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// You may obtain a copy of the License at
68cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com//
78cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com//     http://www.apache.org/licenses/LICENSE-2.0
88cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com//
98cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// Unless required by applicable law or agreed to in writing, software
108cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// distributed under the License is distributed on an "AS IS" BASIS,
118cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
128cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// See the License for the specific language governing permissions and
138cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// limitations under the License.
148cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
158cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com//////////////////////////////////////////////////////////////////////////////////////////
168cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
178cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// This is a fork of the AOSP project ETC1 codec. The original code can be found
188cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// at the following web site:
198cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// https://android.googlesource.com/platform/frameworks/native/+/master/opengl/include/ETC1/
208cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
218cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com//////////////////////////////////////////////////////////////////////////////////////////
228cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
238cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com#include "etc1.h"
248cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
258cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com#include <cstring>
268cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
278cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com/* From http://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
288cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
298cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com The number of bits that represent a 4x4 texel block is 64 bits if
308cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com <internalformat> is given by ETC1_RGB8_OES.
318cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
328cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com The data for a block is a number of bytes,
338cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
348cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com {q0, q1, q2, q3, q4, q5, q6, q7}
358cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
368cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com where byte q0 is located at the lowest memory address and q7 at
378cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com the highest. The 64 bits specifying the block is then represented
388cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com by the following 64 bit integer:
398cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
408cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com int64bit = 256*(256*(256*(256*(256*(256*(256*q0+q1)+q2)+q3)+q4)+q5)+q6)+q7;
418cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
428cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com ETC1_RGB8_OES:
438cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
448cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com a) bit layout in bits 63 through 32 if diffbit = 0
458cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
468cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
478cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com -----------------------------------------------
488cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com | base col1 | base col2 | base col1 | base col2 |
498cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)|
508cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com -----------------------------------------------
518cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
528cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 47 46 45 44 43 42 41 40 39 38 37 36 35 34  33  32
538cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com ---------------------------------------------------
548cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com | base col1 | base col2 | table  | table  |diff|flip|
558cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com | B1 (4bits)| B2 (4bits)| cw 1   | cw 2   |bit |bit |
568cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com ---------------------------------------------------
578cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
588cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
598cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com b) bit layout in bits 63 through 32 if diffbit = 1
608cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
618cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
628cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com -----------------------------------------------
638cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com | base col1    | dcol 2 | base col1    | dcol 2 |
648cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com | R1' (5 bits) | dR2    | G1' (5 bits) | dG2    |
658cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com -----------------------------------------------
668cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
678cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 47 46 45 44 43 42 41 40 39 38 37 36 35 34  33  32
688cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com ---------------------------------------------------
698cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com | base col 1   | dcol 2 | table  | table  |diff|flip|
708cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com | B1' (5 bits) | dB2    | cw 1   | cw 2   |bit |bit |
718cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com ---------------------------------------------------
728cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
738cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
748cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com c) bit layout in bits 31 through 0 (in both cases)
758cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
768cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
778cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com -----------------------------------------------
788cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com |       most significant pixel index bits       |
798cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a|
808cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com -----------------------------------------------
818cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
828cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 15 14 13 12 11 10  9  8  7  6  5  4  3   2   1  0
838cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com --------------------------------------------------
848cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com |         least significant pixel index bits       |
858cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com | p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a |
868cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com --------------------------------------------------
878cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
888cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
898cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com Add table 3.17.2: Intensity modifier sets for ETC1 compressed textures:
908cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
918cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com table codeword                modifier table
928cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com ------------------        ----------------------
938cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 0                     -8  -2  2   8
948cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 1                    -17  -5  5  17
958cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 2                    -29  -9  9  29
968cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 3                    -42 -13 13  42
978cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 4                    -60 -18 18  60
988cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 5                    -80 -24 24  80
998cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 6                   -106 -33 33 106
1008cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 7                   -183 -47 47 183
1018cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
1028cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
1038cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com Add table 3.17.3 Mapping from pixel index values to modifier values for
1048cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com ETC1 compressed textures:
1058cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
1068cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com pixel index value
1078cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com ---------------
1088cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com msb     lsb           resulting modifier value
1098cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com -----   -----          -------------------------
1108cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 1       1            -b (large negative value)
1118cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 1       0            -a (small negative value)
1128cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 0       0             a (small positive value)
1138cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com 0       1             b (large positive value)
1148cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
1158cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
1168cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com */
1178cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
1188cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic const int kModifierTable[] = {
1198cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com/* 0 */2, 8, -2, -8,
1208cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com/* 1 */5, 17, -5, -17,
1218cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com/* 2 */9, 29, -9, -29,
1228cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com/* 3 */13, 42, -13, -42,
1238cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com/* 4 */18, 60, -18, -60,
1248cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com/* 5 */24, 80, -24, -80,
1258cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com/* 6 */33, 106, -33, -106,
1268cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com/* 7 */47, 183, -47, -183 };
1278cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
1288cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic const int kLookup[8] = { 0, 1, 2, 3, -4, -3, -2, -1 };
1298cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
1308cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic inline etc1_byte clamp(int x) {
1318cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return (etc1_byte) (x >= 0 ? (x < 255 ? x : 255) : 0);
1328cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
1338cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
1348cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic
1358cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.cominline int convert4To8(int b) {
1368cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int c = b & 0xf;
1378cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return (c << 4) | c;
1388cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
1398cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
1408cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic
1418cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.cominline int convert5To8(int b) {
1428cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int c = b & 0x1f;
1438cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return (c << 3) | (c >> 2);
1448cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
1458cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
1468cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic
1478cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.cominline int convert6To8(int b) {
1488cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int c = b & 0x3f;
1498cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return (c << 2) | (c >> 4);
1508cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
1518cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
1528cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic
1538cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.cominline int divideBy255(int d) {
1548cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return (d + 128 + (d >> 8)) >> 8;
1558cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
1568cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
1578cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic
1588cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.cominline int convert8To4(int b) {
1598cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int c = b & 0xff;
1608cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return divideBy255(c * 15);
1618cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
1628cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
1638cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic
1648cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.cominline int convert8To5(int b) {
1658cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int c = b & 0xff;
1668cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return divideBy255(c * 31);
1678cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
1688cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
1698cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic
1708cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.cominline int convertDiff(int base, int diff) {
1718cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return convert5To8((0x1f & base) + kLookup[0x7 & diff]);
1728cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
1738cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
1748cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic
1758cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comvoid decode_subblock(etc1_byte* pOut, int r, int g, int b, const int* table,
1768cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        etc1_uint32 low, bool second, bool flipped) {
1778cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int baseX = 0;
1788cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int baseY = 0;
1798cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    if (second) {
1808cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        if (flipped) {
1818cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            baseY = 2;
1828cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        } else {
1838cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            baseX = 2;
1848cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
1858cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    }
1868cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    for (int i = 0; i < 8; i++) {
1878cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int x, y;
1888cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        if (flipped) {
1898cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            x = baseX + (i >> 1);
1908cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            y = baseY + (i & 1);
1918cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        } else {
1928cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            x = baseX + (i >> 2);
1938cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            y = baseY + (i & 3);
1948cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
1958cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int k = y + (x * 4);
1968cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int offset = ((low >> k) & 1) | ((low >> (k + 15)) & 2);
1978cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int delta = table[offset];
1988cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        etc1_byte* q = pOut + 3 * (x + 4 * y);
1998cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        *q++ = clamp(r + delta);
2008cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        *q++ = clamp(g + delta);
2018cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        *q++ = clamp(b + delta);
2028cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    }
2038cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
2048cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
2058cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// Input is an ETC1 compressed version of the data.
2068cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// Output is a 4 x 4 square of 3-byte pixels in form R, G, B
2078cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
2088cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comvoid etc1_decode_block(const etc1_byte* pIn, etc1_byte* pOut) {
2098cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 high = (pIn[0] << 24) | (pIn[1] << 16) | (pIn[2] << 8) | pIn[3];
2108cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 low = (pIn[4] << 24) | (pIn[5] << 16) | (pIn[6] << 8) | pIn[7];
2118cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int r1, r2, g1, g2, b1, b2;
2128cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    if (high & 2) {
2138cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        // differential
2148cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int rBase = high >> 27;
2158cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int gBase = high >> 19;
2168cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int bBase = high >> 11;
2178cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        r1 = convert5To8(rBase);
2188cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        r2 = convertDiff(rBase, high >> 24);
2198cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        g1 = convert5To8(gBase);
2208cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        g2 = convertDiff(gBase, high >> 16);
2218cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        b1 = convert5To8(bBase);
2228cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        b2 = convertDiff(bBase, high >> 8);
2238cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    } else {
2248cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        // not differential
2258cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        r1 = convert4To8(high >> 28);
2268cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        r2 = convert4To8(high >> 24);
2278cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        g1 = convert4To8(high >> 20);
2288cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        g2 = convert4To8(high >> 16);
2298cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        b1 = convert4To8(high >> 12);
2308cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        b2 = convert4To8(high >> 8);
2318cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    }
2328cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int tableIndexA = 7 & (high >> 5);
2338cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int tableIndexB = 7 & (high >> 2);
2348cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    const int* tableA = kModifierTable + tableIndexA * 4;
2358cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    const int* tableB = kModifierTable + tableIndexB * 4;
2368cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    bool flipped = (high & 1) != 0;
2378cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    decode_subblock(pOut, r1, g1, b1, tableA, low, false, flipped);
2388cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    decode_subblock(pOut, r2, g2, b2, tableB, low, true, flipped);
2398cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
2408cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
2418cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comtypedef struct {
2428cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 high;
2438cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 low;
2448cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 score; // Lower is more accurate
2458cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com} etc_compressed;
2468cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
2478cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic
2488cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.cominline void take_best(etc_compressed* a, const etc_compressed* b) {
2498cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    if (a->score > b->score) {
2508cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        *a = *b;
2518cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    }
2528cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
2538cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
2548cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic
2558cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comvoid etc_average_colors_subblock(const etc1_byte* pIn, etc1_uint32 inMask,
2568cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        etc1_byte* pColors, bool flipped, bool second) {
2578cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int r = 0;
2588cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int g = 0;
2598cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int b = 0;
2608cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
2618cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    if (flipped) {
2628cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int by = 0;
2638cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        if (second) {
2648cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            by = 2;
2658cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
2668cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        for (int y = 0; y < 2; y++) {
2678cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            int yy = by + y;
2688cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            for (int x = 0; x < 4; x++) {
2698cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                int i = x + 4 * yy;
2708cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                if (inMask & (1 << i)) {
2718cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                    const etc1_byte* p = pIn + i * 3;
2728cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                    r += *(p++);
2738cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                    g += *(p++);
2748cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                    b += *(p++);
2758cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                }
2768cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            }
2778cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
2788cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    } else {
2798cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int bx = 0;
2808cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        if (second) {
2818cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            bx = 2;
2828cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
2838cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        for (int y = 0; y < 4; y++) {
2848cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            for (int x = 0; x < 2; x++) {
2858cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                int xx = bx + x;
2868cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                int i = xx + 4 * y;
2878cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                if (inMask & (1 << i)) {
2888cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                    const etc1_byte* p = pIn + i * 3;
2898cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                    r += *(p++);
2908cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                    g += *(p++);
2918cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                    b += *(p++);
2928cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                }
2938cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            }
2948cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
2958cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    }
2968cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pColors[0] = (etc1_byte)((r + 4) >> 3);
2978cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pColors[1] = (etc1_byte)((g + 4) >> 3);
2988cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pColors[2] = (etc1_byte)((b + 4) >> 3);
2998cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
3008cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
3018cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic
3028cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.cominline int square(int x) {
3038cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return x * x;
3048cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
3058cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
3068cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic etc1_uint32 chooseModifier(const etc1_byte* pBaseColors,
3078cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        const etc1_byte* pIn, etc1_uint32 *pLow, int bitIndex,
3088cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        const int* pModifierTable) {
3098cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 bestScore = ~0;
3108cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int bestIndex = 0;
3118cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int pixelR = pIn[0];
3128cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int pixelG = pIn[1];
3138cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int pixelB = pIn[2];
3148cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int r = pBaseColors[0];
3158cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int g = pBaseColors[1];
3168cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int b = pBaseColors[2];
3178cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    for (int i = 0; i < 4; i++) {
3188cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int modifier = pModifierTable[i];
3198cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int decodedG = clamp(g + modifier);
3208cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        etc1_uint32 score = (etc1_uint32) (6 * square(decodedG - pixelG));
3218cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        if (score >= bestScore) {
3228cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            continue;
3238cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
3248cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int decodedR = clamp(r + modifier);
3258cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        score += (etc1_uint32) (3 * square(decodedR - pixelR));
3268cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        if (score >= bestScore) {
3278cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            continue;
3288cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
3298cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int decodedB = clamp(b + modifier);
3308cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        score += (etc1_uint32) square(decodedB - pixelB);
3318cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        if (score < bestScore) {
3328cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            bestScore = score;
3338cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            bestIndex = i;
3348cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
3358cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    }
3368cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 lowMask = (((bestIndex >> 1) << 16) | (bestIndex & 1))
3378cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            << bitIndex;
3388cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    *pLow |= lowMask;
3398cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return bestScore;
3408cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
3418cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
3428cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic
3438cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comvoid etc_encode_subblock_helper(const etc1_byte* pIn, etc1_uint32 inMask,
3448cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        etc_compressed* pCompressed, bool flipped, bool second,
3458cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        const etc1_byte* pBaseColors, const int* pModifierTable) {
3468cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int score = pCompressed->score;
3478cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    if (flipped) {
3488cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int by = 0;
3498cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        if (second) {
3508cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            by = 2;
3518cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
3528cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        for (int y = 0; y < 2; y++) {
3538cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            int yy = by + y;
3548cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            for (int x = 0; x < 4; x++) {
3558cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                int i = x + 4 * yy;
3568cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                if (inMask & (1 << i)) {
3578cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                    score += chooseModifier(pBaseColors, pIn + i * 3,
3588cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                            &pCompressed->low, yy + x * 4, pModifierTable);
3598cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                }
3608cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            }
3618cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
3628cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    } else {
3638cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int bx = 0;
3648cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        if (second) {
3658cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            bx = 2;
3668cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
3678cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        for (int y = 0; y < 4; y++) {
3688cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            for (int x = 0; x < 2; x++) {
3698cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                int xx = bx + x;
3708cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                int i = xx + 4 * y;
3718cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                if (inMask & (1 << i)) {
3728cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                    score += chooseModifier(pBaseColors, pIn + i * 3,
3738cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                            &pCompressed->low, y + xx * 4, pModifierTable);
3748cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                }
3758cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            }
3768cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
3778cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    }
3788cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pCompressed->score = score;
3798cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
3808cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
3818cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic bool inRange4bitSigned(int color) {
3828cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return color >= -4 && color <= 3;
3838cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
3848cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
3858cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic void etc_encodeBaseColors(etc1_byte* pBaseColors,
3868cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        const etc1_byte* pColors, etc_compressed* pCompressed) {
3878cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int r1, g1, b1, r2, g2, b2; // 8 bit base colors for sub-blocks
3888cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    bool differential;
3898cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    {
3908cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int r51 = convert8To5(pColors[0]);
3918cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int g51 = convert8To5(pColors[1]);
3928cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int b51 = convert8To5(pColors[2]);
3938cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int r52 = convert8To5(pColors[3]);
3948cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int g52 = convert8To5(pColors[4]);
3958cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int b52 = convert8To5(pColors[5]);
3968cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
3978cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        r1 = convert5To8(r51);
3988cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        g1 = convert5To8(g51);
3998cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        b1 = convert5To8(b51);
4008cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
4018cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int dr = r52 - r51;
4028cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int dg = g52 - g51;
4038cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int db = b52 - b51;
4048cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
4058cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        differential = inRange4bitSigned(dr) && inRange4bitSigned(dg)
4068cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                && inRange4bitSigned(db);
4078cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        if (differential) {
4088cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            r2 = convert5To8(r51 + dr);
4098cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            g2 = convert5To8(g51 + dg);
4108cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            b2 = convert5To8(b51 + db);
4118cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            pCompressed->high |= (r51 << 27) | ((7 & dr) << 24) | (g51 << 19)
4128cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                    | ((7 & dg) << 16) | (b51 << 11) | ((7 & db) << 8) | 2;
4138cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
4148cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    }
4158cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
4168cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    if (!differential) {
4178cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int r41 = convert8To4(pColors[0]);
4188cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int g41 = convert8To4(pColors[1]);
4198cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int b41 = convert8To4(pColors[2]);
4208cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int r42 = convert8To4(pColors[3]);
4218cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int g42 = convert8To4(pColors[4]);
4228cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int b42 = convert8To4(pColors[5]);
4238cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        r1 = convert4To8(r41);
4248cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        g1 = convert4To8(g41);
4258cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        b1 = convert4To8(b41);
4268cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        r2 = convert4To8(r42);
4278cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        g2 = convert4To8(g42);
4288cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        b2 = convert4To8(b42);
4298cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        pCompressed->high |= (r41 << 28) | (r42 << 24) | (g41 << 20) | (g42
4308cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                << 16) | (b41 << 12) | (b42 << 8);
4318cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    }
4328cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pBaseColors[0] = r1;
4338cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pBaseColors[1] = g1;
4348cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pBaseColors[2] = b1;
4358cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pBaseColors[3] = r2;
4368cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pBaseColors[4] = g2;
4378cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pBaseColors[5] = b2;
4388cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
4398cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
4408cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic
4418cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comvoid etc_encode_block_helper(const etc1_byte* pIn, etc1_uint32 inMask,
4428cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        const etc1_byte* pColors, etc_compressed* pCompressed, bool flipped) {
4438cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pCompressed->score = ~0;
4448cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pCompressed->high = (flipped ? 1 : 0);
4458cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pCompressed->low = 0;
4468cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
4478cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_byte pBaseColors[6];
4488cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
4498cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc_encodeBaseColors(pBaseColors, pColors, pCompressed);
4508cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
4518cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    int originalHigh = pCompressed->high;
4528cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
4538cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    const int* pModifierTable = kModifierTable;
4548cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    for (int i = 0; i < 8; i++, pModifierTable += 4) {
4558cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        etc_compressed temp;
4568cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        temp.score = 0;
4578cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        temp.high = originalHigh | (i << 5);
4588cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        temp.low = 0;
4598cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        etc_encode_subblock_helper(pIn, inMask, &temp, flipped, false,
4608cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                pBaseColors, pModifierTable);
4618cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        take_best(pCompressed, &temp);
4628cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    }
4638cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pModifierTable = kModifierTable;
4648cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc_compressed firstHalf = *pCompressed;
4658cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    for (int i = 0; i < 8; i++, pModifierTable += 4) {
4668cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        etc_compressed temp;
4678cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        temp.score = firstHalf.score;
4688cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        temp.high = firstHalf.high | (i << 2);
4698cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        temp.low = firstHalf.low;
4708cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        etc_encode_subblock_helper(pIn, inMask, &temp, flipped, true,
4718cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                pBaseColors + 3, pModifierTable);
4728cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        if (i == 0) {
4738cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            *pCompressed = temp;
4748cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        } else {
4758cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            take_best(pCompressed, &temp);
4768cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
4778cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    }
4788cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
4798cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
4808cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic void writeBigEndian(etc1_byte* pOut, etc1_uint32 d) {
4818cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pOut[0] = (etc1_byte)(d >> 24);
4828cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pOut[1] = (etc1_byte)(d >> 16);
4838cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pOut[2] = (etc1_byte)(d >> 8);
4848cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pOut[3] = (etc1_byte) d;
4858cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
4868cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
4878cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// Input is a 4 x 4 square of 3-byte pixels in form R, G, B
4888cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// inmask is a 16-bit mask where bit (1 << (x + y * 4)) tells whether the corresponding (x,y)
4898cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// pixel is valid or not. Invalid pixel color values are ignored when compressing.
4908cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// Output is an ETC1 compressed version of the data.
4918cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
4928cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comvoid etc1_encode_block(const etc1_byte* pIn, etc1_uint32 inMask,
4938cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        etc1_byte* pOut) {
4948cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_byte colors[6];
4958cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_byte flippedColors[6];
4968cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc_average_colors_subblock(pIn, inMask, colors, false, false);
4978cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc_average_colors_subblock(pIn, inMask, colors + 3, false, true);
4988cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc_average_colors_subblock(pIn, inMask, flippedColors, true, false);
4998cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc_average_colors_subblock(pIn, inMask, flippedColors + 3, true, true);
5008cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
5018cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc_compressed a, b;
5028cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc_encode_block_helper(pIn, inMask, colors, &a, false);
5038cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc_encode_block_helper(pIn, inMask, flippedColors, &b, true);
5048cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    take_best(&a, &b);
5058cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    writeBigEndian(pOut, a.high);
5068cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    writeBigEndian(pOut + 4, a.low);
5078cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
5088cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
5098cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// Return the size of the encoded image data (does not include size of PKM header).
5108cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
5118cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.cometc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height) {
5128cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return (((width + 3) & ~3) * ((height + 3) & ~3)) >> 1;
5138cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
5148cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
5158cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// Encode an entire image.
5168cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// pIn - pointer to the image data. Formatted such that the Red component of
5178cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com//       pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
5188cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// pOut - pointer to encoded data. Must be large enough to store entire encoded image.
5198cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
5208cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comint etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height,
5218cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut) {
5228cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    if (pixelSize < 2 || pixelSize > 3) {
5238cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        return -1;
5248cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    }
5258cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    static const unsigned short kYMask[] = { 0x0, 0xf, 0xff, 0xfff, 0xffff };
5268cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    static const unsigned short kXMask[] = { 0x0, 0x1111, 0x3333, 0x7777,
5278cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            0xffff };
5288cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
5298cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_byte encoded[ETC1_ENCODED_BLOCK_SIZE];
5308cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
5318cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 encodedWidth = (width + 3) & ~3;
5328cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 encodedHeight = (height + 3) & ~3;
5338cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
5348cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
5358cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        etc1_uint32 yEnd = height - y;
5368cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        if (yEnd > 4) {
5378cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            yEnd = 4;
5388cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
5398cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        int ymask = kYMask[yEnd];
5408cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
5418cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            etc1_uint32 xEnd = width - x;
5428cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            if (xEnd > 4) {
5438cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                xEnd = 4;
5448cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            }
5458cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            int mask = ymask & kXMask[xEnd];
5468cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
5478cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                etc1_byte* q = block + (cy * 4) * 3;
5488cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                const etc1_byte* p = pIn + pixelSize * x + stride * (y + cy);
5498cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                if (pixelSize == 3) {
5508cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                    memcpy(q, p, xEnd * 3);
5518cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                } else {
5528cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                    for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
5538cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                        int pixel = (p[1] << 8) | p[0];
5548cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                        *q++ = convert5To8(pixel >> 11);
5558cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                        *q++ = convert6To8(pixel >> 5);
5568cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                        *q++ = convert5To8(pixel);
5578cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                        p += pixelSize;
5588cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                    }
5598cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                }
5608cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            }
5618cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            etc1_encode_block(block, mask, encoded);
5628cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            memcpy(pOut, encoded, sizeof(encoded));
5638cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            pOut += sizeof(encoded);
5648cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
5658cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    }
5668cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return 0;
5678cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
5688cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
5698cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// Decode an entire image.
5708cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// pIn - pointer to encoded data.
5718cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// pOut - pointer to the image data. Will be written such that the Red component of
5728cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com//       pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset. Must be
5738cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com//        large enough to store entire image.
5748cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
5758cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
5768cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comint etc1_decode_image(const etc1_byte* pIn, etc1_byte* pOut,
5778cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        etc1_uint32 width, etc1_uint32 height,
5788cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        etc1_uint32 pixelSize, etc1_uint32 stride) {
5798cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    if (pixelSize < 2 || pixelSize > 3) {
5808cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        return -1;
5818cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    }
5828cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
5838cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
5848cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 encodedWidth = (width + 3) & ~3;
5858cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 encodedHeight = (height + 3) & ~3;
5868cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
5878cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
5888cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        etc1_uint32 yEnd = height - y;
5898cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        if (yEnd > 4) {
5908cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            yEnd = 4;
5918cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
5928cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
5938cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            etc1_uint32 xEnd = width - x;
5948cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            if (xEnd > 4) {
5958cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                xEnd = 4;
5968cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            }
5978cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            etc1_decode_block(pIn, block);
5988cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            pIn += ETC1_ENCODED_BLOCK_SIZE;
5998cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
6008cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                const etc1_byte* q = block + (cy * 4) * 3;
6018cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                etc1_byte* p = pOut + pixelSize * x + stride * (y + cy);
6028cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                if (pixelSize == 3) {
6038cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                    memcpy(p, q, xEnd * 3);
6048cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                } else {
6058cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                    for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
6068cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                        etc1_byte r = *q++;
6078cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                        etc1_byte g = *q++;
6088cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                        etc1_byte b = *q++;
6098cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                        etc1_uint32 pixel = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
6108cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                        *p++ = (etc1_byte) pixel;
6118cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                        *p++ = (etc1_byte) (pixel >> 8);
6128cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                    }
6138cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com                }
6148cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            }
6158cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        }
6168cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    }
6178cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return 0;
6188cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
6198cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
6208cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic const char kMagic[] = { 'P', 'K', 'M', ' ', '1', '0' };
6218cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
6228cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic const etc1_uint32 ETC1_PKM_FORMAT_OFFSET = 6;
6238cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic const etc1_uint32 ETC1_PKM_ENCODED_WIDTH_OFFSET = 8;
6248cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic const etc1_uint32 ETC1_PKM_ENCODED_HEIGHT_OFFSET = 10;
6258cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic const etc1_uint32 ETC1_PKM_WIDTH_OFFSET = 12;
6268cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic const etc1_uint32 ETC1_PKM_HEIGHT_OFFSET = 14;
6278cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
6288cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic const etc1_uint32 ETC1_RGB_NO_MIPMAPS = 0;
6298cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
6308cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic void writeBEUint16(etc1_byte* pOut, etc1_uint32 data) {
6318cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pOut[0] = (etc1_byte) (data >> 8);
6328cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    pOut[1] = (etc1_byte) data;
6338cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
6348cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
6358cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comstatic etc1_uint32 readBEUint16(const etc1_byte* pIn) {
6368cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return (pIn[0] << 8) | pIn[1];
6378cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
6388cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
6398cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// Format a PKM header
6408cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
6418cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.comvoid etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height) {
6428cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    memcpy(pHeader, kMagic, sizeof(kMagic));
6438cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 encodedWidth = (width + 3) & ~3;
6448cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 encodedHeight = (height + 3) & ~3;
6458cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    writeBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET, ETC1_RGB_NO_MIPMAPS);
6468cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    writeBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET, encodedWidth);
6478cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    writeBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET, encodedHeight);
6488cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    writeBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET, width);
6498cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    writeBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET, height);
6508cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
6518cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
6528cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// Check if a PKM header is correctly formatted.
6538cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
6548cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.cometc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader) {
6558cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    if (memcmp(pHeader, kMagic, sizeof(kMagic))) {
6568cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com        return false;
6578cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    }
6588cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 format = readBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET);
6598cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 encodedWidth = readBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET);
6608cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 encodedHeight = readBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET);
6618cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 width = readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
6628cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    etc1_uint32 height = readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
6638cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return format == ETC1_RGB_NO_MIPMAPS &&
6648cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            encodedWidth >= width && encodedWidth - width < 4 &&
6658cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com            encodedHeight >= height && encodedHeight - height < 4;
6668cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
6678cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
6688cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// Read the image width from a PKM header
6698cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
6708cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.cometc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader) {
6718cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
6728cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
6738cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
6748cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com// Read the image height from a PKM header
6758cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com
6768cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.cometc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader){
6778cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com    return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
6788cf81e0f4fa2a8054ac4cea1e7490028809cb893robertphillips@google.com}
679