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