Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / third_party / android_opengl / etc1 / etc1.cpp
blob846abc6ad82ee2093e83deb6df9dca60e311ccf8
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.
15 // This is branched from frameworks/base/opengl/include/ETC1/etc1.cc
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.
26 #include "etc1.h"
28 #include <string>
29 #include <cmath>
31 /* From http://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
33 The number of bits that represent a 4x4 texel block is 64 bits if
34 <internalformat> is given by ETC1_RGB8_OES.
36 The data for a block is a number of bytes,
38 {q0, q1, q2, q3, q4, q5, q6, q7}
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:
44 int64bit = 256*(256*(256*(256*(256*(256*(256*q0+q1)+q2)+q3)+q4)+q5)+q6)+q7;
46 ETC1_RGB8_OES:
48 a) bit layout in bits 63 through 32 if diffbit = 0
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 -----------------------------------------------
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 ---------------------------------------------------
63 b) bit layout in bits 63 through 32 if diffbit = 1
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 -----------------------------------------------
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 ---------------------------------------------------
78 c) bit layout in bits 31 through 0 (in both cases)
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 -----------------------------------------------
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 --------------------------------------------------
93 Add table 3.17.2: Intensity modifier sets for ETC1 compressed textures:
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
107 Add table 3.17.3 Mapping from pixel index values to modifier values for
108 ETC1 compressed textures:
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)
122 #define ETC1_ENCODED_BLOCK_SIZE 8
123 #define ETC1_DECODED_BLOCK_SIZE 48
125 namespace {
127 typedef unsigned char etc1_byte;
128 typedef int etc1_bool;
129 typedef unsigned int etc1_uint32;
131 static 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 };
141 static const int kLookup[8] = { 0, 1, 2, 3, -4, -3, -2, -1 };
143 static inline etc1_byte clamp(int x) {
144 return (etc1_byte) (x >= 0 ? (x < 255 ? x : 255) : 0);
147 static
148 inline int convert4To8(int b) {
149 int c = b & 0xf;
150 return (c << 4) | c;
153 static
154 inline int convert5To8(int b) {
155 int c = b & 0x1f;
156 return (c << 3) | (c >> 2);
159 static
160 inline int convert6To8(int b) {
161 int c = b & 0x3f;
162 return (c << 2) | (c >> 4);
165 static
166 inline int divideBy255(int d) {
167 return (d + 128 + (d >> 8)) >> 8;
170 static
171 inline int convert8To4(int b) {
172 int c = b & 0xff;
173 return divideBy255(c * 15);
176 static
177 inline int convert8To5(int b) {
178 int c = b & 0xff;
179 return divideBy255(c * 31);
182 static
183 inline int convertDiff(int base, int diff) {
184 return convert5To8((0x1f & base) + kLookup[0x7 & diff]);
187 static
188 void 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;
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);
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);
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
221 void 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);
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);
254 typedef struct {
255 etc1_uint32 high;
256 etc1_uint32 low;
257 etc1_uint32 score; // Lower is more accurate
258 } etc_compressed;
260 static
261 inline void take_best(etc_compressed* a, const etc_compressed* b) {
262 if (a->score > b->score) {
263 *a = *b;
267 static
268 void 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;
274 if (flipped) {
275 int by = 0;
276 if (second) {
277 by = 2;
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++);
291 } else {
292 int bx = 0;
293 if (second) {
294 bx = 2;
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++);
309 pColors[0] = (etc1_byte)((r + 4) >> 3);
310 pColors[1] = (etc1_byte)((g + 4) >> 3);
311 pColors[2] = (etc1_byte)((b + 4) >> 3);
314 static
315 inline int square(int x) {
316 return x * x;
319 static 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;
337 int decodedR = clamp(r + modifier);
338 score += (etc1_uint32) (3 * square(decodedR - pixelR));
339 if (score >= bestScore) {
340 continue;
342 int decodedB = clamp(b + modifier);
343 score += (etc1_uint32) square(decodedB - pixelB);
344 if (score < bestScore) {
345 bestScore = score;
346 bestIndex = i;
349 etc1_uint32 lowMask = (((bestIndex >> 1) << 16) | (bestIndex & 1))
350 << bitIndex;
351 *pLow |= lowMask;
352 return bestScore;
355 static
356 void 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;
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);
375 } else {
376 int bx = 0;
377 if (second) {
378 bx = 2;
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);
391 pCompressed->score = score;
394 static bool inRange4bitSigned(int color) {
395 return color >= -4 && color <= 3;
398 static 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;
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]);
410 r1 = convert5To8(r51);
411 g1 = convert5To8(g51);
412 b1 = convert5To8(b51);
414 int dr = r52 - r51;
415 int dg = g52 - g51;
416 int db = b52 - b51;
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;
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);
445 pBaseColors[0] = r1;
446 pBaseColors[1] = g1;
447 pBaseColors[2] = b1;
448 pBaseColors[3] = r2;
449 pBaseColors[4] = g2;
450 pBaseColors[5] = b2;
453 static
454 void 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;
460 etc1_byte pBaseColors[6];
462 etc_encodeBaseColors(pBaseColors, pColors, pCompressed);
464 int originalHigh = pCompressed->high;
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);
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);
493 static 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;
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.
505 static 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);
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);
521 } // anonymous namespace
523 // Return the size of the encoded image data.
525 etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height) {
526 return (((width + 3) & ~3) * ((height + 3) & ~3)) >> 1;
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.
535 bool 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;
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];
547 etc1_uint32 encodedWidth = (outWidth + 3) & ~3;
548 etc1_uint32 encodedHeight = (outHeight + 3) & ~3;
550 for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
551 etc1_uint32 yEnd = outHeight - y;
552 if (yEnd > 4) {
553 yEnd = 4;
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;
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);
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;
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;
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);
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);
653 etc1_encode_block(block, mask, encoded);
654 memcpy(pOut, encoded, sizeof(encoded));
655 } else {
656 memset(pOut, 0xFF, sizeof(encoded));
658 pOut += sizeof(encoded);
661 return true;
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.
671 bool 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;
677 etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
679 etc1_uint32 encodedWidth = (width + 3) & ~3;
680 etc1_uint32 encodedHeight = (height + 3) & ~3;
682 for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
683 etc1_uint32 yEnd = height - y;
684 if (yEnd > 4) {
685 yEnd = 4;
687 for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
688 etc1_uint32 xEnd = width - x;
689 if (xEnd > 4) {
690 xEnd = 4;
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);
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;
722 return true;
725 static const char kMagic[] = { 'P', 'K', 'M', ' ', '1', '0' };
727 static const etc1_uint32 ETC1_PKM_FORMAT_OFFSET = 6;
728 static const etc1_uint32 ETC1_PKM_ENCODED_WIDTH_OFFSET = 8;
729 static const etc1_uint32 ETC1_PKM_ENCODED_HEIGHT_OFFSET = 10;
730 static const etc1_uint32 ETC1_PKM_WIDTH_OFFSET = 12;
731 static const etc1_uint32 ETC1_PKM_HEIGHT_OFFSET = 14;
733 static const etc1_uint32 ETC1_RGB_NO_MIPMAPS = 0;
735 static void writeBEUint16(etc1_byte* pOut, etc1_uint32 data) {
736 pOut[0] = (etc1_byte) (data >> 8);
737 pOut[1] = (etc1_byte) data;
740 static etc1_uint32 readBEUint16(const etc1_byte* pIn) {
741 return (pIn[0] << 8) | pIn[1];
744 // Format a PKM header
746 void 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);
757 // Check if a PKM header is correctly formatted.
759 bool etc1_pkm_is_valid(const etc1_byte* pHeader) {
760 if (memcmp(pHeader, kMagic, sizeof(kMagic))) {
761 return false;
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;
773 // Read the image width from a PKM header
775 etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader) {
776 return readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
779 // Read the image height from a PKM header
781 etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader){
782 return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);