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 {
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
etc_encode_block_helper(const etc1_byte * pIn,etc1_uint32 inMask,const etc1_byte * pColors,etc_compressed * pCompressed,bool flipped)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
writeBigEndian(etc1_byte * pOut,etc1_uint32 d)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
etc1_encode_block(const etc1_byte * pIn,etc1_uint32 inMask,etc1_byte * pOut)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
etc1_get_encoded_data_size(etc1_uint32 width,etc1_uint32 height)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
etc1_encode_image(const etc1_byte * pIn,etc1_uint32 width,etc1_uint32 height,etc1_uint32 pixelSize,etc1_uint32 stride,etc1_byte * pOut)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
etc1_decode_image(const etc1_byte * pIn,etc1_byte * pOut,etc1_uint32 width,etc1_uint32 height,etc1_uint32 pixelSize,etc1_uint32 stride)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
writeBEUint16(etc1_byte * pOut,etc1_uint32 data)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
readBEUint16(const etc1_byte * pIn)627 static etc1_uint32 readBEUint16(const etc1_byte* pIn) {
628 return (pIn[0] << 8) | pIn[1];
629 }
630
631 // Format a PKM header
632
etc1_pkm_format_header(etc1_byte * pHeader,etc1_uint32 width,etc1_uint32 height)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
etc1_pkm_is_valid(const etc1_byte * pHeader)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
etc1_pkm_get_width(const etc1_byte * pHeader)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
etc1_pkm_get_height(const etc1_byte * pHeader)668 etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader){
669 return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
670 }
671