+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Hjg.Pngcs;
+
+namespace Automap
+{
+ public class Snapshotter
+ {
+ public string path;
+ public string chunkPath;
+ public ColumnsMetadata cols;
+ public int chunkSize;
+ public int NorthEdge => cols.North_mostChunk;
+ public int WestEdge => cols.West_mostChunk;
+ public int Width => (cols.East_mostChunk - WestEdge + 1);
+ public int Height => (cols.South_mostChunk - NorthEdge + 1);
+
+ public Snapshotter(string path, string chunkPath, ColumnsMetadata cols, int chunkSize)
+ {
+ this.path = Path.Combine(path, "snapshot.png");
+ this.chunkPath = Path.Combine(path, chunkPath);
+ this.cols = cols;
+ this.chunkSize = chunkSize;
+ }
+
+ /// <summary>
+ /// takes a snapshot. this should be called from an extra thread.
+ /// </summary>
+ /// <param name="path">path to the map dir</param>
+ /// <param name="chunkPath">name of the chunks dir part thing</param>
+ /// <param name="cols"></param>
+ /// <param name="chunkSize"></param>
+ public async void Take()
+ {
+ var t = new Stopwatch();
+ t.Start();
+ Console.WriteLine("snapshot started");
+
+ ImageInfo info = new ImageInfo(Width * chunkSize, Height * chunkSize, 8, false);
+ PngWriter snapWriter = FileHelper.CreatePngWriter(path, info, true);
+ snapWriter.CompLevel = 5;
+ snapWriter.CompressionStrategy = Hjg.Pngcs.Zlib.EDeflateCompressStrategy.Huffman;
+
+ var orderedList =
+ from ch in cols
+ group ch by ch.Location.Y into g
+ orderby g.Key
+ select g;
+ // that sorts things in ascending order so we can only create (chunkSize) lines at once
+ int row = 0;
+ foreach (var chunkGroup in orderedList)
+ {
+ var posY = chunkGroup.Key - NorthEdge;
+ var inGroup = (await ReadAllInGroup(chunkGroup)).ToArray();
+ // oh god here we go...
+ for (int r = 0; r < chunkSize; r++)
+ {
+ var line = new byte[info.BytesPerRow];
+ for (int chunk = 0; chunk < inGroup.Length; chunk++)
+ {
+ var posX = inGroup[chunk].Key * chunkSize * info.BytesPixel;
+ inGroup[chunk].Value?[r].CopyTo(line, posX);
+ }
+ snapWriter.WriteRowByte(line, row++);
+ }
+ }
+ snapWriter.ShouldCloseStream = true;
+
+ Console.WriteLine($"snapshot finished in {t.ElapsedMilliseconds}");
+ }
+
+ private async Task<Dictionary<int, byte[][]>> ReadAllInGroup(IGrouping<int, ColumnMeta> group)
+ {
+ var taskGroup = new Dictionary<int, byte[][]>(group.Count());
+ foreach (var shardMeta in group)
+ {
+ var shardPath = Path.Combine(chunkPath, $"{shardMeta.Location.X}_{shardMeta.Location.Y}.png");
+ taskGroup.Add(shardMeta.Location.X - WestEdge, await ReadNoThrow(shardPath));
+ }
+ return taskGroup;
+ }
+
+ private async Task<byte[][]> ReadNoThrow(string readPath)
+ {
+ try
+ {
+ return await Task.Run(() => FileHelper.CreatePngReader(readPath).ReadRowsByte().ScanlinesB);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e.Message);
+ return null;
+ } // do nothing on error
+
+ }
+ }
+}