CPF/CPF.Skia/SkiaDrawingContext.cs
jasper 71f12b7597 优化调用
创建空对象,避免构建SKBitmap调用TryAllocPixels 申请无用的内存
2024-06-25 22:14:26 +08:00

1012 lines
41 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SkiaSharp;
using System.Runtime.InteropServices;
using CPF.Drawing;
using CPF.Shapes;
using CPF.Platform;
using System.Diagnostics;
using CPF.OpenGL;
namespace CPF.Skia
{
public class SkiaDrawingContext : DrawingContext
{
public SKCanvas SKCanvas
{
get { return canvas; }
}
SKCanvas canvas;
SKBitmap bitmap;
SKSurface surface;
List<PaintWrapper> cachePaints = new List<PaintWrapper>(2);
//GRContext grContext;
GRBackendRenderTarget backendRenderTarget;
SKAutoCanvasRestore canvasRestore;
//static GRGlInterface glInterface;
public SkiaDrawingContext(Bitmap bitmap, SkiaDrawingFactory drawingFactory)
{
this.drawingFactory = drawingFactory;
var bmp = bitmap.BitmapImpl as SkiaBitmap;
//glContext = GlContext.Create(null);
//glContext.MakeCurrent();
////glInterface = GRGlInterface.CreateNativeGlInterface();
//grContext = GRContext.CreateGl();
canvas = new SKCanvas(bmp.Bitmap);
//this.bitmap = bmp.Bitmap;
//surface = SKSurface.Create(grContext, true, new SKImageInfo { Width = this.bitmap.Width, Height = this.bitmap.Height, AlphaType = this.bitmap.AlphaType, ColorSpace = this.bitmap.ColorSpace, ColorType = this.bitmap.ColorType }, 0, GRSurfaceOrigin.TopLeft);
//canvas = surface.Canvas;
}
public SkiaDrawingContext(SKCanvas Canvas, SkiaDrawingFactory drawingFactory)
{
this.drawingFactory = drawingFactory;
canvas = Canvas;
}
GRContext gRContext;
public IGlContext GlContext;
public SkiaDrawingContext(IRenderTarget target, SkiaDrawingFactory drawingFactory)
{
this.drawingFactory = drawingFactory;
if (target.Width < 1 || target.Height < 1)
{
Debug.WriteLine("绘图表面为空");
Console.WriteLine("绘图表面为空");
}
if (!target.CanUseGPU || !drawingFactory.UseGPU)
{
IntPtr hdc = IntPtr.Zero;
if (target is HDCRenderTarget hDC)
{
hdc = hDC.Hdc;
}
else if (target is OpenGlRenderTarget<IntPtr> open)
{
hdc = open.FailBackTarget;
}
if (hdc != IntPtr.Zero)
{
CreateHdcCanvas(hdc);
}
}
else
{
//System.Diagnostics.Stopwatch stopwatch = new Stopwatch();
//stopwatch.Start();
//glInterface = GRGlInterface.CreateNativeGlInterface();
if (target is OpenGlRenderTarget open)
{
var grContext = open.GlContext.GRContext as GRContext;
if (grContext == null)
{
grContext = GRContext.CreateGl();
open.GlContext.GRContext = grContext;
}
gRContext = grContext;
GlContext = open.GlContext;
if (grContext == null)
{
if (target is OpenGlRenderTarget<IntPtr> openHdc)
{
Console.WriteLine("opengl创建失败将自动改为CPU渲染");
Debug.WriteLine("opengl创建失败将自动改为CPU渲染");
drawingFactory.UseGPU = false;
CreateHdcCanvas(openHdc.FailBackTarget);
}
}
else
{
var maxSamples = grContext.GetMaxSurfaceSampleCount(SKColorType.Rgba8888);
var samples = open.Samples;
if (samples > maxSamples)
samples = maxSamples;
var framebufferInfo = new GRGlFramebufferInfo((uint)open.Framebuffer, SKColorType.Rgba8888.ToGlSizedFormat());
backendRenderTarget = new GRBackendRenderTarget(target.Width, target.Height, samples, open.Stencil, framebufferInfo);
surface = SKSurface.Create(grContext, backendRenderTarget, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888);
canvas = surface.Canvas;
canvasRestore = new SKAutoCanvasRestore(canvas, true);
}
}
//System.Diagnostics.Debug.WriteLine(stopwatch.ElapsedMilliseconds);
}
if (canvas == null)
{
throw new Exception("Canvas创建失败:" + target);
}
}
private void CreateHdcCanvas(IntPtr hdc)
{
//获取dc里的位图信息Windows平台
var hbitmap = UnmanagedMethods.GetCurrentObject(hdc, ObjectType.OBJ_BITMAP);
BITMAP BITMAP = new BITMAP();
UnmanagedMethods.GetObject(hbitmap, Marshal.SizeOf(typeof(BITMAP)), BITMAP);
var bitmap = new SKBitmap();
bitmap.InstallPixels(new SKImageInfo(BITMAP.bmWidth, BITMAP.bmHeight, SKImageInfo.PlatformColorType), BITMAP.bmBits, BITMAP.bmWidthBytes);
this.bitmap = bitmap;
canvas = new SKCanvas(this.bitmap);
}
//public SkiaDrawingContext(SKBitmap bitmap)
//{
// this.bitmap = bitmap;
// canvas = new SKCanvas(this.bitmap);
//}
public override void Dispose()
{
//System.Diagnostics.Stopwatch stopwatch = new Stopwatch();
//stopwatch.Start();
if (bitmap != null)
{
bitmap.Dispose();
bitmap = null;
}
canvasRestore?.Dispose();
canvasRestore = null;
if (canvas != null)
{
canvas.Flush();
canvas.Dispose();
canvas = null;
}
if (surface != null)
{
surface.Dispose();
surface = null;
}
if (gRContext != null)
{
//gRContext.Flush();
gRContext.ResetContext();
}
//if (grContext != null)
//{
// //grContext.Flush();
// grContext.Dispose();
// grContext = null;
//}
//if (glContext != null)
//{
// //glContext.DestroyTexture(backendRenderTarget.GetGlFramebufferInfo().FramebufferObjectId);
// //glContext.SwapBuffers();
// glContext.Dispose();
// glContext = null;
//}
if (backendRenderTarget != null)
{
backendRenderTarget.Dispose();
backendRenderTarget = null;
}
foreach (var item in cachePaints)
{
item.Paint.Dispose();
}
cachePaints.Clear();
//System.Diagnostics.Debug.WriteLine(stopwatch.ElapsedMilliseconds);
}
//public SkiaDrawingContext(IntPtr hdc, Rect clip)
//{
// //获取dc里的位图信息Windows平台
// var hbitmap = ConsoleApp1.UnmanagedMethods.GetCurrentObject(hdc, ConsoleApp1.UnmanagedMethods.ObjectType.OBJ_BITMAP);
// ConsoleApp1.UnmanagedMethods.BITMAP BITMAP = new ConsoleApp1.UnmanagedMethods.BITMAP();
// ConsoleApp1.UnmanagedMethods.GetObject(hbitmap, Marshal.SizeOf(typeof(ConsoleApp1.UnmanagedMethods.BITMAP)), BITMAP);
// bitmap = new SKBitmap(BITMAP.bmWidth, BITMAP.bmHeight);
// bitmap.SetPixels(BITMAP.bmBits);
// canvas = new SKCanvas(bitmap);
// //currentRect = new Rect(0, 0, bitmap.Width, bitmap.Height);
//}
public override Matrix Transform
{
get
{
return canvas.TotalMatrix.ToMatrix();
}
set
{
canvas.SetMatrix(value.ToMatrix());
}
}
public override AntialiasMode AntialiasMode
{
get;
set;
}
SkiaDrawingFactory drawingFactory;
public override DrawingFactory DrawingFactory
{
get
{
return drawingFactory;
}
}
public override void Clear(Color color)
{
canvas.Clear(color.ToSKColor());
}
internal PaintWrapper CreatePaint(Brush brush, in Font font = default)
{
PaintWrapper paintWrapper;
SKPaint paint;
GetPaint(out paintWrapper, out paint);
//double opacity = brush.Opacity * _currentOpacity;
//if (brush is SolidColorBrush solid)
//{
// paint.Color = solid.Color.ToSKColor();
// return paintWrapper;
//}
if (font.FontFamily != null)
{
paint.TextEncoding = SKTextEncoding.Utf16;
paint.Typeface = (font.AdapterFont as FontWrapper).SKTypeface;
paint.TextSize = font.FontSize;
}
paint.Shader = brush.AdapterBrush as SKShader;
return paintWrapper;
}
private void GetPaint(out PaintWrapper paintWrapper, out SKPaint paint)
{
paintWrapper = default;
paint = null;
foreach (var item in cachePaints)
{
if (!item.isUsing)
{
paintWrapper = item;
paint = item.Paint;
break;
}
}
if (paint == null)
{
paintWrapper = new PaintWrapper(new SKPaint());
paint = paintWrapper.Paint;
cachePaints.Add(paintWrapper);
}
paint.IsAntialias = AntialiasMode == AntialiasMode.AntiAlias;
paint.FilterQuality = AntialiasMode == AntialiasMode.AntiAlias ? SKFilterQuality.Medium : SKFilterQuality.None;
paintWrapper.isUsing = true;
}
private PaintWrapper CreatePaint(Brush brush, Stroke stroke, in Font font = default)
{
var rv = CreatePaint(brush, font);
var paint = rv.Paint;
paint.IsStroke = true;
paint.StrokeWidth = stroke.Width;
// Need to modify dashes due to Skia modifying their lengths
// https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/paths/dots
// TODO: Still something is off, dashes are now present, but don't look the same as D2D ones.
switch (stroke.StrokeCap)
{
case CapStyles.Round:
paint.StrokeCap = SKStrokeCap.Round;
break;
case CapStyles.Square:
paint.StrokeCap = SKStrokeCap.Square;
break;
default:
paint.StrokeCap = SKStrokeCap.Butt;
break;
}
switch (stroke.LineJoin)
{
case LineJoins.Miter:
paint.StrokeJoin = SKStrokeJoin.Miter;
break;
case LineJoins.Round:
paint.StrokeJoin = SKStrokeJoin.Round;
break;
default:
paint.StrokeJoin = SKStrokeJoin.Bevel;
break;
}
//paint.StrokeMiter = (float)pen.MiterLimit;
var srcDashes = stroke.DashPattern;
if (stroke.DashStyle != DashStyles.Solid)
{
srcDashes = stroke.GetDashPattern();
}
if (srcDashes != null && srcDashes.Length > 0)
{
var dashesArray = new float[srcDashes.Length];
for (var i = 0; i < srcDashes.Length; ++i)
{
dashesArray[i] = srcDashes[i] * paint.StrokeWidth;
}
//var offset = stroke.DashOffset * stroke.Width;
var pe = SKPathEffect.CreateDash(dashesArray, stroke.DashOffset);
paint.PathEffect = pe;
rv.AddDisposable(pe);
}
return rv;
}
public override void DrawEllipse(Brush strokeBrush, in Stroke stroke, in Point center, in float radiusX, in float radiusY)
{
InitializeBrush(strokeBrush);
using (var paint = CreatePaint(strokeBrush, stroke))
{
canvas.DrawOval(center.X, center.Y, radiusX, radiusY, paint.Paint);
}
}
public override void DrawImage(Image image, in Rect destRect, in Rect srcRect, in float opacity = 1)
{
//using (var paint = new SKPaint { Color = new SKColor(255, 255, 255, (byte)(255 * opacity)) })
PaintWrapper paintWrapper;
SKPaint paint;
GetPaint(out paintWrapper, out paint);
using (paintWrapper)
{
paint.Color = new SKColor(255, 255, 255, (byte)(255 * opacity));
//paint.IsAntialias = AntialiasMode == AntialiasMode.AntiAlias;
//paint.FilterQuality = AntialiasMode == AntialiasMode.AntiAlias ? SKFilterQuality.Medium : SKFilterQuality.None;
SKBitmap bitmap;
if (image.ImageImpl is SkiaBitmap bmp)
{
bitmap = bmp.Bitmap;
}
else if (image.ImageImpl is SkiaImage img)
{
bitmap = img.Image;
}
else
{
throw new Exception("图片类型不支持");
}
//if (image is Image img)
//{
// canvas.DrawBitmap((img.ImageImpl as SkiaImage).Image, srcRect.ToSKRect(), destRect.ToSKRect(), paint);
//}
//else if (image is Bitmap bitmap)
//{
canvas.DrawBitmap(bitmap, srcRect.ToSKRect(), destRect.ToSKRect(), paint);
//}
}
}
public override void DrawLine(in Stroke stroke, Brush strokeBrush, in Point point1, in Point point2)
{
InitializeBrush(strokeBrush);
using (var paint = CreatePaint(strokeBrush, stroke))
{
canvas.DrawLine(point1.ToSKPoint(), point2.ToSKPoint(), paint.Paint);
}
}
public override void DrawPath(Brush strokeBrush, in Stroke stroke, PathGeometry path)
{
InitializeBrush(strokeBrush);
using (var paint = CreatePaint(strokeBrush, stroke))
{
canvas.DrawPath((path.PathIml as SkiaPath).SKPath, paint.Paint);
}
}
public override void DrawRectangle(Brush strokeBrush, in Stroke stroke, in Rect rect)
{
InitializeBrush(strokeBrush);
using (var paint = CreatePaint(strokeBrush, stroke))
{
canvas.DrawRect(rect.ToSKRect(), paint.Paint);
}
}
public override void DrawString(in CPF.Drawing.Point location, CPF.Drawing.Brush fillBrush, string text, in CPF.Drawing.Font font, in TextAlignment textAlignment = TextAlignment.Left, in float maxWidth = float.MaxValue, in TextDecoration decoration = default,
in float maxHeight = float.MaxValue,
in TextTrimming textTrimming = TextTrimming.None,
in Stroke stroke = default,
CPF.Drawing.Brush strokeBrush = null)
{
bool ellipsis = false;
DrawString(out ellipsis, location, fillBrush, text, font, textAlignment, maxWidth, decoration, maxHeight, textTrimming, stroke, strokeBrush);
}
public override void DrawString(out bool ellipsis, in Point location, Brush fillBrush, string text, in Font font, in TextAlignment textAlignment = TextAlignment.Left, in float maxWidth = float.MaxValue, in TextDecoration decoration = default,
in float maxHeight = float.MaxValue,
in TextTrimming textTrimming = TextTrimming.None,
in Stroke stroke = default,
Brush strokeBrush = null)
{
InitializeBrush(fillBrush);
ellipsis = false;
PaintWrapper paintWrapper = default;
if (strokeBrush != null && stroke.Width > 0)
{
InitializeBrush(strokeBrush);
paintWrapper = CreatePaint(strokeBrush, stroke, font);
}
var drawline = decoration.Stroke.Width > 0 && decoration.Brush != null && decoration.Location != TextDecorationLocation.None;
List<LinePosition> linePositions = null;
if (drawline)
{
linePositions = new List<LinePosition>();
}
using (var paint = CreatePaint(fillBrush, font))
{
paint.Paint.LcdRenderText = drawingFactory.ClearType;
paint.Paint.TextEncoding = SKTextEncoding.Utf16;
//paint.Paint.SubpixelText = true;
//paint.Paint.HintingLevel = SKPaintHinting.Full;
paint.Paint.IsStroke = false;
if (text.Length == 1 || (text.Length == 2 && char.IsSurrogate(text[0])))
{
var w = 0f;
var x = location.X;
if (textAlignment != TextAlignment.Left && maxWidth != float.MaxValue || drawline)
{
w = paint.Paint.MeasureString(text);
if (textAlignment != TextAlignment.Left && maxWidth != float.MaxValue)
{
if (textAlignment == TextAlignment.Right)
{
x = x + (maxWidth - w);
}
else if (textAlignment == TextAlignment.Center)
{
x = x + (maxWidth - w) / 2;
}
}
}
if (strokeBrush != null && stroke.Width > 0)
{
//using (var path = paintWrapper.Paint.GetTextPath(text, x, location.Y - paintWrapper.Paint.FontMetrics.Ascent))
//{
// canvas.DrawPath(path, paintWrapper.Paint);
// canvas.DrawPath(path, paint.Paint);
//}
canvas.DrawTextPath(paint.Paint, paintWrapper.Paint, text, x, location.Y - paintWrapper.Paint.FontMetrics.Ascent);
}
else
{
//canvas.DrawText(text, x, location.Y - paint.Paint.FontMetrics.Ascent, paint.Paint);
canvas.DrawText(paint.Paint, text, x, location.Y - paint.Paint.FontMetrics.Ascent);
}
if (drawline)
{
if (text == " ")
{
w += 2;
}
if (decoration.Location.HasFlag(TextDecorationLocation.Underline))
{
linePositions.Add(new LinePosition { X = x, Y = location.Y + paint.Paint.FontSpacing, Width = w });
}
if (decoration.Location.HasFlag(TextDecorationLocation.OverLine))
{
linePositions.Add(new LinePosition { X = x, Y = location.Y, Width = w });
}
if (decoration.Location.HasFlag(TextDecorationLocation.Strikethrough))
{
linePositions.Add(new LinePosition { X = x, Y = location.Y + paint.Paint.FontSpacing / 2, Width = w });
}
}
}
else
{
var lines = text.Split('\n');
//float width = 0;
float y = location.Y - paint.Paint.FontMetrics.Ascent;//top
float x = location.X;
List<(string text, float y, float w)> list = new List<(string, float, float)>();
float w = 0f;
float h = 0;
bool br = false;
for (int i = 0; i < lines.Length; i++)
{
var line = lines[i].Trim('\r');
if (i == lines.Length - 1 && line == "")
{
break;
}
var ws = paint.Paint.MeasureAllChar(line);
//var str = line;
var start = 0;
while (true)
{
h += paint.Paint.FontSpacing;
if (h > maxHeight && list.Count > 0)
{
br = true;
break;
}
//var len = Math.Max(1, (int)paint.Paint.BreakText(str, (float)Math.Ceiling(maxWidth)));
var len = ws.BreakText(start, (float)Math.Ceiling(maxWidth));
if (start + len.Item1 <= ws.Count)
{
//str = line.Substring(start, len);
start += len.Item1;
}
//var ww = paint.Paint.MeasureString(str);
list.Add((len.Item3, y, len.Item2));
w = Math.Max(w, len.Item2);
y += paint.Paint.FontSpacing;
if (start <= ws.Count - 1)
{
//str = line.Substring(start);
}
else
{
break;
}
}
if (br)
{
if (textTrimming == TextTrimming.CharacterEllipsis)
{
ellipsis = true;
}
if (textTrimming == TextTrimming.CharacterCenterEllipsis)
{
ellipsis = true;
DrawCenterString(location, fillBrush, text, font, textAlignment, maxWidth, decoration, maxHeight, stroke, strokeBrush);
return;
}
break;
}
}
if (maxWidth != float.MaxValue)
{
w = Math.Max(w, maxWidth);
}
for (int i = 0; i < list.Count; i++)
{
var item = list[i];
var xx = x;
if (textAlignment == TextAlignment.Right)
{
xx = x + w - item.w;
}
else if (textAlignment == TextAlignment.Center)
{
xx = x + (w - item.w) / 2;
}
var txt = item.text;
if (ellipsis && i == list.Count - 1)
{
if (item.w < w - paint.Paint.FontSpacing)
{
txt = txt + "...";
}
else
{
txt = txt.Substring(0, txt.Length - 1) + "...";
}
}
if (strokeBrush != null && stroke.Width > 0)
{
//using (var path = paintWrapper.Paint.GetTextPath(txt, xx, item.y))
//{
// paintWrapper.Paint.TextEncoding = SKTextEncoding.Utf16;
// canvas.DrawPath(path, paintWrapper.Paint);
// canvas.DrawPath(path, paint.Paint);
//}
canvas.DrawTextPath(paint.Paint, paintWrapper.Paint, txt, xx, item.y);
}
else
{
//canvas.DrawText(txt, xx, item.y, paint.Paint);
canvas.DrawText(paint.Paint, txt, xx, item.y);
}
if (drawline)
{
if (decoration.Location.HasFlag(TextDecorationLocation.Underline))
{
linePositions.Add(new LinePosition { X = xx, Y = item.y + paint.Paint.FontSpacing + paint.Paint.FontMetrics.Ascent, Width = item.w });
}
if (decoration.Location.HasFlag(TextDecorationLocation.OverLine))
{
linePositions.Add(new LinePosition { X = xx, Y = item.y + paint.Paint.FontMetrics.Ascent, Width = item.w });
}
if (decoration.Location.HasFlag(TextDecorationLocation.Strikethrough))
{
linePositions.Add(new LinePosition { X = xx, Y = item.y + paint.Paint.FontSpacing / 2 + paint.Paint.FontMetrics.Ascent, Width = item.w });
}
}
}
}
}
paintWrapper?.Dispose();
if (drawline)
{
//using (var brush = decoration.Brush)
{
InitializeBrush(decoration.Brush);
using (var paint = CreatePaint(decoration.Brush, decoration.Stroke))
{
foreach (var item in linePositions)
{
canvas.DrawLine(item.X, item.Y, item.X + item.Width, item.Y, paint.Paint);
}
}
}
}
}
public void DrawCenterString(in Point location, Brush fillBrush, string text, in Font font, in TextAlignment textAlignment = TextAlignment.Left, in float maxWidth = float.MaxValue, in TextDecoration decoration = default,
in float maxHeight = float.MaxValue,
in Stroke stroke = default,
Brush strokeBrush = null)
{
InitializeBrush(fillBrush);
PaintWrapper paintWrapper = default;
if (strokeBrush != null && stroke.Width > 0)
{
InitializeBrush(strokeBrush);
paintWrapper = CreatePaint(strokeBrush, stroke, font);
}
var drawline = decoration.Stroke.Width > 0 && decoration.Brush != null && decoration.Location != TextDecorationLocation.None;
List<LinePosition> linePositions = null;
if (drawline)
{
linePositions = new List<LinePosition>();
}
using (var paint = CreatePaint(fillBrush, font))
{
paint.Paint.LcdRenderText = drawingFactory.ClearType;
paint.Paint.TextEncoding = SKTextEncoding.Utf16;
paint.Paint.IsStroke = false;
if (text.Length == 1 || (text.Length == 2 && char.IsSurrogate(text[0])))
{
var w = 0f;
var x = location.X;
if (textAlignment != TextAlignment.Left && maxWidth != float.MaxValue || drawline)
{
w = paint.Paint.MeasureString(text);
if (textAlignment != TextAlignment.Left && maxWidth != float.MaxValue)
{
if (textAlignment == TextAlignment.Right)
{
x = x + (maxWidth - w);
}
else if (textAlignment == TextAlignment.Center)
{
x = x + (maxWidth - w) / 2;
}
}
}
if (strokeBrush != null && stroke.Width > 0)
{
canvas.DrawTextPath(paint.Paint, paintWrapper.Paint, text, x, location.Y - paintWrapper.Paint.FontMetrics.Ascent);
}
else
{
//canvas.DrawText(text, x, location.Y - paint.Paint.FontMetrics.Ascent, paint.Paint);
canvas.DrawText(paint.Paint, text, x, location.Y - paint.Paint.FontMetrics.Ascent);
}
if (drawline)
{
if (text == " ")
{
w += 2;
}
if (decoration.Location.HasFlag(TextDecorationLocation.Underline))
{
linePositions.Add(new LinePosition { X = x, Y = location.Y + paint.Paint.FontSpacing, Width = w });
}
if (decoration.Location.HasFlag(TextDecorationLocation.OverLine))
{
linePositions.Add(new LinePosition { X = x, Y = location.Y, Width = w });
}
if (decoration.Location.HasFlag(TextDecorationLocation.Strikethrough))
{
linePositions.Add(new LinePosition { X = x, Y = location.Y + paint.Paint.FontSpacing / 2, Width = w });
}
}
}
else
{
string newText = text;
var lines = text.Split('\n');
if (lines.Length > 1)
{
newText = lines[0].Trim('\r') + "..." + lines[lines.Length - 1].Trim('\r');
}
else
{
newText = text.Trim('\r');
}
lines = newText.Split('\n');
var ws = paint.Paint.MeasureAllChar(lines[0]);
var wsEnd = new List<(string, float)>();
for (int i = ws.Count - 1; i >= 0; i--)
{
wsEnd.Add(ws[i]);
}
var lenStart = ws.BreakText(0, (float)Math.Ceiling(maxWidth / 2));
var lenEnd = wsEnd.BreakText(0, (float)Math.Ceiling(maxWidth / 2));
if (lenStart.Item1 + lenEnd.Item1 < ws.Count)
{
//lenEnd = (lenEnd.Item1, lenEnd.Item2, newText.Substring(newText.Length - lenEnd.Item1, lenEnd.Item1));
lenEnd.Item3 = newText.Substring(newText.Length - lenEnd.Item1, lenEnd.Item1);
if (lenEnd.Item3.Length > 2)
{
lenEnd.Item3 = lenEnd.Item3.Substring(1, lenEnd.Item3.Length - 1);
}
//if (lenStart.Item3.Length > 2)
//{
// lenStart.Item3 = lenStart.Item3.Substring(0, lenStart.Item3.Length - 1);
//}
newText = lenStart.Item3 + "..." + lenEnd.Item3;
}
var w = 0f;
var x = location.X;
if (textAlignment != TextAlignment.Left && maxWidth != float.MaxValue || drawline)
{
w = paint.Paint.MeasureString(newText);
if (textAlignment != TextAlignment.Left && maxWidth != float.MaxValue)
{
if (textAlignment == TextAlignment.Right)
{
x = x + (maxWidth - w);
}
else if (textAlignment == TextAlignment.Center)
{
x = x + (maxWidth - w) / 2;
}
}
}
if (strokeBrush != null && stroke.Width > 0)
{
canvas.DrawTextPath(paint.Paint, paintWrapper.Paint, newText, x, location.Y - paintWrapper.Paint.FontMetrics.Ascent);
}
else
{
//canvas.DrawText(text, x, location.Y - paint.Paint.FontMetrics.Ascent, paint.Paint);
canvas.DrawText(paint.Paint, newText, x, location.Y - paint.Paint.FontMetrics.Ascent);
}
if (drawline)
{
if (newText == " ")
{
w += 2;
}
if (decoration.Location.HasFlag(TextDecorationLocation.Underline))
{
linePositions.Add(new LinePosition { X = x, Y = location.Y + paint.Paint.FontSpacing, Width = w });
}
if (decoration.Location.HasFlag(TextDecorationLocation.OverLine))
{
linePositions.Add(new LinePosition { X = x, Y = location.Y, Width = w });
}
if (decoration.Location.HasFlag(TextDecorationLocation.Strikethrough))
{
linePositions.Add(new LinePosition { X = x, Y = location.Y + paint.Paint.FontSpacing / 2, Width = w });
}
}
}
}
paintWrapper?.Dispose();
if (drawline)
{
//using (var brush = decoration.Brush)
{
InitializeBrush(decoration.Brush);
using (var paint = CreatePaint(decoration.Brush, decoration.Stroke))
{
foreach (var item in linePositions)
{
canvas.DrawLine(item.X, item.Y, item.X + item.Width, item.Y, paint.Paint);
}
}
}
}
}
public override void FillEllipse(Brush fillBrush, in Point center, in float radiusX, in float radiusY)
{
InitializeBrush(fillBrush);
using (var paint = CreatePaint(fillBrush))
{
canvas.DrawOval(center.X, center.Y, radiusX, radiusY, paint.Paint);
}
}
public override void FillGeometry(Brush fillBrush, Geometry geometry)
{
InitializeBrush(fillBrush);
using (var paint = CreatePaint(fillBrush))
{
canvas.DrawRegion((geometry.GeometryImpl as SkiaPathGeometry).SKRegion, paint.Paint);
}
}
//public override void DrawGeometry(Brush strokeBrush, in Stroke stroke, Geometry geometry)
//{
// InitializeBrush(strokeBrush);
// using (var paint = CreatePaint(strokeBrush, stroke))
// {
// canvas.DrawRegion((geometry.GeometryImpl as SkiaPathGeometry).SKRegion, paint.Paint);
// }
//}
public override void FillPath(Brush fillBrush, PathGeometry path)
{
InitializeBrush(fillBrush);
using (var paint = CreatePaint(fillBrush))
{
canvas.DrawPath((path.PathIml as SkiaPath).SKPath, paint.Paint);
}
}
public override void FillRectangle(Brush fillBrush, Rect rect)
{
InitializeBrush(fillBrush);
using (var paint = CreatePaint(fillBrush))
{
canvas.DrawRect(rect.ToSKRect(), paint.Paint);
}
}
//Stack<Rect> clips = new Stack<Rect>();
//Rect currentRect;
public override void PushClip(Rect clip)
{
canvas.Save();
canvas.ClipRect(clip.ToSKRect());
//clips.Push(currentRect);
//currentRect = clip;
}
public override void PopClip()
{
canvas.Restore();
//if (clips.Count > 0)
//{
// var c = clips.Pop();
// //canvas.ClipRect(c.ToSKRect());
// currentRect = c;
//}
}
protected override IDisposable CreateLinearGradientBrush(GradientStop[] bcs, in Point start, in Point end, in Matrix matrix)
{
bcs[0].Position = 0;
var shader = SKShader.CreateLinearGradient(
start.ToSKPoint(),
end.ToSKPoint(),
bcs.Select(a => a.Color.ToSKColor()).ToArray(),
bcs.Select(a => a.Position).ToArray(),
SKShaderTileMode.Clamp,
matrix.ToMatrix()
);
return shader;
}
protected override IDisposable CreateRadialGradientBrush(in Point center, in float radius, GradientStop[] bcs, in Matrix matrix)
{
bcs[0].Position = 0;
var shader = SKShader.CreateRadialGradient(
center.ToSKPoint(),
radius,
bcs.Select(a => a.Color.ToSKColor()).ToArray(),
bcs.Select(a => a.Position).ToArray(),
SKShaderTileMode.Clamp,
matrix.ToMatrix()
);
return shader;
}
protected override IDisposable CreateSolidBrush(Color color)
{
return SKShader.CreateColor(color.ToSKColor());
}
protected override IDisposable CreateTextureBrush(Image image, in WrapMode wrapMode, in Matrix matrix)
{
SKBitmap bitmap;
if (image.ImageImpl is SkiaBitmap bmp)
{
bitmap = bmp.Bitmap;
}
else if (image.ImageImpl is SkiaImage img)
{
bitmap = img.Image;
}
else
{
throw new Exception("无效图片");
}
if (wrapMode == WrapMode.Clamp)
{
using (SKBitmap bitmap1 = new SKBitmap(bitmap.Width + 1, bitmap.Height + 1))
{
using (SKCanvas canvas = new SKCanvas(bitmap1))
{
canvas.Clear();
canvas.DrawBitmap(bitmap, 0, 0);
return SKShader.CreateBitmap(bitmap1, SKShaderTileMode.Clamp, SKShaderTileMode.Clamp, matrix.ToMatrix());
}
}
}
using (SKImage im = SKImage.FromBitmap(bitmap))
{
return im.ToShader(wrapMode == WrapMode.Clamp ? SKShaderTileMode.Clamp : SKShaderTileMode.Repeat, wrapMode == WrapMode.Clamp ? SKShaderTileMode.Clamp : SKShaderTileMode.Repeat, matrix.ToMatrix());
}
}
}
struct LinePosition
{
public float X;
public float Y;
public float Width;
}
/// <summary>
/// Skia paint wrapper.
/// </summary>
internal class PaintWrapper : IDisposable
{
public readonly SKPaint Paint;
public bool isUsing;
private IDisposable _disposable1;
public PaintWrapper(SKPaint paint)
{
Paint = paint;
isUsing = true;
_disposable1 = null;
}
public void AddDisposable(IDisposable disposable)
{
_disposable1 = disposable;
}
/// <inheritdoc />
public void Dispose()
{
//Paint?.Dispose();
_disposable1?.Dispose();
_disposable1 = null;
Paint?.Reset();
isUsing = false;
}
}
}