今天介紹的是實際工作中最常用到的著色器:漸變色著色器。
漸變色著色器是一個從一種顏色平滑的過渡到另一種顏色的效果,漸變色著色器的作用主要是增強圖形的視覺吸引力。
線性漸變
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 對象設置的,可以用來繪制任何幾何圖形,
注釋的代碼就是使用這個漸變色繪制圓形和路徑的示例代碼,運行結果如下圖所示:
?
素材: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
方法相同。
程序運行后的結果如下圖所示:
從上圖中可以看出,窗口邊緣的顏色為紅色,這是因為設置了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
方法負責創建一個錐型漸變。這個方法接收兩個圓作為參數。
第一個參數為第一個圓的圓心,第二個參數為第一個圓的半徑。
第二個參數為第二個圓的圓心,第三個參數為第二個圓的半徑,其他參數與制作線性漸變顏色相同。
程序運行結果如下圖所示:
旋轉漸變
旋轉漸變把一系列顏色平均分布到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
方法負責創建旋轉漸變。
此方法前兩個參數為旋轉漸變的圓心,其他參數與制作線性漸變色相同。
為了讓開始顏色和結束顏色更平滑的過度,我把開始顏色和結束顏色設置成了同一個顏色。
?
程序運行結果如下圖所示:
綜合示例:繪制圓柱體
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);
}
在這段代碼中,分兩個步驟來繪制這個圓柱體:
- 使用路徑繪制圓柱體的?
Body
圓柱體的Body
是由一個圓弧和一部分矩形組成的,繪制這個路徑時,使用了一個從左到右的線性漸變色
,左側顏色較深,右側顏色較淺。
這個路徑繪制完成之后,得到的結果如下圖所示:
- 2,使用橢圓繪制圓柱體的頂面
圓柱體的頂面是一個橢圓,橢圓的橫向中軸線就是圓柱體的Body
的頂邊。
同樣也是使用一個線性漸變色繪制這個橢圓,橢圓的左下角顏色較淺,右上角顏色較深。
橢圓繪制完成之后,圓柱體的Body的頂邊就被這個橢圓蓋住了。
最終的繪制結果如下圖所示:
在這個示例中,我們使用 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 度,順時針從綠色漸變到黑色。
這兩個角度之差大于圓錐頂角值,超出圓錐(扇形)表面的顏色將不會被繪制到窗口中。
程序運行后,繪制結果如下圖所示:
?