diff --git a/ext/decoders/native/dxtc.c b/ext/decoders/native/dxtc.c index 28a0070..bed3894 100644 --- a/ext/decoders/native/dxtc.c +++ b/ext/decoders/native/dxtc.c @@ -1,27 +1,35 @@ #include "dxtc.h" +#include "common.h" #include #include -static inline uint_fast32_t color(uint_fast32_t r, uint_fast32_t g, uint_fast32_t b, uint_fast32_t a) +static inline uint_fast32_t color(uint_fast8_t r, uint_fast8_t g, uint_fast8_t b, uint_fast8_t a) { +#if BYTE_ORDER == LITTLE_ENDIAN return r | g << 8 | b << 16 | a << 24; +#else + return a | b << 8 | g << 16 | r << 24; +#endif } -static inline void rgb565(const uint_fast16_t c, int* r, int* g, int* b) +static inline void rgb565(const uint16_t d, uint8_t* r, uint8_t* g, uint8_t* b) { - *r = (c & 0xf800) >> 8; - *g = (c & 0x07e0) >> 3; - *b = (c & 0x001f) << 3; - *r |= *r >> 5; - *g |= *g >> 6; - *b |= *b >> 5; +#if BYTE_ORDER == LITTLE_ENDIAN + *r = (d >> 8 & 0xf8) | (d >> 13); + *g = (d >> 3 & 0xfc) | (d >> 9 & 3); + *b = (d << 3) | (d >> 2 & 7); +#else + *r = (d & 0xf8) | (d >> 5 & 7); + *g = (d << 5 & 0xe0) | (d >> 11 & 0x1c) | (d >> 1 & 3); + *b = (d >> 5 & 0xf8) | (d >> 10 & 0x7); +#endif } -static inline void decode_dxt1_block(const uint64_t* data, uint32_t* outbuf) +static inline void decode_dxt1_block(const uint8_t* data, uint32_t* outbuf) { - int r0, g0, b0, r1, g1, b1; - int q0 = ((uint16_t*)data)[0]; - int q1 = ((uint16_t*)data)[1]; + uint8_t r0, g0, b0, r1, g1, b1; + int q0 = *(uint16_t*)(data); + int q1 = *(uint16_t*)(data + 2); rgb565(q0, &r0, &g0, &b0); rgb565(q1, &r1, &g1, &b1); uint_fast32_t c[4] = { color(r0, g0, b0, 255), color(r1, g1, b1, 255) }; @@ -30,32 +38,39 @@ static inline void decode_dxt1_block(const uint64_t* data, uint32_t* outbuf) c[3] = color((r0 + r1 * 2) / 3, (g0 + g1 * 2) / 3, (b0 + b1 * 2) / 3, 255); } else { c[2] = color((r0 + r1) / 2, (g0 + g1) / 2, (b0 + b1) / 2, 255); + c[3] = color(0, 0, 0, 255); } - uint_fast32_t d = *data >> 32; +#if BYTE_ORDER == LITTLE_ENDIAN + uint_fast32_t d = *(uint32_t*)(data + 4); +#else + uint_fast32_t d = data[4] | data[5] << 8 | data[6] << 16 | data[7] << 24; +#endif for (int i = 0; i < 16; i++, d >>= 2) outbuf[i] = c[d & 3]; } -void decode_dxt1(const uint64_t* data, const int w, const int h, uint32_t* image) +void decode_dxt1(const uint8_t* data, const int w, const int h, uint32_t* image) { - int bcw = (w + 3) / 4; - int bch = (h + 3) / 4; - int clen_last = (w + 3) % 4 + 1; + int num_blocks_x = (w + 3) / 4; + int num_blocks_y = (h + 3) / 4; + int copy_length_last = (w + 3) % 4 + 1; uint32_t buf[16]; - const uint64_t* d = data; - for (int t = 0; t < bch; t++) { - for (int s = 0; s < bcw; s++, d++) { + uint32_t* buf_end = buf + 16; + const uint8_t* d = data; + for (int t = 0; t < num_blocks_y; t++) { + for (int s = 0; s < num_blocks_x; s++, d += 8) { decode_dxt1_block(d, buf); - int clen = (s < bcw - 1 ? 4 : clen_last) * 4; - for (int i = 0, y = h - t * 4 - 1; i < 4 && y >= 0; i++, y--) - memcpy(image + y * w + s * 4, buf + i * 4, clen); + int copy_length = (s < num_blocks_x - 1 ? 4 : copy_length_last) * 4; + uint32_t* b = buf; + for (int y = h - t * 4 - 1; b < buf_end && y >= 0; b += 4, y--) + memcpy(image + y * w + s * 4, b, copy_length); } } } -static inline void decode_dxt5_block(const uint64_t* data, uint32_t* outbuf) +static inline void decode_dxt5_block(const uint8_t* data, uint32_t* outbuf) { - uint_fast32_t a[8] = { ((uint8_t*)data)[0], ((uint8_t*)data)[1] }; + uint_fast32_t a[8] = { data[0], data[1] }; if (a[0] > a[1]) { a[2] = (a[0] * 6 + a[1]) / 7; a[3] = (a[0] * 5 + a[1] * 2) / 7; @@ -68,43 +83,36 @@ static inline void decode_dxt5_block(const uint64_t* data, uint32_t* outbuf) a[3] = (a[0] * 3 + a[1] * 2) / 5; a[4] = (a[0] * 2 + a[1] * 3) / 5; a[5] = (a[0] + a[1] * 4) / 5; + a[6] = 0; a[7] = 255; } for (int i = 0; i < 8; i++) - a[i] <<= 24; - - int r0, g0, b0, r1, g1, b1; - int q0 = ((uint16_t*)(data + 1))[0]; - int q1 = ((uint16_t*)(data + 1))[1]; - rgb565(q0, &r0, &g0, &b0); - rgb565(q1, &r1, &g1, &b1); - uint_fast32_t c[4] = { color(r0, g0, b0, 0), color(r1, g1, b1, 0) }; - if (q0 > q1) { - c[2] = color((r0 * 2 + r1) / 3, (g0 * 2 + g1) / 3, (b0 * 2 + b1) / 3, 0); - c[3] = color((r0 + r1 * 2) / 3, (g0 + g1 * 2) / 3, (b0 + b1 * 2) / 3, 0); - } else { - c[2] = color((r0 + r1) / 2, (g0 + g1) / 2, (b0 + b1) / 2, 0); - } - - uint_fast64_t da = *data >> 16; - uint_fast32_t dc = *(data + 1) >> 32; - for (int i = 0; i < 16; i++, da >>= 3, dc >>= 2) - outbuf[i] = a[da & 7] | c[dc & 3]; + a[i] = color(255, 255, 255, a[i]); + decode_dxt1_block(data + 8, outbuf); +#if BYTE_ORDER == LITTLE_ENDIAN + uint_fast64_t d = *(uint64_t*)data >> 16; +#else + uint_fast64_t d = data[2] | data[3] << 8 | data[4] << 16 | data[5] << 24 | data[6] << 32 | data[7] << 40; +#endif + for (int i = 0; i < 16; i++, d >>= 3) + outbuf[i] &= a[d & 7]; } -void decode_dxt5(const uint64_t* data, const int w, const int h, uint32_t* image) +void decode_dxt5(const uint8_t* data, const int w, const int h, uint32_t* image) { - int bcw = (w + 3) / 4; - int bch = (h + 3) / 4; - int clen_last = (w + 3) % 4 + 1; + int num_blocks_x = (w + 3) / 4; + int num_blocks_y = (h + 3) / 4; + int copy_length_last = (w + 3) % 4 + 1; uint32_t buf[16]; - const uint64_t* d = data; - for (int t = 0; t < bch; t++) { - for (int s = 0; s < bcw; s++, d += 2) { + uint32_t *buf_end = buf + 16; + const uint8_t* d = data; + for (int t = 0; t < num_blocks_y; t++) { + for (int s = 0; s < num_blocks_x; s++, d += 16) { decode_dxt5_block(d, buf); - int clen = (s < bcw - 1 ? 4 : clen_last) * 4; - for (int i = 0, y = h - t * 4 - 1; i < 4 && y >= 0; i++, y--) - memcpy(image + y * w + s * 4, buf + i * 4, clen); + int copy_length = (s < num_blocks_x - 1 ? 4 : copy_length_last) * 4; + uint32_t *b = buf; + for (int y = h - t * 4 - 1; b < buf_end && y >= 0; b += 4, y--) + memcpy(image + y * w + s * 4, b, copy_length); } } } diff --git a/ext/decoders/native/dxtc.h b/ext/decoders/native/dxtc.h index d58156b..429549e 100644 --- a/ext/decoders/native/dxtc.h +++ b/ext/decoders/native/dxtc.h @@ -3,7 +3,7 @@ #include -void decode_dxt1(const uint64_t*, const int, const int, uint32_t*); -void decode_dxt5(const uint64_t*, const int, const int, uint32_t*); +void decode_dxt1(const uint8_t*, const int, const int, uint32_t*); +void decode_dxt5(const uint8_t*, const int, const int, uint32_t*); #endif /* end of include guard: DXTC_H */ diff --git a/ext/decoders/native/etc.c b/ext/decoders/native/etc.c index 87b9052..d349f89 100644 --- a/ext/decoders/native/etc.c +++ b/ext/decoders/native/etc.c @@ -6,6 +6,10 @@ const uint_fast8_t WriteOrderTable[16] = { 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 }; const uint_fast8_t WriteOrderTableRev[16] = { 15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0 }; const uint_fast8_t Etc1ModifierTable[8][2] = { { 2, 8 }, { 5, 17 }, { 9, 29 }, { 13, 42 }, { 18, 60 }, { 24, 80 }, { 33, 106 }, { 47, 183 } }; +const uint_fast8_t Etc2aModifierTable[2][8][2] = { + { { 0, 8 }, { 0, 17 }, { 0, 29 }, { 0, 42 }, { 0, 60 }, { 0, 80 }, { 0, 106 }, { 0, 183 } }, + { { 2, 8 }, { 5, 17 }, { 9, 29 }, { 13, 42 }, { 18, 60 }, { 24, 80 }, { 33, 106 }, { 47, 183 } } +}; const uint_fast8_t Etc1SubblockTable[2][16] = { { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 }, { 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1 } }; const uint_fast8_t Etc2DistanceTable[8] = { 3, 6, 11, 16, 23, 32, 41, 64 }; const int_fast8_t Etc2AlphaModTable[16][8] = { @@ -27,6 +31,12 @@ const int_fast8_t Etc2AlphaModTable[16][8] = { { -3, -5, -7, -9, 2, 4, 6, 8 } }; +#if BYTE_ORDER == LITTLE_ENDIAN +static const uint_fast32_t TRANSPARENT_MASK = 0x00ffffff; +#else +static const uint_fast32_t TRANSPARENT_MASK = 0xffffff00; +#endif + static inline uint_fast32_t color(uint_fast8_t r, uint_fast8_t g, uint_fast8_t b, uint_fast8_t a) { #if BYTE_ORDER == LITTLE_ENDIAN @@ -46,6 +56,11 @@ static inline uint32_t applicate_color(uint_fast8_t c[3], int_fast16_t m) return color(clamp(c[0] + m), clamp(c[1] + m), clamp(c[2] + m), 255); } +static inline uint32_t applicate_color_alpha(uint_fast8_t c[3], int_fast16_t m, int transparent) +{ + return color(clamp(c[0] + m), clamp(c[1] + m), clamp(c[2] + m), transparent ? 0 : 255); +} + static inline uint32_t applicate_color_raw(uint_fast8_t c[3]) { return color(c[0], c[1], c[2], 255); @@ -212,6 +227,103 @@ static inline void decode_etc2_block(const uint8_t* data, uint32_t* outbuf) } } +static inline void decode_etc2a1_block(const uint8_t* data, uint32_t* outbuf) +{ + uint_fast16_t j = data[6] << 8 | data[7]; // 15 -> 0 + uint_fast32_t k = data[4] << 8 | data[5]; // 31 -> 16 + uint_fast8_t c[3][3] = {}; + + int obaq = data[3] >> 1 & 1; + + // diff bit == 1 + uint_fast8_t r = data[0] & 0xf8; + int_fast16_t dr = (data[0] << 3 & 0x18) - (data[0] << 3 & 0x20); + uint_fast8_t g = data[1] & 0xf8; + int_fast16_t dg = (data[1] << 3 & 0x18) - (data[1] << 3 & 0x20); + uint_fast8_t b = data[2] & 0xf8; + int_fast16_t db = (data[2] << 3 & 0x18) - (data[2] << 3 & 0x20); + if (r + dr < 0 || r + dr > 255) { + // T + c[0][0] = (data[0] << 3 & 0xc0) | (data[0] << 4 & 0x30) | (data[0] >> 1 & 0xc) | (data[0] & 3); + c[0][1] = (data[1] & 0xf0) | data[1] >> 4; + c[0][2] = (data[1] & 0x0f) | data[1] << 4; + c[1][0] = (data[2] & 0xf0) | data[2] >> 4; + c[1][1] = (data[2] & 0x0f) | data[2] << 4; + c[1][2] = (data[3] & 0xf0) | data[3] >> 4; + const uint_fast8_t d = Etc2DistanceTable[(data[3] >> 1 & 6) | (data[3] & 1)]; + uint_fast32_t color_set[4] = { applicate_color_raw(c[0]), applicate_color(c[1], d), applicate_color_raw(c[1]), applicate_color(c[1], -d) }; + k <<= 1; + for (int i = 0; i < 16; i++, j >>= 1, k >>= 1) { + int index = (k & 2) | (j & 1); + outbuf[WriteOrderTable[i]] = color_set[index]; + if (!obaq && index == 2) + outbuf[WriteOrderTable[i]] &= TRANSPARENT_MASK; + } + } else if (g + dg < 0 || g + dg > 255) { + // H + c[0][0] = (data[0] << 1 & 0xf0) | (data[0] >> 3 & 0xf); + c[0][1] = (data[0] << 5 & 0xe0) | (data[1] & 0x10); + c[0][1] |= c[0][1] >> 4; + c[0][2] = (data[1] & 8) | (data[1] << 1 & 6) | data[2] >> 7; + c[0][2] |= c[0][2] << 4; + c[1][0] = (data[2] << 1 & 0xf0) | (data[2] >> 3 & 0xf); + c[1][1] = (data[2] << 5 & 0xe0) | (data[3] >> 3 & 0x10); + c[1][1] |= c[1][1] >> 4; + c[1][2] = (data[3] << 1 & 0xf0) | (data[3] >> 3 & 0xf); + uint_fast8_t d = (data[3] & 4) | (data[3] << 1 & 2); + if (c[0][0] > c[1][0] || (c[0][0] == c[1][0] && (c[0][1] > c[1][1] || (c[0][1] == c[1][1] && c[0][2] >= c[1][2])))) + ++d; + d = Etc2DistanceTable[d]; + uint_fast32_t color_set[4] = { applicate_color(c[0], d), applicate_color(c[0], -d), applicate_color(c[1], d), applicate_color(c[1], -d) }; + k <<= 1; + for (int i = 0; i < 16; i++, j >>= 1, k >>= 1) { + int index = (k & 2) | (j & 1); + outbuf[WriteOrderTable[i]] = color_set[index]; + if (!obaq && index == 2) + outbuf[WriteOrderTable[i]] &= TRANSPARENT_MASK; + } + } else if (b + db < 0 || b + db > 255) { + // planar + c[0][0] = (data[0] << 1 & 0xfc) | (data[0] >> 5 & 3); + c[0][1] = (data[0] << 7 & 0x80) | (data[1] & 0x7e) | (data[0] & 1); + c[0][2] = (data[1] << 7 & 0x80) | (data[2] << 2 & 0x60) | (data[2] << 3 & 0x18) | (data[3] >> 5 & 4); + c[0][2] |= c[0][2] >> 6; + c[1][0] = (data[3] << 1 & 0xf8) | (data[3] << 2 & 4) | (data[3] >> 5 & 3); + c[1][1] = (data[4] & 0xfe) | data[4] >> 7; + c[1][2] = (data[4] << 7 & 0x80) | (data[5] >> 1 & 0x7c); + c[1][2] |= c[1][2] >> 6; + c[2][0] = (data[5] << 5 & 0xe0) | (data[6] >> 3 & 0x1c) | (data[5] >> 1 & 3); + c[2][1] = (data[6] << 3 & 0xf8) | (data[7] >> 5 & 0x6) | (data[6] >> 4 & 1); + c[2][2] = data[7] << 2 | (data[7] >> 4 & 3); + for (int y = 0, i = 0; y < 4; y++) { + for (int x = 0; x < 4; x++, i++) { + uint8_t r = clamp((x * (c[1][0] - c[0][0]) + y * (c[2][0] - c[0][0]) + 4 * c[0][0] + 2) >> 2); + uint8_t g = clamp((x * (c[1][1] - c[0][1]) + y * (c[2][1] - c[0][1]) + 4 * c[0][1] + 2) >> 2); + uint8_t b = clamp((x * (c[1][2] - c[0][2]) + y * (c[2][2] - c[0][2]) + 4 * c[0][2] + 2) >> 2); + outbuf[i] = color(r, g, b, 255); + } + } + } else { + // differential + const uint_fast8_t code[2] = { data[3] >> 5, data[3] >> 2 & 7 }; + const uint_fast8_t* table = Etc1SubblockTable[data[3] & 1]; + c[0][0] = r | r >> 5; + c[0][1] = g | g >> 5; + c[0][2] = b | b >> 5; + c[1][0] = r + dr; + c[1][1] = g + dg; + c[1][2] = b + db; + c[1][0] |= c[1][0] >> 5; + c[1][1] |= c[1][1] >> 5; + c[1][2] |= c[1][2] >> 5; + for (int i = 0; i < 16; i++, j >>= 1, k >>= 1) { + uint_fast8_t s = table[i]; + uint_fast8_t m = Etc2aModifierTable[obaq][code[s]][j & 1]; + outbuf[WriteOrderTable[i]] = applicate_color_alpha(c[s], k & 1 ? -m : m, !obaq && (k & 1) && !(j & 1)); + } + } +} + static inline void decode_etc2a8_block(const uint8_t* data, uint32_t* outbuf) { if (data[1] & 0xf0) { @@ -256,10 +368,8 @@ void decode_etc2a1(const void* data, const int w, const int h, uint32_t* image) uint32_t* buf_end = buf + 16; const uint8_t* d = (uint8_t*)data; for (int by = 0; by < num_blocks_y; by++) { - for (int bx = 0, x = 0; bx < num_blocks_x; bx++, d += 9, x += 4) { - decode_etc2_block(d + 1, buf); - for (int i = 0; i < 16; i++) - ((uint8_t*)(buf + i))[3] = d[0]; + for (int bx = 0, x = 0; bx < num_blocks_x; bx++, d += 8, x += 4) { + decode_etc2a1_block(d, buf); int copy_length = (bx < num_blocks_x - 1 ? 4 : copy_length_last) * 4; uint32_t* b = buf; for (int y = h - by * 4 - 1; b < buf_end && y >= 0; y--, b += 4) diff --git a/ext/decoders/native/main.c b/ext/decoders/native/main.c index 1bfc65d..3d861e6 100644 --- a/ext/decoders/native/main.c +++ b/ext/decoders/native/main.c @@ -23,6 +23,23 @@ static VALUE rb_decode_a8(VALUE self, VALUE rb_data, VALUE size) return ret; } +/* + * Decode image from R8 binary + * + * @param [String] rb_data binary to decode + * @param [Integer] size width * height + * @return [String] decoded rgb binary + */ +static VALUE rb_decode_r8(VALUE self, VALUE rb_data, VALUE size) +{ + if (RSTRING_LEN(rb_data) < FIX2LONG(size)) + rb_raise(rb_eStandardError, "Data size is not enough."); + VALUE ret = rb_str_buf_new(FIX2LONG(size) * 3); + decode_r8((uint8_t*)RSTRING_PTR(rb_data), FIX2INT(size), (uint8_t*)RSTRING_PTR(ret)); + rb_str_set_len(ret, FIX2LONG(size) * 3); + return ret; +} + /* * Decode image from R16 binary * @@ -107,11 +124,10 @@ static VALUE rb_decode_etc2(VALUE self, VALUE rb_data, VALUE w, VALUE h) */ static VALUE rb_decode_etc2a1(VALUE self, VALUE rb_data, VALUE w, VALUE h) { - if (RSTRING_LEN(rb_data) < ((FIX2LONG(w) + 3) / 4) * ((FIX2LONG(h) + 3) / 4) * 9) + if (RSTRING_LEN(rb_data) < ((FIX2LONG(w) + 3) / 4) * ((FIX2LONG(h) + 3) / 4) * 8) rb_raise(rb_eStandardError, "Data size is not enough."); uint32_t* image = (uint32_t*)calloc(FIX2LONG(w) * FIX2LONG(h), sizeof(uint32_t)); - decode_etc2a8((uint64_t*)RSTRING_PTR(rb_data), FIX2INT(w), FIX2INT(h), - image); + decode_etc2a1((uint64_t*)RSTRING_PTR(rb_data), FIX2INT(w), FIX2INT(h), image); VALUE ret = rb_str_new((char*)image, FIX2LONG(w) * FIX2LONG(h) * sizeof(uint32_t)); free(image); return ret; @@ -130,8 +146,7 @@ static VALUE rb_decode_etc2a8(VALUE self, VALUE rb_data, VALUE w, VALUE h) if (RSTRING_LEN(rb_data) < ((FIX2LONG(w) + 3) / 4) * ((FIX2LONG(h) + 3) / 4) * 16) rb_raise(rb_eStandardError, "Data size is not enough."); uint32_t* image = (uint32_t*)calloc(FIX2LONG(w) * FIX2LONG(h), sizeof(uint32_t)); - decode_etc2a8((uint64_t*)RSTRING_PTR(rb_data), FIX2INT(w), FIX2INT(h), - image); + decode_etc2a8((uint64_t*)RSTRING_PTR(rb_data), FIX2INT(w), FIX2INT(h), image); VALUE ret = rb_str_new((char*)image, FIX2LONG(w) * FIX2LONG(h) * sizeof(uint32_t)); free(image); return ret; @@ -152,9 +167,8 @@ static VALUE rb_decode_astc(VALUE self, VALUE rb_data, VALUE w, VALUE h, { if (RSTRING_LEN(rb_data) < ((FIX2LONG(w) + FIX2LONG(bw) - 1) / FIX2LONG(bw)) * ((FIX2LONG(h) + FIX2LONG(bh) - 1) / FIX2LONG(bh)) * 16) rb_raise(rb_eStandardError, "Data size is not enough."); - const uint8_t* data = (uint8_t*)RSTRING_PTR(rb_data); uint32_t* image = (uint32_t*)calloc(FIX2LONG(w) * FIX2LONG(h), sizeof(uint32_t)); - decode_astc(data, FIX2INT(w), FIX2INT(h), FIX2INT(bw), FIX2INT(bh), image); + decode_astc((uint8_t*)RSTRING_PTR(rb_data), FIX2INT(w), FIX2INT(h), FIX2INT(bw), FIX2INT(bh), image); VALUE ret = rb_str_new((char*)image, FIX2LONG(w) * FIX2LONG(h) * sizeof(uint32_t)); free(image); return ret; @@ -173,7 +187,7 @@ static VALUE rb_decode_dxt1(VALUE self, VALUE rb_data, VALUE w, VALUE h) if (RSTRING_LEN(rb_data) < ((FIX2LONG(w) + 3) / 4) * ((FIX2LONG(h) + 3) / 4) * 8) rb_raise(rb_eStandardError, "Data size is not enough."); uint32_t* image = (uint32_t*)calloc(FIX2LONG(w) * FIX2LONG(h), sizeof(uint32_t)); - decode_dxt1((uint64_t*)RSTRING_PTR(rb_data), FIX2INT(w), FIX2INT(h), image); + decode_dxt1((uint8_t*)RSTRING_PTR(rb_data), FIX2INT(w), FIX2INT(h), image); VALUE ret = rb_str_new((char*)image, FIX2LONG(w) * FIX2LONG(h) * sizeof(uint32_t)); free(image); return ret; @@ -192,7 +206,7 @@ static VALUE rb_decode_dxt5(VALUE self, VALUE rb_data, VALUE w, VALUE h) if (RSTRING_LEN(rb_data) < ((FIX2LONG(w) + 3) / 4) * ((FIX2LONG(h) + 3) / 4) * 16) rb_raise(rb_eStandardError, "Data size is not enough."); uint32_t* image = (uint32_t*)calloc(FIX2LONG(w) * FIX2LONG(h), sizeof(uint32_t)); - decode_dxt5((uint64_t*)RSTRING_PTR(rb_data), FIX2INT(w), FIX2INT(h), image); + decode_dxt5((uint8_t*)RSTRING_PTR(rb_data), FIX2INT(w), FIX2INT(h), image); VALUE ret = rb_str_new((char*)image, FIX2LONG(w) * FIX2LONG(h) * sizeof(uint32_t)); free(image); return ret; @@ -203,6 +217,7 @@ void Init_native() VALUE mMikunyan = rb_define_module("Mikunyan"); VALUE mDecodeHelper = rb_define_module_under(mMikunyan, "DecodeHelper"); rb_define_module_function(mDecodeHelper, "decode_a8", rb_decode_a8, 2); + rb_define_module_function(mDecodeHelper, "decode_r8", rb_decode_r8, 2); rb_define_module_function(mDecodeHelper, "decode_r16", rb_decode_r16, 3); rb_define_module_function(mDecodeHelper, "decode_rgb565", rb_decode_rgb565, 3); rb_define_module_function(mDecodeHelper, "decode_etc1", rb_decode_etc1, 3); diff --git a/ext/decoders/native/rgb.c b/ext/decoders/native/rgb.c index 34f1253..0554805 100644 --- a/ext/decoders/native/rgb.c +++ b/ext/decoders/native/rgb.c @@ -12,6 +12,16 @@ void decode_a8(const uint8_t* data, const int size, uint8_t* image) } } +void decode_r8(const uint8_t* data, const int size, uint8_t* image) +{ + const uint8_t *d = data, *d_end = data + size; + for (int i = 0; d < d_end; d++) { + image[i++] = *d; + image[i++] = 0; + image[i++] = 0; + } +} + void decode_r16(const uint16_t* data, const int size, const int endian_big, uint8_t* image) { const uint16_t *d = data, *d_end = data + size; @@ -20,16 +30,15 @@ void decode_r16(const uint16_t* data, const int size, const int endian_big, uint for (int i = 0; d < d_end; d++) { uint8_t c = *d >> 8; image[i++] = c; - image[i++] = c; - image[i++] = c; + image[i++] = 0; + image[i++] = 0; } } else { // Different endian for (int i = 0; d < d_end; d++) { - uint8_t c = *d; - image[i++] = c; - image[i++] = c; - image[i++] = c; + image[i++] = *d; + image[i++] = 0; + image[i++] = 0; } } } diff --git a/ext/decoders/native/rgb.h b/ext/decoders/native/rgb.h index 2f25f31..72ad336 100644 --- a/ext/decoders/native/rgb.h +++ b/ext/decoders/native/rgb.h @@ -4,6 +4,7 @@ #include void decode_a8(const uint8_t*, const int, uint8_t*); +void decode_r8(const uint8_t*, const int, uint8_t*); void decode_r16(const uint16_t*, const int, const int, uint8_t*); void decode_rgb565(const uint16_t*, const int, const int, uint8_t*); diff --git a/lib/mikunyan/decoders/image_decoder.rb b/lib/mikunyan/decoders/image_decoder.rb index f0348ed..676f2bb 100644 --- a/lib/mikunyan/decoders/image_decoder.rb +++ b/lib/mikunyan/decoders/image_decoder.rb @@ -106,7 +106,7 @@ module Mikunyan when 62 # RG16 decode_rg16(width, height, bin) when 63 # R8 - decode_a8(width, height, bin) + decode_r8(width, height, bin) end end @@ -119,6 +119,15 @@ module Mikunyan ChunkyPNG::Image.from_rgb_stream(width, height, DecodeHelper.decode_a8(bin, width * height)).flip end + # Decode image from R8 binary + # @param [Integer] width image width + # @param [Integer] height image height + # @param [String] bin binary to decode + # @return [ChunkyPNG::Image] decoded image + def self.decode_r8(width, height, bin) + ChunkyPNG::Image.from_rgb_stream(width, height, DecodeHelper.decode_r8(bin, width * height)).flip + end + # Decode image from ARGB4444 binary # @param [Integer] width image width # @param [Integer] height image height @@ -240,14 +249,12 @@ module Mikunyan mem = String.new(capacity: width * height * 3) (width * height).times do |i| n = endian == :little ? BinUtils.get_int32_le(bin, i * 4) : BinUtils.get_int32_be(bin, i * 4) - e = (n & 0xf8000000) >> 27 - r = (n & 0x7fc0000) >> 9 - g = (n & 0x3fe00) >> 9 - b = n & 0x1ff - r = (r / 512r + 1) * (2**(e - 15)) - g = (g / 512r + 1) * (2**(e - 15)) - b = (b / 512r + 1) * (2**(e - 15)) - BinUtils.append_int8!(mem, f2i(r), f2i(g), f2i(b)) + b = n >> 18 & 0x1ff + g = n >> 9 & 0x1ff + r = n & 0x1ff + scale = n >> 27 & 0x1f + scale = 2**(scale - 24) + BinUtils.append_int8!(mem, f2i(r * scale), f2i(g * scale), f2i(b * scale)) end ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip end @@ -441,24 +448,21 @@ module Mikunyan def self.create_astc_file(object) astc_list = { 48 => 4, 49 => 5, 50 => 6, 51 => 8, 52 => 10, 53 => 12, - 54 => 4, 55 => 5, 56 => 6, 57 => 8, 58 => 10, 59 => 12 + 54 => 4, 55 => 5, 56 => 6, 57 => 8, 58 => 10, 59 => 12, + 66 => 4, 67 => 5, 68 => 6, 69 => 8, 70 => 10, 71 => 12 } - width = object['m_Width'] - height = object['m_Height'] - fmt = object['m_TextureFormat'] - bin = object['image data'] - width = width.value if width.class == ObjectValue - height = height.value if height.class == ObjectValue - fmt = fmt.value if fmt.class == ObjectValue - bin = bin.value if bin.class == ObjectValue - if width && height && fmt && astc_list[fmt] - header = "\x13\xAB\xA1\x5C".force_encoding('ascii-8bit') - header << [astc_list[fmt], astc_list[fmt], 1].pack('C*') - header << [width].pack('V').byteslice(0, 3) - header << [height].pack('V').byteslice(0, 3) - header << "\x01\x00\x00" - header + bin - end + width = object['m_Width']&.value + height = object['m_Height']&.value + fmt = object['m_TextureFormat']&.value + bin = object['image data']&.value + return unless width && height && fmt && bin && astc_list[fmt] + bin = object['m_StreamData']&.value if bin.empty? + return unless bin + header = [0x13, 0xab, 0xa1, 0x5c, astc_list[fmt], astc_list[fmt], 1].pack('C*') + header << [width].pack('V').byteslice(0, 3) + header << [height].pack('V').byteslice(0, 3) + header << [1, 0, 0].pack('C*') + header + bin end # convert 16bit float