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