123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670 |
- #include "base/etc1.h"
- #include <string.h>
- static const int kModifierTable[] = {
- 2, 8, -2, -8,
- 5, 17, -5, -17,
- 9, 29, -9, -29,
- 13, 42, -13, -42,
- 18, 60, -18, -60,
- 24, 80, -24, -80,
- 33, 106, -33, -106,
- 47, 183, -47, -183 };
- static const int kLookup[8] = { 0, 1, 2, 3, -4, -3, -2, -1 };
- static inline etc1_byte clamp(int x) {
- return (etc1_byte) (x >= 0 ? (x < 255 ? x : 255) : 0);
- }
- static
- inline int convert4To8(int b) {
- int c = b & 0xf;
- return (c << 4) | c;
- }
- static
- inline int convert5To8(int b) {
- int c = b & 0x1f;
- return (c << 3) | (c >> 2);
- }
- static
- inline int convert6To8(int b) {
- int c = b & 0x3f;
- return (c << 2) | (c >> 4);
- }
- static
- inline int divideBy255(int d) {
- return (d + 128 + (d >> 8)) >> 8;
- }
- static
- inline int convert8To4(int b) {
- int c = b & 0xff;
- return divideBy255(c * 15);
- }
- static
- inline int convert8To5(int b) {
- int c = b & 0xff;
- return divideBy255(c * 31);
- }
- static
- inline int convertDiff(int base, int diff) {
- return convert5To8((0x1f & base) + kLookup[0x7 & diff]);
- }
- static
- void decode_subblock(etc1_byte* pOut, int r, int g, int b, const int* table,
- etc1_uint32 low, bool second, bool flipped) {
- int baseX = 0;
- int baseY = 0;
- if (second) {
- if (flipped) {
- baseY = 2;
- } else {
- baseX = 2;
- }
- }
- for (int i = 0; i < 8; i++) {
- int x, y;
- if (flipped) {
- x = baseX + (i >> 1);
- y = baseY + (i & 1);
- } else {
- x = baseX + (i >> 2);
- y = baseY + (i & 3);
- }
- int k = y + (x * 4);
- int offset = ((low >> k) & 1) | ((low >> (k + 15)) & 2);
- int delta = table[offset];
- etc1_byte* q = pOut + 3 * (x + 4 * y);
- *q++ = clamp(r + delta);
- *q++ = clamp(g + delta);
- *q++ = clamp(b + delta);
- }
- }
- void etc1_decode_block(const etc1_byte* pIn, etc1_byte* pOut) {
- etc1_uint32 high = (pIn[0] << 24) | (pIn[1] << 16) | (pIn[2] << 8) | pIn[3];
- etc1_uint32 low = (pIn[4] << 24) | (pIn[5] << 16) | (pIn[6] << 8) | pIn[7];
- int r1, r2, g1, g2, b1, b2;
- if (high & 2) {
-
- int rBase = high >> 27;
- int gBase = high >> 19;
- int bBase = high >> 11;
- r1 = convert5To8(rBase);
- r2 = convertDiff(rBase, high >> 24);
- g1 = convert5To8(gBase);
- g2 = convertDiff(gBase, high >> 16);
- b1 = convert5To8(bBase);
- b2 = convertDiff(bBase, high >> 8);
- } else {
-
- r1 = convert4To8(high >> 28);
- r2 = convert4To8(high >> 24);
- g1 = convert4To8(high >> 20);
- g2 = convert4To8(high >> 16);
- b1 = convert4To8(high >> 12);
- b2 = convert4To8(high >> 8);
- }
- int tableIndexA = 7 & (high >> 5);
- int tableIndexB = 7 & (high >> 2);
- const int* tableA = kModifierTable + tableIndexA * 4;
- const int* tableB = kModifierTable + tableIndexB * 4;
- bool flipped = (high & 1) != 0;
- decode_subblock(pOut, r1, g1, b1, tableA, low, false, flipped);
- decode_subblock(pOut, r2, g2, b2, tableB, low, true, flipped);
- }
- typedef struct {
- etc1_uint32 high;
- etc1_uint32 low;
- etc1_uint32 score;
- } etc_compressed;
- static
- inline void take_best(etc_compressed* a, const etc_compressed* b) {
- if (a->score > b->score) {
- *a = *b;
- }
- }
- static
- void etc_average_colors_subblock(const etc1_byte* pIn, etc1_uint32 inMask,
- etc1_byte* pColors, bool flipped, bool second) {
- int r = 0;
- int g = 0;
- int b = 0;
- if (flipped) {
- int by = 0;
- if (second) {
- by = 2;
- }
- for (int y = 0; y < 2; y++) {
- int yy = by + y;
- for (int x = 0; x < 4; x++) {
- int i = x + 4 * yy;
- if (inMask & (1 << i)) {
- const etc1_byte* p = pIn + i * 3;
- r += *(p++);
- g += *(p++);
- b += *(p++);
- }
- }
- }
- } else {
- int bx = 0;
- if (second) {
- bx = 2;
- }
- for (int y = 0; y < 4; y++) {
- for (int x = 0; x < 2; x++) {
- int xx = bx + x;
- int i = xx + 4 * y;
- if (inMask & (1 << i)) {
- const etc1_byte* p = pIn + i * 3;
- r += *(p++);
- g += *(p++);
- b += *(p++);
- }
- }
- }
- }
- pColors[0] = (etc1_byte)((r + 4) >> 3);
- pColors[1] = (etc1_byte)((g + 4) >> 3);
- pColors[2] = (etc1_byte)((b + 4) >> 3);
- }
- static
- inline int square(int x) {
- return x * x;
- }
- static etc1_uint32 chooseModifier(const etc1_byte* pBaseColors,
- const etc1_byte* pIn, etc1_uint32 *pLow, int bitIndex,
- const int* pModifierTable) {
- etc1_uint32 bestScore = ~0;
- int bestIndex = 0;
- int pixelR = pIn[0];
- int pixelG = pIn[1];
- int pixelB = pIn[2];
- int r = pBaseColors[0];
- int g = pBaseColors[1];
- int b = pBaseColors[2];
- for (int i = 0; i < 4; i++) {
- int modifier = pModifierTable[i];
- int decodedG = clamp(g + modifier);
- etc1_uint32 score = (etc1_uint32) (6 * square(decodedG - pixelG));
- if (score >= bestScore) {
- continue;
- }
- int decodedR = clamp(r + modifier);
- score += (etc1_uint32) (3 * square(decodedR - pixelR));
- if (score >= bestScore) {
- continue;
- }
- int decodedB = clamp(b + modifier);
- score += (etc1_uint32) square(decodedB - pixelB);
- if (score < bestScore) {
- bestScore = score;
- bestIndex = i;
- }
- }
- etc1_uint32 lowMask = (((bestIndex >> 1) << 16) | (bestIndex & 1))
- << bitIndex;
- *pLow |= lowMask;
- return bestScore;
- }
- static
- void 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) {
- int score = pCompressed->score;
- if (flipped) {
- int by = 0;
- if (second) {
- by = 2;
- }
- for (int y = 0; y < 2; y++) {
- int yy = by + y;
- for (int x = 0; x < 4; x++) {
- int i = x + 4 * yy;
- if (inMask & (1 << i)) {
- score += chooseModifier(pBaseColors, pIn + i * 3,
- &pCompressed->low, yy + x * 4, pModifierTable);
- }
- }
- }
- } else {
- int bx = 0;
- if (second) {
- bx = 2;
- }
- for (int y = 0; y < 4; y++) {
- for (int x = 0; x < 2; x++) {
- int xx = bx + x;
- int i = xx + 4 * y;
- if (inMask & (1 << i)) {
- score += chooseModifier(pBaseColors, pIn + i * 3,
- &pCompressed->low, y + xx * 4, pModifierTable);
- }
- }
- }
- }
- pCompressed->score = score;
- }
- static bool inRange4bitSigned(int color) {
- return color >= -4 && color <= 3;
- }
- static void etc_encodeBaseColors(etc1_byte* pBaseColors,
- const etc1_byte* pColors, etc_compressed* pCompressed) {
- int r1, g1, b1, r2, g2, b2;
- bool differential;
- {
- int r51 = convert8To5(pColors[0]);
- int g51 = convert8To5(pColors[1]);
- int b51 = convert8To5(pColors[2]);
- int r52 = convert8To5(pColors[3]);
- int g52 = convert8To5(pColors[4]);
- int b52 = convert8To5(pColors[5]);
- r1 = convert5To8(r51);
- g1 = convert5To8(g51);
- b1 = convert5To8(b51);
- int dr = r52 - r51;
- int dg = g52 - g51;
- int db = b52 - b51;
- differential = inRange4bitSigned(dr) && inRange4bitSigned(dg)
- && inRange4bitSigned(db);
- if (differential) {
- r2 = convert5To8(r51 + dr);
- g2 = convert5To8(g51 + dg);
- b2 = convert5To8(b51 + db);
- pCompressed->high |= (r51 << 27) | ((7 & dr) << 24) | (g51 << 19)
- | ((7 & dg) << 16) | (b51 << 11) | ((7 & db) << 8) | 2;
- }
- }
- if (!differential) {
- int r41 = convert8To4(pColors[0]);
- int g41 = convert8To4(pColors[1]);
- int b41 = convert8To4(pColors[2]);
- int r42 = convert8To4(pColors[3]);
- int g42 = convert8To4(pColors[4]);
- int b42 = convert8To4(pColors[5]);
- r1 = convert4To8(r41);
- g1 = convert4To8(g41);
- b1 = convert4To8(b41);
- r2 = convert4To8(r42);
- g2 = convert4To8(g42);
- b2 = convert4To8(b42);
- pCompressed->high |= (r41 << 28) | (r42 << 24) | (g41 << 20) | (g42
- << 16) | (b41 << 12) | (b42 << 8);
- }
- pBaseColors[0] = r1;
- pBaseColors[1] = g1;
- pBaseColors[2] = b1;
- pBaseColors[3] = r2;
- pBaseColors[4] = g2;
- pBaseColors[5] = b2;
- }
- static
- void etc_encode_block_helper(const etc1_byte* pIn, etc1_uint32 inMask,
- const etc1_byte* pColors, etc_compressed* pCompressed, bool flipped) {
- pCompressed->score = ~0;
- pCompressed->high = (flipped ? 1 : 0);
- pCompressed->low = 0;
- etc1_byte pBaseColors[6];
- etc_encodeBaseColors(pBaseColors, pColors, pCompressed);
- int originalHigh = pCompressed->high;
- const int* pModifierTable = kModifierTable;
- for (int i = 0; i < 8; i++, pModifierTable += 4) {
- etc_compressed temp;
- temp.score = 0;
- temp.high = originalHigh | (i << 5);
- temp.low = 0;
- etc_encode_subblock_helper(pIn, inMask, &temp, flipped, false,
- pBaseColors, pModifierTable);
- take_best(pCompressed, &temp);
- }
- pModifierTable = kModifierTable;
- etc_compressed firstHalf = *pCompressed;
- for (int i = 0; i < 8; i++, pModifierTable += 4) {
- etc_compressed temp;
- temp.score = firstHalf.score;
- temp.high = firstHalf.high | (i << 2);
- temp.low = firstHalf.low;
- etc_encode_subblock_helper(pIn, inMask, &temp, flipped, true,
- pBaseColors + 3, pModifierTable);
- if (i == 0) {
- *pCompressed = temp;
- } else {
- take_best(pCompressed, &temp);
- }
- }
- }
- static void writeBigEndian(etc1_byte* pOut, etc1_uint32 d) {
- pOut[0] = (etc1_byte)(d >> 24);
- pOut[1] = (etc1_byte)(d >> 16);
- pOut[2] = (etc1_byte)(d >> 8);
- pOut[3] = (etc1_byte) d;
- }
- void etc1_encode_block(const etc1_byte* pIn, etc1_uint32 inMask,
- etc1_byte* pOut) {
- etc1_byte colors[6];
- etc1_byte flippedColors[6];
- etc_average_colors_subblock(pIn, inMask, colors, false, false);
- etc_average_colors_subblock(pIn, inMask, colors + 3, false, true);
- etc_average_colors_subblock(pIn, inMask, flippedColors, true, false);
- etc_average_colors_subblock(pIn, inMask, flippedColors + 3, true, true);
- etc_compressed a, b;
- etc_encode_block_helper(pIn, inMask, colors, &a, false);
- etc_encode_block_helper(pIn, inMask, flippedColors, &b, true);
- take_best(&a, &b);
- writeBigEndian(pOut, a.high);
- writeBigEndian(pOut + 4, a.low);
- }
- etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height) {
- return (((width + 3) & ~3) * ((height + 3) & ~3)) >> 1;
- }
- int etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height,
- etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut) {
- if (pixelSize < 2 || pixelSize > 3) {
- return -1;
- }
- static const unsigned short kYMask[] = { 0x0, 0xf, 0xff, 0xfff, 0xffff };
- static const unsigned short kXMask[] = { 0x0, 0x1111, 0x3333, 0x7777,
- 0xffff };
- etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
- etc1_byte encoded[ETC1_ENCODED_BLOCK_SIZE];
- etc1_uint32 encodedWidth = (width + 3) & ~3;
- etc1_uint32 encodedHeight = (height + 3) & ~3;
- for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
- etc1_uint32 yEnd = height - y;
- if (yEnd > 4) {
- yEnd = 4;
- }
- int ymask = kYMask[yEnd];
- for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
- etc1_uint32 xEnd = width - x;
- if (xEnd > 4) {
- xEnd = 4;
- }
- int mask = ymask & kXMask[xEnd];
- for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
- etc1_byte* q = block + (cy * 4) * 3;
- const etc1_byte* p = pIn + pixelSize * x + stride * (y + cy);
- if (pixelSize == 3) {
- memcpy(q, p, xEnd * 3);
- } else {
- for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
- int pixel = (p[1] << 8) | p[0];
- *q++ = convert5To8(pixel >> 11);
- *q++ = convert6To8(pixel >> 5);
- *q++ = convert5To8(pixel);
- p += pixelSize;
- }
- }
- }
- etc1_encode_block(block, mask, encoded);
- memcpy(pOut, encoded, sizeof(encoded));
- pOut += sizeof(encoded);
- }
- }
- return 0;
- }
- int etc1_decode_image(const etc1_byte* pIn, etc1_byte* pOut,
- etc1_uint32 width, etc1_uint32 height,
- etc1_uint32 pixelSize, etc1_uint32 stride) {
- if (pixelSize < 2 || pixelSize > 3) {
- return -1;
- }
- etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
- etc1_uint32 encodedWidth = (width + 3) & ~3;
- etc1_uint32 encodedHeight = (height + 3) & ~3;
- for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
- etc1_uint32 yEnd = height - y;
- if (yEnd > 4) {
- yEnd = 4;
- }
- for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
- etc1_uint32 xEnd = width - x;
- if (xEnd > 4) {
- xEnd = 4;
- }
- etc1_decode_block(pIn, block);
- pIn += ETC1_ENCODED_BLOCK_SIZE;
- for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
- const etc1_byte* q = block + (cy * 4) * 3;
- etc1_byte* p = pOut + pixelSize * x + stride * (y + cy);
- if (pixelSize == 3) {
- memcpy(p, q, xEnd * 3);
- } else {
- for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
- etc1_byte r = *q++;
- etc1_byte g = *q++;
- etc1_byte b = *q++;
- etc1_uint32 pixel = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
- *p++ = (etc1_byte) pixel;
- *p++ = (etc1_byte) (pixel >> 8);
- }
- }
- }
- }
- }
- return 0;
- }
- static const char kMagic[] = { 'P', 'K', 'M', ' ', '1', '0' };
- static const etc1_uint32 ETC1_PKM_FORMAT_OFFSET = 6;
- static const etc1_uint32 ETC1_PKM_ENCODED_WIDTH_OFFSET = 8;
- static const etc1_uint32 ETC1_PKM_ENCODED_HEIGHT_OFFSET = 10;
- static const etc1_uint32 ETC1_PKM_WIDTH_OFFSET = 12;
- static const etc1_uint32 ETC1_PKM_HEIGHT_OFFSET = 14;
- static const etc1_uint32 ETC1_RGB_NO_MIPMAPS = 0;
- static void writeBEUint16(etc1_byte* pOut, etc1_uint32 data) {
- pOut[0] = (etc1_byte) (data >> 8);
- pOut[1] = (etc1_byte) data;
- }
- static etc1_uint32 readBEUint16(const etc1_byte* pIn) {
- return (pIn[0] << 8) | pIn[1];
- }
- void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height) {
- memcpy(pHeader, kMagic, sizeof(kMagic));
- etc1_uint32 encodedWidth = (width + 3) & ~3;
- etc1_uint32 encodedHeight = (height + 3) & ~3;
- writeBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET, ETC1_RGB_NO_MIPMAPS);
- writeBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET, encodedWidth);
- writeBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET, encodedHeight);
- writeBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET, width);
- writeBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET, height);
- }
- etc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader) {
- if (memcmp(pHeader, kMagic, sizeof(kMagic))) {
- return false;
- }
- etc1_uint32 format = readBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET);
- etc1_uint32 encodedWidth = readBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET);
- etc1_uint32 encodedHeight = readBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET);
- etc1_uint32 width = readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
- etc1_uint32 height = readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
- return format == ETC1_RGB_NO_MIPMAPS &&
- encodedWidth >= width && encodedWidth - width < 4 &&
- encodedHeight >= height && encodedHeight - height < 4;
- }
- etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader) {
- return readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
- }
- etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader){
- return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
- }
|