2 using System.Collections.Generic;
3 using System.Diagnostics;
6 using System.Threading;
7 using System.Threading.Tasks;
10 using Hjg.Pngcs.Chunks;
14 public class Snapshotter
16 public readonly int chunkSize;
17 private const string customTimeFormat = @"yyyy.MM.dd.HH.mm.ssZzz";
18 public string fileName;
19 public string chunkPath;
20 public ColumnsMetadata cols;
21 //TODO: Refactor - so Edges are set at construction time, as ColumnsMetadata is async updating in real time!
22 public int NorthEdge => cols.North_mostChunk;
23 public int WestEdge => cols.West_mostChunk;
24 public int Width => (cols.East_mostChunk - WestEdge + 1);
25 public int Height => (cols.South_mostChunk - NorthEdge + 1);
28 public Snapshotter(string path, ColumnsMetadata cols, int chunkSize, int worldSeed)
30 this.fileName = Path.Combine(path, $"snapshot_{worldSeed}_{DateTime.Now.ToString(customTimeFormat)}.png");
31 this.chunkPath = Path.Combine(path, AutomapSystem._chunkPath);
33 this.chunkSize = chunkSize;
37 /// takes a snapshot. this should be called from an extra thread.
39 /// <param name="path">path to the map dir</param>
40 /// <param name="chunkPath">name of the chunks dir part thing</param>
41 /// <param name="cols"></param>
42 /// <param name="chunkSize"></param>
43 public async void Take()
45 var t = new Stopwatch();
48 Debug.WriteLine("snapshot started");
50 ImageInfo info = new ImageInfo(Width * chunkSize, Height * chunkSize, 8, false);
51 PngWriter snapWriter = FileHelper.CreatePngWriter(fileName, info, true);
52 PngMetadata meta = snapWriter.GetMetadata( );
54 var transparencyChunk = meta.CreateTRNSChunk( );
55 transparencyChunk.SetRGB(0, 0, 0);//CHECK: This is pure black.
56 meta.SetText("Northmost", NorthEdge.ToString("D"));
57 meta.SetText("Eastmost", WestEdge.ToString("D"));
58 meta.SetText("Width", Width.ToString("D"));
59 meta.SetText("Height", Height.ToString("D"));
64 Red: 2 bytes, range 0 .. (2^bitdepth)-1
65 Green: 2 bytes, range 0 .. (2^bitdepth)-1
66 Blue: 2 bytes, range 0 .. (2^bitdepth)-1
70 snapWriter.CompLevel = 5;
71 snapWriter.CompressionStrategy = Hjg.Pngcs.Zlib.EDeflateCompressStrategy.Filtered;
77 group ch by ch.Location.Y into g
80 // that sorts things in ascending order so we can only create (chunkSize) lines at once
81 int row = 0, lastPosY = 0, posY = 0;
82 foreach (var chunkGroup in orderedList)
84 // oh god here we go...
85 posY = chunkGroup.Key - NorthEdge;
86 var delta = posY - lastPosY;
89 // if there is more than 1 blank step then the gap needs to be filled.
90 for (int i = 1; i < delta; i++)
92 byte[] blankLine = new byte[info.BytesPerRow];
93 for (int j = 0; j < chunkSize; j++)
94 snapWriter.WriteRowByte(blankLine, row++);
97 var inGroup = (await ReadAllInGroup(chunkGroup)).ToArray();
99 for (int r = 0; r < chunkSize; r++)
101 var line = new byte[info.BytesPerRow];
102 for (int chunk = 0; chunk < inGroup.Length; chunk++)
104 var posX = inGroup[chunk].Key * chunkSize * info.BytesPixel;
105 inGroup[chunk].Value?[r].CopyTo(line, posX);
107 snapWriter.WriteRowByte(line, row++);
110 snapWriter.ShouldCloseStream = true;
117 Debug.WriteLine("Snapshot exception!");
119 Debug.WriteLine($"snapshot finished in {t.ElapsedMilliseconds}");
122 private async Task<Dictionary<int, byte[][]>> ReadAllInGroup(IGrouping<int, ColumnMeta> group)
124 var taskGroup = new Dictionary<int, byte[][]>(group.Count());
125 foreach (var shardMeta in group)
127 var shardPath = Path.Combine(chunkPath, $"{shardMeta.Location.X}_{shardMeta.Location.Y}.png");
128 taskGroup.Add(shardMeta.Location.X - WestEdge, await ReadNoThrow(shardPath));
133 private async Task<byte[][]> ReadNoThrow(string readPath)
137 return await Task.Run(() => FileHelper.CreatePngReader(readPath).ReadRowsByte().ScanlinesB);
141 Debug.WriteLine(e.Message);
143 } // do nothing on error