【SkiaSharp繪圖13】SKCanvas方法詳解(二)填充顏色、封裝對象、高性能繪制、點(集)(多段)線、圓角矩形、Surface、沿路徑繪制文字

文章目錄

  • SKCanvas方法
    • DrawColor 填充顏色
    • DrawDrawable 繪制封裝對象
    • DrawImage 高性能繪制圖像
    • SKBitmap與SKImage對比
    • DrawPicture 繪制圖像
      • SKPicture
    • DrawPoint / DrawPoints 繪制點
    • DrawRoundRect/DrawRoundRectDifference繪制圓角矩形
    • DrawSurface 繪制Surface
    • DrawTextOnPath沿路徑繪制文字

SKCanvas方法

DrawColor 填充顏色

public void DrawColor (SkiaSharp.SKColor color, SkiaSharp.SKBlendMode mode = SkiaSharp.SKBlendMode.Src);
public void DrawColor (SkiaSharp.SKColorF color, SkiaSharp.SKBlendMode mode = SkiaSharp.SKBlendMode.Src);

使用指定顏色和混合方式填充當前裁切區域。

var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);using (var paint = new SKPaint())
{canvas.DrawColor(SKColors.LightGreen, SKBlendMode.Src);canvas.ClipRect(new SKRect(100, 100, 300, 200));//將顏色填充當前裁切區域canvas.DrawColor(SKColors.Blue, SKBlendMode.SrcOver);paint.Color = SKColors.Red;paint.TextSize = 24;canvas.DrawText($"DrawColor", 95, 150, paint);
}

使用亮綠填充整個區域,修改裁切區域后,再次填充為藍色。

DrawColor

DrawDrawable 繪制封裝對象

public void DrawDrawable (SkiaSharp.SKDrawable drawable, float x, float y);
public void DrawDrawable (SkiaSharp.SKDrawable drawable, SkiaSharp.SKPoint p);
public void DrawDrawable (SkiaSharp.SKDrawable drawable, ref SkiaSharp.SKMatrix matrix);

繪制一個封裝了繪制業務的對象。

  1. 封裝一個繪制笑臉的業務邏輯。
/// <summary>
/// 封閉一個繪制笑臉對象
/// </summary>
public class FaceDrawable:SKDrawable
{protected override void OnDraw(SKCanvas canvas){using (var paint = new SKPaint()){paint.Color = SKColors.LightGreen;canvas.DrawCircle(100, 100, 100, paint);paint.Color = SKColors.Red;paint.IsStroke = true;canvas.DrawCircle(60,80,25, paint);canvas.DrawCircle(140, 80, 25, paint);canvas.DrawArc(new SKRect(75, 90, 125, 130), 45, 90, false, paint);}                }
}
  1. 重復橫、列多次繪制。
var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);var drawable = new FaceDrawable();using (var paint = new SKPaint())
{//多次繪制一組相同的對象var bounds=drawable.Bounds;for (float x = 0; x < this.Width - bounds.Width; x += bounds.Width){for(float y = 0; y < this.Height - bounds.Height; y += bounds.Height){canvas.DrawDrawable(drawable,x,y);}}
}

DrawDrawable

DrawImage 高性能繪制圖像

public void DrawImage (SkiaSharp.SKImage image, SkiaSharp.SKPoint p, SkiaSharp.SKPaint paint = default);
public void DrawImage (SkiaSharp.SKImage image, SkiaSharp.SKRect dest, SkiaSharp.SKPaint paint = default);
public void DrawImage (SkiaSharp.SKImage image, SkiaSharp.SKRect source, SkiaSharp.SKRect dest, SkiaSharp.SKPaint paint = default);
public void DrawImage (SkiaSharp.SKImage image, float x, float y, SkiaSharp.SKPaint paint = default);

相比SKBitmap,性能更優,但SKImage內容不可變。

var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);if (skBmp == null)
{skBmp = SKBitmap.Decode(@"Images\AIWoman.png");skBmp = skBmp.Resize(new SKSizeI(350, 350), SKFilterQuality.High);
}
if (skImg == null) skImg = SKImage.FromBitmap(skBmp);const int DrawCount = 1000;
using (var paint = new SKPaint())
{paint.IsAntialias = true;paint.FilterQuality = SKFilterQuality.High;paint.TextSize = 24;for (int i = 0; i < 2; i++){count += 1;var sw = Stopwatch.StartNew();for (int j = 0; j < DrawCount; j++){if (count % 2 == 0){canvas.DrawImage(skImg, 400, 20, paint);}else{canvas.DrawBitmap(skBmp, 0, 20, paint);}}sw.Stop();if (count % 2 == 0){canvas.DrawText($"DrawImage {DrawCount}times:{sw.ElapsedMilliseconds}ms", 400, 400, paint);}else{canvas.DrawText($"DrawBitmap {DrawCount}times:{sw.ElapsedMilliseconds}ms", 0, 400, paint);}}
}

對比多次使用SKBitmap與SKImage繪制同一幅圖像。
DrawImage

SKBitmap與SKImage對比

SKBitmap

  1. 可變性
    SKBitmap 是可變的,允許對像素進行直接的讀寫操作。這使得它非常適合需要頻繁修改圖像內容的情況。

  2. 內存管理
    SKBitmap 的內存是可管理的,這意味著你可以控制它的分配和釋放。開發者需要注意內存管理以防止內存泄漏。

  3. 使用場景
    適用于需要直接處理像素數據的場景,如圖像編輯、動態生成圖像等。

SKImage

  1. 不可變性
    SKImage 是不可變的,一旦創建后其內容無法更改。這使得它非常適合用于高效的圖像顯示和傳遞。

  2. 內存管理
    SKImage 通常是通過圖形硬件加速的,因此在性能上優于 SKBitmap。內存管理更為自動化,不需要開發者顯式地管理。

  3. 使用場景
    適用于圖像渲染、顯示和傳輸,如繪制在屏幕上、保存為文件等。

DrawPicture 繪制圖像

public void DrawPicture (SkiaSharp.SKPicture picture, SkiaSharp.SKPaint paint = default);
public void DrawPicture (SkiaSharp.SKPicture picture, ref SkiaSharp.SKMatrix matrix, SkiaSharp.SKPaint paint = default);
public void DrawPicture (SkiaSharp.SKPicture picture, SkiaSharp.SKPoint p, SkiaSharp.SKPaint paint = default);
public void DrawPicture (SkiaSharp.SKPicture picture, float x, float y, SkiaSharp.SKPaint paint = default);

SKPicture

主要用于記錄繪圖命令以便在以后重用。

  1. 高效的重繪

    • SKPicture 可以記錄一次繪圖操作的所有命令,然后在需要的時候進行重放。這樣可以避免每次重繪時重新執行復雜的繪圖邏輯,提高繪圖的效率。
  2. 減少CPU開銷

    • 在一些復雜的繪圖操作中,通過記錄繪圖命令并重放,可以減少CPU的計算開銷,因為重放繪圖命令比重新執行繪圖邏輯所需的計算量更小。
  3. 方便的圖形內容重用

    • SKPicture 允許將繪圖命令記錄下來并在不同的地方重用。例如,可以在多個視圖中重用相同的圖形內容,而不需要每次都重新繪制。
  4. 支持多線程繪圖

    • SKPicture 的記錄和重放機制使得繪圖命令可以在后臺線程中生成,然后在主線程中重放。這有助于實現更流暢的用戶界面和更好的響應性。
  5. 簡化復雜場景的處理

    • 對于一些復雜的場景繪制,可以先將場景繪制到 SKPicture 中,然后在需要的時候重放這個 SKPicture。這使得處理復雜場景變得更加簡單和直觀。
var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);if (skPic == null)
{using (var recorder = new SKPictureRecorder()){using (var picCanvas = recorder.BeginRecording(SKRect.Create(400, 400)))using (var paint = new SKPaint()){paint.Color = SKColors.Red;paint.IsStroke = true;picCanvas.DrawCircle(60, 60, 50, paint);paint.IsStroke = false;paint.Color = SKColors.LightGreen;picCanvas.DrawRoundRect(100, 100, 350, 350, 50, 50, paint);}skPic = recorder.EndRecording();}
}using(var paint=new SKPaint())
{canvas.DrawPicture(skPic, paint);canvas.Translate(350, 350);canvas.DrawPicture(skPic, paint);
}

使用SKPictureRecorder生成SKPicture。
DrawPicture

DrawPoint / DrawPoints 繪制點

public void DrawPoint (SkiaSharp.SKPoint p, SkiaSharp.SKColor color);
public void DrawPoint (SkiaSharp.SKPoint p, SkiaSharp.SKPaint paint);
public void DrawPoint (float x, float y, SkiaSharp.SKColor color);
public void DrawPoint (float x, float y, SkiaSharp.SKPaint paint);
public void DrawPoints (SkiaSharp.SKPointMode mode, SkiaSharp.SKPoint[] points, SkiaSharp.SKPaint paint);

繪制點和點集

var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);using (var paint = new SKPaint())
{canvas.DrawPoint(50,50,SKColors.Red);canvas.DrawPoint(100, 50, SKColors.Blue);paint.StrokeWidth = 10;paint.Color = SKColors.Red;canvas.DrawPoint(50, 100, paint);paint.Color = SKColors.Blue;paint.StrokeCap = SKStrokeCap.Round;canvas.DrawPoint(100, 100, paint);var points = new SKPoint[] {new SKPoint(200,20),new SKPoint(400,20),new SKPoint(400,120),new SKPoint(200,120),new SKPoint(200,160),new SKPoint(300,160)};canvas.DrawPoints(SKPointMode.Points, points, paint);paint.Color = SKColors.Green;canvas.Translate(0, 200);canvas.DrawPoints(SKPointMode.Lines, points, paint);paint.Color = SKColors.Red;canvas.Translate(0, 200);canvas.DrawPoints(SKPointMode.Polygon, points, paint);
}

可以繪制點集、線段(兩個點連一條線),多段線
DrawPoint/DrawPoints

DrawRoundRect/DrawRoundRectDifference繪制圓角矩形

public void DrawRoundRect (SkiaSharp.SKRoundRect rect, SkiaSharp.SKPaint paint);
public void DrawRoundRect (SkiaSharp.SKRect rect, SkiaSharp.SKSize r, SkiaSharp.SKPaint paint);
public void DrawRoundRect (SkiaSharp.SKRect rect, float rx, float ry, SkiaSharp.SKPaint paint);
public void DrawRoundRect (float x, float y, float w, float h, float rx, float ry, SkiaSharp.SKPaint paint);
public void DrawRoundRectDifference (SkiaSharp.SKRoundRect outer, SkiaSharp.SKRoundRect inner, SkiaSharp.SKPaint paint);

繪制圓角矩形及兩個圓角矩形之間的區域。

var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);using (var paint = new SKPaint())
using (var txtPaint = new SKPaint())
{txtPaint.IsAntialias = true;txtPaint.Color = SKColors.Red;txtPaint.TextSize = 18;paint.IsAntialias = true;paint.IsStroke = true;paint.StrokeWidth = 2;var rect = new SKRect(50, 50, 250, 150);canvas.DrawRoundRect(rect, new SKSize(20, 20), paint);canvas.DrawText($"DrawRoundRect, rx=20,ry=20", 50, 180, txtPaint);paint.Color = SKColors.Red;canvas.Translate(250, 0);canvas.DrawRoundRect(rect, new SKSize(50, 20), paint);canvas.DrawText($"DrawRoundRect, rx=50,ry=20", 50, 180, txtPaint);paint.Style = SKPaintStyle.Fill;canvas.Translate(0, 100);// 定義外部圓角矩形var outerRect = new SKRoundRect(new SKRect(100, 100, 500, 400), 50, 50);// 定義內部圓角矩形var innerRect = new SKRoundRect(new SKRect(120, 120, 480, 380), 30, 50);canvas.DrawRoundRectDifference(outerRect, innerRect, paint);}

繪制不同圓角的圓角矩形,及兩個圓角矩形之間區域。

在這里插入圖片描述

DrawSurface 繪制Surface

public void DrawSurface (SkiaSharp.SKSurface surface, SkiaSharp.SKPoint p, SkiaSharp.SKPaint paint = default);
public void DrawSurface (SkiaSharp.SKSurface surface, float x, float y, SkiaSharp.SKPaint paint = default);

將Surface繪制到當前畫布上。

var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);using (var paint = new SKPaint())
{using (var skBmp = new SKBitmap(400, 400))using (var surfaceA = SKSurface.Create(skBmp.Info, skBmp.RowBytes))using (var canvasA = surfaceA.Canvas)using (var paintA = new SKPaint()){paintA.IsAntialias = true;paintA.Color = SKColors.Red;paintA.IsStroke = true;canvasA.Clear(SKColors.White);canvasA.DrawRect(20, 20, 300, 120, paintA);canvasA.DrawCircle(200, 200, 100, paintA);canvas.DrawSurface(surfaceA, 50, 50);canvas.DrawText($"DrawSurface", 50, 350, paint);canvas.DrawSurface(surfaceA, 350, 350);canvas.DrawText($"DrawSurface", 350,650, paint);}
}

DrawSurface

DrawTextOnPath沿路徑繪制文字

public void DrawTextOnPath (string text, SkiaSharp.SKPath path, SkiaSharp.SKPoint offset, bool warpGlyphs, SkiaSharp.SKPaint paint);
public void DrawTextOnPath (string text, SkiaSharp.SKPath path, SkiaSharp.SKPoint offset, SkiaSharp.SKPaint paint);
public void DrawTextOnPath (string text, SkiaSharp.SKPath path, float hOffset, float vOffset, SkiaSharp.SKPaint paint);

定義一個路徑,可使文字基線沿著路徑還不是普通的直線。

var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);string text = $"SkiaSharp可以直接繪制一段繞著路徑(如曲線)行走的文字,是不是很Cool!";
var path = new SKPath();path.MoveTo(50, 50); // 起始點
path.CubicTo(250, 400, 550, 100, 750, 600); // 三次貝塞爾曲線   
var pathMeasure = new SKPathMeasure(path, false);using (var paint = new SKPaint())
{paint.IsAntialias = true;paint.Color = SKColors.Red;paint.TextSize = 22;if (timer == null){var pathLength = pathMeasure.Length / 2;hOffset = pathLength;timer = new System.Windows.Forms.Timer();timer.Interval = 30;timer.Tick += (o, t) =>{hOffset -= 2;if (hOffset <= -pathLength){hOffset = pathLength;}this.TNTechImageBox.Invalidate();};timer.Start();}paint.Typeface = SKTypeface.FromFamilyName("宋體");var vOffset = -paint.TextSize / 2; // 垂直偏移量,使文本中心對齊路徑               canvas.DrawTextOnPath(text, path, hOffset, vOffset, paint);paint.IsStroke = true;canvas.DrawPath(path, paint);}
  1. 定義一段貝賽爾曲線
  2. 定義一段文字
  3. 定義一個定時器
  4. 使文字沿著曲線動態繪制。

DrawTextOnPath

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/37328.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/37328.shtml
英文地址,請注明出處:http://en.pswp.cn/web/37328.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

力扣2055.蠟燭之間的盤子

力扣2055.蠟燭之間的盤子 預處理每個元素左右最近的蠟燭下標 同時求前綴和遍歷每個詢問找到左右端點對應的內部的最近蠟燭(最大區間) class Solution {public:vector<int> platesBetweenCandles(string s, vector<vector<int>>& queries) {vector<…

List接口, ArrayList Vector LinkedList

Collection接口的子接口 子類Vector&#xff0c;ArrayList&#xff0c;LinkedList 1.元素的添加順序和取出順序一致&#xff0c;且可重復 2.每個元素都有其對應的順序索引 方法 在index 1 的位置插入一個對象&#xff0c;list.add(1,list2)獲取指定index位置的元素&#…

sheng的學習筆記-AI-聚類(Clustering)

ai目錄 sheng的學習筆記-AI目錄-CSDN博客 基礎知識 什么是聚類 在“無監督學習”(unsupervised learning)中&#xff0c;訓練樣本的標記信息是未知的&#xff0c;目標是通過對無標記訓練樣本的學習來揭示數據的內在性質及規律&#xff0c;為進一步的數據分析提供基礎。此類學…

Android跨進程通信,binder傳輸數據過大導致客戶端APP,Crash,異常捕獲,監聽異常的數值臨界值,提前Hook攔截。

文章目錄 Android跨進程通信&#xff0c;binder傳輸數據過大導致Crash&#xff0c;異常捕獲&#xff0c;監聽異常的數值臨界值&#xff0c;提前Hook攔截。1.binder在做跨進程傳輸時&#xff0c;最大可以攜帶多少數據1.1有時候這個1m的崩潰系統捕獲不到異常&#xff0c; 2.監測異…

志愿填報指南:為什么我強烈建議你報考計算機專業

首先恭喜2024屆高考的同學們&#xff0c;你們已經通過了高考的考驗&#xff0c;即將進入人生的新階段——大學。 現在正是高考完填報志愿的時刻&#xff0c;Left聽到身邊朋友提到報考志愿的諸多問題&#xff1a; 志愿填報怎么填&#xff1f;我要報考什么專業&#xff1f;這個…

[Cloud Networking] OSPF

OSPF 開放式最短路徑優先&#xff08;Open Shortest Path First&#xff09;是一種動態路由協議&#xff0c;它屬于鏈路狀態路由協議&#xff0c;具有路由變化收斂速度快、無路由環路、支持變長子網掩碼和匯總、層次區域劃分等優點。 1 OSPF Area 為了適應大型網絡&#xff0…

可編程定時計數器8253/8254 - 8253入門

時鐘-給設備打拍子 概述 在計算機系統中&#xff0c;為了使所有設備之間的通信井然有序&#xff0c;各通信設備間必須有統一的節奏&#xff0c;不能各干各的&#xff0c;這個節奏就被稱為定時或時鐘 時鐘并不是計算機處理速度的衡量&#xff0c;而是一種使設備間相互配合而避…

【2024-06-21】網易互娛秋招實習筆試三道編程題解

恭喜發現寶藏!搜索公眾號【TechGuide】回復公司名,解鎖更多新鮮好文和互聯網大廠的筆經面經。 作者@TechGuide【全網同名】 訂閱專欄: 【專享版】2024最新大廠筆試真題解析,錯過必后悔的寶藏資源! 第一題:陰陽師斗技 題目描述 小蓋正在參加陰陽師的斗技。已知斗技的規…

Linux 磁盤掛載與分區

Linux 磁盤掛載與分區 vda1: 其中vd表示虛擬磁盤&#xff0c;a表示第一塊磁盤&#xff0c;b表示第二塊磁盤&#xff0c;1表示第一塊磁盤的第一分區&#xff08;顯然兩塊磁盤都只有一個分區&#xff09;圖中可以看到&#xff0c;vda1磁盤只有一個分區&#xff0c;且全部掛載到根…

vue3使用vant4的列表vant-list點擊進入詳情自動滾動到對應位置,踩坑日記(一天半的踩坑經歷)

1.路由添加keepAlive <!-- Vue3緩存組件&#xff0c;寫法和Vue2不一樣--><router-view v-slot"{ Component }"><keep-alive><component :is"Component" v-if"$route.meta.keepAlive"/></keep-alive><component…

如何在MySQL中按字符串中的數字排序

在管理數據庫時&#xff0c;我們經常遇到需要按嵌入在字符串中的數字進行排序的情況。這在實際應用中尤為常見&#xff0c;比如文件名、代碼版本號等字段中通常包含數字&#xff0c;而這些數字往往是排序的關鍵。本文將詳細介紹如何在MySQL中利用正則表達式提取字符串中的數字并…

LiteDB - 一個單數據文件 .NET NoSQL 文檔存儲

LiteDB 一個小巧、快速、輕量級的 NoSQL 嵌入式數據庫。 Serverless NoSQL 文檔存儲類似于 MongoDB 的簡單 API100% C# 代碼,支持 .NET 3.5 / .NET 4.0 / NETStandard 1.3 / NETStandard 2.0,單 DLL (小于 300 kb)支持線程和進程安全支持文檔/操作級別的 ACID支持寫失敗后的數…

Google 發布最新開放大語言模型 Gemma 2,現已登陸 Hugging Face Hub

Google 發布了最新的開放大語言模型 Gemma 2&#xff0c;我們非常高興與 Google 合作&#xff0c;確保其在 Hugging Face 生態系統中的最佳集成。你可以在 Hub 上找到 4 個開源模型 (2 個基礎模型和 2 個微調模型) 。發布的功能和集成包括&#xff1a; Hub 上的模型https://hf.…

Java家教系統小程序APP公眾號h5源碼

讓學習更高效&#xff0c;更便捷 &#x1f31f; 引言&#xff1a;家教新選擇&#xff0c;小程序來助力 在快節奏的現代生活中&#xff0c;家長們越來越注重孩子的教育問題。然而&#xff0c;如何為孩子找到一位合適的家教老師&#xff0c;成為了許多家長頭疼的問題。現在&…

交叉編譯中的 --build、 --host和 --target

在交叉編譯中比較常見的一些參數就是build、host和target了,正確的理解這三者的含義對于交叉編譯是非常重要的,下面就此進行解釋   --build=編譯該軟件所使用的平臺   --host=該軟件將運行在哪個平臺   --target=該軟件所處理的目標平臺 我們經常會看到如下代碼:   …

谷歌個人號,20人連續封測14天所需設備該怎么解決?

現在&#xff0c;在Google Play上架應用&#xff0c;對于大部分開發者來說&#xff0c;真的是不小的挑戰&#xff0c;因為目前谷歌上架政策越來越嚴格了。特別是從2023年11月13日起&#xff0c;新政策要求個人開發者賬號的應用必須經過20個獨立用戶連續14天的封閉測試&#xff…

【C語言】--分支和循環(1)

&#x1f37f;個人主頁: 起名字真南 &#x1f9c7;個人專欄:【數據結構初階】 【C語言】 目錄 前言1 if 語句1.1 if1.2 else1.3 嵌套if1.4 懸空else 前言 C語言是結構化的程序設計語言&#xff0c;這里的結構指的是順序結構、選擇結構、循環結構。 我們可以用if、switch實現分支…

vue2實例實現一個初步的vuex

vue2實例實現一個初步的vuex 實現源碼&#xff1a;vue2-review 1.App.vue 2.store目錄下的index.js 3.效果 微信公眾號&#xff1a;刺頭拾年

MATLAB的.m文件與Python的.py文件:比較與互參

simulink MATLAB的.m文件與Python的.py文件&#xff1a;比較與互參相似之處**1. 基本結構****2. 執行邏輯****3. 可讀性和維護性** 差異性**1. 語法特性****2. 性能和應用****3. 開發環境** 互相學習的可能性結論 MATLAB的.m文件與Python的.py文件&#xff1a;比較與互參 在編…

擴展閱讀:什么是中斷

如果用一句話概括操作系統的原理,那就是:整個操作系統就是一個中斷驅動的死循環,用最簡單的代碼解釋如下: while(true){doNothing(); } 其他所有事情都是由操作系統提前注冊的中斷機制和其對應的中斷處理函數完成的。我們點擊一下鼠標,敲擊一下鍵盤,執行一個程序,…