CPF/CPF.Skia/SkiaDrawingFactory.cs

396 lines
14 KiB
C#
Raw Normal View History

2023-11-21 23:05:03 +08:00
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Text;
using CPF.Platform;
using SkiaSharp;
using System.Runtime.InteropServices;
using CPF.Drawing;
namespace CPF.Skia
{
public class SkiaDrawingFactory : DrawingFactory
{
/// <summary>
/// 可以让字体显示更清晰,不过在透明背景下会有黑边
/// </summary>
public bool ClearType { get; set; }
/// <summary>
/// 尝试启用GPU加速普通的界面不建议开启GPU因为使用GPU加速后普通界面加速效果不明显但是内存占用会翻倍尤其是使用GIF的时候因为会缓存每一帧。
/// 需要有正确的显卡驱动配置依赖OpenGL。Windows上需要开启了Dwm桌面混合win7不能使用basic主题的情况下才GPU加速效果。如果出现花屏或者界面不显示情况请关闭硬件加速。
/// </summary>
public override bool UseGPU { get; set; }
private static ConcurrentDictionary<FontKey, FontWrapper> _fontFamilies =
new ConcurrentDictionary<FontKey, FontWrapper>();
static ConcurrentDictionary<string, SKTypeface> _fonts =
new ConcurrentDictionary<string, SKTypeface>();
public override IBitmapImpl CreateBitmap(int w, int h)
{
return new SkiaBitmap(new SKBitmap(w, h));
}
public override IBitmapImpl CreateBitmap(int w, int h, int pitch, PixelFormat pixelFormat, IntPtr data)
{
SKColorType sKColorType = SKColorType.Unknown;
SKAlphaType sKAlphaType = SKAlphaType.Unpremul;
switch (pixelFormat)
{
case PixelFormat.Undefined:
break;
case PixelFormat.PRgba:
sKAlphaType = SKAlphaType.Premul;
sKColorType = SKColorType.Rgba8888;
break;
case PixelFormat.Rgba:
sKColorType = SKColorType.Rgba8888;
break;
case PixelFormat.PBgra:
sKAlphaType = SKAlphaType.Premul;
sKColorType = SKColorType.Bgra8888;
break;
case PixelFormat.Bgra:
sKColorType = SKColorType.Bgra8888;
break;
case PixelFormat.Rgb565:
sKColorType = SKColorType.Rgb565;
break;
default:
break;
}
SKImageInfo info = new SKImageInfo { Width = w, Height = h, AlphaType = sKAlphaType, ColorType = sKColorType };
var bitmap = new SKBitmap(info, pitch);
bitmap.SetPixels(data);
return new SkiaBitmap(bitmap);
}
public override IBitmapImpl CreateBitmap(Stream stream)
{
return new SkiaBitmap(SKBitmap.Decode(stream));
}
public override IBitmapImpl CreateBitmap(Image img)
{
if (img.ImageImpl is SkiaBitmap b)
{
return new SkiaBitmap(b.Bitmap.Copy());
}
else if (img.ImageImpl is SkiaImage i)
{
return new SkiaBitmap(i.Image.Copy());
}
return null;
}
public override DrawingContext CreateDrawingContext(Bitmap bitmap)
{
return new SkiaDrawingContext(bitmap, this);
}
public override DrawingContext CreateDrawingContext(IRenderTarget target)
{
return new SkiaDrawingContext(target, this);
}
//public override DrawingContext CreateDrawingContext<T>(T Canvas)
//{
// return new SkiaDrawingContext((SKCanvas)(object)Canvas, this);
//}
public override IDisposable CreateFont(string fontFamily, float fontSize, FontStyles fontStyle)
{
var weight = SKFontStyleWeight.Normal;
if (fontStyle.HasFlag(FontStyles.Bold))
{
weight = SKFontStyleWeight.Bold;
}
var slant = SKFontStyleSlant.Upright;
if (fontStyle.HasFlag(FontStyles.Italic))
{
slant = SKFontStyleSlant.Italic;
}
if (!_fontFamilies.TryGetValue(new FontKey(weight, slant, fontFamily), out FontWrapper fontWrap))
{
if (_fonts.TryGetValue(fontFamily, out var font))
{
#if Net4
SKTypefaceStyle style= SKTypefaceStyle.Normal;
switch (fontStyle)
{
case FontStyles.Bold:
style = SKTypefaceStyle.Bold;
break;
case FontStyles.Italic:
style = SKTypefaceStyle.Italic;
break;
default:
break;
}
fontWrap = new FontWrapper { SKTypeface = SKTypeface.FromTypeface(font, style) };
#else
SKFontStyle style = new SKFontStyle(weight, SKFontStyleWidth.Normal, slant);
if (Application.OperatingSystem == OperatingSystemType.Linux || Application.OperatingSystem == OperatingSystemType.OSX)
{
fontWrap = new FontWrapper { SKTypeface = font };
//Console.WriteLine("Skia的BUG内嵌字体在Linux和Mac下不能使用字体样式选择");
}
else
{
fontWrap = new FontWrapper { SKTypeface = SKFontManager.Default.MatchTypeface(font, style) };
}
#endif
_fontFamilies.TryAdd(new FontKey(weight, slant, fontFamily), fontWrap);
}
else
{
fontWrap = new FontWrapper { SKTypeface = SKTypeface.FromFamilyName(fontFamily, weight, SKFontStyleWidth.Normal, slant) };
if (fontWrap.SKTypeface == null)
{
fontWrap.SKTypeface = SKTypeface.Default;
}
//SKFontStyle style = new SKFontStyle(weight, SKFontStyleWidth.Normal, slant);
//fontWrap = new FontWrapper { SKTypeface = SKFontManager.Default.MatchFamily(fontFamily, style) };
_fontFamilies.TryAdd(new FontKey(weight, slant, fontFamily), fontWrap);
}
}
return fontWrap;
}
public override IGeometryImpl CreateGeometry(CPF.Drawing.PathGeometry path)
{
return new SkiaPathGeometry(path);
}
public override IPathImpl CreatePath()
{
return new SkiaPath();
}
public override IPathImpl CreatePath(in Font font, string text)
{
return new SkiaPath(font, text);
}
public override void Dispose()
{
foreach (var item in _fonts)
{
item.Value.Dispose();
}
_fonts.Clear();
}
public override IImageImpl ImageFromFile(string path)
{
return new SkiaImage(path);
}
public override IImageImpl ImageFromStream(Stream stream)
{
return new SkiaImage(stream);
}
public override void LoadFont(Stream stream, string name)
{
var f = SKTypeface.FromStream(stream);
if (string.IsNullOrWhiteSpace(name))
{
name = f.FamilyName;
}
if (!_fonts.TryGetValue(name, out var value))
{
System.Diagnostics.Debug.WriteLine("加载字体:" + f.FamilyName);
Console.WriteLine("加载字体:" + f.FamilyName);
_fonts.TryAdd(name, f);
}
else
{
f.Dispose();
}
}
public override Size MeasureString(string str, Font font)
{
using (SKPaint paint = new SKPaint())
{
paint.TextEncoding = SKTextEncoding.Utf16;
//paint.IsStroke = false;
//paint.LcdRenderText = true;
//paint.SubpixelText = true;
paint.IsAntialias = true;
paint.Typeface = (font.AdapterFont as FontWrapper).SKTypeface;
paint.TextSize = font.FontSize;
if (str.Length < 3)
{
var width = paint.MeasureString(str);
return new Size(width, paint.FontSpacing + 1);
}
else
{
var lines = str.Split('\n');
float width = 0;
float heght = 0;
for (int i = 0; i < lines.Length; i++)
{
var line = lines[i].Trim('\r');
if (i == lines.Length - 1 && line == "")
{
break;
}
width = Math.Max(width, paint.MeasureString(line));
heght += paint.FontSpacing;
}
return new Size(width, heght);
}
}
}
public override Size MeasureString(string str, Font font, float maxWidth)
{
using (SKPaint paint = new SKPaint())
{
paint.TextEncoding = SKTextEncoding.Utf16;
//paint.IsStroke = false;
//paint.LcdRenderText = true;
//paint.SubpixelText = true;
paint.IsAntialias = true;
paint.Typeface = (font.AdapterFont as FontWrapper).SKTypeface;
paint.TextSize = font.FontSize;
if (str.Length == 1 || (str.Length == 2 && char.IsSurrogate(str[0])))
{
return new Size(paint.MeasureString(str), paint.FontSpacing);
}
else
{
var lines = str.Split('\n');
float width = 0;
float heght = 1;
for (int i = 0; i < lines.Length; i++)
{
var line = lines[i].Trim('\r');
if (i == lines.Length - 1 && line == "")
{
break;
}
//var text = line;
var ws = paint.MeasureAllChar(line);
var start = 0;
while (true)
{
//var len = Math.Max(1, (int)paint.BreakText(text, (float)Math.Ceiling(maxWidth)));
var len = ws.BreakText(start, (float)Math.Ceiling(maxWidth), false);
if (start + len.Item1 <= ws.Count)
{
//text = line.Substring(start, len);
start += len.Item1;
}
width = Math.Max(width, len.Item2);
heght += paint.FontSpacing;
if (start <= ws.Count - 1)
{
//text = line.Substring(start);
}
else
{
break;
}
}
}
return new Size(width, heght);
}
}
}
2023-12-19 22:11:21 +08:00
public override float GetLineHeight(in Font font)
2023-11-21 23:05:03 +08:00
{
using (SKPaint paint = new SKPaint())
{
paint.TextEncoding = SKTextEncoding.Utf16;
//paint.IsStroke = false;
//paint.LcdRenderText = true;
//paint.SubpixelText = true;
paint.IsAntialias = true;
paint.Typeface = (font.AdapterFont as FontWrapper).SKTypeface;
paint.TextSize = font.FontSize;
return paint.FontSpacing;
}
}
2023-12-19 22:11:21 +08:00
public override float GetAscent(in Font font)
{
using (SKPaint paint = new SKPaint())
{
paint.TextEncoding = SKTextEncoding.Utf16;
//paint.IsStroke = false;
//paint.LcdRenderText = true;
//paint.SubpixelText = true;
paint.IsAntialias = true;
paint.Typeface = (font.AdapterFont as FontWrapper).SKTypeface;
paint.TextSize = font.FontSize;
return -paint.FontMetrics.Ascent;
}
}
public override float GetDescent(in Font font)
{
using (SKPaint paint = new SKPaint())
{
paint.TextEncoding = SKTextEncoding.Utf16;
//paint.IsStroke = false;
//paint.LcdRenderText = true;
//paint.SubpixelText = true;
paint.IsAntialias = true;
paint.Typeface = (font.AdapterFont as FontWrapper).SKTypeface;
paint.TextSize = font.FontSize;
return paint.FontMetrics.Descent;
}
}
2023-11-21 23:05:03 +08:00
}
class FontWrapper : IDisposable
{
public SKTypeface SKTypeface;
public void Dispose()
{ }
}
struct FontKey
{
public readonly SKFontStyleSlant Slant;
public readonly SKFontStyleWeight Weight;
public readonly string Name;
public FontKey(SKFontStyleWeight weight, SKFontStyleSlant slant, string name)
{
Slant = slant;
Weight = weight;
Name = name;
}
public override int GetHashCode()
{
var hash = 17;
hash = (hash * 31) + (int)Slant;
hash = (hash * 31) + (int)Weight;
hash ^= Name.GetHashCode();
return hash;
}
public override bool Equals(object other)
{
return other is FontKey key && this.Equals(key);
}
private bool Equals(FontKey other)
{
return Slant == other.Slant &&
Weight == other.Weight &&
Name == other.Name;
}
}
}