1 // Copyright 2009 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //////////////////////////////////////////////////////////////////////////////////////////
16
17 // This is a fork of the AOSP project ETC1 codec. The original code can be found
18 // at the following web site:
19 // https://android.googlesource.com/platform/frameworks/native/+/master/opengl/include/ETC1/
20
21 //////////////////////////////////////////////////////////////////////////////////////////
22
23 #include "etc1.h"
24
25 #include <cstring>
26
27 /* From http://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
28
29 The number of bits that represent a 4x4 texel block is 64 bits if
30 <internalformat> is given by ETC1_RGB8_OES.
31
32 The data for a block is a number of bytes,
33
34 {q0, q1, q2, q3, q4, q5, q6, q7}
35
36 where byte q0 is located at the lowest memory address and q7 at
37 the highest. The 64 bits specifying the block is then represented
38 by the following 64 bit integer:
39
40 int64bit = 256*(256*(256*(256*(256*(256*(256*q0+q1)+q2)+q3)+q4)+q5)+q6)+q7;
41
42 ETC1_RGB8_OES:
43
44 a) bit layout in bits 63 through 32 if diffbit = 0
45
46 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
47 -----------------------------------------------
48 | base col1 | base col2 | base col1 | base col2 |
49 | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)|
50 -----------------------------------------------
51
52 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
53 ---------------------------------------------------
54 | base col1 | base col2 | table | table |diff|flip|
55 | B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit |
56 ---------------------------------------------------
57
58
59 b) bit layout in bits 63 through 32 if diffbit = 1
60
61 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
62 -----------------------------------------------
63 | base col1 | dcol 2 | base col1 | dcol 2 |
64 | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 |
65 -----------------------------------------------
66
67 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
68 ---------------------------------------------------
69 | base col 1 | dcol 2 | table | table |diff|flip|
70 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit |
71 ---------------------------------------------------
72
73
74 c) bit layout in bits 31 through 0 (in both cases)
75
76 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
77 -----------------------------------------------
78 | most significant pixel index bits |
79 | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a|
80 -----------------------------------------------
81
82 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
83 --------------------------------------------------
84 | least significant pixel index bits |
85 | p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a |
86 --------------------------------------------------
87
88
89 Add table 3.17.2: Intensity modifier sets for ETC1 compressed textures:
90
91 table codeword modifier table
92 ------------------ ----------------------
93 0 -8 -2 2 8
94 1 -17 -5 5 17
95 2 -29 -9 9 29
96 3 -42 -13 13 42
97 4 -60 -18 18 60
98 5 -80 -24 24 80
99 6 -106 -33 33 106
100 7 -183 -47 47 183
101
102
103 Add table 3.17.3 Mapping from pixel index values to modifier values for
104 ETC1 compressed textures:
105
106 pixel index value
107 ---------------
108 msb lsb resulting modifier value
109 ----- ----- -------------------------
110 1 1 -b (large negative value)
111 1 0 -a (small negative value)
112 0 0 a (small positive value)
113 0 1 b (large positive value)
114
115
116 */
117
118 static const int kModifierTable[] = {
119 /* 0 */2, 8, -2, -8,
120 /* 1 */5, 17, -5, -17,
121 /* 2 */9, 29, -9, -29,
122 /* 3 */13, 42, -13, -42,
123 /* 4 */18, 60, -18, -60,
124 /* 5 */24, 80, -24, -80,
125 /* 6 */33, 106, -33, -106,
126 /* 7 */47, 183, -47, -183 };
127
128 static const int kLookup[8] = { 0, 1, 2, 3, -4, -3, -2, -1 };
129
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 r2 = g2 = b2 = 0; // to shut the compiler up
415 }
416 }
417
418 if (!differential) {
419 int r41 = convert8To4(pColors[0]);
420 int g41 = convert8To4(pColors[1]);
421 int b41 = convert8To4(pColors[2]);
422 int r42 = convert8To4(pColors[3]);
423 int g42 = convert8To4(pColors[4]);
424 int b42 = convert8To4(pColors[5]);
425 r1 = convert4To8(r41);
426 g1 = convert4To8(g41);
427 b1 = convert4To8(b41);
428 r2 = convert4To8(r42);
429 g2 = convert4To8(g42);
430 b2 = convert4To8(b42);
431 pCompressed->high |= (r41 << 28) | (r42 << 24) | (g41 << 20) | (g42
432 << 16) | (b41 << 12) | (b42 << 8);
433 }
434 pBaseColors[0] = r1;
435 pBaseColors[1] = g1;
436 pBaseColors[2] = b1;
437 pBaseColors[3] = r2;
438 pBaseColors[4] = g2;
439 pBaseColors[5] = b2;
440 }
441
442 static
etc_encode_block_helper(const etc1_byte * pIn,etc1_uint32 inMask,const etc1_byte * pColors,etc_compressed * pCompressed,bool flipped)443 void etc_encode_block_helper(const etc1_byte* pIn, etc1_uint32 inMask,
444 const etc1_byte* pColors, etc_compressed* pCompressed, bool flipped) {
445 pCompressed->score = ~0;
446 pCompressed->high = (flipped ? 1 : 0);
447 pCompressed->low = 0;
448
449 etc1_byte pBaseColors[6];
450
451 etc_encodeBaseColors(pBaseColors, pColors, pCompressed);
452
453 int originalHigh = pCompressed->high;
454
455 const int* pModifierTable = kModifierTable;
456 for (int i = 0; i < 8; i++, pModifierTable += 4) {
457 etc_compressed temp;
458 temp.score = 0;
459 temp.high = originalHigh | (i << 5);
460 temp.low = 0;
461 etc_encode_subblock_helper(pIn, inMask, &temp, flipped, false,
462 pBaseColors, pModifierTable);
463 take_best(pCompressed, &temp);
464 }
465 pModifierTable = kModifierTable;
466 etc_compressed firstHalf = *pCompressed;
467 for (int i = 0; i < 8; i++, pModifierTable += 4) {
468 etc_compressed temp;
469 temp.score = firstHalf.score;
470 temp.high = firstHalf.high | (i << 2);
471 temp.low = firstHalf.low;
472 etc_encode_subblock_helper(pIn, inMask, &temp, flipped, true,
473 pBaseColors + 3, pModifierTable);
474 if (i == 0) {
475 *pCompressed = temp;
476 } else {
477 take_best(pCompressed, &temp);
478 }
479 }
480 }
481
writeBigEndian(etc1_byte * pOut,etc1_uint32 d)482 static void writeBigEndian(etc1_byte* pOut, etc1_uint32 d) {
483 pOut[0] = (etc1_byte)(d >> 24);
484 pOut[1] = (etc1_byte)(d >> 16);
485 pOut[2] = (etc1_byte)(d >> 8);
486 pOut[3] = (etc1_byte) d;
487 }
488
489 // Input is a 4 x 4 square of 3-byte pixels in form R, G, B
490 // inmask is a 16-bit mask where bit (1 << (x + y * 4)) tells whether the corresponding (x,y)
491 // pixel is valid or not. Invalid pixel color values are ignored when compressing.
492 // Output is an ETC1 compressed version of the data.
493
etc1_encode_block(const etc1_byte * pIn,etc1_uint32 inMask,etc1_byte * pOut)494 void etc1_encode_block(const etc1_byte* pIn, etc1_uint32 inMask,
495 etc1_byte* pOut) {
496 etc1_byte colors[6];
497 etc1_byte flippedColors[6];
498 etc_average_colors_subblock(pIn, inMask, colors, false, false);
499 etc_average_colors_subblock(pIn, inMask, colors + 3, false, true);
500 etc_average_colors_subblock(pIn, inMask, flippedColors, true, false);
501 etc_average_colors_subblock(pIn, inMask, flippedColors + 3, true, true);
502
503 etc_compressed a, b;
504 etc_encode_block_helper(pIn, inMask, colors, &a, false);
505 etc_encode_block_helper(pIn, inMask, flippedColors, &b, true);
506 take_best(&a, &b);
507 writeBigEndian(pOut, a.high);
508 writeBigEndian(pOut + 4, a.low);
509 }
510
511 // Return the size of the encoded image data (does not include size of PKM header).
512
etc1_get_encoded_data_size(etc1_uint32 width,etc1_uint32 height)513 etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height) {
514 return (((width + 3) & ~3) * ((height + 3) & ~3)) >> 1;
515 }
516
517 // Encode an entire image.
518 // pIn - pointer to the image data. Formatted such that the Red component of
519 // pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
520 // pOut - pointer to encoded data. Must be large enough to store entire encoded image.
521
etc1_encode_image(const etc1_byte * pIn,etc1_uint32 width,etc1_uint32 height,etc1_uint32 pixelSize,etc1_uint32 stride,etc1_byte * pOut)522 int etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height,
523 etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut) {
524 if (pixelSize < 2 || pixelSize > 3) {
525 return -1;
526 }
527 static const unsigned short kYMask[] = { 0x0, 0xf, 0xff, 0xfff, 0xffff };
528 static const unsigned short kXMask[] = { 0x0, 0x1111, 0x3333, 0x7777,
529 0xffff };
530 etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
531 etc1_byte encoded[ETC1_ENCODED_BLOCK_SIZE];
532
533 etc1_uint32 encodedWidth = (width + 3) & ~3;
534 etc1_uint32 encodedHeight = (height + 3) & ~3;
535
536 for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
537 etc1_uint32 yEnd = height - y;
538 if (yEnd > 4) {
539 yEnd = 4;
540 }
541 int ymask = kYMask[yEnd];
542 for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
543 etc1_uint32 xEnd = width - x;
544 if (xEnd > 4) {
545 xEnd = 4;
546 }
547 int mask = ymask & kXMask[xEnd];
548 for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
549 etc1_byte* q = block + (cy * 4) * 3;
550 const etc1_byte* p = pIn + pixelSize * x + stride * (y + cy);
551 if (pixelSize == 3) {
552 memcpy(q, p, xEnd * 3);
553 } else {
554 for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
555 int pixel = (p[1] << 8) | p[0];
556 *q++ = convert5To8(pixel >> 11);
557 *q++ = convert6To8(pixel >> 5);
558 *q++ = convert5To8(pixel);
559 p += pixelSize;
560 }
561 }
562 }
563 etc1_encode_block(block, mask, encoded);
564 memcpy(pOut, encoded, sizeof(encoded));
565 pOut += sizeof(encoded);
566 }
567 }
568 return 0;
569 }
570
571 // Decode an entire image.
572 // pIn - pointer to encoded data.
573 // pOut - pointer to the image data. Will be written such that the Red component of
574 // pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset. Must be
575 // large enough to store entire image.
576
577
etc1_decode_image(const etc1_byte * pIn,etc1_byte * pOut,etc1_uint32 width,etc1_uint32 height,etc1_uint32 pixelSize,etc1_uint32 stride)578 int etc1_decode_image(const etc1_byte* pIn, etc1_byte* pOut,
579 etc1_uint32 width, etc1_uint32 height,
580 etc1_uint32 pixelSize, etc1_uint32 stride) {
581 if (pixelSize < 2 || pixelSize > 3) {
582 return -1;
583 }
584 etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
585
586 etc1_uint32 encodedWidth = (width + 3) & ~3;
587 etc1_uint32 encodedHeight = (height + 3) & ~3;
588
589 for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
590 etc1_uint32 yEnd = height - y;
591 if (yEnd > 4) {
592 yEnd = 4;
593 }
594 for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
595 etc1_uint32 xEnd = width - x;
596 if (xEnd > 4) {
597 xEnd = 4;
598 }
599 etc1_decode_block(pIn, block);
600 pIn += ETC1_ENCODED_BLOCK_SIZE;
601 for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
602 const etc1_byte* q = block + (cy * 4) * 3;
603 etc1_byte* p = pOut + pixelSize * x + stride * (y + cy);
604 if (pixelSize == 3) {
605 memcpy(p, q, xEnd * 3);
606 } else {
607 for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
608 etc1_byte r = *q++;
609 etc1_byte g = *q++;
610 etc1_byte b = *q++;
611 etc1_uint32 pixel = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
612 *p++ = (etc1_byte) pixel;
613 *p++ = (etc1_byte) (pixel >> 8);
614 }
615 }
616 }
617 }
618 }
619 return 0;
620 }
621
622 static const char kMagic[] = { 'P', 'K', 'M', ' ', '1', '0' };
623
624 static const etc1_uint32 ETC1_PKM_FORMAT_OFFSET = 6;
625 static const etc1_uint32 ETC1_PKM_ENCODED_WIDTH_OFFSET = 8;
626 static const etc1_uint32 ETC1_PKM_ENCODED_HEIGHT_OFFSET = 10;
627 static const etc1_uint32 ETC1_PKM_WIDTH_OFFSET = 12;
628 static const etc1_uint32 ETC1_PKM_HEIGHT_OFFSET = 14;
629
630 static const etc1_uint32 ETC1_RGB_NO_MIPMAPS = 0;
631
writeBEUint16(etc1_byte * pOut,etc1_uint32 data)632 static void writeBEUint16(etc1_byte* pOut, etc1_uint32 data) {
633 pOut[0] = (etc1_byte) (data >> 8);
634 pOut[1] = (etc1_byte) data;
635 }
636
readBEUint16(const etc1_byte * pIn)637 static etc1_uint32 readBEUint16(const etc1_byte* pIn) {
638 return (pIn[0] << 8) | pIn[1];
639 }
640
641 // Format a PKM header
642
etc1_pkm_format_header(etc1_byte * pHeader,etc1_uint32 width,etc1_uint32 height)643 void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height) {
644 memcpy(pHeader, kMagic, sizeof(kMagic));
645 etc1_uint32 encodedWidth = (width + 3) & ~3;
646 etc1_uint32 encodedHeight = (height + 3) & ~3;
647 writeBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET, ETC1_RGB_NO_MIPMAPS);
648 writeBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET, encodedWidth);
649 writeBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET, encodedHeight);
650 writeBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET, width);
651 writeBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET, height);
652 }
653
654 // Check if a PKM header is correctly formatted.
655
etc1_pkm_is_valid(const etc1_byte * pHeader)656 etc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader) {
657 if (memcmp(pHeader, kMagic, sizeof(kMagic))) {
658 return false;
659 }
660 etc1_uint32 format = readBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET);
661 etc1_uint32 encodedWidth = readBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET);
662 etc1_uint32 encodedHeight = readBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET);
663 etc1_uint32 width = readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
664 etc1_uint32 height = readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
665 return format == ETC1_RGB_NO_MIPMAPS &&
666 encodedWidth >= width && encodedWidth - width < 4 &&
667 encodedHeight >= height && encodedHeight - height < 4;
668 }
669
670 // Read the image width from a PKM header
671
etc1_pkm_get_width(const etc1_byte * pHeader)672 etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader) {
673 return readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
674 }
675
676 // Read the image height from a PKM header
677
etc1_pkm_get_height(const etc1_byte * pHeader)678 etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader){
679 return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
680 }
681