1 // Copyright 2009 Google Inc.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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
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.
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;
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 ------------------ ----------------------
107 Add table 3.17.3 Mapping from pixel index values to modifier values for
108 ETC1 compressed textures:
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
127 typedef unsigned char etc1_byte
;
128 typedef int etc1_bool
;
129 typedef unsigned int etc1_uint32
;
131 static const int kModifierTable
[] = {
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);
148 inline int convert4To8(int b
) {
154 inline int convert5To8(int b
) {
156 return (c
<< 3) | (c
>> 2);
160 inline int convert6To8(int b
) {
162 return (c
<< 2) | (c
>> 4);
166 inline int divideBy255(int d
) {
167 return (d
+ 128 + (d
>> 8)) >> 8;
171 inline int convert8To4(int b
) {
173 return divideBy255(c
* 15);
177 inline int convert8To5(int b
) {
179 return divideBy255(c
* 31);
183 inline int convertDiff(int base
, int diff
) {
184 return convert5To8((0x1f & base
) + kLookup
[0x7 & diff
]);
188 void decode_subblock(etc1_byte
* pOut
, int r
, int g
, int b
, const int* table
,
189 etc1_uint32 low
, bool second
, bool flipped
) {
199 for (int i
= 0; i
< 8; i
++) {
202 x
= baseX
+ (i
>> 1);
205 x
= baseX
+ (i
>> 2);
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
;
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);
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
);
257 etc1_uint32 score
; // Lower is more accurate
261 inline void take_best(etc_compressed
* a
, const etc_compressed
* b
) {
262 if (a
->score
> b
->score
) {
268 void etc_average_colors_subblock(const etc1_byte
* pIn
, etc1_uint32 inMask
,
269 etc1_byte
* pColors
, bool flipped
, bool second
) {
279 for (int y
= 0; y
< 2; y
++) {
281 for (int x
= 0; x
< 4; x
++) {
283 if (inMask
& (1 << i
)) {
284 const etc1_byte
* p
= pIn
+ i
* 3;
296 for (int y
= 0; y
< 4; y
++) {
297 for (int x
= 0; x
< 2; x
++) {
300 if (inMask
& (1 << i
)) {
301 const etc1_byte
* p
= pIn
+ i
* 3;
309 pColors
[0] = (etc1_byte
)((r
+ 4) >> 3);
310 pColors
[1] = (etc1_byte
)((g
+ 4) >> 3);
311 pColors
[2] = (etc1_byte
)((b
+ 4) >> 3);
315 inline int square(int 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;
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
) {
337 int decodedR
= clamp(r
+ modifier
);
338 score
+= (etc1_uint32
) (3 * square(decodedR
- pixelR
));
339 if (score
>= bestScore
) {
342 int decodedB
= clamp(b
+ modifier
);
343 score
+= (etc1_uint32
) square(decodedB
- pixelB
);
344 if (score
< bestScore
) {
349 etc1_uint32 lowMask
= (((bestIndex
>> 1) << 16) | (bestIndex
& 1))
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
;
365 for (int y
= 0; y
< 2; y
++) {
367 for (int x
= 0; x
< 4; x
++) {
369 if (inMask
& (1 << i
)) {
370 score
+= chooseModifier(pBaseColors
, pIn
+ i
* 3,
371 &pCompressed
->low
, yy
+ x
* 4, pModifierTable
);
380 for (int y
= 0; y
< 4; y
++) {
381 for (int x
= 0; x
< 2; x
++) {
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
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
);
418 differential
= inRange4bitSigned(dr
) && inRange4bitSigned(dg
)
419 && inRange4bitSigned(db
);
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;
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);
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) {
470 temp
.high
= originalHigh
| (i
<< 5);
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) {
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
);
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
) {
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);
514 etc_encode_block_helper(pIn
, inMask
, colors
, &a
, false);
515 etc_encode_block_helper(pIn
, inMask
, flippedColors
, &b
, true);
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
) {
541 static const unsigned short kYMask
[] = { 0x0, 0xf, 0xff, 0xfff, 0xffff };
542 static const unsigned short kXMask
[] = { 0x0, 0x1111, 0x3333, 0x7777,
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
;
555 int ymask
= kYMask
[yEnd
];
556 for (etc1_uint32 x
= 0; x
< encodedWidth
; x
+= 4) {
557 etc1_uint32 xEnd
= outWidth
- x
;
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.
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
);
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.
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);
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.
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.
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
));
656 memset(pOut
, 0xFF, sizeof(encoded
));
658 pOut
+= sizeof(encoded
);
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) {
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
;
687 for (etc1_uint32 x
= 0; x
< encodedWidth
; x
+= 4) {
688 etc1_uint32 xEnd
= width
- x
;
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
++) {
704 etc1_uint32 pixel
= ((r
>> 3) << 11) | ((g
>> 2) << 5) | (b
>> 3);
705 *p
++ = (etc1_byte
) pixel
;
706 *p
++ = (etc1_byte
) (pixel
>> 8);
709 for (etc1_uint32 cx
= 0; cx
< xEnd
; cx
++) {
716 *p
++ = (etc1_byte
) 0xFF;
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
))) {
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
);