diff options
author | onefang | 2019-09-11 16:36:50 +1000 |
---|---|---|
committer | onefang | 2019-09-11 16:36:50 +1000 |
commit | 50cd1ffd32f69228e566f2b0b89f86ea0d9fe489 (patch) | |
tree | 52f2ab0c04f1a5d7d6ac5dc872981b4b156447e7 /OpenSim/Services/MapImageService/MapImageService.cs | |
parent | Renamed branch to SledjChisl. (diff) | |
parent | Bump to release flavour, build 0. (diff) | |
download | opensim-SC_OLD-50cd1ffd32f69228e566f2b0b89f86ea0d9fe489.zip opensim-SC_OLD-50cd1ffd32f69228e566f2b0b89f86ea0d9fe489.tar.gz opensim-SC_OLD-50cd1ffd32f69228e566f2b0b89f86ea0d9fe489.tar.bz2 opensim-SC_OLD-50cd1ffd32f69228e566f2b0b89f86ea0d9fe489.tar.xz |
Merge branch 'SledjChisl'
Diffstat (limited to 'OpenSim/Services/MapImageService/MapImageService.cs')
-rw-r--r-- | OpenSim/Services/MapImageService/MapImageService.cs | 129 |
1 files changed, 78 insertions, 51 deletions
diff --git a/OpenSim/Services/MapImageService/MapImageService.cs b/OpenSim/Services/MapImageService/MapImageService.cs index 6b4a91a..e9db41d 100644 --- a/OpenSim/Services/MapImageService/MapImageService.cs +++ b/OpenSim/Services/MapImageService/MapImageService.cs | |||
@@ -23,8 +23,8 @@ | |||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | * | 26 | * |
27 | * The design of this map service is based on SimianGrid's PHP-based | 27 | * The design of this map service is based on SimianGrid's PHP-based |
28 | * map service. See this URL for the original PHP version: | 28 | * map service. See this URL for the original PHP version: |
29 | * https://github.com/openmetaversefoundation/simiangrid/ | 29 | * https://github.com/openmetaversefoundation/simiangrid/ |
30 | */ | 30 | */ |
@@ -63,12 +63,14 @@ namespace OpenSim.Services.MapImageService | |||
63 | private const int HALF_WIDTH = 128; | 63 | private const int HALF_WIDTH = 128; |
64 | private const int JPEG_QUALITY = 80; | 64 | private const int JPEG_QUALITY = 80; |
65 | 65 | ||
66 | private static string m_TilesStoragePath = "maptiles"; | 66 | private static string m_TilesStoragePath = Path.Combine(Util.cacheDir(), "/maptiles"); |
67 | 67 | ||
68 | private static object m_Sync = new object(); | 68 | private static object m_Sync = new object(); |
69 | private static bool m_Initialized = false; | 69 | private static bool m_Initialized = false; |
70 | private static string m_WaterTileFile = string.Empty; | 70 | private static string m_WaterTileFile = string.Empty; |
71 | private static Color m_Watercolor = Color.FromArgb(29, 71, 95); | 71 | private static Color m_Watercolor = Color.FromArgb(29, 71, 95); |
72 | private static Bitmap m_WaterBitmap = null; | ||
73 | private static byte[] m_WaterBytes = null; | ||
72 | 74 | ||
73 | public MapImageService(IConfigSource config) | 75 | public MapImageService(IConfigSource config) |
74 | { | 76 | { |
@@ -80,7 +82,7 @@ namespace OpenSim.Services.MapImageService | |||
80 | IConfig serviceConfig = config.Configs["MapImageService"]; | 82 | IConfig serviceConfig = config.Configs["MapImageService"]; |
81 | if (serviceConfig != null) | 83 | if (serviceConfig != null) |
82 | { | 84 | { |
83 | m_TilesStoragePath = "../caches/" + serviceConfig.GetString("TilesStoragePath", m_TilesStoragePath); | 85 | m_TilesStoragePath = serviceConfig.GetString("TilesStoragePath", m_TilesStoragePath); |
84 | if (!Directory.Exists(m_TilesStoragePath)) | 86 | if (!Directory.Exists(m_TilesStoragePath)) |
85 | Directory.CreateDirectory(m_TilesStoragePath); | 87 | Directory.CreateDirectory(m_TilesStoragePath); |
86 | 88 | ||
@@ -91,6 +93,18 @@ namespace OpenSim.Services.MapImageService | |||
91 | Bitmap waterTile = new Bitmap(IMAGE_WIDTH, IMAGE_WIDTH); | 93 | Bitmap waterTile = new Bitmap(IMAGE_WIDTH, IMAGE_WIDTH); |
92 | FillImage(waterTile, m_Watercolor); | 94 | FillImage(waterTile, m_Watercolor); |
93 | waterTile.Save(m_WaterTileFile, ImageFormat.Jpeg); | 95 | waterTile.Save(m_WaterTileFile, ImageFormat.Jpeg); |
96 | m_WaterBitmap = waterTile; | ||
97 | } | ||
98 | |||
99 | if (File.Exists(m_WaterTileFile)) | ||
100 | { | ||
101 | m_WaterBitmap = new Bitmap(m_WaterTileFile); | ||
102 | using (MemoryStream ms = new MemoryStream()) | ||
103 | { | ||
104 | m_WaterBitmap.Save(ms,ImageFormat.Jpeg); | ||
105 | ms.Seek(0, SeekOrigin.Begin); | ||
106 | m_WaterBytes = ms.ToArray(); | ||
107 | } | ||
94 | } | 108 | } |
95 | } | 109 | } |
96 | } | 110 | } |
@@ -98,10 +112,10 @@ namespace OpenSim.Services.MapImageService | |||
98 | 112 | ||
99 | #region IMapImageService | 113 | #region IMapImageService |
100 | 114 | ||
101 | public bool AddMapTile(int x, int y, byte[] imageData, out string reason) | 115 | public bool AddMapTile(int x, int y, byte[] imageData, UUID scopeID, out string reason) |
102 | { | 116 | { |
103 | reason = string.Empty; | 117 | reason = string.Empty; |
104 | string fileName = GetFileName(1, x, y); | 118 | string fileName = GetFileName(1, x, y, scopeID); |
105 | 119 | ||
106 | lock (m_Sync) | 120 | lock (m_Sync) |
107 | { | 121 | { |
@@ -118,13 +132,13 @@ namespace OpenSim.Services.MapImageService | |||
118 | } | 132 | } |
119 | } | 133 | } |
120 | 134 | ||
121 | return UpdateMultiResolutionFilesAsync(x, y, out reason); | 135 | return UpdateMultiResolutionFiles(x, y, scopeID, out reason); |
122 | } | 136 | } |
123 | 137 | ||
124 | public bool RemoveMapTile(int x, int y, out string reason) | 138 | public bool RemoveMapTile(int x, int y, UUID scopeID, out string reason) |
125 | { | 139 | { |
126 | reason = String.Empty; | 140 | reason = String.Empty; |
127 | string fileName = GetFileName(1, x, y); | 141 | string fileName = GetFileName(1, x, y, scopeID); |
128 | 142 | ||
129 | lock (m_Sync) | 143 | lock (m_Sync) |
130 | { | 144 | { |
@@ -134,15 +148,16 @@ namespace OpenSim.Services.MapImageService | |||
134 | } | 148 | } |
135 | catch (Exception e) | 149 | catch (Exception e) |
136 | { | 150 | { |
151 | |||
137 | m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to save delete file {0}: {1}", fileName, e); | 152 | m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to save delete file {0}: {1}", fileName, e); |
138 | reason = e.Message; | 153 | reason = e.Message; |
139 | return false; | 154 | return false; |
140 | } | 155 | } |
141 | } | 156 | } |
142 | 157 | return UpdateMultiResolutionFiles(x, y, scopeID, out reason); | |
143 | return UpdateMultiResolutionFilesAsync(x, y, out reason); | ||
144 | } | 158 | } |
145 | 159 | ||
160 | |||
146 | // When large varregions start up, they can send piles of new map tiles. This causes | 161 | // When large varregions start up, they can send piles of new map tiles. This causes |
147 | // this multi-resolution routine to be called a zillion times an causes much CPU | 162 | // this multi-resolution routine to be called a zillion times an causes much CPU |
148 | // time to be spent creating multi-resolution tiles that will be replaced when | 163 | // time to be spent creating multi-resolution tiles that will be replaced when |
@@ -151,23 +166,27 @@ namespace OpenSim.Services.MapImageService | |||
151 | { | 166 | { |
152 | public int xx; | 167 | public int xx; |
153 | public int yy; | 168 | public int yy; |
154 | public mapToMultiRez(int pX, int pY) | 169 | public UUID scopeID; |
170 | public mapToMultiRez(int pX, int pY, UUID pscopeID) | ||
155 | { | 171 | { |
156 | xx = pX; | 172 | xx = pX; |
157 | yy = pY; | 173 | yy = pY; |
174 | scopeID = pscopeID; | ||
158 | } | 175 | } |
159 | }; | 176 | }; |
160 | private Queue<mapToMultiRez> multiRezToBuild = new Queue<mapToMultiRez>(); | 177 | private Queue<mapToMultiRez> multiRezToBuild = new Queue<mapToMultiRez>(); |
161 | private bool UpdateMultiResolutionFilesAsync(int x, int y, out string reason) | 178 | |
179 | private bool UpdateMultiResolutionFiles(int x, int y, UUID scopeID, out string reason) | ||
162 | { | 180 | { |
163 | reason = String.Empty; | 181 | reason = String.Empty; |
182 | |||
164 | lock (multiRezToBuild) | 183 | lock (multiRezToBuild) |
165 | { | 184 | { |
166 | // m_log.DebugFormat("{0} UpdateMultiResolutionFilesAsync: scheduling update for <{1},{2}>", LogHeader, x, y); | 185 | // m_log.DebugFormat("{0} UpdateMultiResolutionFilesAsync: scheduling update for <{1},{2}>", LogHeader, x, y); |
167 | multiRezToBuild.Enqueue(new mapToMultiRez(x, y)); | 186 | multiRezToBuild.Enqueue(new mapToMultiRez(x, y, scopeID)); |
168 | if (multiRezToBuild.Count == 1) | 187 | if (multiRezToBuild.Count == 1) |
169 | Util.FireAndForget( | 188 | Util.FireAndForget( |
170 | DoUpdateMultiResolutionFilesAsync, null, "MapImageService.DoUpdateMultiResolutionFilesAsync"); | 189 | DoUpdateMultiResolutionFilesAsync); |
171 | } | 190 | } |
172 | 191 | ||
173 | return true; | 192 | return true; |
@@ -175,10 +194,8 @@ namespace OpenSim.Services.MapImageService | |||
175 | 194 | ||
176 | private void DoUpdateMultiResolutionFilesAsync(object o) | 195 | private void DoUpdateMultiResolutionFilesAsync(object o) |
177 | { | 196 | { |
178 | // This sleep causes the FireAndForget thread to be different than the invocation thread. | 197 | // let acumulate large region tiles |
179 | // It also allows other tiles to be uploaded so the multi-rez images are more likely | 198 | Thread.Sleep(60 * 1000); // large regions take time to upload tiles |
180 | // to be correct. | ||
181 | Thread.Sleep(1 * 1000); | ||
182 | 199 | ||
183 | while (multiRezToBuild.Count > 0) | 200 | while (multiRezToBuild.Count > 0) |
184 | { | 201 | { |
@@ -192,20 +209,23 @@ namespace OpenSim.Services.MapImageService | |||
192 | { | 209 | { |
193 | int x = toMultiRez.xx; | 210 | int x = toMultiRez.xx; |
194 | int y = toMultiRez.yy; | 211 | int y = toMultiRez.yy; |
212 | UUID scopeID = toMultiRez.scopeID; | ||
195 | // m_log.DebugFormat("{0} DoUpdateMultiResolutionFilesAsync: doing build for <{1},{2}>", LogHeader, x, y); | 213 | // m_log.DebugFormat("{0} DoUpdateMultiResolutionFilesAsync: doing build for <{1},{2}>", LogHeader, x, y); |
196 | 214 | ||
215 | int width = 1; | ||
216 | |||
197 | // Stitch seven more aggregate tiles together | 217 | // Stitch seven more aggregate tiles together |
198 | for (uint zoomLevel = 2; zoomLevel <= ZOOM_LEVELS; zoomLevel++) | 218 | for (uint zoomLevel = 2; zoomLevel <= ZOOM_LEVELS; zoomLevel++) |
199 | { | 219 | { |
200 | // Calculate the width (in full resolution tiles) and bottom-left | 220 | // Calculate the width (in full resolution tiles) and bottom-left |
201 | // corner of the current zoom level | 221 | // corner of the current zoom level |
202 | int width = (int)Math.Pow(2, (double)(zoomLevel - 1)); | 222 | width *= 2; |
203 | int x1 = x - (x % width); | 223 | int x1 = x - (x % width); |
204 | int y1 = y - (y % width); | 224 | int y1 = y - (y % width); |
205 | 225 | ||
206 | lock (m_Sync) // must lock the reading and writing of the maptile files | 226 | lock (m_Sync) // must lock the reading and writing of the maptile files |
207 | { | 227 | { |
208 | if (!CreateTile(zoomLevel, x1, y1)) | 228 | if (!CreateTile(zoomLevel, x1, y1, scopeID)) |
209 | { | 229 | { |
210 | m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to create tile for {0},{1} at zoom level {1}", x, y, zoomLevel); | 230 | m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to create tile for {0},{1} at zoom level {1}", x, y, zoomLevel); |
211 | return; | 231 | return; |
@@ -214,25 +234,25 @@ namespace OpenSim.Services.MapImageService | |||
214 | } | 234 | } |
215 | } | 235 | } |
216 | } | 236 | } |
217 | |||
218 | return; | 237 | return; |
219 | } | 238 | } |
220 | 239 | ||
221 | public byte[] GetMapTile(string fileName, out string format) | 240 | public byte[] GetMapTile(string fileName, UUID scopeID, out string format) |
222 | { | 241 | { |
223 | // m_log.DebugFormat("[MAP IMAGE SERVICE]: Getting map tile {0}", fileName); | 242 | // m_log.DebugFormat("[MAP IMAGE SERVICE]: Getting map tile {0}", fileName); |
224 | 243 | ||
225 | format = ".jpg"; | 244 | format = ".jpg"; |
226 | string fullName = Path.Combine(m_TilesStoragePath, fileName); | 245 | string fullName = Path.Combine(m_TilesStoragePath, scopeID.ToString()); |
246 | fullName = Path.Combine(fullName, fileName); | ||
227 | if (File.Exists(fullName)) | 247 | if (File.Exists(fullName)) |
228 | { | 248 | { |
229 | format = Path.GetExtension(fileName).ToLower(); | 249 | format = Path.GetExtension(fileName).ToLower(); |
230 | //m_log.DebugFormat("[MAP IMAGE SERVICE]: Found file {0}, extension {1}", fileName, format); | 250 | //m_log.DebugFormat("[MAP IMAGE SERVICE]: Found file {0}, extension {1}", fileName, format); |
231 | return File.ReadAllBytes(fullName); | 251 | return File.ReadAllBytes(fullName); |
232 | } | 252 | } |
233 | else if (File.Exists(m_WaterTileFile)) | 253 | else if (m_WaterBytes != null) |
234 | { | 254 | { |
235 | return File.ReadAllBytes(m_WaterTileFile); | 255 | return (byte[])m_WaterBytes.Clone(); |
236 | } | 256 | } |
237 | else | 257 | else |
238 | { | 258 | { |
@@ -244,10 +264,12 @@ namespace OpenSim.Services.MapImageService | |||
244 | #endregion | 264 | #endregion |
245 | 265 | ||
246 | 266 | ||
247 | private string GetFileName(uint zoomLevel, int x, int y) | 267 | private string GetFileName(uint zoomLevel, int x, int y, UUID scopeID) |
248 | { | 268 | { |
249 | string extension = "jpg"; | 269 | string extension = "jpg"; |
250 | return Path.Combine(m_TilesStoragePath, string.Format("map-{0}-{1}-{2}-objects.{3}", zoomLevel, x, y, extension)); | 270 | string path = Path.Combine(m_TilesStoragePath, scopeID.ToString()); |
271 | Directory.CreateDirectory(path); | ||
272 | return Path.Combine(path, string.Format("map-{0}-{1}-{2}-objects.{3}", zoomLevel, x, y, extension)); | ||
251 | } | 273 | } |
252 | 274 | ||
253 | private Bitmap GetInputTileImage(string fileName) | 275 | private Bitmap GetInputTileImage(string fileName) |
@@ -269,14 +291,14 @@ namespace OpenSim.Services.MapImageService | |||
269 | { | 291 | { |
270 | try | 292 | try |
271 | { | 293 | { |
272 | if (File.Exists(fileName)) | 294 | if (File.Exists(fileName)) |
273 | return new Bitmap(fileName); | 295 | return new Bitmap(fileName); |
274 | 296 | ||
275 | else | 297 | else |
276 | { | 298 | { |
277 | // Create a new output tile with a transparent background | 299 | // Create a new output tile with a transparent background |
278 | Bitmap bm = new Bitmap(IMAGE_WIDTH, IMAGE_WIDTH, PixelFormat.Format24bppRgb); | 300 | Bitmap bm = new Bitmap(IMAGE_WIDTH, IMAGE_WIDTH, PixelFormat.Format24bppRgb); |
279 | bm.MakeTransparent(); | 301 | //bm.MakeTransparent(); // 24bpp does not have transparency, this would make it 32bpp |
280 | return bm; | 302 | return bm; |
281 | } | 303 | } |
282 | } | 304 | } |
@@ -288,7 +310,7 @@ namespace OpenSim.Services.MapImageService | |||
288 | return null; | 310 | return null; |
289 | } | 311 | } |
290 | 312 | ||
291 | private bool CreateTile(uint zoomLevel, int x, int y) | 313 | private bool CreateTile(uint zoomLevel, int x, int y, UUID scopeID) |
292 | { | 314 | { |
293 | // m_log.DebugFormat("[MAP IMAGE SERVICE]: Create tile for {0} {1}, zoom {2}", x, y, zoomLevel); | 315 | // m_log.DebugFormat("[MAP IMAGE SERVICE]: Create tile for {0} {1}, zoom {2}", x, y, zoomLevel); |
294 | int prevWidth = (int)Math.Pow(2, (double)zoomLevel - 2); | 316 | int prevWidth = (int)Math.Pow(2, (double)zoomLevel - 2); |
@@ -303,55 +325,60 @@ namespace OpenSim.Services.MapImageService | |||
303 | int yOut = y - (y % thisWidth); | 325 | int yOut = y - (y % thisWidth); |
304 | 326 | ||
305 | // Try to open the four input tiles from the previous zoom level | 327 | // Try to open the four input tiles from the previous zoom level |
306 | Bitmap inputBL = GetInputTileImage(GetFileName(zoomLevel - 1, xIn, yIn)); | 328 | Bitmap inputBL = GetInputTileImage(GetFileName(zoomLevel - 1, xIn, yIn, scopeID)); |
307 | Bitmap inputBR = GetInputTileImage(GetFileName(zoomLevel - 1, xIn + prevWidth, yIn)); | 329 | Bitmap inputBR = GetInputTileImage(GetFileName(zoomLevel - 1, xIn + prevWidth, yIn, scopeID)); |
308 | Bitmap inputTL = GetInputTileImage(GetFileName(zoomLevel - 1, xIn, yIn + prevWidth)); | 330 | Bitmap inputTL = GetInputTileImage(GetFileName(zoomLevel - 1, xIn, yIn + prevWidth, scopeID)); |
309 | Bitmap inputTR = GetInputTileImage(GetFileName(zoomLevel - 1, xIn + prevWidth, yIn + prevWidth)); | 331 | Bitmap inputTR = GetInputTileImage(GetFileName(zoomLevel - 1, xIn + prevWidth, yIn + prevWidth, scopeID)); |
310 | 332 | ||
311 | // Open the output tile (current zoom level) | 333 | // Open the output tile (current zoom level) |
312 | string outputFile = GetFileName(zoomLevel, xOut, yOut); | 334 | string outputFile = GetFileName(zoomLevel, xOut, yOut, scopeID); |
313 | Bitmap output = GetOutputTileImage(outputFile); | 335 | |
314 | if (output == null) | 336 | int ntiles = 0; |
315 | return false; | 337 | Bitmap output = (Bitmap)m_WaterBitmap.Clone(); |
316 | FillImage(output, m_Watercolor); | ||
317 | 338 | ||
318 | if (inputBL != null) | 339 | if (inputBL != null) |
319 | { | 340 | { |
320 | ImageCopyResampled(output, inputBL, 0, HALF_WIDTH, 0, 0); | 341 | ImageCopyResampled(output, inputBL, 0, HALF_WIDTH, 0, 0); |
321 | inputBL.Dispose(); | 342 | inputBL.Dispose(); |
343 | ntiles++; | ||
322 | } | 344 | } |
323 | if (inputBR != null) | 345 | if (inputBR != null) |
324 | { | 346 | { |
325 | ImageCopyResampled(output, inputBR, HALF_WIDTH, HALF_WIDTH, 0, 0); | 347 | ImageCopyResampled(output, inputBR, HALF_WIDTH, HALF_WIDTH, 0, 0); |
326 | inputBR.Dispose(); | 348 | inputBR.Dispose(); |
349 | ntiles++; | ||
327 | } | 350 | } |
328 | if (inputTL != null) | 351 | if (inputTL != null) |
329 | { | 352 | { |
330 | ImageCopyResampled(output, inputTL, 0, 0, 0, 0); | 353 | ImageCopyResampled(output, inputTL, 0, 0, 0, 0); |
331 | inputTL.Dispose(); | 354 | inputTL.Dispose(); |
355 | ntiles++; | ||
332 | } | 356 | } |
333 | if (inputTR != null) | 357 | if (inputTR != null) |
334 | { | 358 | { |
335 | ImageCopyResampled(output, inputTR, HALF_WIDTH, 0, 0, 0); | 359 | ImageCopyResampled(output, inputTR, HALF_WIDTH, 0, 0, 0); |
336 | inputTR.Dispose(); | 360 | inputTR.Dispose(); |
361 | ntiles++; | ||
337 | } | 362 | } |
338 | 363 | ||
339 | // Write the modified output | 364 | // Write the modified output |
340 | try | 365 | if (ntiles == 0) |
366 | File.Delete(outputFile); | ||
367 | |||
368 | else | ||
341 | { | 369 | { |
342 | using (Bitmap final = new Bitmap(output)) | 370 | |
371 | try | ||
343 | { | 372 | { |
344 | output.Dispose(); | 373 | output.Save(outputFile, ImageFormat.Jpeg); |
345 | final.Save(outputFile, ImageFormat.Jpeg); | ||
346 | } | 374 | } |
347 | } | 375 | catch (Exception e) |
348 | catch (Exception e) | 376 | { |
349 | { | 377 | m_log.WarnFormat("[MAP IMAGE SERVICE]: Oops on saving {0} {1}", outputFile, e); |
350 | m_log.WarnFormat("[MAP IMAGE SERVICE]: Oops on saving {0} {1}", outputFile, e); | 378 | } |
351 | } | 379 | } // Save also as png? |
352 | |||
353 | // Save also as png? | ||
354 | 380 | ||
381 | output.Dispose(); | ||
355 | return true; | 382 | return true; |
356 | } | 383 | } |
357 | 384 | ||