OSDN Git Service

fix text formating error
[automap/automap.git] / Automap / Subsystems / Snapshot.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.IO;
5 using System.Linq;
6 using System.Threading;
7 using System.Threading.Tasks;
8
9 using Hjg.Pngcs;
10 using Hjg.Pngcs.Chunks;
11
12 namespace Automap
13 {
14         public class Snapshotter
15         {
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;
25
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;}
30
31                 /// <summary>
32                 /// Prepares class to take "snapshot" (single use!)
33                 /// </summary>
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)
39                 {                               
40                         colMetadata = cols;
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);
49                 }
50
51                 /// <summary>
52                 /// takes a snapshot. this should be called from an extra thread.
53                 /// </summary>
54                 public async void Take()
55                 {
56                         if (Imaging || Finished) return;//Can't take annother photo...this one is started or complete
57                         Imaging = true;
58                         Timer = new Stopwatch();
59                         Timer.Start();
60
61                         Debug.WriteLine("snapshot started");
62
63                         ImageInfo info = new ImageInfo(Width * chunkSize, Height * chunkSize, 8, false);
64                         PngWriter snapWriter = FileHelper.CreatePngWriter(fileName, info, true);
65                         PngMetadata meta = snapWriter.GetMetadata( );
66                         meta.SetTimeNow( );
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"));
73
74
75                 
76                         /*
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
80                         */
81
82
83                         snapWriter.CompLevel = 5;
84                         snapWriter.CompressionStrategy = Hjg.Pngcs.Zlib.EDeflateCompressStrategy.Filtered;
85
86
87
88                         var orderedList =
89                                 from ch in colMetadata
90                                 group ch by ch.Location.Y into g
91                                 orderby g.Key
92                                 select 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)
96                         {
97                                 // oh god here we go...
98                                 posY = chunkGroup.Key - NorthEdge;
99                                 var delta = posY - lastPosY;
100                                 lastPosY = posY;
101
102                                 // if there is more than 1 blank step then the gap needs to be filled.
103                                 for (int i = 1; i < delta; i++)
104                                 {
105                                         byte[] blankLine = new byte[info.BytesPerRow];
106                                         for (int j = 0; j < chunkSize; j++)
107                                                 snapWriter.WriteRowByte(blankLine, row++);
108                                 }
109
110                                 var inGroup = (await ReadAllInGroup(chunkGroup)).ToArray();
111                                 
112                                 for (int r = 0; r < chunkSize; r++)
113                                 {
114                                         var line = new byte[info.BytesPerRow];
115                                         for (int chunk = 0; chunk < inGroup.Length; chunk++)
116                                         {
117                                                 var posX = inGroup[chunk].Key * chunkSize * info.BytesPixel;
118                                                 inGroup[chunk].Value?[r].CopyTo(line, posX);
119                                         }
120                                         snapWriter.WriteRowByte(line, row++);
121                                 }
122                         }
123                         snapWriter.ShouldCloseStream = true;
124                         try
125                         {
126                                 snapWriter.End();
127                                 Timer.Stop( );
128                                 Imaging = false;
129                                 Finished = true;
130                         }
131                         catch (Exception)
132                         {
133                                 Debug.WriteLine("Snapshot exception!");
134                         }
135                         Debug.WriteLine($"snapshot finished in {Timer.ElapsedMilliseconds}");
136                 }
137
138                 private async Task<Dictionary<int, byte[][]>> ReadAllInGroup(IGrouping<int, ColumnMeta> group)
139                 {
140                         var taskGroup = new Dictionary<int, byte[][]>(group.Count());
141                         foreach (var shardMeta in group)
142                         {
143                                 var shardPath = Path.Combine(chunkPath, $"{shardMeta.Location.X}_{shardMeta.Location.Y}.png");
144                                 taskGroup.Add(shardMeta.Location.X - WestEdge, await ReadNoThrow(shardPath));
145                         }
146                         return taskGroup;
147                 }
148
149                 private async Task<byte[][]> ReadNoThrow(string readPath)
150                 {
151                         try
152                         {
153                                 return await Task.Run(() => FileHelper.CreatePngReader(readPath).ReadRowsByte().ScanlinesB);
154                         }
155                         catch (Exception e)
156                         {
157                                 Debug.WriteLine(e.Message);
158                                 return null;
159                         } // do nothing on error
160                 }
161         }
162 }