From 3f4f2776beaac948f2f94b9a2a3ea3a429042b3d Mon Sep 17 00:00:00 2001 From: Flamefire Date: Thu, 14 Aug 2014 16:07:49 +0200 Subject: [PATCH 01/13] Add first working implementation of the cover generator TODO: Speedup --- Output/Cover/Blue.xml | 38 ++++ Vocaluxe/Base/CCover.cs | 87 ++++++++- Vocaluxe/Base/CNoCoverGenerator.cs | 278 +++++++++++++++++++++++++++++ Vocaluxe/Base/CSongCategorizer.cs | 4 + Vocaluxe/Program.cs | 14 +- Vocaluxe/Vocaluxe.csproj | 1 + 6 files changed, 412 insertions(+), 10 deletions(-) create mode 100644 Vocaluxe/Base/CNoCoverGenerator.cs diff --git a/Output/Cover/Blue.xml b/Output/Cover/Blue.xml index 9befe7d3..069bb79c 100644 --- a/Output/Cover/Blue.xml +++ b/Output/Cover/Blue.xml @@ -5,4 +5,42 @@ Blue flokuep + + + + + %TEXT% + + Outline + + + 500 + + + 0110.5 + 24 + 100 + + EmptyCover.png + true + 0.8 + + + + + %TEXT% + + Outline + + + 500 + + + 0100.5 + 24 + 100 + + EmptyCover.png + + diff --git a/Vocaluxe/Base/CCover.cs b/Vocaluxe/Base/CCover.cs index 61692ce7..177444d8 100644 --- a/Vocaluxe/Base/CCover.cs +++ b/Vocaluxe/Base/CCover.cs @@ -20,6 +20,7 @@ using System.Collections.Generic; using System.IO; using VocaluxeLib; using VocaluxeLib.Draw; +using VocaluxeLib.Songs; namespace Vocaluxe.Base { @@ -32,7 +33,11 @@ namespace Vocaluxe.Base static class CCover { + private const string _NoCoverName = "No Cover"; + private const string _NoCoverNameAlt = "NoCover"; + private const string _DefaultTypeName = "Default"; private static readonly Dictionary _Covers = new Dictionary(); + private static readonly Dictionary _NoCoverThemes = new Dictionary(); private static readonly List _CoverThemes = new List(); public static CTexture NoCover { get; private set; } @@ -92,6 +97,24 @@ namespace Vocaluxe.Base } } + public static CTexture GenerateCover(string text, ESongSorting sorting, CCategory category) + { + CSong firstSong = category.GetSong(0); + CTexture cover = _NoCoverThemes[_SongSortingToType(sorting)].GetCover(text, firstSong != null ? Path.Combine(firstSong.Folder, firstSong.CoverFileName) : null); + if (cover == null) + cover = _NoCoverThemes[_DefaultTypeName].GetCover(text, firstSong != null ? Path.Combine(firstSong.Folder, firstSong.CoverFileName) : null); + if (cover == null) + cover = NoCover; + else + { + lock (_Covers) + { + _Covers.Add(text, cover); + } + } + return cover; + } + /// /// Returns true if a cover with the given name exists. /// MUST HOLD _Covers lock at this point @@ -121,6 +144,10 @@ namespace Vocaluxe.Base } _Covers.Clear(); } + lock (_NoCoverThemes) + { + _NoCoverThemes.Clear(); + } } /// @@ -168,7 +195,7 @@ 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) { @@ -184,8 +211,62 @@ 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 string _SongSortingToType(ESongSorting sorting) + { + switch (sorting) + { + case ESongSorting.TR_CONFIG_NONE: + return _DefaultTypeName; + case ESongSorting.TR_CONFIG_FOLDER: + return "Folder"; + case ESongSorting.TR_CONFIG_ARTIST: + return "Artist"; + case ESongSorting.TR_CONFIG_ARTIST_LETTER: + case ESongSorting.TR_CONFIG_TITLE_LETTER: + return "Letter"; + case ESongSorting.TR_CONFIG_EDITION: + return "Edition"; + case ESongSorting.TR_CONFIG_GENRE: + return "Genre"; + case ESongSorting.TR_CONFIG_LANGUAGE: + return "Language"; + case ESongSorting.TR_CONFIG_YEAR: + return "Year"; + case ESongSorting.TR_CONFIG_DECADE: + return "Decade"; + case ESongSorting.TR_CONFIG_DATEADDED: + return "Date"; + default: + throw new ArgumentOutOfRangeException("sorting"); + } + } + + private static void _LoadNoCoverThemes(SCoverTheme coverTheme) + { + CXMLReader xmlReader = CXMLReader.OpenFile(Path.Combine(CSettings.ProgramFolder, CSettings.FolderNameCover, coverTheme.File)); + List types = new List(); + foreach (ESongSorting sorting in Enum.GetValues(typeof(ESongSorting))) + types.Add(_SongSortingToType(sorting)); + string coverPath = Path.Combine(CSettings.ProgramFolder, CSettings.FolderNameCover, coverTheme.Folder); + lock (_NoCoverThemes) + { + foreach (string type in types) + { + if (_NoCoverThemes.ContainsKey(type)) + continue; + CNoCoverGenerator el = new CNoCoverGenerator(xmlReader, "//root/MissingCover/" + type, coverPath); + _NoCoverThemes.Add(type, el); + } } } diff --git a/Vocaluxe/Base/CNoCoverGenerator.cs b/Vocaluxe/Base/CNoCoverGenerator.cs new file mode 100644 index 00000000..9f5d8a97 --- /dev/null +++ b/Vocaluxe/Base/CNoCoverGenerator.cs @@ -0,0 +1,278 @@ +#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.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Drawing.Text; +using System.IO; +using Vocaluxe.Base.Fonts; +using VocaluxeLib; +using VocaluxeLib.Draw; +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 string _Text; + private readonly SColorF _TextColor; + private readonly string _TextColorName; + private readonly float _TextMargin; + private readonly float _TextIndent; + private readonly int _TextSize; + private readonly string _Font; + private readonly EStyle _Style; + private readonly string _Image; + private readonly bool _ShowFirstCover; + private readonly float _ImageAlpha; + private const float _LineSpace = 5; + + public CNoCoverGenerator(CXMLReader xmlReader, string xPath, string basePath) + { + _Valid = xmlReader.ItemExists(xPath); + if (!_Valid) + return; + _Valid &= xmlReader.GetValue(xPath + "/Text/Text", out _Text); + _Valid &= xmlReader.GetValue(xPath + "/Text/Font", out _Font); + _Valid &= xmlReader.TryGetEnumValue(xPath + "/Text/Style", ref _Style); + _Valid &= xmlReader.TryGetIntValue(xPath + "/Text/Size", ref _TextSize); + if (xmlReader.GetValue(xPath + "/Text/Color", out _TextColorName)) + _Valid &= CBase.Theme.GetColor(_TextColorName, CBase.Theme.GetSkinIndex(-1), out _TextColor); + else + _Valid &= xmlReader.TryGetColorFromRGBA(xPath + "/Text", ref _TextColor); + _Valid &= xmlReader.TryGetFloatValue(xPath + "/Text/Margin", ref _TextMargin); + if (!xmlReader.TryGetFloatValue(xPath + "/Text/Indent", ref _TextIndent)) + _TextIndent = 6 * _TextMargin; + _Valid &= xmlReader.GetValue(xPath + "/Image", out _Image); + string tmp; + if (xmlReader.GetValue(xPath + "/ShowFirstCover", out tmp)) + Boolean.TryParse(tmp, out _ShowFirstCover); + else + _ShowFirstCover = false; + if (!xmlReader.TryGetFloatValue(xPath + "/ImageAlpha", ref _ImageAlpha)) + _ImageAlpha = 0.5f; + else + _ImageAlpha = _ImageAlpha.Clamp(0f, 1f); + if (_Valid) + { + // ReSharper disable AssignNullToNotNullAttribute + _Image = Path.Combine(basePath, _Image); + // ReSharper restore AssignNullToNotNullAttribute + _Valid = File.Exists(_Image); + } + } + + public CTexture GetCover(string text, string firstCoverPath) + { + if (!_Valid) + return null; + text = CLanguage.Translate(_Text.Replace("%TEXT%", text)); + using (Bitmap bmp = new Bitmap(_Image)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.SmoothingMode = SmoothingMode.AntiAlias; + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.TextRenderingHint = TextRenderingHint.AntiAlias; + + if (_ShowFirstCover && !String.IsNullOrEmpty(firstCoverPath) && File.Exists(firstCoverPath)) + { + ColorMatrix cm = new ColorMatrix {Matrix33 = 1f - _ImageAlpha}; + ImageAttributes ia = new ImageAttributes(); + ia.SetColorMatrix(cm); + using (Bitmap bmp2 = new Bitmap(firstCoverPath)) + g.DrawImage(bmp2, new Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, bmp2.Width, bmp2.Height, GraphicsUnit.Pixel, ia); + } + if (text != "") + { + List lines = _SplitText(text); + CFonts.SetFont(_Font); + CFonts.Style = _Style; + float allowedWidth = bmp.Width - 2 * _TextMargin; + if (lines.Count > 1) + allowedWidth -= _TextIndent; + float allowedHeight = bmp.Height - 2 * _TextMargin - _LineSpace * (lines.Count - 1); + CFonts.Height = allowedHeight / lines.Count; + Font fo = CFonts.GetFont(); + List sizes = lines.Select(line => g.MeasureString(line, fo, -1, StringFormat.GenericTypographic)).ToList(); + float maxWidth = sizes.Select(s => s.Width).Max(); + float maxHeight = sizes.Select(s => s.Height).Max(); + maxHeight = (maxHeight + CFonts.Height) / 2; + if (maxWidth > allowedWidth) + { + CFonts.Height *= allowedWidth / maxWidth; + maxHeight *= allowedWidth / maxWidth; + } + if (maxHeight > _TextSize) + { + CFonts.Height *= _TextSize / maxHeight; + maxHeight = _TextSize; + } + fo = CFonts.GetFont(); + + //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.Outline * CFonts.Height; + SColorF outlineColorF = CFonts.OutlineColor; + 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 = (allowedHeight - maxHeight * lines.Count) / 2; + for (int i = 0; i < lines.Count; i++) + { + string line = lines[i]; + float left; + if (lines.Count == 1 || (i == 1 && lines.Count == 3)) + { + //Center Text if this is the only line or the middle line + float width = g.MeasureString(line, fo, -1, StringFormat.GenericTypographic).Width; + left = (bmp.Width - width) / 2; + } + else if (i == lines.Count - 1) + { + //Place last line at right + float width = g.MeasureString(line, fo, -1, StringFormat.GenericTypographic).Width; + left = bmp.Width - width - _TextMargin; + } + else + left = _TextMargin; + //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); + // ReSharper disable ImpureMethodCallOnReadonlyValueField + g.FillPath(new SolidBrush(_TextColor.AsColor()), path); + // ReSharper restore ImpureMethodCallOnReadonlyValueField + } + } + return CDraw.AddTexture(bmp); + } + } + + private static List _SplitText(string text) + { + text = text.Replace(".", ". ").Replace("-", "- ").Replace(" ", " "); + string[] parts = text.Split(' '); + List lines = new List(); + string line = ""; + bool appendingName = false; + foreach (string part in parts) + { + if (part.Length == 1 && !Char.IsLetter(part, 0)) + { + //Add non-letter chars to the end of the line (e.g. in "19 - 20") + line = (line != "" ? " " : "") + part; + appendingName = false; + } + else if (part.Length == 1 || (part.Length == 2 && part[1] == '.')) + { + //We have something like the initials (e.g. "J. R. R. Tolkien") - Keep them together + if (!appendingName && line != "") + { + //When we are not already appending those, add the last line and start a new one + lines.Add(line); + line = part; + } + line = (line != "" ? " " : "") + part; + appendingName = true; + } + else + { + if (line != "") + lines.Add(line); + line = part; + appendingName = false; + } + } + if (line != "") + lines.Add(line); + + int i; + //If we have more than 3 lines try to distribute them evenly + if (lines.Count > 3) + { + int len = lines.Sum(el => el.Length) + lines.Count - 1; + int avgLen = (int)Math.Ceiling(len / 3d); + i = 1; + while (i < lines.Count) + { + string prev = lines[i - 1]; + string cur = lines[i]; + if (prev.Length + cur.Length <= avgLen) + { + lines[i - 1] += " " + cur; + lines.RemoveAt(i); + } + else + i++; + } + } + + //Further reduce the lines by taking the longest line as the max length + i = 1; + while (i < lines.Count) + { + string prev = lines[i - 1]; + string cur = lines[i]; + if (prev.Length + cur.Length <= lines.Max(el => el.Length)) + { + lines[i - 1] += " " + cur; + lines.RemoveAt(i); + } + else + i++; + } + + //If we still have more than 3 lines be more agressive + while (lines.Count > 3) + { + //Join the shortest line with shortest neighbour + int minLen = lines.Min(el => el.Length); + for (i = 0; i < lines.Count; i++) + { + if (lines[i].Length != minLen) + continue; + int other; + if (i == 0) + other = 1; + else if (i == lines.Count - 1) + other = i - 1; + else if (lines[i - 1].Length < lines[i + 1].Length) + other = i - 1; + else + other = i + 1; + lines[i] += " " + lines[other]; + lines.RemoveAt(other); + break; + } + } + return lines; + } + } +} \ No newline at end of file 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/Program.cs b/Vocaluxe/Program.cs index b4720e87..a886ae3d 100644 --- a/Vocaluxe/Program.cs +++ b/Vocaluxe/Program.cs @@ -180,13 +180,6 @@ namespace Vocaluxe Application.DoEvents(); - // Load Cover - CLog.StartBenchmark("Init Cover"); - CCover.Init(); - CLog.StopBenchmark("Init Cover"); - - Application.DoEvents(); - // Theme System CLog.StartBenchmark("Init Theme"); if (!CTheme.Init()) @@ -195,6 +188,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(); diff --git a/Vocaluxe/Vocaluxe.csproj b/Vocaluxe/Vocaluxe.csproj index b8276467..0c355598 100644 --- a/Vocaluxe/Vocaluxe.csproj +++ b/Vocaluxe/Vocaluxe.csproj @@ -216,6 +216,7 @@ + From b634fb98f936f83613f54de2fd80c81e762a6271 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Sun, 24 Aug 2014 20:07:15 +0200 Subject: [PATCH 02/13] Add more options and improve splitting of text --- Output/Cover/Blue.xml | 67 ++-- Vocaluxe/Base/CNoCoverGenerator.cs | 527 +++++++++++++++++++++-------- VocaluxeLib/Extensions.cs | 22 +- VocaluxeLib/Structs.cs | 2 + 4 files changed, 425 insertions(+), 193 deletions(-) diff --git a/Output/Cover/Blue.xml b/Output/Cover/Blue.xml index 069bb79c..4448fe2b 100644 --- a/Output/Cover/Blue.xml +++ b/Output/Cover/Blue.xml @@ -6,41 +6,36 @@ flokuep - - - - %TEXT% - - Outline - - - 500 - - - 0110.5 - 24 - 100 - - EmptyCover.png - true - 0.8 - - - - - %TEXT% - - Outline - - - 500 - - - 0100.5 - 24 - 100 - - EmptyCover.png - + + + + + + %TEXT% + + Outline + + + + 500 + + + 1110.8 + + 24 + 24 + + 100 + + + 1111 + + + EmptyCover.png + + true + + 0.75 + diff --git a/Vocaluxe/Base/CNoCoverGenerator.cs b/Vocaluxe/Base/CNoCoverGenerator.cs index 9f5d8a97..36e49858 100644 --- a/Vocaluxe/Base/CNoCoverGenerator.cs +++ b/Vocaluxe/Base/CNoCoverGenerator.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; @@ -37,17 +38,27 @@ namespace Vocaluxe.Base { private readonly bool _Valid; private readonly string _Text; + private readonly SColorF _BGColor; private readonly SColorF _TextColor; - private readonly string _TextColorName; - private readonly float _TextMargin; - private readonly float _TextIndent; + private readonly int _TextMarginLeft; + private readonly int _TextMarginRight; + private readonly int _TextMarginTop; + private readonly int _TextMarginBottom; + private readonly int _TextIndent; private readonly int _TextSize; private readonly string _Font; private readonly EStyle _Style; private readonly string _Image; private readonly bool _ShowFirstCover; private readonly float _ImageAlpha; - private const float _LineSpace = 5; + 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(CXMLReader xmlReader, string xPath, string basePath) { @@ -58,29 +69,85 @@ namespace Vocaluxe.Base _Valid &= xmlReader.GetValue(xPath + "/Text/Font", out _Font); _Valid &= xmlReader.TryGetEnumValue(xPath + "/Text/Style", ref _Style); _Valid &= xmlReader.TryGetIntValue(xPath + "/Text/Size", ref _TextSize); - if (xmlReader.GetValue(xPath + "/Text/Color", out _TextColorName)) - _Valid &= CBase.Theme.GetColor(_TextColorName, CBase.Theme.GetSkinIndex(-1), out _TextColor); + string colorName; + if (xmlReader.GetValue(xPath + "/Text/Color", out colorName)) + _Valid &= CBase.Theme.GetColor(colorName, CBase.Theme.GetSkinIndex(-1), out _TextColor); else _Valid &= xmlReader.TryGetColorFromRGBA(xPath + "/Text", ref _TextColor); - _Valid &= xmlReader.TryGetFloatValue(xPath + "/Text/Margin", ref _TextMargin); - if (!xmlReader.TryGetFloatValue(xPath + "/Text/Indent", ref _TextIndent)) - _TextIndent = 6 * _TextMargin; + int tmpMargin = 0; + bool marginSet = xmlReader.TryGetIntValue(xPath + "/Text/Margin", ref tmpMargin); + if (marginSet) + { + _TextMarginLeft = tmpMargin; + _TextMarginRight = tmpMargin; + _TextMarginTop = tmpMargin; + _TextMarginBottom = tmpMargin; + } + _Valid &= xmlReader.TryGetIntValue(xPath + "/Text/MarginLeft", ref _TextMarginLeft) || marginSet; + _Valid &= xmlReader.TryGetIntValue(xPath + "/Text/MarginRight", ref _TextMarginRight) || marginSet; + _Valid &= xmlReader.TryGetIntValue(xPath + "/Text/MarginTop", ref _TextMarginTop) || marginSet; + _Valid &= xmlReader.TryGetIntValue(xPath + "/Text/MarginBottom", ref _TextMarginBottom) || marginSet; + if (!xmlReader.TryGetIntValue(xPath + "/Text/Indent", ref _TextIndent)) + _TextIndent = 6 * (_TextMarginLeft + _TextMarginRight) / 2; + + if (xmlReader.GetValue(xPath + "/Background/Color", out colorName)) + _Valid &= CBase.Theme.GetColor(colorName, CBase.Theme.GetSkinIndex(-1), out _BGColor); + else + _Valid &= xmlReader.TryGetColorFromRGBA(xPath + "/Background", ref _BGColor); _Valid &= xmlReader.GetValue(xPath + "/Image", out _Image); string tmp; if (xmlReader.GetValue(xPath + "/ShowFirstCover", out tmp)) Boolean.TryParse(tmp, out _ShowFirstCover); else _ShowFirstCover = false; - if (!xmlReader.TryGetFloatValue(xPath + "/ImageAlpha", ref _ImageAlpha)) - _ImageAlpha = 0.5f; - else - _ImageAlpha = _ImageAlpha.Clamp(0f, 1f); + _ImageAlpha = !xmlReader.TryGetFloatValue(xPath + "/ImageAlpha", ref _ImageAlpha) ? 0.5f : _ImageAlpha.Clamp(0f, 1f); if (_Valid) { // ReSharper disable AssignNullToNotNullAttribute _Image = Path.Combine(basePath, _Image); // ReSharper restore AssignNullToNotNullAttribute _Valid = File.Exists(_Image); + GetCover("Astrid Lindgren (Madicken)", null); + GetCover("David Guetta ft. Flo Rida & Nicki Minaj", null); + } + } + + 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 + CFonts.Height) / 2; + _Graphics = g; + _Font = font; + } + + public void AdjustSize(float factor) + { + Width *= factor; + _WidthTrimmed *= factor; + Height *= factor; } } @@ -89,48 +156,44 @@ namespace Vocaluxe.Base if (!_Valid) return null; text = CLanguage.Translate(_Text.Replace("%TEXT%", text)); - using (Bitmap bmp = new Bitmap(_Image)) + using (Bitmap bmpImage = new Bitmap(_Image)) + using (Bitmap bmp = new Bitmap(bmpImage.Width, bmpImage.Height, PixelFormat.Format32bppArgb)) using (Graphics g = Graphics.FromImage(bmp)) { g.SmoothingMode = SmoothingMode.AntiAlias; g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.TextRenderingHint = TextRenderingHint.AntiAlias; + g.Clear(_BGColor.AsColor()); + + ImageAttributes ia = null; if (_ShowFirstCover && !String.IsNullOrEmpty(firstCoverPath) && File.Exists(firstCoverPath)) { - ColorMatrix cm = new ColorMatrix {Matrix33 = 1f - _ImageAlpha}; - ImageAttributes ia = new ImageAttributes(); - ia.SetColorMatrix(cm); using (Bitmap bmp2 = new Bitmap(firstCoverPath)) - g.DrawImage(bmp2, new Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, bmp2.Width, bmp2.Height, GraphicsUnit.Pixel, ia); + g.DrawImage(bmp2, new Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, bmp2.Width, bmp2.Height, GraphicsUnit.Pixel); + ColorMatrix cm = new ColorMatrix {Matrix33 = _ImageAlpha}; + ia = new ImageAttributes(); + ia.SetColorMatrix(cm); } + g.DrawImage(bmpImage, new Rectangle(0, 0, bmp.Width, bmp.Height), 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, ia); + if (text != "") { - List lines = _SplitText(text); + IEnumerable textParts = _SplitText(text); CFonts.SetFont(_Font); CFonts.Style = _Style; - float allowedWidth = bmp.Width - 2 * _TextMargin; - if (lines.Count > 1) - allowedWidth -= _TextIndent; - float allowedHeight = bmp.Height - 2 * _TextMargin - _LineSpace * (lines.Count - 1); - CFonts.Height = allowedHeight / lines.Count; + CFonts.Height = bmp.Height - _TextMarginTop - _TextMarginBottom; Font fo = CFonts.GetFont(); - List sizes = lines.Select(line => g.MeasureString(line, fo, -1, StringFormat.GenericTypographic)).ToList(); - float maxWidth = sizes.Select(s => s.Width).Max(); - float maxHeight = sizes.Select(s => s.Height).Max(); - maxHeight = (maxHeight + CFonts.Height) / 2; - if (maxWidth > allowedWidth) - { - CFonts.Height *= allowedWidth / maxWidth; - maxHeight *= allowedWidth / maxWidth; - } - if (maxHeight > _TextSize) - { - CFonts.Height *= _TextSize / maxHeight; - maxHeight = _TextSize; - } + 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); + CFonts.Height *= factor / (1f + CFonts.Outline); //Adjust for outline size fo = CFonts.GetFont(); + 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.Outline * CFonts.Height; @@ -142,134 +205,320 @@ namespace Vocaluxe.Base { pen.LineJoin = LineJoin.Round; pen.Alignment = PenAlignment.Outset; - float top = (allowedHeight - maxHeight * lines.Count) / 2; - for (int i = 0; i < lines.Count; i++) + float top = (bmp.Height - _TextMarginBottom - _TextMarginTop - maxHeight * lineCount) / 2 + _TextMarginTop; + int nextLineEl = 0; + for (int i = 0; i < lineCount; i++) { - string line = lines[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 (lines.Count == 1 || (i == 1 && lines.Count == 3)) + if (lineCount == 1 || (i == 1 && lineCount == 3)) { //Center Text if this is the only line or the middle line - float width = g.MeasureString(line, fo, -1, StringFormat.GenericTypographic).Width; - left = (bmp.Width - width) / 2; + float width = _GetWidth(elements, firstEl, nextLineEl - 1); + left = (bmp.Width - _TextMarginLeft - _TextMarginRight - width) / 2 + _TextMarginLeft; } - else if (i == lines.Count - 1) + else if (i == lineCount - 1) { //Place last line at right - float width = g.MeasureString(line, fo, -1, StringFormat.GenericTypographic).Width; - left = bmp.Width - width - _TextMargin; + float width = _GetWidth(elements, firstEl, nextLineEl - 1); + left = bmp.Width - width - _TextMarginRight; } else - left = _TextMargin; + left = _TextMarginLeft; //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); - // ReSharper disable ImpureMethodCallOnReadonlyValueField g.FillPath(new SolidBrush(_TextColor.AsColor()), path); - // ReSharper restore ImpureMethodCallOnReadonlyValueField } } return CDraw.AddTexture(bmp); } } - private static List _SplitText(string text) + /// + /// 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) { - text = text.Replace(".", ". ").Replace("-", "- ").Replace(" ", " "); - string[] parts = text.Split(' '); - List lines = new List(); - string line = ""; - bool appendingName = false; - foreach (string part in parts) - { - if (part.Length == 1 && !Char.IsLetter(part, 0)) - { - //Add non-letter chars to the end of the line (e.g. in "19 - 20") - line = (line != "" ? " " : "") + part; - appendingName = false; - } - else if (part.Length == 1 || (part.Length == 2 && part[1] == '.')) - { - //We have something like the initials (e.g. "J. R. R. Tolkien") - Keep them together - if (!appendingName && line != "") - { - //When we are not already appending those, add the last line and start a new one - lines.Add(line); - line = part; - } - line = (line != "" ? " " : "") + part; - appendingName = true; - } - else - { - if (line != "") - lines.Add(line); - line = part; - appendingName = false; - } - } - if (line != "") - lines.Add(line); + int availableWidth = width - _TextMarginLeft - _TextMarginRight; + int availableHeight = height - _TextMarginTop - _TextMarginBottom; - int i; - //If we have more than 3 lines try to distribute them evenly - if (lines.Count > 3) - { - int len = lines.Sum(el => el.Length) + lines.Count - 1; - int avgLen = (int)Math.Ceiling(len / 3d); - i = 1; - while (i < lines.Count) - { - string prev = lines[i - 1]; - string cur = lines[i]; - if (prev.Length + cur.Length <= avgLen) - { - lines[i - 1] += " " + cur; - lines.RemoveAt(i); - } - else - i++; - } - } + float textHeight = elements.Select(el => el.Height).Max(); - //Further reduce the lines by taking the longest line as the max length - i = 1; - while (i < lines.Count) - { - string prev = lines[i - 1]; - string cur = lines[i]; - if (prev.Length + cur.Length <= lines.Max(el => el.Length)) - { - lines[i - 1] += " " + cur; - lines.RemoveAt(i); - } - else - i++; - } + //Try 1 line: + float textWidth = _GetWidth(elements, 0); + int maxHeight = Math.Min(availableHeight, _TextSize); + float factorH = maxHeight / textHeight; + float factorW = availableWidth / textWidth; + if (factorH <= factorW) + return factorH; //Limited by Height + float factor1 = factorW; - //If we still have more than 3 lines be more agressive - while (lines.Count > 3) + //Try 2 lines + if (elements.Count == 1) { - //Join the shortest line with shortest neighbour - int minLen = lines.Min(el => el.Length); - for (i = 0; i < lines.Count; i++) - { - if (lines[i].Length != minLen) - continue; - int other; - if (i == 0) - other = 1; - else if (i == lines.Count - 1) - other = i - 1; - else if (lines[i - 1].Length < lines[i + 1].Length) - other = i - 1; - else - other = i + 1; - lines[i] += " " + lines[other]; - lines.RemoveAt(other); + //Only 1 element -> 1 line + return factor1; + } + availableWidth -= _TextIndent; + maxHeight = Math.Min(availableHeight / 2 - _LineSpace, _TextSize); + 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, _TextSize); + 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; diff --git a/VocaluxeLib/Extensions.cs b/VocaluxeLib/Extensions.cs index f04d5f40..ae3f9c4c 100644 --- a/VocaluxeLib/Extensions.cs +++ b/VocaluxeLib/Extensions.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Text.RegularExpressions; namespace VocaluxeLib { @@ -91,28 +92,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, " "); } /// diff --git a/VocaluxeLib/Structs.cs b/VocaluxeLib/Structs.cs index d0c73628..f2b09991 100644 --- a/VocaluxeLib/Structs.cs +++ b/VocaluxeLib/Structs.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.Contracts; using System.Drawing; using System.Windows.Forms; using VocaluxeLib.Songs; @@ -48,6 +49,7 @@ namespace VocaluxeLib A = color.A; } + [Pure] public Color AsColor() { return Color.FromArgb((int)(A * 255), (int)(R * 255), (int)(G * 255), (int)(B * 255)); From 9f8dfaa50c121fe57ba9a2fd336b41ed8809a540 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Sat, 6 Sep 2014 12:48:56 +0200 Subject: [PATCH 03/13] Add range checks in XMLReader --- VocaluxeLib/CHelper.cs | 6 ++++++ VocaluxeLib/CXMLReader.cs | 27 ++++++++++++++++----------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/VocaluxeLib/CHelper.cs b/VocaluxeLib/CHelper.cs index 411d39ec..aaadf7bc 100644 --- a/VocaluxeLib/CHelper.cs +++ b/VocaluxeLib/CHelper.cs @@ -64,6 +64,12 @@ namespace VocaluxeLib return (int)result; } + public static bool InRange(this T val, T min, T max) where T : IComparable + { + Debug.Assert(min.CompareTo(max) <= 0); + return val.CompareTo(min) >= 0 && val.CompareTo(max) <= 0; + } + /// /// Makes sure val is between min and max /// Asserts that min<=max diff --git a/VocaluxeLib/CXMLReader.cs b/VocaluxeLib/CXMLReader.cs index 4b06b922..8ebf0d19 100644 --- a/VocaluxeLib/CXMLReader.cs +++ b/VocaluxeLib/CXMLReader.cs @@ -88,20 +88,25 @@ namespace VocaluxeLib return GetValue(xPath, out val, value.ToString()) && CHelper.TryParse(val, out value); } + /// + /// Gets a normalized (0<=x<=1) float value + /// + /// + /// + /// True if exists and is normalized + public bool TryGetNormalizedFloatValue(string xPath, ref float value) + { + string val; + return GetValue(xPath, out val, value.ToString()) && CHelper.TryParse(val, out value) && value.InRange(0f, 1f); + } + 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; } From 32241a91dc8c3caa8fc686cc38d8e08bbea49489 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Sat, 6 Sep 2014 12:49:25 +0200 Subject: [PATCH 04/13] Restructure and simplify Font loading --- Output/Fonts/Fonts.xml | 20 ++- Vocaluxe/Base/CTheme.cs | 2 +- Vocaluxe/Base/Fonts/CFont.cs | 6 +- Vocaluxe/Base/Fonts/CFonts.cs | 320 ++++++++++++++++------------------ Vocaluxe/Base/Fonts/CGlyph.cs | 2 +- Vocaluxe/Program.cs | 10 +- 6 files changed, 169 insertions(+), 191 deletions(-) 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/Vocaluxe/Base/CTheme.cs b/Vocaluxe/Base/CTheme.cs index 520353a4..e884384f 100644 --- a/Vocaluxe/Base/CTheme.cs +++ b/Vocaluxe/Base/CTheme.cs @@ -266,7 +266,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 diff --git a/Vocaluxe/Base/Fonts/CFont.cs b/Vocaluxe/Base/Fonts/CFont.cs index e1408b72..19e26c6e 100644 --- a/Vocaluxe/Base/Fonts/CFont.cs +++ b/Vocaluxe/Base/Fonts/CFont.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Text; +using System.Xml.Serialization; using VocaluxeLib; using VocaluxeLib.Draw; @@ -154,15 +155,14 @@ namespace Vocaluxe.Base.Fonts } } - struct SFont + struct SFontFamily { public string Name; - public bool IsThemeFont; public int PartyModeID; public string ThemeName; - public string Folder; + public string FileNormal; public string FileItalic; public string FileBold; diff --git a/Vocaluxe/Base/Fonts/CFonts.cs b/Vocaluxe/Base/Fonts/CFonts.cs index 1fa9ee31..e599e715 100644 --- a/Vocaluxe/Base/Fonts/CFonts.cs +++ b/Vocaluxe/Base/Fonts/CFonts.cs @@ -27,13 +27,12 @@ namespace Vocaluxe.Base.Fonts { static class CFonts { - private static readonly List _Fonts = new List(); + private static bool _IsInitialized; + private static readonly List _FontFamilies = new List(); private static int _CurrentFont; private static float _Height = 1f; - // ReSharper disable MemberCanBePrivate.Global public static int PartyModeID { get; set; } - // ReSharper restore MemberCanBePrivate.Global public static EStyle Style = EStyle.Normal; @@ -45,34 +44,34 @@ namespace Vocaluxe.Base.Fonts public static float Outline { - get { return _Fonts[_CurrentFont].Outline; } + get { return _FontFamilies[_CurrentFont].Outline; } } public static SColorF OutlineColor { - get { return _Fonts[_CurrentFont].OutlineColor; } + get { return _FontFamilies[_CurrentFont].OutlineColor; } } - public static void Init() + public static bool Init() { + if (_IsInitialized) + return false; _CurrentFont = 0; PartyModeID = -1; - _BuildFonts(); + return _LoadDefaultFonts(); } - private static void _BuildFonts() + public static void Close() { - foreach (SFont font in _Fonts) + foreach (SFontFamily font in _FontFamilies) { font.Normal.Dispose(); font.Bold.Dispose(); font.Italic.Dispose(); font.BoldItalic.Dispose(); } - _Fonts.Clear(); - _CurrentFont = 0; - - _LoadFontList(); + _FontFamilies.Clear(); + _IsInitialized = false; } public static Font GetFont() @@ -85,16 +84,16 @@ namespace Vocaluxe.Base.Fonts switch (Style) { case EStyle.Normal: - return _Fonts[_CurrentFont].Normal; + return _FontFamilies[_CurrentFont].Normal; case EStyle.Italic: - return _Fonts[_CurrentFont].Italic; + return _FontFamilies[_CurrentFont].Italic; case EStyle.Bold: - return _Fonts[_CurrentFont].Bold; + return _FontFamilies[_CurrentFont].Bold; case EStyle.BoldItalic: - return _Fonts[_CurrentFont].BoldItalic; + return _FontFamilies[_CurrentFont].BoldItalic; } //Just in case... - return _Fonts[_CurrentFont].Normal; + return _FontFamilies[_CurrentFont].Normal; } #region DrawText @@ -216,9 +215,9 @@ namespace Vocaluxe.Base.Fonts public static void SetFont(string fontName) { - int index = _GetPartyFontIndex(PartyModeID, fontName); + int index = _GetPartyFontIndex(fontName, PartyModeID); if (index < 0) - index = _GetThemeFontIndex(CConfig.Theme, fontName); + index = _GetThemeFontIndex(fontName, CConfig.Theme); if (index < 0) index = _GetFontIndex(fontName); if (index >= 0) @@ -266,60 +265,112 @@ namespace Vocaluxe.Base.Fonts public static float GetTextHeight(string text) { - //return TextRenderer.MeasureText(text, GetFont()).Height; CFont font = _GetCurrentFont(); return text == "" ? 0 : text.Select(font.GetHeight).Max(); } - private static void _LoadFontFiles(CXMLReader xmlReader, string fontFolder, string themeName = "", int partyModeId = -1) + private static int _GetFontIndex(string fontName) { - string value = string.Empty; - int i = 1; - while (xmlReader.GetValue("//root/Fonts/Font" + i + "/Folder", out value, value)) + for (int i = 0; i < _FontFamilies.Count; i++) { - var sf = new SFont {Folder = value, IsThemeFont = themeName != "", ThemeName = themeName, PartyModeID = partyModeId}; + if (_FontFamilies[i].Name == fontName) + return i; + } + + return -1; + } + + private static int _GetThemeFontIndex(string fontName, string themeName) + { + if (themeName == "" || fontName == "") + return -1; + + 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) + { + if (partyModeID == -1 || fontName == "") + return -1; + + for (int i = 0; i < _FontFamilies.Count; i++) + { + if (_FontFamilies[i].PartyModeID == partyModeID && _FontFamilies[i].Name == fontName) + return i; + } + + return -1; + } + + /// + /// Load default fonts + /// + /// + private static bool _LoadDefaultFonts() + { + 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)); + } + + /// + /// Loads theme fonts from skin file + /// + public static bool LoadThemeFonts(string themeName, string fontFolder, CXMLReader xmlReader) + { + bool ok = _LoadFontFile(xmlReader, fontFolder, themeName); + CLog.StartBenchmark("BuildGlyphs"); + _BuildGlyphs(); + CLog.StopBenchmark("BuildGlyphs"); + return ok; + } + + /// + /// Loads party mode fonts from skin file + /// + public static bool LoadPartyModeFonts(int partyModeID, string fontFolder, CXMLReader xmlReader) + { + bool ok = _LoadFontFile(xmlReader, fontFolder, "", partyModeID); + CLog.StartBenchmark("BuildGlyphs"); + _BuildGlyphs(); + CLog.StopBenchmark("BuildGlyphs"); + return ok; + } + + private static bool _LoadFontFile(CXMLReader xmlReader, string fontFolder, string themeName = "", int partyModeId = -1) + { + string value; + int i = 1; + while (xmlReader.GetValue("//root/Fonts/Font" + i + "/Folder", out value)) + { + 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 CFont(Path.Combine(fontFolder, sf.Folder, sf.FileNormal)); + sf.Italic = new CFont(Path.Combine(fontFolder, sf.Folder, sf.FileItalic)); + sf.Bold = new CFont(Path.Combine(fontFolder, sf.Folder, sf.FileBold)); + sf.BoldItalic = new CFont(Path.Combine(fontFolder, sf.Folder, sf.FileBoldItalic)); + _FontFamilies.Add(sf); + } else { string fontTypes; @@ -330,166 +381,87 @@ 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].Normal.Dispose(); + _FontFamilies[index].Italic.Dispose(); + _FontFamilies[index].Bold.Dispose(); + _FontFamilies[index].BoldItalic.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++) + for (int i = 0; i < _FontFamilies.Count; i++) { _CurrentFont = i; foreach (char chr in text) { Style = EStyle.Normal; - _Fonts[_CurrentFont].Normal.GetOrAddGlyph(chr); + _FontFamilies[_CurrentFont].Normal.GetOrAddGlyph(chr); Style = EStyle.Bold; - _Fonts[_CurrentFont].Bold.GetOrAddGlyph(chr); + _FontFamilies[_CurrentFont].Bold.GetOrAddGlyph(chr); Style = EStyle.Italic; - _Fonts[_CurrentFont].Italic.GetOrAddGlyph(chr); + _FontFamilies[_CurrentFont].Italic.GetOrAddGlyph(chr); Style = EStyle.BoldItalic; - _Fonts[_CurrentFont].BoldItalic.GetOrAddGlyph(chr); + _FontFamilies[_CurrentFont].BoldItalic.GetOrAddGlyph(chr); } } Style = EStyle.Normal; diff --git a/Vocaluxe/Base/Fonts/CGlyph.cs b/Vocaluxe/Base/Fonts/CGlyph.cs index c694c84d..087fa4f2 100644 --- a/Vocaluxe/Base/Fonts/CGlyph.cs +++ b/Vocaluxe/Base/Fonts/CGlyph.cs @@ -64,7 +64,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 diff --git a/Vocaluxe/Program.cs b/Vocaluxe/Program.cs index b4720e87..d317a2c2 100644 --- a/Vocaluxe/Program.cs +++ b/Vocaluxe/Program.cs @@ -173,10 +173,11 @@ namespace Vocaluxe Application.DoEvents(); - // Init Font - CLog.StartBenchmark("Init Font"); - CFonts.Init(); - CLog.StopBenchmark("Init Font"); + // Init Fonts + CLog.StartBenchmark("Init Fonts"); + if (!CFonts.Init()) + throw new CLoadingException("fonts"); + CLog.StopBenchmark("Init Fonts"); Application.DoEvents(); @@ -264,6 +265,7 @@ namespace Vocaluxe CVocaluxeServer.Close(); CGraphics.Close(); CCover.Close(); + CFonts.Close(); CBackgroundMusic.Close(); CWebcam.Close(); CDataBase.Close(); From 7f60e6f3210c4c35c7a3fc420eace61804029253 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Sat, 6 Sep 2014 13:57:55 +0200 Subject: [PATCH 05/13] First Step to make CFonts threadsave --- .../Base/Fonts/{CFont.cs => CFontStyle.cs} | 117 +++++++++--------- Vocaluxe/Base/Fonts/CFonts.cs | 112 ++++++++--------- Vocaluxe/Base/Fonts/CGlyph.cs | 40 +++--- Vocaluxe/Vocaluxe.csproj | 2 +- VocaluxeLib/Structs.cs | 2 + 5 files changed, 135 insertions(+), 138 deletions(-) rename Vocaluxe/Base/Fonts/{CFont.cs => CFontStyle.cs} (54%) diff --git a/Vocaluxe/Base/Fonts/CFont.cs b/Vocaluxe/Base/Fonts/CFontStyle.cs similarity index 54% rename from Vocaluxe/Base/Fonts/CFont.cs rename to Vocaluxe/Base/Fonts/CFontStyle.cs index 19e26c6e..360323ad 100644 --- a/Vocaluxe/Base/Fonts/CFont.cs +++ b/Vocaluxe/Base/Fonts/CFontStyle.cs @@ -19,54 +19,69 @@ using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Text; -using System.Xml.Serialization; using VocaluxeLib; 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) { @@ -81,99 +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; 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; 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; 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.DrawTextureReflection(texture, rect, color, rect, 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 = (_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 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 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 e599e715..2a9525c1 100644 --- a/Vocaluxe/Base/Fonts/CFonts.cs +++ b/Vocaluxe/Base/Fonts/CFonts.cs @@ -15,6 +15,7 @@ // along with Vocaluxe. If not, see . #endregion +using System; using System.Collections.Generic; using System.Drawing; using System.IO; @@ -25,6 +26,38 @@ using VocaluxeLib.Menu; namespace Vocaluxe.Base.Fonts { + struct SFont + { + public string Name; + public EStyle Style; + public float Height; + } + + /// + /// 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; + } + static class CFonts { private static bool _IsInitialized; @@ -42,16 +75,6 @@ namespace Vocaluxe.Base.Fonts set { _Height = value < 0f ? 0f : value; } } - public static float Outline - { - get { return _FontFamilies[_CurrentFont].Outline; } - } - - public static SColorF OutlineColor - { - get { return _FontFamilies[_CurrentFont].OutlineColor; } - } - public static bool Init() { if (_IsInitialized) @@ -74,12 +97,7 @@ namespace Vocaluxe.Base.Fonts _IsInitialized = false; } - public static Font GetFont() - { - return _GetCurrentFont().GetFont(); - } - - private static CFont _GetCurrentFont() + private static CFontStyle _GetCurrentFont() { switch (Style) { @@ -92,8 +110,7 @@ namespace Vocaluxe.Base.Fonts case EStyle.BoldItalic: return _FontFamilies[_CurrentFont].BoldItalic; } - //Just in case... - return _FontFamilies[_CurrentFont].Normal; + throw new ArgumentException("Invalid Style: " + Style); } #region DrawText @@ -125,13 +142,13 @@ namespace Vocaluxe.Base.Fonts return; Height = h; - CFont font = _GetCurrentFont(); + CFontStyle font = _GetCurrentFont(); float dx = x; foreach (char chr in text) { - font.DrawGlyph(chr, dx, y, z, color); - dx += font.GetWidth(chr); + font.DrawGlyph(chr, h, dx, y, z, color); + dx += font.GetWidth(chr, h); } } @@ -144,13 +161,13 @@ namespace Vocaluxe.Base.Fonts return; Height = h; - CFont font = _GetCurrentFont(); + CFontStyle font = _GetCurrentFont(); float dx = x; foreach (char chr in text) { - font.DrawGlyphReflection(chr, dx, y, z, color, rspace, rheight); - dx += font.GetWidth(chr); + font.DrawGlyphReflection(chr, h, dx, y, z, color, rspace, rheight); + dx += font.GetWidth(chr, h); } } @@ -172,11 +189,11 @@ namespace Vocaluxe.Base.Fonts float x1 = x + w * begin; float x2 = x + w * end; - CFont font = _GetCurrentFont(); + CFontStyle font = _GetCurrentFont(); foreach (char chr in text) { - float w2 = font.GetWidth(chr); + float w2 = font.GetWidth(chr, h); float b = (x1 - dx) / w2; if (b < 1f) @@ -188,7 +205,7 @@ namespace Vocaluxe.Base.Fonts { if (e > 1f) e = 1f; - font.DrawGlyph(chr, dx, y, z, color, b, e); + font.DrawGlyph(chr, h, dx, y, z, color, b, e); } } dx += w2; @@ -196,23 +213,6 @@ namespace Vocaluxe.Base.Fonts } #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(fontName, PartyModeID); @@ -259,14 +259,14 @@ namespace Vocaluxe.Base.Fonts public static float GetTextWidth(string text) { - CFont font = _GetCurrentFont(); - return text.Sum(chr => font.GetWidth(chr)); + CFontStyle font = _GetCurrentFont(); + return text.Sum(chr => font.GetWidth(chr, Height)); } public static float GetTextHeight(string text) { - CFont font = _GetCurrentFont(); - return text == "" ? 0 : text.Select(font.GetHeight).Max(); + CFontStyle font = _GetCurrentFont(); + return text == "" ? 0 : text.Select(chr => font.GetHeight(chr, Height)).Max(); } private static int _GetFontIndex(string fontName) @@ -365,10 +365,10 @@ namespace Vocaluxe.Base.Fonts if (ok) { - sf.Normal = new CFont(Path.Combine(fontFolder, sf.Folder, sf.FileNormal)); - sf.Italic = new CFont(Path.Combine(fontFolder, sf.Folder, sf.FileItalic)); - sf.Bold = new CFont(Path.Combine(fontFolder, sf.Folder, sf.FileBold)); - sf.BoldItalic = new CFont(Path.Combine(fontFolder, sf.Folder, sf.FileBoldItalic)); + 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 @@ -455,13 +455,13 @@ namespace Vocaluxe.Base.Fonts foreach (char chr in text) { Style = EStyle.Normal; - _FontFamilies[_CurrentFont].Normal.GetOrAddGlyph(chr); + _FontFamilies[_CurrentFont].Normal.GetOrAddGlyph(chr, Height); Style = EStyle.Bold; - _FontFamilies[_CurrentFont].Bold.GetOrAddGlyph(chr); + _FontFamilies[_CurrentFont].Bold.GetOrAddGlyph(chr, Height); Style = EStyle.Italic; - _FontFamilies[_CurrentFont].Italic.GetOrAddGlyph(chr); + _FontFamilies[_CurrentFont].Italic.GetOrAddGlyph(chr, Height); Style = EStyle.BoldItalic; - _FontFamilies[_CurrentFont].BoldItalic.GetOrAddGlyph(chr); + _FontFamilies[_CurrentFont].BoldItalic.GetOrAddGlyph(chr, Height); } } Style = EStyle.Normal; diff --git a/Vocaluxe/Base/Fonts/CGlyph.cs b/Vocaluxe/Base/Fonts/CGlyph.cs index 087fa4f2..dcc2bff3 100644 --- a/Vocaluxe/Base/Fonts/CGlyph.cs +++ b/Vocaluxe/Base/Fonts/CGlyph.cs @@ -36,26 +36,13 @@ namespace Vocaluxe.Base.Fonts 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)) @@ -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 CTexture 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; diff --git a/Vocaluxe/Vocaluxe.csproj b/Vocaluxe/Vocaluxe.csproj index b8276467..6f34656a 100644 --- a/Vocaluxe/Vocaluxe.csproj +++ b/Vocaluxe/Vocaluxe.csproj @@ -241,7 +241,7 @@ - + diff --git a/VocaluxeLib/Structs.cs b/VocaluxeLib/Structs.cs index d0c73628..f2b09991 100644 --- a/VocaluxeLib/Structs.cs +++ b/VocaluxeLib/Structs.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.Contracts; using System.Drawing; using System.Windows.Forms; using VocaluxeLib.Songs; @@ -48,6 +49,7 @@ namespace VocaluxeLib A = color.A; } + [Pure] public Color AsColor() { return Color.FromArgb((int)(A * 255), (int)(R * 255), (int)(G * 255), (int)(B * 255)); From 94be5ce97b307ff3499ee55906c95fe4fc472a22 Mon Sep 17 00:00:00 2001 From: Flamefire Date: Sun, 7 Sep 2014 10:21:42 +0200 Subject: [PATCH 06/13] Use CFont class to define the font to use --- .../CPartyScreenChallengeMain.cs | 6 +- .../CPartyScreenChallengeMedleyMain.cs | 4 +- Vocaluxe/Base/CGraphics.cs | 9 +- Vocaluxe/Base/CMain.cs | 27 +-- Vocaluxe/Base/Fonts/CFontStyle.cs | 2 +- Vocaluxe/Base/Fonts/CFonts.cs | 210 +++++++----------- Vocaluxe/Screens/CScreenSing.cs | 10 +- VocaluxeLib/CFont.cs | 87 ++++++++ VocaluxeLib/IBasic.cs | 10 +- VocaluxeLib/Menu/CLyric.cs | 13 +- VocaluxeLib/Menu/CText.cs | 114 ++++------ VocaluxeLib/PartyModes/CScreenPartyDummy.cs | 4 +- VocaluxeLib/VocaluxeLib.csproj | 1 + 13 files changed, 242 insertions(+), 255 deletions(-) create mode 100644 VocaluxeLib/CFont.cs 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/CGraphics.cs b/Vocaluxe/Base/CGraphics.cs index eb53d99a..454ff698 100644 --- a/Vocaluxe/Base/CGraphics.cs +++ b/Vocaluxe/Base/CGraphics.cs @@ -624,16 +624,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)); + float textWidth = CFonts.GetTextWidth(txt, font); + RectangleF rect = new RectangleF(CSettings.RenderW - textWidth, y, textWidth, CFonts.GetTextHeight(txt,font)); CDraw.DrawColor(gray, new SRectF(rect.X, rect.Top, rect.Width, rect.Height, CSettings.ZNear)); - CFonts.DrawText(txt, rect.X, rect.Y, CSettings.ZNear); + CFonts.DrawText(txt, font, rect.X, rect.Y, CSettings.ZNear); y += rect.Height; } } diff --git a/Vocaluxe/Base/CMain.cs b/Vocaluxe/Base/CMain.cs index 5856bab5..4b4575b5 100644 --- a/Vocaluxe/Base/CMain.cs +++ b/Vocaluxe/Base/CMain.cs @@ -452,39 +452,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); } } diff --git a/Vocaluxe/Base/Fonts/CFontStyle.cs b/Vocaluxe/Base/Fonts/CFontStyle.cs index 360323ad..0c80c0c1 100644 --- a/Vocaluxe/Base/Fonts/CFontStyle.cs +++ b/Vocaluxe/Base/Fonts/CFontStyle.cs @@ -138,7 +138,7 @@ namespace Vocaluxe.Base.Fonts CGlyph glyph; if (!_Glyphs.TryGetValue(chr, out glyph)) { - float maxHeight = (_MaxGlyphHeight + 50 >= height) ? _MaxGlyphHeight : (float)Math.Round(height / 50) * 50; + float maxHeight = (height < 0 || _MaxGlyphHeight + 50 >= height) ? _MaxGlyphHeight : (float)Math.Round(height / 50) * 50; glyph = new CGlyph(chr, this, maxHeight); _Glyphs.Add(chr, glyph); } diff --git a/Vocaluxe/Base/Fonts/CFonts.cs b/Vocaluxe/Base/Fonts/CFonts.cs index 2a9525c1..e7ac6bdb 100644 --- a/Vocaluxe/Base/Fonts/CFonts.cs +++ b/Vocaluxe/Base/Fonts/CFonts.cs @@ -26,13 +26,6 @@ using VocaluxeLib.Menu; namespace Vocaluxe.Base.Fonts { - struct SFont - { - public string Name; - public EStyle Style; - public float Height; - } - /// /// Struct used for describing a font family (a type of text with 4 different styles) /// @@ -62,24 +55,14 @@ namespace Vocaluxe.Base.Fonts { private static bool _IsInitialized; private static readonly List _FontFamilies = new List(); - private static int _CurrentFont; - private static float _Height = 1f; + private static readonly List _LoggedMissingFonts = new List(); public static int PartyModeID { get; set; } - public static EStyle Style = EStyle.Normal; - - public static float Height - { - get { return _Height; } - set { _Height = value < 0f ? 0f : value; } - } - public static bool Init() { if (_IsInitialized) return false; - _CurrentFont = 0; PartyModeID = -1; return _LoadDefaultFonts(); } @@ -97,133 +80,96 @@ namespace Vocaluxe.Base.Fonts _IsInitialized = false; } - private static CFontStyle _GetCurrentFont() - { - switch (Style) - { - case EStyle.Normal: - return _FontFamilies[_CurrentFont].Normal; - case EStyle.Italic: - return _FontFamilies[_CurrentFont].Italic; - case EStyle.Bold: - return _FontFamilies[_CurrentFont].Bold; - case EStyle.BoldItalic: - return _FontFamilies[_CurrentFont].BoldItalic; - } - throw new ArgumentException("Invalid Style: " + Style); - } - #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; - CFontStyle font = _GetCurrentFont(); + CFontStyle fontStyle = _GetFontStyle(font); float dx = x; foreach (char chr in text) { - font.DrawGlyph(chr, h, dx, y, z, color); - dx += font.GetWidth(chr, h); + 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; - CFontStyle font = _GetCurrentFont(); + CFontStyle fontStyle = _GetFontStyle(font); float dx = x; foreach (char chr in text) { - font.DrawGlyphReflection(chr, h, dx, y, z, color, rspace, rheight); - dx += font.GetWidth(chr, h); + 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; - CFontStyle font = _GetCurrentFont(); + CFontStyle fontStyle = _GetFontStyle(font); foreach (char chr in text) { - float w2 = font.GetWidth(chr, h); - 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, h, 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 void SetFont(string fontName) - { - int index = _GetPartyFontIndex(fontName, PartyModeID); - if (index < 0) - index = _GetThemeFontIndex(fontName, CConfig.Theme); - if (index < 0) - index = _GetFontIndex(fontName); - if (index >= 0) - _CurrentFont = index; - } - /// /// Calculates the bounds for a CText object /// @@ -231,42 +177,50 @@ 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)); } - /// - /// Calculates the bounds for a CText object - /// - /// 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 CFontStyle _GetFontStyle(CFont font) { - 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; + int index = _GetPartyFontIndex(font.Name, PartyModeID); + if (index < 0) + index = _GetThemeFontIndex(font.Name, CConfig.Theme); + if (index < 0) + index = _GetFontIndex(font.Name); + if (index < 0) + { + if (!_LoggedMissingFonts.Contains(font.Name)) + { + _LoggedMissingFonts.Add(font.Name); + CLog.LogError("Font \"" + font.Name + "\" not found!"); + } + index = 0; + } + + 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) + public static float GetTextWidth(string text, CFont font) { - CFontStyle font = _GetCurrentFont(); - return text.Sum(chr => font.GetWidth(chr, Height)); + CFontStyle fontStyle = _GetFontStyle(font); + return text.Sum(chr => fontStyle.GetWidth(chr, font.Height)); } - public static float GetTextHeight(string text) + public static float GetTextHeight(string text, CFont font) { - CFontStyle font = _GetCurrentFont(); - return text == "" ? 0 : text.Select(chr => font.GetHeight(chr, Height)).Max(); + CFontStyle fontStyle = _GetFontStyle(font); + return text == "" ? 0 : text.Select(chr => fontStyle.GetHeight(chr, font.Height)).Max(); } private static int _GetFontIndex(string fontName) @@ -448,24 +402,16 @@ namespace Vocaluxe.Base.Fonts { const string text = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; - for (int i = 0; i < _FontFamilies.Count; i++) + foreach (SFontFamily fontFamily in _FontFamilies) { - _CurrentFont = i; - foreach (char chr in text) { - Style = EStyle.Normal; - _FontFamilies[_CurrentFont].Normal.GetOrAddGlyph(chr, Height); - Style = EStyle.Bold; - _FontFamilies[_CurrentFont].Bold.GetOrAddGlyph(chr, Height); - Style = EStyle.Italic; - _FontFamilies[_CurrentFont].Italic.GetOrAddGlyph(chr, Height); - Style = EStyle.BoldItalic; - _FontFamilies[_CurrentFont].BoldItalic.GetOrAddGlyph(chr, Height); + 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/Screens/CScreenSing.cs b/Vocaluxe/Screens/CScreenSing.cs index d9a44b1a..7190397e 100644 --- a/Vocaluxe/Screens/CScreenSing.cs +++ b/Vocaluxe/Screens/CScreenSing.cs @@ -991,13 +991,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; diff --git a/VocaluxeLib/CFont.cs b/VocaluxeLib/CFont.cs new file mode 100644 index 00000000..00524ec5 --- /dev/null +++ b/VocaluxeLib/CFont.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; + +namespace VocaluxeLib +{ + public interface IFontObserver + { + void FontChanged(); + } + + 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(CFont font) + { + _Name = font.Name; + _Style = font.Style; + _Height = font.Height; + } + + 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/IBasic.cs b/VocaluxeLib/IBasic.cs index ef14e905..32bd781f 100644 --- a/VocaluxeLib/IBasic.cs +++ b/VocaluxeLib/IBasic.cs @@ -159,15 +159,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 diff --git a/VocaluxeLib/Menu/CLyric.cs b/VocaluxeLib/Menu/CLyric.cs index 68b26136..f496330f 100644 --- a/VocaluxeLib/Menu/CLyric.cs +++ b/VocaluxeLib/Menu/CLyric.cs @@ -205,10 +205,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() @@ -350,11 +347,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; @@ -364,7 +361,7 @@ namespace VocaluxeLib.Menu _Text.Draw(); _Text.Y = ty; - _Text.Height = th; + _Text.Font.Height = th; _Text.Z = tz; } } @@ -456,7 +453,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/CText.cs b/VocaluxeLib/Menu/CText.cs index 711cc930..70389fe0 100644 --- a/VocaluxeLib/Menu/CText.cs +++ b/VocaluxeLib/Menu/CText.cs @@ -28,9 +28,12 @@ namespace VocaluxeLib.Menu public string Text; public string ColorName; public string SelColorName; //for Buttons + public string FontFamily; + public EStyle FontStyle; + public float FontHeight; } - public class CText : IMenuElement + public class CText : IMenuElement, IFontObserver { private SThemeText _Theme; private bool _ThemeLoaded; @@ -82,20 +85,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 { @@ -138,29 +127,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; } } @@ -199,10 +176,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 { @@ -246,7 +229,7 @@ namespace VocaluxeLib.Menu { _PartyModeID = partyModeID; _TranslationID = _PartyModeID; - _Font = "Normal"; + _Font.AddObserver(this); } public CText(CText text) @@ -257,12 +240,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); @@ -277,24 +258,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); @@ -323,7 +303,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.ColorName, String.Empty)) @@ -350,8 +329,10 @@ 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); + Font = new CFont(_Theme.FontFamily, _Theme.FontStyle, _Theme.FontHeight); _ThemeLoaded &= xmlReader.GetValue(item + "/Text", out _Theme.Text, String.Empty); @@ -388,7 +369,7 @@ namespace VocaluxeLib.Menu } writer.WriteComment(": Text height"); - writer.WriteElementString("H", Height.ToString("#0")); + writer.WriteElementString("H", _Theme.FontHeight.ToString("#0")); writer.WriteComment(": Maximum text width (if exists)"); if (MaxWidth > 0) @@ -425,10 +406,10 @@ namespace VocaluxeLib.Menu writer.WriteElementString("ResizeAlign", Enum.GetName(typeof(EHAlignment), ResizeAlign)); writer.WriteComment(" 500 - - + + + + 1110.8 - - 24 - 24 - - 100 - - - 1111 - - - EmptyCover.png - - true - - 0.75 - - + + + + 24 + 24 + + + 100 + + + 1111 + + + EmptyCover.png + + true + + 0.75 + diff --git a/Vocaluxe/Base/CConfig.cs b/Vocaluxe/Base/CConfig.cs index 12d913a0..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 { @@ -840,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 0297ab7c..096e988f 100644 --- a/Vocaluxe/Base/CCover.cs +++ b/Vocaluxe/Base/CCover.cs @@ -23,24 +23,23 @@ 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 const string _NoCoverName = "No Cover"; private const string _NoCoverNameAlt = "NoCover"; - private const string _DefaultTypeName = "Default"; private static readonly Dictionary _Covers = new Dictionary(); - private static readonly Dictionary _NoCoverThemes = new Dictionary(); - private static readonly List _CoverThemes = new List(); + private static readonly Dictionary _NoCoverGenerators = new Dictionary(); + private static readonly List _CoverThemes = new List(); public static CTextureRef NoCover { get; private set; } @@ -105,9 +104,10 @@ namespace Vocaluxe.Base CTextureRef texture = CDraw.CopyTexture(NoCover); Task.Factory.StartNew(() => { - Bitmap coverBmp = _NoCoverThemes[_SongSortingToType(sorting)].GetCover(text, firstSong != null ? Path.Combine(firstSong.Folder, firstSong.CoverFileName) : null); - if (coverBmp == null) - coverBmp = _NoCoverThemes[_DefaultTypeName].GetCover(text, firstSong != null ? Path.Combine(firstSong.Folder, firstSong.CoverFileName) : null); + 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); }); @@ -147,23 +147,23 @@ namespace Vocaluxe.Base } _Covers.Clear(); } - lock (_NoCoverThemes) + lock (_NoCoverGenerators) { - _NoCoverThemes.Clear(); + _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(); } /// @@ -182,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); } @@ -202,7 +202,7 @@ namespace Vocaluxe.Base /// private static void _LoadCovers(string coverThemeName) { - SCoverTheme coverTheme = _CoverTheme(coverThemeName); + SThemeCover coverTheme = _CoverTheme(coverThemeName); if (String.IsNullOrEmpty(coverTheme.Name)) return; @@ -224,51 +224,53 @@ namespace Vocaluxe.Base _LoadNoCoverThemes(coverTheme); } - private static string _SongSortingToType(ESongSorting sorting) + private static ECoverGeneratorType _SongSortingToType(ESongSorting sorting) { switch (sorting) { case ESongSorting.TR_CONFIG_NONE: - return _DefaultTypeName; + return ECoverGeneratorType.Default; case ESongSorting.TR_CONFIG_FOLDER: - return "Folder"; + return ECoverGeneratorType.Folder; case ESongSorting.TR_CONFIG_ARTIST: - return "Artist"; + return ECoverGeneratorType.Artist; case ESongSorting.TR_CONFIG_ARTIST_LETTER: case ESongSorting.TR_CONFIG_TITLE_LETTER: - return "Letter"; + return ECoverGeneratorType.Letter; case ESongSorting.TR_CONFIG_EDITION: - return "Edition"; + return ECoverGeneratorType.Edition; case ESongSorting.TR_CONFIG_GENRE: - return "Genre"; + return ECoverGeneratorType.Genre; case ESongSorting.TR_CONFIG_LANGUAGE: - return "Language"; + return ECoverGeneratorType.Language; case ESongSorting.TR_CONFIG_YEAR: - return "Year"; + return ECoverGeneratorType.Year; case ESongSorting.TR_CONFIG_DECADE: - return "Decade"; + return ECoverGeneratorType.Decade; case ESongSorting.TR_CONFIG_DATEADDED: - return "Date"; + return ECoverGeneratorType.Date; default: throw new ArgumentOutOfRangeException("sorting"); } } - private static void _LoadNoCoverThemes(SCoverTheme coverTheme) + private static void _LoadNoCoverThemes(SThemeCover coverTheme) { - CXMLReader xmlReader = CXMLReader.OpenFile(Path.Combine(CSettings.ProgramFolder, CSettings.FolderNameCover, coverTheme.File)); - List types = new List(); - foreach (ESongSorting sorting in Enum.GetValues(typeof(ESongSorting))) - types.Add(_SongSortingToType(sorting)); + CXMLReader xmlReader = CXMLReader.OpenFile(coverTheme.FilePath); string coverPath = Path.Combine(CSettings.ProgramFolder, CSettings.FolderNameCover, coverTheme.Folder); - lock (_NoCoverThemes) + lock (_NoCoverGenerators) { - foreach (string type in types) - { - if (_NoCoverThemes.ContainsKey(type)) - continue; - CNoCoverGenerator el = new CNoCoverGenerator(xmlReader, "//root/MissingCover/" + type, coverPath); - _NoCoverThemes.Add(type, el); + 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/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/CNoCoverGenerator.cs b/Vocaluxe/Base/CNoCoverGenerator.cs index 6c7803ef..d92177e0 100644 --- a/Vocaluxe/Base/CNoCoverGenerator.cs +++ b/Vocaluxe/Base/CNoCoverGenerator.cs @@ -36,20 +36,10 @@ namespace Vocaluxe.Base class CNoCoverGenerator { private readonly bool _Valid; - private readonly string _Text; - private readonly SColorF _BGColor; - private readonly SColorF _TextColor; - private readonly int _TextMarginLeft; - private readonly int _TextMarginRight; - private readonly int _TextMarginTop; - private readonly int _TextMarginBottom; - private readonly int _TextIndent; - private readonly int _TextSize; - private readonly string _Font; - private readonly EStyle _Style; + private readonly SThemeCoverGenerator _Theme; private readonly string _Image; - private readonly bool _ShowFirstCover; - private readonly float _ImageAlpha; + private readonly SColorF _TextColor; + private readonly SColorF _BGColor; private const int _LineSpace = 5; /// @@ -59,52 +49,15 @@ namespace Vocaluxe.Base private static readonly char[] _SplitCharAfter = {':', '/', '-', ' ', ')', '.', '*', ','}; private static readonly char[] _SplitCharBefore = {'('}; - public CNoCoverGenerator(CXMLReader xmlReader, string xPath, string basePath) + public CNoCoverGenerator(SThemeCoverGenerator theme, string basePath) { - _Valid = xmlReader.ItemExists(xPath); - if (!_Valid) - return; - _Valid &= xmlReader.GetValue(xPath + "/Text/Text", out _Text); - _Valid &= xmlReader.GetValue(xPath + "/Text/Font", out _Font); - _Valid &= xmlReader.TryGetEnumValue(xPath + "/Text/Style", ref _Style); - _Valid &= xmlReader.TryGetIntValue(xPath + "/Text/Size", ref _TextSize); - string colorName; - if (xmlReader.GetValue(xPath + "/Text/Color", out colorName)) - _Valid &= CBase.Theme.GetColor(colorName, CBase.Theme.GetSkinIndex(-1), out _TextColor); - else - _Valid &= xmlReader.TryGetColorFromRGBA(xPath + "/Text", ref _TextColor); - int tmpMargin = 0; - bool marginSet = xmlReader.TryGetIntValue(xPath + "/Text/Margin", ref tmpMargin); - if (marginSet) - { - _TextMarginLeft = tmpMargin; - _TextMarginRight = tmpMargin; - _TextMarginTop = tmpMargin; - _TextMarginBottom = tmpMargin; - } - _Valid &= xmlReader.TryGetIntValue(xPath + "/Text/MarginLeft", ref _TextMarginLeft) || marginSet; - _Valid &= xmlReader.TryGetIntValue(xPath + "/Text/MarginRight", ref _TextMarginRight) || marginSet; - _Valid &= xmlReader.TryGetIntValue(xPath + "/Text/MarginTop", ref _TextMarginTop) || marginSet; - _Valid &= xmlReader.TryGetIntValue(xPath + "/Text/MarginBottom", ref _TextMarginBottom) || marginSet; - if (!xmlReader.TryGetIntValue(xPath + "/Text/Indent", ref _TextIndent)) - _TextIndent = 6 * (_TextMarginLeft + _TextMarginRight) / 2; - - if (xmlReader.GetValue(xPath + "/Background/Color", out colorName)) - _Valid &= CBase.Theme.GetColor(colorName, CBase.Theme.GetSkinIndex(-1), out _BGColor); - else - _Valid &= xmlReader.TryGetColorFromRGBA(xPath + "/Background", ref _BGColor); - _Valid &= xmlReader.GetValue(xPath + "/Image", out _Image); - string tmp; - if (xmlReader.GetValue(xPath + "/ShowFirstCover", out tmp)) - Boolean.TryParse(tmp, out _ShowFirstCover); - else - _ShowFirstCover = false; - _ImageAlpha = !xmlReader.TryGetFloatValue(xPath + "/ImageAlpha", ref _ImageAlpha) ? 0.5f : _ImageAlpha.Clamp(0f, 1f); + _Theme = theme; + _Valid = true; + _Valid &= _Theme.Text.Color.Get(-1, out _TextColor); + _Valid &= _Theme.BackgroundColor.Get(-1, out _BGColor); if (_Valid) { - // ReSharper disable AssignNullToNotNullAttribute - _Image = Path.Combine(basePath, _Image); - // ReSharper restore AssignNullToNotNullAttribute + _Image = Path.Combine(basePath, _Theme.Image); _Valid = File.Exists(_Image); } } @@ -153,11 +106,11 @@ namespace Vocaluxe.Base g.Clear(_BGColor.AsColor()); ImageAttributes ia = null; - if (_ShowFirstCover && !String.IsNullOrEmpty(firstCoverPath) && File.Exists(firstCoverPath)) + 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 = _ImageAlpha}; + ColorMatrix cm = new ColorMatrix {Matrix33 = _Theme.ImageAlpha}; ia = new ImageAttributes(); ia.SetColorMatrix(cm); } @@ -182,7 +135,7 @@ namespace Vocaluxe.Base { pen.LineJoin = LineJoin.Round; pen.Alignment = PenAlignment.Outset; - float top = (bmpSize.Height - _TextMarginBottom - _TextMarginTop - maxHeight * lineCount) / 2 + _TextMarginTop; + 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++) { @@ -199,16 +152,16 @@ namespace Vocaluxe.Base { //Center Text if this is the only line or the middle line float width = _GetWidth(elements, firstEl, nextLineEl - 1); - left = (bmpSize.Width - _TextMarginLeft - _TextMarginRight - width) / 2 + _TextMarginLeft; + 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 - _TextMarginRight; + left = bmpSize.Width - width - _Theme.Text.Margin.Right; } else - left = _TextMarginLeft; + 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; @@ -222,7 +175,7 @@ namespace Vocaluxe.Base { if (!_Valid) return null; - text = CLanguage.Translate(_Text.Replace("%TEXT%", text)); + 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); @@ -238,7 +191,7 @@ namespace Vocaluxe.Base if (text != "") { - CFont font = new CFont(_Font, _Style, bmp.Height - _TextMarginTop - _TextMarginBottom); + 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(); @@ -269,14 +222,14 @@ namespace Vocaluxe.Base /// Factor for resizing the text (Maximizing this factor means maximizing the height) private float _DistributeText(List elements, int width, int height) { - int availableWidth = width - _TextMarginLeft - _TextMarginRight; - int availableHeight = height - _TextMarginTop - _TextMarginBottom; + 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, _TextSize); + int maxHeight = Math.Min(availableHeight, (int)_Theme.Text.Font.Size); float factorH = maxHeight / textHeight; float factorW = availableWidth / textWidth; if (factorH <= factorW) @@ -289,8 +242,8 @@ namespace Vocaluxe.Base //Only 1 element -> 1 line return factor1; } - availableWidth -= _TextIndent; - maxHeight = Math.Min(availableHeight / 2 - _LineSpace, _TextSize); + 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 @@ -311,7 +264,7 @@ namespace Vocaluxe.Base float factor2 = factorW; //Try 3 lines - maxHeight = Math.Min(availableHeight / 3 - 2 * _LineSpace, _TextSize); + maxHeight = Math.Min(availableHeight / 3 - 2 * _LineSpace, (int)_Theme.Text.Font.Size); factorH = maxHeight / textHeight; if (elements.Count == 2 || factorH <= Math.Max(factor1, factor2)) { 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/CTheme.cs b/Vocaluxe/Base/CTheme.cs index 8c8e9068..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 { diff --git a/Vocaluxe/Base/Fonts/CFonts.cs b/Vocaluxe/Base/Fonts/CFonts.cs index a617dfd5..bdcdefbe 100644 --- a/Vocaluxe/Base/Fonts/CFonts.cs +++ b/Vocaluxe/Base/Fonts/CFonts.cs @@ -24,6 +24,7 @@ using System.Linq; using System.Xml; using VocaluxeLib; using VocaluxeLib.Menu; +using VocaluxeLib.Xml; namespace Vocaluxe.Base.Fonts { 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/VocaluxeLib/CFont.cs b/VocaluxeLib/CFont.cs index 00524ec5..bb8fed4e 100644 --- a/VocaluxeLib/CFont.cs +++ b/VocaluxeLib/CFont.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Xml.Serialization; namespace VocaluxeLib { @@ -8,6 +9,13 @@ namespace VocaluxeLib void FontChanged(); } + public struct SThemeFont + { + public string Name; + public EStyle Style; + public float Size; + } + public class CFont { private string _Name; @@ -59,11 +67,11 @@ namespace VocaluxeLib _Height = 25f; } - public CFont(CFont font) + public CFont(SThemeFont font) { _Name = font.Name; _Style = font.Style; - _Height = font.Height; + _Height = font.Size; } public CFont(string fontFamily, EStyle style, float h) diff --git a/VocaluxeLib/Menu/CBackground.cs b/VocaluxeLib/Menu/CBackground.cs index 9faeb4c9..46a64c3d 100644 --- a/VocaluxeLib/Menu/CBackground.cs +++ b/VocaluxeLib/Menu/CBackground.cs @@ -21,6 +21,7 @@ using System.Xml.Serialization; using System.Diagnostics; using System.Collections.Generic; using VocaluxeLib.Draw; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu { @@ -36,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; } diff --git a/VocaluxeLib/Menu/CButton.cs b/VocaluxeLib/Menu/CButton.cs index d842a04a..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 @@ -257,7 +249,6 @@ namespace VocaluxeLib.Menu return _ThemeLoaded; } - public void Draw(bool forceDraw = false) { if (!Visible && CBase.Settings.GetProgramState() != EProgramState.EditTheme && !forceDraw) diff --git a/VocaluxeLib/Menu/CEqualizer.cs b/VocaluxeLib/Menu/CEqualizer.cs index 732d3105..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 @@ -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 7a15a8a3..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,7 +189,7 @@ namespace VocaluxeLib.Menu private void _SetText(CSongNote note) { _Text.Text = note.Text; - _Text.Font.Style = (note.Type == ENoteType.Freestyle)? EStyle.BoldItalic:EStyle.Bold; + _Text.Font.Style = (note.Type == ENoteType.Freestyle) ? EStyle.BoldItalic : EStyle.Bold; } public void Clear() @@ -338,7 +335,7 @@ namespace VocaluxeLib.Menu float tz = _Text.Z; SRectF normalRect = _Text.Rect; - _Text.Font.Height *= 1f+ 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; diff --git a/VocaluxeLib/Menu/CMenu.cs b/VocaluxeLib/Menu/CMenu.cs index 2a69aed7..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 { diff --git a/VocaluxeLib/Menu/CNameSelection.cs b/VocaluxeLib/Menu/CNameSelection.cs index f02fa261..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 @@ -119,7 +102,6 @@ namespace VocaluxeLib.Menu 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 25c72041..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 @@ -159,8 +153,6 @@ namespace VocaluxeLib.Menu _MaxAge = maxage; _Rotation = (float)(CBase.Game.GetRandomDouble() * 360.0); } - - #endregion Constructors public void Update() diff --git a/VocaluxeLib/Menu/CPlaylist.cs b/VocaluxeLib/Menu/CPlaylist.cs index 6d3ec87f..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(); } diff --git a/VocaluxeLib/Menu/CScreenSetting.cs b/VocaluxeLib/Menu/CScreenSetting.cs index c46667c1..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 diff --git a/VocaluxeLib/Menu/CSelectSlide.cs b/VocaluxeLib/Menu/CSelectSlide.cs index 639e1c9e..05289057 100644 --- a/VocaluxeLib/Menu/CSelectSlide.cs +++ b/VocaluxeLib/Menu/CSelectSlide.cs @@ -20,6 +20,7 @@ using System.Collections.Generic; using System.Drawing; using System.Xml.Serialization; using VocaluxeLib.Draw; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu { diff --git a/VocaluxeLib/Menu/CStatic.cs b/VocaluxeLib/Menu/CStatic.cs index 7d1dde1e..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; @@ -73,10 +72,7 @@ namespace VocaluxeLib.Menu public EAspect Aspect = EAspect.Stretch; - public CStatic() - { - - } + public CStatic() {} public CStatic(int partyModeID) { diff --git a/VocaluxeLib/Menu/CText.cs b/VocaluxeLib/Menu/CText.cs index d03de785..9f8c164f 100644 --- a/VocaluxeLib/Menu/CText.cs +++ b/VocaluxeLib/Menu/CText.cs @@ -18,6 +18,7 @@ using System; using System.Drawing; using System.Xml.Serialization; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu { 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 bda44a04..276f586b 100644 --- a/VocaluxeLib/Menu/SingNotes/CSingNotes.cs +++ b/VocaluxeLib/Menu/SingNotes/CSingNotes.cs @@ -21,6 +21,7 @@ using System.Diagnostics; using System.Xml.Serialization; using VocaluxeLib.Draw; using VocaluxeLib.Songs; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu.SingNotes { 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 8e7a224d..7906499d 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,10 +268,10 @@ namespace VocaluxeLib.Menu.SongMenu } public virtual void OnShow() {} - + public virtual void OnHide() { - if(CBase.Graphics.GetNextScreen() != EScreens.ScreenNames) + if (CBase.Graphics.GetNextScreen() != EScreens.ScreenNames) _ResetPreview(); } @@ -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 14504d80..8a3c8190 100644 --- a/VocaluxeLib/Menu/SongMenu/CSongMenuTileBoard.cs +++ b/VocaluxeLib/Menu/SongMenu/CSongMenuTileBoard.cs @@ -23,6 +23,7 @@ using System.Xml; using VocaluxeLib.Draw; using VocaluxeLib.PartyModes; using VocaluxeLib.Songs; +using VocaluxeLib.Xml; namespace VocaluxeLib.Menu.SongMenu { 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/Structs.cs b/VocaluxeLib/Structs.cs index 4d80e919..743585d5 100644 --- a/VocaluxeLib/Structs.cs +++ b/VocaluxeLib/Structs.cs @@ -26,6 +26,44 @@ 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 @@ -82,6 +120,16 @@ namespace VocaluxeLib 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 diff --git a/VocaluxeLib/VocaluxeLib.csproj b/VocaluxeLib/VocaluxeLib.csproj index f270b839..fb1e0737 100644 --- a/VocaluxeLib/VocaluxeLib.csproj +++ b/VocaluxeLib/VocaluxeLib.csproj @@ -103,7 +103,8 @@ - + + @@ -139,6 +140,7 @@ + diff --git a/VocaluxeLib/CXMLReader.cs b/VocaluxeLib/Xml/CXMLReader.cs similarity index 70% rename from VocaluxeLib/CXMLReader.cs rename to VocaluxeLib/Xml/CXMLReader.cs index 3f06c661..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,49 +57,6 @@ 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); - } - - /// - /// Gets a normalized (0<=x<=1) float value - /// - /// - /// - /// True if exists and is normalized - public bool TryGetNormalizedFloatValue(string xPath, ref float value) - { - string val; - return GetValue(xPath, out val, value.ToString()) && CHelper.TryParse(val, out value) && value.IsInRange(0f, 1f); - } - public bool TryGetColorFromRGBA(string xPath, ref SColorF value) { bool result = true; @@ -110,7 +67,7 @@ namespace VocaluxeLib 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; @@ -133,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