• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "third_party/etc1/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 
clamp(int x)130 static inline etc1_byte clamp(int x) {
131     return (etc1_byte) (x >= 0 ? (x < 255 ? x : 255) : 0);
132 }
133 
134 static
convert4To8(int b)135 inline int convert4To8(int b) {
136     int c = b & 0xf;
137     return (c << 4) | c;
138 }
139 
140 static
convert5To8(int b)141 inline int convert5To8(int b) {
142     int c = b & 0x1f;
143     return (c << 3) | (c >> 2);
144 }
145 
146 static
convert6To8(int b)147 inline int convert6To8(int b) {
148     int c = b & 0x3f;
149     return (c << 2) | (c >> 4);
150 }
151 
152 static
divideBy255(int d)153 inline int divideBy255(int d) {
154     return (d + 128 + (d >> 8)) >> 8;
155 }
156 
157 static
convert8To4(int b)158 inline int convert8To4(int b) {
159     int c = b & 0xff;
160     return divideBy255(c * 15);
161 }
162 
163 static
convert8To5(int b)164 inline int convert8To5(int b) {
165     int c = b & 0xff;
166     return divideBy255(c * 31);
167 }
168 
169 static
convertDiff(int base,int diff)170 inline int convertDiff(int base, int diff) {
171     return convert5To8((0x1f & base) + kLookup[0x7 & diff]);
172 }
173 
174 static
decode_subblock(etc1_byte * pOut,int r,int g,int b,const int * table,etc1_uint32 low,bool second,bool flipped)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 
etc1_decode_block(const etc1_byte * pIn,etc1_byte * pOut)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
take_best(etc_compressed * a,const etc_compressed * b)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
etc_average_colors_subblock(const etc1_byte * pIn,etc1_uint32 inMask,etc1_byte * pColors,bool flipped,bool second)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
square(int x)302 inline int square(int x) {
303     return x * x;
304 }
305 
chooseModifier(const etc1_byte * pBaseColors,const etc1_byte * pIn,etc1_uint32 * pLow,int bitIndex,const int * pModifierTable)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
etc_encode_subblock_helper(const etc1_byte * pIn,etc1_uint32 inMask,etc_compressed * pCompressed,bool flipped,bool second,const etc1_byte * pBaseColors,const int * pModifierTable)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 
inRange4bitSigned(int color)381 static bool inRange4bitSigned(int color) {
382     return color >= -4 && color <= 3;
383 }
384 
etc_encodeBaseColors(etc1_byte * pBaseColors,const etc1_byte * pColors,etc_compressed * pCompressed)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         int r41 = convert8To4(pColors[0]);
415         int g41 = convert8To4(pColors[1]);
416         int b41 = convert8To4(pColors[2]);
417         int r42 = convert8To4(pColors[3]);
418         int g42 = convert8To4(pColors[4]);
419         int b42 = convert8To4(pColors[5]);
420         r1 = convert4To8(r41);
421         g1 = convert4To8(g41);
422         b1 = convert4To8(b41);
423         r2 = convert4To8(r42);
424         g2 = convert4To8(g42);
425         b2 = convert4To8(b42);
426         pCompressed->high |= (r41 << 28) | (r42 << 24) | (g41 << 20) | (g42
427                 << 16) | (b41 << 12) | (b42 << 8);
428     }
429     pBaseColors[0] = r1;
430     pBaseColors[1] = g1;
431     pBaseColors[2] = b1;
432     pBaseColors[3] = r2;
433     pBaseColors[4] = g2;
434     pBaseColors[5] = b2;
435 }
436 
437 static
etc_encode_block_helper(const etc1_byte * pIn,etc1_uint32 inMask,const etc1_byte * pColors,etc_compressed * pCompressed,bool flipped)438 void etc_encode_block_helper(const etc1_byte* pIn, etc1_uint32 inMask,
439         const etc1_byte* pColors, etc_compressed* pCompressed, bool flipped) {
440     pCompressed->score = ~0;
441     pCompressed->high = (flipped ? 1 : 0);
442     pCompressed->low = 0;
443 
444     etc1_byte pBaseColors[6];
445 
446     etc_encodeBaseColors(pBaseColors, pColors, pCompressed);
447 
448     int originalHigh = pCompressed->high;
449 
450     const int* pModifierTable = kModifierTable;
451     for (int i = 0; i < 8; i++, pModifierTable += 4) {
452         etc_compressed temp;
453         temp.score = 0;
454         temp.high = originalHigh | (i << 5);
455         temp.low = 0;
456         etc_encode_subblock_helper(pIn, inMask, &temp, flipped, false,
457                 pBaseColors, pModifierTable);
458         take_best(pCompressed, &temp);
459     }
460     pModifierTable = kModifierTable;
461     etc_compressed firstHalf = *pCompressed;
462     for (int i = 0; i < 8; i++, pModifierTable += 4) {
463         etc_compressed temp;
464         temp.score = firstHalf.score;
465         temp.high = firstHalf.high | (i << 2);
466         temp.low = firstHalf.low;
467         etc_encode_subblock_helper(pIn, inMask, &temp, flipped, true,
468                 pBaseColors + 3, pModifierTable);
469         if (i == 0) {
470             *pCompressed = temp;
471         } else {
472             take_best(pCompressed, &temp);
473         }
474     }
475 }
476 
writeBigEndian(etc1_byte * pOut,etc1_uint32 d)477 static void writeBigEndian(etc1_byte* pOut, etc1_uint32 d) {
478     pOut[0] = (etc1_byte) (d >> 24);
479     pOut[1] = (etc1_byte)((d >> 16) & 0xFF);
480     pOut[2] = (etc1_byte)((d >>  8) & 0xFF);
481     pOut[3] = (etc1_byte)((d >>  0) & 0xFF);
482 }
483 
484 // Input is a 4 x 4 square of 3-byte pixels in form R, G, B
485 // inmask is a 16-bit mask where bit (1 << (x + y * 4)) tells whether the corresponding (x,y)
486 // pixel is valid or not. Invalid pixel color values are ignored when compressing.
487 // Output is an ETC1 compressed version of the data.
488 
etc1_encode_block(const etc1_byte * pIn,etc1_uint32 inMask,etc1_byte * pOut)489 void etc1_encode_block(const etc1_byte* pIn, etc1_uint32 inMask,
490         etc1_byte* pOut) {
491     etc1_byte colors[6];
492     etc1_byte flippedColors[6];
493     etc_average_colors_subblock(pIn, inMask, colors, false, false);
494     etc_average_colors_subblock(pIn, inMask, colors + 3, false, true);
495     etc_average_colors_subblock(pIn, inMask, flippedColors, true, false);
496     etc_average_colors_subblock(pIn, inMask, flippedColors + 3, true, true);
497 
498     etc_compressed a, b;
499     etc_encode_block_helper(pIn, inMask, colors, &a, false);
500     etc_encode_block_helper(pIn, inMask, flippedColors, &b, true);
501     take_best(&a, &b);
502     writeBigEndian(pOut, a.high);
503     writeBigEndian(pOut + 4, a.low);
504 }
505 
506 // Return the size of the encoded image data (does not include size of PKM header).
507 
etc1_get_encoded_data_size(etc1_uint32 width,etc1_uint32 height)508 etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height) {
509     return (((width + 3) & ~3) * ((height + 3) & ~3)) >> 1;
510 }
511 
512 // Encode an entire image.
513 // pIn - pointer to the image data. Formatted such that the Red component of
514 //       pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
515 // pOut - pointer to encoded data. Must be large enough to store entire encoded image.
516 
etc1_encode_image(const etc1_byte * pIn,etc1_uint32 width,etc1_uint32 height,etc1_uint32 pixelSize,etc1_uint32 stride,etc1_byte * pOut)517 int etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height,
518         etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut) {
519     if (pixelSize < 2 || pixelSize > 3) {
520         return -1;
521     }
522     static const unsigned short kYMask[] = { 0x0, 0xf, 0xff, 0xfff, 0xffff };
523     static const unsigned short kXMask[] = { 0x0, 0x1111, 0x3333, 0x7777,
524             0xffff };
525     etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
526     etc1_byte encoded[ETC1_ENCODED_BLOCK_SIZE];
527 
528     etc1_uint32 encodedWidth = (width + 3) & ~3;
529     etc1_uint32 encodedHeight = (height + 3) & ~3;
530 
531     for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
532         etc1_uint32 yEnd = height - y;
533         if (yEnd > 4) {
534             yEnd = 4;
535         }
536         int ymask = kYMask[yEnd];
537         for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
538             etc1_uint32 xEnd = width - x;
539             if (xEnd > 4) {
540                 xEnd = 4;
541             }
542             int mask = ymask & kXMask[xEnd];
543             for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
544                 etc1_byte* q = block + (cy * 4) * 3;
545                 const etc1_byte* p = pIn + pixelSize * x + stride * (y + cy);
546                 if (pixelSize == 3) {
547                     memcpy(q, p, xEnd * 3);
548                 } else {
549                     for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
550                         int pixel = (p[1] << 8) | p[0];
551                         *q++ = convert5To8(pixel >> 11);
552                         *q++ = convert6To8(pixel >> 5);
553                         *q++ = convert5To8(pixel);
554                         p += pixelSize;
555                     }
556                 }
557             }
558             etc1_encode_block(block, mask, encoded);
559             memcpy(pOut, encoded, sizeof(encoded));
560             pOut += sizeof(encoded);
561         }
562     }
563     return 0;
564 }
565 
566 // Decode an entire image.
567 // pIn - pointer to encoded data.
568 // pOut - pointer to the image data. Will be written such that the Red component of
569 //       pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset. Must be
570 //        large enough to store entire image.
571 
572 
etc1_decode_image(const etc1_byte * pIn,etc1_byte * pOut,etc1_uint32 width,etc1_uint32 height,etc1_uint32 pixelSize,etc1_uint32 stride)573 int etc1_decode_image(const etc1_byte* pIn, etc1_byte* pOut,
574         etc1_uint32 width, etc1_uint32 height,
575         etc1_uint32 pixelSize, etc1_uint32 stride) {
576     if (pixelSize < 2 || pixelSize > 3) {
577         return -1;
578     }
579     etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
580 
581     etc1_uint32 encodedWidth = (width + 3) & ~3;
582     etc1_uint32 encodedHeight = (height + 3) & ~3;
583 
584     for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
585         etc1_uint32 yEnd = height - y;
586         if (yEnd > 4) {
587             yEnd = 4;
588         }
589         for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
590             etc1_uint32 xEnd = width - x;
591             if (xEnd > 4) {
592                 xEnd = 4;
593             }
594             etc1_decode_block(pIn, block);
595             pIn += ETC1_ENCODED_BLOCK_SIZE;
596             for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
597                 const etc1_byte* q = block + (cy * 4) * 3;
598                 etc1_byte* p = pOut + pixelSize * x + stride * (y + cy);
599                 if (pixelSize == 3) {
600                     memcpy(p, q, xEnd * 3);
601                 } else {
602                     for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
603                         etc1_byte r = *q++;
604                         etc1_byte g = *q++;
605                         etc1_byte b = *q++;
606                         etc1_uint32 pixel = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
607                         *p++ = (etc1_byte) pixel;
608                         *p++ = (etc1_byte) (pixel >> 8);
609                     }
610                 }
611             }
612         }
613     }
614     return 0;
615 }
616 
617 static const char kMagic[] = { 'P', 'K', 'M', ' ', '1', '0' };
618 
619 static const etc1_uint32 ETC1_PKM_FORMAT_OFFSET = 6;
620 static const etc1_uint32 ETC1_PKM_ENCODED_WIDTH_OFFSET = 8;
621 static const etc1_uint32 ETC1_PKM_ENCODED_HEIGHT_OFFSET = 10;
622 static const etc1_uint32 ETC1_PKM_WIDTH_OFFSET = 12;
623 static const etc1_uint32 ETC1_PKM_HEIGHT_OFFSET = 14;
624 
625 static const etc1_uint32 ETC1_RGB_NO_MIPMAPS = 0;
626 
writeBEUint16(etc1_byte * pOut,etc1_uint32 data)627 static void writeBEUint16(etc1_byte* pOut, etc1_uint32 data) {
628     pOut[0] = (etc1_byte) (data >> 8);
629     pOut[1] = (etc1_byte) data;
630 }
631 
readBEUint16(const etc1_byte * pIn)632 static etc1_uint32 readBEUint16(const etc1_byte* pIn) {
633     return (pIn[0] << 8) | pIn[1];
634 }
635 
636 // Format a PKM header
637 
etc1_pkm_format_header(etc1_byte * pHeader,etc1_uint32 width,etc1_uint32 height)638 void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height) {
639     memcpy(pHeader, kMagic, sizeof(kMagic));
640     etc1_uint32 encodedWidth = (width + 3) & ~3;
641     etc1_uint32 encodedHeight = (height + 3) & ~3;
642     writeBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET, ETC1_RGB_NO_MIPMAPS);
643     writeBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET, encodedWidth);
644     writeBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET, encodedHeight);
645     writeBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET, width);
646     writeBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET, height);
647 }
648 
649 // Check if a PKM header is correctly formatted.
650 
etc1_pkm_is_valid(const etc1_byte * pHeader)651 etc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader) {
652     if (memcmp(pHeader, kMagic, sizeof(kMagic))) {
653         return false;
654     }
655     etc1_uint32 format = readBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET);
656     etc1_uint32 encodedWidth = readBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET);
657     etc1_uint32 encodedHeight = readBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET);
658     etc1_uint32 width = readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
659     etc1_uint32 height = readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
660     return format == ETC1_RGB_NO_MIPMAPS &&
661             encodedWidth >= width && encodedWidth - width < 4 &&
662             encodedHeight >= height && encodedHeight - height < 4;
663 }
664 
665 // Read the image width from a PKM header
666 
etc1_pkm_get_width(const etc1_byte * pHeader)667 etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader) {
668     return readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
669 }
670 
671 // Read the image height from a PKM header
672 
etc1_pkm_get_height(const etc1_byte * pHeader)673 etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader){
674     return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
675 }
676