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