【SkiaSharp绘图08】SKPaint方法:自动换行、是否乱码、字符偏移、边界、截距、文本轮廓、测量文本

news2024/11/29 0:37:59

文章目录

  • SKPaint方法
    • BreakText 计算指定宽度内可绘制的字符个数
    • ContainsGlyphs字体是否包含文本字符(是否会乱码)
    • GetGlyphOffsets 字符偏移量
    • GetGlyphPositions 偏移坐标
    • GetGlyphWidths 每个字符的宽度与边界
    • GetHorizontalTextIntercepts 轮廓截距
    • GetPositionedTextIntercepts轮廓截距
    • GetTextIntercepts 轮廓截距
    • 代码示例
    • GetTextPath 文本轮廓路径
    • MeasureText 测量文本
    • Reset重置
    • Clone 克隆
    • ToFont
    • SetColor
    • 其它待补充
      • MaskFilter 掩码过滤器
      • PathEffect 路径效果
      • Shader 着色器

SKPaint方法

BreakText 计算指定宽度内可绘制的字符个数

public long BreakText (string text, float maxWidth);
public long BreakText (byte[] text, float maxWidth, out float measuredWidth);

指定要测量的文本和最大文本绘制宽度,获取指定宽度内可绘制的字符个数和实际绘制宽度。

实现一个可自动换行绘制的函数

/// <summary>
/// 绘制自动换行的文本
/// </summary>
/// <param name="canvas"></param>
/// <param name="text">待绘制的文本</param>
/// <param name="maxWidth">文本最大宽度</param>
/// <param name="left">文本框左上角X轴起始</param>
/// <param name="top">文本框左上角Y轴起始</param>
/// <param name="paint"></param>
/// <param name="endWidth">最后一行实际绘制宽度</param>
/// <returns>总文本框高度</returns>
public float DrawTextWithWordWrap(SKCanvas canvas, string text, float maxWidth, 
                                  float left, float top, SKPaint paint,out float endWidth)
{
    endWidth = 0F;
    if (string.IsNullOrEmpty(text)) return 0.0f;

    //文本基线
    float baselineY = top - paint.FontMetrics.Ascent;

    while (true)
    {
        var breakIndex = (int)paint.BreakText(text, maxWidth, out endWidth);

        string line = text.Substring(0, breakIndex);
        canvas.DrawText(line, left, baselineY, paint);
        text = text.Substring(breakIndex).TrimStart(); // 去掉已经绘制的部分,继续处理剩余的文本
        if (!string.IsNullOrEmpty(text))
        {
            baselineY += paint.FontSpacing;
        }
        else
        {
            break;
        }
    }
    return baselineY + paint.FontMetrics.Descent - top;
}

给定文本框左上角起始点、文本框宽度、文本内容。绘制后,将文本框绘制一个矩形,并在结尾位置画一蓝色竖线。

var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);

const string TEXT = "SkiaSharp 是一个跨平台的 2D 图形库,用于 .NET 平台。它是 Skia 图形引擎的 .NET 绑定,提供了高性能的绘图和图形处理功能。SkiaSharp 允许开发人员在 .NET 平台上创建丰富的用户界面、图像处理应用程序、游戏和数据可视化工具。";

using (var paint = new SKPaint())
{
    paint.IsAntialias = true;
    paint.TextSize = 24;
    paint.Typeface = SKTypeface.FromFamilyName("微软雅黑");
                   
    var edge = 20;
    var top = 50;

    var txtWidth = Math.Max(30, info.Width - edge * 2);
    var height = DrawTextWithWordWrap(canvas, TEXT, txtWidth, edge, top, paint,out float endWidth);

    paint.Color = SKColors.Red;
    paint.IsStroke = true;
    //实际的文本框
    canvas.DrawRect(edge, top, txtWidth, height, paint);

    //文本结尾的位置
    paint.Color = SKColors.Blue;
    paint.StrokeWidth = 5;
    canvas.DrawLine(edge + endWidth, top + height - paint.FontSpacing, edge + endWidth, top + height,paint);
}

BreakText自动换行

ContainsGlyphs字体是否包含文本字符(是否会乱码)

public bool ContainsGlyphs (string text);
public bool ContainsGlyphs (byte[] text);

获取指定文本所有字符是否对应于非零字形索引(不会乱码)。

var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);

const string TEXT = "SkiaSharp绘图";

using (var paint = new SKPaint())
{
    paint.IsAntialias = true;
    paint.TextSize = 24;
    paint.Typeface = SKTypeface.FromFamilyName("Arial");
    //无法显示中文
    canvas.DrawText($"{TEXT},{paint.Typeface.FamilyName},ContainsGlyphs:{paint.ContainsGlyphs(TEXT)}",20,50,paint);

    paint.Typeface = SKTypeface.FromFamilyName("微软雅黑");
    //可以显示中文
    canvas.DrawText($"{TEXT},{paint.Typeface.FamilyName},ContainsGlyphs:{paint.ContainsGlyphs(TEXT)}", 20, 100, paint);
}

判断指定的字体是否会显示乱码。
ContainsGlyphs

GetGlyphOffsets 字符偏移量

public float[] GetGlyphOffsets (string text, float origin = 0);//origin相对起始偏移

用于获取给定文本中每个字形的偏移量。这对于在进行复杂的文本布局时非常有用,因为它允许您精确控制每个字形的位置,而不仅仅是使用默认的字符间距和行间距。

获取一句文本中每个字符的偏移量,并在字符前画一竖线。
GetGlyphOffsets

GetGlyphPositions 偏移坐标

public SkiaSharp.SKPoint[] GetGlyphPositions (string text, SkiaSharp.SKPoint origin = default);

获取每个字符的偏移坐标

GetGlyphWidths 每个字符的宽度与边界

public float[] GetGlyphWidths (string text, out SkiaSharp.SKRect[] bounds);

获取每个字符占的宽度和边界。

GetHorizontalTextIntercepts 轮廓截距

public float[] GetHorizontalTextIntercepts (string text, float[] xpositions, float y, float upperBounds, float lowerBounds);

获取文本与两条平行相交的截距。(只能单字?多字时,其它坐标不知如何处理)
xpositions:为文本起始位置
y:为文本垂直的基线位置
upperBounds:上水平线
lowerBound:下水平线,这两条线表示需要测量的范围。

GetPositionedTextIntercepts轮廓截距

public float[] GetPositionedTextIntercepts (string text, SkiaSharp.SKPoint[] positions, float upperBounds, float lowerBounds);

与GetHorizontalTextIntercepts 基本相同。

GetTextIntercepts 轮廓截距

public float[] GetTextIntercepts (string text, float x, float y, float upperBounds, float lowerBounds);

获取文字与平行线的截距,有前面函数的效果基本相同。

代码示例

var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);

const string TEXT = "SkiaSharp绘图";

using (var paint = new SKPaint())
{
    paint.IsAntialias = true;
    paint.TextSize = 48;
    paint.TextScaleX = 1F;

    var startX = 20F;
    var startY = 100F;

    paint.Typeface = SKTypeface.FromFamilyName("微软雅黑");
    canvas.DrawText($"{TEXT}", startX, startY, paint);

    //x偏移量
    var xOffsets = paint.GetGlyphOffsets(TEXT, startX);
    paint.Color = SKColors.Red;
    foreach(var x in  xOffsets)
    {
        canvas.DrawLine(x, 80, x, 100, paint);
    }

    //x偏移量
    var points = paint.GetGlyphPositions(TEXT, new SKPoint(startX, startY));

    paint.Color = SKColors.Blue;
    foreach (var pt in points)
    {
        canvas.DrawLine(pt.X, pt.Y, pt.X, pt.Y + 20, paint);
    }

    startY += 100;
    canvas.DrawText($"{TEXT}", startX, startY, paint);
    //宽度与边界
    var widths = paint.GetGlyphWidths(TEXT, out SKRect[] bounds);

    paint.Color = SKColors.Red;
    float offsetX = startX;
    paint.IsStroke= true;
    var index = 0;
    foreach(var rect in bounds)
    {
        //绘制边界矩形
        canvas.DrawRect(offsetX + rect.Left, rect.Top + startY, rect.Width, rect.Height, paint);
        offsetX += widths[index++];
    }


    // 指定文本的起始 X 坐标数组
    float[] xpositions = { 100 };

    // 文本的起始 Y 坐标
    float y = 350;

    // 指定测量区域的上边界和下边界 Y 坐标
    float upperBounds = 280;
    float lowerBounds = 340;

    paint.TextSize = 120;
    // 获取文本的水平截距
    float[] intercepts = paint.GetHorizontalTextIntercepts("心", xpositions, y, upperBounds, lowerBounds);

    // 绘制文本
    canvas.DrawText("心", xpositions[0], y, paint);

    paint.TextSize = 24;
    canvas.DrawText("GetHorizontalTextIntercepts", xpositions[0] + 250, y-30, paint);
    paint.TextSize = 120;

    //两条水平线
    paint.Color = SKColors.Green;
    canvas.DrawLine(0, upperBounds, info.Width, upperBounds, paint);
    canvas.DrawLine(0, lowerBounds, info.Width, lowerBounds, paint);

    // 绘制截距线
    paint.Color = SKColors.Red;
    paint.StrokeWidth = 2;
    foreach (var intercept in intercepts)
    {
        canvas.DrawLine(intercept, upperBounds, intercept, lowerBounds, paint);
    }

    y += 100;
    upperBounds += 100;
    lowerBounds += 100;

    var skpoints = new SKPoint[] { new SKPoint(xpositions[0], y) };
    intercepts = paint.GetPositionedTextIntercepts("心", skpoints, upperBounds, lowerBounds);
    // 绘制文本
    canvas.DrawText("心", xpositions[0], y, paint);
    paint.TextSize = 24;
    canvas.DrawText("GetPositionedTextIntercepts", xpositions[0] + 250, y - 30, paint);
    paint.TextSize = 120;

    //两条水平线
    paint.Color = SKColors.Green;
    canvas.DrawLine(0, upperBounds, info.Width, upperBounds, paint);
    canvas.DrawLine(0, lowerBounds, info.Width, lowerBounds, paint);

    // 绘制截距线
    paint.Color = SKColors.Red;
    paint.StrokeWidth = 2;
    foreach (var intercept in intercepts)
    {
        canvas.DrawLine(intercept, upperBounds, intercept, lowerBounds, paint);
    }


    y += 100;
    upperBounds += 100;
    lowerBounds += 100;
    intercepts = paint.GetTextIntercepts("心", xpositions[0],y, upperBounds, lowerBounds);
    // 绘制文本
    canvas.DrawText("心", xpositions[0], y, paint);
    paint.TextSize = 24;
    canvas.DrawText("GetTextIntercepts", xpositions[0] + 250, y - 30, paint);
    paint.TextSize = 120;

    //两条水平线
    paint.Color = SKColors.Green;
    canvas.DrawLine(0, upperBounds, info.Width, upperBounds, paint);
    canvas.DrawLine(0, lowerBounds, info.Width, lowerBounds, paint);

    // 绘制截距线
    paint.Color = SKColors.Red;
    paint.StrokeWidth = 2;
    foreach (var intercept in intercepts)
    {
        canvas.DrawLine(intercept, upperBounds, intercept, lowerBounds, paint);
    }
}

运行结果示例
轮廓截距

GetTextPath 文本轮廓路径

public SkiaSharp.SKPath GetTextPath (string text, float x, float y);
public SkiaSharp.SKPath GetTextPath (string text, SkiaSharp.SKPoint[] points);

用于生成给定文本的路径数据。路径数据包含文本的轮廓信息,可以用于高级文本渲染和图形操作,例如自定义文本效果、路径动画、变形、剪切等。通过这个方法,开发者可以获取文本的几何形状,并将其用于各种自定义绘制操作。

var canvas = e.Surface.Canvas;
var info = e.Info;
canvas.Clear(SKColors.White);

const string TEXT = "SkiaSharp绘图";

using (var paint = new SKPaint())
{
    paint.IsAntialias = true;
    paint.TextSize = 96;
    paint.Typeface = SKTypeface.FromFamilyName("微软雅黑");

    var x = 20F;
    var y = 100F;
    canvas.DrawText(TEXT, x,y, paint);

    //文本轮廓路径
    var textPath = paint.GetTextPath(TEXT,x,y);

    paint.StrokeWidth = 2;
    paint.IsStroke = true;
    paint.Color = SKColors.Red;
    canvas.DrawPath(textPath, paint);
}

使用DrawText绘制填充黑色文本,再使用GetTextPath获取文本轮廓,再使用DrawPath绘制文本红色描边轮廓。

GetTextPath

MeasureText 测量文本

public float MeasureText (string text);
public float MeasureText (string text, ref SkiaSharp.SKRect bounds);

用于测量给定文本字符串的宽度。这个方法的主要作用在于计算文本在指定字体和样式下的绘制尺寸,通常用于布局和排版,确保文本能够正确地放置在指定的绘制区域内。

 var canvas = e.Surface.Canvas;
 var info = e.Info;
 canvas.Clear(SKColors.White);

 const string TEXT = "SkiaSharp绘图";

 using (var paint = new SKPaint())
 {
     paint.IsAntialias = true;
     paint.TextSize = 96;
     paint.Typeface = SKTypeface.FromFamilyName("微软雅黑");

     var x = 20F;
     var y = 100F;

     var rect=new SKRect();

     var width = paint.MeasureText(TEXT, ref rect);
     canvas.DrawText(TEXT,x,y, paint);

     paint.IsStroke = true;
     canvas.DrawRect(rect.Left + x, y + rect.Top, rect.Width, rect.Height, paint);

     //修改字体、水平放大1.1倍
     y +=200;
     paint.Typeface = SKTypeface.FromFamilyName("宋体");
     paint.TextScaleX = 1.1F;
     paint.Color = SKColors.Red;
     width = paint.MeasureText(TEXT, ref rect);
     
     canvas.DrawText(TEXT, x, y, paint);
     canvas.DrawRect(rect.Left + x, y + rect.Top, rect.Width, rect.Height, paint);
 }

测量相同文本、两种字体的宽度和边界矩形,并在文字四边绘制边界矩形。

MeasureText
使用场景

  1. 自动换行

    • 根据测量的文本宽度决定何时换行,以适应固定宽度的文本容器。
  2. 对齐和布局

    • 根据测量结果调整文本的位置,实现文本的居中对齐、左对齐或右对齐。
  3. 文本容器调整

    • 根据文本宽度动态调整文本容器的大小或位置,确保文本能够正确显示。

Reset重置

public void Reset ();

重置 SKPaint 对象的所有属性到它们的默认状态。这在绘图过程中特别有用,可以确保在开始绘制新的图形或文本之前,所有的绘图设置都是清空的,从而避免意外影响之前的绘图设置。

Clone 克隆

public SkiaSharp.SKPaint Clone ();

浅拷贝SKPaint对象。

ToFont

public SkiaSharp.SKFont ToFont ();

获取SKPaint对应的SKFont

SetColor

public void SetColor (SkiaSharp.SKColorF color, SkiaSharp.SKColorSpace colorspace);

用于设置 SKPaint 对象的颜色属性,并指定颜色空间。这个方法允许你使用浮点颜色和指定的颜色空间来设置绘制操作的颜色属性。
浮点数范围(从0到1)表示RGBA分量。

其它待补充

MaskFilter 掩码过滤器

用于在绘制时应用掩码过滤效果。掩码过滤器可以对绘制的内容进行模糊处理、锐化处理、浮雕效果等特殊效果的处理。通过使用SKPaint.MaskFilter,你可以改变绘制对象的外观,增加视觉效果。

PathEffect 路径效果

用于对路径进行特殊效果处理。路径效果可以对绘制的路径进行各种转换和修改,比如虚线效果、路径的偏移效果、路径变换等。通过使用SKPaint.PathEffect,你可以增加绘制路径的复杂性和美观度。

Shader 着色器

用于创建和应用复杂的填充效果。着色器(Shader)可以用于填充形状和路径,产生渐变、图案填充、图像纹理等效果。通过使用SKPaint.Shader,你可以为绘制的内容添加丰富的视觉效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1848121.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python100题(含答案)

温馨提示&#xff1a;篇幅有限&#xff0c;已打包文件夹&#xff0c;获取方式在&#xff1a;文末 温馨提示&#xff1a;篇幅有限&#xff0c;已打包文件夹&#xff0c;获取方式在&#xff1a;文末 篇幅有限&#xff0c;这份完整版的Python100题已经上传&#xff0c;朋友们如果…

Python xlwt库:写excel表格

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

Linux开发讲课8--- linux的5种IO模型

一、这里IO是什么 操作系统为了保护自己&#xff0c;设计了用户态、内核态两个状态。应用程序一般工作在用户态&#xff0c;当调用一些底层操作的时候&#xff08;比如 IO 操作&#xff09;&#xff0c;就需要切换到内核态才可以进行 服务器从网络接收的大致流程如下&#xff1…

Excel条件格式的经典用法

目录&#xff1a; 一、自动设置填充颜色 二、设置Excel到期自动销毁 三、隔行自动标记 四、美化表格 五、快速突出显示重复值 六、标记空单元格 七、突出显示前N名单元格数值 八、表格添加新内容自动加边框 一、自动设置填充颜色 1、选择内容 首先我们选中表格的数据…

SEGGER Embedded Studio IDE移植embOS

SEGGER Embedded Studio IDE移植embOS 一、背景介绍二、任务目标三、技术实现3.1 获得embOS3.2 创建SES工程3.2.1 创建初始Solution和Project3.2.2 制作项目文件结构3.2.3 移植embOS库和有关头文件3.2.3.1 头文件3.2.3.2 库文件3.2.3.3 创建RTOSInit.c源文件3.2.3.4 OS_Error.c…

第1章 MySQL数据库概述

1.1 基本概念 数据库是什么&#xff1f; 存储数据的地方 DB&#xff1a;数据库&#xff08;Database&#xff09; 为什么要用数据库&#xff1f; 因为应用程序产生的数据是在内存中的&#xff0c;如果程序退出或者是断电了&#xff0c;则数据就会消失。使用数据库是为了…

【中学教资科目二】02中学课程

02中学课程 第一节 课程概述1.1 课程的分类 第二节 课程组织2.1 课程内容的文本表现形式2.2 课程评价 第三节 基础教育课程改革3.1 基础教育改革的目标3.2 新课改的课程结构 第一节 课程概述 1.1 课程的分类 学校课程有多种类型&#xff0c;其中最利于学生系统掌握人类所取得的…

收费4980的AI批量混剪,素材技术方法工具配套,详细拆解!

前几天有朋友跟我讲&#xff0c;他说有做旅游卡的&#xff0c;他们收费4980元&#xff0c;给500张卡&#xff0c;送AI批量混剪技术&#xff0c;问我们有没有&#xff1f; 批量混剪技术&#xff0c;这个其他早在2022年的时候我们就已经使用了。有开通抖音企业号的朋友都知道&am…

HarmonyOS角落里的知识—Stage模型应用程序

开发态包结构 在DevEco Studio上创建一个项目工程&#xff0c;并尝试创建多个不同类型的Module。根据实际工程中的目录对照本章节进行学习&#xff0c;可以有助于理解开发态的应用程序结构。 图1 项目工程结构示意图&#xff08;以实际为准&#xff09; 工程结构主要包含的文…

舒适佩戴,享受沉浸式音乐体验,西圣AVA2耳机体验

平时不管是听音乐&#xff0c;还是打电话&#xff0c;戴上一副耳机都可以让我们获得更好的隐私性&#xff0c;并且在公共场所&#xff0c;比如办公室、车厢里&#xff0c;也可以获得属于自己的空间。现在市面上耳机的选择非常多&#xff0c;音质、续航和佩戴的舒适度是我们选择…

2004年-2022年 全国31省市场分割指数数据

市场分割指数在经济学领域是一个关键的概念&#xff0c;特别是在评估不同区域市场一体化水平时。陆铭等学者深入研究了市场分割问题&#xff0c;并对市场分割指数给出了定义&#xff1a;它是一个衡量在相同时间点不同区域或同一区域在不同时间点的某类商品相对价格差异的指标。…

homework 2024.06.17 math, UI

A的宽度225 B的宽度150 这样画出来就比较标准&#xff0c; 225 * 2 150 * 3 2A 3B

技术驱动会展:展位导航系统的架构与实现

随着会展行业的快速发展&#xff0c;大型会展中心面临着如何提升参展者体验、提高招商效率的挑战。针对客户反馈的展馆面积大、展位查找困难等问题&#xff0c;维小帮提出一套智慧会展导航解决方案&#xff0c;旨在通过先进的室内导航技术提升会展中心的运营效率和参展者的满意…

驱动芯片退饱和保护(DESAT)

短路测试和双脉冲测试。 功率模块的短路承受能力的评估分为短路时间评估和短路能量评估两大类。短路时间由短路检测时间与短路关断时间共同构成 短路检测需要兼顾时效性与抗扰性能&#xff0c;要求系统能够及时响应&#xff0c;避免功率模块损坏。同时能够屏蔽开关过程的干扰…

JAVA每日总结day6.21

ok了家人们&#xff0c;今天我们学习了面向对象中关键字的使用和抽象类&#xff0c;话不多说&#xff0c;我们一起看看吧&#xff0c;&#xff08;今天终于星期五了&#xff0c;芜湖&#xff01;&#xff01;&#xff01;&#xff09; 一&#xff0c;this和super关键字 1&…

【C++ | 重载运算符】一文弄懂C++运算符重载,怎样声明、定义运算符,重载为友元函数

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a;2024-06-21 2…

从艳彩山水到艳彩艺术 薛永年:郭泰来艳彩艺术填补了中国美术史的空白

薛永年先生 自6月12日开展以来&#xff0c;郭泰来现代艺术大展杭州如火如荼地进行着&#xff0c;吸引了众多艺术爱好者和专业人士前往。毫不夸张地说&#xff0c;总统和清洁工人都能在他的作品中找到自己心中的那一块共振带并与之产生强烈的共鸣&#xff0c;这便是郭泰来先生的…

如何禁止学生开启windows防火墙

信息课上&#xff0c;学生最喜欢开启windows防火墙来脱离电子教室的控制&#xff0c;如何禁止学生开启Windows防火墙&#xff0c;可以从以下几个方面入手&#xff1a; 一、使用组策略编辑器 打开组策略编辑器&#xff1a;按下WinR键&#xff0c;输入gpedit.msc并回车&#xf…

眼在手上标定结果应用:像素坐标转机械臂世界坐标

像素坐标转世界坐标主要用到如下的公式&#xff1a;其中boar2camera矩阵可由通过拍摄的标定板图片直接求解&#xff0c;为相机内参矩阵 camera_matrix: rows: 3 cols: 3 data: [428.3066849046146, 0, 675.2344606795484, 0, 431.0838735333736, 405.3373367752419, 0, 0, 1] …

cron.timezone

系统 date 数据库 show timezone插件 show cron.timezonealter system set cron.timezonePRC;show cron.timezone