diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj
index fde8f110..458e1cd5 100644
--- a/ArcFormats/ArcFormats.csproj
+++ b/ArcFormats/ArcFormats.csproj
@@ -109,8 +109,11 @@
WidgetAGS.xaml
+
+
+
@@ -155,6 +158,7 @@
+
@@ -174,6 +178,7 @@
+
@@ -683,6 +688,7 @@
+
@@ -920,6 +926,10 @@
{453c087f-e416-4ae9-8c03-d8760da0574b}
GameRes
+
+ {73b6c693-9846-4d33-8300-a80239fcfff9}
+ Net20
+
diff --git a/ArcFormats/Ethornell/ImageBGI.cs b/ArcFormats/Ethornell/ImageBGI.cs
index b2630569..2f9df00d 100644
--- a/ArcFormats/Ethornell/ImageBGI.cs
+++ b/ArcFormats/Ethornell/ImageBGI.cs
@@ -23,14 +23,9 @@
// IN THE SOFTWARE.
//
-using System;
using System.ComponentModel.Composition;
using System.IO;
using System.Windows.Media;
-using GameRes.Utility;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
namespace GameRes.Formats.BGI
{
@@ -135,726 +130,4 @@ namespace GameRes.Formats.BGI
}
}
}
-
- internal class CbgMetaData : ImageMetaData
- {
- public int IntermediateLength;
- public uint Key;
- public int EncLength;
- public byte CheckSum;
- public byte CheckXor;
- public int Version;
- }
-
- [Export(typeof(ImageFormat))]
- public class CompressedBGFormat : ImageFormat
- {
- public override string Tag { get { return "CompressedBG"; } }
- public override string Description { get { return "BGI/Ethornell compressed image format"; } }
- public override uint Signature { get { return 0x706D6F43; } }
-
- public CompressedBGFormat ()
- {
- Extensions = new string[] { "", "bgi" };
- }
-
- public override void Write (Stream file, ImageData image)
- {
- throw new System.NotImplementedException ("BgiFormat.Write not implemented");
- }
-
- public override ImageMetaData ReadMetaData (IBinaryStream stream)
- {
- var header = stream.ReadHeader (0x30);
- if (!header.AsciiEqual ("CompressedBG___"))
- return null;
- return new CbgMetaData
- {
- Width = header.ToUInt16 (0x10),
- Height = header.ToUInt16 (0x12),
- BPP = header.ToInt32 (0x14),
- IntermediateLength = header.ToInt32 (0x20),
- Key = header.ToUInt32 (0x24),
- EncLength = header.ToInt32 (0x28),
- CheckSum = header[0x2C],
- CheckXor = header[0x2D],
- Version = header.ToUInt16 (0x2E),
- };
- }
-
- public override ImageData Read (IBinaryStream stream, ImageMetaData info)
- {
- var meta = (CbgMetaData)info as CbgMetaData;
- using (var reader = new CbgReader (stream.AsStream, meta))
- {
- reader.Unpack();
- return ImageData.Create (meta, reader.Format, null, reader.Data, reader.Stride);
- }
- }
- }
-
- internal class CbgReader : BgiDecoderBase
- {
- byte[] m_output;
- CbgMetaData m_info;
- int m_pixel_size;
-
- public byte[] Data { get { return m_output; } }
- public PixelFormat Format { get; private set; }
- public int Stride { get; private set; }
-
- public CbgReader (Stream input, CbgMetaData info) : base (input, true)
- {
- m_info = info;
- m_pixel_size = m_info.BPP / 8;
- Stride = (int)info.Width * m_pixel_size;
- m_key = m_info.Key;
- m_magic = 0;
- switch (m_info.BPP)
- {
- case 32: Format = PixelFormats.Bgra32; break;
- case 24: Format = PixelFormats.Bgr24; break;
- case 8: Format = PixelFormats.Gray8; break;
- case 16:
- if (2 == m_info.Version)
- throw new InvalidFormatException();
- Format = PixelFormats.Bgr565;
- break;
- default: throw new InvalidFormatException();
- }
- }
-
- public void Unpack ()
- {
- Input.Position = 0x30;
- if (m_info.Version < 2)
- UnpackV1();
- else if (2 == m_info.Version)
- {
- if (m_info.EncLength < 0x80)
- throw new InvalidFormatException();
- using (var decoder = new ParallelCbgDecoder (m_info, ReadEncoded()))
- UnpackV2 (decoder);
- }
- else
- throw new NotSupportedException ("Not supported CompressedBG version");
- }
-
- protected byte[] ReadEncoded ()
- {
- var data = new byte[m_info.EncLength];
- if (data.Length != Input.Read (data, 0, data.Length))
- throw new EndOfStreamException();
- byte sum = 0;
- byte xor = 0;
- for (int i = 0; i < data.Length; ++i)
- {
- data[i] -= UpdateKey();
- sum += data[i];
- xor ^= data[i];
- }
- if (sum != m_info.CheckSum || xor != m_info.CheckXor)
- throw new InvalidFormatException ("Compressed stream failed checksum check");
- return data;
- }
-
- static internal int ReadInteger (Stream input)
- {
- int v = 0;
- int code;
- int code_length = 0;
- do
- {
- code = input.ReadByte();
- if (-1 == code || code_length >= 32)
- return -1;
- v |= (code & 0x7f) << code_length;
- code_length += 7;
- }
- while (0 != (code & 0x80));
- return v;
- }
-
- static protected uint[] ReadWeightTable (Stream input, int length)
- {
- uint[] leaf_nodes_weight = new uint[length];
- for (int i = 0; i < length; ++i)
- {
- int weight = ReadInteger (input);
- if (-1 == weight)
- throw new InvalidFormatException ("Invalid compressed stream");
- leaf_nodes_weight[i] = (uint)weight;
- }
- return leaf_nodes_weight;
- }
-
- void UnpackV1 ()
- {
- uint[] leaf_nodes_weight;
- using (var enc = new MemoryStream (ReadEncoded()))
- leaf_nodes_weight = ReadWeightTable (enc, 0x100);
- var tree = new HuffmanTree (leaf_nodes_weight);
- byte[] packed = new byte[m_info.IntermediateLength];
-
- HuffmanDecompress (tree, packed);
- m_output = new byte[Stride * (int)m_info.Height];
- UnpackZeros (packed);
- ReverseAverageSampling();
- }
-
- void HuffmanDecompress (HuffmanTree tree, byte[] output)
- {
- for (int dst = 0; dst < output.Length; dst++)
- {
- output[dst] = (byte)tree.DecodeToken (this);
- }
- }
-
- void UnpackZeros (byte[] input)
- {
- int dst = 0;
- int dec_zero = 0;
- int src = 0;
- while (dst < m_output.Length)
- {
- int code_length = 0;
- int count = 0;
- byte code;
- do
- {
- if (src >= input.Length)
- return;
-
- code = input[src++];
- count |= (code & 0x7f) << code_length;
- code_length += 7;
- }
- while (0 != (code & 0x80));
-
- if (dst + count > m_output.Length)
- break;
-
- if (0 == dec_zero)
- {
- if (src + count > input.Length)
- break;
- Buffer.BlockCopy (input, src, m_output, dst, count);
- src += count;
- }
- else
- {
- for (int i = 0; i < count; ++i)
- m_output[dst+i] = 0;
- }
- dec_zero ^= 1;
- dst += count;
- }
- }
-
- void ReverseAverageSampling ()
- {
- for (int y = 0; y < m_info.Height; ++y)
- {
- int line = y * Stride;
- for (int x = 0; x < m_info.Width; ++x)
- {
- int pixel = line + x * m_pixel_size;
- for (int p = 0; p < m_pixel_size; p++)
- {
- int avg = 0;
- if (x > 0)
- avg += m_output[pixel + p - m_pixel_size];
- if (y > 0)
- avg += m_output[pixel + p - Stride];
- if (x > 0 && y > 0)
- avg /= 2;
- if (0 != avg)
- m_output[pixel + p] += (byte)avg;
- }
- }
- }
- }
-
- void UnpackV2 (ParallelCbgDecoder decoder)
- {
- var base_offset = Input.Position;
- decoder.Tree1 = new HuffmanTree (ReadWeightTable (Input, 0x10), true);
- decoder.Tree2 = new HuffmanTree (ReadWeightTable (Input, 0xB0), true);
-
- int y_blocks = decoder.Height / 8;
- 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))
- {
- for (int i = 0; i < offsets.Length; ++i)
- offsets[i] = reader.ReadInt32() - input_base;
- decoder.Input = reader.ReadBytes ((int)(Input.Length - Input.Position));
- }
- int pad_skip = ((decoder.Width >> 3) + 7) >> 3;
- var tasks = new List (y_blocks+1);
- decoder.Output = new byte[decoder.Width * decoder.Height * 4];
- int dst = 0;
- for (int i = 0; i < y_blocks; ++i)
- {
- int block_offset = offsets[i] + pad_skip;
- int next_offset = i+1 == y_blocks ? decoder.Input.Length : offsets[i+1];
- int closure_dst = dst;
- var task = Task.Run (() => decoder.UnpackBlock (block_offset, next_offset-block_offset, closure_dst));
- tasks.Add (task);
- dst += decoder.Width * 32;
- }
- if (32 == m_info.BPP)
- {
- var task = Task.Run (() => decoder.UnpackAlpha (offsets[y_blocks]));
- tasks.Add (task);
- }
- var complete = Task.WhenAll (tasks);
- complete.Wait();
- Format = decoder.HasAlpha ? PixelFormats.Bgra32 : PixelFormats.Bgr32;
- Stride = decoder.Width * 4;
- m_output = decoder.Output;
- }
- }
-
- internal class HuffmanTree
- {
- HuffmanNode[] m_nodes;
-
- class HuffmanNode
- {
- public bool Valid;
- public bool IsParent;
- public uint Weight;
- public int LeftChildIndex;
- public int RightChildIndex;
- }
-
- public HuffmanTree (uint[] leaf_nodes_weight, bool v2 = false)
- {
- var node_list = new List (leaf_nodes_weight.Length * 2);
- uint root_node_weight = 0;
- for (int i = 0; i < leaf_nodes_weight.Length; ++i)
- {
- var node = new HuffmanNode
- {
- Valid = leaf_nodes_weight[i] != 0,
- Weight = leaf_nodes_weight[i],
- IsParent = false
- };
- node_list.Add (node);
- root_node_weight += node.Weight;
- }
- int[] child_node_index = new int[2];
- for (;;)
- {
- uint weight = 0;
- for (int i = 0; i < 2; i++)
- {
- uint min_weight = uint.MaxValue;
- child_node_index[i] = -1;
- int n = 0;
- if (v2)
- {
- for (; n < node_list.Count; ++n)
- {
- if (node_list[n].Valid)
- {
- min_weight = node_list[n].Weight;
- child_node_index[i] = n++;
- break;
- }
- }
- n = Math.Max (n, i+1);
- }
- for (; n < node_list.Count; ++n)
- {
- if (node_list[n].Valid && node_list[n].Weight < min_weight)
- {
- min_weight = node_list[n].Weight;
- child_node_index[i] = n;
- }
- }
- if (-1 == child_node_index[i])
- continue;
- node_list[child_node_index[i]].Valid = false;
- weight += node_list[child_node_index[i]].Weight;
- }
- var parent_node = new HuffmanNode
- {
- Valid = true,
- IsParent = true,
- LeftChildIndex = child_node_index[0],
- RightChildIndex = child_node_index[1],
- Weight = weight,
- };
- node_list.Add (parent_node);
- if (weight >= root_node_weight)
- break;
- }
- m_nodes = node_list.ToArray();
- }
-
- public int DecodeToken (IBitStream input)
- {
- int node_index = m_nodes.Length-1;
- do
- {
- int bit = input.GetNextBit();
- if (-1 == bit)
- throw new EndOfStreamException();
- if (0 == bit)
- node_index = m_nodes[node_index].LeftChildIndex;
- else
- node_index = m_nodes[node_index].RightChildIndex;
- }
- while (m_nodes[node_index].IsParent);
- return node_index;
- }
- }
-
- internal sealed class ParallelCbgDecoder : IDisposable
- {
- public byte[] Input;
- public byte[] Output;
- public int BPP;
- public int Width;
- public int Height;
- public HuffmanTree Tree1;
- public HuffmanTree Tree2;
- public bool HasAlpha = false;
- float[,] DCT = new float[2, 64];
-
- public ParallelCbgDecoder (CbgMetaData info, byte[] dct_data)
- {
- BPP = info.BPP;
- Width = ((int)info.Width + 7) & -8;
- Height = ((int)info.Height + 7) & -8;
-
- for (int i = 0; i < 0x80; ++i)
- {
- DCT[i >> 6, i & 0x3F] = dct_data[i] * DCT_Table[i & 0x3F];
- }
- }
-
- static readonly float[] DCT_Table = {
- 1.00000000f, 1.38703990f, 1.30656302f, 1.17587554f, 1.00000000f, 0.78569496f, 0.54119611f, 0.27589938f,
- 1.38703990f, 1.92387950f, 1.81225491f, 1.63098633f, 1.38703990f, 1.08979023f, 0.75066054f, 0.38268343f,
- 1.30656302f, 1.81225491f, 1.70710683f, 1.53635550f, 1.30656302f, 1.02655995f, 0.70710677f, 0.36047992f,
- 1.17587554f, 1.63098633f, 1.53635550f, 1.38268340f, 1.17587554f, 0.92387950f, 0.63637930f, 0.32442334f,
- 1.00000000f, 1.38703990f, 1.30656302f, 1.17587554f, 1.00000000f, 0.78569496f, 0.54119611f, 0.27589938f,
- 0.78569496f, 1.08979023f, 1.02655995f, 0.92387950f, 0.78569496f, 0.61731654f, 0.42521504f, 0.21677275f,
- 0.54119611f, 0.75066054f, 0.70710677f, 0.63637930f, 0.54119611f, 0.42521504f, 0.29289323f, 0.14931567f,
- 0.27589938f, 0.38268343f, 0.36047992f, 0.32442334f, 0.27589938f, 0.21677275f, 0.14931567f, 0.07612047f,
- };
-
- public void UnpackBlock (int offset, int length, int dst)
- {
- using (var input = new MemoryStream (this.Input, offset, length))
- using (var reader = new MsbBitStream (input))
- {
- int block_size = CbgReader.ReadInteger (input);
- if (-1 == block_size)
- return;
- var color_data = new short[block_size];
- int acc = 0;
- for (int i = 0; i < block_size && input.Position < input.Length; i += 64)
- {
- int count = Tree1.DecodeToken (reader);
- if (count != 0)
- {
- int v = reader.GetBits (count);
- if (0 == (v >> (count - 1)))
- v = (-1 << count | v) + 1;
- acc += v;
- }
- color_data[i] = (short)acc;
- }
-
- if (0 != (reader.CacheSize & 7))
- reader.GetBits (reader.CacheSize & 7);
-
- for (int i = 0; i < block_size && input.Position < input.Length; i += 64)
- {
- int index = 1;
- while (index < 64 && input.Position < input.Length)
- {
- int code = Tree2.DecodeToken (reader);
- if (0 == code)
- break;
- if (0xF == code)
- {
- index += 0x10;
- continue;
- }
- index += code & 0xF;
- if (index >= block_fill_order.Length)
- break;
- code >>= 4;
- int v = reader.GetBits (code);
- if (code != 0 && 0 == (v >> (code - 1)))
- v = (-1 << code | v) + 1;
- color_data[i + block_fill_order[index]] = (short)v;
- ++index;
- }
- }
- if (8 == BPP)
- DecodeGrayscale (color_data, dst);
- else
- DecodeRGB (color_data, dst);
- }
- }
-
- static readonly byte[] block_fill_order =
- {
- 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
- 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28,
- 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
- 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
- };
-
- ThreadLocal s_YCbCr_block = new ThreadLocal (() => new short[64, 3]);
-
- short[,] YCbCr_block { get { return s_YCbCr_block.Value; } }
-
- void DecodeRGB (short[] data, int dst)
- {
- int block_count = Width / 8;
- for (int i = 0; i < block_count; ++i)
- {
- int src = i * 64;
- for (int channel = 0; channel < 3; ++channel)
- {
- DecodeDCT (channel, data, src);
- src += Width * 8;
- }
- for (int j = 0; j < 64; ++j)
- {
- float cy = YCbCr_block[j,0];
- float cb = YCbCr_block[j,1];
- float cr = YCbCr_block[j,2];
-
- // Full-range YCbCr->RGB conversion
- //
- // | 1.0 0.0 1.402 | | Y |
- // | 1.0 -0.34414 -0.71414 | x | Cb - 128 |
- // | 1.0 1.772 0.0 | | Cr - 128 |
- var r = cy + 1.402f * cr - 178.956f;
- var g = cy - 0.34414f * cb - 0.71414f * cr + 135.95984f;
- var b = cy + 1.772f * cb - 226.316f;
-
- int y = j >> 3;
- int x = j & 7;
- int p = (y * Width + x) * 4;
- Output[dst+p] = FloatToByte (b);
- Output[dst+p+1] = FloatToByte (g);
- Output[dst+p+2] = FloatToByte (r);
- }
- dst += 32;
- }
- }
-
- void DecodeGrayscale (short[] data, int dst)
- {
- int src = 0;
- int block_count = Width / 8;
- for (int i = 0; i < block_count; ++i)
- {
- DecodeDCT (0, data, src);
- src += 64;
- for (int j = 0; j < 64; ++j)
- {
- int y = j >> 3;
- int x = j & 7;
- int p = (y * Width + x) * 4;
- Output[dst+p] = (byte)YCbCr_block[j,0];
- Output[dst+p+1] = (byte)YCbCr_block[j,0];
- Output[dst+p+2] = (byte)YCbCr_block[j,0];
- }
- dst += 32;
- }
- }
-
- public void UnpackAlpha (int offset)
- {
- using (var input = new BinMemoryStream (this.Input, offset, Input.Length-offset))
- {
- if (1 != input.ReadInt32())
- return;
- int dst = 3;
- int ctl = 1 << 1;
- while (dst < Output.Length)
- {
- ctl >>= 1;
- if (1 == ctl)
- ctl = input.ReadUInt8() | 0x100;
-
- if (0 != (ctl & 1))
- {
- int v = input.ReadUInt16();
- int x = v & 0x3F;
- if (x > 0x1F)
- x |= -0x40;
- int y = (v >> 6) & 7;
- if (y != 0)
- y |= -8;
- int count = ((v >> 9) & 0x7F) + 3;
-
- int src = dst + (x + y * Width) * 4;
- if (src < 0 || src >= dst)
- return;
-
- for (int i = 0; i < count; ++i)
- {
- Output[dst] = Output[src];
- src += 4;
- dst += 4;
- }
- }
- else
- {
- Output[dst] = input.ReadUInt8();
- dst += 4;
- }
- }
- HasAlpha = true;
- }
- }
-
- ThreadLocal s_tmp = new ThreadLocal (() => new float[8,8]);
-
- void DecodeDCT (int channel, short[] data, int src)
- {
- float v1, v2, v3, v4, v5, v6, v7, v8;
- float v9, v10, v11, v12, v13, v14, v15, v16, v17;
- int d = channel > 0 ? 1 : 0;
- var tmp = s_tmp.Value;
-
- for (int i = 0; i < 8; ++i)
- {
- if (0 == data[src + 8 + i] && 0 == data[src + 16 + i] && 0 == data[src + 24 + i]
- && 0 == data[src + 32 + i] && 0 == data[src + 40 + i] && 0 == data[src + 48 + i]
- && 0 == data[src + 56 + i])
- {
- var t = data[src + i] * DCT[d, i];
- tmp[0,i] = t;
- tmp[1,i] = t;
- tmp[2,i] = t;
- tmp[3,i] = t;
- tmp[4,i] = t;
- tmp[5,i] = t;
- tmp[6,i] = t;
- tmp[7,i] = t;
- continue;
- }
-
- v1 = data[src + i] * DCT[d,i];
- v2 = data[src + 8 + i] * DCT[d, 8 + i];
- v3 = data[src + 16 + i] * DCT[d, 16 + i];
- v4 = data[src + 24 + i] * DCT[d, 24 + i];
- v5 = data[src + 32 + i] * DCT[d, 32 + i];
- v6 = data[src + 40 + i] * DCT[d, 40 + i];
- v7 = data[src + 48 + i] * DCT[d, 48 + i];
- v8 = data[src + 56 + i] * DCT[d, 56 + i];
-
- v10 = v1 + v5;
- v11 = v1 - v5;
- v12 = v3 + v7;
- v13 = (v3 - v7) * 1.414213562f - v12;
- v1 = v10 + v12;
- v7 = v10 - v12;
- v3 = v11 + v13;
- v5 = v11 - v13;
- v14 = v2 + v8;
- v15 = v2 - v8;
- v16 = v6 + v4;
- v17 = v6 - v4;
- v8 = v14 + v16;
- v11 = (v14 - v16) * 1.414213562f;
- v9 = (v17 + v15) * 1.847759065f;
- v10 = 1.082392200f * v15 - v9;
- v13 = -2.613125930f * v17 + v9;
- v6 = v13 - v8;
- v4 = v11 - v6;
- v2 = v10 + v4;
-
- tmp[0,i] = v1 + v8;
- tmp[1,i] = v3 + v6;
- tmp[2,i] = v5 + v4;
- tmp[3,i] = v7 - v2;
- tmp[4,i] = v7 + v2;
- tmp[5,i] = v5 - v4;
- tmp[6,i] = v3 - v6;
- tmp[7,i] = v1 - v8;
- }
- int dst = 0;
- for (int i = 0; i < 8; ++i)
- {
- v10 = tmp[i,0] + tmp[i,4];
- v11 = tmp[i,0] - tmp[i,4];
- v12 = tmp[i,2] + tmp[i,6];
- v13 = tmp[i,2] - tmp[i,6];
- v14 = tmp[i,1] + tmp[i,7];
- v15 = tmp[i,1] - tmp[i,7];
- v16 = tmp[i,5] + tmp[i,3];
- v17 = tmp[i,5] - tmp[i,3];
-
- v13 = 1.414213562f * v13 - v12;
- v1 = v10 + v12;
- v7 = v10 - v12;
- v3 = v11 + v13;
- v5 = v11 - v13;
- v8 = v14 + v16;
- v11 = (v14 - v16) * 1.414213562f;
- v9 = (v17 + v15) * 1.847759065f;
- v10 = v9 - v15 * 1.082392200f;
- v13 = v9 - v17 * 2.613125930f;
- v6 = v13 - v8;
- v4 = v11 - v6;
- v2 = v10 - v4;
-
- YCbCr_block[dst++, channel] = FloatToShort (v1 + v8);
- YCbCr_block[dst++, channel] = FloatToShort (v3 + v6);
- YCbCr_block[dst++, channel] = FloatToShort (v5 + v4);
- YCbCr_block[dst++, channel] = FloatToShort (v7 + v2);
- YCbCr_block[dst++, channel] = FloatToShort (v7 - v2);
- YCbCr_block[dst++, channel] = FloatToShort (v5 - v4);
- YCbCr_block[dst++, channel] = FloatToShort (v3 - v6);
- YCbCr_block[dst++, channel] = FloatToShort (v1 - v8);
- }
- }
-
- static short FloatToShort (float f)
- {
- int a = 0x80 + (((int)f) >> 3);
- if (a <= 0)
- return 0;
- if (a <= 0xFF)
- return (short)a;
- if (a < 0x180)
- return 0xFF;
- return 0;
- }
-
- static byte FloatToByte (float f)
- {
- if (f >= 0xFF)
- return 0xFF;
- if (f <= 0)
- return 0;
- return (byte)f;
- }
-
- #region IDisposable Members
- bool _disposed = false;
- public void Dispose ()
- {
- if (!_disposed)
- {
- s_YCbCr_block.Dispose();
- s_tmp.Dispose();
- _disposed = true;
- }
- }
- #endregion
- }
}
diff --git a/ArcFormats/Ethornell/ImageCBG.cs b/ArcFormats/Ethornell/ImageCBG.cs
new file mode 100644
index 00000000..56e73cde
--- /dev/null
+++ b/ArcFormats/Ethornell/ImageCBG.cs
@@ -0,0 +1,757 @@
+//! \file ImageCBG.cs
+//! \date 2018 Aug 31
+//! \brief BGI/Ethornell engine image format.
+//
+// Copyright (C) 2015-2018 by morkt
+//
+// 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.
+//
+
+using System;
+using System.ComponentModel.Composition;
+using System.IO;
+using System.Windows.Media;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace GameRes.Formats.BGI
+{
+ internal class CbgMetaData : ImageMetaData
+ {
+ public int IntermediateLength;
+ public uint Key;
+ public int EncLength;
+ public byte CheckSum;
+ public byte CheckXor;
+ public int Version;
+ }
+
+ [Export(typeof(ImageFormat))]
+ public class CompressedBGFormat : ImageFormat
+ {
+ public override string Tag { get { return "CompressedBG"; } }
+ public override string Description { get { return "BGI/Ethornell compressed image format"; } }
+ public override uint Signature { get { return 0x706D6F43; } }
+
+ public CompressedBGFormat ()
+ {
+ Extensions = new string[] { "", "bgi" };
+ }
+
+ public override void Write (Stream file, ImageData image)
+ {
+ throw new System.NotImplementedException ("BgiFormat.Write not implemented");
+ }
+
+ public override ImageMetaData ReadMetaData (IBinaryStream stream)
+ {
+ var header = stream.ReadHeader (0x30);
+ if (!header.AsciiEqual ("CompressedBG___"))
+ return null;
+ return new CbgMetaData
+ {
+ Width = header.ToUInt16 (0x10),
+ Height = header.ToUInt16 (0x12),
+ BPP = header.ToInt32 (0x14),
+ IntermediateLength = header.ToInt32 (0x20),
+ Key = header.ToUInt32 (0x24),
+ EncLength = header.ToInt32 (0x28),
+ CheckSum = header[0x2C],
+ CheckXor = header[0x2D],
+ Version = header.ToUInt16 (0x2E),
+ };
+ }
+
+ public override ImageData Read (IBinaryStream stream, ImageMetaData info)
+ {
+ var meta = (CbgMetaData)info as CbgMetaData;
+ using (var reader = new CbgReader (stream.AsStream, meta))
+ {
+ reader.Unpack();
+ return ImageData.Create (meta, reader.Format, null, reader.Data, reader.Stride);
+ }
+ }
+ }
+
+ internal class CbgReader : BgiDecoderBase
+ {
+ byte[] m_output;
+ CbgMetaData m_info;
+ int m_pixel_size;
+
+ public byte[] Data { get { return m_output; } }
+ public PixelFormat Format { get; private set; }
+ public int Stride { get; private set; }
+
+ public CbgReader (Stream input, CbgMetaData info) : base (input, true)
+ {
+ m_info = info;
+ m_pixel_size = m_info.BPP / 8;
+ Stride = (int)info.Width * m_pixel_size;
+ m_key = m_info.Key;
+ m_magic = 0;
+ switch (m_info.BPP)
+ {
+ case 32: Format = PixelFormats.Bgra32; break;
+ case 24: Format = PixelFormats.Bgr24; break;
+ case 8: Format = PixelFormats.Gray8; break;
+ case 16:
+ if (2 == m_info.Version)
+ throw new InvalidFormatException();
+ Format = PixelFormats.Bgr565;
+ break;
+ default: throw new InvalidFormatException();
+ }
+ }
+
+ public void Unpack ()
+ {
+ Input.Position = 0x30;
+ if (m_info.Version < 2)
+ UnpackV1();
+ else if (2 == m_info.Version)
+ {
+ if (m_info.EncLength < 0x80)
+ throw new InvalidFormatException();
+ using (var decoder = new ParallelCbgDecoder (m_info, ReadEncoded()))
+ UnpackV2 (decoder);
+ }
+ else
+ throw new NotSupportedException ("Not supported CompressedBG version");
+ }
+
+ protected byte[] ReadEncoded ()
+ {
+ var data = new byte[m_info.EncLength];
+ if (data.Length != Input.Read (data, 0, data.Length))
+ throw new EndOfStreamException();
+ byte sum = 0;
+ byte xor = 0;
+ for (int i = 0; i < data.Length; ++i)
+ {
+ data[i] -= UpdateKey();
+ sum += data[i];
+ xor ^= data[i];
+ }
+ if (sum != m_info.CheckSum || xor != m_info.CheckXor)
+ throw new InvalidFormatException ("Compressed stream failed checksum check");
+ return data;
+ }
+
+ static internal int ReadInteger (Stream input)
+ {
+ int v = 0;
+ int code;
+ int code_length = 0;
+ do
+ {
+ code = input.ReadByte();
+ if (-1 == code || code_length >= 32)
+ return -1;
+ v |= (code & 0x7f) << code_length;
+ code_length += 7;
+ }
+ while (0 != (code & 0x80));
+ return v;
+ }
+
+ static protected uint[] ReadWeightTable (Stream input, int length)
+ {
+ uint[] leaf_nodes_weight = new uint[length];
+ for (int i = 0; i < length; ++i)
+ {
+ int weight = ReadInteger (input);
+ if (-1 == weight)
+ throw new InvalidFormatException ("Invalid compressed stream");
+ leaf_nodes_weight[i] = (uint)weight;
+ }
+ return leaf_nodes_weight;
+ }
+
+ void UnpackV1 ()
+ {
+ uint[] leaf_nodes_weight;
+ using (var enc = new MemoryStream (ReadEncoded()))
+ leaf_nodes_weight = ReadWeightTable (enc, 0x100);
+ var tree = new HuffmanTree (leaf_nodes_weight);
+ byte[] packed = new byte[m_info.IntermediateLength];
+
+ HuffmanDecompress (tree, packed);
+ m_output = new byte[Stride * (int)m_info.Height];
+ UnpackZeros (packed);
+ ReverseAverageSampling();
+ }
+
+ void HuffmanDecompress (HuffmanTree tree, byte[] output)
+ {
+ for (int dst = 0; dst < output.Length; dst++)
+ {
+ output[dst] = (byte)tree.DecodeToken (this);
+ }
+ }
+
+ void UnpackZeros (byte[] input)
+ {
+ int dst = 0;
+ int dec_zero = 0;
+ int src = 0;
+ while (dst < m_output.Length)
+ {
+ int code_length = 0;
+ int count = 0;
+ byte code;
+ do
+ {
+ if (src >= input.Length)
+ return;
+
+ code = input[src++];
+ count |= (code & 0x7f) << code_length;
+ code_length += 7;
+ }
+ while (0 != (code & 0x80));
+
+ if (dst + count > m_output.Length)
+ break;
+
+ if (0 == dec_zero)
+ {
+ if (src + count > input.Length)
+ break;
+ Buffer.BlockCopy (input, src, m_output, dst, count);
+ src += count;
+ }
+ else
+ {
+ for (int i = 0; i < count; ++i)
+ m_output[dst+i] = 0;
+ }
+ dec_zero ^= 1;
+ dst += count;
+ }
+ }
+
+ void ReverseAverageSampling ()
+ {
+ for (int y = 0; y < m_info.Height; ++y)
+ {
+ int line = y * Stride;
+ for (int x = 0; x < m_info.Width; ++x)
+ {
+ int pixel = line + x * m_pixel_size;
+ for (int p = 0; p < m_pixel_size; p++)
+ {
+ int avg = 0;
+ if (x > 0)
+ avg += m_output[pixel + p - m_pixel_size];
+ if (y > 0)
+ avg += m_output[pixel + p - Stride];
+ if (x > 0 && y > 0)
+ avg /= 2;
+ if (0 != avg)
+ m_output[pixel + p] += (byte)avg;
+ }
+ }
+ }
+ }
+
+ void UnpackV2 (ParallelCbgDecoder decoder)
+ {
+ var base_offset = Input.Position;
+ decoder.Tree1 = new HuffmanTree (ReadWeightTable (Input, 0x10), true);
+ decoder.Tree2 = new HuffmanTree (ReadWeightTable (Input, 0xB0), true);
+
+ int y_blocks = decoder.Height / 8;
+ 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))
+ {
+ for (int i = 0; i < offsets.Length; ++i)
+ offsets[i] = reader.ReadInt32() - input_base;
+ decoder.Input = reader.ReadBytes ((int)(Input.Length - Input.Position));
+ }
+ int pad_skip = ((decoder.Width >> 3) + 7) >> 3;
+ var tasks = new List (y_blocks+1);
+ decoder.Output = new byte[decoder.Width * decoder.Height * 4];
+ int dst = 0;
+ for (int i = 0; i < y_blocks; ++i)
+ {
+ int block_offset = offsets[i] + pad_skip;
+ int next_offset = i+1 == y_blocks ? decoder.Input.Length : offsets[i+1];
+ int closure_dst = dst;
+ var task = Task.Run (() => decoder.UnpackBlock (block_offset, next_offset-block_offset, closure_dst));
+ tasks.Add (task);
+ dst += decoder.Width * 32;
+ }
+ if (32 == m_info.BPP)
+ {
+ var task = Task.Run (() => decoder.UnpackAlpha (offsets[y_blocks]));
+ tasks.Add (task);
+ }
+ var complete = Task.WhenAll (tasks);
+ complete.Wait();
+ Format = decoder.HasAlpha ? PixelFormats.Bgra32 : PixelFormats.Bgr32;
+ Stride = decoder.Width * 4;
+ m_output = decoder.Output;
+ }
+ }
+
+ internal class HuffmanTree
+ {
+ HuffmanNode[] m_nodes;
+
+ class HuffmanNode
+ {
+ public bool Valid;
+ public bool IsParent;
+ public uint Weight;
+ public int LeftChildIndex;
+ public int RightChildIndex;
+ }
+
+ public HuffmanTree (uint[] leaf_nodes_weight, bool v2 = false)
+ {
+ var node_list = new List (leaf_nodes_weight.Length * 2);
+ uint root_node_weight = 0;
+ for (int i = 0; i < leaf_nodes_weight.Length; ++i)
+ {
+ var node = new HuffmanNode
+ {
+ Valid = leaf_nodes_weight[i] != 0,
+ Weight = leaf_nodes_weight[i],
+ IsParent = false
+ };
+ node_list.Add (node);
+ root_node_weight += node.Weight;
+ }
+ int[] child_node_index = new int[2];
+ for (;;)
+ {
+ uint weight = 0;
+ for (int i = 0; i < 2; i++)
+ {
+ uint min_weight = uint.MaxValue;
+ child_node_index[i] = -1;
+ int n = 0;
+ if (v2)
+ {
+ for (; n < node_list.Count; ++n)
+ {
+ if (node_list[n].Valid)
+ {
+ min_weight = node_list[n].Weight;
+ child_node_index[i] = n++;
+ break;
+ }
+ }
+ n = Math.Max (n, i+1);
+ }
+ for (; n < node_list.Count; ++n)
+ {
+ if (node_list[n].Valid && node_list[n].Weight < min_weight)
+ {
+ min_weight = node_list[n].Weight;
+ child_node_index[i] = n;
+ }
+ }
+ if (-1 == child_node_index[i])
+ continue;
+ node_list[child_node_index[i]].Valid = false;
+ weight += node_list[child_node_index[i]].Weight;
+ }
+ var parent_node = new HuffmanNode
+ {
+ Valid = true,
+ IsParent = true,
+ LeftChildIndex = child_node_index[0],
+ RightChildIndex = child_node_index[1],
+ Weight = weight,
+ };
+ node_list.Add (parent_node);
+ if (weight >= root_node_weight)
+ break;
+ }
+ m_nodes = node_list.ToArray();
+ }
+
+ public int DecodeToken (IBitStream input)
+ {
+ int node_index = m_nodes.Length-1;
+ do
+ {
+ int bit = input.GetNextBit();
+ if (-1 == bit)
+ throw new EndOfStreamException();
+ if (0 == bit)
+ node_index = m_nodes[node_index].LeftChildIndex;
+ else
+ node_index = m_nodes[node_index].RightChildIndex;
+ }
+ while (m_nodes[node_index].IsParent);
+ return node_index;
+ }
+ }
+
+ internal sealed class ParallelCbgDecoder : IDisposable
+ {
+ public byte[] Input;
+ public byte[] Output;
+ public int BPP;
+ public int Width;
+ public int Height;
+ public HuffmanTree Tree1;
+ public HuffmanTree Tree2;
+ public bool HasAlpha = false;
+ float[,] DCT = new float[2, 64];
+
+ public ParallelCbgDecoder (CbgMetaData info, byte[] dct_data)
+ {
+ BPP = info.BPP;
+ Width = ((int)info.Width + 7) & -8;
+ Height = ((int)info.Height + 7) & -8;
+
+ for (int i = 0; i < 0x80; ++i)
+ {
+ DCT[i >> 6, i & 0x3F] = dct_data[i] * DCT_Table[i & 0x3F];
+ }
+ }
+
+ static readonly float[] DCT_Table = {
+ 1.00000000f, 1.38703990f, 1.30656302f, 1.17587554f, 1.00000000f, 0.78569496f, 0.54119611f, 0.27589938f,
+ 1.38703990f, 1.92387950f, 1.81225491f, 1.63098633f, 1.38703990f, 1.08979023f, 0.75066054f, 0.38268343f,
+ 1.30656302f, 1.81225491f, 1.70710683f, 1.53635550f, 1.30656302f, 1.02655995f, 0.70710677f, 0.36047992f,
+ 1.17587554f, 1.63098633f, 1.53635550f, 1.38268340f, 1.17587554f, 0.92387950f, 0.63637930f, 0.32442334f,
+ 1.00000000f, 1.38703990f, 1.30656302f, 1.17587554f, 1.00000000f, 0.78569496f, 0.54119611f, 0.27589938f,
+ 0.78569496f, 1.08979023f, 1.02655995f, 0.92387950f, 0.78569496f, 0.61731654f, 0.42521504f, 0.21677275f,
+ 0.54119611f, 0.75066054f, 0.70710677f, 0.63637930f, 0.54119611f, 0.42521504f, 0.29289323f, 0.14931567f,
+ 0.27589938f, 0.38268343f, 0.36047992f, 0.32442334f, 0.27589938f, 0.21677275f, 0.14931567f, 0.07612047f,
+ };
+
+ public void UnpackBlock (int offset, int length, int dst)
+ {
+ using (var input = new MemoryStream (this.Input, offset, length))
+ using (var reader = new MsbBitStream (input))
+ {
+ int block_size = CbgReader.ReadInteger (input);
+ if (-1 == block_size)
+ return;
+ var color_data = new short[block_size];
+ int acc = 0;
+ for (int i = 0; i < block_size && input.Position < input.Length; i += 64)
+ {
+ int count = Tree1.DecodeToken (reader);
+ if (count != 0)
+ {
+ int v = reader.GetBits (count);
+ if (0 == (v >> (count - 1)))
+ v = (-1 << count | v) + 1;
+ acc += v;
+ }
+ color_data[i] = (short)acc;
+ }
+
+ if (0 != (reader.CacheSize & 7))
+ reader.GetBits (reader.CacheSize & 7);
+
+ for (int i = 0; i < block_size && input.Position < input.Length; i += 64)
+ {
+ int index = 1;
+ while (index < 64 && input.Position < input.Length)
+ {
+ int code = Tree2.DecodeToken (reader);
+ if (0 == code)
+ break;
+ if (0xF == code)
+ {
+ index += 0x10;
+ continue;
+ }
+ index += code & 0xF;
+ if (index >= block_fill_order.Length)
+ break;
+ code >>= 4;
+ int v = reader.GetBits (code);
+ if (code != 0 && 0 == (v >> (code - 1)))
+ v = (-1 << code | v) + 1;
+ color_data[i + block_fill_order[index]] = (short)v;
+ ++index;
+ }
+ }
+ if (8 == BPP)
+ DecodeGrayscale (color_data, dst);
+ else
+ DecodeRGB (color_data, dst);
+ }
+ }
+
+ static readonly byte[] block_fill_order =
+ {
+ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
+ };
+
+ ThreadLocal s_YCbCr_block = new ThreadLocal (() => new short[64, 3]);
+
+ short[,] YCbCr_block { get { return s_YCbCr_block.Value; } }
+
+ void DecodeRGB (short[] data, int dst)
+ {
+ int block_count = Width / 8;
+ for (int i = 0; i < block_count; ++i)
+ {
+ int src = i * 64;
+ for (int channel = 0; channel < 3; ++channel)
+ {
+ DecodeDCT (channel, data, src);
+ src += Width * 8;
+ }
+ for (int j = 0; j < 64; ++j)
+ {
+ float cy = YCbCr_block[j,0];
+ float cb = YCbCr_block[j,1];
+ float cr = YCbCr_block[j,2];
+
+ // Full-range YCbCr->RGB conversion
+ //
+ // | 1.0 0.0 1.402 | | Y |
+ // | 1.0 -0.34414 -0.71414 | x | Cb - 128 |
+ // | 1.0 1.772 0.0 | | Cr - 128 |
+ var r = cy + 1.402f * cr - 178.956f;
+ var g = cy - 0.34414f * cb - 0.71414f * cr + 135.95984f;
+ var b = cy + 1.772f * cb - 226.316f;
+
+ int y = j >> 3;
+ int x = j & 7;
+ int p = (y * Width + x) * 4;
+ Output[dst+p] = FloatToByte (b);
+ Output[dst+p+1] = FloatToByte (g);
+ Output[dst+p+2] = FloatToByte (r);
+ }
+ dst += 32;
+ }
+ }
+
+ void DecodeGrayscale (short[] data, int dst)
+ {
+ int src = 0;
+ int block_count = Width / 8;
+ for (int i = 0; i < block_count; ++i)
+ {
+ DecodeDCT (0, data, src);
+ src += 64;
+ for (int j = 0; j < 64; ++j)
+ {
+ int y = j >> 3;
+ int x = j & 7;
+ int p = (y * Width + x) * 4;
+ Output[dst+p] = (byte)YCbCr_block[j,0];
+ Output[dst+p+1] = (byte)YCbCr_block[j,0];
+ Output[dst+p+2] = (byte)YCbCr_block[j,0];
+ }
+ dst += 32;
+ }
+ }
+
+ public void UnpackAlpha (int offset)
+ {
+ using (var input = new BinMemoryStream (this.Input, offset, Input.Length-offset))
+ {
+ if (1 != input.ReadInt32())
+ return;
+ int dst = 3;
+ int ctl = 1 << 1;
+ while (dst < Output.Length)
+ {
+ ctl >>= 1;
+ if (1 == ctl)
+ ctl = input.ReadUInt8() | 0x100;
+
+ if (0 != (ctl & 1))
+ {
+ int v = input.ReadUInt16();
+ int x = v & 0x3F;
+ if (x > 0x1F)
+ x |= -0x40;
+ int y = (v >> 6) & 7;
+ if (y != 0)
+ y |= -8;
+ int count = ((v >> 9) & 0x7F) + 3;
+
+ int src = dst + (x + y * Width) * 4;
+ if (src < 0 || src >= dst)
+ return;
+
+ for (int i = 0; i < count; ++i)
+ {
+ Output[dst] = Output[src];
+ src += 4;
+ dst += 4;
+ }
+ }
+ else
+ {
+ Output[dst] = input.ReadUInt8();
+ dst += 4;
+ }
+ }
+ HasAlpha = true;
+ }
+ }
+
+ ThreadLocal s_tmp = new ThreadLocal (() => new float[8,8]);
+
+ void DecodeDCT (int channel, short[] data, int src)
+ {
+ float v1, v2, v3, v4, v5, v6, v7, v8;
+ float v9, v10, v11, v12, v13, v14, v15, v16, v17;
+ int d = channel > 0 ? 1 : 0;
+ var tmp = s_tmp.Value;
+
+ for (int i = 0; i < 8; ++i)
+ {
+ if (0 == data[src + 8 + i] && 0 == data[src + 16 + i] && 0 == data[src + 24 + i]
+ && 0 == data[src + 32 + i] && 0 == data[src + 40 + i] && 0 == data[src + 48 + i]
+ && 0 == data[src + 56 + i])
+ {
+ var t = data[src + i] * DCT[d, i];
+ tmp[0,i] = t;
+ tmp[1,i] = t;
+ tmp[2,i] = t;
+ tmp[3,i] = t;
+ tmp[4,i] = t;
+ tmp[5,i] = t;
+ tmp[6,i] = t;
+ tmp[7,i] = t;
+ continue;
+ }
+
+ v1 = data[src + i] * DCT[d,i];
+ v2 = data[src + 8 + i] * DCT[d, 8 + i];
+ v3 = data[src + 16 + i] * DCT[d, 16 + i];
+ v4 = data[src + 24 + i] * DCT[d, 24 + i];
+ v5 = data[src + 32 + i] * DCT[d, 32 + i];
+ v6 = data[src + 40 + i] * DCT[d, 40 + i];
+ v7 = data[src + 48 + i] * DCT[d, 48 + i];
+ v8 = data[src + 56 + i] * DCT[d, 56 + i];
+
+ v10 = v1 + v5;
+ v11 = v1 - v5;
+ v12 = v3 + v7;
+ v13 = (v3 - v7) * 1.414213562f - v12;
+ v1 = v10 + v12;
+ v7 = v10 - v12;
+ v3 = v11 + v13;
+ v5 = v11 - v13;
+ v14 = v2 + v8;
+ v15 = v2 - v8;
+ v16 = v6 + v4;
+ v17 = v6 - v4;
+ v8 = v14 + v16;
+ v11 = (v14 - v16) * 1.414213562f;
+ v9 = (v17 + v15) * 1.847759065f;
+ v10 = 1.082392200f * v15 - v9;
+ v13 = -2.613125930f * v17 + v9;
+ v6 = v13 - v8;
+ v4 = v11 - v6;
+ v2 = v10 + v4;
+
+ tmp[0,i] = v1 + v8;
+ tmp[1,i] = v3 + v6;
+ tmp[2,i] = v5 + v4;
+ tmp[3,i] = v7 - v2;
+ tmp[4,i] = v7 + v2;
+ tmp[5,i] = v5 - v4;
+ tmp[6,i] = v3 - v6;
+ tmp[7,i] = v1 - v8;
+ }
+ int dst = 0;
+ for (int i = 0; i < 8; ++i)
+ {
+ v10 = tmp[i,0] + tmp[i,4];
+ v11 = tmp[i,0] - tmp[i,4];
+ v12 = tmp[i,2] + tmp[i,6];
+ v13 = tmp[i,2] - tmp[i,6];
+ v14 = tmp[i,1] + tmp[i,7];
+ v15 = tmp[i,1] - tmp[i,7];
+ v16 = tmp[i,5] + tmp[i,3];
+ v17 = tmp[i,5] - tmp[i,3];
+
+ v13 = 1.414213562f * v13 - v12;
+ v1 = v10 + v12;
+ v7 = v10 - v12;
+ v3 = v11 + v13;
+ v5 = v11 - v13;
+ v8 = v14 + v16;
+ v11 = (v14 - v16) * 1.414213562f;
+ v9 = (v17 + v15) * 1.847759065f;
+ v10 = v9 - v15 * 1.082392200f;
+ v13 = v9 - v17 * 2.613125930f;
+ v6 = v13 - v8;
+ v4 = v11 - v6;
+ v2 = v10 - v4;
+
+ YCbCr_block[dst++, channel] = FloatToShort (v1 + v8);
+ YCbCr_block[dst++, channel] = FloatToShort (v3 + v6);
+ YCbCr_block[dst++, channel] = FloatToShort (v5 + v4);
+ YCbCr_block[dst++, channel] = FloatToShort (v7 + v2);
+ YCbCr_block[dst++, channel] = FloatToShort (v7 - v2);
+ YCbCr_block[dst++, channel] = FloatToShort (v5 - v4);
+ YCbCr_block[dst++, channel] = FloatToShort (v3 - v6);
+ YCbCr_block[dst++, channel] = FloatToShort (v1 - v8);
+ }
+ }
+
+ static short FloatToShort (float f)
+ {
+ int a = 0x80 + (((int)f) >> 3);
+ if (a <= 0)
+ return 0;
+ if (a <= 0xFF)
+ return (short)a;
+ if (a < 0x180)
+ return 0xFF;
+ return 0;
+ }
+
+ static byte FloatToByte (float f)
+ {
+ if (f >= 0xFF)
+ return 0xFF;
+ if (f <= 0)
+ return 0;
+ return (byte)f;
+ }
+
+ #region IDisposable Members
+ bool _disposed = false;
+ public void Dispose ()
+ {
+ if (!_disposed)
+ {
+ s_YCbCr_block.Dispose();
+ s_tmp.Dispose();
+ _disposed = true;
+ }
+ }
+ #endregion
+ }
+}