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