diff --git a/ArcFormats/Interheart/ImageKG.cs b/ArcFormats/Interheart/ImageKG.cs index 43677246..4a95ed52 100644 --- a/ArcFormats/Interheart/ImageKG.cs +++ b/ArcFormats/Interheart/ImageKG.cs @@ -26,7 +26,11 @@ using System; using System.ComponentModel.Composition; using System.IO; +using System.Linq; +using System.Collections.Generic; +using System.Windows; using System.Windows.Media; +using System.Windows.Media.Imaging; using GameRes.Utility; namespace GameRes.Formats.Interheart @@ -37,6 +41,7 @@ namespace GameRes.Formats.Interheart public override string Tag { get { return "KG"; } } public override string Description { get { return "Interheart image format"; } } public override uint Signature { get { return 0x4B474347; } } // 'GCGK' + public override bool CanWrite { get { return true; } } public override ImageMetaData ReadMetaData (IBinaryStream stream) { @@ -94,9 +99,78 @@ namespace GameRes.Formats.Interheart return ImageData.Create (info, PixelFormats.Bgra32, null, pixels); } - public override void Write (Stream file, ImageData image) - { - throw new System.NotImplementedException ("KgFormat.Write not implemented"); + public override void Write (Stream file, ImageData image) { + int stride = (int)image.Width * 4; + byte[] bitmap = new byte[stride * image.Height]; + { + var image_bgra = image.Bitmap.Format != PixelFormats.Bgra32 + ? new FormatConvertedBitmap(image.Bitmap, PixelFormats.Bgra32, null, 0) + : image.Bitmap; + image_bgra.CopyPixels(bitmap, stride, 0); + } + uint[] offset_table = new uint[image.Height]; + var kg_data = new List(); + + for (int y = 0; y < image.Height; y++) { + offset_table[y] = (uint)kg_data.Count(); + + bitmap + .AsMemory() + .Slice(y * stride, stride) + .Chunk(4) + .Aggregate(new List<(byte alpha, List data)> { }, (chunks, x) => { + var pixel = x.ToArray(); + void new_chunk () => chunks.Add((alpha: pixel[3], data: new List { pixel })); + + if (!chunks.Any()) { + new_chunk(); + } + else { + var (alpha, data) = chunks.Last(); + if (pixel[3] != data.Last()[3] || data.Count >= 256) { + new_chunk(); + } + else { + data.Add(pixel); + } + } + return chunks; + } + ) + .ForEach(chunk => { + kg_data.Add(chunk.alpha); + kg_data.Add((byte)chunk.data.Count()); + if (chunk.alpha > 0) { + kg_data.AddRange(chunk.data.SelectMany(p => new[] { p[2], p[1], p[0] })); + } + }); + } + + + var bw = new BinaryWriter(file); + + bw.Write(new byte[] { 0x47, 0x43, 0x47, 0x4B }); + bw.Write((UInt16)image.Width); + bw.Write((UInt16)image.Height); + bw.Write((uint)kg_data.Count); + + { + byte[] buf = new byte[offset_table.Length * sizeof(uint)]; + Buffer.BlockCopy(offset_table, 0, buf, 0, buf.Length); + bw.Write(buf); + } + + bw.Write(kg_data.ToArray()); + } + } + + static class Extensions { + public static IEnumerable> Chunk(this Memory arr, int size) { + for (var i = 0; i < arr.Length / size + 1; i++) { + if (i * size < arr.Length) { + yield return arr.Slice(i * size, size); + } + } } } }