Created
February 18, 2016 19:29
-
-
Save yume-chan/f89e2314ec199abcfd6e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.Graphics.Canvas; | |
using Microsoft.Graphics.Canvas.Brushes; | |
using Microsoft.Graphics.Canvas.Effects; | |
using Microsoft.Graphics.Canvas.Text; | |
using Microsoft.Graphics.Canvas.UI; | |
using Microsoft.Graphics.Canvas.UI.Xaml; | |
using System; | |
using System.Collections.Generic; | |
using Windows.Foundation; | |
using Windows.Graphics.Display; | |
using Windows.UI; | |
using Windows.UI.Xaml; | |
using Windows.UI.Xaml.Controls; | |
namespace SoyMilk | |
{ | |
public sealed class DanmakuControl : ContentPresenter | |
{ | |
CanvasAnimatedControl Canvas; | |
public DanmakuControl() | |
{ | |
IsHitTestVisible = false; | |
SizeChanged += DanmakuControl_SizeChanged; | |
RegisterPropertyChangedCallback(VisibilityProperty, DanmakuControl_VisibilityChanged); | |
Canvas = new CanvasAnimatedControl(); | |
Canvas.Paused = true; | |
Canvas.IsFixedTimeStep = false; | |
Canvas.TargetElapsedTime = TimeSpan.FromSeconds(1.0 / 25); | |
Canvas.CreateResources += Canvas_CreateResources; | |
Canvas.Update += Canvas_Update; | |
Canvas.Draw += Canvas_Draw; | |
Content = Canvas; | |
} | |
bool IsVisible = true; | |
private void DanmakuControl_VisibilityChanged(DependencyObject sender, DependencyProperty dp) | |
{ | |
IsVisible = Visibility == Visibility.Visible; | |
} | |
class Danmaku | |
{ | |
public readonly int LineIndex; | |
public readonly string Content; | |
public readonly bool FromSelf; | |
public float Left; | |
public float Top; | |
public CanvasRenderTarget Layout; | |
public float Width; | |
public float Right => Left + Width; | |
public Danmaku(int lineIndex, string content, bool fromSelf, float left, float top) | |
{ | |
LineIndex = lineIndex; | |
Content = content; | |
FromSelf = fromSelf; | |
Left = left; | |
Top = top; | |
} | |
} | |
readonly object _lock = new object(); | |
const int Speed = -120; | |
float screenWidth; | |
float lineHeight = 30; | |
float[] Lines; | |
readonly List<Danmaku> Danmakus = new List<Danmaku>(80); | |
public void AddDanmaku(string content, bool fromSelf) | |
{ | |
lock (_lock) | |
{ | |
if (Canvas == null || Lines?.Length < 1) | |
return; | |
float min = float.MaxValue; | |
int minIndex = -1; | |
for (int i = 0; i < Lines.Length; ++i) | |
{ | |
var v = Lines[i]; | |
if (v <= screenWidth) | |
{ | |
minIndex = i; | |
break; | |
} | |
else if (v < min) | |
{ | |
min = v; | |
minIndex = i; | |
} | |
} | |
var danmaku = new Danmaku(minIndex, content, fromSelf, screenWidth + 20, minIndex * lineHeight); | |
createLayout(danmaku); | |
Danmakus.Add(danmaku); | |
Lines[minIndex] = danmaku.Right; | |
if (Danmakus.Count != 0) | |
Canvas.Paused = false; | |
} | |
} | |
ShadowEffect shadowEffect = new ShadowEffect() | |
{ | |
BlurAmount = 2, | |
Optimization = EffectOptimization.Speed | |
}; | |
CanvasSolidColorBrush brush; | |
CanvasTextFormat format = new CanvasTextFormat() { FontSize = 24, FontFamily = "Microsoft YaHei UI" }; | |
void createLayout(Danmaku danmaku) | |
{ | |
using (var layout = new CanvasTextLayout(Canvas, danmaku.Content, format, float.MaxValue, float.MaxValue)) | |
{ | |
layout.Options = CanvasDrawTextOptions.NoPixelSnap; | |
danmaku.Width = (float)layout.LayoutBounds.Width + 8; | |
using (var command = new CanvasCommandList(Canvas)) | |
{ | |
using (var session = command.CreateDrawingSession()) | |
{ | |
session.DrawTextLayout(layout, 6, 2, brush); | |
if (danmaku.FromSelf) | |
session.DrawRectangle(2, 2, danmaku.Width, lineHeight, brush, 2); | |
} | |
shadowEffect.Source = command; | |
var result = new CanvasRenderTarget(Canvas, new Size(danmaku.Width + 4, lineHeight + 5)); | |
using (var session = result.CreateDrawingSession()) | |
{ | |
session.Clear(Colors.Transparent); | |
session.DrawImage(shadowEffect); | |
session.DrawImage(command); | |
} | |
danmaku.Layout = result; | |
} | |
} | |
} | |
public void RemoveFromVisualTree() | |
{ | |
lock (_lock) | |
{ | |
Canvas?.RemoveFromVisualTree(); | |
Canvas = null; | |
brush?.Dispose(); | |
brush = null; | |
shadowEffect?.Dispose(); | |
shadowEffect = null; | |
foreach (var danmaku in Danmakus) | |
danmaku.Layout?.Dispose(); | |
} | |
} | |
private void DanmakuControl_SizeChanged(object sender, SizeChangedEventArgs e) | |
{ | |
lock (_lock) | |
{ | |
if (Canvas == null) | |
return; | |
var size = e.NewSize; | |
if (size.Width == 0 && size.Height == 0) | |
return; | |
screenWidth = (float)size.Width; | |
createResource(false); | |
} | |
} | |
void createResource(bool dpiChanged) | |
{ | |
if (!Canvas.ReadyToDraw) | |
return; | |
brush?.Dispose(); | |
brush = new CanvasSolidColorBrush(Canvas, Colors.White); | |
if (dpiChanged) | |
{ | |
float scale = 1; | |
switch (DisplayInformation.GetForCurrentView().ResolutionScale) | |
{ | |
case ResolutionScale.Scale120Percent: | |
scale = 1.2F; | |
break; | |
case ResolutionScale.Scale125Percent: | |
scale = 1.25F; | |
break; | |
case ResolutionScale.Scale140Percent: | |
scale = 1.4F; | |
break; | |
case ResolutionScale.Scale150Percent: | |
scale = 1.5F; | |
break; | |
case ResolutionScale.Scale160Percent: | |
scale = 1.6F; | |
break; | |
case ResolutionScale.Scale175Percent: | |
scale = 1.75F; | |
break; | |
case ResolutionScale.Scale180Percent: | |
scale = 1.80F; | |
break; | |
case ResolutionScale.Scale200Percent: | |
scale = 2F; | |
break; | |
case ResolutionScale.Scale225Percent: | |
scale = 2.25F; | |
break; | |
case ResolutionScale.Scale250Percent: | |
scale = 2.5F; | |
break; | |
case ResolutionScale.Scale300Percent: | |
scale = 3F; | |
break; | |
case ResolutionScale.Scale350Percent: | |
scale = 3.5F; | |
break; | |
case ResolutionScale.Scale400Percent: | |
scale = 4F; | |
break; | |
case ResolutionScale.Scale450Percent: | |
scale = 4.5F; | |
break; | |
case ResolutionScale.Scale500Percent: | |
scale = 5F; | |
break; | |
} | |
format.FontSize = 12 + 10 / scale; | |
using (var testLayout = new CanvasTextLayout(Canvas, "T", format, float.MaxValue, float.MaxValue)) | |
lineHeight = (float)testLayout.LayoutBounds.Height + 1; | |
} | |
var newLineCount = (int)(ActualHeight / lineHeight); | |
Lines = new float[newLineCount]; | |
for (var i = 0; i < Danmakus.Count; i++) | |
{ | |
var danmaku = Danmakus[i]; | |
danmaku.Layout?.Dispose(); | |
var lineIndex = danmaku.LineIndex; | |
if (lineIndex < newLineCount) | |
{ | |
danmaku.Top = danmaku.LineIndex * lineHeight; | |
createLayout(danmaku); | |
if (Lines[lineIndex] != 0) | |
danmaku.Left = Lines[lineIndex] + 20; | |
Lines[lineIndex] = danmaku.Right; | |
} | |
else | |
{ | |
Danmakus.RemoveAt(i); | |
i--; | |
} | |
} | |
} | |
private void Canvas_CreateResources(CanvasAnimatedControl sender, CanvasCreateResourcesEventArgs args) | |
{ | |
lock (_lock) | |
{ | |
createResource(true); | |
} | |
} | |
private void Canvas_Update(ICanvasAnimatedControl sender, CanvasAnimatedUpdateEventArgs args) | |
{ | |
lock (_lock) | |
{ | |
if (Canvas == null) | |
return; | |
var distance = Speed * (float)args.Timing.ElapsedTime.TotalSeconds; | |
for (var i = 0; i < Danmakus.Count; i++) | |
{ | |
var danmaku = Danmakus[i]; | |
danmaku.Left += distance; | |
if (danmaku.Right < -5) | |
{ | |
danmaku.Layout?.Dispose(); | |
Danmakus.RemoveAt(i); | |
i--; | |
} | |
} | |
for (var i = 0; i < Lines.Length; i++) | |
Lines[i] += distance; | |
} | |
} | |
private void Canvas_Draw(ICanvasAnimatedControl sender, CanvasAnimatedDrawEventArgs args) | |
{ | |
if (!IsVisible) | |
return; | |
lock (_lock) | |
{ | |
if (Canvas == null) | |
return; | |
if (Danmakus.Count == 0) | |
{ | |
Canvas.Paused = true; | |
return; | |
} | |
var session = args.DrawingSession; | |
foreach (var danmaku in Danmakus) | |
session.DrawImage(danmaku.Layout, danmaku.Left, danmaku.Top); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment