CPF/CPF.Skia/SkiaDrawingContext.cs

1012 lines
41 KiB
C#
Raw Normal View History

2023-11-21 23:05:03 +08:00
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;
2023-12-26 11:20:49 +08:00
if (target.Width < 1 || target.Height < 1)
{
Debug.WriteLine("绘图表面为空");
Console.WriteLine("绘图表面为空");
}
2023-11-21 23:05:03 +08:00
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);
2023-11-21 23:05:03 +08:00
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;
}
2023-11-29 21:06:01 +08:00
if (gRContext != null)
{
//gRContext.Flush();
gRContext.ResetContext();
}
2023-11-21 23:05:03 +08:00
//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);
}
2023-12-19 22:11:21 +08:00
var drawline = decoration.Stroke.Width > 0 && decoration.Brush != null && decoration.Location != TextDecorationLocation.None;
2023-11-21 23:05:03 +08:00
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);
2023-12-19 22:11:21 +08:00
if (textAlignment != TextAlignment.Left && maxWidth != float.MaxValue)
2023-11-21 23:05:03 +08:00
{
2023-12-19 22:11:21 +08:00
if (textAlignment == TextAlignment.Right)
{
x = x + (maxWidth - w);
}
else if (textAlignment == TextAlignment.Center)
{
x = x + (maxWidth - w) / 2;
}
2023-11-21 23:05:03 +08:00
}
}
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;
}
2023-12-19 22:11:21 +08:00
if (decoration.Location.HasFlag(TextDecorationLocation.Underline))
2023-11-21 23:05:03 +08:00
{
2023-12-19 22:11:21 +08:00
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 });
2023-11-21 23:05:03 +08:00
}
}
}
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)
{
2023-12-19 22:11:21 +08:00
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))
2023-11-21 23:05:03 +08:00
{
2023-12-19 22:11:21 +08:00
linePositions.Add(new LinePosition { X = xx, Y = item.y + paint.Paint.FontSpacing / 2 + paint.Paint.FontMetrics.Ascent, Width = item.w });
2023-11-21 23:05:03 +08:00
}
}
}
}
}
paintWrapper?.Dispose();
if (drawline)
{
2023-12-19 22:11:21 +08:00
//using (var brush = decoration.Brush)
2023-11-21 23:05:03 +08:00
{
2023-12-19 22:11:21 +08:00
InitializeBrush(decoration.Brush);
using (var paint = CreatePaint(decoration.Brush, decoration.Stroke))
2023-11-21 23:05:03 +08:00
{
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);
}
2023-12-19 22:11:21 +08:00
var drawline = decoration.Stroke.Width > 0 && decoration.Brush != null && decoration.Location != TextDecorationLocation.None;
2023-11-21 23:05:03 +08:00
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);
2023-12-19 22:11:21 +08:00
if (textAlignment != TextAlignment.Left && maxWidth != float.MaxValue)
2023-11-21 23:05:03 +08:00
{
2023-12-19 22:11:21 +08:00
if (textAlignment == TextAlignment.Right)
{
x = x + (maxWidth - w);
}
else if (textAlignment == TextAlignment.Center)
{
x = x + (maxWidth - w) / 2;
}
2023-11-21 23:05:03 +08:00
}
}
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;
}
2023-12-19 22:11:21 +08:00
if (decoration.Location.HasFlag(TextDecorationLocation.Underline))
2023-11-21 23:05:03 +08:00
{
2023-12-19 22:11:21 +08:00
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 });
2023-11-21 23:05:03 +08:00
}
}
}
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]);
2023-11-29 21:06:01 +08:00
2023-11-21 23:05:03 +08:00
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);
2023-11-29 21:06:01 +08:00
if (lenEnd.Item3.Length > 2)
2023-11-21 23:05:03 +08:00
{
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);
2023-12-19 22:11:21 +08:00
if (textAlignment != TextAlignment.Left && maxWidth != float.MaxValue)
2023-11-21 23:05:03 +08:00
{
2023-12-19 22:11:21 +08:00
if (textAlignment == TextAlignment.Right)
{
x = x + (maxWidth - w);
}
else if (textAlignment == TextAlignment.Center)
{
x = x + (maxWidth - w) / 2;
}
2023-11-21 23:05:03 +08:00
}
}
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;
}
2023-12-19 22:11:21 +08:00
if (decoration.Location.HasFlag(TextDecorationLocation.Underline))
2023-11-21 23:05:03 +08:00
{
2023-12-19 22:11:21 +08:00
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 });
2023-11-21 23:05:03 +08:00
}
}
}
}
paintWrapper?.Dispose();
if (drawline)
{
2023-12-19 22:11:21 +08:00
//using (var brush = decoration.Brush)
2023-11-21 23:05:03 +08:00
{
2023-12-19 22:11:21 +08:00
InitializeBrush(decoration.Brush);
using (var paint = CreatePaint(decoration.Brush, decoration.Stroke))
2023-11-21 23:05:03 +08:00
{
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;
}
}
}