Skia 是围绕 SkCanvas
对象组织的。它是“绘制”方法的宿主对象,如它有 drawRect
,drawPath
,drawText
等绘制方法。这些方法都需要传入两个参数:正在绘制的图元(SkRect
,SkPath
等)和颜色/样式属性(SkPaint
)。
canvas->drawRect(rect, paint);
画笔 paint
对象指示图元如何被绘制(如上例中,指的是矩形):矩形是什么颜色,是填充(filled
)或描边(stroked
)的,应该如何与先前绘制的颜色融合(blend
)。
画布 canvas
是 Skia 绘制的上下文, 相对描述较少信息。它指向要绘制的实际像素,并且维护一堆矩阵(matrice
)和剪辑(clip
)。因此,在上述调用中,画布的当前矩阵可以变换矩形的坐标(平移translation
,旋转rotation
,倾斜skewing
,透视perspective
),并且画布的当前剪辑可以限制将在画布上绘制矩形的位置,但是所有其他样式绘图的属性由 paint
控制。
示例: 以下方法绘制一个实心的七角形
void draw(SkCanvas* canvas) {
const SkScalar scale = 256.0f; // SkScalar 其实是 float 类型
const SkScalar R = 0.45f * scale;
const SkScalar TAU = 6.2831853f;
SkPath path;
path.moveTo(R, 0.0f);
for (int i = 1; i < 7; ++i) {
SkScalar theta = 3 * i * TAU / 7;
path.lineTo(R * cos(theta), R * sin(theta));
}
path.close();
SkPaint p;
p.setAntiAlias(true);
canvas->clear(SK_ColorWHITE);
canvas->translate(0.5f * scale, 0.5f * scale);
canvas->drawPath(path, p);
}
示例: 以下方法绘制一个旋转45度的正方形
void draw(SkCanvas* canvas) {
canvas->save();
canvas->translate(SkIntToScalar(128), SkIntToScalar(128));
canvas->rotate(SkIntToScalar(45));
SkRect rect = SkRect::MakeXYWH(-90.5f, -90.5f, 181.0f, 181.0f);
SkPaint paint;
paint.setColor(SK_ColorBLUE);
canvas->drawRect(rect, paint);
canvas->restore();
}
注意以上示例中, 绘制方法传入图元和 paint
两个参数. Skia 中一般都是这种模式来绘画.
有时我们绘制之前, 希望清空画布, 可以在它上面画一个巨大的矩形达到效果. 但其实有更简单的做法:
void draw(SkCanvas* canvas) {
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas->drawPaint(paint);
}
这会使用当前 paint
的设置来绘制整个 canvas
. 如果仅仅是绘制一个颜色, 则有更简单的方法, 甚至不用创建 paint
对象.
void draw(SkCanvas* canvas) {
canvas->drawColor(SK_ColorWHITE);
}
一个综合性的示例:
void draw(SkCanvas* canvas) {
canvas->drawColor(SK_ColorWHITE);
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(4);
paint.setColor(SK_ColorRED);
SkRect rect = SkRect::MakeXYWH(50, 50, 40, 60);
canvas->drawRect(rect, paint);
SkRRect oval;
oval.setOval(rect);
oval.offset(40, 60);
paint.setColor(SK_ColorBLUE);
canvas->drawRRect(oval, paint);
paint.setColor(SK_ColorCYAN);
canvas->drawCircle(180, 50, 25, paint);
rect.offset(80, 0);
paint.setColor(SK_ColorYELLOW);
canvas->drawRoundRect(rect, 10, 10, paint);
SkPath path;
path.cubicTo(768, 0, -512, 256, 256, 256);
paint.setColor(SK_ColorGREEN);
canvas->drawPath(path, paint);
canvas->drawImage(image, 128, 128, &paint);
SkRect rect2 = SkRect::MakeXYWH(0, 0, 40, 60);
canvas->drawImageRect(image, rect2, &paint);
SkPaint paint2;
auto text = SkTextBlob::MakeFromString("Hello, Skia!", SkFont(nullptr, 18));
canvas->drawTextBlob(text.get(), 50, 25, paint2);
}
注意: 这条线的宽度是同时向两边进行扩展的,例如绘制一个圆时,将其宽度设置为 120 则会向外扩展 60 ,向内缩进 60,如下图所示。
在画笔宽度为 0 的情况下,使用 drawLine 或者使用描边模式(STROKE)也可以绘制出内容。只是绘制出的内容始终是 1 像素,不受画布缩放的影响。该模式被称为hairline mode (发际线模式)。
这里的画笔模式(Paint.Style)就是指绘制一个图形时,是绘制边缘轮廓,绘制内容区域还是两者都绘制,它有三种模式。
//填充
mPaint.setStyle(Paint.Style.FILL);
// 描边
mPaint.setStyle(Paint.Style.STROKE);
// 描边+填充
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);