Actively dispose of Bitmaps in Warp3D image module and world map module once we've finished with them.

This might help with memory leakage issues though I suspect it won't.
0.7.4.1
Justin Clark-Casey (justincc) 2012-06-06 04:11:16 +01:00
parent 3b25021180
commit 2b0de66216
3 changed files with 248 additions and 215 deletions

View File

@ -204,7 +204,10 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
Bitmap bitmap = renderer.Scene.getImage(); Bitmap bitmap = renderer.Scene.getImage();
if (m_useAntiAliasing) if (m_useAntiAliasing)
bitmap = ImageUtils.ResizeImage(bitmap, viewport.Width, viewport.Height); {
using (Bitmap origBitmap = bitmap)
bitmap = ImageUtils.ResizeImage(origBitmap, viewport.Width, viewport.Height);
}
return bitmap; return bitmap;
} }
@ -318,8 +321,17 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
uint globalX, globalY; uint globalX, globalY;
Utils.LongToUInts(m_scene.RegionInfo.RegionHandle, out globalX, out globalY); Utils.LongToUInts(m_scene.RegionInfo.RegionHandle, out globalX, out globalY);
Bitmap image = TerrainSplat.Splat(heightmap, textureIDs, startHeights, heightRanges, new Vector3d(globalX, globalY, 0.0), m_scene.AssetService, textureTerrain); warp_Texture texture;
warp_Texture texture = new warp_Texture(image);
using (
Bitmap image
= TerrainSplat.Splat(
heightmap, textureIDs, startHeights, heightRanges,
new Vector3d(globalX, globalY, 0.0), m_scene.AssetService, textureTerrain))
{
texture = new warp_Texture(image);
}
warp_Material material = new warp_Material(texture); warp_Material material = new warp_Material(texture);
material.setReflectivity(50); material.setReflectivity(50);
renderer.Scene.addMaterial("TerrainColor", material); renderer.Scene.addMaterial("TerrainColor", material);
@ -546,42 +558,46 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
{ {
try try
{ {
Bitmap bitmap = (Bitmap)J2kImage.FromStream(stream); int pixelBytes;
width = bitmap.Width;
height = bitmap.Height;
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat); using (Bitmap bitmap = (Bitmap)J2kImage.FromStream(stream))
int pixelBytes = (bitmap.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4;
// Sum up the individual channels
unsafe
{ {
if (pixelBytes == 4) width = bitmap.Width;
height = bitmap.Height;
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
pixelBytes = (bitmap.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4;
// Sum up the individual channels
unsafe
{ {
for (int y = 0; y < height; y++) if (pixelBytes == 4)
{ {
byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride); for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{ {
b += row[x * pixelBytes + 0]; byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
g += row[x * pixelBytes + 1];
r += row[x * pixelBytes + 2]; for (int x = 0; x < width; x++)
a += row[x * pixelBytes + 3]; {
b += row[x * pixelBytes + 0];
g += row[x * pixelBytes + 1];
r += row[x * pixelBytes + 2];
a += row[x * pixelBytes + 3];
}
} }
} }
} else
else
{
for (int y = 0; y < height; y++)
{ {
byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride); for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{ {
b += row[x * pixelBytes + 0]; byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
g += row[x * pixelBytes + 1];
r += row[x * pixelBytes + 2]; for (int x = 0; x < width; x++)
{
b += row[x * pixelBytes + 0];
g += row[x * pixelBytes + 1];
r += row[x * pixelBytes + 2];
}
} }
} }
} }

View File

@ -84,218 +84,234 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
Debug.Assert(heightRanges.Length == 4); Debug.Assert(heightRanges.Length == 4);
Bitmap[] detailTexture = new Bitmap[4]; Bitmap[] detailTexture = new Bitmap[4];
Bitmap output = null;
BitmapData outputData = null;
if (textureTerrain) try
{ {
// Swap empty terrain textureIDs with default IDs if (textureTerrain)
for (int i = 0; i < textureIDs.Length; i++)
{ {
if (textureIDs[i] == UUID.Zero) // Swap empty terrain textureIDs with default IDs
textureIDs[i] = DEFAULT_TERRAIN_DETAIL[i]; for (int i = 0; i < textureIDs.Length; i++)
}
#region Texture Fetching
if (assetService != null)
{
for (int i = 0; i < 4; i++)
{ {
AssetBase asset; if (textureIDs[i] == UUID.Zero)
UUID cacheID = UUID.Combine(TERRAIN_CACHE_MAGIC, textureIDs[i]); textureIDs[i] = DEFAULT_TERRAIN_DETAIL[i];
}
// Try to fetch a cached copy of the decoded/resized version of this texture
asset = assetService.GetCached(cacheID.ToString()); #region Texture Fetching
if (asset != null)
if (assetService != null)
{
for (int i = 0; i < 4; i++)
{ {
try AssetBase asset;
{ UUID cacheID = UUID.Combine(TERRAIN_CACHE_MAGIC, textureIDs[i]);
using (System.IO.MemoryStream stream = new System.IO.MemoryStream(asset.Data))
detailTexture[i] = (Bitmap)Image.FromStream(stream); // Try to fetch a cached copy of the decoded/resized version of this texture
} asset = assetService.GetCached(cacheID.ToString());
catch (Exception ex)
{
m_log.Warn("Failed to decode cached terrain texture " + cacheID +
" (textureID: " + textureIDs[i] + "): " + ex.Message);
}
}
if (detailTexture[i] == null)
{
// Try to fetch the original JPEG2000 texture, resize if needed, and cache as PNG
asset = assetService.Get(textureIDs[i].ToString());
if (asset != null) if (asset != null)
{ {
try { detailTexture[i] = (Bitmap)CSJ2K.J2kImage.FromBytes(asset.Data); } try
{
using (System.IO.MemoryStream stream = new System.IO.MemoryStream(asset.Data))
detailTexture[i] = (Bitmap)Image.FromStream(stream);
}
catch (Exception ex) catch (Exception ex)
{ {
m_log.Warn("Failed to decode terrain texture " + asset.ID + ": " + ex.Message); m_log.Warn("Failed to decode cached terrain texture " + cacheID +
" (textureID: " + textureIDs[i] + "): " + ex.Message);
} }
} }
if (detailTexture[i] != null) if (detailTexture[i] == null)
{ {
Bitmap bitmap = detailTexture[i]; // Try to fetch the original JPEG2000 texture, resize if needed, and cache as PNG
asset = assetService.Get(textureIDs[i].ToString());
// Make sure this texture is the correct size, otherwise resize if (asset != null)
if (bitmap.Width != 256 || bitmap.Height != 256)
bitmap = ImageUtils.ResizeImage(bitmap, 256, 256);
// Save the decoded and resized texture to the cache
byte[] data;
using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
{ {
bitmap.Save(stream, ImageFormat.Png); try { detailTexture[i] = (Bitmap)CSJ2K.J2kImage.FromBytes(asset.Data); }
data = stream.ToArray(); catch (Exception ex)
{
m_log.Warn("Failed to decode terrain texture " + asset.ID + ": " + ex.Message);
}
} }
// Cache a PNG copy of this terrain texture if (detailTexture[i] != null)
AssetBase newAsset = new AssetBase
{ {
Data = data, Bitmap bitmap = detailTexture[i];
Description = "PNG",
Flags = AssetFlags.Collectable, // Make sure this texture is the correct size, otherwise resize
FullID = cacheID, if (bitmap.Width != 256 || bitmap.Height != 256)
ID = cacheID.ToString(), {
Local = true, using (Bitmap origBitmap = bitmap)
Name = String.Empty, {
Temporary = true, bitmap = ImageUtils.ResizeImage(origBitmap, 256, 256);
Type = (sbyte)AssetType.Unknown }
}; }
newAsset.Metadata.ContentType = "image/png";
assetService.Store(newAsset); // Save the decoded and resized texture to the cache
byte[] data;
using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
{
bitmap.Save(stream, ImageFormat.Png);
data = stream.ToArray();
}
// Cache a PNG copy of this terrain texture
AssetBase newAsset = new AssetBase
{
Data = data,
Description = "PNG",
Flags = AssetFlags.Collectable,
FullID = cacheID,
ID = cacheID.ToString(),
Local = true,
Name = String.Empty,
Temporary = true,
Type = (sbyte)AssetType.Unknown
};
newAsset.Metadata.ContentType = "image/png";
assetService.Store(newAsset);
}
} }
} }
} }
#endregion Texture Fetching
} }
#endregion Texture Fetching // Fill in any missing textures with a solid color
} for (int i = 0; i < 4; i++)
// Fill in any missing textures with a solid color
for (int i = 0; i < 4; i++)
{
if (detailTexture[i] == null)
{ {
// Create a solid color texture for this layer if (detailTexture[i] == null)
detailTexture[i] = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
using (Graphics gfx = Graphics.FromImage(detailTexture[i]))
{ {
using (SolidBrush brush = new SolidBrush(DEFAULT_TERRAIN_COLOR[i])) // Create a solid color texture for this layer
gfx.FillRectangle(brush, 0, 0, 256, 256); detailTexture[i] = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
using (Graphics gfx = Graphics.FromImage(detailTexture[i]))
{
using (SolidBrush brush = new SolidBrush(DEFAULT_TERRAIN_COLOR[i]))
gfx.FillRectangle(brush, 0, 0, 256, 256);
}
} }
} }
}
#region Layer Map
#region Layer Map
float[] layermap = new float[256 * 256];
float[] layermap = new float[256 * 256];
for (int y = 0; y < 256; y++)
{
for (int x = 0; x < 256; x++)
{
float height = heightmap[y * 256 + x];
float pctX = (float)x / 255f;
float pctY = (float)y / 255f;
// Use bilinear interpolation between the four corners of start height and
// height range to select the current values at this position
float startHeight = ImageUtils.Bilinear(
startHeights[0],
startHeights[2],
startHeights[1],
startHeights[3],
pctX, pctY);
startHeight = Utils.Clamp(startHeight, 0f, 255f);
float heightRange = ImageUtils.Bilinear(
heightRanges[0],
heightRanges[2],
heightRanges[1],
heightRanges[3],
pctX, pctY);
heightRange = Utils.Clamp(heightRange, 0f, 255f);
// Generate two frequencies of perlin noise based on our global position
// The magic values were taken from http://opensimulator.org/wiki/Terrain_Splatting
Vector3 vec = new Vector3
(
((float)regionPosition.X + x) * 0.20319f,
((float)regionPosition.Y + y) * 0.20319f,
height * 0.25f
);
float lowFreq = Perlin.noise2(vec.X * 0.222222f, vec.Y * 0.222222f) * 6.5f;
float highFreq = Perlin.turbulence2(vec.X, vec.Y, 2f) * 2.25f;
float noise = (lowFreq + highFreq) * 2f;
// Combine the current height, generated noise, start height, and height range parameters, then scale all of it
float layer = ((height + noise - startHeight) / heightRange) * 4f;
if (Single.IsNaN(layer)) layer = 0f;
layermap[y * 256 + x] = Utils.Clamp(layer, 0f, 3f);
}
}
#endregion Layer Map
#region Texture Compositing
Bitmap output = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
BitmapData outputData = output.LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
unsafe
{
// Get handles to all of the texture data arrays
BitmapData[] datas = new BitmapData[]
{
detailTexture[0].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[0].PixelFormat),
detailTexture[1].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[1].PixelFormat),
detailTexture[2].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[2].PixelFormat),
detailTexture[3].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[3].PixelFormat)
};
int[] comps = new int[]
{
(datas[0].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
(datas[1].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
(datas[2].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
(datas[3].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3
};
for (int y = 0; y < 256; y++) for (int y = 0; y < 256; y++)
{ {
for (int x = 0; x < 256; x++) for (int x = 0; x < 256; x++)
{ {
float layer = layermap[y * 256 + x]; float height = heightmap[y * 256 + x];
// Select two textures float pctX = (float)x / 255f;
int l0 = (int)Math.Floor(layer); float pctY = (float)y / 255f;
int l1 = Math.Min(l0 + 1, 3);
// Use bilinear interpolation between the four corners of start height and
byte* ptrA = (byte*)datas[l0].Scan0 + y * datas[l0].Stride + x * comps[l0]; // height range to select the current values at this position
byte* ptrB = (byte*)datas[l1].Scan0 + y * datas[l1].Stride + x * comps[l1]; float startHeight = ImageUtils.Bilinear(
byte* ptrO = (byte*)outputData.Scan0 + y * outputData.Stride + x * 3; startHeights[0],
startHeights[2],
float aB = *(ptrA + 0); startHeights[1],
float aG = *(ptrA + 1); startHeights[3],
float aR = *(ptrA + 2); pctX, pctY);
startHeight = Utils.Clamp(startHeight, 0f, 255f);
float bB = *(ptrB + 0);
float bG = *(ptrB + 1); float heightRange = ImageUtils.Bilinear(
float bR = *(ptrB + 2); heightRanges[0],
heightRanges[2],
float layerDiff = layer - l0; heightRanges[1],
heightRanges[3],
// Interpolate between the two selected textures pctX, pctY);
*(ptrO + 0) = (byte)Math.Floor(aB + layerDiff * (bB - aB)); heightRange = Utils.Clamp(heightRange, 0f, 255f);
*(ptrO + 1) = (byte)Math.Floor(aG + layerDiff * (bG - aG));
*(ptrO + 2) = (byte)Math.Floor(aR + layerDiff * (bR - aR)); // Generate two frequencies of perlin noise based on our global position
// The magic values were taken from http://opensimulator.org/wiki/Terrain_Splatting
Vector3 vec = new Vector3
(
((float)regionPosition.X + x) * 0.20319f,
((float)regionPosition.Y + y) * 0.20319f,
height * 0.25f
);
float lowFreq = Perlin.noise2(vec.X * 0.222222f, vec.Y * 0.222222f) * 6.5f;
float highFreq = Perlin.turbulence2(vec.X, vec.Y, 2f) * 2.25f;
float noise = (lowFreq + highFreq) * 2f;
// Combine the current height, generated noise, start height, and height range parameters, then scale all of it
float layer = ((height + noise - startHeight) / heightRange) * 4f;
if (Single.IsNaN(layer)) layer = 0f;
layermap[y * 256 + x] = Utils.Clamp(layer, 0f, 3f);
} }
} }
#endregion Layer Map
#region Texture Compositing
output = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
outputData = output.LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
unsafe
{
// Get handles to all of the texture data arrays
BitmapData[] datas = new BitmapData[]
{
detailTexture[0].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[0].PixelFormat),
detailTexture[1].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[1].PixelFormat),
detailTexture[2].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[2].PixelFormat),
detailTexture[3].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[3].PixelFormat)
};
int[] comps = new int[]
{
(datas[0].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
(datas[1].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
(datas[2].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
(datas[3].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3
};
for (int y = 0; y < 256; y++)
{
for (int x = 0; x < 256; x++)
{
float layer = layermap[y * 256 + x];
// Select two textures
int l0 = (int)Math.Floor(layer);
int l1 = Math.Min(l0 + 1, 3);
byte* ptrA = (byte*)datas[l0].Scan0 + y * datas[l0].Stride + x * comps[l0];
byte* ptrB = (byte*)datas[l1].Scan0 + y * datas[l1].Stride + x * comps[l1];
byte* ptrO = (byte*)outputData.Scan0 + y * outputData.Stride + x * 3;
float aB = *(ptrA + 0);
float aG = *(ptrA + 1);
float aR = *(ptrA + 2);
float bB = *(ptrB + 0);
float bG = *(ptrB + 1);
float bR = *(ptrB + 2);
float layerDiff = layer - l0;
// Interpolate between the two selected textures
*(ptrO + 0) = (byte)Math.Floor(aB + layerDiff * (bB - aB));
*(ptrO + 1) = (byte)Math.Floor(aG + layerDiff * (bG - aG));
*(ptrO + 2) = (byte)Math.Floor(aR + layerDiff * (bR - aR));
}
}
for (int i = 0; i < 4; i++)
detailTexture[i].UnlockBits(datas[i]);
}
}
finally
{
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
detailTexture[i].UnlockBits(datas[i]); if (detailTexture[i] != null)
detailTexture[i].Dispose();
} }
output.UnlockBits(outputData); output.UnlockBits(outputData);

View File

@ -1343,14 +1343,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
if (terrain == null) if (terrain == null)
return; return;
m_log.DebugFormat("[WORLDMAP]: Generating map image for {0}", m_scene.RegionInfo.RegionName);
byte[] data = terrain.WriteJpeg2000Image(); byte[] data = terrain.WriteJpeg2000Image();
if (data == null) if (data == null)
return; return;
byte[] overlay = GenerateOverlay(); byte[] overlay = GenerateOverlay();
m_log.Debug("[WORLDMAP]: STORING MAPTILE IMAGE");
UUID terrainImageID = UUID.Random(); UUID terrainImageID = UUID.Random();
UUID parcelImageID = UUID.Zero; UUID parcelImageID = UUID.Zero;
@ -1365,7 +1365,8 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
asset.Flags = AssetFlags.Maptile; asset.Flags = AssetFlags.Maptile;
// Store the new one // Store the new one
m_log.DebugFormat("[WORLDMAP]: Storing map tile {0}", asset.ID); m_log.DebugFormat("[WORLDMAP]: Storing map tile {0} for {1}", asset.ID, m_scene.RegionInfo.RegionName);
m_scene.AssetService.Store(asset); m_scene.AssetService.Store(asset);
if (overlay != null) if (overlay != null)