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