.Net Core使用SkiaSharp绘制小程序分享图

一、前言

  最近因业务需要开发了一款地产类微信小程序,开发的过程中需要实现分享给好友和分享到朋友圈的功能。好友分享可以直接调用微信小程序API来实现,调用时需提供一张5:4的图片,如果不提供则使用默认截图,为了更好的效果我们一般会选择自行生成图片。而分享到朋友圈的功能由于小程序并未开放,则需要我们生成一张长图供用户自行发送朋友圈。下面我以生成好友好享图片为例来讲讲如何在.Net Core环境中使用SkiaSharp绘制图片。

 

(图1:最终效果图)

 

二、SkiaSharp类库介绍

  SkiaSharp是一个基于Google的Skia图形库(https://skia.org/)打造的供.Net平台使用的跨平台的2D绘图API类库。它提供一个全面的2D绘图API,能用在移动端、服务端和桌面端呈现图像。

  目前SkiaSharp可供以下平台使用:

  • .NET Standard 1.3
  • .NET Core
  • Tizen
  • Xamarin.Android
  • Xamarin.iOS
  • Xamarin.tvOS
  • Xamarin.watchOS
  • Xamarin.Mac
  • Windows Classic Desktop (Windows.Forms / WPF)
  • Windows UWP (Desktop / Mobile / Xbox / HoloLens)

  github地址:https://github.com/mono/SkiaSharp

三、准备工作

  1、新建一个.Net Core API项目,这里我命名为ShareImage。

  2、安装SkiaSharp,使用NuGet命令或者包管理器安装:

nuget install SkiaSharp

  3、准备底图

  分析最终效果图(图1),找出不会变化的部分,让UI抠下来作为底图。可以将底图保存在项目的Images目录下,我这里存为bg.png。

(图2:底图)

  4、准备字体

  我这里中文部分用了PingFangSC Regular字体,数字部分用了PingFangSC Medium字体。在项目根目录下新建Fonts目录,复制这两种字体,我这里分别命名为PIngfangScRegular.ttf和PingfangScMedium.ttf。由于载入字体是一个耗时的操作,因此我们可以定义一个静态类TypeFace,预先将字体载入。

using SkiaSharp;

namespace ShareImage
{
    public static class Typeface
    {
        public static TypefaseWithMuiltWeight PingFang = new TypefaseWithMuiltWeight
        {
            Regular = SKTypeface.FromFile("Fonts/PIngfangScRegular.ttf"),
            Medium = SKTypeface.FromFile("Fonts/PingfangScMedium.ttf")
        };      
    }


    public class TypefaseWithMuiltWeight
    {
        public SKTypeface Regular { get; set; }
        public SKTypeface Medium { get; set; }
    }
}

四、绘图

  1、读取要写入的数据,一般这类数据从数据库读取,这里我直接定义示例数据

//示例数据
var shop = new ShopInfo
{
    Name = "李学雯",
    Company = "某某地产",
    ViewNum = "1024",
    Nums = new string[] { "128", "64", "96", "48" },
    Location = "广州市-黄埔区",
    Des = "专业为大宗资产提供中介、法律、会计服务,买卖厂房、仓库、土地请找我们为您提供更专业更安心的服务"
};

  2、载入底图并初始化画布

//载入底图
var bmp = SKBitmap.Decode("Images/cardShopBg.png");
//初始化画布
var canvas = new SKCanvas(bmp);

  3、画头像

//保存当前画布状态,即正常全图绘制状态
canvas.Save();
//定义圆形头像路径
var avatarPath = new SKPath();
avatarPath.AddCircle(114, 120, 70);
//在当前画布上剪切出头像路径,作为当前绘制区域
canvas.ClipPath(avatarPath, SKClipOperation.Intersect, true);
//载入头像图片,这里用Images/avatar0.jpg文件代替
using (var avatarImage = SKImage.FromEncodedData(SKData.Create("Images/avatar0.jpg")))
{
    //定义绘制头像的位置和尺寸
    var rect = SKRect.Create(44, 50, 140, 140);
    //绘制头像
    canvas.DrawImage(avatarImage, rect);
}
//恢复画布状态为全图绘制状态
canvas.Restore();

  由于头像是圆形,而我们获取到的头像一般是方形,所以我们需要在底图上定义一个圆形区域作为本次画图的区域,在绘制完成后需要恢复为全图绘制状态,否则就无法继续接下来的写字等操作了。

  4、写单行文本(以姓名为例)

//定义画笔
using (var paint = new SKPaint())
{
    //字体
    paint.Typeface = Typeface.PingFang.Regular;
    //字体大小
    paint.TextSize = 48;
    //字体颜色
    paint.Color = SKColors.White;
    //抗锯齿不能少
    paint.IsAntialias = true;
    //调用画单行文字扩展方法
    canvas.DrawSingleLineText(shop.Name, 225, 62, paint);
}

//画单行文字扩展方法
public static void DrawSingleLineText(this SKCanvas canvas,string text,float x,float y,SKPaint paint)
{
    //定义一个矩形,此矩形为计算文字区域的结果
    var tRect = new SKRect();
    //计算文字占用区域
    paint.MeasureText(text, ref tRect);
    //调用画布的画字方法
    canvas.DrawText(text, x - tRect.Left, y - tRect.Top, paint);
}

   写文字的时候需要注意有一个坐标编移量,可以直接调用canvas.DrawText(text, 0, 0, paint)来观察一下,写出文字的起点并非是0,0。所以我们在写出文字的时候要算出这个偏移量并抵消掉。这个偏移量是如何产生的本人并不知道,如果有热心网友能帮忙解答一下就好了!

  5、画带色矩形背景框(见图1公司名“某某地产”黄色矩形背景)

//定义画笔
using (var paint = new SKPaint())
{
    //字体
    paint.Typeface = Typeface.PingFang.Regular;
    //字体大小
    paint.TextSize = 26;
    //抗锯齿
    paint.IsAntialias = true;    
    //颜色
    paint.Color = SKColor.Parse("#FFB33E");

    //由于这个黄色背景矩形长度是根据文字长度而定的,所以我们需要计算文字的区域大小
    var tRect = new SKRect();
    //文字长度
    var tWidth = paint.MeasureText(shop.Company,ref tRect);
    //矩形背景区域
    var rect = SKRect.Create(224, 134, tWidth + 20, 40);
    //画矩形
    canvas.DrawRect(rect, paint);
}

  6、写多行文字。由于本人并未在SkiaSharp文档中找到写多行文字的方法,所以写一个扩展方法来写出多行文字:

/// </summary>
/// <param name="canvas">画布</param>
/// <param name="text">多行文字</param>
/// <param name="x">x坐标</param>
/// <param name="y">y坐标</param>
/// <param name="paint">画笔</param>
/// <param name="maxWidth">单行文字最大宽度</param>
/// <param name="maxLines">最大行数,如果超出,则使用...</param>
public static void DrawMuiltLineText(this SKCanvas canvas, string text, float x, float y, SKPaint paint, float maxWidth, int maxLines)
{
    //已写出行数
    int lines = 0;
    //省略号宽度
    var ellipseWidth = paint.MeasureText("...");
    //如果文字没写完且已写行数小于最大行数,则继续写出
    while (!string.IsNullOrEmpty(text) && lines < maxLines)
    {
        //单行文字
        string lineStr;
        //单行文字长度
        long strLength;
        //非最后一行
        if (lines != maxLines - 1)
        {
            //调用BreakText方法,可计算指定宽度能写出多少文字
            strLength = paint.BreakText(text, maxWidth, out float ww, out lineStr);
        }
        //最后一行
        else
        {
            //调用BreakText方法计算单行文字长度,这里需减去省略号宽度
            strLength = paint.BreakText(text, maxWidth - ellipseWidth, out float ww, out lineStr);
            //假如字还没写完,需加省略号
            if (strLength < text.Length)
            {
                lineStr += "...";
            }
        }
        //文字矩形
        var tRect = new SKRect();
        //计算文字矩形
        paint.MeasureText(lineStr, ref tRect);
        //计算坐标
        var tPoint = new SKPoint
        {
            X = x - tRect.Left,
            //这里注意Y坐标需要加上行高
            Y = y + lines * paint.FontSpacing - tRect.Top
        };
        //写出一行文字
        canvas.DrawText(lineStr, tPoint, paint);
        //已写出行数加1
        lines++;
        //取剩余文字
        text  = text.Substring((int)strLength);
    }
}

五、总结

  SkiaSharp是一个非常强大且易用的.Net平台绘图库,这里以绘制小程序分享图为例仅列出一些基础用法。由于本人水平有限,以上代码如有纰漏、错误或有待优化之处,欢迎大家指出!

  附SkiaSharp文档地址:https://docs.microsoft.com/en-us/dotnet/api/SkiaSharp

 

posted @ 2018-12-09 11:28  Intouchables  阅读(5313)  评论(0编辑  收藏  举报