Winform用的GDI+,像全屏动画这种卡的稀巴烂,任务管理器看进程如果GPU没动静那就是渣,没加速。
可以使用OpenGL或者DirectX这样有GPU加速就不卡了。
OpenGL用下面这个,控件里会多出一个glControl,拖到form上
- Create a Windows Application (.NET Framework 4.6)
- NuGet to install OpenTK.GLControl
- NuGet to install SkiaSharp
using OpenTK.Graphics.ES20;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SkiaDemo
{
public partial class Form1 : Form
{
Random rand = new Random();
SKColorType colorType = SKColorType.Rgba8888;
public Form1()
{
InitializeComponent();
glControl1.VSync = false; // why?
glControl1.Paint += new PaintEventHandler(RenderWithOpenGL);
}
private void RenderWithOpenGL(object sender, PaintEventArgs e)
{
Control sctl = (Control)sender;
int width = sctl.Width;
int height = sctl.Height;
// setup the Skia surface using OpenGL
GRContext contextOpenGL = GRContext.Create(GRBackend.OpenGL, GRGlInterface.CreateNativeGlInterface());
GL.GetInteger(GetPName.FramebufferBinding, out var framebuffer);
GRGlFramebufferInfo glInfo = new GRGlFramebufferInfo((uint)framebuffer, colorType.ToGlSizedFormat());
GL.GetInteger(GetPName.StencilBits, out var stencil);
GRBackendRenderTarget renderTarget = new GRBackendRenderTarget(width, height, contextOpenGL.GetMaxSurfaceSampleCount(colorType), stencil, glInfo);
SKSurface surface = SKSurface.Create(contextOpenGL, renderTarget, GRSurfaceOrigin.BottomLeft, colorType);
// Draw some stuff on the canvas
surface.Canvas.Clear(SKColor.Parse("#FFFFFF")); // adds about 3ms fullscreen
byte alpha = 128;
var paint = new SKPaint();
for (int i = 0; i < 1_000; i++)
{
float x1 = (float)(rand.NextDouble() * width);
float x2 = (float)(rand.NextDouble() * width);
float y1 = (float)(rand.NextDouble() * height);
float y2 = (float)(rand.NextDouble() * height);
paint.Color = new SKColor(
red: (byte)(rand.NextDouble() * 255),
green: (byte)(rand.NextDouble() * 255),
blue: (byte)(rand.NextDouble() * 255),
alpha: alpha
);
surface.Canvas.DrawLine(x1, y1, x2, y2, paint);
}
surface.Canvas.Flush();
glControl1.SwapBuffers();
// prevent memory access violations by disposing before exiting
renderTarget?.Dispose();
contextOpenGL?.Dispose();
surface?.Dispose();
}
private void BtnBenchmark_Click(object sender, EventArgs e)
{
int count = 250;
btnBenchmark.Enabled = false;
System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew();
progressBar1.Maximum = count;
for (int i = 0; i < count; i++)
{
lblStatus.Text = $"on render {i + 1} of {count}...";
progressBar1.Value = i + 1;
progressBar1.Value = i;
Application.DoEvents(); // adds about 1ms fullscreen
glControl1.Refresh();
}
progressBar1.Value = 0;
double meanSec = (double)stopwatch.ElapsedTicks / System.Diagnostics.Stopwatch.Frequency / count;
lblStatus.Text = string.Format("mean render {0:0.00} ms ({1:0.00} Hz)", meanSec * 1000, 1.0 / meanSec);
btnBenchmark.Enabled = true;
}
}
}
DirectX可以用SharpDX,也有提供窗体,改造窗体等方式
需安装SharpDX,SharpDX.Desktop,SharpDX.Direct2D1,SharpDX.DXGI,SharpDX.Mathematics
using System;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.DirectWrite;
using SharpDX.Windows;
using System.Windows.Forms;
using D2DFactory = SharpDX.Direct2D1.Factory;
using DWriteFactory = SharpDX.DirectWrite.Factory;
namespace AdvancedTextRenderingApp
{
public class CustomColorRenderer : SharpDX.DirectWrite.TextRendererBase
{
private RenderTarget renderTarget;
private SolidColorBrush defaultBrush;
public void AssignResources(RenderTarget renderTarget,SolidColorBrush defaultBrush)
{
this.renderTarget = renderTarget;
this.defaultBrush = defaultBrush;
}
public override Result DrawGlyphRun(object clientDrawingContext, float baselineOriginX, float baselineOriginY, MeasuringMode measuringMode, GlyphRun glyphRun, GlyphRunDescription glyphRunDescription, ComObject clientDrawingEffect)
{
SolidColorBrush sb = defaultBrush;
if (clientDrawingEffect != null && clientDrawingEffect is SolidColorBrush)
{
sb = (SolidColorBrush)clientDrawingEffect;
}
try
{
this.renderTarget.DrawGlyphRun(new Vector2(baselineOriginX, baselineOriginY), glyphRun, sb, measuringMode);
return Result.Ok;
}
catch
{
return Result.Fail;
}
}
}
public class Program
{
private static D2DFactory d2dFactory;
private static DWriteFactory dwFactory;
private static RenderForm mainForm;
private static WindowRenderTarget renderTarget;
private static TextFormat textFormat;
private static TextLayout textLayout;
//Various brushes for our example
private static SolidColorBrush backgroundBrush;
private static SolidColorBrush defaultBrush;
private static SolidColorBrush greenBrush;
private static SolidColorBrush redBrush;
private static CustomColorRenderer textRenderer;
private static RectangleF fullTextBackground;
//This one is only a measured region
private static RectangleF textRegionRect;
private static string introText = @"Hello from SharpDX, this is a long text to show some more advanced features like paragraph alignment, custom drawing...";
[STAThread]
static void Main(string[] args)
{
mainForm = new RenderForm("Advanced Text rendering demo");
d2dFactory = new D2DFactory();
dwFactory = new DWriteFactory(SharpDX.DirectWrite.FactoryType.Shared);
textRenderer = new CustomColorRenderer();
CreateResources();
var bgcolor = new Color4(0.1f,0.1f,0.1f,1.0f);
//This is the offset where we start our text layout
Vector2 offset = new Vector2(202.0f,250.0f);
textFormat = new TextFormat(dwFactory, "Arial", FontWeight.Regular, FontStyle.Normal, 16.0f);
textLayout = new TextLayout(dwFactory, introText, textFormat, 300.0f, 200.0f);
//Apply various modifications to text
textLayout.SetUnderline(true, new TextRange(0, 5));
textLayout.SetDrawingEffect(greenBrush, new TextRange(10, 20));
textLayout.SetFontSize(24.0f, new TextRange(6, 4));
textLayout.SetFontFamilyName("Comic Sans MS", new TextRange(11,7));
//Measure full layout
var textSize = textLayout.Metrics;
fullTextBackground = new RectangleF(textSize.Left + offset.X, textSize.Top + offset.Y, textSize.Width, textSize.Height);
//Measure text to apply background to
var metrics = textLayout.HitTestTextRange(53, 4, 0.0f, 0.0f)[0];
textRegionRect = new RectangleF(metrics.Left + offset.X, metrics.Top + offset.Y, metrics.Width, metrics.Height);
//Assign render target and brush to our custom renderer
textRenderer.AssignResources(renderTarget, defaultBrush);
RenderLoop.Run(mainForm, () =>
{
renderTarget.BeginDraw();
renderTarget.Clear(bgcolor);
renderTarget.FillRectangle(fullTextBackground, backgroundBrush);
renderTarget.FillRectangle(textRegionRect, redBrush);
textLayout.Draw(textRenderer, offset.X, offset.Y);
try
{
renderTarget.EndDraw();
}
catch
{
CreateResources();
}
});
d2dFactory.Dispose();
dwFactory.Dispose();
renderTarget.Dispose();
}
private static void CreateResources()
{
if (renderTarget != null) { renderTarget.Dispose(); }
if (defaultBrush != null) { defaultBrush.Dispose(); }
if (greenBrush != null) { greenBrush.Dispose(); }
if (redBrush != null) { redBrush.Dispose(); }
if (backgroundBrush != null) { backgroundBrush.Dispose(); }
HwndRenderTargetProperties wtp = new HwndRenderTargetProperties();
wtp.Hwnd = mainForm.Handle;
wtp.PixelSize = new Size2(mainForm.ClientSize.Width, mainForm.ClientSize.Height);
wtp.PresentOptions = PresentOptions.Immediately;
renderTarget = new WindowRenderTarget(d2dFactory, new RenderTargetProperties(), wtp);
defaultBrush = new SolidColorBrush(renderTarget, Color.White);
greenBrush = new SolidColorBrush(renderTarget, Color.Green);
redBrush = new SolidColorBrush(renderTarget, Color.Red);
backgroundBrush = new SolidColorBrush(renderTarget, new Color4(0.3f, 0.3f, 0.3f, 0.5f));
textRenderer.AssignResources(renderTarget, defaultBrush);
}
}
}