• 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 #include <ETC1/etc1.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 
clamp(int x)122 static inline etc1_byte clamp(int x) {
123     return (etc1_byte) (x >= 0 ? (x < 255 ? x : 255) : 0);
124 }
125 
126 static
convert4To8(int b)127 inline int convert4To8(int b) {
128     int c = b & 0xf;
129     return (c << 4) | c;
130 }
131 
132 static
convert5To8(int b)133 inline int convert5To8(int b) {
134     int c = b & 0x1f;
135     return (c << 3) | (c >> 2);
136 }
137 
138 static
convert6To8(int b)139 inline int convert6To8(int b) {
140     int c = b & 0x3f;
141     return (c << 2) | (c >> 4);
142 }
143 
144 static
divideBy255(int d)145 inline int divideBy255(int d) {
146     return (d + 128 + (d >> 8)) >> 8;
147 }
148 
149 static
convert8To4(int b)150 inline int convert8To4(int b) {
151     int c = b & 0xff;
152     return divideBy255(c * 15);
153 }
154 
155 static
convert8To5(int b)156 inline int convert8To5(int b) {
157     int c = b & 0xff;
158     return divideBy255(c * 31);
159 }
160 
161 static
convertDiff(int base,int diff)162 inline int convertDiff(int base, int diff) {
163     return convert5To8((0x1f & base) + kLookup[0x7 & diff]);
164 }
165 
166 static
decode_subblock(etc1_byte * pOut,int r,int g,int b,const int * table,etc1_uint32 low,bool second,bool flipped)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 
etc1_decode_block(const etc1_byte * pIn,etc1_byte * pOut)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
take_best(etc_compressed * a,const etc_compressed * b)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
etc_average_colors_subblock(const etc1_byte * pIn,etc1_uint32 inMask,etc1_byte * pColors,bool flipped,bool second)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
square(int x)294 inline int square(int x) {
295     return x * x;
296 }
297 
chooseModifier(const etc1_byte * pBaseColors,const etc1_byte * pIn,etc1_uint32 * pLow,int bitIndex,const int * pModifierTable)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
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)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 
inRange4bitSigned(int color)373 static bool inRange4bitSigned(int color) {
374     return color >= -4 && color <= 3;
375 }
376 
etc_encodeBaseColors(etc1_byte * pBaseColors,const etc1_byte * pColors,etc_compressed * pCompressed)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
etc_encode_block_helper(const etc1_byte * pIn,etc1_uint32 inMask,const etc1_byte * pColors,etc_compressed * pCompressed,bool flipped)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 
writeBigEndian(etc1_byte * pOut,etc1_uint32 d)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 
etc1_encode_block(const etc1_byte * pIn,etc1_uint32 inMask,etc1_byte * pOut)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 
etc1_get_encoded_data_size(etc1_uint32 width,etc1_uint32 height)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 
etc1_encode_image(const etc1_byte * pIn,etc1_uint32 width,etc1_uint32 height,etc1_uint32 pixelSize,etc1_uint32 stride,etc1_byte * pOut)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 
etc1_decode_image(const etc1_byte * pIn,etc1_byte * pOut,etc1_uint32 width,etc1_uint32 height,etc1_uint32 pixelSize,etc1_uint32 stride)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 
writeBEUint16(etc1_byte * pOut,etc1_uint32 data)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 
readBEUint16(const etc1_byte * pIn)627 static etc1_uint32 readBEUint16(const etc1_byte* pIn) {
628     return (pIn[0] << 8) | pIn[1];
629 }
630 
631 // Format a PKM header
632 
etc1_pkm_format_header(etc1_byte * pHeader,etc1_uint32 width,etc1_uint32 height)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 
etc1_pkm_is_valid(const etc1_byte * pHeader)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 
etc1_pkm_get_width(const etc1_byte * pHeader)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 
etc1_pkm_get_height(const etc1_byte * pHeader)668 etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader){
669     return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
670 }
671