use parallel tasks to decode CompressedBG.

This commit is contained in:
morkt 2015-12-27 23:47:22 +04:00
parent b24a91a57f
commit 294a8d2581

View File

@ -29,6 +29,8 @@ using System.IO;
using System.Windows.Media; using System.Windows.Media;
using GameRes.Utility; using GameRes.Utility;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace GameRes.Formats.BGI namespace GameRes.Formats.BGI
{ {
@ -259,7 +261,7 @@ namespace GameRes.Formats.BGI
{ {
for (int dst = 0; dst < output.Length; dst++) for (int dst = 0; dst < output.Length; dst++)
{ {
output[dst] = (byte)DecodeToken (tree); output[dst] = (byte)DecodeToken (this, tree);
} }
} }
@ -402,12 +404,12 @@ namespace GameRes.Formats.BGI
return node_list.ToArray(); return node_list.ToArray();
} }
int DecodeToken (HuffmanNode[] tree) int DecodeToken (MsbBitStream input, HuffmanNode[] tree)
{ {
int node_index = tree.Length-1; int node_index = tree.Length-1;
do do
{ {
int bit = GetNextBit(); int bit = input.GetNextBit();
if (-1 == bit) if (-1 == bit)
throw new EndOfStreamException(); throw new EndOfStreamException();
if (0 == bit) if (0 == bit)
@ -434,65 +436,80 @@ namespace GameRes.Formats.BGI
{ {
if (m_info.EncLength < 0x80) if (m_info.EncLength < 0x80)
throw new InvalidFormatException(); throw new InvalidFormatException();
var info = new DecodeInfo { BPP = m_info.BPP };
var dct_data = ReadEncoded(); var dct_data = ReadEncoded();
var base_offset = Input.Position;
var dct = new float[2,64];
for (int i = 0; i < 0x80; ++i) for (int i = 0; i < 0x80; ++i)
{ {
dct[i >> 6, i & 0x3F] = dct_data[i] * DCT_Table[i & 0x3F]; info.DCT[i >> 6, i & 0x3F] = dct_data[i] * DCT_Table[i & 0x3F];
} }
var tree1 = CreateHuffmanTree (ReadWeightTable (Input, 0x10), true); var base_offset = Input.Position;
var tree2 = CreateHuffmanTree (ReadWeightTable (Input, 0xB0), true); info.Tree1 = CreateHuffmanTree (ReadWeightTable (Input, 0x10), true);
int aligned_width = ((int)m_info.Width + 7) & -8; info.Tree2 = CreateHuffmanTree (ReadWeightTable (Input, 0xB0), true);
int aligned_height = ((int)m_info.Height + 7) & -8; info.Width = ((int)m_info.Width + 7) & -8;
info.Height = ((int)m_info.Height + 7) & -8;
m_output = new byte[aligned_width * aligned_height * 4]; m_output = new byte[info.Width * info.Height * 4];
Stride = aligned_width * 4; Stride = info.Width * 4;
int y_blocks = aligned_height / 8; int y_blocks = info.Height / 8;
var offsets = new uint[y_blocks+1]; var offsets = new int[y_blocks+1];
int input_base = (int)(Input.Position + offsets.Length*4 - base_offset);
using (var reader = new ArcView.Reader (Input)) using (var reader = new ArcView.Reader (Input))
{ {
int pad_skip = ((aligned_width >> 3) + 7) >> 3;
for (int i = 0; i < offsets.Length; ++i) for (int i = 0; i < offsets.Length; ++i)
offsets[i] = reader.ReadUInt32(); offsets[i] = reader.ReadInt32() - input_base;
info.Input = reader.ReadBytes ((int)(Input.Length - Input.Position));
}
int pad_skip = ((info.Width >> 3) + 7) >> 3;
var tasks = new List<Task> (y_blocks+1);
int dst = 0; int dst = 0;
for (int i = 0; i < y_blocks; ++i) for (int i = 0; i < y_blocks; ++i)
{ {
Reset(); int block_offset = offsets[i] + pad_skip;
Input.Position = base_offset + offsets[i] + pad_skip; int next_offset = i+1 == y_blocks ? info.Input.Length : offsets[i+1];
int block_size = ReadInteger (Input); int closure_dst = dst;
if (-1 == block_size) var task = Task.Run (() => UnpackBlock (info, block_offset, next_offset-block_offset, closure_dst));
throw new EndOfStreamException(); tasks.Add (task);
long input_end = i+1 == y_blocks ? Input.Length : (base_offset + offsets[i+1]); dst += info.Width * 32;
var data = UnpackBlock (input_end, block_size, tree1, tree2);
if (8 == m_info.BPP)
DecodeGrayscale (data, dct, aligned_width, dst);
else
DecodeRGB (data, dct, aligned_width, dst);
dst += aligned_width * 32;
} }
bool has_alpha = false;
if (32 == m_info.BPP) if (32 == m_info.BPP)
{ {
Input.Position = base_offset + offsets[y_blocks]; var task = Task.Run (() => UnpackAlpha (info, offsets[y_blocks]));
has_alpha = UnpackAlpha (reader, aligned_width); tasks.Add (task);
}
Format = has_alpha ? PixelFormats.Bgra32 : PixelFormats.Bgr32;
} }
var complete = Task.WhenAll (tasks);
complete.Wait();
Format = info.HasAlpha ? PixelFormats.Bgra32 : PixelFormats.Bgr32;
} }
short[] UnpackBlock (long input_end, int block_size, HuffmanNode[] tree1, HuffmanNode[] tree2) class DecodeInfo
{ {
public byte[] Input;
public int BPP;
public int Width;
public int Height;
public HuffmanNode[] Tree1;
public HuffmanNode[] Tree2;
public float[,] DCT = new float[2, 64];
public bool HasAlpha = false;
}
void UnpackBlock (DecodeInfo info, int offset, int length, int dst)
{
using (var input = new MemoryStream (info.Input, offset, length))
using (var reader = new MsbBitStream (input))
{
int block_size = ReadInteger (input);
if (-1 == block_size)
return;
var color_data = new short[block_size]; var color_data = new short[block_size];
int acc = 0; int acc = 0;
for (int i = 0; i < block_size && Input.Position < input_end; i += 64) for (int i = 0; i < block_size && input.Position < input.Length; i += 64)
{ {
int count = DecodeToken (tree1); int count = DecodeToken (reader, info.Tree1);
if (count != 0) if (count != 0)
{ {
int v = GetBits (count); int v = reader.GetBits (count);
if (0 == (v >> (count - 1))) if (0 == (v >> (count - 1)))
v = (-1 << count | v) + 1; v = (-1 << count | v) + 1;
acc += v; acc += v;
@ -500,15 +517,15 @@ namespace GameRes.Formats.BGI
color_data[i] = (short)acc; color_data[i] = (short)acc;
} }
if (0 != (CacheSize & 7)) if (0 != (reader.CacheSize & 7))
GetBits (CacheSize & 7); reader.GetBits (reader.CacheSize & 7);
for (int i = 0; i < block_size && Input.Position < input_end; i += 64) for (int i = 0; i < block_size && input.Position < input.Length; i += 64)
{ {
int index = 1; int index = 1;
while (index < 64) while (index < 64 && input.Position < input.Length)
{ {
int code = DecodeToken (tree2); int code = DecodeToken (reader, info.Tree2);
if (0 == code) if (0 == code)
break; break;
if (0xF == code) if (0xF == code)
@ -520,14 +537,18 @@ namespace GameRes.Formats.BGI
if (index >= block_fill_order.Length) if (index >= block_fill_order.Length)
break; break;
code >>= 4; code >>= 4;
int v = GetBits (code); int v = reader.GetBits (code);
if (code != 0 && 0 == (v >> (code - 1))) if (code != 0 && 0 == (v >> (code - 1)))
v = (-1 << code | v) + 1; v = (-1 << code | v) + 1;
color_data[i + block_fill_order[index]] = (short)v; color_data[i + block_fill_order[index]] = (short)v;
++index; ++index;
} }
} }
return color_data; if (8 == info.BPP)
DecodeGrayscale (color_data, info.DCT, info.Width, dst);
else
DecodeRGB (color_data, info.DCT, info.Width, dst);
}
} }
static readonly byte[] block_fill_order = static readonly byte[] block_fill_order =
@ -538,7 +559,9 @@ namespace GameRes.Formats.BGI
58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
}; };
short[,] YCbCr_block = new short[64,3]; ThreadLocal<short[,]> s_YCbCr_block = new ThreadLocal<short[,]> (() => new short[64, 3]);
short[,] YCbCr_block { get { return s_YCbCr_block.Value; } }
void DecodeRGB (short[] data, float[,] dct, int width, int dst) void DecodeRGB (short[] data, float[,] dct, int width, int dst)
{ {
@ -598,10 +621,13 @@ namespace GameRes.Formats.BGI
} }
} }
bool UnpackAlpha (BinaryReader input, int width) void UnpackAlpha (DecodeInfo info, int offset)
{
using (var data = new MemoryStream (info.Input, offset, info.Input.Length-offset))
using (var input = new BinaryReader (data))
{ {
if (1 != input.ReadInt32()) if (1 != input.ReadInt32())
return false; return;
int dst = 3; int dst = 3;
int ctl = 1 << 1; int ctl = 1 << 1;
while (dst < m_output.Length) while (dst < m_output.Length)
@ -621,9 +647,9 @@ namespace GameRes.Formats.BGI
y |= -8; y |= -8;
int count = ((v >> 9) & 0x7F) + 3; int count = ((v >> 9) & 0x7F) + 3;
int src = dst + (x + y * width) * 4; int src = dst + (x + y * info.Width) * 4;
if (src < 0 || src >= m_output.Length) if (src < 0 || src >= m_output.Length)
return false; return;
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
{ {
@ -638,16 +664,18 @@ namespace GameRes.Formats.BGI
dst += 4; dst += 4;
} }
} }
return true; info.HasAlpha = true;
}
} }
float[,] tmp = new float[8,8]; ThreadLocal<float[,]> s_tmp = new ThreadLocal<float[,]> (() => new float[8,8]);
void DecodeDCT (int channel, short[] data, int src, float[,] dct_table) void DecodeDCT (int channel, short[] data, int src, float[,] dct_table)
{ {
float v1, v2, v3, v4, v5, v6, v7, v8; float v1, v2, v3, v4, v5, v6, v7, v8;
float v9, v10, v11, v12, v13, v14, v15, v16, v17; float v9, v10, v11, v12, v13, v14, v15, v16, v17;
int d = channel > 0 ? 1 : 0; int d = channel > 0 ? 1 : 0;
var tmp = s_tmp.Value;
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
{ {