mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 19:24:55 +08:00
227 lines
8.3 KiB
C#
227 lines
8.3 KiB
C#
using BetterLyrics.WinUI3.Extensions;
|
|
using BetterLyrics.WinUI3.Models;
|
|
using BetterLyrics.WinUI3.Models.Settings;
|
|
using Lyricify.Lyrics.Providers.Web.Netease;
|
|
using Microsoft.Graphics.Canvas;
|
|
using Microsoft.Graphics.Canvas.Effects;
|
|
using Microsoft.Graphics.Canvas.Geometry;
|
|
using Microsoft.Graphics.Canvas.Text;
|
|
using Microsoft.Graphics.Canvas.UI.Xaml;
|
|
using System;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using Windows.UI;
|
|
|
|
namespace BetterLyrics.WinUI3.Renderer
|
|
{
|
|
public class LyricsRenderer
|
|
{
|
|
private readonly PlayingLineRenderer _playingRenderer = new();
|
|
private readonly UnplayingLineRenderer _unplayingRenderer = new();
|
|
|
|
private Matrix4x4 _threeDimMatrix = Matrix4x4.Identity;
|
|
|
|
public void Draw(
|
|
ICanvasAnimatedControl control,
|
|
CanvasDrawingSession ds,
|
|
LyricsData? lyricsData,
|
|
int playingLineIndex,
|
|
int startVisibleIndex,
|
|
int endVisibleIndex,
|
|
double canvasHeight,
|
|
double lyricsX,
|
|
double lyricsWidth,
|
|
LyricsWindowStatus windowStatus,
|
|
Color strokeColor,
|
|
Color bgColor,
|
|
Func<int, LinePlaybackState> getPlaybackState)
|
|
{
|
|
if (windowStatus.LyricsEffectSettings.Is3DLyricsEnabled)
|
|
{
|
|
using (var layer = new CanvasCommandList(control))
|
|
{
|
|
using (var layerDs = layer.CreateDrawingSession())
|
|
{
|
|
DrawLyrics(
|
|
control,
|
|
layerDs,
|
|
lyricsData,
|
|
playingLineIndex,
|
|
startVisibleIndex,
|
|
endVisibleIndex,
|
|
canvasHeight,
|
|
lyricsX,
|
|
lyricsWidth,
|
|
windowStatus,
|
|
strokeColor,
|
|
bgColor,
|
|
getPlaybackState);
|
|
}
|
|
|
|
ds.DrawImage(new Transform3DEffect
|
|
{
|
|
Source = layer,
|
|
TransformMatrix = _threeDimMatrix
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DrawLyrics(
|
|
control,
|
|
ds,
|
|
lyricsData,
|
|
playingLineIndex,
|
|
startVisibleIndex,
|
|
endVisibleIndex,
|
|
canvasHeight,
|
|
lyricsX,
|
|
lyricsWidth,
|
|
windowStatus,
|
|
strokeColor,
|
|
bgColor,
|
|
getPlaybackState);
|
|
}
|
|
}
|
|
|
|
private void DrawLyrics(
|
|
ICanvasAnimatedControl control,
|
|
CanvasDrawingSession ds,
|
|
LyricsData? lyricsData,
|
|
int playingLineIndex,
|
|
int startVisibleIndex,
|
|
int endVisibleIndex,
|
|
double canvasHeight,
|
|
double lyricsX,
|
|
double lyricsWidth,
|
|
LyricsWindowStatus windowStatus,
|
|
Color strokeColor,
|
|
Color bgColor,
|
|
Func<int, LinePlaybackState> getPlaybackState)
|
|
{
|
|
if (lyricsData == null) return;
|
|
|
|
var currentPlayingLine = lyricsData.LyricsLines.ElementAtOrDefault(playingLineIndex);
|
|
if (currentPlayingLine == null) return;
|
|
|
|
var effectSettings = windowStatus.LyricsEffectSettings;
|
|
var styleSettings = windowStatus.LyricsStyleSettings;
|
|
|
|
var rotationY = currentPlayingLine.OriginalPosition.WithX(effectSettings.FanLyricsAngle < 0 ? (float)lyricsWidth : 0);
|
|
|
|
for (int i = startVisibleIndex; i <= endVisibleIndex; i++)
|
|
{
|
|
var line = lyricsData.LyricsLines.ElementAtOrDefault(i);
|
|
if (line == null) continue;
|
|
|
|
if (line.OriginalCanvasTextLayout == null) continue;
|
|
if (line.OriginalCanvasTextLayout.LayoutBounds.Width <= 0) continue;
|
|
|
|
double yOffset = line.YOffsetTransition.Value + canvasHeight / 2;
|
|
|
|
var transform =
|
|
Matrix3x2.CreateScale((float)line.ScaleTransition.Value, line.CenterPosition) *
|
|
Matrix3x2.CreateRotation((float)line.AngleTransition.Value, rotationY) *
|
|
Matrix3x2.CreateTranslation((float)lyricsX, (float)yOffset);
|
|
|
|
ds.Transform = transform;
|
|
|
|
using (var textOnlyLayer = RenderBaseTextLayer(control, line, styleSettings.LyricsFontStrokeWidth, strokeColor, bgColor))
|
|
{
|
|
if (i == playingLineIndex)
|
|
{
|
|
var state = getPlaybackState(i);
|
|
|
|
_playingRenderer.Draw(control, ds, textOnlyLayer, line, state, effectSettings);
|
|
}
|
|
else
|
|
{
|
|
_unplayingRenderer.Draw(ds, textOnlyLayer, line);
|
|
}
|
|
}
|
|
|
|
ds.Transform = Matrix3x2.Identity;
|
|
}
|
|
}
|
|
|
|
private CanvasCommandList RenderBaseTextLayer(
|
|
ICanvasResourceCreator resourceCreator,
|
|
LyricsLine line,
|
|
double strokeWidth,
|
|
Color strokeColor,
|
|
Color fillColor)
|
|
{
|
|
var commandList = new CanvasCommandList(resourceCreator);
|
|
using (var clds = commandList.CreateDrawingSession())
|
|
{
|
|
if (strokeWidth > 0)
|
|
{
|
|
DrawGeometrySafely(clds, line.PhoneticCanvasGeometry, line.PhoneticPosition, strokeColor, strokeWidth);
|
|
DrawGeometrySafely(clds, line.OriginalCanvasGeometry, line.OriginalPosition, strokeColor, strokeWidth);
|
|
DrawGeometrySafely(clds, line.TranslatedCanvasGeometry, line.TranslatedPosition, strokeColor, strokeWidth);
|
|
}
|
|
|
|
DrawTextLayoutSafely(clds, line.PhoneticCanvasTextLayout, line.PhoneticPosition, fillColor);
|
|
DrawTextLayoutSafely(clds, line.OriginalCanvasTextLayout, line.OriginalPosition, fillColor);
|
|
DrawTextLayoutSafely(clds, line.TranslatedCanvasTextLayout, line.TranslatedPosition, fillColor);
|
|
}
|
|
return commandList;
|
|
}
|
|
|
|
private void DrawGeometrySafely(CanvasDrawingSession ds, CanvasGeometry? geo, Vector2 pos, Color color, double width)
|
|
{
|
|
if (geo == null) return;
|
|
|
|
try
|
|
{
|
|
ds.DrawGeometry(geo, pos, color, (float)width);
|
|
}
|
|
catch (Exception) { }
|
|
}
|
|
|
|
private void DrawTextLayoutSafely(CanvasDrawingSession ds, CanvasTextLayout? layout, Vector2 pos, Color color)
|
|
{
|
|
if (layout == null) return;
|
|
|
|
try
|
|
{
|
|
ds.DrawTextLayout(layout, pos, color);
|
|
}
|
|
catch (Exception) { }
|
|
}
|
|
|
|
public void CalculateLyrics3DMatrix(LyricsEffectSettings lyricsEffect, double lyricsX, double lyricsY, double lyricsWidth, double canvasHeight)
|
|
{
|
|
if (!lyricsEffect.Is3DLyricsEnabled) return;
|
|
|
|
Vector3 center = new(
|
|
(float)(lyricsX + lyricsWidth / 2),
|
|
(float)(lyricsY + canvasHeight / 2),
|
|
0);
|
|
|
|
float rotationX = (float)(Math.PI * lyricsEffect.Lyrics3DXAngle / 180.0);
|
|
float rotationY = (float)(Math.PI * lyricsEffect.Lyrics3DYAngle / 180.0);
|
|
float rotationZ = (float)(Math.PI * lyricsEffect.Lyrics3DZAngle / 180.0);
|
|
|
|
Matrix4x4 rotation =
|
|
Matrix4x4.CreateRotationX(rotationX) *
|
|
Matrix4x4.CreateRotationY(rotationY) *
|
|
Matrix4x4.CreateRotationZ(rotationZ);
|
|
Matrix4x4 perspective = Matrix4x4.Identity;
|
|
perspective.M34 = 1.0f / lyricsEffect.Lyrics3DDepth;
|
|
|
|
// 组合变换:
|
|
// 1. 将中心移到原点
|
|
// 2. 旋转
|
|
// 3. 应用透视
|
|
// 4. 将中心移回原位
|
|
_threeDimMatrix =
|
|
Matrix4x4.CreateTranslation(-center) *
|
|
rotation *
|
|
perspective *
|
|
Matrix4x4.CreateTranslation(center);
|
|
}
|
|
|
|
}
|
|
}
|