gradients.cpp 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067
  1. /*
  2. * Copyright 2011 Google Inc.
  3. *
  4. * Use of this source code is governed by a BSD-style license that can be
  5. * found in the LICENSE file.
  6. */
  7. #include "gm/gm.h"
  8. #include "include/core/SkBlendMode.h"
  9. #include "include/core/SkCanvas.h"
  10. #include "include/core/SkColor.h"
  11. #include "include/core/SkColorSpace.h"
  12. #include "include/core/SkMatrix.h"
  13. #include "include/core/SkPaint.h"
  14. #include "include/core/SkPicture.h"
  15. #include "include/core/SkPictureRecorder.h"
  16. #include "include/core/SkPoint.h"
  17. #include "include/core/SkRect.h"
  18. #include "include/core/SkRefCnt.h"
  19. #include "include/core/SkScalar.h"
  20. #include "include/core/SkShader.h"
  21. #include "include/core/SkSize.h"
  22. #include "include/core/SkString.h"
  23. #include "include/core/SkTileMode.h"
  24. #include "include/core/SkTypes.h"
  25. #include "include/effects/SkGradientShader.h"
  26. #include <math.h>
  27. namespace {
  28. struct GradData {
  29. int fCount;
  30. const SkColor* fColors;
  31. const SkColor4f* fColors4f;
  32. const SkScalar* fPos;
  33. };
  34. constexpr SkColor gColors[] = {
  35. SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
  36. };
  37. constexpr SkColor4f gColors4f[] ={
  38. { 1.0f, 0.0f, 0.0f, 1.0f }, // Red
  39. { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
  40. { 0.0f, 0.0f, 1.0f, 1.0f }, // Blue
  41. { 1.0f, 1.0f, 1.0f, 1.0f }, // White
  42. { 0.0f, 0.0f, 0.0f, 1.0f } // Black
  43. };
  44. constexpr SkScalar gPos0[] = { 0, SK_Scalar1 };
  45. constexpr SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
  46. constexpr SkScalar gPos2[] = {
  47. 0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
  48. };
  49. constexpr SkScalar gPosClamp[] = {0.0f, 0.0f, 1.0f, 1.0f};
  50. constexpr SkColor gColorClamp[] = {
  51. SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorBLUE
  52. };
  53. constexpr SkColor4f gColor4fClamp[] ={
  54. { 1.0f, 0.0f, 0.0f, 1.0f }, // Red
  55. { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
  56. { 0.0f, 1.0f, 0.0f, 1.0f }, // Green
  57. { 0.0f, 0.0f, 1.0f, 1.0f } // Blue
  58. };
  59. constexpr GradData gGradData[] = {
  60. { 2, gColors, gColors4f, nullptr },
  61. { 2, gColors, gColors4f, gPos0 },
  62. { 2, gColors, gColors4f, gPos1 },
  63. { 5, gColors, gColors4f, nullptr },
  64. { 5, gColors, gColors4f, gPos2 },
  65. { 4, gColorClamp, gColor4fClamp, gPosClamp }
  66. };
  67. static sk_sp<SkShader> MakeLinear(const SkPoint pts[2], const GradData& data,
  68. SkTileMode tm, const SkMatrix& localMatrix) {
  69. return SkGradientShader::MakeLinear(pts, data.fColors, data.fPos, data.fCount, tm, 0,
  70. &localMatrix);
  71. }
  72. static sk_sp<SkShader> MakeLinear4f(const SkPoint pts[2], const GradData& data,
  73. SkTileMode tm, const SkMatrix& localMatrix) {
  74. auto srgb = SkColorSpace::MakeSRGB();
  75. return SkGradientShader::MakeLinear(pts, data.fColors4f, srgb, data.fPos, data.fCount, tm, 0,
  76. &localMatrix);
  77. }
  78. static sk_sp<SkShader> MakeRadial(const SkPoint pts[2], const GradData& data,
  79. SkTileMode tm, const SkMatrix& localMatrix) {
  80. SkPoint center;
  81. center.set(SkScalarAve(pts[0].fX, pts[1].fX),
  82. SkScalarAve(pts[0].fY, pts[1].fY));
  83. return SkGradientShader::MakeRadial(center, center.fX, data.fColors, data.fPos, data.fCount,
  84. tm, 0, &localMatrix);
  85. }
  86. static sk_sp<SkShader> MakeRadial4f(const SkPoint pts[2], const GradData& data,
  87. SkTileMode tm, const SkMatrix& localMatrix) {
  88. SkPoint center;
  89. center.set(SkScalarAve(pts[0].fX, pts[1].fX),
  90. SkScalarAve(pts[0].fY, pts[1].fY));
  91. auto srgb = SkColorSpace::MakeSRGB();
  92. return SkGradientShader::MakeRadial(center, center.fX, data.fColors4f, srgb, data.fPos,
  93. data.fCount, tm, 0, &localMatrix);
  94. }
  95. static sk_sp<SkShader> MakeSweep(const SkPoint pts[2], const GradData& data,
  96. SkTileMode, const SkMatrix& localMatrix) {
  97. SkPoint center;
  98. center.set(SkScalarAve(pts[0].fX, pts[1].fX),
  99. SkScalarAve(pts[0].fY, pts[1].fY));
  100. return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors, data.fPos, data.fCount,
  101. 0, &localMatrix);
  102. }
  103. static sk_sp<SkShader> MakeSweep4f(const SkPoint pts[2], const GradData& data,
  104. SkTileMode, const SkMatrix& localMatrix) {
  105. SkPoint center;
  106. center.set(SkScalarAve(pts[0].fX, pts[1].fX),
  107. SkScalarAve(pts[0].fY, pts[1].fY));
  108. auto srgb = SkColorSpace::MakeSRGB();
  109. return SkGradientShader::MakeSweep(center.fX, center.fY, data.fColors4f, srgb, data.fPos,
  110. data.fCount, 0, &localMatrix);
  111. }
  112. static sk_sp<SkShader> Make2Radial(const SkPoint pts[2], const GradData& data,
  113. SkTileMode tm, const SkMatrix& localMatrix) {
  114. SkPoint center0, center1;
  115. center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
  116. SkScalarAve(pts[0].fY, pts[1].fY));
  117. center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
  118. SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
  119. return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
  120. center0, (pts[1].fX - pts[0].fX) / 2,
  121. data.fColors, data.fPos, data.fCount, tm,
  122. 0, &localMatrix);
  123. }
  124. static sk_sp<SkShader> Make2Radial4f(const SkPoint pts[2], const GradData& data,
  125. SkTileMode tm, const SkMatrix& localMatrix) {
  126. SkPoint center0, center1;
  127. center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
  128. SkScalarAve(pts[0].fY, pts[1].fY));
  129. center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3) / 5),
  130. SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1) / 4));
  131. auto srgb = SkColorSpace::MakeSRGB();
  132. return SkGradientShader::MakeTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
  133. center0, (pts[1].fX - pts[0].fX) / 2,
  134. data.fColors4f, srgb, data.fPos, data.fCount, tm,
  135. 0, &localMatrix);
  136. }
  137. static sk_sp<SkShader> Make2Conical(const SkPoint pts[2], const GradData& data,
  138. SkTileMode tm, const SkMatrix& localMatrix) {
  139. SkPoint center0, center1;
  140. SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
  141. SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
  142. center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
  143. center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
  144. return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0,
  145. data.fColors, data.fPos,
  146. data.fCount, tm, 0, &localMatrix);
  147. }
  148. static sk_sp<SkShader> Make2Conical4f(const SkPoint pts[2], const GradData& data,
  149. SkTileMode tm, const SkMatrix& localMatrix) {
  150. SkPoint center0, center1;
  151. SkScalar radius0 = (pts[1].fX - pts[0].fX) / 10;
  152. SkScalar radius1 = (pts[1].fX - pts[0].fX) / 3;
  153. center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
  154. center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
  155. auto srgb = SkColorSpace::MakeSRGB();
  156. return SkGradientShader::MakeTwoPointConical(center1, radius1, center0, radius0,
  157. data.fColors4f, srgb, data.fPos,
  158. data.fCount, tm, 0, &localMatrix);
  159. }
  160. typedef sk_sp<SkShader> (*GradMaker)(const SkPoint pts[2], const GradData& data,
  161. SkTileMode tm, const SkMatrix& localMatrix);
  162. constexpr GradMaker gGradMakers[] = {
  163. MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2Conical
  164. };
  165. constexpr GradMaker gGradMakers4f[] ={
  166. MakeLinear4f, MakeRadial4f, MakeSweep4f, Make2Radial4f, Make2Conical4f
  167. };
  168. ///////////////////////////////////////////////////////////////////////////////
  169. class GradientsGM : public skiagm::GM {
  170. public:
  171. GradientsGM(bool dither) : fDither(dither) {}
  172. protected:
  173. const bool fDither;
  174. void onDraw(SkCanvas* canvas) override {
  175. SkPoint pts[2] = {
  176. { 0, 0 },
  177. { SkIntToScalar(100), SkIntToScalar(100) }
  178. };
  179. SkTileMode tm = SkTileMode::kClamp;
  180. SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
  181. SkPaint paint;
  182. paint.setAntiAlias(true);
  183. paint.setDither(fDither);
  184. canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
  185. for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
  186. canvas->save();
  187. for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
  188. SkMatrix scale = SkMatrix::I();
  189. if (i == 5) { // if the clamp case
  190. scale.setScale(0.5f, 0.5f);
  191. scale.postTranslate(25.f, 25.f);
  192. }
  193. paint.setShader(gGradMakers[j](pts, gGradData[i], tm, scale));
  194. canvas->drawRect(r, paint);
  195. canvas->translate(0, SkIntToScalar(120));
  196. }
  197. canvas->restore();
  198. canvas->translate(SkIntToScalar(120), 0);
  199. }
  200. }
  201. private:
  202. void onOnceBeforeDraw() override { this->setBGColor(0xFFDDDDDD); }
  203. SkString onShortName() override {
  204. return SkString(fDither ? "gradients" : "gradients_nodither");
  205. }
  206. SkISize onISize() override { return {840, 815}; }
  207. };
  208. DEF_GM( return new GradientsGM(true); )
  209. DEF_GM( return new GradientsGM(false); )
  210. // Like the original gradients GM, but using the SkColor4f shader factories. Should be identical.
  211. class Gradients4fGM : public skiagm::GM {
  212. public:
  213. Gradients4fGM(bool dither) : fDither(dither) {}
  214. private:
  215. void onOnceBeforeDraw() override { this->setBGColor(0xFFDDDDDD); }
  216. SkString onShortName() override {
  217. return SkString(fDither ? "gradients4f" : "gradients4f_nodither");
  218. }
  219. SkISize onISize() override { return {840, 815}; }
  220. void onDraw(SkCanvas* canvas) override {
  221. SkPoint pts[2] ={
  222. { 0, 0 },
  223. { SkIntToScalar(100), SkIntToScalar(100) }
  224. };
  225. SkTileMode tm = SkTileMode::kClamp;
  226. SkRect r ={ 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
  227. SkPaint paint;
  228. paint.setAntiAlias(true);
  229. paint.setDither(fDither);
  230. canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
  231. for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
  232. canvas->save();
  233. for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers4f); j++) {
  234. SkMatrix scale = SkMatrix::I();
  235. if (i == 5) { // if the clamp case
  236. scale.setScale(0.5f, 0.5f);
  237. scale.postTranslate(25.f, 25.f);
  238. }
  239. paint.setShader(gGradMakers4f[j](pts, gGradData[i], tm, scale));
  240. canvas->drawRect(r, paint);
  241. canvas->translate(0, SkIntToScalar(120));
  242. }
  243. canvas->restore();
  244. canvas->translate(SkIntToScalar(120), 0);
  245. }
  246. }
  247. bool fDither;
  248. };
  249. DEF_GM(return new Gradients4fGM(true); )
  250. DEF_GM(return new Gradients4fGM(false); )
  251. // Based on the original gradient slide, but with perspective applied to the
  252. // gradient shaders' local matrices
  253. class GradientsLocalPerspectiveGM : public skiagm::GM {
  254. public:
  255. GradientsLocalPerspectiveGM(bool dither) : fDither(dither) {
  256. this->setBGColor(0xFFDDDDDD);
  257. }
  258. private:
  259. SkString onShortName() override {
  260. return SkString(fDither ? "gradients_local_perspective" :
  261. "gradients_local_perspective_nodither");
  262. }
  263. SkISize onISize() override { return {840, 815}; }
  264. void onDraw(SkCanvas* canvas) override {
  265. SkPoint pts[2] = {
  266. { 0, 0 },
  267. { SkIntToScalar(100), SkIntToScalar(100) }
  268. };
  269. SkTileMode tm = SkTileMode::kClamp;
  270. SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
  271. SkPaint paint;
  272. paint.setAntiAlias(true);
  273. paint.setDither(fDither);
  274. canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
  275. for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
  276. canvas->save();
  277. for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
  278. // apply an increasing y perspective as we move to the right
  279. SkMatrix perspective;
  280. perspective.setIdentity();
  281. perspective.setPerspY(SkIntToScalar(i+1) / 500);
  282. perspective.setSkewX(SkIntToScalar(i+1) / 10);
  283. paint.setShader(gGradMakers[j](pts, gGradData[i], tm, perspective));
  284. canvas->drawRect(r, paint);
  285. canvas->translate(0, SkIntToScalar(120));
  286. }
  287. canvas->restore();
  288. canvas->translate(SkIntToScalar(120), 0);
  289. }
  290. }
  291. bool fDither;
  292. };
  293. DEF_GM( return new GradientsLocalPerspectiveGM(true); )
  294. DEF_GM( return new GradientsLocalPerspectiveGM(false); )
  295. // Based on the original gradient slide, but with perspective applied to
  296. // the view matrix
  297. class GradientsViewPerspectiveGM : public GradientsGM {
  298. public:
  299. GradientsViewPerspectiveGM(bool dither) : INHERITED(dither) { }
  300. private:
  301. SkString onShortName() override {
  302. return SkString(fDither ? "gradients_view_perspective" :
  303. "gradients_view_perspective_nodither");
  304. }
  305. SkISize onISize() override { return {840, 500}; }
  306. void onDraw(SkCanvas* canvas) override {
  307. SkMatrix perspective;
  308. perspective.setIdentity();
  309. perspective.setPerspY(0.001f);
  310. perspective.setSkewX(SkIntToScalar(8) / 25);
  311. canvas->concat(perspective);
  312. this->INHERITED::onDraw(canvas);
  313. }
  314. private:
  315. typedef GradientsGM INHERITED;
  316. };
  317. DEF_GM( return new GradientsViewPerspectiveGM(true); )
  318. DEF_GM( return new GradientsViewPerspectiveGM(false); )
  319. /*
  320. Inspired by this <canvas> javascript, where we need to detect that we are not
  321. solving a quadratic equation, but must instead solve a linear (since our X^2
  322. coefficient is 0)
  323. ctx.fillStyle = '#f00';
  324. ctx.fillRect(0, 0, 100, 50);
  325. var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
  326. g.addColorStop(0, '#f00');
  327. g.addColorStop(0.01, '#0f0');
  328. g.addColorStop(0.99, '#0f0');
  329. g.addColorStop(1, '#f00');
  330. ctx.fillStyle = g;
  331. ctx.fillRect(0, 0, 100, 50);
  332. */
  333. class GradientsDegenrate2PointGM : public skiagm::GM {
  334. public:
  335. GradientsDegenrate2PointGM(bool dither) : fDither(dither) {}
  336. private:
  337. SkString onShortName() override {
  338. return SkString(fDither ? "gradients_degenerate_2pt" : "gradients_degenerate_2pt_nodither");
  339. }
  340. SkISize onISize() override { return {320, 320}; }
  341. void onDraw(SkCanvas* canvas) override {
  342. canvas->drawColor(SK_ColorBLUE);
  343. SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED };
  344. SkScalar pos[] = { 0, 0.01f, 0.99f, SK_Scalar1 };
  345. SkPoint c0;
  346. c0.iset(-80, 25);
  347. SkScalar r0 = SkIntToScalar(70);
  348. SkPoint c1;
  349. c1.iset(0, 25);
  350. SkScalar r1 = SkIntToScalar(150);
  351. SkPaint paint;
  352. paint.setShader(SkGradientShader::MakeTwoPointConical(c0, r0, c1, r1, colors,
  353. pos, SK_ARRAY_COUNT(pos),
  354. SkTileMode::kClamp));
  355. paint.setDither(fDither);
  356. canvas->drawPaint(paint);
  357. }
  358. bool fDither;
  359. };
  360. DEF_GM( return new GradientsDegenrate2PointGM(true); )
  361. DEF_GM( return new GradientsDegenrate2PointGM(false); )
  362. /* bug.skia.org/517
  363. <canvas id="canvas"></canvas>
  364. <script>
  365. var c = document.getElementById("canvas");
  366. var ctx = c.getContext("2d");
  367. ctx.fillStyle = '#ff0';
  368. ctx.fillRect(0, 0, 100, 50);
  369. var g = ctx.createRadialGradient(200, 25, 20, 200, 25, 10);
  370. g.addColorStop(0, '#0f0');
  371. g.addColorStop(0.003, '#f00'); // 0.004 makes this work
  372. g.addColorStop(1, '#ff0');
  373. ctx.fillStyle = g;
  374. ctx.fillRect(0, 0, 100, 50);
  375. </script>
  376. */
  377. // should draw only green
  378. DEF_SIMPLE_GM(small_color_stop, canvas, 100, 150) {
  379. SkColor colors[] = { SK_ColorGREEN, SK_ColorRED, SK_ColorYELLOW };
  380. SkScalar pos[] = { 0, 0.003f, SK_Scalar1 }; // 0.004f makes this work
  381. SkPoint c0 = { 200, 25 };
  382. SkScalar r0 = 20;
  383. SkPoint c1 = { 200, 25 };
  384. SkScalar r1 = 10;
  385. SkPaint paint;
  386. paint.setColor(SK_ColorYELLOW);
  387. canvas->drawRect(SkRect::MakeWH(100, 150), paint);
  388. paint.setShader(SkGradientShader::MakeTwoPointConical(c0, r0, c1, r1, colors, pos,
  389. SK_ARRAY_COUNT(pos),
  390. SkTileMode::kClamp));
  391. canvas->drawRect(SkRect::MakeWH(100, 150), paint);
  392. }
  393. /// Tests correctness of *optimized* codepaths in gradients.
  394. class ClampedGradientsGM : public skiagm::GM {
  395. public:
  396. ClampedGradientsGM(bool dither) : fDither(dither) {}
  397. private:
  398. SkString onShortName() override {
  399. return SkString(fDither ? "clamped_gradients" : "clamped_gradients_nodither");
  400. }
  401. SkISize onISize() override { return {640, 510}; }
  402. void onDraw(SkCanvas* canvas) override {
  403. canvas->drawColor(0xFFDDDDDD);
  404. SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) };
  405. SkPaint paint;
  406. paint.setDither(fDither);
  407. paint.setAntiAlias(true);
  408. SkPoint center;
  409. center.iset(0, 300);
  410. canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
  411. paint.setShader(SkGradientShader::MakeRadial(
  412. SkPoint(center),
  413. SkIntToScalar(200), gColors, nullptr, 5,
  414. SkTileMode::kClamp));
  415. canvas->drawRect(r, paint);
  416. }
  417. bool fDither;
  418. };
  419. DEF_GM( return new ClampedGradientsGM(true); )
  420. DEF_GM( return new ClampedGradientsGM(false); )
  421. /// Checks quality of large radial gradients, which may display
  422. /// some banding.
  423. class RadialGradientGM : public skiagm::GM {
  424. SkString onShortName() override { return SkString("radial_gradient"); }
  425. SkISize onISize() override { return {1280, 1280}; }
  426. void onDraw(SkCanvas* canvas) override {
  427. const SkISize dim = this->getISize();
  428. canvas->drawColor(0xFF000000);
  429. SkPaint paint;
  430. paint.setDither(true);
  431. SkPoint center;
  432. center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2);
  433. SkScalar radius = SkIntToScalar(dim.width())/2;
  434. const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 };
  435. const SkScalar pos[] = { 0.0f,
  436. 0.35f,
  437. 1.0f };
  438. paint.setShader(SkGradientShader::MakeRadial(center, radius, colors, pos,
  439. SK_ARRAY_COUNT(pos),
  440. SkTileMode::kClamp));
  441. SkRect r = {
  442. 0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height())
  443. };
  444. canvas->drawRect(r, paint);
  445. }
  446. };
  447. DEF_GM( return new RadialGradientGM; )
  448. class RadialGradient2GM : public skiagm::GM {
  449. public:
  450. RadialGradient2GM(bool dither) : fDither(dither) {}
  451. private:
  452. SkString onShortName() override {
  453. return SkString(fDither ? "radial_gradient2" : "radial_gradient2_nodither");
  454. }
  455. SkISize onISize() override { return {800, 400}; }
  456. // Reproduces the example given in bug 7671058.
  457. void onDraw(SkCanvas* canvas) override {
  458. SkPaint paint1, paint2, paint3;
  459. paint1.setStyle(SkPaint::kFill_Style);
  460. paint2.setStyle(SkPaint::kFill_Style);
  461. paint3.setStyle(SkPaint::kFill_Style);
  462. const SkColor sweep_colors[] =
  463. { 0xFFFF0000, 0xFFFFFF00, 0xFF00FF00, 0xFF00FFFF, 0xFF0000FF, 0xFFFF00FF, 0xFFFF0000 };
  464. const SkColor colors1[] = { 0xFFFFFFFF, 0x00000000 };
  465. const SkColor colors2[] = { 0xFF000000, 0x00000000 };
  466. const SkScalar cx = 200, cy = 200, radius = 150;
  467. SkPoint center;
  468. center.set(cx, cy);
  469. // We can either interpolate endpoints and premultiply each point (default, more precision),
  470. // or premultiply the endpoints first, avoiding the need to premultiply each point (cheap).
  471. const uint32_t flags[] = { 0, SkGradientShader::kInterpolateColorsInPremul_Flag };
  472. for (size_t i = 0; i < SK_ARRAY_COUNT(flags); i++) {
  473. paint1.setShader(SkGradientShader::MakeSweep(cx, cy, sweep_colors,
  474. nullptr, SK_ARRAY_COUNT(sweep_colors),
  475. flags[i], nullptr));
  476. paint2.setShader(SkGradientShader::MakeRadial(center, radius, colors1,
  477. nullptr, SK_ARRAY_COUNT(colors1),
  478. SkTileMode::kClamp,
  479. flags[i], nullptr));
  480. paint3.setShader(SkGradientShader::MakeRadial(center, radius, colors2,
  481. nullptr, SK_ARRAY_COUNT(colors2),
  482. SkTileMode::kClamp,
  483. flags[i], nullptr));
  484. paint1.setDither(fDither);
  485. paint2.setDither(fDither);
  486. paint3.setDither(fDither);
  487. canvas->drawCircle(cx, cy, radius, paint1);
  488. canvas->drawCircle(cx, cy, radius, paint3);
  489. canvas->drawCircle(cx, cy, radius, paint2);
  490. canvas->translate(400, 0);
  491. }
  492. }
  493. private:
  494. bool fDither;
  495. typedef GM INHERITED;
  496. };
  497. DEF_GM( return new RadialGradient2GM(true); )
  498. DEF_GM( return new RadialGradient2GM(false); )
  499. // Shallow radial (shows banding on raster)
  500. class RadialGradient3GM : public skiagm::GM {
  501. public:
  502. RadialGradient3GM(bool dither) : fDither(dither) { }
  503. private:
  504. SkString onShortName() override {
  505. return SkString(fDither ? "radial_gradient3" : "radial_gradient3_nodither");
  506. }
  507. SkISize onISize() override { return {500, 500}; }
  508. bool runAsBench() const override { return true; }
  509. void onOnceBeforeDraw() override {
  510. const SkPoint center = { 0, 0 };
  511. const SkScalar kRadius = 3000;
  512. const SkColor gColors[] = { 0xFFFFFFFF, 0xFF000000 };
  513. fShader = SkGradientShader::MakeRadial(center, kRadius, gColors, nullptr, 2,
  514. SkTileMode::kClamp);
  515. }
  516. void onDraw(SkCanvas* canvas) override {
  517. SkPaint paint;
  518. paint.setShader(fShader);
  519. paint.setDither(fDither);
  520. canvas->drawRect(SkRect::MakeWH(500, 500), paint);
  521. }
  522. private:
  523. sk_sp<SkShader> fShader;
  524. bool fDither;
  525. typedef GM INHERITED;
  526. };
  527. DEF_GM( return new RadialGradient3GM(true); )
  528. DEF_GM( return new RadialGradient3GM(false); )
  529. class RadialGradient4GM : public skiagm::GM {
  530. public:
  531. RadialGradient4GM(bool dither) : fDither(dither) { }
  532. private:
  533. SkString onShortName() override {
  534. return SkString(fDither ? "radial_gradient4" : "radial_gradient4_nodither");
  535. }
  536. SkISize onISize() override { return {500, 500}; }
  537. void onOnceBeforeDraw() override {
  538. const SkPoint center = { 250, 250 };
  539. const SkScalar kRadius = 250;
  540. const SkColor colors[] = { SK_ColorRED, SK_ColorRED, SK_ColorWHITE, SK_ColorWHITE,
  541. SK_ColorRED };
  542. const SkScalar pos[] = { 0, .4f, .4f, .8f, .8f, 1 };
  543. fShader = SkGradientShader::MakeRadial(center, kRadius, colors, pos,
  544. SK_ARRAY_COUNT(gColors), SkTileMode::kClamp);
  545. }
  546. void onDraw(SkCanvas* canvas) override {
  547. SkPaint paint;
  548. paint.setAntiAlias(true);
  549. paint.setDither(fDither);
  550. paint.setShader(fShader);
  551. canvas->drawRect(SkRect::MakeWH(500, 500), paint);
  552. }
  553. private:
  554. sk_sp<SkShader> fShader;
  555. bool fDither;
  556. typedef GM INHERITED;
  557. };
  558. DEF_GM( return new RadialGradient4GM(true); )
  559. DEF_GM( return new RadialGradient4GM(false); )
  560. class LinearGradientGM : public skiagm::GM {
  561. public:
  562. LinearGradientGM(bool dither) : fDither(dither) { }
  563. private:
  564. SkString onShortName() override {
  565. return SkString(fDither ? "linear_gradient" : "linear_gradient_nodither");
  566. }
  567. const SkScalar kWidthBump = 30.f;
  568. const SkScalar kHeight = 5.f;
  569. const SkScalar kMinWidth = 540.f;
  570. SkISize onISize() override { return {500, 500}; }
  571. void onOnceBeforeDraw() override {
  572. SkPoint pts[2] = { {0, 0}, {0, 0} };
  573. const SkColor colors[] = { SK_ColorWHITE, SK_ColorWHITE, 0xFF008200, 0xFF008200,
  574. SK_ColorWHITE, SK_ColorWHITE };
  575. const SkScalar unitPos[] = { 0, 50, 70, 500, 540 };
  576. SkScalar pos[6];
  577. pos[5] = 1;
  578. for (int index = 0; index < (int) SK_ARRAY_COUNT(fShader); ++index) {
  579. pts[1].fX = 500.f + index * kWidthBump;
  580. for (int inner = 0; inner < (int) SK_ARRAY_COUNT(unitPos); ++inner) {
  581. pos[inner] = unitPos[inner] / (kMinWidth + index * kWidthBump);
  582. }
  583. fShader[index] = SkGradientShader::MakeLinear(pts, colors, pos,
  584. SK_ARRAY_COUNT(gColors), SkTileMode::kClamp);
  585. }
  586. }
  587. void onDraw(SkCanvas* canvas) override {
  588. SkPaint paint;
  589. paint.setAntiAlias(true);
  590. paint.setDither(fDither);
  591. for (int index = 0; index < (int) SK_ARRAY_COUNT(fShader); ++index) {
  592. paint.setShader(fShader[index]);
  593. canvas->drawRect(SkRect::MakeLTRB(0, index * kHeight, kMinWidth + index * kWidthBump,
  594. (index + 1) * kHeight), paint);
  595. }
  596. }
  597. private:
  598. sk_sp<SkShader> fShader[100];
  599. bool fDither;
  600. typedef GM INHERITED;
  601. };
  602. DEF_GM( return new LinearGradientGM(true); )
  603. DEF_GM( return new LinearGradientGM(false); )
  604. class LinearGradientTinyGM : public skiagm::GM {
  605. static constexpr uint32_t kFlags = 0;
  606. SkString onShortName() override { return SkString("linear_gradient_tiny"); }
  607. SkISize onISize() override { return {600, 500}; }
  608. void onDraw(SkCanvas* canvas) override {
  609. const SkScalar kRectSize = 100;
  610. const unsigned kStopCount = 3;
  611. const SkColor colors[kStopCount] = { SK_ColorGREEN, SK_ColorRED, SK_ColorGREEN };
  612. const struct {
  613. SkPoint pts[2];
  614. SkScalar pos[kStopCount];
  615. } configs[] = {
  616. { { SkPoint::Make(0, 0), SkPoint::Make(10, 0) }, { 0, 0.999999f, 1 }},
  617. { { SkPoint::Make(0, 0), SkPoint::Make(10, 0) }, { 0, 0.000001f, 1 }},
  618. { { SkPoint::Make(0, 0), SkPoint::Make(10, 0) }, { 0, 0.999999999f, 1 }},
  619. { { SkPoint::Make(0, 0), SkPoint::Make(10, 0) }, { 0, 0.000000001f, 1 }},
  620. { { SkPoint::Make(0, 0), SkPoint::Make(0, 10) }, { 0, 0.999999f, 1 }},
  621. { { SkPoint::Make(0, 0), SkPoint::Make(0, 10) }, { 0, 0.000001f, 1 }},
  622. { { SkPoint::Make(0, 0), SkPoint::Make(0, 10) }, { 0, 0.999999999f, 1 }},
  623. { { SkPoint::Make(0, 0), SkPoint::Make(0, 10) }, { 0, 0.000000001f, 1 }},
  624. { { SkPoint::Make(0, 0), SkPoint::Make(0.00001f, 0) }, { 0, 0.5f, 1 }},
  625. { { SkPoint::Make(9.99999f, 0), SkPoint::Make(10, 0) }, { 0, 0.5f, 1 }},
  626. { { SkPoint::Make(0, 0), SkPoint::Make(0, 0.00001f) }, { 0, 0.5f, 1 }},
  627. { { SkPoint::Make(0, 9.99999f), SkPoint::Make(0, 10) }, { 0, 0.5f, 1 }},
  628. };
  629. SkPaint paint;
  630. for (unsigned i = 0; i < SK_ARRAY_COUNT(configs); ++i) {
  631. SkAutoCanvasRestore acr(canvas, true);
  632. paint.setShader(SkGradientShader::MakeLinear(configs[i].pts, colors, configs[i].pos,
  633. kStopCount, SkTileMode::kClamp,
  634. kFlags, nullptr));
  635. canvas->translate(kRectSize * ((i % 4) * 1.5f + 0.25f),
  636. kRectSize * ((i / 4) * 1.5f + 0.25f));
  637. canvas->drawRect(SkRect::MakeWH(kRectSize, kRectSize), paint);
  638. }
  639. }
  640. };
  641. DEF_GM( return new LinearGradientTinyGM; )
  642. } // namespace
  643. ///////////////////////////////////////////////////////////////////////////////////////////////////
  644. struct GradRun {
  645. SkColor fColors[4];
  646. SkScalar fPos[4];
  647. int fCount;
  648. };
  649. #define SIZE 121
  650. static sk_sp<SkShader> make_linear(const GradRun& run, SkTileMode mode) {
  651. const SkPoint pts[] { { 30, 30 }, { SIZE - 30, SIZE - 30 } };
  652. return SkGradientShader::MakeLinear(pts, run.fColors, run.fPos, run.fCount, mode);
  653. }
  654. static sk_sp<SkShader> make_radial(const GradRun& run, SkTileMode mode) {
  655. const SkScalar half = SIZE * 0.5f;
  656. return SkGradientShader::MakeRadial({half,half}, half - 10, run.fColors, run.fPos,
  657. run.fCount, mode);
  658. }
  659. static sk_sp<SkShader> make_conical(const GradRun& run, SkTileMode mode) {
  660. const SkScalar half = SIZE * 0.5f;
  661. const SkPoint center { half, half };
  662. return SkGradientShader::MakeTwoPointConical(center, 20, center, half - 10,
  663. run.fColors, run.fPos, run.fCount, mode);
  664. }
  665. static sk_sp<SkShader> make_sweep(const GradRun& run, SkTileMode) {
  666. const SkScalar half = SIZE * 0.5f;
  667. return SkGradientShader::MakeSweep(half, half, run.fColors, run.fPos, run.fCount);
  668. }
  669. /*
  670. * Exercise duplicate color-stops, at the ends, and in the middle
  671. *
  672. * At the time of this writing, only Linear correctly deals with duplicates at the ends,
  673. * and then only correctly on CPU backend.
  674. */
  675. DEF_SIMPLE_GM(gradients_dup_color_stops, canvas, 704, 564) {
  676. const SkColor preColor = 0xFFFF0000; // clamp color before start
  677. const SkColor postColor = 0xFF0000FF; // clamp color after end
  678. const SkColor color0 = 0xFF000000;
  679. const SkColor color1 = 0xFF00FF00;
  680. const SkColor badColor = 0xFF3388BB; // should never be seen, fills out fixed-size array
  681. const GradRun runs[] = {
  682. { { color0, color1, badColor, badColor },
  683. { 0, 1, -1, -1 },
  684. 2,
  685. },
  686. { { preColor, color0, color1, badColor },
  687. { 0, 0, 1, -1 },
  688. 3,
  689. },
  690. { { color0, color1, postColor, badColor },
  691. { 0, 1, 1, -1 },
  692. 3,
  693. },
  694. { { preColor, color0, color1, postColor },
  695. { 0, 0, 1, 1 },
  696. 4,
  697. },
  698. { { color0, color0, color1, color1 },
  699. { 0, 0.5f, 0.5f, 1 },
  700. 4,
  701. },
  702. };
  703. sk_sp<SkShader> (*factories[])(const GradRun&, SkTileMode) {
  704. make_linear, make_radial, make_conical, make_sweep
  705. };
  706. const SkRect rect = SkRect::MakeWH(SIZE, SIZE);
  707. const SkScalar dx = SIZE + 20;
  708. const SkScalar dy = SIZE + 20;
  709. const SkTileMode mode = SkTileMode::kClamp;
  710. SkPaint paint;
  711. canvas->translate(10, 10 - dy);
  712. for (auto factory : factories) {
  713. canvas->translate(0, dy);
  714. SkAutoCanvasRestore acr(canvas, true);
  715. for (const auto& run : runs) {
  716. paint.setShader(factory(run, mode));
  717. canvas->drawRect(rect, paint);
  718. canvas->translate(dx, 0);
  719. }
  720. }
  721. }
  722. static void draw_many_stops(SkCanvas* canvas) {
  723. const unsigned kStopCount = 200;
  724. const SkPoint pts[] = { {50, 50}, {450, 465}};
  725. SkColor colors[kStopCount];
  726. for (unsigned i = 0; i < kStopCount; i++) {
  727. switch (i % 5) {
  728. case 0: colors[i] = SK_ColorRED; break;
  729. case 1: colors[i] = SK_ColorGREEN; break;
  730. case 2: colors[i] = SK_ColorGREEN; break;
  731. case 3: colors[i] = SK_ColorBLUE; break;
  732. case 4: colors[i] = SK_ColorRED; break;
  733. }
  734. }
  735. SkPaint p;
  736. p.setShader(SkGradientShader::MakeLinear(
  737. pts, colors, nullptr, SK_ARRAY_COUNT(colors), SkTileMode::kClamp));
  738. canvas->drawRect(SkRect::MakeXYWH(0, 0, 500, 500), p);
  739. }
  740. DEF_SIMPLE_GM(gradient_many_stops, canvas, 500, 500) {
  741. draw_many_stops(canvas);
  742. }
  743. static void draw_circle_shader(SkCanvas* canvas, SkScalar cx, SkScalar cy, SkScalar r,
  744. sk_sp<SkShader> (*shaderFunc)()) {
  745. SkPaint p;
  746. p.setAntiAlias(true);
  747. p.setShader(shaderFunc());
  748. canvas->drawCircle(cx, cy, r, p);
  749. p.setShader(nullptr);
  750. p.setColor(SK_ColorGRAY);
  751. p.setStyle(SkPaint::kStroke_Style);
  752. p.setStrokeWidth(2);
  753. canvas->drawCircle(cx, cy, r, p);
  754. }
  755. DEF_SIMPLE_GM(fancy_gradients, canvas, 800, 300) {
  756. draw_circle_shader(canvas, 150, 150, 100, []() -> sk_sp<SkShader> {
  757. // Checkerboard using two linear gradients + picture shader.
  758. SkScalar kTileSize = 80 / sqrtf(2);
  759. SkColor colors1[] = { 0xff000000, 0xff000000,
  760. 0xffffffff, 0xffffffff,
  761. 0xff000000, 0xff000000 };
  762. SkColor colors2[] = { 0xff000000, 0xff000000,
  763. 0x00000000, 0x00000000,
  764. 0xff000000, 0xff000000 };
  765. SkScalar pos[] = { 0, .25f, .25f, .75f, .75f, 1 };
  766. static_assert(SK_ARRAY_COUNT(colors1) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
  767. static_assert(SK_ARRAY_COUNT(colors2) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
  768. SkPictureRecorder recorder;
  769. recorder.beginRecording(SkRect::MakeWH(kTileSize, kTileSize));
  770. SkPaint p;
  771. SkPoint pts1[] = { { 0, 0 }, { kTileSize, kTileSize }};
  772. p.setShader(SkGradientShader::MakeLinear(pts1, colors1, pos, SK_ARRAY_COUNT(colors1),
  773. SkTileMode::kClamp, 0, nullptr));
  774. recorder.getRecordingCanvas()->drawPaint(p);
  775. SkPoint pts2[] = { { 0, kTileSize }, { kTileSize, 0 }};
  776. p.setShader(SkGradientShader::MakeLinear(pts2, colors2, pos, SK_ARRAY_COUNT(colors2),
  777. SkTileMode::kClamp, 0, nullptr));
  778. recorder.getRecordingCanvas()->drawPaint(p);
  779. SkMatrix m = SkMatrix::I();
  780. m.preRotate(45);
  781. return recorder.finishRecordingAsPicture()->makeShader(
  782. SkTileMode::kRepeat,
  783. SkTileMode::kRepeat, &m, nullptr);
  784. });
  785. draw_circle_shader(canvas, 400, 150, 100, []() -> sk_sp<SkShader> {
  786. // Checkerboard using a sweep gradient + picture shader.
  787. SkScalar kTileSize = 80;
  788. SkColor colors[] = { 0xff000000, 0xff000000,
  789. 0xffffffff, 0xffffffff,
  790. 0xff000000, 0xff000000,
  791. 0xffffffff, 0xffffffff };
  792. SkScalar pos[] = { 0, .25f, .25f, .5f, .5f, .75f, .75f, 1 };
  793. static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
  794. SkPaint p;
  795. p.setShader(SkGradientShader::MakeSweep(kTileSize / 2, kTileSize / 2,
  796. colors, pos, SK_ARRAY_COUNT(colors), 0, nullptr));
  797. SkPictureRecorder recorder;
  798. recorder.beginRecording(SkRect::MakeWH(kTileSize, kTileSize))->drawPaint(p);
  799. return recorder.finishRecordingAsPicture()->makeShader(
  800. SkTileMode::kRepeat,
  801. SkTileMode::kRepeat);
  802. });
  803. draw_circle_shader(canvas, 650, 150, 100, []() -> sk_sp<SkShader> {
  804. // Dartboard using sweep + radial.
  805. const SkColor a = 0xffffffff;
  806. const SkColor b = 0xff000000;
  807. SkColor colors[] = { a, a, b, b, a, a, b, b, a, a, b, b, a, a, b, b};
  808. SkScalar pos[] = { 0, .125f, .125f, .25f, .25f, .375f, .375f, .5f, .5f,
  809. .625f, .625f, .75f, .75f, .875f, .875f, 1};
  810. static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos), "color/pos size mismatch");
  811. SkPoint center = { 650, 150 };
  812. sk_sp<SkShader> sweep1 = SkGradientShader::MakeSweep(center.x(), center.y(), colors, pos,
  813. SK_ARRAY_COUNT(colors), 0, nullptr);
  814. SkMatrix m = SkMatrix::I();
  815. m.preRotate(22.5f, center.x(), center.y());
  816. sk_sp<SkShader> sweep2 = SkGradientShader::MakeSweep(center.x(), center.y(), colors, pos,
  817. SK_ARRAY_COUNT(colors), 0, &m);
  818. sk_sp<SkShader> sweep(SkShaders::Blend(SkBlendMode::kExclusion, sweep1, sweep2));
  819. SkScalar radialPos[] = { 0, .02f, .02f, .04f, .04f, .08f, .08f, .16f, .16f, .31f, .31f,
  820. .62f, .62f, 1, 1, 1 };
  821. static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(radialPos),
  822. "color/pos size mismatch");
  823. return SkShaders::Blend(SkBlendMode::kExclusion, sweep,
  824. SkGradientShader::MakeRadial(center, 100, colors,
  825. radialPos,
  826. SK_ARRAY_COUNT(radialPos),
  827. SkTileMode::kClamp));
  828. });
  829. }
  830. DEF_SIMPLE_GM(sweep_tiling, canvas, 690, 512) {
  831. static constexpr SkScalar size = 160;
  832. static constexpr SkColor colors[] = { SK_ColorBLUE, SK_ColorYELLOW, SK_ColorGREEN };
  833. static constexpr SkScalar pos[] = { 0, .25f, .50f };
  834. static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos), "size mismatch");
  835. static constexpr SkTileMode modes[] = { SkTileMode::kClamp,
  836. SkTileMode::kRepeat,
  837. SkTileMode::kMirror };
  838. static const struct {
  839. SkScalar start, end;
  840. } angles[] = {
  841. { -330, -270 },
  842. { 30, 90 },
  843. { 390, 450 },
  844. { -30, 800 },
  845. };
  846. SkPaint p;
  847. const SkRect r = SkRect::MakeWH(size, size);
  848. for (auto mode : modes) {
  849. {
  850. SkAutoCanvasRestore acr(canvas, true);
  851. for (auto angle : angles) {
  852. p.setShader(SkGradientShader::MakeSweep(size / 2, size / 2, colors, pos,
  853. SK_ARRAY_COUNT(colors), mode,
  854. angle.start, angle.end, 0, nullptr));
  855. canvas->drawRect(r, p);
  856. canvas->translate(size * 1.1f, 0);
  857. }
  858. }
  859. canvas->translate(0, size * 1.1f);
  860. }
  861. }
  862. // Exercises the special-case Ganesh gradient effects.
  863. DEF_SIMPLE_GM(gradients_interesting, canvas, 640, 1300) {
  864. static const SkColor colors2[] = { SK_ColorRED, SK_ColorBLUE };
  865. static const SkColor colors3[] = { SK_ColorRED, SK_ColorYELLOW, SK_ColorBLUE };
  866. static const SkColor colors4[] = { SK_ColorRED, SK_ColorYELLOW, SK_ColorYELLOW, SK_ColorBLUE };
  867. static const SkScalar softRight[] = { 0, .999f, 1 }; // Based on Android launcher "clipping"
  868. static const SkScalar hardLeft[] = { 0, 0, 1 };
  869. static const SkScalar hardRight[] = { 0, 1, 1 };
  870. static const SkScalar hardCenter[] = { 0, .5f, .5f, 1 };
  871. static const struct {
  872. const SkColor* colors;
  873. const SkScalar* pos;
  874. int count;
  875. } configs[] = {
  876. { colors2, nullptr, 2 }, // kTwo_ColorType
  877. { colors3, nullptr, 3 }, // kThree_ColorType (simple)
  878. { colors3, softRight, 3 }, // kThree_ColorType (tricky)
  879. { colors3, hardLeft, 3 }, // kHardStopLeftEdged_ColorType
  880. { colors3, hardRight, 3 }, // kHardStopRightEdged_ColorType
  881. { colors4, hardCenter, 4 }, // kSingleHardStop_ColorType
  882. };
  883. static const SkTileMode modes[] = {
  884. SkTileMode::kClamp,
  885. SkTileMode::kRepeat,
  886. SkTileMode::kMirror,
  887. };
  888. static constexpr SkScalar size = 200;
  889. static const SkPoint pts[] = { { size / 3, size / 3 }, { size * 2 / 3, size * 2 / 3} };
  890. SkPaint p;
  891. for (const auto& cfg : configs) {
  892. {
  893. SkAutoCanvasRestore acr(canvas, true);
  894. for (auto mode : modes) {
  895. p.setShader(SkGradientShader::MakeLinear(pts, cfg.colors, cfg.pos, cfg.count,
  896. mode));
  897. canvas->drawRect(SkRect::MakeWH(size, size), p);
  898. canvas->translate(size * 1.1f, 0);
  899. }
  900. }
  901. canvas->translate(0, size * 1.1f);
  902. }
  903. }