clockwerk-opensim-stable – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 /*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
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
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
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:
29 * https://github.com/openmetaversefoundation/simiangrid/
30 */
31  
32 using System;
33 using System.Collections.Generic;
34 using System.Drawing;
35 using System.Drawing.Imaging;
36 using System.IO;
37 using System.Net;
38 using System.Reflection;
39  
40 using Nini.Config;
41 using log4net;
42 using OpenMetaverse;
43  
44 using OpenSim.Framework;
45 using OpenSim.Framework.Console;
46 using OpenSim.Services.Interfaces;
47  
48  
49 namespace OpenSim.Services.MapImageService
50 {
51 public class MapImageService : IMapImageService
52 {
53 private static readonly ILog m_log =
54 LogManager.GetLogger(
55 MethodBase.GetCurrentMethod().DeclaringType);
56  
57 private const int ZOOM_LEVELS = 8;
58 private const int IMAGE_WIDTH = 256;
59 private const int HALF_WIDTH = 128;
60 private const int JPEG_QUALITY = 80;
61  
62 private static string m_TilesStoragePath = "maptiles";
63  
64 private static object m_Sync = new object();
65 private static bool m_Initialized = false;
66 private static string m_WaterTileFile = string.Empty;
67 private static Color m_Watercolor = Color.FromArgb(29, 71, 95);
68  
69 public MapImageService(IConfigSource config)
70 {
71 if (!m_Initialized)
72 {
73 m_Initialized = true;
74 m_log.Debug("[MAP IMAGE SERVICE]: Starting MapImage service");
75  
76 IConfig serviceConfig = config.Configs["MapImageService"];
77 if (serviceConfig != null)
78 {
79 m_TilesStoragePath = serviceConfig.GetString("TilesStoragePath", m_TilesStoragePath);
80 if (!Directory.Exists(m_TilesStoragePath))
81 Directory.CreateDirectory(m_TilesStoragePath);
82  
83  
84 m_WaterTileFile = Path.Combine(m_TilesStoragePath, "water.jpg");
85 if (!File.Exists(m_WaterTileFile))
86 {
87 Bitmap waterTile = new Bitmap(IMAGE_WIDTH, IMAGE_WIDTH);
88 FillImage(waterTile, m_Watercolor);
89 waterTile.Save(m_WaterTileFile, ImageFormat.Jpeg);
90 }
91 }
92 }
93 }
94  
95 #region IMapImageService
96  
97 public bool AddMapTile(int x, int y, byte[] imageData, out string reason)
98 {
99 reason = string.Empty;
100 string fileName = GetFileName(1, x, y);
101  
102 lock (m_Sync)
103 {
104 try
105 {
106 using (FileStream f = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.Write))
107 f.Write(imageData, 0, imageData.Length);
108 }
109 catch (Exception e)
110 {
111 m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to save image file {0}: {1}", fileName, e);
112 reason = e.Message;
113 return false;
114 }
115  
116 // Also save in png format?
117  
118 // Stitch seven more aggregate tiles together
119 for (uint zoomLevel = 2; zoomLevel <= ZOOM_LEVELS; zoomLevel++)
120 {
121 // Calculate the width (in full resolution tiles) and bottom-left
122 // corner of the current zoom level
123 int width = (int)Math.Pow(2, (double)(zoomLevel - 1));
124 int x1 = x - (x % width);
125 int y1 = y - (y % width);
126  
127 if (!CreateTile(zoomLevel, x1, y1))
128 {
129 m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to create tile for {0} at zoom level {1}", fileName, zoomLevel);
130 reason = string.Format("Map tile at zoom level {0} failed", zoomLevel);
131 return false;
132 }
133 }
134 }
135  
136 return true;
137 }
138  
139 public byte[] GetMapTile(string fileName, out string format)
140 {
141 // m_log.DebugFormat("[MAP IMAGE SERVICE]: Getting map tile {0}", fileName);
142  
143 format = ".jpg";
144 string fullName = Path.Combine(m_TilesStoragePath, fileName);
145 if (File.Exists(fullName))
146 {
147 format = Path.GetExtension(fileName).ToLower();
148 //m_log.DebugFormat("[MAP IMAGE SERVICE]: Found file {0}, extension {1}", fileName, format);
149 return File.ReadAllBytes(fullName);
150 }
151 else if (File.Exists(m_WaterTileFile))
152 {
153 return File.ReadAllBytes(m_WaterTileFile);
154 }
155 else
156 {
157 m_log.DebugFormat("[MAP IMAGE SERVICE]: unable to get file {0}", fileName);
158 return new byte[0];
159 }
160 }
161  
162 #endregion
163  
164  
165 private string GetFileName(uint zoomLevel, int x, int y)
166 {
167 string extension = "jpg";
168 return Path.Combine(m_TilesStoragePath, string.Format("map-{0}-{1}-{2}-objects.{3}", zoomLevel, x, y, extension));
169 }
170  
171 private Bitmap GetInputTileImage(string fileName)
172 {
173 try
174 {
175 if (File.Exists(fileName))
176 return new Bitmap(fileName);
177 }
178 catch (Exception e)
179 {
180 m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to read image data from {0}: {1}", fileName, e);
181 }
182  
183 return null;
184 }
185  
186 private Bitmap GetOutputTileImage(string fileName)
187 {
188 try
189 {
190 if (File.Exists(fileName))
191 return new Bitmap(fileName);
192  
193 else
194 {
195 // Create a new output tile with a transparent background
196 Bitmap bm = new Bitmap(IMAGE_WIDTH, IMAGE_WIDTH, PixelFormat.Format24bppRgb);
197 bm.MakeTransparent();
198 return bm;
199 }
200 }
201 catch (Exception e)
202 {
203 m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to read image data from {0}: {1}", fileName, e);
204 }
205  
206 return null;
207 }
208  
209 private bool CreateTile(uint zoomLevel, int x, int y)
210 {
211 // m_log.DebugFormat("[MAP IMAGE SERVICE]: Create tile for {0} {1}, zoom {2}", x, y, zoomLevel);
212 int prevWidth = (int)Math.Pow(2, (double)zoomLevel - 2);
213 int thisWidth = (int)Math.Pow(2, (double)zoomLevel - 1);
214  
215 // Convert x and y to the bottom left tile for this zoom level
216 int xIn = x - (x % prevWidth);
217 int yIn = y - (y % prevWidth);
218  
219 // Convert x and y to the bottom left tile for the next zoom level
220 int xOut = x - (x % thisWidth);
221 int yOut = y - (y % thisWidth);
222  
223 // Try to open the four input tiles from the previous zoom level
224 Bitmap inputBL = GetInputTileImage(GetFileName(zoomLevel - 1, xIn, yIn));
225 Bitmap inputBR = GetInputTileImage(GetFileName(zoomLevel - 1, xIn + prevWidth, yIn));
226 Bitmap inputTL = GetInputTileImage(GetFileName(zoomLevel - 1, xIn, yIn + prevWidth));
227 Bitmap inputTR = GetInputTileImage(GetFileName(zoomLevel - 1, xIn + prevWidth, yIn + prevWidth));
228  
229 // Open the output tile (current zoom level)
230 string outputFile = GetFileName(zoomLevel, xOut, yOut);
231 Bitmap output = GetOutputTileImage(outputFile);
232 if (output == null)
233 return false;
234 FillImage(output, m_Watercolor);
235  
236 if (inputBL != null)
237 {
238 ImageCopyResampled(output, inputBL, 0, HALF_WIDTH, 0, 0);
239 inputBL.Dispose();
240 }
241 if (inputBR != null)
242 {
243 ImageCopyResampled(output, inputBR, HALF_WIDTH, HALF_WIDTH, 0, 0);
244 inputBR.Dispose();
245 }
246 if (inputTL != null)
247 {
248 ImageCopyResampled(output, inputTL, 0, 0, 0, 0);
249 inputTL.Dispose();
250 }
251 if (inputTR != null)
252 {
253 ImageCopyResampled(output, inputTR, HALF_WIDTH, 0, 0, 0);
254 inputTR.Dispose();
255 }
256  
257 // Write the modified output
258 try
259 {
260 using (Bitmap final = new Bitmap(output))
261 {
262 output.Dispose();
263 final.Save(outputFile, ImageFormat.Jpeg);
264 }
265 }
266 catch (Exception e)
267 {
268 m_log.WarnFormat("[MAP IMAGE SERVICE]: Oops on saving {0} {1}", outputFile, e);
269 }
270  
271 // Save also as png?
272  
273 return true;
274 }
275  
276 #region Image utilities
277  
278 private void FillImage(Bitmap bm, Color c)
279 {
280 for (int x = 0; x < bm.Width; x++)
281 for (int y = 0; y < bm.Height; y++)
282 bm.SetPixel(x, y, c);
283 }
284  
285 private void ImageCopyResampled(Bitmap output, Bitmap input, int destX, int destY, int srcX, int srcY)
286 {
287 int resamplingRateX = 2; // (input.Width - srcX) / (output.Width - destX);
288 int resamplingRateY = 2; // (input.Height - srcY) / (output.Height - destY);
289  
290 for (int x = destX; x < destX + HALF_WIDTH; x++)
291 for (int y = destY; y < destY + HALF_WIDTH; y++)
292 {
293 Color p = input.GetPixel(srcX + (x - destX) * resamplingRateX, srcY + (y - destY) * resamplingRateY);
294 output.SetPixel(x, y, p);
295 }
296 }
297  
298 #endregion
299 }
300 }