Skia---渐变色着色器

今天介绍的是实际工作中最常用到的着色器:渐变色着色器。

渐变色着色器是一个从一种颜色平滑的过渡到另一种颜色的效果,渐变色着色器的作用主要是增强图形的视觉吸引力。

线性渐变

Skia 里的线性渐变色着色器是最简单的渐变色着色器,它用于在 2D 图形中创建从一点到另一点平滑过渡的颜色效果。它通过在起点和终点之间进行线性插值,实现了两种或多种颜色之间的平滑渐变。

现在来看一下如何使用线性渐变色着色器来绘制窗口背景,如下代码所示:

// #include "include/effects/SkGradientShader.h"

void drawLinearGradientColor(SkCanvas *canvas)
{
    SkPaint paint;
    paint.setAntiAlias(true);
    SkPoint pts[2]{SkPoint::Make(0, 0), SkPoint::Make(w, h)};
    SkColor colors[6]{ 0xFF00FFFF, 0xFFFF00FF, 0xFFFFFF00,0xFF0000FF,0xFF00FF00,0xFFFF0000 };
    sk_sp<SkShader> shader = SkGradientShader::MakeLinear(pts, colors, nullptr, 6, SkTileMode::kClamp);
    paint.setShader(shader);
    canvas->drawPaint(paint);

    //用线性渐变色绘制圆形
    //auto x = w / 2;
    //auto y = h / 2;
    //auto r = std::min(x - 60, y - 60);
    //canvas->drawCircle(x, y, r, paint);

    //用线性渐变色绘制路径
    //paint.setStroke(true);
    //paint.setStrokeWidth(16);
    //paint.setStrokeJoin(SkPaint::kRound_Join);
    //SkPath path;
    //path.moveTo(60, 120);
    //path.lineTo(180, 60);
    //path.lineTo(w - 60, 120);
    //path.lineTo(w - 160, h - 160);
    //path.lineTo(180, h - 60);
    //path.close();
    //canvas->drawPath(path, paint);
}

在这段代码中使用 SkGradientShader 类型的静态方法 MakeLinear 创建了一个线性渐变着色器。

MakeLinear 方法的第一个参数是一个只有两个 SkPoint 元素的数组,数组内存储的两个点为线性渐变的起点终点

第二个参数是一个颜色数组,这个数组中存储的颜色将被分布在线性渐变的起点和终点之间。

第三个参数为一个 SkPoint 数组,这个数组用于控制各个颜色的位置,如果不传递这个参数(nullptr),那么颜色将被均匀分布。

第四个参数为第二个数组参数和第三个数组参数的元素数量。

第五个参数 SkTileMode::kClamp 表示如果着色器在起点和终点之外绘制,则超出的区域使用 colors 首尾两端的颜色进行绘制。

除此之外 SkTileMode 还有一些其他的枚举项,这里就不一一介绍了。

MakeLinear方法返回一个 SkShader 类型的对象,这个对象可以被设置到 SkPaint 对象中。

SkPaint设置了shader之后,它设置的Color就不再生效,SkPaint优先使用shader

程序运行后的结果如下图所示:

由于渐变色是在 SkPaint 对象设置的,可以用来绘制任何几何图形,

注释的代码就是使用这个渐变色绘制圆形和路径的示例代码,运行结果如下图所示:

线性渐变2.png

 

素材:WebGradients 是一个提供了180个线性渐变色的免费网站。 

径向渐变

径向渐变允许颜色从中心点向四周以圆形扩散,绘制径向渐变颜色与绘制线性渐变颜色非常相似,代码如下所示:

void drawRadialGradientColor(SkCanvas *canvas)
{
    SkPaint paint;
    paint.setAntiAlias(true);
    SkColor colors[6]{ 0xFF00FFFF, 0xFFFF00FF, 0xFFFFFF00,0xFF0000FF,0xFF00FF00,0xFFFF0000 };
    auto x = w / 2;
    auto y = h / 2;
    auto r = std::min(x - 10, y - 10);
    auto shader = SkGradientShader::MakeRadial(SkPoint::Make(x, y), r, colors, nullptr, 6, SkTileMode::kClamp);
    paint.setShader(shader);
    canvas->drawPaint(paint);
}

使用SkGradientShader类型的MakeRadial方法创建径向渐变,

这个方法的第一个参数是径向渐变的圆心,第二个参数是径向渐变的半径,其他参数与线性渐变SkGradientShader::MakeLinear方法相同。

程序运行后的结果如下图所示:

径向渐变.png

从上图中可以看出,窗口边缘的颜色为红色,这是因为设置了SkTileMode::kClamp,所以超出的区域使用colors数组首尾两端的颜色进行绘制。

锥形渐变

锥型渐变允许颜色从一个中心点向四周以锥形的方式扩散渐变,这种渐变色常用于绘制光晕或放射效果。如下代码所示:

void drawConicalGradientColor(SkCanvas* canvas)
{
    SkPaint paint;
    auto x = w / 2;
    auto y = h / 2;
    SkColor colors[6]{ 0xFF00FFFF, 0xFFFFFF66, 0xFFFF00FF, 0xFF66FFFF, 0xFFFFFF00, 0xFFFF66FF };
    auto shader = SkGradientShader::MakeTwoPointConical(SkPoint::Make(x, y), y, SkPoint::Make(x, 60.0f), 20.0f,
        colors, nullptr, 6, SkTileMode::kClamp);
    paint.setShader(shader);
    canvas->drawPaint(paint);
}

SkGradientShader::MakeTwoPointConical方法负责创建一个锥型渐变。这个方法接收两个圆作为参数。

第一个参数为第一个圆的圆心,第二个参数为第一个圆的半径。

第二个参数为第二个圆的圆心,第三个参数为第二个圆的半径,其他参数与制作线性渐变颜色相同。

程序运行结果如下图所示:

锥型渐变.png

旋转渐变

旋转渐变把一系列颜色平均分布到360°的角度范围内(默认情况下以0°开始,以360°结束),如下代码所示:

void drawSweepGradientColor(SkCanvas* canvas)
{
    SkPaint paint;
    auto x = w / 2;
    auto y = h / 2;
    SkColor colors[7]{ 0xFF00FFFF, 0xFFFF00FF, 0xFFFFFF00,0xFF0000FF,0xFF00FF00,0xFFFF0000,0xFF00FFFF };
    auto shader = SkGradientShader::MakeSweep(x, y, colors, nullptr, 7, 0, nullptr);
    paint.setShader(shader);
    canvas->drawPaint(paint);
}

SkGradientShader::MakeSweep方法负责创建旋转渐变。

此方法前两个参数为旋转渐变的圆心,其他参数与制作线性渐变色相同。

为了让开始颜色和结束颜色更平滑的过度,我把开始颜色和结束颜色设置成了同一个颜色。

 

程序运行结果如下图所示:

旋转渐变.png

综合示例:绘制圆柱体

Skia里的几何图形、路径、颜色和渐变色着色器,运用这些知识绘制一个圆柱体,如下代码所示:

void drawCylinder(SkCanvas* canvas) {

    int bodyW{ 160 }, top{60};
    int x1{ w / 2 - bodyW / 2 }, x2{ w / 2 + bodyW / 2 };

    SkPath path;
    SkRect rect;
    rect.setXYWH(x1, h - 80, bodyW, 40);
    path.arcTo(rect, 0, 180,true);
    path.lineTo(x1, top+20);
    path.lineTo(x2, top+20);
    path.close();

    SkPaint paint;
    paint.setAntiAlias(true);

    SkPoint pts[2]{ SkPoint::Make(x1, top), SkPoint::Make(x2, top) };
    SkColor colors[2]{ 0xFF287191, 0xFF85B5CB };
    sk_sp<SkShader> shader = SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp);
    paint.setShader(shader);
    canvas->drawPath(path, paint);

    rect.setXYWH(x1, top, bodyW, 40);
    colors[0] = 0xffBDE4F8;
    colors[1] = 0xffA7CFE6;
    pts[0] = SkPoint::Make(x1, top + 40);
    pts[1] = SkPoint::Make(x2, top);
    shader = SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp);
    paint.setShader(shader);
    canvas->drawOval(rect, paint);    
}

在这段代码中,分两个步骤来绘制这个圆柱体:

  1. 使用路径绘制圆柱体的 Body

圆柱体的Body是由一个圆弧和一部分矩形组成的,绘制这个路径时,使用了一个从左到右的线性渐变色,左侧颜色较深,右侧颜色较浅。

这个路径绘制完成之后,得到的结果如下图所示:

圆柱体0.png

  1. 2,使用椭圆绘制圆柱体的顶面

圆柱体的顶面是一个椭圆,椭圆的横向中轴线就是圆柱体的Body的顶边。

同样也是使用一个线性渐变色绘制这个椭圆,椭圆的左下角颜色较浅,右上角颜色较深。

椭圆绘制完成之后,圆柱体的Body的顶边就被这个椭圆盖住了。

最终的绘制结果如下图所示:

圆柱体.png

在这个示例中,我们使用 2D 图形绘图知识绘制了一个 3D 图形。

要知道无论是 2D 图形引擎还是 3D 图形引擎,最终都是在处理像素,交付给客户的也都是二维平面像素数据(你的显示器只能承载二维平面像素数据)。3D 图形只不过看起来是立体的而已。

所以,2D 图形引擎可以画 3D 图形,3D 图形引擎也可以画 2D 图形,只不过这两个图形引擎的内部实现原理大相径庭。

综合示例:绘制圆锥体

稍稍改动一下圆柱体的绘制代码,就可以绘制圆锥体了,如下代码所示:

void drawCone(SkCanvas* canvas) {

    int bodyW{ 160 }, top{ 60 };
    int x1{ w / 2 - bodyW / 2 }, x2{ w / 2 + bodyW / 2 };
    SkPath path;
    SkRect rect;
    rect.setXYWH(x1, h - 80, bodyW, 40);
    path.arcTo(rect, 0, 180, true);
    path.lineTo(x1+bodyW/2, top + 20);
    path.close();
    SkPaint paint;
    paint.setAntiAlias(true);
    SkColor colors[2]{ 0xFF00FF00, 0xFF000000 };
    auto shader = SkGradientShader::MakeSweep(w/2, top + 20, colors, nullptr,2,SkTileMode::kClamp, 50.0, 130.0, 0,nullptr);
    paint.setShader(shader);
    canvas->drawPath(path, paint);
}

 

这段代码去除了圆柱体的顶面和圆柱体 Body 的顶边,在绘制路径时,使用了旋转渐变颜色。

旋转渐变的起始角度为 50 度,结束角度为 130 度,顺时针从绿色渐变到黑色。

这两个角度之差大于圆锥顶角值,超出圆锥(扇形)表面的颜色将不会被绘制到窗口中。

程序运行后,绘制结果如下图所示:

圆锥.png

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liulun

如果文章真帮到了你,谢谢您打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值