-
Notifications
You must be signed in to change notification settings - Fork 5
/
LyricsRenderer.cs
155 lines (136 loc) · 5.92 KB
/
LyricsRenderer.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;
namespace MusicBeePlugin
{
public static class LyricsRenderer
{
private static readonly Brush ShadowBrush = new SolidBrush(Color.FromArgb(110, 0, 0, 0));
private const int ShadowOffset = 2;
private static readonly StringFormat Format = new StringFormat(StringFormatFlags.NoWrap)
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Near
};
private static Pen _borderPen;
private static Font _mainFont, _subFont;
private static Color _color1, _color2;
private static GradientType _type;
private static int _width = 1024;
private static int _dpi = 72;
// Thread unsafe!!!
public static void UpdateFromSettings(SettingsObj settings, int width)
{
_mainFont = settings.FontActual;
_subFont = new Font(_mainFont.FontFamily, _mainFont.Size * 0.8f, _mainFont.Style, _mainFont.Unit, _mainFont.GdiCharSet);
_borderPen?.Dispose();
_borderPen = new Pen(settings.BorderColor, 2f)
{
LineJoin = LineJoin.Round,
EndCap = LineCap.Round,
StartCap = LineCap.Round
};
_type = (GradientType)settings.GradientType;
_color1 = settings.Color1;
_color2 = settings.Color2;
switch ((AlignmentType)settings.AlignmentType)
{
case AlignmentType.Left:
Format.Alignment = StringAlignment.Near;
break;
case AlignmentType.Right:
Format.Alignment = StringAlignment.Far;
break;
case AlignmentType.Center:
default:
Format.Alignment = StringAlignment.Center;
break;
}
_width = width;
}
public static void SetDpi(int deviceDpi)
{
_dpi = deviceDpi;
}
public static Bitmap Render1LineLyrics(string line1, IDeviceContext dc)
{
return RenderLyrics(line1, _mainFont, dc);
}
public static Bitmap Render2LineLyrics(string line1, string line2, IDeviceContext dc)
{
using (Bitmap line1Bitmap = RenderLyrics(line1, _mainFont, dc),
line2Bitmap = RenderLyrics(line2, _subFont, dc))
{
var bitmap = new Bitmap(Math.Max(line1Bitmap.Width, line2Bitmap.Width), line1Bitmap.Height + line2Bitmap.Height);
using (var g = Graphics.FromImage(bitmap))
{
g.DrawImage(line1Bitmap, new PointF(0, 0));
g.DrawImage(line2Bitmap, new PointF(0, line1Bitmap.Height * 0.9f)); // TODO: a better approach to reduce line space for a more compact view...
}
return bitmap;
}
}
private static Bitmap RenderLyrics(string lyric, Font font, IDeviceContext dc)
{
// TODO: bounds returned by `TextRenderer.MeasureTex` differs greatly
// from bounds created by `GraphicsPath.AddString`, need further
// investigation.
var fontBounds = TextRenderer.MeasureText(dc, lyric, font);
var height = fontBounds.Height;
if (height <= 0) height = 1;
var bitmap = new Bitmap(_width, height);
bitmap.SetResolution(_dpi, _dpi);
using (var g = Graphics.FromImage(bitmap))
{
g.InterpolationMode = InterpolationMode.High;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
g.CompositingQuality = CompositingQuality.HighQuality;
var fontEmSize = font.SizeInPoints * _dpi / 72;
var initialRect = new RectangleF(0, 0, _width, height);
using (var stringPath = new GraphicsPath(FillMode.Alternate))
{
stringPath.AddString(lyric, font.FontFamily, (int)font.Style, fontEmSize, initialRect, Format);
// Using matrix to translate the path
using (var mat = new Matrix())
{
mat.Translate(-ShadowOffset, -ShadowOffset);
stringPath.Transform(mat);
g.FillPath(ShadowBrush, stringPath);
mat.Translate(ShadowOffset * 2, ShadowOffset * 2);
stringPath.Transform(mat);
}
if (_borderPen != null)
g.DrawPath(_borderPen, stringPath);
using (var stringBrush = CreateGradientBrush(initialRect))
{
g.FillPath(stringBrush, stringPath);
}
}
}
return bitmap;
}
private static Brush CreateGradientBrush(RectangleF dstRect)
{
// ReSharper disable once SwitchStatementMissingSomeCases
switch (_type)
{
case GradientType.DoubleColor:
return new LinearGradientBrush(dstRect.Location, new PointF(dstRect.X, dstRect.Y + dstRect.Height), _color1, _color2)
{
WrapMode = WrapMode.TileFlipXY
};
case GradientType.TripleColor:
return new LinearGradientBrush(dstRect.Location, new PointF(dstRect.X, dstRect.Y + dstRect.Height / 3 * 2), _color1, _color2)
{
WrapMode = WrapMode.TileFlipXY
};
case GradientType.NoGradient:
default:
return new SolidBrush(_color1);
}
}
}
}