VectorRenderModule.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  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. using System;
  28. using System.Drawing;
  29. using System.Drawing.Imaging;
  30. using System.Globalization;
  31. using System.IO;
  32. using System.Net;
  33. using Nini.Config;
  34. using OpenMetaverse;
  35. using OpenMetaverse.Imaging;
  36. using OpenSim.Region.Framework.Interfaces;
  37. using OpenSim.Region.Framework.Scenes;
  38. using log4net;
  39. using System.Reflection;
  40. //using Cairo;
  41. namespace OpenSim.Region.CoreModules.Scripting.VectorRender
  42. {
  43. public class VectorRenderModule : IRegionModule, IDynamicTextureRender
  44. {
  45. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  46. private string m_name = "VectorRenderModule";
  47. private Scene m_scene;
  48. private IDynamicTextureManager m_textureManager;
  49. private Graphics m_graph;
  50. public VectorRenderModule()
  51. {
  52. }
  53. #region IDynamicTextureRender Members
  54. public string GetContentType()
  55. {
  56. return ("vector");
  57. }
  58. public string GetName()
  59. {
  60. return m_name;
  61. }
  62. public bool SupportsAsynchronous()
  63. {
  64. return true;
  65. }
  66. public byte[] ConvertUrl(string url, string extraParams)
  67. {
  68. return null;
  69. }
  70. public byte[] ConvertStream(Stream data, string extraParams)
  71. {
  72. return null;
  73. }
  74. public bool AsyncConvertUrl(UUID id, string url, string extraParams)
  75. {
  76. return false;
  77. }
  78. public bool AsyncConvertData(UUID id, string bodyData, string extraParams)
  79. {
  80. Draw(bodyData, id, extraParams);
  81. return true;
  82. }
  83. public void GetDrawStringSize(string text, string fontName, int fontSize,
  84. out double xSize, out double ySize)
  85. {
  86. Font myFont = new Font(fontName, fontSize);
  87. SizeF stringSize = new SizeF();
  88. lock (m_graph) {
  89. stringSize = m_graph.MeasureString(text, myFont);
  90. xSize = stringSize.Width;
  91. ySize = stringSize.Height;
  92. }
  93. }
  94. #endregion
  95. #region IRegionModule Members
  96. public void Initialise(Scene scene, IConfigSource config)
  97. {
  98. if (m_scene == null)
  99. {
  100. m_scene = scene;
  101. }
  102. if (m_graph == null)
  103. {
  104. Bitmap bitmap = new Bitmap(1024, 1024, PixelFormat.Format32bppArgb);
  105. m_graph = Graphics.FromImage(bitmap);
  106. }
  107. }
  108. public void PostInitialise()
  109. {
  110. m_textureManager = m_scene.RequestModuleInterface<IDynamicTextureManager>();
  111. if (m_textureManager != null)
  112. {
  113. m_textureManager.RegisterRender(GetContentType(), this);
  114. }
  115. }
  116. public void Close()
  117. {
  118. }
  119. public string Name
  120. {
  121. get { return m_name; }
  122. }
  123. public bool IsSharedModule
  124. {
  125. get { return true; }
  126. }
  127. #endregion
  128. private void Draw(string data, UUID id, string extraParams)
  129. {
  130. // We need to cater for old scripts that didnt use extraParams neatly, they use either an integer size which represents both width and height, or setalpha
  131. // we will now support multiple comma seperated params in the form width:256,height:512,alpha:255
  132. int width = 256;
  133. int height = 256;
  134. int alpha = 255; // 0 is transparent
  135. Color bgColour = Color.White; // Default background color
  136. char altDataDelim = ';';
  137. char[] paramDelimiter = { ',' };
  138. char[] nvpDelimiter = { ':' };
  139. extraParams = extraParams.Trim();
  140. extraParams = extraParams.ToLower();
  141. string[] nvps = extraParams.Split(paramDelimiter);
  142. int temp = -1;
  143. foreach (string pair in nvps)
  144. {
  145. string[] nvp = pair.Split(nvpDelimiter);
  146. string name = "";
  147. string value = "";
  148. if (nvp[0] != null)
  149. {
  150. name = nvp[0].Trim();
  151. }
  152. if (nvp.Length == 2)
  153. {
  154. value = nvp[1].Trim();
  155. }
  156. switch (name)
  157. {
  158. case "width":
  159. temp = parseIntParam(value);
  160. if (temp != -1)
  161. {
  162. if (temp < 1)
  163. {
  164. width = 1;
  165. }
  166. else if (temp > 2048)
  167. {
  168. width = 2048;
  169. }
  170. else
  171. {
  172. width = temp;
  173. }
  174. }
  175. break;
  176. case "height":
  177. temp = parseIntParam(value);
  178. if (temp != -1)
  179. {
  180. if (temp < 1)
  181. {
  182. height = 1;
  183. }
  184. else if (temp > 2048)
  185. {
  186. height = 2048;
  187. }
  188. else
  189. {
  190. height = temp;
  191. }
  192. }
  193. break;
  194. case "alpha":
  195. temp = parseIntParam(value);
  196. if (temp != -1)
  197. {
  198. if (temp < 0)
  199. {
  200. alpha = 0;
  201. }
  202. else if (temp > 255)
  203. {
  204. alpha = 255;
  205. }
  206. else
  207. {
  208. alpha = temp;
  209. }
  210. }
  211. // Allow a bitmap w/o the alpha component to be created
  212. else if (value.ToLower() == "false") {
  213. alpha = 256;
  214. }
  215. break;
  216. case "bgcolour":
  217. int hex = 0;
  218. if (Int32.TryParse(value, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out hex))
  219. {
  220. bgColour = Color.FromArgb(hex);
  221. }
  222. else
  223. {
  224. bgColour = Color.FromName(value);
  225. }
  226. break;
  227. case "altdatadelim":
  228. altDataDelim = value.ToCharArray()[0];
  229. break;
  230. case "":
  231. // blank string has been passed do nothing just use defaults
  232. break;
  233. default: // this is all for backwards compat, all a bit ugly hopfully can be removed in future
  234. // could be either set alpha or just an int
  235. if (name == "setalpha")
  236. {
  237. alpha = 0; // set the texture to have transparent background (maintains backwards compat)
  238. }
  239. else
  240. {
  241. // this function used to accept an int on its own that represented both
  242. // width and height, this is to maintain backwards compat, could be removed
  243. // but would break existing scripts
  244. temp = parseIntParam(name);
  245. if (temp != -1)
  246. {
  247. if (temp > 1024)
  248. temp = 1024;
  249. if (temp < 128)
  250. temp = 128;
  251. width = temp;
  252. height = temp;
  253. }
  254. }
  255. break;
  256. }
  257. }
  258. Bitmap bitmap;
  259. if (alpha == 256)
  260. {
  261. bitmap = new Bitmap(width, height, PixelFormat.Format32bppRgb);
  262. }
  263. else
  264. {
  265. bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
  266. }
  267. Graphics graph = Graphics.FromImage(bitmap);
  268. // this is really just to save people filling the
  269. // background color in their scripts, only do when fully opaque
  270. if (alpha >= 255)
  271. {
  272. graph.FillRectangle(new SolidBrush(bgColour), 0, 0, width, height);
  273. }
  274. for (int w = 0; w < bitmap.Width; w++)
  275. {
  276. if (alpha <= 255)
  277. {
  278. for (int h = 0; h < bitmap.Height; h++)
  279. {
  280. bitmap.SetPixel(w, h, Color.FromArgb(alpha, bitmap.GetPixel(w, h)));
  281. }
  282. }
  283. }
  284. GDIDraw(data, graph, altDataDelim);
  285. byte[] imageJ2000 = new byte[0];
  286. try
  287. {
  288. imageJ2000 = OpenJPEG.EncodeFromImage(bitmap, true);
  289. }
  290. catch (Exception)
  291. {
  292. m_log.Error(
  293. "[VECTORRENDERMODULE]: OpenJpeg Encode Failed. Empty byte data returned!");
  294. }
  295. m_textureManager.ReturnData(id, imageJ2000);
  296. }
  297. private int parseIntParam(string strInt)
  298. {
  299. int parsed;
  300. try
  301. {
  302. parsed = Convert.ToInt32(strInt);
  303. }
  304. catch (Exception)
  305. {
  306. //Ckrinke: Add a WriteLine to remove the warning about 'e' defined but not used
  307. // m_log.Debug("Problem with Draw. Please verify parameters." + e.ToString());
  308. parsed = -1;
  309. }
  310. return parsed;
  311. }
  312. /*
  313. private void CairoDraw(string data, System.Drawing.Graphics graph)
  314. {
  315. using (Win32Surface draw = new Win32Surface(graph.GetHdc()))
  316. {
  317. Context contex = new Context(draw);
  318. contex.Antialias = Antialias.None; //fastest method but low quality
  319. contex.LineWidth = 7;
  320. char[] lineDelimiter = { ';' };
  321. char[] partsDelimiter = { ',' };
  322. string[] lines = data.Split(lineDelimiter);
  323. foreach (string line in lines)
  324. {
  325. string nextLine = line.Trim();
  326. if (nextLine.StartsWith("MoveTO"))
  327. {
  328. float x = 0;
  329. float y = 0;
  330. GetParams(partsDelimiter, ref nextLine, ref x, ref y);
  331. contex.MoveTo(x, y);
  332. }
  333. else if (nextLine.StartsWith("LineTo"))
  334. {
  335. float x = 0;
  336. float y = 0;
  337. GetParams(partsDelimiter, ref nextLine, ref x, ref y);
  338. contex.LineTo(x, y);
  339. contex.Stroke();
  340. }
  341. }
  342. }
  343. graph.ReleaseHdc();
  344. }
  345. */
  346. private void GDIDraw(string data, Graphics graph, char dataDelim)
  347. {
  348. Point startPoint = new Point(0, 0);
  349. Point endPoint = new Point(0, 0);
  350. Pen drawPen = new Pen(Color.Black, 7);
  351. string fontName = "Arial";
  352. float fontSize = 14;
  353. Font myFont = new Font(fontName, fontSize);
  354. SolidBrush myBrush = new SolidBrush(Color.Black);
  355. char[] lineDelimiter = {dataDelim};
  356. char[] partsDelimiter = {','};
  357. string[] lines = data.Split(lineDelimiter);
  358. foreach (string line in lines)
  359. {
  360. string nextLine = line.Trim();
  361. //replace with switch, or even better, do some proper parsing
  362. if (nextLine.StartsWith("MoveTo"))
  363. {
  364. float x = 0;
  365. float y = 0;
  366. GetParams(partsDelimiter, ref nextLine, 6, ref x, ref y);
  367. startPoint.X = (int) x;
  368. startPoint.Y = (int) y;
  369. }
  370. else if (nextLine.StartsWith("LineTo"))
  371. {
  372. float x = 0;
  373. float y = 0;
  374. GetParams(partsDelimiter, ref nextLine, 6, ref x, ref y);
  375. endPoint.X = (int) x;
  376. endPoint.Y = (int) y;
  377. graph.DrawLine(drawPen, startPoint, endPoint);
  378. startPoint.X = endPoint.X;
  379. startPoint.Y = endPoint.Y;
  380. }
  381. else if (nextLine.StartsWith("Text"))
  382. {
  383. nextLine = nextLine.Remove(0, 4);
  384. nextLine = nextLine.Trim();
  385. graph.DrawString(nextLine, myFont, myBrush, startPoint);
  386. }
  387. else if (nextLine.StartsWith("Image"))
  388. {
  389. float x = 0;
  390. float y = 0;
  391. GetParams(partsDelimiter, ref nextLine, 5, ref x, ref y);
  392. endPoint.X = (int) x;
  393. endPoint.Y = (int) y;
  394. Image image = ImageHttpRequest(nextLine);
  395. graph.DrawImage(image, (float) startPoint.X, (float) startPoint.Y, x, y);
  396. startPoint.X += endPoint.X;
  397. startPoint.Y += endPoint.Y;
  398. }
  399. else if (nextLine.StartsWith("Rectangle"))
  400. {
  401. float x = 0;
  402. float y = 0;
  403. GetParams(partsDelimiter, ref nextLine, 9, ref x, ref y);
  404. endPoint.X = (int) x;
  405. endPoint.Y = (int) y;
  406. graph.DrawRectangle(drawPen, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);
  407. startPoint.X += endPoint.X;
  408. startPoint.Y += endPoint.Y;
  409. }
  410. else if (nextLine.StartsWith("FillRectangle"))
  411. {
  412. float x = 0;
  413. float y = 0;
  414. GetParams(partsDelimiter, ref nextLine, 13, ref x, ref y);
  415. endPoint.X = (int) x;
  416. endPoint.Y = (int) y;
  417. graph.FillRectangle(myBrush, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);
  418. startPoint.X += endPoint.X;
  419. startPoint.Y += endPoint.Y;
  420. }
  421. else if (nextLine.StartsWith("Ellipse"))
  422. {
  423. float x = 0;
  424. float y = 0;
  425. GetParams(partsDelimiter, ref nextLine, 7, ref x, ref y);
  426. endPoint.X = (int) x;
  427. endPoint.Y = (int) y;
  428. graph.DrawEllipse(drawPen, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);
  429. startPoint.X += endPoint.X;
  430. startPoint.Y += endPoint.Y;
  431. }
  432. else if (nextLine.StartsWith("FontSize"))
  433. {
  434. nextLine = nextLine.Remove(0, 8);
  435. nextLine = nextLine.Trim();
  436. fontSize = Convert.ToSingle(nextLine, CultureInfo.InvariantCulture);
  437. myFont = new Font(fontName, fontSize);
  438. }
  439. else if (nextLine.StartsWith("FontProp"))
  440. {
  441. nextLine = nextLine.Remove(0, 8);
  442. nextLine = nextLine.Trim();
  443. string [] fprops = nextLine.Split(partsDelimiter);
  444. foreach (string prop in fprops) {
  445. switch (prop)
  446. {
  447. case "B":
  448. if (!(myFont.Bold))
  449. myFont = new Font(myFont, myFont.Style | FontStyle.Bold);
  450. break;
  451. case "I":
  452. if (!(myFont.Italic))
  453. myFont = new Font(myFont, myFont.Style | FontStyle.Italic);
  454. break;
  455. case "U":
  456. if (!(myFont.Underline))
  457. myFont = new Font(myFont, myFont.Style | FontStyle.Underline);
  458. break;
  459. case "S":
  460. if (!(myFont.Strikeout))
  461. myFont = new Font(myFont, myFont.Style | FontStyle.Strikeout);
  462. break;
  463. case "R":
  464. myFont = new Font(myFont, FontStyle.Regular);
  465. break;
  466. }
  467. }
  468. }
  469. else if (nextLine.StartsWith("FontName"))
  470. {
  471. nextLine = nextLine.Remove(0, 8);
  472. fontName = nextLine.Trim();
  473. myFont = new Font(fontName, fontSize);
  474. }
  475. else if (nextLine.StartsWith("PenSize"))
  476. {
  477. nextLine = nextLine.Remove(0, 7);
  478. nextLine = nextLine.Trim();
  479. float size = Convert.ToSingle(nextLine, CultureInfo.InvariantCulture);
  480. drawPen.Width = size;
  481. }
  482. else if (nextLine.StartsWith("PenColour"))
  483. {
  484. nextLine = nextLine.Remove(0, 9);
  485. nextLine = nextLine.Trim();
  486. int hex = 0;
  487. Color newColour;
  488. if (Int32.TryParse(nextLine, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out hex))
  489. {
  490. newColour = Color.FromArgb(hex);
  491. }
  492. else
  493. {
  494. // this doesn't fail, it just returns black if nothing is found
  495. newColour = Color.FromName(nextLine);
  496. }
  497. myBrush.Color = newColour;
  498. drawPen.Color = newColour;
  499. }
  500. }
  501. }
  502. private static void GetParams(char[] partsDelimiter, ref string line, int startLength, ref float x, ref float y)
  503. {
  504. line = line.Remove(0, startLength);
  505. string[] parts = line.Split(partsDelimiter);
  506. if (parts.Length == 2)
  507. {
  508. string xVal = parts[0].Trim();
  509. string yVal = parts[1].Trim();
  510. x = Convert.ToSingle(xVal, CultureInfo.InvariantCulture);
  511. y = Convert.ToSingle(yVal, CultureInfo.InvariantCulture);
  512. }
  513. else if (parts.Length > 2)
  514. {
  515. string xVal = parts[0].Trim();
  516. string yVal = parts[1].Trim();
  517. x = Convert.ToSingle(xVal, CultureInfo.InvariantCulture);
  518. y = Convert.ToSingle(yVal, CultureInfo.InvariantCulture);
  519. line = "";
  520. for (int i = 2; i < parts.Length; i++)
  521. {
  522. line = line + parts[i].Trim();
  523. line = line + " ";
  524. }
  525. }
  526. }
  527. private Bitmap ImageHttpRequest(string url)
  528. {
  529. WebRequest request = HttpWebRequest.Create(url);
  530. //Ckrinke: Comment out for now as 'str' is unused. Bring it back into play later when it is used.
  531. //Ckrinke Stream str = null;
  532. HttpWebResponse response = (HttpWebResponse) (request).GetResponse();
  533. if (response.StatusCode == HttpStatusCode.OK)
  534. {
  535. Bitmap image = new Bitmap(response.GetResponseStream());
  536. return image;
  537. }
  538. return null;
  539. }
  540. }
  541. }