OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
DynamicTextureModule.cs
Go to the documentation of this file.
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 
28 using System;
29 using System.Collections.Generic;
30 using System.Drawing;
31 using System.Drawing.Imaging;
32 using Nini.Config;
33 using OpenMetaverse;
34 using OpenMetaverse.Imaging;
35 using OpenSim.Framework;
36 using OpenSim.Region.Framework.Interfaces;
37 using OpenSim.Region.Framework.Scenes;
38 using log4net;
39 using System.Reflection;
40 using Mono.Addins;
41 
42 namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
43 {
44  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "DynamicTextureModule")]
46  {
47 // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
48 
49  private const int ALL_SIDES = -1;
50 
51  public const int DISP_EXPIRE = 1;
52  public const int DISP_TEMP = 2;
53 
57  public bool ReuseTextures { get; set; }
58 
68  public bool ReuseLowDataTextures { get; set; }
69 
70  private Dictionary<UUID, Scene> RegisteredScenes = new Dictionary<UUID, Scene>();
71 
72  private Dictionary<string, IDynamicTextureRender> RenderPlugins =
73  new Dictionary<string, IDynamicTextureRender>();
74 
75  private Dictionary<UUID, DynamicTextureUpdater> Updaters = new Dictionary<UUID, DynamicTextureUpdater>();
76 
84  private Cache m_reuseableDynamicTextures;
85 
91  {
92  m_reuseableDynamicTextures = new Cache(CacheMedium.Memory, CacheStrategy.Conservative);
93  m_reuseableDynamicTextures.DefaultTTL = new TimeSpan(24, 0, 0);
94  }
95 
96  #region IDynamicTextureManager Members
97 
98  public void RegisterRender(string handleType, IDynamicTextureRender render)
99  {
100  if (!RenderPlugins.ContainsKey(handleType))
101  {
102  RenderPlugins.Add(handleType, render);
103  }
104  }
105 
111  public void ReturnData(UUID updaterId, IDynamicTexture texture)
112  {
113  DynamicTextureUpdater updater = null;
114 
115  lock (Updaters)
116  {
117  if (Updaters.ContainsKey(updaterId))
118  {
119  updater = Updaters[updaterId];
120  }
121  }
122 
123  if (updater != null)
124  {
125  if (RegisteredScenes.ContainsKey(updater.SimUUID))
126  {
127  Scene scene = RegisteredScenes[updater.SimUUID];
128  UUID newTextureID = updater.DataReceived(texture.Data, scene);
129 
130  if (ReuseTextures
131  && !updater.BlendWithOldTexture
132  && texture.IsReuseable
133  && (ReuseLowDataTextures || IsDataSizeReuseable(texture)))
134  {
135  m_reuseableDynamicTextures.Store(
136  GenerateReusableTextureKey(texture.InputCommands, texture.InputParams), newTextureID);
137  }
138  }
139  }
140 
141  if (updater.UpdateTimer == 0)
142  {
143  lock (Updaters)
144  {
145  if (!Updaters.ContainsKey(updater.UpdaterID))
146  {
147  Updaters.Remove(updater.UpdaterID);
148  }
149  }
150  }
151  }
152 
162  private bool IsDataSizeReuseable(IDynamicTexture texture)
163  {
164 // Console.WriteLine("{0} {1}", texture.Size.Width, texture.Size.Height);
165  int discardLevel2DataThreshold = (int)Math.Ceiling((texture.Size.Width >> 2) * (texture.Size.Height >> 2) * 0.5);
166 
167 // m_log.DebugFormat(
168 // "[DYNAMIC TEXTURE MODULE]: Discard level 2 threshold {0}, texture data length {1}",
169 // discardLevel2DataThreshold, texture.Data.Length);
170 
171  return discardLevel2DataThreshold < texture.Data.Length;
172  }
173 
174  public UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url,
175  string extraParams, int updateTimer)
176  {
177  return AddDynamicTextureURL(simID, primID, contentType, url, extraParams, updateTimer, false, 255);
178  }
179 
180  public UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url,
181  string extraParams, int updateTimer, bool SetBlending, byte AlphaValue)
182  {
183  return AddDynamicTextureURL(simID, primID, contentType, url,
184  extraParams, updateTimer, SetBlending,
185  (int)(DISP_TEMP|DISP_EXPIRE), AlphaValue, ALL_SIDES);
186  }
187 
188  public UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url,
189  string extraParams, int updateTimer, bool SetBlending,
190  int disp, byte AlphaValue, int face)
191  {
192  if (RenderPlugins.ContainsKey(contentType))
193  {
195  updater.SimUUID = simID;
196  updater.PrimID = primID;
197  updater.ContentType = contentType;
198  updater.Url = url;
199  updater.UpdateTimer = updateTimer;
200  updater.UpdaterID = UUID.Random();
201  updater.Params = extraParams;
202  updater.BlendWithOldTexture = SetBlending;
203  updater.FrontAlpha = AlphaValue;
204  updater.Face = face;
205  updater.Disp = disp;
206 
207  lock (Updaters)
208  {
209  if (!Updaters.ContainsKey(updater.UpdaterID))
210  {
211  Updaters.Add(updater.UpdaterID, updater);
212  }
213  }
214 
215  RenderPlugins[contentType].AsyncConvertUrl(updater.UpdaterID, url, extraParams);
216  return updater.UpdaterID;
217  }
218  return UUID.Zero;
219  }
220 
221  public UUID AddDynamicTextureData(UUID simID, UUID primID, string contentType, string data,
222  string extraParams, int updateTimer)
223  {
224  return AddDynamicTextureData(simID, primID, contentType, data, extraParams, updateTimer, false, 255);
225  }
226 
227  public UUID AddDynamicTextureData(UUID simID, UUID primID, string contentType, string data,
228  string extraParams, int updateTimer, bool SetBlending, byte AlphaValue)
229  {
230  return AddDynamicTextureData(simID, primID, contentType, data, extraParams, updateTimer, SetBlending,
231  (int) (DISP_TEMP|DISP_EXPIRE), AlphaValue, ALL_SIDES);
232  }
233 
234  public UUID AddDynamicTextureData(UUID simID, UUID primID, string contentType, string data,
235  string extraParams, int updateTimer, bool SetBlending, int disp, byte AlphaValue, int face)
236  {
237  if (!RenderPlugins.ContainsKey(contentType))
238  return UUID.Zero;
239 
240  Scene scene;
241  RegisteredScenes.TryGetValue(simID, out scene);
242 
243  if (scene == null)
244  return UUID.Zero;
245 
246  SceneObjectPart part = scene.GetSceneObjectPart(primID);
247 
248  if (part == null)
249  return UUID.Zero;
250 
251  // If we want to reuse dynamic textures then we have to ignore any request from the caller to expire
252  // them.
253  if (ReuseTextures)
254  disp = disp & ~DISP_EXPIRE;
255 
257  updater.SimUUID = simID;
258  updater.PrimID = primID;
259  updater.ContentType = contentType;
260  updater.BodyData = data;
261  updater.UpdateTimer = updateTimer;
262  updater.UpdaterID = UUID.Random();
263  updater.Params = extraParams;
264  updater.BlendWithOldTexture = SetBlending;
265  updater.FrontAlpha = AlphaValue;
266  updater.Face = face;
267  updater.Url = "Local image";
268  updater.Disp = disp;
269 
270  object objReusableTextureUUID = null;
271 
272  if (ReuseTextures && !updater.BlendWithOldTexture)
273  {
274  string reuseableTextureKey = GenerateReusableTextureKey(data, extraParams);
275  objReusableTextureUUID = m_reuseableDynamicTextures.Get(reuseableTextureKey);
276 
277  if (objReusableTextureUUID != null)
278  {
279  // If something else has removed this temporary asset from the cache, detect and invalidate
280  // our cached uuid.
281  if (scene.AssetService.GetMetadata(objReusableTextureUUID.ToString()) == null)
282  {
283  m_reuseableDynamicTextures.Invalidate(reuseableTextureKey);
284  objReusableTextureUUID = null;
285  }
286  }
287  }
288 
289  // We cannot reuse a dynamic texture if the data is going to be blended with something already there.
290  if (objReusableTextureUUID == null)
291  {
292  lock (Updaters)
293  {
294  if (!Updaters.ContainsKey(updater.UpdaterID))
295  {
296  Updaters.Add(updater.UpdaterID, updater);
297  }
298  }
299 
300 // m_log.DebugFormat(
301 // "[DYNAMIC TEXTURE MODULE]: Requesting generation of new dynamic texture for {0} in {1}",
302 // part.Name, part.ParentGroup.Scene.Name);
303 
304  RenderPlugins[contentType].AsyncConvertData(updater.UpdaterID, data, extraParams);
305  }
306  else
307  {
308 // m_log.DebugFormat(
309 // "[DYNAMIC TEXTURE MODULE]: Reusing cached texture {0} for {1} in {2}",
310 // objReusableTextureUUID, part.Name, part.ParentGroup.Scene.Name);
311 
312  // No need to add to updaters as the texture is always the same. Not that this functionality
313  // apppears to be implemented anyway.
314  updater.UpdatePart(part, (UUID)objReusableTextureUUID);
315  }
316 
317  return updater.UpdaterID;
318  }
319 
320  private string GenerateReusableTextureKey(string data, string extraParams)
321  {
322  return string.Format("{0}{1}", data, extraParams);
323  }
324 
325  public void GetDrawStringSize(string contentType, string text, string fontName, int fontSize,
326  out double xSize, out double ySize)
327  {
328  xSize = 0;
329  ySize = 0;
330  if (RenderPlugins.ContainsKey(contentType))
331  {
332  RenderPlugins[contentType].GetDrawStringSize(text, fontName, fontSize, out xSize, out ySize);
333  }
334  }
335 
336  #endregion
337 
338  #region ISharedRegionModule Members
339 
340  public void Initialise(IConfigSource config)
341  {
342  IConfig texturesConfig = config.Configs["Textures"];
343  if (texturesConfig != null)
344  {
345  ReuseTextures = texturesConfig.GetBoolean("ReuseDynamicTextures", false);
346  ReuseLowDataTextures = texturesConfig.GetBoolean("ReuseDynamicLowDataTextures", false);
347 
348  if (ReuseTextures)
349  {
350  m_reuseableDynamicTextures = new Cache(CacheMedium.Memory, CacheStrategy.Conservative);
351  m_reuseableDynamicTextures.DefaultTTL = new TimeSpan(24, 0, 0);
352  }
353  }
354  }
355 
356  public void PostInitialise()
357  {
358  }
359 
360  public void AddRegion(Scene scene)
361  {
362  if (!RegisteredScenes.ContainsKey(scene.RegionInfo.RegionID))
363  {
364  RegisteredScenes.Add(scene.RegionInfo.RegionID, scene);
365  scene.RegisterModuleInterface<IDynamicTextureManager>(this);
366  }
367  }
368 
369  public void RegionLoaded(Scene scene)
370  {
371  }
372 
373  public void RemoveRegion(Scene scene)
374  {
375  if (RegisteredScenes.ContainsKey(scene.RegionInfo.RegionID))
376  RegisteredScenes.Remove(scene.RegionInfo.RegionID);
377  }
378 
379  public void Close()
380  {
381  }
382 
383  public string Name
384  {
385  get { return "DynamicTextureModule"; }
386  }
387 
388  public Type ReplaceableInterface
389  {
390  get { return null; }
391  }
392 
393  #endregion
394 
395  #region Nested type: DynamicTextureUpdater
396 
398  {
399  private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
400 
401  public bool BlendWithOldTexture = false;
402  public string BodyData;
403  public string ContentType;
404  public byte FrontAlpha = 255;
405  public string Params;
406  public UUID PrimID;
407  public bool SetNewFrontAlpha = false;
408  public UUID SimUUID;
409  public UUID UpdaterID;
410  public int UpdateTimer;
411  public int Face;
412  public int Disp;
413  public string Url;
414 
416  {
417  UpdateTimer = 0;
418  BodyData = null;
419  }
420 
427  public UUID UpdatePart(SceneObjectPart part, UUID textureID)
428  {
429  UUID oldID;
430 
431  lock (part)
432  {
433  // mostly keep the values from before
434  Primitive.TextureEntry tmptex = part.Shape.Textures;
435 
436  // FIXME: Need to return the appropriate ID if only a single face is replaced.
437  oldID = tmptex.DefaultTexture.TextureID;
438 
439  if (Face == ALL_SIDES)
440  {
441  oldID = tmptex.DefaultTexture.TextureID;
442  tmptex.DefaultTexture.TextureID = textureID;
443  }
444  else
445  {
446  try
447  {
448  Primitive.TextureEntryFace texface = tmptex.CreateFace((uint)Face);
449  texface.TextureID = textureID;
450  tmptex.FaceTextures[Face] = texface;
451  }
452  catch (Exception)
453  {
454  tmptex.DefaultTexture.TextureID = textureID;
455  }
456  }
457 
458  // I'm pretty sure we always want to force this to true
459  // I'm pretty sure noone whats to set fullbright true if it wasn't true before.
460  // tmptex.DefaultTexture.Fullbright = true;
461 
462  part.UpdateTextureEntry(tmptex.GetBytes());
463  }
464 
465  return oldID;
466  }
467 
475  public UUID DataReceived(byte[] data, Scene scene)
476  {
477  SceneObjectPart part = scene.GetSceneObjectPart(PrimID);
478 
479  if (part == null || data == null || data.Length <= 1)
480  {
481  string msg =
482  String.Format("DynamicTextureModule: Error preparing image using URL {0}", Url);
483  scene.SimChat(Utils.StringToBytes(msg), ChatTypeEnum.Say,
484  0, part.ParentGroup.RootPart.AbsolutePosition, part.Name, part.UUID, false);
485 
486  return UUID.Zero;
487  }
488 
489  byte[] assetData = null;
490  AssetBase oldAsset = null;
491 
492  if (BlendWithOldTexture)
493  {
494  Primitive.TextureEntryFace defaultFace = part.Shape.Textures.DefaultTexture;
495  if (defaultFace != null)
496  {
497  oldAsset = scene.AssetService.Get(defaultFace.TextureID.ToString());
498 
499  if (oldAsset != null)
500  assetData = BlendTextures(data, oldAsset.Data, SetNewFrontAlpha, FrontAlpha);
501  }
502  }
503 
504  if (assetData == null)
505  {
506  assetData = new byte[data.Length];
507  Array.Copy(data, assetData, data.Length);
508  }
509 
510  // Create a new asset for user
511  AssetBase asset
512  = new AssetBase(
513  UUID.Random(), "DynamicImage" + Util.RandomClass.Next(1, 10000), (sbyte)AssetType.Texture,
514  scene.RegionInfo.RegionID.ToString());
515  asset.Data = assetData;
516  asset.Description = String.Format("URL image : {0}", Url);
517  if (asset.Description.Length > 128)
518  asset.Description = asset.Description.Substring(0, 128);
519  asset.Local = true; // dynamic images aren't saved in the assets server
520  asset.Temporary = ((Disp & DISP_TEMP) != 0);
521  scene.AssetService.Store(asset); // this will only save the asset in the local asset cache
522 
523  IJ2KDecoder cacheLayerDecode = scene.RequestModuleInterface<IJ2KDecoder>();
524  if (cacheLayerDecode != null)
525  {
526  if (!cacheLayerDecode.Decode(asset.FullID, asset.Data))
527  m_log.WarnFormat(
528  "[DYNAMIC TEXTURE MODULE]: Decoding of dynamically generated asset {0} for {1} in {2} failed",
529  asset.ID, part.Name, part.ParentGroup.Scene.Name);
530  }
531 
532  UUID oldID = UpdatePart(part, asset.FullID);
533 
534  if (oldID != UUID.Zero && ((Disp & DISP_EXPIRE) != 0))
535  {
536  if (oldAsset == null)
537  oldAsset = scene.AssetService.Get(oldID.ToString());
538 
539  if (oldAsset != null)
540  {
541  if (oldAsset.Temporary)
542  {
543  scene.AssetService.Delete(oldID.ToString());
544  }
545  }
546  }
547 
548  return asset.FullID;
549  }
550 
551  private byte[] BlendTextures(byte[] frontImage, byte[] backImage, bool setNewAlpha, byte newAlpha)
552  {
553  ManagedImage managedImage;
554  Image image;
555 
556  if (OpenJPEG.DecodeToImage(frontImage, out managedImage, out image))
557  {
558  Bitmap image1 = new Bitmap(image);
559 
560  if (OpenJPEG.DecodeToImage(backImage, out managedImage, out image))
561  {
562  Bitmap image2 = new Bitmap(image);
563 
564  if (setNewAlpha)
565  SetAlpha(ref image1, newAlpha);
566 
567  Bitmap joint = MergeBitMaps(image1, image2);
568 
569  byte[] result = new byte[0];
570 
571  try
572  {
573  result = OpenJPEG.EncodeFromImage(joint, true);
574  }
575  catch (Exception e)
576  {
577  m_log.ErrorFormat(
578  "[DYNAMICTEXTUREMODULE]: OpenJpeg Encode Failed. Exception {0}{1}",
579  e.Message, e.StackTrace);
580  }
581 
582  return result;
583  }
584  }
585 
586  return null;
587  }
588 
589  public Bitmap MergeBitMaps(Bitmap front, Bitmap back)
590  {
591  Bitmap joint;
592  Graphics jG;
593 
594  joint = new Bitmap(back.Width, back.Height, PixelFormat.Format32bppArgb);
595  jG = Graphics.FromImage(joint);
596 
597  jG.DrawImage(back, 0, 0, back.Width, back.Height);
598  jG.DrawImage(front, 0, 0, back.Width, back.Height);
599 
600  return joint;
601  }
602 
603  private void SetAlpha(ref Bitmap b, byte alpha)
604  {
605  for (int w = 0; w < b.Width; w++)
606  {
607  for (int h = 0; h < b.Height; h++)
608  {
609  b.SetPixel(w, h, Color.FromArgb(alpha, b.GetPixel(w, h)));
610  }
611  }
612  }
613  }
614 
615  #endregion
616  }
617 }
UUID DataReceived(byte[] data, Scene scene)
Called once new texture data has been received for this updater.
UUID AddDynamicTextureData(UUID simID, UUID primID, string contentType, string data, string extraParams, int updateTimer)
UUID AddDynamicTextureData(UUID simID, UUID primID, string contentType, string data, string extraParams, int updateTimer, bool SetBlending, byte AlphaValue)
UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url, string extraParams, int updateTimer, bool SetBlending, int disp, byte AlphaValue, int face)
void PostInitialise()
This is called exactly once after all the shared region-modules have been instanciated and IRegionMod...
UUID UpdatePart(SceneObjectPart part, UUID textureID)
Update the given part with the new texture.
Scene Scene
The scene to which this entity belongs
Definition: EntityBase.cs:46
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
string InputCommands
Input commands used to generate this data.
UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url, string extraParams, int updateTimer, bool SetBlending, byte AlphaValue)
DynamicTextureModule()
This constructor is only here because of the Unit Tests... Don't use it.
bool IsReuseable
Signal whether the texture is reuseable (i.e. whether the same input data will always generate the sa...
void Initialise(IConfigSource config)
This is called to initialize the region module. For shared modules, this is called exactly once...
Asset class. All Assets are reference by this class or a class derived from this class ...
Definition: AssetBase.cs:49
void ReturnData(UUID updaterId, IDynamicTexture texture)
Called by code which actually renders the dynamic texture to supply texture data. ...
void RegisterRender(string handleType, IDynamicTextureRender render)
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url, string extraParams, int updateTimer)
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
string InputParams
Extra input params used to generate this data.
UUID FullID
Asset UUID
Definition: AssetBase.cs:168
UUID AddDynamicTextureData(UUID simID, UUID primID, string contentType, string data, string extraParams, int updateTimer, bool SetBlending, int disp, byte AlphaValue, int face)
Apply a dynamically generated texture to the given prim.
void GetDrawStringSize(string contentType, string text, string fontName, int fontSize, out double xSize, out double ySize)
string ID
Asset MetaData ID (transferring from UUID to string ID)
Definition: AssetBase.cs:177
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
bool Temporary
Is this asset going to be saved to the asset database?
Definition: AssetBase.cs:222