Fix some race conditions with the texture references
Add queuing of files Split CEncoding to separate file
This commit is contained in:
1 parent
1121261a4a
commit
649f81494e
11 files changed
+513
-196
No files matched your search
+12
-1
@@ -196,13 +196,24 @@ namespace Vocaluxe.Base
|
|||||||
/// Requests adding a texture from a bitmap <br />
|
/// Requests adding a texture from a bitmap <br />
|
||||||
/// Use this if you add textures from another thread or don't need it immediatelly
|
/// Use this if you add textures from another thread or don't need it immediatelly
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bmp"></param>
|
/// <param name="bmp">Bitmap to add, gets disposed after adding!</param>
|
||||||
/// <returns>Reference to texture</returns>
|
/// <returns>Reference to texture</returns>
|
||||||
public static CTextureRef EnqueueTexture(Bitmap bmp)
|
public static CTextureRef EnqueueTexture(Bitmap bmp)
|
||||||
{
|
{
|
||||||
return _Draw.EnqueueTexture(bmp);
|
return _Draw.EnqueueTexture(bmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Requests adding a texture from a file <br />
|
||||||
|
/// Use this if you add textures from another thread or don't need it immediatelly
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texturePath">Full path to the image file</param>
|
||||||
|
/// <returns>Reference to texture</returns>
|
||||||
|
public static CTextureRef EnqueueTexture(String texturePath)
|
||||||
|
{
|
||||||
|
return _Draw.EnqueueTexture(texturePath);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Requests updating a texture from a bitmap<br />
|
/// Requests updating a texture from a bitmap<br />
|
||||||
/// Use this if you add textures from another thread or don't need it immediatelly
|
/// Use this if you add textures from another thread or don't need it immediatelly
|
||||||
|
|||||||
@@ -42,21 +42,27 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CD3DTexture : CTextureBase, IDisposable
|
class CD3DTexture : CTextureBase
|
||||||
{
|
{
|
||||||
public readonly Texture D3DTexture;
|
public readonly Texture D3DTexture;
|
||||||
|
|
||||||
public CD3DTexture(Device device, Size dataSize, int texWidth = 0, int texHeight = 0) : base(dataSize, texWidth, texHeight)
|
public CD3DTexture(Device device, Size dataSize, int texWidth = 0, int texHeight = 0) : base(dataSize, texWidth, texHeight)
|
||||||
{
|
{
|
||||||
D3DTexture = new Texture(device, W2, H2, 0, Usage.AutoGenerateMipMap, Format.A8R8G8B8, Pool.Managed);
|
//Create a new texture in the managed pool, which does not need to be recreated on a lost device
|
||||||
|
//because a copy of the texture is hold in the Ram
|
||||||
|
D3DTexture = device == null ? null : new Texture(device, W2, H2, 0, Usage.AutoGenerateMipMap, Format.A8R8G8B8, Pool.Managed);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public override bool IsLoaded
|
||||||
{
|
{
|
||||||
if (D3DTexture.Disposed)
|
get { return D3DTexture != null; }
|
||||||
throw new ObjectDisposedException(GetType().Name);
|
}
|
||||||
D3DTexture.Dispose();
|
|
||||||
RefCount = 0;
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
base.Dispose();
|
||||||
|
if (D3DTexture != null)
|
||||||
|
D3DTexture.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -575,8 +581,8 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
#region Textures
|
#region Textures
|
||||||
protected override CD3DTexture _CreateTexture(Size dataSize)
|
protected override CD3DTexture _CreateTexture(Size dataSize)
|
||||||
{
|
{
|
||||||
//Create a new texture in the managed pool, which does not need to be recreated on a lost device
|
if (dataSize.Width < 0)
|
||||||
//because a copy of the texture is hold in the Ram
|
return new CD3DTexture(null, dataSize);
|
||||||
return new CD3DTexture(_Device, dataSize, _CheckForNextPowerOf2(dataSize.Width), _CheckForNextPowerOf2(dataSize.Height));
|
return new CD3DTexture(_Device, dataSize, _CheckForNextPowerOf2(dataSize.Width), _CheckForNextPowerOf2(dataSize.Height));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+356
-93
@@ -2,9 +2,10 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Drawing.Imaging;
|
using System.Drawing.Imaging;
|
||||||
using System.IO;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Vocaluxe.Base;
|
using Vocaluxe.Base;
|
||||||
using VocaluxeLib;
|
using VocaluxeLib;
|
||||||
using VocaluxeLib.Draw;
|
using VocaluxeLib.Draw;
|
||||||
@@ -12,40 +13,59 @@ using System.Diagnostics;
|
|||||||
|
|
||||||
namespace Vocaluxe.Lib.Draw
|
namespace Vocaluxe.Lib.Draw
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for graphic drivers
|
||||||
|
/// A few notes:
|
||||||
|
/// Writes to textures (TTextureType) should only be done in the main thread
|
||||||
|
/// Writes to the RefCount should only be done in the main thread and guarded by _Textures lock
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TTextureType"></typeparam>
|
||||||
abstract class CDrawBase<TTextureType> where TTextureType : CTextureBase, IDisposable
|
abstract class CDrawBase<TTextureType> where TTextureType : CTextureBase, IDisposable
|
||||||
{
|
{
|
||||||
private enum EQueueAction
|
private enum EQueueAction
|
||||||
{
|
{
|
||||||
Add,
|
Add,
|
||||||
Update
|
Update,
|
||||||
|
Delete
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct STextureQueue
|
private struct STextureQueue
|
||||||
{
|
{
|
||||||
public readonly WeakReference TextureRef;
|
public readonly WeakReference TextureRef;
|
||||||
|
/// <summary>
|
||||||
|
/// Only valid if Data is byte[]
|
||||||
|
/// </summary>
|
||||||
public readonly Size DataSize;
|
public readonly Size DataSize;
|
||||||
//Use either Data or DataBmp!
|
public readonly object Data;
|
||||||
public readonly byte[] Data;
|
|
||||||
//This is disposed after use
|
|
||||||
public readonly Bitmap DataBmp;
|
|
||||||
public readonly EQueueAction Action;
|
public readonly EQueueAction Action;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new Queue entry
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="textureRef"></param>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
/// <param name="data">Texture data as a bitmap</param>
|
||||||
|
public STextureQueue(CTextureRef textureRef, EQueueAction action, Bitmap data)
|
||||||
|
{
|
||||||
|
TextureRef = new WeakReference(textureRef);
|
||||||
|
Action = action;
|
||||||
|
Data = data;
|
||||||
|
DataSize = new Size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new Queue entry
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="textureRef"></param>
|
||||||
|
/// <param name="action"></param>
|
||||||
|
/// <param name="dataSize">Size of data</param>
|
||||||
|
/// <param name="data">Texture data</param>
|
||||||
public STextureQueue(CTextureRef textureRef, EQueueAction action, Size dataSize, byte[] data)
|
public STextureQueue(CTextureRef textureRef, EQueueAction action, Size dataSize, byte[] data)
|
||||||
{
|
{
|
||||||
TextureRef = new WeakReference(textureRef);
|
TextureRef = new WeakReference(textureRef);
|
||||||
Action = action;
|
Action = action;
|
||||||
DataSize = dataSize;
|
DataSize = dataSize;
|
||||||
Data = data;
|
Data = data;
|
||||||
DataBmp = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public STextureQueue(CTextureRef textureRef, EQueueAction action, Bitmap bmp)
|
|
||||||
{
|
|
||||||
TextureRef = new WeakReference(textureRef);
|
|
||||||
Action = action;
|
|
||||||
DataSize = bmp.GetSize();
|
|
||||||
Data = null;
|
|
||||||
DataBmp = bmp;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,14 +86,15 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
protected readonly CKeys _Keys = new CKeys();
|
protected readonly CKeys _Keys = new CKeys();
|
||||||
protected readonly CMouse _Mouse = new CMouse();
|
protected readonly CMouse _Mouse = new CMouse();
|
||||||
|
|
||||||
private readonly Object _MutexID = new object();
|
|
||||||
private int _NextID;
|
private int _NextID;
|
||||||
|
private readonly Object _MutexID = new object();
|
||||||
|
|
||||||
// Maps texture IDs to textures. Multiple IDs can be mapped to one texture (-->texture.RefCount>1)
|
// Maps texture IDs to textures. Multiple IDs can be mapped to one texture (-->texture.RefCount>1)
|
||||||
private readonly Dictionary<int, TTextureType> _Textures = new Dictionary<int, TTextureType>();
|
private readonly Dictionary<int, TTextureType> _Textures = new Dictionary<int, TTextureType>();
|
||||||
// Maps texturePaths to textures. Texture may have already been disposed (RefCount<=0) or even null
|
// Maps texturePaths to textures. Texture may have already been disposed (RefCount<=0)
|
||||||
private readonly Dictionary<string, STextureCacheEntry> _TextureCache = new Dictionary<string, STextureCacheEntry>();
|
private readonly Dictionary<string, STextureCacheEntry> _TextureCache = new Dictionary<string, STextureCacheEntry>();
|
||||||
private readonly Queue<STextureQueue> _TexturesToLoad = new Queue<STextureQueue>();
|
private readonly Queue<STextureQueue> _TextureQueue = new Queue<STextureQueue>();
|
||||||
|
private readonly Dictionary<string, Task<Size>> _BitmapsLoading = new Dictionary<string, Task<Size>>();
|
||||||
private int _TextureCount;
|
private int _TextureCount;
|
||||||
|
|
||||||
protected int _H;
|
protected int _H;
|
||||||
@@ -87,6 +108,8 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
protected int _BorderBottom;
|
protected int _BorderBottom;
|
||||||
protected EGeneralAlignment _CurrentAlignment = EGeneralAlignment.Middle;
|
protected EGeneralAlignment _CurrentAlignment = EGeneralAlignment.Middle;
|
||||||
|
|
||||||
|
private int _MainThreadID;
|
||||||
|
|
||||||
protected abstract void _ClearScreen();
|
protected abstract void _ClearScreen();
|
||||||
protected abstract void _AdjustNewBorders();
|
protected abstract void _AdjustNewBorders();
|
||||||
protected abstract void _LeaveFullScreen();
|
protected abstract void _LeaveFullScreen();
|
||||||
@@ -99,8 +122,23 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
|
|
||||||
protected abstract void _EnterFullScreen();
|
protected abstract void _EnterFullScreen();
|
||||||
|
|
||||||
|
public virtual bool Init()
|
||||||
|
{
|
||||||
|
_MainThreadID = Thread.CurrentThread.ManagedThreadId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This makes sure, the function is called from within the main thread
|
||||||
|
/// </summary>
|
||||||
|
private void _EnsureMainThread()
|
||||||
|
{
|
||||||
|
Debug.Assert(_MainThreadID == Thread.CurrentThread.ManagedThreadId);
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void Unload()
|
public virtual void Unload()
|
||||||
{
|
{
|
||||||
|
_EnsureMainThread();
|
||||||
lock (_Textures)
|
lock (_Textures)
|
||||||
{
|
{
|
||||||
//Dispose all textures
|
//Dispose all textures
|
||||||
@@ -196,6 +234,18 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if a texture with the given size needs to be resized
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="size"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static bool _RequiresResize(Size size)
|
||||||
|
{
|
||||||
|
int maxArea = _GetMaxTextureArea();
|
||||||
|
int curArea = size.Width * size.Height;
|
||||||
|
return curArea > maxArea;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the size for a new texture using the quality and smart values for rescaling
|
/// Calculates the size for a new texture using the quality and smart values for rescaling
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -207,6 +257,7 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
int curArea = size.Width * size.Height;
|
int curArea = size.Width * size.Height;
|
||||||
if (curArea <= maxArea)
|
if (curArea <= maxArea)
|
||||||
return size;
|
return size;
|
||||||
|
Debug.Assert(_RequiresResize(size));
|
||||||
double factor = Math.Sqrt((double)maxArea / curArea);
|
double factor = Math.Sqrt((double)maxArea / curArea);
|
||||||
Size newSize = new Size((int)(size.Width * factor), (int)(size.Height * factor));
|
Size newSize = new Size((int)(size.Width * factor), (int)(size.Height * factor));
|
||||||
if (!_NonPowerOf2TextureSupported)
|
if (!_NonPowerOf2TextureSupported)
|
||||||
@@ -240,7 +291,8 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Factory method to create a texture of the actual type
|
/// Factory method to create a texture of the actual type <br />
|
||||||
|
/// The implementer has to create an invalid texture (IsLoaded=false) if dataSize=(-1,-1)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dataSize"></param>
|
/// <param name="dataSize"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
@@ -293,21 +345,36 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the texture to the cache
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texture"></param>
|
||||||
|
/// <param name="origSize"></param>
|
||||||
|
/// <param name="texturePath"></param>
|
||||||
private void _AddToCache(TTextureType texture, Size origSize, string texturePath)
|
private void _AddToCache(TTextureType texture, Size origSize, string texturePath)
|
||||||
{
|
{
|
||||||
if (texture != null && !String.IsNullOrEmpty(texturePath))
|
if (String.IsNullOrEmpty(texturePath))
|
||||||
|
return;
|
||||||
|
Debug.Assert(texture != null);
|
||||||
|
lock (_Textures)
|
||||||
{
|
{
|
||||||
|
Debug.Assert(!_TextureCache.ContainsKey(texturePath));
|
||||||
texture.TexturePath = texturePath;
|
texture.TexturePath = texturePath;
|
||||||
STextureCacheEntry cacheEntry = new STextureCacheEntry {OrigSize = origSize, Texture = texture};
|
STextureCacheEntry cacheEntry = new STextureCacheEntry {OrigSize = origSize, Texture = texture};
|
||||||
lock (_Textures)
|
_TextureCache.Add(texturePath, cacheEntry);
|
||||||
{
|
|
||||||
_TextureCache[texturePath] = cacheEntry;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a new texture reference from the cache <br />
|
||||||
|
/// Thread safe but you should hold the _TextureCache lock if you need the loader task
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="texturePath"></param>
|
||||||
|
/// <returns></returns>
|
||||||
private CTextureRef _GetFromCache(string texturePath)
|
private CTextureRef _GetFromCache(string texturePath)
|
||||||
{
|
{
|
||||||
|
if (String.IsNullOrEmpty(texturePath))
|
||||||
|
return null;
|
||||||
lock (_Textures)
|
lock (_Textures)
|
||||||
{
|
{
|
||||||
STextureCacheEntry cacheEntry;
|
STextureCacheEntry cacheEntry;
|
||||||
@@ -323,8 +390,9 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Method used to add a texture to the texture list and return a TextureRef to it<br />
|
/// Method used to add a texture to the texture list and return a TextureRef to it <br />
|
||||||
/// Important: This should be the only function to ever write to _Textures
|
/// If you are using an already used texture, you have to hold the _Textures lock or call from the main thread! <br />
|
||||||
|
/// Thread safe
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="origSize"></param>
|
/// <param name="origSize"></param>
|
||||||
/// <param name="texture"></param>
|
/// <param name="texture"></param>
|
||||||
@@ -332,25 +400,26 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
protected CTextureRef _GetTextureReference(Size origSize, TTextureType texture)
|
protected CTextureRef _GetTextureReference(Size origSize, TTextureType texture)
|
||||||
{
|
{
|
||||||
Debug.Assert(origSize.Width > 0 && origSize.Height > 0);
|
Debug.Assert(origSize.Width > 0 && origSize.Height > 0);
|
||||||
|
Debug.Assert(texture != null);
|
||||||
int id;
|
int id;
|
||||||
lock (_MutexID)
|
lock (_MutexID)
|
||||||
{
|
{
|
||||||
id = _NextID++;
|
id = _NextID++;
|
||||||
}
|
}
|
||||||
CTextureRef textureRef = new CTextureRef(id, origSize);
|
CTextureRef textureRef = new CTextureRef(id, origSize);
|
||||||
if (texture != null)
|
|
||||||
texture.RefCount++;
|
|
||||||
if (texture == null || texture.RefCount == 1)
|
|
||||||
_TextureCount++;
|
|
||||||
lock (_Textures)
|
lock (_Textures)
|
||||||
{
|
{
|
||||||
|
if (texture.RefCount == 0)
|
||||||
|
_TextureCount++;
|
||||||
|
texture.RefCount++;
|
||||||
_Textures.Add(id, texture);
|
_Textures.Add(id, texture);
|
||||||
}
|
}
|
||||||
return textureRef;
|
return textureRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Method used to add a texture to the texture list and return a TextureRef to it<br />
|
/// Method used to add a texture to the texture list and return a TextureRef to it <br />
|
||||||
|
/// Thread safe <br />
|
||||||
/// Convenience method
|
/// Convenience method
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="origWidth"></param>
|
/// <param name="origWidth"></param>
|
||||||
@@ -376,7 +445,7 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool _GetTexture(CTextureRef textureRef, out TTextureType texture)
|
protected bool _GetTexture(CTextureRef textureRef, out TTextureType texture, bool checkDrawable = true)
|
||||||
{
|
{
|
||||||
if (textureRef == null)
|
if (textureRef == null)
|
||||||
{
|
{
|
||||||
@@ -385,7 +454,7 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
}
|
}
|
||||||
lock (_Textures)
|
lock (_Textures)
|
||||||
{
|
{
|
||||||
return _Textures.TryGetValue(textureRef.ID, out texture) && texture != null;
|
return _Textures.TryGetValue(textureRef.ID, out texture) && (!checkDrawable || texture.IsLoaded);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,23 +464,27 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
/// <param name="texture"></param>
|
/// <param name="texture"></param>
|
||||||
private void _DisposeTexture(TTextureType texture)
|
private void _DisposeTexture(TTextureType texture)
|
||||||
{
|
{
|
||||||
if (texture != null && --texture.RefCount <= 0)
|
_EnsureMainThread();
|
||||||
|
lock (_Textures)
|
||||||
{
|
{
|
||||||
if (texture.TexturePath != null)
|
if (texture != null && --texture.RefCount <= 0)
|
||||||
_TextureCache.Remove(texture.TexturePath);
|
{
|
||||||
texture.Dispose();
|
if (texture.TexturePath != null)
|
||||||
_TextureCount--;
|
_TextureCache.Remove(texture.TexturePath);
|
||||||
|
texture.Dispose();
|
||||||
|
_TextureCount--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _CheckQueue()
|
private void _CheckQueue()
|
||||||
{
|
{
|
||||||
lock (_Textures)
|
_EnsureMainThread();
|
||||||
|
lock (_TextureQueue)
|
||||||
{
|
{
|
||||||
while (_TexturesToLoad.Count > 0)
|
while (_TextureQueue.Count > 0)
|
||||||
{
|
{
|
||||||
STextureQueue q = _TexturesToLoad.Dequeue();
|
STextureQueue q = _TextureQueue.Dequeue();
|
||||||
Debug.Assert(q.Data != null ^ q.DataBmp != null);
|
|
||||||
CTextureRef textureRef = q.TextureRef.Target as CTextureRef;
|
CTextureRef textureRef = q.TextureRef.Target as CTextureRef;
|
||||||
if (!q.TextureRef.IsAlive || textureRef == null || !_Textures.ContainsKey(textureRef.ID))
|
if (!q.TextureRef.IsAlive || textureRef == null || !_Textures.ContainsKey(textureRef.ID))
|
||||||
continue;
|
continue;
|
||||||
@@ -419,23 +492,78 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
if (q.Action == EQueueAction.Add)
|
if (q.Action == EQueueAction.Add)
|
||||||
{
|
{
|
||||||
TTextureType texture = null;
|
TTextureType texture = null;
|
||||||
if (q.Data == null)
|
// ReSharper disable CanBeReplacedWithTryCastAndCheckForNull
|
||||||
_WriteBitmapToTexture(ref texture, q.DataBmp);
|
if (q.Data is Bitmap)
|
||||||
|
_WriteBitmapToTexture(ref texture, (Bitmap)q.Data);
|
||||||
|
else if (q.Data is byte[])
|
||||||
|
texture = _CreateAndFillTexture(q.DataSize, (byte[])q.Data);
|
||||||
else
|
else
|
||||||
texture = _CreateAndFillTexture(q.DataSize, q.Data);
|
throw new ArgumentException("q.Data is of invalid type");
|
||||||
texture.RefCount = 1; //Just created, so only 1 reference
|
// ReSharper restore CanBeReplacedWithTryCastAndCheckForNull
|
||||||
_Textures[textureRef.ID] = texture;
|
TTextureType oldTexture = _Textures[textureRef.ID];
|
||||||
|
_MergeTextures(oldTexture, texture);
|
||||||
}
|
}
|
||||||
else
|
else if (q.Action == EQueueAction.Update)
|
||||||
{
|
{
|
||||||
if (q.Data == null)
|
// ReSharper disable CanBeReplacedWithTryCastAndCheckForNull
|
||||||
UpdateTexture(textureRef, q.DataBmp);
|
if (q.Data is Bitmap)
|
||||||
|
UpdateTexture(textureRef, (Bitmap)q.Data);
|
||||||
|
else if (q.Data is byte[])
|
||||||
|
UpdateTexture(textureRef, q.DataSize.Width, q.DataSize.Height, (byte[])q.Data);
|
||||||
else
|
else
|
||||||
UpdateTexture(textureRef, q.DataSize.Width, q.DataSize.Height, q.Data);
|
throw new ArgumentException("q.Data is of invalid type");
|
||||||
|
// ReSharper restore CanBeReplacedWithTryCastAndCheckForNull
|
||||||
}
|
}
|
||||||
|
else if (q.Action == EQueueAction.Delete)
|
||||||
|
RemoveTexture(ref textureRef);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces one oldTexture by newTexture summing the refCounts. Also disposes oldTexture
|
||||||
|
/// IMPORTANT: Caller MUST hold the _Textures lock!
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="oldTexture"></param>
|
||||||
|
/// <param name="newTexture"></param>
|
||||||
|
private void _MergeTextures(TTextureType oldTexture, TTextureType newTexture)
|
||||||
|
{
|
||||||
|
Debug.Assert(oldTexture.RefCount > 0);
|
||||||
|
newTexture.RefCount += oldTexture.RefCount;
|
||||||
|
// ReSharper disable AccessToDisposedClosure
|
||||||
|
IEnumerable<int> oldKeys = _Textures.Where(pair => pair.Value == oldTexture).Select(pair => pair.Key).ToArray();
|
||||||
|
// ReSharper restore AccessToDisposedClosure
|
||||||
|
foreach (int key in oldKeys)
|
||||||
|
_Textures[key] = newTexture;
|
||||||
|
oldTexture.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a texture is currently beeing loaded and waits for completion if it is <br />
|
||||||
|
/// Required for cached entries, but should not be called from inside any locks unless async is true
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="textureRef"></param>
|
||||||
|
/// <param name="filePath"></param>
|
||||||
|
/// <param name="async">If true, do not block</param>
|
||||||
|
private void _WaitForTextureLoaded(CTextureRef textureRef, string filePath, bool async)
|
||||||
|
{
|
||||||
|
// Check if texture is already loaded
|
||||||
|
if (textureRef.OrigSize.Width > 0)
|
||||||
|
return;
|
||||||
|
Task<Size> loader;
|
||||||
|
lock (_BitmapsLoading)
|
||||||
|
{
|
||||||
|
if (!_BitmapsLoading.TryGetValue(filePath, out loader))
|
||||||
|
Debug.Assert(false, "Corruption: Texture not loaded but no loader found");
|
||||||
|
}
|
||||||
|
// We found the texture in the cache, but its bitmap is not yet loaded
|
||||||
|
// So wait for completion and update the origSize once it is available
|
||||||
|
// Note: loader.Result blocks till completion of the task
|
||||||
|
if (async)
|
||||||
|
Task.Factory.StartNew(() => textureRef.OrigSize = loader.Result);
|
||||||
|
else
|
||||||
|
textureRef.OrigSize = loader.Result;
|
||||||
|
}
|
||||||
#endregion private/protected
|
#endregion private/protected
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -445,25 +573,22 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
/// <returns>A STexture object containing the added texture</returns>
|
/// <returns>A STexture object containing the added texture</returns>
|
||||||
public CTextureRef AddTexture(string texturePath)
|
public CTextureRef AddTexture(string texturePath)
|
||||||
{
|
{
|
||||||
CTextureRef textureRef = _GetFromCache(texturePath);
|
_EnsureMainThread();
|
||||||
|
CTextureRef textureRef;
|
||||||
|
lock (_TextureCache)
|
||||||
|
{
|
||||||
|
textureRef = _GetFromCache(texturePath);
|
||||||
|
}
|
||||||
if (textureRef != null)
|
if (textureRef != null)
|
||||||
|
{
|
||||||
|
_WaitForTextureLoaded(textureRef, texturePath, false);
|
||||||
|
Debug.Assert(textureRef.OrigSize.Width > 0);
|
||||||
return textureRef;
|
return textureRef;
|
||||||
|
}
|
||||||
|
|
||||||
if (!File.Exists(texturePath))
|
Bitmap bmp = CHelper.LoadBitmap(texturePath);
|
||||||
{
|
if (bmp == null)
|
||||||
CLog.LogError("Can't find File: " + texturePath);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
Bitmap bmp;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bmp = new Bitmap(texturePath);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
CLog.LogError("Error loading Texture: " + texturePath);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
textureRef = AddTexture(bmp, texturePath);
|
textureRef = AddTexture(bmp, texturePath);
|
||||||
@@ -477,6 +602,7 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
|
|
||||||
public CTextureRef AddTexture(Bitmap bmp)
|
public CTextureRef AddTexture(Bitmap bmp)
|
||||||
{
|
{
|
||||||
|
_EnsureMainThread();
|
||||||
return AddTexture(bmp, null);
|
return AddTexture(bmp, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -485,52 +611,176 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bmp">The Bitmap of which the texure will be created from</param>
|
/// <param name="bmp">The Bitmap of which the texure will be created from</param>
|
||||||
/// <param name="texturePath"></param>
|
/// <param name="texturePath"></param>
|
||||||
/// <returns>A STexture object containing the added texture</returns>
|
/// <returns>A reference to the texture</returns>
|
||||||
public CTextureRef AddTexture(Bitmap bmp, string texturePath)
|
public CTextureRef AddTexture(Bitmap bmp, string texturePath)
|
||||||
{
|
{
|
||||||
|
_EnsureMainThread();
|
||||||
if (bmp.Height == 0 || bmp.Width == 0)
|
if (bmp.Height == 0 || bmp.Width == 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
CTextureRef textureRef;
|
||||||
Size origSize = bmp.GetSize();
|
Size origSize = bmp.GetSize();
|
||||||
|
|
||||||
TTextureType texture = null;
|
TTextureType texture = null;
|
||||||
_WriteBitmapToTexture(ref texture, bmp);
|
// Make the Get/Add Cache methods atomic
|
||||||
|
lock (_TextureCache)
|
||||||
|
{
|
||||||
|
textureRef = _GetFromCache(texturePath);
|
||||||
|
if (textureRef == null)
|
||||||
|
{
|
||||||
|
_WriteBitmapToTexture(ref texture, bmp);
|
||||||
|
|
||||||
_AddToCache(texture, origSize, texturePath);
|
_AddToCache(texture, origSize, texturePath);
|
||||||
return _GetTextureReference(origSize, texture);
|
}
|
||||||
|
}
|
||||||
|
if (textureRef == null)
|
||||||
|
textureRef = _GetTextureReference(origSize, texture);
|
||||||
|
else
|
||||||
|
_WaitForTextureLoaded(textureRef, texturePath, false);
|
||||||
|
Debug.Assert(textureRef.OrigSize.Width > 0);
|
||||||
|
return textureRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CTextureRef AddTexture(int w, int h, byte[] data)
|
public CTextureRef AddTexture(int w, int h, byte[] data)
|
||||||
{
|
{
|
||||||
|
_EnsureMainThread();
|
||||||
TTextureType texture = _CreateAndFillTexture(new Size(w, h), data);
|
TTextureType texture = _CreateAndFillTexture(new Size(w, h), data);
|
||||||
return _GetTextureReference(w, h, texture);
|
return _GetTextureReference(w, h, texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CTextureRef EnqueueTexture(int w, int h, byte[] data)
|
public CTextureRef EnqueueTexture(int w, int h, byte[] data)
|
||||||
{
|
{
|
||||||
lock (_Textures)
|
lock (_TextureQueue)
|
||||||
{
|
{
|
||||||
CTextureRef textureRef = _GetTextureReference(w, h, null);
|
CTextureRef textureRef = _GetTextureReference(w, h, _CreateTexture(new Size(-1, -1)));
|
||||||
_TexturesToLoad.Enqueue(new STextureQueue(textureRef, EQueueAction.Add, new Size(w, h), data));
|
_TextureQueue.Enqueue(new STextureQueue(textureRef, EQueueAction.Add, new Size(w, h), data));
|
||||||
return textureRef;
|
return textureRef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CTextureRef EnqueueTexture(Bitmap bmp)
|
public CTextureRef EnqueueTexture(Bitmap bmp)
|
||||||
{
|
{
|
||||||
lock (_Textures)
|
CTextureRef textureRef = _GetTextureReference(bmp.GetSize(), _CreateTexture(new Size(-1, -1)));
|
||||||
|
if (_RequiresResize(bmp.GetSize()))
|
||||||
{
|
{
|
||||||
CTextureRef textureRef = _GetTextureReference(bmp.GetSize(), null);
|
Task.Factory.StartNew(() =>
|
||||||
_TexturesToLoad.Enqueue(new STextureQueue(textureRef, EQueueAction.Add, bmp));
|
{
|
||||||
|
Bitmap bmp2 = bmp.Resize(_GetNewTextureSize(bmp.Size));
|
||||||
|
bmp.Dispose();
|
||||||
|
lock (_TextureQueue)
|
||||||
|
{
|
||||||
|
_TextureQueue.Enqueue(new STextureQueue(textureRef, EQueueAction.Add, bmp2));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lock (_TextureQueue)
|
||||||
|
{
|
||||||
|
_TextureQueue.Enqueue(new STextureQueue(textureRef, EQueueAction.Add, bmp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return textureRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CTextureRef EnqueueTexture(String filePath)
|
||||||
|
{
|
||||||
|
// 3 important scenarios: (+ means after, || means in parallel)
|
||||||
|
// 1) EnqueueTexture(fileA) (+ or ||) EnqueueTexture(fileA) --> No block on 2nd call and same texture returned
|
||||||
|
// 2) EnqueueTexture(fileA) + AddTexture(fileA) before texture is loaded --> Wait in AddTexture for this to finish
|
||||||
|
// 3) a) AddTexture(fileA) || b) EnqueueTexture(fileA) with a)getFromCache + b)getFromCache + a)addToCache + b)addToCache --> Only keep one copy
|
||||||
|
// All solved with atomic get/add cache and _WaitForTextureLoaded
|
||||||
|
|
||||||
|
CTextureRef textureRef;
|
||||||
|
TTextureType texture = null;
|
||||||
|
Task<Size> loader = null;
|
||||||
|
// Make Get/Add cache atomic
|
||||||
|
lock (_TextureCache)
|
||||||
|
{
|
||||||
|
textureRef = _GetFromCache(filePath);
|
||||||
|
if (textureRef == null)
|
||||||
|
{
|
||||||
|
Size invalidSize = new Size(-1, -1);
|
||||||
|
texture = _CreateTexture(invalidSize);
|
||||||
|
// This cache entry has to be updated once the real origSize is available
|
||||||
|
_AddToCache(texture, invalidSize, filePath);
|
||||||
|
textureRef = _GetTextureReference(invalidSize, texture);
|
||||||
|
// Add the loader to the dictionary in the same lock as the add cache call
|
||||||
|
// Avoids that we get a cache entry in AddTexture but no Task
|
||||||
|
loader = new Task<Size>(() => _LoadAndEnqueueBitmap(filePath, textureRef));
|
||||||
|
lock (_BitmapsLoading)
|
||||||
|
{
|
||||||
|
_BitmapsLoading.Add(filePath, loader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texture == null)
|
||||||
|
{
|
||||||
|
// Found in cache
|
||||||
|
_WaitForTextureLoaded(textureRef, filePath, true);
|
||||||
return textureRef;
|
return textureRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Debug.Assert(loader != null);
|
||||||
|
Debug.Assert(texture != null);
|
||||||
|
loader.Start();
|
||||||
|
return textureRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper function for loading a bitmap in a task
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePath"></param>
|
||||||
|
/// <param name="textureRef"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private Size _LoadAndEnqueueBitmap(string filePath, CTextureRef textureRef)
|
||||||
|
{
|
||||||
|
Size origSize;
|
||||||
|
Bitmap bmp = CHelper.LoadBitmap(filePath);
|
||||||
|
if (bmp == null)
|
||||||
|
{
|
||||||
|
RemoveTexture(ref textureRef); // Done asynchonously in the function
|
||||||
|
origSize = new Size(-1, -1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
origSize = bmp.GetSize();
|
||||||
|
textureRef.OrigSize = origSize;
|
||||||
|
// Update cache, use the same lock as in add/get cache methods
|
||||||
|
lock (_Textures)
|
||||||
|
{
|
||||||
|
STextureCacheEntry cacheEntry;
|
||||||
|
if (_TextureCache.TryGetValue(filePath, out cacheEntry))
|
||||||
|
{
|
||||||
|
cacheEntry.OrigSize = origSize;
|
||||||
|
_TextureCache[filePath] = cacheEntry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Size size = _GetNewTextureSize(bmp.GetSize());
|
||||||
|
if (!size.Equals(bmp.GetSize()))
|
||||||
|
{
|
||||||
|
Bitmap bmp2 = bmp.Resize(size);
|
||||||
|
bmp.Dispose();
|
||||||
|
bmp = bmp2;
|
||||||
|
}
|
||||||
|
lock (_TextureQueue)
|
||||||
|
{
|
||||||
|
_TextureQueue.Enqueue(new STextureQueue(textureRef, EQueueAction.Add, bmp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lock (_BitmapsLoading)
|
||||||
|
{
|
||||||
|
_BitmapsLoading.Remove(filePath);
|
||||||
|
}
|
||||||
|
return origSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EnqueueTextureUpdate(CTextureRef textureRef, Bitmap bmp)
|
public void EnqueueTextureUpdate(CTextureRef textureRef, Bitmap bmp)
|
||||||
{
|
{
|
||||||
lock (_Textures)
|
lock (_TextureQueue)
|
||||||
{
|
{
|
||||||
_TexturesToLoad.Enqueue(new STextureQueue(textureRef, EQueueAction.Update, bmp));
|
_TextureQueue.Enqueue(new STextureQueue(textureRef, EQueueAction.Update, bmp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,11 +794,12 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
/// <returns>True if succeeded</returns>
|
/// <returns>True if succeeded</returns>
|
||||||
public void UpdateTexture(CTextureRef textureRef, int w, int h, byte[] data)
|
public void UpdateTexture(CTextureRef textureRef, int w, int h, byte[] data)
|
||||||
{
|
{
|
||||||
|
_EnsureMainThread();
|
||||||
TTextureType texture;
|
TTextureType texture;
|
||||||
if (!_GetTexture(textureRef, out texture))
|
if (!_GetTexture(textureRef, out texture, false))
|
||||||
return;
|
return;
|
||||||
bool reuseTexture = _IsTextureUsable(texture, w, h);
|
bool reuseTexture = _IsTextureUsable(texture, w, h);
|
||||||
if (reuseTexture)
|
if (reuseTexture && texture.RefCount == 1)
|
||||||
{
|
{
|
||||||
texture.DataSize = new Size(w, h);
|
texture.DataSize = new Size(w, h);
|
||||||
_WriteDataToTexture(texture, data);
|
_WriteDataToTexture(texture, data);
|
||||||
@@ -558,20 +809,18 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
_DisposeTexture(texture);
|
_DisposeTexture(texture);
|
||||||
texture = _CreateAndFillTexture(new Size(w, h), data);
|
texture = _CreateAndFillTexture(new Size(w, h), data);
|
||||||
texture.RefCount = 1;
|
texture.RefCount = 1;
|
||||||
lock (_Textures)
|
_Textures[textureRef.ID] = texture;
|
||||||
{
|
|
||||||
_Textures[textureRef.ID] = texture;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateTexture(CTextureRef textureRef, Bitmap bmp)
|
public void UpdateTexture(CTextureRef textureRef, Bitmap bmp)
|
||||||
{
|
{
|
||||||
|
_EnsureMainThread();
|
||||||
TTextureType texture;
|
TTextureType texture;
|
||||||
if (!_GetTexture(textureRef, out texture))
|
if (!_GetTexture(textureRef, out texture, false))
|
||||||
return;
|
return;
|
||||||
bool reuseTexture = _IsTextureUsable(texture, bmp.Width, bmp.Height);
|
bool reuseTexture = _IsTextureUsable(texture, bmp.Width, bmp.Height);
|
||||||
if (reuseTexture)
|
if (reuseTexture && texture.RefCount == 1)
|
||||||
_WriteBitmapToTexture(ref texture, bmp);
|
_WriteBitmapToTexture(ref texture, bmp);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -579,18 +828,19 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
texture = null;
|
texture = null;
|
||||||
_WriteBitmapToTexture(ref texture, bmp);
|
_WriteBitmapToTexture(ref texture, bmp);
|
||||||
texture.RefCount = 1;
|
texture.RefCount = 1;
|
||||||
lock (_Textures)
|
_Textures[textureRef.ID] = texture;
|
||||||
{
|
|
||||||
_Textures[textureRef.ID] = texture;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CTextureRef CopyTexture(CTextureRef textureRef)
|
public CTextureRef CopyTexture(CTextureRef textureRef)
|
||||||
{
|
{
|
||||||
|
_EnsureMainThread();
|
||||||
TTextureType texture;
|
TTextureType texture;
|
||||||
if (!_GetTexture(textureRef, out texture))
|
if (!_GetTexture(textureRef, out texture, false))
|
||||||
return null;
|
return null;
|
||||||
|
// If bitmap is not yet loaded, wait for it
|
||||||
|
if (!String.IsNullOrEmpty(texture.TexturePath))
|
||||||
|
_WaitForTextureLoaded(textureRef, texture.TexturePath, false);
|
||||||
return _GetTextureReference(textureRef.OrigSize, texture);
|
return _GetTextureReference(textureRef.OrigSize, texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -602,6 +852,15 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
{
|
{
|
||||||
if (textureRef == null)
|
if (textureRef == null)
|
||||||
return;
|
return;
|
||||||
|
if (_MainThreadID != Thread.CurrentThread.ManagedThreadId)
|
||||||
|
{
|
||||||
|
lock (_TextureQueue)
|
||||||
|
{
|
||||||
|
_TextureQueue.Enqueue(new STextureQueue(textureRef, EQueueAction.Delete, null));
|
||||||
|
}
|
||||||
|
textureRef = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
lock (_Textures)
|
lock (_Textures)
|
||||||
{
|
{
|
||||||
TTextureType t;
|
TTextureType t;
|
||||||
@@ -621,11 +880,15 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
// Resharper doesn't get that this is used -.-
|
||||||
|
// ReSharper disable UnusedMemberHiearchy.Global
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Starts the rendering
|
/// Starts the rendering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void MainLoop()
|
public virtual void MainLoop()
|
||||||
{
|
{
|
||||||
|
// ReSharper restore UnusedMemberHiearchy.Global
|
||||||
|
_EnsureMainThread();
|
||||||
_Run = true;
|
_Run = true;
|
||||||
|
|
||||||
_Fullscreen = false;
|
_Fullscreen = false;
|
||||||
|
|||||||
@@ -171,8 +171,10 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public virtual bool Init()
|
public override bool Init()
|
||||||
{
|
{
|
||||||
|
if (!base.Init())
|
||||||
|
return false;
|
||||||
_Form.Icon = new Icon(Path.Combine(CSettings.ProgramFolder, CSettings.FileNameIcon));
|
_Form.Icon = new Icon(Path.Combine(CSettings.ProgramFolder, CSettings.FileNameIcon));
|
||||||
_Form.Text = CSettings.GetFullVersionText();
|
_Form.Text = CSettings.GetFullVersionText();
|
||||||
((IFormHook)_Form).OnMessage = _OnMessageAvoidScreenOff;
|
((IFormHook)_Form).OnMessage = _OnMessageAvoidScreenOff;
|
||||||
|
|||||||
@@ -380,6 +380,11 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
return AddTexture(w, h, data);
|
return AddTexture(w, h, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CTextureRef EnqueueTexture(string texturePath)
|
||||||
|
{
|
||||||
|
return AddTexture(texturePath);
|
||||||
|
}
|
||||||
|
|
||||||
public CTextureRef EnqueueTexture(Bitmap bmp)
|
public CTextureRef EnqueueTexture(Bitmap bmp)
|
||||||
{
|
{
|
||||||
return AddTexture(bmp);
|
return AddTexture(bmp);
|
||||||
|
|||||||
@@ -56,25 +56,38 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class COGLTexture : CTextureBase, IDisposable
|
class COGLTexture : CTextureBase
|
||||||
{
|
{
|
||||||
//The texture "name" according to the specs
|
//The texture "name" according to the specs
|
||||||
public readonly int Name;
|
public readonly int Name;
|
||||||
|
|
||||||
private bool _IsDisposed;
|
|
||||||
|
|
||||||
public COGLTexture(int name, Size dataSize, int texWidth = 0, int texHeight = 0) : base(dataSize, texWidth, texHeight)
|
public COGLTexture(int name, Size dataSize, int texWidth = 0, int texHeight = 0) : base(dataSize, texWidth, texHeight)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
|
if (name == 0)
|
||||||
|
return;
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, Name);
|
||||||
|
|
||||||
|
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, W2, H2, 0, PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero);
|
||||||
|
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
||||||
|
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
|
||||||
|
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public override bool IsLoaded
|
||||||
{
|
{
|
||||||
if (_IsDisposed)
|
get { return Name != 0; }
|
||||||
throw new ObjectDisposedException(GetType().Name);
|
}
|
||||||
_IsDisposed = true;
|
|
||||||
GL.DeleteTexture(Name);
|
public override void Dispose()
|
||||||
RefCount = 0;
|
{
|
||||||
|
base.Dispose();
|
||||||
|
if (Name != 0)
|
||||||
|
GL.DeleteTexture(Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,17 +422,10 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
#region Textures
|
#region Textures
|
||||||
protected override COGLTexture _CreateTexture(Size dataSize)
|
protected override COGLTexture _CreateTexture(Size dataSize)
|
||||||
{
|
{
|
||||||
|
if (dataSize.Width < 0)
|
||||||
|
return new COGLTexture(0, dataSize);
|
||||||
COGLTexture texture = new COGLTexture(GL.GenTexture(), dataSize, _CheckForNextPowerOf2(dataSize.Width), _CheckForNextPowerOf2(dataSize.Height));
|
COGLTexture texture = new COGLTexture(GL.GenTexture(), dataSize, _CheckForNextPowerOf2(dataSize.Width), _CheckForNextPowerOf2(dataSize.Height));
|
||||||
GL.BindTexture(TextureTarget.Texture2D, texture.Name);
|
|
||||||
|
|
||||||
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, texture.W2, texture.H2, 0, PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero);
|
|
||||||
|
|
||||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
|
|
||||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
|
|
||||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
|
||||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
|
|
||||||
|
|
||||||
GL.BindTexture(TextureTarget.Texture2D, 0);
|
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
using System.Drawing;
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
namespace Vocaluxe.Lib.Draw
|
namespace Vocaluxe.Lib.Draw
|
||||||
{
|
{
|
||||||
abstract class CTextureBase
|
abstract class CTextureBase : IDisposable
|
||||||
{
|
{
|
||||||
public int RefCount;
|
public int RefCount;
|
||||||
public string TexturePath;
|
public string TexturePath;
|
||||||
@@ -21,82 +22,75 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int _W2, _H2;
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Internal texture width (on device), a power of 2 if necessary
|
/// texture width (on device), a power of 2 if necessary
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int W2
|
public int W2 { get; protected set; }
|
||||||
{
|
|
||||||
get { return _W2; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_W2 = value;
|
|
||||||
_CalcRatios();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Internal texture height (on device), a power of 2 if necessary
|
/// texture height (on device), a power of 2 if necessary
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int H2
|
public int H2 { get; protected set; }
|
||||||
{
|
|
||||||
get { return _H2; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_H2 = value;
|
|
||||||
_CalcRatios();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Internal use. Specifies which part of texture memory is actually used
|
/// Specifies which part of texture memory is actually used
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float WidthRatio { get; private set; }
|
public float WidthRatio { get; private set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Internal use. Specifies which part of texture memory is actually used
|
/// Specifies which part of texture memory is actually used
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float HeightRatio { get; private set; }
|
public float HeightRatio { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new texture reference
|
/// True if the texture contains usefull data
|
||||||
|
/// </summary>
|
||||||
|
public abstract bool IsLoaded { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new texture
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dataSize">Size of the data used</param>
|
/// <param name="dataSize">Size of the data used</param>
|
||||||
/// <param name="texWidth">Width in video memory</param>
|
/// <param name="texWidth">Width in video memory</param>
|
||||||
/// <param name="texHeight">Height in video memory</param>
|
/// <param name="texHeight">Height in video memory</param>
|
||||||
public CTextureBase(Size dataSize, int texWidth = 0, int texHeight = 0)
|
protected CTextureBase(Size dataSize, int texWidth = 0, int texHeight = 0)
|
||||||
{
|
{
|
||||||
_DataSize = dataSize;
|
_DataSize = dataSize;
|
||||||
_W2 = (texWidth > 0) ? texWidth : dataSize.Width;
|
W2 = (texWidth > 0) ? texWidth : dataSize.Width;
|
||||||
_H2 = (texHeight > 0) ? texHeight : dataSize.Height;
|
H2 = (texHeight > 0) ? texHeight : dataSize.Height;
|
||||||
|
|
||||||
_CalcRatios();
|
_CalcRatios();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsValid()
|
|
||||||
{
|
|
||||||
return RefCount > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void _CalcRatios()
|
private void _CalcRatios()
|
||||||
{
|
{
|
||||||
// OpenGL has a problem with partial (NPOT) textures, so we remove 1 pixel
|
// OpenGL has a problem with partial (NPOT) textures, so we remove 1 pixel
|
||||||
if (_DataSize.Width == _W2)
|
if (_DataSize.Width == W2)
|
||||||
WidthRatio = 1f;
|
WidthRatio = 1f;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int mod = 0;
|
int mod = 0;
|
||||||
if (_DataSize.Width > 1)
|
if (_DataSize.Width > 1)
|
||||||
mod = -1;
|
mod = -1;
|
||||||
WidthRatio = (float)(_DataSize.Width + mod) / _W2;
|
WidthRatio = (float)(_DataSize.Width + mod) / W2;
|
||||||
}
|
}
|
||||||
if (_DataSize.Height == _H2)
|
if (_DataSize.Height == H2)
|
||||||
HeightRatio = 1f;
|
HeightRatio = 1f;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int mod = 0;
|
int mod = 0;
|
||||||
if (_DataSize.Height > 1)
|
if (_DataSize.Height > 1)
|
||||||
mod = -1;
|
mod = -1;
|
||||||
HeightRatio = (float)(_DataSize.Height + mod) / _H2;
|
HeightRatio = (float)(_DataSize.Height + mod) / H2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool _IsDisposed;
|
||||||
|
|
||||||
|
public virtual void Dispose()
|
||||||
|
{
|
||||||
|
if (_IsDisposed)
|
||||||
|
throw new ObjectDisposedException(GetType().Name);
|
||||||
|
RefCount = 0;
|
||||||
|
_IsDisposed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,11 +39,12 @@ namespace Vocaluxe.Lib.Draw
|
|||||||
CTextureRef AddTexture(string texturePath);
|
CTextureRef AddTexture(string texturePath);
|
||||||
CTextureRef AddTexture(Bitmap bitmap);
|
CTextureRef AddTexture(Bitmap bitmap);
|
||||||
CTextureRef AddTexture(int w, int h, byte[] data);
|
CTextureRef AddTexture(int w, int h, byte[] data);
|
||||||
CTextureRef EnqueueTexture(int w, int h, byte[] data);
|
|
||||||
CTextureRef EnqueueTexture(Bitmap bmp);
|
|
||||||
void EnqueueTextureUpdate(CTextureRef textureRef, Bitmap bmp);
|
|
||||||
void UpdateTexture(CTextureRef texture, int w, int h, byte[] data);
|
|
||||||
void UpdateTexture(CTextureRef texture, Bitmap bmp);
|
void UpdateTexture(CTextureRef texture, Bitmap bmp);
|
||||||
|
void UpdateTexture(CTextureRef texture, int w, int h, byte[] data);
|
||||||
|
CTextureRef EnqueueTexture(string texturePath);
|
||||||
|
CTextureRef EnqueueTexture(Bitmap bmp);
|
||||||
|
CTextureRef EnqueueTexture(int w, int h, byte[] data);
|
||||||
|
void EnqueueTextureUpdate(CTextureRef textureRef, Bitmap bmp);
|
||||||
CTextureRef CopyTexture(CTextureRef textureRef);
|
CTextureRef CopyTexture(CTextureRef textureRef);
|
||||||
void RemoveTexture(ref CTextureRef texture);
|
void RemoveTexture(ref CTextureRef texture);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace VocaluxeLib
|
||||||
|
{
|
||||||
|
static class CEncoding
|
||||||
|
{
|
||||||
|
public static Encoding GetEncoding(this string encodingName)
|
||||||
|
{
|
||||||
|
switch (encodingName)
|
||||||
|
{
|
||||||
|
case "AUTO":
|
||||||
|
return Encoding.Default;
|
||||||
|
case "CP1250":
|
||||||
|
return Encoding.GetEncoding(1250);
|
||||||
|
case "CP1252":
|
||||||
|
return Encoding.GetEncoding(1252);
|
||||||
|
case "LOCALE":
|
||||||
|
return Encoding.Default;
|
||||||
|
case "UTF8":
|
||||||
|
return Encoding.UTF8;
|
||||||
|
default:
|
||||||
|
return Encoding.Default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetEncodingName(this Encoding enc)
|
||||||
|
{
|
||||||
|
string result = "UTF8";
|
||||||
|
|
||||||
|
if (enc.CodePage == 1250)
|
||||||
|
result = "CP1250";
|
||||||
|
|
||||||
|
if (enc.CodePage == 1252)
|
||||||
|
result = "CP1252";
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
-31
@@ -21,7 +21,6 @@ using System.Diagnostics;
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace VocaluxeLib
|
namespace VocaluxeLib
|
||||||
{
|
{
|
||||||
@@ -302,40 +301,30 @@ namespace VocaluxeLib
|
|||||||
filename = Path.Combine(path, filename);
|
filename = Path.Combine(path, filename);
|
||||||
return filename + ext;
|
return filename + ext;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static class CEncoding
|
/// <summary>
|
||||||
{
|
/// Loads a bitmap from a file logging errors
|
||||||
public static Encoding GetEncoding(this string encodingName)
|
/// </summary>
|
||||||
|
/// <param name="filePath">Full path to image file</param>
|
||||||
|
/// <returns>Bitmap or null on error</returns>
|
||||||
|
public static Bitmap LoadBitmap(string filePath)
|
||||||
{
|
{
|
||||||
switch (encodingName)
|
if (!File.Exists(filePath))
|
||||||
{
|
{
|
||||||
case "AUTO":
|
CBase.Log.LogError("Can't find File: " + filePath);
|
||||||
return Encoding.Default;
|
return null;
|
||||||
case "CP1250":
|
|
||||||
return Encoding.GetEncoding(1250);
|
|
||||||
case "CP1252":
|
|
||||||
return Encoding.GetEncoding(1252);
|
|
||||||
case "LOCALE":
|
|
||||||
return Encoding.Default;
|
|
||||||
case "UTF8":
|
|
||||||
return Encoding.UTF8;
|
|
||||||
default:
|
|
||||||
return Encoding.Default;
|
|
||||||
}
|
}
|
||||||
}
|
Bitmap bmp;
|
||||||
|
try
|
||||||
public static string GetEncodingName(this Encoding enc)
|
{
|
||||||
{
|
bmp = new Bitmap(filePath);
|
||||||
string result = "UTF8";
|
}
|
||||||
|
catch (Exception)
|
||||||
if (enc.CodePage == 1250)
|
{
|
||||||
result = "CP1250";
|
CBase.Log.LogError("Error loading bitmap: " + filePath);
|
||||||
|
return null;
|
||||||
if (enc.CodePage == 1252)
|
}
|
||||||
result = "CP1252";
|
return bmp;
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,6 +76,7 @@
|
|||||||
<Reference Include="System.XML" />
|
<Reference Include="System.XML" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="CEncoding.cs" />
|
||||||
<Compile Include="CFading.cs" />
|
<Compile Include="CFading.cs" />
|
||||||
<Compile Include="Extensions.cs" />
|
<Compile Include="Extensions.cs" />
|
||||||
<Compile Include="GlobalSuppressions.cs" />
|
<Compile Include="GlobalSuppressions.cs" />
|
||||||
|
|||||||
Reference in new issue
Block a user