diff --git a/ArcFormats/YuRis/ImageYDG.cs b/ArcFormats/YuRis/ImageYDG.cs index afa11e81..6f408479 100644 --- a/ArcFormats/YuRis/ImageYDG.cs +++ b/ArcFormats/YuRis/ImageYDG.cs @@ -23,15 +23,14 @@ // IN THE SOFTWARE. // -using System; -using System.ComponentModel; -using System.ComponentModel.Composition; -using System.IO; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Windows.Media; using GameRes.Formats.QoiCodec; using GameRes.Utility; +using System; +using System.ComponentModel.Composition; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using System.Windows.Media; namespace GameRes.Formats.YuRis { @@ -40,22 +39,24 @@ namespace GameRes.Formats.YuRis public int HeaderLength; } - internal class YdgTile + internal class YdgSlice { - public uint Offset; - public uint Size; - public ushort X; - public ushort Height; + public uint Offset; + public uint Size; + public byte[] Data; + public int X; + public int Y; + public int Height; } [Export(typeof(ImageFormat))] public class YdgFormat : ImageFormat { - public override string Tag { get { return "YDG"; } } + public override string Tag { get { return "YDG"; } } public override string Description { get { return "YU-RIS compressed image format"; } } - public override uint Signature { get { return 0x00474459; } } // 'YDG\0' + public override uint Signature { get { return 0x00474459; } } // 'YDG\0' - public YdgFormat() + public YdgFormat () { Extensions = new string[] { "ydg" }; } @@ -67,21 +68,21 @@ namespace GameRes.Formats.YuRis return null; if (!header.AsciiEqual (4, "YU-RIS")) return null; - return new YdgMetaData { - Width = header.ToUInt16 (0x20), - Height = header.ToUInt16 (0x22), - BPP = 32, + Width = header.ToUInt16 (0x20), + Height = header.ToUInt16 (0x22), + BPP = 32, HeaderLength = header.ToInt32 (0x10), }; } public override ImageData Read (IBinaryStream stream, ImageMetaData info) { - var reader = new YdgReader(stream, (YdgMetaData)info); - reader.Unpack(); - return ImageData.Create (info, reader.Format, null, reader.Data); + var meta = (YdgMetaData) info; + var reader = new YdgReader (stream, meta); + reader.Unpack (); + return ImageData.Create (meta, reader.Format, null, reader.Data); } public override void Write (Stream file, ImageData image) @@ -92,80 +93,84 @@ namespace GameRes.Formats.YuRis internal sealed class YdgReader { - IBinaryStream m_input; - YdgMetaData m_info; + readonly IBinaryStream m_input; + readonly YdgMetaData m_info; + readonly byte[] m_output; public PixelFormat Format { get; private set; } - public byte[] Data { get { return m_output; } } + public byte[] Data { get { return m_output; } } - public YdgReader(IBinaryStream input, YdgMetaData info) + public YdgReader (IBinaryStream input, YdgMetaData info) { m_input = input; m_info = info; - m_output = new byte[4 * (int)info.Width * (int)info.Height]; + m_output = new byte[4*(int)info.Width*(int)info.Height]; Format = PixelFormats.Bgra32; } - byte[] m_output; public void Unpack () { - var tiles = ReadIndex (); - int width = (int)m_info.Width; - int height = (int)m_info.Height; - int dst_y = 0; - foreach (var tile in tiles) + var slices = ReadSlices (); + var options = new ParallelOptions { MaxDegreeOfParallelism = 4 }; + Parallel.ForEach (slices, options, slice => { - byte[] pixels = ReadTile (tile, out width, out height); - - int tile_height = Math.Min(height, tile.Height); - CopyTile (pixels, width, tile_height, tile.X, dst_y); - dst_y += tile_height; - if (dst_y >= m_info.Height) - break; - } + var pixels = DecodeSlice (slice.Data, out int slice_width, out int slice_height); + if (slice_height > slice.Height) + throw new InvalidFormatException (); + var height = Math.Min (slice_height, slice.Height); + CopySlice (pixels, slice_width, height, slice.X, slice.Y); + }); } - YdgTile[] ReadIndex () + YdgSlice[] ReadSlices () { m_input.Position = m_info.HeaderLength; - int tileCount = m_input.ReadInt32(); - var dir = new YdgTile[tileCount]; - for (int i = 0; i < dir.Length; ++i) + var count = m_input.ReadInt32 (); + if (count <= 0) + throw new InvalidFormatException (); + var slices = new YdgSlice[count]; + var y = 0; + for (var i = 0; i < slices.Length; i++) { - dir[i] = new YdgTile + var slice = new YdgSlice { - Offset = m_input.ReadUInt32(), - Size = m_input.ReadUInt32(), - X = m_input.ReadUInt16(), - Height = m_input.ReadUInt16(), + Offset = m_input.ReadUInt32 (), + Size = m_input.ReadUInt32 (), + X = m_input.ReadUInt16 (), + Y = y, + Height = m_input.ReadUInt16 (), }; - m_input.ReadUInt32(); + y += slice.Height; + m_input.ReadUInt32 (); + slices[i] = slice; } - return dir; + foreach (var slice in slices) + { + m_input.Position = slice.Offset; + slice.Data = m_input.ReadBytes ((int)slice.Size); + } + return slices; } - byte[] ReadTile (YdgTile tile, out int width, out int height) + byte[] DecodeSlice (byte[] data, out int width, out int height) { - m_input.Position = tile.Offset; - var data = m_input.ReadBytes ((int)tile.Size); if (Binary.AsciiEqual (data, 0, "RIFF") && Binary.AsciiEqual (data, 8, "WEBP")) return DecodeWebP (data, out width, out height); return DecodeQoi (data, out width, out height); } - void CopyTile (byte[] pixels, int src_width, int src_height, int dst_x, int dst_y) + void CopySlice (byte[] pixels, int src_width, int src_height, int dst_x, int dst_y) { - int copy_width = Math.Min (src_width, (int)m_info.Width - dst_x); - int copy_height = Math.Min (src_height, (int)m_info.Height - dst_y); + var copy_width = Math.Min (src_width, (int)m_info.Width-dst_x); + var copy_height = Math.Min (src_height, (int)m_info.Height-dst_y); if (copy_width <= 0 || copy_height <= 0) return; - - int src_stride = src_width * 4; - int dst_stride = (int)m_info.Width * 4; - int dst = dst_y * dst_stride + dst_x * 4; - int row_bytes = copy_width * 4; - int src = 0; - for (int y = 0; y < copy_height; ++y) + var src_stride = 4*src_width; + var dst_stride = 4*(int)m_info.Width; + var dst = dst_y*dst_stride + 4*dst_x; + var row_bytes = 4*copy_width; + var src = 0; + for (var y = 0; y < copy_height; y++) { Buffer.BlockCopy (pixels, src, m_output, dst, row_bytes); src += src_stride; @@ -175,23 +180,18 @@ namespace GameRes.Formats.YuRis byte[] DecodeWebP (byte[] data, out int width, out int height) { - WebPCodec.Load (); - width = 0; height = 0; - if (1 != WebPCodec.WebPGetInfo(data, (UIntPtr)data.Length, ref width, ref height)) + WebPCodec.Load (); + if (1 != WebPCodec.WebPGetInfo (data, (UIntPtr)data.Length, ref width, ref height)) throw new InvalidFormatException ("WebP image decoder failed."); - - int stride = width * 4; - var output = new byte[stride * height]; + var stride = 4*width; + var output = new byte[stride*height]; var handle = GCHandle.Alloc (output, GCHandleType.Pinned); try { - if (IntPtr.Zero == WebPCodec.WebPDecodeBGRAInto (data, (UIntPtr)data.Length, - handle.AddrOfPinnedObject (), (UIntPtr)output.Length, stride)) - { + if (IntPtr.Zero == WebPCodec.WebPDecodeBGRAInto (data, (UIntPtr)data.Length, handle.AddrOfPinnedObject (), (UIntPtr)output.Length, stride)) throw new InvalidFormatException ("WebP image decoder failed."); - } } finally { @@ -200,20 +200,19 @@ namespace GameRes.Formats.YuRis return output; } - byte[] DecodeQoi(byte[] data, out int width, out int height) + byte[] DecodeQoi (byte[] data, out int width, out int height) { - using (var input = new BinMemoryStream(data)) + using (var input = new BinMemoryStream (data)) { - if (0x66696F71 != input.ReadInt32()) // "qoif" + if (0x66696F71 != input.ReadInt32 ()) // "qoif" throw new InvalidFormatException ("Invalid QOI signature"); - - width = (int)Binary.BigEndian(input.ReadUInt32()); - height = (int)Binary.BigEndian(input.ReadUInt32()); - int channels = input.ReadByte(); - int colorspace = input.ReadByte(); - var count = 4 * width * height; + width = (int)Binary.BigEndian (input.ReadUInt32 ()); + height = (int)Binary.BigEndian (input.ReadUInt32 ()); + var channels = input.ReadByte (); + var colorspace = input.ReadByte (); + var count = 4*width*height; var output = new byte[count]; - var qoi = new QoiDecodeStream(input); + var qoi = new QoiDecodeStream (input); uint pixel = 0; var run = 0; var dst = 0; @@ -225,10 +224,10 @@ namespace GameRes.Formats.YuRis { run = qoi.Read (out pixel); } - output[dst ] = (byte)pixel; - output[dst + 1] = (byte)(pixel >> 8); - output[dst + 2] = (byte)(pixel >> 16); - output[dst + 3] = (byte)(pixel >> 24); + output[dst ] = (byte)pixel; + output[dst+1] = (byte)(pixel >> 8); + output[dst+2] = (byte)(pixel >> 16); + output[dst+3] = (byte)(pixel >> 24); dst += 4; } return output;