应用程序(网页、桌面应用或移动应用)大多数都是由基本的几何图形构成的。那我们该如何使用 Skia 绘制基本的几何图形。
画矩形
void drawRect(SkCanvas* canvas) {
SkPaint paint;
paint.setColor(SK_ColorRED);
paint.setStroke(true);
paint.setStrokeWidth(16);
SkRect rect = SkRect::MakeLTRB(w / 2 - 100, h / 2 - 100, w / 2 + 100, h / 2 + 100);
bool isPointInRect = rect.contains(w / 2, h / 2);
canvas->drawRect(rect, paint);
}
void paint(const HWND hWnd)
{
//...省略了一些代码(前一章介绍过)
drawRect(canvas.get());
//...省略了一些代码(前一章介绍过)
}
drawRect 方法负责在窗口中绘制矩形。 窗口重绘时执行 paint 方法,在此方法中,把画布指针传递给 drawRect 方法,让 drawRect方法在窗口中绘制矩形。
非填充绘图对象。 默认情况下绘图对象 SkPaint 为填充状态,当执行 paint.setStroke(true) 代码后,绘图对象就变成了非填充状态。 绘图对象的 setStrokeWidth 方法,用于设置边框粗细。
用另一种方式创建矩形对象。 使用 SkRect 类型的静态方法 MakeXYWH 创建矩形对象。 除了这种方式设置矩形外,还可以用 SkRect::MakeLTRB 方法创建矩形。
setLTRB 方法的四个参数分别是矩形的 left(左)、top(上)、right(右)、bottom(下)四个位置。 值得注意的是此处设置的四个值,left的值不必小于right的值,top的值也不必小于bottom的值,setLTRB方法内部会安排好这些值。 此处创建的矩形(长和宽均为200)位于窗口的中心。
判断一个点是否位于矩形内部 使用矩形的contains方法来判断一个点是否在矩形中。 很多 Skia 内置的几何元素都有这个方法,比如圆,椭圆,闭合得路径等,它对于一些交互应用程序、游戏程序非常重要。
运行程序得到的结果如下图所示:
画点
使用 Skia 画一个点非常简单,只要给出点的坐标即可,如下代码所示:
void drawPoint(SkCanvas* canvas)
{
SkPaint paint;
paint.setColor(SK_ColorRED);
paint.setStrokeWidth(16);
canvas->drawPoint(w/2,h/2, paint);
}
SkCanvas 对象的 drawPoint 方法负责绘制点,这个方法的前两个参数就是点的x和y坐标。
值得注意的是 SkPaint 的 setStrokeWidth 方法同样可以应用到 点 上。
把 StrokeWidth 设置成 16 ,这样画的点就大了。
上述程序运行结果如下图所示:
要绘制一系列的点,可以让 SkCanvas 对象绘制一个 SkPoint
数组,如下代码所示:
// #include "include/core/SkPoint.h"
void drawPoint2(SkCanvas* canvas)
{
SkPaint paint;
paint.setColor(SK_ColorRED);
paint.setStrokeWidth(16);
SkPoint pts[]{ SkPoint::Make(60, 60),
SkPoint::Make(w / 2, h / 2),
SkPoint::Make(w - 60, h - 60) };
canvas->drawPoints(SkCanvas::PointMode::kPoints_PointMode, 3, pts, paint);
}
SkPoint 类型用于表示一个点,它持有两个 float 类型的数据分别表示点的 x坐标 和 y坐标。
SkCanvas 对象的 drawPoints 方法的第一个参数为绘制点的方式,
kPoints_PointMode枚举用于表示只是绘制点,用这个参数来控制是否需要用线把点连起来或是否需要把这些点连成一个多边形等,
drawPoints方法的第二个参数为数组内包含几个点,第三个参数为SkPoint数组。
程序的运行结果如下图所示:
画线
画直线也是非常常见的需求,如下代码就是 Skia 绘制直线的示例代码:
void drawLine(SkCanvas* canvas) {
SkPaint paint;
paint.setColor(SK_ColorRED);
paint.setAntiAlias(true);
paint.setStroke(true);
paint.setStrokeWidth(16);
paint.setStrokeCap(SkPaint::Cap::kRound_Cap);
canvas->drawLine(80, 80, w - 80, h - 80, paint);
}
这段代码从窗口的左上角(80,80)处开始,到窗口的右下角(w - 80, h - 80)绘制了一条宽度为16的直线。
通过 paint.setStrokeCap 方法设置了直线两端的样式:让线段两端呈现一个圆帽的形式。
kRound_Cap同样可以应用到画点的工作上,这样做可以让一个方形的点,变成一个圆形的点。
程序运行结果如下图所示:
画圆
前面使用 SKRect 绘制了矩形,接下来我们绘制一个圆,如下代码所示:
void drawCircle(SkCanvas* canvas) {
SkPaint paint;
paint.setColor(SK_ColorRED);
auto x = w / 2;
auto y = h / 2;
auto r = std::min(x-10, y-10);
canvas->drawCircle(x, y, r, paint); //此方法用于绘制正圆
}
其中 x 为圆心的 x 坐标,y 为圆心的 y 坐标,r 为圆的半径。
使圆的半径小于窗口宽度或高度的一半(使圆始终在窗口内)。
运行代码你将在窗口中看到一个红色的圆,如下图所示:
抗锯齿
如果你仔细观察这个圆的边缘,你会发现圆的边缘存在锯齿,如下图所示:
如果你希望消除这些锯齿,让圆的边缘更平滑,那么你就需要设置 paint 对象的抗锯齿属性,如下代码所示:
paint.setAntiAlias(true);
抗锯齿是每一个渲染引擎都要面临的问题和重要任务之一。
设置了抗锯齿之后,再运行代码,圆的边缘就平滑了,如下图所示:
画椭圆
绘制正圆的业务场景比较少,大部分是绘制椭圆,用 Skia 绘制椭圆也非常简单,如下代码所示:
void drawEllipse(SkCanvas* canvas) {
SkPaint paint;
paint.setColor(SK_ColorRED);
paint.setAntiAlias(true);
SkRect rect;
rect.setLTRB(10, 10, w - 10, h - 10); //椭圆与窗口边缘距离10像素
canvas->drawOval(rect, paint); //此方法用于绘制椭圆
}
上面代码 使用一个矩形来控制椭圆的大小和位置 。
当然如果矩形的长宽相等(正方形),那么绘制出来的就是正圆。
运行程序,如下图所示:
画圆角矩形
如果你开发过前端网页,那么你肯定知道圆角矩形是非常常见的网页元素。
Skia 也为绘制圆角矩形提供了特定的 API ,如下代码所示:
#include "include/core/SkRRect.h"
void drawRRect(SkCanvas* canvas) {
SkPaint paint;
paint.setColor(SK_ColorRED);
paint.setAntiAlias(true);
SkRect rect;
rect.setLTRB(60, 60, w -60, h - 60);
SkVector radii[4]{
{16, 16}, // 矩形左上角圆角尺寸;
{16, 16}, // 矩形右上角圆角尺寸;
{16, 16}, // 矩形右下角圆角尺寸;
{16, 16} // 矩形左下角圆角尺寸;
};
SkRRect rr;
rr.setRectRadii(rect, radii);
canvas->drawRRect(rr, paint); //绘制圆角矩形
}
我们用 SKRRect
类型来表示圆角矩形。
SKRRect
是基于一个矩形(SkRect)创建的,而且开发者可以自由的定义每个角的圆角尺寸。
上述代码中 SkVector
数组 radii
中存储的是四个圆角的大小。
SkVector
实际上就是SkPoint
。它并不是一个容器
每个圆角由两个值组成,第一个值是水平方向上的圆角半径
,第二个值是垂直方向上的圆角半径
,一般情况下这两个值是相同的。
上述代码运行后的效果如下图所示:
画圆弧
使用Skia绘制圆弧也非常简单,如下代码所示:
void drawArc(SkCanvas* canvas) {
SkPaint paint;
paint.setColor(SK_ColorRED);
paint.setAntiAlias(true);
paint.setStroke(true);
paint.setStrokeWidth(16);
paint.setStrokeCap(SkPaint::Cap::kRound_Cap);
SkRect rect;
rect.setLTRB(60, 60, w - 60, h - 60);
canvas->drawArc(rect, 0, -90, false, paint);
}
使用 canvas 的 drawArc 方法绘制圆弧,第一个参数是一个矩形(SkRect),
第二个参数是圆弧的起点角度,此处传入 0 ,代表着以矩形右侧边的中心点为起点开始绘制。
第三个参数为圆弧的终点角度,此处传入-90,代表着以矩形的顶边的中点为终点。
也就是说我们以逆时针方向绘制这段圆弧,如果传入的是90 则以顺时针方向绘制圆弧,终点将落在矩形的底边中点。
第四个参数为 是否把起点和终点与椭圆中心连接
,如果这个参数设置为true,那么它就是一个扇形,不再仅仅是一个圆弧了。
运行代码,得到的结果如下图所示:
如何使用 Skia 绘制基本的几何元素:点、线、圆、方、弧,你会了吗?