From bd4b10f871fec013039cb6f46470e28d483cebd0 Mon Sep 17 00:00:00 2001 From: Ishotihadus Date: Tue, 10 Dec 2019 04:25:58 +0900 Subject: [PATCH] add ASTC/HDR support and improve ImageDecoder --- ext/decoders/native/astc.c | 1136 +++++++++++++++++------- ext/decoders/native/common.h | 25 + ext/decoders/native/dxtc.c | 32 +- ext/decoders/native/etc.c | 296 +++--- ext/decoders/native/fp16.h | 40 + ext/decoders/native/fp16/bitcasts.h | 76 ++ ext/decoders/native/fp16/fp16.h | 451 ++++++++++ ext/decoders/native/main.c | 108 ++- ext/decoders/native/rgb.c | 75 +- ext/decoders/native/rgb.h | 2 + lib/mikunyan/decoders/image_decoder.rb | 126 ++- 11 files changed, 1745 insertions(+), 622 deletions(-) create mode 100644 ext/decoders/native/common.h create mode 100644 ext/decoders/native/fp16.h create mode 100644 ext/decoders/native/fp16/bitcasts.h create mode 100644 ext/decoders/native/fp16/fp16.h diff --git a/ext/decoders/native/astc.c b/ext/decoders/native/astc.c index 3c6928b..5938d73 100644 --- a/ext/decoders/native/astc.c +++ b/ext/decoders/native/astc.c @@ -1,59 +1,69 @@ -#include -#include -#include -#include #include "astc.h" +#include "fp16.h" +#include +#include +#include +#include +#include static const int BitReverseTable[] = { - 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, - 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, - 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, - 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, - 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, - 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, - 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, - 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, - 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, - 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, - 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, - 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, - 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, - 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, - 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, - 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, + 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, + 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, + 0x3C, 0xBC, 0x7C, 0xFC, 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 0x0A, 0x8A, 0x4A, 0xCA, + 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, + 0x36, 0xB6, 0x76, 0xF6, 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 0x01, 0x81, 0x41, 0xC1, + 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, + 0x39, 0xB9, 0x79, 0xF9, 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 0x0D, 0x8D, 0x4D, 0xCD, + 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, + 0x33, 0xB3, 0x73, 0xF3, 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 0x07, 0x87, 0x47, 0xC7, + 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, + 0x3F, 0xBF, 0x7F, 0xFF }; -static int WeightPrecTableA[] = {0, 0, 0, 3, 0, 5, 3, 0, 0, 0, 5, 3, 0, 5, 3, 0}; -static int WeightPrecTableB[] = {0, 0, 1, 0, 2, 0, 1, 3, 0, 0, 1, 2, 4, 2, 3, 5}; +static const int WeightPrecTableA[] = { 0, 0, 0, 3, 0, 5, 3, 0, 0, 0, 5, 3, 0, 5, 3, 0 }; +static const int WeightPrecTableB[] = { 0, 0, 1, 0, 2, 0, 1, 3, 0, 0, 1, 2, 4, 2, 3, 5 }; -static int CemTableA[] = {0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 0, 0}; -static int CemTableB[] = {8, 6, 5, 7, 5, 4, 6, 4, 3, 5, 3, 2, 4, 2, 1, 3, 1, 2, 1}; +static const int CemTableA[] = { 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 0, 0 }; +static const int CemTableB[] = { 8, 6, 5, 7, 5, 4, 6, 4, 3, 5, 3, 2, 4, 2, 1, 3, 1, 2, 1 }; -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 uint_fast8_t bit_reverse_u8(const uint_fast8_t c, const int bits) { +static inline uint_fast8_t bit_reverse_u8(const uint_fast8_t c, const int bits) +{ return BitReverseTable[c] >> (8 - bits); } -static inline uint_fast64_t bit_reverse_u64(const uint_fast64_t d, const int bits) { - uint_fast64_t ret = - (uint_fast64_t)BitReverseTable[d & 0xff] << 56 | - (uint_fast64_t)BitReverseTable[d >> 8 & 0xff] << 48 | - (uint_fast64_t)BitReverseTable[d >> 16 & 0xff] << 40 | - (uint_fast64_t)BitReverseTable[d >> 24 & 0xff] << 32 | - (uint_fast32_t)BitReverseTable[d >> 32 & 0xff] << 24 | - (uint_fast32_t)BitReverseTable[d >> 40 & 0xff] << 16 | - (uint_fast16_t)BitReverseTable[d >> 48 & 0xff] << 8 | BitReverseTable[d >> 56 & 0xff]; +static inline uint_fast64_t bit_reverse_u64(const uint_fast64_t d, const int bits) +{ + uint_fast64_t ret = (uint_fast64_t)BitReverseTable[d & 0xff] << 56 | (uint_fast64_t)BitReverseTable[d >> 8 & 0xff] << 48 | (uint_fast64_t)BitReverseTable[d >> 16 & 0xff] << 40 | (uint_fast64_t)BitReverseTable[d >> 24 & 0xff] << 32 | (uint_fast32_t)BitReverseTable[d >> 32 & 0xff] << 24 | (uint_fast32_t)BitReverseTable[d >> 40 & 0xff] << 16 | (uint_fast16_t)BitReverseTable[d >> 48 & 0xff] << 8 | BitReverseTable[d >> 56 & 0xff]; return ret >> (64 - bits); } -static inline int getbits(const uint8_t *buf, const int bit, const int len) { +static inline int getbits(const uint8_t* buf, const int bit, const int len) +{ return (*(int*)(buf + bit / 8) >> (bit % 8)) & ((1 << len) - 1); } -static inline uint_fast64_t getbits64(const uint8_t *buf, const int bit, const int len) { +static inline uint_fast64_t getbits64(const uint8_t* buf, const int bit, const int len) +{ uint_fast64_t mask = len == 64 ? 0xffffffffffffffff : (1ull << len) - 1; if (len < 1) return 0; @@ -67,18 +77,30 @@ static inline uint_fast64_t getbits64(const uint8_t *buf, const int bit, const i return ((*(uint_fast64_t*)buf) >> bit | *(uint_fast64_t*)(buf + 8) << (64 - bit)) & mask; } -static inline uint_fast8_t clamp(const int n) { +static inline uint16_t u8ptr_to_u16(const uint8_t* ptr) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + return *(uint16_t*)ptr; +#else + return ptr[0] | ptr[1] << 8; +#endif +} + +static inline uint_fast8_t clamp(const int n) +{ return n < 0 ? 0 : n > 255 ? 255 : n; } -static inline void bit_transfer_signed(int *a, int *b) { +static inline void bit_transfer_signed(int* a, int* b) +{ *b = (*b >> 1) | (*a & 0x80); *a = (*a >> 1) & 0x3f; if (*a & 0x20) *a -= 0x40; } -static inline void set_endpoint(int endpoint[8], int r1, int g1, int b1, int a1, int r2, int g2, int b2, int a2) { +static inline void set_endpoint(int endpoint[8], int r1, int g1, int b1, int a1, int r2, int g2, int b2, int a2) +{ endpoint[0] = r1; endpoint[1] = g1; endpoint[2] = b1; @@ -89,7 +111,8 @@ static inline void set_endpoint(int endpoint[8], int r1, int g1, int b1, int a1, endpoint[7] = a2; } -static inline void set_endpoint_clamp(int endpoint[8], int r1, int g1, int b1, int a1, int r2, int g2, int b2, int a2) { +static inline void set_endpoint_clamp(int endpoint[8], int r1, int g1, int b1, int a1, int r2, int g2, int b2, int a2) +{ endpoint[0] = clamp(r1); endpoint[1] = clamp(g1); endpoint[2] = clamp(b1); @@ -100,7 +123,8 @@ static inline void set_endpoint_clamp(int endpoint[8], int r1, int g1, int b1, i endpoint[7] = clamp(a2); } -static inline void set_endpoint_blue(int endpoint[8], int r1, int g1, int b1, int a1, int r2, int g2, int b2, int a2) { +static inline void set_endpoint_blue(int endpoint[8], int r1, int g1, int b1, int a1, int r2, int g2, int b2, int a2) +{ endpoint[0] = (r1 + b1) >> 1; endpoint[1] = (g1 + b1) >> 1; endpoint[2] = b1; @@ -111,7 +135,8 @@ static inline void set_endpoint_blue(int endpoint[8], int r1, int g1, int b1, in endpoint[7] = a2; } -static inline void set_endpoint_blue_clamp(int endpoint[8], int r1, int g1, int b1, int a1, int r2, int g2, int b2, int a2) { +static inline void set_endpoint_blue_clamp(int endpoint[8], int r1, int g1, int b1, int a1, int r2, int g2, int b2, int a2) +{ endpoint[0] = clamp((r1 + b1) >> 1); endpoint[1] = clamp((g1 + b1) >> 1); endpoint[2] = clamp(b1); @@ -122,10 +147,79 @@ static inline void set_endpoint_blue_clamp(int endpoint[8], int r1, int g1, int endpoint[7] = clamp(a2); } -static inline uint_fast8_t select_color(int v0, int v1, int weight) { +static inline uint_fast16_t clamp_hdr(const int n) +{ + return n < 0 ? 0 : n > 0xfff ? 0xfff : n; +} + +static inline void set_endpoint_hdr(int endpoint[8], int r1, int g1, int b1, int a1, int r2, int g2, int b2, int a2) +{ + endpoint[0] = r1; + endpoint[1] = g1; + endpoint[2] = b1; + endpoint[3] = a1; + endpoint[4] = r2; + endpoint[5] = g2; + endpoint[6] = b2; + endpoint[7] = a2; +} + +static inline void set_endpoint_hdr_clamp(int endpoint[8], int r1, int g1, int b1, int a1, int r2, int g2, int b2, int a2) +{ + endpoint[0] = clamp_hdr(r1); + endpoint[1] = clamp_hdr(g1); + endpoint[2] = clamp_hdr(b1); + endpoint[3] = clamp_hdr(a1); + endpoint[4] = clamp_hdr(r2); + endpoint[5] = clamp_hdr(g2); + endpoint[6] = clamp_hdr(b2); + endpoint[7] = clamp_hdr(a2); +} + +typedef uint_fast8_t (*t_select_folor_func_ptr)(int, int, int); + +static uint_fast8_t select_color(int v0, int v1, int weight) +{ return ((((v0 << 8 | v0) * (64 - weight) + (v1 << 8 | v1) * weight + 32) >> 6) * 255 + 32768) / 65536; } +static uint_fast8_t select_color_hdr(int v0, int v1, int weight) +{ + uint16_t c = ((v0 << 4) * (64 - weight) + (v1 << 4) * weight + 32) >> 6; + uint16_t m = c & 0x7ff; + if (m < 512) + m *= 3; + else if (m < 1536) + m = 4 * m - 512; + else + m = 5 * m - 2048; + float f = fp16_ieee_to_fp32_value((c >> 1 & 0x7c00) | m >> 3); + return isfinite(f) ? clamp(roundf(f * 255)) : 255; +} + +static inline uint8_t f32_to_u8(const float f) +{ + float c = roundf(f * 255); + if (c < 0) + return 0; + else if (c > 255) + return 255; + else + return c; +} + +static inline uint8_t f16ptr_to_u8(const uint8_t* ptr) +{ + const uint16_t c = +#if BYTE_ORDER == LITTLE_ENDIAN + *(uint16_t*)ptr +#else + ptr[0] | ptr[1] << 8 +#endif + ; + return f32_to_u8(fp16_ieee_to_fp32_value(c)); +} + typedef struct { int bw; int bh; @@ -149,20 +243,86 @@ typedef struct { int nonbits; } IntSeqData; -void decode_intseq(const uint8_t *buf, int offset, const int a, const int b, const int count, const int reverse, IntSeqData *out) { +void decode_intseq(const uint8_t* buf, int offset, const int a, const int b, const int count, const int reverse, IntSeqData* out) +{ static int mt[] = { 0, 2, 4, 5, 7 }; static int mq[] = { 0, 3, 5 }; static int TritsTable[5][256] = { - {0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 1, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 1, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2}, - {0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 1}, - {0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2} + { 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, + 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, + 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 1, 0, 1, 2, 0, 0, 1, 2, 1, + 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, + 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, + 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, + 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 1, + 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 1, + 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, + 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, + 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2 }, + { 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 0, + 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 2, 2, 2, 1, 2, 2, 2, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, + 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 2, 2, 2, 1, 2, 2, 2, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 1 }, + { 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, + 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, + 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, + 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, + 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, + 1, 1, 1, 2, 2, 2, 2, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, + 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, + 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, + 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, + 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, + 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 } }; static int QuintsTable[3][128] = { - {0, 1, 2, 3, 4, 0, 4, 4, 0, 1, 2, 3, 4, 1, 4, 4, 0, 1, 2, 3, 4, 2, 4, 4, 0, 1, 2, 3, 4, 3, 4, 4, 0, 1, 2, 3, 4, 0, 4, 0, 0, 1, 2, 3, 4, 1, 4, 1, 0, 1, 2, 3, 4, 2, 4, 2, 0, 1, 2, 3, 4, 3, 4, 3, 0, 1, 2, 3, 4, 0, 2, 3, 0, 1, 2, 3, 4, 1, 2, 3, 0, 1, 2, 3, 4, 2, 2, 3, 0, 1, 2, 3, 4, 3, 2, 3, 0, 1, 2, 3, 4, 0, 0, 1, 0, 1, 2, 3, 4, 1, 0, 1, 0, 1, 2, 3, 4, 2, 0, 1, 0, 1, 2, 3, 4, 3, 0, 1}, - {0, 0, 0, 0, 0, 4, 4, 4, 1, 1, 1, 1, 1, 4, 4, 4, 2, 2, 2, 2, 2, 4, 4, 4, 3, 3, 3, 3, 3, 4, 4, 4, 0, 0, 0, 0, 0, 4, 0, 4, 1, 1, 1, 1, 1, 4, 1, 4, 2, 2, 2, 2, 2, 4, 2, 4, 3, 3, 3, 3, 3, 4, 3, 4, 0, 0, 0, 0, 0, 4, 0, 0, 1, 1, 1, 1, 1, 4, 1, 1, 2, 2, 2, 2, 2, 4, 2, 2, 3, 3, 3, 3, 3, 4, 3, 3, 0, 0, 0, 0, 0, 4, 0, 0, 1, 1, 1, 1, 1, 4, 1, 1, 2, 2, 2, 2, 2, 4, 2, 2, 3, 3, 3, 3, 3, 4, 3, 3}, - {0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 0, 0, 2, 4, 0, 0, 0, 0, 0, 0, 3, 4, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4} + { 0, 1, 2, 3, 4, 0, 4, 4, 0, 1, 2, 3, 4, 1, 4, 4, 0, 1, 2, 3, 4, 2, + 4, 4, 0, 1, 2, 3, 4, 3, 4, 4, 0, 1, 2, 3, 4, 0, 4, 0, 0, 1, 2, 3, + 4, 1, 4, 1, 0, 1, 2, 3, 4, 2, 4, 2, 0, 1, 2, 3, 4, 3, 4, 3, 0, 1, + 2, 3, 4, 0, 2, 3, 0, 1, 2, 3, 4, 1, 2, 3, 0, 1, 2, 3, 4, 2, 2, 3, + 0, 1, 2, 3, 4, 3, 2, 3, 0, 1, 2, 3, 4, 0, 0, 1, 0, 1, 2, 3, 4, 1, + 0, 1, 0, 1, 2, 3, 4, 2, 0, 1, 0, 1, 2, 3, 4, 3, 0, 1 }, + { 0, 0, 0, 0, 0, 4, 4, 4, 1, 1, 1, 1, 1, 4, 4, 4, 2, 2, 2, 2, 2, 4, + 4, 4, 3, 3, 3, 3, 3, 4, 4, 4, 0, 0, 0, 0, 0, 4, 0, 4, 1, 1, 1, 1, + 1, 4, 1, 4, 2, 2, 2, 2, 2, 4, 2, 4, 3, 3, 3, 3, 3, 4, 3, 4, 0, 0, + 0, 0, 0, 4, 0, 0, 1, 1, 1, 1, 1, 4, 1, 1, 2, 2, 2, 2, 2, 4, 2, 2, + 3, 3, 3, 3, 3, 4, 3, 3, 0, 0, 0, 0, 0, 4, 0, 0, 1, 1, 1, 1, 1, 4, + 1, 1, 2, 2, 2, 2, 2, 4, 2, 2, 3, 3, 3, 3, 3, 4, 3, 3 }, + { 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 0, 0, + 2, 4, 0, 0, 0, 0, 0, 0, 3, 4, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, + 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 4, 4, 2, 2, + 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, + 2, 2, 2, 2, 2, 2, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, + 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4 } }; if (count <= 0) @@ -187,7 +347,8 @@ void decode_intseq(const uint8_t *buf, int offset, const int a, const int b, con } } else { for (int i = 0, p = offset; i < block_count; i++, p += block_size) { - uint_fast64_t d = getbits64(buf, p, (i < block_count - 1) ? block_size : last_block_size); + uint_fast64_t d = getbits64( + buf, p, (i < block_count - 1) ? block_size : last_block_size); int x = (d >> b & 3) | (d >> b * 2 & 0xc) | (d >> b * 3 & 0x10) | (d >> b * 4 & 0x60) | (d >> b * 5 & 0x80); for (int j = 0; j < 5 && n < count; j++, n++) out[n] = (IntSeqData){ d >> (mt[j] + b * j) & mask, TritsTable[j][x] }; @@ -210,7 +371,8 @@ void decode_intseq(const uint8_t *buf, int offset, const int a, const int b, con } } else { for (int i = 0, p = offset; i < block_count; i++, p += block_size) { - uint_fast64_t d = getbits64(buf, p, (i < block_count - 1) ? block_size : last_block_size); + uint_fast64_t d = getbits64( + buf, p, (i < block_count - 1) ? block_size : last_block_size); int x = (d >> b & 7) | (d >> b * 2 & 0x18) | (d >> b * 3 & 0x60); for (int j = 0; j < 3 && n < count; j++, n++) out[n] = (IntSeqData){ d >> (mq[j] + b * j) & mask, QuintsTable[j][x] }; @@ -226,56 +388,59 @@ void decode_intseq(const uint8_t *buf, int offset, const int a, const int b, con } } -void decode_block_params(const uint8_t *buf, BlockData *block_data) { - block_data->dual_plane = (buf[1] & 4) >> 2; +void decode_block_params(const uint8_t* buf, BlockData* block_data) +{ + block_data->dual_plane = !!(buf[1] & 4); block_data->weight_range = (buf[0] >> 4 & 1) | (buf[1] << 2 & 8); if (buf[0] & 3) { + // ← W | H H | ρ0 | ? | ? | ρ2 | ρ1 block_data->weight_range |= buf[0] << 1 & 6; switch (buf[0] & 0xc) { - case 0: - block_data->width = (*(int*)buf >> 7 & 3) + 4; + case 0: + block_data->width = (u8ptr_to_u16(buf) >> 7 & 3) + 4; + block_data->height = (buf[0] >> 5 & 3) + 2; + break; + case 4: + block_data->width = (u8ptr_to_u16(buf) >> 7 & 3) + 8; + block_data->height = (buf[0] >> 5 & 3) + 2; + break; + case 8: + block_data->width = (buf[0] >> 5 & 3) + 2; + block_data->height = (u8ptr_to_u16(buf) >> 7 & 3) + 8; + break; + case 12: + if (buf[1] & 1) { + block_data->width = (buf[0] >> 7 & 1) + 2; block_data->height = (buf[0] >> 5 & 3) + 2; - break; - case 4: - block_data->width = (*(int*)buf >> 7 & 3) + 8; - block_data->height = (buf[0] >> 5 & 3) + 2; - break; - case 8: + } else { block_data->width = (buf[0] >> 5 & 3) + 2; - block_data->height = (*(int*)buf >> 7 & 3) + 8; - break; - case 12: - if (buf[1] & 1) { - block_data->width = (buf[0] >> 7 & 1) + 2; - block_data->height = (buf[0] >> 5 & 3) + 2; - } else { - block_data->width = (buf[0] >> 5 & 3) + 2; - block_data->height = (buf[0] >> 7 & 1) + 6; - } - break; + block_data->height = (buf[0] >> 7 & 1) + 6; + } + break; } } else { + // ← W | H H | ρ0 | ρ2 | ρ1 | 0 | 0 block_data->weight_range |= buf[0] >> 1 & 6; - switch ((*(int*)buf) & 0x180) { - case 0: - block_data->width = 12; - block_data->height = (buf[0] >> 5 & 3) + 2; - break; - case 0x80: - block_data->width = (buf[0] >> 5 & 3) + 2; - block_data->height = 12; - break; - case 0x100: - block_data->width = (buf[0] >> 5 & 3) + 6; - block_data->height = (buf[1] >> 1 & 3) + 6; - block_data->dual_plane = 0; - block_data->weight_range &= 7; - break; - case 0x180: - block_data->width = (buf[0] & 0x20) ? 10 : 6; - block_data->height = (buf[0] & 0x20) ? 6 : 10; - break; + switch (u8ptr_to_u16(buf) & 0x180) { + case 0: + block_data->width = 12; + block_data->height = (buf[0] >> 5 & 3) + 2; + break; + case 0x80: + block_data->width = (buf[0] >> 5 & 3) + 2; + block_data->height = 12; + break; + case 0x100: + block_data->width = (buf[0] >> 5 & 3) + 6; + block_data->height = (buf[1] >> 1 & 3) + 6; + block_data->dual_plane = 0; + block_data->weight_range &= 7; + break; + case 0x180: + block_data->width = (buf[0] & 0x20) ? 10 : 6; + block_data->height = (buf[0] & 0x20) ? 6 : 10; + break; } } @@ -288,21 +453,21 @@ void decode_block_params(const uint8_t *buf, BlockData *block_data) { int weight_bits, config_bits, cem_base = 0; switch (WeightPrecTableA[block_data->weight_range]) { - case 3: - weight_bits = block_data->weight_num * WeightPrecTableB[block_data->weight_range] + (block_data->weight_num * 8 + 4) / 5; - break; - case 5: - weight_bits = block_data->weight_num * WeightPrecTableB[block_data->weight_range] + (block_data->weight_num * 7 + 2) / 3; - break; - default: - weight_bits = block_data->weight_num * WeightPrecTableB[block_data->weight_range]; + case 3: + weight_bits = block_data->weight_num * WeightPrecTableB[block_data->weight_range] + (block_data->weight_num * 8 + 4) / 5; + break; + case 5: + weight_bits = block_data->weight_num * WeightPrecTableB[block_data->weight_range] + (block_data->weight_num * 7 + 2) / 3; + break; + default: + weight_bits = block_data->weight_num * WeightPrecTableB[block_data->weight_range]; } if (block_data->part_num == 1) { - block_data->cem[0] = *(int*)(buf + 1) >> 5 & 0xf; + block_data->cem[0] = u8ptr_to_u16(buf + 1) >> 5 & 0xf; config_bits = 17; } else { - cem_base = *(int*)(buf + 2) >> 7 & 3; + cem_base = u8ptr_to_u16(buf + 2) >> 7 & 3; if (cem_base == 0) { int cem = buf[3] >> 1 & 0xf; for (int i = 0; i < block_data->part_num; i++) @@ -312,20 +477,20 @@ void decode_block_params(const uint8_t *buf, BlockData *block_data) { for (int i = 0; i < block_data->part_num; i++) block_data->cem[i] = ((buf[3] >> (i + 1) & 1) + cem_base - 1) << 2; switch (block_data->part_num) { - case 2: - block_data->cem[0] |= buf[3] >> 3 & 3; - block_data->cem[1] |= getbits(buf, 126 - weight_bits, 2); - break; - case 3: - block_data->cem[0] |= buf[3] >> 4 & 1; - block_data->cem[0] |= getbits(buf, 122 - weight_bits, 2) & 2; - block_data->cem[1] |= getbits(buf, 124 - weight_bits, 2); - block_data->cem[2] |= getbits(buf, 126 - weight_bits, 2); - break; - case 4: - for (int i = 0; i < 4; i++) - block_data->cem[i] |= getbits(buf, 120 + i * 2 - weight_bits, 2); - break; + case 2: + block_data->cem[0] |= buf[3] >> 3 & 3; + block_data->cem[1] |= getbits(buf, 126 - weight_bits, 2); + break; + case 3: + block_data->cem[0] |= buf[3] >> 4 & 1; + block_data->cem[0] |= getbits(buf, 122 - weight_bits, 2) & 2; + block_data->cem[1] |= getbits(buf, 124 - weight_bits, 2); + block_data->cem[2] |= getbits(buf, 126 - weight_bits, 2); + break; + case 4: + for (int i = 0; i < 4; i++) + block_data->cem[i] |= getbits(buf, 120 + i * 2 - weight_bits, 2); + break; } config_bits = 25 + block_data->part_num * 3; } @@ -344,14 +509,14 @@ void decode_block_params(const uint8_t *buf, BlockData *block_data) { for (int i = 0, endpoint_bits; i < (int)(sizeof(CemTableA) / sizeof(int)); i++) { switch (CemTableA[i]) { - case 3: - endpoint_bits = block_data->endpoint_value_num * CemTableB[i] + (block_data->endpoint_value_num * 8 + 4) / 5; - break; - case 5: - endpoint_bits = block_data->endpoint_value_num * CemTableB[i] + (block_data->endpoint_value_num * 7 + 2) / 3; - break; - default: - endpoint_bits = block_data->endpoint_value_num * CemTableB[i]; + case 3: + endpoint_bits = block_data->endpoint_value_num * CemTableB[i] + (block_data->endpoint_value_num * 8 + 4) / 5; + break; + case 5: + endpoint_bits = block_data->endpoint_value_num * CemTableB[i] + (block_data->endpoint_value_num * 7 + 2) / 3; + break; + default: + endpoint_bits = block_data->endpoint_value_num * CemTableB[i]; } if (endpoint_bits <= remain_bits) { @@ -361,194 +526,448 @@ void decode_block_params(const uint8_t *buf, BlockData *block_data) { } } -void decode_endpoints(const uint8_t *buf, BlockData *data) { - static int TritsTable[] = { 0, 204, 93, 44, 22, 11, 5 }; - static int QuintsTable[] = { 0, 113, 54, 26, 13, 6 }; +void decode_endpoints_hdr7(int* endpoints, int* v) +{ + int modeval = (v[2] >> 4 & 0x8) | (v[1] >> 5 & 0x4) | (v[0] >> 6); + int major_component, mode; + if ((modeval & 0xc) != 0xc) { + major_component = modeval >> 2; + mode = modeval & 3; + } else if (modeval != 0xf) { + major_component = modeval & 3; + mode = 4; + } else { + major_component = 0; + mode = 5; + } + int c[] = { v[0] & 0x3f, v[1] & 0x1f, v[2] & 0x1f, v[3] & 0x1f }; + + switch (mode) { + case 0: + c[3] |= v[3] & 0x60; + c[0] |= v[3] >> 1 & 0x40; + c[0] |= v[2] << 1 & 0x80; + c[0] |= v[1] << 3 & 0x300; + c[0] |= v[2] << 5 & 0x400; + c[0] <<= 1; + c[1] <<= 1; + c[2] <<= 1; + c[3] <<= 1; + break; + case 1: + c[1] |= v[1] & 0x20; + c[2] |= v[2] & 0x20; + c[0] |= v[3] >> 1 & 0x40; + c[0] |= v[2] << 1 & 0x80; + c[0] |= v[1] << 2 & 0x100; + c[0] |= v[3] << 4 & 0x600; + c[0] <<= 1; + c[1] <<= 1; + c[2] <<= 1; + c[3] <<= 1; + break; + case 2: + c[3] |= v[3] & 0xe0; + c[0] |= v[2] << 1 & 0xc0; + c[0] |= v[1] << 3 & 0x300; + c[0] <<= 2; + c[1] <<= 2; + c[2] <<= 2; + c[3] <<= 2; + break; + case 3: + c[1] |= v[1] & 0x20; + c[2] |= v[2] & 0x20; + c[3] |= v[3] & 0x60; + c[0] |= v[3] >> 1 & 0x40; + c[0] |= v[2] << 1 & 0x80; + c[0] |= v[1] << 2 & 0x100; + c[0] <<= 3; + c[1] <<= 3; + c[2] <<= 3; + c[3] <<= 3; + break; + case 4: + c[1] |= v[1] & 0x60; + c[2] |= v[2] & 0x60; + c[3] |= v[3] & 0x20; + c[0] |= v[3] >> 1 & 0x40; + c[0] |= v[3] << 1 & 0x80; + c[0] <<= 4; + c[1] <<= 4; + c[2] <<= 4; + c[3] <<= 4; + break; + case 5: + c[1] |= v[1] & 0x60; + c[2] |= v[2] & 0x60; + c[3] |= v[3] & 0x60; + c[0] |= v[3] >> 1 & 0x40; + c[0] <<= 5; + c[1] <<= 5; + c[2] <<= 5; + c[3] <<= 5; + break; + } + if (mode != 5) { + c[1] = c[0] - c[1]; + c[2] = c[0] - c[2]; + } + if (major_component == 1) + set_endpoint_hdr_clamp(endpoints, c[1] - c[3], c[0] - c[3], c[2] - c[3], 0x780, c[1], c[0], c[2], 0x780); + else if (major_component == 2) + set_endpoint_hdr_clamp(endpoints, c[2] - c[3], c[1] - c[3], c[0] - c[3], 0x780, c[2], c[1], c[0], 0x780); + else + set_endpoint_hdr_clamp(endpoints, c[0] - c[3], c[1] - c[3], c[2] - c[3], 0x780, c[0], c[1], c[2], 0x780); +} + +void decode_endpoints_hdr11(int* endpoints, int* v, int alpha1, int alpha2) +{ + int major_component = (v[4] >> 7) | (v[5] >> 6 & 2); + if (major_component == 3) { + set_endpoint_hdr(endpoints, v[0] << 4, v[2] << 4, v[4] << 5 & 0xfe0, alpha1, v[1] << 4, v[3] << 4, v[5] << 5 & 0xfe0, alpha2); + return; + } + int mode = (v[1] >> 7) | (v[2] >> 6 & 2) | (v[3] >> 5 & 4); + int va = v[0] | (v[1] << 2 & 0x100); + int vb0 = v[2] & 0x3f, vb1 = v[3] & 0x3f; + int vc = v[1] & 0x3f; + int16_t vd0, vd1; + + switch (mode) { + case 0: + case 2: + vd0 = v[4] & 0x7f; + if (vd0 & 0x40) + vd0 |= 0xff80; + vd1 = v[5] & 0x7f; + if (vd1 & 0x40) + vd1 |= 0xff80; + break; + case 1: + case 3: + case 5: + case 7: + vd0 = v[4] & 0x3f; + if (vd0 & 0x20) + vd0 |= 0xffc0; + vd1 = v[5] & 0x3f; + if (vd1 & 0x20) + vd1 |= 0xffc0; + break; + default: + vd0 = v[4] & 0x1f; + if (vd0 & 0x10) + vd0 |= 0xffe0; + vd1 = v[5] & 0x1f; + if (vd1 & 0x10) + vd1 |= 0xffe0; + break; + } + + switch (mode) { + case 0: + vb0 |= v[2] & 0x40; + vb1 |= v[3] & 0x40; + break; + case 1: + vb0 |= v[2] & 0x40; + vb1 |= v[3] & 0x40; + vb0 |= v[4] << 1 & 0x80; + vb1 |= v[5] << 1 & 0x80; + break; + case 2: + va |= v[2] << 3 & 0x200; + vc |= v[3] & 0x40; + break; + case 3: + va |= v[4] << 3 & 0x200; + vc |= v[5] & 0x40; + vb0 |= v[2] & 0x40; + vb1 |= v[3] & 0x40; + break; + case 4: + va |= v[4] << 4 & 0x200; + va |= v[5] << 5 & 0x400; + vb0 |= v[2] & 0x40; + vb1 |= v[3] & 0x40; + vb0 |= v[4] << 1 & 0x80; + vb1 |= v[5] << 1 & 0x80; + break; + case 5: + va |= v[2] << 3 & 0x200; + va |= v[3] << 4 & 0x400; + vc |= v[5] & 0x40; + vc |= v[4] << 1 & 0x80; + break; + case 6: + va |= v[4] << 4 & 0x200; + va |= v[5] << 5 & 0x400; + va |= v[4] << 5 & 0x800; + vc |= v[5] & 0x40; + vb0 |= v[2] & 0x40; + vb1 |= v[3] & 0x40; + break; + case 7: + va |= v[2] << 3 & 0x200; + va |= v[3] << 4 & 0x400; + va |= v[4] << 5 & 0x800; + vc |= v[5] & 0x40; + break; + } + + int shamt = (mode >> 1) ^ 3; + va <<= shamt; + vb0 <<= shamt; + vb1 <<= shamt; + vc <<= shamt; + int mult = 1 << shamt; + vd0 *= mult; + vd1 *= mult; + + if (major_component == 1) + set_endpoint_hdr_clamp(endpoints, va - vb0 - vc - vd0, va - vc, va - vb1 - vc - vd1, alpha1, va - vb0, va, va - vb1, alpha2); + else if (major_component == 2) + set_endpoint_hdr_clamp(endpoints, va - vb1 - vc - vd1, va - vb0 - vc - vd0, va - vc, alpha1, va - vb1, va - vb0, va, alpha2); + else + set_endpoint_hdr_clamp(endpoints, va - vc, va - vb0 - vc - vd0, va - vb1 - vc - vd1, alpha1, va, va - vb0, va - vb1, alpha2); +} + +void decode_endpoints(const uint8_t* buf, BlockData* data) +{ + static const int TritsTable[] = { 0, 204, 93, 44, 22, 11, 5 }; + static const int QuintsTable[] = { 0, 113, 54, 26, 13, 6 }; IntSeqData seq[32]; int ev[32]; decode_intseq(buf, data->part_num == 1 ? 17 : 29, CemTableA[data->cem_range], CemTableB[data->cem_range], data->endpoint_value_num, 0, seq); switch (CemTableA[data->cem_range]) { - case 3: - for (int i = 0, b, c = TritsTable[CemTableB[data->cem_range]]; i < data->endpoint_value_num; i++) { - int a = (seq[i].bits & 1) * 0x1ff; - int x = seq[i].bits >> 1; - switch (CemTableB[data->cem_range]) { - case 1: - b = 0; - break; - case 2: - b = 0b100010110 * x; - break; - case 3: - b = x << 7 | x << 2 | x; - break; - case 4: - b = x << 6 | x; - break; - case 5: - b = x << 5 | x >> 2; - break; - case 6: - b = x << 4 | x >> 4; - break; - } - ev[i] = (a & 0x80) | ((seq[i].nonbits * c + b) ^ a) >> 2; - } - break; - case 5: - for (int i = 0, b, c = QuintsTable[CemTableB[data->cem_range]]; i < data->endpoint_value_num; i++) { - int a = (seq[i].bits & 1) * 0x1ff; - int x = seq[i].bits >> 1; - switch (CemTableB[data->cem_range]) { - case 1: - b = 0; - break; - case 2: - b = 0b100001100 * x; - break; - case 3: - b = x << 7 | x << 1 | x >> 1; - break; - case 4: - b = x << 6 | x >> 1; - break; - case 5: - b = x << 5 | x >> 3; - break; - } - ev[i] = (a & 0x80) | ((seq[i].nonbits * c + b) ^ a) >> 2; - } - break; - default: + case 3: + for (int i = 0, b, c = TritsTable[CemTableB[data->cem_range]]; i < data->endpoint_value_num; i++) { + int a = (seq[i].bits & 1) * 0x1ff; + int x = seq[i].bits >> 1; switch (CemTableB[data->cem_range]) { - case 1: - for (int i = 0; i < data->endpoint_value_num; i++) - ev[i] = seq[i].bits * 0xff; - break; - case 2: - for (int i = 0; i < data->endpoint_value_num; i++) - ev[i] = seq[i].bits * 0x55; - break; - case 3: - for (int i = 0; i < data->endpoint_value_num; i++) - ev[i] = seq[i].bits << 5 | seq[i].bits << 2 | seq[i].bits >> 1; - break; - case 4: - for (int i = 0; i < data->endpoint_value_num; i++) - ev[i] = seq[i].bits << 4 | seq[i].bits; - break; - case 5: - for (int i = 0; i < data->endpoint_value_num; i++) - ev[i] = seq[i].bits << 3 | seq[i].bits >> 2; - break; - case 6: - for (int i = 0; i < data->endpoint_value_num; i++) - ev[i] = seq[i].bits << 2 | seq[i].bits >> 4; - break; - case 7: - for (int i = 0; i < data->endpoint_value_num; i++) - ev[i] = seq[i].bits << 1 | seq[i].bits >> 6; - break; - case 8: - for (int i = 0; i < data->endpoint_value_num; i++) - ev[i] = seq[i].bits; - break; - } - } - - int *v = ev; - for (int cem = 0; cem < data->part_num; v += (data->cem[cem] / 4 + 1) * 2, cem++) { - switch (data->cem[cem]) { - case 0: - set_endpoint(data->endpoints[cem], v[0], v[0], v[0], 255, v[1], v[1], v[1], 255); - break; case 1: - { - int l0 = (v[0] >> 2) | (v[1] & 0xc0); - int l1 = clamp(l0 + (v[1] & 0x3f)); - set_endpoint(data->endpoints[cem], l0, l0, l0, 255, l1, l1, l1, 255); - } + b = 0; + break; + case 2: + b = 0b100010110 * x; + break; + case 3: + b = x << 7 | x << 2 | x; break; case 4: - set_endpoint(data->endpoints[cem], v[0], v[0], v[0], v[2], v[1], v[1], v[1], v[3]); + b = x << 6 | x; break; case 5: - bit_transfer_signed(&v[1], &v[0]); - bit_transfer_signed(&v[3], &v[2]); - v[1] += v[0]; - set_endpoint_clamp(data->endpoints[cem], v[0], v[0], v[0], v[2], v[1], v[1], v[1], v[2] + v[3]); + b = x << 5 | x >> 2; break; case 6: - set_endpoint(data->endpoints[cem], v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8, 255, v[0], v[1], v[2], 255); + b = x << 4 | x >> 4; break; - case 8: - if (v[0] + v[2] + v[4] <= v[1] + v[3] + v[5]) - set_endpoint(data->endpoints[cem], v[0], v[2], v[4], 255, v[1], v[3], v[5], 255); - else - set_endpoint_blue(data->endpoints[cem], v[1], v[3], v[5], 255, v[0], v[2], v[4], 255); + } + ev[i] = (a & 0x80) | ((seq[i].nonbits * c + b) ^ a) >> 2; + } + break; + case 5: + for (int i = 0, b, c = QuintsTable[CemTableB[data->cem_range]]; i < data->endpoint_value_num; i++) { + int a = (seq[i].bits & 1) * 0x1ff; + int x = seq[i].bits >> 1; + switch (CemTableB[data->cem_range]) { + case 1: + b = 0; break; - case 9: - bit_transfer_signed(&v[1], &v[0]); - bit_transfer_signed(&v[3], &v[2]); - bit_transfer_signed(&v[5], &v[4]); - if (v[1] + v[3] + v[5] >= 0) - set_endpoint_clamp(data->endpoints[cem], v[0], v[2], v[4], 255, v[0] + v[1], v[2] + v[3], v[4] + v[5], 255); - else - set_endpoint_blue_clamp(data->endpoints[cem], v[0] + v[1], v[2] + v[3], v[4] + v[5], 255, v[0], v[2], v[4], 255); + case 2: + b = 0b100001100 * x; break; - case 10: - set_endpoint(data->endpoints[cem], v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8, v[4], v[0], v[1], v[2], v[5]); + case 3: + b = x << 7 | x << 1 | x >> 1; break; - case 12: - if (v[0] + v[2] + v[4] <= v[1] + v[3] + v[5]) - set_endpoint(data->endpoints[cem], v[0], v[2], v[4], v[6], v[1], v[3], v[5], v[7]); - else - set_endpoint_blue(data->endpoints[cem], v[1], v[3], v[5], v[7], v[0], v[2], v[4], v[6]); + case 4: + b = x << 6 | x >> 1; break; - case 13: - bit_transfer_signed(&v[1], &v[0]); - bit_transfer_signed(&v[3], &v[2]); - bit_transfer_signed(&v[5], &v[4]); - bit_transfer_signed(&v[7], &v[6]); - if (v[1] + v[3] + v[5] >= 0) - set_endpoint_clamp(data->endpoints[cem], v[0], v[2], v[4], v[6], v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7]); - else - set_endpoint_blue_clamp(data->endpoints[cem], v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7], v[0], v[2], v[4], v[6]); + case 5: + b = x << 5 | x >> 3; break; - default: - rb_raise(rb_eStandardError, "Unsupported ASTC format"); + } + ev[i] = (a & 0x80) | ((seq[i].nonbits * c + b) ^ a) >> 2; + } + break; + default: + switch (CemTableB[data->cem_range]) { + case 1: + for (int i = 0; i < data->endpoint_value_num; i++) + ev[i] = seq[i].bits * 0xff; + break; + case 2: + for (int i = 0; i < data->endpoint_value_num; i++) + ev[i] = seq[i].bits * 0x55; + break; + case 3: + for (int i = 0; i < data->endpoint_value_num; i++) + ev[i] = seq[i].bits << 5 | seq[i].bits << 2 | seq[i].bits >> 1; + break; + case 4: + for (int i = 0; i < data->endpoint_value_num; i++) + ev[i] = seq[i].bits << 4 | seq[i].bits; + break; + case 5: + for (int i = 0; i < data->endpoint_value_num; i++) + ev[i] = seq[i].bits << 3 | seq[i].bits >> 2; + break; + case 6: + for (int i = 0; i < data->endpoint_value_num; i++) + ev[i] = seq[i].bits << 2 | seq[i].bits >> 4; + break; + case 7: + for (int i = 0; i < data->endpoint_value_num; i++) + ev[i] = seq[i].bits << 1 | seq[i].bits >> 6; + break; + case 8: + for (int i = 0; i < data->endpoint_value_num; i++) + ev[i] = seq[i].bits; + break; + } + } + + int* v = ev; + for (int cem = 0; cem < data->part_num; v += (data->cem[cem] / 4 + 1) * 2, cem++) { + switch (data->cem[cem]) { + case 0: + set_endpoint(data->endpoints[cem], v[0], v[0], v[0], 255, v[1], v[1], v[1], 255); + break; + case 1: { + int l0 = (v[0] >> 2) | (v[1] & 0xc0); + int l1 = clamp(l0 + (v[1] & 0x3f)); + set_endpoint(data->endpoints[cem], l0, l0, l0, 255, l1, l1, l1, 255); + } break; + case 2: { + int y0, y1; + if (v[0] <= v[1]) { + y0 = v[0] << 4; + y1 = v[1] << 4; + } else { + y0 = (v[1] << 4) + 8; + y1 = (v[0] << 4) - 8; + } + set_endpoint_hdr(data->endpoints[cem], y0, y0, y0, 0x780, y1, y1, y1, 0x780); + } break; + case 3: { + int y0, d; + if (v[0] & 0x80) { + y0 = (v[1] & 0xe0) << 4 | (v[0] & 0x7f) << 2; + d = (v[1] & 0x1f) << 2; + } else { + y0 = (v[1] & 0xf0) << 4 | (v[0] & 0x7f) << 1; + d = (v[1] & 0x0f) << 1; + } + int y1 = clamp_hdr(y0 + d); + set_endpoint_hdr(data->endpoints[cem], y0, y0, y0, 0x780, y1, y1, y1, 0x780); + } break; + case 4: + set_endpoint(data->endpoints[cem], v[0], v[0], v[0], v[2], v[1], v[1], v[1], v[3]); + break; + case 5: + bit_transfer_signed(&v[1], &v[0]); + bit_transfer_signed(&v[3], &v[2]); + v[1] += v[0]; + set_endpoint_clamp(data->endpoints[cem], v[0], v[0], v[0], v[2], v[1], v[1], v[1], v[2] + v[3]); + break; + case 6: + set_endpoint(data->endpoints[cem], v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8, 255, v[0], v[1], v[2], 255); + break; + case 7: + decode_endpoints_hdr7(data->endpoints[cem], v); + break; + case 8: + if (v[0] + v[2] + v[4] <= v[1] + v[3] + v[5]) + set_endpoint(data->endpoints[cem], v[0], v[2], v[4], 255, v[1], v[3], v[5], 255); + else + set_endpoint_blue(data->endpoints[cem], v[1], v[3], v[5], 255, v[0], v[2], v[4], 255); + break; + case 9: + bit_transfer_signed(&v[1], &v[0]); + bit_transfer_signed(&v[3], &v[2]); + bit_transfer_signed(&v[5], &v[4]); + if (v[1] + v[3] + v[5] >= 0) + set_endpoint_clamp(data->endpoints[cem], v[0], v[2], v[4], 255, v[0] + v[1], v[2] + v[3], v[4] + v[5], 255); + else + set_endpoint_blue_clamp(data->endpoints[cem], v[0] + v[1], v[2] + v[3], v[4] + v[5], 255, v[0], v[2], v[4], 255); + break; + case 10: + set_endpoint(data->endpoints[cem], v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8, v[4], v[0], v[1], v[2], v[5]); + break; + case 11: + decode_endpoints_hdr11(data->endpoints[cem], v, 0x780, 0x780); + break; + case 12: + if (v[0] + v[2] + v[4] <= v[1] + v[3] + v[5]) + set_endpoint(data->endpoints[cem], v[0], v[2], v[4], v[6], v[1], v[3], v[5], v[7]); + else + set_endpoint_blue(data->endpoints[cem], v[1], v[3], v[5], v[7], v[0], v[2], v[4], v[6]); + break; + case 13: + bit_transfer_signed(&v[1], &v[0]); + bit_transfer_signed(&v[3], &v[2]); + bit_transfer_signed(&v[5], &v[4]); + bit_transfer_signed(&v[7], &v[6]); + if (v[1] + v[3] + v[5] >= 0) + set_endpoint_clamp(data->endpoints[cem], v[0], v[2], v[4], v[6], v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7]); + else + set_endpoint_blue_clamp(data->endpoints[cem], v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7], v[0], v[2], v[4], v[6]); + break; + case 14: + decode_endpoints_hdr11(data->endpoints[cem], v, v[6], v[7]); + break; + case 15: { + int mode = ((v[6] >> 7) & 1) | ((v[7] >> 6) & 2); + v[6] &= 0x7f; + v[7] &= 0x7f; + if (mode == 3) { + decode_endpoints_hdr11(data->endpoints[cem], v, v[6] << 5, v[7] << 5); + } else { + v[6] |= (v[7] << (mode + 1)) & 0x780; + v[7] = ((v[7] & (0x3f >> mode)) ^ (0x20 >> mode)) - (0x20 >> mode); + v[6] <<= 4 - mode; + v[7] <<= 4 - mode; + decode_endpoints_hdr11(data->endpoints[cem], v, v[6], clamp_hdr(v[6] + v[7])); + } + } break; + default: + rb_raise(rb_eStandardError, "Unsupported ASTC format"); } } } -void decode_weights(const uint8_t *buf, BlockData *data) { +void decode_weights(const uint8_t* buf, BlockData* data) +{ IntSeqData seq[128]; int wv[128] = {}; - decode_intseq(buf, 128, WeightPrecTableA[data->weight_range], WeightPrecTableB[data->weight_range], data->weight_num, 1, seq); + decode_intseq(buf, 128, WeightPrecTableA[data->weight_range], + WeightPrecTableB[data->weight_range], data->weight_num, 1, seq); if (WeightPrecTableA[data->weight_range] == 0) { switch (WeightPrecTableB[data->weight_range]) { - case 1: - for (int i = 0; i < data->weight_num; i++) - wv[i] = seq[i].bits ? 63 : 0; - break; - case 2: - for (int i = 0; i < data->weight_num; i++) - wv[i] = seq[i].bits << 4 | seq[i].bits << 2 | seq[i].bits; - break; - case 3: - for (int i = 0; i < data->weight_num; i++) - wv[i] = seq[i].bits << 3 | seq[i].bits; - break; - case 4: - for (int i = 0; i < data->weight_num; i++) - wv[i] = seq[i].bits << 2 | seq[i].bits >> 2; - break; - case 5: - for (int i = 0; i < data->weight_num; i++) - wv[i] = seq[i].bits << 1 | seq[i].bits >> 4; - break; + case 1: + for (int i = 0; i < data->weight_num; i++) + wv[i] = seq[i].bits ? 63 : 0; + break; + case 2: + for (int i = 0; i < data->weight_num; i++) + wv[i] = seq[i].bits << 4 | seq[i].bits << 2 | seq[i].bits; + break; + case 3: + for (int i = 0; i < data->weight_num; i++) + wv[i] = seq[i].bits << 3 | seq[i].bits; + break; + case 4: + for (int i = 0; i < data->weight_num; i++) + wv[i] = seq[i].bits << 2 | seq[i].bits >> 2; + break; + case 5: + for (int i = 0; i < data->weight_num; i++) + wv[i] = seq[i].bits << 1 | seq[i].bits >> 4; + break; } for (int i = 0; i < data->weight_num; i++) if (wv[i] > 32) @@ -560,35 +979,35 @@ void decode_weights(const uint8_t *buf, BlockData *data) { } else { if (WeightPrecTableA[data->weight_range] == 3) { switch (WeightPrecTableB[data->weight_range]) { - case 1: - for (int i = 0; i < data->weight_num; i++) - wv[i] = seq[i].nonbits * 50; - break; - case 2: - for (int i = 0; i < data->weight_num; i++) { - wv[i] = seq[i].nonbits * 23; - if (seq[i].bits & 2) - wv[i] += 0b1000101; - } - break; - case 3: - for (int i = 0; i < data->weight_num; i++) - wv[i] = seq[i].nonbits * 11 + ((seq[i].bits << 4 | seq[i].bits >> 1) & 0b1100011); - break; + case 1: + for (int i = 0; i < data->weight_num; i++) + wv[i] = seq[i].nonbits * 50; + break; + case 2: + for (int i = 0; i < data->weight_num; i++) { + wv[i] = seq[i].nonbits * 23; + if (seq[i].bits & 2) + wv[i] += 0b1000101; + } + break; + case 3: + for (int i = 0; i < data->weight_num; i++) + wv[i] = seq[i].nonbits * 11 + ((seq[i].bits << 4 | seq[i].bits >> 1) & 0b1100011); + break; } } else if (WeightPrecTableA[data->weight_range] == 5) { switch (WeightPrecTableB[data->weight_range]) { - case 1: - for (int i = 0; i < data->weight_num; i++) - wv[i] = seq[i].nonbits * 28; - break; - case 2: - for (int i = 0; i < data->weight_num; i++) { - wv[i] = seq[i].nonbits * 13; - if (seq[i].bits & 2) - wv[i] += 0b1000010; - } - break; + case 1: + for (int i = 0; i < data->weight_num; i++) + wv[i] = seq[i].nonbits * 28; + break; + case 2: + for (int i = 0; i < data->weight_num; i++) { + wv[i] = seq[i].nonbits * 13; + if (seq[i].bits & 2) + wv[i] += 0b1000010; + } + break; } } for (int i = 0; i < data->weight_num; i++) { @@ -626,7 +1045,8 @@ void decode_weights(const uint8_t *buf, BlockData *data) { } } -void select_partition(const uint8_t *buf, BlockData *data) { +void select_partition(const uint8_t* buf, BlockData* data) +{ int small_block = data->bw * data->bh < 31; int seed = (*(int*)buf >> 13 & 0x3ff) | (data->part_num - 1) << 10; @@ -682,51 +1102,75 @@ void select_partition(const uint8_t *buf, BlockData *data) { } } -void applicate_color(const BlockData *data, uint32_t *outbuf) { +void applicate_color(const BlockData* data, uint32_t* outbuf) +{ + static const t_select_folor_func_ptr FuncTableC[] = { + select_color, select_color, select_color_hdr, select_color_hdr, + select_color, select_color, select_color, select_color_hdr, + select_color, select_color, select_color, select_color_hdr, + select_color, select_color, select_color_hdr, select_color_hdr + }; + static const t_select_folor_func_ptr FuncTableA[] = { + select_color, select_color, select_color_hdr, select_color_hdr, + select_color, select_color, select_color, select_color_hdr, + select_color, select_color, select_color, select_color_hdr, + select_color, select_color, select_color, select_color_hdr + }; if (data->dual_plane) { int ps[] = { 0, 0, 0, 0 }; ps[data->plane_selector] = 1; if (data->part_num > 1) { for (int i = 0; i < data->bw * data->bh; i++) { int p = data->partition[i]; - uint_fast8_t r = select_color(data->endpoints[p][0], data->endpoints[p][4], data->weights[i][ps[0]]); - uint_fast8_t g = select_color(data->endpoints[p][1], data->endpoints[p][5], data->weights[i][ps[1]]); - uint_fast8_t b = select_color(data->endpoints[p][2], data->endpoints[p][6], data->weights[i][ps[2]]); - uint_fast8_t a = select_color(data->endpoints[p][3], data->endpoints[p][7], data->weights[i][ps[3]]); + uint_fast8_t r = FuncTableC[data->cem[p]](data->endpoints[p][0], data->endpoints[p][4], data->weights[i][ps[0]]); + uint_fast8_t g = FuncTableC[data->cem[p]](data->endpoints[p][1], data->endpoints[p][5], data->weights[i][ps[1]]); + uint_fast8_t b = FuncTableC[data->cem[p]](data->endpoints[p][2], data->endpoints[p][6], data->weights[i][ps[2]]); + uint_fast8_t a = FuncTableA[data->cem[p]](data->endpoints[p][3], data->endpoints[p][7], data->weights[i][ps[3]]); outbuf[i] = color(r, g, b, a); } } else { for (int i = 0; i < data->bw * data->bh; i++) { - uint_fast8_t r = select_color(data->endpoints[0][0], data->endpoints[0][4], data->weights[i][ps[0]]); - uint_fast8_t g = select_color(data->endpoints[0][1], data->endpoints[0][5], data->weights[i][ps[1]]); - uint_fast8_t b = select_color(data->endpoints[0][2], data->endpoints[0][6], data->weights[i][ps[2]]); - uint_fast8_t a = select_color(data->endpoints[0][3], data->endpoints[0][7], data->weights[i][ps[3]]); + uint_fast8_t r = FuncTableC[data->cem[0]](data->endpoints[0][0], data->endpoints[0][4], data->weights[i][ps[0]]); + uint_fast8_t g = FuncTableC[data->cem[0]](data->endpoints[0][1], data->endpoints[0][5], data->weights[i][ps[1]]); + uint_fast8_t b = FuncTableC[data->cem[0]](data->endpoints[0][2], data->endpoints[0][6], data->weights[i][ps[2]]); + uint_fast8_t a = FuncTableA[data->cem[0]](data->endpoints[0][3], data->endpoints[0][7], data->weights[i][ps[3]]); outbuf[i] = color(r, g, b, a); } } } else if (data->part_num > 1) { for (int i = 0; i < data->bw * data->bh; i++) { int p = data->partition[i]; - uint_fast8_t r = select_color(data->endpoints[p][0], data->endpoints[p][4], data->weights[i][0]); - uint_fast8_t g = select_color(data->endpoints[p][1], data->endpoints[p][5], data->weights[i][0]); - uint_fast8_t b = select_color(data->endpoints[p][2], data->endpoints[p][6], data->weights[i][0]); - uint_fast8_t a = select_color(data->endpoints[p][3], data->endpoints[p][7], data->weights[i][0]); + uint_fast8_t r = FuncTableC[data->cem[p]](data->endpoints[p][0], data->endpoints[p][4], data->weights[i][0]); + uint_fast8_t g = FuncTableC[data->cem[p]](data->endpoints[p][1], data->endpoints[p][5], data->weights[i][0]); + uint_fast8_t b = FuncTableC[data->cem[p]](data->endpoints[p][2], data->endpoints[p][6], data->weights[i][0]); + uint_fast8_t a = FuncTableA[data->cem[p]](data->endpoints[p][3], data->endpoints[p][7], data->weights[i][0]); outbuf[i] = color(r, g, b, a); } } else { for (int i = 0; i < data->bw * data->bh; i++) { - uint_fast8_t r = select_color(data->endpoints[0][0], data->endpoints[0][4], data->weights[i][0]); - uint_fast8_t g = select_color(data->endpoints[0][1], data->endpoints[0][5], data->weights[i][0]); - uint_fast8_t b = select_color(data->endpoints[0][2], data->endpoints[0][6], data->weights[i][0]); - uint_fast8_t a = select_color(data->endpoints[0][3], data->endpoints[0][7], data->weights[i][0]); + uint_fast8_t r = FuncTableC[data->cem[0]](data->endpoints[0][0], data->endpoints[0][4], data->weights[i][0]); + uint_fast8_t g = FuncTableC[data->cem[0]](data->endpoints[0][1], data->endpoints[0][5], data->weights[i][0]); + uint_fast8_t b = FuncTableC[data->cem[0]](data->endpoints[0][2], data->endpoints[0][6], data->weights[i][0]); + uint_fast8_t a = FuncTableA[data->cem[0]](data->endpoints[0][3], data->endpoints[0][7], data->weights[i][0]); outbuf[i] = color(r, g, b, a); } } } -void decode_block(const uint8_t *buf, const int bw, const int bh, uint32_t *outbuf) { +void decode_block(const uint8_t* buf, const int bw, const int bh, uint32_t* outbuf) +{ if (buf[0] == 0xfc && (buf[1] & 1) == 1) { - uint_fast32_t c = color(buf[9], buf[11], buf[13], buf[15]); + // void-extent + uint_fast32_t c; + if (buf[1] & 2) + c = color(f16ptr_to_u8(buf + 8), f16ptr_to_u8(buf + 10), f16ptr_to_u8(buf + 12), f16ptr_to_u8(buf + 14)); + else + c = color(buf[9], buf[11], buf[13], buf[15]); + for (int i = 0; i < bw * bh; i++) + outbuf[i] = c; + } else if (((buf[0] & 0xc3) == 0xc0 && (buf[1] & 1) == 1) || (buf[0] & 0xf) == 0) { + // reserved (illegal) + uint_fast32_t c = color(255, 0, 255, 255); for (int i = 0; i < bw * bh; i++) outbuf[i] = c; } else { @@ -742,19 +1186,21 @@ void decode_block(const uint8_t *buf, const int bw, const int bh, uint32_t *outb } } -void decode_astc(const uint8_t *data, const int w, const int h, const int bw, const int bh, uint32_t *image) { - int bcw = (w + bw - 1) / bw; - int bch = (h + bh - 1) / bh; - int clen_last = (w + bw - 1) % bw + 1; - uint32_t *buf = (uint32_t*)calloc(bw * bh, sizeof(uint32_t)); - const uint8_t *ptr = data; - for (int t = 0; t < bch; t++) { - for (int s = 0; s < bcw; s++, ptr += 16) { +void decode_astc(const uint8_t* data, const int w, const int h, const int bw, const int bh, uint32_t* image) +{ + const int num_blocks_x = (w + bw - 1) / bw; + const int num_blocks_y = (h + bh - 1) / bh; + const int copy_length_last = (w + bw - 1) % bw + 1; + uint32_t buf[144]; + uint32_t* buf_end = buf + bw * bh; + const uint8_t* ptr = data; + for (int by = 0; by < num_blocks_y; by++) { + for (int bx = 0, x = 0; bx < num_blocks_x; bx++, ptr += 16, x += bw) { decode_block(ptr, bw, bh, buf); - int clen = (s < bcw - 1 ? bw : clen_last) * 4; - for (int i = 0, y = h - t * bh - 1; i < bh && y >= 0; i++, y--) - memcpy(image + y * w + s * bw, buf + i * bw, clen); + int copy_length = (bx < num_blocks_x - 1 ? bw : copy_length_last) * 4; + uint32_t* b = buf; + for (int y = h - by * bh - 1; b < buf_end && y >= 0; y--, b += bw) + memcpy(image + y * w + x, b, copy_length); } } - free(buf); } diff --git a/ext/decoders/native/common.h b/ext/decoders/native/common.h new file mode 100644 index 0000000..80325c3 --- /dev/null +++ b/ext/decoders/native/common.h @@ -0,0 +1,25 @@ +#include + +/* https://github.com/ruby/ruby/blob/master/siphash.c */ + +#ifdef _WIN32 +#define BYTE_ORDER __LITTLE_ENDIAN +#elif !defined BYTE_ORDER +#include +#endif +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN __LITTLE_ENDIAN +#endif +#ifndef BIG_ENDIAN +#define BIG_ENDIAN __BIG_ENDIAN +#endif + +#if BYTE_ORDER == LITTLE_ENDIAN +#define IS_LITTLE_ENDIAN 1 +#define IS_BIG_ENDIAN 0 +#elif BYTE_ORDER == BIG_ENDIAN +#define IS_LITTLE_ENDIAN 0 +#define IS_BIG_ENDIAN 1 +#else +#error "Only strictly little or big endian supported" +#endif diff --git a/ext/decoders/native/dxtc.c b/ext/decoders/native/dxtc.c index 50db421..28a0070 100644 --- a/ext/decoders/native/dxtc.c +++ b/ext/decoders/native/dxtc.c @@ -1,12 +1,14 @@ +#include "dxtc.h" #include #include -#include "dxtc.h" -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_fast32_t r, uint_fast32_t g, uint_fast32_t b, uint_fast32_t a) +{ return r | g << 8 | b << 16 | a << 24; } -static inline void rgb565(const uint_fast16_t c, int *r, int *g, int *b) { +static inline void rgb565(const uint_fast16_t c, int* r, int* g, int* b) +{ *r = (c & 0xf800) >> 8; *g = (c & 0x07e0) >> 3; *b = (c & 0x001f) << 3; @@ -15,7 +17,8 @@ static inline void rgb565(const uint_fast16_t c, int *r, int *g, int *b) { *b |= *b >> 5; } -static inline void decode_dxt1_block(const uint64_t *data, uint32_t *outbuf) { +static inline void decode_dxt1_block(const uint64_t* data, uint32_t* outbuf) +{ int r0, g0, b0, r1, g1, b1; int q0 = ((uint16_t*)data)[0]; int q1 = ((uint16_t*)data)[1]; @@ -33,12 +36,13 @@ static inline void decode_dxt1_block(const uint64_t *data, uint32_t *outbuf) { 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 uint64_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; uint32_t buf[16]; - const uint64_t *d = data; + const uint64_t* d = data; for (int t = 0; t < bch; t++) { for (int s = 0; s < bcw; s++, d++) { decode_dxt1_block(d, buf); @@ -49,20 +53,21 @@ void decode_dxt1(const uint64_t *data, const int w, const int h, uint32_t *image } } -static inline void decode_dxt5_block(const uint64_t *data, uint32_t *outbuf) { +static inline void decode_dxt5_block(const uint64_t* data, uint32_t* outbuf) +{ uint_fast32_t a[8] = { ((uint8_t*)data)[0], ((uint8_t*)data)[1] }; if (a[0] > a[1]) { - a[2] = (a[0] * 6 + a[1] ) / 7; + a[2] = (a[0] * 6 + a[1]) / 7; a[3] = (a[0] * 5 + a[1] * 2) / 7; a[4] = (a[0] * 4 + a[1] * 3) / 7; a[5] = (a[0] * 3 + a[1] * 4) / 7; a[6] = (a[0] * 2 + a[1] * 5) / 7; - a[7] = (a[0] + a[1] * 6) / 7; + a[7] = (a[0] + a[1] * 6) / 7; } else { - a[2] = (a[0] * 4 + a[1] ) / 5; + a[2] = (a[0] * 4 + a[1]) / 5; 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[5] = (a[0] + a[1] * 4) / 5; a[7] = 255; } for (int i = 0; i < 8; i++) @@ -87,12 +92,13 @@ static inline void decode_dxt5_block(const uint64_t *data, uint32_t *outbuf) { outbuf[i] = a[da & 7] | c[dc & 3]; } -void decode_dxt5(const uint64_t *data, const int w, const int h, uint32_t *image) { +void decode_dxt5(const uint64_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; uint32_t buf[16]; - const uint64_t *d = data; + const uint64_t* d = data; for (int t = 0; t < bch; t++) { for (int s = 0; s < bcw; s++, d += 2) { decode_dxt5_block(d, buf); diff --git a/ext/decoders/native/etc.c b/ext/decoders/native/etc.c index f65dd1f..87b9052 100644 --- a/ext/decoders/native/etc.c +++ b/ext/decoders/native/etc.c @@ -1,52 +1,63 @@ +#include "etc.h" +#include "common.h" #include #include -#include "etc.h" -uint_fast8_t WriteOrderTable[16] = { 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 }; -uint_fast8_t WriteOrderTableRev[16] = { 15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0 }; -uint_fast8_t Etc1ModifierTable[8][2] = {{2, 8}, {5, 17}, {9, 29}, {13, 42}, {18, 60}, {24, 80}, {33, 106}, {47, 183}}; -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}}; -uint_fast8_t Etc2DistanceTable[8] = {3, 6, 11, 16, 23, 32, 41, 64}; -int_fast8_t Etc2AlphaModTable[16][8] = { - {-3, -6, -9, -15, 2, 5, 8, 14}, - {-3, -7, -10, -13, 2, 6, 9, 12}, - {-2, -5, -8, -13, 1, 4, 7, 12}, - {-2, -4, -6, -13, 1, 3, 5, 12}, - {-3, -6, -8, -12, 2, 5, 7, 11}, - {-3, -7, -9, -11, 2, 6, 8, 10}, - {-4, -7, -8, -11, 3, 6, 7, 10}, - {-3, -5, -8, -11, 2, 4, 7, 10}, - {-2, -6, -8, -10, 1, 5, 7, 9}, - {-2, -5, -8, -10, 1, 4, 7, 9}, - {-2, -4, -8, -10, 1, 3, 7, 9}, - {-2, -5, -7, -10, 1, 4, 6, 9}, - {-3, -4, -7, -10, 2, 3, 6, 9}, - {-1, -2, -3, -10, 0, 1, 2, 9}, - {-4, -6, -8, -9, 3, 5, 7, 8}, - {-3, -5, -7, -9, 2, 4, 6, 8} +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 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] = { + { -3, -6, -9, -15, 2, 5, 8, 14 }, + { -3, -7, -10, -13, 2, 6, 9, 12 }, + { -2, -5, -8, -13, 1, 4, 7, 12 }, + { -2, -4, -6, -13, 1, 3, 5, 12 }, + { -3, -6, -8, -12, 2, 5, 7, 11 }, + { -3, -7, -9, -11, 2, 6, 8, 10 }, + { -4, -7, -8, -11, 3, 6, 7, 10 }, + { -3, -5, -8, -11, 2, 4, 7, 10 }, + { -2, -6, -8, -10, 1, 5, 7, 9 }, + { -2, -5, -8, -10, 1, 4, 7, 9 }, + { -2, -4, -8, -10, 1, 3, 7, 9 }, + { -2, -5, -7, -10, 1, 4, 6, 9 }, + { -3, -4, -7, -10, 2, 3, 6, 9 }, + { -1, -2, -3, -10, 0, 1, 2, 9 }, + { -4, -6, -8, -9, 3, 5, 7, 8 }, + { -3, -5, -7, -9, 2, 4, 6, 8 } }; -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 uint_fast8_t clamp(const int n) { +static inline uint_fast8_t clamp(const int n) +{ return n < 0 ? 0 : n > 255 ? 255 : n; } -static inline uint32_t applicate_color(uint_fast8_t c[3], int_fast16_t m) { +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_raw(uint_fast8_t c[3]) { +static inline uint32_t applicate_color_raw(uint_fast8_t c[3]) +{ return color(c[0], c[1], c[2], 255); } -static inline void decode_etc1_block(const uint8_t *data, uint32_t *outbuf) { - uint_fast8_t code[2] = { data[3] >> 5, data[3] >> 2 & 7 }; - uint_fast8_t *table = Etc1SubblockTable[data[3] & 1]; +static inline void decode_etc1_block(const uint8_t* data, uint32_t* outbuf) +{ + const uint_fast8_t code[2] = { data[3] >> 5, data[3] >> 2 & 7 }; // Table codewords + const uint_fast8_t* table = Etc1SubblockTable[data[3] & 1]; uint_fast8_t c[2][3]; if (data[3] & 2) { + // diff bit == 1 c[0][0] = data[0] & 0xf8; c[0][1] = data[1] & 0xf8; c[0][2] = data[2] & 0xf8; @@ -60,16 +71,17 @@ static inline void decode_etc1_block(const uint8_t *data, uint32_t *outbuf) { c[1][1] |= c[1][1] >> 5; c[1][2] |= c[1][2] >> 5; } else { - c[0][0] = data[0] & 0xf0 | data[0] >> 4; - c[1][0] = data[0] & 0x0f | data[0] << 4; - c[0][1] = data[1] & 0xf0 | data[1] >> 4; - c[1][1] = data[1] & 0x0f | data[1] << 4; - c[0][2] = data[2] & 0xf0 | data[2] >> 4; - c[1][2] = data[2] & 0x0f | data[2] << 4; + // diff bit == 0 + c[0][0] = (data[0] & 0xf0) | data[0] >> 4; + c[1][0] = (data[0] & 0x0f) | data[0] << 4; + c[0][1] = (data[1] & 0xf0) | data[1] >> 4; + c[1][1] = (data[1] & 0x0f) | data[1] << 4; + c[0][2] = (data[2] & 0xf0) | data[2] >> 4; + c[1][2] = (data[2] & 0x0f) | data[2] << 4; } - uint_fast16_t j = data[6] << 8 | data[7]; - uint_fast16_t k = data[4] << 8 | data[5]; + uint_fast16_t j = data[6] << 8 | data[7]; // less significant pixel index bits + uint_fast16_t k = data[4] << 8 | data[5]; // more significant pixel index bits for (int i = 0; i < 16; i++, j >>= 1, k >>= 1) { uint_fast8_t s = table[i]; uint_fast8_t m = Etc1ModifierTable[code[s]][j & 1]; @@ -77,28 +89,33 @@ static inline void decode_etc1_block(const uint8_t *data, uint32_t *outbuf) { } } -void decode_etc1(const void *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; +void decode_etc1(const void* data, const int w, const int h, uint32_t* image) +{ + 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 uint8_t *d = (uint8_t*)data; - for (int t = 0; t < bch; t++) { - for (int s = 0; s < bcw; s++, d += 8) { + 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 += 8, x += 4) { decode_etc1_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 = (bx < num_blocks_x - 1 ? 4 : copy_length_last) * 4; + uint32_t* b = buf; + for (int y = h - 1 - by * 4; b < buf_end && y >= 0; y--, b += 4) + memcpy(image + y * w + x, b, copy_length); } } } -static inline void decode_etc2_block(const uint8_t *data, uint32_t *outbuf) { - uint_fast16_t j = data[6] << 8 | data[7]; - uint_fast16_t k = data[4] << 8 | data[5]; +static inline void decode_etc2_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] = {}; if (data[3] & 2) { + // 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; @@ -107,57 +124,49 @@ static inline void decode_etc2_block(const uint8_t *data, uint32_t *outbuf) { 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; - 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) - }; + 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) - outbuf[WriteOrderTable[i]] = color_set[k << 1 & 2 | j & 1]; + outbuf[WriteOrderTable[i]] = color_set[(k & 2) | (j & 1)]; } 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][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] = (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][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; + 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) - }; + 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) - outbuf[WriteOrderTable[i]] = color_set[k << 1 & 2 | j & 1]; + outbuf[WriteOrderTable[i]] = color_set[(k & 2) | (j & 1)]; } 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][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][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; + 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); @@ -168,8 +177,8 @@ static inline void decode_etc2_block(const uint8_t *data, uint32_t *outbuf) { } } else { // differential - uint_fast8_t code[2] = { data[3] >> 5, data[3] >> 2 & 7 }; - uint_fast8_t *table = Etc1SubblockTable[data[3] & 1]; + 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; @@ -186,15 +195,15 @@ static inline void decode_etc2_block(const uint8_t *data, uint32_t *outbuf) { } } } else { - // individual - uint_fast8_t code[2] = { data[3] >> 5, data[3] >> 2 & 7 }; - uint_fast8_t *table = Etc1SubblockTable[data[3] & 1]; - c[0][0] = data[0] & 0xf0 | data[0] >> 4; - c[1][0] = data[0] & 0x0f | data[0] << 4; - c[0][1] = data[1] & 0xf0 | data[1] >> 4; - c[1][1] = data[1] & 0x0f | data[1] << 4; - c[0][2] = data[2] & 0xf0 | data[2] >> 4; - c[1][2] = data[2] & 0x0f | data[2] << 4; + // individual (diff bit == 0) + 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] = (data[0] & 0xf0) | data[0] >> 4; + c[1][0] = (data[0] & 0x0f) | data[0] << 4; + c[0][1] = (data[1] & 0xf0) | data[1] >> 4; + c[1][1] = (data[1] & 0x0f) | data[1] << 4; + c[0][2] = (data[2] & 0xf0) | data[2] >> 4; + c[1][2] = (data[2] & 0x0f) | data[2] << 4; for (int i = 0; i < 16; i++, j >>= 1, k >>= 1) { uint_fast8_t s = table[i]; uint_fast8_t m = Etc1ModifierTable[code[s]][j & 1]; @@ -203,69 +212,78 @@ static inline void decode_etc2_block(const uint8_t *data, uint32_t *outbuf) { } } -static inline void decode_etc2a8_block(const uint8_t *data, uint32_t *outbuf) { +static inline void decode_etc2a8_block(const uint8_t* data, uint32_t* outbuf) +{ if (data[1] & 0xf0) { - uint_fast8_t mult = data[1] >> 4; - int_fast8_t *table = Etc2AlphaModTable[data[1] & 0xf]; - uint_fast64_t l = - data[7] | (uint_fast16_t)data[6] << 8 | - (uint_fast32_t)data[5] << 16 | (uint_fast32_t)data[4] << 24 | - (uint_fast64_t)data[3] << 32 | (uint_fast64_t)data[2] << 40; + // multiplier != 0 + const uint_fast8_t multiplier = data[1] >> 4; + const int_fast8_t* table = Etc2AlphaModTable[data[1] & 0xf]; + uint_fast64_t l = data[7] | (uint_fast16_t)data[6] << 8 | (uint_fast32_t)data[5] << 16 | (uint_fast32_t)data[4] << 24 | (uint_fast64_t)data[3] << 32 | (uint_fast64_t)data[2] << 40; for (int i = 0; i < 16; i++, l >>= 3) - ((uint8_t*)(outbuf + WriteOrderTableRev[i]))[3] = clamp(data[0] + mult * table[l & 7]); + ((uint8_t*)(outbuf + WriteOrderTableRev[i]))[3] = clamp(data[0] + multiplier * table[l & 7]); } else { - for (int i = 0; i < 16; i++) - ((uint8_t*)(outbuf + i))[3] = data[0]; + // multiplier == 0 (always same as base codeword) + for (int i = 0; i < 16; i++, outbuf++) + ((uint8_t*)outbuf)[3] = data[0]; } } -void decode_etc2(const void *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; +void decode_etc2(const void* data, const int w, const int h, uint32_t* image) +{ + 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 uint8_t *d = (uint8_t*)data; - for (int t = 0; t < bch; t++) { - for (int s = 0; s < bcw; s++, d += 8) { + 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 += 8, x += 4) { decode_etc2_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 = (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) + memcpy(image + y * w + x, b, copy_length); } } } -void decode_etc2a1(const void *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; +void decode_etc2a1(const void* data, const int w, const int h, uint32_t* image) +{ + 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 uint8_t *d = (uint8_t*)data; - for (int t = 0; t < bch; t++) { - for (int s = 0; s < bcw; s++, d += 9) { + 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]; - 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 = (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) + memcpy(image + y * w + x, b, copy_length); } } } -void decode_etc2a8(const void *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; +void decode_etc2a8(const void* data, const int w, const int h, uint32_t* image) +{ + 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 uint8_t *d = (uint8_t*)data; - for (int t = 0; t < bch; t++) { - for (int s = 0; s < bcw; s++, d += 16) { + 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 += 16, x += 4) { decode_etc2_block(d + 8, buf); decode_etc2a8_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 = (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) + memcpy(image + y * w + x, b, copy_length); } } } diff --git a/ext/decoders/native/fp16.h b/ext/decoders/native/fp16.h new file mode 100644 index 0000000..4721740 --- /dev/null +++ b/ext/decoders/native/fp16.h @@ -0,0 +1,40 @@ +#pragma once +#ifndef FP16_H +#define FP16_H + +#include "fp16/fp16.h" + +#endif /* FP16_H */ + +/** + * + * License Information + * + * FP16 library is derived from https://github.com/Maratyszcza/FP16. + * The library is licensed under the MIT License shown below. + +The MIT License (MIT) + +Copyright (c) 2017 Facebook Inc. +Copyright (c) 2017 Georgia Institute of Technology +Copyright 2019 Google LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + **/ diff --git a/ext/decoders/native/fp16/bitcasts.h b/ext/decoders/native/fp16/bitcasts.h new file mode 100644 index 0000000..26a755c --- /dev/null +++ b/ext/decoders/native/fp16/bitcasts.h @@ -0,0 +1,76 @@ +#pragma once +#ifndef FP16_BITCASTS_H +#define FP16_BITCASTS_H + +#if defined(__cplusplus) && (__cplusplus >= 201103L) + #include +#elif !defined(__OPENCL_VERSION__) + #include +#endif + + +static inline float fp32_from_bits(uint32_t w) { +#if defined(__OPENCL_VERSION__) + return as_float(w); +#elif defined(__CUDA_ARCH__) + return __uint_as_float((unsigned int) w); +#elif defined(__INTEL_COMPILER) + return _castu32_f32(w); +#else + union { + uint32_t as_bits; + float as_value; + } fp32 = { w }; + return fp32.as_value; +#endif +} + +static inline uint32_t fp32_to_bits(float f) { +#if defined(__OPENCL_VERSION__) + return as_uint(f); +#elif defined(__CUDA_ARCH__) + return (uint32_t) __float_as_uint(f); +#elif defined(__INTEL_COMPILER) + return _castf32_u32(f); +#else + union { + float as_value; + uint32_t as_bits; + } fp32 = { f }; + return fp32.as_bits; +#endif +} + +static inline double fp64_from_bits(uint64_t w) { +#if defined(__OPENCL_VERSION__) + return as_double(w); +#elif defined(__CUDA_ARCH__) + return __longlong_as_double((long long) w); +#elif defined(__INTEL_COMPILER) + return _castu64_f64(w); +#else + union { + uint64_t as_bits; + double as_value; + } fp64 = { w }; + return fp64.as_value; +#endif +} + +static inline uint64_t fp64_to_bits(double f) { +#if defined(__OPENCL_VERSION__) + return as_ulong(f); +#elif defined(__CUDA_ARCH__) + return (uint64_t) __double_as_longlong(f); +#elif defined(__INTEL_COMPILER) + return _castf64_u64(f); +#else + union { + double as_value; + uint64_t as_bits; + } fp64 = { f }; + return fp64.as_bits; +#endif +} + +#endif /* FP16_BITCASTS_H */ diff --git a/ext/decoders/native/fp16/fp16.h b/ext/decoders/native/fp16/fp16.h new file mode 100644 index 0000000..642f2f8 --- /dev/null +++ b/ext/decoders/native/fp16/fp16.h @@ -0,0 +1,451 @@ +#pragma once +#ifndef FP16_FP16_H +#define FP16_FP16_H + +#if defined(__cplusplus) && (__cplusplus >= 201103L) + #include + #include +#elif !defined(__OPENCL_VERSION__) + #include + #include +#endif + +#ifdef _MSC_VER + #include +#endif + +#include "fp16/bitcasts.h" + + +/* + * Convert a 16-bit floating-point number in IEEE half-precision format, in bit representation, to + * a 32-bit floating-point number in IEEE single-precision format, in bit representation. + * + * @note The implementation doesn't use any floating-point operations. + */ +static inline uint32_t fp16_ieee_to_fp32_bits(uint16_t h) { + /* + * Extend the half-precision floating-point number to 32 bits and shift to the upper part of the 32-bit word: + * +---+-----+------------+-------------------+ + * | S |EEEEE|MM MMMM MMMM|0000 0000 0000 0000| + * +---+-----+------------+-------------------+ + * Bits 31 26-30 16-25 0-15 + * + * S - sign bit, E - bits of the biased exponent, M - bits of the mantissa, 0 - zero bits. + */ + const uint32_t w = (uint32_t) h << 16; + /* + * Extract the sign of the input number into the high bit of the 32-bit word: + * + * +---+----------------------------------+ + * | S |0000000 00000000 00000000 00000000| + * +---+----------------------------------+ + * Bits 31 0-31 + */ + const uint32_t sign = w & UINT32_C(0x80000000); + /* + * Extract mantissa and biased exponent of the input number into the bits 0-30 of the 32-bit word: + * + * +---+-----+------------+-------------------+ + * | 0 |EEEEE|MM MMMM MMMM|0000 0000 0000 0000| + * +---+-----+------------+-------------------+ + * Bits 30 27-31 17-26 0-16 + */ + const uint32_t nonsign = w & UINT32_C(0x7FFFFFFF); + /* + * Renorm shift is the number of bits to shift mantissa left to make the half-precision number normalized. + * If the initial number is normalized, some of its high 6 bits (sign == 0 and 5-bit exponent) equals one. + * In this case renorm_shift == 0. If the number is denormalize, renorm_shift > 0. Note that if we shift + * denormalized nonsign by renorm_shift, the unit bit of mantissa will shift into exponent, turning the + * biased exponent into 1, and making mantissa normalized (i.e. without leading 1). + */ +#ifdef _MSC_VER + unsigned long nonsign_bsr; + _BitScanReverse(&nonsign_bsr, (unsigned long) nonsign); + uint32_t renorm_shift = (uint32_t) nonsign_bsr ^ 31; +#else + uint32_t renorm_shift = __builtin_clz(nonsign); +#endif + renorm_shift = renorm_shift > 5 ? renorm_shift - 5 : 0; + /* + * Iff half-precision number has exponent of 15, the addition overflows it into bit 31, + * and the subsequent shift turns the high 9 bits into 1. Thus + * inf_nan_mask == + * 0x7F800000 if the half-precision number had exponent of 15 (i.e. was NaN or infinity) + * 0x00000000 otherwise + */ + const int32_t inf_nan_mask = ((int32_t) (nonsign + 0x04000000) >> 8) & INT32_C(0x7F800000); + /* + * Iff nonsign is 0, it overflows into 0xFFFFFFFF, turning bit 31 into 1. Otherwise, bit 31 remains 0. + * The signed shift right by 31 broadcasts bit 31 into all bits of the zero_mask. Thus + * zero_mask == + * 0xFFFFFFFF if the half-precision number was zero (+0.0h or -0.0h) + * 0x00000000 otherwise + */ + const int32_t zero_mask = (int32_t) (nonsign - 1) >> 31; + /* + * 1. Shift nonsign left by renorm_shift to normalize it (if the input was denormal) + * 2. Shift nonsign right by 3 so the exponent (5 bits originally) becomes an 8-bit field and 10-bit mantissa + * shifts into the 10 high bits of the 23-bit mantissa of IEEE single-precision number. + * 3. Add 0x70 to the exponent (starting at bit 23) to compensate the different in exponent bias + * (0x7F for single-precision number less 0xF for half-precision number). + * 4. Subtract renorm_shift from the exponent (starting at bit 23) to account for renormalization. As renorm_shift + * is less than 0x70, this can be combined with step 3. + * 5. Binary OR with inf_nan_mask to turn the exponent into 0xFF if the input was NaN or infinity. + * 6. Binary ANDNOT with zero_mask to turn the mantissa and exponent into zero if the input was zero. + * 7. Combine with the sign of the input number. + */ + return sign | ((((nonsign << renorm_shift >> 3) + ((0x70 - renorm_shift) << 23)) | inf_nan_mask) & ~zero_mask); +} + +/* + * Convert a 16-bit floating-point number in IEEE half-precision format, in bit representation, to + * a 32-bit floating-point number in IEEE single-precision format. + * + * @note The implementation relies on IEEE-like (no assumption about rounding mode and no operations on denormals) + * floating-point operations and bitcasts between integer and floating-point variables. + */ +static inline float fp16_ieee_to_fp32_value(uint16_t h) { + /* + * Extend the half-precision floating-point number to 32 bits and shift to the upper part of the 32-bit word: + * +---+-----+------------+-------------------+ + * | S |EEEEE|MM MMMM MMMM|0000 0000 0000 0000| + * +---+-----+------------+-------------------+ + * Bits 31 26-30 16-25 0-15 + * + * S - sign bit, E - bits of the biased exponent, M - bits of the mantissa, 0 - zero bits. + */ + const uint32_t w = (uint32_t) h << 16; + /* + * Extract the sign of the input number into the high bit of the 32-bit word: + * + * +---+----------------------------------+ + * | S |0000000 00000000 00000000 00000000| + * +---+----------------------------------+ + * Bits 31 0-31 + */ + const uint32_t sign = w & UINT32_C(0x80000000); + /* + * Extract mantissa and biased exponent of the input number into the high bits of the 32-bit word: + * + * +-----+------------+---------------------+ + * |EEEEE|MM MMMM MMMM|0 0000 0000 0000 0000| + * +-----+------------+---------------------+ + * Bits 27-31 17-26 0-16 + */ + const uint32_t two_w = w + w; + + /* + * Shift mantissa and exponent into bits 23-28 and bits 13-22 so they become mantissa and exponent + * of a single-precision floating-point number: + * + * S|Exponent | Mantissa + * +-+---+-----+------------+----------------+ + * |0|000|EEEEE|MM MMMM MMMM|0 0000 0000 0000| + * +-+---+-----+------------+----------------+ + * Bits | 23-31 | 0-22 + * + * Next, there are some adjustments to the exponent: + * - The exponent needs to be corrected by the difference in exponent bias between single-precision and half-precision + * formats (0x7F - 0xF = 0x70) + * - Inf and NaN values in the inputs should become Inf and NaN values after conversion to the single-precision number. + * Therefore, if the biased exponent of the half-precision input was 0x1F (max possible value), the biased exponent + * of the single-precision output must be 0xFF (max possible value). We do this correction in two steps: + * - First, we adjust the exponent by (0xFF - 0x1F) = 0xE0 (see exp_offset below) rather than by 0x70 suggested + * by the difference in the exponent bias (see above). + * - Then we multiply the single-precision result of exponent adjustment by 2**(-112) to reverse the effect of + * exponent adjustment by 0xE0 less the necessary exponent adjustment by 0x70 due to difference in exponent bias. + * The floating-point multiplication hardware would ensure than Inf and NaN would retain their value on at least + * partially IEEE754-compliant implementations. + * + * Note that the above operations do not handle denormal inputs (where biased exponent == 0). However, they also do not + * operate on denormal inputs, and do not produce denormal results. + */ + const uint32_t exp_offset = UINT32_C(0xE0) << 23; +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) || defined(__GNUC__) && !defined(__STRICT_ANSI__) + const float exp_scale = 0x1.0p-112f; +#else + const float exp_scale = fp32_from_bits(UINT32_C(0x7800000)); +#endif + const float normalized_value = fp32_from_bits((two_w >> 4) + exp_offset) * exp_scale; + + /* + * Convert denormalized half-precision inputs into single-precision results (always normalized). + * Zero inputs are also handled here. + * + * In a denormalized number the biased exponent is zero, and mantissa has on-zero bits. + * First, we shift mantissa into bits 0-9 of the 32-bit word. + * + * zeros | mantissa + * +---------------------------+------------+ + * |0000 0000 0000 0000 0000 00|MM MMMM MMMM| + * +---------------------------+------------+ + * Bits 10-31 0-9 + * + * Now, remember that denormalized half-precision numbers are represented as: + * FP16 = mantissa * 2**(-24). + * The trick is to construct a normalized single-precision number with the same mantissa and thehalf-precision input + * and with an exponent which would scale the corresponding mantissa bits to 2**(-24). + * A normalized single-precision floating-point number is represented as: + * FP32 = (1 + mantissa * 2**(-23)) * 2**(exponent - 127) + * Therefore, when the biased exponent is 126, a unit change in the mantissa of the input denormalized half-precision + * number causes a change of the constructud single-precision number by 2**(-24), i.e. the same ammount. + * + * The last step is to adjust the bias of the constructed single-precision number. When the input half-precision number + * is zero, the constructed single-precision number has the value of + * FP32 = 1 * 2**(126 - 127) = 2**(-1) = 0.5 + * Therefore, we need to subtract 0.5 from the constructed single-precision number to get the numerical equivalent of + * the input half-precision number. + */ + const uint32_t magic_mask = UINT32_C(126) << 23; + const float magic_bias = 0.5f; + const float denormalized_value = fp32_from_bits((two_w >> 17) | magic_mask) - magic_bias; + + /* + * - Choose either results of conversion of input as a normalized number, or as a denormalized number, depending on the + * input exponent. The variable two_w contains input exponent in bits 27-31, therefore if its smaller than 2**27, the + * input is either a denormal number, or zero. + * - Combine the result of conversion of exponent and mantissa with the sign of the input number. + */ + const uint32_t denormalized_cutoff = UINT32_C(1) << 27; + const uint32_t result = sign | + (two_w < denormalized_cutoff ? fp32_to_bits(denormalized_value) : fp32_to_bits(normalized_value)); + return fp32_from_bits(result); +} + +/* + * Convert a 32-bit floating-point number in IEEE single-precision format to a 16-bit floating-point number in + * IEEE half-precision format, in bit representation. + * + * @note The implementation relies on IEEE-like (no assumption about rounding mode and no operations on denormals) + * floating-point operations and bitcasts between integer and floating-point variables. + */ +static inline uint16_t fp16_ieee_from_fp32_value(float f) { +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) || defined(__GNUC__) && !defined(__STRICT_ANSI__) + const float scale_to_inf = 0x1.0p+112f; + const float scale_to_zero = 0x1.0p-110f; +#else + const float scale_to_inf = fp32_from_bits(UINT32_C(0x77800000)); + const float scale_to_zero = fp32_from_bits(UINT32_C(0x08800000)); +#endif + float base = (fabsf(f) * scale_to_inf) * scale_to_zero; + + const uint32_t w = fp32_to_bits(f); + const uint32_t shl1_w = w + w; + const uint32_t sign = w & UINT32_C(0x80000000); + uint32_t bias = shl1_w & UINT32_C(0xFF000000); + if (bias < UINT32_C(0x71000000)) { + bias = UINT32_C(0x71000000); + } + + base = fp32_from_bits((bias >> 1) + UINT32_C(0x07800000)) + base; + const uint32_t bits = fp32_to_bits(base); + const uint32_t exp_bits = (bits >> 13) & UINT32_C(0x00007C00); + const uint32_t mantissa_bits = bits & UINT32_C(0x00000FFF); + const uint32_t nonsign = exp_bits + mantissa_bits; + return (sign >> 16) | (shl1_w > UINT32_C(0xFF000000) ? UINT16_C(0x7E00) : nonsign); +} + +/* + * Convert a 16-bit floating-point number in ARM alternative half-precision format, in bit representation, to + * a 32-bit floating-point number in IEEE single-precision format, in bit representation. + * + * @note The implementation doesn't use any floating-point operations. + */ +static inline uint32_t fp16_alt_to_fp32_bits(uint16_t h) { + /* + * Extend the half-precision floating-point number to 32 bits and shift to the upper part of the 32-bit word: + * +---+-----+------------+-------------------+ + * | S |EEEEE|MM MMMM MMMM|0000 0000 0000 0000| + * +---+-----+------------+-------------------+ + * Bits 31 26-30 16-25 0-15 + * + * S - sign bit, E - bits of the biased exponent, M - bits of the mantissa, 0 - zero bits. + */ + const uint32_t w = (uint32_t) h << 16; + /* + * Extract the sign of the input number into the high bit of the 32-bit word: + * + * +---+----------------------------------+ + * | S |0000000 00000000 00000000 00000000| + * +---+----------------------------------+ + * Bits 31 0-31 + */ + const uint32_t sign = w & UINT32_C(0x80000000); + /* + * Extract mantissa and biased exponent of the input number into the bits 0-30 of the 32-bit word: + * + * +---+-----+------------+-------------------+ + * | 0 |EEEEE|MM MMMM MMMM|0000 0000 0000 0000| + * +---+-----+------------+-------------------+ + * Bits 30 27-31 17-26 0-16 + */ + const uint32_t nonsign = w & UINT32_C(0x7FFFFFFF); + /* + * Renorm shift is the number of bits to shift mantissa left to make the half-precision number normalized. + * If the initial number is normalized, some of its high 6 bits (sign == 0 and 5-bit exponent) equals one. + * In this case renorm_shift == 0. If the number is denormalize, renorm_shift > 0. Note that if we shift + * denormalized nonsign by renorm_shift, the unit bit of mantissa will shift into exponent, turning the + * biased exponent into 1, and making mantissa normalized (i.e. without leading 1). + */ +#ifdef _MSC_VER + unsigned long nonsign_bsr; + _BitScanReverse(&nonsign_bsr, (unsigned long) nonsign); + uint32_t renorm_shift = (uint32_t) nonsign_bsr ^ 31; +#else + uint32_t renorm_shift = __builtin_clz(nonsign); +#endif + renorm_shift = renorm_shift > 5 ? renorm_shift - 5 : 0; + /* + * Iff nonsign is 0, it overflows into 0xFFFFFFFF, turning bit 31 into 1. Otherwise, bit 31 remains 0. + * The signed shift right by 31 broadcasts bit 31 into all bits of the zero_mask. Thus + * zero_mask == + * 0xFFFFFFFF if the half-precision number was zero (+0.0h or -0.0h) + * 0x00000000 otherwise + */ + const int32_t zero_mask = (int32_t) (nonsign - 1) >> 31; + /* + * 1. Shift nonsign left by renorm_shift to normalize it (if the input was denormal) + * 2. Shift nonsign right by 3 so the exponent (5 bits originally) becomes an 8-bit field and 10-bit mantissa + * shifts into the 10 high bits of the 23-bit mantissa of IEEE single-precision number. + * 3. Add 0x70 to the exponent (starting at bit 23) to compensate the different in exponent bias + * (0x7F for single-precision number less 0xF for half-precision number). + * 4. Subtract renorm_shift from the exponent (starting at bit 23) to account for renormalization. As renorm_shift + * is less than 0x70, this can be combined with step 3. + * 5. Binary ANDNOT with zero_mask to turn the mantissa and exponent into zero if the input was zero. + * 6. Combine with the sign of the input number. + */ + return sign | (((nonsign << renorm_shift >> 3) + ((0x70 - renorm_shift) << 23)) & ~zero_mask); +} + +/* + * Convert a 16-bit floating-point number in ARM alternative half-precision format, in bit representation, to + * a 32-bit floating-point number in IEEE single-precision format. + * + * @note The implementation relies on IEEE-like (no assumption about rounding mode and no operations on denormals) + * floating-point operations and bitcasts between integer and floating-point variables. + */ +static inline float fp16_alt_to_fp32_value(uint16_t h) { + /* + * Extend the half-precision floating-point number to 32 bits and shift to the upper part of the 32-bit word: + * +---+-----+------------+-------------------+ + * | S |EEEEE|MM MMMM MMMM|0000 0000 0000 0000| + * +---+-----+------------+-------------------+ + * Bits 31 26-30 16-25 0-15 + * + * S - sign bit, E - bits of the biased exponent, M - bits of the mantissa, 0 - zero bits. + */ + const uint32_t w = (uint32_t) h << 16; + /* + * Extract the sign of the input number into the high bit of the 32-bit word: + * + * +---+----------------------------------+ + * | S |0000000 00000000 00000000 00000000| + * +---+----------------------------------+ + * Bits 31 0-31 + */ + const uint32_t sign = w & UINT32_C(0x80000000); + /* + * Extract mantissa and biased exponent of the input number into the high bits of the 32-bit word: + * + * +-----+------------+---------------------+ + * |EEEEE|MM MMMM MMMM|0 0000 0000 0000 0000| + * +-----+------------+---------------------+ + * Bits 27-31 17-26 0-16 + */ + const uint32_t two_w = w + w; + + /* + * Shift mantissa and exponent into bits 23-28 and bits 13-22 so they become mantissa and exponent + * of a single-precision floating-point number: + * + * S|Exponent | Mantissa + * +-+---+-----+------------+----------------+ + * |0|000|EEEEE|MM MMMM MMMM|0 0000 0000 0000| + * +-+---+-----+------------+----------------+ + * Bits | 23-31 | 0-22 + * + * Next, the exponent is adjusted for the difference in exponent bias between single-precision and half-precision + * formats (0x7F - 0xF = 0x70). This operation never overflows or generates non-finite values, as the largest + * half-precision exponent is 0x1F and after the adjustment is can not exceed 0x8F < 0xFE (largest single-precision + * exponent for non-finite values). + * + * Note that this operation does not handle denormal inputs (where biased exponent == 0). However, they also do not + * operate on denormal inputs, and do not produce denormal results. + */ + const float exp_offset = UINT32_C(0x70) << 23; + const float normalized_value = fp32_from_bits((two_w >> 4) + exp_offset); + + /* + * Convert denormalized half-precision inputs into single-precision results (always normalized). + * Zero inputs are also handled here. + * + * In a denormalized number the biased exponent is zero, and mantissa has on-zero bits. + * First, we shift mantissa into bits 0-9 of the 32-bit word. + * + * zeros | mantissa + * +---------------------------+------------+ + * |0000 0000 0000 0000 0000 00|MM MMMM MMMM| + * +---------------------------+------------+ + * Bits 10-31 0-9 + * + * Now, remember that denormalized half-precision numbers are represented as: + * FP16 = mantissa * 2**(-24). + * The trick is to construct a normalized single-precision number with the same mantissa and thehalf-precision input + * and with an exponent which would scale the corresponding mantissa bits to 2**(-24). + * A normalized single-precision floating-point number is represented as: + * FP32 = (1 + mantissa * 2**(-23)) * 2**(exponent - 127) + * Therefore, when the biased exponent is 126, a unit change in the mantissa of the input denormalized half-precision + * number causes a change of the constructud single-precision number by 2**(-24), i.e. the same ammount. + * + * The last step is to adjust the bias of the constructed single-precision number. When the input half-precision number + * is zero, the constructed single-precision number has the value of + * FP32 = 1 * 2**(126 - 127) = 2**(-1) = 0.5 + * Therefore, we need to subtract 0.5 from the constructed single-precision number to get the numerical equivalent of + * the input half-precision number. + */ + const uint32_t magic_mask = UINT32_C(126) << 23; + const float magic_bias = 0.5f; + const float denormalized_value = fp32_from_bits((two_w >> 17) | magic_mask) - magic_bias; + + /* + * - Choose either results of conversion of input as a normalized number, or as a denormalized number, depending on the + * input exponent. The variable two_w contains input exponent in bits 27-31, therefore if its smaller than 2**27, the + * input is either a denormal number, or zero. + * - Combine the result of conversion of exponent and mantissa with the sign of the input number. + */ + const uint32_t denormalized_cutoff = UINT32_C(1) << 27; + const uint32_t result = sign | + (two_w < denormalized_cutoff ? fp32_to_bits(denormalized_value) : fp32_to_bits(normalized_value)); + return fp32_from_bits(result); +} + +/* + * Convert a 32-bit floating-point number in IEEE single-precision format to a 16-bit floating-point number in + * ARM alternative half-precision format, in bit representation. + * + * @note The implementation relies on IEEE-like (no assumption about rounding mode and no operations on denormals) + * floating-point operations and bitcasts between integer and floating-point variables. + */ +static inline uint16_t fp16_alt_from_fp32_value(float f) { + const uint32_t w = fp32_to_bits(f); + const uint32_t sign = w & UINT32_C(0x80000000); + const uint32_t shl1_w = w + w; + + const uint32_t shl1_max_fp16_fp32 = UINT32_C(0x8FFFC000); + const uint32_t shl1_base = shl1_w > shl1_max_fp16_fp32 ? shl1_max_fp16_fp32 : shl1_w; + uint32_t shl1_bias = shl1_base & UINT32_C(0xFF000000); + const uint32_t exp_difference = 23 - 10; + const uint32_t shl1_bias_min = (127 - 1 - exp_difference) << 24; + if (shl1_bias < shl1_bias_min) { + shl1_bias = shl1_bias_min; + } + + const float bias = fp32_from_bits((shl1_bias >> 1) + ((exp_difference + 2) << 23)); + const float base = fp32_from_bits((shl1_base >> 1) + (2 << 23)) + bias; + + const uint32_t exp_f = fp32_to_bits(base) >> 13; + return (sign >> 16) | ((exp_f & UINT32_C(0x00007C00)) + (fp32_to_bits(base) & UINT32_C(0x00000FFF))); +} + +#endif /* FP16_FP16_H */ diff --git a/ext/decoders/native/main.c b/ext/decoders/native/main.c index 046f7e9..1bfc65d 100644 --- a/ext/decoders/native/main.c +++ b/ext/decoders/native/main.c @@ -1,10 +1,45 @@ -#include -#include -#include -#include "rgb.h" -#include "etc.h" #include "astc.h" #include "dxtc.h" +#include "etc.h" +#include "rgb.h" +#include +#include +#include + +/* + * Decode image from A8 binary + * + * @param [String] rb_data binary to decode + * @param [Integer] size width * height + * @return [String] decoded rgb binary + */ +static VALUE rb_decode_a8(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_a8((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 + * + * @param [String] rb_data binary to decode + * @param [Integer] size width * height + * @param [Boolean] big whether input data are big endian + * @return [String] decoded rgb binary + */ +static VALUE rb_decode_r16(VALUE self, VALUE rb_data, VALUE size, VALUE big) +{ + if (RSTRING_LEN(rb_data) < FIX2LONG(size) * 2) + rb_raise(rb_eStandardError, "Data size is not enough."); + VALUE ret = rb_str_buf_new(FIX2LONG(size) * 3); + decode_r16((uint16_t*)RSTRING_PTR(rb_data), FIX2INT(size), RTEST(big), (uint8_t*)RSTRING_PTR(ret)); + rb_str_set_len(ret, FIX2LONG(size) * 3); + return ret; +} /* * Decode image from RGB565 binary @@ -12,15 +47,15 @@ * @param [String] rb_data binary to decode * @param [Integer] size width * height * @param [Boolean] big whether input data are big endian - * @return [String] decoded rgba binary + * @return [String] decoded rgb binary */ -static VALUE rb_decode_rgb565(VALUE self, VALUE rb_data, VALUE size, VALUE big) { +static VALUE rb_decode_rgb565(VALUE self, VALUE rb_data, VALUE size, VALUE big) +{ if (RSTRING_LEN(rb_data) < FIX2LONG(size) * 2) rb_raise(rb_eStandardError, "Data size is not enough."); - uint8_t *image = (uint8_t*)malloc(FIX2LONG(size) * 4); - decode_rgb565((uint16_t*)RSTRING_PTR(rb_data), FIX2INT(size), RTEST(big), image); - VALUE ret = rb_str_new((char*)image, FIX2LONG(size) * 4); - free(image); + VALUE ret = rb_str_buf_new(FIX2LONG(size) * 3); + decode_rgb565((uint16_t*)RSTRING_PTR(rb_data), FIX2INT(size), RTEST(big), (uint8_t*)RSTRING_PTR(ret)); + rb_str_set_len(ret, FIX2LONG(size) * 3); return ret; } @@ -32,10 +67,11 @@ static VALUE rb_decode_rgb565(VALUE self, VALUE rb_data, VALUE size, VALUE big) * @param [Integer] h image height * @return [String] decoded rgba binary */ -static VALUE rb_decode_etc1(VALUE self, VALUE rb_data, VALUE w, VALUE h) { +static VALUE rb_decode_etc1(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)); + uint32_t* image = (uint32_t*)calloc(FIX2LONG(w) * FIX2LONG(h), sizeof(uint32_t)); decode_etc1((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); @@ -50,10 +86,11 @@ static VALUE rb_decode_etc1(VALUE self, VALUE rb_data, VALUE w, VALUE h) { * @param [Integer] h image height * @return [String] decoded rgba binary */ -static VALUE rb_decode_etc2(VALUE self, VALUE rb_data, VALUE w, VALUE h) { +static VALUE rb_decode_etc2(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)); + uint32_t* image = (uint32_t*)calloc(FIX2LONG(w) * FIX2LONG(h), sizeof(uint32_t)); decode_etc2((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); @@ -68,11 +105,13 @@ static VALUE rb_decode_etc2(VALUE self, VALUE rb_data, VALUE w, VALUE h) { * @param [Integer] h image height * @return [String] decoded rgba binary */ -static VALUE rb_decode_etc2a1(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) 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); + 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); VALUE ret = rb_str_new((char*)image, FIX2LONG(w) * FIX2LONG(h) * sizeof(uint32_t)); free(image); return ret; @@ -86,11 +125,13 @@ static VALUE rb_decode_etc2a1(VALUE self, VALUE rb_data, VALUE w, VALUE h) { * @param [Integer] h image height * @return [String] decoded rgba binary */ -static VALUE rb_decode_etc2a8(VALUE self, VALUE rb_data, VALUE w, VALUE h) { +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); + 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); VALUE ret = rb_str_new((char*)image, FIX2LONG(w) * FIX2LONG(h) * sizeof(uint32_t)); free(image); return ret; @@ -106,11 +147,13 @@ static VALUE rb_decode_etc2a8(VALUE self, VALUE rb_data, VALUE w, VALUE h) { * @param [Integer] bh block height * @return [String] decoded rgba binary */ -static VALUE rb_decode_astc(VALUE self, VALUE rb_data, VALUE w, VALUE h, VALUE bw, VALUE bh) { +static VALUE rb_decode_astc(VALUE self, VALUE rb_data, VALUE w, VALUE h, + VALUE bw, VALUE bh) +{ 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)); + 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); VALUE ret = rb_str_new((char*)image, FIX2LONG(w) * FIX2LONG(h) * sizeof(uint32_t)); free(image); @@ -125,10 +168,11 @@ static VALUE rb_decode_astc(VALUE self, VALUE rb_data, VALUE w, VALUE h, VALUE b * @param [Integer] h image height * @return [String] decoded rgba binary */ -static VALUE rb_decode_dxt1(VALUE self, VALUE rb_data, VALUE w, VALUE h) { +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)); + 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); VALUE ret = rb_str_new((char*)image, FIX2LONG(w) * FIX2LONG(h) * sizeof(uint32_t)); free(image); @@ -143,25 +187,29 @@ static VALUE rb_decode_dxt1(VALUE self, VALUE rb_data, VALUE w, VALUE h) { * @param [Integer] h image height * @return [String] decoded rgba binary */ -static VALUE rb_decode_dxt5(VALUE self, VALUE rb_data, VALUE w, VALUE h) { +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)); + 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); VALUE ret = rb_str_new((char*)image, FIX2LONG(w) * FIX2LONG(h) * sizeof(uint32_t)); free(image); return ret; } -void Init_native() { +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_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); rb_define_module_function(mDecodeHelper, "decode_etc2", rb_decode_etc2, 3); rb_define_module_function(mDecodeHelper, "decode_etc2a1", rb_decode_etc2a1, 3); rb_define_module_function(mDecodeHelper, "decode_etc2a8", rb_decode_etc2a8, 3); - rb_define_module_function(mDecodeHelper, "decode_astc", rb_decode_astc, 5); + rb_define_module_function(mDecodeHelper, "decode_astc", rb_decode_astc, 5); rb_define_module_function(mDecodeHelper, "decode_dxt1", rb_decode_dxt1, 3); rb_define_module_function(mDecodeHelper, "decode_dxt5", rb_decode_dxt5, 3); } diff --git a/ext/decoders/native/rgb.c b/ext/decoders/native/rgb.c index d1ddd24..34f1253 100644 --- a/ext/decoders/native/rgb.c +++ b/ext/decoders/native/rgb.c @@ -1,26 +1,57 @@ +#include "rgb.h" +#include "common.h" #include -static inline int is_system_little() { - int x = 1; - return *(char*)&x == 1; -} - -void decode_rgb565(const uint16_t* data, const int size, const int is_big_endian, uint8_t* image) { - const uint16_t *d = data; - if (is_big_endian == is_system_little()) { - uint8_t *p = image; - for (int i = 0; i < size; i++, d++, p += 4) { - uint_fast8_t r = *d & 0x00f8; - uint_fast8_t g = (*d & 0x0007) << 5 | (*d & 0xe000) >> 11; - uint_fast8_t b = (*d & 0x1f00) >> 5; - p[0] = r | r >> 5; - p[1] = g | g >> 6; - p[2] = b | b >> 5; - p[3] = 255; - } - } else { - uint32_t *p = (uint32_t*)image; - for (int i = 0; i < size; i++, d++, p++) - *p = (*d & 0xf800) >> 8 | *d >> 13 | (*d & 0x7e0) << 5 | (*d & 0x60) << 3 | *d << 19 | (*d & 0x1c) << 14 | 0xff000000; +void decode_a8(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++] = *d; + image[i++] = *d; + } +} + +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; + if (IS_LITTLE_ENDIAN == !endian_big) { + // Same endian + for (int i = 0; d < d_end; d++) { + uint8_t c = *d >> 8; + image[i++] = c; + image[i++] = c; + image[i++] = c; + } + } else { + // Different endian + for (int i = 0; d < d_end; d++) { + uint8_t c = *d; + image[i++] = c; + image[i++] = c; + image[i++] = c; + } + } +} + +void decode_rgb565(const uint16_t* data, const int size, const int endian_big, uint8_t* image) +{ + const uint16_t *d = data, *d_end = data + size; + if (IS_LITTLE_ENDIAN == !endian_big) { + // Same endian + // RRRRR GGG | GGG BBBBB + for (int i = 0; d < d_end; d++) { + image[i++] = (*d >> 8 & 0xf8) | (*d >> 13); + image[i++] = (*d >> 3 & 0xfc) | (*d >> 9 & 3); + image[i++] = (*d << 3) | (*d >> 2 & 7); + } + } else { + // Different endian + // GGG BBBBB | RRRRR GGG + for (int i = 0; d < d_end; d++) { + image[i++] = (*d & 0xf8) | (*d >> 5 & 7); + image[i++] = (*d << 5 & 0xe0) | (*d >> 11 & 0x1c) | (*d >> 1 & 3); + image[i++] = (*d >> 5 & 0xf8) | (*d >> 10 & 0x7); + } } } diff --git a/ext/decoders/native/rgb.h b/ext/decoders/native/rgb.h index d144634..2f25f31 100644 --- a/ext/decoders/native/rgb.h +++ b/ext/decoders/native/rgb.h @@ -3,6 +3,8 @@ #include +void decode_a8(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*); #endif /* end of include guard: RGB_H */ diff --git a/lib/mikunyan/decoders/image_decoder.rb b/lib/mikunyan/decoders/image_decoder.rb index 6734872..f5788ae 100644 --- a/lib/mikunyan/decoders/image_decoder.rb +++ b/lib/mikunyan/decoders/image_decoder.rb @@ -105,26 +105,19 @@ module Mikunyan when 62 # RG16 decode_rg16(width, height, bin) when 63 # R8 - decode_r8(width, height, bin) + decode_a8(width, height, bin) # when 64 # ETC_RGB4Crunched # when 65 # ETC2_RGBA8Crunched end end - # Decode image from RGBA4444 binary + # Decode image from A8 binary # @param [Integer] width image width # @param [Integer] height image height # @param [String] bin binary to decode - # @param [Symbol] endian endianness of binary # @return [ChunkyPNG::Image] decoded image - def self.decode_rgba4444(width, height, bin, endian = :big) - mem = String.new(capacity: width * height * 4) - (width * height).times do |i| - c = endian == :little ? BinUtils.get_int16_le(bin, i * 2) : BinUtils.get_int16_be(bin, i * 2) - c = ((c & 0xf000) << 12) | ((c & 0x0f00) << 8) | ((c & 0x00f0) << 4) | (c & 0x000f) - BinUtils.append_int32_be!(mem, c << 4 | c) - end - ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip + def self.decode_a8(width, height, bin) + ChunkyPNG::Image.from_rgb_stream(width, height, DecodeHelper.decode_a8(bin, width * height)).flip end # Decode image from ARGB4444 binary @@ -143,52 +136,6 @@ module Mikunyan ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip end - # Decode image from RGB565 binary - # @param [Integer] width image width - # @param [Integer] height image height - # @param [String] bin binary to decode - # @param [Symbol] endian endianness of binary - # @return [ChunkyPNG::Image] decoded image - def self.decode_rgb565(width, height, bin, endian = :big) - ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_rgb565(bin, width * height, endian == :big)).flip - end - - # Decode image from A8 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_a8(width, height, bin) - mem = String.new(capacity: width * height * 3) - (width * height).times do |i| - c = BinUtils.get_int8(bin, i) - BinUtils.append_int8!(mem, c, c, c) - end - ChunkyPNG::Image.from_rgb_stream(width, height, mem).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) - decode_a8(width, height, bin) - end - - # Decode image from RG16 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_rg16(width, height, bin) - mem = String.new(capacity: width * height * 3) - (width * height).times do |i| - BinUtils.append_int16_int8_be!(mem, BinUtils.get_int16_be(bin, i * 2), 0) - end - ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip - end - # Decode image from RGB24 binary # @param [Integer] width image width # @param [Integer] height image height @@ -221,6 +168,55 @@ module Mikunyan ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip end + # Decode image from RGB565 binary + # @param [Integer] width image width + # @param [Integer] height image height + # @param [String] bin binary to decode + # @param [Symbol] endian endianness of binary + # @return [ChunkyPNG::Image] decoded image + def self.decode_rgb565(width, height, bin, endian = :big) + ChunkyPNG::Image.from_rgb_stream(width, height, DecodeHelper.decode_rgb565(bin, width * height, endian == :big)).flip + end + + # Decode image from R16 binary + # @param [Integer] width image width + # @param [Integer] height image height + # @param [String] bin binary to decode + # @param [Symbol] endian endianness of binary + # @return [ChunkyPNG::Image] decoded image + def self.decode_r16(width, height, bin, endian = :big) + ChunkyPNG::Image.from_rgb_stream(width, height, DecodeHelper.decode_r16(bin, width * height, endian == :big)).flip + end + + # Decode image from RGBA4444 binary + # @param [Integer] width image width + # @param [Integer] height image height + # @param [String] bin binary to decode + # @param [Symbol] endian endianness of binary + # @return [ChunkyPNG::Image] decoded image + def self.decode_rgba4444(width, height, bin, endian = :big) + mem = String.new(capacity: width * height * 4) + (width * height).times do |i| + c = endian == :little ? BinUtils.get_int16_le(bin, i * 2) : BinUtils.get_int16_be(bin, i * 2) + c = ((c & 0xf000) << 12) | ((c & 0x0f00) << 8) | ((c & 0x00f0) << 4) | (c & 0x000f) + BinUtils.append_int32_be!(mem, c << 4 | c) + end + ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip + end + + # Decode image from RG16 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_rg16(width, height, bin) + mem = String.new(capacity: width * height * 3) + (width * height).times do |i| + BinUtils.append_int16_int8_be!(mem, BinUtils.get_int16_be(bin, i * 2), 0) + end + ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip + end + # Decode image from BGRA32 binary # @param [Integer] width image width # @param [Integer] height image height @@ -235,22 +231,6 @@ module Mikunyan ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip end - # Decode image from R16 binary - # @param [Integer] width image width - # @param [Integer] height image height - # @param [String] bin binary to decode - # @param [Symbol] endian endianness of binary - # @return [ChunkyPNG::Image] decoded image - def self.decode_r16(width, height, bin, endian = :big) - mem = String.new(capacity: width * height * 3) - (width * height).times do |i| - c = endian == :little ? BinUtils.get_int16_le(bin, i * 2) : BinUtils.get_int16_be(bin, i * 2) - c = f2i(r / 65535.0) - BinUtils.append_int8!(mem, c, c, c) - end - ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip - end - # Decode image from RGB9e5 binary # @param [Integer] width image width # @param [Integer] height image height