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 private const string customTimeFormat = @"yyyy.MM.dd.HH.mm.ssZzz";
17 public readonly int chunkSize;
18 public readonly int worldSeed;
19 public readonly string fileName;
20 public readonly string chunkPath;
21 public readonly int NorthEdge;
22 public readonly int WestEdge;
23 public readonly int Width;
24 public readonly int Height;
26 public ColumnsMetadata colMetadata;
27 public bool Imaging { get; private set; }
28 public bool Finished { get; private set; }
29 public Stopwatch Timer { get; private set;}
32 /// Prepares class to take "snapshot" (single use!)
34 /// <param name="path">path to the map dir</param>
35 /// <param name="cols">Input metadata</param>
36 /// <param name="chunkSizeP">size in blocks of std. chunk</param>
37 /// <param name="worldSeedP">World Seed #</param>
38 public Snapshotter(string path, ColumnsMetadata cols, int chunkSizeP, int worldSeedP)
41 chunkSize = chunkSizeP;
42 worldSeed = worldSeedP;
43 NorthEdge = cols.North_mostChunk;
44 WestEdge = cols.West_mostChunk;
45 Width = (cols.East_mostChunk - WestEdge + 1);
46 Height = (cols.South_mostChunk - NorthEdge + 1);
47 fileName = Path.Combine(path, $"snapshot_{worldSeed}_{DateTime.Now.ToString(customTimeFormat)}.png");
48 chunkPath = Path.Combine(path, AutomapSystem._chunkPath);
52 /// takes a snapshot. this should be called from an extra thread.
54 public async void Take()
56 if (Imaging || Finished) return;//Can't take annother photo...this one is started or complete
58 Timer = new Stopwatch();
61 Debug.WriteLine("snapshot started");
63 ImageInfo info = new ImageInfo(Width * chunkSize, Height * chunkSize, 8, false);
64 PngWriter snapWriter = FileHelper.CreatePngWriter(fileName, info, true);
65 PngMetadata meta = snapWriter.GetMetadata( );
67 var transparencyChunk = meta.CreateTRNSChunk( );
68 transparencyChunk.SetRGB(0, 0, 0);//CHECK: This is pure black.
69 meta.SetText("Northmost", NorthEdge.ToString("D"));
70 meta.SetText("Eastmost", WestEdge.ToString("D"));
71 meta.SetText("Width", Width.ToString("D"));
72 meta.SetText("Height", Height.ToString("D"));
77 Red: 2 bytes, range 0 .. (2^bitdepth)-1
78 Green: 2 bytes, range 0 .. (2^bitdepth)-1
79 Blue: 2 bytes, range 0 .. (2^bitdepth)-1
83 snapWriter.CompLevel = 5;
84 snapWriter.CompressionStrategy = Hjg.Pngcs.Zlib.EDeflateCompressStrategy.Filtered;
89 from ch in colMetadata
90 group ch by ch.Location.Y into g
93 // that sorts things in ascending order so we can only create (chunkSize) lines at once
94 int row = 0, lastPosY = 0, posY = 0;
95 foreach (var chunkGroup in orderedList)
97 // oh god here we go...
98 posY = chunkGroup.Key - NorthEdge;
99 var delta = posY - lastPosY;
102 // if there is more than 1 blank step then the gap needs to be filled.
103 for (int i = 1; i < delta; i++)
105 byte[] blankLine = new byte[info.BytesPerRow];
106 for (int j = 0; j < chunkSize; j++)
107 snapWriter.WriteRowByte(blankLine, row++);
110 var inGroup = (await ReadAllInGroup(chunkGroup)).ToArray();
112 for (int r = 0; r < chunkSize; r++)
114 var line = new byte[info.BytesPerRow];
115 for (int chunk = 0; chunk < inGroup.Length; chunk++)
117 var posX = inGroup[chunk].Key * chunkSize * info.BytesPixel;
118 inGroup[chunk].Value?[r].CopyTo(line, posX);
120 snapWriter.WriteRowByte(line, row++);
123 snapWriter.ShouldCloseStream = true;
133 Debug.WriteLine("Snapshot exception!");
135 Debug.WriteLine($"snapshot finished in {Timer.ElapsedMilliseconds}");
138 private async Task<Dictionary<int, byte[][]>> ReadAllInGroup(IGrouping<int, ColumnMeta> group)
140 var taskGroup = new Dictionary<int, byte[][]>(group.Count());
141 foreach (var shardMeta in group)
143 var shardPath = Path.Combine(chunkPath, $"{shardMeta.Location.X}_{shardMeta.Location.Y}.png");
144 taskGroup.Add(shardMeta.Location.X - WestEdge, await ReadNoThrow(shardPath));
149 private async Task<byte[][]> ReadNoThrow(string readPath)
153 return await Task.Run(() => FileHelper.CreatePngReader(readPath).ReadRowsByte().ScanlinesB);
157 Debug.WriteLine(e.Message);
159 } // do nothing on error