diff --git a/lib/mikunyan/decoders/dxtc_block_decoder.rb b/lib/mikunyan/decoders/dxtc_block_decoder.rb index 4ea7090..e80c77d 100644 --- a/lib/mikunyan/decoders/dxtc_block_decoder.rb +++ b/lib/mikunyan/decoders/dxtc_block_decoder.rb @@ -8,7 +8,7 @@ module Mikunyan def self.decode_dxt1_block(bin) c0 = BinUtils.get_int16_le(bin, 0) c1 = BinUtils.get_int16_le(bin, 2) - color = [get_rgb565(c0), get_rgb565(c1), nil, nil] + color = [get_rgb565a(c0), get_rgb565a(c1), nil, nil] if c0 > c1 color[2] = [(color[0][0] * 2 + color[1][0]) / 3, (color[0][1] * 2 + color[1][1]) / 3, (color[0][2] * 2 + color[1][2]) / 3, 255] color[3] = [(color[0][0] + color[1][0] * 2) / 3, (color[0][1] + color[1][1] * 2) / 3, (color[0][2] + color[1][2] * 2) / 3, 255] @@ -26,7 +26,59 @@ module Mikunyan mem end + def self.decode_dxt5_block(bin) + alpha_list, alpha_code = decode_dxt5_alpha(bin) + color_list, color_code = decode_dxtc_rgb(bin) + mem = String.new(capacity: 64) + 16.times do + mem << color_list[color_code & 3] + mem << alpha_list[alpha_code & 7] + color_code >>= 2 + alpha_code >>= 3 + end + mem + end + + def self.decode_dxtc_rgb(bin) + c0 = BinUtils.get_int16_le(bin, 8) + c1 = BinUtils.get_int16_le(bin, 10) + color = [get_rgb565(c0), get_rgb565(c1), nil, nil] + if c0 > c1 + color[2] = (0...3).map{|i| (color[0][i] * 2 + color[1][i]) / 3} + color[3] = (0...3).map{|i| (color[0][i] + color[1][i] * 2) / 3} + else + color[2] = (0...3).map{|i| (color[0][i] + color[1][i]) / 2} + color[3] = [0, 0, 0] + end + color.map!{|e| e.pack('C3')} + code = BinUtils.get_int32_le(bin, 12) + [color, code] + end + + def self.decode_dxt5_alpha(bin) + a0 = BinUtils.get_int8(bin, 0) + a1 = BinUtils.get_int8(bin, 1) + alpha = [a0, a1, nil, nil, nil, nil, nil, nil] + if a0 > a1 + alpha[2, 6] = (1..6).map{|n| (a0 * (7-n) + a1 * n) / 7} + else + alpha[2, 4] = (1..4).map{|n| (a0 * (5-n) + a1 * n) / 5} + alpha[6] = 0 + alpha[7] = 255 + end + alpha.pack('C8') + code = BinUtils.get_int64_le(bin) >> 16 + [alpha, code] + end + def self.get_rgb565(c) + r = (c & 0xf800) >> 8 + g = (c & 0x07e0) >> 3 + b = (c & 0x001f) << 3 + [r | r >> 5, g | g >> 6, b | b >> 5] + end + + def self.get_rgb565a(c) r = (c & 0xf800) >> 8 g = (c & 0x07e0) >> 3 b = (c & 0x001f) << 3 diff --git a/lib/mikunyan/decoders/image_decoder.rb b/lib/mikunyan/decoders/image_decoder.rb index a29dac7..dcf91d5 100644 --- a/lib/mikunyan/decoders/image_decoder.rb +++ b/lib/mikunyan/decoders/image_decoder.rb @@ -41,6 +41,8 @@ module Mikunyan decode_r16(width, height, bin) when 10 decode_dxt1(width, height, bin) + when 12 + decode_dxt5(width, height, bin) when 13 decode_rgba4444(width, height, bin, endian) when 14 @@ -353,6 +355,11 @@ module Mikunyan ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip end + # Decode image from DXT1 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_dxt1(width, height, bin) bw = (width + 3) / 4 bh = (height + 3) / 4 @@ -366,6 +373,24 @@ module Mikunyan ret.crop(0, 0, height, width).flip end + # Decode image from DXT5 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_dxt5(width, height, bin) + bw = (width + 3) / 4 + bh = (height + 3) / 4 + ret = ChunkyPNG::Image.new(bh * 4, bw * 4) + bh.times do |by| + bw.times do |bx| + block = DecodeHelper::DxtcBlockDecoder::decode_dxt5_block(bin.byteslice((bx + by * bw) * 16, 16)) + ret.replace!(ChunkyPNG::Image.from_rgba_stream(4, 4, block), bx * 4, by * 4) + end + end + ret.crop(0, 0, height, width).flip + end + # Decode image from ETC1 compressed binary # @param [Integer] width image width # @param [Integer] height image height