From c37dd497320561d90147f9f9c66786e44446ad70 Mon Sep 17 00:00:00 2001 From: Ishotihadus Date: Mon, 15 Jan 2018 18:54:48 +0900 Subject: [PATCH] Support ASTC Image --- lib/mikunyan/decoders.rb | 6 + lib/mikunyan/decoders/astc_block_decoder.rb | 530 ++++++++++++++++++++ lib/mikunyan/decoders/image_decoder.rb | 32 ++ 3 files changed, 568 insertions(+) create mode 100644 lib/mikunyan/decoders/astc_block_decoder.rb diff --git a/lib/mikunyan/decoders.rb b/lib/mikunyan/decoders.rb index d94e9c8..fe6acf5 100644 --- a/lib/mikunyan/decoders.rb +++ b/lib/mikunyan/decoders.rb @@ -1 +1,7 @@ require 'mikunyan/decoders/image_decoder' + +module Mikunyan + # Module for helper classes for decoding object + module DecodeHelper + end +end diff --git a/lib/mikunyan/decoders/astc_block_decoder.rb b/lib/mikunyan/decoders/astc_block_decoder.rb new file mode 100644 index 0000000..94964d3 --- /dev/null +++ b/lib/mikunyan/decoders/astc_block_decoder.rb @@ -0,0 +1,530 @@ +require 'bin_utils' +require 'fiddle' + +module Mikunyan + module DecodeHelper + # Class for decode ASTC block + # @attr_reader [String] data decoded data + class AstcBlockDecoder + attr_reader :data + + # Decode block + # @param [String] bin binary + # @param [Integer] bw block width + # @param [Integer] bh block height + def initialize(bin, bw, bh) + if bin[0].ord == 0xfc && bin[1].ord % 2 == 1 + @data = (bin[9] + bin[11] + bin[13] + bin[15]) * bw * bh + else + @d2 = BinUtils.get_int64_le(bin) + @d1 = BinUtils.get_int64_le(bin, 8) + @bw = bw + @bh = bh + + decode_block_params + decode_endpoints + decode_weights + select_partition + applicate_color + end + end + + private + + WeightPrecTableA = [nil, nil, 0, 3, 0, 5, 3, 0, nil, nil, 5, 3, 0, 5, 3, 0] + WeightPrecTableB = [nil, nil, 1, 0, 2, 0, 1, 3, nil, nil, 1, 2, 4, 2, 3, 5] + + CemTableA = [0, 0, 3, 0, 5, 3, 0, 5, 3, 0, 5, 3, 0, 5, 3, 0, 5, 3, 0] + CemTableB = [1, 2, 1, 3, 1, 2, 4, 2, 3, 5, 3, 4, 6, 4, 5, 7, 5, 6, 8] + + TritsTable = [ + [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] + ] + + QuintsTable = [ + [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] + ] + + def [](i, j = 1) + if j < 1 + 0 + elsif j == 1 + i < 64 ? @d2[i] : @d1[i - 64] + else + if i + j <= 64 + @d2 >> i & (1 << j) - 1 + elsif i >= 64 + @d1 >> (i - 64) & (1 << j) - 1 + else + @d2 >> i | (@d1 & (1 << i + j - 64) - 1) << 64 - i + end + end + end + + def decode_block_params + # Block Mode + @weight_range = @d2 >> 4 & 1 | @d2 >> 6 & 8 + @dual_plane = @d2 & 0x400 == 0x400 + if @d2 & 0x3 != 0 + @weight_range |= @d2 << 1 & 6 + case @d2 & 0xc + when 0 + @width = (@d2 >> 7 & 3) + 4 + @height = (@d2 >> 5 & 3) + 2 + when 0x4 + @width = (@d2 >> 7 & 3) + 8 + @height = (@d2 >> 5 & 3) + 2 + when 0x8 + @width = (@d2 >> 5 & 3) + 2 + @height = (@d2 >> 7 & 3) + 8 + else # 0xc + if @d2 & 0x100 == 0 + @width = (@d2 >> 5 & 3) + 2 + @height = @d2[7] + 6 + else + @width = @d2[7] + 2 + @height = (@d2 >> 5 & 3) + 2 + end + end + else + @weight_range |= @d2 >> 1 & 6 + case @d2 & 0x180 + when 0 + @width = 12 + @height = (@d2 >> 5 & 3) + 2 + when 0x80 + @width = (@d2 >> 5 & 3) + 2 + @height = 12 + when 0x180 + @width = (@d2 & 0x20 == 0) ? 6 : 10 + @height = 16 - @width + else # 0x100 + @width = (@d2 >> 5 & 3) + 6 + @height = (@d2 >> 9 & 3) + 6 + @dual_plane = false + @weight_range &= 7 + end + end + + # Count Partitions + @part_num = (@d2 >> 11 & 3) + 1 + + # Count Weight Bits + @weight_num = @width * @height + @weight_num *= 2 if @dual_plane + case WeightPrecTableA[@weight_range] + when 3 + @weight_bit = @weight_num * WeightPrecTableB[@weight_range] + (@weight_num * 8 + 4) / 5 + when 5 + @weight_bit = @weight_num * WeightPrecTableB[@weight_range] + (@weight_num * 7 + 2) / 3 + else # 0 + @weight_bit = @weight_num * WeightPrecTableB[@weight_range] + end + + # CEM + if @part_num == 1 + @cem = [@d2 >> 13 & 0xf] + config_bit = 17 + else + cembase = @d2 >> 23 & 3 + if cembase == 0 + @cem = Array.new(@part_num, @d2 >> 25 & 0xf) + config_bit = 29 + else + @cem = (0...@part_num).map{|i| ((@d2 >> (25 + i) & 1) + cembase - 1) << 2} + + case @part_num + when 2 + @cem[0] |= @d2 >> 27 & 3 + @cem[1] |= self[126 - @weight_bit, 2] + when 3 + @cem[0] |= @d2[28] + @cem[0] |= self[123 - @weight_bit] << 1 + @cem[1] |= self[124 - @weight_bit, 2] + @cem[2] |= self[126 - @weight_bit, 2] + else # 4 + 4.times do |i| + @cem[i] |= self[120 + 2 * i - @weight_bit, 2] + end + end + + config_bit = 25 + @part_num * 3 + end + end + + # Count Color Endpoint Bits + config_bit += 2 if @dual_plane + remain_bit = 128 - config_bit - @weight_bit + @cem_num = @cem.map{|i| (i >> 1 & 6) + 2}.inject(:+) + + CemTableA.count.times do |n| + i = CemTableA.count - n - 1 + case CemTableA[i] + when 3 + @cem_bit = @cem_num * CemTableB[i] + (@cem_num * 8 + 4) / 5 + when 5 + @cem_bit = @cem_num * CemTableB[i] + (@cem_num * 7 + 2) / 3 + else # 0 + @cem_bit = @cem_num * CemTableB[i] + end + + if @cem_bit <= remain_bit + @cem_range = i + break + end + end + + if @dual_plane + if @part_num == 1 || cembase == 0 + @plane_selector = self[126 - @weight_bit, 2] + else + @plane_selector = self[130 - @weight_bit - @part_num * 3, 2] + end + end + end + + def decode_endpoints + values = decode_intseq_raw(self[@part_num == 1 ? 17 : 29, @cem_bit], CemTableA[@cem_range], CemTableB[@cem_range], @cem_num).map do |e| + unquantize_endpoint(CemTableA[@cem_range], CemTableB[@cem_range], e[0], e[1]) + end + + @endpoint = @cem.map do |cem| + v = values.slice!(0, (cem >> 1 & 6) + 2) + case cem + when 0 + [v[0], v[0], v[0], 255, v[1], v[1], v[1], 255] + when 1 + l0 = (v[0] >> 2) | (v[1] & 0xc0) + l1 = (l0 + (v[1] & 0x3f)).clamp(0, 255) + [l0, l0, l0, 255, l1, l1, l1, 255] + when 4 + [v[0], v[0], v[0], v[2], v[1], v[1], v[1], v[3]] + when 5 + v[1], v[0] = bit_transfer_signed(v[1], v[0]) + v[3], v[2] = bit_transfer_signed(v[3], v[2]) + [v[0], v[0], v[0], v[2], v[0] + v[1], v[0] + v[1], v[0] + v[1], v[2] + v[3]].map{|i| i.clamp(0, 255)} + when 6 + [v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8, 255, v[0], v[1], v[2], 255] + when 8 + if v[0] + v[2] + v[4] <= v[1] + v[3] + v[5] + [v[0], v[2], v[4], 255, v[1], v[3], v[5], 255] + else + blue_contract(v[1], v[3], v[5], 255, v[0], v[2], v[4], 255) + end + when 9 + v[1], v[0] = bit_transfer_signed(v[1], v[0]) + v[3], v[2] = bit_transfer_signed(v[3], v[2]) + v[5], v[4] = bit_transfer_signed(v[5], v[4]) + if v[1] + v[3] + v[5] >= 0 + [v[0], v[2], v[4], 255, v[0] + v[1], v[2] + v[3], v[4] + v[5], 255].map{|i| i.clamp(0, 255)} + else + blue_contract(v[0] + v[1], v[2] + v[3], v[4] + v[5], 255, v[0], v[2], v[4], 255).map{|i| i.clamp(0, 255)} + end + when 10 + [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]] + when 12 + if v[0] + v[2] + v[4] <= v[1] + v[3] + v[5] + [v[0], v[2], v[4], v[6], v[1], v[3], v[5], v[7]] + else + blue_contract(v[1], v[3], v[5], v[7], v[0], v[2], v[4], v[6]) + end + when 13 + v[1], v[0] = bit_transfer_signed(v[1], v[0]) + v[3], v[2] = bit_transfer_signed(v[3], v[2]) + v[5], v[4] = bit_transfer_signed(v[5], v[4]) + v[7], v[6] = bit_transfer_signed(v[7], v[6]) + if v[1] + v[3] + v[5] >= 0 + [v[0], v[2], v[4], v[6], v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7]].map{|i| i.clamp(0, 255)} + else + blue_contract(v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7], v[0], v[2], v[4], v[6]).map{|i| i.clamp(0, 255)} + end + else + throw NotImplementedError.new("HDR image is not supported. (CEM: #{cem})") + end + end + end + + def bit_transfer_signed(a, b) + b = (b >> 1) | (a & 0x80) + a = (a >> 1) & 0x3f + [a[5] == 1 ? a - 0x40 : a, b] + end + + def blue_contract(r1, g1, b1, a1, r2, g2, b2, a2) + [(r1 + b1) >> 1, (g1 + b1) >> 1, b1, a1, (r2 + b2) >> 1, (g2 + b2) >> 1, b2, a2] + end + + def decode_weights + data = (0...(@weight_bit + 7) / 8).map{|i| (((self[120 - i * 8, 8]) * 0x80200802) & 0x0884422110) * 0x0101010101 >> 32 & 0xff} + .map.with_index{|e, i| e << i * 8}.inject(:|) & (1 << @weight_bit) - 1 + + weight_point = decode_intseq_raw(data, WeightPrecTableA[@weight_range], WeightPrecTableB[@weight_range], @weight_num).map do |e| + unquantize_weight(WeightPrecTableA[@weight_range], WeightPrecTableB[@weight_range], e[0], e[1]) + end + + ds = (1024 + @bw / 2) / (@bw - 1) + dt = (1024 + @bh / 2) / (@bh - 1) + + if @dual_plane + @weight0 = Fiddle::Pointer.malloc(@bw * @bh) + @weight1 = Fiddle::Pointer.malloc(@bw * @bh) + + for t in 0...@bh + for s in 0...@bw + gs = (ds * s * (@width - 1) + 32) >> 6 + gt = (dt * t * (@height - 1) + 32) >> 6 + fs = gs & 0xf + ft = gt & 0xf + v = (gs >> 4) + (gt >> 4) * @width + w11 = (fs * ft + 8) >> 4 + w10 = ft - w11 + w01 = fs - w11 + w00 = 16 - fs - ft + w11 + + p00 = weight_point[v * 2] || 0 + p01 = weight_point[(v + 1) * 2] || 0 + p10 = weight_point[(v + @width) * 2] || 0 + p11 = weight_point[(v + @width + 1) * 2] || 0 + @weight0[s + t * @bw] = (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11 + 8) >> 4 + + p00 = weight_point[v * 2 + 1] || 0 + p01 = weight_point[(v + 1) * 2 + 1] || 0 + p10 = weight_point[(v + @width) * 2 + 1] || 0 + p11 = weight_point[(v + @width + 1) * 2 + 1] || 0 + @weight1[s + t * @bw] = (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11 + 8) >> 4 + end + end + else + @weight = Fiddle::Pointer.malloc(@bw * @bh) + + for t in 0...@bh + for s in 0...@bw + gs = (ds * s * (@width - 1) + 32) >> 6 + gt = (dt * t * (@height - 1) + 32) >> 6 + fs = gs & 0xf + ft = gt & 0xf + v = (gs >> 4) + (gt >> 4) * @width + w11 = (fs * ft + 8) >> 4 + + p00 = weight_point[v] || 0 + p01 = weight_point[v + 1] || 0 + p10 = weight_point[v + @width] || 0 + p11 = weight_point[v + @width + 1] || 0 + @weight[s + t * @bw] = (p00 * (16 - fs - ft + w11) + p01 * (fs - w11) + p10 * (ft - w11) + p11 * w11 + 8) >> 4 + end + end + end + end + + def select_partition + if @part_num > 1 + small_block = @bw * @bh < 31 + + seed = (@d2 >> 13 & 0x3ff) | ((@part_num - 1) << 10) + + rnum = seed + rnum ^= (rnum >> 15) + rnum = (rnum - (rnum << 17)) & 0xffffffff + rnum = (rnum + (rnum << 7)) & 0xffffffff + rnum = (rnum + (rnum << 4)) & 0xffffffff + rnum ^= rnum >> 5 + rnum = (rnum + (rnum << 16)) & 0xffffffff + rnum ^= rnum >> 7 + rnum ^= rnum >> 3 + rnum = (rnum ^ (rnum << 6)) & 0xffffffff + rnum = rnum ^ (rnum >> 17) + + seeds = [0, 4, 8, 12, 16, 20, 24, 28].map{|i| (rnum >> i) & 0xf}.map!{|e| e * e} + sh = [seed & 2 == 2 ? 4 : 5, @part_num == 3 ? 6 : 5] + sh.reverse! if seed & 1 == 0 + seeds.map!.with_index{|e, i| e >> sh[i % 2]} + + @partition = Array.new(@bw * @bh) + + for y in 0...@bh + for x in 0...@bw + idx = x + y * @bw + + if small_block + x <<= 1 + y <<= 1 + end + + a = (seeds[0] * x + seeds[1] * y + (rnum >> 14)) & 0x3f + b = (seeds[2] * x + seeds[3] * y + (rnum >> 10)) & 0x3f + c = @part_num < 3 ? 0 : (seeds[4] * x + seeds[5] * y + (rnum >> 6)) & 0x3f + d = @part_num < 4 ? 0 : (seeds[6] * x + seeds[7] * y + (rnum >> 2)) & 0x3f + + @partition[idx] = 3 - [d, c, b, a].each_with_index.max[1] + end + end + end + end + + def applicate_color + mem = Fiddle::Pointer.malloc(@bw * @bh * 4) + + if @dual_plane + plane_arr = [0, 1, 2, 3] + plane_arr.delete_at(@plane_selector) + + if @partition + (@bw * @bh).times do |i| + part = @partition[i] + plane_arr.each{|c| mem[i * 4 + c] = select_color(@endpoint[part][c], @endpoint[part][4 + c], @weight0[i])} + mem[i * 4 + @plane_selector] = select_color(@endpoint[part][@plane_selector], @endpoint[part][4 + @plane_selector], @weight1[i]) + end + else + (@bw * @bh).times do |i| + plane_arr.each{|c| mem[i * 4 + c] = select_color(@endpoint[0][c], @endpoint[0][4 + c], @weight0[i])} + mem[i * 4 + @plane_selector] = select_color(@endpoint[0][@plane_selector], @endpoint[0][4 + @plane_selector], @weight1[i]) + end + end + elsif @partition + (@bw * @bh).times do |i| + part = @partition[i] + 4.times{|c| mem[i * 4 + c] = select_color(@endpoint[part][c], @endpoint[part][4 + c], @weight[i])} + end + else + (@bw * @bh).times do |i| + 4.times{|c| mem[i * 4 + c] = select_color(@endpoint[0][c], @endpoint[0][4 + c], @weight[i])} + end + end + + @data = mem.to_str + end + + def select_color(v0, v1, weight) + v0 |= v0 << 8 + v1 |= v1 << 8 + v = (v0 * (64 - weight) + v1 * weight + 32) >> 6 + (v * 255 + 32768) / 65536 + end + + def decode_intseq_raw(data, a, b, count) + mask = (1 << b) - 1 + case a + when 3 + rc = (count + 4) / 5 + ret = Array.new(rc * 5) + m = [0, 2 + b, 4 + b * 2, 5 + b * 3, 7 + b * 4] + rc.times do |i| + t = (data >> b & 3) | (data >> b * 2 & 0xc) | (data >> b * 3 & 0x10) | (data >> b * 4 & 0x60) | (data >> b * 5 & 0x80) + 5.times do |j| + ret[i * 5 + j] = [data >> m[j] & mask, TritsTable[j][t]] + end + data >>= b * 5 + 8 + end + ret[0, count] + when 5 + rc = (count + 2) / 3 + ret = Array.new(rc * 3) + m = [0, 3 + b, 5 + b * 2] + rc.times do |i| + q = (data >> b & 7) | (data >> b * 2 & 0x18) | (data >> b * 3 & 0x60) + 3.times do |j| + ret[i * 3 + j] = [data >> m[j] & mask, QuintsTable[j][q]] + end + data >>= b * 3 + 7 + end + ret[0, count] + else # 0 + (0...count).map do |i| + [data >> b * i & mask, 0] + end + end + end + + def unquantize_endpoint(a, b, bit, val_d) + if a == 0 + case b + when 1 + bit * 0xff + when 2 + bit * 0x55 + when 3 + bit << 5 | bit << 2 | bit >> 1 + when 4 + bit << 4 | bit + when 5 + bit << 3 | bit >> 2 + when 6 + bit << 2 | bit >> 4 + when 7 + bit << 1 | bit >> 6 + else # 8 + bit + end + else + val_a = (bit & 1) * 0x1ff + tmp_b = bit >> 1 + case b + when 1 + val_b = 0 + val_c = a == 3 ? 204 : 113 + when 2 + val_b = a == 3 ? (0b100010110) * tmp_b : (0b100001100) * tmp_b + val_c = a == 3 ? 93 : 54 + when 3 + val_b = a == 3 ? tmp_b << 7 | tmp_b << 2 | tmp_b : tmp_b << 7 | tmp_b << 1 | tmp_b >> 1 + val_c = a == 3 ? 44 : 26 + when 4 + val_b = tmp_b << 6 | tmp_b >> (a == 3 ? 0 : 1) + val_c = a == 3 ? 22 : 13 + when 5 + val_b = tmp_b << 5 | tmp_b >> (a == 3 ? 2 : 3) + val_c = a == 3 ? 11 : 6 + else # 6 + val_b = tmp_b << 4 | tmp_b >> 4 + val_c = 5 + end + t = val_d * val_c + val_b + t ^= val_a + (val_a & 0x80) | (t >> 2) + end + end + + def unquantize_weight(a, b, bit, val_d) + if a == 0 + case b + when 1 + t = bit == 1 ? 63 : 0 + when 2 + t = bit << 4 | bit << 2 | bit + when 3 + t = bit << 3 | bit + when 4 + t = bit << 2 | bit >> 2 + else # 5 + t = bit << 1 | bit >> 4 + end + elsif b == 0 + t = (a == 3 ? [0, 32, 63] : [0, 16, 32, 47, 63])[val_d] + else + val_a = (bit & 1) * 0x7f + case b + when 1 + val_b = 0 + val_c = a == 3 ? 50 : 28 + when 2 + val_b = (a == 3 ? 0b1000101 : 0b1000010) * bit[1] + val_c = a == 3 ? 23 : 13 + else # 3 + val_b = (bit << 4 | bit >> 1) & 0b1100011 + val_c = 11 + end + t = val_d * val_c + val_b + t ^= val_a + t = (val_a & 0x20) | (t >> 2) + end + t > 32 ? t + 1 : t + end + end + end +end diff --git a/lib/mikunyan/decoders/image_decoder.rb b/lib/mikunyan/decoders/image_decoder.rb index 3b9324f..6183f88 100644 --- a/lib/mikunyan/decoders/image_decoder.rb +++ b/lib/mikunyan/decoders/image_decoder.rb @@ -1,6 +1,7 @@ begin; require 'oily_png'; rescue LoadError; require 'chunky_png'; end require 'bin_utils' require 'fiddle' +require 'mikunyan/decoders/astc_block_decoder' module Mikunyan # Class for image decoding tools @@ -62,6 +63,18 @@ module Mikunyan decode_etc2rgb(width, height, bin) when 47 decode_etc2rgba8(width, height, bin) + when 48, 54 + decode_astc(width, height, 4, bin) + when 49, 55 + decode_astc(width, height, 5, bin) + when 50, 56 + decode_astc(width, height, 6, bin) + when 51, 57 + decode_astc(width, height, 8, bin) + when 52, 58 + decode_astc(width, height, 10, bin) + when 53, 59 + decode_astc(width, height, 12, bin) when 62 decode_rg16(width, height, bin) when 63 @@ -399,6 +412,25 @@ module Mikunyan ChunkyPNG::Image.from_rgba_stream(bw * 4, bh * 4, mem.to_str).crop(0, 0, width, height) end + # Decode image from ASTC compressed binary + # @param [Integer] width image width + # @param [Integer] height image height + # @param [Integer] blocksize block size + # @param [String] bin binary to decode + # @return [ChunkyPNG::Image] decoded image + def self.decode_astc(width, height, blocksize, bin) + bw = (width + blocksize - 1) / blocksize + bh = (height + blocksize - 1) / blocksize + ret = ChunkyPNG::Image.new(bw * blocksize, bh * blocksize) + bh.times do |by| + bw.times do |bx| + block = DecodeHelper::AstcBlockDecoder.new(bin.byteslice((by * bw + bx) * 16, 16), blocksize, blocksize).data + ret.replace!(ChunkyPNG::Image.from_rgba_stream(blocksize, blocksize, block), bx * blocksize, by * blocksize) + end + end + ret.crop(0, 0, width, height).flip + end + # Create ASTC file data from ObjectValue # @param [Mikunyan::ObjectValue,Hash] object target object # @return [String,nil] created file