diff --git a/README.md b/README.md index db872d2..dbe4e21 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ obj.key Mikunyan generates `ChunkyPNG::Image` images directly from Texture2D objects. -Mikunyan can decode images in basic texture formats (1–5, 7, 9, 13–20, 22, 62, and 63), DXT1 (10), DXT5 (12), PVRTC1 (30–33), ETC (34), ETC2 (45–47), ASTC (48–59), HDR ASTC (66–71), or Crunched format (28, 29, 64, 65). +Mikunyan can decode images in basic texture formats (1–5, 7, 9, 13–20, 22, 62, 63), DXT1 (10), DXT5 (12), PVRTC1 (30–33), ETC (34), EAC (41–44), ETC2 (45–47), ASTC (48–59), HDR ASTC (66–71), or Crunched format (28, 29, 64, 65). ```ruby # get some Texture2D asset diff --git a/ext/decoders/native/etc.c b/ext/decoders/native/etc.c index 4de0145..7187bac 100644 --- a/ext/decoders/native/etc.c +++ b/ext/decoders/native/etc.c @@ -284,8 +284,7 @@ static void decode_etc2a8_block(const uint8_t *data, uint32_t *outbuf) { // 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; + uint_fast64_t l = bton64(*(uint64_t*)data); for (int i = 0; i < 16; i++, l >>= 3) ((uint8_t *)(outbuf + WriteOrderTableRev[i]))[3] = clamp(data[0] + multiplier * table[l & 7]); } else { @@ -295,6 +294,31 @@ static void decode_etc2a8_block(const uint8_t *data, uint32_t *outbuf) { } } +static void decode_eac_block(const uint8_t *data, int color, uint32_t *outbuf) { + uint_fast8_t multiplier = data[1] >> 1 & 0x78; + if (multiplier == 0) + multiplier = 1; + const int_fast8_t *table = Etc2AlphaModTable[data[1] & 0xf]; + uint_fast64_t l = bton64(*(uint64_t*)data); + for (int i = 0; i < 16; i++, l >>= 3) { + int_fast16_t val = data[0] * 8 + multiplier * table[l & 7] + 4; + ((uint8_t *)(outbuf + WriteOrderTableRev[i]))[color] = val < 0 ? 0 : val >= 2048 ? 0xff : val >> 3; + } +} + +static void decode_eac_signed_block(const uint8_t *data, int color, uint32_t *outbuf) { + int8_t base = (int8_t)data[0]; + uint_fast8_t multiplier = data[1] >> 1 & 0x78; + if (multiplier == 0) + multiplier = 1; + const int_fast8_t *table = Etc2AlphaModTable[data[1] & 0xf]; + uint_fast64_t l = bton64(*(uint64_t*)data); + for (int i = 0; i < 16; i++, l >>= 3) { + int_fast16_t val = base * 8 + multiplier * table[l & 7] + 1023; + ((uint8_t *)(outbuf + WriteOrderTableRev[i]))[color] = val < 0 ? 0 : val >= 2048 ? 0xff : val >> 3; + } +} + int decode_etc1(const uint8_t *data, const long w, const long h, uint32_t *image) { long num_blocks_x = (w + 3) / 4; long num_blocks_y = (h + 3) / 4; @@ -347,3 +371,73 @@ int decode_etc2a8(const uint8_t *data, const long w, const long h, uint32_t *ima } return 1; } + +int decode_eacr(const uint8_t *data, const long w, const long h, uint32_t *image) { + long num_blocks_x = (w + 3) / 4; + long num_blocks_y = (h + 3) / 4; + uint32_t buffer[16]; + uint32_t base_buffer[16]; + for (int i = 0; i < 16; i++) + base_buffer[i] = color(0, 0, 0, 255); + for (long by = 0; by < num_blocks_y; by++) { + for (long bx = 0; bx < num_blocks_x; bx++, data += 8) { + memcpy(buffer, base_buffer, sizeof(buffer)); + decode_eac_block(data, 0, buffer); + copy_block_buffer(bx, by, w, h, 4, 4, buffer, image); + } + } + return 1; +} + +int decode_eacr_signed(const uint8_t *data, const long w, const long h, uint32_t *image) { + long num_blocks_x = (w + 3) / 4; + long num_blocks_y = (h + 3) / 4; + uint32_t buffer[16]; + uint32_t base_buffer[16]; + for (int i = 0; i < 16; i++) + base_buffer[i] = color(0, 0, 0, 255); + for (long by = 0; by < num_blocks_y; by++) { + for (long bx = 0; bx < num_blocks_x; bx++, data += 8) { + memcpy(buffer, base_buffer, sizeof(buffer)); + decode_eac_signed_block(data, 0, buffer); + copy_block_buffer(bx, by, w, h, 4, 4, buffer, image); + } + } + return 1; +} + +int decode_eacrg(const uint8_t *data, const long w, const long h, uint32_t *image) { + long num_blocks_x = (w + 3) / 4; + long num_blocks_y = (h + 3) / 4; + uint32_t buffer[16]; + uint32_t base_buffer[16]; + for (int i = 0; i < 16; i++) + base_buffer[i] = color(0, 0, 0, 255); + for (long by = 0; by < num_blocks_y; by++) { + for (long bx = 0; bx < num_blocks_x; bx++, data += 16) { + memcpy(buffer, base_buffer, sizeof(buffer)); + decode_eac_block(data, 0, buffer); + decode_eac_block(data + 8, 1, buffer); + copy_block_buffer(bx, by, w, h, 4, 4, buffer, image); + } + } + return 1; +} + +int decode_eacrg_signed(const uint8_t *data, const long w, const long h, uint32_t *image) { + long num_blocks_x = (w + 3) / 4; + long num_blocks_y = (h + 3) / 4; + uint32_t buffer[16]; + uint32_t base_buffer[16]; + for (int i = 0; i < 16; i++) + base_buffer[i] = color(0, 0, 0, 255); + for (long by = 0; by < num_blocks_y; by++) { + for (long bx = 0; bx < num_blocks_x; bx++, data += 16) { + memcpy(buffer, base_buffer, sizeof(buffer)); + decode_eac_signed_block(data, 0, buffer); + decode_eac_signed_block(data + 8, 1, buffer); + copy_block_buffer(bx, by, w, h, 4, 4, buffer, image); + } + } + return 1; +} diff --git a/ext/decoders/native/etc.h b/ext/decoders/native/etc.h index f68f804..5546b8a 100644 --- a/ext/decoders/native/etc.h +++ b/ext/decoders/native/etc.h @@ -7,5 +7,9 @@ int decode_etc1(const uint8_t *, const long, const long, uint32_t *); int decode_etc2(const uint8_t *, const long, const long, uint32_t *); int decode_etc2a1(const uint8_t *, const long, const long, uint32_t *); int decode_etc2a8(const uint8_t *, const long, const long, uint32_t *); +int decode_eacr(const uint8_t *, const long, const long, uint32_t *); +int decode_eacr_signed(const uint8_t *, const long, const long, uint32_t *); +int decode_eacrg(const uint8_t *, const long, const long, uint32_t *); +int decode_eacrg_signed(const uint8_t *, const long, const long, uint32_t *); #endif /* end of include guard: ETC_H */ diff --git a/ext/decoders/native/main.c b/ext/decoders/native/main.c index 74da224..a2a65ba 100644 --- a/ext/decoders/native/main.c +++ b/ext/decoders/native/main.c @@ -194,8 +194,8 @@ static VALUE rb_decode_etc1(VALUE self, VALUE rb_data, VALUE rb_w, VALUE rb_h) { * Decode image from ETC2 compressed binary * * @param [String] rb_data binary to decode - * @param [Integer] w image width - * @param [Integer] h image height + * @param [Integer] rb_w image width + * @param [Integer] rb_h image height * @return [String] decoded rgba binary */ static VALUE rb_decode_etc2(VALUE self, VALUE rb_data, VALUE rb_w, VALUE rb_h) { @@ -212,8 +212,8 @@ static VALUE rb_decode_etc2(VALUE self, VALUE rb_data, VALUE rb_w, VALUE rb_h) { * Decode image from ETC2 Alpha1 compressed binary * * @param [String] rb_data binary to decode - * @param [Integer] w image width - * @param [Integer] h image height + * @param [Integer] rb_w image width + * @param [Integer] rb_h image height * @return [String] decoded rgba binary */ static VALUE rb_decode_etc2a1(VALUE self, VALUE rb_data, VALUE rb_w, VALUE rb_h) { @@ -230,8 +230,8 @@ static VALUE rb_decode_etc2a1(VALUE self, VALUE rb_data, VALUE rb_w, VALUE rb_h) * Decode image from ETC2 Alpha8 compressed binary * * @param [String] rb_data binary to decode - * @param [Integer] w image width - * @param [Integer] h image height + * @param [Integer] rb_w image width + * @param [Integer] rb_h image height * @return [String] decoded rgba binary */ static VALUE rb_decode_etc2a8(VALUE self, VALUE rb_data, VALUE rb_w, VALUE rb_h) { @@ -244,6 +244,78 @@ static VALUE rb_decode_etc2a8(VALUE self, VALUE rb_data, VALUE rb_w, VALUE rb_h) return ret; } +/* + * Decode image from EAC R11 compressed binary + * + * @param [String] rb_data binary to decode + * @param [Integer] rb_w image width + * @param [Integer] rb_h image height + * @return [String] decoded rgba binary + */ +static VALUE rb_decode_eacr(VALUE self, VALUE rb_data, VALUE rb_w, VALUE rb_h) { + long w = FIX2LONG(rb_w), h = FIX2LONG(rb_h); + if (!check_str_len_block(rb_data, w, h, 4, 4, 8)) + return Qnil; + VALUE ret = rb_alloc_rgba(w * h); + if (!decode_eacr((uint8_t *)RSTRING_PTR(rb_data), w, h, (uint32_t *)RSTRING_PTR(ret))) + return Qnil; + return ret; +} + +/* + * Decode image from EAC Signed R11 compressed binary + * + * @param [String] rb_data binary to decode + * @param [Integer] rb_w image width + * @param [Integer] rb_h image height + * @return [String] decoded rgba binary + */ +static VALUE rb_decode_eacsr(VALUE self, VALUE rb_data, VALUE rb_w, VALUE rb_h) { + long w = FIX2LONG(rb_w), h = FIX2LONG(rb_h); + if (!check_str_len_block(rb_data, w, h, 4, 4, 8)) + return Qnil; + VALUE ret = rb_alloc_rgba(w * h); + if (!decode_eacr_signed((uint8_t *)RSTRING_PTR(rb_data), w, h, (uint32_t *)RSTRING_PTR(ret))) + return Qnil; + return ret; +} + +/* + * Decode image from EAC RG11 compressed binary + * + * @param [String] rb_data binary to decode + * @param [Integer] rb_w image width + * @param [Integer] rb_h image height + * @return [String] decoded rgba binary + */ +static VALUE rb_decode_eacrg(VALUE self, VALUE rb_data, VALUE rb_w, VALUE rb_h) { + long w = FIX2LONG(rb_w), h = FIX2LONG(rb_h); + if (!check_str_len_block(rb_data, w, h, 4, 4, 16)) + return Qnil; + VALUE ret = rb_alloc_rgba(w * h); + if (!decode_eacrg((uint8_t *)RSTRING_PTR(rb_data), w, h, (uint32_t *)RSTRING_PTR(ret))) + return Qnil; + return ret; +} + +/* + * Decode image from EAC RG11 compressed binary + * + * @param [String] rb_data binary to decode + * @param [Integer] rb_w image width + * @param [Integer] rb_h image height + * @return [String] decoded rgba binary + */ +static VALUE rb_decode_eacsrg(VALUE self, VALUE rb_data, VALUE rb_w, VALUE rb_h) { + long w = FIX2LONG(rb_w), h = FIX2LONG(rb_h); + if (!check_str_len_block(rb_data, w, h, 4, 4, 16)) + return Qnil; + VALUE ret = rb_alloc_rgba(w * h); + if (!decode_eacrg_signed((uint8_t *)RSTRING_PTR(rb_data), w, h, (uint32_t *)RSTRING_PTR(ret))) + return Qnil; + return ret; +} + /* * Decode image from ASTC compressed binary * @@ -336,6 +408,10 @@ void Init_native() { 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_eacr", rb_decode_eacr, 3); + rb_define_module_function(mDecodeHelper, "decode_eacsr", rb_decode_eacsr, 3); + rb_define_module_function(mDecodeHelper, "decode_eacrg", rb_decode_eacrg, 3); + rb_define_module_function(mDecodeHelper, "decode_eacsrg", rb_decode_eacsrg, 3); 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/lib/mikunyan/decoders/image_decoder.rb b/lib/mikunyan/decoders/image_decoder.rb index 0bf17f0..2c104bc 100644 --- a/lib/mikunyan/decoders/image_decoder.rb +++ b/lib/mikunyan/decoders/image_decoder.rb @@ -79,10 +79,14 @@ module Mikunyan decode_pvrtc1(width, height, bin, 4) when 34 # ETC_RGB4 decode_etc1(width, height, bin) - # when 41 # EAC_R - # when 42 # EAC_R_SIGNED - # when 43 # EAC_RG - # when 44 # EAC_RG_SIGNED + when 41 # EAC_R + decode_eacr(width, height, bin) + when 42 # EAC_R_SIGNED + decode_eacsr(width, height, bin) + when 43 # EAC_RG + decode_eacrg(width, height, bin) + when 44 # EAC_RG_SIGNED + decode_eacsrg(width, height, bin) when 45 # ETC2_RGB decode_etc2rgb(width, height, bin) when 46 # ETC2_RGBA1 @@ -375,6 +379,42 @@ module Mikunyan ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_etc1(bin, width, height)) end + # Decode image from EAC R11 compressed 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_eacr(width, height, bin) + ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_eacr(bin, width, height)) + end + + # Decode image from EAC Signed R11 compressed 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_eacsr(width, height, bin) + ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_eacsr(bin, width, height)) + end + + # Decode image from EAC RG11 compressed 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_eacrg(width, height, bin) + ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_eacrg(bin, width, height)) + end + + # Decode image from EAC Signed RG11 compressed 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_eacsrg(width, height, bin) + ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_eacsrg(bin, width, height)) + end + # Decode image from ETC2 compressed binary # @param [Integer] width image width # @param [Integer] height image height