diff --git a/Output/Cover/Blue.xml b/Output/Cover/Blue.xml index 9befe7d3..8afbbb3b 100644 --- a/Output/Cover/Blue.xml +++ b/Output/Cover/Blue.xml @@ -5,4 +5,41 @@ Blue flokuep + + + + Default + + + %TEXT% + + Normal + + + + 500 + + + + + 1110.8 + + + + 24 + 24 + + + 100 + + + 1111 + + + EmptyCover.png + + true + + 0.75 + diff --git a/Output/Fonts/Fonts.xml b/Output/Fonts/Fonts.xml index 2bbf61c3..70102ffa 100644 --- a/Output/Fonts/Fonts.xml +++ b/Output/Fonts/Fonts.xml @@ -4,10 +4,12 @@ Normal 0,00 - 0,00 - 0,00 - 0,00 - 1,00 + + 0,00 + 0,00 + 0,00 + 1,00 + FreeSans FreeSans.ttf FreeSansBold.ttf @@ -17,10 +19,12 @@ Outline 0,10 - 0,00 - 0,00 - 0,00 - 1,00 + + 0,00 + 0,00 + 0,00 + 1,00 + FreeSans FreeSans.ttf FreeSansBold.ttf diff --git a/Output/Languages/English.xml b/Output/Languages/English.xml index c0ece78c..6231e96b 100644 --- a/Output/Languages/English.xml +++ b/Output/Languages/English.xml @@ -212,8 +212,8 @@ Webcams Webcam-Resolution Screen adjustments - Press CTR + ALT + SHIFT + Arrows to adjust screen - CTR + ALT + SHIFT + 0 to reset + Press CTR + ALT (+ SHIFT) + Arrows|"+" to adjust screen + CTR + ALT + 0 to reset Top left Top right Bottom left diff --git a/Output/Languages/German.xml b/Output/Languages/German.xml index 4ded8f3f..751bf476 100644 --- a/Output/Languages/German.xml +++ b/Output/Languages/German.xml @@ -211,8 +211,8 @@ Webcams Webcam-Auflösung Anzeigeanpassungen - STRG + ALT + SHIFT + Pfeiltasten drücken um den Bildschirm anzupassen - STRG + ALT + SHIFT + 0 um die Anpassungen zurückzusetzen + STRG + ALT (+ SHIFT) + Pfeiltasten|"+" drücken um den Bildschirm anzupassen + STRG + ALT + 0 um die Anpassungen zurückzusetzen Oben links Oben rechts Unten links diff --git a/PartyModes/PartyModeChallenge/CPartyScreenChallengeMain.cs b/PartyModes/PartyModeChallenge/CPartyScreenChallengeMain.cs index 22945876..ea025b8e 100644 --- a/PartyModes/PartyModeChallenge/CPartyScreenChallengeMain.cs +++ b/PartyModes/PartyModeChallenge/CPartyScreenChallengeMain.cs @@ -446,7 +446,7 @@ namespace VocaluxeLib.PartyModes.Challenge _RoundsTableScrollArea.Y = numberY; _RoundsTableScrollArea.W = CBase.Settings.GetRenderW() - _Texts[_TextRoundNumber].X - 20; - float delta = _Texts[_TextRoundNumber].Height; + float delta = _Texts[_TextRoundNumber].Rect.H; //Update statics and texts for rounds foreach (CRoundsTableRow roundRow in _RoundsTable) @@ -479,7 +479,7 @@ namespace VocaluxeLib.PartyModes.Challenge roundRow.TextScores[column].Y = numberY + delta; roundRow.TextScores[column].MaxWidth = maxw; } - numberY = numberY + delta + delta; + numberY = numberY + 2*delta; } numberY = numberY + delta / 2; } @@ -532,7 +532,7 @@ namespace VocaluxeLib.PartyModes.Challenge _PlayerTableScrollArea = new SRectF {X = _Texts[_TextPosition].X, Y = _Texts[_TextPosition].Y, W = _Texts[_TextGamePoints].X - _Texts[_TextPosition].X}; _PlayerTable = new List(); - float delta = _Texts[_TextPosition].Height * 1.2f; + float delta = _Texts[_TextPosition].Rect.H * 1.2f; float h = 0; diff --git a/PartyModes/PartyModeChallengeMedley/CPartyScreenChallengeMedleyMain.cs b/PartyModes/PartyModeChallengeMedley/CPartyScreenChallengeMedleyMain.cs index 47dcb43c..0a0240f1 100644 --- a/PartyModes/PartyModeChallengeMedley/CPartyScreenChallengeMedleyMain.cs +++ b/PartyModes/PartyModeChallengeMedley/CPartyScreenChallengeMedleyMain.cs @@ -446,7 +446,7 @@ namespace VocaluxeLib.PartyModes.ChallengeMedley _RoundsTableScrollArea.Y = numberY; _RoundsTableScrollArea.W = CBase.Settings.GetRenderW() - _Texts[_TextRoundNumber].X - 20; - float delta = _Texts[_TextRoundNumber].Height; + float delta = _Texts[_TextRoundNumber].Rect.H; //Update statics and texts for rounds foreach (CRoundsTableRow roundRow in _RoundsTable) @@ -532,7 +532,7 @@ namespace VocaluxeLib.PartyModes.ChallengeMedley _PlayerTableScrollArea = new SRectF {X = _Texts[_TextPosition].X, Y = _Texts[_TextPosition].Y, W = _Texts[_TextGamePoints].X - _Texts[_TextPosition].X}; _PlayerTable = new List(); - float delta = _Texts[_TextPosition].Height * 1.2f; + float delta = _Texts[_TextPosition].Rect.H * 1.2f; float h = 0; diff --git a/Vocaluxe/Base/CBackgroundMusic.cs b/Vocaluxe/Base/CBackgroundMusic.cs index 4006f4c9..f8685ff9 100644 --- a/Vocaluxe/Base/CBackgroundMusic.cs +++ b/Vocaluxe/Base/CBackgroundMusic.cs @@ -52,25 +52,26 @@ namespace Vocaluxe.Base public static bool VideoEnabled { get { return _PlaysPreview ? _PreviewPlayer.VideoEnabled : _SongPlayer.VideoEnabled; } - set { - if(_PlaysPreview) + set + { + if (_PlaysPreview) _PreviewPlayer.VideoEnabled = value; - else + else _SongPlayer.VideoEnabled = value; } } - public static bool CanSing - { - get + public static bool CanSing + { + get { if (_PlaysPreview) return false; + else if (_BGMusicFiles.Contains(_CurrentPlaylistElement)) + return false; else - if (_BGMusicFiles.Contains(_CurrentPlaylistElement)) - return false; - else return true; - } + return true; + } } public static bool Disabled @@ -85,7 +86,8 @@ namespace Vocaluxe.Base } } - public static bool RepeatSong { + public static bool RepeatSong + { get { return _SongPlayer.RepeatSong; } set { _SongPlayer.RepeatSong = value; } } @@ -100,13 +102,20 @@ namespace Vocaluxe.Base get { return _SongPlayer.SongHasVideo; } } - public static bool IsPlaying { get { return _PlaysPreview ? _PreviewPlayer.IsPlaying : _SongPlayer.IsPlaying; } } + public static bool IsPlaying + { + get { return _PlaysPreview ? _PreviewPlayer.IsPlaying : _SongPlayer.IsPlaying; } + } - public static bool IsPlayingPreview { get { return _PlaysPreview; } } + public static bool IsPlayingPreview + { + get { return _PlaysPreview; } + } public static string ArtistAndTitle { - get { + get + { if (_PlaysPreview) return _PreviewPlayer.ArtistAndTitle; else @@ -119,7 +128,7 @@ namespace Vocaluxe.Base get { return _PlaysPreview ? _PreviewPlayer.Length : _SongPlayer.Length; } } - public static CTexture Cover + public static CTextureRef Cover { get { return _PlaysPreview ? _PreviewPlayer.Cover : _SongPlayer.Cover; } } @@ -185,11 +194,13 @@ namespace Vocaluxe.Base if (_PlaysPreview) _PreviewPlayer.Play(); else if(CConfig.BackgroundMusic == EBackgroundMusicOffOn.TR_CONFIG_ON) + { if (_SongPlayer.SongLoaded) //Resume _SongPlayer.TogglePause(); else Next(); + } } public static void Stop() @@ -278,7 +289,7 @@ namespace Vocaluxe.Base public static void Update() { - if (!IsPlaying && (!_SongPlayer.SongLoaded || !_PreviewPlayer.SongLoaded) ) + if (!IsPlaying && (!_SongPlayer.SongLoaded || !_PreviewPlayer.SongLoaded)) return; if (_PlaysPreview) @@ -291,7 +302,7 @@ namespace Vocaluxe.Base } } - public static CTexture GetVideoTexture() + public static CTextureRef GetVideoTexture() { return _PlaysPreview ? _PreviewPlayer.GetVideoTexture() : _SongPlayer.GetVideoTexture(); } @@ -439,6 +450,5 @@ namespace Vocaluxe.Base { return _BGMusicFiles.Contains(element); } - } } \ No newline at end of file diff --git a/Vocaluxe/Base/CConfig.cs b/Vocaluxe/Base/CConfig.cs index ac4cf8b9..3e201c06 100644 --- a/Vocaluxe/Base/CConfig.cs +++ b/Vocaluxe/Base/CConfig.cs @@ -28,6 +28,7 @@ using Vocaluxe.Lib.Sound.Record; using Vocaluxe.Lib.Webcam; using VocaluxeLib; using VocaluxeLib.Profile; +using VocaluxeLib.Xml; namespace Vocaluxe.Base { @@ -72,6 +73,8 @@ namespace Vocaluxe.Base public static int BorderTop; public static int BorderBottom; + public static EGeneralAlignment ScreenAlignment = EGeneralAlignment.Middle; + public static EAntiAliasingModes AAMode = EAntiAliasingModes.X0; public static EOffOn VSync = EOffOn.TR_CONFIG_ON; @@ -226,6 +229,8 @@ namespace Vocaluxe.Base xmlReader.TryGetIntValue("//root/Graphics/ScreenW", ref ScreenW); xmlReader.TryGetIntValue("//root/Graphics/ScreenH", ref ScreenH); + xmlReader.TryGetEnumValue("//root/Graphics/ScreenAlignment", ref ScreenAlignment); + xmlReader.TryGetIntValue("//root/Graphics/BorderLeft", ref BorderLeft); xmlReader.TryGetIntValue("//root/Graphics/BorderRight", ref BorderRight); xmlReader.TryGetIntValue("//root/Graphics/BorderTop", ref BorderTop); @@ -404,6 +409,8 @@ namespace Vocaluxe.Base writer.WriteElementString("ScreenW", ScreenW.ToString()); writer.WriteElementString("ScreenH", ScreenH.ToString()); + writer.WriteElementString("ScreenAlignment", Enum.GetName(typeof(EGeneralAlignment), ScreenAlignment)); + writer.WriteComment("Screen borders (pixels)"); writer.WriteElementString("BorderLeft", BorderLeft.ToString()); writer.WriteElementString("BorderRight", BorderRight.ToString()); @@ -834,7 +841,7 @@ namespace Vocaluxe.Base break; case "oldtheme": - if(value == "yes") + if (value == "yes") LoadOldThemeFiles = true; break; } diff --git a/Vocaluxe/Base/CCover.cs b/Vocaluxe/Base/CCover.cs index 61692ce7..096e988f 100644 --- a/Vocaluxe/Base/CCover.cs +++ b/Vocaluxe/Base/CCover.cs @@ -17,25 +17,31 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.IO; +using System.Threading.Tasks; using VocaluxeLib; using VocaluxeLib.Draw; +using VocaluxeLib.Songs; +using VocaluxeLib.Xml; namespace Vocaluxe.Base { - struct SCoverTheme + struct SThemeCover { - public string Name; - public string Folder; - public string File; + public string Name, Folder; + public string FilePath; } static class CCover { - private static readonly Dictionary _Covers = new Dictionary(); - private static readonly List _CoverThemes = new List(); + private const string _NoCoverName = "No Cover"; + private const string _NoCoverNameAlt = "NoCover"; + private static readonly Dictionary _Covers = new Dictionary(); + private static readonly Dictionary _NoCoverGenerators = new Dictionary(); + private static readonly List _CoverThemes = new List(); - public static CTexture NoCover { get; private set; } + public static CTextureRef NoCover { get; private set; } public static void Init() { @@ -81,7 +87,7 @@ namespace Vocaluxe.Base /// /// Returns a STexture for a given cover name. Returns "NoCover" if the cover does not exist. /// - public static CTexture Cover(string name) + public static CTextureRef Cover(string name) { lock (_Covers) { @@ -92,6 +98,26 @@ namespace Vocaluxe.Base } } + public static CTextureRef GenerateCover(string text, ESongSorting sorting, CCategory category) + { + CSong firstSong = category.GetSong(0); + CTextureRef texture = CDraw.CopyTexture(NoCover); + Task.Factory.StartNew(() => + { + ECoverGeneratorType type = _SongSortingToType(sorting); + Bitmap coverBmp = !_NoCoverGenerators.ContainsKey(type)?null:_NoCoverGenerators[type].GetCover(text, firstSong != null ? Path.Combine(firstSong.Folder, firstSong.CoverFileName) : null); + if (coverBmp == null && _NoCoverGenerators.ContainsKey(ECoverGeneratorType.Default)) + coverBmp = _NoCoverGenerators[ECoverGeneratorType.Default].GetCover(text, firstSong != null ? Path.Combine(firstSong.Folder, firstSong.CoverFileName) : null); + if (coverBmp != null) + CDraw.EnqueueTextureUpdate(texture, coverBmp); + }); + lock (_Covers) + { + _Covers.Add(text, texture); + } + return texture; + } + /// /// Returns true if a cover with the given name exists. /// MUST HOLD _Covers lock at this point @@ -116,24 +142,28 @@ namespace Vocaluxe.Base { foreach (string key in _Covers.Keys) { - CTexture texture = _Covers[key]; + CTextureRef texture = _Covers[key]; CDraw.RemoveTexture(ref texture); } _Covers.Clear(); } + lock (_NoCoverGenerators) + { + _NoCoverGenerators.Clear(); + } } /// /// Returns a SCoverTheme by cover-theme-name /// - private static SCoverTheme _CoverTheme(string coverThemeName) + private static SThemeCover _CoverTheme(string coverThemeName) { for (int i = 0; i < _CoverThemes.Count; i++) { if (_CoverThemes[i].Name == coverThemeName) return _CoverThemes[i]; } - return new SCoverTheme(); + return new SThemeCover(); } /// @@ -152,14 +182,14 @@ namespace Vocaluxe.Base if (xmlReader != null) { - var coverTheme = new SCoverTheme(); + var coverTheme = new SThemeCover(); xmlReader.GetValue("//root/Info/Name", out coverTheme.Name, String.Empty); xmlReader.GetValue("//root/Info/Folder", out coverTheme.Folder, String.Empty); if (coverTheme.Folder != "" && coverTheme.Name != "") { - coverTheme.File = file; + coverTheme.FilePath = xmlReader.FilePath; _CoverThemes.Add(coverTheme); } @@ -168,11 +198,11 @@ namespace Vocaluxe.Base } /// - /// Loads all cover which are defined in the cover config file. + /// Loads all covers from the theme /// private static void _LoadCovers(string coverThemeName) { - SCoverTheme coverTheme = _CoverTheme(coverThemeName); + SThemeCover coverTheme = _CoverTheme(coverThemeName); if (String.IsNullOrEmpty(coverTheme.Name)) return; @@ -184,8 +214,64 @@ namespace Vocaluxe.Base { foreach (string file in files) _AddCover(Path.GetFileNameWithoutExtension(file), file); - if (_CoverExists("No Cover")) - NoCover = _Covers["No Cover"]; + if (_CoverExists(_NoCoverName)) + NoCover = _Covers[_NoCoverName]; + else if (_CoverExists(_NoCoverNameAlt)) + NoCover = _Covers[_NoCoverNameAlt]; + else + CBase.Log.LogError("Covertheme \"" + coverThemeName + "\" does not include a cover file named \"" + _NoCoverName + "\" and cannot be used!", true, true); + } + _LoadNoCoverThemes(coverTheme); + } + + private static ECoverGeneratorType _SongSortingToType(ESongSorting sorting) + { + switch (sorting) + { + case ESongSorting.TR_CONFIG_NONE: + return ECoverGeneratorType.Default; + case ESongSorting.TR_CONFIG_FOLDER: + return ECoverGeneratorType.Folder; + case ESongSorting.TR_CONFIG_ARTIST: + return ECoverGeneratorType.Artist; + case ESongSorting.TR_CONFIG_ARTIST_LETTER: + case ESongSorting.TR_CONFIG_TITLE_LETTER: + return ECoverGeneratorType.Letter; + case ESongSorting.TR_CONFIG_EDITION: + return ECoverGeneratorType.Edition; + case ESongSorting.TR_CONFIG_GENRE: + return ECoverGeneratorType.Genre; + case ESongSorting.TR_CONFIG_LANGUAGE: + return ECoverGeneratorType.Language; + case ESongSorting.TR_CONFIG_YEAR: + return ECoverGeneratorType.Year; + case ESongSorting.TR_CONFIG_DECADE: + return ECoverGeneratorType.Decade; + case ESongSorting.TR_CONFIG_DATEADDED: + return ECoverGeneratorType.Date; + default: + throw new ArgumentOutOfRangeException("sorting"); + } + } + + private static void _LoadNoCoverThemes(SThemeCover coverTheme) + { + CXMLReader xmlReader = CXMLReader.OpenFile(coverTheme.FilePath); + string coverPath = Path.Combine(CSettings.ProgramFolder, CSettings.FolderNameCover, coverTheme.Folder); + lock (_NoCoverGenerators) + { + int i = 1; + while(xmlReader.ItemExists("//root/CoverGenerator"+i)){ + SThemeCoverGenerator theme; + if (xmlReader.Read("//root/CoverGenerator" + i, out theme)) + { + if (_NoCoverGenerators.ContainsKey(theme.Type)) + continue; + CNoCoverGenerator el = new CNoCoverGenerator(theme, coverPath); + _NoCoverGenerators.Add(theme.Type, el); + } + i++; + } } } diff --git a/Vocaluxe/Base/CCursor.cs b/Vocaluxe/Base/CCursor.cs index 46a6e2b1..01ede32b 100644 --- a/Vocaluxe/Base/CCursor.cs +++ b/Vocaluxe/Base/CCursor.cs @@ -27,7 +27,7 @@ namespace Vocaluxe.Base { private int _MouseMoveDiffMin = CSettings.MouseMoveDiffMinActive; private CFading _Fading; - private CTexture _Cursor; + private CTextureRef _Cursor; private Point _LastDiffPos; private readonly Stopwatch _Movetimer = new Stopwatch(); diff --git a/Vocaluxe/Base/CDataBase.cs b/Vocaluxe/Base/CDataBase.cs index 6297433e..a3e6397d 100644 --- a/Vocaluxe/Base/CDataBase.cs +++ b/Vocaluxe/Base/CDataBase.cs @@ -93,7 +93,7 @@ namespace Vocaluxe.Base _HighscoreDB.IncreaseSongCounter(dataBaseSongID); } - public static bool GetCover(string fileName, ref CTexture tex, int maxSize) + public static bool GetCover(string fileName, ref CTextureRef tex, int maxSize) { return _CoverDB.GetCover(fileName, ref tex, maxSize); } @@ -103,7 +103,7 @@ namespace Vocaluxe.Base _CoverDB.CommitCovers(); } - public static bool GetCreditsRessource(string fileName, ref CTexture tex) + public static bool GetCreditsRessource(string fileName, ref CTextureRef tex) { return _ResourceDB.GetCreditsRessource(fileName, ref tex); } diff --git a/Vocaluxe/Base/CDraw.cs b/Vocaluxe/Base/CDraw.cs index f109645b..526d30cc 100644 --- a/Vocaluxe/Base/CDraw.cs +++ b/Vocaluxe/Base/CDraw.cs @@ -20,7 +20,6 @@ using System.Drawing; using Vocaluxe.Lib.Draw; using VocaluxeLib; using VocaluxeLib.Draw; -using VocaluxeLib.Menu; namespace Vocaluxe.Base { @@ -28,6 +27,10 @@ namespace Vocaluxe.Base { private static IDraw _Draw; + /// + /// Initializes the Driver selected in the config + /// + /// public static bool Init() { if (_Draw != null) @@ -74,12 +77,20 @@ namespace Vocaluxe.Base return _Draw.Init(); } + /// + /// Has to be called to start rendering
+ /// Will only return if program is closed
+ /// Also inits the first screen + ///
public static void MainLoop() { CGraphics.InitFirstScreen(); _Draw.MainLoop(); } + /// + /// Unloads the current driver, e.g. to switch to another one + /// public static void Unload() { if (_Draw != null) @@ -89,143 +100,303 @@ namespace Vocaluxe.Base } } + /// + /// Gets the width of the rendering area + /// + /// public static int GetScreenWidth() { return _Draw.GetScreenWidth(); } + /// + /// Gets the height of the rendering area + /// + /// public static int GetScreenHeight() { return _Draw.GetScreenHeight(); } - public static void DrawLine(int a, int r, int g, int b, int w, int x1, int y1, int x2, int y2) - { - _Draw.DrawLine(a, r, g, b, w, x1, y1, x2, y2); - } - - public static void DrawRect(SColorF color, SRectF rect, float thickness) - { - if (thickness <= 0f) - return; - - _Draw.DrawColor(color, new SRectF(rect.X - thickness / 2, rect.Y - thickness / 2, rect.W + thickness, thickness, rect.Z)); - _Draw.DrawColor(color, new SRectF(rect.X - thickness / 2, rect.Y + rect.H - thickness / 2, rect.W + thickness, thickness, rect.Z)); - _Draw.DrawColor(color, new SRectF(rect.X - thickness / 2, rect.Y - thickness / 2, thickness, rect.H + thickness, rect.Z)); - _Draw.DrawColor(color, new SRectF(rect.X + rect.W - thickness / 2, rect.Y - thickness / 2, thickness, rect.H + thickness, rect.Z)); - } - - public static void DrawColor(SColorF color, SRectF rect) - { - _Draw.DrawColor(color, rect); - } - - public static void DrawColorReflection(SColorF color, SRectF rect, float space, float height) - { - _Draw.DrawColorReflection(color, rect, space, height); - } - - public static void ClearScreen() - { - _Draw.ClearScreen(); - } - - public static CTexture CopyScreen() - { - return _Draw.CopyScreen(); - } - - public static void CopyScreen(ref CTexture texture) - { - _Draw.CopyScreen(ref texture); - } - + /// + /// Takes a screenshot and saves it to the Screenshot folder + /// public static void MakeScreenShot() { _Draw.MakeScreenShot(); } - public static CTexture AddTexture(Bitmap bitmap) + /// + /// Gets the current screen content as a texture + /// + /// + public static CTextureRef CopyScreen() + { + return _Draw.CopyScreen(); + } + + /// + /// Gets the current screen content updating the given texture + /// + /// + public static void CopyScreen(ref CTextureRef texture) + { + _Draw.CopyScreen(ref texture); + } + + /// + /// Adds a texture from a Bitmap and returns a reference to it
+ /// Must be called from main thread! + ///
+ /// + /// Reference to texture + public static CTextureRef AddTexture(Bitmap bitmap) { return _Draw.AddTexture(bitmap); } - public static CTexture AddTexture(string texturePath) + /// + /// Adds a texture from an image file given by its file path
+ /// Must be called from main thread! + ///
+ /// + /// Reference to texture or null on error + public static CTextureRef AddTexture(string texturePath) { return _Draw.AddTexture(texturePath); } - public static CTexture AddTexture(int w, int h, byte[] data) + /// + /// Adds a texture from a byte array
+ /// Must be called from main thread! + ///
+ /// Width of the image + /// Height of the image + /// Array of 4 byte values for each pixel + /// Reference to texture + public static CTextureRef AddTexture(int w, int h, byte[] data) { return _Draw.AddTexture(w, h, data); } - public static CTexture EnqueueTexture(int w, int h, byte[] data) + /// + /// Requests aadding a texture from a byte array
+ /// Use this if you add textures from another thread or don't need it immediatelly + ///
+ /// Width of the image + /// Height of the image + /// Array of 4 byte values for each pixel + /// Reference to texture + public static CTextureRef EnqueueTexture(int w, int h, byte[] data) { return _Draw.EnqueueTexture(w, h, data); } - public static bool UpdateTexture(CTexture texture, int w, int h, byte[] data) + /// + /// 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.UpdateTexture(texture, w, h, data); + return _Draw.EnqueueTexture(bmp); } - public static bool UpdateOrAddTexture(ref CTexture texture, int w, int h, byte[] data) + /// + /// 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.UpdateOrAddTexture(ref texture, w, h, data); + return _Draw.EnqueueTexture(texturePath); } - public static void RemoveTexture(ref CTexture texture) + /// + /// Requests updating a texture from a bitmap
+ /// Use this if you add textures from another thread or don't need it immediatelly
+ /// Bitmap is freed after use + ///
+ /// Reference to the texture to update + /// + public static void EnqueueTextureUpdate(CTextureRef textureRef, Bitmap bmp) { - _Draw.RemoveTexture(ref texture); + _Draw.EnqueueTextureUpdate(textureRef, bmp); } - public static void DrawTexture(CTexture texture) + /// + /// Creates a copy of the texture
+ /// Updates to the copy do not affect the original texture + ///
+ /// Original texture reference + /// New texture reference of the copy + public static CTextureRef CopyTexture(CTextureRef textureRef) { - _Draw.DrawTexture(texture); + return _Draw.CopyTexture(textureRef); } - public static void DrawTexture(CTexture texture, SRectF rect) + /// + /// Updates a texture, filling it with the new data + /// + /// + /// Width of the image + /// Height of the image + /// Array of 4 byte values for each pixel + public static void UpdateTexture(CTextureRef textureRef, int w, int h, byte[] data) { - _Draw.DrawTexture(texture, rect); + _Draw.UpdateTexture(textureRef, w, h, data); } - public static void DrawTexture(CTexture texture, SRectF rect, SColorF color, bool mirrored = false) + /// + /// Updates a texture, filling it with the new data from the bitmap + /// + /// + /// + public static void UpdateTexture(CTextureRef textureRef, Bitmap bmp) { - _Draw.DrawTexture(texture, rect, color, mirrored); + _Draw.UpdateTexture(textureRef, bmp); } - public static void DrawTexture(CTexture texture, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false) + /// + /// Removes a texture from the VRAM and sets it to null to avoid accidential usage
+ /// Note: You can also use its Dispose method to do the same as this gets automaticly called from the Dispose method and destructor of a texture reference + ///
+ /// + public static void RemoveTexture(ref CTextureRef texture) { - _Draw.DrawTexture(texture, rect, color, bounds, mirrored); - } - - public static void DrawTexture(CTexture texture, SRectF rect, SColorF color, float begin, float end) - { - _Draw.DrawTexture(texture, rect, color, begin, end); - } - - public static void DrawTexture(CStatic staticBounds, CTexture texture, EAspect aspect) - { - if (texture == null) - return; - - var bounds = new RectangleF(staticBounds.Rect.X, staticBounds.Rect.Y, staticBounds.Rect.W, staticBounds.Rect.H); - RectangleF rect; - - CHelper.SetRect(bounds, out rect, texture.OrigAspect, aspect); - DrawTexture(texture, new SRectF(rect.X, rect.Y, rect.Width, rect.Height, staticBounds.Rect.Z), - texture.Color, new SRectF(bounds.X, bounds.Y, bounds.Width, bounds.Height, 0f)); - } - - public static void DrawTextureReflection(CTexture texture, SRectF rect, SColorF color, SRectF bounds, float space, float height) - { - _Draw.DrawTextureReflection(texture, rect, color, bounds, space, height); + // Disposed textures call this, possibly at program end! + if (_Draw != null) + _Draw.RemoveTexture(ref texture); } + /// + /// Returns the number of textures in the VRAM (textures might get reused so this might not be the actual number of textures in use) + /// + /// public static int TextureCount() { return _Draw.GetTextureCount(); } + + /// + /// Draws a simple (filled) rectangle with the given color + /// + /// + /// + public static void DrawRect(SColorF color, SRectF rect) + { + _Draw.DrawRect(color, rect); + } + + /// + /// Draws the reflection of a filled rectangle with the given color + /// + /// + /// (Original) rectangle to draw the reflection for + /// Spacing between the rect and the reflection + /// Height of the reflection + public static void DrawRectReflection(SColorF color, SRectF rect, float space, float height) + { + _Draw.DrawRectReflection(color, rect, space, height); + } + + /// + /// Draws a texture at its position and with its color + /// + /// The texture to be drawn + public static void DrawTexture(CTextureRef texture) + { + DrawTexture(texture, texture.Rect); + } + + /// + /// Draws a texture with its color at the given position + /// + /// The texture to be drawn + /// A rectangle with the destination coordinates + public static void DrawTexture(CTextureRef textureRef, SRectF rect) + { + DrawTexture(textureRef, rect, textureRef.Color); + } + + /// + /// Draws a texture at the given position with the given color + /// + /// + /// + /// + /// True to mirror the texture on the y axis + public static void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, bool mirrored = false) + { + _Draw.DrawTexture(textureRef, rect, color, mirrored); + } + + /// + /// Draws a texture at the given position with the given color + /// + /// + /// + /// + /// Bounds to draw the texture in (rest gets cropped off) + /// True to mirror the texture on the y axis + public static void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false) + { + _Draw.DrawTexture(textureRef, rect, color, bounds, mirrored); + } + + /// + /// Draws a part of a texture at the given position with the given color + /// + /// + /// + /// + /// Normalized start x-coordinate (0..1) + /// Normalized end x-coordinate (0..1) + public static void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, float begin, float end) + { + _Draw.DrawTexture(textureRef, rect, color, begin, end); + } + + /// + /// Draws a texture fitting it within the given bounds with the method specified by aspect + /// + /// + /// Where to draw the texture + /// How to fit a texture in the bounds if the size differs + public static void DrawTexture(CTextureRef textureRef, SRectF bounds, EAspect aspect) + { + DrawTexture(textureRef, bounds, aspect, textureRef.Color); + } + + /// + /// Draws a texture fitting it within the given color and bounds with the method specified by aspect + /// + /// + /// Where to draw the texture + /// How to fit a texture in the bounds if the size differs + /// Color to use + public static void DrawTexture(CTextureRef textureRef, SRectF bounds, EAspect aspect, SColorF color) + { + if (textureRef == null) + return; + + SRectF rect = CHelper.FitInBounds(bounds, textureRef.OrigAspect, aspect); + DrawTexture(textureRef, rect, color); + } + + /// + /// Draws the reflection of a texture at the given position and with the given color + /// + /// + /// (Original) rectangle to draw the reflection for + /// + /// Spacing between the rect and the reflection + /// Height of the reflection + public static void DrawTextureReflection(CTextureRef textureRef, SRectF rect, SColorF color, float space, float height) + { + _Draw.DrawTextureReflection(textureRef, rect, color, space, height); + } } } \ No newline at end of file diff --git a/Vocaluxe/Base/CGraphics.cs b/Vocaluxe/Base/CGraphics.cs index fa427bef..6ec89672 100644 --- a/Vocaluxe/Base/CGraphics.cs +++ b/Vocaluxe/Base/CGraphics.cs @@ -196,9 +196,6 @@ namespace Vocaluxe.Base CController.Update(); CProfiles.Update(); - if (CConfig.CoverLoading == ECoverLoading.TR_CONFIG_COVERLOADING_DYNAMIC && _CurrentScreen != EScreens.ScreenSing) - CSongs.LoadCover(); - if (CSettings.ProgramState != EProgramState.EditTheme) { run &= _HandleInputs(keys, mouse); @@ -362,32 +359,55 @@ namespace Vocaluxe.Base if (!eventsAvailable) keyEvent = inputKeyEvent; - if (keyEvent.IsArrowKey() || keyEvent.Key == Keys.NumPad0 || keyEvent.Key == Keys.D0) + if (keyEvent.IsArrowKey() || keyEvent.Key == Keys.NumPad0 || keyEvent.Key == Keys.D0 || keyEvent.Key == Keys.Add) { _Cursor.Deactivate(); - if (keyEvent.ModAlt && keyEvent.ModCtrl && keyEvent.ModShift) + if (keyEvent.ModAlt && keyEvent.ModCtrl) { switch (keyEvent.Key) { - case Keys.Left: - CConfig.BorderLeft -= 1; - CConfig.BorderRight -= 1; - CConfig.BorderTop -= 1; - CConfig.BorderBottom -= 1; - break; case Keys.Right: - CConfig.BorderLeft += 1; - CConfig.BorderRight += 1; - CConfig.BorderTop += 1; - CConfig.BorderBottom += 1; + if (keyEvent.ModShift) + CConfig.BorderLeft++; + else + CConfig.BorderRight--; + break; + case Keys.Left: + if (keyEvent.ModShift) + CConfig.BorderLeft--; + else + CConfig.BorderRight++; + break; + case Keys.Down: + if (keyEvent.ModShift) + CConfig.BorderTop++; + else + CConfig.BorderBottom--; + break; + case Keys.Up: + if (keyEvent.ModShift) + CConfig.BorderTop--; + else + CConfig.BorderBottom++; break; case Keys.D0: case Keys.NumPad0: - CConfig.BorderLeft = 0; - CConfig.BorderRight = 0; - CConfig.BorderTop = 0; - CConfig.BorderBottom = 0; + CConfig.BorderLeft = CConfig.BorderRight = CConfig.BorderTop = CConfig.BorderBottom = 0; + break; + case Keys.Add: + switch (CConfig.ScreenAlignment) + { + case EGeneralAlignment.Middle: + CConfig.ScreenAlignment = EGeneralAlignment.End; + break; + case EGeneralAlignment.End: + CConfig.ScreenAlignment = EGeneralAlignment.Start; + break; + default: + CConfig.ScreenAlignment = EGeneralAlignment.Middle; + break; + } break; } CConfig.SaveConfig(); @@ -638,16 +658,15 @@ namespace Vocaluxe.Base } } } - CFonts.Style = EStyle.Normal; - CFonts.SetFont("Normal"); - CFonts.Height = 25; + CFont font=new CFont("Normal",EStyle.Normal, 25); SColorF gray = new SColorF(1f, 1f, 1f, 0.5f); float y = 0; foreach (string txt in debugOutput) { - RectangleF rect = new RectangleF(CSettings.RenderW - CFonts.GetTextWidth(txt), y, CFonts.GetTextWidth(txt), CFonts.GetTextHeight(txt)); - CDraw.DrawColor(gray, new SRectF(rect.X, rect.Top, rect.Width, rect.Height, CSettings.ZNear)); - CFonts.DrawText(txt, rect.X, rect.Y, CSettings.ZNear); + float textWidth = CFonts.GetTextWidth(txt, font); + RectangleF rect = new RectangleF(CSettings.RenderW - textWidth, y, textWidth, CFonts.GetTextHeight(txt,font)); + CDraw.DrawRect(gray, new SRectF(rect.X, rect.Top, rect.Width, rect.Height, CSettings.ZNear)); + CFonts.DrawText(txt, font, rect.X, rect.Y, CSettings.ZNear); y += rect.Height; } } diff --git a/Vocaluxe/Base/CLanguage.cs b/Vocaluxe/Base/CLanguage.cs index fb452747..9f529b4f 100644 --- a/Vocaluxe/Base/CLanguage.cs +++ b/Vocaluxe/Base/CLanguage.cs @@ -20,6 +20,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using VocaluxeLib; +using VocaluxeLib.Xml; namespace Vocaluxe.Base { diff --git a/Vocaluxe/Base/CMain.cs b/Vocaluxe/Base/CMain.cs index 2af43f68..36fa3aa5 100644 --- a/Vocaluxe/Base/CMain.cs +++ b/Vocaluxe/Base/CMain.cs @@ -272,12 +272,12 @@ namespace Vocaluxe.Base return CTheme.GetSkinIndex(partyModeID); } - public CTexture GetSkinTexture(string textureName, int partyModeID) + public CTextureRef GetSkinTexture(string textureName, int partyModeID) { return CTheme.GetSkinTexture(textureName, partyModeID); } - public CTexture GetSkinVideoTexture(string videoName, int partyModeID) + public CTextureRef GetSkinVideoTexture(string videoName, int partyModeID) { return CTheme.GetSkinVideoTexture(videoName, partyModeID); } @@ -405,7 +405,7 @@ namespace Vocaluxe.Base CBackgroundMusic.StopPreview(); } - public CTexture GetVideoTexture() + public CTextureRef GetVideoTexture() { return CBackgroundMusic.GetVideoTexture(); } @@ -413,44 +413,54 @@ namespace Vocaluxe.Base class CBdraw : IDrawing { - public void DrawTexture(CTexture texture, SRectF rect) + public void DrawTexture(CTextureRef texture, SRectF rect) { CDraw.DrawTexture(texture, rect); } - public void DrawTexture(CTexture texture, SRectF rect, SColorF color) + public void DrawTexture(CTextureRef texture, SRectF rect, SColorF color) { CDraw.DrawTexture(texture, rect, color); } - public void DrawTexture(CTexture texture, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false) + public void DrawTexture(CTextureRef texture, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false) { CDraw.DrawTexture(texture, rect, color, bounds, mirrored); } - public void DrawTextureReflection(CTexture texture, SRectF rect, SColorF color, SRectF bounds, float reflectionSpace, float reflectionHeight) + public void DrawTexture(CTextureRef textureRef, SRectF bounds, EAspect aspect) { - CDraw.DrawTextureReflection(texture, rect, color, bounds, reflectionSpace, reflectionHeight); + CDraw.DrawTexture(textureRef, bounds, aspect); } - public CTexture AddTexture(string fileName) + public void DrawTexture(CTextureRef textureRef, SRectF bounds, EAspect aspect, SColorF color) + { + CDraw.DrawTexture(textureRef, bounds, aspect, color); + } + + public void DrawTextureReflection(CTextureRef texture, SRectF rect, SColorF color, float reflectionSpace, float reflectionHeight) + { + CDraw.DrawTextureReflection(texture, rect, color, reflectionSpace, reflectionHeight); + } + + public CTextureRef AddTexture(string fileName) { return CDraw.AddTexture(fileName); } - public void RemoveTexture(ref CTexture texture) + public void RemoveTexture(ref CTextureRef texture) { CDraw.RemoveTexture(ref texture); } - public void DrawColor(SColorF color, SRectF rect) + public void DrawRect(SColorF color, SRectF rect) { - CDraw.DrawColor(color, rect); + CDraw.DrawRect(color, rect); } - public void DrawColorReflection(SColorF color, SRectF rect, float space, float height) + public void DrawRectReflection(SColorF color, SRectF rect, float space, float height) { - CDraw.DrawColorReflection(color, rect, space, height); + CDraw.DrawRectReflection(color, rect, space, height); } } @@ -502,39 +512,24 @@ namespace Vocaluxe.Base class CBfonts : IFonts { - public void SetFont(string fontName) - { - CFonts.SetFont(fontName); - } - - public void SetStyle(EStyle fontStyle) - { - CFonts.Style = fontStyle; - } - public RectangleF GetTextBounds(CText text) { return CFonts.GetTextBounds(text); } - public RectangleF GetTextBounds(CText text, float textHeight) + public void DrawText(string text, CFont font, float x, float y, float z, SColorF color) { - return CFonts.GetTextBounds(text, textHeight); + CFonts.DrawText(text, font, x, y, z, color); } - public void DrawText(string text, float textHeight, float x, float y, float z, SColorF color) + public void DrawTextReflection(string text, CFont font, float x, float y, float z, SColorF color, float reflectionSpace, float reflectionHeight) { - CFonts.DrawText(text, textHeight, x, y, z, color); + CFonts.DrawTextReflection(text, font, x, y, z, color, reflectionSpace, reflectionHeight); } - public void DrawTextReflection(string text, float textHeight, float x, float y, float z, SColorF color, float reflectionSpace, float reflectionHeight) + public void DrawText(string text, CFont font, float x, float y, float z, SColorF color, float begin, float end) { - CFonts.DrawTextReflection(text, textHeight, x, y, z, color, reflectionSpace, reflectionHeight); - } - - public void DrawText(string text, float textHeight, float x, float y, float z, SColorF color, float begin, float end) - { - CFonts.DrawText(text, textHeight, x, y, z, color, begin, end); + CFonts.DrawText(text, font, x, y, z, color, begin, end); } } @@ -656,7 +651,7 @@ namespace Vocaluxe.Base return CProfiles.GetPlayerName(profileID, playerNum); } - public CTexture GetAvatar(int profileID) + public CTextureRef GetAvatar(int profileID) { return CProfiles.GetAvatarTextureFromProfile(profileID); } @@ -815,7 +810,7 @@ namespace Vocaluxe.Base return CVideo.Skip(videoStream, startPosition, videoGap); } - public bool GetFrame(int videoStream, ref CTexture videoTexture, float time, out float videoTime) + public bool GetFrame(int videoStream, ref CTextureRef videoTexture, float time, out float videoTime) { return CVideo.GetFrame(videoStream, ref videoTexture, time, out videoTime); } @@ -896,7 +891,7 @@ namespace Vocaluxe.Base class CBcover : ICover { - public CTexture GetNoCover() + public CTextureRef GetNoCover() { return CCover.NoCover; } @@ -904,7 +899,7 @@ namespace Vocaluxe.Base class CBdataBase : IDataBase { - public bool GetCover(string fileName, ref CTexture texture, int coverSize) + public bool GetCover(string fileName, ref CTextureRef texture, int coverSize) { return CDataBase.GetCover(fileName, ref texture, coverSize); } diff --git a/Vocaluxe/Base/CNoCoverGenerator.cs b/Vocaluxe/Base/CNoCoverGenerator.cs new file mode 100644 index 00000000..d92177e0 --- /dev/null +++ b/Vocaluxe/Base/CNoCoverGenerator.cs @@ -0,0 +1,495 @@ +#region license +// This file is part of Vocaluxe. +// +// Vocaluxe is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Vocaluxe is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Vocaluxe. If not, see . +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Drawing.Text; +using System.IO; +using Vocaluxe.Base.Fonts; +using VocaluxeLib; +using System.Linq; + +namespace Vocaluxe.Base +{ + /// + /// Class that generates Covers if the right Cover is missing + /// Themeable via the xml of the Cover theme + /// + class CNoCoverGenerator + { + private readonly bool _Valid; + private readonly SThemeCoverGenerator _Theme; + private readonly string _Image; + private readonly SColorF _TextColor; + private readonly SColorF _BGColor; + private const int _LineSpace = 5; + + /// + /// Split lines after special chars, descending priority + /// + private static readonly char[] _SplitCharPreferences = {':', '/', '-'}; + private static readonly char[] _SplitCharAfter = {':', '/', '-', ' ', ')', '.', '*', ','}; + private static readonly char[] _SplitCharBefore = {'('}; + + public CNoCoverGenerator(SThemeCoverGenerator theme, string basePath) + { + _Theme = theme; + _Valid = true; + _Valid &= _Theme.Text.Color.Get(-1, out _TextColor); + _Valid &= _Theme.BackgroundColor.Get(-1, out _BGColor); + if (_Valid) + { + _Image = Path.Combine(basePath, _Theme.Image); + _Valid = File.Exists(_Image); + } + } + + private class CTextElement + { + public readonly string Text; + public float Width, Height; + private readonly Graphics _Graphics; + private readonly Font _Font; + private float _WidthTrimmed = -1; + public float WidthTrimmed + { + get + { + if (_WidthTrimmed < 0f) + { + string text = Text.TrimEnd(null); + _WidthTrimmed = text.Length == Text.Length ? Width : _Graphics.MeasureString(text, _Font, -1, StringFormat.GenericTypographic).Width; + } + return _WidthTrimmed; + } + } + public int Line; + + public CTextElement(string text, Graphics g, Font font) + { + Text = text; + SizeF dimensions = g.MeasureString(text, font, -1, StringFormat.GenericTypographic); + Width = dimensions.Width; + Height = (dimensions.Height + font.Height) / 2; + _Graphics = g; + _Font = font; + } + + public void AdjustSize(float factor) + { + Width *= factor; + _WidthTrimmed *= factor; + Height *= factor; + } + } + + private void _DrawBackground(Graphics g, Bitmap bmpBackground, String firstCoverPath) + { + g.Clear(_BGColor.AsColor()); + + ImageAttributes ia = null; + if (_Theme.ShowFirstCover && !String.IsNullOrEmpty(firstCoverPath) && File.Exists(firstCoverPath)) + { + using (Bitmap bmp2 = new Bitmap(firstCoverPath)) + g.DrawImage(bmp2, bmpBackground.GetRect(), 0, 0, bmp2.Width, bmp2.Height, GraphicsUnit.Pixel); + ColorMatrix cm = new ColorMatrix {Matrix33 = _Theme.ImageAlpha}; + ia = new ImageAttributes(); + ia.SetColorMatrix(cm); + } + g.DrawImage(bmpBackground, bmpBackground.GetRect(), 0, 0, bmpBackground.Width, bmpBackground.Height, GraphicsUnit.Pixel, ia); + } + + private void _DrawText(Graphics g, Size bmpSize, CFont font, List elements) + { + Font fo = CFonts.GetSystemFont(font); + + float maxHeight = elements.Select(el => el.Height).Max(); + int lineCount = elements.Last().Line + 1; + + //Have to use size in em not pixels! + float emSize = fo.Size * fo.FontFamily.GetCellAscent(fo.Style) / fo.FontFamily.GetEmHeight(fo.Style); + float outlineSize = CFonts.GetOutlineSize(font) * font.Height; + SColorF outlineColorF = CFonts.GetOutlineColor(font); + outlineColorF.A = outlineColorF.A * _TextColor.A; + + using (var path = new GraphicsPath()) + using (var pen = new Pen(outlineColorF.AsColor(), outlineSize / 2)) + { + pen.LineJoin = LineJoin.Round; + pen.Alignment = PenAlignment.Outset; + float top = (bmpSize.Height - _Theme.Text.Margin.Bottom - _Theme.Text.Margin.Top - maxHeight * lineCount) / 2 + _Theme.Text.Margin.Top; + int nextLineEl = 0; + for (int i = 0; i < lineCount; i++) + { + int firstEl = nextLineEl; + for (; nextLineEl < elements.Count; nextLineEl++) + { + if (elements[nextLineEl].Line > i) + break; + } + + string line = elements.GetRange(firstEl, nextLineEl - firstEl).Aggregate("", (current, element) => current + element.Text); + float left; + if (lineCount == 1 || (i == 1 && lineCount == 3)) + { + //Center Text if this is the only line or the middle line + float width = _GetWidth(elements, firstEl, nextLineEl - 1); + left = (bmpSize.Width - _Theme.Text.Margin.Left - _Theme.Text.Margin.Right - width) / 2 + _Theme.Text.Margin.Left; + } + else if (i == lineCount - 1) + { + //Place last line at right + float width = _GetWidth(elements, firstEl, nextLineEl - 1); + left = bmpSize.Width - width - _Theme.Text.Margin.Right; + } + else + left = _Theme.Text.Margin.Left; + //g.DrawString(line, fo, new SolidBrush(_TextColor.AsColor()), left, top, StringFormat.GenericTypographic); + path.AddString(line, fo.FontFamily, (int)fo.Style, emSize, new PointF(left, top), StringFormat.GenericTypographic); + top += maxHeight + _LineSpace; + } + g.DrawPath(pen, path); + g.FillPath(new SolidBrush(_TextColor.AsColor()), path); + } + } + + public Bitmap GetCover(string text, string firstCoverPath) + { + if (!_Valid) + return null; + text = CLanguage.Translate(_Theme.Text.Text.Replace("%TEXT%", text)); + using (Bitmap bmpImage = new Bitmap(_Image)) + { + Bitmap bmp = new Bitmap(bmpImage.Width, bmpImage.Height, PixelFormat.Format32bppArgb); + try + { + using (Graphics g = Graphics.FromImage(bmp)) + { + g.SmoothingMode = SmoothingMode.AntiAlias; + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.TextRenderingHint = TextRenderingHint.AntiAlias; + + _DrawBackground(g, bmpImage, firstCoverPath); + + if (text != "") + { + CFont font = new CFont(_Theme.Text.Font); + Font fo = CFonts.GetSystemFont(font); + IEnumerable textParts = _SplitText(text); + List elements = textParts.Select(line => new CTextElement(line, g, fo)).ToList(); + float factor = _DistributeText(elements, bmp.Width, bmp.Height); + foreach (CTextElement element in elements) + element.AdjustSize(factor); + font.Height *= factor / (1f + CFonts.GetOutlineSize(font)); //Adjust for outline size + _DrawText(g, bmp.GetSize(), font, elements); + } + } + return bmp; + } + catch (Exception) + { + bmp.Dispose(); + } + } + return null; + } + + /// + /// Distributes the text among max. 3 lines maximizing the height of the line
+ /// Sets the Line field in the elements + ///
+ /// Text with set Width and Height + /// Image Width + /// Image Height + /// Factor for resizing the text (Maximizing this factor means maximizing the height) + private float _DistributeText(List elements, int width, int height) + { + int availableWidth = width - _Theme.Text.Margin.Left - _Theme.Text.Margin.Right; + int availableHeight = height - _Theme.Text.Margin.Top - _Theme.Text.Margin.Bottom; + + float textHeight = elements.Select(el => el.Height).Max(); + + //Try 1 line: + float textWidth = _GetWidth(elements, 0); + int maxHeight = Math.Min(availableHeight, (int)_Theme.Text.Font.Size); + float factorH = maxHeight / textHeight; + float factorW = availableWidth / textWidth; + if (factorH <= factorW) + return factorH; //Limited by Height + float factor1 = factorW; + + //Try 2 lines + if (elements.Count == 1) + { + //Only 1 element -> 1 line + return factor1; + } + availableWidth -= _Theme.Text.Indent; + maxHeight = Math.Min(availableHeight / 2 - _LineSpace, (int)_Theme.Text.Font.Size); + factorH = maxHeight / textHeight; + if (factorH <= factor1) + return factor1; //Cannot get any bigger with more lines + int splitEl = _GetSplitElement(elements, textWidth / 2); + Debug.Assert(splitEl >= 0 && splitEl < elements.Count - 1); + float width1 = _GetWidth(elements, 0, splitEl); + float width2 = _GetWidth(elements, splitEl + 1); + factorW = availableWidth / Math.Max(width1, width2); + if (factorH <= factorW) + { + // Assertion: factorH>factor1 (check above) && factorH<=factorW --> Min(factorH,factorW)>factor 1 + // but for 2 lines it is limited by height + // --> Use 2 lines + _SetLine(elements, splitEl + 1, elements.Count - 1, 1); + return factorH; + } + // factor2 < factorH && factorH>factor1 + float factor2 = factorW; + + //Try 3 lines + maxHeight = Math.Min(availableHeight / 3 - 2 * _LineSpace, (int)_Theme.Text.Font.Size); + factorH = maxHeight / textHeight; + if (elements.Count == 2 || factorH <= Math.Max(factor1, factor2)) + { + //Only 2 elements or cannot get any bigger + if (factor2 <= factor1) + return factor1; + _SetLine(elements, splitEl + 1, elements.Count - 1, 1); + return factor2; + } + int splitEl21 = _GetSplitElement(elements, textWidth / 3, false); + int splitEl22 = _GetSplitElement(elements, textWidth / 3, true, splitEl21 + 1); + Debug.Assert(splitEl21 >= 0 && splitEl21 < splitEl22 && splitEl22 < elements.Count - 1); + float width21 = _GetWidth(elements, 0, splitEl21); + float width22 = _GetWidth(elements, splitEl21 + 1, splitEl22); + float width23 = _GetWidth(elements, splitEl22 + 1); + factorW = availableWidth / Math.Max(Math.Max(width21, width22), width23); + float factor3 = Math.Min(factorH, factorW); + if (factor3 > Math.Max(factor1, factor2)) + { + _SetLine(elements, splitEl21 + 1, splitEl22, 1); + _SetLine(elements, splitEl22 + 1, elements.Count - 1, 2); + return factor3; + } + if (factor2 > factor1) + { + _SetLine(elements, splitEl + 1, elements.Count - 1, 1); + return factor2; + } + return factor1; + } + + /// + /// Returns the width of the elements with index from start to end of the list (inclusive) if they are put on 1 line + /// + /// + /// + /// + private static float _GetWidth(List list, int start) + { + return _GetWidth(list, start, list.Count - 1); + } + + /// + /// Returns the width of the elements with index from start to end (inclusive) if they are put on 1 line + /// + /// + /// + /// + /// + private static float _GetWidth(List list, int start, int end) + { + if (start < 0) + start = 0; + if (end >= list.Count) + end = list.Count - 1; + if (start > end) + return 0f; + float width = list.GetRange(start, end - start).Select(el => el.Width).Sum(); + return width + list[end].WidthTrimmed; + } + + private static void _SetLine(List list, int start, int end, int line) + { + for (int i = start; i <= end; i++) + list[i].Line = line; + } + + /// + /// Find the element after which to split the line + /// + /// List with elements, width must be set + /// The requested line width + /// Split on one point or two + /// First element on line + /// Index of element AFTER which to split + private static int _GetSplitElement(List elements, float requestedWidth, bool singleSplit = true, int startElement = 0) + { + //Assert we have enough elements + Debug.Assert(singleSplit && elements.Count - startElement >= 2 || !singleSplit && elements.Count - startElement >= 3); + float curWidth = 0f; + int splitEl; + for (splitEl = startElement; splitEl < elements.Count; splitEl++) + { + curWidth += elements[splitEl].Width; + if (curWidth >= requestedWidth) + break; + } + + // At this point we can either keep splitEl on this line for move it to the next line + // ==> line with splitEl is to long, without it is to short. so find the best option + // Check if line starts or ends with a long word, than put that on a single line + if (splitEl == startElement) + return splitEl; + // These 2 conditions also cover if the text fits on 1 line + if (!singleSplit && splitEl >= elements.Count - 2) + return elements.Count - 3; // Make sure we have 2 elements left if in multi split mode! + if (splitEl >= elements.Count - 1) + return elements.Count - 2; // Make sure we have 1 element left! + + float diffWith = curWidth - requestedWidth; + float diffWithout = requestedWidth - (curWidth - elements[splitEl].Width); + if (!singleSplit) + { + // If we split in 3 lines, add the error of the next 2 lines to the current error for both cases + int splitEl2 = _GetSplitElement(elements, requestedWidth, true, splitEl + 1); + int splitEl3 = _GetSplitElement(elements, requestedWidth, true, splitEl); + diffWith += Math.Abs(requestedWidth - _GetWidth(elements, splitEl + 1, splitEl2)) + Math.Abs(requestedWidth - _GetWidth(elements, splitEl2 + 1)); + diffWithout += Math.Abs(requestedWidth - _GetWidth(elements, splitEl, splitEl3)) + Math.Abs(requestedWidth - _GetWidth(elements, splitEl3 + 1)); + } + float diff = diffWith - diffWithout; + //Differences below this value are considered acceptable + float equalDist = requestedWidth * 0.025f; + if (diff > equalDist) + return splitEl - 1; // width error with element is much higher than without -> put element on next line + if (diff < -equalDist) + return splitEl; // width error with element is much less than without -> keep element on line + + //The real split would be somewhere in the middle of the element so we can move it to either line + string tmp = elements[splitEl].Text.TrimEnd(null); + char lastCharWith = tmp[tmp.Length - 1]; + tmp = elements[splitEl - 1].Text.TrimEnd(null); + char lastCharWithout = tmp[tmp.Length - 1]; + //Check if last chars are special chars + if (Char.IsLetterOrDigit(lastCharWith)) + { + if (Char.IsLetterOrDigit(lastCharWithout)) + return startElement == 0 ? splitEl : splitEl - 1; //Both are alphanumeric, favor longer first lines but shorter middle lines + return splitEl - 1; //Split after non-alphanumeric char + } + if (Char.IsLetterOrDigit(lastCharWithout)) + return splitEl; //Split after non-alphanumeric char + + int indexWith = Array.IndexOf(_SplitCharPreferences, lastCharWith); + int indexWithout = Array.IndexOf(_SplitCharPreferences, lastCharWithout); + if (indexWith <= indexWithout) + return startElement == 0 ? splitEl : splitEl - 1; //favor longer first lines but shorter middle lines + return splitEl - 1; + } + + /// + /// Split the text in parts that can then be distributet among the lines + /// + /// + /// + private static IEnumerable _SplitText(string text) + { + Debug.Assert(!String.IsNullOrWhiteSpace(text)); + + text = text.Trim().TrimMultipleWs(); + List lines = new List(); + if (text.Length == 1) + { + lines.Add(text); + return lines; + } + //Split the text on non-letter chars + int curStart = 0; + for (int i = 1; i < text.Length - 1; i++) + { + if ((Array.IndexOf(_SplitCharAfter, text[i]) >= 0 && (Char.IsLetterOrDigit(text, i + 1) || Array.IndexOf(_SplitCharBefore, text[i + 1]) >= 0)) || + (Char.IsLetterOrDigit(text, i) && Array.IndexOf(_SplitCharBefore, text[i + 1]) >= 0)) + { + lines.Add(text.Substring(curStart, i - curStart + 1)); + curStart = ++i; + } + } + //Add the rest + lines.Add(text.Substring(curStart)); + + //Check for initials (like J. R.R. Tolkien) and join them + curStart = -1; + string curText = ""; + for (int i = 0; i < lines.Count; i++) + { + string part = lines[i].Trim(); + if (part.Length == 2 && part[1] == '.') + { + //Initials found, continue or start new + if (curStart < 0) + curStart = i; + curText += lines[i]; + } + else if (curStart >= 0) + { + //Initials end + if (curStart < i - 1) + { + //More than 1 part + lines[curStart] = curText; + lines.RemoveRange(curStart + 1, i - curStart - 1); + i = curStart; + } + curText = ""; + curStart = -1; + } + } + //Handle last part + if (curStart >= 0) + { + //Initials end + if (curStart < lines.Count - 1) + { + //More than 1 part + lines[curStart] = curText; + lines.RemoveRange(curStart + 1, lines.Count - curStart - 1); + } + } + + // Remove single char lines + for (int i = 0; i < lines.Count; i++) + { + string part = lines[i]; + if (part.Trim().Length == 1) + { + lines.RemoveAt(i); + if (i == 0) + lines[0] = part + lines[0]; + else + { + lines[i - 1] += part; + i--; + } + } + } + return lines; + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Base/CParty.cs b/Vocaluxe/Base/CParty.cs index 469e8af9..87607df9 100644 --- a/Vocaluxe/Base/CParty.cs +++ b/Vocaluxe/Base/CParty.cs @@ -23,6 +23,7 @@ using System.Reflection; using VocaluxeLib; using VocaluxeLib.Menu; using VocaluxeLib.PartyModes; +using VocaluxeLib.Xml; namespace Vocaluxe.Base { @@ -99,7 +100,6 @@ namespace Vocaluxe.Base pair.Value.PartyMode.SaveScreens(); } - public static List GetPartyModeInfos() { var infos = new List(); diff --git a/Vocaluxe/Base/CPlaylistElement.cs b/Vocaluxe/Base/CPlaylistElement.cs index 3b6588de..eec209c1 100644 --- a/Vocaluxe/Base/CPlaylistElement.cs +++ b/Vocaluxe/Base/CPlaylistElement.cs @@ -61,7 +61,7 @@ namespace Vocaluxe.Base get { return _Song != null ? _Song.Finish : 0f; } } - public CTexture Cover + public CTextureRef Cover { get { return _Song != null ? _Song.CoverTextureSmall : CCover.NoCover; } } diff --git a/Vocaluxe/Base/CProfiles.cs b/Vocaluxe/Base/CProfiles.cs index 5c05719f..8cc7d8b5 100644 --- a/Vocaluxe/Base/CProfiles.cs +++ b/Vocaluxe/Base/CProfiles.cs @@ -504,7 +504,7 @@ namespace Vocaluxe.Base #endregion profile properties #region avatar texture - public static CTexture GetAvatarTexture(int avatarID) + public static CTextureRef GetAvatarTexture(int avatarID) { if (!IsAvatarIDValid(avatarID)) return null; @@ -512,7 +512,7 @@ namespace Vocaluxe.Base return _Avatars[avatarID].Texture; } - public static CTexture GetAvatarTextureFromProfile(int profileID) + public static CTextureRef GetAvatarTextureFromProfile(int profileID) { if (!IsProfileIDValid(profileID) || _Profiles[profileID].Avatar == null) return null; diff --git a/Vocaluxe/Base/CSongCategorizer.cs b/Vocaluxe/Base/CSongCategorizer.cs index 4c16bf3a..7104c795 100644 --- a/Vocaluxe/Base/CSongCategorizer.cs +++ b/Vocaluxe/Base/CSongCategorizer.cs @@ -178,7 +178,11 @@ namespace Vocaluxe.Base } foreach (CCategory cat in _Categories) + { cat.CoverTextureSmall = CCover.Cover(cat.Name); + if (cat.CoverTextureSmall == CCover.NoCover) + cat.CoverTextureSmall = CCover.GenerateCover(cat.Name, CSongs.Sorter.SongSorting, cat); + } _Changed = false; } } diff --git a/Vocaluxe/Base/CSongs.cs b/Vocaluxe/Base/CSongs.cs index 2bf74681..2216f63b 100644 --- a/Vocaluxe/Base/CSongs.cs +++ b/Vocaluxe/Base/CSongs.cs @@ -21,6 +21,7 @@ using System.Collections.ObjectModel; using System.Linq; using System.Threading; using System.IO; +using System.Threading.Tasks; using VocaluxeLib; using VocaluxeLib.Songs; @@ -134,23 +135,13 @@ namespace Vocaluxe.Base Category--; } - public static int GetNextSongWithoutCover(ref CSong song) + private static int _NumSongsWithCoverLoaded; + public static int NumSongsWithCoverLoaded { - if (!SongsLoaded) - return -1; - - if (NumSongsWithCoverLoaded < _Songs.Count) - { - song = _Songs[NumSongsWithCoverLoaded]; - NumSongsWithCoverLoaded++; - return NumSongsWithCoverLoaded; - } - - return -2; + get { return _NumSongsWithCoverLoaded; } + private set { _NumSongsWithCoverLoaded = value; } } - public static int NumSongsWithCoverLoaded { get; private set; } - public static string GetCurrentCategoryName() { return _IsCatIndexValid(_CatIndex) ? Categories[_CatIndex].Name : ""; @@ -371,59 +362,50 @@ namespace Vocaluxe.Base Category = -1; SongsLoaded = true; - if (CConfig.CoverLoading == ECoverLoading.TR_CONFIG_COVERLOADING_ATSTART) + switch (CConfig.CoverLoading) { - CLog.StartBenchmark("Load Covers/Notes"); - _LoadCover(); - CLog.StopBenchmark("Load Covers/Notes"); + case ECoverLoading.TR_CONFIG_COVERLOADING_ATSTART: + _LoadCovers(); + break; + case ECoverLoading.TR_CONFIG_COVERLOADING_DYNAMIC: + _LoadCoversAsync(); + break; } - CLog.StopBenchmark("Load Songs "); + CLog.StopBenchmark("Load Songs"); } - public static void LoadCover() + private static void _LoadCoversAsync() { - if (CConfig.Renderer == ERenderer.TR_CONFIG_SOFTWARE) - return; //should be removed as soon as the other renderer are ready for queue - if (!SongsLoaded || CoverLoaded) return; if (_CoverLoaderThread != null) return; - _CoverLoaderThread = new Thread(_LoadCover) {Name = "CoverLoader", Priority = ThreadPriority.BelowNormal, IsBackground = true}; + _CoverLoaderThread = new Thread(_LoadCovers) {Name = "CoverLoader", Priority = ThreadPriority.BelowNormal, IsBackground = true}; _CoverLoaderThread.Start(); - - /* - if (_CoverLoadTimer.ElapsedMilliseconds >= WaitTime) - { - for (int i = 0; i < NumLoads; i++) - { - CSong song = null; - int n = GetNextSongWithoutCover(ref song); - - if (n < 0) - return; - - song.LoadSmallCover(); - - if (n == NumAllSongs) - CDataBase.CommitCovers(); - } - _CoverLoadTimer.Reset(); - _CoverLoadTimer.Start(); - } - * */ } - private static void _LoadCover() + private static void _LoadCovers() { + CLog.StartBenchmark("Load Covers"); + int songCount = _Songs.Count; + AutoResetEvent ev = new AutoResetEvent(songCount == 0); + + NumSongsWithCoverLoaded = 0; foreach (CSong song in _Songs) { - song.LoadSmallCover(); - NumSongsWithCoverLoaded++; + CSong tmp = song; + Task.Factory.StartNew(() => + { + tmp.LoadSmallCover(); + if (Interlocked.Increment(ref _NumSongsWithCoverLoaded) >= songCount) + ev.Set(); + }); } + ev.WaitOne(); _CoverLoaded = true; CDataBase.CommitCovers(); + CLog.StopBenchmark("Load Covers"); } } } \ No newline at end of file diff --git a/Vocaluxe/Base/CTheme.cs b/Vocaluxe/Base/CTheme.cs index 520353a4..b66796f1 100644 --- a/Vocaluxe/Base/CTheme.cs +++ b/Vocaluxe/Base/CTheme.cs @@ -23,6 +23,7 @@ using System.Xml; using Vocaluxe.Base.Fonts; using VocaluxeLib; using VocaluxeLib.Draw; +using VocaluxeLib.Xml; namespace Vocaluxe.Base { @@ -44,7 +45,7 @@ namespace Vocaluxe.Base class CSkinElement { public string Value; - public CTexture Texture; + public CTextureRef Texture; } class CVideoSkinElement : CSkinElement @@ -266,7 +267,7 @@ namespace Vocaluxe.Base { CFonts.LoadThemeFonts( _Themes[themeIndex].Name, - Path.Combine(Path.Combine(_Themes[themeIndex].Path, _Themes[themeIndex].SkinFolder), CSettings.FolderNameThemeFonts), + Path.Combine(_Themes[themeIndex].Path, _Themes[themeIndex].SkinFolder, CSettings.FolderNameThemeFonts), xmlReader); } else @@ -554,7 +555,7 @@ namespace Vocaluxe.Base return path; } - public static CTexture GetSkinTexture(string textureName, int partyModeID) + public static CTextureRef GetSkinTexture(string textureName, int partyModeID) { int skinIndex = GetSkinIndex(partyModeID); CSkinElement sk; @@ -597,7 +598,7 @@ namespace Vocaluxe.Base return null; } - public static CTexture GetSkinVideoTexture(string videoName, int partyModeID) + public static CTextureRef GetSkinVideoTexture(string videoName, int partyModeID) { CVideoSkinElement sk = GetSkinVideo(videoName, partyModeID); if (sk == null) diff --git a/Vocaluxe/Base/CVideo.cs b/Vocaluxe/Base/CVideo.cs index 3d57cbd3..9b127eae 100644 --- a/Vocaluxe/Base/CVideo.cs +++ b/Vocaluxe/Base/CVideo.cs @@ -75,7 +75,7 @@ namespace Vocaluxe.Base return _VideoDecoder.GetLength(streamID); } - public static bool GetFrame(int streamID, ref CTexture frame, float time, out float videoTime) + public static bool GetFrame(int streamID, ref CTextureRef frame, float time, out float videoTime) { return _VideoDecoder.GetFrame(streamID, ref frame, time, out videoTime); } diff --git a/Vocaluxe/Base/CVideoPlayer.cs b/Vocaluxe/Base/CVideoPlayer.cs index 8dd47d4b..0838ae7c 100644 --- a/Vocaluxe/Base/CVideoPlayer.cs +++ b/Vocaluxe/Base/CVideoPlayer.cs @@ -7,7 +7,7 @@ namespace Vocaluxe.Base { class CVideoPlayer { - private CTexture _VideoTexture; + private CTextureRef _VideoTexture; private int _VideoStream; private readonly Stopwatch _VideoTimer = new Stopwatch(); private bool _Finished; @@ -62,11 +62,8 @@ namespace Vocaluxe.Base } if (_VideoTexture == null) return; - var bounds = new RectangleF(0f, 0f, CSettings.RenderW, CSettings.RenderH); - RectangleF rect; - CHelper.SetRect(bounds, out rect, _VideoTexture.OrigAspect, EAspect.Crop); - CDraw.DrawTexture(_VideoTexture, new SRectF(rect.X, rect.Y, rect.Width, rect.Height, CSettings.ZFar / 4)); + CDraw.DrawTexture(_VideoTexture, new SRectF(0, 0, CSettings.RenderW, CSettings.RenderH, CSettings.ZFar / 4), EAspect.Crop); } public void PreLoad() diff --git a/Vocaluxe/Base/CWebcam.cs b/Vocaluxe/Base/CWebcam.cs index af66025a..980c6f58 100644 --- a/Vocaluxe/Base/CWebcam.cs +++ b/Vocaluxe/Base/CWebcam.cs @@ -57,7 +57,7 @@ namespace Vocaluxe.Base _Webcam = null; } - public static bool GetFrame(ref CTexture tex) + public static bool GetFrame(ref CTextureRef tex) { return _Webcam != null && _Webcam.GetFrame(ref tex); } diff --git a/Vocaluxe/Base/Fonts/CFont.cs b/Vocaluxe/Base/Fonts/CFontStyle.cs similarity index 51% rename from Vocaluxe/Base/Fonts/CFont.cs rename to Vocaluxe/Base/Fonts/CFontStyle.cs index e1408b72..a71c949f 100644 --- a/Vocaluxe/Base/Fonts/CFont.cs +++ b/Vocaluxe/Base/Fonts/CFontStyle.cs @@ -24,48 +24,64 @@ using VocaluxeLib.Draw; namespace Vocaluxe.Base.Fonts { - class CFont : IDisposable + class CFontStyle : IDisposable { - private readonly Dictionary _Glyphs; + private readonly string _FilePath; + private readonly EStyle _Style; + public readonly float Outline; //0..1, 0=not outline 1=100% outline + public readonly SColorF OutlineColor; + private readonly float _MaxGlyphHeight; + private readonly Dictionary _Glyphs = new Dictionary(); private PrivateFontCollection _Fonts; private FontFamily _Family; - private readonly float _MaxGlyphHeight; - private readonly string _FilePath; - - private bool _Disposed; - - public CFont(string file) + public CFontStyle(string file, EStyle style, float outline, SColorF outlineColor) { _FilePath = file; - - _Glyphs = new Dictionary(); + _Style = style; + Outline = outline; + OutlineColor = outlineColor; switch (CConfig.TextureQuality) { case ETextureQuality.TR_CONFIG_TEXTURE_LOWEST: - _MaxGlyphHeight = 25f; + _MaxGlyphHeight = 20f; break; case ETextureQuality.TR_CONFIG_TEXTURE_LOW: - _MaxGlyphHeight = 50f; + _MaxGlyphHeight = 40f; break; case ETextureQuality.TR_CONFIG_TEXTURE_MEDIUM: - _MaxGlyphHeight = 100f; + _MaxGlyphHeight = 60f; break; case ETextureQuality.TR_CONFIG_TEXTURE_HIGH: - _MaxGlyphHeight = 200f; + _MaxGlyphHeight = 80f; break; case ETextureQuality.TR_CONFIG_TEXTURE_HIGHEST: - _MaxGlyphHeight = 400f; - break; - default: _MaxGlyphHeight = 100f; break; + default: + _MaxGlyphHeight = 60f; + break; } - _MaxGlyphHeight = 50; } - public Font GetFont() + private FontStyle _GetSystemFontStyle() + { + switch (_Style) + { + case EStyle.Normal: + return FontStyle.Regular; + case EStyle.Italic: + return FontStyle.Italic; + case EStyle.Bold: + return FontStyle.Bold; + case EStyle.BoldItalic: + return FontStyle.Bold | FontStyle.Italic; + } + throw new ArgumentException("Invalid style: " + _Style); + } + + public Font GetSystemFont(float height) { if (_Fonts == null) { @@ -80,100 +96,83 @@ namespace Vocaluxe.Base.Fonts CLog.LogError("Error opening font file " + _FilePath + ": " + e.Message); } } - return new Font(_Family, CFonts.Height, CFonts.GetFontStyle(), GraphicsUnit.Pixel); + return new Font(_Family, height, _GetSystemFontStyle(), GraphicsUnit.Pixel); } - public void DrawGlyph(char chr, float x, float y, float z, SColorF color) + public void DrawGlyph(char chr, float fontHeight, float x, float y, float z, SColorF color) { - CTexture texture; + CTextureRef texture; SRectF rect; - GetOrAddGlyph(chr).GetTextureAndRect(x, y, z, out texture, out rect); + GetOrAddGlyph(chr, fontHeight).GetTextureAndRect(fontHeight, x, y, z, out texture, out rect); CDraw.DrawTexture(texture, rect, color); } - public void DrawGlyph(char chr, float x, float y, float z, SColorF color, float begin, float end) + public void DrawGlyph(char chr, float fontHeight, float x, float y, float z, SColorF color, float begin, float end) { - CTexture texture; + CTextureRef texture; SRectF rect; - GetOrAddGlyph(chr).GetTextureAndRect(x, y, z, out texture, out rect); + GetOrAddGlyph(chr, fontHeight).GetTextureAndRect(fontHeight, x, y, z, out texture, out rect); CDraw.DrawTexture(texture, rect, color, begin, end); } - public void DrawGlyphReflection(char chr, float x, float y, float z, SColorF color, float rspace, float rheight) + public void DrawGlyphReflection(char chr, float fontHeight, float x, float y, float z, SColorF color, float rspace, float rheight) { - CTexture texture; + CTextureRef texture; SRectF rect; - GetOrAddGlyph(chr).GetTextureAndRect(x, y, z, out texture, out rect); - CDraw.DrawTextureReflection(texture, rect, color, rect, rspace, rheight); + GetOrAddGlyph(chr, fontHeight).GetTextureAndRect(fontHeight, x, y, z, out texture, out rect); + CDraw.DrawTextureReflection(texture, rect, color, rspace, rheight); } - public float GetWidth(char chr) + public float GetWidth(char chr, float height) { - return GetOrAddGlyph(chr).Width; + return GetOrAddGlyph(chr, height).GetWidth(height); } - public float GetHeight(char chr) + public float GetHeight(char chr, float height) { - return GetOrAddGlyph(chr).Height; + return GetOrAddGlyph(chr, height).GetHeight(height); } - public CGlyph GetOrAddGlyph(char chr) + public CGlyph GetOrAddGlyph(char chr, float height) { CGlyph glyph; if (!_Glyphs.TryGetValue(chr, out glyph)) { - glyph = new CGlyph(chr, _MaxGlyphHeight); + float maxHeight = (height < 0 || _MaxGlyphHeight + 50 >= height) ? _MaxGlyphHeight : (float)Math.Round(height / 50) * 50; + glyph = new CGlyph(chr, this, maxHeight); _Glyphs.Add(chr, glyph); } - if (glyph.MaxHeight + 50 < CFonts.Height) + if (glyph.MaxHeight + 50 < height) { glyph.UnloadTexture(); - glyph = new CGlyph(chr, (float)Math.Round(CFonts.Height / 50) * 50); + glyph = new CGlyph(chr, this, (float)Math.Round(height / 50) * 50); _Glyphs[chr] = glyph; } return glyph; } - public void UnloadGlyphs() + private void _UnloadGlyphs() { foreach (CGlyph glyph in _Glyphs.Values) glyph.UnloadTexture(); _Glyphs.Clear(); } + private bool _Disposed; + public void Dispose() { if (!_Disposed) { if (_Fonts != null) + { _Fonts.Dispose(); - UnloadGlyphs(); + _Fonts = null; + } + _UnloadGlyphs(); _Disposed = true; } GC.SuppressFinalize(this); } } - - struct SFont - { - public string Name; - - public bool IsThemeFont; - public int PartyModeID; - public string ThemeName; - - public string Folder; - public string FileNormal; - public string FileItalic; - public string FileBold; - public string FileBoldItalic; - - public float Outline; //0..1, 0=not outline 1=100% outline - public SColorF OutlineColor; - - public CFont Normal; - public CFont Italic; - public CFont Bold; - public CFont BoldItalic; - } } \ No newline at end of file diff --git a/Vocaluxe/Base/Fonts/CFonts.cs b/Vocaluxe/Base/Fonts/CFonts.cs index 1fa9ee31..bdcdefbe 100644 --- a/Vocaluxe/Base/Fonts/CFonts.cs +++ b/Vocaluxe/Base/Fonts/CFonts.cs @@ -15,216 +15,166 @@ // along with Vocaluxe. If not, see . #endregion +using System; using System.Collections.Generic; +using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Xml; using VocaluxeLib; using VocaluxeLib.Menu; +using VocaluxeLib.Xml; namespace Vocaluxe.Base.Fonts { + /// + /// Struct used for describing a font family (a type of text with 4 different styles) + /// + struct SFontFamily + { + public string Name; + + public int PartyModeID; + public string ThemeName; + public string Folder; + + public string FileNormal; + public string FileItalic; + public string FileBold; + public string FileBoldItalic; + + public float Outline; //0..1, 0=not outline 1=100% outline + public SColorF OutlineColor; + + public CFontStyle Normal; + public CFontStyle Italic; + public CFontStyle Bold; + public CFontStyle BoldItalic; + + public void Dispose() + { + Normal.Dispose(); + Italic.Dispose(); + Bold.Dispose(); + BoldItalic.Dispose(); + } + } + static class CFonts { - private static readonly List _Fonts = new List(); - private static int _CurrentFont; - private static float _Height = 1f; + private static bool _IsInitialized; + private static readonly List _FontFamilies = new List(); + private static readonly List _LoggedMissingFonts = new List(); - // ReSharper disable MemberCanBePrivate.Global public static int PartyModeID { get; set; } - // ReSharper restore MemberCanBePrivate.Global - public static EStyle Style = EStyle.Normal; - - public static float Height + public static bool Init() { - get { return _Height; } - set { _Height = value < 0f ? 0f : value; } - } - - public static float Outline - { - get { return _Fonts[_CurrentFont].Outline; } - } - - public static SColorF OutlineColor - { - get { return _Fonts[_CurrentFont].OutlineColor; } - } - - public static void Init() - { - _CurrentFont = 0; + if (_IsInitialized) + return false; PartyModeID = -1; - _BuildFonts(); + return _LoadDefaultFonts(); } - private static void _BuildFonts() + public static void Close() { - foreach (SFont font in _Fonts) - { - font.Normal.Dispose(); - font.Bold.Dispose(); - font.Italic.Dispose(); - font.BoldItalic.Dispose(); - } - _Fonts.Clear(); - _CurrentFont = 0; - - _LoadFontList(); - } - - public static Font GetFont() - { - return _GetCurrentFont().GetFont(); - } - - private static CFont _GetCurrentFont() - { - switch (Style) - { - case EStyle.Normal: - return _Fonts[_CurrentFont].Normal; - case EStyle.Italic: - return _Fonts[_CurrentFont].Italic; - case EStyle.Bold: - return _Fonts[_CurrentFont].Bold; - case EStyle.BoldItalic: - return _Fonts[_CurrentFont].BoldItalic; - } - //Just in case... - return _Fonts[_CurrentFont].Normal; + foreach (SFontFamily font in _FontFamilies) + font.Dispose(); + _FontFamilies.Clear(); + _IsInitialized = false; } #region DrawText - public static void DrawText(string text, int x, int y, int h) + /// + /// Draws a black text + /// + /// The text to be drawn + /// The texts font + /// The texts x-position + /// The texts y-position + /// The texts z-position + public static void DrawText(string text, CFont font, float x, float y, float z) { - DrawText(text, h, x, y, 0f, new SColorF(0f, 0f, 0f, 1f)); - } - - public static void DrawText(string text, float x, float y, float z) - { - DrawText(text, Height, x, y, z, new SColorF(0f, 0f, 0f, 1f)); + DrawText(text, font, x, y, z, new SColorF(0f, 0f, 0f, 1f)); } /// - /// Draws a text string + /// Draws a text /// /// The text to be drawn - /// The text's x-position - /// The text's y-position - /// The text's height - /// The text's z-position + /// The texts font + /// The texts x-position + /// The texts y-position + /// The texts z-position /// The text color - public static void DrawText(string text, float h, float x, float y, float z, SColorF color) + public static void DrawText(string text, CFont font, float x, float y, float z, SColorF color) { - if (h <= 0f) + if (font.Height <= 0f || text == "") return; - if (text == "") - return; - - Height = h; - CFont font = _GetCurrentFont(); + CFontStyle fontStyle = _GetFontStyle(font); float dx = x; foreach (char chr in text) { - font.DrawGlyph(chr, dx, y, z, color); - dx += font.GetWidth(chr); + fontStyle.DrawGlyph(chr, font.Height, dx, y, z, color); + dx += fontStyle.GetWidth(chr, font.Height); } } - public static void DrawTextReflection(string text, float h, float x, float y, float z, SColorF color, float rspace, float rheight) + public static void DrawTextReflection(string text, CFont font, float x, float y, float z, SColorF color, float rspace, float rheight) { - if (h <= 0f) + if (font.Height <= 0f || text == "") return; - if (text == "") - return; - - Height = h; - CFont font = _GetCurrentFont(); + CFontStyle fontStyle = _GetFontStyle(font); float dx = x; foreach (char chr in text) { - font.DrawGlyphReflection(chr, dx, y, z, color, rspace, rheight); - dx += font.GetWidth(chr); + fontStyle.DrawGlyphReflection(chr, font.Height, dx, y, z, color, rspace, rheight); + dx += fontStyle.GetWidth(chr, font.Height); } } - public static void DrawText(string text, float h, float x, float y, float z, SColorF color, float begin, float end) + public static void DrawText(string text, CFont font, float x, float y, float z, SColorF color, float begin, float end) { - if (h <= 0f) + if (font.Height <= 0f || text == "") return; - if (text == "") - return; - - Height = h; - - float dx = x; - float w = GetTextWidth(text); + float w = GetTextWidth(text, font); if (w <= 0f) return; - float x1 = x + w * begin; - float x2 = x + w * end; + float xStart = x + w * begin; + float xEnd = x + w * end; + float xCur = x; - CFont font = _GetCurrentFont(); + CFontStyle fontStyle = _GetFontStyle(font); foreach (char chr in text) { - float w2 = font.GetWidth(chr); - float b = (x1 - dx) / w2; + float w2 = fontStyle.GetWidth(chr, font.Height); + float b = (xStart - xCur) / w2; if (b < 1f) { if (b < 0f) b = 0f; - float e = (x2 - dx) / w2; + float e = (xEnd - xCur) / w2; if (e > 0f) { if (e > 1f) e = 1f; - font.DrawGlyph(chr, dx, y, z, color, b, e); + fontStyle.DrawGlyph(chr, font.Height, xCur, y, z, color, b, e); } } - dx += w2; + xCur += w2; } } #endregion DrawText - public static FontStyle GetFontStyle() - { - switch (Style) - { - case EStyle.Normal: - return FontStyle.Regular; - case EStyle.Italic: - return FontStyle.Italic; - case EStyle.Bold: - return FontStyle.Bold; - case EStyle.BoldItalic: - return FontStyle.Bold | FontStyle.Italic; - default: - return FontStyle.Regular; - } - } - - public static void SetFont(string fontName) - { - int index = _GetPartyFontIndex(PartyModeID, fontName); - if (index < 0) - index = _GetThemeFontIndex(CConfig.Theme, fontName); - if (index < 0) - index = _GetFontIndex(fontName); - if (index >= 0) - _CurrentFont = index; - } - /// /// Calculates the bounds for a CText object /// @@ -232,94 +182,172 @@ namespace Vocaluxe.Base.Fonts /// RectangleF object containing the bounds public static RectangleF GetTextBounds(CText text) { - return GetTextBounds(text, text.Height); + return new RectangleF(text.X, text.Y, GetTextWidth(text.TranslatedText, text.Font), GetTextHeight(text.TranslatedText, text.Font)); + } + + public static Font GetSystemFont(CFont font) + { + return _GetFontStyle(font).GetSystemFont(font.Height); + } + + public static float GetOutlineSize(CFont font) + { + return _FontFamilies[_GetFontIndex(font.Name)].Outline; + } + + public static SColorF GetOutlineColor(CFont font) + { + return _FontFamilies[_GetFontIndex(font.Name)].OutlineColor; + } + + private static CFontStyle _GetFontStyle(CFont font) + { + int index = _GetFontIndex(font.Name); + + switch (font.Style) + { + case EStyle.Normal: + return _FontFamilies[index].Normal; + case EStyle.Italic: + return _FontFamilies[index].Italic; + case EStyle.Bold: + return _FontFamilies[index].Bold; + case EStyle.BoldItalic: + return _FontFamilies[index].BoldItalic; + } + throw new ArgumentException("Invalid Style: " + font.Style); + } + + public static float GetTextWidth(string text, CFont font) + { + CFontStyle fontStyle = _GetFontStyle(font); + return text.Sum(chr => fontStyle.GetWidth(chr, font.Height)); + } + + public static float GetTextHeight(string text, CFont font) + { + CFontStyle fontStyle = _GetFontStyle(font); + return text == "" ? 0 : text.Select(chr => fontStyle.GetHeight(chr, font.Height)).Max(); + } + + private static int _GetFontIndex(string fontName) + { + if (fontName != "") + { + int index = _GetPartyFontIndex(fontName, PartyModeID); + if (index < 0) + index = _GetThemeFontIndex(fontName, CConfig.Theme); + if (index >= 0) + return index; + for (int i = 0; i < _FontFamilies.Count; i++) + { + if (_FontFamilies[i].Name == fontName) + return i; + } + if (!_LoggedMissingFonts.Contains(fontName)) + { + _LoggedMissingFonts.Add(fontName); + CLog.LogError("Font \"" + fontName + "\" not found!"); + } + } + else + CLog.LogError("Empty fontName requested", false, false, new Exception()); + + if (_FontFamilies.Count == 0) + CLog.LogError("No fonts found!", true, true); + + return 0; + } + + private static int _GetThemeFontIndex(string fontName, string themeName) + { + Debug.Assert(fontName != "" && themeName != ""); + + for (int i = 0; i < _FontFamilies.Count; i++) + { + if (_FontFamilies[i].Name == fontName && _FontFamilies[i].ThemeName == themeName) + return i; + } + + return -1; + } + + private static int _GetPartyFontIndex(string fontName, int partyModeID) + { + Debug.Assert(fontName != ""); + + for (int i = 0; i < _FontFamilies.Count; i++) + { + if (_FontFamilies[i].PartyModeID == partyModeID && _FontFamilies[i].Name == fontName) + return i; + } + + return -1; } /// - /// Calculates the bounds for a CText object + /// Load default fonts /// - /// The CText object of which the bounds should be calculated for - /// The height of the CText object - /// RectangleF object containing the bounds - public static RectangleF GetTextBounds(CText text, float height) + /// + private static bool _LoadDefaultFonts() { - float oldHeight = Height; - int oldFont = _CurrentFont; - EStyle oldStyle = Style; - Height = height; - SetFont(text.Font); - Style = text.Style; - var result = new RectangleF(text.X, text.Y, GetTextWidth(CLanguage.Translate(text.Text, text.TranslationID)), - GetTextHeight(CLanguage.Translate(text.Text, text.TranslationID))); - //restore old values - Height = oldHeight; - _CurrentFont = oldFont; - Style = oldStyle; - return result; + CXMLReader xmlReader = CXMLReader.OpenFile(Path.Combine(CSettings.ProgramFolder, CSettings.FolderNameFonts, CSettings.FileNameFonts)); + if (xmlReader == null) + return false; + + return _LoadFontFile(xmlReader, Path.Combine(CSettings.ProgramFolder, CSettings.FolderNameFonts)); } - public static float GetTextWidth(string text) + /// + /// Loads theme fonts from skin file + /// + public static bool LoadThemeFonts(string themeName, string fontFolder, CXMLReader xmlReader) { - CFont font = _GetCurrentFont(); - return text.Sum(chr => font.GetWidth(chr)); + bool ok = _LoadFontFile(xmlReader, fontFolder, themeName); + CLog.StartBenchmark("BuildGlyphs"); + _BuildGlyphs(); + CLog.StopBenchmark("BuildGlyphs"); + return ok; } - public static float GetTextHeight(string text) + /// + /// Loads party mode fonts from skin file + /// + public static bool LoadPartyModeFonts(int partyModeID, string fontFolder, CXMLReader xmlReader) { - //return TextRenderer.MeasureText(text, GetFont()).Height; - CFont font = _GetCurrentFont(); - return text == "" ? 0 : text.Select(font.GetHeight).Max(); + bool ok = _LoadFontFile(xmlReader, fontFolder, "", partyModeID); + CLog.StartBenchmark("BuildGlyphs"); + _BuildGlyphs(); + CLog.StopBenchmark("BuildGlyphs"); + return ok; } - private static void _LoadFontFiles(CXMLReader xmlReader, string fontFolder, string themeName = "", int partyModeId = -1) + private static bool _LoadFontFile(CXMLReader xmlReader, string fontFolder, string themeName = "", int partyModeId = -1) { - string value = string.Empty; + string value; int i = 1; - while (xmlReader.GetValue("//root/Fonts/Font" + i + "/Folder", out value, value)) + while (xmlReader.GetValue("//root/Fonts/Font" + i + "/Folder", out value)) { - var sf = new SFont {Folder = value, IsThemeFont = themeName != "", ThemeName = themeName, PartyModeID = partyModeId}; + var sf = new SFontFamily {Folder = value, ThemeName = themeName, PartyModeID = partyModeId}; bool ok = true; - string name; - ok &= xmlReader.GetValue("//root/Fonts/Font" + i + "/Name", out name, value); - sf.Name = name; - - ok &= xmlReader.GetValue("//root/Fonts/Font" + i + "/FileNormal", out value, value); - sf.FileNormal = value; - value = Path.Combine(fontFolder, Path.Combine(sf.Folder, value)); - var f = new CFont(value); - sf.Normal = f; - - ok &= xmlReader.GetValue("//root/Fonts/Font" + i + "/FileItalic", out value, value); - sf.FileItalic = value; - value = Path.Combine(fontFolder, Path.Combine(sf.Folder, value)); - f = new CFont(value); - sf.Italic = f; - - ok &= xmlReader.GetValue("//root/Fonts/Font" + i + "/FileBold", out value, value); - sf.FileBold = value; - value = Path.Combine(fontFolder, Path.Combine(sf.Folder, value)); - f = new CFont(value); - sf.Bold = f; - - ok &= xmlReader.GetValue("//root/Fonts/Font" + i + "/FileBoldItalic", out value, value); - sf.FileBoldItalic = value; - value = Path.Combine(fontFolder, Path.Combine(sf.Folder, value)); - f = new CFont(value); - sf.BoldItalic = f; - - sf.Outline = 0f; - ok &= xmlReader.TryGetFloatValue("//root/Fonts/Font" + i + "/Outline", ref sf.Outline); - - sf.OutlineColor = new SColorF(0f, 0f, 0f, 1f); - ok &= xmlReader.TryGetFloatValue("//root/Fonts/Font" + i + "/OutlineColorR", ref sf.OutlineColor.R); - ok &= xmlReader.TryGetFloatValue("//root/Fonts/Font" + i + "/OutlineColorG", ref sf.OutlineColor.G); - ok &= xmlReader.TryGetFloatValue("//root/Fonts/Font" + i + "/OutlineColorB", ref sf.OutlineColor.B); - ok &= xmlReader.TryGetFloatValue("//root/Fonts/Font" + i + "/OutlineColorA", ref sf.OutlineColor.A); + ok &= xmlReader.GetValue("//root/Fonts/Font" + i + "/Name", out sf.Name); + ok &= xmlReader.GetValue("//root/Fonts/Font" + i + "/FileNormal", out sf.FileNormal); + ok &= xmlReader.GetValue("//root/Fonts/Font" + i + "/FileItalic", out sf.FileItalic); + ok &= xmlReader.GetValue("//root/Fonts/Font" + i + "/FileBold", out sf.FileBold); + ok &= xmlReader.GetValue("//root/Fonts/Font" + i + "/FileBoldItalic", out sf.FileBoldItalic); + ok &= xmlReader.TryGetNormalizedFloatValue("//root/Fonts/Font" + i + "/Outline", ref sf.Outline); + ok &= xmlReader.TryGetColorFromRGBA("//root/Fonts/Font" + i + "/OutlineColor", ref sf.OutlineColor); if (ok) - _Fonts.Add(sf); + { + sf.Normal = new CFontStyle(Path.Combine(fontFolder, sf.Folder, sf.FileNormal), EStyle.Normal, sf.Outline, sf.OutlineColor); + sf.Italic = new CFontStyle(Path.Combine(fontFolder, sf.Folder, sf.FileItalic), EStyle.Italic, sf.Outline, sf.OutlineColor); + sf.Bold = new CFontStyle(Path.Combine(fontFolder, sf.Folder, sf.FileBold), EStyle.Bold, sf.Outline, sf.OutlineColor); + sf.BoldItalic = new CFontStyle(Path.Combine(fontFolder, sf.Folder, sf.FileBoldItalic), EStyle.BoldItalic, sf.Outline, sf.OutlineColor); + _FontFamilies.Add(sf); + } else { string fontTypes; @@ -330,170 +358,80 @@ namespace Vocaluxe.Base.Fonts else fontTypes = "basic fonts"; CLog.LogError("Error loading " + fontTypes + ": Error in Font" + i); + return false; } i++; } - } - - /// - /// Load default fonts - /// - /// - private static void _LoadFontList() - { - CXMLReader xmlReader = CXMLReader.OpenFile(Path.Combine(CSettings.ProgramFolder, CSettings.FolderNameFonts, CSettings.FileNameFonts)); - if (xmlReader == null) - return; - - _LoadFontFiles(xmlReader, Path.Combine(CSettings.ProgramFolder, CSettings.FolderNameFonts)); - } - - /// - /// Loads theme fonts from skin file - /// - public static void LoadThemeFonts(string themeName, string fontFolder, CXMLReader xmlReader) - { - _LoadFontFiles(xmlReader, fontFolder, themeName); - CLog.StartBenchmark("BuildGlyphs"); - _BuildGlyphs(); - CLog.StopBenchmark("BuildGlyphs"); - } - - /// - /// Loads party mode fonts from skin file - /// - public static void LoadPartyModeFonts(int partyModeID, string fontFolder, CXMLReader xmlReader) - { - _LoadFontFiles(xmlReader, fontFolder, "", partyModeID); - CLog.StartBenchmark("BuildGlyphs"); - _BuildGlyphs(); - CLog.StopBenchmark("BuildGlyphs"); + return true; } public static void SaveThemeFonts(string themeName, XmlWriter writer) { - if (_Fonts.Count == 0) + if (_FontFamilies.Count == 0) return; - int index = 0; + writer.WriteStartElement("Fonts"); int fontNr = 1; - bool setStart = false; - while (index < _Fonts.Count) + foreach (SFontFamily font in _FontFamilies) { - if (_Fonts[index].IsThemeFont && _Fonts[index].ThemeName == themeName) + if (font.ThemeName == themeName) { - if (!setStart) - { - writer.WriteStartElement("Fonts"); - setStart = true; - } - writer.WriteStartElement("Font" + fontNr); - writer.WriteElementString("Name", _Fonts[index].Name); - writer.WriteElementString("Folder", _Fonts[index].Folder); + writer.WriteElementString("Name", font.Name); + writer.WriteElementString("Folder", font.Folder); - writer.WriteElementString("Outline", _Fonts[index].Outline.ToString("#0.00")); - writer.WriteElementString("OutlineColorR", _Fonts[index].OutlineColor.R.ToString("#0.00")); - writer.WriteElementString("OutlineColorG", _Fonts[index].OutlineColor.G.ToString("#0.00")); - writer.WriteElementString("OutlineColorB", _Fonts[index].OutlineColor.B.ToString("#0.00")); - writer.WriteElementString("OutlineColorA", _Fonts[index].OutlineColor.A.ToString("#0.00")); + writer.WriteElementString("Outline", font.Outline.ToString("#0.00")); + writer.WriteStartElement("OutlineColor"); + writer.WriteElementString("R", font.OutlineColor.R.ToString("#0.00")); + writer.WriteElementString("G", font.OutlineColor.G.ToString("#0.00")); + writer.WriteElementString("B", font.OutlineColor.B.ToString("#0.00")); + writer.WriteElementString("A", font.OutlineColor.A.ToString("#0.00")); + writer.WriteEndElement(); - writer.WriteElementString("FileNormal", _Fonts[index].FileNormal); - writer.WriteElementString("FileBold", _Fonts[index].FileBold); - writer.WriteElementString("FileItalic", _Fonts[index].FileItalic); - writer.WriteElementString("FileBoldItalic", _Fonts[index].FileBoldItalic); + writer.WriteElementString("FileNormal", font.FileNormal); + writer.WriteElementString("FileBold", font.FileBold); + writer.WriteElementString("FileItalic", font.FileItalic); + writer.WriteElementString("FileBoldItalic", font.FileBoldItalic); writer.WriteEndElement(); fontNr++; } - index++; } - if (setStart) - writer.WriteEndElement(); + writer.WriteEndElement(); } public static void UnloadThemeFonts(string themeName) { int index = 0; - while (index < _Fonts.Count) + while (index < _FontFamilies.Count) { - if (_Fonts[index].IsThemeFont && _Fonts[index].ThemeName == themeName) + if (_FontFamilies[index].ThemeName == themeName) { - _Fonts[index].Normal.UnloadGlyphs(); - _Fonts[index].Italic.UnloadGlyphs(); - _Fonts[index].Bold.UnloadGlyphs(); - _Fonts[index].BoldItalic.UnloadGlyphs(); - _Fonts.RemoveAt(index); + _FontFamilies[index].Dispose(); + _FontFamilies.RemoveAt(index); } else index++; } } - private static int _GetFontIndex(string fontName) - { - for (int i = 0; i < _Fonts.Count; i++) - { - if (!_Fonts[i].IsThemeFont && _Fonts[i].Name == fontName) - return i; - } - - return -1; - } - - private static int _GetThemeFontIndex(string themeName, string fontName) - { - if (themeName == "" || fontName == "") - return -1; - - for (int i = 0; i < _Fonts.Count; i++) - { - if (_Fonts[i].IsThemeFont && _Fonts[i].Name == fontName && _Fonts[i].ThemeName == themeName) - return i; - } - - return -1; - } - - private static int _GetPartyFontIndex(int partyModeID, string fontName) - { - if (partyModeID == -1 || fontName == "") - return -1; - - for (int i = 0; i < _Fonts.Count; i++) - { - if (!_Fonts[i].IsThemeFont && _Fonts[i].PartyModeID == partyModeID && _Fonts[i].Name == fontName) - return i; - } - - return -1; - } - private static void _BuildGlyphs() { const string text = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; - for (int i = 0; i < _Fonts.Count; i++) + foreach (SFontFamily fontFamily in _FontFamilies) { - _CurrentFont = i; - foreach (char chr in text) { - Style = EStyle.Normal; - _Fonts[_CurrentFont].Normal.GetOrAddGlyph(chr); - Style = EStyle.Bold; - _Fonts[_CurrentFont].Bold.GetOrAddGlyph(chr); - Style = EStyle.Italic; - _Fonts[_CurrentFont].Italic.GetOrAddGlyph(chr); - Style = EStyle.BoldItalic; - _Fonts[_CurrentFont].BoldItalic.GetOrAddGlyph(chr); + fontFamily.Normal.GetOrAddGlyph(chr, -1); + fontFamily.Bold.GetOrAddGlyph(chr, -1); + fontFamily.Italic.GetOrAddGlyph(chr, -1); + fontFamily.BoldItalic.GetOrAddGlyph(chr, -1); } } - Style = EStyle.Normal; - SetFont("Normal"); } } } \ No newline at end of file diff --git a/Vocaluxe/Base/Fonts/CGlyph.cs b/Vocaluxe/Base/Fonts/CGlyph.cs index c694c84d..6b949903 100644 --- a/Vocaluxe/Base/Fonts/CGlyph.cs +++ b/Vocaluxe/Base/Fonts/CGlyph.cs @@ -31,31 +31,18 @@ namespace Vocaluxe.Base.Fonts { class CGlyph { - private CTexture _Texture; + private CTextureRef _Texture; private readonly SizeF _BoundingBox; private readonly RectangleF _DrawBounding; public readonly float MaxHeight; - public float Width - { - get { return _BoundingBox.Width * _GetFactor(); } - } - - public float Height - { - get { return _BoundingBox.Height * _GetFactor(); } - } - - public CGlyph(char chr, float maxHeight) + public CGlyph(char chr, CFontStyle fontStyle, float maxHeight) { MaxHeight = maxHeight; - float oldHeight = CFonts.Height; - float outline = CFonts.Outline; - float outlineSize = outline * maxHeight; + float outlineSize = fontStyle.Outline * maxHeight; string chrString = chr.ToString(); - CFonts.Height = maxHeight; - Font fo = CFonts.GetFont(); + Font fo = fontStyle.GetSystemFont(maxHeight); SizeF fullSize; Size bmpSize; using (Graphics g = Graphics.FromHwnd(IntPtr.Zero)) @@ -64,7 +51,7 @@ namespace Vocaluxe.Base.Fonts if (chr != ' ') { //Gets exact height and width for drawing more than 1 char. But width is to small to draw char on bitmap as e.g. italic chars will get cropped - _BoundingBox = g.MeasureString(chrString, fo, -1, new StringFormat(StringFormat.GenericTypographic)); + _BoundingBox = g.MeasureString(chrString, fo, -1, StringFormat.GenericTypographic); // ReSharper disable CompareOfFloatsByEqualityOperator if (_BoundingBox.Height == 0) // ReSharper restore CompareOfFloatsByEqualityOperator @@ -105,7 +92,7 @@ namespace Vocaluxe.Base.Fonts float emSize = fo.Size * fo.FontFamily.GetCellAscent(fo.Style) / fo.FontFamily.GetEmHeight(fo.Style); path.AddString(chrString, fo.FontFamily, (int)fo.Style, emSize, point, new StringFormat()); - using (var pen = new Pen(CFonts.OutlineColor.AsColor(), outlineSize)) + using (var pen = new Pen(fontStyle.OutlineColor.AsColor(), outlineSize)) { pen.LineJoin = LineJoin.Round; g.DrawPath(pen, path); @@ -134,7 +121,6 @@ namespace Vocaluxe.Base.Fonts } } } - CFonts.Height = oldHeight; } public void UnloadTexture() @@ -142,15 +128,25 @@ namespace Vocaluxe.Base.Fonts CDraw.RemoveTexture(ref _Texture); } - private float _GetFactor() + private float _GetFactor(float fontHeight) { - return CFonts.Height / _BoundingBox.Height; + return fontHeight / _BoundingBox.Height; } - public void GetTextureAndRect(float x, float y, float z, out CTexture texture, out SRectF rect) + public float GetWidth(float fontHeight) + { + return _BoundingBox.Width * _GetFactor(fontHeight); + } + + public float GetHeight(float fontHeight) + { + return _BoundingBox.Height * _GetFactor(fontHeight); + } + + public void GetTextureAndRect(float fontHeight, float x, float y, float z, out CTextureRef texture, out SRectF rect) { texture = _Texture; - float factor = _GetFactor(); + float factor = _GetFactor(fontHeight); x += _DrawBounding.X * factor; y += _DrawBounding.Y * factor; float h = _DrawBounding.Height * factor; @@ -161,7 +157,7 @@ namespace Vocaluxe.Base.Fonts private static Rectangle _GetRealBounds(Bitmap bmp) { int minX = 0, maxX = bmp.Width - 1, minY = 0; - BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + BitmapData bmpData = bmp.LockBits(bmp.GetRect(), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); int values = bmpData.Width * bmp.Height; var rgbValues = new Int32[values]; Marshal.Copy(bmpData.Scan0, rgbValues, 0, values); diff --git a/Vocaluxe/COSFunctions.cs b/Vocaluxe/COSFunctions.cs index f41d5bbe..33faab37 100644 --- a/Vocaluxe/COSFunctions.cs +++ b/Vocaluxe/COSFunctions.cs @@ -16,6 +16,7 @@ #endregion using System; +using System.Drawing; using System.Runtime.InteropServices; namespace Vocaluxe diff --git a/Vocaluxe/Lib/Database/CCoverDB.cs b/Vocaluxe/Lib/Database/CCoverDB.cs index 7723e2d4..efd33c13 100644 --- a/Vocaluxe/Lib/Database/CCoverDB.cs +++ b/Vocaluxe/Lib/Database/CCoverDB.cs @@ -22,6 +22,7 @@ using System.Drawing.Imaging; using System.IO; using System.Runtime.InteropServices; using Vocaluxe.Base; +using VocaluxeLib; using VocaluxeLib.Draw; #if WIN using System.Data.SQLite; @@ -34,28 +35,29 @@ namespace Vocaluxe.Lib.Database { public class CCoverDB : CDatabaseBase { - //You have to lock all actions using cover connection or transaction, otherwhise order is not guaranted - private readonly object _CoverMutex = new object(); private SQLiteTransaction _TransactionCover; public CCoverDB(string filePath) : base(filePath) {} public override bool Init() { - if (!base.Init()) - return false; + lock (_Mutex) + { + if (!base.Init()) + return false; - if (_Version < 0) - return _CreateCoverDB(); - if (_Version < CSettings.DatabaseCoverVersion) - throw new NotImplementedException("Upgrading of cover DB not implemented"); + if (_Version < 0) + return _CreateCoverDB(); + if (_Version < CSettings.DatabaseCoverVersion) + throw new NotImplementedException("Upgrading of cover DB not implemented"); + } return true; } public override void Close() { //Do commit and close atomicly otherwhise we may loose changes - lock (_CoverMutex) + lock (_Mutex) { _CommitCovers(); @@ -63,7 +65,7 @@ namespace Vocaluxe.Lib.Database } } - public bool GetCover(string coverPath, ref CTexture tex, int maxSize) + public bool GetCover(string coverPath, ref CTextureRef tex, int maxSize) { if (_Connection == null) return false; @@ -73,7 +75,7 @@ namespace Vocaluxe.Lib.Database return false; } - lock (_CoverMutex) + lock (_Mutex) { //Double check here because we may have just closed our connection if (_Connection == null) @@ -81,7 +83,7 @@ namespace Vocaluxe.Lib.Database using (var command = new SQLiteCommand(_Connection)) { command.CommandText = "SELECT id, width, height FROM Cover WHERE [Path] = @path"; - command.Parameters.Add("@path", DbType.String, 0).Value = coverPath; + command.Parameters.Add("@path", DbType.String).Value = coverPath; SQLiteDataReader reader = command.ExecuteReader(); @@ -100,81 +102,81 @@ namespace Vocaluxe.Lib.Database if (reader.HasRows) { reader.Read(); - byte[] data = _GetBytes(reader); + byte[] data2 = _GetBytes(reader); reader.Dispose(); - tex = CDraw.EnqueueTexture(w, h, data); + tex = CDraw.EnqueueTexture(w, h, data2); return true; } - } - else - { - if (reader != null) - reader.Close(); - - if (_TransactionCover == null) - _TransactionCover = _Connection.BeginTransaction(); - - Bitmap origin; - try - { - origin = new Bitmap(coverPath); - } - catch (Exception) - { - CLog.LogError("Error loading Texture: " + coverPath); - return false; - } - - int w = maxSize; - int h = maxSize; - byte[] data; - - try - { - if (origin.Width >= origin.Height && origin.Width > w) - h = (int)Math.Round((float)w / origin.Width * origin.Height); - else if (origin.Height > origin.Width && origin.Height > h) - w = (int)Math.Round((float)h / origin.Height * origin.Width); - - using (var bmp = new Bitmap(w, h)) - { - using (Graphics g = Graphics.FromImage(bmp)) - g.DrawImage(origin, new Rectangle(0, 0, w, h)); - - data = new byte[w * h * 4]; - BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); - Marshal.Copy(bmpData.Scan0, data, 0, w * h * 4); - bmp.UnlockBits(bmpData); - } - } - finally - { - origin.Dispose(); - } - - tex = CDraw.EnqueueTexture(w, h, data); - - command.CommandText = "INSERT INTO Cover (Path, width, height) VALUES (@path, @w, @h)"; - command.Parameters.Add("@w", DbType.Int32).Value = w; - command.Parameters.Add("@h", DbType.Int32).Value = h; - command.Parameters.Add("@path", DbType.String, 0).Value = coverPath; + command.CommandText = "DELETE FROM Cover WHERE id = @id"; + command.Parameters.Add("@id", DbType.Int32).Value = id; command.ExecuteNonQuery(); + } + if (reader != null) + reader.Close(); + } + } - command.CommandText = "SELECT id FROM Cover WHERE [Path] = @path"; - command.Parameters.Add("@path", DbType.String, 0).Value = coverPath; - reader = command.ExecuteReader(); + // At this point we do not have a mathing entry in the CoverDB (either no Data found and deleted or nothing at all) + // We break out of the lock to do the bitmap loading and resizing here to allow multithreaded loading - if (reader != null) - { - reader.Read(); - int id = reader.GetInt32(0); - reader.Dispose(); - command.CommandText = "INSERT INTO CoverData (CoverID, Data) VALUES (@id, @data)"; - command.Parameters.Add("@id", DbType.Int32).Value = id; - command.Parameters.Add("@data", DbType.Binary, 20).Value = data; - command.ExecuteReader(); - return true; - } + Bitmap origin = CHelper.LoadBitmap(coverPath); + if (origin == null) + return false; + + Size size = origin.GetSize(); + if (size.Width > maxSize || size.Height > maxSize) + { + size = CHelper.FitInBounds(new SRectF(0, 0, maxSize, maxSize, 0), (float)size.Width / size.Height, EAspect.LetterBox).SizeI; + Bitmap tmp = origin.Resize(size); + origin.Dispose(); + origin = tmp; + } + + byte[] data; + + try + { + data = new byte[size.Width * size.Height * 4]; + BitmapData bmpData = origin.LockBits(origin.GetRect(), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + Marshal.Copy(bmpData.Scan0, data, 0, data.Length); + origin.UnlockBits(bmpData); + } + finally + { + origin.Dispose(); + } + + tex = CDraw.EnqueueTexture(size.Width, size.Height, data); + + lock (_Mutex) + { + //Double check here because we may have just closed our connection + if (_Connection == null) + return false; + if (_TransactionCover == null) + _TransactionCover = _Connection.BeginTransaction(); + using (var command = new SQLiteCommand(_Connection)) + { + command.CommandText = "INSERT INTO Cover (Path, width, height) VALUES (@path, @w, @h)"; + command.Parameters.Add("@w", DbType.Int32).Value = size.Width; + command.Parameters.Add("@h", DbType.Int32).Value = size.Height; + command.Parameters.Add("@path", DbType.String).Value = coverPath; + command.ExecuteNonQuery(); + + command.CommandText = "SELECT id FROM Cover WHERE [Path] = @path"; + command.Parameters.Add("@path", DbType.String).Value = coverPath; + SQLiteDataReader reader = command.ExecuteReader(); + + if (reader != null) + { + reader.Read(); + int id = reader.GetInt32(0); + reader.Dispose(); + command.CommandText = "INSERT INTO CoverData (CoverID, Data) VALUES (@id, @data)"; + command.Parameters.Add("@id", DbType.Int32).Value = id; + command.Parameters.Add("@data", DbType.Binary).Value = data; + command.ExecuteNonQuery(); + return true; } } } @@ -183,7 +185,7 @@ namespace Vocaluxe.Lib.Database public void CommitCovers() { - lock (_CoverMutex) + lock (_Mutex) { _CommitCovers(); } diff --git a/Vocaluxe/Lib/Database/CDatabaseBase.cs b/Vocaluxe/Lib/Database/CDatabaseBase.cs index 068530e5..bbe0f026 100644 --- a/Vocaluxe/Lib/Database/CDatabaseBase.cs +++ b/Vocaluxe/Lib/Database/CDatabaseBase.cs @@ -17,7 +17,6 @@ using System; using System.IO; -using Vocaluxe.Base; #if WIN using System.Data.SQLite; @@ -27,61 +26,72 @@ using Mono.Data.Sqlite; namespace Vocaluxe.Lib.Database { - public class CDatabaseBase + public abstract class CDatabaseBase { protected readonly string _FilePath; protected SQLiteConnection _Connection; + //You have to lock all actions using connection or transaction, otherwhise order is not guaranted + protected readonly object _Mutex = new object(); + protected int _Version; //Version of the database (value of row "Value" in table "Version") or -1 if none - public CDatabaseBase(string filePath) + protected CDatabaseBase(string filePath) { _FilePath = filePath; } public virtual bool Init() { - _Connection = new SQLiteConnection("Data Source=" + _FilePath); - try + lock (_Mutex) { - _Connection.Open(); - } - catch (Exception) - { - _Connection.Dispose(); - _Connection = null; - return false; - } - - using (var command = new SQLiteCommand(_Connection)) - { - command.CommandText = "SELECT Value FROM Version"; - - SQLiteDataReader reader = null; - + if (_Connection != null) + return false; + _Connection = new SQLiteConnection("Data Source=" + _FilePath); try { - reader = command.ExecuteReader(); + _Connection.Open(); + } + catch (Exception) + { + _Connection.Dispose(); + _Connection = null; + return false; } - catch (Exception) {} - if (reader == null || !reader.Read() || reader.FieldCount == 0) - _Version = -1; - else - _Version = reader.GetInt32(0); + using (var command = new SQLiteCommand(_Connection)) + { + command.CommandText = "SELECT Value FROM Version"; - if (reader != null) - reader.Dispose(); + SQLiteDataReader reader = null; + + try + { + reader = command.ExecuteReader(); + } + catch (Exception) {} + + if (reader == null || !reader.Read() || reader.FieldCount == 0) + _Version = -1; + else + _Version = reader.GetInt32(0); + + if (reader != null) + reader.Dispose(); + } } return true; } public virtual void Close() { - if (_Connection != null) + lock (_Mutex) { - _Connection.Dispose(); - _Connection = null; + if (_Connection != null) + { + _Connection.Dispose(); + _Connection = null; + } } } @@ -92,6 +102,13 @@ namespace Vocaluxe.Lib.Database return t70.Ticks; } + /// + /// Reads the contents of a field as a byte array
+ /// Assumes you did the locking! + ///
+ /// + /// Field number to read from (default=first) + /// protected static byte[] _GetBytes(SQLiteDataReader reader, int field = 0) { const int chunkSize = 2 * 1024; diff --git a/Vocaluxe/Lib/Database/CResourceDB.cs b/Vocaluxe/Lib/Database/CResourceDB.cs index 4891a19e..77d9c455 100644 --- a/Vocaluxe/Lib/Database/CResourceDB.cs +++ b/Vocaluxe/Lib/Database/CResourceDB.cs @@ -66,7 +66,7 @@ namespace Vocaluxe.Lib.Database return true; } - public bool GetCreditsRessource(string fileName, ref CTexture tex) + public bool GetCreditsRessource(string fileName, ref CTextureRef tex) { if (_Connection == null) return false; diff --git a/Vocaluxe/Lib/Draw/CDirect3D.cs b/Vocaluxe/Lib/Draw/CDirect3D.cs index 1c04f6fd..682905bc 100644 --- a/Vocaluxe/Lib/Draw/CDirect3D.cs +++ b/Vocaluxe/Lib/Draw/CDirect3D.cs @@ -15,13 +15,10 @@ // along with Vocaluxe. If not, see . #endregion -using System.Drawing.Drawing2D; -using System.Threading; using SlimDX; using SlimDX.Direct3D9; using System; using System.Collections.Generic; -using System.ComponentModel; using System.Drawing; using System.Drawing.Imaging; using System.IO; @@ -34,49 +31,56 @@ using VocaluxeLib.Draw; namespace Vocaluxe.Lib.Draw { - class CDirect3D : RenderForm, IDraw + class CRenderFormHook : RenderForm, IFormHook + { + public MessageEventHandler OnMessage { private get; set; } + + protected override void WndProc(ref Message m) + { + if (OnMessage == null || OnMessage(ref m)) + base.WndProc(ref m); + } + } + + class CD3DTexture : CTextureBase + { + public readonly Texture D3DTexture; + + public CD3DTexture(Device device, Size dataSize, int texWidth = 0, int texHeight = 0) : base(dataSize, texWidth, texHeight) + { + //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 override bool IsLoaded + { + get { return D3DTexture != null; } + } + + public override void Dispose() + { + base.Dispose(); + if (D3DTexture != null) + D3DTexture.Dispose(); + } + } + + class CDirect3D : CDrawBaseWindows, IDraw { #region private vars - private const int _MaxID = 100000; - - private readonly CKeys _Keys; - private readonly CMouse _Mouse; - private bool _Run; - private readonly Direct3D _D3D; private readonly Device _Device; private readonly PresentParameters _PresentParameters; - private bool _Fullscreen; - private Size _OldSize; - private Size _SizeBeforeMinimize; - - private readonly Dictionary _D3DTextures = new Dictionary(); - private readonly Queue _TexturesToLoad = new Queue(); - private readonly Queue _IDs = new Queue(_MaxID); - - private readonly Object _MutexTexture = new Object(); - private VertexBuffer _VertexBuffer; private IndexBuffer _IndexBuffer; - private int _H = 1; - private int _W = 1; - private int _Y; - private int _X; - - private int _BorderLeft; - private int _BorderRight; - private int _BorderTop; - private int _BorderBottom; - - private CTexture _BlankTexture; + private CTextureRef _BlankTexture; private readonly Queue _Vertices = new Queue(); private readonly Queue _VerticesTextures = new Queue(); - private readonly Queue _VerticesRotationMatrices = new Queue(); - - private readonly bool _NonPowerOf2TextureSupported; + private readonly Queue _VerticesRotationMatrices = new Queue(); #endregion private vars /// @@ -84,13 +88,8 @@ namespace Vocaluxe.Lib.Draw /// public CDirect3D() { - Icon = new Icon(Path.Combine(CSettings.ProgramFolder, CSettings.FileNameIcon)); + _Form = new CRenderFormHook {ClientSize = new Size(CConfig.ScreenW, CConfig.ScreenH)}; - //Fill Queue with 100000 IDs - for (int i = 0; i < _MaxID; i++) - _IDs.Enqueue(i); - - _Keys = new CKeys(); try { _D3D = new Direct3D(); @@ -100,25 +99,17 @@ namespace Vocaluxe.Lib.Draw CLog.LogError("No DirectX runtimes were found, please download and install them from http://www.microsoft.com/download/en/details.aspx?id=8109", true, true, e); } - Paint += _OnPaintEvent; - Closing += _OnClosingEvent; - Resize += _OnResizeEvent; + _Form.KeyDown += _OnKeyDown; + _Form.PreviewKeyDown += _OnPreviewKeyDown; + _Form.KeyPress += _OnKeyPress; + _Form.KeyUp += _OnKeyUp; - KeyDown += _OnKeyDown; - PreviewKeyDown += _OnPreviewKeyDown; - KeyPress += _OnKeyPress; - KeyUp += _OnKeyUp; - - _Mouse = new CMouse(); - MouseMove += _OnMouseMove; - MouseWheel += _OnMouseWheel; - MouseDown += _OnMouseDown; - MouseUp += _OnMouseUp; - MouseLeave += _OnMouseLeave; - MouseEnter += _OnMouseEnter; - - ClientSize = new Size(CConfig.ScreenW, CConfig.ScreenH); - _SizeBeforeMinimize = ClientSize; + _Form.MouseMove += _OnMouseMove; + _Form.MouseWheel += _OnMouseWheel; + _Form.MouseDown += _OnMouseDown; + _Form.MouseUp += _OnMouseUp; + _Form.MouseLeave += _OnMouseLeave; + _Form.MouseEnter += _OnMouseEnter; _PresentParameters = new PresentParameters { @@ -184,7 +175,11 @@ namespace Vocaluxe.Lib.Draw try { - _Device = new Device(_D3D, _D3D.Adapters.DefaultAdapter.Adapter, DeviceType.Hardware, Handle, flags, _PresentParameters); + _Device = new Device(_D3D, _D3D.Adapters.DefaultAdapter.Adapter, DeviceType.Hardware, _Form.Handle, flags, _PresentParameters); + } + catch (Exception e) + { + CLog.LogError("Error during D3D device creation.", false, false, e); } finally { @@ -195,213 +190,87 @@ namespace Vocaluxe.Lib.Draw true, true); } } - - CenterToScreen(); } - #region form events - private void _OnPaintEvent(object sender, PaintEventArgs e) {} - - private void _OnResizeEvent(object sender, EventArgs e) {} - - private void _OnClosingEvent(object sender, CancelEventArgs e) - { - _Run = false; - } - - protected override void OnLoad(EventArgs e) - { - _Device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0); - } - - protected override void OnResize(EventArgs e) - { - base.OnResize(e); - _DoResize(); - } - - protected override void WndProc(ref Message m) - { - switch (m.Msg) - { - case 0x112: // WM_SYSCOMMAND - switch ((int)m.WParam & 0xFFF0) - { - case 0xF100: // SC_KEYMENU - m.Result = IntPtr.Zero; - break; - case 0xF140: // SC_SCREENSAVER - case 0xF170: // SC_MONITORPOWER - break; - default: - base.WndProc(ref m); - break; - } - break; - default: - base.WndProc(ref m); - break; - } - } - #endregion form events - #region resize /// /// Resizes the viewport /// - private void _DoResize() + protected override void _DoResize() { // The window was minimized, so restore it to the last known size - if (ClientSize.Width == 0 || ClientSize.Height == 0) - ClientSize = _SizeBeforeMinimize; - if (!_Run) - return; - _H = ClientSize.Height; - _W = ClientSize.Width; - _Y = 0; - _X = 0; + if (_Form.ClientSize.Width == 0 || _Form.ClientSize.Height == 0) + _Form.ClientSize = _SizeBeforeMinimize; - if (_W / (float)_H > CSettings.GetRenderAspect()) - { - //The windows's width is too big - _W = (int)Math.Round(_H * CSettings.GetRenderAspect()); - _X = (ClientSize.Width - _W) / 2; - } - else - { - //The windows's height is too big - _H = (int)Math.Round(_W / CSettings.GetRenderAspect()); - _Y = (ClientSize.Height - _H) / 2; - } + if (_H == _Form.ClientSize.Height && _W == _Form.ClientSize.Width && CConfig.ScreenAlignment == _CurrentAlignment) + return; + + _CurrentAlignment = CConfig.ScreenAlignment; + _H = _Form.ClientSize.Height; + _W = _Form.ClientSize.Width; + + _AdjustAspect(false); //Apply the new sizes to the PresentParameters - _PresentParameters.BackBufferWidth = ClientSize.Width; - _PresentParameters.BackBufferHeight = ClientSize.Height; - ClearScreen(); - //To set new PresentParameters the device has to be resetted - _Reset(); - //All configurations got flushed due to Reset(), so apply them again - Init(); + _PresentParameters.BackBufferWidth = _Form.ClientSize.Width; + _PresentParameters.BackBufferHeight = _Form.ClientSize.Height; + if (_Run) + { + _ClearScreen(); + //To set new PresentParameters the device has to be resetted + _Reset(); + //All configurations got flushed due to Reset(), so apply them again + _InitDevice(); + _Device.Viewport = new Viewport(_X, _Y, _W, _H); + } - //Set the new Viewport - _Device.Viewport = new Viewport(_X, _Y, _W, _H); //Store size so it can get restored after the window gets minimized - _SizeBeforeMinimize = ClientSize; + _SizeBeforeMinimize = _Form.ClientSize; } + // ReSharper disable RedundantOverridenMember /// /// Triggers the Fullscreen mode /// - private void _EnterFullScreen() + protected override void _EnterFullScreen() { //This currently not using real fullscreen mode but a borderless window //Real fullscreen could be gained setting _PresentParameters.Windowed = true //And calling Reset() and Init() after - _OldSize = ClientSize; - - int screenNr = 0; - for (int i = 0; i < Screen.AllScreens.Length; i++) - { - Screen scr = Screen.AllScreens[i]; - if (scr.Bounds.Top <= Top && scr.Bounds.Left <= Left) - screenNr = i; - } - - ClientSize = new Size(Screen.AllScreens[screenNr].Bounds.Width, Screen.AllScreens[screenNr].Bounds.Height); - FormBorderStyle = FormBorderStyle.None; - CenterToScreen(); - _Fullscreen = true; - - if (WindowState == FormWindowState.Maximized) - { - WindowState = FormWindowState.Normal; - _DoResize(); - WindowState = FormWindowState.Maximized; - } - else - _DoResize(); + base._EnterFullScreen(); } - /// - /// Triggers the windowed mode - /// - private void _LeaveFullScreen() - { - ClientSize = _OldSize; - FormBorderStyle = FormBorderStyle.Sizable; - CenterToScreen(); - _Fullscreen = false; - } + // ReSharper restore RedundantOverridenMember #endregion resize - #region mouse event handlers - private void _OnMouseMove(object sender, MouseEventArgs e) - { - _Mouse.MouseMove(e); - } - - private void _OnMouseWheel(object sender, MouseEventArgs e) - { - _Mouse.MouseWheel(e); - } - - private void _OnMouseDown(object sender, MouseEventArgs e) - { - _Mouse.MouseDown(e); - } - - private void _OnMouseUp(object sender, MouseEventArgs e) - { - _Mouse.MouseUp(e); - } - - private void _OnMouseLeave(object sender, EventArgs e) - { - _Mouse.Visible = false; - Cursor.Show(); - } - - private void _OnMouseEnter(object sender, EventArgs e) - { - Cursor.Hide(); - _Mouse.Visible = true; - } - #endregion - - #region keyboard event handlers - private void _OnPreviewKeyDown(object sender, PreviewKeyDownEventArgs e) - { - _OnKeyDown(sender, new KeyEventArgs(e.KeyData)); - } - - private void _OnKeyDown(object sender, KeyEventArgs e) - { - _Keys.KeyDown(e); - } - - private void _OnKeyPress(object sender, KeyPressEventArgs e) - { - _Keys.KeyPress(e); - } - - private void _OnKeyUp(object sender, KeyEventArgs e) - { - _Keys.KeyUp(e); - } - #endregion keyboard event handlers - - #region implementation - #region main stuff /// /// Inits the Device /// /// True if it succeeded else false - public bool Init() + public override bool Init() { + if (!base.Init()) + return false; + if (_Device.Disposed) return false; - Text = CSettings.GetFullVersionText(); + + _InitDevice(); + + //This creates a new white texture and adds it to the texture pool + //This texture is used for the DrawRect method + using (var blankMap = new Bitmap(1, 1)) + using (Graphics g = Graphics.FromImage(blankMap)) + { + g.Clear(Color.White); + _BlankTexture = AddTexture(blankMap); + } + return true; + } + + private void _InitDevice() + { _AdjustNewBorders(); _VertexBuffer = new VertexBuffer(_Device, CSettings.VertexBufferElements * (4 * Marshal.SizeOf(typeof(STexturedColoredVertex))), Usage.WriteOnly | Usage.Dynamic, @@ -417,9 +286,9 @@ namespace Vocaluxe.Lib.Draw CLog.LogError("Failed to enable alpha blending"); if (_Device.SetRenderState(RenderState.Lighting, false).IsFailure) CLog.LogError("Failed to disable lighting"); - if (_Device.SetRenderState(RenderState.DestinationBlend, SlimDX.Direct3D9.Blend.InverseSourceAlpha).IsFailure) + if (_Device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha).IsFailure) CLog.LogError("Failed to set destination blend"); - if (_Device.SetRenderState(RenderState.SourceBlend, SlimDX.Direct3D9.Blend.SourceAlpha).IsFailure) + if (_Device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha).IsFailure) CLog.LogError("Failed to set source blend"); if (_PresentParameters.Multisample != MultisampleType.None) { @@ -457,99 +326,37 @@ namespace Vocaluxe.Lib.Draw stream.WriteRange(indices); _IndexBuffer.Unlock(); _Device.Indices = _IndexBuffer; - - //This creates a new white texture and adds it to the texture pool - //This texture is used for the DrawRect method - using (var blankMap = new Bitmap(1, 1)) - { - Graphics g = Graphics.FromImage(blankMap); - g.Clear(Color.White); - g.Dispose(); - _BlankTexture = AddTexture(blankMap); - } - return true; } - /// - /// Starts the rendering - /// - public void MainLoop() + protected override void _OnBeforeDraw() { - _Run = true; - int delay = 0; - Show(); + if (_Device.BeginScene().IsFailure) + CLog.LogError("Failed to begin scene"); + } - if (CConfig.FullScreen == EOffOn.TR_CONFIG_ON) - _EnterFullScreen(); - - //Resize window if aspect ratio is incorrect - if (Math.Abs(_W / (float)_H - CSettings.GetRenderAspect()) > 0.01) - _DoResize(); - - while (_Run) + protected override void _OnAfterDraw() + { + _RenderVertexBuffer(); + if (_Device.EndScene().IsFailure) + CLog.LogError("Failed to end scene"); + try { - Application.DoEvents(); - //Clear the previous Frame - ClearScreen(); - //We want to begin drawing - if (_Device.BeginScene().IsFailure) - CLog.LogError("Failed to begin scene"); - _CheckQueue(); - if (!CGraphics.Draw()) - _Run = false; - _RenderVertexBuffer(); - //We finished drawing the frame - if (_Device.EndScene().IsFailure) - CLog.LogError("Failed to end scene"); - if (!CGraphics.UpdateGameLogic(_Keys, _Mouse)) - _Run = false; - try - { - //Now push the frame to the Viewport - _Device.Present(); - } - catch (Direct3D9Exception) - { - //In Direct3D devices can get lost. - //This happens for example when opening the Task Manager in Vista/Windows 7 or if a UAC message is opening - //We need to reset the device to get it to workable state again - //After a reset Init() needs to be called because all data in the Direct3D default pool are lost and need to be recreated - if (_Device.TestCooperativeLevel() == ResultCode.DeviceNotReset) - { - _Reset(); - Init(); - } - } - //Apply fullscreen mode - if ((CConfig.FullScreen == EOffOn.TR_CONFIG_ON) != _Fullscreen) - { - if (!_Fullscreen) - _EnterFullScreen(); - else - _LeaveFullScreen(); - } - - //Apply border changes - if (_BorderLeft != CConfig.BorderLeft || _BorderRight != CConfig.BorderRight || _BorderTop != CConfig.BorderTop || _BorderBottom != CConfig.BorderBottom) - { - _BorderLeft = CConfig.BorderLeft; - _BorderRight = CConfig.BorderRight; - _BorderTop = CConfig.BorderTop; - _BorderBottom = CConfig.BorderBottom; - - _AdjustNewBorders(); - } - - if (CTime.IsRunning()) - delay = (int)Math.Floor(CConfig.CalcCycleTime() - CTime.GetMilliseconds()); - - if (delay >= 1 && CConfig.VSync == EOffOn.TR_CONFIG_OFF) - Thread.Sleep(delay); - //Calculate the FPS Rate and restart the timer after a frame - CTime.CalculateFPS(); - CTime.Restart(); + //Now push the frame to the Viewport + _Device.Present(); } - Close(); + catch (Direct3D9Exception) + { + //In Direct3D devices can get lost. + //This happens for example when opening the Task Manager in Vista/Windows 7 or if a UAC message is opening + //We need to reset the device to get it to workable state again + //After a reset Init() needs to be called because all data in the Direct3D default pool are lost and need to be recreated + if (_Device.TestCooperativeLevel() == ResultCode.DeviceNotReset) + { + _Reset(); + _InitDevice(); + } + } + Application.DoEvents(); } /// @@ -565,12 +372,14 @@ namespace Vocaluxe.Lib.Draw CLog.LogError("Failed to reset the device"); } - private void _AdjustNewBorders() + protected override void _AdjustNewBorders() { - SlimDX.Matrix translate = SlimDX.Matrix.Translation(new Vector3(-(float)CSettings.RenderW / 2, (float)CSettings.RenderH / 2, 0)); - SlimDX.Matrix projection = SlimDX.Matrix.OrthoOffCenterLH( - -(float)CSettings.RenderW / 2 - _BorderLeft, (float)CSettings.RenderW / 2 + _BorderRight, - -(float)CSettings.RenderH / 2 - _BorderBottom, (float)CSettings.RenderH / 2 + _BorderTop, + const float dx = (float)CSettings.RenderW / 2; + const float dy = (float)CSettings.RenderH / 2; + Matrix translate = Matrix.Translation(new Vector3(-dx, dy, 0)); + Matrix projection = Matrix.OrthoOffCenterLH( + -dx - _BorderLeft, dx + _BorderRight, + -dy - _BorderBottom, dy + _BorderTop, CSettings.ZNear, CSettings.ZFar); if (_Device.SetTransform(TransformState.Projection, projection).IsFailure) @@ -583,21 +392,10 @@ namespace Vocaluxe.Lib.Draw /// Unloads all Textures and other objects used by Direct3D for rendering /// /// - public void Unload() + public override void Unload() { - try - { - Close(); - } - catch {} - //Dispose all textures - foreach (Texture texture in _D3DTextures.Values) - { - if (texture != null) - texture.Dispose(); - } - - STexturedColoredVertex.GetDeclaration(_Device).Dispose(); + base.Unload(); + STexturedColoredVertex.DisposeDeclaration(); _VertexBuffer.Dispose(); _IndexBuffer.Dispose(); _Device.Dispose(); @@ -628,7 +426,7 @@ namespace Vocaluxe.Lib.Draw /// A TexturedColoredVertex array containg 4 vertices /// The texture the vertex should be textured with /// The vertices' rotation - private void _AddToVertexBuffer(STexturedColoredVertex[] vertices, Texture tex, SlimDX.Matrix rotation) + private void _AddToVertexBuffer(STexturedColoredVertex[] vertices, Texture tex, Matrix rotation) { //The vertexbuffer is full, so we need to flush it before we can continue if (_Vertices.Count >= CSettings.VertexBufferElements) @@ -677,7 +475,7 @@ namespace Vocaluxe.Lib.Draw /// /// Removes all textures from the screen /// - public void ClearScreen() + protected override void _ClearScreen() { if (_Device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0).IsFailure) CLog.LogError("Failed to clear the backbuffer"); @@ -687,41 +485,35 @@ namespace Vocaluxe.Lib.Draw /// Copies the current frame into a texture /// A texture holding the current frame ///
- public CTexture CopyScreen() + public CTextureRef CopyScreen() { - CTexture texture = _GetNewTexture(_W, _H); - + CD3DTexture tex = _CreateTexture(new Size(_W, _H)); Surface backbufferSurface = _Device.GetBackBuffer(0, 0); - var tex = new Texture(_Device, texture.W2, texture.H2, 0, Usage.AutoGenerateMipMap, Format.A8R8G8B8, Pool.Managed); - Surface textureSurface = tex.GetSurfaceLevel(0); + Surface textureSurface = tex.D3DTexture.GetSurfaceLevel(0); Surface.FromSurface(textureSurface, backbufferSurface, Filter.Default, 0, new Rectangle(0, 0, _W, _H), new Rectangle(0, 0, _W, _H)); backbufferSurface.Dispose(); - lock (_MutexTexture) - { - texture.ID = _IDs.Dequeue(); - _D3DTextures.Add(texture.ID, tex); - return texture; - } + return _GetTextureReference(_W, _H, tex); } /// /// Copies the current frame into a texture /// - /// The texture in which the frame is copied to - public void CopyScreen(ref CTexture texture) + /// The texture in which the frame is copied to + public void CopyScreen(ref CTextureRef textureRef) { - //Check for actual texture sizes! (W2/H2) as it may be up/downsized compared to OrigSize - if (!_TextureExists(texture) || texture.W2 != GetScreenWidth() || texture.H2 != GetScreenHeight()) + CD3DTexture texture; + if (!_GetTexture(textureRef, out texture) || texture.DataSize.Width != GetScreenWidth() || texture.DataSize.Height != GetScreenHeight()) { - RemoveTexture(ref texture); - texture = CopyScreen(); + RemoveTexture(ref textureRef); + textureRef = CopyScreen(); } else { Surface backbufferSurface = _Device.GetBackBuffer(0, 0); - Surface textureSurface = _D3DTextures[texture.ID].GetSurfaceLevel(0); + Surface textureSurface = texture.D3DTexture.GetSurfaceLevel(0); Surface.FromSurface(textureSurface, backbufferSurface, Filter.Default, 0); + backbufferSurface.Dispose(); } } @@ -741,36 +533,12 @@ namespace Vocaluxe.Lib.Draw } } - /// - /// Draws a line - /// - /// The alpha value from 0-255 - /// The red value from 0-255 - /// The red value from 0-255 - /// The red value from 0-255 - /// The width of the line - /// The start x-value - /// The start y-value - /// The end x-value - /// The end y-value - public void DrawLine(int a, int r, int g, int b, int w, int x1, int y1, int x2, int y2) - { - var lineVector = new Vector2[] {new Vector2(x1, y1), new Vector2(x2, y2)}; - using (var line = new Line(_Device)) - { - line.Antialias = true; - line.Begin(); - line.Draw(lineVector, new Color4((float)a / 255, (float)r / 255, (float)g / 255, (float)b / 255)); - line.End(); - } - } - /// /// Draws a colored rectangle /// /// The color in which the rectangle will be drawn in /// The coordinates in a SRectF struct - public void DrawColor(SColorF color, SRectF rect) + public void DrawRect(SColorF color, SRectF rect) { DrawTexture(_BlankTexture, rect, color); } @@ -782,154 +550,25 @@ namespace Vocaluxe.Lib.Draw /// The coordinates in a SRectF struct /// The space between the texture and the reflection /// The height of the reflection - public void DrawColorReflection(SColorF color, SRectF rect, float space, float height) + public void DrawRectReflection(SColorF color, SRectF rect, float space, float height) { - DrawTextureReflection(_BlankTexture, rect, color, rect, space, height); + DrawTextureReflection(_BlankTexture, rect, color, space, height); } #endregion Basic Draw Methods #region Textures - - #region adding - /// - /// Adds a texture and stores it in the VRam - /// - /// The texture's filepath - /// A STexture object containing the added texture - public CTexture AddTexture(string texturePath) + protected override CD3DTexture _CreateTexture(Size dataSize) { - if (!File.Exists(texturePath)) - { - CLog.LogError("Can't find File: " + texturePath); - return null; - } - Bitmap bmp; - try - { - bmp = new Bitmap(texturePath); - } - catch (Exception) - { - CLog.LogError("Error loading Texture: " + texturePath); - return null; - } - CTexture s; - try - { - s = AddTexture(bmp); - } - finally - { - bmp.Dispose(); - } - return s; + if (dataSize.Width < 0) + return new CD3DTexture(null, dataSize); + return new CD3DTexture(_Device, dataSize, _CheckForNextPowerOf2(dataSize.Width), _CheckForNextPowerOf2(dataSize.Height)); } - /// - /// Adds a texture and stores it in the Vram - /// - /// The Bitmap of which the texure will be created from - /// A STexture object containing the added texture - public CTexture AddTexture(Bitmap bmp) - { - if (bmp.Height == 0 || bmp.Width == 0) - return null; - int maxSize; - //Apply the right max size - switch (CConfig.TextureQuality) - { - case ETextureQuality.TR_CONFIG_TEXTURE_LOWEST: - maxSize = 128; - break; - case ETextureQuality.TR_CONFIG_TEXTURE_LOW: - maxSize = 256; - break; - case ETextureQuality.TR_CONFIG_TEXTURE_MEDIUM: - maxSize = 512; - break; - case ETextureQuality.TR_CONFIG_TEXTURE_HIGH: - maxSize = 1024; - break; - case ETextureQuality.TR_CONFIG_TEXTURE_HIGHEST: - maxSize = 2048; - break; - default: - maxSize = 512; - break; - } - - int w = Math.Min(bmp.Width, maxSize); - int h = Math.Min(bmp.Height, maxSize); - w = _CheckForNextPowerOf2(w); - h = _CheckForNextPowerOf2(h); - - var texture = new CTexture(bmp.Width, bmp.Height, w, h, true); - - Bitmap bmp2 = null; - byte[] data; - try - { - if (bmp.Width != w || bmp.Height != h) - { - //Create a new Bitmap with the new sizes - bmp2 = new Bitmap(w, h); - //Scale the texture - using (Graphics g = Graphics.FromImage(bmp2)) - { - g.InterpolationMode = InterpolationMode.HighQualityBicubic; - g.SmoothingMode = SmoothingMode.HighQuality; - g.DrawImage(bmp, new Rectangle(0, 0, bmp2.Width, bmp2.Height)); - } - bmp = bmp2; - } - - //Fill the new Bitmap with the texture data - BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); - data = new byte[4 * w * h]; - Marshal.Copy(bmpData.Scan0, data, 0, data.Length); - bmp.UnlockBits(bmpData); - } - finally - { - if (bmp2 != null) - bmp2.Dispose(); - } - - return _AddTexture(texture, w, data); - } - - public CTexture AddTexture(int w, int h, byte[] data) - { - CTexture texture = _GetNewTexture(w, h); - return _AddTexture(texture, w, data); - } - - private CTexture _AddTexture(CTexture texture, int w, byte[] data) - { - Texture t = _CreateTexture(texture.W2, texture.H2, w, data); - - lock (_MutexTexture) - { - texture.ID = _IDs.Dequeue(); - _D3DTextures.Add(texture.ID, t); - } - return texture; - } - - private Texture _CreateTexture(int textureW, int textureH, int w, byte[] data) - { - //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 - var t = new Texture(_Device, textureW, textureH, 0, Usage.AutoGenerateMipMap, Format.A8R8G8B8, Pool.Managed); - _WriteDataToTexture(t, w, data); - return t; - } - - private static void _WriteDataToTexture(Texture t, int w, byte[] data) + protected override void _WriteDataToTexture(CD3DTexture texture, byte[] data) { //Lock the texture and fill it with the data - DataRectangle rect = t.LockRectangle(0, LockFlags.Discard); - int rowWidth = 4 * w; + DataRectangle rect = texture.D3DTexture.LockRectangle(0, LockFlags.Discard); + int rowWidth = 4 * texture.DataSize.Width; if (rowWidth == rect.Pitch) rect.Data.Write(data, 0, data.Length); else @@ -941,141 +580,78 @@ namespace Vocaluxe.Lib.Draw rect.Data.Position = rect.Data.Position - rowWidth + rect.Pitch; } } - t.UnlockRectangle(0); - } - - public CTexture EnqueueTexture(int w, int h, byte[] data) - { - CTexture texture = _GetNewTexture(w, h); - - lock (_MutexTexture) - { - texture.ID = _IDs.Dequeue(); - _D3DTextures.Add(texture.ID, null); - var queue = new STextureQueue(texture.ID, texture.W2, texture.H2, w, h, data); - _TexturesToLoad.Enqueue(queue); - } - return texture; - } - - private CTexture _GetNewTexture(int w, int h) - { - return new CTexture(w, h, _CheckForNextPowerOf2(w), _CheckForNextPowerOf2(h)); - } - #endregion adding - - #region updating - /// - /// Updates the data of a texture - /// - /// The texture to update - /// - /// - /// A byte array containing the new texture's data - /// True if succeeded - public bool UpdateTexture(CTexture texture, int w, int h, byte[] data) - { - //Check if texture exists and extend matches - if (!_TextureExists(texture) || texture.UsedWidth != w || texture.UsedHeight != h) - return false; - lock (_MutexTexture) - { - _WriteDataToTexture(_D3DTextures[texture.ID], w, data); - } - return true; - } - - public bool UpdateOrAddTexture(ref CTexture texture, int w, int h, byte[] data) - { - if (!UpdateTexture(texture, w, h, data)) - { - RemoveTexture(ref texture); - texture = AddTexture(w, h, data); - } - return true; - } - #endregion updating - - /// - /// Checks if a texture exists - /// - /// The texture to check - /// True if the texture exists - private bool _TextureExists(CTexture texture) - { - lock (_MutexTexture) - { - return texture != null && _D3DTextures.ContainsKey(texture.ID); - } - } - - /// - /// Removes a texture from the Vram - /// - /// The texture to be removed - public void RemoveTexture(ref CTexture texture) - { - if (_TextureExists(texture)) - { - lock (_MutexTexture) - { - _D3DTextures[texture.ID].Dispose(); - _D3DTextures.Remove(texture.ID); - _IDs.Enqueue(texture.ID); - } - } - texture = null; + texture.D3DTexture.UnlockRectangle(0); } #region drawing /// /// Draws a texture /// - /// The texture to be drawn - public void DrawTexture(CTexture texture) - { - if (texture == null) - return; - DrawTexture(texture, texture.Rect, texture.Color); - } - - /// - /// Draws a texture - /// - /// The texture to be drawn - /// A SRectF struct containing the destination coordinates - public void DrawTexture(CTexture texture, SRectF rect) - { - if (texture == null) - return; - DrawTexture(texture, rect, texture.Color); - } - - /// - /// Draws a texture - /// - /// The texture to be drawn + /// The texture to be drawn /// A SRectF struct containing the destination coordinates /// A SColorF struct containing a color which the texture will be colored in /// True if the texture should be mirrored - public void DrawTexture(CTexture texture, SRectF rect, SColorF color, bool mirrored = false) + public void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, bool mirrored = false) { - DrawTexture(texture, rect, color, new SRectF(0, 0, CSettings.RenderW, CSettings.RenderH, rect.Z), mirrored); + if (rect.W < 1 || rect.H < 1) + return; + + CD3DTexture texture; + if (!_GetTexture(textureRef, out texture)) + return; + + //Calculate the position + const float x1 = 0; + float x2 = texture.WidthRatio; + float y1 = 0; + float y2 = texture.HeightRatio; + + //Calculate the size + float rx1 = rect.X; + float rx2 = rect.X + rect.W; + float ry1 = rect.Y; + float ry2 = rect.Y + rect.H; + + color.A *= CGraphics.GlobalAlpha; + + Color c = color.AsColor(); + + if (mirrored) + { + float tmp = y2; + y2 = y1; + y1 = tmp; + } + var vert = new STexturedColoredVertex[4]; + vert[0] = new STexturedColoredVertex(new Vector3(rx1, -ry1, rect.Z + CGraphics.ZOffset), new Vector2(x1, y1), c.ToArgb()); + vert[1] = new STexturedColoredVertex(new Vector3(rx1, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x1, y2), c.ToArgb()); + vert[2] = new STexturedColoredVertex(new Vector3(rx2, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x2, y2), c.ToArgb()); + vert[3] = new STexturedColoredVertex(new Vector3(rx2, -ry1, rect.Z + CGraphics.ZOffset), new Vector2(x2, y1), c.ToArgb()); + _AddToVertexBuffer(vert, texture.D3DTexture, _CalculateRotationMatrix(rect.Rotation, rx1, rx2, ry1, ry2)); } /// /// Draws a texture /// - /// The texture to be drawn + /// The texture to be drawn /// A SRectF struct containing the destination coordinates /// A SColorF struct containing a color which the texture will be colored in /// A SRectF struct containing which part of the texture should be drawn /// True if the texture should be mirrored - public void DrawTexture(CTexture texture, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false) + public void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false) { - if (!_TextureExists(texture)) + if (Math.Abs(rect.W) < 1 || Math.Abs(rect.H) < 1 || Math.Abs(bounds.H) < 1 || Math.Abs(bounds.W) < 1 || + Math.Abs(color.A) < 0.01) return; - if (_D3DTextures[texture.ID] == null) + + if (bounds.X > rect.X + rect.W || bounds.X + bounds.W < rect.X) + return; + + if (bounds.Y > rect.Y + rect.H || bounds.Y + bounds.H < rect.Y) + return; + + CD3DTexture texture; + if (!_GetTexture(textureRef, out texture)) return; //Calculate the position @@ -1114,58 +690,35 @@ namespace Vocaluxe.Lib.Draw if (ry2 > bounds.Y + bounds.H) ry2 = bounds.Y + bounds.H; - //Align the pixels because Direct3D expects the pixels to be the left top corner - rx1 -= 0.5f; - ry1 -= 0.5f; - rx2 -= 0.5f; - ry2 -= 0.5f; - color.A *= CGraphics.GlobalAlpha; - if (color.A > 1) - color.A = 1; - if (color.R > 1) - color.R = 1; - if (color.G > 1) - color.G = 1; - if (color.B > 1) - color.B = 1; - Color c = color.AsColor(); - if (!mirrored) + if (mirrored) { - var vert = new STexturedColoredVertex[4]; - vert[0] = new STexturedColoredVertex(new Vector3(rx1, -ry1, rect.Z + CGraphics.ZOffset), new Vector2(x1, y1), c.ToArgb()); - vert[1] = new STexturedColoredVertex(new Vector3(rx1, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x1, y2), c.ToArgb()); - vert[2] = new STexturedColoredVertex(new Vector3(rx2, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x2, y2), c.ToArgb()); - vert[3] = new STexturedColoredVertex(new Vector3(rx2, -ry1, rect.Z + CGraphics.ZOffset), new Vector2(x2, y1), c.ToArgb()); - _AddToVertexBuffer(vert, _D3DTextures[texture.ID], _CalculateRotationMatrix(rect.Rotation, rx1, rx2, ry1, ry2)); - } - else - { - var vert = new STexturedColoredVertex[4]; - vert[0] = new STexturedColoredVertex(new Vector3(rx1, -ry1, rect.Z + CGraphics.ZOffset), new Vector2(x1, -y1), c.ToArgb()); - vert[1] = new STexturedColoredVertex(new Vector3(rx1, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x1, -y2), c.ToArgb()); - vert[2] = new STexturedColoredVertex(new Vector3(rx2, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x2, -y2), c.ToArgb()); - vert[3] = new STexturedColoredVertex(new Vector3(rx2, -ry1, rect.Z + CGraphics.ZOffset), new Vector2(x2, -y1), c.ToArgb()); - _AddToVertexBuffer(vert, _D3DTextures[texture.ID], _CalculateRotationMatrix(rect.Rotation, rx1, rx2, ry1, ry2)); + y1 = -y1; + y2 = -y2; } + var vert = new STexturedColoredVertex[4]; + vert[0] = new STexturedColoredVertex(new Vector3(rx1, -ry1, rect.Z + CGraphics.ZOffset), new Vector2(x1, y1), c.ToArgb()); + vert[1] = new STexturedColoredVertex(new Vector3(rx1, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x1, y2), c.ToArgb()); + vert[2] = new STexturedColoredVertex(new Vector3(rx2, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x2, y2), c.ToArgb()); + vert[3] = new STexturedColoredVertex(new Vector3(rx2, -ry1, rect.Z + CGraphics.ZOffset), new Vector2(x2, y1), c.ToArgb()); + _AddToVertexBuffer(vert, texture.D3DTexture, _CalculateRotationMatrix(rect.Rotation, rx1, rx2, ry1, ry2)); } /// /// Draws a texture /// - /// The texture to be drawn + /// The texture to be drawn /// A SRectF struct containing the destination coordinates /// A SColorF struct containing a color which the texture will be colored in /// A Value ranging from 0 to 1 containing the beginning of the texture /// A Value ranging from 0 to 1 containing the ending of the texture - public void DrawTexture(CTexture texture, SRectF rect, SColorF color, float begin, float end) + public void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, float begin, float end) { - if (!_TextureExists(texture)) - return; - if (_D3DTextures[texture.ID] == null) + CD3DTexture texture; + if (!_GetTexture(textureRef, out texture)) return; float x1 = 0f + begin * texture.WidthRatio; @@ -1193,73 +746,39 @@ namespace Vocaluxe.Lib.Draw vert[1] = new STexturedColoredVertex(new Vector3(rx1, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x1, y2), c.ToArgb()); vert[2] = new STexturedColoredVertex(new Vector3(rx2, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x2, y2), c.ToArgb()); vert[3] = new STexturedColoredVertex(new Vector3(rx2, -ry1, rect.Z + CGraphics.ZOffset), new Vector2(x2, y1), c.ToArgb()); - _AddToVertexBuffer(vert, _D3DTextures[texture.ID], _CalculateRotationMatrix(rect.Rotation, rx1, rx2, ry1, ry2)); + _AddToVertexBuffer(vert, texture.D3DTexture, _CalculateRotationMatrix(rect.Rotation, rx1, rx2, ry1, ry2)); } /// /// Draws a reflection of a texture /// - /// The texture of which a reflection should be drawn + /// The texture of which a reflection should be drawn /// A SRectF struct containing the destination coordinates /// A SColorF struct containing a color which the texture will be colored in - /// A SRectF struct containing which part of the texture should be drawn /// The space between the texture and the reflection /// The height of the reflection - public void DrawTextureReflection(CTexture texture, SRectF rect, SColorF color, SRectF bounds, float space, float height) + public void DrawTextureReflection(CTextureRef textureRef, SRectF rect, SColorF color, float space, float height) { - if (!_TextureExists(texture)) - return; - if (_D3DTextures[texture.ID] == null) + CD3DTexture texture; + if (!_GetTexture(textureRef, out texture)) return; - if (Math.Abs(rect.W) < float.Epsilon || Math.Abs(rect.H) < float.Epsilon || Math.Abs(bounds.H) < float.Epsilon || Math.Abs(bounds.W) < float.Epsilon || - Math.Abs(color.A) < float.Epsilon || height <= 0f) + if (Math.Abs(rect.W) < float.Epsilon || Math.Abs(rect.H) < float.Epsilon || Math.Abs(color.A) < float.Epsilon || height <= 0f) return; - if (bounds.X > rect.X + rect.W || bounds.X + bounds.W < rect.X) - return; - - if (bounds.Y > rect.Y + rect.H || bounds.Y + bounds.H < rect.Y) - return; - - if (height > bounds.H) - height = bounds.H; - - float x1 = (bounds.X - rect.X) / rect.W * texture.WidthRatio; - float x2 = (bounds.X + bounds.W - rect.X) / rect.W * texture.WidthRatio; - float y1 = (bounds.Y - rect.Y + rect.H - height) / rect.H * texture.HeightRatio; - float y2 = (bounds.Y + bounds.H - rect.Y) / rect.H * texture.HeightRatio; - - if (x1 < 0) - x1 = 0f; - - if (x2 > texture.WidthRatio) - x2 = texture.WidthRatio; - - if (y1 < 0) - y1 = 0f; - - if (y2 > texture.HeightRatio) - y2 = texture.HeightRatio; + if (height > rect.H) + height = rect.H; + const float x1 = 0; + float x2 = texture.WidthRatio; + float y1 = (rect.H - height) / rect.H * texture.HeightRatio; + float y2 = texture.HeightRatio; float rx1 = rect.X; float rx2 = rect.X + rect.W; float ry1 = rect.Y + rect.H + space; float ry2 = rect.Y + rect.H + space + height; - if (rx1 < bounds.X) - rx1 = bounds.X; - - if (rx2 > bounds.X + bounds.W) - rx2 = bounds.X + bounds.W; - - if (ry1 < bounds.Y + space) - ry1 = bounds.Y + space; - - if (ry2 > bounds.Y + bounds.H + space + height) - ry2 = bounds.Y + bounds.H + space + height; - //Align the pixels because Direct3D expects the pixels to be the left top corner rx1 -= 0.5f; ry1 -= 0.5f; @@ -1276,79 +795,35 @@ namespace Vocaluxe.Lib.Draw vert[1] = new STexturedColoredVertex(new Vector3(rx1, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x1, y1), transparent.ToArgb()); vert[2] = new STexturedColoredVertex(new Vector3(rx2, -ry2, rect.Z + CGraphics.ZOffset), new Vector2(x2, y1), transparent.ToArgb()); vert[3] = new STexturedColoredVertex(new Vector3(rx2, -ry1, rect.Z + CGraphics.ZOffset), new Vector2(x2, y2), c.ToArgb()); - _AddToVertexBuffer(vert, _D3DTextures[texture.ID], _CalculateRotationMatrix(rect.Rotation, rx1, rx2, ry1, ry2)); + _AddToVertexBuffer(vert, texture.D3DTexture, _CalculateRotationMatrix(rect.Rotation, rx1, rx2, ry1, ry2)); } #endregion drawing - private void _CheckQueue() - { - lock (_MutexTexture) - { - while (_TexturesToLoad.Count > 0) - { - STextureQueue q = _TexturesToLoad.Dequeue(); - Texture t; - if (_D3DTextures.TryGetValue(q.ID, out t) && t != null) - continue; - - t = _CreateTexture(q.TextureWidth, q.TextureHeight, q.DataWidth, q.Data); - _D3DTextures[q.ID] = t; - } - } - } - - /// - /// Gets the count of current textures - /// - /// The amount of textures - public int GetTextureCount() - { - return _D3DTextures.Count; - } #endregion Textures - #endregion implementation - - #region utility - /// - /// Calculates the next power of two if the device has the POW2 flag set - /// - /// The value of which the next power of two will be calculated - /// The next power of two - private int _CheckForNextPowerOf2(int n) + private static Matrix _CalculateRotationMatrix(float rot, float rx1, float rx2, float ry1, float ry2) { - if (_NonPowerOf2TextureSupported) - return n; - if (n < 0) - throw new ArgumentOutOfRangeException("n", "Must be positive."); - return (int)Math.Pow(2, Math.Ceiling(Math.Log(n, 2))); - } - - private SlimDX.Matrix _CalculateRotationMatrix(float rot, float rx1, float rx2, float ry1, float ry2) - { - SlimDX.Matrix originTranslation = SlimDX.Matrix.Translation(new Vector3(-(float)CSettings.RenderW / 2, (float)CSettings.RenderH / 2, 0)); + Matrix originTranslation = Matrix.Translation(new Vector3(-(float)CSettings.RenderW / 2, (float)CSettings.RenderH / 2, 0)); if (Math.Abs(rot) > float.Epsilon) { float rotation = rot * (float)Math.PI / 180; float centerX = (rx1 + rx2) / 2f; float centerY = -(ry1 + ry2) / 2f; - SlimDX.Matrix translationA = SlimDX.Matrix.Translation(-centerX, -centerY, 0); - SlimDX.Matrix rotationMat = SlimDX.Matrix.RotationZ(-rotation); - SlimDX.Matrix translationB = SlimDX.Matrix.Translation(centerX, centerY, 0); + Matrix translationA = Matrix.Translation(-centerX, -centerY, 0); + Matrix rotationMat = Matrix.RotationZ(-rotation); + Matrix translationB = Matrix.Translation(centerX, centerY, 0); //Multiplicate the matrices to get the real world matrix, //First shift the texture into the center //Rotate it and shift it back to the origin position //Apply the originTranslation after - SlimDX.Matrix result = translationA * rotationMat * translationB * originTranslation; + Matrix result = translationA * rotationMat * translationB * originTranslation; return result; } return originTranslation; } - #endregion utility - #region TexturedColoredVertex private struct STexturedColoredVertex { private static VertexDeclaration _Declaration; @@ -1380,7 +855,15 @@ namespace Vocaluxe.Lib.Draw return _Declaration; } + + public static void DisposeDeclaration() + { + if (_Declaration != null && !_Declaration.Disposed) + { + _Declaration.Dispose(); + _Declaration = null; + } + } } - #endregion TexturedColoredVertex } } \ No newline at end of file diff --git a/Vocaluxe/Lib/Draw/CDrawBase.cs b/Vocaluxe/Lib/Draw/CDrawBase.cs new file mode 100644 index 00000000..3a8eeed2 --- /dev/null +++ b/Vocaluxe/Lib/Draw/CDrawBase.cs @@ -0,0 +1,1004 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Vocaluxe.Base; +using VocaluxeLib; +using VocaluxeLib.Draw; +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 of existing textures 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, + Delete + } + + private struct STextureQueue + { + public readonly object TextureOrRef; + /// + /// Only valid if Data is byte[] + /// + public readonly Size DataSize; + public readonly object Data; + public readonly EQueueAction Action; + + /// + /// Creates a new Queue entry + /// + /// TTextureType or CTextureRef + /// + /// Texture data as a bitmap + public STextureQueue(object textureOrRef, EQueueAction action, Bitmap data) + { + TextureOrRef = textureOrRef; + Action = action; + Data = data; + DataSize = new Size(); + } + + /// + /// Creates a new Queue entry + /// + /// TTextureType or CTextureRef + /// + /// Size of data + /// Texture data + public STextureQueue(object textureOrRef, EQueueAction action, Size dataSize, byte[] data) + { + TextureOrRef = textureOrRef; + Action = action; + DataSize = dataSize; + Data = data; + } + } + + private struct STextureCacheEntry + { + public TTextureType Texture; + /// + /// Size of original image (e.g. of bmp) + /// + public Size OrigSize; + } + + protected bool _NonPowerOf2TextureSupported; + protected bool _Fullscreen; + + protected bool _Run; + + protected readonly CKeys _Keys = new CKeys(); + protected readonly CMouse _Mouse = new CMouse(); + + 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) + private readonly Dictionary _TextureCache = new Dictionary(); + private readonly Queue _TextureQueue = new Queue(); + private readonly Dictionary> _BitmapsLoading = new Dictionary>(); + private int _TextureCount; + + protected int _H; + protected int _W; + protected int _Y; + protected int _X; + + protected int _BorderLeft; + protected int _BorderRight; + protected int _BorderTop; + protected int _BorderBottom; + protected EGeneralAlignment _CurrentAlignment = EGeneralAlignment.Middle; + + private int _MainThreadID; + + protected abstract void _ClearScreen(); + protected abstract void _AdjustNewBorders(); + protected abstract void _LeaveFullScreen(); + + protected abstract void _OnAfterDraw(); + + protected abstract void _OnBeforeDraw(); + + protected abstract void _DoResize(); + + 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 + foreach (TTextureType texture in _Textures.Values) + { + if (texture != null && --texture.RefCount <= 0) + texture.Dispose(); + } + _Textures.Clear(); + _TextureCache.Clear(); + } + } + + protected void _AdjustAspect(bool reverse) + { + if (_W / (float)_H > CSettings.GetRenderAspect()) + { + _Y = 0; + //The windows width is too big + int old = _W; + _W = (int)Math.Round(_H * CSettings.GetRenderAspect()); + int diff = old - _W; + switch (_CurrentAlignment) + { + case EGeneralAlignment.Start: + _X = 0; + break; + case EGeneralAlignment.Middle: + _X = diff / 2; + break; + case EGeneralAlignment.End: + _X = diff; + break; + } + } + else + { + _X = 0; + //The windows height is too big + int old = _H; + _H = (int)Math.Round(_W / CSettings.GetRenderAspect()); + int diff = old - _H; + switch (_CurrentAlignment) + { + case EGeneralAlignment.Start: + _Y = reverse ? diff : 0; + break; + case EGeneralAlignment.Middle: + _Y = diff / 2; + break; + case EGeneralAlignment.End: + _Y = reverse ? 0 : diff; + break; + } + } + } + + /// + /// Calculates the next power of two if the device has the POW2 flag set + /// + /// The value of which the next power of two will be calculated + /// The next power of two + protected int _CheckForNextPowerOf2(int n) + { + if (_NonPowerOf2TextureSupported) + return n; + if (n < 0) + throw new ArgumentOutOfRangeException("n", "Must be positive."); + return (int)Math.Pow(2, Math.Ceiling(Math.Log(n, 2))); + } + + #region Textures + + #region private/protected + /// + /// Returns the maximum area allowed for a texture based on the current quality + /// + /// + private static int _GetMaxTextureArea() + { + switch (CConfig.TextureQuality) + { + case ETextureQuality.TR_CONFIG_TEXTURE_LOWEST: + return 128 * 128; + case ETextureQuality.TR_CONFIG_TEXTURE_LOW: + return 256 * 128; + case ETextureQuality.TR_CONFIG_TEXTURE_MEDIUM: + return 512 * 256; + case ETextureQuality.TR_CONFIG_TEXTURE_HIGH: + return 1024 * 512; + case ETextureQuality.TR_CONFIG_TEXTURE_HIGHEST: + return CSettings.RenderW * CSettings.RenderH; + default: + throw new ArgumentException(); + } + } + + /// + /// 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 + /// + /// New size for the texture + private Size _GetNewTextureSize(Size size) + { + Debug.Assert(size.Width > 0 && size.Height > 0); + int maxArea = _GetMaxTextureArea(); + 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) + { + if (size.Width < size.Height) + { + newSize.Width = _CheckForNextPowerOf2(newSize.Width); + newSize.Height = _CheckForNextPowerOf2(maxArea / newSize.Width); + if (newSize.Width * newSize.Height > maxArea) + newSize.Height /= 2; + } + else + { + newSize.Height = _CheckForNextPowerOf2(newSize.Height); + newSize.Width = _CheckForNextPowerOf2(maxArea / newSize.Height); + if (newSize.Width * newSize.Height > maxArea) + newSize.Width /= 2; + } + } + Debug.Assert(newSize.Width * newSize.Height <= maxArea); + return newSize; + } + + protected abstract void _WriteDataToTexture(TTextureType texture, byte[] data); + + protected virtual void _WriteDataToTexture(TTextureType texture, IntPtr data) + { + byte[] dataArray = new byte[4 * texture.DataSize.Width * texture.DataSize.Height]; + Marshal.Copy(data, dataArray, 0, dataArray.Length); + _WriteDataToTexture(texture, dataArray); + } + + /// + /// Factory method to create a texture of the actual type
+ /// The implementer has to create an invalid texture (IsLoaded=false) if dataSize=(-1,-1) + ///
+ /// + /// + protected abstract TTextureType _CreateTexture(Size dataSize); + + /// + /// Creates the texture specified by the reference and fills it with the given data + /// + /// + /// + /// + private TTextureType _CreateAndFillTexture(Size dataSize, byte[] data) + { + TTextureType texture = _CreateTexture(dataSize); + _WriteDataToTexture(texture, data); + return texture; + } + + /// + /// Writes the specified bitmap to the texture, creating it if null
+ /// Also does the resizing if required + ///
+ /// + /// + private void _WriteBitmapToTexture(ref TTextureType texture, Bitmap bmp) + { + Bitmap bmp2 = null; + try + { + Size size = _GetNewTextureSize(bmp.GetSize()); + if (!size.Equals(bmp.GetSize())) + { + bmp2 = bmp.Resize(size); + bmp = bmp2; + } + + //Fill the new Bitmap with the texture data + BitmapData bmpData = bmp.LockBits(bmp.GetRect(), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + if (texture == null) + texture = _CreateTexture(size); + else + texture.DataSize = size; + _WriteDataToTexture(texture, bmpData.Scan0); + bmp.UnlockBits(bmpData); + } + finally + { + if (bmp2 != null) + bmp2.Dispose(); + } + } + + /// + /// Adds the texture to the cache
+ /// Asserts that the cache entry does not exist yet. So make Get/Add cache atomic through use of _TextureCache lock!
+ /// Thread safe + ///
+ /// + /// + /// + private void _AddToCache(TTextureType texture, Size origSize, string 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}; + _TextureCache.Add(texturePath, cacheEntry); + } + } + + /// + /// Gets a new texture reference from the cache
+ /// Thread safe + ///
+ /// + /// Task that loads the texture or null if not loading + /// + private CTextureRef _GetFromCache(string texturePath, out Task loader) + { + loader = null; + if (String.IsNullOrEmpty(texturePath)) + return null; + lock (_TextureCache) + lock (_Textures) + { + STextureCacheEntry cacheEntry; + if (_TextureCache.TryGetValue(texturePath, out cacheEntry)) + { + if (cacheEntry.Texture.RefCount <= 0) + _TextureCache.Remove(texturePath); + else + { + if (cacheEntry.OrigSize.Width < 0) + { + lock (_BitmapsLoading) + { + loader = _BitmapsLoading[texturePath]; + } + } + return _GetTextureReference(cacheEntry.OrigSize, cacheEntry.Texture); + } + } + } + return null; + } + + /// + /// 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 + ///
+ /// + /// + /// + 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); + 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
+ /// Thread safe
+ /// Convenience method + ///
+ /// + /// + /// + /// + protected CTextureRef _GetTextureReference(int origWidth, int origHeight, TTextureType texture) + { + return _GetTextureReference(new Size(origWidth, origHeight), texture); + } + + private static bool _IsTextureUsable(TTextureType texture, int dataWidth, int dataHeight) + { + if (texture == null) + return false; + if (texture.DataSize.Width != dataWidth || texture.DataSize.Height != dataHeight) + { + if (texture.W2 > dataWidth || texture.H2 > dataHeight) + return false; // Texture memory to small + if (texture.W2 * 0.9 < dataWidth || texture.H2 * 0.9 < dataHeight) + return false; // Texture memory to big + } + return true; + } + + protected bool _GetTexture(CTextureRef textureRef, out TTextureType texture, bool checkDrawable = true) + { + if (textureRef == null) + { + texture = null; + return false; + } + lock (_Textures) + { + return _Textures.TryGetValue(textureRef.ID, out texture) && (!checkDrawable || texture.IsLoaded); + } + } + + /// + /// Decreases the RefCount of a texture and disposes it if necessary + /// + /// + private void _DisposeTexture(TTextureType texture) + { + _EnsureMainThread(); + lock (_Textures) + { + if (texture != null && --texture.RefCount <= 0) + { + if (texture.TexturePath != null) + _TextureCache.Remove(texture.TexturePath); + texture.Dispose(); + _TextureCount--; + } + } + } + + private void _CheckQueue() + { + _EnsureMainThread(); + lock (_TextureQueue) + { + while (_TextureQueue.Count > 0) + { + STextureQueue q = _TextureQueue.Dequeue(); + + if (q.Action == EQueueAction.Add) + { + TTextureType oldTexture = q.TextureOrRef as TTextureType; + Debug.Assert(oldTexture != null, "Queued type is wrong"); + Debug.Assert(!oldTexture.IsLoaded); + if (oldTexture.RefCount <= 0) + continue; + TTextureType texture = null; + // 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 + throw new ArgumentException("q.Data is of invalid type"); + // ReSharper restore CanBeReplacedWithTryCastAndCheckForNull + _MergeTextures(oldTexture, texture); + } + else + { + CTextureRef textureRef = q.TextureOrRef as CTextureRef; + Debug.Assert(textureRef != null, "Queued type is wrong"); + if (!_Textures.ContainsKey(textureRef.ID)) + continue; + if (q.Action == EQueueAction.Update) + { + // 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 + 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 + /// + /// + /// + private void _MergeTextures(TTextureType oldTexture, TTextureType newTexture) + { + _EnsureMainThread(); + Debug.Assert(oldTexture.RefCount > 0); + lock (_Textures) + { + newTexture.RefCount += oldTexture.RefCount; + IEnumerable oldKeys = _Textures.Where(pair => pair.Value == oldTexture).Select(pair => pair.Key).ToArray(); + foreach (int key in oldKeys) + _Textures[key] = newTexture; + if (!String.IsNullOrEmpty(oldTexture.TexturePath)) + { + newTexture.TexturePath = oldTexture.TexturePath; + STextureCacheEntry cacheEntry; + if (_TextureCache.TryGetValue(newTexture.TexturePath, out cacheEntry)) + { + cacheEntry.Texture = newTexture; + _TextureCache[newTexture.TexturePath] = cacheEntry; + } + } + oldTexture.Dispose(); + } + } + #endregion private/protected + + /// + /// Adds a texture and stores it in the VRam + /// + /// The texture's filepath + /// A STexture object containing the added texture + public CTextureRef AddTexture(string texturePath) + { + _EnsureMainThread(); + CTextureRef textureRef; + Task loader; + lock (_TextureCache) + { + textureRef = _GetFromCache(texturePath, out loader); + } + if (textureRef != null) + { + if (loader != null) + textureRef.OrigSize = loader.Result; + Debug.Assert(textureRef.OrigSize.Width > 0); + return textureRef; + } + + Bitmap bmp = CHelper.LoadBitmap(texturePath); + if (bmp == null) + return null; + try + { + textureRef = AddTexture(bmp, texturePath); + } + finally + { + bmp.Dispose(); + } + return textureRef; + } + + public CTextureRef AddTexture(Bitmap bmp) + { + _EnsureMainThread(); + return AddTexture(bmp, null); + } + + /// + /// Adds a texture and stores it in the Vram + /// + /// The Bitmap of which the texure will be created from + /// + /// 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; + Task loader; + // Make the Get/Add Cache methods atomic + lock (_TextureCache) + { + textureRef = _GetFromCache(texturePath, out loader); + if (textureRef == null) + { + _WriteBitmapToTexture(ref texture, bmp); + + _AddToCache(texture, origSize, texturePath); + } + } + if (textureRef == null) + textureRef = _GetTextureReference(origSize, texture); + else if (loader != null) + textureRef.OrigSize = loader.Result; + 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 (_TextureQueue) + { + TTextureType texture = _CreateTexture(new Size(-1, -1)); + CTextureRef textureRef = _GetTextureReference(w, h, texture); + _TextureQueue.Enqueue(new STextureQueue(texture, EQueueAction.Add, new Size(w, h), data)); + return textureRef; + } + } + + /// + /// Enqueues a bitmap to add or update the texture + /// + /// TTextureType (Add) or CTextureRef(Update) + /// + /// + /// True if resizing should be done in an extra thread + private void _EnqueueTextureAddOrUpdate(Object texture, Bitmap bmp, EQueueAction action, bool asyncResize) + { + Debug.Assert(action == EQueueAction.Add || action == EQueueAction.Update); + Debug.Assert(action != EQueueAction.Add || texture is TTextureType); + Debug.Assert(action != EQueueAction.Update || texture is CTextureRef); + if (_RequiresResize(bmp.GetSize())) + { + if (asyncResize) + Task.Factory.StartNew(() => _ResizeTextureAndEnqueue(texture, bmp, action)); + else + _ResizeTextureAndEnqueue(texture, bmp, action); + } + else + { + lock (_TextureQueue) + { + _TextureQueue.Enqueue(new STextureQueue(texture, action, bmp)); + } + } + } + + /// + /// Helper function to resize a bitmap and enqueue it + /// + /// + /// + /// + private void _ResizeTextureAndEnqueue(object texture, Bitmap bmp, EQueueAction action) + { + Bitmap bmp2 = bmp.Resize(_GetNewTextureSize(bmp.Size)); + bmp.Dispose(); + lock (_TextureQueue) + { + _TextureQueue.Enqueue(new STextureQueue(texture, action, bmp2)); + } + } + + public CTextureRef EnqueueTexture(Bitmap bmp) + { + TTextureType texture = _CreateTexture(new Size(-1, -1)); + CTextureRef textureRef = _GetTextureReference(bmp.GetSize(), texture); + _EnqueueTextureAddOrUpdate(texture, bmp, EQueueAction.Add, true); + + 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; + // Make Get/Add cache atomic + lock (_TextureCache) + { + textureRef = _GetFromCache(filePath, out loader); + if (textureRef == null) + { + Debug.Assert(loader == 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, texture)); + lock (_BitmapsLoading) + { + _BitmapsLoading.Add(filePath, loader); + } + } + } + + if (texture == null) + { + // Found in cache + CTextureRef tmp = textureRef; // Workaround for implicitly captured closure warning (false positive) + if (loader != null) + Task.Factory.StartNew(() => tmp.OrigSize = loader.Result); + 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, TTextureType texture) + { + Bitmap bmp = CHelper.LoadBitmap(filePath); + if (bmp == null) + { + RemoveTexture(ref textureRef); // Done asynchonously in the function + lock (_Textures) + { + _TextureCache.Remove(filePath); + lock (_BitmapsLoading) + { + _BitmapsLoading.Remove(filePath); + } + } + return new Size(-1, -1); + } + Size 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; + } + lock (_BitmapsLoading) + { + _BitmapsLoading.Remove(filePath); + } + } + _EnqueueTextureAddOrUpdate(texture, bmp, EQueueAction.Add, false); + return origSize; + } + + public void EnqueueTextureUpdate(CTextureRef textureRef, Bitmap bmp) + { + _EnqueueTextureAddOrUpdate(textureRef, bmp, EQueueAction.Update, true); + } + + /// + /// Updates the data of a texture + /// + /// The texture to update + /// + /// + /// A byte array containing the new texture's data + /// True if succeeded + public void UpdateTexture(CTextureRef textureRef, int w, int h, byte[] data) + { + _EnsureMainThread(); + TTextureType texture; + if (!_GetTexture(textureRef, out texture, false)) + return; + bool reuseTexture = _IsTextureUsable(texture, w, h); + if (reuseTexture && texture.RefCount == 1) + { + texture.DataSize = new Size(w, h); + _WriteDataToTexture(texture, data); + } + else + { + _DisposeTexture(texture); + texture = _CreateAndFillTexture(new Size(w, h), data); + texture.RefCount = 1; + _Textures[textureRef.ID] = texture; + } + } + + public void UpdateTexture(CTextureRef textureRef, Bitmap bmp) + { + _EnsureMainThread(); + TTextureType texture; + if (!_GetTexture(textureRef, out texture, false)) + return; + bool reuseTexture = _IsTextureUsable(texture, bmp.Width, bmp.Height); + if (reuseTexture && texture.RefCount == 1) + _WriteBitmapToTexture(ref texture, bmp); + else + { + _DisposeTexture(texture); + texture = null; + _WriteBitmapToTexture(ref texture, bmp); + texture.RefCount = 1; + _Textures[textureRef.ID] = texture; + } + } + + public CTextureRef CopyTexture(CTextureRef textureRef) + { + _EnsureMainThread(); + TTextureType texture; + if (!_GetTexture(textureRef, out texture, false)) + return null; + // If bitmap is not yet loaded, wait for it + if (!String.IsNullOrEmpty(texture.TexturePath)) + { + Task loader; + lock (_BitmapsLoading) + { + _BitmapsLoading.TryGetValue(texture.TexturePath, out loader); + } + if (loader != null) + textureRef.OrigSize = loader.Result; + } + Debug.Assert(textureRef.OrigSize.Width > 0); + return _GetTextureReference(textureRef.OrigSize, texture); + } + + /// + /// Removes a texture from the Vram + /// + /// The texture to be removed + public void RemoveTexture(ref CTextureRef textureRef) + { + if (textureRef == null) + return; + if (textureRef.ID > 0) + { + if (_MainThreadID != Thread.CurrentThread.ManagedThreadId) + { + lock (_TextureQueue) + { + _TextureQueue.Enqueue(new STextureQueue(textureRef, EQueueAction.Delete, null)); + } + textureRef = null; + return; + } + lock (_Textures) + { + TTextureType t; + if (_Textures.TryGetValue(textureRef.ID, out t)) + { + _DisposeTexture(t); + _Textures.Remove(textureRef.ID); + } + textureRef.SetRemoved(); + } + } + textureRef = null; + } + + public int GetTextureCount() + { + return _TextureCount; + } + #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; + if (CConfig.FullScreen == EOffOn.TR_CONFIG_ON) + _EnterFullScreen(); + else + _DoResize(); //Resize window if aspect ratio is incorrect + + while (_Run) + { + _CheckQueue(); + + //We want to begin drawing + _OnBeforeDraw(); + + //Clear the previous Frame + _ClearScreen(); + if (!CGraphics.Draw()) + _Run = false; + _OnAfterDraw(); + + if (!CGraphics.UpdateGameLogic(_Keys, _Mouse)) + _Run = false; + + //Apply fullscreen mode + if ((CConfig.FullScreen == EOffOn.TR_CONFIG_ON) != _Fullscreen) + _ToggleFullScreen(); + + //Apply border changes + if (_BorderLeft != CConfig.BorderLeft || _BorderRight != CConfig.BorderRight || _BorderTop != CConfig.BorderTop || _BorderBottom != CConfig.BorderBottom) + { + _BorderLeft = CConfig.BorderLeft; + _BorderRight = CConfig.BorderRight; + _BorderTop = CConfig.BorderTop; + _BorderBottom = CConfig.BorderBottom; + + _AdjustNewBorders(); + } + + if (_CurrentAlignment != CConfig.ScreenAlignment) + _DoResize(); + + if (CConfig.VSync == EOffOn.TR_CONFIG_OFF) + { + if (CTime.IsRunning()) + { + int delay = (int)Math.Floor(CConfig.CalcCycleTime() - CTime.GetMilliseconds()); + + if (delay >= 1 && delay < 500) + Thread.Sleep(delay); + } + } + //Calculate the FPS Rate and restart the timer after a frame + CTime.CalculateFPS(); + CTime.Restart(); + } + } + + private void _ToggleFullScreen() + { + if (!_Fullscreen) + _EnterFullScreen(); + else + _LeaveFullScreen(); + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Draw/CDrawBaseWindows.cs b/Vocaluxe/Lib/Draw/CDrawBaseWindows.cs new file mode 100644 index 00000000..8ac494e8 --- /dev/null +++ b/Vocaluxe/Lib/Draw/CDrawBaseWindows.cs @@ -0,0 +1,198 @@ +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Windows.Forms; +using Vocaluxe.Base; + +namespace Vocaluxe.Lib.Draw +{ + public delegate bool MessageEventHandler(ref Message m); + + public interface IFormHook + { + MessageEventHandler OnMessage { set; } + } + + abstract class CDrawBaseWindows : CDrawBase where TTextureType : CTextureBase, IDisposable + { + private struct SClientRect + { + public Point Location; + public int Width; + public int Height; + } + + protected Form _Form; + private SClientRect _Restore; + protected Size _SizeBeforeMinimize; + + public override void Unload() + { + base.Unload(); + try + { + _Form.Close(); + } + catch {} + } + + protected void _CenterToScreen() + { + Screen screen = Screen.FromControl(_Form); + _Form.Location = new Point((screen.WorkingArea.Width - _Form.Width) / 2, + (screen.WorkingArea.Height - _Form.Height) / 2); + } + + private static bool _OnMessageAvoidScreenOff(ref Message m) + { + switch (m.Msg) + { + case 0x112: // WM_SYSCOMMAND + switch ((int)m.WParam & 0xFFF0) + { + case 0xF100: // SC_KEYMENU + m.Result = IntPtr.Zero; + return false; + case 0xF140: // SC_SCREENSAVER + case 0xF170: // SC_MONITORPOWER + return false; + } + break; + } + return true; + } + + protected override void _EnterFullScreen() + { + Debug.Assert(!_Fullscreen); + _Fullscreen = true; + + _Restore.Location = _Form.Location; + _Restore.Width = _Form.Width; + _Restore.Height = _Form.Height; + + _Form.FormBorderStyle = FormBorderStyle.None; + + Screen screen = Screen.FromControl(_Form); + _Form.DesktopBounds = new Rectangle(screen.Bounds.Location, new Size(screen.Bounds.Width, screen.Bounds.Height)); + + if (_Form.WindowState == FormWindowState.Maximized) + { + _Form.WindowState = FormWindowState.Normal; + _DoResize(); + _Form.WindowState = FormWindowState.Maximized; + } + else + _DoResize(); + } + + protected override void _LeaveFullScreen() + { + Debug.Assert(_Fullscreen); + _Fullscreen = false; + + _Form.FormBorderStyle = FormBorderStyle.Sizable; + _Form.DesktopBounds = new Rectangle(_Restore.Location, new Size(_Restore.Width, _Restore.Height)); + } + + #region form event handlers + private void _OnClose(object sender, CancelEventArgs e) + { + _Run = false; + } + + private void _OnLoad(object sender, EventArgs e) + { + _ClearScreen(); + } + + protected virtual void _OnResize(object sender, EventArgs e) + { + _DoResize(); + } + + #region mouse event handlers + protected void _OnMouseMove(object sender, MouseEventArgs e) + { + _Mouse.MouseMove(e); + } + + protected void _OnMouseWheel(object sender, MouseEventArgs e) + { + _Mouse.MouseWheel(e); + } + + protected void _OnMouseDown(object sender, MouseEventArgs e) + { + _Mouse.MouseDown(e); + } + + protected void _OnMouseUp(object sender, MouseEventArgs e) + { + _Mouse.MouseUp(e); + } + + protected void _OnMouseLeave(object sender, EventArgs e) + { + _Mouse.Visible = false; + Cursor.Show(); + } + + protected void _OnMouseEnter(object sender, EventArgs e) + { + Cursor.Hide(); + _Mouse.Visible = true; + } + #endregion + + #region keyboard event handlers + protected void _OnPreviewKeyDown(object sender, PreviewKeyDownEventArgs e) + { + _OnKeyDown(sender, new KeyEventArgs(e.KeyData)); + } + + protected void _OnKeyDown(object sender, KeyEventArgs e) + { + _Keys.KeyDown(e); + } + + protected void _OnKeyPress(object sender, KeyPressEventArgs e) + { + _Keys.KeyPress(e); + } + + protected void _OnKeyUp(object sender, KeyEventArgs e) + { + _Keys.KeyUp(e); + } + #endregion keyboard event handlers + + #endregion + + 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; + _Form.Closing += _OnClose; + _Form.Resize += _OnResize; + _Form.Load += _OnLoad; + + _SizeBeforeMinimize = _Form.ClientSize; + _CenterToScreen(); + + return true; + } + + public override void MainLoop() + { + _Form.Show(); + base.MainLoop(); + _Form.Hide(); + } + } +} \ No newline at end of file diff --git a/Vocaluxe/Lib/Draw/CDrawWinForm.cs b/Vocaluxe/Lib/Draw/CDrawWinForm.cs index 08720117..0c9cb5c4 100644 --- a/Vocaluxe/Lib/Draw/CDrawWinForm.cs +++ b/Vocaluxe/Lib/Draw/CDrawWinForm.cs @@ -44,7 +44,8 @@ namespace Vocaluxe.Lib.Draw private bool _TopMost; private Rectangle _Bounds; - private readonly Dictionary _Textures = new Dictionary(); + private readonly Dictionary _Textures = new Dictionary(); + private readonly Dictionary _TextureCache = new Dictionary(); private readonly List _Bitmaps = new List(); private readonly Color _ClearColor = Color.DarkBlue; @@ -64,7 +65,6 @@ namespace Vocaluxe.Lib.Draw _G = Graphics.FromImage(_Backbuffer); _G.Clear(Color.DarkBlue); - Paint += _OnPaintEvent; Closing += _OnClosingEvent; KeyDown += _OnKeyDownEvent; KeyPress += _OnKeyPressEvent; @@ -107,8 +107,6 @@ namespace Vocaluxe.Lib.Draw frontBuffer.DrawImage(_Backbuffer, new Rectangle(x, y, w, h), new Rectangle(0, 0, _Backbuffer.Width, _Backbuffer.Height), GraphicsUnit.Pixel); } - private void _OnPaintEvent(object sender, PaintEventArgs e) {} - #region FullScreenStuff private void _ToggleFullScreen() { @@ -234,6 +232,7 @@ namespace Vocaluxe.Lib.Draw if (_Run) { + ClearScreen(); _Run = _Run && CGraphics.Draw(); _Run = CGraphics.UpdateGameLogic(_Keys, _Mouse); _FlipBuffer(); @@ -279,11 +278,11 @@ namespace Vocaluxe.Lib.Draw _G.Clear(_ClearColor); } - public CTexture CopyScreen() + public CTextureRef CopyScreen() { var bmp = new Bitmap(_Backbuffer); _Bitmaps.Add(bmp); - CTexture texture = _GetNewTexture(bmp.Width, bmp.Height); + CTextureRef texture = _GetNewTexture(bmp.GetSize()); // Add to Texture List _Textures[texture.ID] = texture; @@ -291,9 +290,9 @@ namespace Vocaluxe.Lib.Draw return texture; } - public void CopyScreen(ref CTexture texture) + public void CopyScreen(ref CTextureRef texture) { - if (!_TextureExists(texture) || texture.W2 != GetScreenWidth() || texture.H2 != GetScreenHeight()) + if (!_TextureExists(texture) || texture.OrigSize.Width != GetScreenWidth() || texture.OrigSize.Height != GetScreenHeight()) { RemoveTexture(ref texture); texture = CopyScreen(); @@ -309,20 +308,15 @@ namespace Vocaluxe.Lib.Draw _Backbuffer.Save(file, ImageFormat.Png); } - public void DrawLine(int a, int r, int g, int b, int w, int x1, int y1, int x2, int y2) - { - _G.DrawLine(new Pen(Color.FromArgb(a, r, g, b), w), new Point(x1, y1), new Point(x2, y2)); - } + public void DrawRect(SColorF color, SRectF rect) {} - public void DrawColor(SColorF color, SRectF rect) {} + public void DrawRectReflection(SColorF color, SRectF rect, float space, float height) {} - public void DrawColorReflection(SColorF color, SRectF rect, float space, float height) {} - - public CTexture AddTexture(Bitmap bmp) + public CTextureRef AddTexture(Bitmap bmp) { var bmp2 = new Bitmap(bmp); _Bitmaps.Add(bmp2); - CTexture texture = _GetNewTexture(bmp.Width, bmp.Height); + CTextureRef texture = _GetNewTexture(bmp.GetSize()); // Add to Texture List _Textures[texture.ID] = texture; @@ -330,62 +324,84 @@ namespace Vocaluxe.Lib.Draw return texture; } - private CTexture _GetNewTexture(int w, int h) + private CTextureRef _GetNewTexture(Size size) { - return new CTexture(w, h) {ID = _Bitmaps.Count - 1}; + return new CTextureRef(_Bitmaps.Count - 1, size); } - public CTexture AddTexture(string texturePath) + public CTextureRef AddTexture(string texturePath) { if (File.Exists(texturePath)) { - foreach (CTexture tex in _Textures.Values) - { - if (tex.TexturePath == texturePath) - return tex; - } + CTextureRef texture; + if (_TextureCache.TryGetValue(texturePath, out texture)) + return texture; using (var bmp = new Bitmap(texturePath)) { - CTexture texture = AddTexture(bmp); - texture.TexturePath = texturePath; + texture = AddTexture(bmp); + _TextureCache.Add(texturePath, texture); } + return texture; } return null; } - private bool _TextureExists(CTexture texture) + private bool _TextureExists(CTextureRef texture) { return texture != null && _Textures.ContainsKey(texture.ID); } - public void RemoveTexture(ref CTexture texture) + public CTextureRef CopyTexture(CTextureRef textureRef) + { + if (_TextureExists(textureRef)) + return AddTexture(_Bitmaps[textureRef.ID]); + return null; + } + + public void RemoveTexture(ref CTextureRef texture) { if (_TextureExists(texture)) { _Bitmaps[texture.ID].Dispose(); _Textures.Remove(texture.ID); + texture.SetRemoved(); } texture = null; } - public CTexture EnqueueTexture(int w, int h, byte[] data) + public CTextureRef EnqueueTexture(int w, int h, byte[] data) { return AddTexture(w, h, data); } - public CTexture AddTexture(int w, int h, byte[] data) + public CTextureRef EnqueueTexture(string texturePath) + { + return AddTexture(texturePath); + } + + public CTextureRef EnqueueTexture(Bitmap bmp) + { + return AddTexture(bmp); + } + + public void EnqueueTextureUpdate(CTextureRef textureRef, Bitmap bmp) + { + UpdateTexture(textureRef, bmp); + } + + public CTextureRef AddTexture(int w, int h, byte[] data) { using (var bmp = new Bitmap(w, h)) { - BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); + BitmapData bmpData = bmp.LockBits(bmp.GetRect(), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); Marshal.Copy(data, 0, bmpData.Scan0, data.Length); bmp.UnlockBits(bmpData); return AddTexture(bmp); } } - public bool UpdateTexture(CTexture texture, int w, int h, byte[] data) + public void UpdateTexture(CTextureRef texture, int w, int h, byte[] data) { if (_TextureExists(texture)) { @@ -393,40 +409,22 @@ namespace Vocaluxe.Lib.Draw ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); Marshal.Copy(data, 0, bmpData.Scan0, data.Length); _Bitmaps[texture.ID].UnlockBits(bmpData); - return true; } - return false; } - public bool UpdateOrAddTexture(ref CTexture texture, int w, int h, byte[] data) + public void UpdateTexture(CTextureRef textureRef, Bitmap bmp) { - if (!UpdateTexture(texture, w, h, data)) - { - RemoveTexture(ref texture); - texture = AddTexture(w, h, data); - } - return true; + if (_TextureExists(textureRef)) + _Bitmaps[textureRef.ID] = bmp; } - public void DrawTexture(CTexture texture) - { - if (texture != null) - DrawTexture(texture, texture.Rect, texture.Color); - } - - public void DrawTexture(CTexture texture, SRectF rect) - { - if (texture != null) - DrawTexture(texture, rect, texture.Color); - } - - public void DrawTexture(CTexture texture, SRectF rect, SColorF color, bool mirrored = false) + public void DrawTexture(CTextureRef texture, SRectF rect, SColorF color, bool mirrored = false) { if (texture != null) DrawTexture(texture, rect, color, rect, mirrored); } - public void DrawTexture(CTexture texture, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false) + public void DrawTexture(CTextureRef texture, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false) { if (!_TextureExists(texture)) return; @@ -435,9 +433,9 @@ namespace Vocaluxe.Lib.Draw coloredBitmap.Dispose(); } - public void DrawTexture(CTexture texture, SRectF rect, SColorF color, float begin, float end) {} + public void DrawTexture(CTextureRef texture, SRectF rect, SColorF color, float begin, float end) {} - public void DrawTextureReflection(CTexture texture, SRectF rect, SColorF color, SRectF bounds, float space, float height) {} + public void DrawTextureReflection(CTextureRef texture, SRectF rect, SColorF color, float space, float height) {} public int GetTextureCount() { diff --git a/Vocaluxe/Lib/Draw/COpenGL.cs b/Vocaluxe/Lib/Draw/COpenGL.cs index 43ba70c2..94f0b63d 100644 --- a/Vocaluxe/Lib/Draw/COpenGL.cs +++ b/Vocaluxe/Lib/Draw/COpenGL.cs @@ -15,17 +15,12 @@ // along with Vocaluxe. If not, see . #endregion -using System.Drawing.Drawing2D; using System.Drawing.Imaging; -using System.Threading; using OpenTK; using OpenTK.Graphics; using System; -using System.Collections.Generic; -using System.ComponentModel; using System.Drawing; using System.IO; -using System.Runtime.InteropServices; using System.Windows.Forms; using Vocaluxe.Base; using VocaluxeLib; @@ -33,15 +28,11 @@ using VocaluxeLib.Draw; using BeginMode = OpenTK.Graphics.OpenGL.BeginMode; using BlendingFactorDest = OpenTK.Graphics.OpenGL.BlendingFactorDest; using BlendingFactorSrc = OpenTK.Graphics.OpenGL.BlendingFactorSrc; -using BufferAccess = OpenTK.Graphics.OpenGL.BufferAccess; -using BufferTarget = OpenTK.Graphics.OpenGL.BufferTarget; -using BufferUsageHint = OpenTK.Graphics.OpenGL.BufferUsageHint; using ClearBufferMask = OpenTK.Graphics.OpenGL.ClearBufferMask; using DepthFunction = OpenTK.Graphics.OpenGL.DepthFunction; using EnableCap = OpenTK.Graphics.OpenGL.EnableCap; using GL = OpenTK.Graphics.OpenGL.GL; using GenerateMipmapTarget = OpenTK.Graphics.OpenGL.GenerateMipmapTarget; -using KeyPressEventArgs = System.Windows.Forms.KeyPressEventArgs; using MatrixMode = OpenTK.Graphics.OpenGL.MatrixMode; using PixelFormat = OpenTK.Graphics.OpenGL.PixelFormat; using PixelInternalFormat = OpenTK.Graphics.OpenGL.PixelInternalFormat; @@ -54,67 +45,61 @@ using TextureTarget = OpenTK.Graphics.OpenGL.TextureTarget; namespace Vocaluxe.Lib.Draw { - struct SClientRect + class CFormHook : Form, IFormHook { - public Point Location; - public int Width; - public int Height; - }; + public MessageEventHandler OnMessage { private get; set; } - struct STextureQueue - { - public readonly int ID; - public readonly int TextureWidth; - public readonly int TextureHeight; - public readonly int DataWidth; - public readonly int DataHeight; - public readonly byte[] Data; - - public STextureQueue(int id, int textureWidth, int textureHeight, int dataWidth, int dataHeight, byte[] data) + protected override void WndProc(ref Message m) { - ID = id; - TextureWidth = textureWidth; - TextureHeight = textureHeight; - DataWidth = dataWidth; - DataHeight = dataHeight; - Data = data; + if (OnMessage == null || OnMessage(ref m)) + base.WndProc(ref m); } } - class COpenGL : Form, IDraw + class COGLTexture : CTextureBase + { + //The texture "name" according to the specs + public readonly int Name; + + 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 override bool IsLoaded + { + get { return Name != 0; } + } + + public override void Dispose() + { + base.Dispose(); + if (Name != 0) + GL.DeleteTexture(Name); + } + } + + class COpenGL : CDrawBaseWindows, IDraw { #region private vars - private const int _MaxID = 100000; - - private readonly CKeys _Keys; - private readonly CMouse _Mouse; - private bool _Run; - private readonly GLControl _Control; - - private SClientRect _Restore; - private bool _Fullscreen; - - private readonly Dictionary _Textures = new Dictionary(); - private readonly Queue _IDs = new Queue(_MaxID); - private readonly Queue _TexturesToLoad = new Queue(); - - private readonly Object _MutexTexture = new Object(); - - private int _H = 1; - private int _W = 1; - private int _Y; - private int _X; - - private bool _UsePBO; #endregion private vars public COpenGL() { - Icon = new Icon(Path.Combine(CSettings.ProgramFolder, CSettings.FileNameIcon)); - - for (int i = 1; i < _MaxID; i++) - _IDs.Enqueue(i); + _Form = new CFormHook(); //Check AA Mode CConfig.AAMode = (EAntiAliasingModes)_CheckAntiAliasingMode((int)CConfig.AAMode); @@ -138,20 +123,13 @@ namespace Vocaluxe.Lib.Draw _Control.MakeCurrent(); _Control.VSync = CConfig.VSync == EOffOn.TR_CONFIG_ON; - Controls.Add(_Control); + _Form.Controls.Add(_Control); + _Control.KeyDown += _OnKeyDown; + _Control.PreviewKeyDown += _OnPreviewKeyDown; + _Control.KeyPress += _OnKeyPress; + _Control.KeyUp += _OnKeyUp; - _Keys = new CKeys(); - Paint += _OnPaintEvent; - Closing += _OnClosingEvent; - Resize += _OnResizeEvent; - - _Control.KeyDown += _OnKeyDownEvent; - _Control.PreviewKeyDown += _OnPreviewKeyDownEvent; - _Control.KeyPress += _OnKeyPressEvent; - _Control.KeyUp += _OnKeyUpEvent; - - _Mouse = new CMouse(); _Control.MouseMove += _OnMouseMove; _Control.MouseWheel += _OnMouseWheel; _Control.MouseDown += _OnMouseDown; @@ -159,12 +137,11 @@ namespace Vocaluxe.Lib.Draw _Control.MouseLeave += _OnMouseLeave; _Control.MouseEnter += _OnMouseEnter; - ClientSize = new Size(CConfig.ScreenW, CConfig.ScreenH); - CenterToScreen(); + _NonPowerOf2TextureSupported = false; } #region Helpers - private int _CheckAntiAliasingMode(int setValue) + private static int _CheckAntiAliasingMode(int setValue) { int samples = 0; @@ -223,189 +200,37 @@ namespace Vocaluxe.Lib.Draw return result - 8; } */ - - private void _ToggleFullScreen() - { - if (!_Fullscreen) - _EnterFullScreen(); - else - _LeaveFullScreen(); - } - - private void _EnterFullScreen() - { - _Fullscreen = true; - - _Restore.Location = Location; - _Restore.Width = Width; - _Restore.Height = Height; - - FormBorderStyle = FormBorderStyle.None; - - int screenNr = 0; - for (int i = 0; i < Screen.AllScreens.Length; i++) - { - Screen scr = Screen.AllScreens[i]; - if (scr.Bounds.Top <= Top && scr.Bounds.Left <= Left) - screenNr = i; - } - - DesktopBounds = new Rectangle(Screen.AllScreens[screenNr].Bounds.Location, new Size(Screen.AllScreens[screenNr].Bounds.Width, Screen.AllScreens[screenNr].Bounds.Height)); - - if (WindowState == FormWindowState.Maximized) - { - WindowState = FormWindowState.Normal; - _DoResize(); - WindowState = FormWindowState.Maximized; - } - else - _DoResize(); - } - - private void _LeaveFullScreen() - { - _Fullscreen = false; - - FormBorderStyle = FormBorderStyle.Sizable; - DesktopBounds = new Rectangle(_Restore.Location, new Size(_Restore.Width, _Restore.Height)); - } #endregion Helpers #region form events - private void _OnPaintEvent(object sender, PaintEventArgs e) {} - - private void _OnResizeEvent(object sender, EventArgs e) {} - - private void _OnClosingEvent(object sender, CancelEventArgs e) + protected override void _OnResize(object sender, EventArgs e) { - _Run = false; - } - - protected override void OnLoad(EventArgs e) - { - GL.ClearColor(Color.Black); - } - - protected override void OnResize(EventArgs e) - { - base.OnResize(e); - - _Control.ClientSize = ClientSize; - _DoResize(); - } - - protected override void WndProc(ref Message m) - { - switch (m.Msg) - { - case 0x112: // WM_SYSCOMMAND - switch ((int)m.WParam & 0xFFF0) - { - case 0xF100: // SC_KEYMENU - m.Result = IntPtr.Zero; - break; - case 0xF140: // SC_SCREENSAVER - case 0xF170: // SC_MONITORPOWER - break; - default: - base.WndProc(ref m); - break; - } - break; - default: - base.WndProc(ref m); - break; - } + _Control.ClientSize = _Form.ClientSize; + base._OnResize(sender, e); } #endregion form events - #region mouse event handlers - private void _OnMouseMove(object sender, MouseEventArgs e) - { - _Mouse.MouseMove(e); - } - - private void _OnMouseWheel(object sender, MouseEventArgs e) - { - _Mouse.MouseWheel(e); - } - - private void _OnMouseDown(object sender, MouseEventArgs e) - { - _Mouse.MouseDown(e); - } - - private void _OnMouseUp(object sender, MouseEventArgs e) - { - _Mouse.MouseUp(e); - } - - private void _OnMouseLeave(object sender, EventArgs e) - { - _Mouse.Visible = false; - Cursor.Show(); - } - - private void _OnMouseEnter(object sender, EventArgs e) - { - Cursor.Hide(); - _Mouse.Visible = true; - } - #endregion - - #region keyboard event handlers - private void _OnPreviewKeyDownEvent(object sender, PreviewKeyDownEventArgs e) - { - _OnKeyDownEvent(sender, new KeyEventArgs(e.KeyData)); - } - - private void _OnKeyDownEvent(object sender, KeyEventArgs e) - { - _Keys.KeyDown(e); - } - - private void _OnKeyPressEvent(object sender, KeyPressEventArgs e) - { - _Keys.KeyPress(e); - } - - private void _OnKeyUpEvent(object sender, KeyEventArgs e) - { - _Keys.KeyUp(e); - } - #endregion keyboard event handlers - - private void _DoResize() + protected override void _DoResize() { _H = _Control.Height; _W = _Control.Width; - _Y = 0; - _X = 0; + _CurrentAlignment = CConfig.ScreenAlignment; - - if (_W / (float)_H > CSettings.GetRenderAspect()) - { - _W = (int)Math.Round(_H * CSettings.GetRenderAspect()); - _X = (_Control.Width - _W) / 2; - } - else - { - _H = (int)Math.Round(_W / CSettings.GetRenderAspect()); - _Y = (_Control.Height - _H) / 2; - } - - GL.MatrixMode(MatrixMode.Projection); - GL.LoadIdentity(); - GL.Ortho(0, CSettings.RenderW, CSettings.RenderH, 0, CSettings.ZNear, CSettings.ZFar); + _AdjustAspect(true); + _AdjustNewBorders(); GL.Viewport(_X, _Y, _W, _H); } #region implementation #region main stuff - public bool Init() + public override bool Init() { - Text = CSettings.GetFullVersionText(); + if (!base.Init()) + return false; + + //OpenGL needs that here but D3D needs it in the constructor, so do NOT unify! + _Form.ClientSize = new Size(CConfig.ScreenW, CConfig.ScreenH); // Init Texturing GL.Enable(EnableCap.Texture2D); @@ -416,59 +241,20 @@ namespace Vocaluxe.Lib.Draw GL.DepthRange(CSettings.ZFar, CSettings.ZNear); GL.DepthFunc(DepthFunction.Lequal); GL.Enable(EnableCap.DepthTest); + GL.ClearColor(Color.Black); return true; } - public void MainLoop() + protected override void _OnBeforeDraw() { - _Run = true; - int delay = 0; - Show(); - - if (CConfig.FullScreen == EOffOn.TR_CONFIG_ON) - _EnterFullScreen(); - - while (_Run) - { - ClearScreen(); - if (!CGraphics.Draw()) - _Run = false; - if (!CGraphics.UpdateGameLogic(_Keys, _Mouse)) - _Run = false; - - _Control.SwapBuffers(); - - if ((CConfig.FullScreen == EOffOn.TR_CONFIG_ON) != _Fullscreen) - _ToggleFullScreen(); - - _CheckQueue(); - - if (CTime.IsRunning()) - delay = (int)Math.Floor(CConfig.CalcCycleTime() - CTime.GetMilliseconds()); - - if (delay >= 1 && CConfig.VSync == EOffOn.TR_CONFIG_OFF) - Thread.Sleep(delay); - - CTime.CalculateFPS(); - CTime.Restart(); - - Application.DoEvents(); - } - Close(); + //Nothing to do } - public void Unload() + protected override void _OnAfterDraw() { - try - { - Close(); - } - catch {} - var textures = new CTexture[_Textures.Count]; - _Textures.Values.CopyTo(textures, 0); - for (int i = 0; i < _Textures.Count; i++) - RemoveTexture(ref textures[i]); + _Control.SwapBuffers(); + Application.DoEvents(); } public int GetScreenWidth() @@ -480,56 +266,17 @@ namespace Vocaluxe.Lib.Draw { return _Control.Height; } - #endregion main stuff - #region Basic Draw Methods - public void ClearScreen() + protected override void _ClearScreen() { GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); } - public CTexture CopyScreen() + protected override void _AdjustNewBorders() { - CTexture texture = _GetNewTexture(_W, _H); - - texture.Name = GL.GenTexture(); - 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.CopyTexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, _X, _Y, _W, _H); - - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); - - GL.BindTexture(TextureTarget.Texture2D, 0); - - // Add to Texture List - - lock (_MutexTexture) - { - texture.ID = _IDs.Dequeue(); - _Textures[texture.ID] = texture; - } - - return texture; - } - - public void CopyScreen(ref CTexture texture) - { - //Check for actual texture sizes as it may be downsized compared to OrigSize - if (!_TextureExists(texture) || texture.UsedWidth != GetScreenWidth() || texture.UsedHeight != GetScreenHeight()) - { - RemoveTexture(ref texture); - texture = CopyScreen(); - } - else - { - GL.BindTexture(TextureTarget.Texture2D, texture.Name); - - GL.CopyTexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, 0, 0, GetScreenWidth(), GetScreenHeight()); - - GL.BindTexture(TextureTarget.Texture2D, 0); - } + GL.MatrixMode(MatrixMode.Projection); + GL.LoadIdentity(); + GL.Ortho(-CConfig.BorderLeft, CConfig.BorderRight + CSettings.RenderW, CConfig.BorderBottom + CSettings.RenderH, -CConfig.BorderTop, CSettings.ZNear, CSettings.ZFar); } public void MakeScreenShot() @@ -550,36 +297,63 @@ namespace Vocaluxe.Lib.Draw screen.Save(file, ImageFormat.Bmp); } } + #endregion main stuff - public void DrawLine(int a, int r, int g, int b, int w, int x1, int y1, int x2, int y2) + #region Basic Draw Methods + public CTextureRef CopyScreen() { - GL.Enable(EnableCap.Blend); - GL.Color4(r, g, b, a * CGraphics.GlobalAlpha); + //TODO: Check if _W,_H needs to be used or not + Size size = new Size(GetScreenWidth(), GetScreenHeight()); + COGLTexture texture = _CreateTexture(size); - GL.Begin(BeginMode.Lines); - GL.Vertex3(x1, y1, CGraphics.ZOffset); - GL.Vertex3(x2, y2, CGraphics.ZOffset); - GL.End(); + GL.BindTexture(TextureTarget.Texture2D, texture.Name); + GL.CopyTexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, 0, 0, size.Width, size.Height); //TODO: Use _X,_Y and _W,_H? + GL.BindTexture(TextureTarget.Texture2D, 0); - GL.Disable(EnableCap.Blend); + return _GetTextureReference(size, texture); } - public void DrawColor(SColorF color, SRectF rect) + public void CopyScreen(ref CTextureRef textureRef) + { + COGLTexture texture; + //Check for actual texture sizes as it may be downsized compared to OrigSize + if (!_GetTexture(textureRef, out texture) || texture.DataSize.Width != GetScreenWidth() || texture.DataSize.Height != GetScreenHeight()) + { + RemoveTexture(ref textureRef); + textureRef = CopyScreen(); + } + else + { + GL.BindTexture(TextureTarget.Texture2D, texture.Name); + GL.CopyTexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, 0, 0, GetScreenWidth(), GetScreenHeight()); + GL.BindTexture(TextureTarget.Texture2D, 0); + } + } + + public void DrawRect(SColorF color, SRectF rect) { GL.Enable(EnableCap.Blend); GL.Color4(color.R, color.G, color.B, color.A * CGraphics.GlobalAlpha); GL.Begin(BeginMode.Quads); + GL.MatrixMode(MatrixMode.Color); + GL.PushMatrix(); + if (Math.Abs(rect.Rotation) > 0.001) + { + GL.Translate(0.5f, 0.5f, 0); + GL.Rotate(-rect.Rotation, 0f, 0f, 1f); + GL.Translate(-0.5f, -0.5f, 0); + } GL.Vertex3(rect.X, rect.Y, rect.Z + CGraphics.ZOffset); GL.Vertex3(rect.X, rect.Y + rect.H, rect.Z + CGraphics.ZOffset); GL.Vertex3(rect.X + rect.W, rect.Y + rect.H, rect.Z + CGraphics.ZOffset); GL.Vertex3(rect.X + rect.W, rect.Y, rect.Z + CGraphics.ZOffset); GL.End(); - + GL.PopMatrix(); GL.Disable(EnableCap.Blend); } - public void DrawColorReflection(SColorF color, SRectF rect, float space, float height) + public void DrawRectReflection(SColorF color, SRectF rect, float space, float height) { if (rect.H < height) height = rect.H; @@ -603,7 +377,8 @@ namespace Vocaluxe.Lib.Draw GL.Enable(EnableCap.Blend); - + GL.MatrixMode(MatrixMode.Texture); + GL.PushMatrix(); if (Math.Abs(rect.Rotation) > 0.001) { GL.Translate(0.5f, 0.5f, 0); @@ -626,299 +401,113 @@ namespace Vocaluxe.Lib.Draw GL.Vertex3(rx1, ry1, rect.Z + CGraphics.ZOffset); GL.End(); - + GL.PopMatrix(); GL.Disable(EnableCap.Blend); } #endregion Basic Draw Methods #region Textures - - #region adding - public CTexture AddTexture(string texturePath) + protected override COGLTexture _CreateTexture(Size dataSize) { - if (!File.Exists(texturePath)) - { - CLog.LogError("Can't find File: " + texturePath); - return null; - } - Bitmap bmp; - try - { - bmp = new Bitmap(texturePath); - } - catch (Exception) - { - CLog.LogError("Error loading Texture: " + texturePath); - return null; - } - try - { - return AddTexture(bmp); - } - finally - { - bmp.Dispose(); - } - } - - public CTexture AddTexture(Bitmap bmp) - { - if (bmp.Height == 0 || bmp.Width == 0) - return null; - - int maxSize; - switch (CConfig.TextureQuality) - { - case ETextureQuality.TR_CONFIG_TEXTURE_LOWEST: - maxSize = 128; - break; - case ETextureQuality.TR_CONFIG_TEXTURE_LOW: - maxSize = 256; - break; - case ETextureQuality.TR_CONFIG_TEXTURE_MEDIUM: - maxSize = 512; - break; - case ETextureQuality.TR_CONFIG_TEXTURE_HIGH: - maxSize = 1024; - break; - case ETextureQuality.TR_CONFIG_TEXTURE_HIGHEST: - maxSize = 2048; - break; - default: - maxSize = 512; - break; - } - - int w = Math.Min(bmp.Width, maxSize); - int h = Math.Min(bmp.Height, maxSize); - w = MathHelper.NextPowerOfTwo(w); - h = MathHelper.NextPowerOfTwo(h); - - var texture = new CTexture(bmp.Width, bmp.Height, w, h, true) {Name = GL.GenTexture()}; - - GL.BindTexture(TextureTarget.Texture2D, texture.Name); - Bitmap bmp2 = null; - try - { - if (bmp.Width != w || bmp.Height != h) - { - //Create a new Bitmap with the new sizes - bmp2 = new Bitmap(w, h); - //Scale the texture - using (Graphics g = Graphics.FromImage(bmp2)) - { - g.InterpolationMode = InterpolationMode.HighQualityBicubic; - g.SmoothingMode = SmoothingMode.HighQuality; - g.DrawImage(bmp, new Rectangle(0, 0, bmp2.Width, bmp2.Height)); - } - bmp = bmp2; - } - - BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, texture.W2, texture.H2, 0, PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero); - GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, bmpData.Width, bmpData.Height, PixelFormat.Bgra, PixelType.UnsignedByte, bmpData.Scan0); - bmp.UnlockBits(bmpData); - } - finally - { - if (bmp2 != null) - bmp2.Dispose(); - } - - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureParameterName.ClampToEdge); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureParameterName.ClampToEdge); - - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear); - GL.Ext.GenerateMipmap(GenerateMipmapTarget.Texture2D); - - GL.BindTexture(TextureTarget.Texture2D, 0); - // Add to Texture List - texture.TexturePath = String.Empty; - - lock (_MutexTexture) - { - texture.ID = _IDs.Dequeue(); - _Textures[texture.ID] = texture; - } + if (dataSize.Width < 0) + return new COGLTexture(0, dataSize); + COGLTexture texture = new COGLTexture(GL.GenTexture(), dataSize, _CheckForNextPowerOf2(dataSize.Width), _CheckForNextPowerOf2(dataSize.Height)); return texture; } - public CTexture AddTexture(int w, int h, byte[] data) + protected override void _WriteDataToTexture(COGLTexture texture, byte[] data) { - CTexture texture = _GetNewTexture(w, h); - - _CreateTexture(texture, w, h, data); - - lock (_MutexTexture) - { - texture.ID = _IDs.Dequeue(); - _Textures[texture.ID] = texture; - } - - return texture; - } - - private void _CreateTexture(CTexture texture, int w, int h, byte[] data) - { - if (_UsePBO) - { - try - { - GL.GenBuffers(1, out texture.PBO); - GL.BindBuffer(BufferTarget.PixelUnpackBuffer, texture.PBO); - GL.BufferData(BufferTarget.PixelUnpackBuffer, (IntPtr)data.Length, IntPtr.Zero, BufferUsageHint.StreamDraw); - GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0); - } - catch (Exception) - { - //throw; - _UsePBO = false; - } - } - - texture.Name = GL.GenTexture(); 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.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, w, h, PixelFormat.Bgra, PixelType.UnsignedByte, data); - - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureParameterName.ClampToEdge); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureParameterName.ClampToEdge); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear); + GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, texture.DataSize.Width, texture.DataSize.Height, PixelFormat.Bgra, PixelType.UnsignedByte, data); GL.Ext.GenerateMipmap(GenerateMipmapTarget.Texture2D); GL.BindTexture(TextureTarget.Texture2D, 0); } - public CTexture EnqueueTexture(int w, int h, byte[] data) + protected override void _WriteDataToTexture(COGLTexture texture, IntPtr data) { - CTexture texture = _GetNewTexture(w, h); - - lock (_MutexTexture) - { - texture.ID = _IDs.Dequeue(); - var queue = new STextureQueue(texture.ID, texture.W2, texture.H2, w, h, data); - _TexturesToLoad.Enqueue(queue); - _Textures[texture.ID] = texture; - } - - return texture; - } - - private static CTexture _GetNewTexture(int w, int h) - { - return new CTexture(w, h, MathHelper.NextPowerOfTwo(w), MathHelper.NextPowerOfTwo(h)); - } - #endregion adding - - #region updating - public bool UpdateTexture(CTexture texture, int w, int h, byte[] data) - { - //Check if texture exists and extend matches - if (!_TextureExists(texture) || texture.UsedWidth != w || texture.UsedHeight != h) - return false; - if (_UsePBO) - { - GL.BindBuffer(BufferTarget.PixelUnpackBuffer, texture.PBO); - - IntPtr buffer = GL.MapBuffer(BufferTarget.PixelUnpackBuffer, BufferAccess.WriteOnly); - Marshal.Copy(data, 0, buffer, data.Length); - - GL.UnmapBuffer(BufferTarget.PixelUnpackBuffer); - - GL.BindTexture(TextureTarget.Texture2D, texture.Name); - - GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, w, h, PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero); - - GL.Ext.GenerateMipmap(GenerateMipmapTarget.Texture2D); - - GL.BindTexture(TextureTarget.Texture2D, 0); - GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0); - - return true; - } - GL.BindTexture(TextureTarget.Texture2D, texture.Name); - GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, w, h, PixelFormat.Bgra, PixelType.UnsignedByte, data); - + GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, texture.DataSize.Width, texture.DataSize.Height, PixelFormat.Bgra, PixelType.UnsignedByte, data); GL.Ext.GenerateMipmap(GenerateMipmapTarget.Texture2D); GL.BindTexture(TextureTarget.Texture2D, 0); - - return true; - } - - public bool UpdateOrAddTexture(ref CTexture texture, int w, int h, byte[] data) - { - if (!UpdateTexture(texture, w, h, data)) - { - RemoveTexture(ref texture); - texture = AddTexture(w, h, data); - } - return true; - } - #endregion updating - - public void RemoveTexture(ref CTexture texture) - { - if (_TextureExists(texture, false)) - { - lock (_MutexTexture) - { - _IDs.Enqueue(texture.ID); - GL.DeleteTexture(texture.Name); - if (texture.PBO > 0) - GL.DeleteBuffers(1, ref texture.PBO); - _Textures.Remove(texture.ID); - } - } - texture = null; - } - - private bool _TextureExists(CTexture texture, bool checkIfLoaded = true) - { - lock (_MutexTexture) - { - if (texture != null && _Textures.ContainsKey(texture.ID)) - { - if (!checkIfLoaded || _Textures[texture.ID].Name > 0) - return true; - } - } - return false; } #region drawing - public void DrawTexture(CTexture texture) + public void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, bool mirrored = false) { - if (texture == null) - return; - DrawTexture(texture, texture.Rect, texture.Color); - } - - public void DrawTexture(CTexture texture, SRectF rect) - { - if (texture == null) - return; - DrawTexture(texture, rect, texture.Color); - } - - public void DrawTexture(CTexture texture, SRectF rect, SColorF color, bool mirrored = false) - { - DrawTexture(texture, rect, color, new SRectF(0, 0, CSettings.RenderW, CSettings.RenderH, rect.Z), mirrored); - } - - public void DrawTexture(CTexture texture, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false) - { - if (!_TextureExists(texture)) + COGLTexture texture; + if (!_GetTexture(textureRef, out texture)) return; - if (Math.Abs(rect.W) < float.Epsilon || Math.Abs(rect.H) < float.Epsilon || Math.Abs(bounds.H) < float.Epsilon || Math.Abs(bounds.W) < float.Epsilon || - Math.Abs(color.A) < float.Epsilon) + if (Math.Abs(rect.W) < 1 || Math.Abs(rect.H) < 1 || Math.Abs(color.A) < 0.01) + return; + + GL.BindTexture(TextureTarget.Texture2D, texture.Name); + + const float x1 = 0; + float x2 = texture.WidthRatio; + float y1 = 0; + float y2 = texture.HeightRatio; + + float rx1 = rect.X; + float rx2 = rect.X + rect.W; + float ry1 = rect.Y; + float ry2 = rect.Y + rect.H; + + GL.Enable(EnableCap.Blend); + GL.Color4(color.R, color.G, color.B, color.A * CGraphics.GlobalAlpha); + + GL.MatrixMode(MatrixMode.Texture); + GL.PushMatrix(); + + if (Math.Abs(rect.Rotation) > float.Epsilon) + { + GL.Translate(0.5f, 0.5f, 0); + GL.Rotate(-rect.Rotation, 0f, 0f, 1f); + GL.Translate(-0.5f, -0.5f, 0); + } + + if (mirrored) + { + float tmp = y2; + y2 = y1; + y1 = tmp; + } + + GL.Begin(BeginMode.Quads); + + GL.TexCoord2(x1, y1); + GL.Vertex3(rx1, ry1, rect.Z + CGraphics.ZOffset); + + GL.TexCoord2(x1, y2); + GL.Vertex3(rx1, ry2, rect.Z + CGraphics.ZOffset); + + GL.TexCoord2(x2, y2); + GL.Vertex3(rx2, ry2, rect.Z + CGraphics.ZOffset); + + GL.TexCoord2(x2, y1); + GL.Vertex3(rx2, ry1, rect.Z + CGraphics.ZOffset); + + GL.End(); + + GL.PopMatrix(); + + GL.Disable(EnableCap.Blend); + GL.BindTexture(TextureTarget.Texture2D, 0); + } + + public void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false) + { + COGLTexture texture; + if (!_GetTexture(textureRef, out texture)) + return; + + if (Math.Abs(rect.W) < 1 || Math.Abs(rect.H) < 1 || Math.Abs(bounds.H) < 1 || Math.Abs(bounds.W) < 1 || + Math.Abs(color.A) < 0.01) return; if (bounds.X > rect.X + rect.W || bounds.X + bounds.W < rect.X) @@ -977,42 +566,28 @@ namespace Vocaluxe.Lib.Draw GL.Translate(-0.5f, -0.5f, 0); } - if (!mirrored) + if (mirrored) { - GL.Begin(BeginMode.Quads); - - GL.TexCoord2(x1, y1); - GL.Vertex3(rx1, ry1, rect.Z + CGraphics.ZOffset); - - GL.TexCoord2(x1, y2); - GL.Vertex3(rx1, ry2, rect.Z + CGraphics.ZOffset); - - GL.TexCoord2(x2, y2); - GL.Vertex3(rx2, ry2, rect.Z + CGraphics.ZOffset); - - GL.TexCoord2(x2, y1); - GL.Vertex3(rx2, ry1, rect.Z + CGraphics.ZOffset); - - GL.End(); + float tmp = y2; + y2 = y1; + y1 = tmp; } - else - { - GL.Begin(BeginMode.Quads); - GL.TexCoord2(x2, y2); - GL.Vertex3(rx2, ry1, rect.Z + CGraphics.ZOffset); + GL.Begin(BeginMode.Quads); - GL.TexCoord2(x2, y1); - GL.Vertex3(rx2, ry2, rect.Z + CGraphics.ZOffset); + GL.TexCoord2(x1, y1); + GL.Vertex3(rx1, ry1, rect.Z + CGraphics.ZOffset); - GL.TexCoord2(x1, y1); - GL.Vertex3(rx1, ry2, rect.Z + CGraphics.ZOffset); + GL.TexCoord2(x1, y2); + GL.Vertex3(rx1, ry2, rect.Z + CGraphics.ZOffset); - GL.TexCoord2(x1, y2); - GL.Vertex3(rx1, ry1, rect.Z + CGraphics.ZOffset); + GL.TexCoord2(x2, y2); + GL.Vertex3(rx2, ry2, rect.Z + CGraphics.ZOffset); - GL.End(); - } + GL.TexCoord2(x2, y1); + GL.Vertex3(rx2, ry1, rect.Z + CGraphics.ZOffset); + + GL.End(); GL.PopMatrix(); @@ -1020,10 +595,12 @@ namespace Vocaluxe.Lib.Draw GL.BindTexture(TextureTarget.Texture2D, 0); } - public void DrawTexture(CTexture texture, SRectF rect, SColorF color, float begin, float end) + public void DrawTexture(CTextureRef textureRef, SRectF rect, SColorF color, float begin, float end) { - if (!_TextureExists(texture)) + COGLTexture texture; + if (!_GetTexture(textureRef, out texture)) return; + GL.BindTexture(TextureTarget.Texture2D, texture.Name); GL.Enable(EnableCap.Blend); @@ -1046,47 +623,28 @@ namespace Vocaluxe.Lib.Draw GL.End(); - GL.Disable(EnableCap.Blend); GL.BindTexture(TextureTarget.Texture2D, 0); } - public void DrawTextureReflection(CTexture texture, SRectF rect, SColorF color, SRectF bounds, float space, float height) + public void DrawTextureReflection(CTextureRef textureRef, SRectF rect, SColorF color, float space, float height) { - if (!_TextureExists(texture)) + COGLTexture texture; + if (!_GetTexture(textureRef, out texture)) return; - if (Math.Abs(rect.W) < float.Epsilon || Math.Abs(rect.H) < float.Epsilon || Math.Abs(bounds.H) < float.Epsilon || Math.Abs(bounds.W) < float.Epsilon || - Math.Abs(color.A) < float.Epsilon || height <= float.Epsilon) + if (Math.Abs(rect.W) < float.Epsilon || Math.Abs(rect.H) < float.Epsilon || Math.Abs(color.A) < float.Epsilon || height <= float.Epsilon) return; - if (bounds.X > rect.X + rect.W || bounds.X + bounds.W < rect.X) - return; - - if (bounds.Y > rect.Y + rect.H || bounds.Y + bounds.H < rect.Y) - return; - - if (height > bounds.H) - height = bounds.H; + if (height > rect.H) + height = rect.H; GL.BindTexture(TextureTarget.Texture2D, texture.Name); - float x1 = (bounds.X - rect.X) / rect.W * texture.WidthRatio; - float x2 = (bounds.X + bounds.W - rect.X) / rect.W * texture.WidthRatio; - float y1 = (bounds.Y - rect.Y + rect.H - height) / rect.H * texture.HeightRatio; - float y2 = (bounds.Y + bounds.H - rect.Y) / rect.H * texture.HeightRatio; - - if (x1 < 0) - x1 = 0f; - - if (x2 > texture.WidthRatio) - x2 = texture.WidthRatio; - - if (y1 < 0) - y1 = 0f; - - if (y2 > texture.HeightRatio) - y2 = texture.HeightRatio; + const float x1 = 0; + float x2 = texture.WidthRatio; + float y1 = (rect.H - height) / rect.H * texture.HeightRatio; + float y2 = texture.HeightRatio; float rx1 = rect.X; @@ -1094,18 +652,6 @@ namespace Vocaluxe.Lib.Draw float ry1 = rect.Y + rect.H + space; float ry2 = rect.Y + rect.H + space + height; - if (rx1 < bounds.X) - rx1 = bounds.X; - - if (rx2 > bounds.X + bounds.W) - rx2 = bounds.X + bounds.W; - - if (ry1 < bounds.Y + space) - ry1 = bounds.Y + space; - - if (ry2 > bounds.Y + bounds.H + space + height) - ry2 = bounds.Y + bounds.H + space + height; - GL.Enable(EnableCap.Blend); GL.MatrixMode(MatrixMode.Texture); @@ -1118,7 +664,6 @@ namespace Vocaluxe.Lib.Draw GL.Translate(-0.5f, -0.5f, 0); } - GL.Begin(BeginMode.Quads); GL.Color4(color.R, color.G, color.B, color.A * CGraphics.GlobalAlpha); @@ -1139,7 +684,6 @@ namespace Vocaluxe.Lib.Draw GL.End(); - GL.PopMatrix(); GL.Disable(EnableCap.Blend); @@ -1147,26 +691,6 @@ namespace Vocaluxe.Lib.Draw } #endregion drawing - public int GetTextureCount() - { - return _Textures.Count; - } - - private void _CheckQueue() - { - lock (_MutexTexture) - { - while (_TexturesToLoad.Count > 0) - { - STextureQueue q = _TexturesToLoad.Dequeue(); - CTexture texture; - if (!_Textures.TryGetValue(q.ID, out texture)) - continue; - - _CreateTexture(texture, q.DataWidth, q.DataHeight, q.Data); - } - } - } #endregion Textures #endregion implementation diff --git a/Vocaluxe/Lib/Draw/CTextureBase.cs b/Vocaluxe/Lib/Draw/CTextureBase.cs new file mode 100644 index 00000000..eb9d11bf --- /dev/null +++ b/Vocaluxe/Lib/Draw/CTextureBase.cs @@ -0,0 +1,96 @@ +using System; +using System.Drawing; + +namespace Vocaluxe.Lib.Draw +{ + abstract class CTextureBase : IDisposable + { + public int RefCount; + public string TexturePath; + + private Size _DataSize; + /// + /// Size of data used (mostly equal to OrigSize but due to resizing this might change) + /// + public Size DataSize + { + get { return _DataSize; } + set + { + _DataSize = value; + _CalcRatios(); + } + } + + /// + /// texture width (on device), a power of 2 if necessary + /// + public int W2 { get; protected set; } + /// + /// texture height (on device), a power of 2 if necessary + /// + public int H2 { get; protected set; } + + /// + /// Specifies which part of texture memory is actually used + /// + public float WidthRatio { get; private set; } + /// + /// Specifies which part of texture memory is actually used + /// + public float HeightRatio { get; private set; } + + /// + /// 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 + protected CTextureBase(Size dataSize, int texWidth = 0, int texHeight = 0) + { + _DataSize = dataSize; + W2 = (texWidth > 0) ? texWidth : dataSize.Width; + H2 = (texHeight > 0) ? texHeight : dataSize.Height; + + _CalcRatios(); + } + + private void _CalcRatios() + { + // OpenGL has a problem with partial (NPOT) textures, so we remove 1 pixel + if (_DataSize.Width == W2) + WidthRatio = 1f; + else + { + int mod = 0; + if (_DataSize.Width > 1) + mod = -1; + WidthRatio = (float)(_DataSize.Width + mod) / W2; + } + if (_DataSize.Height == H2) + HeightRatio = 1f; + else + { + int mod = 0; + if (_DataSize.Height > 1) + mod = -1; + 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 f5e3fad3..0b2421ff 100644 --- a/Vocaluxe/Lib/Draw/IDraw.cs +++ b/Vocaluxe/Lib/Draw/IDraw.cs @@ -32,30 +32,30 @@ namespace Vocaluxe.Lib.Draw int GetTextureCount(); - void ClearScreen(); - CTexture CopyScreen(); - void CopyScreen(ref CTexture texture); + CTextureRef CopyScreen(); + void CopyScreen(ref CTextureRef texture); void MakeScreenShot(); - CTexture AddTexture(string texturePath); - CTexture AddTexture(Bitmap bitmap); - CTexture AddTexture(int w, int h, byte[] data); - CTexture EnqueueTexture(int w, int h, byte[] data); - bool UpdateTexture(CTexture texture, int w, int h, byte[] data); - bool UpdateOrAddTexture(ref CTexture texture, int w, int h, byte[] data); - void RemoveTexture(ref CTexture texture); + CTextureRef AddTexture(string texturePath); + CTextureRef AddTexture(Bitmap bitmap); + CTextureRef AddTexture(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); // Basic Draw Methods - void DrawLine(int a, int r, int g, int b, int w, int x1, int y1, int x2, int y2); - void DrawColor(SColorF color, SRectF rect); - void DrawColorReflection(SColorF color, SRectF rect, float space, float height); + void DrawRect(SColorF color, SRectF rect); + void DrawRectReflection(SColorF color, SRectF rect, float space, float height); - void DrawTexture(CTexture texture); - void DrawTexture(CTexture texture, SRectF rect); - void DrawTexture(CTexture texture, SRectF rect, SColorF color, bool mirrored = false); - void DrawTexture(CTexture texture, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false); - void DrawTexture(CTexture texture, SRectF rect, SColorF color, float begin, float end); + void DrawTexture(CTextureRef texture, SRectF rect, SColorF color, bool mirrored = false); + void DrawTexture(CTextureRef texture, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false); + void DrawTexture(CTextureRef texture, SRectF rect, SColorF color, float begin, float end); - void DrawTextureReflection(CTexture texture, SRectF rect, SColorF color, SRectF bounds, float space, float height); + void DrawTextureReflection(CTextureRef texture, SRectF rect, SColorF color, float space, float height); } } \ No newline at end of file diff --git a/Vocaluxe/Lib/Playlist/CPlaylistFile.cs b/Vocaluxe/Lib/Playlist/CPlaylistFile.cs index ff86215f..d09c9b95 100644 --- a/Vocaluxe/Lib/Playlist/CPlaylistFile.cs +++ b/Vocaluxe/Lib/Playlist/CPlaylistFile.cs @@ -24,6 +24,7 @@ using Vocaluxe.Base; using VocaluxeLib; using VocaluxeLib.Menu; using VocaluxeLib.Songs; +using VocaluxeLib.Xml; namespace Vocaluxe.Lib.Playlist { diff --git a/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudio.cs b/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudio.cs index 7d35df67..e32d3795 100644 --- a/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudio.cs +++ b/Vocaluxe/Lib/Sound/Playback/GstreamerSharp/CGstreamerSharpAudio.cs @@ -17,6 +17,7 @@ using Gst; using System; +using Vocaluxe.Base; namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp { @@ -26,13 +27,20 @@ namespace Vocaluxe.Lib.Sound.Playback.GstreamerSharp { if (_Initialized) return false; + #if ARCH_X86 - var gstreamerEnvVar = Environment.GetEnvironmentVariable("GSTREAMER_1_0_ROOT_X86", EnvironmentVariableTarget.User); + const string varName = "GSTREAMER_1_0_ROOT_X86"; #endif #if ARCH_X64 - var gstreamerEnvVar = Environment.GetEnvironmentVariable("GSTREAMER_1_0_ROOT_X86_64", EnvironmentVariableTarget.User); + const string varName = "GSTREAMER_1_0_ROOT_X86_64"; #endif - var dllDirectory = gstreamerEnvVar + "bin\\"; + string gstreamerEnvVar = Environment.GetEnvironmentVariable(varName, EnvironmentVariableTarget.User); + if (gstreamerEnvVar == null) + { + CLog.LogError("Gstreamer not found! Make sure you installed it correctly and if it set the environment variable '" + varName + "'!", true); + return false; + } + string dllDirectory = gstreamerEnvVar + "bin\\"; COSFunctions.AddEnvironmentPath(dllDirectory); Application.Init(); diff --git a/Vocaluxe/Lib/Video/Acinerella/CDecoder.cs b/Vocaluxe/Lib/Video/Acinerella/CDecoder.cs index 9203b118..823c5290 100644 --- a/Vocaluxe/Lib/Video/Acinerella/CDecoder.cs +++ b/Vocaluxe/Lib/Video/Acinerella/CDecoder.cs @@ -102,7 +102,7 @@ namespace Vocaluxe.Lib.Video.Acinerella Finished = true; } - public bool GetFrame(ref CTexture frame, float time, out float videoTime) + public bool GetFrame(ref CTextureRef frame, float time, out float videoTime) { if (Finished) { diff --git a/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs b/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs index 0ff06799..78707644 100644 --- a/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs +++ b/Vocaluxe/Lib/Video/Acinerella/CDecoderThread.cs @@ -193,7 +193,7 @@ namespace Vocaluxe.Lib.Video.Acinerella /// Maximum start time for frame /// Set to whether there are no more frames (stream finished, no loop, future calls will always return false) /// True if a new frame was gotten - public bool GetFrame(ref CTexture frame, ref float time, out bool finished) + public bool GetFrame(ref CTextureRef frame, ref float time, out bool finished) { bool result; if (Math.Abs(_LastShownTime - time) < _FrameDuration && frame != null) //Check 1 frame difference @@ -206,7 +206,10 @@ namespace Vocaluxe.Lib.Video.Acinerella CFramebuffer.CFrame curFrame = _FindFrame(time); if (curFrame != null) { - CDraw.UpdateOrAddTexture(ref frame, _Width, _Height, curFrame.Data); + if (frame == null) + frame = CDraw.AddTexture(_Width, _Height, curFrame.Data); + else + CDraw.UpdateTexture(frame, _Width, _Height, curFrame.Data); if (!_Paused) curFrame.SetRead(); time = curFrame.Time; diff --git a/Vocaluxe/Lib/Video/CVideoDecoderFFmpeg.cs b/Vocaluxe/Lib/Video/CVideoDecoderFFmpeg.cs index 27f2baeb..045a6344 100644 --- a/Vocaluxe/Lib/Video/CVideoDecoderFFmpeg.cs +++ b/Vocaluxe/Lib/Video/CVideoDecoderFFmpeg.cs @@ -69,7 +69,7 @@ namespace Vocaluxe.Lib.Video return _Decoder.Count; } - public bool GetFrame(int streamID, ref CTexture frame, float time, out float videoTime) + public bool GetFrame(int streamID, ref CTextureRef frame, float time, out float videoTime) { CDecoder decoder; if (_Decoder.TryGetValue(streamID, out decoder)) diff --git a/Vocaluxe/Lib/Video/IVideoDecoder.cs b/Vocaluxe/Lib/Video/IVideoDecoder.cs index b1da1feb..ebf5e575 100644 --- a/Vocaluxe/Lib/Video/IVideoDecoder.cs +++ b/Vocaluxe/Lib/Video/IVideoDecoder.cs @@ -73,7 +73,7 @@ namespace Vocaluxe.Lib.Video /// /// The actual position in s of the returned frame. Should be ~ time+VideoGap /// True if frame is valid, false: frame and videoTime might be unchanged or invalid - bool GetFrame(int streamID, ref CTexture frame, float time, out float videoTime); + bool GetFrame(int streamID, ref CTextureRef frame, float time, out float videoTime); /// /// Seeks to given position diff --git a/Vocaluxe/Lib/Webcam/CAForgeNet.cs b/Vocaluxe/Lib/Webcam/CAForgeNet.cs index 13bc4d20..0d6e9918 100644 --- a/Vocaluxe/Lib/Webcam/CAForgeNet.cs +++ b/Vocaluxe/Lib/Webcam/CAForgeNet.cs @@ -201,13 +201,16 @@ namespace Vocaluxe.Lib.Webcam } } - public bool GetFrame(ref CTexture frame) + public bool GetFrame(ref CTextureRef frame) { lock (_MutexData) { if (_Data != null && _Data.Length == _Width * _Height * 4 && _NewFrameAvailable) { - CDraw.UpdateOrAddTexture(ref frame, _Width, _Height, _Data); + if (frame == null) + frame = CDraw.AddTexture(_Width, _Height, _Data); + else + CDraw.UpdateTexture(frame, _Width, _Height, _Data); _NewFrameAvailable = false; return true; } diff --git a/Vocaluxe/Lib/Webcam/IWebcam.cs b/Vocaluxe/Lib/Webcam/IWebcam.cs index 98ae1804..9960b979 100644 --- a/Vocaluxe/Lib/Webcam/IWebcam.cs +++ b/Vocaluxe/Lib/Webcam/IWebcam.cs @@ -58,7 +58,7 @@ namespace Vocaluxe.Lib.Webcam /// /// /// - bool GetFrame(ref CTexture frame); + bool GetFrame(ref CTextureRef frame); /// /// Gets the last captured frame as a bitma diff --git a/Vocaluxe/Program.cs b/Vocaluxe/Program.cs index b4720e87..65e9a06d 100644 --- a/Vocaluxe/Program.cs +++ b/Vocaluxe/Program.cs @@ -81,7 +81,7 @@ namespace Vocaluxe { // Init Log CLog.Init(); - + CLog.StartBenchmark("Init Program"); if (!CProgrammHelper.CheckRequirements()) return; CProgrammHelper.Init(); @@ -173,17 +173,11 @@ namespace Vocaluxe Application.DoEvents(); - // Init Font - CLog.StartBenchmark("Init Font"); - CFonts.Init(); - CLog.StopBenchmark("Init Font"); - - Application.DoEvents(); - - // Load Cover - CLog.StartBenchmark("Init Cover"); - CCover.Init(); - CLog.StopBenchmark("Init Cover"); + // Init Fonts + CLog.StartBenchmark("Init Fonts"); + if (!CFonts.Init()) + throw new CLoadingException("fonts"); + CLog.StopBenchmark("Init Fonts"); Application.DoEvents(); @@ -195,6 +189,13 @@ namespace Vocaluxe Application.DoEvents(); + // Load Cover + CLog.StartBenchmark("Init Cover"); + CCover.Init(); + CLog.StopBenchmark("Init Cover"); + + Application.DoEvents(); + // Init Screens CLog.StartBenchmark("Init Screens"); CGraphics.Init(); @@ -235,6 +236,7 @@ namespace Vocaluxe //Only reasonable point to call GC.Collect() because initialization may cause lots of garbage //Rely on GC doing its job afterwards and call Dispose methods where appropriate GC.Collect(); + CLog.StopBenchmark("Init Program"); } catch (Exception e) { @@ -250,7 +252,6 @@ namespace Vocaluxe // Start Main Loop if (_SplashScreen != null) _SplashScreen.Close(); - CVocaluxeServer.Start(); CDraw.MainLoop(); } @@ -264,6 +265,7 @@ namespace Vocaluxe CVocaluxeServer.Close(); CGraphics.Close(); CCover.Close(); + CFonts.Close(); CBackgroundMusic.Close(); CWebcam.Close(); CDataBase.Close(); diff --git a/Vocaluxe/Screens/CPopupScreenPlayerControl.cs b/Vocaluxe/Screens/CPopupScreenPlayerControl.cs index e0c1cdf2..4475e1da 100644 --- a/Vocaluxe/Screens/CPopupScreenPlayerControl.cs +++ b/Vocaluxe/Screens/CPopupScreenPlayerControl.cs @@ -195,7 +195,7 @@ namespace Vocaluxe.Screens return false; _Statics[_StaticCover].Texture = CBackgroundMusic.Cover; if (CBackgroundMusic.VideoEnabled && _VideoPreview && CBackgroundMusic.SongHasVideo) - CDraw.DrawTexture(_Statics[_StaticCover], CBackgroundMusic.GetVideoTexture(), EAspect.Crop); + CDraw.DrawTexture(CBackgroundMusic.GetVideoTexture(), _Statics[_StaticCover].Rect, EAspect.Crop); _Buttons[_ButtonPause].Visible = CBackgroundMusic.IsPlaying; _Buttons[_ButtonPlay].Visible = !CBackgroundMusic.IsPlaying; _Texts[_TextCurrentSong].Text = CBackgroundMusic.ArtistAndTitle; diff --git a/Vocaluxe/Screens/CPopupScreenServerQR.cs b/Vocaluxe/Screens/CPopupScreenServerQR.cs index febb08a2..e6d4beeb 100644 --- a/Vocaluxe/Screens/CPopupScreenServerQR.cs +++ b/Vocaluxe/Screens/CPopupScreenServerQR.cs @@ -33,11 +33,11 @@ namespace Vocaluxe.Screens get { return 1; } } - private CTexture _QRServerAddress; - private CTexture _QRAndroidLink; - private CTexture _QRSymbianLink; - private CTexture _QRWebOSLink; - private CTexture _QRWindowsPhoneLink; + private CTextureRef _QRServerAddress; + private CTextureRef _QRAndroidLink; + private CTextureRef _QRSymbianLink; + private CTextureRef _QRWebOSLink; + private CTextureRef _QRWindowsPhoneLink; private const string _StaticQRServer = "StaticQRServer"; private const string _StaticQRAndroid = "StaticQRAndroid"; diff --git a/Vocaluxe/Screens/CScreenCredits.cs b/Vocaluxe/Screens/CScreenCredits.cs index 67dcfdf8..dc9904fc 100644 --- a/Vocaluxe/Screens/CScreenCredits.cs +++ b/Vocaluxe/Screens/CScreenCredits.cs @@ -126,21 +126,21 @@ namespace Vocaluxe.Screens private List _Paragraphs; private List _ParagraphTexts; - private CTexture _TexLogo; - private CTexture _TexPerfectNoteStar; + private CTextureRef _TexLogo; + private CTextureRef _TexPerfectNoteStar; - private CTexture _TexRedDot; - private CTexture _TexBlueDot; + private CTextureRef _TexRedDot; + private CTextureRef _TexBlueDot; - private CTexture _TexNameBrunzel; - private CTexture _TexNameDarkice; - private CTexture _TexNameFlokuep; - private CTexture _TexNameFlamefire; - private CTexture _TexNameMesand; - private CTexture _TexNameBohning; - private CTexture _TexNameBabene03; - private CTexture _TexNamePantero; - private CTexture _TexNamePinky007; + private CTextureRef _TexNameBrunzel; + private CTextureRef _TexNameDarkice; + private CTextureRef _TexNameFlokuep; + private CTextureRef _TexNameFlamefire; + private CTextureRef _TexNameMesand; + private CTextureRef _TexNameBohning; + private CTextureRef _TexNameBabene03; + private CTextureRef _TexNamePantero; + private CTextureRef _TexNamePinky007; public override void Init() { @@ -286,12 +286,12 @@ namespace Vocaluxe.Screens return GetNewParticleEffect(numStars, partColor, rect, _TexPerfectNoteStar, partSize, EParticleType.Star); } - private void _AddNewCreditName(CTexture texture, int particleOffsetX, int particleOffsetY, bool bigParticles) + private void _AddNewCreditName(CTextureRef texture, int particleOffsetX, int particleOffsetY, bool bigParticles) { bool isRight = _CreditNames.Count % 2 == 0; int partRectSize = bigParticles ? 25 : 20; int partCount = bigParticles ? 8 : 6; - CTexture texDot = isRight ? _TexRedDot : _TexBlueDot; + CTextureRef texDot = isRight ? _TexRedDot : _TexBlueDot; CStatic image = GetNewStatic(texture, new SColorF(1, 1, 1, 1), new SRectF(-1, -1, 400, 120, -4)); @@ -360,7 +360,7 @@ namespace Vocaluxe.Screens base.Draw(); //Draw background - CDraw.DrawColor(new SColorF(0, 0.18f, 0.474f, 1), new SRectF(0, 0, CSettings.RenderW, CSettings.RenderH, 0)); + CDraw.DrawRect(new SColorF(0, 0.18f, 0.474f, 1), new SRectF(0, 0, CSettings.RenderW, CSettings.RenderH, 0)); //Draw stars _StarsBlue.Draw(); diff --git a/Vocaluxe/Screens/CScreenNames.cs b/Vocaluxe/Screens/CScreenNames.cs index 3a566060..4e9b3b06 100644 --- a/Vocaluxe/Screens/CScreenNames.cs +++ b/Vocaluxe/Screens/CScreenNames.cs @@ -55,7 +55,7 @@ namespace Vocaluxe.Screens {"EqualizerPlayer1", "EqualizerPlayer2", "EqualizerPlayer3", "EqualizerPlayer4", "EqualizerPlayer5", "EqualizerPlayer6"}; private readonly string[] _SelectSlideDuetPlayer = new string[] {"SelectSlideDuetPlayer1", "SelectSlideDuetPlayer2", "SelectSlideDuetPlayer3", "SelectSlideDuetPlayer4", "SelectSlideDuetPlayer5", "SelectSlideDuetPlayer6"}; - private readonly CTexture[] _OriginalPlayerAvatarTextures = new CTexture[CSettings.MaxNumPlayer]; + private readonly CTextureRef[] _OriginalPlayerAvatarTextures = new CTextureRef[CSettings.MaxNumPlayer]; private bool _SelectingKeyboardActive; private bool _SelectingFast; diff --git a/Vocaluxe/Screens/CScreenOptionsVideo.cs b/Vocaluxe/Screens/CScreenOptionsVideo.cs index 9a6e6a57..81fdb4eb 100644 --- a/Vocaluxe/Screens/CScreenOptionsVideo.cs +++ b/Vocaluxe/Screens/CScreenOptionsVideo.cs @@ -49,7 +49,7 @@ namespace Vocaluxe.Screens private const string _TextWebcamResolution = "TextWebcamResolution"; private SWebcamConfig _Config; - private CTexture _WebcamTexture; + private CTextureRef _WebcamTexture; private int _DeviceNr; private int _CapabilityNr; diff --git a/Vocaluxe/Screens/CScreenProfiles.cs b/Vocaluxe/Screens/CScreenProfiles.cs index 0835e451..532f3a8a 100644 --- a/Vocaluxe/Screens/CScreenProfiles.cs +++ b/Vocaluxe/Screens/CScreenProfiles.cs @@ -64,7 +64,7 @@ namespace Vocaluxe.Screens private EEditMode _EditMode; - private CTexture _WebcamTexture; + private CTextureRef _WebcamTexture; private Bitmap _Snapshot; public override void Init() diff --git a/Vocaluxe/Screens/CScreenSing.cs b/Vocaluxe/Screens/CScreenSing.cs index 077afca4..5e3183d6 100644 --- a/Vocaluxe/Screens/CScreenSing.cs +++ b/Vocaluxe/Screens/CScreenSing.cs @@ -99,9 +99,9 @@ namespace Vocaluxe.Screens private const float _Volume = 100f; private int _CurrentVideo = -1; private EAspect _VideoAspect = EAspect.Crop; - private CTexture _CurrentVideoTexture; - private CTexture _CurrentWebcamFrameTexture; - private CTexture _Background; + private CTextureRef _CurrentVideoTexture; + private CTextureRef _CurrentWebcamFrameTexture; + private CTextureRef _Background; private float _CurrentTime; private float _FinishTime; @@ -274,7 +274,7 @@ namespace Vocaluxe.Screens case Keys.V: if (_VideoAspect == EAspect.Zoom2) - _VideoAspect = 0; + _VideoAspect = EAspect.Crop; else _VideoAspect++; @@ -463,7 +463,7 @@ namespace Vocaluxe.Screens { if (_Active) { - CTexture background; + CTextureRef background; var aspect = EAspect.Crop; if (_CurrentVideo != -1 && CConfig.VideosInSongs == EOffOn.TR_CONFIG_ON && !_Webcam) { @@ -479,11 +479,8 @@ namespace Vocaluxe.Screens background = _Background; if (background != null) { - var bounds = new RectangleF(0, 0, CSettings.RenderW, CSettings.RenderH); - RectangleF rect; - CHelper.SetRect(bounds, out rect, background.OrigAspect, aspect); - CDraw.DrawTexture(background, new SRectF(rect.X, rect.Y, rect.Width, rect.Height, 0f), - background.Color, new SRectF(bounds.X, bounds.Y, bounds.Width, bounds.Height, 0f)); + var bounds = new SRectF(0, 0, CSettings.RenderW, CSettings.RenderH, 0); + CDraw.DrawTexture(background, bounds, aspect, background.Color); } else if (_SlideShow != null) _SlideShow.Draw(); @@ -1004,13 +1001,13 @@ namespace Vocaluxe.Screens float partSeconds = timeDiff - fullSeconds; _Texts[_TextMedleyCountdown].Visible = true; _Texts[_TextMedleyCountdown].Text = fullSeconds.ToString(); - float h = partSeconds * CSettings.RenderH; - float w = CFonts.GetTextBounds(_Texts[_TextMedleyCountdown], h).Width; - float x = CSettings.RenderW / 2 - w / 2; - float y = CSettings.RenderH / 2 - h / 2; + _Texts[_TextMedleyCountdown].Font.Height = partSeconds * CSettings.RenderH; + + RectangleF textBounds = CFonts.GetTextBounds(_Texts[_TextMedleyCountdown]); + float x = CSettings.RenderW / 2 - textBounds.Width / 2; + float y = CSettings.RenderH / 2 - textBounds.Height / 2; _Texts[_TextMedleyCountdown].X = x; _Texts[_TextMedleyCountdown].Y = y; - _Texts[_TextMedleyCountdown].Height = h; } else _Texts[_TextMedleyCountdown].Visible = false; @@ -1216,8 +1213,8 @@ namespace Vocaluxe.Screens } else { - _Statics[_StaticSongText].Color.A = 10f - t / 3f; - _Texts[_TextSongName].Color.A = 10f - t / 3f; + _Statics[_StaticSongText].Color.A = (10f - t) / 3f; + _Texts[_TextSongName].Color.A = (10f - t) / 3f; } } else diff --git a/Vocaluxe/Screens/CScreenSong.cs b/Vocaluxe/Screens/CScreenSong.cs index 89e6d1ef..57a5ed16 100644 --- a/Vocaluxe/Screens/CScreenSong.cs +++ b/Vocaluxe/Screens/CScreenSong.cs @@ -413,7 +413,7 @@ namespace Vocaluxe.Screens _SongMenu.Active = !_PlaylistActive; _ToggleSongOptions(ESongOptionsView.None); } - else if (CHelper.IsInBounds(_SongMenu.Rect, mouseEvent.X, mouseEvent.Y)) + else if (CHelper.IsInBounds(_SongMenu.Rect, mouseEvent)) { _PlaylistActive = false; _Playlist.Selected = _PlaylistActive; diff --git a/Vocaluxe/Vocaluxe.csproj b/Vocaluxe/Vocaluxe.csproj index b8276467..48110d1e 100644 --- a/Vocaluxe/Vocaluxe.csproj +++ b/Vocaluxe/Vocaluxe.csproj @@ -216,6 +216,7 @@ + @@ -224,6 +225,9 @@ + + + @@ -241,7 +245,7 @@ - + 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/CFont.cs b/VocaluxeLib/CFont.cs new file mode 100644 index 00000000..bb8fed4e --- /dev/null +++ b/VocaluxeLib/CFont.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Xml.Serialization; + +namespace VocaluxeLib +{ + public interface IFontObserver + { + void FontChanged(); + } + + public struct SThemeFont + { + public string Name; + public EStyle Style; + public float Size; + } + + public class CFont + { + private string _Name; + private EStyle _Style; + private float _Height; + private readonly List _Observers = new List(); + + public string Name + { + get { return _Name; } + set + { + if (_Name == value) + return; + _Name = value; + foreach (IFontObserver observer in _Observers) + observer.FontChanged(); + } + } + public EStyle Style + { + get { return _Style; } + set + { + if (_Style == value) + return; + _Style = value; + foreach (IFontObserver observer in _Observers) + observer.FontChanged(); + } + } + public float Height + { + get { return _Height; } + set + { + if (Math.Abs(_Height - value) < 0.001) + return; + _Height = value; + foreach (IFontObserver observer in _Observers) + observer.FontChanged(); + } + } + + public CFont() + { + _Name = "Normal"; + _Style = EStyle.Normal; + _Height = 25f; + } + + public CFont(SThemeFont font) + { + _Name = font.Name; + _Style = font.Style; + _Height = font.Size; + } + + public CFont(string fontFamily, EStyle style, float h) + { + _Name = fontFamily; + _Style = style; + _Height = h; + } + + public void AddObserver(IFontObserver observer) + { + if (!_Observers.Contains(observer)) + _Observers.Add(observer); + } + + public void RemoveObserver(IFontObserver observer) + { + _Observers.Remove(observer); + } + } +} \ No newline at end of file diff --git a/VocaluxeLib/CHelper.cs b/VocaluxeLib/CHelper.cs index 7eb1cd7a..d54a2d00 100644 --- a/VocaluxeLib/CHelper.cs +++ b/VocaluxeLib/CHelper.cs @@ -17,11 +17,9 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Drawing; using System.Globalization; using System.IO; -using System.Text; namespace VocaluxeLib { @@ -64,46 +62,6 @@ namespace VocaluxeLib return (int)result; } - /// - /// Makes sure val is between min and max - /// Asserts that min<=max - /// - /// - /// - /// - /// - /// Clamped value - public static T Clamp(this T val, T min, T max) where T : IComparable - { - Debug.Assert(min.CompareTo(max) <= 0); - if (val.CompareTo(min) < 0) - return min; - if (val.CompareTo(max) > 0) - return max; - return val; - } - - /// - /// Makes sure val is between min and max but also handles the case where min>max - /// - /// - /// - /// - /// - /// - /// Clamped value - public static T Clamp(this T val, T min, T max, bool preferMin) where T : IComparable - { - if (min.CompareTo(max) > 0) - { - if (preferMin) - max = min; - else - min = max; - } - return Clamp(val, min, max); - } - /// /// Concat strings into one string with ", " as separator. /// @@ -120,90 +78,88 @@ namespace VocaluxeLib return result; } - public static void SetRect(SRectF bounds, out SRectF rect, float rectAspect, EAspect aspect) + /// + /// Places a rect within the bounds maintaining the aspectRatio and using the given aspect + /// + /// Bounds to fit the rect in + /// The original aspectRatio of the rect/image/... + /// + /// Crop: Fit fully in bounds cutting off overhanging parts (same on both sides)
+ /// LetterBox: Fit the long side possibly leaving some space on the other (rect will be centered)
+ /// StretcH: Just fit the rect in the bounds (rect=bounds) + /// + public static SRectF FitInBounds(SRectF bounds, float aspectRatio, EAspect aspect) { - var bounds2 = new RectangleF(bounds.X, bounds.Y, bounds.W, bounds.H); - RectangleF rect2; - SetRect(bounds2, out rect2, rectAspect, aspect); + if (aspect == EAspect.Stretch) + return bounds; - rect = new SRectF(rect2.X, rect2.Y, rect2.Width, rect2.Height, bounds.Z); - } + float boundsAspectRatio = bounds.W / bounds.H; - public static void SetRect(RectangleF bounds, out RectangleF rect, float rectAspect, EAspect aspect) - { - float boundsW = bounds.Width; - float boundsH = bounds.Height; - float boundsAspect = boundsW / boundsH; - - float scaledWidth; - float scaledHeight; + float scaledWidth, scaledHeight; switch (aspect) { case EAspect.Crop: - if (boundsAspect >= rectAspect) + if (boundsAspectRatio >= aspectRatio) { - scaledWidth = boundsW; - scaledHeight = boundsW / rectAspect; + scaledWidth = bounds.W; + scaledHeight = bounds.W / aspectRatio; } else { - scaledHeight = boundsH; - scaledWidth = boundsH * rectAspect; + scaledHeight = bounds.H; + scaledWidth = bounds.H * aspectRatio; } break; case EAspect.Zoom1: - if (boundsAspect >= rectAspect) + if (boundsAspectRatio >= aspectRatio) { - scaledWidth = boundsW * 1.33f; - scaledHeight = boundsW * 1.33f / rectAspect; + scaledWidth = bounds.W * 1.33f; + scaledHeight = bounds.W * 1.33f / aspectRatio; } else { - scaledHeight = boundsH / 1.33f; - scaledWidth = boundsH / 1.33f * rectAspect; + scaledHeight = bounds.H / 1.33f; + scaledWidth = bounds.H / 1.33f * aspectRatio; } break; case EAspect.Zoom2: - if (boundsAspect >= rectAspect) + if (boundsAspectRatio >= aspectRatio) { - scaledWidth = boundsW * 1.17f; - scaledHeight = boundsW * 1.17f / rectAspect; + scaledWidth = bounds.W * 1.17f; + scaledHeight = bounds.W * 1.17f / aspectRatio; } else { - scaledHeight = boundsH / 1.17f; - scaledWidth = boundsH / 1.17f * rectAspect; + scaledHeight = bounds.H / 1.17f; + scaledWidth = bounds.H / 1.17f * aspectRatio; } break; case EAspect.LetterBox: - if (boundsAspect <= rectAspect) + if (boundsAspectRatio <= aspectRatio) { - scaledWidth = boundsW; - scaledHeight = boundsW / rectAspect; + scaledWidth = bounds.W; + scaledHeight = bounds.W / aspectRatio; } else { - scaledHeight = boundsH; - scaledWidth = boundsH * rectAspect; + scaledHeight = bounds.H; + scaledWidth = bounds.H * aspectRatio; } break; default: - scaledWidth = boundsW; - scaledHeight = boundsH; - break; + return bounds; } + float left = (bounds.W - scaledWidth) / 2 + bounds.X; + float top = (bounds.H - scaledHeight) / 2 + bounds.Y; - float left = (boundsW - scaledWidth) / 2 + bounds.Left; - float upper = (boundsH - scaledHeight) / 2 + bounds.Top; - - rect = new RectangleF(left, upper, scaledWidth, scaledHeight); + return new SRectF(left, top, scaledWidth, scaledHeight, bounds.Z); } /// /// Returns a list with all files in the given path that match a given pattern /// - /// Path to search for + /// Path to search /// Pattern to match (e.g. "*.jpg") /// Search directories recursively /// False for just file names, True for full path @@ -301,7 +257,7 @@ namespace VocaluxeLib public static bool IsInBounds(SRectF bounds, int x, int y) { - return (bounds.X <= x) && (bounds.X + bounds.W >= x) && (bounds.Y <= y) && (bounds.Y + bounds.H >= y); + return ((float)x).IsInRange(bounds.X, bounds.X + bounds.W) && ((float)y).IsInRange(bounds.Y, bounds.Y + bounds.H); } /// @@ -331,40 +287,30 @@ namespace VocaluxeLib { return (n * n + n) / 2; } - } - 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/Draw/CTexture.cs b/VocaluxeLib/Draw/CTexture.cs deleted file mode 100644 index 76a03818..00000000 --- a/VocaluxeLib/Draw/CTexture.cs +++ /dev/null @@ -1,149 +0,0 @@ -#region license -// This file is part of Vocaluxe. -// -// Vocaluxe is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Vocaluxe is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Vocaluxe. If not, see . -#endregion - -using System.Drawing; - -namespace VocaluxeLib.Draw -{ - public class CTexture - { - public int ID = -1; - public int PBO; - //Only used by OpenGL (the texture "name" according to the specs) - public int Name = -1; - - public string TexturePath = ""; - - /// - /// Original size (e.g. of bmp) - /// - public readonly Size OrigSize; - public float OrigAspect - { - get { return (float)OrigSize.Width / OrigSize.Height; } - } - - /// - /// Current size when drawn - /// - public SRectF Rect; - - public SColorF Color = new SColorF(1f, 1f, 1f, 1f); - - private int _W2, _H2; - private readonly bool _UseFullTexture; - /// - /// Internal texture width (on device), a power of 2 if necessary - /// - public int W2 - { - get { return _W2; } - set - { - _W2 = value; - if (_UseFullTexture) - WidthRatio = 1; - else - WidthRatio = (float)OrigSize.Width / _W2; - } - } - /// - /// Internal texture height (on device), a power of 2 if necessary - /// - public int H2 - { - get { return _H2; } - set - { - _H2 = value; - if (_UseFullTexture) - HeightRatio = 1; - else - HeightRatio = (float)OrigSize.Height / _H2; - } - } - - /// - /// Internal use. 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 - /// - public float HeightRatio { get; private set; } - - /// - /// Actual texture space used in x direction - /// - public int UsedWidth - { - get { return (int)(_W2 * WidthRatio); } - } - - /// - /// Actual texture space used in y direction - /// - public int UsedHeight - { - get { return (int)(_H2 * HeightRatio); } - } - - /* - /// - /// Internal use. Specifies if full texture memory should be used - /// Set to true if you resized the original image to the maximum - /// - public bool UseFullTexture - { - get { return _UseFullTexture; } - set - { - _UseFullTexture = value; - if (value) - { - WidthRatio = 1; - HeightRatio = 1; - } - else - { - WidthRatio = (float)OrigSize.Width / _W2; - HeightRatio = (float)OrigSize.Height / _H2; - } - } - } -*/ - - /// - /// Creates a new texture reference - /// - /// Original width (Bitmap size) - /// Original height (Bitmap size) - /// Width in video memory - /// Height in video memory - /// True if it is using the full size of the video memory (no blank parts due to Pow-2-Issue) - public CTexture(int origWidth, int origHeight, int texWidth = 0, int texHeight = 0, bool useFullTexture = false) - { - OrigSize = new Size(origWidth, origHeight); - Rect = new SRectF(0f, 0f, origWidth, origHeight, 0f); - _UseFullTexture = useFullTexture; - - //IMPORTANT: Use setter here to calculate ratios! - W2 = (texWidth > 0) ? texWidth : origWidth; - H2 = (texHeight > 0) ? texHeight : origHeight; - } - } -} \ No newline at end of file diff --git a/VocaluxeLib/Draw/CTextureRef.cs b/VocaluxeLib/Draw/CTextureRef.cs new file mode 100644 index 00000000..d593cb12 --- /dev/null +++ b/VocaluxeLib/Draw/CTextureRef.cs @@ -0,0 +1,87 @@ +#region license +// This file is part of Vocaluxe. +// +// Vocaluxe is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Vocaluxe is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Vocaluxe. If not, see . +#endregion + +using System; +using System.Drawing; + +namespace VocaluxeLib.Draw +{ + /// + /// Reference to a texture in the drawing driver + /// + public class CTextureRef : IDisposable + { + public int ID { get; private set; } + /// + /// Size of original image (e.g. of bmp) + /// + public Size OrigSize; + + public float OrigAspect + { + get { return (float)OrigSize.Width / OrigSize.Height; } + } + + /// + /// Current size when drawn + /// + public SRectF Rect; + + public SColorF Color = new SColorF(1f, 1f, 1f, 1f); + + /// + /// Creates a new texture reference + /// + /// ID of the texture + /// Original size (Bitmap size) + public CTextureRef(int id, Size origSize) + { + ID = id; + OrigSize = origSize; + + Rect = new SRectF(0f, 0f, origSize.Width, origSize.Height, 0f); + } + + ~CTextureRef() + { + Dispose(); + } + + public void Dispose() + { + if (ID >= 0) + { + //Free textures that are no longer referenced + CTextureRef tmp = this; + CBase.Drawing.RemoveTexture(ref tmp); + } + SetRemoved(); + } + + /// + /// Call this from the graphics driver to denote that this reference is remove and no longer valid
+ /// This avoids the finalizer + ///
+ public void SetRemoved() + { + if (ID < 0) + throw new ObjectDisposedException(GetType().Name); + ID = -1; + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/VocaluxeLib/Enums.cs b/VocaluxeLib/Enums.cs index 13a57b6b..5a7b4005 100644 --- a/VocaluxeLib/Enums.cs +++ b/VocaluxeLib/Enums.cs @@ -48,6 +48,13 @@ namespace VocaluxeLib Zoom2 } + public enum EGeneralAlignment + { + Middle, + Start, + End + } + public enum EAlignment { Left, diff --git a/VocaluxeLib/Extensions.cs b/VocaluxeLib/Extensions.cs index f04d5f40..12f06137 100644 --- a/VocaluxeLib/Extensions.cs +++ b/VocaluxeLib/Extensions.cs @@ -17,13 +17,71 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Drawing2D; using System.Globalization; using System.Linq; +using System.Text.RegularExpressions; namespace VocaluxeLib { public static class CExtensions { + /// + /// Makes sure val is between min and max + /// Asserts that min<=max + /// + /// + /// + /// + /// + /// Clamped value + public static T Clamp(this T val, T min, T max) where T : IComparable + { + Debug.Assert(min.CompareTo(max) <= 0); + if (val.CompareTo(min) < 0) + return min; + if (val.CompareTo(max) > 0) + return max; + return val; + } + + /// + /// Makes sure val is between min and max but also handles the case where min>max + /// + /// + /// + /// + /// + /// + /// Clamped value + public static T Clamp(this T val, T min, T max, bool preferMin) where T : IComparable + { + if (min.CompareTo(max) > 0) + { + if (preferMin) + max = min; + else + min = max; + } + return Clamp(val, min, max); + } + + /// + /// Checks if the value is in the specified range + /// + /// + /// + /// + /// + /// + public static bool IsInRange(this T val, T min, T max) where T : IComparable + { + Debug.Assert(min.CompareTo(max) <= 0); + return min.CompareTo(val) <= 0 && max.CompareTo(val) >= 0; + } + /// /// Resizes the given list to the given size. Removes elements or adds default values /// @@ -91,28 +149,13 @@ namespace VocaluxeLib return value.IndexOf(other, StringComparison.CurrentCultureIgnoreCase) >= 0; } + private static Regex _MultipleWhiteSpaceRegEx = new Regex(@" {2,}"); + public static string TrimMultipleWs(this string value) { if (string.IsNullOrEmpty(value)) return ""; - int start = 0; - int len = value.Length; - while (value[start] == ' ') - { - start++; - if (start >= len) - return ""; - } - if (start > 0) - start--; - int end = --len; - while (value[end] == ' ') - end--; - if (end < len) - end++; - if (end - start < len) - value = value.Substring(start, end - start + 1); - return value; + return _MultipleWhiteSpaceRegEx.Replace(value, " "); } /// @@ -134,5 +177,47 @@ namespace VocaluxeLib rect.H + 2 * rect.H * (scale - 1f), rect.Z); } + + public static Size GetSize(this Bitmap bmp) + { + return new Size(bmp.Width, bmp.Height); + } + + public static Rectangle GetRect(this Bitmap bmp) + { + return Rectangle.Round(new Rectangle(0, 0, bmp.Width, bmp.Height)); + } + + /// + /// Resizes the bitmap to the given size creating a copy of it + /// + /// + /// + /// Resized bitmap or null on error + public static Bitmap Resize(this Bitmap bmp, Size newSize) + { + Bitmap result = null; + try + { + //Create a new Bitmap with the new sizes + result = new Bitmap(newSize.Width, newSize.Height); + //Scale the texture + using (Graphics g = Graphics.FromImage(result)) + { + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.SmoothingMode = SmoothingMode.HighQuality; + g.DrawImage(bmp, result.GetRect()); + } + } + catch (Exception) + { + if (result != null) + { + result.Dispose(); + result = null; + } + } + return result; + } } } \ No newline at end of file diff --git a/VocaluxeLib/IBasic.cs b/VocaluxeLib/IBasic.cs index e461c22b..f58ec27b 100644 --- a/VocaluxeLib/IBasic.cs +++ b/VocaluxeLib/IBasic.cs @@ -101,8 +101,8 @@ namespace VocaluxeLib { string GetThemeScreensPath(int partyModeID); int GetSkinIndex(int partyModeID); - CTexture GetSkinTexture(string textureName, int partyModeID); - CTexture GetSkinVideoTexture(string videoName, int partyModeID); + CTextureRef GetSkinTexture(string textureName, int partyModeID); + CTextureRef GetSkinVideoTexture(string videoName, int partyModeID); void SkinVideoResume(string videoName, int partyModeID); void SkinVideoPause(string videoName, int partyModeID); @@ -133,26 +133,27 @@ namespace VocaluxeLib void Play(); void Stop(); - CTexture GetVideoTexture(); + CTextureRef GetVideoTexture(); void LoadPreview(CSong song, float start = 0f); void PlayPreview(float start = -1f); void StopPreview(); - } public interface IDrawing { - void DrawTexture(CTexture texture, SRectF rect); - void DrawTexture(CTexture texture, SRectF rect, SColorF color); - void DrawTexture(CTexture texture, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false); - void DrawTextureReflection(CTexture texture, SRectF rect, SColorF color, SRectF bounds, float reflectionSpace, float reflectionHeight); + void DrawTexture(CTextureRef texture, SRectF rect); + void DrawTexture(CTextureRef texture, SRectF rect, SColorF color); + void DrawTexture(CTextureRef texture, SRectF rect, SColorF color, SRectF bounds, bool mirrored = false); + void DrawTexture(CTextureRef textureRef, SRectF bounds, EAspect aspect); + void DrawTexture(CTextureRef textureRef, SRectF bounds, EAspect aspect, SColorF color); + void DrawTextureReflection(CTextureRef texture, SRectF rect, SColorF color, float reflectionSpace, float reflectionHeight); - CTexture AddTexture(string fileName); - void RemoveTexture(ref CTexture texture); + CTextureRef AddTexture(string fileName); + void RemoveTexture(ref CTextureRef texture); - void DrawColor(SColorF color, SRectF rect); - void DrawColorReflection(SColorF color, SRectF rect, float space, float height); + void DrawRect(SColorF color, SRectF rect); + void DrawRectReflection(SColorF color, SRectF rect, float space, float height); } public interface IGraphics @@ -175,15 +176,11 @@ namespace VocaluxeLib public interface IFonts { - void SetFont(string fontName); - void SetStyle(EStyle fontStyle); - RectangleF GetTextBounds(CText text); - RectangleF GetTextBounds(CText text, float textHeight); - void DrawText(string text, float textHeight, float x, float y, float z, SColorF color); - void DrawTextReflection(string text, float textHeight, float x, float y, float z, SColorF color, float reflectionSpace, float reflectionHeight); - void DrawText(string text, float textHeight, float x, float y, float z, SColorF color, float begin, float end); + void DrawText(string text, CFont font, float x, float y, float z, SColorF color); + void DrawTextReflection(string text, CFont font, float x, float y, float z, SColorF color, float reflectionSpace, float reflectionHeight); + void DrawText(string text, CFont font, float x, float y, float z, SColorF color, float begin, float end); } public interface ILanguage @@ -226,7 +223,7 @@ namespace VocaluxeLib CProfile[] GetProfiles(); EGameDifficulty GetDifficulty(int profileID); string GetPlayerName(int profileID, int playerNum = 0); - CTexture GetAvatar(int profileID); + CTextureRef GetAvatar(int profileID); CAvatar GetAvatarByFilename(string fileName); bool IsProfileIDValid(int profileID); bool IsGuest(int profileID); @@ -267,7 +264,7 @@ namespace VocaluxeLib { int Load(string videoFileName); bool Skip(int streamID, float startPosition, float videoGap); - bool GetFrame(int streamID, ref CTexture videoTexture, float time, out float videoTime); + bool GetFrame(int streamID, ref CTextureRef videoTexture, float time, out float videoTime); bool IsFinished(int streamID); bool Close(int streamID); void SetLoop(int streamID, bool loop = true); @@ -292,12 +289,12 @@ namespace VocaluxeLib public interface ICover { - CTexture GetNoCover(); + CTextureRef GetNoCover(); } public interface IDataBase { - bool GetCover(string fileName, ref CTexture texture, int coverSize); + bool GetCover(string fileName, ref CTextureRef texture, int coverSize); bool GetDataBaseSongInfos(string artist, string title, out int numPlayed, out DateTime dateAdded, out int highscoreID); } @@ -336,10 +333,9 @@ namespace VocaluxeLib void Load(CSong song, float start = 0f); void Stop(); void TogglePause(); - CTexture GetCover(); - CTexture GetVideoTexture(); + CTextureRef GetCover(); + CTextureRef GetVideoTexture(); bool IsPlaying(); float GetLength(); - } } \ No newline at end of file diff --git a/VocaluxeLib/Menu/CBackground.cs b/VocaluxeLib/Menu/CBackground.cs index 9e9c8317..46a64c3d 100644 --- a/VocaluxeLib/Menu/CBackground.cs +++ b/VocaluxeLib/Menu/CBackground.cs @@ -16,12 +16,12 @@ #endregion using System; -using System.Drawing; using System.Xml; using System.Xml.Serialization; using System.Diagnostics; using System.Collections.Generic; using VocaluxeLib.Draw; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu { @@ -37,16 +37,11 @@ namespace VocaluxeLib.Menu [XmlType("Background")] public struct SThemeBackground { - [XmlAttributeAttribute(AttributeName = "Name")] - public string Name; - [XmlElement("Type")] - public EBackgroundTypes Type; - [XmlArray] - public List SlideShowTextures; - [XmlElement("Video")] - public string VideoName; - [XmlElement("Skin")] - public string TextureName; + [XmlAttribute(AttributeName = "Name")] public string Name; + [XmlElement("Type")] public EBackgroundTypes Type; + [XmlArray] public List SlideShowTextures; + [XmlElement("Video")] public string VideoName; + [XmlElement("Skin")] public string TextureName; public SThemeColor Color; } @@ -58,7 +53,7 @@ namespace VocaluxeLib.Menu private int _SlideShowCurrent; private readonly Stopwatch _SlideShowTimer = new Stopwatch(); - private readonly List _SlideShowTextures = new List(); + private readonly List _SlideShowTextures = new List(); public SColorF Color; public bool ThemeLoaded @@ -149,36 +144,31 @@ namespace VocaluxeLib.Menu CBase.Theme.SkinVideoPause(_Theme.VideoName, _PartyModeID); } - public bool Draw() + public void Draw() { bool ok = false; - if (_Theme.Type == EBackgroundTypes.Video && CBase.Config.GetVideoBackgrounds() == EOffOn.TR_CONFIG_ON && - CBase.Config.GetVideosToBackground() == EOffOn.TR_CONFIG_ON && CBase.BackgroundMusic.IsPlaying() && CBase.BackgroundMusic.SongHasVideo() && - CBase.BackgroundMusic.VideoEnabled() && !CBase.BackgroundMusic.IsDisabled()) + if (_Theme.Type == EBackgroundTypes.Video && CBase.Config.GetVideoBackgrounds() == EOffOn.TR_CONFIG_ON) { - Pause(); - ok = _DrawBackgroundMusicVideo(); + if (CBase.Config.GetVideosToBackground() == EOffOn.TR_CONFIG_ON && CBase.BackgroundMusic.IsPlaying() && CBase.BackgroundMusic.SongHasVideo() && + CBase.BackgroundMusic.VideoEnabled()) + { + Pause(); + ok = _DrawBackgroundMusicVideo(); + } + else + { + Resume(); + ok = _DrawVideo(); + } } - else if (_Theme.Type == EBackgroundTypes.Video && CBase.Config.GetVideoBackgrounds() == EOffOn.TR_CONFIG_ON) - { - Resume(); - ok = _DrawVideo(); - } - - - if (_Theme.Type == EBackgroundTypes.SlideShow && _Theme.SlideShowTextures.Count > 0) + else if (_Theme.Type == EBackgroundTypes.SlideShow && _Theme.SlideShowTextures.Count > 0) ok = _DrawSlideShow(); - if (!String.IsNullOrEmpty(_Theme.TextureName) && - (_Theme.Type == EBackgroundTypes.Texture || - (_Theme.Type == EBackgroundTypes.Video && CBase.Config.GetVideoBackgrounds() == EOffOn.TR_CONFIG_OFF))) + if (!String.IsNullOrEmpty(_Theme.TextureName) && (_Theme.Type == EBackgroundTypes.Texture || !ok)) ok = _DrawTexture(); - if (_Theme.Type == EBackgroundTypes.Color || _Theme.Type == EBackgroundTypes.Texture && !ok || - (_Theme.Type == EBackgroundTypes.Video && CBase.Config.GetVideoBackgrounds() == EOffOn.TR_CONFIG_OFF && !ok)) + if (_Theme.Type == EBackgroundTypes.Color || !ok) _DrawColor(); - - return true; } public void UnloadTextures() @@ -200,7 +190,7 @@ namespace VocaluxeLib.Menu _Theme.Type = EBackgroundTypes.SlideShow; if (!String.IsNullOrEmpty(image)) { - CTexture texture = CBase.Drawing.AddTexture(image); + CTextureRef texture = CBase.Drawing.AddTexture(image); if (texture != null) _SlideShowTextures.Add(texture); } @@ -208,9 +198,9 @@ namespace VocaluxeLib.Menu public void RemoveSlideShowTextures() { - foreach (CTexture tex in _SlideShowTextures) + foreach (CTextureRef tex in _SlideShowTextures) { - CTexture texture = tex; + CTextureRef texture = tex; CBase.Drawing.RemoveTexture(ref texture); } _SlideShowTextures.Clear(); @@ -229,23 +219,22 @@ namespace VocaluxeLib.Menu #endregion public #region internal + private static SRectF _GetDrawRect() + { + return new SRectF(0f, 0f, CBase.Settings.GetRenderW(), CBase.Settings.GetRenderH(), CBase.Settings.GetZFar() / 4); + } + private void _DrawColor() { - var bounds = new SRectF(0f, 0f, CBase.Settings.GetRenderW(), CBase.Settings.GetRenderH(), CBase.Settings.GetZFar() / 4); - - CBase.Drawing.DrawColor(Color, bounds); + CBase.Drawing.DrawRect(Color, _GetDrawRect()); } private bool _DrawTexture() { - CTexture texture = CBase.Theme.GetSkinTexture(_Theme.TextureName, _PartyModeID); + CTextureRef texture = CBase.Theme.GetSkinTexture(_Theme.TextureName, _PartyModeID); if (texture != null) { - var bounds = new RectangleF(0f, 0f, CBase.Settings.GetRenderW(), CBase.Settings.GetRenderH()); - RectangleF rect; - CHelper.SetRect(bounds, out rect, texture.OrigAspect, EAspect.Crop); - - CBase.Drawing.DrawTexture(texture, new SRectF(rect.X, rect.Y, rect.Width, rect.Height, CBase.Settings.GetZFar() / 4)); + CBase.Drawing.DrawTexture(texture, _GetDrawRect(), EAspect.Crop); return true; } return false; @@ -266,20 +255,16 @@ namespace VocaluxeLib.Menu _SlideShowTimer.Restart(); if (_SlideShowCurrent + 1 < _SlideShowTextures.Count) _SlideShowCurrent++; - else if (_SlideShowCurrent != 0) + else _SlideShowCurrent = 0; } - CTexture texture = _SlideShowTextures[_SlideShowCurrent]; + CTextureRef texture = _SlideShowTextures[_SlideShowCurrent]; if (texture == null) return false; - var bounds = new RectangleF(0f, 0f, CBase.Settings.GetRenderW(), CBase.Settings.GetRenderH()); - RectangleF rect; - CHelper.SetRect(bounds, out rect, texture.OrigAspect, EAspect.Crop); - - CBase.Drawing.DrawTexture(texture, new SRectF(rect.X, rect.Y, rect.Width, rect.Height, CBase.Settings.GetZFar() / 4)); + CBase.Drawing.DrawTexture(texture, _GetDrawRect(), EAspect.Crop); if (_SlideShowTimer.ElapsedMilliseconds >= CBase.Settings.GetSlideShowImageTime()) { @@ -292,9 +277,9 @@ namespace VocaluxeLib.Menu if (texture != null) { - float alpha = (_SlideShowTimer.ElapsedMilliseconds - CBase.Settings.GetSlideShowImageTime()) / CBase.Settings.GetSlideShowFadeTime(); - CHelper.SetRect(bounds, out rect, texture.OrigAspect, EAspect.Crop); - CBase.Drawing.DrawTexture(texture, new SRectF(rect.X, rect.Y, rect.Width, rect.Height, (CBase.Settings.GetZFar() / 4) - 1), new SColorF(1, 1, 1, alpha)); + SColorF color = texture.Color; + color.A = (_SlideShowTimer.ElapsedMilliseconds - CBase.Settings.GetSlideShowImageTime()) / CBase.Settings.GetSlideShowFadeTime(); + CBase.Drawing.DrawTexture(texture, _GetDrawRect(), EAspect.Crop, color); } } @@ -305,14 +290,10 @@ namespace VocaluxeLib.Menu private bool _DrawVideo() { - CTexture videoTexture = CBase.Theme.GetSkinVideoTexture(_Theme.VideoName, _PartyModeID); + CTextureRef videoTexture = CBase.Theme.GetSkinVideoTexture(_Theme.VideoName, _PartyModeID); if (videoTexture != null) { - var bounds = new RectangleF(0f, 0f, CBase.Settings.GetRenderW(), CBase.Settings.GetRenderH()); - RectangleF rect; - CHelper.SetRect(bounds, out rect, videoTexture.OrigAspect, EAspect.Crop); - - CBase.Drawing.DrawTexture(videoTexture, new SRectF(rect.X, rect.Y, rect.Width, rect.Height, CBase.Settings.GetZFar() / 4)); + CBase.Drawing.DrawTexture(videoTexture, _GetDrawRect(), EAspect.Crop); return true; } return false; @@ -320,14 +301,10 @@ namespace VocaluxeLib.Menu private bool _DrawBackgroundMusicVideo() { - CTexture videoTexture = CBase.BackgroundMusic.GetVideoTexture(); + CTextureRef videoTexture = CBase.BackgroundMusic.GetVideoTexture(); if (videoTexture != null) { - var bounds = new RectangleF(0f, 0f, CBase.Settings.GetRenderW(), CBase.Settings.GetRenderH()); - RectangleF rect; - CHelper.SetRect(bounds, out rect, videoTexture.OrigAspect, EAspect.Crop); - - CBase.Drawing.DrawTexture(videoTexture, new SRectF(rect.X, rect.Y, rect.Width, rect.Height, CBase.Settings.GetZFar() / 4)); + CBase.Drawing.DrawTexture(videoTexture, _GetDrawRect(), EAspect.Crop); return true; } return false; diff --git a/VocaluxeLib/Menu/CButton.cs b/VocaluxeLib/Menu/CButton.cs index f2830195..d2652ba4 100644 --- a/VocaluxeLib/Menu/CButton.cs +++ b/VocaluxeLib/Menu/CButton.cs @@ -19,33 +19,25 @@ using System; using System.Xml; using System.Xml.Serialization; using VocaluxeLib.Draw; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu { [XmlType("Button")] public struct SThemeButton { - [XmlAttributeAttribute(AttributeName = "Name")] - public string Name; + [XmlAttribute(AttributeName = "Name")] public string Name; - [XmlElement("Skin")] - public string TextureName; - [XmlElement("SkinSelected")] - public string SelTextureName; + [XmlElement("Skin")] public string TextureName; + [XmlElement("SkinSelected")] public string SelTextureName; public SRectF Rect; - [XmlElement("Color")] - public SThemeColor Color; - [XmlElement("SelColor")] - public SThemeColor SelColor; - [XmlElement("Text")] - public SThemeText Text; - [XmlElement("SText")] - public SThemeText SText; + [XmlElement("Color")] public SThemeColor Color; + [XmlElement("SelColor")] public SThemeColor SelColor; + [XmlElement("Text")] public SThemeText Text; + [XmlElement("SText")] public SThemeText SText; public bool STextSpecified; - [XmlElement("Reflection")] - public SReflection Reflection; - [XmlElement("SelReflection")] - public SReflection SelReflection; + [XmlElement("Reflection")] public SReflection Reflection; + [XmlElement("SelReflection")] public SReflection SelReflection; } public class CButton : IMenuElement @@ -54,8 +46,8 @@ namespace VocaluxeLib.Menu private bool _ThemeLoaded; private readonly int _PartyModeID; - public CTexture Texture; - public CTexture SelTexture; + public CTextureRef Texture; + public CTextureRef SelTexture; public SRectF Rect; public SColorF Color; public SColorF SelColor; @@ -257,13 +249,12 @@ namespace VocaluxeLib.Menu return _ThemeLoaded; } - public void Draw(bool forceDraw = false) { if (!Visible && CBase.Settings.GetProgramState() != EProgramState.EditTheme && !forceDraw) return; - CTexture texture; + CTextureRef texture; if (!Selected && !Pressed || !_Enabled) { @@ -273,7 +264,7 @@ namespace VocaluxeLib.Menu if (_Reflection) { - CBase.Drawing.DrawTextureReflection(texture, Rect, Color, Rect, _ReflectionSpace, _ReflectionHeight); + CBase.Drawing.DrawTextureReflection(texture, Rect, Color, _ReflectionSpace, _ReflectionHeight); Text.DrawRelative(Rect.X, Rect.Y, _ReflectionHeight, _ReflectionSpace, Rect.H); } else @@ -287,7 +278,7 @@ namespace VocaluxeLib.Menu if (_Reflection) { - CBase.Drawing.DrawTextureReflection(texture, Rect, SelColor, Rect, _ReflectionSpace, _ReflectionHeight); + CBase.Drawing.DrawTextureReflection(texture, Rect, SelColor, _ReflectionSpace, _ReflectionHeight); Text.DrawRelative(Rect.X, Rect.Y, _ReflectionHeight, _ReflectionSpace, Rect.H); } else @@ -301,7 +292,7 @@ namespace VocaluxeLib.Menu if (_Reflection) { - CBase.Drawing.DrawTextureReflection(texture, Rect, SelColor, Rect, _ReflectionSpace, _ReflectionHeight); + CBase.Drawing.DrawTextureReflection(texture, Rect, SelColor, _ReflectionSpace, _ReflectionHeight); _SelText.DrawRelative(Rect.X, Rect.Y, _ReflectionHeight, _ReflectionSpace, Rect.H); } else diff --git a/VocaluxeLib/Menu/CEqualizer.cs b/VocaluxeLib/Menu/CEqualizer.cs index 6cce6455..3faf40a1 100644 --- a/VocaluxeLib/Menu/CEqualizer.cs +++ b/VocaluxeLib/Menu/CEqualizer.cs @@ -18,6 +18,7 @@ using System; using System.Xml; using System.Xml.Serialization; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu { @@ -29,30 +30,20 @@ namespace VocaluxeLib.Menu [XmlType("Equalizer")] public struct SThemeEqualizer { - [XmlAttributeAttribute(AttributeName = "Name")] - public string Name; + [XmlAttribute(AttributeName = "Name")] public string Name; - [XmlElement("Skin")] - public string TextureName; + [XmlElement("Skin")] public string TextureName; - [XmlElement("Rect")] - public SRectF Rect; + [XmlElement("Rect")] public SRectF Rect; - [XmlElement("NumBars")] - public int NumBars; - [XmlElement("Space")] - public float Space; - [XmlElement("Style")] - public EEqualizerStyle Style; - [XmlElement("DrawNegative")] - public EOffOn DrawNegative; + [XmlElement("NumBars")] public int NumBars; + [XmlElement("Space")] public float Space; + [XmlElement("Style")] public EEqualizerStyle Style; + [XmlElement("DrawNegative")] public EOffOn DrawNegative; - [XmlElement("Color")] - public SThemeColor Color; - [XmlElement("MaxColor")] - public SThemeColor MaxColor; - [XmlElement("Reflection")] - public SReflection Reflection; + [XmlElement("Color")] public SThemeColor Color; + [XmlElement("MaxColor")] public SThemeColor MaxColor; + [XmlElement("Reflection")] public SReflection Reflection; } public class CEqualizer : IMenuElement @@ -249,10 +240,10 @@ namespace VocaluxeLib.Menu if (i == _MaxBar) color = MaxColor; - CBase.Drawing.DrawColor(color, bar); + CBase.Drawing.DrawRect(color, bar); if (Reflection) - CBase.Drawing.DrawColorReflection(color, bar, ReflectionSpace, ReflectionHeight); + CBase.Drawing.DrawRectReflection(color, bar, ReflectionSpace, ReflectionHeight); } } @@ -273,7 +264,7 @@ namespace VocaluxeLib.Menu Rect = _Theme.Rect; Space = _Theme.Space; Reflection = _Theme.Reflection.Enabled; - if(Reflection) + if (Reflection) { ReflectionHeight = _Theme.Reflection.Height; ReflectionSpace = _Theme.Reflection.Space; diff --git a/VocaluxeLib/Menu/CLyric.cs b/VocaluxeLib/Menu/CLyric.cs index deddd67c..eb057bbd 100644 --- a/VocaluxeLib/Menu/CLyric.cs +++ b/VocaluxeLib/Menu/CLyric.cs @@ -19,22 +19,19 @@ using System; using System.Xml; using System.Xml.Serialization; using VocaluxeLib.Songs; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu { [XmlType("Lyric")] public struct SThemeLyrics { - [XmlAttributeAttribute(AttributeName = "Name")] - public string Name; + [XmlAttribute(AttributeName = "Name")] public string Name; - [XmlElement("Rect")] - public SRectF Rect; + [XmlElement("Rect")] public SRectF Rect; - [XmlElement("Color")] - public SThemeColor Color; - [XmlElement("SColor")] - public SThemeColor SColor; + [XmlElement("Color")] public SThemeColor Color; + [XmlElement("SColor")] public SThemeColor SColor; } public class CLyric : IMenuElement @@ -192,10 +189,7 @@ namespace VocaluxeLib.Menu private void _SetText(CSongNote note) { _Text.Text = note.Text; - _Text.Style = EStyle.Bold; - - if (note.Type == ENoteType.Freestyle) - _Text.Style = EStyle.BoldItalic; + _Text.Font.Style = (note.Type == ENoteType.Freestyle) ? EStyle.BoldItalic : EStyle.Bold; } public void Clear() @@ -337,11 +331,11 @@ namespace VocaluxeLib.Menu p = 1f - p; float ty = _Text.Y; - float th = _Text.Height; + float th = _Text.Font.Height; float tz = _Text.Z; SRectF normalRect = _Text.Rect; - _Text.Height += _Text.Height * p * 0.4f; + _Text.Font.Height *= 1f + p * 0.4f; _Text.X -= (_Text.Rect.W - normalRect.W) / 2f; _Text.Y -= (_Text.Rect.H - normalRect.H) / 2f; _Text.Z -= 0.1f; @@ -351,7 +345,7 @@ namespace VocaluxeLib.Menu _Text.Draw(); _Text.Y = ty; - _Text.Height = th; + _Text.Font.Height = th; _Text.Z = tz; } } @@ -443,7 +437,7 @@ namespace VocaluxeLib.Menu else { float y = _Text.Y; - _Text.Y -= _Text.Height * 0.1f * p; + _Text.Y -= _Text.Font.Height * 0.1f * p; _Text.Draw(); _Text.Y = y; } diff --git a/VocaluxeLib/Menu/CMenu.cs b/VocaluxeLib/Menu/CMenu.cs index 2eea7aa4..500c1d82 100644 --- a/VocaluxeLib/Menu/CMenu.cs +++ b/VocaluxeLib/Menu/CMenu.cs @@ -25,6 +25,7 @@ using System.Xml.Serialization; using VocaluxeLib.Draw; using VocaluxeLib.Menu.SingNotes; using VocaluxeLib.Menu.SongMenu; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu { @@ -587,7 +588,7 @@ namespace VocaluxeLib.Menu return new CStatic(oldStatic); } - public CStatic GetNewStatic(CTexture texture, SColorF color, SRectF rect) + public CStatic GetNewStatic(CTextureRef texture, SColorF color, SRectF rect) { return new CStatic(_PartyModeID, texture, color, rect); } @@ -632,7 +633,7 @@ namespace VocaluxeLib.Menu return new CPlaylist(_PartyModeID); } - public CParticleEffect GetNewParticleEffect(int maxNumber, SColorF color, SRectF area, CTexture texture, float size, EParticleType type) + public CParticleEffect GetNewParticleEffect(int maxNumber, SColorF color, SRectF area, CTextureRef texture, float size, EParticleType type) { return new CParticleEffect(_PartyModeID, maxNumber, color, area, texture, size, type); } diff --git a/VocaluxeLib/Menu/CMenuPartyNameSelection.cs b/VocaluxeLib/Menu/CMenuPartyNameSelection.cs index 76015829..f5a39dee 100644 --- a/VocaluxeLib/Menu/CMenuPartyNameSelection.cs +++ b/VocaluxeLib/Menu/CMenuPartyNameSelection.cs @@ -683,7 +683,7 @@ namespace VocaluxeLib.Menu for (int i = 0; i < _TeamList[_CurrentTeam].Count; i++) { string name = CBase.Profiles.GetPlayerName(_TeamList[_CurrentTeam][i]); - CTexture avatar = CBase.Profiles.GetAvatar(_TeamList[_CurrentTeam][i]); + CTextureRef avatar = CBase.Profiles.GetAvatar(_TeamList[_CurrentTeam][i]); _SelectSlides[_SelectSlidePlayer].AddValue(name, avatar); } for (int i = _TeamList[_CurrentTeam].Count; i < _NumPlayerTeams[_CurrentTeam]; i++) diff --git a/VocaluxeLib/Menu/CNameSelection.cs b/VocaluxeLib/Menu/CNameSelection.cs index 3787d386..99a39a5d 100644 --- a/VocaluxeLib/Menu/CNameSelection.cs +++ b/VocaluxeLib/Menu/CNameSelection.cs @@ -22,56 +22,39 @@ using System.Xml; using System.Xml.Serialization; using VocaluxeLib.Draw; using VocaluxeLib.Profile; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu { [XmlType("NameSelection")] public struct SThemeNameSelection { - [XmlAttributeAttribute(AttributeName = "Name")] - public string Name; - [XmlElement("Rect")] - public SRectF Rect; - [XmlElement("SkinEmptyTile")] - public string TextureEmptyTileName; - [XmlElement("ColorEmptyTile")] - public SThemeColor ColorEmptyTile; - [XmlElement("SkinTileSelected")] - public string TextureTileSelectedName; - [XmlElement("Tiles")] - public SThemeNameSelectionTiles Tiles; + [XmlAttribute(AttributeName = "Name")] public string Name; + [XmlElement("Rect")] public SRectF Rect; + [XmlElement("SkinEmptyTile")] public string TextureEmptyTileName; + [XmlElement("ColorEmptyTile")] public SThemeColor ColorEmptyTile; + [XmlElement("SkinTileSelected")] public string TextureTileSelectedName; + [XmlElement("Tiles")] public SThemeNameSelectionTiles Tiles; } public struct SThemeNameSelectionTiles { - [XmlElement("W")] - public int W; - [XmlElement("H")] - public int H; - [XmlElement("NumW")] - public int NumW; - [XmlElement("NumH")] - public int NumH; - [XmlElement("SpaceW")] - public float SpaceW; - [XmlElement("SpaceH")] - public float SpaceH; - [XmlElement("Name")] - public SThemeNameSelectionName Name; + [XmlElement("W")] public int W; + [XmlElement("H")] public int H; + [XmlElement("NumW")] public int NumW; + [XmlElement("NumH")] public int NumH; + [XmlElement("SpaceW")] public float SpaceW; + [XmlElement("SpaceH")] public float SpaceH; + [XmlElement("Name")] public SThemeNameSelectionName Name; } public struct SThemeNameSelectionName { - [XmlElement("Space")] - public float Space; - [XmlElement("H")] - public float Height; - [XmlElement("Font")] - public string Font; - [XmlElement("Style")] - public EStyle Style; - [XmlElement("Color")] - public SThemeColor Color; + [XmlElement("Space")] public float Space; + [XmlElement("H")] public float Height; + [XmlElement("Font")] public string Font; + [XmlElement("Style")] public EStyle Style; + [XmlElement("Color")] public SThemeColor Color; } public class CNameSelection : IMenuElement @@ -110,16 +93,15 @@ namespace VocaluxeLib.Menu public SRectF Rect; private readonly List _Tiles; - private CTexture _TextureEmptyTile; - private CTexture _TextureTileSelected; - public CTexture TextureEmptyTile + private CTextureRef _TextureEmptyTile; + private CTextureRef _TextureTileSelected; + public CTextureRef TextureEmptyTile { get { return _TextureEmptyTile; } } public SColorF ColorEmptyTile; - public int Offset; private int _ActualSelection = -1; public int Selection = -1; @@ -517,9 +499,11 @@ namespace VocaluxeLib.Menu { for (int j = 0; j < _Theme.Tiles.NumW; j++) { - var rect = new SRectF(Rect.X + j * (_Theme.Tiles.W + _Theme.Tiles.SpaceW), Rect.Y + i * (_Theme.Tiles.H + _Theme.Tiles.SpaceH), _Theme.Tiles.W, _Theme.Tiles.H, Rect.Z); + var rect = new SRectF(Rect.X + j * (_Theme.Tiles.W + _Theme.Tiles.SpaceW), Rect.Y + i * (_Theme.Tiles.H + _Theme.Tiles.SpaceH), _Theme.Tiles.W, _Theme.Tiles.H, + Rect.Z); var tileStatic = new CStatic(_PartyModeID, _TextureEmptyTile, ColorEmptyTile, rect) {Aspect = EAspect.Crop}; - var tileText = new CText(rect.X + rect.W / 2, rect.Y + rect.H + _Theme.Tiles.Name.Space, rect.Z, _Theme.Tiles.Name.Height, rect.W, EAlignment.Center, _Theme.Tiles.Name.Style, + var tileText = new CText(rect.X + rect.W / 2, rect.Y + rect.H + _Theme.Tiles.Name.Space, rect.Z, _Theme.Tiles.Name.Height, rect.W, EAlignment.Center, + _Theme.Tiles.Name.Style, _Theme.Tiles.Name.Font, _Theme.Tiles.Name.Color.Color, ""); _Tiles.Add(new CTile(tileStatic, tileText, -1)); } diff --git a/VocaluxeLib/Menu/CParticle.cs b/VocaluxeLib/Menu/CParticle.cs index f0ba52a5..b21d52f6 100644 --- a/VocaluxeLib/Menu/CParticle.cs +++ b/VocaluxeLib/Menu/CParticle.cs @@ -21,27 +21,21 @@ using System.Diagnostics; using System.Xml; using System.Xml.Serialization; using VocaluxeLib.Draw; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu { [XmlType("ParticleEffect")] public struct SThemeParticleEffect { - [XmlAttributeAttribute(AttributeName = "Name")] - public string Name; + [XmlAttribute(AttributeName = "Name")] public string Name; - [XmlElement("Skin")] - public string TextureName; - [XmlElement("Rect")] - public SRectF Rect; - [XmlElement("Color")] - public SThemeColor Color; - [XmlElement("Type")] - public EParticleType Type; - [XmlElement("Size")] - public float Size; - [XmlElement("MaxNumber")] - public int MaxNumber; + [XmlElement("Skin")] public string TextureName; + [XmlElement("Rect")] public SRectF Rect; + [XmlElement("Color")] public SThemeColor Color; + [XmlElement("Type")] public EParticleType Type; + [XmlElement("Size")] public float Size; + [XmlElement("MaxNumber")] public int MaxNumber; } public enum EParticleType @@ -58,7 +52,7 @@ namespace VocaluxeLib.Menu #region private vars private readonly int _PartyModeID; private readonly string _TextureName; - private readonly CTexture _Texture; + private readonly CTextureRef _Texture; private SRectF _Rect; private float _Size; private SColorF _Color; @@ -141,7 +135,7 @@ namespace VocaluxeLib.Menu _Rotation = (float)(CBase.Game.GetRandomDouble() * 360.0); } - public CParticle(int partyModeID, CTexture texture, SColorF color, float x, float y, float size, float maxage, float z, float vx, float vy, float vr, float vsize, + public CParticle(int partyModeID, CTextureRef texture, SColorF color, float x, float y, float size, float maxage, float z, float vx, float vy, float vr, float vsize, EParticleType type) { _PartyModeID = partyModeID; @@ -159,8 +153,6 @@ namespace VocaluxeLib.Menu _MaxAge = maxage; _Rotation = (float)(CBase.Game.GetRandomDouble() * 360.0); } - - #endregion Constructors public void Update() @@ -316,7 +308,7 @@ namespace VocaluxeLib.Menu private SThemeParticleEffect _Theme; private bool _ThemeLoaded; - public CTexture Texture; + public CTextureRef Texture; public SColorF Color; public SRectF Rect; @@ -370,7 +362,7 @@ namespace VocaluxeLib.Menu Visible = true; } - public CParticleEffect(int partyModeID, int maxNumber, SColorF color, SRectF rect, CTexture texture, float size, EParticleType type) + public CParticleEffect(int partyModeID, int maxNumber, SColorF color, SRectF rect, CTextureRef texture, float size, EParticleType type) { _PartyModeID = partyModeID; _Theme = new SThemeParticleEffect(); diff --git a/VocaluxeLib/Menu/CPlaylist.cs b/VocaluxeLib/Menu/CPlaylist.cs index e1f283c6..57381f9a 100644 --- a/VocaluxeLib/Menu/CPlaylist.cs +++ b/VocaluxeLib/Menu/CPlaylist.cs @@ -21,6 +21,7 @@ using System.Windows.Forms; using System.Xml; using System.Xml.Serialization; using VocaluxeLib.Songs; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu { @@ -57,48 +58,31 @@ namespace VocaluxeLib.Menu [XmlType("Playlist")] public struct SThemePlaylist { - [XmlAttributeAttribute(AttributeName = "Name")] - public string Name; + [XmlAttribute(AttributeName = "Name")] public string Name; - [XmlElement("Rect")] - public SRectF Rect; + [XmlElement("Rect")] public SRectF Rect; - [XmlElement("EntryHeight")] - public float EntryHeight; + [XmlElement("EntryHeight")] public float EntryHeight; - [XmlElement("SkinBackground")] - public string TextureBackgroundName; - [XmlElement("SkinBackgroundSelected")] - public string SelTextureBackgroundName; + [XmlElement("SkinBackground")] public string TextureBackgroundName; + [XmlElement("SkinBackgroundSelected")] public string SelTextureBackgroundName; - [XmlElement("ColorBackground")] - public SThemeColor ColorBackground; - [XmlElement("SColorBackground")] - public SThemeColor SelColorBackground; + [XmlElement("ColorBackground")] public SThemeColor ColorBackground; + [XmlElement("SColorBackground")] public SThemeColor SelColorBackground; - [XmlElement("Text1")] - public SThemeText SText1; + [XmlElement("Text1")] public SThemeText SText1; - [XmlElement("StaticCover")] - public SThemeStatic SStaticCover; - [XmlElement("StaticPlaylistHeader")] - public SThemeStatic SStaticPlaylistHeader; - [XmlElement("StaticPlaylistFooter")] - public SThemeStatic SStaticPlaylistFooter; + [XmlElement("StaticCover")] public SThemeStatic SStaticCover; + [XmlElement("StaticPlaylistHeader")] public SThemeStatic SStaticPlaylistHeader; + [XmlElement("StaticPlaylistFooter")] public SThemeStatic SStaticPlaylistFooter; - [XmlElement("ButtonPlaylistName")] - public SThemeButton SButtonPlaylistName; - [XmlElement("ButtonPlaylistClose")] - public SThemeButton SButtonPlaylistClose; - [XmlElement("ButtonPlaylistSave")] - public SThemeButton SButtonPlaylistSave; - [XmlElement("ButtonPlaylistDelete")] - public SThemeButton SButtonPlaylistDelete; - [XmlElement("ButtonPlaylistSing")] - public SThemeButton SButtonPlaylistSing; + [XmlElement("ButtonPlaylistName")] public SThemeButton SButtonPlaylistName; + [XmlElement("ButtonPlaylistClose")] public SThemeButton SButtonPlaylistClose; + [XmlElement("ButtonPlaylistSave")] public SThemeButton SButtonPlaylistSave; + [XmlElement("ButtonPlaylistDelete")] public SThemeButton SButtonPlaylistDelete; + [XmlElement("ButtonPlaylistSing")] public SThemeButton SButtonPlaylistSing; - [XmlElement("SelectSlideGameMode")] - public SThemeSelectSlide SSelectSlideGameMode; + [XmlElement("SelectSlideGameMode")] public SThemeSelectSlide SSelectSlideGameMode; } public class CPlaylist : IMenuElement @@ -187,7 +171,7 @@ namespace VocaluxeLib.Menu public SColorF BackgroundSelColor; public CButton ButtonPlaylistName; - public CButton ButtonPlaylistClose; + public CButton ButtonPlaylistClose; public CButton ButtonPlaylistSave; public CButton ButtonPlaylistDelete; public CButton ButtonPlaylistSing; @@ -353,7 +337,7 @@ namespace VocaluxeLib.Menu _ThemeLoaded &= ButtonPlaylistSave.LoadTheme(item, "ButtonPlaylistSave", xmlReader, skinIndex); _ThemeLoaded &= ButtonPlaylistDelete.LoadTheme(item, "ButtonPlaylistDelete", xmlReader, skinIndex); - _ThemeLoaded &=SelectSlideGameMode.LoadTheme(item, "SelectSlideGameMode", xmlReader, skinIndex); + _ThemeLoaded &= SelectSlideGameMode.LoadTheme(item, "SelectSlideGameMode", xmlReader, skinIndex); _ThemeLoaded &= xmlReader.TryGetFloatValue(item + "/EntryHeight", ref _Theme.EntryHeight); if (_ThemeLoaded) @@ -366,11 +350,11 @@ namespace VocaluxeLib.Menu _Theme.SStaticCover = StaticCover.GetTheme(); _Theme.SStaticPlaylistFooter = StaticPlaylistFooter.GetTheme(); _Theme.SStaticPlaylistHeader = StaticPlaylistHeader.GetTheme(); - _Theme.SButtonPlaylistName =ButtonPlaylistName.GetTheme(); - _Theme.SButtonPlaylistSing =ButtonPlaylistSing.GetTheme(); - _Theme.SButtonPlaylistClose =ButtonPlaylistClose.GetTheme(); - _Theme.SButtonPlaylistSave =ButtonPlaylistSave.GetTheme(); - _Theme.SButtonPlaylistDelete =ButtonPlaylistDelete.GetTheme(); + _Theme.SButtonPlaylistName = ButtonPlaylistName.GetTheme(); + _Theme.SButtonPlaylistSing = ButtonPlaylistSing.GetTheme(); + _Theme.SButtonPlaylistClose = ButtonPlaylistClose.GetTheme(); + _Theme.SButtonPlaylistSave = ButtonPlaylistSave.GetTheme(); + _Theme.SButtonPlaylistDelete = ButtonPlaylistDelete.GetTheme(); LoadTextures(); } @@ -405,7 +389,7 @@ namespace VocaluxeLib.Menu public bool IsMouseOver(SMouseEvent mouseEvent) { - return CHelper.IsInBounds(CompleteRect, mouseEvent.X, mouseEvent.Y) || _Interactions.IsMouseOver(mouseEvent); + return CHelper.IsInBounds(CompleteRect, mouseEvent) || _Interactions.IsMouseOver(mouseEvent); } public void UnloadTextures() diff --git a/VocaluxeLib/Menu/CScreenSetting.cs b/VocaluxeLib/Menu/CScreenSetting.cs index 7c003865..2bbc887c 100644 --- a/VocaluxeLib/Menu/CScreenSetting.cs +++ b/VocaluxeLib/Menu/CScreenSetting.cs @@ -19,6 +19,7 @@ using System; using System.Xml; using System.Xml.Serialization; using VocaluxeLib.Draw; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu { @@ -33,12 +34,9 @@ namespace VocaluxeLib.Menu [XmlType("ScreenSetting")] public struct SScreenSetting { - [XmlAttributeAttribute(AttributeName = "Name")] - public string Name; - [XmlElement("Value")] - public string Value; - [XmlElement("Type")] - public ESettingType Type; + [XmlAttribute(AttributeName = "Name")] public string Name; + [XmlElement("Value")] public string Value; + [XmlElement("Type")] public ESettingType Type; } // ReSharper disable ClassNeverInstantiated.Global @@ -131,7 +129,7 @@ namespace VocaluxeLib.Menu } } - private CTexture _GetTextureValue(string value) + private CTextureRef _GetTextureValue(string value) { return CBase.Theme.GetSkinTexture(value, _PartyModeID); } diff --git a/VocaluxeLib/Menu/CSelectSlide.cs b/VocaluxeLib/Menu/CSelectSlide.cs index 3167b238..05289057 100644 --- a/VocaluxeLib/Menu/CSelectSlide.cs +++ b/VocaluxeLib/Menu/CSelectSlide.cs @@ -18,75 +18,50 @@ using System; using System.Collections.Generic; using System.Drawing; -using System.Xml; using System.Xml.Serialization; using VocaluxeLib.Draw; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu { [XmlType("SelectSlide")] public struct SThemeSelectSlide { - [XmlAttributeAttribute(AttributeName = "Name")] - public string Name; + [XmlAttribute(AttributeName = "Name")] public string Name; - [XmlElement("Skin")] - public string TextureName; - [XmlElement("SkinArrowLeft")] - public string TextureArrowLeftName; - [XmlElement("SkinArrowRight")] - public string TextureArrowRightName; + [XmlElement("Skin")] public string TextureName; + [XmlElement("SkinArrowLeft")] public string TextureArrowLeftName; + [XmlElement("SkinArrowRight")] public string TextureArrowRightName; - [XmlElement("SkinSelected")] - public string SelTextureName; - [XmlElement("SkinArrowLeftSelected")] - public string SelTextureArrowLeftName; - [XmlElement("SkinArrowRightSelected")] - public string SelTextureArrowRightName; + [XmlElement("SkinSelected")] public string SelTextureName; + [XmlElement("SkinArrowLeftSelected")] public string SelTextureArrowLeftName; + [XmlElement("SkinArrowRightSelected")] public string SelTextureArrowRightName; - [XmlElement("SkinHighlighted")] - public string HighlightTextureName; + [XmlElement("SkinHighlighted")] public string HighlightTextureName; - [XmlElement("Rect")] - public SRectF Rect; - [XmlElement("RectArrowLeft")] - public SRectF RectArrowLeft; - [XmlElement("RectArrowRight")] - public SRectF RectArrowRight; + [XmlElement("Rect")] public SRectF Rect; + [XmlElement("RectArrowLeft")] public SRectF RectArrowLeft; + [XmlElement("RectArrowRight")] public SRectF RectArrowRight; - [XmlElement("Color")] - public SThemeColor Color; - [XmlElement("SColor")] - public SThemeColor SColor; - [XmlElement("HColor")] - public SThemeColor HColor; + [XmlElement("Color")] public SThemeColor Color; + [XmlElement("SColor")] public SThemeColor SColor; + [XmlElement("HColor")] public SThemeColor HColor; - [XmlElement("ArrowColor")] - public SThemeColor ArrowColor; - [XmlElement("ArrowSColor")] - public SThemeColor ArrowSColor; + [XmlElement("ArrowColor")] public SThemeColor ArrowColor; + [XmlElement("ArrowSColor")] public SThemeColor ArrowSColor; - [XmlElement("TextColor")] - public SThemeColor TextColor; - [XmlElement("TextSColor")] - public SThemeColor TextSColor; + [XmlElement("TextColor")] public SThemeColor TextColor; + [XmlElement("TextSColor")] public SThemeColor TextSColor; - [XmlElement("TextH")] - public float TextH; - [XmlElement("TextRelativeX")] - public float TextRelativeX; - [XmlElement("TextRelativeY")] - public float TextRelativeY; - [XmlElement("TextMaxW")] - public float TextMaxW; + [XmlElement("TextH")] public float TextH; + [XmlElement("TextRelativeX")] public float TextRelativeX; + [XmlElement("TextRelativeY")] public float TextRelativeY; + [XmlElement("TextMaxW")] public float TextMaxW; - [XmlElement("TextFont")] - public string TextFont; - [XmlElement("TextStyle")] - public EStyle TextStyle; + [XmlElement("TextFont")] public string TextFont; + [XmlElement("TextStyle")] public EStyle TextStyle; - [XmlElement("NumVisible")] - public int NumVisible; + [XmlElement("NumVisible")] public int NumVisible; } public class CSelectSlide : IMenuElement, ICloneable @@ -154,7 +129,7 @@ namespace VocaluxeLib.Menu private readonly List _ValueNames; private readonly List _ValuePartyModeIDs; - private readonly List _Textures; + private readonly List _Textures; private readonly List _ValueIndexes; private readonly List _ValueBounds = new List(); @@ -222,7 +197,7 @@ namespace VocaluxeLib.Menu MaxW = 0f; _Selected = false; - _Textures = new List(); + _Textures = new List(); _ValueIndexes = new List(); _ValueNames = new List(); _ValuePartyModeIDs = new List(); @@ -270,7 +245,7 @@ namespace VocaluxeLib.Menu MaxW = slide.MaxW; _Selected = slide._Selected; - _Textures = new List(slide._Textures); + _Textures = new List(slide._Textures); _ValueIndexes = new List(slide._ValueIndexes); _ValueNames = new List(slide._ValueNames); _ValueBounds = new List(slide._ValueBounds); @@ -288,7 +263,7 @@ namespace VocaluxeLib.Menu _Theme = theme; _Selected = false; - _Textures = new List(); + _Textures = new List(); _ValueIndexes = new List(); _ValueNames = new List(); _ValuePartyModeIDs = new List(); @@ -444,17 +419,17 @@ namespace VocaluxeLib.Menu AddValue(value, null, _ValueIndexes.Count, partyModeID); } - public void AddValue(string value, CTexture texture) + public void AddValue(string value, CTextureRef texture) { AddValue(value, texture, _ValueIndexes.Count, _PartyModeID); } - private void _AddValue(string value, CTexture texture, int valueIndex) + private void _AddValue(string value, CTextureRef texture, int valueIndex) { AddValue(value, texture, valueIndex, _PartyModeID); } - public void AddValue(string value, CTexture texture, int valueIndex, int partyModeID) + public void AddValue(string value, CTextureRef texture, int valueIndex, int partyModeID) { _ValueNames.Add(value); _Textures.Add(texture); @@ -475,7 +450,7 @@ namespace VocaluxeLib.Menu _ValueBounds.Clear(); } - public void AddValues(string[] values, CTexture[] textures) + public void AddValues(string[] values, CTextureRef[] textures) { if (values.Length != textures.Length) return; @@ -499,7 +474,7 @@ namespace VocaluxeLib.Menu RenameValue(Selection, newName); } - public void RenameValue(int selection, string newName, CTexture newTexture = null) + public void RenameValue(int selection, string newName, CTextureRef newTexture = null) { if (selection < 0 && selection >= _ValueNames.Count) return; @@ -623,15 +598,15 @@ namespace VocaluxeLib.Menu if (!Visible && CBase.Settings.GetProgramState() != EProgramState.EditTheme) return; - CTexture texture = CBase.Theme.GetSkinTexture(_Theme.TextureName, _PartyModeID); - CTexture textureArrowLeft = CBase.Theme.GetSkinTexture(_Theme.TextureArrowLeftName, _PartyModeID); - CTexture textureArrowRight = CBase.Theme.GetSkinTexture(_Theme.TextureArrowRightName, _PartyModeID); + CTextureRef texture = CBase.Theme.GetSkinTexture(_Theme.TextureName, _PartyModeID); + CTextureRef textureArrowLeft = CBase.Theme.GetSkinTexture(_Theme.TextureArrowLeftName, _PartyModeID); + CTextureRef textureArrowRight = CBase.Theme.GetSkinTexture(_Theme.TextureArrowRightName, _PartyModeID); - CTexture selTexture = CBase.Theme.GetSkinTexture(_Theme.SelTextureName, _PartyModeID); - CTexture selTextureArrowLeft = CBase.Theme.GetSkinTexture(_Theme.SelTextureArrowLeftName, _PartyModeID); - CTexture selTextureArrowRight = CBase.Theme.GetSkinTexture(_Theme.SelTextureArrowRightName, _PartyModeID); + CTextureRef selTexture = CBase.Theme.GetSkinTexture(_Theme.SelTextureName, _PartyModeID); + CTextureRef selTextureArrowLeft = CBase.Theme.GetSkinTexture(_Theme.SelTextureArrowLeftName, _PartyModeID); + CTextureRef selTextureArrowRight = CBase.Theme.GetSkinTexture(_Theme.SelTextureArrowRightName, _PartyModeID); - CTexture highlightTexture = CBase.Theme.GetSkinTexture(_Theme.HighlightTextureName, _PartyModeID); + CTextureRef highlightTexture = CBase.Theme.GetSkinTexture(_Theme.HighlightTextureName, _PartyModeID); if (Selected) { @@ -773,7 +748,6 @@ namespace VocaluxeLib.Menu TextRelativeX = _Theme.TextRelativeX; TextRelativeY = _Theme.TextRelativeY; MaxW = _Theme.TextMaxW; - } public void ReloadTextures() diff --git a/VocaluxeLib/Menu/CStatic.cs b/VocaluxeLib/Menu/CStatic.cs index b8012f4b..a223dc05 100644 --- a/VocaluxeLib/Menu/CStatic.cs +++ b/VocaluxeLib/Menu/CStatic.cs @@ -19,16 +19,15 @@ using System; using System.Xml; using System.Xml.Serialization; using VocaluxeLib.Draw; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu { [XmlType("Static")] public struct SThemeStatic { - [XmlAttributeAttribute(AttributeName = "Name")] - public string Name; - [XmlElement("Skin")] - public string TextureName; + [XmlAttribute(AttributeName = "Name")] public string Name; + [XmlElement("Skin")] public string TextureName; public SThemeColor Color; public SRectF Rect; public SReflection Reflection; @@ -51,8 +50,8 @@ namespace VocaluxeLib.Menu get { return _ThemeLoaded; } } - private CTexture _Texture; - public CTexture Texture + private CTextureRef _Texture; + public CTextureRef Texture { get { return _Texture ?? CBase.Theme.GetSkinTexture(_Theme.TextureName, _PartyModeID); } @@ -73,10 +72,7 @@ namespace VocaluxeLib.Menu public EAspect Aspect = EAspect.Stretch; - public CStatic() - { - - } + public CStatic() {} public CStatic(int partyModeID) { @@ -99,7 +95,7 @@ namespace VocaluxeLib.Menu Visible = s.Visible; } - public CStatic(int partyModeID, CTexture texture, SColorF color, SRectF rect) + public CStatic(int partyModeID, CTextureRef texture, SColorF color, SRectF rect) { _PartyModeID = partyModeID; @@ -179,26 +175,27 @@ namespace VocaluxeLib.Menu public void Draw(EAspect aspect, float scale = 1f, float zModify = 0f, bool forceDraw = false) { - CTexture texture = Texture ?? new CTexture((int)Rect.W, (int)Rect.H); - - SRectF bounds = Rect.Scale(scale); - SRectF rect; - if (aspect != EAspect.Stretch) - CHelper.SetRect(bounds, out rect, texture.OrigAspect, aspect); - else - rect = bounds; - rect.Z = Rect.Z + zModify; + CTextureRef texture = Texture; + SRectF rect = Rect.Scale(scale); + rect.Z += zModify; + if (texture != null) + rect = CHelper.FitInBounds(rect, texture.OrigAspect, aspect); var color = new SColorF(Color.R, Color.G, Color.B, Color.A * Alpha); if (Visible || forceDraw || (CBase.Settings.GetProgramState() == EProgramState.EditTheme)) { - CBase.Drawing.DrawTexture(texture, rect, color, bounds); - if (Reflection) - CBase.Drawing.DrawTextureReflection(texture, rect, color, bounds, ReflectionSpace, ReflectionHeight); + if (texture != null) + { + CBase.Drawing.DrawTexture(texture, rect, color); + if (Reflection) + CBase.Drawing.DrawTextureReflection(texture, rect, color, ReflectionSpace, ReflectionHeight); + } + else + CBase.Drawing.DrawRect(color, rect); } if (Selected && (CBase.Settings.GetProgramState() == EProgramState.EditTheme)) - CBase.Drawing.DrawColor(new SColorF(1f, 1f, 1f, 0.5f), rect); + CBase.Drawing.DrawRect(new SColorF(1f, 1f, 1f, 0.5f), rect); } public void UnloadTextures() {} diff --git a/VocaluxeLib/Menu/CText.cs b/VocaluxeLib/Menu/CText.cs index bb6d567e..9f8c164f 100644 --- a/VocaluxeLib/Menu/CText.cs +++ b/VocaluxeLib/Menu/CText.cs @@ -17,43 +17,32 @@ using System; using System.Drawing; -using System.Xml; using System.Xml.Serialization; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu { [XmlType("Text")] public struct SThemeText { - [XmlAttributeAttribute(AttributeName = "Name")] - public string Name; + [XmlAttribute(AttributeName = "Name")] public string Name; - [XmlElement("X")] - public float X; - [XmlElement("Y")] - public float Y; - [XmlElement("Z")] - public float Z; - [XmlElement("H")] - public float Height; - [XmlElement("MaxW")] - public float MaxWidth; + [XmlElement("X")] public float X; + [XmlElement("Y")] public float Y; + [XmlElement("Z")] public float Z; + [XmlElement("H")] public float FontHeight; + [XmlElement("MaxW")] public float MaxWidth; public SThemeColor Color; public SThemeColor SColor; //for Buttons - [XmlElement("Align")] - public EAlignment Align; - [XmlElement("ResizeAlign")] - public EHAlignment ResizeAlign; - [XmlElement("Style")] - public EStyle Style; - [XmlElement("Font")] - public string Font; - [XmlElement("Text")] - public string Text; + [XmlElement("Align")] public EAlignment Align; + [XmlElement("ResizeAlign")] public EHAlignment ResizeAlign; + [XmlElement("Style")] public EStyle FontStyle; + [XmlElement("Font")] public string FontFamily; + [XmlElement("Text")] public string Text; public SReflection Reflection; } - public class CText : IMenuElement + public class CText : IMenuElement, IFontObserver { private SThemeText _Theme; private bool _ThemeLoaded; @@ -110,20 +99,6 @@ namespace VocaluxeLib.Menu set { _Z = value; } } - private float _Height; - public float Height - { - get { return _Height; } - set - { - if (Math.Abs(_Height - value) > 0.01) - { - _Height = value; - _PositionNeedsUpdate = true; - } - } - } - private float _MaxWidth; public float MaxWidth { @@ -166,29 +141,17 @@ namespace VocaluxeLib.Menu } } - private EStyle _Style = EStyle.Normal; - public EStyle Style - { - get { return _Style; } - set - { - if (_Style != value) - { - _Style = value; - _PositionNeedsUpdate = true; - } - } - } - - private string _Font = String.Empty; - public string Font + private CFont _Font = new CFont(); + public CFont Font { get { return _Font; } set { - if (_Font != value) + if (!_Font.Equals(value)) { + _Font.RemoveObserver(this); _Font = value; + _Font.AddObserver(this); _PositionNeedsUpdate = true; } } @@ -227,10 +190,16 @@ namespace VocaluxeLib.Menu _Text = translation; if (_EditMode) _Text += "|"; + //Reset height, it might have been reduced in _UpdatePosition + Font.Height = _Theme.FontHeight; _PositionNeedsUpdate = true; } } } + public string TranslatedText + { + get { return _Text; } + } public int PartyModeID { @@ -274,7 +243,7 @@ namespace VocaluxeLib.Menu { _PartyModeID = partyModeID; _TranslationID = _PartyModeID; - _Font = "Normal"; + _Font.AddObserver(this); } public CText(CText text) @@ -285,12 +254,10 @@ namespace VocaluxeLib.Menu _X = text._X; _Y = text._Y; _Z = text._Z; - _Height = text._Height; _MaxWidth = text._MaxWidth; _Align = text._Align; _ResizeAlign = text._ResizeAlign; - _Style = text._Style; - _Font = text._Font; + Font = text.Font; //Use setter to set observer Color = new SColorF(text.Color); SelColor = new SColorF(text.SelColor); @@ -305,24 +272,23 @@ namespace VocaluxeLib.Menu _EditMode = text._EditMode; } - public CText(float x, float y, float z, float h, float mw, EAlignment align, EStyle style, string font, SColorF col, string text, int partyModeID = -1, float rheight = 0, - float rspace = 0) + public CText(float x, float y, float z, float h, float mw, EAlignment align, EStyle style, string fontFamily, SColorF col, string text, int partyModeID = -1, + float rheight = 0, + float rspace = 0) : this(partyModeID) { - _Theme = new SThemeText(); + _Theme = new SThemeText {FontFamily = fontFamily, FontStyle = style, FontHeight = h, Text = text}; _ThemeLoaded = false; _ButtonText = false; - _PartyModeID = partyModeID; - _TranslationID = _PartyModeID; X = x; Y = y; Z = z; - Height = h; MaxWidth = mw; Align = align; ResizeAlign = EHAlignment.Center; - Style = style; - Font = font; + _Font.Name = fontFamily; + _Font.Style = style; + _Font.Height = h; Color = col; SelColor = new SColorF(col); @@ -362,7 +328,6 @@ namespace VocaluxeLib.Menu if (!buttonText) _ThemeLoaded &= xmlReader.TryGetFloatValue(item + "/Z", ref _Z); - _ThemeLoaded &= xmlReader.TryGetFloatValue(item + "/H", ref _Height); xmlReader.TryGetFloatValue(item + "/MaxW", ref _MaxWidth); if (xmlReader.GetValue(item + "/Color", out _Theme.Color.Name, String.Empty)) @@ -389,8 +354,9 @@ namespace VocaluxeLib.Menu _ThemeLoaded &= xmlReader.TryGetEnumValue(item + "/Align", ref _Align); xmlReader.TryGetEnumValue(item + "/ResizeAlign", ref _ResizeAlign); - _ThemeLoaded &= xmlReader.TryGetEnumValue(item + "/Style", ref _Style); - _ThemeLoaded &= xmlReader.GetValue(item + "/Font", out _Font, "Normal"); + _ThemeLoaded &= xmlReader.TryGetFloatValue(item + "/H", ref _Theme.FontHeight); + _ThemeLoaded &= xmlReader.TryGetEnumValue(item + "/Style", ref _Theme.FontStyle); + _ThemeLoaded &= xmlReader.GetValue(item + "/Font", out _Theme.FontFamily); _ThemeLoaded &= xmlReader.GetValue(item + "/Text", out _Theme.Text, String.Empty); @@ -407,15 +373,12 @@ namespace VocaluxeLib.Menu _Theme.Name = elementName; _Theme.Align = _Align; _Theme.Color.Color = Color; - _Theme.Font = _Font; _Theme.ResizeAlign = _ResizeAlign; _Theme.SColor.Color = SelColor; - _Theme.Style = _Style; _Theme.MaxWidth = _MaxWidth; _Theme.X = _X; _Theme.Y = _Y; _Theme.Z = _Z; - _Theme.Height = _Height; _ButtonText = buttonText; _PositionNeedsUpdate = true; @@ -425,7 +388,6 @@ namespace VocaluxeLib.Menu return _ThemeLoaded; } - public void Draw(bool forceDraw = false) { if (!forceDraw && !Visible && CBase.Settings.GetProgramState() != EProgramState.EditTheme) @@ -434,30 +396,24 @@ namespace VocaluxeLib.Menu // Update Text Text = Text; - CBase.Fonts.SetFont(Font); - CBase.Fonts.SetStyle(Style); - SColorF currentColor = (Selected) ? SelColor : Color; var color = new SColorF(currentColor.R, currentColor.G, currentColor.B, currentColor.A * Alpha); - CBase.Fonts.DrawText(_Text, Rect.H, Rect.X, Rect.Y, Z, color); + CBase.Fonts.DrawText(_Text, Font, Rect.X, Rect.Y, Z, color); if (ReflectionHeight > 0) - CBase.Fonts.DrawTextReflection(_Text, Rect.H, Rect.X, Rect.Y, Z, color, ReflectionSpace, ReflectionHeight); + CBase.Fonts.DrawTextReflection(_Text, Font, Rect.X, Rect.Y, Z, color, ReflectionSpace, ReflectionHeight); if (Selected && (CBase.Settings.GetProgramState() == EProgramState.EditTheme)) - CBase.Drawing.DrawColor(new SColorF(0.5f, 1f, 0.5f, 0.5f), new SRectF(Rect.X, Rect.Y, Rect.W, Rect.H, Z)); + CBase.Drawing.DrawRect(new SColorF(0.5f, 1f, 0.5f, 0.5f), new SRectF(Rect.X, Rect.Y, Rect.W, Rect.H, Z)); } public void Draw(float begin, float end) { - CBase.Fonts.SetFont(Font); - CBase.Fonts.SetStyle(Style); - SColorF currentColor = (Selected) ? SelColor : Color; var color = new SColorF(currentColor.R, currentColor.G, currentColor.B, currentColor.A * Alpha); - CBase.Fonts.DrawText(Text, Rect.H, Rect.X, Rect.Y, Z, color, begin, end); + CBase.Fonts.DrawText(Text, Font, Rect.X, Rect.Y, Z, color, begin, end); if (ReflectionHeight > 0) { @@ -465,7 +421,7 @@ namespace VocaluxeLib.Menu } if (Selected && (CBase.Settings.GetProgramState() == EProgramState.EditTheme)) - CBase.Drawing.DrawColor(new SColorF(0.5f, 1f, 0.5f, 0.5f), new SRectF(X, Y, Rect.W, Rect.H, Z)); + CBase.Drawing.DrawRect(new SColorF(0.5f, 1f, 0.5f, 0.5f), new SRectF(X, Y, Rect.W, Rect.H, Z)); } public void DrawRelative(float rx, float ry, float reflectionHeight = 0f, float reflectionSpace = 0f, float rectHeight = 0f) @@ -505,12 +461,10 @@ namespace VocaluxeLib.Menu _X = _Theme.X; _Y = _Theme.Y; _Z = _Theme.Z; - _Height = _Theme.Height; _MaxWidth = _Theme.MaxWidth; _Align = _Theme.Align; _ResizeAlign = _Theme.ResizeAlign; - _Style = _Theme.Style; - _Font = _Theme.Font; + Font = new CFont(_Theme.FontFamily, _Theme.FontStyle, _Theme.FontHeight); if (_Theme.Reflection.Enabled) { @@ -541,17 +495,16 @@ namespace VocaluxeLib.Menu X += stepX; Y += stepY; - _Theme.X += stepX; - _Theme.Y += stepY; + _Theme.X = X; + _Theme.Y = Y; } public void ResizeElement(int stepW, int stepH) { - Height += stepH; - if (Height <= 0) - Height = 1; - - _Theme.Height = _Height; + _Theme.FontHeight += stepH; + if (_Theme.FontHeight <= 0) + _Theme.FontHeight = 1; + _Font.Height = _Theme.FontHeight; } #endregion ThemeEdit @@ -560,10 +513,7 @@ namespace VocaluxeLib.Menu if (_Text == "") return; - CBase.Fonts.SetFont(Font); - CBase.Fonts.SetStyle(Style); - - float h = Height; + float h = Font.Height; float y = Y; RectangleF bounds = CBase.Fonts.GetTextBounds(this); @@ -584,8 +534,8 @@ namespace VocaluxeLib.Menu y += step * 0.75f; break; } - - bounds = CBase.Fonts.GetTextBounds(this, h); + Font.Height = h; + bounds = CBase.Fonts.GetTextBounds(this); } float x = X; @@ -606,5 +556,10 @@ namespace VocaluxeLib.Menu _PositionNeedsUpdate = false; } + + public void FontChanged() + { + _PositionNeedsUpdate = true; + } } } \ No newline at end of file diff --git a/VocaluxeLib/Menu/IMenuElement.cs b/VocaluxeLib/Menu/IMenuElement.cs index dc805d70..391dd1ad 100644 --- a/VocaluxeLib/Menu/IMenuElement.cs +++ b/VocaluxeLib/Menu/IMenuElement.cs @@ -16,6 +16,7 @@ #endregion using System.Xml; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu { diff --git a/VocaluxeLib/Menu/SingNotes/CSingNotes.cs b/VocaluxeLib/Menu/SingNotes/CSingNotes.cs index 41b87e8a..276f586b 100644 --- a/VocaluxeLib/Menu/SingNotes/CSingNotes.cs +++ b/VocaluxeLib/Menu/SingNotes/CSingNotes.cs @@ -18,10 +18,10 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Xml; using System.Xml.Serialization; using VocaluxeLib.Draw; using VocaluxeLib.Songs; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu.SingNotes { @@ -42,42 +42,30 @@ namespace VocaluxeLib.Menu.SingNotes } [XmlType("Position")] - public struct SBarPosition + public struct SBarPosition { - [XmlAttributeAttribute(AttributeName = "Name")] - public string Name; + [XmlAttribute(AttributeName = "Name")] public string Name; public SRectF Rect; } [XmlType("SingBar")] public struct SThemeSingBar { - [XmlAttributeAttribute(AttributeName = "Name")] - public string Name; + [XmlAttribute(AttributeName = "Name")] public string Name; - [XmlElement("SkinLeft")] - public string SkinLeftName; - [XmlElement("SkinMiddle")] - public string SkinMiddleName; - [XmlElement("SkinRight")] - public string SkinRightName; + [XmlElement("SkinLeft")] public string SkinLeftName; + [XmlElement("SkinMiddle")] public string SkinMiddleName; + [XmlElement("SkinRight")] public string SkinRightName; - [XmlElement("SkinBackgroundLeft")] - public string SkinBackgroundLeftName; - [XmlElement("SkinBackgroundMiddle")] - public string SkinBackgroundMiddleName; - [XmlElement("SkinBackgroundRight")] - public string SkinBackgroundRightName; + [XmlElement("SkinBackgroundLeft")] public string SkinBackgroundLeftName; + [XmlElement("SkinBackgroundMiddle")] public string SkinBackgroundMiddleName; + [XmlElement("SkinBackgroundRight")] public string SkinBackgroundRightName; - [XmlElement("SkinGoldenStar")] - public string SkinGoldenStarName; - [XmlElement("SkinToneHelper")] - public string SkinToneHelperName; - [XmlElement("SkinPerfectNoteStart")] - public string SkinPerfectNoteStarName; + [XmlElement("SkinGoldenStar")] public string SkinGoldenStarName; + [XmlElement("SkinToneHelper")] public string SkinToneHelperName; + [XmlElement("SkinPerfectNoteStart")] public string SkinPerfectNoteStarName; - [XmlArray("BarPositions")] - public SBarPosition[] BarPos; + [XmlArray("BarPositions")] public SBarPosition[] BarPos; } public abstract class CSingNotes : IMenuElement @@ -116,7 +104,7 @@ namespace VocaluxeLib.Menu.SingNotes _PlayerNotes = new List(); _ActID = 0; - BarPos = new SRectF[CBase.Settings.GetMaxNumPlayer(), CBase.Settings.GetMaxNumPlayer()]; + BarPos = new SRectF[CBase.Settings.GetMaxNumPlayer(),CBase.Settings.GetMaxNumPlayer()]; LoadTextures(); } @@ -152,21 +140,18 @@ namespace VocaluxeLib.Menu.SingNotes BarPos = new SRectF[CBase.Settings.GetMaxNumPlayer(),CBase.Settings.GetMaxNumPlayer()]; for (int numplayer = 0; numplayer < CBase.Settings.GetMaxNumPlayer(); numplayer++) { - for (int player = 0; player < CBase.Settings.GetMaxNumPlayer(); player++) + for (int player = 0; player <= numplayer; player++) { - if (player <= numplayer) - { - BarPos[player, numplayer] = new SRectF(); - string target = "/BarPositions/P" + (player + 1) + "N" + (numplayer + 1); - _ThemeLoaded &= xmlReader.TryGetFloatValue(item + target + "X", ref BarPos[player, numplayer].X); - _ThemeLoaded &= xmlReader.TryGetFloatValue(item + target + "Y", ref BarPos[player, numplayer].Y); - _ThemeLoaded &= xmlReader.TryGetFloatValue(item + target + "Z", ref BarPos[player, numplayer].Z); - _ThemeLoaded &= xmlReader.TryGetFloatValue(item + target + "W", ref BarPos[player, numplayer].W); - _ThemeLoaded &= xmlReader.TryGetFloatValue(item + target + "H", ref BarPos[player, numplayer].H); - _Theme.BarPos[i].Name = "P" + (player + 1) + "N" + (numplayer + 1); - _Theme.BarPos[i].Rect = new SRectF(BarPos[player, numplayer]); - i++; - } + BarPos[player, numplayer] = new SRectF(); + string target = "/BarPositions/P" + (player + 1) + "N" + (numplayer + 1); + _ThemeLoaded &= xmlReader.TryGetFloatValue(item + target + "X", ref BarPos[player, numplayer].X); + _ThemeLoaded &= xmlReader.TryGetFloatValue(item + target + "Y", ref BarPos[player, numplayer].Y); + _ThemeLoaded &= xmlReader.TryGetFloatValue(item + target + "Z", ref BarPos[player, numplayer].Z); + _ThemeLoaded &= xmlReader.TryGetFloatValue(item + target + "W", ref BarPos[player, numplayer].W); + _ThemeLoaded &= xmlReader.TryGetFloatValue(item + target + "H", ref BarPos[player, numplayer].H); + _Theme.BarPos[i].Name = "P" + (player + 1) + "N" + (numplayer + 1); + _Theme.BarPos[i].Rect = new SRectF(BarPos[player, numplayer]); + i++; } } @@ -179,7 +164,6 @@ namespace VocaluxeLib.Menu.SingNotes return _ThemeLoaded; } - public void Reset() { _PlayerNotes.Clear(); @@ -317,7 +301,7 @@ namespace VocaluxeLib.Menu.SingNotes ); _DrawNoteBG(rect, color, 1f, _PlayerNotes[n].Timer); - _DrawNote(rect, new SColorF(5f, 5f, 5f, 0.7f * _PlayerNotes[n].Alpha), 0.7f); + _DrawNote(rect, new SColorF(1f, 1f, 1f, 0.7f * _PlayerNotes[n].Alpha), 0.7f); if (note.Type == ENoteType.Golden) { @@ -428,7 +412,7 @@ namespace VocaluxeLib.Menu.SingNotes public void UnloadTextures() {} - public void LoadTextures() + public void LoadTextures() { foreach (SBarPosition bp in _Theme.BarPos) { @@ -471,9 +455,9 @@ namespace VocaluxeLib.Menu.SingNotes var noteRect = new SRectF(rect.X + dw, rect.Y + dh, rect.W - 2 * dw, rect.H - 2 * dh, rect.Z); - CTexture noteBegin = CBase.Theme.GetSkinTexture(_Theme.SkinLeftName, _PartyModeID); - CTexture noteMiddle = CBase.Theme.GetSkinTexture(_Theme.SkinMiddleName, _PartyModeID); - CTexture noteEnd = CBase.Theme.GetSkinTexture(_Theme.SkinRightName, _PartyModeID); + CTextureRef noteBegin = CBase.Theme.GetSkinTexture(_Theme.SkinLeftName, _PartyModeID); + CTextureRef noteMiddle = CBase.Theme.GetSkinTexture(_Theme.SkinMiddleName, _PartyModeID); + CTextureRef noteEnd = CBase.Theme.GetSkinTexture(_Theme.SkinRightName, _PartyModeID); //Width of each of the ends (round parts) //Need 2 of them so use minimum @@ -518,9 +502,9 @@ namespace VocaluxeLib.Menu.SingNotes rect.Z ); - CTexture noteBackgroundBegin = CBase.Theme.GetSkinTexture(_Theme.SkinBackgroundLeftName, _PartyModeID); - CTexture noteBackgroundMiddle = CBase.Theme.GetSkinTexture(_Theme.SkinBackgroundMiddleName, _PartyModeID); - CTexture noteBackgroundEnd = CBase.Theme.GetSkinTexture(_Theme.SkinBackgroundRightName, _PartyModeID); + CTextureRef noteBackgroundBegin = CBase.Theme.GetSkinTexture(_Theme.SkinBackgroundLeftName, _PartyModeID); + CTextureRef noteBackgroundMiddle = CBase.Theme.GetSkinTexture(_Theme.SkinBackgroundMiddleName, _PartyModeID); + CTextureRef noteBackgroundEnd = CBase.Theme.GetSkinTexture(_Theme.SkinBackgroundRightName, _PartyModeID); float dx = r.H * noteBackgroundBegin.OrigAspect; if (2 * dx > r.W) @@ -538,12 +522,13 @@ namespace VocaluxeLib.Menu.SingNotes CBase.Drawing.DrawTexture(noteBackgroundEnd, new SRectF(r.X + r.W - dx, r.Y, dx, r.H, r.Z), col); } - protected void _DrawNoteLines(SRectF rect, SColorF color) + private static void _DrawNoteLines(SRectF rect, SColorF color) { - for (int i = 0; i < CBase.Settings.GetNumNoteLines() - 1; i++) + SRectF lineRect = new SRectF(rect) {H = 1.5f}; + for (int i = 0; i < CBase.Settings.GetNumNoteLines(); i++) { - float y = rect.Y + rect.H / CBase.Settings.GetNumNoteLines() * (i + 1); - CBase.Drawing.DrawColor(color, new SRectF(rect.X, y, rect.W, 1, -1.0f)); + lineRect.Y = rect.Y + rect.H / CBase.Settings.GetNumNoteLines() * (i + 1); + CBase.Drawing.DrawRect(color, lineRect); } } @@ -613,7 +598,7 @@ namespace VocaluxeLib.Menu.SingNotes rect.Z ); - CTexture noteBegin = CBase.Theme.GetSkinTexture(_Theme.SkinLeftName, _PartyModeID); + CTextureRef noteBegin = CBase.Theme.GetSkinTexture(_Theme.SkinLeftName, _PartyModeID); float dx = r.H * noteBegin.OrigAspect; if (2 * dx > r.W) dx = r.W / 2; @@ -670,7 +655,7 @@ namespace VocaluxeLib.Menu.SingNotes _PlayerNotes[n].Color.B, _PlayerNotes[n].Color.A * _PlayerNotes[n].Alpha); - CTexture toneHelper = CBase.Theme.GetSkinTexture(_Theme.SkinToneHelperName, _PartyModeID); + CTextureRef toneHelper = CBase.Theme.GetSkinTexture(_Theme.SkinToneHelperName, _PartyModeID); CBase.Drawing.DrawTexture(toneHelper, drawRect, color); diff --git a/VocaluxeLib/Menu/SongMenu/CSongMenu.cs b/VocaluxeLib/Menu/SongMenu/CSongMenu.cs index 529ec03b..1fcb9619 100644 --- a/VocaluxeLib/Menu/SongMenu/CSongMenu.cs +++ b/VocaluxeLib/Menu/SongMenu/CSongMenu.cs @@ -17,6 +17,7 @@ using System.Xml; using VocaluxeLib.PartyModes; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu.SongMenu { diff --git a/VocaluxeLib/Menu/SongMenu/CSongMenuFramework.cs b/VocaluxeLib/Menu/SongMenu/CSongMenuFramework.cs index f7a27cc6..73e53075 100644 --- a/VocaluxeLib/Menu/SongMenu/CSongMenuFramework.cs +++ b/VocaluxeLib/Menu/SongMenu/CSongMenuFramework.cs @@ -23,34 +23,26 @@ using System.Xml.Serialization; using VocaluxeLib.Draw; using VocaluxeLib.PartyModes; using VocaluxeLib.Songs; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu.SongMenu { [XmlType("SongMenu")] public struct SThemeSongMenu { - [XmlAttributeAttribute(AttributeName = "Name")] - public string Name; + [XmlAttribute(AttributeName = "Name")] public string Name; - [XmlElement("CoverBackground")] - public string CoverBackgroundName; - [XmlElement("CoverBigBackground")] - public string CoverBigBackgroundName; - [XmlElement("DuetIcon")] - public string DuetIconName; - [XmlElement("VideoIcon")] - public string VideoIconName; - [XmlElement("MedleyCalcIcon")] - public string MedleyCalcIcon; - [XmlElement("MedleyTagIcon")] - public string MedleyTagIcon; - [XmlElement("Color")] - public SThemeColor Color; + [XmlElement("CoverBackground")] public string CoverBackgroundName; + [XmlElement("CoverBigBackground")] public string CoverBigBackgroundName; + [XmlElement("DuetIcon")] public string DuetIconName; + [XmlElement("VideoIcon")] public string VideoIconName; + [XmlElement("MedleyCalcIcon")] public string MedleyCalcIcon; + [XmlElement("MedleyTagIcon")] public string MedleyTagIcon; + [XmlElement("Color")] public SThemeColor Color; //public SThemeSongMenuBook songMenuBook; //public SThemeSongMenuDreidel songMenuDreidel; //public SThemeSongMenuList songMenuList; - [XmlElement("SongMenuTileBoard")] public SThemeSongMenuTileBoard SongMenuTileBoard; public SThemeSongMenu(SThemeSongMenu theme) @@ -69,65 +61,48 @@ namespace VocaluxeLib.Menu.SongMenu public struct SThemeSongMenuTileBoard { - [XmlElement("NumW")] /// /// Number of tiles horizontal /// public int NumW; - [XmlElement("NumH")] /// /// Number of tiles vertical /// public int NumH; - [XmlElement("NumWsmall")] /// /// Number of tiles horizontal in small-modus /// public int NumWsmall; - - [XmlElement("NumHsmall")] + /// /// Number of tiles vertical in small-modus /// public int NumHsmall; - [XmlElement("SpaceW")] /// /// Space between tiles horizontal /// public float SpaceW; - [XmlElement("SpaceH")] /// /// Space between tiles vertical /// public float SpaceH; - [XmlElement("TileRect")] public SRectF TileRect; - [XmlElement("TileRectSmall")] public SRectF TileRectSmall; - [XmlElement("TextArtist")] public SThemeText TextArtist; - [XmlElement("TextTitle")] public SThemeText TextTitle; - [XmlElement("TextSongLength")] public SThemeText TextSongLength; - [XmlElement("StaticCoverBig")] public SThemeStatic StaticCoverBig; - [XmlElement("StaticTextBG")] public SThemeStatic StaticTextBG; - [XmlElement("StaticDuetIcon")] public SThemeStatic StaticDuetIcon; - [XmlElement("StaticVideoIcon")] public SThemeStatic StaticVideoIcon; - [XmlElement("StaticMedleyCalcIcon")] public SThemeStatic StaticMedleyCalcIcon; - [XmlElement("StaticMedleyTagIcon")] public SThemeStatic StaticMedleyTagIcon; } @@ -278,7 +253,6 @@ namespace VocaluxeLib.Menu.SongMenu return _ThemeLoaded; } - #endregion Theme public virtual void Init() @@ -294,7 +268,7 @@ namespace VocaluxeLib.Menu.SongMenu } public virtual void OnShow() {} - + public virtual void OnHide() { if(CBase.Graphics.GetNextScreen() != EScreens.ScreenNames || CBase.Config.GetBackgroundMusicStatus() == EBackgroundMusicOffOn.TR_CONFIG_OFF) @@ -373,7 +347,6 @@ namespace VocaluxeLib.Menu.SongMenu _ColorInternal = CBase.Theme.GetColor(_Theme.Color.Name, _PartyModeID); else _ColorInternal = _Theme.Color.Color; - } public void ReloadTextures() diff --git a/VocaluxeLib/Menu/SongMenu/CSongMenuTileBoard.cs b/VocaluxeLib/Menu/SongMenu/CSongMenuTileBoard.cs index 759a9585..8a3c8190 100644 --- a/VocaluxeLib/Menu/SongMenu/CSongMenuTileBoard.cs +++ b/VocaluxeLib/Menu/SongMenu/CSongMenuTileBoard.cs @@ -17,13 +17,13 @@ using System; using System.Collections.Generic; -using System.Drawing; using System.Linq; using System.Windows.Forms; using System.Xml; using VocaluxeLib.Draw; using VocaluxeLib.PartyModes; using VocaluxeLib.Songs; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu.SongMenu { @@ -38,8 +38,8 @@ namespace VocaluxeLib.Menu.SongMenu private CStatic _MedleyCalcIcon; private CStatic _MedleyTagIcon; - private CTexture _CoverBigBGTexture; - private CTexture _CoverBGTexture; + private CTextureRef _CoverBigBGTexture; + private CTextureRef _CoverBGTexture; private CText _Artist; private CText _Title; @@ -494,20 +494,16 @@ namespace VocaluxeLib.Menu.SongMenu _TextBG.Draw(); - CTexture vidtex = CBase.BackgroundMusic.IsPlayingPreview() ? CBase.BackgroundMusic.GetVideoTexture() : null; + CTextureRef vidtex = CBase.BackgroundMusic.IsPlayingPreview() ? CBase.BackgroundMusic.GetVideoTexture() : null; if (vidtex != null) { if (vidtex.Color.A < 1) _CoverBig.Draw(EAspect.Crop); - var bounds = new RectangleF(_CoverBig.Rect.X, _CoverBig.Rect.Y, _CoverBig.Rect.W, _CoverBig.Rect.H); - RectangleF rect; - CHelper.SetRect(bounds, out rect, vidtex.OrigAspect, EAspect.Crop); - var vidRect = new SRectF(rect.X, rect.Y, rect.Width, rect.Height, _CoverBig.Rect.Z); - var vidRectBounds = new SRectF(bounds.X, bounds.Y, bounds.Width, bounds.Height, 0f); + SRectF rect = CHelper.FitInBounds(_CoverBig.Rect, vidtex.OrigAspect, EAspect.Crop); - CBase.Drawing.DrawTexture(vidtex, vidRect, vidtex.Color, vidRectBounds); - CBase.Drawing.DrawTextureReflection(vidtex, vidRect, vidtex.Color, vidRectBounds, _CoverBig.ReflectionSpace, _CoverBig.ReflectionHeight); + CBase.Drawing.DrawTexture(vidtex, rect); + CBase.Drawing.DrawTextureReflection(vidtex, rect, vidtex.Color, _CoverBig.ReflectionSpace, _CoverBig.ReflectionHeight); } else _CoverBig.Draw(EAspect.Crop); diff --git a/VocaluxeLib/PartyModes/CScreenPartyDummy.cs b/VocaluxeLib/PartyModes/CScreenPartyDummy.cs index 4ac0d76d..26574458 100644 --- a/VocaluxeLib/PartyModes/CScreenPartyDummy.cs +++ b/VocaluxeLib/PartyModes/CScreenPartyDummy.cs @@ -32,11 +32,9 @@ namespace VocaluxeLib.PartyModes public override void LoadTheme(string xmlPath) { _Warning = GetNewText(); - _Warning.Height = 100f; + _Warning.Font.Height = 100f; _Warning.X = 150; _Warning.Y = 300; - _Warning.Font = "Normal"; - _Warning.Style = EStyle.Normal; _Warning.Color = new SColorF(1f, 0f, 0f, 1f); _Warning.SelColor = new SColorF(1f, 0f, 0f, 1f); _Warning.Text = "SOMETHING WENT WRONG!"; diff --git a/VocaluxeLib/Profile/CAvatar.cs b/VocaluxeLib/Profile/CAvatar.cs index 1daba02e..dc0325e9 100644 --- a/VocaluxeLib/Profile/CAvatar.cs +++ b/VocaluxeLib/Profile/CAvatar.cs @@ -25,10 +25,10 @@ namespace VocaluxeLib.Profile { public int ID; private readonly string _FileName = ""; - private CTexture _Texture; + private CTextureRef _Texture; private const int _MaxNameLen = 12; - public CTexture Texture + public CTextureRef Texture { get { return _Texture; } } @@ -39,11 +39,11 @@ namespace VocaluxeLib.Profile public static CAvatar GetAvatar(string fileName) { - CTexture texture = CBase.Drawing.AddTexture(fileName); + CTextureRef texture = CBase.Drawing.AddTexture(fileName); return texture == null ? null : new CAvatar(texture, fileName); } - private CAvatar(CTexture texture, string fileName, int id = -1) + private CAvatar(CTextureRef texture, string fileName, int id = -1) { _Texture = texture; _FileName = fileName; diff --git a/VocaluxeLib/Profile/CProfile.cs b/VocaluxeLib/Profile/CProfile.cs index cf03f5b0..95ad5d4f 100644 --- a/VocaluxeLib/Profile/CProfile.cs +++ b/VocaluxeLib/Profile/CProfile.cs @@ -18,6 +18,7 @@ using System; using System.IO; using System.Xml; +using VocaluxeLib.Xml; namespace VocaluxeLib.Profile { diff --git a/VocaluxeLib/Songs/CCategory.cs b/VocaluxeLib/Songs/CCategory.cs index 79f5087c..1fec7346 100644 --- a/VocaluxeLib/Songs/CCategory.cs +++ b/VocaluxeLib/Songs/CCategory.cs @@ -24,8 +24,8 @@ namespace VocaluxeLib.Songs public class CCategory { public readonly string Name; - private CTexture _CoverTextureSmall; - private CTexture _CoverTextureBig; + private CTextureRef _CoverTextureSmall; + private CTextureRef _CoverTextureBig; public readonly List Songs = new List(); public CCategory(string name) @@ -33,14 +33,14 @@ namespace VocaluxeLib.Songs Name = name; } - public CTexture CoverTextureSmall + public CTextureRef CoverTextureSmall { get { return _CoverTextureSmall; } set { _CoverTextureSmall = value; } } - public CTexture CoverTextureBig + public CTextureRef CoverTextureBig { get { return _CoverTextureBig ?? _CoverTextureSmall; } set diff --git a/VocaluxeLib/Songs/CSong.cs b/VocaluxeLib/Songs/CSong.cs index a3752341..a5baf8e5 100644 --- a/VocaluxeLib/Songs/CSong.cs +++ b/VocaluxeLib/Songs/CSong.cs @@ -78,8 +78,8 @@ namespace VocaluxeLib.Songs public partial class CSong { - private CTexture _CoverTextureSmall; - private CTexture _CoverTextureBig; + private CTextureRef _CoverTextureSmall; + private CTextureRef _CoverTextureBig; public SMedley Medley; @@ -104,7 +104,7 @@ namespace VocaluxeLib.Songs public bool NotesLoaded { get; private set; } - public CTexture CoverTextureSmall + public CTextureRef CoverTextureSmall { get { @@ -116,7 +116,7 @@ namespace VocaluxeLib.Songs set { _CoverTextureSmall = value; } } - public CTexture CoverTextureBig + public CTextureRef CoverTextureBig { get { return _CoverTextureBig ?? _CoverTextureSmall; } set { _CoverTextureBig = value; } diff --git a/VocaluxeLib/Structs.cs b/VocaluxeLib/Structs.cs index aeb49246..743585d5 100644 --- a/VocaluxeLib/Structs.cs +++ b/VocaluxeLib/Structs.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.Contracts; using System.Drawing; using System.Windows.Forms; @@ -25,21 +26,57 @@ using VocaluxeLib.Songs; namespace VocaluxeLib { + public enum ECoverGeneratorType + { + Default, + Folder, + Artist, + Letter, + Edition, + Genre, + Language, + Year, + Decade, + Date + } + + public struct SMargin + { + public int Default; + public int Left, Right, Top, Bottom; + } + + public struct SThemeCoverGeneratorText + { + public string Text; + public SThemeFont Font; + public SThemeColor Color; + public SMargin Margin; + public int Indent; + } + + public struct SThemeCoverGenerator + { + public ECoverGeneratorType Type; + public SThemeCoverGeneratorText Text; + public SThemeColor BackgroundColor; + public string Image; + public float ImageAlpha; + public bool ShowFirstCover; + } #region Drawing public struct SColorF { - [XmlElement("R")] - public float R; - [XmlElement("G")] - public float G; - [XmlElement("B")] - public float B; - [XmlElement("A")] - public float A; + [XmlElement("R")] public float R; + [XmlElement("G")] public float G; + [XmlElement("B")] public float B; + [XmlElement("A")] public float A; public SColorF(float r, float g, float b, float a) { + Debug.Assert(r.IsInRange(0, 1) && g.IsInRange(0, 1) && b.IsInRange(0, 1)); + Debug.Assert(a.IsInRange(0, 1)); R = r; G = g; B = b; @@ -69,14 +106,30 @@ namespace VocaluxeLib public string Name; //Needed for serialization - public bool ColorSpecified { get { return String.IsNullOrEmpty(Name); } } - public bool NameSpecified { get { return !String.IsNullOrEmpty(Name); } } + public bool ColorSpecified + { + get { return String.IsNullOrEmpty(Name); } + } + public bool NameSpecified + { + get { return !String.IsNullOrEmpty(Name); } + } public SThemeColor(SThemeColor theme) { Color = new SColorF(theme.Color); Name = theme.Name; } + + public bool Get(int partyModeId, out SColorF color) + { + if (String.IsNullOrEmpty(Name)) + { + color = Color; + return true; + } + return CBase.Theme.GetColor(Name, CBase.Theme.GetSkinIndex(partyModeId), out color); + } } public struct SRectF @@ -86,8 +139,24 @@ namespace VocaluxeLib public float W; public float H; public float Z; - [XmlIgnore] - public float Rotation; //0..360° + [XmlIgnore] public float Rotation; //0..360° + + public float Right + { + get { return X + W; } + } + public float Bottom + { + get { return Y + H; } + } + public Size SizeI + { + get { return new Size((int)W, (int)H); } + } + public SizeF Size + { + get { return new SizeF(W, H); } + } public SRectF(float x, float y, float w, float h, float z) { @@ -108,19 +177,34 @@ namespace VocaluxeLib Z = rect.Z; Rotation = 0f; } + + public SRectF(Rectangle rect) + { + X = rect.X; + Y = rect.Y; + W = rect.Width; + H = rect.Height; + Z = 0; + Rotation = 0; + } } [XmlRoot("Reflection")] public struct SReflection { - [XmlAttributeAttribute(AttributeName = "Enabled")] - public bool Enabled; + [XmlAttribute(AttributeName = "Enabled")] public bool Enabled; public float Height; public float Space; //Needed for serialization - public bool HeightSpecified { get { return Enabled; } } - public bool SpaceSpecified { get { return Enabled; } } + public bool HeightSpecified + { + get { return Enabled; } + } + public bool SpaceSpecified + { + get { return Enabled; } + } public SReflection(bool enabled, float height, float space) { diff --git a/VocaluxeLib/Utils/Player/CSongPlayer.cs b/VocaluxeLib/Utils/Player/CSongPlayer.cs index 078be067..d90eb1b6 100644 --- a/VocaluxeLib/Utils/Player/CSongPlayer.cs +++ b/VocaluxeLib/Utils/Player/CSongPlayer.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; - using VocaluxeLib.Draw; using VocaluxeLib.Songs; @@ -14,7 +13,7 @@ namespace VocaluxeLib.Utils.Player private CSong _Song; private bool _VideoEnabled; private int _Video = -1; - private CTexture _CurrentVideoTexture; + private CTextureRef _CurrentVideoTexture; private CFading _VideoFading; public bool VideoEnabled @@ -41,7 +40,7 @@ namespace VocaluxeLib.Utils.Player } public bool SongHasVideo { - get { return _Song != null && File.Exists(Path.Combine(_Song.Folder,_Song.VideoFileName)); } + get { return _Song != null && File.Exists(Path.Combine(_Song.Folder, _Song.VideoFileName)); } } public string ArtistAndTitle @@ -56,7 +55,7 @@ namespace VocaluxeLib.Utils.Player } } - public CTexture Cover + public CTextureRef Cover { get { return _Song == null ? CBase.Cover.GetNoCover() : _Song.CoverTextureBig; } } @@ -66,11 +65,10 @@ namespace VocaluxeLib.Utils.Player get { return _StreamID != -1; } } - - public CSongPlayer(bool loop = false) : base(loop) { } + public CSongPlayer(bool loop = false) : base(loop) {} public CSongPlayer(CSong song, bool loop = false, float position = 0f, bool autoplay = false) - : base(Path.Combine(song.Folder,song.MP3FileName), loop, position, autoplay) + : base(Path.Combine(song.Folder, song.MP3FileName), loop, position, autoplay) { _Song = song; _LoadVideo(); @@ -82,7 +80,7 @@ namespace VocaluxeLib.Utils.Player _Song = null; } - public CTexture GetVideoTexture() + public CTextureRef GetVideoTexture() { if (_Video == -1 || _Song == null) return null; @@ -101,7 +99,7 @@ namespace VocaluxeLib.Utils.Player return null; } - public void Load(CSong song, float position = 0f, bool autoplay = false) + public void Load(CSong song, float position = 0f, bool autoplay = false) { if (song == null) return; @@ -109,7 +107,7 @@ namespace VocaluxeLib.Utils.Player Stop(); _Song = song; - + base.Load(Path.Combine(song.Folder, song.MP3FileName), position, false); _LoadVideo(); @@ -155,13 +153,10 @@ namespace VocaluxeLib.Utils.Player CBase.Video.Pause(_Video); CBase.Video.Skip(_Video, CBase.Sound.GetPosition(_StreamID) + _FadeTime, _Song.VideoGap); } - else if(_Video != -1) - { + else if (_Video != -1) CBase.Video.Resume(_Video); - } base.TogglePause(); } - private void _LoadVideo() { @@ -182,4 +177,4 @@ namespace VocaluxeLib.Utils.Player } } } -} +} \ No newline at end of file diff --git a/VocaluxeLib/VocaluxeLib.csproj b/VocaluxeLib/VocaluxeLib.csproj index a367e8e1..fb1e0737 100644 --- a/VocaluxeLib/VocaluxeLib.csproj +++ b/VocaluxeLib/VocaluxeLib.csproj @@ -76,7 +76,9 @@ + + @@ -100,8 +102,9 @@ - - + + + @@ -137,6 +140,7 @@ + diff --git a/VocaluxeLib/CXMLReader.cs b/VocaluxeLib/Xml/CXMLReader.cs similarity index 69% rename from VocaluxeLib/CXMLReader.cs rename to VocaluxeLib/Xml/CXMLReader.cs index 4b06b922..2f266ba6 100644 --- a/VocaluxeLib/CXMLReader.cs +++ b/VocaluxeLib/Xml/CXMLReader.cs @@ -19,9 +19,9 @@ using System; using System.Collections.Generic; using System.Xml.XPath; -namespace VocaluxeLib +namespace VocaluxeLib.Xml { - public class CXMLReader + public class CXMLReader : CXmlDeserializer { private readonly XPathNavigator _Navigator; private readonly String _FilePath; @@ -57,55 +57,17 @@ namespace VocaluxeLib } } - public bool TryGetEnumValue(string xPath, ref T value) - where T : struct - { - string val; - if (GetValue(xPath, out val, Enum.GetName(typeof(T), value))) - return CHelper.TryParse(val, out value, true); - return false; - } - - public bool TryGetIntValue(string xPath, ref int value) - { - string val; - if (GetValue(xPath, out val, value.ToString())) - return int.TryParse(val, out value); - return false; - } - - public bool TryGetIntValueRange(string xPath, ref int value, int min = 0, int max = 100) - { - bool result = TryGetIntValue(xPath, ref value); - if (result) - value = value.Clamp(min, max); - return result; - } - - public bool TryGetFloatValue(string xPath, ref float value) - { - string val; - return GetValue(xPath, out val, value.ToString()) && CHelper.TryParse(val, out value); - } - public bool TryGetColorFromRGBA(string xPath, ref SColorF value) { bool result = true; - result &= TryGetFloatValue(xPath + "/R", ref value.R); - result &= TryGetFloatValue(xPath + "/G", ref value.G); - result &= TryGetFloatValue(xPath + "/B", ref value.B); - result &= TryGetFloatValue(xPath + "/A", ref value.A); - if (result) - { - value.R = value.R.Clamp(0f, 1f); - value.G = value.G.Clamp(0f, 1f); - value.B = value.B.Clamp(0f, 1f); - value.A = value.A.Clamp(0f, 1f); - } + result &= TryGetNormalizedFloatValue(xPath + "/R", ref value.R); + result &= TryGetNormalizedFloatValue(xPath + "/G", ref value.G); + result &= TryGetNormalizedFloatValue(xPath + "/B", ref value.B); + result &= TryGetNormalizedFloatValue(xPath + "/A", ref value.A); return result; } - public bool GetValue(string xPath, out string value, string defaultValue = "") + public override bool GetValue(string xPath, out string value, string defaultValue = "") { int resultCt = 0; string val = string.Empty; @@ -128,6 +90,19 @@ namespace VocaluxeLib return true; } + public override bool GetAttribute(string xPath, string attribute, out string value) + { + _Navigator.MoveToRoot(); + XPathNodeIterator iterator = _Navigator.Select(xPath); + if (iterator.MoveNext()) + { + value = iterator.Current.GetAttribute(attribute, ""); + return true; + } + value = ""; + return false; + } + public List GetNames(string xPath) { var values = new List(); diff --git a/VocaluxeLib/Xml/CXmlDeserializer.cs b/VocaluxeLib/Xml/CXmlDeserializer.cs new file mode 100644 index 00000000..0b202c6b --- /dev/null +++ b/VocaluxeLib/Xml/CXmlDeserializer.cs @@ -0,0 +1,68 @@ +using System.Linq; + +namespace VocaluxeLib.Xml +{ + public abstract class CXmlDeserializer : CXmlReaderBase + { + public bool Read(string xPath, out SThemeFont el) + { + el = new SThemeFont(); + bool ok = true; + ok &= GetValue(xPath + "/Name", out el.Name); + ok &= TryGetFloatValue(xPath + "/Size", ref el.Size); + ok &= TryGetEnumValue(xPath + "/Style", ref el.Style); + return ok; + } + + public bool Read(string xPath, out SMargin el) + { + el = new SMargin(); + bool ok = true; + bool defOk = TryGetIntValue(xPath + "/Default", ref el.Default); + ok &= TryGetIntValue(xPath + "/Left", ref el.Left, el.Default); + ok &= TryGetIntValue(xPath + "/Right", ref el.Right, el.Default); + ok &= TryGetIntValue(xPath + "/Top", ref el.Top, el.Default); + ok &= TryGetIntValue(xPath + "/Bottom", ref el.Bottom, el.Default); + return ok || defOk; + } + + public bool Read(string xPath, out SThemeCoverGeneratorText el) + { + el = new SThemeCoverGeneratorText(); + bool ok = true; + ok &= GetValue(xPath + "/Text", out el.Text); + ok &= Read(xPath + "/Font", out el.Font); + ok &= Read(xPath + "/Color", out el.Color); + ok &= Read(xPath + "/Margin", out el.Margin); + if (ok) + TryGetIntValue(xPath + "/Indent", ref el.Indent, 6 * (el.Margin.Left + el.Margin.Right) / 2); + return ok; + } + + public bool Read(string xPath, out SThemeColor el) + { + el = new SThemeColor(); + bool ok = true; + ok &= TryGetNormalizedFloatValue(xPath + "/R", ref el.Color.R); + ok &= TryGetNormalizedFloatValue(xPath + "/G", ref el.Color.G); + ok &= TryGetNormalizedFloatValue(xPath + "/B", ref el.Color.B); + ok &= TryGetNormalizedFloatValue(xPath + "/A", ref el.Color.A); + if (!ok) + ok = GetValue(xPath, out el.Name) && !el.Name.Contains('<'); + return ok; + } + + public bool Read(string xPath, out SThemeCoverGenerator el) + { + el = new SThemeCoverGenerator(); + bool ok = true; + ok &= TryGetEnumValue(xPath + "/Type", ref el.Type); + ok &= Read(xPath + "/Text", out el.Text); + ok &= Read(xPath + "/BackgroundColor", out el.BackgroundColor); + ok &= GetValue(xPath + "/Image", out el.Image); + TryGetNormalizedFloatValue(xPath + "/ImageAlpha", ref el.ImageAlpha, 0.5f); + TryGetBoolValue(xPath + "/ShowFirstCover", out el.ShowFirstCover); + return ok; + } + } +} \ No newline at end of file diff --git a/VocaluxeLib/Xml/CXmlReaderBase.cs b/VocaluxeLib/Xml/CXmlReaderBase.cs new file mode 100644 index 00000000..152ecfad --- /dev/null +++ b/VocaluxeLib/Xml/CXmlReaderBase.cs @@ -0,0 +1,61 @@ +using System; + +namespace VocaluxeLib.Xml +{ + /// + /// Base class that provides basic methods for reading xml, while the actual XML implementation is still abstract to allow different implementations + /// + public abstract class CXmlReaderBase + { + public abstract bool GetValue(string xPath, out string value, string defaultValue = ""); + public abstract bool GetAttribute(string xPath, string attribute, out string value); + + public bool TryGetBoolValue(string xPath, out bool value, bool defaultValue = false) + { + value = defaultValue; + string val; + return GetValue(xPath, out val) && bool.TryParse(val, out value); + } + + public bool TryGetIntValue(string xPath, ref int value, int? defaultValue = null) + { + string val; + bool ok = GetValue(xPath, out val) && int.TryParse(val, out value); + if (!ok && defaultValue.HasValue) + value = defaultValue.Value; + return ok; + } + + public bool TryGetFloatValue(string xPath, ref float value) + { + string val; + return GetValue(xPath, out val, value.ToString()) && CHelper.TryParse(val, out value); + } + + public bool TryGetEnumValue(string xPath, ref T value) where T : struct + { + string val; + return GetValue(xPath, out val, Enum.GetName(typeof(T), value)) && CHelper.TryParse(val, out value, true); + } + + public bool TryGetIntValueRange(string xPath, ref int value, int min = 0, int max = 100) + { + return TryGetIntValue(xPath, ref value) && value.IsInRange(min, max); + } + + /// + /// Gets a normalized (0<=x<=1) float value + /// + /// + /// + /// + /// True if exists and is normalized + public bool TryGetNormalizedFloatValue(string xPath, ref float value, float defaultValue = 0) + { + bool ok = TryGetFloatValue(xPath, ref value) && value.IsInRange(0f, 1f); + if (!ok) + value = defaultValue; + return ok; + } + } +} \ No newline at end of file