diff --git a/Vocaluxe/Base/CDraw.cs b/Vocaluxe/Base/CDraw.cs
index 7cb2e463..ee71bf34 100644
--- a/Vocaluxe/Base/CDraw.cs
+++ b/Vocaluxe/Base/CDraw.cs
@@ -196,13 +196,24 @@ namespace Vocaluxe.Base
/// Requests adding a texture from a bitmap
/// Use this if you add textures from another thread or don't need it immediatelly
///
- ///
+ /// Bitmap to add, gets disposed after adding!
/// Reference to texture
public static CTextureRef EnqueueTexture(Bitmap bmp)
{
return _Draw.EnqueueTexture(bmp);
}
+ ///
+ /// Requests adding a texture from a file
+ /// Use this if you add textures from another thread or don't need it immediatelly
+ ///
+ /// Full path to the image file
+ /// Reference to texture
+ public static CTextureRef EnqueueTexture(String texturePath)
+ {
+ return _Draw.EnqueueTexture(texturePath);
+ }
+
///
/// Requests updating a texture from a bitmap
/// Use this if you add textures from another thread or don't need it immediatelly
diff --git a/Vocaluxe/Lib/Draw/CDirect3D.cs b/Vocaluxe/Lib/Draw/CDirect3D.cs
index 0aa91787..430e4a54 100644
--- a/Vocaluxe/Lib/Draw/CDirect3D.cs
+++ b/Vocaluxe/Lib/Draw/CDirect3D.cs
@@ -42,21 +42,27 @@ namespace Vocaluxe.Lib.Draw
}
}
- class CD3DTexture : CTextureBase, IDisposable
+ class CD3DTexture : CTextureBase
{
public readonly Texture D3DTexture;
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)
- throw new ObjectDisposedException(GetType().Name);
- D3DTexture.Dispose();
- RefCount = 0;
+ get { return D3DTexture != null; }
+ }
+
+ public override void Dispose()
+ {
+ base.Dispose();
+ if (D3DTexture != null)
+ D3DTexture.Dispose();
}
}
@@ -575,8 +581,8 @@ namespace Vocaluxe.Lib.Draw
#region Textures
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
- //because a copy of the texture is hold in the Ram
+ if (dataSize.Width < 0)
+ return new CD3DTexture(null, dataSize);
return new CD3DTexture(_Device, dataSize, _CheckForNextPowerOf2(dataSize.Width), _CheckForNextPowerOf2(dataSize.Height));
}
diff --git a/Vocaluxe/Lib/Draw/CDrawBase.cs b/Vocaluxe/Lib/Draw/CDrawBase.cs
index 1eca5564..06718c84 100644
--- a/Vocaluxe/Lib/Draw/CDrawBase.cs
+++ b/Vocaluxe/Lib/Draw/CDrawBase.cs
@@ -2,9 +2,10 @@
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
-using System.IO;
+using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
+using System.Threading.Tasks;
using Vocaluxe.Base;
using VocaluxeLib;
using VocaluxeLib.Draw;
@@ -12,40 +13,59 @@ using System.Diagnostics;
namespace Vocaluxe.Lib.Draw
{
+ ///
+ /// 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
+ ///
+ ///
abstract class CDrawBase where TTextureType : CTextureBase, IDisposable
{
private enum EQueueAction
{
Add,
- Update
+ Update,
+ Delete
}
private struct STextureQueue
{
public readonly WeakReference TextureRef;
+ ///
+ /// Only valid if Data is byte[]
+ ///
public readonly Size DataSize;
- //Use either Data or DataBmp!
- public readonly byte[] Data;
- //This is disposed after use
- public readonly Bitmap DataBmp;
+ public readonly object Data;
public readonly EQueueAction Action;
+ ///
+ /// Creates a new Queue entry
+ ///
+ ///
+ ///
+ /// Texture data as a bitmap
+ public STextureQueue(CTextureRef textureRef, EQueueAction action, Bitmap data)
+ {
+ TextureRef = new WeakReference(textureRef);
+ Action = action;
+ Data = data;
+ DataSize = new Size();
+ }
+
+ ///
+ /// Creates a new Queue entry
+ ///
+ ///
+ ///
+ /// Size of data
+ /// Texture data
public STextureQueue(CTextureRef textureRef, EQueueAction action, Size dataSize, byte[] data)
{
TextureRef = new WeakReference(textureRef);
Action = action;
DataSize = dataSize;
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 CMouse _Mouse = new CMouse();
- private readonly Object _MutexID = new object();
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)
private readonly Dictionary _Textures = new Dictionary();
- // 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 _TextureCache = new Dictionary();
- private readonly Queue _TexturesToLoad = new Queue();
+ private readonly Queue _TextureQueue = new Queue();
+ private readonly Dictionary> _BitmapsLoading = new Dictionary>();
private int _TextureCount;
protected int _H;
@@ -87,6 +108,8 @@ namespace Vocaluxe.Lib.Draw
protected int _BorderBottom;
protected EGeneralAlignment _CurrentAlignment = EGeneralAlignment.Middle;
+ private int _MainThreadID;
+
protected abstract void _ClearScreen();
protected abstract void _AdjustNewBorders();
protected abstract void _LeaveFullScreen();
@@ -99,8 +122,23 @@ namespace Vocaluxe.Lib.Draw
protected abstract void _EnterFullScreen();
+ public virtual bool Init()
+ {
+ _MainThreadID = Thread.CurrentThread.ManagedThreadId;
+ return true;
+ }
+
+ ///
+ /// This makes sure, the function is called from within the main thread
+ ///
+ private void _EnsureMainThread()
+ {
+ Debug.Assert(_MainThreadID == Thread.CurrentThread.ManagedThreadId);
+ }
+
public virtual void Unload()
{
+ _EnsureMainThread();
lock (_Textures)
{
//Dispose all textures
@@ -196,6 +234,18 @@ namespace Vocaluxe.Lib.Draw
}
}
+ ///
+ /// Returns true if a texture with the given size needs to be resized
+ ///
+ ///
+ ///
+ private static bool _RequiresResize(Size size)
+ {
+ int maxArea = _GetMaxTextureArea();
+ int curArea = size.Width * size.Height;
+ return curArea > maxArea;
+ }
+
///
/// Calculates the size for a new texture using the quality and smart values for rescaling
///
@@ -207,6 +257,7 @@ namespace Vocaluxe.Lib.Draw
int curArea = size.Width * size.Height;
if (curArea <= maxArea)
return size;
+ Debug.Assert(_RequiresResize(size));
double factor = Math.Sqrt((double)maxArea / curArea);
Size newSize = new Size((int)(size.Width * factor), (int)(size.Height * factor));
if (!_NonPowerOf2TextureSupported)
@@ -240,7 +291,8 @@ namespace Vocaluxe.Lib.Draw
}
///
- /// Factory method to create a texture of the actual type
+ /// Factory method to create a texture of the actual type
+ /// The implementer has to create an invalid texture (IsLoaded=false) if dataSize=(-1,-1)
///
///
///
@@ -293,21 +345,36 @@ namespace Vocaluxe.Lib.Draw
}
}
+ ///
+ /// Adds the texture to the cache
+ ///
+ ///
+ ///
+ ///
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;
STextureCacheEntry cacheEntry = new STextureCacheEntry {OrigSize = origSize, Texture = texture};
- lock (_Textures)
- {
- _TextureCache[texturePath] = cacheEntry;
- }
+ _TextureCache.Add(texturePath, cacheEntry);
}
}
+ ///
+ /// Gets a new texture reference from the cache
+ /// Thread safe but you should hold the _TextureCache lock if you need the loader task
+ ///
+ ///
+ ///
private CTextureRef _GetFromCache(string texturePath)
{
+ if (String.IsNullOrEmpty(texturePath))
+ return null;
lock (_Textures)
{
STextureCacheEntry cacheEntry;
@@ -323,8 +390,9 @@ namespace Vocaluxe.Lib.Draw
}
///
- /// Method used to add a texture to the texture list and return a TextureRef to it
- /// Important: This should be the only function to ever write to _Textures
+ /// Method used to add a texture to the texture list and return a TextureRef to it
+ /// If you are using an already used texture, you have to hold the _Textures lock or call from the main thread!
+ /// Thread safe
///
///
///
@@ -332,25 +400,26 @@ namespace Vocaluxe.Lib.Draw
protected CTextureRef _GetTextureReference(Size origSize, TTextureType texture)
{
Debug.Assert(origSize.Width > 0 && origSize.Height > 0);
+ Debug.Assert(texture != null);
int id;
lock (_MutexID)
{
id = _NextID++;
}
CTextureRef textureRef = new CTextureRef(id, origSize);
- if (texture != null)
- texture.RefCount++;
- if (texture == null || texture.RefCount == 1)
- _TextureCount++;
lock (_Textures)
{
+ if (texture.RefCount == 0)
+ _TextureCount++;
+ texture.RefCount++;
_Textures.Add(id, texture);
}
return textureRef;
}
///
- /// Method used to add a texture to the texture list and return a TextureRef to it
+ /// Method used to add a texture to the texture list and return a TextureRef to it
+ /// Thread safe
/// Convenience method
///
///
@@ -376,7 +445,7 @@ namespace Vocaluxe.Lib.Draw
return true;
}
- protected bool _GetTexture(CTextureRef textureRef, out TTextureType texture)
+ protected bool _GetTexture(CTextureRef textureRef, out TTextureType texture, bool checkDrawable = true)
{
if (textureRef == null)
{
@@ -385,7 +454,7 @@ namespace Vocaluxe.Lib.Draw
}
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
///
private void _DisposeTexture(TTextureType texture)
{
- if (texture != null && --texture.RefCount <= 0)
+ _EnsureMainThread();
+ lock (_Textures)
{
- if (texture.TexturePath != null)
- _TextureCache.Remove(texture.TexturePath);
- texture.Dispose();
- _TextureCount--;
+ if (texture != null && --texture.RefCount <= 0)
+ {
+ if (texture.TexturePath != null)
+ _TextureCache.Remove(texture.TexturePath);
+ texture.Dispose();
+ _TextureCount--;
+ }
}
}
private void _CheckQueue()
{
- lock (_Textures)
+ _EnsureMainThread();
+ lock (_TextureQueue)
{
- while (_TexturesToLoad.Count > 0)
+ while (_TextureQueue.Count > 0)
{
- STextureQueue q = _TexturesToLoad.Dequeue();
- Debug.Assert(q.Data != null ^ q.DataBmp != null);
+ STextureQueue q = _TextureQueue.Dequeue();
CTextureRef textureRef = q.TextureRef.Target as CTextureRef;
if (!q.TextureRef.IsAlive || textureRef == null || !_Textures.ContainsKey(textureRef.ID))
continue;
@@ -419,23 +492,78 @@ namespace Vocaluxe.Lib.Draw
if (q.Action == EQueueAction.Add)
{
TTextureType texture = null;
- if (q.Data == null)
- _WriteBitmapToTexture(ref texture, q.DataBmp);
+ // ReSharper disable CanBeReplacedWithTryCastAndCheckForNull
+ 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
- texture = _CreateAndFillTexture(q.DataSize, q.Data);
- texture.RefCount = 1; //Just created, so only 1 reference
- _Textures[textureRef.ID] = texture;
+ throw new ArgumentException("q.Data is of invalid type");
+ // ReSharper restore CanBeReplacedWithTryCastAndCheckForNull
+ TTextureType oldTexture = _Textures[textureRef.ID];
+ _MergeTextures(oldTexture, texture);
}
- else
+ else if (q.Action == EQueueAction.Update)
{
- if (q.Data == null)
- UpdateTexture(textureRef, q.DataBmp);
+ // ReSharper disable CanBeReplacedWithTryCastAndCheckForNull
+ 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
- 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);
}
}
}
+
+ ///
+ /// Replaces one oldTexture by newTexture summing the refCounts. Also disposes oldTexture
+ /// IMPORTANT: Caller MUST hold the _Textures lock!
+ ///
+ ///
+ ///
+ private void _MergeTextures(TTextureType oldTexture, TTextureType newTexture)
+ {
+ Debug.Assert(oldTexture.RefCount > 0);
+ newTexture.RefCount += oldTexture.RefCount;
+ // ReSharper disable AccessToDisposedClosure
+ IEnumerable 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();
+ }
+
+ ///
+ /// Checks if a texture is currently beeing loaded and waits for completion if it is
+ /// Required for cached entries, but should not be called from inside any locks unless async is true
+ ///
+ ///
+ ///
+ /// If true, do not block
+ private void _WaitForTextureLoaded(CTextureRef textureRef, string filePath, bool async)
+ {
+ // Check if texture is already loaded
+ if (textureRef.OrigSize.Width > 0)
+ return;
+ Task 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
///
@@ -445,25 +573,22 @@ namespace Vocaluxe.Lib.Draw
/// A STexture object containing the added texture
public CTextureRef AddTexture(string texturePath)
{
- CTextureRef textureRef = _GetFromCache(texturePath);
+ _EnsureMainThread();
+ CTextureRef textureRef;
+ lock (_TextureCache)
+ {
+ textureRef = _GetFromCache(texturePath);
+ }
if (textureRef != null)
+ {
+ _WaitForTextureLoaded(textureRef, texturePath, false);
+ Debug.Assert(textureRef.OrigSize.Width > 0);
return textureRef;
+ }
- if (!File.Exists(texturePath))
- {
- CLog.LogError("Can't find File: " + texturePath);
+ Bitmap bmp = CHelper.LoadBitmap(texturePath);
+ if (bmp == null)
return null;
- }
- Bitmap bmp;
- try
- {
- bmp = new Bitmap(texturePath);
- }
- catch (Exception)
- {
- CLog.LogError("Error loading Texture: " + texturePath);
- return null;
- }
try
{
textureRef = AddTexture(bmp, texturePath);
@@ -477,6 +602,7 @@ namespace Vocaluxe.Lib.Draw
public CTextureRef AddTexture(Bitmap bmp)
{
+ _EnsureMainThread();
return AddTexture(bmp, null);
}
@@ -485,52 +611,176 @@ namespace Vocaluxe.Lib.Draw
///
/// The Bitmap of which the texure will be created from
///
- /// A STexture object containing the added texture
+ /// A reference to the texture
public CTextureRef AddTexture(Bitmap bmp, string texturePath)
{
+ _EnsureMainThread();
if (bmp.Height == 0 || bmp.Width == 0)
return null;
+ CTextureRef textureRef;
Size origSize = bmp.GetSize();
-
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);
- return _GetTextureReference(origSize, texture);
+ _AddToCache(texture, origSize, texturePath);
+ }
+ }
+ 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)
{
+ _EnsureMainThread();
TTextureType texture = _CreateAndFillTexture(new Size(w, h), data);
return _GetTextureReference(w, h, texture);
}
public CTextureRef EnqueueTexture(int w, int h, byte[] data)
{
- lock (_Textures)
+ lock (_TextureQueue)
{
- CTextureRef textureRef = _GetTextureReference(w, h, null);
- _TexturesToLoad.Enqueue(new STextureQueue(textureRef, EQueueAction.Add, new Size(w, h), data));
+ CTextureRef textureRef = _GetTextureReference(w, h, _CreateTexture(new Size(-1, -1)));
+ _TextureQueue.Enqueue(new STextureQueue(textureRef, EQueueAction.Add, new Size(w, h), data));
return textureRef;
}
}
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);
- _TexturesToLoad.Enqueue(new STextureQueue(textureRef, EQueueAction.Add, bmp));
+ Task.Factory.StartNew(() =>
+ {
+ 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 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(() => _LoadAndEnqueueBitmap(filePath, textureRef));
+ lock (_BitmapsLoading)
+ {
+ _BitmapsLoading.Add(filePath, loader);
+ }
+ }
+ }
+
+ if (texture == null)
+ {
+ // Found in cache
+ _WaitForTextureLoaded(textureRef, filePath, true);
return textureRef;
}
+
+ Debug.Assert(loader != null);
+ Debug.Assert(texture != null);
+ loader.Start();
+ return textureRef;
+ }
+
+ ///
+ /// Helper function for loading a bitmap in a task
+ ///
+ ///
+ ///
+ ///
+ 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)
{
- 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
/// True if succeeded
public void UpdateTexture(CTextureRef textureRef, int w, int h, byte[] data)
{
+ _EnsureMainThread();
TTextureType texture;
- if (!_GetTexture(textureRef, out texture))
+ if (!_GetTexture(textureRef, out texture, false))
return;
bool reuseTexture = _IsTextureUsable(texture, w, h);
- if (reuseTexture)
+ if (reuseTexture && texture.RefCount == 1)
{
texture.DataSize = new Size(w, h);
_WriteDataToTexture(texture, data);
@@ -558,20 +809,18 @@ namespace Vocaluxe.Lib.Draw
_DisposeTexture(texture);
texture = _CreateAndFillTexture(new Size(w, h), data);
texture.RefCount = 1;
- lock (_Textures)
- {
- _Textures[textureRef.ID] = texture;
- }
+ _Textures[textureRef.ID] = texture;
}
}
public void UpdateTexture(CTextureRef textureRef, Bitmap bmp)
{
+ _EnsureMainThread();
TTextureType texture;
- if (!_GetTexture(textureRef, out texture))
+ if (!_GetTexture(textureRef, out texture, false))
return;
bool reuseTexture = _IsTextureUsable(texture, bmp.Width, bmp.Height);
- if (reuseTexture)
+ if (reuseTexture && texture.RefCount == 1)
_WriteBitmapToTexture(ref texture, bmp);
else
{
@@ -579,18 +828,19 @@ namespace Vocaluxe.Lib.Draw
texture = null;
_WriteBitmapToTexture(ref texture, bmp);
texture.RefCount = 1;
- lock (_Textures)
- {
- _Textures[textureRef.ID] = texture;
- }
+ _Textures[textureRef.ID] = texture;
}
}
public CTextureRef CopyTexture(CTextureRef textureRef)
{
+ _EnsureMainThread();
TTextureType texture;
- if (!_GetTexture(textureRef, out texture))
+ if (!_GetTexture(textureRef, out texture, false))
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);
}
@@ -602,6 +852,15 @@ namespace Vocaluxe.Lib.Draw
{
if (textureRef == null)
return;
+ if (_MainThreadID != Thread.CurrentThread.ManagedThreadId)
+ {
+ lock (_TextureQueue)
+ {
+ _TextureQueue.Enqueue(new STextureQueue(textureRef, EQueueAction.Delete, null));
+ }
+ textureRef = null;
+ return;
+ }
lock (_Textures)
{
TTextureType t;
@@ -621,11 +880,15 @@ namespace Vocaluxe.Lib.Draw
}
#endregion
+ // Resharper doesn't get that this is used -.-
+ // ReSharper disable UnusedMemberHiearchy.Global
///
/// Starts the rendering
///
public virtual void MainLoop()
{
+ // ReSharper restore UnusedMemberHiearchy.Global
+ _EnsureMainThread();
_Run = true;
_Fullscreen = false;
diff --git a/Vocaluxe/Lib/Draw/CDrawBaseWindows.cs b/Vocaluxe/Lib/Draw/CDrawBaseWindows.cs
index e387281b..8ac494e8 100644
--- a/Vocaluxe/Lib/Draw/CDrawBaseWindows.cs
+++ b/Vocaluxe/Lib/Draw/CDrawBaseWindows.cs
@@ -171,8 +171,10 @@ namespace Vocaluxe.Lib.Draw
#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.Text = CSettings.GetFullVersionText();
((IFormHook)_Form).OnMessage = _OnMessageAvoidScreenOff;
diff --git a/Vocaluxe/Lib/Draw/CDrawWinForm.cs b/Vocaluxe/Lib/Draw/CDrawWinForm.cs
index 070f38b4..efec130f 100644
--- a/Vocaluxe/Lib/Draw/CDrawWinForm.cs
+++ b/Vocaluxe/Lib/Draw/CDrawWinForm.cs
@@ -380,6 +380,11 @@ namespace Vocaluxe.Lib.Draw
return AddTexture(w, h, data);
}
+ public CTextureRef EnqueueTexture(string texturePath)
+ {
+ return AddTexture(texturePath);
+ }
+
public CTextureRef EnqueueTexture(Bitmap bmp)
{
return AddTexture(bmp);
diff --git a/Vocaluxe/Lib/Draw/COpenGL.cs b/Vocaluxe/Lib/Draw/COpenGL.cs
index 997bd148..c66e442a 100644
--- a/Vocaluxe/Lib/Draw/COpenGL.cs
+++ b/Vocaluxe/Lib/Draw/COpenGL.cs
@@ -56,25 +56,38 @@ namespace Vocaluxe.Lib.Draw
}
}
- class COGLTexture : CTextureBase, IDisposable
+ class COGLTexture : CTextureBase
{
//The texture "name" according to the specs
public readonly int Name;
- private bool _IsDisposed;
-
public COGLTexture(int name, Size dataSize, int texWidth = 0, int texHeight = 0) : base(dataSize, texWidth, texHeight)
{
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)
- throw new ObjectDisposedException(GetType().Name);
- _IsDisposed = true;
- GL.DeleteTexture(Name);
- RefCount = 0;
+ get { return Name != 0; }
+ }
+
+ public override void Dispose()
+ {
+ base.Dispose();
+ if (Name != 0)
+ GL.DeleteTexture(Name);
}
}
@@ -409,17 +422,10 @@ namespace Vocaluxe.Lib.Draw
#region Textures
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));
- 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;
}
diff --git a/Vocaluxe/Lib/Draw/CTextureBase.cs b/Vocaluxe/Lib/Draw/CTextureBase.cs
index 2d79345e..eb9d11bf 100644
--- a/Vocaluxe/Lib/Draw/CTextureBase.cs
+++ b/Vocaluxe/Lib/Draw/CTextureBase.cs
@@ -1,8 +1,9 @@
-using System.Drawing;
+using System;
+using System.Drawing;
namespace Vocaluxe.Lib.Draw
{
- abstract class CTextureBase
+ abstract class CTextureBase : IDisposable
{
public int RefCount;
public string TexturePath;
@@ -21,82 +22,75 @@ namespace Vocaluxe.Lib.Draw
}
}
- private int _W2, _H2;
///
- /// Internal texture width (on device), a power of 2 if necessary
+ /// texture width (on device), a power of 2 if necessary
///
- public int W2
- {
- get { return _W2; }
- set
- {
- _W2 = value;
- _CalcRatios();
- }
- }
+ public int W2 { get; protected set; }
///
- /// Internal texture height (on device), a power of 2 if necessary
+ /// texture height (on device), a power of 2 if necessary
///
- public int H2
- {
- get { return _H2; }
- set
- {
- _H2 = value;
- _CalcRatios();
- }
- }
+ public int H2 { get; protected set; }
///
- /// Internal use. Specifies which part of texture memory is actually used
+ /// Specifies which part of texture memory is actually used
///
public float WidthRatio { get; private set; }
///
- /// Internal use. Specifies which part of texture memory is actually used
+ /// Specifies which part of texture memory is actually used
///
public float HeightRatio { get; private set; }
///
- /// Creates a new texture reference
+ /// True if the texture contains usefull data
+ ///
+ public abstract bool IsLoaded { get; }
+
+ ///
+ /// Creates a new texture
///
/// Size of the data used
/// Width in video memory
/// Height in video memory
- public CTextureBase(Size dataSize, int texWidth = 0, int texHeight = 0)
+ protected CTextureBase(Size dataSize, int texWidth = 0, int texHeight = 0)
{
_DataSize = dataSize;
- _W2 = (texWidth > 0) ? texWidth : dataSize.Width;
- _H2 = (texHeight > 0) ? texHeight : dataSize.Height;
+ W2 = (texWidth > 0) ? texWidth : dataSize.Width;
+ H2 = (texHeight > 0) ? texHeight : dataSize.Height;
_CalcRatios();
}
- public bool IsValid()
- {
- return RefCount > 0;
- }
-
private void _CalcRatios()
{
// OpenGL has a problem with partial (NPOT) textures, so we remove 1 pixel
- if (_DataSize.Width == _W2)
+ if (_DataSize.Width == W2)
WidthRatio = 1f;
else
{
int mod = 0;
if (_DataSize.Width > 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;
else
{
int mod = 0;
if (_DataSize.Height > 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;
+ }
}
-}
+}
\ No newline at end of file
diff --git a/Vocaluxe/Lib/Draw/IDraw.cs b/Vocaluxe/Lib/Draw/IDraw.cs
index 8a912cc2..7b4b37ad 100644
--- a/Vocaluxe/Lib/Draw/IDraw.cs
+++ b/Vocaluxe/Lib/Draw/IDraw.cs
@@ -39,11 +39,12 @@ namespace Vocaluxe.Lib.Draw
CTextureRef AddTexture(string texturePath);
CTextureRef AddTexture(Bitmap bitmap);
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, 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);
void RemoveTexture(ref CTextureRef texture);
diff --git a/VocaluxeLib/CEncoding.cs b/VocaluxeLib/CEncoding.cs
new file mode 100644
index 00000000..7b763d5b
--- /dev/null
+++ b/VocaluxeLib/CEncoding.cs
@@ -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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/VocaluxeLib/CHelper.cs b/VocaluxeLib/CHelper.cs
index b3392fe4..ea314518 100644
--- a/VocaluxeLib/CHelper.cs
+++ b/VocaluxeLib/CHelper.cs
@@ -21,7 +21,6 @@ using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
-using System.Text;
namespace VocaluxeLib
{
@@ -302,40 +301,30 @@ namespace VocaluxeLib
filename = Path.Combine(path, filename);
return filename + ext;
}
- }
- static class CEncoding
- {
- public static Encoding GetEncoding(this string encodingName)
+ ///
+ /// Loads a bitmap from a file logging errors
+ ///
+ /// Full path to image file
+ /// Bitmap or null on error
+ public static Bitmap LoadBitmap(string filePath)
{
- switch (encodingName)
+ if (!File.Exists(filePath))
{
- 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;
+ CBase.Log.LogError("Can't find File: " + filePath);
+ return null;
}
- }
-
- public static string GetEncodingName(this Encoding enc)
- {
- string result = "UTF8";
-
- if (enc.CodePage == 1250)
- result = "CP1250";
-
- if (enc.CodePage == 1252)
- result = "CP1252";
-
- return result;
+ Bitmap bmp;
+ try
+ {
+ bmp = new Bitmap(filePath);
+ }
+ catch (Exception)
+ {
+ CBase.Log.LogError("Error loading bitmap: " + filePath);
+ return null;
+ }
+ return bmp;
}
}
}
\ No newline at end of file
diff --git a/VocaluxeLib/VocaluxeLib.csproj b/VocaluxeLib/VocaluxeLib.csproj
index 2ac08aae..11f27989 100644
--- a/VocaluxeLib/VocaluxeLib.csproj
+++ b/VocaluxeLib/VocaluxeLib.csproj
@@ -76,6 +76,7 @@
+