diff --git a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
index 18bd0186b8..13b74983b1 100644
--- a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs
@@ -49,6 +49,11 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
public const int DISP_EXPIRE = 1;
public const int DISP_TEMP = 2;
+ ///
+ /// If true then where possible dynamic textures are reused.
+ ///
+ public bool ReuseTextures { get; set; }
+
private Dictionary RegisteredScenes = new Dictionary();
private Dictionary RenderPlugins =
@@ -56,6 +61,15 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
private Dictionary Updaters = new Dictionary();
+ ///
+ /// Record dynamic textures that we can reuse for a given data and parameter combination rather than
+ /// regenerate.
+ ///
+ ///
+ /// Key is string.Format("{0}{1}", data
+ ///
+ private Cache m_reuseableDynamicTextures;
+
#region IDynamicTextureManager Members
public void RegisterRender(string handleType, IDynamicTextureRender render)
@@ -71,7 +85,8 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
///
///
///
- public void ReturnData(UUID id, byte[] data)
+ /// True if the data generated can be reused for subsequent identical requests
+ public void ReturnData(UUID id, byte[] data, bool isReuseable)
{
DynamicTextureUpdater updater = null;
@@ -88,7 +103,11 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
if (RegisteredScenes.ContainsKey(updater.SimUUID))
{
Scene scene = RegisteredScenes[updater.SimUUID];
- updater.DataReceived(data, scene);
+ UUID newTextureID = updater.DataReceived(data, scene);
+
+ if (ReuseTextures && isReuseable && !updater.BlendWithOldTexture)
+ m_reuseableDynamicTextures.Store(
+ GenerateReusableTextureKey(updater.BodyData, updater.Params), newTextureID);
}
}
@@ -169,6 +188,11 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
{
if (RenderPlugins.ContainsKey(contentType))
{
+ // If we want to reuse dynamic textures then we have to ignore any request from the caller to expire
+ // them.
+ if (ReuseTextures)
+ disp = disp & ~DISP_EXPIRE;
+
DynamicTextureUpdater updater = new DynamicTextureUpdater();
updater.SimUUID = simID;
updater.PrimID = primID;
@@ -183,21 +207,49 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
updater.Url = "Local image";
updater.Disp = disp;
- lock (Updaters)
+ object reusableTextureUUID = null;
+
+ if (ReuseTextures)
+ reusableTextureUUID
+ = m_reuseableDynamicTextures.Get(GenerateReusableTextureKey(data, extraParams));
+
+ // We cannot reuse a dynamic texture if the data is going to be blended with something already there.
+ if (reusableTextureUUID == null || updater.BlendWithOldTexture)
{
- if (!Updaters.ContainsKey(updater.UpdaterID))
+ lock (Updaters)
{
- Updaters.Add(updater.UpdaterID, updater);
+ if (!Updaters.ContainsKey(updater.UpdaterID))
+ {
+ Updaters.Add(updater.UpdaterID, updater);
+ }
+ }
+
+ RenderPlugins[contentType].AsyncConvertData(updater.UpdaterID, data, extraParams);
+ }
+ else
+ {
+ // No need to add to updaters as the texture is always the same. Not that this functionality
+ // apppears to be implemented anyway.
+ if (RegisteredScenes.ContainsKey(updater.SimUUID))
+ {
+ SceneObjectPart part = RegisteredScenes[updater.SimUUID].GetSceneObjectPart(updater.PrimID);
+
+ if (part != null)
+ updater.UpdatePart(part, (UUID)reusableTextureUUID);
}
}
- RenderPlugins[contentType].AsyncConvertData(updater.UpdaterID, data, extraParams);
return updater.UpdaterID;
}
return UUID.Zero;
}
+ private string GenerateReusableTextureKey(string data, string extraParams)
+ {
+ return string.Format("{0}{1}", data, extraParams);
+ }
+
public void GetDrawStringSize(string contentType, string text, string fontName, int fontSize,
out double xSize, out double ySize)
{
@@ -224,6 +276,12 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
public void PostInitialise()
{
+// ReuseTextures = true;
+ if (ReuseTextures)
+ {
+ m_reuseableDynamicTextures = new Cache(CacheMedium.Memory, CacheStrategy.Conservative);
+ m_reuseableDynamicTextures.DefaultTTL = new TimeSpan(24, 0, 0);
+ }
}
public void Close()
@@ -268,10 +326,61 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
BodyData = null;
}
+ ///
+ /// Update the given part with the new texture.
+ ///
+ ///
+ /// The old texture UUID.
+ ///
+ public UUID UpdatePart(SceneObjectPart part, UUID textureID)
+ {
+ UUID oldID;
+
+ lock (part)
+ {
+ // mostly keep the values from before
+ Primitive.TextureEntry tmptex = part.Shape.Textures;
+
+ // FIXME: Need to return the appropriate ID if only a single face is replaced.
+ oldID = tmptex.DefaultTexture.TextureID;
+
+ if (Face == ALL_SIDES)
+ {
+ oldID = tmptex.DefaultTexture.TextureID;
+ tmptex.DefaultTexture.TextureID = textureID;
+ }
+ else
+ {
+ try
+ {
+ Primitive.TextureEntryFace texface = tmptex.CreateFace((uint)Face);
+ texface.TextureID = textureID;
+ tmptex.FaceTextures[Face] = texface;
+ }
+ catch (Exception)
+ {
+ tmptex.DefaultTexture.TextureID = textureID;
+ }
+ }
+
+ // I'm pretty sure we always want to force this to true
+ // I'm pretty sure noone whats to set fullbright true if it wasn't true before.
+ // tmptex.DefaultTexture.Fullbright = true;
+
+ part.UpdateTextureEntry(tmptex.GetBytes());
+ }
+
+ return oldID;
+ }
+
///
/// Called once new texture data has been received for this updater.
///
- public void DataReceived(byte[] data, Scene scene)
+ ///
+ ///
+ /// True if the data given is reuseable.
+ /// The asset UUID given to the incoming data.
+ public UUID DataReceived(byte[] data, Scene scene)
{
SceneObjectPart part = scene.GetSceneObjectPart(PrimID);
@@ -281,7 +390,8 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
String.Format("DynamicTextureModule: Error preparing image using URL {0}", Url);
scene.SimChat(Utils.StringToBytes(msg), ChatTypeEnum.Say,
0, part.ParentGroup.RootPart.AbsolutePosition, part.Name, part.UUID, false);
- return;
+
+ return UUID.Zero;
}
byte[] assetData = null;
@@ -323,52 +433,23 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
cacheLayerDecode = null;
}
- UUID oldID = UUID.Zero;
-
- lock (part)
- {
- // mostly keep the values from before
- Primitive.TextureEntry tmptex = part.Shape.Textures;
-
- // remove the old asset from the cache
- oldID = tmptex.DefaultTexture.TextureID;
-
- if (Face == ALL_SIDES)
- {
- tmptex.DefaultTexture.TextureID = asset.FullID;
- }
- else
- {
- try
- {
- Primitive.TextureEntryFace texface = tmptex.CreateFace((uint)Face);
- texface.TextureID = asset.FullID;
- tmptex.FaceTextures[Face] = texface;
- }
- catch (Exception)
- {
- tmptex.DefaultTexture.TextureID = asset.FullID;
- }
- }
-
- // I'm pretty sure we always want to force this to true
- // I'm pretty sure noone whats to set fullbright true if it wasn't true before.
- // tmptex.DefaultTexture.Fullbright = true;
-
- part.UpdateTextureEntry(tmptex.GetBytes());
- }
+ UUID oldID = UpdatePart(part, asset.FullID);
if (oldID != UUID.Zero && ((Disp & DISP_EXPIRE) != 0))
{
- if (oldAsset == null) oldAsset = scene.AssetService.Get(oldID.ToString());
+ if (oldAsset == null)
+ oldAsset = scene.AssetService.Get(oldID.ToString());
+
if (oldAsset != null)
{
- if (oldAsset.Temporary == true)
+ if (oldAsset.Temporary)
{
scene.AssetService.Delete(oldID.ToString());
}
}
}
+
+ return asset.FullID;
}
private byte[] BlendTextures(byte[] frontImage, byte[] backImage, bool setNewAlpha, byte newAlpha)
diff --git a/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs b/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs
index 6e38b3f44c..2b3a0f29ff 100644
--- a/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/LoadImageURL/LoadImageURLModule.cs
@@ -67,6 +67,12 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
return true;
}
+// public bool AlwaysIdenticalConversion(string bodyData, string extraParams)
+// {
+// // We don't support conversion of body data.
+// return false;
+// }
+
public byte[] ConvertUrl(string url, string extraParams)
{
return null;
@@ -236,9 +242,11 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
stream.Close();
}
}
+
m_log.DebugFormat("[LOADIMAGEURLMODULE] Returning {0} bytes of image data for request {1}",
imageJ2000.Length, state.RequestID);
- m_textureManager.ReturnData(state.RequestID, imageJ2000);
+
+ m_textureManager.ReturnData(state.RequestID, imageJ2000, false);
}
#region Nested type: RequestState
diff --git a/OpenSim/Region/CoreModules/Scripting/VectorRender/Tests/VectorRenderModuleTests.cs b/OpenSim/Region/CoreModules/Scripting/VectorRender/Tests/VectorRenderModuleTests.cs
index 180ea9f015..b50c0bd7cd 100644
--- a/OpenSim/Region/CoreModules/Scripting/VectorRender/Tests/VectorRenderModuleTests.cs
+++ b/OpenSim/Region/CoreModules/Scripting/VectorRender/Tests/VectorRenderModuleTests.cs
@@ -51,12 +51,15 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
DynamicTextureModule m_dtm;
VectorRenderModule m_vrm;
- [SetUp]
- public void SetUp()
+ private void SetupScene(bool reuseTextures)
{
m_scene = new SceneHelpers().SetupScene();
+
m_dtm = new DynamicTextureModule();
+ m_dtm.ReuseTextures = reuseTextures;
+
m_vrm = new VectorRenderModule();
+
SceneHelpers.SetupSceneModules(m_scene, m_dtm, m_vrm);
}
@@ -65,6 +68,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
{
TestHelpers.InMethod();
+ SetupScene(false);
SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
UUID originalTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
@@ -86,6 +90,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
string dtText = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;";
+ SetupScene(false);
SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
m_dtm.AddDynamicTextureData(
@@ -116,6 +121,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
string dtText = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;";
+ SetupScene(false);
SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
m_dtm.AddDynamicTextureData(
@@ -147,6 +153,121 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
string dtText
= "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World; Image http://localhost/shouldnotexist.png";
+ SetupScene(false);
+ SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "",
+ 0);
+
+ UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "",
+ 0);
+
+ Assert.That(firstDynamicTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
+ }
+
+ [Test]
+ public void TestDrawReusingTexture()
+ {
+ TestHelpers.InMethod();
+
+ SetupScene(true);
+ SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
+ UUID originalTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;",
+ "",
+ 0);
+
+ Assert.That(originalTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
+ }
+
+ [Test]
+ public void TestRepeatSameDrawReusingTexture()
+ {
+ TestHelpers.InMethod();
+
+ string dtText = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;";
+
+ SetupScene(true);
+ SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "",
+ 0);
+
+ UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "",
+ 0);
+
+ Assert.That(firstDynamicTextureID, Is.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
+ }
+
+ [Test]
+ public void TestRepeatSameDrawDifferentExtraParamsReusingTexture()
+ {
+ TestHelpers.InMethod();
+
+ string dtText = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;";
+
+ SetupScene(true);
+ SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "",
+ 0);
+
+ UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
+
+ m_dtm.AddDynamicTextureData(
+ m_scene.RegionInfo.RegionID,
+ so.UUID,
+ m_vrm.GetContentType(),
+ dtText,
+ "alpha:250",
+ 0);
+
+ Assert.That(firstDynamicTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
+ }
+
+ [Test]
+ public void TestRepeatSameDrawContainingImageReusingTexture()
+ {
+ TestHelpers.InMethod();
+
+ string dtText
+ = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World; Image http://localhost/shouldnotexist.png";
+
+ SetupScene(true);
SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
m_dtm.AddDynamicTextureData(
diff --git a/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs b/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs
index 3a758c591a..4268f2e98a 100644
--- a/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/VectorRender/VectorRenderModule.cs
@@ -30,6 +30,7 @@ using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
+using System.Linq;
using System.Net;
using Nini.Config;
using OpenMetaverse;
@@ -73,6 +74,12 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
return true;
}
+// public bool AlwaysIdenticalConversion(string bodyData, string extraParams)
+// {
+// string[] lines = GetLines(bodyData);
+// return lines.Any((str, r) => str.StartsWith("Image"));
+// }
+
public byte[] ConvertUrl(string url, string extraParams)
{
return null;
@@ -80,7 +87,13 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
public byte[] ConvertData(string bodyData, string extraParams)
{
- return Draw(bodyData, extraParams);
+ bool reuseable;
+ return Draw(bodyData, extraParams, out reuseable);
+ }
+
+ private byte[] ConvertData(string bodyData, string extraParams, out bool reuseable)
+ {
+ return Draw(bodyData, extraParams, out reuseable);
}
public bool AsyncConvertUrl(UUID id, string url, string extraParams)
@@ -91,7 +104,11 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
public bool AsyncConvertData(UUID id, string bodyData, string extraParams)
{
// XXX: This isn't actually being done asynchronously!
- m_textureManager.ReturnData(id, ConvertData(bodyData, extraParams));
+ bool reuseable;
+ byte[] data = ConvertData(bodyData, extraParams, out reuseable);
+
+ m_textureManager.ReturnData(id, data, reuseable);
+
return true;
}
@@ -162,7 +179,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
#endregion
- private byte[] Draw(string data, string extraParams)
+ private byte[] Draw(string data, string extraParams, out bool reuseable)
{
// 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
// we will now support multiple comma seperated params in the form width:256,height:512,alpha:255
@@ -343,7 +360,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
}
}
- GDIDraw(data, graph, altDataDelim);
+ GDIDraw(data, graph, altDataDelim, out reuseable);
}
byte[] imageJ2000 = new byte[0];
@@ -434,8 +451,21 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
}
*/
- private void GDIDraw(string data, Graphics graph, char dataDelim)
+ ///
+ /// Split input data into discrete command lines.
+ ///
+ ///
+ ///
+ ///
+ private string[] GetLines(string data, char dataDelim)
{
+ char[] lineDelimiter = { dataDelim };
+ return data.Split(lineDelimiter);
+ }
+
+ private void GDIDraw(string data, Graphics graph, char dataDelim, out bool reuseable)
+ {
+ reuseable = true;
Point startPoint = new Point(0, 0);
Point endPoint = new Point(0, 0);
Pen drawPen = null;
@@ -450,11 +480,9 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
myFont = new Font(fontName, fontSize);
myBrush = new SolidBrush(Color.Black);
- char[] lineDelimiter = {dataDelim};
char[] partsDelimiter = {','};
- string[] lines = data.Split(lineDelimiter);
- foreach (string line in lines)
+ foreach (string line in GetLines(data, dataDelim))
{
string nextLine = line.Trim();
//replace with switch, or even better, do some proper parsing
@@ -485,6 +513,10 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
}
else if (nextLine.StartsWith("Image"))
{
+ // We cannot reuse any generated texture involving fetching an image via HTTP since that image
+ // can change.
+ reuseable = false;
+
float x = 0;
float y = 0;
GetParams(partsDelimiter, ref nextLine, 5, ref x, ref y);
diff --git a/OpenSim/Region/Framework/Interfaces/IDynamicTextureManager.cs b/OpenSim/Region/Framework/Interfaces/IDynamicTextureManager.cs
index 773ba737ca..1a3bcbbc71 100644
--- a/OpenSim/Region/Framework/Interfaces/IDynamicTextureManager.cs
+++ b/OpenSim/Region/Framework/Interfaces/IDynamicTextureManager.cs
@@ -33,7 +33,7 @@ namespace OpenSim.Region.Framework.Interfaces
public interface IDynamicTextureManager
{
void RegisterRender(string handleType, IDynamicTextureRender render);
- void ReturnData(UUID id, byte[] data);
+ void ReturnData(UUID id, byte[] data, bool isReuseable);
UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url, string extraParams,
int updateTimer);
@@ -113,6 +113,18 @@ namespace OpenSim.Region.Framework.Interfaces
string GetName();
string GetContentType();
bool SupportsAsynchronous();
+
+// ///
+// /// Return true if converting the input body and extra params data will always result in the same byte[] array
+// ///
+// ///
+// /// This method allows the caller to use a previously generated asset if it has one.
+// ///
+// ///
+// ///
+// ///
+// bool AlwaysIdenticalConversion(string bodyData, string extraParams);
+
byte[] ConvertUrl(string url, string extraParams);
byte[] ConvertData(string bodyData, string extraParams);
bool AsyncConvertUrl(UUID id, string url, string extraParams);