文章目錄
- SKCanvas
- 構造SKCanvas
- 構造光柵 Surface
- 構造GPU Surface
- 構造PDF文檔
- 構造XPS文檔
- 構造SVG文檔
- SKNoDrawCanvas
- 變換
- 剪裁和狀態
- 構造函數
- 相關屬性
- DeviceClipBounds獲取裁切邊界(設備坐標系)
- ClipRect修改裁切區域
- IsClipEmpty當前裁切區域是否為空
- IsClipRect裁切區域是否為矩形
- LocalClipBounds獲取裁切邊界(本地坐標系)
- SaveCount 狀態記數
- TotalMatrix 變換矩陣
- 示例
SKCanvas
用于在位圖、表面或其他繪圖設備上執行 2D 圖形操作。
-
繪圖操作:
SKCanvas
提供了豐富的繪圖方法,如繪制幾何圖形(圓形、矩形、路徑等)、文本、位圖等。- 可以設置畫筆(
SKPaint
)的顏色、樣式、線寬等屬性,實現各種繪制效果。
-
圖像合成和組合:
- 可以將多個繪圖操作合成為單個圖像,支持透明度和混合模式,實現復雜的圖形效果。
- 支持在不同圖層上進行繪制,并可以進行圖層的合并和組合。
-
處理和轉換:
- 支持坐標變換(平移、旋轉、縮放等),方便實現復雜的圖形變換和動畫效果。
- 可以通過剪切區域(
ClipRect
、ClipPath
)限制繪制區域,優化繪制性能。
-
跨平臺支持:
- SkiaSharp 是跨平臺的,可以在 Windows、macOS、Linux 和移動平臺(Android、iOS)上使用,
SKCanvas
提供了統一的繪圖 API。
- SkiaSharp 是跨平臺的,可以在 Windows、macOS、Linux 和移動平臺(Android、iOS)上使用,
-
高性能渲染:
- SkiaSharp 使用 GPU 加速和多線程優化,能夠高效地處理大規模的圖形渲染和復雜的圖形操作。
構造SKCanvas
構造光柵 Surface
光柵后端繪制到內存塊。該內存可由SkiaSharp或客戶端自行管理。
默認推薦使用管理繪制畫布命令的內存的對象SKSurface。
OnPaintSurface(object sender, SkiaSharp.Views.Desktop.SKPaintGLSurfaceEventArgs e)事件中e參數的Surface就是SKSurface對象。
可通過SKSurface.Create方法的不同參數創建由SkiaSharp或自行管理內存的SKSurface對象。
- 由SkiaSharp管理
var canvas = e.Surface.Canvas;
canvas.Clear(SKColors.White);var info = new SKImageInfo(400, 300);
//通過SKSurface獲取Canvas
using (var skSurface = SKSurface.Create(info))
using (var skCanvas = skSurface.Canvas)
using (var skPaint = new SKPaint())
{skPaint.TextSize = 18;skPaint.Color = SKColors.LightGreen;skPaint.IsStroke = true;skCanvas.DrawRect(100, 100, 250, 100, skPaint);skCanvas.DrawText($"SKSurface1", 0, 200, skPaint);canvas.DrawSurface(skSurface, 0, 0, skPaint);
}
- 自動分配內存
var info2 = new SKImageInfo(400, 400);
//自行分配內存
var memory = Marshal.AllocCoTaskMem(info2.BytesSize);try
{using (var surface2 = SKSurface.Create(info2, memory, info2.RowBytes))using (var canvas2 = surface2.Canvas)using (var paint = new SKPaint()){paint.Color = SKColors.Blue;paint.TextSize = 18;canvas2.DrawCircle(200, 200, 100, paint);canvas2.DrawText($"SKSurface2", 0, 200, paint);canvas.DrawSurface(surface2, 0, 350, paint);}
}
finally
{Marshal.FreeCoTaskMem(memory);
}
在測試的過程,一開始不知為什么會出現兩個矩形,還以為是SkiaSharp的BUG,后不知怎么搞的,又沒事了…
構造GPU Surface
GPU Surface必須有一個GRContext對象來管理GPU上下文以及紋理和字體的相關緩存。
GRContext對象與OpenGL上下文或Vulkan設備一一匹配。
如果使用SKGLControl控件,默認的的e.Surface.Context就是啟用了GPU的OpenGL
構造PDF文檔
使用SkiaSharp直接繪制并生成PDF文檔(不支持顯示PDF)
PDF后端使用SKDocument而不是SKSurface,因為文檔必須包含多個頁面。
var canvas = e.Surface.Canvas;
canvas.Clear(SKColors.White);using (FileStream stream = new FileStream(@"Images\test.pdf", FileMode.CreateNew, FileAccess.Write))
using (var doc = SKDocument.CreatePdf(stream, 72))
using (var pdfCanvas = doc.BeginPage(600, 800))
using (var paint = new SKPaint())
{paint.Color = SKColors.LightGreen;paint.IsAntialias = true;paint.TextSize = 24;pdfCanvas.DrawText("Create PDF Doc by SkiaSharp", 20, 30, paint);pdfCanvas.DrawCircle(300, 400, 150, paint);doc.EndPage();doc.Close();
}
- 創建一個寫的文件流
- 根據文件流和DPI創建SKDocument對象
- 根據SKDocument對象創建SKCanvas
- 可以SKCanvas繪制內容,生成PDF。
構造XPS文檔
與構造Pdf類似,用SKDocument.CreateXps
var canvas = e.Surface.Canvas;
canvas.Clear(SKColors.White);using (FileStream stream = new FileStream(@"Images\test.xps", FileMode.Create, FileAccess.Write))
using (var doc = SKDocument.CreatePdf(stream, 72))
using (var pdfCanvas = doc.BeginPage(600, 800))
using (var paint = new SKPaint())
{paint.Color = SKColors.LightGreen;paint.IsAntialias = true;paint.TextSize = 24;pdfCanvas.DrawText("Create XPS doc by SkiaSharp", 20, 30, paint);pdfCanvas.DrawCircle(300, 400, 150, paint);doc.EndPage();doc.Close();
}
構造SVG文檔
使用SKSvgCanvas構造SVG文檔。
using (FileStream stream = new FileStream(@"Images\test.svg", FileMode.Create, FileAccess.Write))
using (var svgCanvas = SKSvgCanvas.Create(new SKRect(0, 0, 600, 800), stream))
using (var paint = new SKPaint())
{paint.Color = SKColors.LightGreen;paint.IsAntialias = true;paint.TextSize = 24;svgCanvas.DrawText("Create SVG doc by SkiaSharp", 20, 30, paint);svgCanvas.DrawCircle(300, 400, 150, paint);
}
SKNoDrawCanvas
構造不繪制的畫布,是提供一個“無操作”的畫布,這意味著它不會進行任何實際的繪圖操作。一般用于性能測試、繪圖命令的記錄與分析、避免不必要的繪圖。
變換
SKCanvas提供Scale、Skew、Translate、RotateDegrees、RotateRadians等常用的二維變換。
還可以使用SetMatrix指定變換矩陣。
使用ResetMatrix可重置矩陣狀態。
剪裁和狀態
可使用變換矩陣的Save方法來保存當前變換狀態,然后使用Restore或RestoreToCount方法恢復以前的狀態。還有SaveLayer方法。
構造函數
public SKCanvas (SkiaSharp.SKBitmap bitmap);
創建一個畫布,其中包含要繪制的指定位圖。
相關屬性
DeviceClipBounds獲取裁切邊界(設備坐標系)
public SkiaSharp.SKRectI DeviceClipBounds { get; }
獲取當前裁切邊界(在設備坐標中)。
ClipRect修改裁切區域
public void ClipRect (SkiaSharp.SKRect rect, SkiaSharp.SKClipOperation operation = SkiaSharp.SKClipOperation.Intersect, bool antialias = false);
修改當前裁切區域
IsClipEmpty當前裁切區域是否為空
public bool IsClipEmpty { get; }
判斷當前裁切區域是否為空。
IsClipRect裁切區域是否為矩形
public bool IsClipRect { get; }
判斷當前裁切區域是否為矩形。
LocalClipBounds獲取裁切邊界(本地坐標系)
public SkiaSharp.SKRect LocalClipBounds { get; }
獲取當前的裁切邊界(本地坐標系中,矩陣變換后的)
SaveCount 狀態記數
public int SaveCount { get; }
獲取畫布私有堆棧上的矩陣/裁切狀態的數量。
當調用SKCanvas對象的Save()方法時,加1。調用Restore()時,減1。
一個新的畫布的SaveCount為1。
TotalMatrix 變換矩陣
public SkiaSharp.SKMatrix TotalMatrix { get; }
獲取當前畫布的變換矩陣。
示例
var canvas = e.Surface.Canvas;
canvas.Clear(SKColors.White);
using (var paint = new SKPaint())
{paint.Color = SKColors.Red;paint.IsAntialias = true;paint.TextSize = 24;var yOffset = 50F;if (canvas.GetDeviceClipBounds(out var rect)){canvas.DrawText($"DeviceClipBounds:{rect}", 20, yOffset, paint);}yOffset += paint.FontSpacing;rect = new SKRectI(25, 20, 800, 800);//設置裁切區域canvas.ClipRect(rect);canvas.DrawText($"ClipRect:{rect}", 20, yOffset, paint);yOffset += paint.FontSpacing;if (canvas.GetDeviceClipBounds(out rect)){canvas.DrawText($"DeviceClipBounds:{rect}", 20, yOffset, paint);yOffset += paint.FontSpacing;}canvas.DrawText($"IsClipEmpty:{canvas.IsClipEmpty}", 50, yOffset, paint);yOffset += paint.FontSpacing;canvas.DrawText($"IsClipRect:{canvas.IsClipRect}", 50, yOffset, paint);yOffset += paint.FontSpacing;rect = SKRectI.Create(100, 50);canvas.DrawText($"ClipRect:{rect} Difference", 50, yOffset, paint);yOffset += paint.FontSpacing;canvas.ClipRect(rect, SKClipOperation.Difference);canvas.DrawText($"IsClipRect:{canvas.IsClipRect}", 50, yOffset, paint);yOffset += paint.FontSpacing;canvas.DrawText($"LocalClipBounds:{canvas.LocalClipBounds}", 50, yOffset, paint);yOffset += paint.FontSpacing;// 設置畫布變換(如平移)canvas.Translate(50, 50);canvas.DrawText($"Translate(50, 50)", 50, yOffset, paint);yOffset += paint.FontSpacing;canvas.DrawText($"LocalClipBounds:{canvas.LocalClipBounds}", 50, yOffset, paint);yOffset += paint.FontSpacing;if (canvas.GetDeviceClipBounds(out rect)){canvas.DrawText($"DeviceClipBounds:{rect}", 50, yOffset, paint);yOffset += paint.FontSpacing;}canvas.DrawText($"SaveCount:{canvas.SaveCount}", 50, yOffset, paint);yOffset += paint.FontSpacing;canvas.Save();canvas.DrawText($"Call Save()", 50, yOffset, paint);yOffset += paint.FontSpacing;canvas.DrawText($"SaveCount:{canvas.SaveCount}", 50, yOffset, paint);yOffset += paint.FontSpacing;canvas.Restore();canvas.DrawText($"Call Restore()", 50, yOffset, paint);yOffset += paint.FontSpacing;canvas.DrawText($"SaveCount:{canvas.SaveCount}", 50, yOffset, paint);yOffset += paint.FontSpacing;using (var newCanvas = new SKCanvas(new SKBitmap())){canvas.DrawText($"new SKCanvas SaveCount:{newCanvas.SaveCount}", 50, yOffset, paint);yOffset += paint.FontSpacing;}var matrix = canvas.TotalMatrix;canvas.DrawText($"TotalMatrix:{string.Join(",",matrix.Values)}", 50, yOffset, paint);yOffset += paint.FontSpacing;
}