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