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_utils.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 122 static inline etc1_byte clamp(int x) { 123 return (etc1_byte) (x >= 0 ? (x < 255 ? x : 255) : 0); 124 } 125 126 static 127 inline int convert4To8(int b) { 128 int c = b & 0xf; 129 return (c << 4) | c; 130 } 131 132 static 133 inline int convert5To8(int b) { 134 int c = b & 0x1f; 135 return (c << 3) | (c >> 2); 136 } 137 138 static 139 inline int convert6To8(int b) { 140 int c = b & 0x3f; 141 return (c << 2) | (c >> 4); 142 } 143 144 static 145 inline int divideBy255(int d) { 146 return (d + 128 + (d >> 8)) >> 8; 147 } 148 149 static 150 inline int convert8To4(int b) { 151 int c = b & 0xff; 152 return divideBy255(b * 15); 153 } 154 155 static 156 inline int convert8To5(int b) { 157 int c = b & 0xff; 158 return divideBy255(b * 31); 159 } 160 161 static 162 inline int convertDiff(int base, int diff) { 163 return convert5To8((0x1f & base) + kLookup[0x7 & diff]); 164 } 165 166 static 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 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 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 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 294 inline int square(int x) { 295 return x * x; 296 } 297 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 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 373 static bool inRange4bitSigned(int color) { 374 return color >= -4 && color <= 3; 375 } 376 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 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 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 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 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 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 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 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 627 static etc1_uint32 readBEUint16(const etc1_byte* pIn) { 628 return (pIn[0] << 8) | pIn[1]; 629 } 630 631 // Format a PKM header 632 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 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 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 668 etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader){ 669 return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET); 670 }