Skip to content

Instantly share code, notes, and snippets.

@linsea
Last active July 11, 2023 11:31
Show Gist options
  • Save linsea/1054eefbb558079a7242b8f9a7026fbe to your computer and use it in GitHub Desktop.
Save linsea/1054eefbb558079a7242b8f9a7026fbe to your computer and use it in GitHub Desktop.

Skia 2D 基础

https://skia.org/user/api/

Skia 是围绕 SkCanvas 对象组织的。它是“绘制”方法的宿主对象,如它有 drawRectdrawPathdrawText 等绘制方法。这些方法都需要传入两个参数:正在绘制的图元(SkRectSkPath等)和颜色/样式属性(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);
}

image

示例: 以下方法绘制一个旋转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();
}

image

注意以上示例中, 绘制方法传入图元和 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);
}

image

注意: 这条线的宽度是同时向两边进行扩展的,例如绘制一个圆时,将其宽度设置为 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);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment