3 * Copyright 2011 Google Inc.
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
9 #include "SkGradientShader.h"
15 const SkColor* fColors;
19 static const SkColor gColors[] = {
20 SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
22 static const SkScalar gPos0[] = { 0, SK_Scalar1 };
23 static const SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
24 static const SkScalar gPos2[] = {
25 0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
28 static const SkScalar gPosClamp[] = {0.0f, 0.0f, 1.0f, 1.0f};
29 static const SkColor gColorClamp[] = {
30 SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorBLUE
33 static const GradData gGradData[] = {
35 { 2, gColors, gPos0 },
36 { 2, gColors, gPos1 },
38 { 5, gColors, gPos2 },
39 { 4, gColorClamp, gPosClamp }
42 static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
43 SkShader::TileMode tm, const SkMatrix& localMatrix) {
44 return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos,
45 data.fCount, tm, 0, &localMatrix);
48 static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data,
49 SkShader::TileMode tm, const SkMatrix& localMatrix) {
51 center.set(SkScalarAve(pts[0].fX, pts[1].fX),
52 SkScalarAve(pts[0].fY, pts[1].fY));
53 return SkGradientShader::CreateRadial(center, center.fX, data.fColors,
54 data.fPos, data.fCount, tm, 0, &localMatrix);
57 static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
58 SkShader::TileMode, const SkMatrix& localMatrix) {
60 center.set(SkScalarAve(pts[0].fX, pts[1].fX),
61 SkScalarAve(pts[0].fY, pts[1].fY));
62 return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors,
63 data.fPos, data.fCount, 0, &localMatrix);
66 static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data,
67 SkShader::TileMode tm, const SkMatrix& localMatrix) {
68 SkPoint center0, center1;
69 center0.set(SkScalarAve(pts[0].fX, pts[1].fX),
70 SkScalarAve(pts[0].fY, pts[1].fY));
71 center1.set(SkScalarInterp(pts[0].fX, pts[1].fX, SkIntToScalar(3)/5),
72 SkScalarInterp(pts[0].fY, pts[1].fY, SkIntToScalar(1)/4));
73 return SkGradientShader::CreateTwoPointRadial(
74 center1, (pts[1].fX - pts[0].fX) / 7,
75 center0, (pts[1].fX - pts[0].fX) / 2,
76 data.fColors, data.fPos, data.fCount, tm,
80 static SkShader* Make2Conical(const SkPoint pts[2], const GradData& data,
81 SkShader::TileMode tm, const SkMatrix& localMatrix) {
82 SkPoint center0, center1;
83 SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 10);
84 SkScalar radius1 = SkScalarDiv(pts[1].fX - pts[0].fX, 3);
85 center0.set(pts[0].fX + radius0, pts[0].fY + radius0);
86 center1.set(pts[1].fX - radius1, pts[1].fY - radius1);
87 return SkGradientShader::CreateTwoPointConical(center1, radius1,
89 data.fColors, data.fPos,
90 data.fCount, tm, 0, &localMatrix);
93 typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
94 SkShader::TileMode tm, const SkMatrix& localMatrix);
95 static const GradMaker gGradMakers[] = {
96 MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2Conical
99 ///////////////////////////////////////////////////////////////////////////////
101 class GradientsGM : public GM {
104 this->setBGColor(0xFFDDDDDD);
109 SkString onShortName() {
110 return SkString("gradients");
113 virtual SkISize onISize() { return SkISize::Make(840, 815); }
115 virtual void onDraw(SkCanvas* canvas) {
119 { SkIntToScalar(100), SkIntToScalar(100) }
121 SkShader::TileMode tm = SkShader::kClamp_TileMode;
122 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
124 paint.setAntiAlias(true);
126 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
127 for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
129 for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
130 SkMatrix scale = SkMatrix::I();
132 if (i == 5) { // if the clamp case
133 scale.setScale(0.5f, 0.5f);
134 scale.postTranslate(25.f, 25.f);
137 SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, scale);
139 paint.setShader(shader);
140 canvas->drawRect(r, paint);
142 canvas->translate(0, SkIntToScalar(120));
145 canvas->translate(SkIntToScalar(120), 0);
150 typedef GM INHERITED;
153 // Based on the original gradient slide, but with perspective applied to the
154 // gradient shaders' local matrices
155 class GradientsLocalPerspectiveGM : public GM {
157 GradientsLocalPerspectiveGM() {
158 this->setBGColor(0xFFDDDDDD);
163 SkString onShortName() {
164 return SkString("gradients_local_perspective");
167 virtual SkISize onISize() { return SkISize::Make(840, 815); }
169 virtual void onDraw(SkCanvas* canvas) {
173 { SkIntToScalar(100), SkIntToScalar(100) }
175 SkShader::TileMode tm = SkShader::kClamp_TileMode;
176 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
178 paint.setAntiAlias(true);
180 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
181 for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
183 for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
184 // apply an increasing y perspective as we move to the right
185 SkMatrix perspective;
186 perspective.setIdentity();
187 perspective.setPerspY(SkScalarDiv(SkIntToScalar((unsigned) i+1),
188 SkIntToScalar(500)));
189 perspective.setSkewX(SkScalarDiv(SkIntToScalar((unsigned) i+1),
192 SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, perspective);
194 paint.setShader(shader);
195 canvas->drawRect(r, paint);
197 canvas->translate(0, SkIntToScalar(120));
200 canvas->translate(SkIntToScalar(120), 0);
205 typedef GM INHERITED;
208 // Based on the original gradient slide, but with perspective applied to
210 class GradientsViewPerspectiveGM : public GradientsGM {
212 SkString onShortName() {
213 return SkString("gradients_view_perspective");
216 virtual SkISize onISize() { return SkISize::Make(840, 500); }
218 virtual void onDraw(SkCanvas* canvas) {
219 SkMatrix perspective;
220 perspective.setIdentity();
221 perspective.setPerspY(SkScalarDiv(SK_Scalar1, SkIntToScalar(1000)));
222 perspective.setSkewX(SkScalarDiv(SkIntToScalar(8), SkIntToScalar(25)));
223 canvas->concat(perspective);
224 INHERITED::onDraw(canvas);
228 typedef GradientsGM INHERITED;
232 Inspired by this <canvas> javascript, where we need to detect that we are not
233 solving a quadratic equation, but must instead solve a linear (since our X^2
236 ctx.fillStyle = '#f00';
237 ctx.fillRect(0, 0, 100, 50);
239 var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
240 g.addColorStop(0, '#f00');
241 g.addColorStop(0.01, '#0f0');
242 g.addColorStop(0.99, '#0f0');
243 g.addColorStop(1, '#f00');
245 ctx.fillRect(0, 0, 100, 50);
247 class GradientsDegenrate2PointGM : public GM {
249 GradientsDegenrate2PointGM() {}
252 SkString onShortName() {
253 return SkString("gradients_degenerate_2pt");
256 virtual SkISize onISize() { return SkISize::Make(320, 320); }
258 void drawBG(SkCanvas* canvas) {
259 canvas->drawColor(SK_ColorBLUE);
262 virtual void onDraw(SkCanvas* canvas) {
263 this->drawBG(canvas);
265 SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED };
266 SkScalar pos[] = { 0, 0.01f, 0.99f, SK_Scalar1 };
269 SkScalar r0 = SkIntToScalar(70);
272 SkScalar r1 = SkIntToScalar(150);
273 SkShader* s = SkGradientShader::CreateTwoPointRadial(c0, r0, c1, r1, colors,
274 pos, SK_ARRAY_COUNT(pos),
275 SkShader::kClamp_TileMode);
277 paint.setShader(s)->unref();
278 canvas->drawPaint(paint);
282 typedef GM INHERITED;
285 /// Tests correctness of *optimized* codepaths in gradients.
287 class ClampedGradientsGM : public GM {
289 ClampedGradientsGM() {}
292 SkString onShortName() { return SkString("clamped_gradients"); }
294 virtual SkISize onISize() { return SkISize::Make(640, 510); }
296 void drawBG(SkCanvas* canvas) {
297 canvas->drawColor(0xFFDDDDDD);
300 virtual void onDraw(SkCanvas* canvas) {
301 this->drawBG(canvas);
303 SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) };
305 paint.setAntiAlias(true);
309 canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
310 SkShader* shader = SkGradientShader::CreateRadial(
312 SkIntToScalar(200), gColors, NULL, 5,
313 SkShader::kClamp_TileMode);
314 paint.setShader(shader);
315 canvas->drawRect(r, paint);
320 typedef GM INHERITED;
323 /// Checks quality of large radial gradients, which may display
326 class RadialGradientGM : public GM {
328 RadialGradientGM() {}
332 SkString onShortName() SK_OVERRIDE { return SkString("radial_gradient"); }
333 SkISize onISize() SK_OVERRIDE { return SkISize::Make(1280, 1280); }
334 void drawBG(SkCanvas* canvas) {
335 canvas->drawColor(0xFF000000);
337 void onDraw(SkCanvas* canvas) SK_OVERRIDE {
338 const SkISize dim = this->getISize();
340 this->drawBG(canvas);
343 paint.setDither(true);
345 center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2);
346 SkScalar radius = SkIntToScalar(dim.width())/2;
347 const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 };
348 const SkScalar pos[] = { 0.0f,
352 SkGradientShader::CreateRadial(center, radius, colors,
353 pos, SK_ARRAY_COUNT(pos),
354 SkShader::kClamp_TileMode);
355 paint.setShader(shader)->unref();
357 0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height())
359 canvas->drawRect(r, paint);
362 typedef GM INHERITED;
366 class RadialGradient2GM : public GM {
368 RadialGradient2GM() {}
372 SkString onShortName() SK_OVERRIDE { return SkString("radial_gradient2"); }
373 SkISize onISize() SK_OVERRIDE { return SkISize::Make(800, 400); }
374 void drawBG(SkCanvas* canvas) {
375 canvas->drawColor(0xFF000000);
378 // Reproduces the example given in bug 7671058.
379 void onDraw(SkCanvas* canvas) SK_OVERRIDE {
380 SkPaint paint1, paint2, paint3;
381 paint1.setStyle(SkPaint::kFill_Style);
382 paint2.setStyle(SkPaint::kFill_Style);
383 paint3.setStyle(SkPaint::kFill_Style);
385 const SkColor sweep_colors[] =
386 { 0xFFFF0000, 0xFFFFFF00, 0xFF00FF00, 0xFF00FFFF, 0xFF0000FF, 0xFFFF00FF, 0xFFFF0000 };
387 const SkColor colors1[] = { 0xFFFFFFFF, 0x00000000 };
388 const SkColor colors2[] = { 0xFF000000, 0x00000000 };
390 const SkScalar cx = 200, cy = 200, radius = 150;
394 // We can either interpolate endpoints and premultiply each point (default, more precision),
395 // or premultiply the endpoints first, avoiding the need to premultiply each point (cheap).
396 const uint32_t flags[] = { 0, SkGradientShader::kInterpolateColorsInPremul_Flag };
398 for (size_t i = 0; i < SK_ARRAY_COUNT(flags); i++) {
399 SkAutoTUnref<SkShader> sweep(
400 SkGradientShader::CreateSweep(cx, cy, sweep_colors,
401 NULL, SK_ARRAY_COUNT(sweep_colors),
403 SkAutoTUnref<SkShader> radial1(
404 SkGradientShader::CreateRadial(center, radius, colors1,
405 NULL, SK_ARRAY_COUNT(colors1),
406 SkShader::kClamp_TileMode,
408 SkAutoTUnref<SkShader> radial2(
409 SkGradientShader::CreateRadial(center, radius, colors2,
410 NULL, SK_ARRAY_COUNT(colors2),
411 SkShader::kClamp_TileMode,
413 paint1.setShader(sweep);
414 paint2.setShader(radial1);
415 paint3.setShader(radial2);
417 canvas->drawCircle(cx, cy, radius, paint1);
418 canvas->drawCircle(cx, cy, radius, paint3);
419 canvas->drawCircle(cx, cy, radius, paint2);
421 canvas->translate(400, 0);
426 typedef GM INHERITED;
429 ///////////////////////////////////////////////////////////////////////////////
431 static GM* MyFactory(void*) { return new GradientsGM; }
432 static GMRegistry reg(MyFactory);
434 static GM* MyFactory2(void*) { return new GradientsDegenrate2PointGM; }
435 static GMRegistry reg2(MyFactory2);
437 static GM* MyFactory3(void*) { return new ClampedGradientsGM; }
438 static GMRegistry reg3(MyFactory3);
440 static GM* MyFactory4(void*) { return new RadialGradientGM; }
441 static GMRegistry reg4(MyFactory4);
443 static GM* MyFactory5(void*) { return new GradientsLocalPerspectiveGM; }
444 static GMRegistry reg5(MyFactory5);
446 static GM* MyFactory6(void*) { return new GradientsViewPerspectiveGM; }
447 static GMRegistry reg6(MyFactory6);
449 static GM* MyFactory7(void*) { return new RadialGradient2GM; }
450 static GMRegistry reg7(MyFactory7);